#
#
# rename "src/model/Attributes.cpp"
# to "src/model/GetAttributes.cpp"
#
# rename "src/model/Attributes.h"
# to "src/model/GetAttributes.h"
#
# add_file "res/forms/add_edit_attribute.ui"
# content [82503020fbedbad0c1879c82e4203aaed9db637a]
#
# add_file "res/forms/file_history.ui"
# content [04c0cef841a0eee70161d76b12d6772a92238bc0]
#
# add_file "res/forms/unaccounted_renames.ui"
# content [035b00da70ee362272c7fe50039ec41364a231ff]
#
# add_file "res/icons/arrow_down.png"
# content [6390b8ce26bb5b11266833f57a57266df5667388]
#
# add_file "res/icons/blue_dot.png"
# content [374a767d9c0e4543e105686b885ad71950941e1d]
#
# add_file "res/icons/green_dot.png"
# content [9a806782c23117a5bebf10a1ca5f581e7aa13f7e]
#
# add_file "res/icons/red_dot.png"
# content [137b7c7a3e8e311b7168f1bc6b3364d7f1865060]
#
# add_file "res/icons/yellow_dot.png"
# content [d534547752c8546f91dfefa8b352b9eb5e59a87b]
#
# add_file "src/model/GetContentChanged.cpp"
# content [fac748457f536fe8ae1e4022dfc4be9ae9e3a4eb]
#
# add_file "src/model/GetContentChanged.h"
# content [2caf1a98c245f56c89c7036aaf0814338b7327f7]
#
# add_file "src/util/BasicIOWriter.cpp"
# content [7965db7212e1d26f78c7923a3c8add0fd8dd46b7]
#
# add_file "src/util/BasicIOWriter.h"
# content [052ca233a44fa5c872b699b5eee4fd4cc6187f1b]
#
# add_file "src/view/MacStartMenu.cpp"
# content [4cf7133a0572dc78749e556696e812b213d1d0c5]
#
# add_file "src/view/MacStartMenu.h"
# content [d98d19bcc23c5c867e4c9a8ecc9c3d850bfa9b8c]
#
# add_file "src/view/dialogs/AddEditAttribute.cpp"
# content [1f9555c4103d227d66191031fea7b1e9d41462e7]
#
# add_file "src/view/dialogs/AddEditAttribute.h"
# content [aea71c7ba22359058eaccfc8be8d112dd79bf231]
#
# add_file "src/view/dialogs/FileHistory.cpp"
# content [86e856616418ed7875cbd92515116b406947abc4]
#
# add_file "src/view/dialogs/FileHistory.h"
# content [ed76ac56e2b114a67196d38bae14c01b8b9fda63]
#
# add_file "src/view/dialogs/UnaccountedRenames.cpp"
# content [938d5eac2b9884b18445e4eacde26c5d31dea0e8]
#
# add_file "src/view/dialogs/UnaccountedRenames.h"
# content [0691bf54c8ff2d8606e88eb855613a4977ad8b9d]
#
# add_file "tests/MonotoneTest.h"
# content [73986287a1873b2e72dbc62743a7cdd9ab317fb7]
#
# add_file "tests/StdioParserTest.h"
# content [3974e240f162fc6cebbd6df97c3f36ed0440817d]
#
# patch "NEWS"
# from [c5dfe130fb53d6553fc530e7e94140ffd99fe2c3]
# to [949d2a577317be4215e33daa7157824f994577d8]
#
# patch "guitone.pro"
# from [33545cda1a516aede1c2b494f1432818727048c9]
# to [793a62ed84e3c859c3963721260c4e6fa379f1fa]
#
# patch "notes/IDEAS"
# from [0f7d32bac1e3d7405a733c7548508969064efe21]
# to [bdf47a7c58a2a0c0f183ea9ef59290a46d7dbc3c]
#
# patch "notes/RELEASE_CHECKLIST"
# from [3dd539c8e9b77fb957571a2c4dcb641ce25a44f0]
# to [78816f3f795e8651b5ac5fd7b3870b735c7d3bd2]
#
# patch "notes/TODO"
# from [ea5eaa751fa11645c142bca08911a145ef87d4a5]
# to [336f24bc9bc9f3914981275846cbd7b05ff7e19e]
#
# patch "res/forms/application_update.ui"
# from [cfb6889da734fdfa20f68943c73d93983951ce5a]
# to [7d2b3993e719ba0fea62c815d0cee25d6fd48562]
#
# patch "res/forms/changeset_browser.ui"
# from [b305fba833874efa7b6b53773d8cea303c0366d0]
# to [ac59737143d7aac046048608d14ed2dbdefd23b6]
#
# patch "res/forms/commit_revision.ui"
# from [7743280f847be7c2fd842e55b24e48be78d2b6ab]
# to [869cc6762e7b033d8186dc527e9d96ab5e77ff49]
#
# patch "res/forms/file_diff.ui"
# from [7892257391ce4faa476cd26cb04e424ac91a4e2d]
# to [9999a04c668f9a40b1a9b20f7ecd0812e8dc7cd5]
#
# patch "res/forms/key_management.ui"
# from [edec01c656964ce19977a8305c380207fa222bff]
# to [4913ed46e0685544cb5e3cf7cdf98b54aacd42ce]
#
# patch "res/forms/main_window.ui"
# from [6c5b7e9f7df0fc48722a97cf9e48cbc51ea46e2d]
# to [2a502c4c0a34fefa6c210f0f74f6e98a7ad6fe2b]
#
# patch "res/forms/manifest.ui"
# from [b0411bd9c26399c10dd09a486cc6403b80af3f7c]
# to [5b3e49e7bec6ba679c67b10ef1c9c0ee7e6799d2]
#
# patch "res/forms/preferences.ui"
# from [4c7a832f0d210371eb68882a1f736ff150affdff]
# to [9898dad2631df0a8a5cb6f06fc5055160b7e24d2]
#
# patch "res/forms/revision_diff.ui"
# from [d4d25a844c0a4a141bc9ac01933d96371b7116c0]
# to [56845d2399bd6d4a4e6936b65311cb63a599ba99]
#
# patch "res/forms/select_revision.ui"
# from [c1cecd2c790c38b27a87df2bea6e99ca65579c49]
# to [b901de21917e7ec4fa9b7ebfa3f1bb5a93bdcf47]
#
# patch "res/guitone-icon.svg"
# from [f485e11fe3d6d8003fec375967c5375a4ecb9b2f]
# to [00effff3ab338037f85f83a29ee1a41e1f6525b6]
#
# patch "res/guitone.qrc"
# from [d68d97cabc3486a59beaaac87fdb9a936dc75ac3]
# to [f79689930cf514724cb11d18e452cba44630c2b6]
#
# patch "res/i18n/guitone_de.ts"
# from [7d2efd4ce19b2d6b7fe344dfebb06da025cc22c1]
# to [368821342cae5393b64812ebc3addd7032346589]
#
# patch "src/Guitone.cpp"
# from [cae5d0b8210527a4327743b7ea8c4993d4e7639a]
# to [444b0af3793794d34e4657b2fea5e5e983d90ae2]
#
# patch "src/Guitone.h"
# from [881dffddd5bdd9999f2ed36ecdcaa019de153cb4]
# to [9c9143ee93f0633494e48c42e0ee0f64d6af46d6]
#
# patch "src/main.cpp"
# from [e36e3f6a9adf493840944a11b718bf25abe079de]
# to [b3c466a5103800420aa2b0cab6c9d1be4994f5c5]
#
# patch "src/model/Certs.cpp"
# from [0158c7df5e847ce46b6b56557ea7647d5b423b52]
# to [e4a81c6408c64be1a305aa5eb70842e589e34311]
#
# patch "src/model/ContentDiff.cpp"
# from [af2b8ba1408305c6413d2b0900bc9f7d1e76cb3c]
# to [330cf8f074e03c6fc73d6581933660f2e5ac411c]
#
# patch "src/model/ContentDiff.h"
# from [91ee6ba1a90506ce0890d45c4f55d9b146b2d871]
# to [9f0b45bde2c813734bf6f4a9a5c8cb88e629a41d]
#
# patch "src/model/GetAttributes.cpp"
# from [2ea5d72737c95823852fbf7038235b8af7349d49]
# to [c1f8ccabf73da0b5f91634619197f0adbfeb31de]
#
# patch "src/model/GetAttributes.h"
# from [a4d4844a547daa12380f96da44609c627e859b25]
# to [f9f06f87a1177277bce55b69800a14df55fffbd3]
#
# patch "src/model/GetFile.cpp"
# from [5151a9eccdcf6f92a140dc662d5c845f79484c34]
# to [54bfdc61b73d50418793fca0b86bc05f434ee311]
#
# patch "src/model/GetFile.h"
# from [2542dad0537061ec09b64b453ac07a972788b1c6]
# to [341b069c5778068d50bbf01cd34557a04b639db3]
#
# patch "src/model/GetRevision.cpp"
# from [a953b589dc8091738c22564d6c18ff070eb48211]
# to [1ca82e3b9ba7a2956fc60cff0e976c0ec1e681e2]
#
# patch "src/model/GetRevision.h"
# from [0308fe66ff1749162131c693e28efe7497579d55]
# to [9c2675f10e96cf957979a04a2df71ae989d1d6c6]
#
# patch "src/model/Inventory.cpp"
# from [fe03f18135e1c1a39e069ea74b3f6f36028156e5]
# to [38d53b03b3303315ba61b72f5241ffa81e24f8f3]
#
# patch "src/model/Inventory.h"
# from [e9d25b6dda9dcb6b3678cb3699da93695f4ac1ca]
# to [c9834d77c69b3b9d7c6dc35a5a8bc6118a5b01ec]
#
# patch "src/model/Keys.cpp"
# from [9d8302773f28fa012ce8550b4afc166f5318790b]
# to [4fc764b9241d0fcdd8745c96a32dc7504ed26565]
#
# patch "src/model/Manifest.cpp"
# from [2fbdb523819234d538fa763d4affeb9d81a9af6f]
# to [e4700d858ea55077620324ec7c97cdb8286a876a]
#
# patch "src/model/Tags.cpp"
# from [031fb14119fc2c97d0eee5a32b46e0acff4fbf7b]
# to [f0fab0f388865ab22baea3acf6e5d98c13835b99]
#
# patch "src/monotone/FileExporter.cpp"
# from [69943651783b092d180d1b8c350f1665bed145e4]
# to [fe0cd260a3d9c57b64a3c0c1e2de197fe77b531f]
#
# patch "src/monotone/Monotone.cpp"
# from [8d3d1bfe62b2dcb9f94f5f7f676e85bca5b16bbd]
# to [21de72375ce610dba385952d2ba8edb939b42810]
#
# patch "src/monotone/Monotone.h"
# from [eb86d372a5790b81c4e17e6c8a66ce7ec5cc90c7]
# to [b8a441bb1a91236c16fa397e87751a0a2101da59]
#
# patch "src/monotone/MonotoneDelegate.cpp"
# from [e662336f12dfd870e514d14b05d0ba5c028eaf48]
# to [0b1b0ebfaa468cbf89d3ef32a54e9791405b86f5]
#
# patch "src/monotone/MonotoneDelegate.h"
# from [907a718c030f96ed3c2951e5b5b1442b72b6891a]
# to [6971e5c0d79d50fc766f84fb395cf648da80c8e5]
#
# patch "src/monotone/WorkspaceCommitter.cpp"
# from [976aad817f2bb63cbb6c0220e0f30cf6bd8f6517]
# to [2c27ad30e62fd2782321ae897406b9f095ec5c53]
#
# patch "src/monotone/WorkspaceCommitter.h"
# from [df5912a27f8f00ff7c954bb50dfe354125a855fc]
# to [360938d22820d95f116691a962b6cb92ce9bc771]
#
# patch "src/util/AbstractParser.h"
# from [af473981a098c6223e3d11183e1470ce86d8befe]
# to [5d83ade859fbf057e5326f2bdcf8b0f6560a5be2]
#
# patch "src/util/BasicIOParser.cpp"
# from [41c485e6ae2759a0084019bf94663f20b0730949]
# to [f11c59b9074eee097cb97feea7f93bdd146ccb0d]
#
# patch "src/util/BasicIOParser.h"
# from [2abdc40c948b2678ceeebf976e5424076036a83d]
# to [b96df0f29dd64ac740c6e8a7f34c080406d3837e]
#
# patch "src/util/CocoaUtil.h"
# from [8424a0c7f877f0167271bb2386fd00472a32f551]
# to [7bab17bbe14218ea6e1dc33bbca110c0cc13a248]
#
# patch "src/util/CocoaUtil.mm"
# from [af7058410a39e0fa1f7fdb9d1ff5019261fd0960]
# to [5fe5b302f4e7198a04700856bab989b3b7e2f44c]
#
# patch "src/util/DebugLog.cpp"
# from [ebbb4ff026ff490147e9c81f53f3c1b23c82febf]
# to [513339e2eb09994edeed4ad8ed3d98b9a6101c81]
#
# patch "src/util/DebugLog.h"
# from [15ae25ff50330f9a76933a81f344875cd72554d8]
# to [94f2cac41d2381a8ab113dc5adb29131844beaf4]
#
# patch "src/util/DiffParser.cpp"
# from [e98a52a2c7bee64b13376f3cf71b47629a213a7d]
# to [4a0256295820e64a621bee2c82c34357ac711b91]
#
# patch "src/util/StdioParser.h"
# from [8488d8ccabd54eef76d773c0ccaf91ac47d95d3d]
# to [ed0742f484843b709c946a2c030cca934432335d]
#
# patch "src/view/AttributesView.cpp"
# from [622bd8e562abeaa8299ecac4d3a725a0f8eb73ae]
# to [e4b0b75ba4c9abd6fa8efa2c6def8a4f2ae6dc96]
#
# patch "src/view/AttributesView.h"
# from [6331d48f84cd19a9389ea33efd8e11278867b3eb]
# to [919369059dde5efb8b1e8b0c0d1e25e49dfc6825]
#
# patch "src/view/InventoryView.cpp"
# from [bb051af5d94c6e995a1b33658b5d529b87b2df95]
# to [441ff9c1a21693b33b00efe2894d0f50425f6b3c]
#
# patch "src/view/InventoryView.h"
# from [0ad2ef23cc24538fcd3fd656b712f7b6dca17e3e]
# to [fd0d9c5eb87a628057d22b2a41426dcbbe84ca03]
#
# patch "src/view/MainWindow.cpp"
# from [c485c30152a7f43a73d96c33e13054771c5f0250]
# to [c4b6cf1f128df54b18ba2a5b8ca2a2fddc0231aa]
#
# patch "src/view/MainWindow.h"
# from [7ea22986b3ba2005db4b604c592a91365ba78122]
# to [49a87144c3f5ef2d99f1ca0acc4acb11e21a402c]
#
# patch "src/view/TreeView.cpp"
# from [5b4f86399682825dd48a24084136979464fd5c69]
# to [3707234163333d91629f88fd1c84576610f942f0]
#
# patch "src/view/dialogs/ApplicationUpdate.cpp"
# from [7c2ecb7a44dcd75bd5d53e94efe173113df90547]
# to [5b6a164ff54ef8fabc95a3769c2b3c1fd2a878b3]
#
# patch "src/view/dialogs/CommitRevision.cpp"
# from [ffe771b0f74202063686e3d30bcff6f6b9d0636a]
# to [bba8d6475e14a2003cad91417b9bdd901ac15f55]
#
# patch "src/view/dialogs/Dialog.cpp"
# from [926c311a65c0a2eeea6cae03256a56b8adbb7087]
# to [4a9329c127de291fc2e8c236ee2ae3f97344bdfa]
#
# patch "src/view/dialogs/FileDiff.cpp"
# from [3dae40899816900fb362793fef3deda6d2809e5c]
# to [0df18f51a4020c5a2a36495b8da49fc5348c53c5]
#
# patch "src/view/dialogs/FileDiff.h"
# from [dcba92805d020b9d0d83de903f27abda0a5a7c74]
# to [c254fa1cdec0a6fe6545adcf784e5cbcb5ca1573]
#
# patch "src/view/dialogs/Preferences.cpp"
# from [c34d5f6dce4113366c6d807b1b0a4bb97d3350b9]
# to [4f3d46e215748754366bafc460894a91b6febc34]
#
# patch "src/vocab.h"
# from [dbf6b8075bfee39d1c9494bf7fbc58b842904213]
# to [afe123e8771f70b90c89dc2a9cddeb6206f81142]
#
# patch "tests/test.cpp"
# from [1bc1c23455d91468e22f229dffa8fcee9277a8a9]
# to [8c345925e352aea61e5f51a0b2ad01fb6475c282]
#
# patch "tests/test.pro"
# from [5005bf499b91c8cf6e51e3c74915ee1d9069a312]
# to [b437a3ca1b89faa23f2665324caa1a2f121184a3]
#
# set "res/icons/arrow_down.png"
# attr "mtn:manual_merge"
# value "true"
#
# set "res/icons/blue_dot.png"
# attr "mtn:manual_merge"
# value "true"
#
# set "res/icons/green_dot.png"
# attr "mtn:manual_merge"
# value "true"
#
# set "res/icons/red_dot.png"
# attr "mtn:manual_merge"
# value "true"
#
# set "res/icons/yellow_dot.png"
# attr "mtn:manual_merge"
# value "true"
#
============================================================
--- res/forms/add_edit_attribute.ui 82503020fbedbad0c1879c82e4203aaed9db637a
+++ res/forms/add_edit_attribute.ui 82503020fbedbad0c1879c82e4203aaed9db637a
@@ -0,0 +1,150 @@
+
+ AddEditAttributeDialog
+
+
+
+ 0
+ 0
+ 422
+ 136
+
+
+
+ Add / edit an attribute
+
+
+ :/icons/guitone.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Key
+
+
+
+ -
+
+
+ Value
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+
+ 3
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ rejected()
+ AddEditAttributeDialog
+ reject()
+
+
+ 271
+ 109
+
+
+ 236
+ 122
+
+
+
+
+ buttonBox
+ accepted()
+ AddEditAttributeDialog
+ accept()
+
+
+ 353
+ 107
+
+
+ 379
+ 127
+
+
+
+
+
============================================================
--- res/forms/file_history.ui 04c0cef841a0eee70161d76b12d6772a92238bc0
+++ res/forms/file_history.ui 04c0cef841a0eee70161d76b12d6772a92238bc0
@@ -0,0 +1,206 @@
+
+ FileHistoryDialog
+
+
+
+ 0
+ 0
+ 611
+ 411
+
+
+
+ History of %1
+
+
+ :/icons/guitone.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Vertical
+
+
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ true
+
+
+ QAbstractItemView::SingleSelection
+
+
+ false
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ false
+
+
+ Select as first revision
+
+
+
+ -
+
+
+ false
+
+
+ Select as second revision
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 538
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+ Show differences
+
+
+
+
+
+
+
+
+
+ true
+
+
+ QAbstractItemView::NoSelection
+
+
+ QAbstractItemView::ScrollPerPixel
+
+
+ false
+
+
+ false
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Close
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ Splitter
+ QSplitter
+
+
+
+ TreeView
+ QTreeView
+
+
+
+
+
+
+
+
+ closeButton
+ clicked()
+ FileHistoryDialog
+ close()
+
+
+ 229
+ 252
+
+
+ 199
+ 149
+
+
+
+
+
============================================================
--- res/forms/unaccounted_renames.ui 035b00da70ee362272c7fe50039ec41364a231ff
+++ res/forms/unaccounted_renames.ui 035b00da70ee362272c7fe50039ec41364a231ff
@@ -0,0 +1,104 @@
+
+ UnaccountedRenamesDialog
+
+
+
+ 0
+ 0
+ 454
+ 316
+
+
+
+ Unaccounted renames
+
+
+ :/icons/guitone.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ false
+
+
+ Perform checked renames
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Close
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ closeButton
+ clicked()
+ UnaccountedRenamesDialog
+ accept()
+
+
+ 399
+ 272
+
+
+ 252
+ 154
+
+
+
+
+
============================================================
# res/icons/arrow_down.png is binary
============================================================
# res/icons/blue_dot.png is binary
============================================================
# res/icons/green_dot.png is binary
============================================================
# res/icons/red_dot.png is binary
============================================================
# res/icons/yellow_dot.png is binary
============================================================
--- src/model/GetContentChanged.cpp fac748457f536fe8ae1e4022dfc4be9ae9e3a4eb
+++ src/model/GetContentChanged.cpp fac748457f536fe8ae1e4022dfc4be9ae9e3a4eb
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "GetContentChanged.h"
+#include "BasicIOParser.h"
+
+#include
+
+GetContentChanged::GetContentChanged(QObject *parent)
+ : QAbstractItemModel(parent)
+{
+ mtnDelegate = new MonotoneDelegate(this);
+}
+
+GetContentChanged::~GetContentChanged()
+{
+ if (commandStack.size() > 0)
+ {
+ W("Unfinished commands.");
+ }
+
+ revisions.clear();
+ pathInRevision.clear();
+
+ delete mtnDelegate;
+}
+
+//
+// The basic flow is the following:
+//
+// [ workspace parent revision ] [ path in this revision ]
+// \ /
+// \ /
+// \ /
+// |
+// V
+// |---------------> [ get content changed ]
+// | |
+// | V
+// | [ add the marked rev(s) to the list ]
+// | |
+// | V
+// | [ get corresponding path for each marked rev ]
+// | |
+// | V
+// |---- < ---- [ get parents of each marked rev ] -> stop if no parents
+// | |
+// | V
+// |--- < --- [ get corresponding path for each parent ] -> stop if no path
+//
+bool GetContentChanged::readChanges(const QString & path)
+{
+ revisions.clear();
+ pathInRevision.clear();
+
+ // reset the view
+ reset();
+
+ // find a starting point
+ // FIXME: we assume that the given path is part of this revision!
+ startRev = MonotoneDelegate::getBaseWorkspaceRevision(this);
+ I(!startRev.isNull());
+ startPath = path;
+
+ return queryContentChanged(startRev, startPath);
+}
+
+bool GetContentChanged::queryContentChanged(const QString & rev, const QString & path)
+{
+ commandStack.enqueue(ContentChanged);
+
+ QStringList cmd;
+ cmd << "get_content_changed" << rev << path;
+ return mtnDelegate->triggerCommand(cmd);
+}
+
+bool GetContentChanged::queryParents(const QString & rev)
+{
+ commandStack.enqueue(Parents);
+
+ QStringList cmd;
+ cmd << "parents" << rev;
+ return mtnDelegate->triggerCommand(cmd);
+}
+
+bool GetContentChanged::queryCorrespondingPath(const QString & rev, const QString & path, const QString & par)
+{
+ commandStack.enqueue(CorrespondingPath);
+
+ QStringList cmd;
+ cmd << "get_corresponding_path" << rev << path << par;
+ return mtnDelegate->triggerCommand(cmd);
+}
+
+
+void GetContentChanged::parseOutput()
+{
+ // this method is called when we either called get_content_changed,
+ // parents or get_corresponding_path
+ // to decide what to do next, we need to know what was triggered
+ // lately in the queue
+ Command current = commandStack.dequeue();
+
+ if (current == Parents)
+ {
+ if (AutomateCommand::data.isEmpty())
+ {
+ reset();
+ emit rootReached();
+ return;
+ }
+
+ QStringList parents = AutomateCommand::data.split(
+ '\n', QString::SkipEmptyParts
+ );
+
+ foreach (QString par, parents)
+ {
+ // if we checked this revision already, skip it
+ if (pathInRevision.contains(par))
+ {
+ continue;
+ }
+
+ revsForPathStack.enqueue(par);
+ I(queryCorrespondingPath(startRev, startPath, par));
+ }
+ return;
+ }
+
+ if (current == ContentChanged)
+ {
+ // since we're looking for a file in a certain revision
+ // _beforehand_ via get_corresponding_path, we should always
+ // get a stanza out here
+ BasicIOParser parser(AutomateCommand::data);
+ I(parser.parse());
+ StanzaList stanzas = parser.getStanzas();
+ foreach (Stanza st, stanzas)
+ {
+ I(st.size() == 1);
+ QString rev = st.at(0).hash;
+ I(!rev.isNull());
+
+ // if we have this particular revision already recorded,
+ // skip it
+ if (revisions.contains(rev)) continue;
+
+ // append the revision to the ordered list
+ revisions.append(rev);
+
+ // check if we already know the path in that revision
+ if (!pathInRevision.contains(rev))
+ {
+ // add the revision to the stack of revs which need
+ // to get a valid path queried
+ revsForPathStack.enqueue(rev);
+
+ // query for the corresponding path
+ I(queryCorrespondingPath(startRev, startPath, rev));
+ }
+
+ // query for the parents of this revision for the
+ // next round
+ I(queryParents(rev));
+ }
+ return;
+ }
+
+ if (current == CorrespondingPath)
+ {
+ I(revsForPathStack.size() > 0);
+ QString rev = revsForPathStack.dequeue();
+
+ if (AutomateCommand::data.isEmpty())
+ {
+ // check if this is a marked node, if so,
+ // we have a serious problem (node is changed in this rev,
+ // but has no corresponding path)
+ if (revisions.contains(rev))
+ {
+ I(false);
+ }
+
+ // apparently this is another, not interesting (parent) node
+ // note that this can happen
+ reset();
+ emit endOfLineReached();
+ return;
+ }
+
+ BasicIOParser parser(AutomateCommand::data);
+ I(parser.parse());
+ StanzaList stanzas = parser.getStanzas();
+ I(stanzas.size() == 1);
+ Stanza st = stanzas.at(0);
+ I(st.size() == 1);
+ StanzaEntry en = st.at(0);
+ I(en.sym == "file" && en.vals.size() == 1);
+ QString path = en.vals.at(0);
+
+ // we're also inserting a lot of uninteresting (revision, path)
+ // tuples here, i.e. for every parent revision of a marked node,
+ // however the only list that counts is revisions
+ pathInRevision.insert(rev, path);
+
+ // try to get the next marked node
+ I(queryContentChanged(rev, path));
+ return;
+ }
+
+ I(false);
+}
+
+bool GetContentChanged::handleError(int retCode)
+{
+ if (retCode == 2)
+ {
+ I(commandStack.size() > 0);
+
+ // FIXME: this is a big hack, since we assume that the only error 2
+ // we can get here for this command basically reads like
+ // file "foo" doesn't exists in revision "bar"
+ // but yeah, localized string parsing is stupid as well
+ if (commandStack.head() == CorrespondingPath)
+ {
+ AutomateCommand::data.clear();
+ parseOutput();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int GetContentChanged::columnCount(const QModelIndex & parent) const
+{
+ return 2;
+}
+
+QVariant GetContentChanged::data(const QModelIndex & index, int role) const
+{
+ if (!index.isValid())
+ {
+ return QVariant();
+ }
+
+ int col = index.column();
+
+ if (role == Qt::FontRole && col == 0)
+ {
+ QFont font;
+ font.setStyleHint(QFont::Courier);
+ font.setFamily("Courier");
+ return QVariant(font);
+ }
+
+ if (role == Qt::DisplayRole)
+ {
+ int row = index.row();
+ if (row >= revisions.size()) return QVariant();
+ QString rev = revisions.at(row);
+ I(pathInRevision.contains(rev));
+
+ switch (col)
+ {
+ case 0: return QVariant(rev);
+ case 1: return QVariant(pathInRevision.value(rev));
+ }
+ return QVariant();
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags GetContentChanged::flags(const QModelIndex & index) const
+{
+ if (index.isValid())
+ {
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ }
+ return 0;
+}
+
+QVariant GetContentChanged::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case 0: return QVariant(tr("Revision ID"));
+ case 1: return QVariant(tr("Path in this revision"));
+ }
+ }
+ return QVariant();
+}
+
+int GetContentChanged::rowCount(const QModelIndex & parent) const
+{
+ return revisions.size();
+}
+
+QModelIndex GetContentChanged::index(int row, int column, const QModelIndex & parent) const
+{
+ if (!hasIndex(row, column, parent))
+ {
+ return QModelIndex();
+ }
+
+ return createIndex(row, column, 0);
+}
+
+QModelIndex GetContentChanged::parent(const QModelIndex & index) const
+{
+ return QModelIndex();
+}
+
============================================================
--- src/model/GetContentChanged.h 2caf1a98c245f56c89c7036aaf0814338b7327f7
+++ src/model/GetContentChanged.h 2caf1a98c245f56c89c7036aaf0814338b7327f7
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef GETCONTENTCHANGED_H
+#define GETCONTENTCHANGED_H
+
+#include "AutomateCommand.h"
+#include "MonotoneDelegate.h"
+
+#include
+#include
+#include
+
+typedef QList RevisionList;
+
+class GetContentChanged : public QAbstractItemModel, public AutomateCommand
+{
+ Q_OBJECT
+public:
+ GetContentChanged(QObject*);
+ virtual ~GetContentChanged();
+
+ // needed Qt Model methods
+ QVariant data(const QModelIndex&, int) const;
+ Qt::ItemFlags flags(const QModelIndex&) const;
+ QVariant headerData(int, Qt::Orientation, int) const;
+ QModelIndex index(int, int, const QModelIndex&) const;
+ QModelIndex parent(const QModelIndex&) const;
+ int rowCount(const QModelIndex&) const;
+ int columnCount(const QModelIndex&) const;
+
+public slots:
+ bool readChanges(const QString &);
+
+signals:
+ // is emitted each time the algorithm reaches the end of a
+ // particular development line for a specific file
+ void endOfLineReached();
+ // is emitted if the root revision has been reached,
+ // which has per se no parent revisions
+ void rootReached();
+
+private:
+ bool queryContentChanged(const QString &, const QString &);
+ bool queryParents(const QString &);
+ bool queryCorrespondingPath(const QString &, const QString &, const QString &);
+
+ void parseOutput();
+ bool handleError(int);
+
+ RevisionList revisions;
+ MonotoneDelegate * mtnDelegate;
+
+ enum Command { ContentChanged, CorrespondingPath, Parents };
+
+ QQueue commandStack;
+ QQueue revsForPathStack;
+
+ QMap pathInRevision;
+
+ QString startRev;
+ QString startPath;
+};
+
+#endif
+
============================================================
--- src/util/BasicIOWriter.cpp 7965db7212e1d26f78c7923a3c8add0fd8dd46b7
+++ src/util/BasicIOWriter.cpp 7965db7212e1d26f78c7923a3c8add0fd8dd46b7
@@ -0,0 +1,80 @@
+/***************************************************************************
+* Copyright (C) 2006 by Thomas Keller *
+* address@hidden *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+***************************************************************************/
+
+#include "BasicIOWriter.h"
+
+BasicIOWriter::BasicIOWriter(const StanzaList & st) : stanzas(st) {}
+
+BasicIOWriter::~BasicIOWriter() {}
+
+QString BasicIOWriter::write()
+{
+ QString out;
+ foreach (Stanza st, stanzas)
+ {
+ out.append(writeStanza(st));
+ }
+ return out;
+}
+
+QString BasicIOWriter::writeStanza(const Stanza & stanza)
+{
+ QString out;
+ foreach (StanzaEntry en, stanza)
+ {
+ // ensure that not both, a hash and a value list, are given
+ I(!(!en.hash.isNull() && en.vals.size() > 0));
+
+ if (!en.hash.isNull())
+ {
+ out.append(writeHashLine(en));
+ }
+ else
+ {
+ out.append(writeValueLine(en));
+ }
+ }
+ out.append("\n");
+ return out;
+}
+
+QString BasicIOWriter::writeHashLine(const StanzaEntry & entry)
+{
+ return QString("%1 [%2]\n").arg(entry.sym).arg(entry.hash);
+}
+
+QString BasicIOWriter::writeValueLine(const StanzaEntry & entry)
+{
+ QString vals;
+ foreach (QString val, entry.vals)
+ {
+ vals.append(QString(" \"%1\"").arg(escape(val)));
+ }
+ return QString("%1%2\n").arg(entry.sym).arg(vals);
+}
+
+QString BasicIOWriter::escape(const QString & in)
+{
+ QString out(in);
+ out.replace("\\", "\\\\");
+ out.replace("\"", "\\\"");
+ return out;
+}
+
============================================================
--- src/util/BasicIOWriter.h 052ca233a44fa5c872b699b5eee4fd4cc6187f1b
+++ src/util/BasicIOWriter.h 052ca233a44fa5c872b699b5eee4fd4cc6187f1b
@@ -0,0 +1,45 @@
+/***************************************************************************
+* Copyright (C) 2007 by Thomas Keller *
+* address@hidden *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+***************************************************************************/
+
+#ifndef BASICIO_WRITER_H
+#define BASICIO_WRITER_H
+
+#include "vocab.h"
+
+class BasicIOWriter
+{
+public:
+ BasicIOWriter(const StanzaList &);
+ ~BasicIOWriter();
+
+ QString write();
+
+
+private:
+ QString writeStanza(const Stanza &);
+ QString writeHashLine(const StanzaEntry &);
+ QString writeValueLine(const StanzaEntry &);
+ QString escape(const QString &);
+
+ StanzaList stanzas;
+};
+
+#endif
+
============================================================
--- src/view/MacStartMenu.cpp 4cf7133a0572dc78749e556696e812b213d1d0c5
+++ src/view/MacStartMenu.cpp 4cf7133a0572dc78749e556696e812b213d1d0c5
@@ -0,0 +1,205 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "MacStartMenu.h"
+#include "About.h"
+#include "Preferences.h"
+#include "CocoaUtil.h"
+#include "Settings.h"
+
+#include
+#include
+
+//
+// FIXME: This is pretty much a plain copy of ui_main_window.h and
+// MainWindow.cpp - if we keep this, then maybe this should be generalized
+// i.e. into a "CommonMenuBar" class or something alike
+//
+MacStartMenu::MacStartMenu() : QMenuBar(0)
+{
+ actionOpen_Workspace = new QAction(this);
+ actionOpen_Workspace->setObjectName(QString::fromUtf8("actionOpen_Workspace"));
+ actionNo_recent_workspaces_found = new QAction(this);
+ actionNo_recent_workspaces_found->setObjectName(QString::fromUtf8("actionNo_recent_workspaces_found"));
+ actionPreferences = new QAction(this);
+ actionPreferences->setObjectName(QString::fromUtf8("actionPreferences"));
+ actionPreferences->setMenuRole(QAction::PreferencesRole);
+ actionAbout_Qt = new QAction(this);
+ actionAbout_Qt->setObjectName(QString::fromUtf8("actionAbout_Qt"));
+ actionAbout_Qt->setMenuRole(QAction::AboutQtRole);
+ actionAbout_guitone = new QAction(this);
+ actionAbout_guitone->setObjectName(QString::fromUtf8("actionAbout_guitone"));
+ actionAbout_guitone->setMenuRole(QAction::AboutRole);
+ actionOpen_Database = new QAction(this);
+ actionOpen_Database->setObjectName(QString::fromUtf8("actionOpen_Database"));
+ actionNo_previous_databases_available = new QAction(this);
+ actionNo_previous_databases_available->setObjectName(QString::fromUtf8("actionNo_previous_databases_available"));
+ actionCheck_for_updates = new QAction(this);
+ actionCheck_for_updates->setObjectName(QString::fromUtf8("actionCheck_for_updates"));
+
+ menuFile = new QMenu(this);
+ menuFile->setObjectName(QString::fromUtf8("menuFile"));
+ menuRecent_Databases = new QMenu(menuFile);
+ menuRecent_Databases->setObjectName(QString::fromUtf8("menuRecent_Databases"));
+ menuRecent_Workspaces = new QMenu(menuFile);
+ menuRecent_Workspaces->setObjectName(QString::fromUtf8("menuRecent_Workspaces"));
+
+ menuFile->addAction(actionOpen_Workspace);
+ menuFile->addAction(menuRecent_Workspaces->menuAction());
+ menuFile->addSeparator();
+ menuFile->addAction(actionOpen_Database);
+ menuFile->addAction(menuRecent_Databases->menuAction());
+ menuFile->addSeparator();
+ menuFile->addAction(actionPreferences);
+ menuFile->addAction(actionCheck_for_updates);
+ menuFile->addAction(actionAbout_Qt);
+ menuFile->addAction(actionAbout_guitone);
+ menuRecent_Databases->addAction(actionNo_previous_databases_available);
+ menuRecent_Workspaces->addAction(actionNo_recent_workspaces_found);
+
+ // add the created menu to this menu bar
+ addAction(menuFile->menuAction());
+
+ // translate everything
+ actionOpen_Workspace->setText(tr("Open Workspace"));
+ actionOpen_Workspace->setShortcut(tr("Ctrl+O"));
+ actionNo_recent_workspaces_found->setText(tr("No previous workspaces available."));
+ actionNo_recent_workspaces_found->setIconText(tr("No previous workspaces available."));
+ actionNo_recent_workspaces_found->setToolTip(tr("No previous workspaces available."));
+ actionPreferences->setText(tr("Preferences..."));
+ actionPreferences->setShortcut(tr("Ctrl+P"));
+ actionAbout_Qt->setText(tr("About Qt"));
+ actionAbout_guitone->setText(tr("About guitone"));
+ actionOpen_Database->setText(tr("Open Database"));
+ actionOpen_Database->setShortcut(tr("Ctrl+Shift+O"));
+ actionNo_previous_databases_available->setText(tr("No previous databases available."));
+ actionCheck_for_updates->setText(tr("Check for updates"));
+ menuFile->setTitle(tr("File"));
+ menuRecent_Databases->setTitle(tr("Recent Databases"));
+ menuRecent_Workspaces->setTitle(tr("Recent Workspaces"));
+
+ // fill in any recently loaded databases and workspaces
+ QStringList previousDb = Settings::getItemList("RecentDatabaseList");
+ int elemCount = previousDb.size();
+ if (elemCount > 0)
+ {
+ menuRecent_Databases->clear();
+
+ QAction *act;
+ for (int i = 0; i < elemCount; ++i)
+ {
+ act = menuRecent_Databases->addAction(
+ tr("&%1 %2").arg(i + 1).arg(previousDb[i]),
+ this,
+ SLOT(openRecentDatabase())
+ );
+ act->setData(previousDb[i]);
+ }
+ }
+
+ QStringList previousWs = Settings::getItemList("RecentWorkspaceList");
+ elemCount = previousWs.size();
+ if (elemCount > 0)
+ {
+ menuRecent_Workspaces->clear();
+
+ QAction *act;
+ for (int i = 0; i < elemCount; ++i)
+ {
+ act = menuRecent_Workspaces->addAction(
+ tr("&%1 %2").arg(i + 1).arg(previousWs[i]),
+ this,
+ SLOT(openRecentWorkspace())
+ );
+ act->setData(previousWs[i]);
+ }
+ }
+
+ // auto-connect all signals/slots
+ QMetaObject::connectSlotsByName(this);
+}
+
+MacStartMenu::~MacStartMenu() {}
+
+void MacStartMenu::on_actionPreferences_triggered()
+{
+ Preferences dialog(this);
+ dialog.exec();
+}
+
+void MacStartMenu::on_actionAbout_guitone_triggered()
+{
+ About dialog(this);
+ dialog.exec();
+}
+
+void MacStartMenu::on_actionAbout_Qt_triggered()
+{
+ qApp->aboutQt();
+}
+
+void MacStartMenu::on_actionCheck_for_updates_triggered()
+{
+ CocoaUtil::checkForUpdates();
+}
+
+void MacStartMenu::on_actionOpen_Workspace_triggered()
+{
+ QString fn = QFileDialog::getExistingDirectory(0, tr("Select your workspace..."));
+
+ if (!fn.isEmpty())
+ {
+ emit loadWorkspace(fn);
+ }
+}
+
+void MacStartMenu::on_actionOpen_Database_triggered()
+{
+ QString fn = QFileDialog::getOpenFileName(
+ 0,
+ tr("Select your database..."),
+ QString(),
+ tr("monotone Databases (*.mtn *.db)")
+ );
+
+ if (!fn.isEmpty())
+ {
+ emit loadDatabase(fn);
+ }
+}
+
+void MacStartMenu::openRecentWorkspace()
+{
+ QAction *action = qobject_cast(sender());
+ if (action)
+ {
+ emit loadWorkspace(action->data().toString());
+ }
+}
+
+void MacStartMenu::openRecentDatabase()
+{
+ QAction *action = qobject_cast(sender());
+ if (action)
+ {
+ emit loadDatabase(action->data().toString());
+ }
+}
+
============================================================
--- src/view/MacStartMenu.h d98d19bcc23c5c867e4c9a8ecc9c3d850bfa9b8c
+++ src/view/MacStartMenu.h d98d19bcc23c5c867e4c9a8ecc9c3d850bfa9b8c
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef MAC_START_MENU_H
+#define MAC_START_MENU_H
+
+#include
+
+class MacStartMenu : public QMenuBar
+{
+ Q_OBJECT
+
+public:
+ MacStartMenu();
+ ~MacStartMenu();
+
+signals:
+ void loadWorkspace(const QString &);
+ void loadDatabase(const QString &);
+
+public:
+ QAction * actionOpen_Workspace;
+ QAction * actionNo_recent_workspaces_found;
+ QAction * actionPreferences;
+ QAction * actionAbout_Qt;
+ QAction * actionAbout_guitone;
+ QAction * actionOpen_Database;
+ QAction * actionNo_previous_databases_available;
+ QMenu * menuFile;
+ QMenu * menuRecent_Databases;
+ QMenu * menuRecent_Workspaces;
+ QAction * actionCheck_for_updates;
+
+private slots:
+ void on_actionOpen_Workspace_triggered();
+ void on_actionOpen_Database_triggered();
+ void on_actionPreferences_triggered();
+ void on_actionAbout_guitone_triggered();
+ void on_actionAbout_Qt_triggered();
+ void on_actionCheck_for_updates_triggered();
+ void openRecentDatabase();
+ void openRecentWorkspace();
+};
+
+#endif
+
============================================================
--- src/view/dialogs/AddEditAttribute.cpp 1f9555c4103d227d66191031fea7b1e9d41462e7
+++ src/view/dialogs/AddEditAttribute.cpp 1f9555c4103d227d66191031fea7b1e9d41462e7
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "AddEditAttribute.h"
+
+AddEditAttribute::AddEditAttribute(
+ QWidget * parent, const QString & key, const QString & value
+) : Dialog(parent)
+{
+ setupUi(this);
+ Dialog::init();
+
+ // these are added here and not in designer to prevent their
+ // automatic localization through uic
+ attrKey->insertItem(0, "mtn:execute");
+ attrKey->insertItem(1, "mtn:manual_merge");
+
+ int keyPos = attrKey->findText(key);
+ if (keyPos == -1)
+ {
+ attrKey->insertItem(0, key);
+ keyPos = 0;
+ }
+
+ attrKey->setCurrentIndex(keyPos);
+ attrValue->setText(value);
+}
+
+AddEditAttribute::~AddEditAttribute() {}
+
============================================================
--- src/view/dialogs/AddEditAttribute.h aea71c7ba22359058eaccfc8be8d112dd79bf231
+++ src/view/dialogs/AddEditAttribute.h aea71c7ba22359058eaccfc8be8d112dd79bf231
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef ADDEDITATTRIBUTE_H
+#define ADDEDITATTRIBUTE_H
+
+#include "ui_add_edit_attribute.h"
+#include "Dialog.h"
+#include "vocab.h"
+
+class AddEditAttribute : public Dialog, private Ui::AddEditAttributeDialog
+{
+ Q_OBJECT
+
+public:
+ AddEditAttribute(QWidget *,
+ const QString & key = QString(), const QString & value = QString());
+ ~AddEditAttribute();
+ QString getKey() const { return attrKey->currentText(); }
+ QString getValue() const { return attrValue->text(); }
+};
+
+#endif
+
============================================================
--- src/view/dialogs/FileHistory.cpp 86e856616418ed7875cbd92515116b406947abc4
+++ src/view/dialogs/FileHistory.cpp 86e856616418ed7875cbd92515116b406947abc4
@@ -0,0 +1,139 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "FileHistory.h"
+#include "FileDiff.h"
+
+FileHistory::FileHistory(QWidget * parent, const QString & fileName)
+ : Dialog(parent)
+{
+ setupUi(this);
+ Dialog::init();
+
+ // OSX sheet-alike dialog
+ setWindowFlags(Qt::Sheet);
+
+ splitter->init();
+
+ changeModel = new GetContentChanged(this);
+ certsModel = new Certs(this);
+
+ // assign the models to the views
+ revisionFileList->setModel(changeModel);
+ certList->setModel(certsModel);
+
+ // display the certs of a selected revision on click
+ connect(
+ revisionFileList, SIGNAL(clicked(const QModelIndex &)),
+ this, SLOT(readCerts(const QModelIndex &))
+ );
+
+ connect(
+ selectFirst, SIGNAL(clicked()),
+ this, SLOT(setFirstRevision())
+ );
+
+ connect(
+ selectSecond, SIGNAL(clicked()),
+ this, SLOT(setSecondRevision())
+ );
+
+ connect(
+ showDiff, SIGNAL(clicked()),
+ this, SLOT(showDiffDialog())
+ );
+
+ connect(
+ revisionFileList, SIGNAL(clicked(const QModelIndex &)),
+ this, SLOT(revisionFileListClicked(const QModelIndex &))
+ );
+
+ QString title = windowTitle();
+ setWindowTitle(title.arg(fileName));
+
+ // read the changes
+ I(changeModel->readChanges(fileName));
+}
+
+FileHistory::~FileHistory()
+{
+ delete changeModel;
+ delete certsModel;
+}
+
+void FileHistory::readCerts(const QModelIndex & index)
+{
+ if (!index.isValid()) return;
+
+ QModelIndex revIdx = changeModel->index(index.row(), 0, QModelIndex());
+ QString rev(revIdx.data().toString());
+
+ if (!certsModel->readCerts(rev))
+ {
+ C(QString("Couldn't read certs for %1").arg(rev));
+ }
+}
+
+void FileHistory::setFirstRevision()
+{
+ QModelIndex revIdx = changeModel->index(curRow, 0, QModelIndex());
+ QModelIndex fileIdx = changeModel->index(curRow, 1, QModelIndex());
+ I(revIdx.isValid() && fileIdx.isValid());
+
+ firstRevision = revIdx.data().toString();
+ fileName = fileIdx.data().toString();
+
+ if (secondRevision.size() > 0) showDiff->setEnabled(true);
+
+ selectFirst->setText(tr("First: %1...").arg(firstRevision.left(12)));
+}
+
+void FileHistory::setSecondRevision()
+{
+ QModelIndex revIdx = changeModel->index(curRow, 0, QModelIndex());
+ I(revIdx.isValid());
+
+ secondRevision = revIdx.data().toString();
+
+ if (firstRevision.size() > 0) showDiff->setEnabled(true);
+
+ selectSecond->setText(tr("Second: %1...").arg(secondRevision.left(12)));
+}
+
+void FileHistory::revisionFileListClicked(const QModelIndex & index)
+{
+ if (!index.isValid()) return;
+
+ curRow = index.row();
+
+ // make sure both button are enabled
+ selectFirst->setEnabled(true);
+ selectSecond->setEnabled(true);
+}
+
+void FileHistory::showDiffDialog()
+{
+ I(firstRevision.size() > 0 && secondRevision.size() > 0);
+
+ FileDiff dlg(this);
+ dlg.init(fileName, firstRevision, secondRevision);
+ dlg.exec();
+}
+
============================================================
--- src/view/dialogs/FileHistory.h ed76ac56e2b114a67196d38bae14c01b8b9fda63
+++ src/view/dialogs/FileHistory.h ed76ac56e2b114a67196d38bae14c01b8b9fda63
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef FILE_HISTORY_H
+#define FILE_HISTORY_H
+
+#include "Dialog.h"
+#include "ui_file_history.h"
+#include "Certs.h"
+#include "GetContentChanged.h"
+
+class FileHistory : public Dialog, private Ui::FileHistoryDialog
+{
+ Q_OBJECT
+
+public:
+ FileHistory(QWidget *, const QString &);
+ ~FileHistory();
+
+private:
+ GetContentChanged * changeModel;
+ Certs * certsModel;
+
+ int curRow;
+ QString firstRevision;
+ QString secondRevision;
+ QString fileName;
+
+private slots:
+ void readCerts(const QModelIndex &);
+ void setFirstRevision();
+ void setSecondRevision();
+ void revisionFileListClicked(const QModelIndex &);
+ void showDiffDialog();
+};
+
+#endif
============================================================
--- src/view/dialogs/UnaccountedRenames.cpp 938d5eac2b9884b18445e4eacde26c5d31dea0e8
+++ src/view/dialogs/UnaccountedRenames.cpp 938d5eac2b9884b18445e4eacde26c5d31dea0e8
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "UnaccountedRenames.h"
+#include "IconProvider.h"
+
+#include
+#include
+
+UnaccountedRenames::UnaccountedRenames(QWidget * parent, const QMap & r):
+ Dialog(parent), renames(r)
+{
+ setupUi(this);
+ Dialog::init();
+
+ // OSX sheet-alike dialog
+ setWindowFlags(Qt::Sheet);
+
+ connect(
+ renameTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
+ this, SLOT(itemClicked(QTreeWidgetItem *, int))
+ );
+
+ renameTree->setHeaderLabels(QStringList()
+ << tr("Missing paths and possible rename targets")
+ << tr("File ID (if applicable)")
+ );
+
+ renameTree->header()->resizeSection(0, 300);
+
+ QFont boldFont;
+ boldFont.setBold(true);
+ IconProvider * iconProvider = IconProvider::singleton();
+
+ QMapIterator i(renames);
+ while (i.hasNext())
+ {
+ i.next();
+ FileEntry sen = i.key();
+ FileEntryList list = i.value();
+ QTreeWidgetItem * source = new QTreeWidgetItem(renameTree);
+ source->setFlags(Qt::ItemIsEnabled);
+ source->setText(0, QString("%1 (%2)").arg(sen.path).arg(list.size()));
+ source->setData(0, Qt::UserRole, QVariant(sen.path));
+ source->setFont(0, boldFont);
+ source->setIcon(0, sen.is_dir ?
+ iconProvider->getPlainFolderIcon() :
+ iconProvider->getPlainFileIcon());
+ source->setText(1, sen.fileid.isEmpty() ? tr("n/a") : sen.fileid);
+ source->setFont(1, boldFont);
+
+ foreach (FileEntry en, list)
+ {
+ QTreeWidgetItem * target = new QTreeWidgetItem(source);
+ target->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
+ target->setCheckState(0, Qt::Unchecked);
+ target->setText(0, en.path);
+ target->setToolTip(0, en.fileid.isEmpty() || sen.fileid != en.fileid ?
+ tr("matched by name") :
+ tr("matched by content"));
+
+ target->setText(1, en.fileid.isEmpty() ? tr("n/a") : en.fileid);
+ }
+ }
+}
+
+UnaccountedRenames::~UnaccountedRenames()
+{
+}
+
+void UnaccountedRenames::itemClicked(QTreeWidgetItem * item, int column)
+{
+ if (item == 0 || column == -1) return;
+ QTreeWidgetItem * parent = item->parent();
+ // top level item clicked
+ if (!parent) return;
+
+ Qt::CheckState state = item->checkState(0);
+
+ for (int i=0, j=parent->childCount(); ichild(i)->setCheckState(0, Qt::Unchecked);
+ }
+
+ item->setCheckState(0, state);
+}
+
============================================================
--- src/view/dialogs/UnaccountedRenames.h 0691bf54c8ff2d8606e88eb855613a4977ad8b9d
+++ src/view/dialogs/UnaccountedRenames.h 0691bf54c8ff2d8606e88eb855613a4977ad8b9d
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef UNACCOUNTED_RENAMES_H
+#define UNACCOUNTED_RENAMES_H
+
+#include "Dialog.h"
+#include "vocab.h"
+#include "ui_unaccounted_renames.h"
+
+class UnaccountedRenames : public Dialog, private Ui::UnaccountedRenamesDialog
+{
+ Q_OBJECT
+
+public:
+ UnaccountedRenames(QWidget *, const QMap &);
+ ~UnaccountedRenames();
+
+private:
+ QMap renames;
+
+private slots:
+ void itemClicked(QTreeWidgetItem *, int);
+};
+
+#endif
============================================================
--- tests/MonotoneTest.h 73986287a1873b2e72dbc62743a7cdd9ab317fb7
+++ tests/MonotoneTest.h 73986287a1873b2e72dbc62743a7cdd9ab317fb7
@@ -0,0 +1,69 @@
+
+#include
+#include
+#include
+#include "Monotone.h"
+
+class MonotoneTest: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QVERIFY(Monotone::checkProgramVersion("mtn"));
+ QDir tmpDir = QDir::temp();
+ QVERIFY(tmpDir.mkdir("guitone-test"));
+ QVERIFY(tmpDir.cd("guitone-test"));
+
+ QString out;
+ QVERIFY(runmtn(QStringList() << "init", out));
+ QVERIFY(runmtn(QStringList() << "setup" << "-b foo" << ".", out));
+
+ mtn = new Monotone(0);
+ QVERIFY(mtn->loadWorkspace(tmpDir.absolutePath()));
+ }
+
+ void getOptionTest()
+ {
+ QVERIFY(true);
+ }
+
+ void cleanupTestCase()
+ {
+ QDir tmpDir = QDir::temp();
+ removePath(tmpDir.filePath("guitone-test"));
+ delete mtn;
+ }
+
+private:
+ bool runmtn(const QStringList & a, QString & output)
+ {
+ QDir tmpDir = QDir::temp();
+ Q_ASSERT(tmpDir.cd("guitone-test"));
+ QStringList args;
+ args << QString("-d %1").arg(tmpDir.filePath("test.mtn")) << a;
+ return Monotone::runCommand("mtn", args, output);
+ }
+
+ void removePath(const QString & path)
+ {
+ QFileInfo fileInfo(path);
+ if (fileInfo.isDir())
+ {
+ QDir dir(path);
+ QStringList entries = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
+ foreach (QString entry, entries)
+ {
+ removePath(dir.filePath(entry));
+ }
+ dir.rmdir(path);
+ return;
+ }
+ QFile::remove(path);
+ return;
+ }
+
+ Monotone * mtn;
+};
+
============================================================
--- tests/StdioParserTest.h 3974e240f162fc6cebbd6df97c3f36ed0440817d
+++ tests/StdioParserTest.h 3974e240f162fc6cebbd6df97c3f36ed0440817d
@@ -0,0 +1,58 @@
+
+#include
+#include
+#include "StdioParser.h"
+
+/*
+ StdioParser(const QByteArray &);
+
+ bool parse();
+ inline int getCommandNumber() const { return commandNumber; }
+ inline int getErrorCode() const { return errorCode; }
+ inline char getChunkType() const { return chunkType; }
+ inline int getChunkSize() const { return chunkSize; }
+ inline QByteArray getPayload() const { return payload; }
+
+*/
+
+class StdioParserTest: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void emptyPayloadTest()
+ {
+ QByteArray in("2:0:l:0:");
+ StdioParser parser(in);
+ QVERIFY(parser.parse());
+ QVERIFY(parser.getCommandNumber() == 2);
+ QVERIFY(parser.getErrorCode() == 0);
+ QVERIFY(parser.getChunkType() == 'l');
+ QVERIFY(parser.getChunkSize() == 0);
+ QByteArray out = parser.getPayload();
+ QVERIFY(out.isNull());
+ }
+
+ void missingPayloadTest()
+ {
+ QByteArray in("0:0:m:20:ten bytes!");
+ StdioParser parser(in);
+ QVERIFY(!parser.parse());
+ }
+
+ void morePayloadTest()
+ {
+ QByteArray in("0:0:m:10:ten bytes!BUT HERE IS MORE");
+ StdioParser parser(in);
+ QVERIFY(parser.parse());
+ QVERIFY(parser.getCommandNumber() == 0);
+ QVERIFY(parser.getErrorCode() == 0);
+ QVERIFY(parser.getChunkType() == 'm');
+ QVERIFY(parser.getChunkSize() == 10);
+ QByteArray out = parser.getPayload();
+ QVERIFY(out == "ten bytes!");
+ QByteArray left = parser.getLeftBytes();
+ QVERIFY(left == "BUT HERE IS MORE");
+ }
+};
+
============================================================
--- NEWS c5dfe130fb53d6553fc530e7e94140ffd99fe2c3
+++ NEWS 949d2a577317be4215e33daa7157824f994577d8
@@ -1,3 +1,13 @@
+????-??-??
+ - new: possibility to display the history of a single file in chronological
+ order (right-click on any tracked file and click "History")
+ - new: find unaccounted renames in your workspace (i.e. finds those renames
+ which only happened in the filesystem). Matches files by content and
+ directories by name (actual rename is not yet implemented due to a missing
+ version of mtn automate rename)
+ - new: add, drop and edit file attributes
+ - bugfix: the workspace is now properly updated after a commit
+
2007-08-20 (0.6.4)
- compatibility with monotone 0.36 (and any upcoming monotone with an
interface_version above 5.0 and below 5.99)
============================================================
--- guitone.pro 33545cda1a516aede1c2b494f1432818727048c9
+++ guitone.pro 793a62ed84e3c859c3963721260c4e6fa379f1fa
@@ -35,6 +35,9 @@ HEADERS = src/view/MainWindow.h \
src/view/dialogs/ChangesetBrowser.h \
src/view/dialogs/RevisionManifest.h \
src/view/dialogs/CommitRevision.h \
+ src/view/dialogs/FileHistory.h \
+ src/view/dialogs/UnaccountedRenames.h \
+ src/view/dialogs/AddEditAttribute.h \
src/monotone/Monotone.h \
src/monotone/MonotoneDelegate.h \
src/monotone/FileExporter.h \
@@ -43,7 +46,7 @@ HEADERS = src/view/MainWindow.h \
src/model/Inventory.h \
src/model/InventoryItem.h \
src/model/InventoryProxyModel.h \
- src/model/Attributes.h \
+ src/model/GetAttributes.h \
src/model/Select.h \
src/model/Certs.h \
src/model/ContentDiff.h \
@@ -59,9 +62,11 @@ HEADERS = src/view/MainWindow.h \
src/model/Ancestors.h \
src/model/GetBranchLog.h \
src/model/GetRevision.h \
+ src/model/GetContentChanged.h \
src/util/IconProvider.h \
src/util/AbstractParser.h \
src/util/BasicIOParser.h \
+ src/util/BasicIOWriter.h \
src/util/Settings.h \
src/util/DiffParser.h \
src/util/SignalWaiter.h \
@@ -92,6 +97,9 @@ SOURCES += src/view/MainWindow.cpp \
src/view/dialogs/ChangesetBrowser.cpp \
src/view/dialogs/RevisionManifest.cpp \
src/view/dialogs/CommitRevision.cpp \
+ src/view/dialogs/FileHistory.cpp \
+ src/view/dialogs/UnaccountedRenames.cpp \
+ src/view/dialogs/AddEditAttribute.cpp \
src/monotone/Monotone.cpp \
src/monotone/MonotoneDelegate.cpp \
src/monotone/FileExporter.cpp \
@@ -100,7 +108,7 @@ SOURCES += src/view/MainWindow.cpp \
src/model/Inventory.cpp \
src/model/InventoryItem.cpp \
src/model/InventoryProxyModel.cpp \
- src/model/Attributes.cpp \
+ src/model/GetAttributes.cpp \
src/model/Select.cpp \
src/model/Certs.cpp \
src/model/ContentDiff.cpp \
@@ -116,9 +124,11 @@ SOURCES += src/view/MainWindow.cpp \
src/model/Ancestors.cpp \
src/model/GetBranchLog.cpp \
src/model/GetRevision.cpp \
+ src/model/GetContentChanged.cpp \
src/util/IconProvider.cpp \
src/util/AbstractParser.cpp \
src/util/BasicIOParser.cpp \
+ src/util/BasicIOWriter.cpp \
src/util/Settings.cpp \
src/util/DiffParser.cpp \
src/util/SignalWaiter.cpp \
@@ -142,7 +152,11 @@ FORMS += res/forms/select_revision.ui
res/forms/changeset_browser.ui \
res/forms/manifest.ui \
res/forms/commit_revision.ui \
- res/forms/application_update.ui
+ res/forms/application_update.ui \
+ res/forms/file_history.ui \
+ res/forms/unaccounted_renames.ui \
+ res/forms/add_edit_attribute.ui
+
UI_DIR = tmp
OBJECTS_DIR = tmp
@@ -184,9 +198,11 @@ macx {
#
macx {
- # add sources for Sparkle
- HEADERS += src/util/CocoaUtil.h
- SOURCES += src/util/CocoaUtil.mm
+ # add specific Mac sources
+ HEADERS += src/util/CocoaUtil.h \
+ src/view/MacStartMenu.h
+ SOURCES += src/util/CocoaUtil.mm \
+ src/view/MacStartMenu.cpp
# add the Sparkle and the Carbon framework
QMAKE_LFLAGS += -framework Sparkle -framework Carbon
============================================================
--- notes/IDEAS 0f7d32bac1e3d7405a733c7548508969064efe21
+++ notes/IDEAS bdf47a7c58a2a0c0f183ea9ef59290a46d7dbc3c
@@ -1,6 +1,7 @@ mid- to longterm, for a list of definite
This file lists some ideas what could be improved / implemented in guitone
mid- to longterm, for a list of definite changes see TODO:
+* markdown-alike functionality for diff view
* make changed items view configurable (currently they're just bold)
* integrate support to make guitone a kpart module under KDE
* add functionality which is currently missing from monotone (update, ...)
============================================================
--- notes/RELEASE_CHECKLIST 3dd539c8e9b77fb957571a2c4dcb641ce25a44f0
+++ notes/RELEASE_CHECKLIST 78816f3f795e8651b5ac5fd7b3870b735c7d3bd2
@@ -24,7 +24,7 @@ 5) ZIP release (binary, win32, mingw):
$ mv bin/guitone.exe bin/guitone-MAJOR.MINOR.BUGFIX
$ cp COPYING NEWS README bin/guitone-MAJOR.MINOR.BUGFIX
$ cp $QTDIR/bin/mingwm10.dll $QTDIR/bin/QtCore4.dll $QTDIR/bin/QtGui4.dll \
- $QTDIR/bin/QtXml4.dll $QTDIR/bin/QtNetwork4.dll \
+ $QTDIR/bin/QtXml4.dll $QTDIR/bin/QtNetwork4.dll $QTDIR/bin/QtSvg4.dll \
bin/guitone-MAJOR.MINOR.BUGFIX
Finally _test_ the binary and zip the folder.
============================================================
--- notes/TODO ea5eaa751fa11645c142bca08911a145ef87d4a5
+++ notes/TODO 336f24bc9bc9f3914981275846cbd7b05ff7e19e
@@ -1,2 +1,11 @@ Current TODO, for "long-term" stuff see
Current TODO, for "long-term" stuff see IDEAS.
+* more testing with commit functionality (seems to make problems
+ especially if binary files are added / edited)
+* better version detection from rss
+* fix segfault on OSX when doing Cmd+Q (seems as not everything
+ gets cleaned afterwards nicely)
+* fix non-localization for MacStartMenu
+* fix crash in ChangesetBrowser on all platforms when clicking on
+ a branch name (Monotone-wrapper related)
+
============================================================
--- res/forms/application_update.ui cfb6889da734fdfa20f68943c73d93983951ce5a
+++ res/forms/application_update.ui 7d2b3993e719ba0fea62c815d0cee25d6fd48562
@@ -42,6 +42,13 @@
6
-
+
+
+ Visit website
+
+
+
+ -
Qt::Horizontal
@@ -55,17 +62,13 @@
-
-
-
- Visit website
-
-
-
- -
Close
+
+ true
+
============================================================
--- res/forms/changeset_browser.ui b305fba833874efa7b6b53773d8cea303c0366d0
+++ res/forms/changeset_browser.ui ac59737143d7aac046048608d14ed2dbdefd23b6
@@ -122,6 +122,9 @@
close
+
+ true
+
@@ -134,7 +137,7 @@
Splitter
QSplitter
-
+
TreeView
============================================================
--- res/forms/commit_revision.ui 7743280f847be7c2fd842e55b24e48be78d2b6ab
+++ res/forms/commit_revision.ui 869cc6762e7b033d8186dc527e9d96ab5e77ff49
@@ -249,41 +249,14 @@
-
-
-
- 0
+
+
+ Qt::Horizontal
-
- 6
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Abort
-
-
-
- -
-
-
- Commit
-
-
-
-
+
@@ -305,12 +278,12 @@
setEnabled(bool)
- 89
- 458
+ 100
+ 514
- 238
- 461
+ 456
+ 515
@@ -321,44 +294,44 @@
setEnabled(bool)
- 87
- 425
+ 98
+ 484
- 243
- 427
+ 456
+ 485
- abort
- clicked()
+ buttonBox
+ rejected()
CommitRevision
reject()
- 294
- 502
+ 302
+ 541
- 146
- 487
+ 252
+ 561
- commitRev
- clicked()
+ buttonBox
+ accepted()
CommitRevision
accept()
- 371
- 492
+ 411
+ 543
- 45
- 483
+ 392
+ 560
============================================================
--- res/forms/file_diff.ui 7892257391ce4faa476cd26cb04e424ac91a4e2d
+++ res/forms/file_diff.ui 9999a04c668f9a40b1a9b20f7ecd0812e8dc7cd5
@@ -5,8 +5,8 @@
0
0
- 553
- 436
+ 676
+ 491
@@ -47,6 +47,68 @@
-
+
+
+ Show
+
+
+
+ 9
+
+
+ 6
+
+
-
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ first (%1)
+
+
+
+ -
+
+
+ second (%2)
+
+
+
+ -
+
+
+ both
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+ -
0
@@ -55,47 +117,6 @@
6
-
-
-
- Show Version
-
-
-
- -
-
-
- 0
-
-
- 6
-
-
-
-
-
- Left
-
-
-
- -
-
-
- Right
-
-
-
- -
-
-
- Both
-
-
- true
-
-
-
-
-
- -
Qt::Horizontal
@@ -109,10 +130,27 @@
-
+
+
+ Prev
+
+
+
+ -
+
+
+ Next
+
+
+
+ -
Close
+
+ true
+
@@ -123,15 +161,15 @@
+ DiffStatusView
+ QWidget
+
+
+
DiffView
QTreeView
-
- DiffStatusView
- QWidget
-
-
============================================================
--- res/forms/key_management.ui edec01c656964ce19977a8305c380207fa222bff
+++ res/forms/key_management.ui 4913ed46e0685544cb5e3cf7cdf98b54aacd42ce
@@ -79,6 +79,9 @@
Close
+
+ true
+
============================================================
--- res/forms/main_window.ui 6c5b7e9f7df0fc48722a97cf9e48cbc51ea46e2d
+++ res/forms/main_window.ui 2a502c4c0a34fefa6c210f0f74f6e98a7ad6fe2b
@@ -15,27 +15,33 @@
:/icons/guitone.png
+
+ false
+
-
- 9
-
-
- 6
-
-
- 1
+ 0
-
+
0
-
+
0
+
+ 0
+
+
+ 0
+
+
+ 0
+
-
@@ -61,7 +67,11 @@
QAbstractItemView::ExtendedSelection
-
+
+
+ true
+
+
@@ -69,12 +79,21 @@
-
+
0
-
+
0
+
+ 0
+
+
+ 0
+
+
+ 0
+
-
@@ -143,21 +162,6 @@
-
-
+
+
@@ -450,14 +471,22 @@
Check for updates
+
+
+ Reload workspace
+
+
+ Ctrl+R
+
+
+
+
+ Find unaccounted renames
+
+
- Splitter
- QSplitter
-
-
-
InventoryView
QTreeView
@@ -467,6 +496,12 @@
QTreeView
+
+ Splitter
+ QSplitter
+
+ 1
+
============================================================
--- res/forms/manifest.ui b0411bd9c26399c10dd09a486cc6403b80af3f7c
+++ res/forms/manifest.ui 5b3e49e7bec6ba679c67b10ef1c9c0ee7e6799d2
@@ -77,6 +77,9 @@
Close
+
+ true
+
============================================================
--- res/forms/preferences.ui 4c7a832f0d210371eb68882a1f736ff150affdff
+++ res/forms/preferences.ui 9898dad2631df0a8a5cb6f06fc5055160b7e24d2
@@ -214,17 +214,20 @@
-
-
+
- OK
+ Cancel
-
-
+
- Cancel
+ OK
+
+ true
+
============================================================
--- res/forms/revision_diff.ui d4d25a844c0a4a141bc9ac01933d96371b7116c0
+++ res/forms/revision_diff.ui 56845d2399bd6d4a4e6936b65311cb63a599ba99
@@ -56,6 +56,9 @@
Close
+
+ true
+
============================================================
--- res/forms/select_revision.ui c1cecd2c790c38b27a87df2bea6e99ca65579c49
+++ res/forms/select_revision.ui b901de21917e7ec4fa9b7ebfa3f1bb5a93bdcf47
@@ -148,6 +148,9 @@
QAbstractItemView::NoSelection
+
+ QAbstractItemView::ScrollPerPixel
+
10
@@ -182,17 +185,20 @@
-
-
+
- OK
+ Cancel
-
-
+
- Cancel
+ OK
+
+ true
+
@@ -203,15 +209,15 @@
+ Splitter
+ QSplitter
+
+
+
TreeView
QTreeView
-
- Splitter
- QSplitter
-
-
selectorBox
============================================================
--- res/guitone-icon.svg f485e11fe3d6d8003fec375967c5375a4ecb9b2f
+++ res/guitone-icon.svg 00effff3ab338037f85f83a29ee1a41e1f6525b6
@@ -9,13 +9,13 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="214.62422"
- height="260.50723"
+ width="260"
+ height="260"
id="svg1944"
sodipodi:version="0.32"
inkscape:version="0.44.1"
version="1.0"
- sodipodi:docbase="/home/tkeller/private/guitone/guitone/res"
+ sodipodi:docbase="/home/tkeller/private/guitone/res"
sodipodi:docname="guitone-icon.svg">
@@ -214,15 +214,17 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="0.35"
+ inkscape:zoom="0.7"
inkscape:cx="350"
- inkscape:cy="520"
+ inkscape:cy="62.857143"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="937"
inkscape:window-height="582"
- inkscape:window-x="550"
- inkscape:window-y="311" />
+ inkscape:window-x="165"
+ inkscape:window-y="152"
+ height="260px"
+ width="260px" />
@@ -241,7 +243,7 @@
transform="translate(-138.4022,-199.2514)">
+ transform="translate(160.4022,199.2514)">
guitone-logo.svg
i18n/guitone_de.qm
icons/guitone.png
+ icons/red_dot.png
+ icons/yellow_dot.png
+ icons/green_dot.png
+ icons/blue_dot.png
+ icons/arrow_down.png
overlays/added.png
overlays/added_missing.png
overlays/cdup.png
============================================================
--- res/i18n/guitone_de.ts 7d2efd4ce19b2d6b7fe344dfebb06da025cc22c1
+++ res/i18n/guitone_de.ts 368821342cae5393b64812ebc3addd7032346589
@@ -1,14 +1,9 @@
About
-
-
- <small>Version %1.%2</small>
-
-
<br/><br/>Autoren: <a href="mailto:address@hidden">Thomas Keller</a>, <a href="mailto:address@hidden">Ingo Maindorfer</a> und <a href="mailto:address@hidden">Jean-Louis Fuchs</a><br/><br/>Dieses Programm ist freie Software. Sie können es unter den<br/>Bedingungen der GNU General Public License, wie von der<br/>Free Software Foundation veröffentlicht, weitergeben und/oder<br/>modifizieren, entweder gemäß Version 2 der Lizenz oder <br/>(nach Ihrer Option) jeder späteren Version.<br/><br/>Fehler? Vorschläge? Hilfe? <a href="http://guitone.thomaskeller.biz">guitone Webseite</a><br/><br/>Vielen Dank an die freundlichen Seelen auf <a href="irc://irc.freenode.net/qt">#qt</a> für die Hilfe während der<br/>unendlich langen Programmierstunden - Ihr Jungs s
eid Spitze!
@@ -33,6 +28,24 @@
+ AddEditAttributeDialog
+
+
+
+ Hinzufügen / Editieren eines Attributes
+
+
+
+
+ Schlüssel
+
+
+
+
+ Wert
+
+
+
Ancestors
@@ -48,7 +61,7 @@
Eine neue Version von guitone ist verfügbar!
-
+
Webseite besuchen
@@ -59,42 +72,22 @@
- Attributes
+ AttributesView
-
-
- hinzugefügt
+
+
+ Neues Attribut hinzufügen
-
-
- entfernt
+
+
+ Attribut editieren
-
-
- verändert
+
+
+ Attribut löschen
-
-
-
- unverändert
-
-
-
-
- Schlüssel
-
-
-
-
- Wert
-
-
-
-
- Status
-
Branches
@@ -374,16 +367,6 @@
Zeige Änderungen gegenüber Elternrevision
-
-
- Alle auswählen
-
-
-
-
- Auswahl umkehren
-
-
Changelog-Eintrag
@@ -406,20 +389,20 @@
- Abbrechen
+ Abbrechen
- Einpflegen
+ Einpflegen
-
+
Keine Änderungen
-
+
Der derzeitige Arbeitsbereich hat keine Änderungen zum Einpflegen.
@@ -473,7 +456,7 @@ den Schlüssel, mit dem Sie diese Revisi
- Revision eingepflegt
+ Revision eingepflegt
@@ -483,7 +466,7 @@ In any case close the current view to av
%1
In any case close the current view to avoid committing the same revision again.
- Die Revision wurde erfolgreich eingepflegt. Da jedoch momentan noch kein "update"-Kommando über die monotone-Schnittstelle verfügbar ist, konnte Ihr Arbeitsbereich nicht automatisch aktualisiert werden.
+ Die Revision wurde erfolgreich eingepflegt. Da jedoch momentan noch kein "update"-Kommando über die monotone-Schnittstelle verfügbar ist, konnte Ihr Arbeitsbereich nicht automatisch aktualisiert werden.
Sie müssen dies entweder per Hand erledigen oder die neue Revision auschecken:
%1
@@ -495,57 +478,67 @@ In jedem Fall sollte der derzeitige Arbe
Bitte geben Sie den Namen eines alternativen Zweigs an.
+
+
+
+ Konnte Revision nicht einpflegen
+
+
+
+
+ Revision konnte nicht eingepflegt werden. Dies ist entweder ein Fehler in guitone oder monotone selbst. Bitte beachten Sie .
+
+
+
+
+ Revision konnte nicht eingepflegt werden. Dies ist entweder ein Fehler in guitone oder monotone selbst. Bitte beachten Sie die Ausgaben im Log und senden Sie optional eine Fehlermeldung ein.
+
ContentDiff
-
+
%1 (binär)
-
+
%1 (%2 Bereiche)
-
+
Zeile
-
+
Datei/Inhalt
- DatabaseView
+ FileDiff
-
-
- Datenbank
+
+
+ Vorgänger des Arbeitsbereichs
-
-
- Alle Änderungen
+
+
+ Arbeitsbereichrevision
-
-
- 50 weitere Änderungen
+
+
+ Datei wurde nicht geändert
-
-
- Beenden
+
+
+ Die Datei wurde zwischen diesen Revisionen nicht geändert.
-
-
-
- ...
-
FileDiffDialog
@@ -555,39 +548,44 @@ In jedem Fall sollte der derzeitige Arbe
Unterschiede in der Datei "%1"
-
-
- Zeige Version
+
+
+ Schließen
-
-
- links
+
+
+ Zeige
-
-
- rechts
+
+
+ erste (%1)
-
-
+
+
+ zweite (%2)
+
+
+
+
beide
-
-
- Schließen
+
+
+ Vorherige
+
+
+
+ Nächste
+
FileExporter
-
-
- Exportiere Dateien...
-
-
Abbrechen
@@ -599,6 +597,47 @@ In jedem Fall sollte der derzeitige Arbe
+ FileHistory
+
+
+
+ Erste: %1...
+
+
+
+
+ Zweite: %1...
+
+
+
+ FileHistoryDialog
+
+
+
+ Geschichte von %1
+
+
+
+
+ Als erste Revision auswählen
+
+
+
+
+ Als zweite Revision auswählen
+
+
+
+
+ Unterschiede anzeigen
+
+
+
+
+ Schließen
+
+
+
GenerateKeypair
@@ -664,14 +703,65 @@ In jedem Fall sollte der derzeitige Arbe
+ GetAttributes
+
+
+
+ leer
+
+
+
+
+ hinzugefügt
+
+
+
+
+ entfernt
+
+
+
+
+ verändert
+
+
+
+
+ unverändert
+
+
+
+
+ Schlüssel
+
+
+
+
+ Wert
+
+
+
+ GetContentChanged
+
+
+
+ Revisions-ID
+
+
+
+
+ Pfad in dieser Revision
+
+
+
GetFile
-
+
Zeile
-
+
Inhalt
@@ -692,12 +782,12 @@ In jedem Fall sollte der derzeitige Arbe
Guitone
-
+
Fehler
-
+
Der Pfad zur ausführbaren Datei von monotone ist entweder ungültig oder zeigt auf eine ältere Version von monotone. Guitone benötigt monotone Version %1 oder ein monotone mit einer Interface-Version %2 oder neuer.
@@ -707,7 +797,7 @@ In jedem Fall sollte der derzeitige Arbe
Kritischer monotone-Fehler
-
+
Wählen Sie Ihren Arbeitsbereich aus...
@@ -801,275 +891,200 @@ In jedem Fall sollte der derzeitige Arbe
InventoryView
-
-
- &Hinzufügen
-
-
-
+
Zum Arbeitsbereich hinzufügen
-
-
- En&tfernen
-
-
-
+
Vom Arbeitsbereich entfernen
-
-
- &Einpflegen
-
-
-
+
Einpflegen
-
-
- Datei &ignorieren
-
-
-
+
Datei ignorieren
-
-
- Datei nicht ign&orieren
-
-
-
+
Datei nicht mehr ignorieren
-
-
- &Zurücksetzen
-
-
-
+
Nicht eingepflegte Änderungen verwerfen
-
-
- Um&benennen
-
-
-
+
Datei umbenennen
-
-
- U&nterschiede anzeigen
-
-
-
+
Unterschiede im Vergleich zur Basisrevision anzeigen
-
-
- &Wechseln zu
-
-
-
+
Wechsle in das Verzeichnis
-
-
- &Öffnen
-
-
-
+
In Standardprogramm öffnen
-
+
Fehler
-
+
Die Datei, die Sie versucht haben zu öffnen, existiert nicht.
-
+
Kann keine Dateien auf Ihrer Plattform öffnen - bitte kontaktieren Sie den Autor über dieses Problem.
-
-
- Alle U&nterschiede anzeigen
-
-
-
+
Zeigt Unterschiede in allen Dateien
-
+
Keine gemeinsamen Aktionen auf selektierte Einträge anwendbar
-
+
Wechseln zu
-
+
Öffnen
-
+
Hinzufügen
-
+
Entfernen
-
+
Datei ignorieren
-
+
Datei nicht ignorieren
-
+
Zurücksetzen
-
+
Unterschiede anzeigen
-
+
Alle Unterschiede anzeigen
-
+
Umbenennen
-
+
Füge %1 Einträge hinzu
-
-
- Fügt mehrere Einträge hinzu
-
-
-
+
Entferne %1 Einträge
-
-
- Entfernt mehrere Einträge
-
-
-
+
Pflege %1 Einträge ein
-
-
- Pflegt mehrere Einträge ein
-
-
-
+
Ignoriere %1 Einträge
-
-
- Ignoriert mehrere Einträge
-
-
-
+
Ignoriere %1 Einträge nicht mehr
-
-
- Ignoriere mehrere Einträge nicht mehr
-
-
-
+
Setze %1 Einträge zurück
-
-
- Setze mehrere Einträge zurück
-
-
-
+
Füge mehrere Einträge hinzu
-
+
Entferne mehrere Einträge
-
+
Pflege mehrere Einträge ein
-
+
Ignoriere mehrere Einträge
-
+
Ignoriere mehrere Einträge nicht mehr
-
+
Setze mehrere Einträge zurück
+
+
+
+ Geschichte
+
+
+
+
+ Zeige die Geschichte dieser Datei an
+
KeyManagement
@@ -1153,209 +1168,182 @@ In jedem Fall sollte der derzeitige Arbe
+ MacStartMenu
+
+
+
+ Keine Aktualisierungen verfügbar
+
+
+
+
+ Ihre Version von guitone (%1) ist bereits aktuell.
+
+
+
+
+ Wählen Sie Ihren Arbeitsbereich aus...
+
+
+
+
+ Wählen Sie eine Datenbank aus...
+
+
+
+
+ monotone-Datenbanken (*.mtn *.db)
+
+
+
MainWindow
-
+
Ansicht
-
+
Hilfe
-
+
Arbeitsbereich
-
+
Datei
-
+
Vorherige Arbeitsbereiche
-
+
Arbeitsbereich öffnen
-
+
Strg+O
-
+
Keine vorherigen Arbeitsbereiche verfügbar.
-
-
- Einstellungen...
-
-
-
+
Strg+P
-
+
Beenden
-
+
Strg+Q
-
+
Ignorierte Dateien verstecken
-
+
Strg+H
-
+
Alle Dateien
-
-
- A
-
-
-
+
Alle geänderten Dateien
-
-
- G
-
-
-
+
Inhaltlich geänderte Dateien
-
-
- P
-
-
-
+
Hinzugefügte Dateien
-
-
- H
-
-
-
+
Entfernte Dateien
-
-
- E
-
-
-
+
Umbenannte Dateien
-
-
- U
-
-
-
+
Fehlende Dateien
-
-
- F
-
-
-
+
Unbekannte Dateien
-
-
- K
-
-
-
+
Ignorierte Dateien
-
-
- I
-
-
-
+
Baum aufklappen
-
+
Strg+T
-
-
- Revision wechseln
-
-
-
+
- Strg+R
+ Strg+R
-
+
Schlüsselverwaltung
-
+
Strg+K
-
+
Über Qt
-
+
Über guitone
-
+
Zeige
@@ -1370,208 +1358,172 @@ In jedem Fall sollte der derzeitige Arbe
Wählen Sie Ihren Arbeitsbereich aus...
-
+
Laden abgebrochen
-
-
- Ungültiger Arbeitsbereich
-
-
-
-
- Das gewählte Verzeichnis ist kein monotone-Arbeitsverzeichnis!
-
-
-
-
- Konnte Kommando nicht ausführen
-
-
-
-
- Konnte '%1' nicht ausführen - eventuell läuft noch ein anderes Kommando?
-
-
-
+
Zeige ignorierte Dateien
-
+
Baum zuklappen
-
+
&%1 %2
-
+
Vorherige Datenbanken
-
+
Datenbank öffnen
-
+
Keine vorherigen geöffneten Datenbanken verfügbar.
-
+
Datenbank
-
+
Strg+Shift+O
-
+
Geladene Datenbank: %1
-
+
Wählen Sie eine Datenbank aus...
-
+
monotone-Datenbanken (*.mtn *.db)
-
+
Strg+B
-
+
Änderungen-Browser
-
+
Schließen
-
+
Alt+A
-
+
Alt+C
-
+
Alt+P
-
+
Alt+N
-
-
- Derzeit ist weder ein Arbeitsbereich noch eine Datenbank geladen,
-
-Um einen Arbeitsbereich zu öffnen, gehen Sie auf Datei > Arbeitsbereich öffnen
-oder respektive auf Datei > Datenbank öffnen.
-
-
-
+
Fenster
-
+
Alt+D
-
+
Alt+R
-
+
Alt+M
-
+
Alt+U
-
+
Arbeitsbereich aktualisieren
-
+
Strg+U
-
+
Alt+I
-
+
Revision auschecken
-
+
Strg+Umschalt+U
-
+
Alle nach vorne bringen
-
+
%1 - Datenbankmodus - guitone
-
+
%1 - Arbeitsbereichmodus - guitone
-
-
- Kein Arbeitsbereich oder Datenbank geladen - guitone
-
-
-
+
Konnte Arbeitsbereich nicht laden
-
+
-
+
Revision einpflegen
-
+
Strg+C
@@ -1608,12 +1560,12 @@ Die letzte Ausgabe war:
%1
-
+
Konnte Datenbank nicht laden
-
+
-
+
Einstellungen...
-
+
Auf Aktualisierungen prüfen
-
+
Keine Aktualisierungen verfügbar
-
+
Ihre Version von guitone (%1) ist bereits aktuell.
+
+
+
+ Arbeitsbereich neu laden
+
+
+
+
+ Finde nicht verfolgte Umbenennungen
+
+
+
+
+ Nichts gefunden
+
+
+
+
+ Keine nicht verfolgten Umbenennungen in diesem Arbeitsbereich gefunden.
+
Manifest
@@ -1683,18 +1655,7 @@ Die letzte Ausgabe war:
Monotone
-
-
- Der monotone-Prozess wurde unerwartet beendet (Rückgabewert %1). Bitte überprüfen Sie den Pfad zur ausführbaren Datei von monotone im Eigenschaften-Dialog und stellen Sie sicher, dass die Version der Datenbank, die Sie versucht haben zu laden, mit der Version von monotone übereinstimmt.
-
-monotone gab zurück:
-%2
-
-
-
+
[Prozess nicht erzeugt]
@@ -1702,7 +1663,7 @@ monotone gab zurück:
MonotoneDelegate
-
+
[unbekannter Zweig]
@@ -1710,60 +1671,60 @@ monotone gab zurück:
Preferences
-
-
- Niedrig (fatale Fehler)
-
-
-
-
- Mittel (kritische Fehler)
-
-
-
-
- Hoch (Warnungen)
-
-
-
-
- Alle (Debugmeldungen)
-
-
-
+
Fehler
-
+
Der Pfad zur ausführbaren Datei von monotone ist entweder ungültig oder zeigt auf eine ältere Version von monotone. Guitone benötigt monotone Version %1 oder ein monotone mit einer Interface-Version %2 oder neuer.
-
+
Hinweis
-
+
Sie müssen Ihren derzeitigen Arbeitsbereich oder guitone neu laden, um die neue monotone-Binary zu nutzen.
-
+
Wählen Sie die ausführbare Datei von monotone
-
+
Binärdateien (mtn mtn.exe)
-
-
- Der Pfad zur ausführbaren Datei von monotone ist entweder ungültig oder zeigt auf eine Version von monotone, mit der guitone nicht arbeiten kann. Guitone benötigt ein monotone mit einer Interface-Version zwischen %2 und %3. Sie können die Interface-Version Ihrer monotone-Installation durch den Aufruf des Kommandos `mtn automate interface_version` in Erfahrung bringen.
+
+
+ Sehr niedrig (nur fatale Fehler)
+
+
+
+ Niedrig (kritische Fehler)
+
+
+
+
+ Mittel (Warnungen)
+
+
+
+
+ Hoch (Informationen)
+
+
+
+
+ Sehr hoch (Debug-Meldungen)
+
PreferencesDialog
@@ -1803,12 +1764,12 @@ monotone gab zurück:
Log-Level
-
+
OK
-
+
Abbrechen
@@ -1861,43 +1822,21 @@ monotone gab zurück:
Attr gelöscht
-
-
- %1 nach %2
-
-
-
-
- '%1' von %2
-
-
-
-
- '%1' auf '%2' für %3
-
-
-
+
%1 nach
%2
-
+
'%1' von
%2
-
-
- '%1' auf '%2'
-für %3
-
-
-
+
@@ -2054,12 +1993,12 @@ für %3
Unterschiede anzeigen
-
+
OK
-
+
Abbrechen
@@ -2137,27 +2076,73 @@ für %3
Tags
-
+
Name
-
+
Revision
-
+
Unterschreiber
-
+
Zweige
+ UnaccountedRenames
+
+
+
+ Fehlende Pfade und mögliche Ziele
+
+
+
+
+ Datei-ID (sofern anwendbar)
+
+
+
+
+ n/a
+
+
+
+
+ passt auf Grund des Names
+
+
+
+
+ passt auf Grund des Inhalts
+
+
+
+ UnaccountedRenamesDialog
+
+
+
+ Nicht verfolgte Umbenennungen
+
+
+
+
+ Benenne markierte um
+
+
+
+
+ Schließen
+
+
+
UpdateWorkspace
@@ -2254,11 +2239,6 @@ für %3
-
- Die ausgewählte Revision besitzt mehr als ein Zweigzertifikat - bitte wählen Sie eins:
-
-
-
Die ausgewählte Revision besitzt mehr als ein
============================================================
--- src/Guitone.cpp cae5d0b8210527a4327743b7ea8c4993d4e7639a
+++ src/Guitone.cpp 444b0af3793794d34e4657b2fea5e5e983d90ae2
@@ -26,7 +26,6 @@
#ifdef Q_WS_MACX
#include "CocoaUtil.h"
-#include
#endif
#include
@@ -34,107 +33,18 @@
#include
#include
#include
+#include
-#ifdef Q_WS_MACX
-static OSErr OpenDocumentAE(const AppleEvent * evt, AppleEvent *, long)
-{
- AEDescList docList;
- DescType retType;
- AEKeyword keywd;
- FSRef fsref;
- long count, size;
-
- AEGetParamDesc(evt, keyDirectObject, typeAEList, &docList);
- AECountItems(&docList, &count);
-
- OSErr err;
-
- for (int i = 1; i <= count; i++)
- {
- err = AEGetNthPtr(&docList, i, typeFSRef, &keywd,
- &retType, (Ptr)&fsref, sizeof(fsref), &size);
-
- if (err != noErr)
- {
- W("Couldn't retrieve FSRef.");
- continue;
- }
-
- QString path = CocoaUtil::FSRefToPath(fsref);
- if (path.isEmpty())
- {
- W("Couldn't convert FSRef to path.");
- continue;
- }
-
- D(QString("Acquired new path: %1").arg(path));
- APP->loadFromPath(path);
- }
- AEDisposeDesc(&docList);
-
- return 0;
-}
-
-bool Guitone::macEventFilter(EventHandlerCallRef caller, EventRef event)
-{
- //
- // taken from
- // http://developer.apple.com/documentation/AppleScript/Conceptual/
- // AppleEvents/dispatch_aes_aepg/chapter_4_section_3.html
- //
- Boolean release = false;
- EventRecord eventRecord;
- OSErr ignoredErr;
-
- // Events of type kEventAppleEvent must be removed from the queue
- // before being passed to AEProcessAppleEvent.
- if (IsEventInQueue(GetMainEventQueue(), event))
- {
- // RemoveEventFromQueue will release the event, which will
- // destroy it if we don't retain it first.
- RetainEvent(event);
- release = true;
- RemoveEventFromQueue(GetMainEventQueue(), event);
- }
-
- // Convert the event ref to the type AEProcessAppleEvent expects.
- ConvertEventRefToEventRecord(event, &eventRecord);
- ignoredErr = AEProcessAppleEvent(&eventRecord);
-
- if (release)
- ReleaseEvent(event);
-
- // This Carbon event has been handled, even if no AppleEvent handlers
- // were installed for the Apple event.
- return noErr;
-}
-#endif
-
Guitone::Guitone(int argc, char** argv)
- : QApplication(argc, argv), updateDialog(0)
+ : QApplication(argc, argv), updateDialog(0), somethingLoaded(false)
{
setQuitOnLastWindowClosed(false);
setOrganizationName("Thomas Keller");
setOrganizationDomain("thomaskeller.biz");
setApplicationName("guitone");
-
-#ifdef Q_WS_MACX
- // install apple event handler to open documents
- OSErr err = AEInstallEventHandler(
- kCoreEventClass, kAEOpenDocuments,
- NewAEEventHandlerUPP(OpenDocumentAE), 0, false
- );
- require_noerr(err, error_installing_event_handler);
- return;
-error_installing_event_handler:
- C("Could not install OpenDocumentAE event handler");
-#endif
-}
-
-bool Guitone::init()
-{
+ // check for updates immediatly on launch
if (Settings::getBool("CheckForUpdates", true))
{
#ifdef Q_WS_MACX
@@ -148,13 +58,77 @@ bool Guitone::init()
}
#endif
}
+
+ //
+ // Try to load something given on the command line
+ // or found in one of the recent items lists
+ //
+ // FIXME: This is disabled for OSX since it interfers terribly with
+ // FileOpen events on this platform, i.e. we can't determine
+ // properly if the program is opened and shortly afterwards gets
+ // a FileOpen event or if there will no such event and we have to look
+ // at the command line or in the recent lists for something to load.
+ //
+ // What we're doing instead here for Mac OS X (whose users would otherwise
+ // fail to use guitone by opening it without also opening a workspace or
+ // database) is to create a global QMenuBar (MacStartMenu), which contains
+ // the basic file menu entries to open (recent) workspaces and databases
+ // amongst regular stuff like about dialogs and the update stuff. This is
+ // certainly not the end for "load recent automatically" on Mac, but a
+ // workaround.
+ //
+#ifdef Q_WS_MACX
+ macStartMenu = new MacStartMenu();
+ connect(
+ macStartMenu, SIGNAL(loadWorkspace(const QString &)),
+ this, SLOT(loadWorkspace(const QString &))
+ );
+
+ connect(
+ macStartMenu, SIGNAL(loadDatabase(const QString &)),
+ this, SLOT(loadDatabase(const QString &))
+ );
+#else
+ QTimer::singleShot(0, this, SLOT(loadSomething()));
+#endif
+}
+
+Guitone::~Guitone()
+{
+ I(openWindows.size() == 0);
+ I(monotoneInstances.size() == 0);
+ if (updateDialog) delete updateDialog;
+
+#ifdef Q_WS_MACX
+ disconnect(
+ macStartMenu, SIGNAL(loadWorkspace(const QString &)),
+ this, SLOT(loadWorkspace(const QString &))
+ );
+
+ disconnect(
+ macStartMenu, SIGNAL(loadDatabase(const QString &)),
+ this, SLOT(loadDatabase(const QString &))
+ );
+
+ delete macStartMenu;
+#endif
+}
+
+#ifndef Q_WS_MACX
+void Guitone::loadSomething()
+{
QStringList args = arguments();
- bool somethingLoaded = false;
- for (int i=1, j=args.size(); i 0 && loadWorkspace(workspaces.at(0));
}
if (!somethingLoaded)
{
+ D("trying to load recent database");
QStringList databases = Settings::getItemList("RecentDatabaseList");
somethingLoaded = databases.size() > 0 && loadDatabase(databases.at(0));
}
@@ -175,21 +151,67 @@ bool Guitone::init()
// if still nothing is loaded, prompt the user to load a workspace
if (!somethingLoaded)
{
+ D("prompting for a workspace");
QString workspaceDir =
QFileDialog::getExistingDirectory(0, tr("Select your workspace..."));
somethingLoaded = !workspaceDir.isEmpty() && loadWorkspace(workspaceDir);
}
// if nothing could be loaded (= no window could be created),
- // let the app close itself
- return somethingLoaded;
+ // quit the application
+ if (!somethingLoaded)
+ {
+ D("nothing could be loaded, quitting");
+ quit();
+ }
}
+#endif
-Guitone::~Guitone()
+// this code is borrowed and adapted from QDesigner - thanks to the Trolls!
+bool Guitone::event(QEvent * ev)
{
- I(openWindows.size() == 0);
- I(monotoneInstances.size() == 0);
- if (updateDialog) delete updateDialog;
+ bool eaten;
+ switch (ev->type())
+ {
+ case QEvent::FileOpen:
+ {
+ QApplication::processEvents();
+ D("got load event");
+ if (loadFromPath(static_cast(ev)->file()))
+ {
+ QApplication::processEvents();
+ D("loading from path succeeded");
+ somethingLoaded = true;
+ }
+ eaten = true;
+ break;
+ }
+ case QEvent::Close:
+ {
+ D("got close event");
+ QCloseEvent * closeEvent = static_cast(ev);
+
+ // try to close all windows, if any window refuses to be closed,
+ // just eat the event and do nothing
+ if (closeAllWindows())
+ {
+ closeEvent->setAccepted(true);
+ eaten = QApplication::event(ev);
+ D("closing all windows succeeded");
+ }
+ else
+ {
+ closeEvent->setAccepted(false);
+ eaten = true;
+ D("could not close all windows");
+ }
+ break;
+ }
+ default:
+ eaten = QApplication::event(ev);
+ break;
+ }
+ return eaten;
}
bool Guitone::loadFromPath(const QString & path)
@@ -385,7 +407,7 @@ const QList Guitone::window
return openWindows;
}
-void Guitone::quit()
+bool Guitone::closeAllWindows()
{
// do we need to close any windows?
if (openWindows.size() > 0)
@@ -397,11 +419,16 @@ void Guitone::quit()
// problems
foreach (MainWindow * wnd, openWindows)
{
- wnd->close();
+ // QWidget::close() returns true if the widget could be closed,
+ // i.e. hasn't ignored the close event
+ if (!wnd->close()) return false;
}
- return;
}
-
+ return true;
+}
+
+void Guitone::quit()
+{
Settings::sync();
QApplication::quit();
}
============================================================
--- src/Guitone.h 881dffddd5bdd9999f2ed36ecdcaa019de153cb4
+++ src/Guitone.h 9c9143ee93f0633494e48c42e0ee0f64d6af46d6
@@ -24,7 +24,12 @@
#include "MainWindow.h"
#include "Monotone.h"
#include "ApplicationUpdate.h"
+#include "DebugLog.h"
+#ifdef Q_WS_MACX
+#include "MacStartMenu.h"
+#endif
+
#include
#include
#include
@@ -37,7 +42,6 @@ public:
public:
Guitone(int, char**);
~Guitone();
- bool init();
const QList windowList() const;
MainWindow * findMainWindow(QObject *);
@@ -54,8 +58,11 @@ private slots:
bool loadWorkspace(const QString &);
bool loadDatabase(const QString &);
void windowClosed(MainWindow *);
+#ifndef Q_WS_MACX
+ void loadSomething();
+#endif
void quit();
-
+
private:
MainWindow * addWindow();
void removeWindow(MainWindow *);
@@ -63,15 +70,20 @@ private:
bool addMonotoneInstance(MainWindow *);
bool removeMonotoneInstance(MainWindow *);
-#ifdef Q_WS_MACX
- bool macEventFilter(EventHandlerCallRef, EventRef);
-#endif
+ bool closeAllWindows();
+
+ bool event(QEvent *);
QList openWindows;
QMap monotoneInstances;
QMutex mutex;
-
ApplicationUpdate * updateDialog;
+ bool somethingLoaded;
+
+#ifdef Q_WS_MACX
+ MacStartMenu * macStartMenu;
+#endif
+
};
#endif
============================================================
--- src/main.cpp e36e3f6a9adf493840944a11b718bf25abe079de
+++ src/main.cpp b3c466a5103800420aa2b0cab6c9d1be4994f5c5
@@ -48,21 +48,30 @@ int main(int argc, char** argv)
// install our own message handler to catch debug messages, warnings, etc.
qInstallMsgHandler(guitoneMsgHandler);
- // try to find a suitable locale and setup translations
QString locale = QLocale::system().name();
- QTranslator translator;
+
+ // load the default Qt translations
+ QTranslator qtTranslator;
+ if (!qtTranslator.load("qt_" + locale))
+ {
+ W(QString("Couldn't load qt translation for %1 - using default")
+ .arg(locale));
+ }
+
+ // try to load the application's translation
+ QTranslator appTranslator;
QString transFileName("guitone_" + locale);
- if (!translator.load(transFileName, ":/i18n"))
+ if (!appTranslator.load(transFileName, ":/i18n"))
{
- W(QString("Couldn't load translation file for %1 - using default")
+ W(QString("Couldn't load app translation for %1 - using default")
.arg(locale));
}
Guitone app(argc, argv);
- app.installTranslator(&translator);
- if (!app.init()) return 1;
+ app.installTranslator(&appTranslator);
+ app.installTranslator(&qtTranslator);
return app.exec();
}
============================================================
--- src/model/Certs.cpp 0158c7df5e847ce46b6b56557ea7647d5b423b52
+++ src/model/Certs.cpp e4a81c6408c64be1a305aa5eb70842e589e34311
@@ -91,7 +91,7 @@ void Certs::parseOutput()
if (entry.sym == "value")
{
I(entry.vals.size() == 1);
- cert.value = entry.vals.at(0);
+ cert.value = entry.vals.at(0).trimmed();
continue;
}
@@ -149,8 +149,14 @@ QVariant Certs::data(const QModelIndex &
return QVariant();
}
+ if (role == Qt::TextAlignmentRole)
+ {
+ Qt::Alignment flags = Qt::AlignLeft | Qt::AlignTop;
+ return QVariant(flags);
+ }
+
if (role != Qt::DisplayRole)
- {
+ {
return QVariant();
}
============================================================
--- src/model/ContentDiff.cpp af2b8ba1408305c6413d2b0900bc9f7d1e76cb3c
+++ src/model/ContentDiff.cpp 330cf8f074e03c6fc73d6581933660f2e5ac411c
@@ -39,18 +39,8 @@ ContentDiff::~ContentDiff()
delete mtnDelegate;
}
-bool ContentDiff::readDiff(QString fileName)
-{
- return readDiff(fileName, QString(), QString());
-}
-
-bool ContentDiff::readDiff(QString fileName, QString leftRevision)
-{
- return readDiff(fileName, leftRevision, QString());
-}
-
bool ContentDiff::readDiff(
- QString fileName, QString leftRevision, QString rightRevision
+ const QString & fileName, const QString & base, const QString & target
)
{
// reset the view
@@ -61,16 +51,18 @@ bool ContentDiff::readDiff(
QStringList opts;
- if (leftRevision.length() == 0)
+ if (base.isEmpty())
{
- leftRevision = MonotoneDelegate::getBaseWorkspaceRevision(this);
+ opts << "r" << MonotoneDelegate::getBaseWorkspaceRevision(this);
}
+ else
+ {
+ opts << "r" << base;
+ }
- opts << "r" << leftRevision;
-
- if (rightRevision.length() > 0)
+ if (!target.isEmpty())
{
- opts << "r" << rightRevision;
+ opts << "r" << target;
}
return mtnDelegate->triggerCommand(cmd, opts);
============================================================
--- src/model/ContentDiff.h 91ee6ba1a90506ce0890d45c4f55d9b146b2d871
+++ src/model/ContentDiff.h 9f0b45bde2c813734bf6f4a9a5c8cb88e629a41d
@@ -76,9 +76,7 @@ public slots:
inline FileDiffs getAllDiffs() { return diffParser->getAllDiffs(); }
public slots:
- bool readDiff(QString);
- bool readDiff(QString, QString);
- bool readDiff(QString, QString, QString);
+ bool readDiff(const QString &, const QString & baseRev = QString(), const QString & targetRev = QString());
signals:
void diffRead();
============================================================
--- src/model/Attributes.cpp 2ea5d72737c95823852fbf7038235b8af7349d49
+++ src/model/GetAttributes.cpp c1f8ccabf73da0b5f91634619197f0adbfeb31de
@@ -18,59 +18,51 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
-#include "Attributes.h"
-#include "InventoryItem.h"
+#include "GetAttributes.h"
#include "Monotone.h"
#include "BasicIOParser.h"
-#include
+#include
+#include
-Attributes::Attributes(QObject *parent)
+GetAttributes::GetAttributes(QObject *parent)
: QAbstractItemModel(parent)
{
attributes = new AttributeList();
mtnDelegate = new MonotoneDelegate(this);
}
-Attributes::~Attributes()
+GetAttributes::~GetAttributes()
{
attributes->clear();
delete attributes;
delete mtnDelegate;
}
-bool Attributes::readAttributes(const QModelIndex & index)
+void GetAttributes::revert()
{
- // convert ProxyModel index to real Model index
- QModelIndex sourceIndex = static_cast(index.model())->mapToSource(index);
- // get the Model's item reference out there
- InventoryItem *item = static_cast(sourceIndex.internalPointer());
+ attributes->clear();
+ path = QString();
+ reset();
+}
+bool GetAttributes::readAttributes(const QString & p)
+{
// clear current attributes list
attributes->clear();
// reset the view
reset();
- if (item->isRootDirectory() || item->isCdUp())
- {
- D("item is pseudo item (root or cdup)");
- return false;
- }
-
- if (item->hasStatus(InventoryItem::Dropped) || !item->isTracked())
- {
- D("item is not tracked or dropped");
- return false;
- }
-
QStringList cmd;
cmd.append("get_attributes");
- cmd.append(item->getPath());
+ cmd.append(p);
+ path = p;
+
return mtnDelegate->triggerCommand(cmd);
}
-void Attributes::parseOutput()
+void GetAttributes::parseOutput()
{
BasicIOParser parser(AutomateCommand::data);
I(parser.parse());
@@ -143,77 +135,122 @@ void Attributes::parseOutput()
emit attributesRead();
}
-int Attributes::columnCount(const QModelIndex &parent) const
+int GetAttributes::columnCount(const QModelIndex &parent) const
{
return 3;
}
-QVariant Attributes::data(const QModelIndex &index, int role) const
+QVariant GetAttributes::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
- if (role != Qt::DisplayRole)
+ int row = index.row(), col = index.column();
+ if (row >= attributes->size()) return QVariant();
+
+ Attribute attr = attributes->at(row);
+
+ if (role == Qt::DisplayRole)
{
- return QVariant();
+ switch (col)
+ {
+ case 1: return QVariant(attr.key.length() == 0 ? tr("empty") : attr.key);
+ case 2: return QVariant(attr.value.length() == 0 ? tr("empty") : attr.value);
+ default: return QVariant();
+ }
}
-
- int row = index.row();
- if (row >= attributes->size()) return QVariant();
- Attribute attr = attributes->at(row);
+ if (role == Qt::ToolTipRole)
+ {
+ switch (attr.state)
+ {
+ case Attribute::Added: return QVariant(tr("added"));
+ case Attribute::Dropped: return QVariant(tr("dropped"));
+ case Attribute::Changed: return QVariant(tr("changed"));
+ case Attribute::Unchanged: return QVariant(tr("unchanged"));
+ default: return QVariant();
+ }
+ }
- switch (index.column())
+ if (role == Qt::DecorationRole && col == 0)
{
- case 0: return QVariant(attr.key);
- case 1: return QVariant(attr.value);
- case 2:
- switch (attr.state)
- {
- case Attribute::Added: return QVariant(tr("added"));
- case Attribute::Dropped: return QVariant(tr("dropped"));
- case Attribute::Changed: return QVariant(tr("changed"));
- case Attribute::Unchanged: return QVariant(tr("unchanged"));
- }
+ switch (attr.state)
+ {
+ case Attribute::Added: return QVariant(QIcon(":/icons/green_dot.png"));
+ case Attribute::Dropped: return QVariant(QIcon(":/icons/red_dot.png"));
+ case Attribute::Unchanged: return QVariant(QIcon(":/icons/yellow_dot.png"));
+ case Attribute::Changed: return QVariant(QIcon(":/icons/blue_dot.png"));
+ default: return QVariant();
+ }
}
+ if (role == Qt::FontRole)
+ {
+ QFont font;
+ font.setItalic(true);
+
+ if (col == 1 && attr.key.length() == 0)
+ return QVariant(font);
+
+ if (col == 2 && attr.value.length() == 0)
+ return QVariant(font);
+
+ return QVariant();
+ }
+
+ if (role == Qt::UserRole)
+ {
+ switch (col)
+ {
+ case 1: return QVariant(attr.key);
+ case 2: return QVariant(attr.value);
+ default: return QVariant();
+ }
+ }
+
return QVariant();
}
-Qt::ItemFlags Attributes::flags(const QModelIndex &index) const
+Qt::ItemFlags GetAttributes::flags(const QModelIndex &index) const
{
- // TODO: add ItemIsEditable as soon as we can set/drop attributes through
- // the automation interface, implement setData() then as well
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
-QVariant Attributes::headerData(int section, Qt::Orientation orientation, int role) const
+QVariant GetAttributes::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ I(orientation == Qt::Horizontal);
+
+ if (role == Qt::DisplayRole)
{
switch (section)
{
- case 0: return QVariant(tr("Key"));
- case 1: return QVariant(tr("Value"));
- case 2: return QVariant(tr("State"));
+ case 1: return QVariant(tr("Key"));
+ case 2: return QVariant(tr("Value"));
+ default: return QVariant();
}
}
+
+ if (role == Qt::DecorationRole && section == 0)
+ {
+ return QVariant(QIcon(":/icons/arrow_down.png"));
+ }
+
return QVariant();
}
-int Attributes::rowCount(const QModelIndex& parent) const
+int GetAttributes::rowCount(const QModelIndex& parent) const
{
return attributes->size();
}
-QModelIndex Attributes::index(int row, int column, const QModelIndex& parent) const
+QModelIndex GetAttributes::index(int row, int column, const QModelIndex& parent) const
{
return hasIndex(row, column, parent) ? createIndex(row, column, 0) : QModelIndex();
}
-QModelIndex Attributes::parent(const QModelIndex& index) const
+QModelIndex GetAttributes::parent(const QModelIndex& index) const
{
return QModelIndex();
}
============================================================
--- src/model/Attributes.h a4d4844a547daa12380f96da44609c627e859b25
+++ src/model/GetAttributes.h f9f06f87a1177277bce55b69800a14df55fffbd3
@@ -18,11 +18,9 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
-// TODO: enable editing of items as well as dropping/adding them
+#ifndef GETATTRIBUTES_H
+#define GETATTRIBUTES_H
-#ifndef ATTRIBUTES_H
-#define ATTRIBUTES_H
-
#include "AutomateCommand.h"
#include "MonotoneDelegate.h"
@@ -35,13 +33,15 @@ typedef QList AttributeList;
} Attribute;
typedef QList AttributeList;
-class Attributes : public QAbstractItemModel, public AutomateCommand
+class GetAttributes : public QAbstractItemModel, public AutomateCommand
{
Q_OBJECT
public:
- Attributes(QObject*);
- virtual ~Attributes();
-
+ GetAttributes(QObject*);
+ virtual ~GetAttributes();
+
+ inline QString currentPath() const { return path; }
+
// needed Qt Model methods
QVariant data(const QModelIndex&, int) const;
Qt::ItemFlags flags(const QModelIndex&) const;
@@ -49,11 +49,12 @@ public:
QModelIndex index(int, int, const QModelIndex&) const;
QModelIndex parent(const QModelIndex&) const;
int rowCount(const QModelIndex&) const;
- int columnCount(const QModelIndex&) const;
+ int columnCount(const QModelIndex&) const;
public slots:
- bool readAttributes(const QModelIndex &);
-
+ bool readAttributes(const QString &);
+ void revert();
+
signals:
void attributesRead();
@@ -61,6 +62,7 @@ private:
void parseOutput();
AttributeList * attributes;
MonotoneDelegate * mtnDelegate;
+ QString path;
};
#endif
============================================================
--- src/model/GetFile.cpp 5151a9eccdcf6f92a140dc662d5c845f79484c34
+++ src/model/GetFile.cpp 54bfdc61b73d50418793fca0b86bc05f434ee311
@@ -33,23 +33,25 @@ GetFile::~GetFile()
delete mtnDelegate;
}
-bool GetFile::readFileByName(QString fileName)
+bool GetFile::readFileByName(const QString & fileName, const QString & rev)
{
- return readFileByName(fileName, MonotoneDelegate::getBaseWorkspaceRevision(this));
-}
-
-bool GetFile::readFileByName(QString fileName, QString revision)
-{
QStringList cmd;
cmd << "get_file_of" << fileName;
QStringList opts;
- opts << "r" << revision;
+ if (rev.isEmpty())
+ {
+ opts << "r" << MonotoneDelegate::getBaseWorkspaceRevision(this);
+ }
+ else
+ {
+ opts << "r" << rev;
+ }
return readFile(cmd, opts);
}
-bool GetFile::readFileById(QString fileID)
+bool GetFile::readFileById(const QString & fileID)
{
QStringList cmd;
cmd << "get_file" << fileID;
@@ -130,6 +132,75 @@ void GetFile::applyDiff(Diff * diff)
reset();
}
+QModelIndex GetFile::getNextGroup(const QModelIndex & base, bool recurse)
+{
+ int startRow = 0;
+ ContentLine::Marker lastMarker = ContentLine::Unchanged;
+ if (base.isValid() && base.row() < fileContents.size())
+ {
+ startRow = base.row();
+ lastMarker = fileContents.at(startRow)->marker;
+ }
+
+ for (int i=startRow, j=fileContents.size(); imarker != lastMarker && line->marker != ContentLine::Unchanged)
+ {
+ return index(i, 0, QModelIndex());
+ }
+ lastMarker = line->marker;
+ }
+
+ // if we already recursed, stop here
+ if (recurse) return QModelIndex();
+ // try to get the first group
+ return getNextGroup(QModelIndex(), true);
+}
+
+QModelIndex GetFile::getPrevGroup(const QModelIndex & base, bool recurse)
+{
+ int startRow = fileContents.size() - 1;
+ ContentLine::Marker lastMarker = ContentLine::Unchanged;
+ if (base.isValid() && base.row() < fileContents.size())
+ {
+ startRow = base.row();
+ lastMarker = fileContents.at(startRow)->marker;
+ }
+
+ bool foundLast = false;
+ for (int i=startRow; i>=0; i--)
+ {
+ ContentLine * line = fileContents.at(i);
+
+ // did we already found the last line of a group of similar lines?
+ if (!foundLast)
+ {
+ if (line->marker != lastMarker && line->marker != ContentLine::Unchanged)
+ {
+ foundLast = true;
+ }
+ lastMarker = line->marker;
+ continue;
+ }
+
+ if (foundLast)
+ {
+ // if the marker just changed, the previous line was our
+ // first line
+ if (line->marker != lastMarker)
+ {
+ return index(i + 1, 0, QModelIndex());
+ }
+ }
+ }
+
+ // if we've already recursed, stop here
+ if (recurse) return QModelIndex();
+ // try to get the last group
+ return getPrevGroup(QModelIndex(), true);
+}
+
int GetFile::columnCount(const QModelIndex &parent) const
{
return 2;
============================================================
--- src/model/GetFile.h 2542dad0537061ec09b64b453ac07a972788b1c6
+++ src/model/GetFile.h 341b069c5778068d50bbf01cd34557a04b639db3
@@ -53,11 +53,12 @@ public:
QModelIndex parent(const QModelIndex&) const;
int rowCount(const QModelIndex&) const;
int columnCount(const QModelIndex&) const;
+ QModelIndex getNextGroup(const QModelIndex &, bool recurse = false);
+ QModelIndex getPrevGroup(const QModelIndex &, bool recurse = false);
public slots:
- bool readFileById(QString);
- bool readFileByName(QString);
- bool readFileByName(QString, QString);
+ bool readFileById(const QString &);
+ bool readFileByName(const QString &, const QString & rev = QString());
void applyDiff(Diff * diff);
signals:
============================================================
--- src/model/GetRevision.cpp a953b589dc8091738c22564d6c18ff070eb48211
+++ src/model/GetRevision.cpp 1ca82e3b9ba7a2956fc60cff0e976c0ec1e681e2
@@ -85,15 +85,15 @@ void GetRevision::parseOutput()
if (j == 0 && entry.sym == "new_manifest")
{
- I(entry.vals.size() == 1);
- revision.new_manifest = entry.vals.at(0);
+ I(!entry.hash.isNull());
+ revision.new_manifest = entry.hash;
break;
}
if (entry.sym == "old_revision")
{
- I(entry.vals.size() == 1);
- currentRevision = entry.vals.at(0);
+ I(!entry.hash.isNull());
+ currentRevision = entry.hash;
QList changelist;
revision.changesAgainstParent.insert(currentRevision, changelist);
break;
============================================================
--- src/model/GetRevision.h 0308fe66ff1749162131c693e28efe7497579d55
+++ src/model/GetRevision.h 9c2675f10e96cf957979a04a2df71ae989d1d6c6
@@ -63,50 +63,6 @@ struct Change {
return Qt::transparent;
}
- inline QString escape(const QString & in)
- {
- QString out(in);
- out.replace("\\", "\\\\");
- out.replace("\"", "\\\"");
- return out;
- }
-
- inline QString getStanzaData()
- {
- switch (type)
- {
- case AddDir:
- return QString("add_dir \"%1\"")
- .arg(escape(stanza.at(0).vals.at(0)));
- case AddFile:
- return QString("add_file \"%1\"\ncontent [%2]")
- .arg(escape(stanza.at(0).vals.at(0)))
- .arg(stanza.at(1).vals.at(0));
- case Delete:
- return QString("delete \"%1\"")
- .arg(escape(stanza.at(0).vals.at(0)));
- case Patch:
- return QString("patch \"%1\"\nfrom [%2]\nto [%3]")
- .arg(escape(stanza.at(0).vals.at(0)))
- .arg(stanza.at(1).vals.at(0))
- .arg(stanza.at(2).vals.at(0));
- case Rename:
- return QString("rename \"%1\"\nto \"%2\"")
- .arg(escape(stanza.at(0).vals.at(0)))
- .arg(escape(stanza.at(1).vals.at(0)));
- case AttrClear:
- return QString("clear \"%1\"\nattr \"%2\"")
- .arg(escape(stanza.at(0).vals.at(0)))
- .arg(escape(stanza.at(1).vals.at(0)));
- case AttrSet:
- return QString("set \"%1\"\nattr \"%2\"\nvalue \"%3\"")
- .arg(stanza.at(0).vals.at(0))
- .arg(stanza.at(1).vals.at(0))
- .arg(stanza.at(2).vals.at(0));
- }
- return QString();
- }
-
inline QString getDisplayData()
{
switch (type)
============================================================
--- src/model/Inventory.cpp fe03f18135e1c1a39e069ea74b3f6f36028156e5
+++ src/model/Inventory.cpp 38d53b03b3303315ba61b72f5241ffa81e24f8f3
@@ -27,10 +27,10 @@ Inventory::Inventory(QObject *parent) :
Inventory::Inventory(QObject *parent) : QAbstractItemModel(parent)
{
- // create a dummy item since the view needs at least one item
- // in the model, otherwise the app crashes
- rootItem = new InventoryItem();
- regex = new QRegExp("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$");
+ // create a dummy item since the view needs at least one item
+ // in the model, otherwise the app crashes
+ rootItem = new InventoryItem();
+ regex = new QRegExp("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$");
regex->setMinimal(true);
mtnDelegate = new MonotoneDelegate(this);
@@ -43,8 +43,8 @@ Inventory::~Inventory()
Inventory::~Inventory()
{
- delete rootItem;
- delete regex;
+ delete rootItem;
+ delete regex;
delete mtnDelegate;
}
@@ -53,74 +53,80 @@ bool Inventory::readInventory()
QStringList cmd;
cmd << "inventory";
- return mtnDelegate->triggerCommand(cmd);
+ return mtnDelegate->triggerCommand(cmd);
}
void Inventory::parseOutput()
{
- QStringList lines = AutomateCommand::data.split("\n", QString::SkipEmptyParts);
- QMap renameMap;
- QMap::iterator renameIter;
+ QStringList lines = AutomateCommand::data.split("\n", QString::SkipEmptyParts);
+ QMap renameMap;
+ QMap::iterator renameIter;
- InventoryItem *item;
- QList tempItems;
- int status(0);
- int from_id(0);
- int to_id(0);
- QString path("");
- bool isDirectory(false);
+ QList items;
+ InventoryItem * item;
+
+ int status(0);
+ int from_id(0);
+ int to_id(0);
+ QString path("");
+ bool isDirectory(false);
- for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
- {
- if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory))
- {
- continue;
- }
-
- // this item is given a parent explicitely later on
- item = new InventoryItem(isDirectory);
+ for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ {
+ if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory))
+ {
+ continue;
+ }
+
+ // this item is given a parent explicitely later on
+ item = new InventoryItem(isDirectory);
item->setPath(path);
item->setStatus(status);
- if (from_id > 0)
- {
- renameMap[-from_id] = item;
- }
- if (to_id > 0)
- {
- renameMap[to_id] = item;
- }
+ if (from_id > 0)
+ {
+ renameMap[-from_id] = item;
+ }
+ if (to_id > 0)
+ {
+ renameMap[to_id] = item;
+ }
- tempItems.push_back(item);
- }
+ items.push_back(item);
+ }
- int id = 0;
+ int id = 0;
- while (true)
- {
- renameIter = renameMap.find(++id);
- if (renameIter == renameMap.end()) break;
+ while (true)
+ {
+ renameIter = renameMap.find(++id);
+ if (renameIter == renameMap.end()) break;
- renameMap[id]->setRenamedFrom(renameMap[-id]);
- renameMap[-id]->setRenamedTo(renameMap[id]);
- }
+ renameMap[id]->setRenamedFrom(renameMap[-id]);
+ renameMap[-id]->setRenamedTo(renameMap[id]);
+ }
+ flatItemList.clear();
+ flatItemList = items;
+
// FIXME: we shouldn't really add a workspace root item here, but
// mtn automate inventory currently doesn't print the root workspace dir
InventoryItem * branch = new InventoryItem(true, true);
branch->setParent(rootItem);
branch->setPath(".");
branch->setStatus(0);
- branch->setChildren(buildTreeRecursive(tempItems, NULL));
+ branch->setChildren(buildTreeRecursive(items, NULL));
+ // remove any older item
+ rootItem->deleteAllChildren();
rootItem->appendChild(branch);
- // reset the model to repaint the view completly
- // (all QModelIndexes are discarded through that, e.g. selections!)
- reset();
+ // reset the model to repaint the view completly
+ // (all QModelIndexes are discarded through that, e.g. selections!)
+ reset();
- // restore the normal cursor
- qApp->restoreOverrideCursor();
+ // restore the normal cursor
+ qApp->restoreOverrideCursor();
emit modelCreated();
}
@@ -137,116 +143,115 @@ void Inventory::loadBranchName()
#endif
}
-QList Inventory::buildTreeRecursive(QList &items, InventoryItem* parentItem)
+QList Inventory::buildTreeRecursive(QList & items, InventoryItem * parentItem)
{
- QList finalItems;
+ QList finalItems;
- QString parentPath = "";
- int parentStatus = 0;
-
- if (parentItem != NULL)
- {
- parentPath = parentItem->getPath();
- parentStatus = parentItem->getStatus();
- }
+ QString parentPath = "";
- // add pseudo item "cd up" for each directory
+ if (parentItem != NULL)
+ {
+ parentPath = parentItem->getPath();
+ }
+
+ // add pseudo item "cd up" for each directory level
InventoryItem * cdUp = new InventoryItem(true);
cdUp->setParent(parentItem);
cdUp->setPath(parentPath + QString("/.."));
+ finalItems.append(cdUp);
- items.prepend(cdUp);
-
InventoryItem * currentItem;
- while (items.size() > 0)
- {
- currentItem = items.front();
+
+ while (items.size() > 0)
+ {
+ currentItem = items.front();
- //
- // if the item is not directly inside the current path stop immediately
- //
- QString tmpPath = parentPath;
- tmpPath.append("/");
- tmpPath.append(currentItem->getFilename());
+ //
+ // if the item is not directly inside the current path stop immediately
+ //
+ QString tmpPath = parentPath;
+ tmpPath.append("/");
+ tmpPath.append(currentItem->getFilename());
- // path may not be empty for this condition
- if (!parentPath.isEmpty() && currentItem->getPath().compare(tmpPath) != 0)
- {
- break;
- }
+ // path may not be empty for this condition
+ if (!parentPath.isEmpty() && currentItem->getPath().compare(tmpPath) != 0)
+ {
+ break;
+ }
- // remove the item if we can append it now
- items.pop_front();
+ // remove the item if we can append it now
+ items.pop_front();
- //
- // it seems to be a valid item
- //
+ //
+ // it seems to be a valid item
+ //
- // if the item is directory a directory, make sure we catch decendant items... recursion!
- if (currentItem->isDirectory() && !currentItem->isCdUp())
- {
- currentItem->setChildren(buildTreeRecursive(items, currentItem));
+ // if the item is directory a directory, catch items inside it
+ if (currentItem->isDirectory())
+ {
+ currentItem->setChildren(buildTreeRecursive(items, currentItem));
}
- // append item to final list
- finalItems.push_back(currentItem);
+ // append item to final list
+ finalItems.push_back(currentItem);
- }
- return finalItems;
+ }
+
+ return finalItems;
}
QModelIndex Inventory::index(int row, int column, const QModelIndex &parent)
const
{
- InventoryItem * parentItem;
+ InventoryItem * parentItem;
- if (!parent.isValid())
- {
- parentItem = rootItem;
- }
- else
- {
- parentItem = static_cast(parent.internalPointer());
- }
+ if (!parent.isValid())
+ {
+ parentItem = rootItem;
+ }
+ else
+ {
+ parentItem = static_cast(parent.internalPointer());
+ }
- InventoryItem * childItem = parentItem->child(row);
+ InventoryItem * childItem = parentItem->child(row);
- if (childItem)
- {
- return createIndex(row, column, childItem);
- }
+ if (childItem)
+ {
+ return createIndex(row, column, childItem);
+ }
- return QModelIndex();
+ return QModelIndex();
}
int Inventory::columnCount(const QModelIndex &parent) const
{
- if (parent.isValid())
- {
- return static_cast(parent.internalPointer())->columnCount();
- }
+ if (parent.isValid())
+ {
+ return static_cast(parent.internalPointer())->columnCount();
+ }
- return rootItem->columnCount();
+ return rootItem->columnCount();
}
QVariant Inventory::data(const QModelIndex &index, int role) const
{
- if (!index.isValid())
- {
- return QVariant();
- }
+ if (!index.isValid())
+ {
+ return QVariant();
+ }
- InventoryItem * item = static_cast(index.internalPointer());
+ InventoryItem * item = static_cast(index.internalPointer());
- if ((role == Qt::DecorationRole) && (index.column() == 0))
- {
+ if ((role == Qt::DecorationRole) && (index.column() == 0))
+ {
IconProvider * provider = IconProvider::singleton();
- return provider->getIcon(item);
- }
- else
- {
- return item->data(index.column(), role);
- }
+ return provider->getIcon(item);
+ }
+ else
+ {
+ return item->data(index.column(), role);
+ }
}
bool Inventory::setData(const QModelIndex & idx, const QVariant & value, int role)
@@ -261,7 +266,7 @@ Qt::ItemFlags Inventory::flags(const QMo
Qt::ItemFlags Inventory::flags(const QModelIndex &index) const
{
- if (!index.isValid()) return 0;
+ if (!index.isValid()) return 0;
QFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
@@ -274,10 +279,10 @@ Qt::ItemFlags Inventory::flags(const QMo
flags |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
if (item->isDirectory())
- {
- flags |= Qt::ItemIsDropEnabled;
+ {
+ flags |= Qt::ItemIsDropEnabled;
}
- */
+ */
return flags;
}
@@ -285,42 +290,42 @@ QVariant Inventory::headerData(int secti
// the root item needs to store the header for each field
QVariant Inventory::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
- {
- return rootItem->data(section, role);
- }
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ return rootItem->data(section, role);
+ }
- return QVariant();
+ return QVariant();
}
QModelIndex Inventory::parent(const QModelIndex& index) const
{
- if (!index.isValid())
- {
- return QModelIndex();
- }
+ if (!index.isValid())
+ {
+ return QModelIndex();
+ }
- InventoryItem * childItem = static_cast(index.internalPointer());
- InventoryItem * parentItem = childItem->parent();
+ InventoryItem * childItem = static_cast(index.internalPointer());
+ InventoryItem * parentItem = childItem->parent();
- if (parentItem == rootItem)
- {
- return QModelIndex();
- }
+ if (parentItem == rootItem)
+ {
+ return QModelIndex();
+ }
- return createIndex(parentItem->row(), 0, parentItem);
+ return createIndex(parentItem->row(), 0, parentItem);
}
int Inventory::rowCount(const QModelIndex& parent) const
{
- InventoryItem * parentItem = rootItem;
+ InventoryItem * parentItem = rootItem;
- if (parent.isValid())
- {
- parentItem = static_cast(parent.internalPointer());
- }
+ if (parent.isValid())
+ {
+ parentItem = static_cast(parent.internalPointer());
+ }
- return parentItem->childCount();
+ return parentItem->childCount();
}
bool Inventory::parseInventoryLine(
@@ -403,19 +408,17 @@ bool Inventory::parseInventoryLine(
// now determine if the file has been renamed
from_id = list[4].toInt();
to_id = list[5].toInt();
-
- // remove trailing slash
- path = list[6].trimmed();
+ path = list[6].trimmed();
isDirectory = false;
- if (path.endsWith('/'))
- {
- isDirectory = true;
- path = path.left(path.length() - 1);
- }
-
- // parsing was successful
- return true;
+ if (path.endsWith('/'))
+ {
+ isDirectory = true;
+ path = path.left(path.length() - 1);
+ }
+
+ // parsing was successful
+ return true;
}
bool Inventory::handleError(int errCode)
@@ -436,3 +439,107 @@ bool Inventory::handleError(int errCode)
return true;
}
+QMap Inventory::findUnaccountedRenames()
+{
+ QList missingItems;
+ QList unknownItems;
+
+ foreach (InventoryItem * item, flatItemList)
+ {
+ if (item->hasStatus(InventoryItem::Missing))
+ {
+ missingItems.append(item);
+ }
+
+ if (item->hasStatus(InventoryItem::Unknown))
+ {
+ unknownItems.append(item);
+ }
+ }
+
+ // TODO: progess bar here!
+
+ QString parentRev = MonotoneDelegate::getBaseWorkspaceRevision(this);
+ FileEntryList parentList =
+ MonotoneDelegate::getRevisionManifest(this, parentRev);
+
+ QMap unaccountedRenames;
+ QMap fileIds;
+
+ foreach (InventoryItem * missingItem, missingItems)
+ {
+ FileEntryList candidates;
+ FileEntry entry;
+
+ // this is a new entry not recorded in the base roster
+ if (missingItem->hasStatus(InventoryItem::Added) ||
+ missingItem->hasStatus(InventoryItem::RenamedTo))
+ {
+ entry.path = missingItem->getPath();
+ entry.is_dir = missingItem->isDirectory();
+ }
+ else
+ {
+ bool found = false;
+ foreach (entry, parentList)
+ {
+ if (entry.path == missingItem->getPath())
+ {
+ found = true;
+ break;
+ }
+ }
+ I(found);
+ }
+
+ foreach (InventoryItem * unknownItem, unknownItems)
+ {
+ QString unknownPath = unknownItem->getPath();
+
+ // calculate the file id of the unknown file
+ if (!unknownItem->isDirectory() &&
+ !fileIds.contains(unknownPath))
+ {
+ QString fileid = MonotoneDelegate::getFileId(this, unknownPath);
+ // file was not readable, etc.
+ if (fileid.isEmpty()) continue;
+ fileIds.insert(unknownPath, fileid);
+ }
+
+ // at first do a simple file name check
+ if ((missingItem->getFilename() == unknownItem->getFilename()) &&
+ (missingItem->isDirectory() == unknownItem->isDirectory()))
+ {
+ FileEntry can(unknownPath, unknownItem->isDirectory());
+ if (fileIds.contains(unknownPath))
+ {
+ can.fileid = fileIds.value(unknownPath);
+ }
+ candidates.append(can);
+ continue;
+ }
+
+ // we can't do anything for directories from here on
+ if (missingItem->isDirectory() || unknownItem->isDirectory())
+ continue;
+
+ // we now rely on the fact that we have a fileid
+ I(fileIds.contains(unknownPath));
+
+ if (fileIds.value(unknownPath) == entry.fileid)
+ {
+ candidates.append(FileEntry(unknownPath, false, entry.fileid));
+ }
+ }
+
+ // only add those missing items to the map which are having any
+ // candidates, i.e. are likely to be renamed
+ if (candidates.size() > 0)
+ {
+ unaccountedRenames.insert(entry, candidates);
+ }
+ }
+
+ return unaccountedRenames;
+}
+
============================================================
--- src/model/Inventory.h e9d25b6dda9dcb6b3678cb3699da93695f4ac1ca
+++ src/model/Inventory.h c9834d77c69b3b9d7c6dc35a5a8bc6118a5b01ec
@@ -37,9 +37,7 @@ class Inventory : public QAbstractItemMo
Inventory(QObject *parent);
~Inventory();
bool readInventory();
- static QString getOption(QObject *, const QString &);
- static QString getBranchName(QObject *);
- static QString getBranchNameShort(QObject *);
+ QMap findUnaccountedRenames();
// needed Qt Model methods
QVariant data(const QModelIndex&, int) const;
@@ -55,17 +53,18 @@ class Inventory : public QAbstractItemMo
private:
void parseOutput();
bool handleError(int);
- bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &);
- QList buildTreeRecursive(QList &, InventoryItem*);
-
- InventoryItem * rootItem;
- QRegExp * regex;
- MonotoneDelegate * mtnDelegate;
- QString branchName;
+ bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &);
+ QList buildTreeRecursive(QList &, InventoryItem*);
+
+ InventoryItem * rootItem;
+ QRegExp * regex;
+ MonotoneDelegate * mtnDelegate;
+ QString branchName;
+ QList flatItemList;
private slots:
void loadBranchName();
-
+
signals:
void modelCreated();
void invalidWorkspaceFormat(const QString &);
============================================================
--- src/model/Keys.cpp 9d8302773f28fa012ce8550b4afc166f5318790b
+++ src/model/Keys.cpp 4fc764b9241d0fcdd8745c96a32dc7504ed26565
@@ -77,15 +77,15 @@ void Keys::parseOutput()
if (entry.sym == "public_hash")
{
- I(entry.vals.size() == 1);
- key.public_hash = entry.vals.at(0);
+ I(!entry.hash.isNull());
+ key.public_hash = entry.hash;
continue;
}
if (entry.sym == "private_hash")
{
- I(entry.vals.size() == 1);
- key.private_hash = entry.vals.at(0);
+ I(!entry.hash.isNull());
+ key.private_hash = entry.hash;
continue;
}
============================================================
--- src/model/Manifest.cpp 2fbdb523819234d538fa763d4affeb9d81a9af6f
+++ src/model/Manifest.cpp e4700d858ea55077620324ec7c97cdb8286a876a
@@ -114,8 +114,8 @@ void Manifest::parseOutput()
if (entry.sym == "content")
{
- I(entry.vals.size() == 1);
- mEntry->hash = entry.vals.at(0);
+ I(!entry.hash.isNull());
+ mEntry->hash = entry.hash;
continue;
}
============================================================
--- src/model/Tags.cpp 031fb14119fc2c97d0eee5a32b46e0acff4fbf7b
+++ src/model/Tags.cpp f0fab0f388865ab22baea3acf6e5d98c13835b99
@@ -81,9 +81,8 @@ void Tags::parseOutput()
if (entry.sym == "revision")
{
- I(entry.vals.size() == 1);
- tag.revision = entry.vals.at(0);
- continue;
+ I(!entry.hash.isNull());
+ tag.revision = entry.hash;
}
if (entry.sym == "signer")
============================================================
--- src/monotone/FileExporter.cpp 69943651783b092d180d1b8c350f1665bed145e4
+++ src/monotone/FileExporter.cpp fe0cd260a3d9c57b64a3c0c1e2de197fe77b531f
@@ -71,9 +71,9 @@ bool FileExporter::exportFile(const File
bool FileExporter::exportFile(const FileEntry & entry)
{
- if (entry.second)
+ if (entry.is_dir)
{
- if (!entry.first.isEmpty() && !rootDir.mkpath(entry.first))
+ if (entry.path.isEmpty() || !rootDir.mkpath(entry.path))
{
C("Cannot create directory");
return false;
@@ -85,7 +85,7 @@ bool FileExporter::exportFile(const File
int commandNumber;
if (!mtn->executeCommand(
- QStringList() << "get_file_of" << entry.first,
+ QStringList() << "get_file_of" << entry.path,
QStringList() << "r" << revision, commandNumber) ||
mtn->getReturnCode(commandNumber) > 0)
{
@@ -93,7 +93,7 @@ bool FileExporter::exportFile(const File
return false;
}
- QFile file(rootDir.filePath(entry.first));
+ QFile file(rootDir.filePath(entry.path));
if (file.exists()) file.remove();
if (!file.open(QIODevice::WriteOnly))
============================================================
--- src/monotone/Monotone.cpp 8d3d1bfe62b2dcb9f94f5f7f676e85bca5b16bbd
+++ src/monotone/Monotone.cpp 21de72375ce610dba385952d2ba8edb939b42810
@@ -75,7 +75,6 @@
#include "Monotone.h"
#include "SignalWaiter.h"
#include "StdioParser.h"
-#include "Guitone.h"
#include
#include
@@ -349,26 +348,27 @@ QString Monotone::getStderr()
return stripMtnPrefix(output);
}
-bool Monotone::executeCommand(const QStringList & command, int & commandNumber)
+bool Monotone::executeCommand(const QStringList & command, int & commandNumber, bool skipEmpty)
{
- return executeCommand(command, QStringList(), commandNumber);
+ return executeCommand(command, QStringList(), commandNumber, skipEmpty);
}
-bool Monotone::executeCommand(const ByteArrayList & command, int & commandNumber)
+bool Monotone::executeCommand(const ByteArrayList & command, int & commandNumber, bool skipEmpty)
{
- return executeCommand(command, ByteArrayList(), commandNumber);
+ return executeCommand(command, ByteArrayList(), commandNumber, skipEmpty);
}
-bool Monotone::executeCommand(const QStringList & command, const QStringList & options, int & commandNumber)
+bool Monotone::executeCommand(const QStringList & command, const QStringList & options, int & commandNumber, bool skipEmpty)
{
return executeCommand(
stringToByteArrayList(command),
stringToByteArrayList(options),
- commandNumber
+ commandNumber,
+ skipEmpty
);
}
-bool Monotone::executeCommand(const ByteArrayList & command, const ByteArrayList & options, int & commandNumber)
+bool Monotone::executeCommand(const ByteArrayList & command, const ByteArrayList & options, int & commandNumber, bool skipEmpty)
{
if (doAbortRequests || process->state() != QProcess::Running)
{
@@ -376,8 +376,8 @@ bool Monotone::executeCommand(const Byte
return false;
}
- commandNumber = writeStdin(command, options);
- SignalWaiter waiter(this, SIGNAL(commandFinished()));
+ commandNumber = writeStdin(command, options, skipEmpty);
+ SignalWaiter waiter(this, SIGNAL(commandFinished(int)));
do
{
@@ -388,26 +388,27 @@ bool Monotone::executeCommand(const Byte
return !doAbortRequests;
}
-bool Monotone::triggerCommand(const QStringList & command, int & commandNumber)
+bool Monotone::triggerCommand(const QStringList & command, int & commandNumber, bool skipEmpty)
{
- return triggerCommand(command, QStringList(), commandNumber);
+ return triggerCommand(command, QStringList(), commandNumber, skipEmpty);
}
-bool Monotone::triggerCommand(const ByteArrayList & command, int & commandNumber)
+bool Monotone::triggerCommand(const ByteArrayList & command, int & commandNumber, bool skipEmpty)
{
- return triggerCommand(command, ByteArrayList(), commandNumber);
+ return triggerCommand(command, ByteArrayList(), commandNumber, skipEmpty);
}
-bool Monotone::triggerCommand(const QStringList & command, const QStringList & options, int & commandNumber)
+bool Monotone::triggerCommand(const QStringList & command, const QStringList & options, int & commandNumber, bool skipEmpty)
{
return triggerCommand(
stringToByteArrayList(command),
stringToByteArrayList(options),
- commandNumber
+ commandNumber,
+ skipEmpty
);
}
-bool Monotone::triggerCommand(const ByteArrayList & command, const ByteArrayList & options, int & commandNumber)
+bool Monotone::triggerCommand(const ByteArrayList & command, const ByteArrayList & options, int & commandNumber, bool skipEmpty)
{
if (doAbortRequests || process->state() != QProcess::Running)
{
@@ -415,11 +416,11 @@ bool Monotone::triggerCommand(const Byte
return false;
}
- commandNumber = writeStdin(command, options);
+ commandNumber = writeStdin(command, options, skipEmpty);
return true;
}
-int Monotone::writeStdin(const ByteArrayList & command, const ByteArrayList & options)
+int Monotone::writeStdin(const ByteArrayList & command, const ByteArrayList & options, bool skipEmpty)
{
QByteArray commandLine;
QTextStream streamCmdLine(&commandLine);
@@ -433,7 +434,7 @@ int Monotone::writeStdin(const ByteArray
for (int i=0, c=options.size(); i 0)
+ {
+ W(QString("%1 pending commands").arg(cnt));
+ APP->restoreOverrideCursor();
+ }
+}
bool MonotoneDelegate::triggerCommand(const QStringList & cmd)
{
@@ -41,35 +49,40 @@ bool MonotoneDelegate::triggerCommand(co
I(obj);
Monotone * mtn = MTN(obj);
- connect(
- mtn, SIGNAL(commandFinished()),
- this, SLOT(commandFinished())
- );
-
- APP->setOverrideCursor(Qt::WaitCursor);
-
- return mtn->triggerCommand(cmd, opts, commandNumber);
+ int cmdNum;
+ if (mtn->triggerCommand(cmd, opts, cmdNum))
+ {
+ if (commandNumbers.size() == 0)
+ {
+ APP->setOverrideCursor(Qt::WaitCursor);
+
+ connect(
+ mtn, SIGNAL(commandFinished(int)),
+ this, SLOT(commandFinished(int))
+ );
+ }
+
+ commandNumbers.insert(cmdNum);
+
+ return true;
+ }
+
+ return false;
}
// FIXME: if a request is aborted, commandFinished is never signalled,
// do we need to take care of that here?
-void MonotoneDelegate::commandFinished()
+void MonotoneDelegate::commandFinished(int cmdNum)
{
+ if (!commandNumbers.contains(cmdNum)) return;
+
QObject * obj = dynamic_cast(cmdModel);
I(obj);
Monotone * mtn = MTN(obj);
- // check if this is the command which is interesting for us
- if (!mtn->isCommandFinished(commandNumber)) return;
-
- disconnect(
- mtn, SIGNAL(commandFinished()),
- this, SLOT(commandFinished())
- );
-
// FIXME: does any of our models ever need the raw data?
- cmdModel->setAutomateData(mtn->getDecodedData(commandNumber));
- int returnCode = mtn->getReturnCode(commandNumber);
+ cmdModel->setAutomateData(mtn->getDecodedData(cmdNum));
+ int returnCode = mtn->getReturnCode(cmdNum);
if (returnCode == 0)
{
@@ -82,8 +95,18 @@ void MonotoneDelegate::commandFinished()
C(QString("Couldn't handle error %1").arg(returnCode));
}
}
-
- APP->restoreOverrideCursor();
+
+ commandNumbers.remove(cmdNum);
+
+ if (commandNumbers.size() == 0)
+ {
+ disconnect(
+ mtn, SIGNAL(commandFinished(int)),
+ this, SLOT(commandFinished(int))
+ );
+
+ APP->restoreOverrideCursor();
+ }
}
QString MonotoneDelegate::getBaseWorkspaceRevision(QObject * obj)
@@ -91,16 +114,24 @@ QString MonotoneDelegate::getBaseWorkspa
Monotone * mtn = MTN(obj);
int commandNumber;
- if (!mtn->executeCommand(QStringList() << "get_base_revision_id", commandNumber) ||
- mtn->getReturnCode(commandNumber) > 0)
+ bool ret = mtn->executeCommand(
+ QStringList() << "get_base_revision_id", commandNumber
+ );
+
+ QString data = mtn->getDecodedData(commandNumber);
+
+ if (!ret || mtn->getReturnCode(commandNumber) > 0)
{
- W("Could not execute get_base_revision_id");
- return false;
+ C(QString("Could not execute get_base_revision_id: %1").arg(data));
+ return QString();
}
- QString baseRevision = mtn->getDecodedData(commandNumber);
- baseRevision.chop(1);
- return baseRevision;
+ data.chop(1);
+
+ // we cannot handle workspaces with multiple parents yet
+ I(data.indexOf('\n') == -1);
+
+ return data;
}
QString MonotoneDelegate::getOption(QObject * obj, const QString & opt)
@@ -250,8 +281,15 @@ FileEntryList MonotoneDelegate::getRevis
{
Monotone * mtn = MTN(obj);
+ QStringList cmd;
+ cmd << "get_manifest_of";
+ if (!revision.isEmpty())
+ {
+ cmd << revision;
+ }
+
int cmdNum;
- mtn->executeCommand(QStringList() << "get_manifest_of" << revision, cmdNum);
+ mtn->executeCommand(cmd, cmdNum);
QString data = mtn->getDecodedData(cmdNum);
FileEntryList entries;
@@ -272,18 +310,40 @@ FileEntryList MonotoneDelegate::getRevis
StanzaList stanzas = parser.getStanzas();
foreach (Stanza st, stanzas)
{
+ FileEntry entry;
+ bool is_item = false;
+
foreach (StanzaEntry en, st)
{
- if (en.sym == "format_version") break;
-
- FileEntry entry;
- entry.first = en.vals.at(0);
- I(en.sym == "dir" || en.sym == "file");
- entry.second = en.sym == "dir";
-
- entries.append(entry);
- break;
+ if (en.sym == "format_version")
+ {
+ I(en.vals.size() == 1 && en.vals.at(0) == "1");
+ break;
+ }
+ else
+ if (en.sym == "dir" || en.sym == "file")
+ {
+ is_item = true;
+ entry.path = en.vals.at(0);
+ entry.is_dir = en.sym == "dir";
+ }
+ else
+ if (en.sym == "content")
+ {
+ I(is_item);
+ entry.fileid = en.hash;
+ }
+ else
+ if (en.sym == "attr")
+ {
+ I(is_item);
+ I(en.vals.size() == 2);
+ entry.attrs.insert(en.vals.at(0), en.vals.at(1));
+ }
+ else
+ I(false);
}
+ if (is_item) entries.append(entry);
}
return entries;
}
@@ -333,3 +393,23 @@ QStringList MonotoneDelegate::getPrivate
}
return keys;
}
+
+QString MonotoneDelegate::getFileId(QObject * obj, const QString & path)
+{
+ Monotone * mtn = MTN(obj);
+
+ int cmdNum;
+ mtn->executeCommand(QStringList() << "identify" << path, cmdNum);
+
+ QString data = mtn->getDecodedData(cmdNum);
+
+ if (mtn->getReturnCode(cmdNum) > 0)
+ {
+ C(QString("Couldn't identify path: %1").arg(data));
+ return QString();
+ }
+
+ data.chop(1);
+ return data;
+}
+
============================================================
--- src/monotone/MonotoneDelegate.h 907a718c030f96ed3c2951e5b5b1442b72b6891a
+++ src/monotone/MonotoneDelegate.h 6971e5c0d79d50fc766f84fb395cf648da80c8e5
@@ -25,6 +25,7 @@
#include "vocab.h"
#include
+#include
class MonotoneDelegate : public QObject
@@ -44,15 +45,16 @@ public:
static QStringList resolveSelector(QObject *, const QString &);
static RevisionCerts getRevisionCerts(QObject *, const QString &);
static QString getDatabaseFilePath(QObject *);
- static FileEntryList getRevisionManifest(QObject *, const QString &);
+ static FileEntryList getRevisionManifest(QObject *, const QString & rev = QString());
static QStringList getPrivateKeyList(QObject *);
+ static QString getFileId(QObject *, const QString &);
private:
AutomateCommand * cmdModel;
- int commandNumber;
+ QSet commandNumbers;
private slots:
- void commandFinished();
+ void commandFinished(int);
};
#endif
============================================================
--- src/monotone/WorkspaceCommitter.cpp 976aad817f2bb63cbb6c0220e0f30cf6bd8f6517
+++ src/monotone/WorkspaceCommitter.cpp 2c27ad30e62fd2782321ae897406b9f095ec5c53
@@ -22,6 +22,7 @@
#include "MonotoneDelegate.h"
#include "Guitone.h"
#include "BasicIOParser.h"
+#include "BasicIOWriter.h"
#include
#include
@@ -66,8 +67,8 @@ bool WorkspaceCommitter::run()
QString fullRevision = data;
- BasicIOParser parser(fullRevision);
- if (!parser.parse())
+ BasicIOParser revparser(fullRevision);
+ if (!revparser.parse())
{
C("Could not parse basic_io.");
return false;
@@ -75,7 +76,7 @@ bool WorkspaceCommitter::run()
QList > fileChanges;
- StanzaList stanzas = parser.getStanzas();
+ StanzaList stanzas = revparser.getStanzas();
foreach (Stanza st, stanzas)
{
QPair pair;
@@ -100,7 +101,8 @@ bool WorkspaceCommitter::run()
if (isPatch && en.sym == "from")
{
I(!pair.first.isEmpty());
- pair.second = en.vals.at(0);
+ I(!en.hash.isNull());
+ pair.second = en.hash;
fileChanges.append(pair);
break;
}
@@ -131,6 +133,7 @@ bool WorkspaceCommitter::run()
data.chop(1);
revisionId = data;
+ I(revisionId.length() == 40);
// attach certificates
QMapIterator i(certs);
@@ -155,9 +158,78 @@ bool WorkspaceCommitter::run()
}
D(QString("Committed revision %1").arg(revisionId));
+
+ // update _MTN/revision and _MTN/options
+ I(workspaceDir.cd("_MTN"));
+
+ return writeRevision() && writeOptions();
+}
+
+bool WorkspaceCommitter::writeRevision()
+{
+ QFile file(workspaceDir.filePath("revision"));
+ if (!file.open(QIODevice::WriteOnly))
+ {
+ C("Can't open revision for writing");
+ return false;
+ }
+
+ StanzaList stanzas;
+
+ Stanza format;
+ format.append(StanzaEntry("format_version", QStringList() << "1"));
+ stanzas.append(format);
+
+ Stanza manifest;
+ manifest.append(StanzaEntry("new_manifest",
+ "0000000000000000000000000000000000000000"));
+ stanzas.append(manifest);
+
+ Stanza oldrev;
+ oldrev.append(StanzaEntry("old_revision", revisionId));
+ stanzas.append(oldrev);
+
+ BasicIOWriter writer(stanzas);
+ QString rev = writer.write();
+ I(file.write(rev.toLatin1()));
+ file.close();
+
return true;
}
+bool WorkspaceCommitter::writeOptions()
+{
+ QFile file(workspaceDir.filePath("options"));
+ if (!file.open(QIODevice::ReadWrite))
+ {
+ C("Can't open options for reading and writing");
+ return false;
+ }
+
+ // read and parse the basic_io stanzas and set the correct branch name
+ BasicIOParser parser(QString::fromUtf8(file.readAll()));
+ I(parser.parse());
+ StanzaList stanzas = parser.getStanzas();
+ I(stanzas.size() == 1);
+
+ for (int i=0, j=stanzas[0].size(); i
#include
+#include
-class AbstractParser : public QObject
+class AbstractParser
{
- Q_OBJECT
public:
AbstractParser(const QByteArray &);
AbstractParser(const QString &);
- ~AbstractParser();
+ virtual ~AbstractParser();
virtual bool parse() = 0;
inline QByteArray getLeftBytes() const { return input; }
============================================================
--- src/util/BasicIOParser.cpp 41c485e6ae2759a0084019bf94663f20b0730949
+++ src/util/BasicIOParser.cpp f11c59b9074eee097cb97feea7f93bdd146ccb0d
@@ -51,12 +51,11 @@ Stanza BasicIOParser::getStanza()
W("Couldn't get symbol.");
}
QString hash(getHash());
+
// was this a hash?
if (!hash.isNull())
{
- // for now we do not make a distinction between normal hashes
- // and content value lists
- entry.vals.append(hash);
+ entry.hash = hash;
}
else
{
============================================================
--- src/util/BasicIOParser.h 2abdc40c948b2678ceeebf976e5424076036a83d
+++ src/util/BasicIOParser.h b96df0f29dd64ac740c6e8a7f34c080406d3837e
@@ -22,20 +22,10 @@
#define BASICIO_PARSER_H
#include "AbstractParser.h"
+#include "vocab.h"
-#include
-
-typedef struct {
- QString sym;
- QStringList vals;
-} StanzaEntry;
-
-typedef QList Stanza;
-typedef QList StanzaList;
-
class BasicIOParser : public AbstractParser
{
- Q_OBJECT
public:
BasicIOParser(const QString &);
============================================================
--- src/util/CocoaUtil.h 8424a0c7f877f0167271bb2386fd00472a32f551
+++ src/util/CocoaUtil.h 7bab17bbe14218ea6e1dc33bbca110c0cc13a248
@@ -5,7 +5,6 @@
#ifndef COCOAUTIL_H
#define COCOAUTIL_H
-#include
#include
class SUUpdater;
@@ -14,7 +13,6 @@ namespace CocoaUtil
{
void initialize();
void checkForUpdates();
- QString FSRefToPath(FSRef ref);
};
#endif
============================================================
--- src/util/CocoaUtil.mm af7058410a39e0fa1f7fdb9d1ff5019261fd0960
+++ src/util/CocoaUtil.mm 5fe5b302f4e7198a04700856bab989b3b7e2f44c
@@ -20,15 +20,3 @@ void CocoaUtil::checkForUpdates()
[updater checkForUpdates:nil];
}
-QString CocoaUtil::FSRefToPath(FSRef fsref)
-{
- CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref);
- if (!url)
- {
- return QString();
- }
- NSString * pathName = (NSString*)CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
- [pathName autorelease];
- CFRelease(url);
- return QString::fromUtf8([pathName UTF8String]);
-}
============================================================
--- src/util/DebugLog.cpp ebbb4ff026ff490147e9c81f53f3c1b23c82febf
+++ src/util/DebugLog.cpp 513339e2eb09994edeed4ad8ed3d98b9a6101c81
@@ -56,12 +56,12 @@ DebugLog::DebugLog()
QString sep;
QDate today = QDate::currentDate();
sep.fill('=', 40);
- log(Debug, sep);
- log(Debug,
+ log(Info, sep);
+ log(Info,
QString(" guitone session started (%1)")
.arg(today.toString("yyyy-MM-dd"))
);
- log(Debug, sep);
+ log(Info, sep);
}
DebugLog::~DebugLog()
@@ -89,13 +89,15 @@ void DebugLog::log(Type t, QString msg)
if (logLevel == Fatal && t > Fatal) return;
if (logLevel == Critical && t > Critical) return;
if (logLevel == Warn && t > Warn) return;
+ if (logLevel == Info && t > Info) return;
if (logLevel == Debug && t > Debug) return;
QMap errors;
errors[0] = "fatal";
errors[1] = "critical";
errors[2] = "warning";
- errors[3] = "debug";
+ errors[3] = "info";
+ errors[4] = "debug";
// do not use I here since this calls qFatal and results in
// an endless loop
@@ -131,11 +133,20 @@ void DebugLog::closeLogfile()
if (logFile.isOpen()) logFile.close();
}
+#ifndef QT_NO_DEBUG
void DebugLog::debug(QString msg)
{
singleton()->log(Debug, msg);
}
+#else
+void DebugLog::debug(QString msg) { Q_UNUSED(msg); }
+#endif
+void DebugLog::info(QString msg)
+{
+ singleton()->log(Info, msg);
+}
+
void DebugLog::warn(QString msg)
{
singleton()->log(Warn, msg);
============================================================
--- src/util/DebugLog.h 15ae25ff50330f9a76933a81f344875cd72554d8
+++ src/util/DebugLog.h 94f2cac41d2381a8ab113dc5adb29131844beaf4
@@ -30,7 +30,7 @@ public:
// higher levels include lower ones, i.e.
// 'warn' also prints out 'critical' and 'fatal';
// 'debug' prints out everything we have
- enum Level {Fatal = 1, Critical, Warn, Debug };
+ enum Level {Fatal = 1, Critical, Warn, Info, Debug };
// as we have different Levels each level's name is also
// the name for a specific log type
typedef Level Type;
@@ -45,6 +45,7 @@ public:
static QString logFilePath();
+ static void info(QString);
static void debug(QString);
inline static void debug(const char * msg) { return debug(QString::fromUtf8(msg)); }
static void warn(QString);
============================================================
--- src/util/DiffParser.cpp e98a52a2c7bee64b13376f3cf71b47629a213a7d
+++ src/util/DiffParser.cpp 4a0256295820e64a621bee2c82c34357ac711b91
@@ -37,7 +37,11 @@ void DiffParser::parse(const QString & i
void DiffParser::parse(const QString & input)
{
- if (input.length() == 0) return;
+ if (input.length() == 0)
+ {
+ W("Nothing to parse");
+ return;
+ }
// remove last newline character
QString in(input);
@@ -139,9 +143,9 @@ void DiffParser::parse(const QString & i
}
}
-Diff* DiffParser::getDiff(const QString & filename)
+Diff * DiffParser::getDiff(const QString & filename)
{
- I(fileDiffs.contains(filename));
+ if (!fileDiffs.contains(filename)) return 0;
return fileDiffs.value(filename);
}
============================================================
--- src/util/StdioParser.h 8488d8ccabd54eef76d773c0ccaf91ac47d95d3d
+++ src/util/StdioParser.h ed0742f484843b709c946a2c030cca934432335d
@@ -25,7 +25,6 @@ class StdioParser : public AbstractParse
class StdioParser : public AbstractParser
{
- Q_OBJECT
public:
StdioParser(const QByteArray &);
============================================================
--- src/view/AttributesView.cpp 622bd8e562abeaa8299ecac4d3a725a0f8eb73ae
+++ src/view/AttributesView.cpp e4b0b75ba4c9abd6fa8efa2c6def8a4f2ae6dc96
@@ -18,14 +18,195 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
+#include "vocab.h"
#include "AttributesView.h"
+#include "AddEditAttribute.h"
+#include "Guitone.h"
-AttributesView::AttributesView(QWidget* parent)
-: TreeView(parent)
+#include
+
+// FIXME: this dependency is ugly, we should probably use
+// Qt's row editing, but this is ugly (and complex)
+// to implement as well
+#include "GetAttributes.h"
+
+#include
+
+AttributesView::AttributesView(QWidget * parent) : TreeView(parent)
{
setRootIsDecorated(false);
setItemsExpandable(false);
+
+ QHeaderView * headerView = header();
+ headerView->setClickable(true);
+ headerView->setResizeMode(QHeaderView::ResizeToContents);
+
+ connect(
+ headerView, SIGNAL(sectionClicked(int)),
+ this, SLOT(sectionClicked(int))
+ );
+
+ connect(
+ this, SIGNAL(contextMenuRequested(const QModelIndexList &, const QPoint &)),
+ this, SLOT(menuRequested(const QModelIndexList &, const QPoint &))
+ );
+
+ connect(
+ this, SIGNAL(doubleClicked(const QModelIndex &)),
+ this, SLOT(aboutToEditAttribute(const QModelIndex &))
+ );
}
AttributesView::~AttributesView() {}
+void AttributesView::sectionClicked(int logicalIndex)
+{
+ // do not popup the menu if we haven't focused a valid item
+ GetAttributes * attrModel = qobject_cast(model());
+ if (attrModel->currentPath().isEmpty()) return;
+
+ if (logicalIndex != 0) return;
+
+ QHeaderView * headerView = header();
+ int visualIndex = headerView->visualIndex(logicalIndex);
+
+ int left = 0;
+ int top = headerView->height();
+
+ if (visualIndex > 0)
+ {
+ for (int i=0; isectionSize(headerView->logicalIndex(i));
+ }
+ }
+
+ if (left > headerView->width())
+ {
+ left = headerView->width() - headerView->sectionSize(logicalIndex);
+ }
+
+ QMenu popupMenu(this);
+ popupMenu.addAction(
+ tr("add new attribute"), this, SLOT(addAttribute())
+ );
+ popupMenu.exec(mapToGlobal(QPoint(left, top)));
+}
+
+void AttributesView::menuRequested(const QModelIndexList & indexes, const QPoint & pt)
+{
+ if (indexes.size() != 1) return;
+
+ selectedIndex = indexes.at(0);
+
+ QMenu popupMenu(this);
+
+ QFont activeFont;
+ activeFont.setBold(true);
+
+ popupMenu.addAction(
+ tr("edit attribute"), this, SLOT(editAttribute())
+ )->setFont(activeFont);
+
+ popupMenu.addAction(
+ tr("drop attribute"), this, SLOT(dropAttribute())
+ );
+
+ popupMenu.exec(pt);
+}
+
+void AttributesView::addAttribute()
+{
+ AddEditAttribute dlg(this);
+
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ GetAttributes * attrModel = qobject_cast(model());
+
+ QStringList cmd;
+ cmd << "set_attribute";
+ cmd << attrModel->currentPath();
+ cmd << dlg.getKey() << dlg.getValue();
+
+ Monotone * mtn = MTN(this);
+ int cmdNum = 0;
+ // do not skip empty parameters
+ mtn->executeCommand(cmd, cmdNum, false);
+
+ if (mtn->getReturnCode(cmdNum) > 0)
+ {
+ C(QString("Couldn't set attribute"));
+ return;
+ }
+
+ attrModel->readAttributes(attrModel->currentPath());
+ }
+}
+
+void AttributesView::aboutToEditAttribute(const QModelIndex & index)
+{
+ if (!index.isValid()) return;
+ selectedIndex = index;
+ editAttribute();
+}
+
+void AttributesView::editAttribute()
+{
+ QModelIndex keyIdx = model()->index(selectedIndex.row(), 1, QModelIndex());
+ QModelIndex valIdx = model()->index(selectedIndex.row(), 2, QModelIndex());
+
+ // UserRole is overloaded with the actual string value of the
+ // key / value pair
+ QString key = model()->data(keyIdx, Qt::UserRole).toString();
+ QString val = model()->data(valIdx, Qt::UserRole).toString();
+
+ AddEditAttribute dlg(this, key, val);
+
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ GetAttributes * attrModel = qobject_cast(model());
+
+ QStringList cmd;
+ cmd << "set_attribute";
+ cmd << attrModel->currentPath();
+ cmd << dlg.getKey() << dlg.getValue();
+
+ Monotone * mtn = MTN(this);
+ int cmdNum = 0;
+ // do not skip empty parameters
+ mtn->executeCommand(cmd, cmdNum, false);
+
+ if (mtn->getReturnCode(cmdNum) > 0)
+ {
+ C(QString("Couldn't set attribute"));
+ return;
+ }
+
+ attrModel->readAttributes(attrModel->currentPath());
+ }
+}
+
+void AttributesView::dropAttribute()
+{
+ GetAttributes * attrModel = qobject_cast(model());
+ QModelIndex keyIdx = attrModel->index(selectedIndex.row(), 1, QModelIndex());
+
+ QStringList cmd;
+ cmd << "drop_attribute";
+ cmd << attrModel->currentPath();
+ cmd << model()->data(keyIdx, Qt::UserRole).toString(); // the key
+
+ Monotone * mtn = MTN(this);
+ int cmdNum = 0;
+ // do not skip empty parameters
+ mtn->executeCommand(cmd, cmdNum, false);
+
+ if (mtn->getReturnCode(cmdNum) > 0)
+ {
+ C(QString("Couldn't drop attribute"));
+ return;
+ }
+
+ attrModel->readAttributes(attrModel->currentPath());
+}
+
============================================================
--- src/view/AttributesView.h 6331d48f84cd19a9389ea33efd8e11278867b3eb
+++ src/view/AttributesView.h 919369059dde5efb8b1e8b0c0d1e25e49dfc6825
@@ -22,6 +22,7 @@
#define ATTRIBUTES_VIEW_H
#include "TreeView.h"
+#include
class AttributesView : public TreeView
{
@@ -30,6 +31,19 @@ public:
public:
AttributesView(QWidget*);
~AttributesView();
+
+private slots:
+ void sectionClicked(int);
+ void menuRequested(const QModelIndexList &, const QPoint &);
+
+ void addAttribute();
+ void editAttribute();
+ void aboutToEditAttribute(const QModelIndex &);
+ void dropAttribute();
+
+private:
+ QModelIndex selectedIndex;
+ QString path;
};
#endif
============================================================
--- src/view/InventoryView.cpp bb051af5d94c6e995a1b33658b5d529b87b2df95
+++ src/view/InventoryView.cpp 441ff9c1a21693b33b00efe2894d0f50425f6b3c
@@ -24,6 +24,7 @@
#include "InventoryItem.h"
#include "Monotone.h"
#include "FileDiff.h"
+#include "FileHistory.h"
#include "RevisionDiff.h"
#include "vocab.h"
#include "Guitone.h"
@@ -88,17 +89,93 @@ void InventoryView::setType(Type t)
}
}
+void InventoryView::createAndConnectContextActions()
+{
+ actChdir = new QAction(tr("Go into"), this);
+ actChdir->setStatusTip(tr("Go into the directory"));
+ connect(actChdir, SIGNAL(triggered()), this, SLOT(slotChdir()));
+
+ actOpen = new QAction(tr("Open"), this);
+ actOpen->setStatusTip(tr("Open in default program"));
+ connect(actOpen, SIGNAL(triggered()), this, SLOT(slotOpen()));
+
+ actAdd = new QAction(tr("Add"), this);
+ actAdd->setStatusTip(tr("Add to workspace"));
+ connect(actAdd, SIGNAL(triggered()), this, SLOT(slotAdd()));
+
+ actRemove = new QAction(tr("Remove"), this);
+ actRemove->setStatusTip(tr("Remove from workspace"));
+ connect(actRemove, SIGNAL(triggered()), this, SLOT(slotRemove()));
+
+ actCommit = new QAction(tr("Commit"), this);
+ actCommit->setStatusTip(tr("Commit"));
+ connect(actCommit, SIGNAL(triggered()), this, SLOT(slotCommit()));
+
+ actIgnore = new QAction(tr("Ignore"), this);
+ actIgnore->setStatusTip(tr("Ignore file"));
+ connect(actIgnore, SIGNAL(triggered()), this, SLOT(slotIgnore()));
+
+ actUnignore = new QAction(tr("Unignore"), this);
+ actUnignore->setStatusTip(tr("Unignore file"));
+ connect(actUnignore, SIGNAL(triggered()), this, SLOT(slotUnignore()));
+
+ actRevert = new QAction(tr("Revert"), this);
+ actRevert->setStatusTip(tr("Revert uncommitted changes"));
+ connect(actRevert, SIGNAL(triggered()), this, SLOT(slotRevert()));
+
+ actFileDiff = new QAction(tr("Diff"), this);
+ actFileDiff->setStatusTip(tr("Diff against base revision"));
+ connect(actFileDiff, SIGNAL(triggered()), this, SLOT(slotFileDiff()));
+
+ actFileHistory = new QAction(tr("History"), this);
+ actFileHistory->setStatusTip(tr("Display the history of this file"));
+ connect(actFileHistory, SIGNAL(triggered()), this, SLOT(slotFileHistory()));
+
+ actRevisionDiff = new QAction(tr("Diff all"), this);
+ actRevisionDiff->setStatusTip(tr("Show all differences"));
+ connect(actRevisionDiff, SIGNAL(triggered()), this, SLOT(slotRevisionDiff()));
+
+ actRename = new QAction(tr("Rename"), this);
+ actRename->setStatusTip(tr("Rename file"));
+ connect(actRename, SIGNAL(triggered()), this, SLOT(slotRename()));
+
+ actAddMultiple = new QAction(tr("Add %1 items"), this);
+ actAddMultiple->setStatusTip(tr("Add multiple items"));
+ connect(actAddMultiple, SIGNAL(triggered()), this, SLOT(slotAdd()));
+
+ actRemoveMultiple = new QAction(tr("Remove %1 items"), this);
+ actRemoveMultiple->setStatusTip(tr("Remove multiple items"));
+ connect(actRemoveMultiple, SIGNAL(triggered()), this, SLOT(slotRemove()));
+
+ actCommitMultiple = new QAction(tr("Commit %1 items"), this);
+ actCommitMultiple->setStatusTip(tr("Commit multiple items"));
+ connect(actCommitMultiple, SIGNAL(triggered()), this, SLOT(slotCommit()));
+
+ actIgnoreMultiple = new QAction(tr("Ignore %1 items"), this);
+ actIgnoreMultiple->setStatusTip(tr("Ignore multiple items"));
+ connect(actIgnoreMultiple, SIGNAL(triggered()), this, SLOT(slotIgnore()));
+
+ actUnignoreMultiple = new QAction(tr("Unignore %1 items"), this);
+ actUnignoreMultiple->setStatusTip(tr("Unignore multiple items"));
+ connect(actUnignoreMultiple, SIGNAL(triggered()), this, SLOT(slotUnignore()));
+
+ actRevertMultiple = new QAction(tr("Revert %1 items"), this);
+ actRevertMultiple->setStatusTip(tr("Revert multiple items"));
+ connect(actRevertMultiple, SIGNAL(triggered()), this, SLOT(slotRevert()));
+}
+
InventoryView::~InventoryView()
{
delete actChdir;
- delete actOpen;
- delete actAdd;
- delete actRemove;
- delete actCommit;
- delete actIgnore;
- delete actUnignore;
+ delete actOpen;
+ delete actAdd;
+ delete actRemove;
+ delete actCommit;
+ delete actIgnore;
+ delete actUnignore;
delete actRevert;
- delete actFileDiff;
+ delete actFileDiff;
+ delete actFileHistory;
delete actRevisionDiff;
delete actRename;
delete actAddMultiple;
@@ -217,6 +294,12 @@ void InventoryView::slotContextMenuReque
menu.addAction(actFileDiff);
}
}
+
+ if (item->hasNotStatus(InventoryItem::Added) &&
+ !item->isDirectory())
+ {
+ menu.addAction(actFileHistory);
+ }
}
//
@@ -332,77 +415,6 @@ void InventoryView::slotContextMenuReque
}
}
-void InventoryView::createAndConnectContextActions(void)
-{
- actChdir = new QAction(tr("Go into"), this);
- actChdir->setStatusTip(tr("Go into the directory"));
- connect(actChdir, SIGNAL(triggered()), this, SLOT(slotChdir()));
-
- actOpen = new QAction(tr("Open"), this);
- actOpen->setStatusTip(tr("Open in default program"));
- connect(actOpen, SIGNAL(triggered()), this, SLOT(slotOpen()));
-
- actAdd = new QAction(tr("Add"), this);
- actAdd->setStatusTip(tr("Add to workspace"));
- connect(actAdd, SIGNAL(triggered()), this, SLOT(slotAdd()));
-
- actRemove = new QAction(tr("Remove"), this);
- actRemove->setStatusTip(tr("Remove from workspace"));
- connect(actRemove, SIGNAL(triggered()), this, SLOT(slotRemove()));
-
- actCommit = new QAction(tr("Commit"), this);
- actCommit->setStatusTip(tr("Commit"));
- connect(actCommit, SIGNAL(triggered()), this, SLOT(slotCommit()));
-
- actIgnore = new QAction(tr("Ignore"), this);
- actIgnore->setStatusTip(tr("Ignore file"));
- connect(actIgnore, SIGNAL(triggered()), this, SLOT(slotIgnore()));
-
- actUnignore = new QAction(tr("Unignore"), this);
- actUnignore->setStatusTip(tr("Unignore file"));
- connect(actUnignore, SIGNAL(triggered()), this, SLOT(slotUnignore()));
-
- actRevert = new QAction(tr("Revert"), this);
- actRevert->setStatusTip(tr("Revert uncommitted changes"));
- connect(actRevert, SIGNAL(triggered()), this, SLOT(slotRevert()));
-
- actFileDiff = new QAction(tr("Diff"), this);
- actFileDiff->setStatusTip(tr("Diff against base revision"));
- connect(actFileDiff, SIGNAL(triggered()), this, SLOT(slotFileDiff()));
-
- actRevisionDiff = new QAction(tr("Diff all"), this);
- actRevisionDiff->setStatusTip(tr("Show all differences"));
- connect(actRevisionDiff, SIGNAL(triggered()), this, SLOT(slotRevisionDiff()));
-
- actRename = new QAction(tr("Rename"), this);
- actRename->setStatusTip(tr("Rename file"));
- connect(actRename, SIGNAL(triggered()), this, SLOT(slotRename()));
-
- actAddMultiple = new QAction(tr("Add %1 items"), this);
- actAddMultiple->setStatusTip(tr("Add multiple items"));
- connect(actAddMultiple, SIGNAL(triggered()), this, SLOT(slotAdd()));
-
- actRemoveMultiple = new QAction(tr("Remove %1 items"), this);
- actRemoveMultiple->setStatusTip(tr("Remove multiple items"));
- connect(actRemoveMultiple, SIGNAL(triggered()), this, SLOT(slotRemove()));
-
- actCommitMultiple = new QAction(tr("Commit %1 items"), this);
- actCommitMultiple->setStatusTip(tr("Commit multiple items"));
- connect(actCommitMultiple, SIGNAL(triggered()), this, SLOT(slotCommit()));
-
- actIgnoreMultiple = new QAction(tr("Ignore %1 items"), this);
- actIgnoreMultiple->setStatusTip(tr("Ignore multiple items"));
- connect(actIgnoreMultiple, SIGNAL(triggered()), this, SLOT(slotIgnore()));
-
- actUnignoreMultiple = new QAction(tr("Unignore %1 items"), this);
- actUnignoreMultiple->setStatusTip(tr("Unignore multiple items"));
- connect(actUnignoreMultiple, SIGNAL(triggered()), this, SLOT(slotUnignore()));
-
- actRevertMultiple = new QAction(tr("Revert %1 items"), this);
- actRevertMultiple->setStatusTip(tr("Revert multiple items"));
- connect(actRevertMultiple, SIGNAL(triggered()), this, SLOT(slotRevert()));
-}
-
void InventoryView::slotChdir()
{
QItemSelectionModel *selectionModel = this->selectionModel();
@@ -448,7 +460,7 @@ void InventoryView::changeDirectory(cons
);
}
-void InventoryView::slotOpen(void)
+void InventoryView::slotOpen()
{
QModelIndex index(getSingleSelection());
if (!index.isValid()) return;
@@ -480,27 +492,27 @@ void InventoryView::slotOpen(void)
}
}
-void InventoryView::slotAdd(void)
+void InventoryView::slotAdd()
{
C("Not implemented.");
}
-void InventoryView::slotRemove(void)
+void InventoryView::slotRemove()
{
C("Not implemented.");
}
-void InventoryView::slotCommit(void)
+void InventoryView::slotCommit()
{
C("Not implemented.");
}
-void InventoryView::slotRevert(void)
+void InventoryView::slotRevert()
{
C("Not implemented.");
}
-void InventoryView::slotRename(void)
+void InventoryView::slotRename()
{
C("Not implemented.");
}
@@ -511,17 +523,17 @@ void InventoryView::slotRename(void)
// Q: If this file is under version control, should a change
// be committed in the background?
//
-void InventoryView::slotIgnore(void)
+void InventoryView::slotIgnore()
{
C("Not implemented.");
}
-void InventoryView::slotUnignore(void)
+void InventoryView::slotUnignore()
{
C("Not implemented.");
}
-void InventoryView::slotFileDiff(void)
+void InventoryView::slotFileDiff()
{
QModelIndex index(getSingleSelection());
if (!index.isValid()) return;
@@ -540,13 +552,41 @@ void InventoryView::slotFileDiff(void)
clearSelection();
}
-void InventoryView::slotRevisionDiff(void)
+void InventoryView::slotFileHistory()
{
QModelIndex index(getSingleSelection());
if (!index.isValid()) return;
InventoryItem * item = static_cast(index.internalPointer());
+ QString path = item->getPath();
+ // determine the original path, if needed
+ if (item->hasStatus(InventoryItem::RenamedFrom))
+ {
+ InventoryItem * newItem = item->getRenamedTo();
+ I(newItem);
+ path = newItem->getPath();
+ }
+ if (item->hasStatus(InventoryItem::RenamedTo))
+ {
+ InventoryItem * oldItem = item->getRenamedFrom();
+ I(oldItem);
+ path = oldItem->getPath();
+ }
+
+ FileHistory dlg(this, path);
+ dlg.exec();
+
+ clearSelection();
+}
+
+void InventoryView::slotRevisionDiff()
+{
+ QModelIndex index(getSingleSelection());
+ if (!index.isValid()) return;
+
+ InventoryItem * item = static_cast(index.internalPointer());
+
RevisionDiff dlg(this);
dlg.init(item->getPath(), QString(), QString());
dlg.exec();
============================================================
--- src/view/InventoryView.h 0ad2ef23cc24538fcd3fd656b712f7b6dca17e3e
+++ src/view/InventoryView.h fd0d9c5eb87a628057d22b2a41426dcbbe84ca03
@@ -31,15 +31,15 @@ class InventoryView : public TreeView
class InventoryView : public TreeView
{
- Q_OBJECT
+ Q_OBJECT
public:
- enum Type { FolderTree, FileList };
-
- InventoryView(QWidget*);
- ~InventoryView();
+ enum Type { FolderTree, FileList };
+
+ InventoryView(QWidget*);
+ ~InventoryView();
- void setModel(QSortFilterProxyModel *);
+ void setModel(QSortFilterProxyModel *);
void setType(Type);
signals:
@@ -47,20 +47,21 @@ private:
private:
void setModel(QAbstractItemModel *);
- void createAndConnectContextActions(void);
- void closeEvent(void);
+ void createAndConnectContextActions();
+ void closeEvent();
QModelIndex getSingleSelection(bool mapToSource = true);
QAction * actChdir;
QAction * actOpen;
- QAction * actAdd;
- QAction * actRemove;
- QAction * actCommit;
- QAction * actIgnore;
+ QAction * actAdd;
+ QAction * actRemove;
+ QAction * actCommit;
+ QAction * actIgnore;
QAction * actUnignore;
QAction * actRevert;
QAction * actRename;
QAction * actFileDiff;
+ QAction * actFileHistory;
QAction * actRevisionDiff;
QAction * actAddMultiple;
@@ -82,17 +83,18 @@ private slots:
void itemClicked(const QModelIndex & index);
void slotContextMenuRequested(const QModelIndexList &, const QPoint &);
- void slotChdir(void);
- void slotOpen(void);
- void slotAdd(void);
- void slotRemove(void);
- void slotCommit(void);
- void slotIgnore(void);
- void slotUnignore(void);
- void slotRevert(void);
- void slotRename(void);
- void slotFileDiff(void);
- void slotRevisionDiff(void);
+ void slotChdir();
+ void slotOpen();
+ void slotAdd();
+ void slotRemove();
+ void slotCommit();
+ void slotIgnore();
+ void slotUnignore();
+ void slotRevert();
+ void slotRename();
+ void slotFileDiff();
+ void slotFileHistory();
+ void slotRevisionDiff();
};
#endif
============================================================
--- src/view/MainWindow.cpp c485c30152a7f43a73d96c33e13054771c5f0250
+++ src/view/MainWindow.cpp c4b6cf1f128df54b18ba2a5b8ca2a2fddc0231aa
@@ -22,7 +22,7 @@
#include "Monotone.h"
#include "Inventory.h"
#include "InventoryItem.h"
-#include "Attributes.h"
+#include "GetAttributes.h"
#include "InventoryProxyModel.h"
#include "Splitter.h"
#include "InventoryView.h"
@@ -30,7 +30,6 @@
#include "UpdateWorkspace.h"
#include "CommitRevision.h"
#include "CheckoutRevision.h"
-#include "ApplicationUpdate.h"
#include "Platform.h"
#include "Preferences.h"
#include "KeyManagement.h"
@@ -38,10 +37,13 @@
#include "Settings.h"
#include "ChangesetBrowser.h"
#include "WorkspaceCreator.h"
+#include "UnaccountedRenames.h"
#include "Guitone.h"
#ifdef Q_WS_MAC
#include "CocoaUtil.h"
+#else
+#include "ApplicationUpdate.h"
#endif
#include
@@ -55,8 +57,6 @@ MainWindow::MainWindow()
MainWindow::MainWindow()
: QMainWindow(), closeCounter(0)
{
- setAttribute(Qt::WA_MacMetalStyle);
-
// ensure that the shortcut keys are properly recognized by linguist
QShortcut::tr("Ctrl");
QShortcut::tr("Alt");
@@ -67,7 +67,7 @@ MainWindow::MainWindow()
// create the main models
invModel = new Inventory(this);
- attrModel = new Attributes(this);
+ attrModel = new GetAttributes(this);
connect(
invModel, SIGNAL(invalidWorkspaceFormat(const QString &)),
@@ -90,11 +90,11 @@ MainWindow::MainWindow()
// query attributes on click
connect(
treeView, SIGNAL(clicked(const QModelIndex &)),
- attrModel, SLOT(readAttributes(const QModelIndex &))
+ this, SLOT(readAttributes(const QModelIndex &))
);
connect(
listView, SIGNAL(clicked(const QModelIndex &)),
- attrModel, SLOT(readAttributes(const QModelIndex &))
+ this, SLOT(readAttributes(const QModelIndex &))
);
// filelist/tree synchronization
@@ -192,12 +192,7 @@ bool MainWindow::doLoadWorkspace(QString
if (!invModel->readInventory())
{
- QMessageBox::information(
- this,
- tr("Unable to execute command"),
- tr("Unable to execute '%1' - maybe another command is still running?").arg("inventory"),
- QMessageBox::Ok
- );
+ C("Could not read inventory");
return false;
}
@@ -210,24 +205,6 @@ bool MainWindow::doLoadWorkspace(QString
return true;
}
-void MainWindow::closeEvent(QCloseEvent * event)
-{
- I(closeCounter >= 0);
-
- // ignore the close event if there are still open dialog windows
- if (closeCounter > 0)
- {
- D(QString("Ignoring close request for %1").arg((int)this));
- event->ignore();
- return;
- }
-
- // the last closed window sets the geometry for the next one which is opened
- Settings::setWindowGeometry(saveGeometry(), "MainWindow_mode" + mode);
- event->accept();
- emit windowClosed(this);
-}
-
void MainWindow::on_actionOpen_Database_triggered()
{
QString fn = QFileDialog::getOpenFileName(
@@ -556,7 +533,10 @@ void MainWindow::on_actionCommit_revisio
void MainWindow::on_actionCommit_revision_triggered()
{
CommitRevision dialog(this);
- dialog.execDocumentModal();
+ if (dialog.execDocumentModal() == QDialog::Accepted)
+ {
+ on_actionReload_workspace_triggered();
+ }
}
void MainWindow::on_actionCheckout_revision_triggered()
@@ -658,3 +638,67 @@ void MainWindow::disableClosing()
closeCounter++;
}
+void MainWindow::closeEvent(QCloseEvent * event)
+{
+ I(closeCounter >= 0);
+
+ // ignore the close event if there are still open dialog windows
+ if (closeCounter > 0)
+ {
+ D(QString("Ignoring close request for %1").arg((int)this));
+ event->ignore();
+ return;
+ }
+
+ // the last closed window sets the geometry for the next one which is opened
+ Settings::setWindowGeometry(saveGeometry(), "MainWindow_mode" + mode);
+ event->accept();
+ emit windowClosed(this);
+}
+
+void MainWindow::on_actionReload_workspace_triggered()
+{
+ bool ret = invModel->readInventory();
+ if (!ret) C("Could not read inventory.");
+}
+
+void MainWindow::on_actionFind_unaccounted_renames_triggered()
+{
+ QMap renames = invModel->findUnaccountedRenames();
+ if (renames.size() == 0)
+ {
+ QMessageBox::information(
+ this,
+ tr("Nothing found"),
+ tr("No unaccounted renames found for this workspace."),
+ QMessageBox::Ok
+ );
+ return;
+ }
+
+ UnaccountedRenames dlg(this, renames);
+ dlg.execDocumentModal();
+}
+
+void MainWindow::readAttributes(const QModelIndex & index)
+{
+ QModelIndex sourceIndex = static_cast(index.model())->mapToSource(index);
+ InventoryItem * item = static_cast(sourceIndex.internalPointer());
+
+ if (item->isRootDirectory() || item->isCdUp())
+ {
+ D("item is pseudo item (root or cdup)");
+ attrModel->revert();
+ return;
+ }
+
+ if (item->hasStatus(InventoryItem::Dropped) || !item->isTracked())
+ {
+ D("item is not tracked or dropped");
+ attrModel->revert();
+ return;
+ }
+
+ attrModel->readAttributes(item->getPath());
+}
+
============================================================
--- src/view/MainWindow.h 7ea22986b3ba2005db4b604c592a91365ba78122
+++ src/view/MainWindow.h 49a87144c3f5ef2d99f1ca0acc4acb11e21a402c
@@ -26,7 +26,7 @@ class Inventory;
class QModelIndex;
class Inventory;
-class Attributes;
+class GetAttributes;
class InventoryProxyModel;
class InventoryView;
class AttributesView;
@@ -55,8 +55,8 @@ signals:
void quitApplication();
void updatePreviousWorkspacesMenu();
void updatePreviousDatabasesMenu();
- void loadDatabase(const QString &);
- void loadWorkspace(const QString &);
+ void loadDatabase(const QString &);
+ void loadWorkspace(const QString &);
private slots:
void on_actionOpen_Workspace_triggered();
@@ -74,21 +74,24 @@ private slots:
void on_actionChangeset_browser_triggered();
void on_actionBring_all_to_front_triggered();
void on_actionCheck_for_updates_triggered();
+ void on_actionReload_workspace_triggered();
+ void on_actionFind_unaccounted_renames_triggered();
void openRecentWorkspace();
void openRecentDatabase();
void updateWindowList();
void activateOtherWindow();
void invalidWorkspaceFormat(const QString &);
+ void readAttributes(const QModelIndex &);
private:
void closeEvent(QCloseEvent *);
void switchMode(Mode);
- Inventory *invModel;
- Attributes *attrModel;
- InventoryProxyModel *proxyModelFolderTree;
- InventoryProxyModel *proxyModelFileList;
+ Inventory * invModel;
+ GetAttributes * attrModel;
+ InventoryProxyModel * proxyModelFolderTree;
+ InventoryProxyModel * proxyModelFileList;
Mode mode;
int closeCounter;
};
============================================================
--- src/view/TreeView.cpp 5b4f86399682825dd48a24084136979464fd5c69
+++ src/view/TreeView.cpp 3707234163333d91629f88fd1c84576610f942f0
@@ -73,7 +73,7 @@ void TreeView::contextMenuEvent(QContext
);
setSelectionModel(selection);
- // remove multiple cols of the same column, since we're selected
+ // remove multiple cols of the same row, since we're selected
// complete rows we're not interested in these
QModelIndexList currentSelection = selection->selectedIndexes();
for (int i=0; i < currentSelection.size(); i++)
@@ -85,6 +85,21 @@ void TreeView::contextMenuEvent(QContext
}
}
- emit contextMenuRequested(currentSelection, mapToGlobal(ev->pos()));
+ QPoint pos = ev->pos();
+
+ QHeaderView * headerView = header();
+ if (headerView)
+ {
+ if (headerView->orientation() == Qt::Horizontal)
+ {
+ pos += QPoint(0, headerView->height());
+ }
+ else
+ {
+ pos += QPoint(headerView->width(), 0);
+ }
+ }
+
+ emit contextMenuRequested(currentSelection, mapToGlobal(pos));
}
============================================================
--- src/view/dialogs/ApplicationUpdate.cpp 7c2ecb7a44dcd75bd5d53e94efe173113df90547
+++ src/view/dialogs/ApplicationUpdate.cpp 5b6a164ff54ef8fabc95a3769c2b3c1fd2a878b3
@@ -39,7 +39,7 @@ ApplicationUpdate::ApplicationUpdate(QWi
QHttp http;
http.setHost("guitone.thomaskeller.biz");
- http.get("/appcast.xml");
+ http.get("/web/appcast.xml");
SignalWaiter waiter(&http, SIGNAL(done(bool)));
============================================================
--- src/view/dialogs/CommitRevision.cpp ffe771b0f74202063686e3d30bcff6f6b9d0636a
+++ src/view/dialogs/CommitRevision.cpp bba8d6475e14a2003cad91417b9bdd901ac15f55
@@ -174,24 +174,19 @@ void CommitRevision::accept()
if (!committer.run())
{
- C("Could not commit revision.");
+ QMessageBox::critical(
+ this,
+ tr("Could not commit revision"),
+ tr("Unable to commit the revision - this may be a bug in guitone "
+ "or a bug in monotone itself. Please take a closer look at the "
+ "logs and optionally send in a bug report."),
+ QMessageBox::Ok
+ );
+ done(0);
return;
}
- QMessageBox::information(
- this,
- tr("Revision committed"),
- tr("The revision was successfully committed, however "
- "since there is no update command available over automate yet, "
- "your workspace cannot be updated automatically.\n"
- "You need to do this by hand or check out the new revision:\n\n"
- "\t%1\n\n"
- "In any case close the current view to avoid committing the "
- "same revision again.").arg(committer.getRevisionId()),
- QMessageBox::Ok
- );
-
- done(0);
+ done(1);
}
void CommitRevision::revisionRead()
============================================================
--- src/view/dialogs/Dialog.cpp 926c311a65c0a2eeea6cae03256a56b8adbb7087
+++ src/view/dialogs/Dialog.cpp 4a9329c127de291fc2e8c236ee2ae3f97344bdfa
@@ -28,14 +28,12 @@ Dialog::Dialog(QWidget * parent) : QDial
Dialog::Dialog(QWidget * parent) : QDialog(parent)
{
- setAttribute(Qt::WA_MacMetalStyle);
}
Dialog::Dialog(QWidget * parent, QString objName)
: QDialog(parent)
{
setObjectName(objName);
- setAttribute(Qt::WA_MacMetalStyle);
}
Dialog::~Dialog()
============================================================
--- src/view/dialogs/FileDiff.cpp 3dae40899816900fb362793fef3deda6d2809e5c
+++ src/view/dialogs/FileDiff.cpp 0df18f51a4020c5a2a36495b8da49fc5348c53c5
@@ -20,10 +20,10 @@
#include "FileDiff.h"
#include "Monotone.h"
-#include "SignalWaiter.h"
#include "Settings.h"
#include
+#include
FileDiff::FileDiff(QWidget* parent) : Dialog(parent)
{
@@ -31,79 +31,165 @@ FileDiff::FileDiff(QWidget* parent) : Di
Dialog::init();
}
-FileDiff::FileDiff(QWidget* parent, QString fileName) : Dialog(parent)
+FileDiff::FileDiff(QWidget * parent, const QString & fileName,
+ const QString & base, const QString & target): Dialog(parent)
{
setupUi(this);
Dialog::init();
- init(fileName);
+ init(fileName, base, target);
}
-void FileDiff::init(QString fileName)
+void FileDiff::init(
+ const QString & fileName, const QString & base, const QString & target
+)
{
- connect(
- showVersionLeft, SIGNAL(toggled(bool)),
- this, SLOT(versionToggled(bool))
- );
- connect(
- showVersionRight, SIGNAL(toggled(bool)),
- this, SLOT(versionToggled(bool))
- );
- connect(
- showVersionBoth, SIGNAL(toggled(bool)),
- this, SLOT(versionToggled(bool))
- );
+ file = fileName;
+ loaded = false;
QString title = windowTitle();
setWindowTitle(title.arg(fileName));
+ if (base.isNull() || base.isEmpty())
+ {
+ firstRevision->setText(tr("workspace parent"));
+ }
+ else
+ {
+ firstRevision->setText(
+ firstRevision->text().arg(base.left(12).append("..."))
+ );
+ }
+
+ if (target.isNull() || target.isEmpty())
+ {
+ secondRevision->setText(tr("workspace revision"));
+ }
+ else
+ {
+ secondRevision->setText(
+ secondRevision->text().arg(target.left(12).append("..."))
+ );
+ }
+
fileModel = new GetFile(this);
- fileModel->readFileByName(fileName);
fileProxyModel = new GetFileProxyModel(this);
fileProxyModel->setSourceModel(fileModel);
+ diffModel = new ContentDiff(this);
+
diffView->setModel(fileProxyModel);
diffStatusView->setModel(fileProxyModel);
// make the line number col a little smaller
diffView->header()->resizeSection(0, 40);
+
+ connect(
+ firstRevision, SIGNAL(toggled(bool)),
+ this, SLOT(versionToggled(bool))
+ );
+ connect(
+ secondRevision, SIGNAL(toggled(bool)),
+ this, SLOT(versionToggled(bool))
+ );
+ connect(
+ bothRevisions, SIGNAL(toggled(bool)),
+ this, SLOT(versionToggled(bool))
+ );
- SignalWaiter fileWaiter(fileModel, SIGNAL(fileRead()));
+ connect(
+ fileModel, SIGNAL(fileRead()),
+ this, SLOT(applyDiff())
+ );
- if (fileWaiter.wait())
+ connect(
+ diffModel, SIGNAL(diffRead()),
+ this, SLOT(applyDiff())
+ );
+
+ connect(
+ scrollToNext, SIGNAL(clicked()),
+ this, SLOT(getNextGroup())
+ );
+
+ connect(
+ scrollToPrev, SIGNAL(clicked()),
+ this, SLOT(getPrevGroup())
+ );
+
+ fileModel->readFileByName(fileName, base);
+ diffModel->readDiff(fileName, base, target);
+}
+
+FileDiff::~FileDiff() {}
+
+void FileDiff::applyDiff()
+{
+ if (!loaded)
{
- diffModel = new ContentDiff(this);
- diffModel->readDiff(fileName);
-
- SignalWaiter diffWaiter(diffModel, SIGNAL(diffRead()));
-
- if (diffWaiter.wait())
- {
- fileModel->applyDiff(diffModel->getDiff(fileName));
- diffStatusView->update();
- }
- else
- {
- C("diffWaiter timed out");
- }
+ loaded = true;
+ return;
}
- else
+
+ Diff * diff = diffModel->getDiff(file);
+ if (diff == 0)
{
- C("fileWaiter timed out");
+ QMessageBox::information(
+ this,
+ tr("File has not changed"),
+ tr("The file has not been changed between these revisions."),
+ QMessageBox::Ok
+ );
+ reject();
+ return;
}
+
+ fileModel->applyDiff(diff);
+ diffStatusView->update();
}
-FileDiff::~FileDiff() {}
-
void FileDiff::versionToggled(bool dummy)
{
- if (showVersionLeft->isChecked())
+ if (firstRevision->isChecked())
fileProxyModel->setFileVersion(GetFileProxyModel::Left);
- if (showVersionRight->isChecked())
+ if (secondRevision->isChecked())
fileProxyModel->setFileVersion(GetFileProxyModel::Right);
- if (showVersionBoth->isChecked())
+ if (bothRevisions->isChecked())
fileProxyModel->setFileVersion(GetFileProxyModel::Both);
diffStatusView->update();
}
+void FileDiff::scrollToGroup(bool forward)
+{
+ if (!currentIndex.isValid()) return;
+ QModelIndex proxyIndex = fileProxyModel->mapFromSource(currentIndex);
+
+ // recursion: if the queried group is not valid in the view because it
+ // has been filtered out, get the next group and try again
+ if (!proxyIndex.isValid())
+ {
+ if (forward)
+ {
+ getNextGroup();
+ return;
+ }
+ getPrevGroup();
+ return;
+ }
+
+ diffView->scrollTo(proxyIndex, QAbstractItemView::PositionAtTop);
+}
+
+void FileDiff::getPrevGroup()
+{
+ currentIndex = fileModel->getPrevGroup(currentIndex);
+ scrollToGroup(false);
+}
+
+void FileDiff::getNextGroup()
+{
+ currentIndex = fileModel->getNextGroup(currentIndex);
+ scrollToGroup(true);
+}
+
============================================================
--- src/view/dialogs/FileDiff.h dcba92805d020b9d0d83de903f27abda0a5a7c74
+++ src/view/dialogs/FileDiff.h c254fa1cdec0a6fe6545adcf784e5cbcb5ca1573
@@ -32,19 +32,26 @@ public:
Q_OBJECT
public:
- FileDiff(QWidget*);
- FileDiff(QWidget*, QString);
- void init(QString);
+ FileDiff(QWidget *);
+ FileDiff(QWidget *, const QString &, const QString & base = QString(), const QString & target = QString());
+ void init(const QString &, const QString & base = QString(), const QString & target = QString());
~FileDiff();
private slots:
+ void applyDiff();
void versionToggled(bool);
-
+ void scrollToGroup(bool);
+ void getPrevGroup();
+ void getNextGroup();
+
private:
ContentDiff * diffModel;
GetFile * fileModel;
GetFileProxyModel * fileProxyModel;
+ QModelIndex currentIndex;
+ QString file;
+ bool loaded;
};
#endif
============================================================
--- src/view/dialogs/Preferences.cpp c34d5f6dce4113366c6d807b1b0a4bb97d3350b9
+++ src/view/dialogs/Preferences.cpp 4f3d46e215748754366bafc460894a91b6febc34
@@ -41,12 +41,19 @@ Preferences::Preferences(QWidget* parent
enableFileLog->setText(enableFileLog->text().arg(DebugLog::logFilePath()));
- logLevel->addItem(tr("Low (fatal errors)"), 1);
- logLevel->addItem(tr("Medium (critical errors)"), 2);
- logLevel->addItem(tr("High (warnings)"), 3);
- logLevel->addItem(tr("All (debug messages)"), 4);
+ logLevel->addItem(tr("Very low (only fatal)"), 1);
+ logLevel->addItem(tr("Low (critical)"), 2);
+ logLevel->addItem(tr("Medium (warnings)"), 3);
+ logLevel->addItem(tr("High (info messages)"), 4);
- logLevel->setCurrentIndex(DebugLog::getLogLevel()-1);
+ int lvl = DebugLog::getLogLevel();
+#ifndef QT_NO_DEBUG
+ logLevel->addItem(tr("Very high (debug messages)"), 5);
+#else
+ lvl = lvl > 4 ? 4 : lvl;
+#endif
+
+ logLevel->setCurrentIndex(lvl - 1);
checkForUpdates->setCheckState(
Settings::getBool("CheckForUpdates", true) ?
============================================================
--- src/vocab.h dbf6b8075bfee39d1c9494bf7fbc58b842904213
+++ src/vocab.h afe123e8771f70b90c89dc2a9cddeb6206f81142
@@ -5,38 +5,58 @@
// global macros and defines
//
+// if you use any of those two, you also have to include Guitone.h
class Guitone;
#define APP reinterpret_cast(qApp)
#define MTN(arg) APP->getMonotoneInstance(arg)
-// FIXME: uncomment the following lines if you have compile problems
-// on Windows using MSVC++
-/*
-#ifdef _MSC_VER
-#define __FUNCTION__ ""
-#endif
-*/
-
+#include "DebugLog.h"
#ifdef QT_NO_DEBUG
-#define D(arg) void()
+#define D(msg) void(msg)
#else
-#define D(arg) qDebug("%s:%s:%d: %s", __FILE__, __FUNCTION__, __LINE__, qPrintable(QString(arg)))
+#define D(msg) DebugLog::debug(QString("%1:%2:%3: %4") \
+ .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__).arg(QString(msg)))
#endif
-#define W(arg) qWarning("%s:%s:%d: %s", __FILE__, __FUNCTION__, __LINE__, qPrintable(QString(arg)))
-#define C(arg) qCritical("%s:%s:%d: %s", __FILE__, __FUNCTION__, __LINE__, qPrintable(QString(arg)))
-#define F(arg) qFatal("%s:%s:%d: %s", __FILE__, __FUNCTION__, __LINE__, qPrintable(QString(arg)))
-#define I(arg) if (!(arg)) qFatal("%s:%s:%d: invariant \"%s\" violated", __FILE__, __FUNCTION__, __LINE__, #arg)
+#define L(msg) DebugLog::info(QString("%1:%2:%3: %4") \
+ .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__).arg(QString(msg)))
+#define W(msg) DebugLog::warn(QString("%1:%2:%3: %4") \
+ .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__).arg(QString(msg)))
+#define C(msg) DebugLog::critical(QString("%1:%2:%3: %4") \
+ .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__).arg(QString(msg)))
+#define F(msg) { \
+ DebugLog::fatal(QString("%1:%2:%3: %4") \
+ .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__).arg(QString(msg))); \
+ abort(); \
+ }
+#define I(expr) if (!(expr)) F(QString("invariant \"%1\" violated").arg(#expr))
+
//
// type definitions
//
+#include
#include
#include
+#include
// used for manifest entries, if the bool var is true, the entry is a directory
-typedef QPair FileEntry;
+struct FileEntry {
+ FileEntry() {}
+ FileEntry(QString p, bool d) : path(p), is_dir(d) {}
+ FileEntry(QString p, bool d, QString f) : path(p), is_dir(d), fileid(f) {}
+ QString path;
+ bool is_dir;
+ QString fileid;
+ QMap attrs;
+
+ inline bool operator<(const FileEntry & other) const
+ {
+ return path < other.path;
+ }
+};
+
typedef QList FileEntryList;
// used for revision certs
@@ -47,4 +67,18 @@ typedef QList ByteArrayList;
typedef QList ByteArrayList;
+// used for BasicIOParser and BasicIOWriter
+struct StanzaEntry {
+ StanzaEntry() {}
+ StanzaEntry(QString s, QString h) : sym(s), hash(h) {}
+ StanzaEntry(QString s, QStringList v) : sym(s), vals(v) {}
+
+ QString sym;
+ QString hash;
+ QStringList vals;
+};
+
+typedef QList Stanza;
+typedef QList StanzaList;
+
#endif
============================================================
--- tests/test.cpp 1bc1c23455d91468e22f229dffa8fcee9277a8a9
+++ tests/test.cpp 8c345925e352aea61e5f51a0b2ad01fb6475c282
@@ -1,10 +1,18 @@
+#include
+
+#define TEST(name, argc, argv) QTest::qExec(new name(), argc, argv);
+
#include "TestTest.h"
+#include "StdioParserTest.h"
+#include "MonotoneTest.h"
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
- QTest::qExec(&TestTest(), argc, argv);
+ TEST(TestTest, argc, argv);
+ TEST(StdioParserTest, argc, argv);
+ TEST(MonotoneTest, argc, argv);
}
============================================================
--- tests/test.pro 5005bf499b91c8cf6e51e3c74915ee1d9069a312
+++ tests/test.pro b437a3ca1b89faa23f2665324caa1a2f121184a3
@@ -1,16 +1,25 @@
-TEMPLATE = app
-TARGET = test
-CONFIG += qt debug qtestlib
+TEMPLATE = app
+TARGET = test
+CONFIG += qtestlib
+macx:CONFIG -= app_bundle
INCLUDEPATH = . \
- ../guitone/src/ \
- ../guitone/src/view \
- ../guitone/src/view/dialogs \
- ../guitone/src/model \
- ../guitone/src/monotone \
- ../guitone/src/util
-HEADERS += TestTest.h
-SOURCES += test.cpp
+ ../src/ \
+ ../src/view \
+ ../src/view/dialogs \
+ ../src/model \
+ ../src/monotone \
+ ../src/util
+HEADERS += TestTest.h \
+ StdioParserTest.h \
+ MonotoneTest.h \
+ ../src/monotone/Monotone.h \
+ ../src/util/SignalWaiter.h
+SOURCES += test.cpp \
+ ../src/util/StdioParser.cpp \
+ ../src/util/AbstractParser.cpp \
+ ../src/monotone/Monotone.cpp \
+ ../src/util/SignalWaiter.cpp
OBJECTS_DIR = tmp
MOC_DIR = tmp