diff --git a/fuel.pro b/fuel.pro
index 789cb24..f606f86 100644
--- a/fuel.pro
+++ b/fuel.pro
@@ -1,87 +1,102 @@
-#-------------------------------------------------
-# Fuel
-#-------------------------------------------------
-
-QT += core gui webkit
-
-contains(QT_VERSION, ^5\\..*) {
- QT += widgets webkitwidgets
-}
-
-TARGET = Fuel
-TEMPLATE = app
-
-win32 {
- RC_FILE = rsrc/fuel.rc
- LIBS += -luser32 -lshell32 -luuid
-}
-
-macx {
- ICON = rsrc/icons/fuel.icns
-}
-
-unix:!macx {
- TARGET = fuel
- ICON = rsrc/icons/fuel.png
- PREFIX = /usr
- BINDIR = $$PREFIX/bin
- DATADIR = $$PREFIX/share
- target.path = $$BINDIR
-
- desktop.path = $$DATADIR/applications
- desktop.files += rsrc/fuel.desktop
-
- icon.path = $$DATADIR/icons/hicolor/256x256/apps
- icon.files += rsrc/icons/fuel.png
-
- INSTALLS += target desktop icon
- system(intl/convert.sh)
-}
-
-
-INCLUDEPATH += src
-
-SOURCES += src/main.cpp\
- src/MainWindow.cpp \
- src/CommitDialog.cpp \
- src/FileActionDialog.cpp \
- src/SettingsDialog.cpp \
- src/Utils.cpp \
- src/FileTableView.cpp \
- src/CloneDialog.cpp \
- src/LoggedProcess.cpp \
- src/BrowserWidget.cpp \
- src/CustomWebView.cpp
-
-HEADERS += src/MainWindow.h \
- src/CommitDialog.h \
- src/FileActionDialog.h \
- src/SettingsDialog.h \
- src/Utils.h \
- src/FileTableView.h \
- src/CloneDialog.h \
- src/LoggedProcess.h \
- src/BrowserWidget.h \
- src/CustomWebView.h
-
-FORMS += ui/MainWindow.ui \
- ui/CommitDialog.ui \
- ui/FileActionDialog.ui \
- ui/SettingsDialog.ui \
- ui/CloneDialog.ui \
- ui/BrowserWidget.ui
-
-RESOURCES += \
- rsrc/resources.qrc
-
-CODECFORTR = UTF-8
-
-TRANSLATIONS += \
- intl/en_US.ts \
- intl/el_GR.ts \
- intl/de_DE.ts \
- intl/es_ES.ts \
- intl/fr_FR.ts \
- intl/ru_RU.ts \
- intl/pt_PT.ts
-
+#-------------------------------------------------
+# Fuel
+#-------------------------------------------------
+
+QT = core gui webkit
+
+contains(QT_VERSION, ^5\\..*) {
+ QT += widgets webkitwidgets
+ QT -= quick multimediawidgets opengl printsupport qml multimedia positioning sensors
+}
+
+TARGET = Fuel
+TEMPLATE = app
+
+win32 {
+ RC_FILE = rsrc/fuel.rc
+ LIBS += -luser32 -lshell32 -luuid
+}
+
+macx {
+ ICON = rsrc/icons/fuel.icns
+}
+
+unix:!macx {
+ TARGET = fuel
+ ICON = rsrc/icons/fuel.png
+ PREFIX = /usr
+ BINDIR = $$PREFIX/bin
+ DATADIR = $$PREFIX/share
+ target.path = $$BINDIR
+
+ desktop.path = $$DATADIR/applications
+ desktop.files += rsrc/fuel.desktop
+
+ icon.path = $$DATADIR/icons/hicolor/256x256/apps
+ icon.files += rsrc/icons/fuel.png
+
+ INSTALLS += target desktop icon
+ system(intl/convert.sh)
+}
+
+
+INCLUDEPATH += src
+
+SOURCES += src/main.cpp\
+ src/MainWindow.cpp \
+ src/CommitDialog.cpp \
+ src/FileActionDialog.cpp \
+ src/SettingsDialog.cpp \
+ src/FslSettingsDialog.cpp \
+ src/CloneDialog.cpp \
+ src/RevisionDialog.cpp \
+ src/Utils.cpp \
+ src/FileTableView.cpp \
+ src/LoggedProcess.cpp \
+ src/BrowserWidget.cpp \
+ src/CustomWebView.cpp \
+ src/Fossil.cpp \
+ src/Workspace.cpp \
+ src/SearchBox.cpp \
+ src/Settings.cpp
+
+HEADERS += src/MainWindow.h \
+ src/CommitDialog.h \
+ src/FileActionDialog.h \
+ src/SettingsDialog.h \
+ src/FslSettingsDialog.h \
+ src/CloneDialog.h \
+ src/RevisionDialog.h \
+ src/Utils.h \
+ src/FileTableView.h \
+ src/LoggedProcess.h \
+ src/BrowserWidget.h \
+ src/CustomWebView.h \
+ src/Fossil.h \
+ src/Workspace.h \
+ src/SearchBox.h \
+ src/Settings.h
+
+FORMS += ui/MainWindow.ui \
+ ui/CommitDialog.ui \
+ ui/FileActionDialog.ui \
+ ui/SettingsDialog.ui \
+ ui/FslSettingsDialog.ui \
+ ui/CloneDialog.ui \
+ ui/BrowserWidget.ui \
+ ui/RevisionDialog.ui
+
+RESOURCES += \
+ rsrc/resources.qrc
+
+CODECFORTR = UTF-8
+
+TRANSLATIONS += \
+ intl/en_US.ts \
+ intl/el_GR.ts \
+ intl/de_DE.ts \
+ intl/es_ES.ts \
+ intl/fr_FR.ts \
+ intl/ru_RU.ts \
+ intl/pt_PT.ts
+
diff --git a/manifest b/manifest
index 45a76fe..404c4e0 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Set\sversion\sto\s1.0.1
-D 2015-05-12T18:18:33.512
+C Merged\snew\sworkspace
+D 2015-05-26T18:03:05.443
F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35
F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b
F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b
@@ -15,7 +15,7 @@ F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e
F doc/Building.txt 17b43fa23da764b5d1b828cc48c5a95e612bbd8f
F doc/Changes.txt b03302545e4a6c0b16a30d623a7627f8aef65ef6
F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c
-F fuel.pro 844a18c3faf5239e0d0025d8b7feac3900c28e71
+F fuel.pro b010c4ee3093112003a9d27045927efce5985dab
F intl/convert.bat 4222ae403418381452b843929d15259ea9850ab1 x
F intl/convert.sh 2ca2179ff53e727f241925b75e19182607883c45 x
F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741
@@ -183,37 +183,52 @@ F rsrc/icons/Zoom-01.png 67ca532922e9166325c5c75fce1ca3fbb0d2b6a6
F rsrc/icons/fuel.icns 81e535004b62db801a02f3e15d0a33afc9d4070b
F rsrc/icons/fuel.ico eb529ab3332a17b9302ef3e851db5b9ebce2a038
F rsrc/icons/fuel.png 40daf53b7f6bdcdd0d6aa5ef433d078ec5ea4342
-F rsrc/resources.qrc 4098be128fd6c045db933d041fe8844b14643a6f
+F rsrc/resources.qrc 388225a5b09c56c12d4cbde8b4d0b4466740fc97
F src/BrowserWidget.cpp 8b8f545cdff4a4188edc698a1b4777f5df46f056
F src/BrowserWidget.h 764d66aa9a93b890298bd0301097739cb4e16597
-F src/CloneDialog.cpp 812ef7d361c16da21540b7047c9d4d5e74f18539
-F src/CloneDialog.h e9f0fc8e5cc5ea2e7c43d6e77b5c4a9cc850b59e
-F src/CommitDialog.cpp 5300522ac11bc1096a11a6ce22f8c1665d4afc05
-F src/CommitDialog.h f1ee8db92103164e7db55a8407ccdcff24571b72
+F src/CloneDialog.cpp 4fc5aa8146ac63ba6ba7341b1635b3025819d708
+F src/CloneDialog.h 8813d91f893eb3eb86a4ea5e50f9a53a0ea07047
+F src/CommitDialog.cpp bbf5fe1c66d28068cc3fd061f4f9f1faa9e89196
+F src/CommitDialog.h 921bf27c0c538ab9e9d6bdc750064337d346270b
F src/CustomWebView.cpp b7dd0c41977c2cba005df07ed8967ba6f58d07d9
F src/CustomWebView.h fbc8ee55812d1acb3c3b2bc31be7533e8a112822
F src/FileActionDialog.cpp fcaebf9986f789b3440d5390b3458ad5f86fe0c8
F src/FileActionDialog.h 15db1650b3a13d70bc338371e4c033c66e3b79ce
F src/FileTableView.cpp 5ddf8c391c9a3ac449ec61fb1db837b577afeec2
F src/FileTableView.h 03e56d87c2d46411b9762b87f4d301619aaf18df
+F src/Fossil.cpp 0d4c50327a61c48506d2d45e28cd6f71f1697ea2
+F src/Fossil.h 31765ef57e20a860914372d56c024033b30aa765
+F src/FslSettingsDialog.cpp f5a34a70ecb0560d2b6eea6bf27e42048548aedd
+F src/FslSettingsDialog.h dfe2a61884a55a74cbb9206b6f6b482b979725e7
F src/LoggedProcess.cpp 2a1e5c94bc1e57c8984563e66c210e43a14dc60c
F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261
-F src/MainWindow.cpp 6758e29796f411f920a0a59ece5002e4aeefd9f2
-F src/MainWindow.h 77038e9c9fe8a64a1c2dfb8d4c2be7558ab5f372
-F src/SettingsDialog.cpp a46cff5e5dd425e3dbdd15632abfd5829f5562b4
-F src/SettingsDialog.h 4e2790f581e991c744ae9f86580f1972b8c7ff43
-F src/Utils.cpp 9aff456712e4276b49083426301b3b96d3819c77
-F src/Utils.h c546e478a1225a28c99cd4c30f70cf9be9804a2a
-F src/main.cpp e1217b2331f1b0fd30756fc80a72f9676f09cf6b
+F src/MainWindow.cpp 4cbfa1fdf3092b97649711388576230e4808d50e
+F src/MainWindow.h a848462f21423b5c1e0c218cab1805c308299607
+F src/RevisionDialog.cpp 51065c65a07c118dd1a7363da4a55a135d1c6c9c
+F src/RevisionDialog.h b718c3009342eaabad39c8a11a253a4e4fef7a73
+F src/SearchBox.cpp d4209c575baa9933e1ce5ed376e785b289a145ba
+F src/SearchBox.h 0c78d3a68136dab3e0e71b83ae36f22bd2688ab2
+F src/Settings.cpp 6ab826273b9693bfcd65f0f59b550ae2aa3577f1
+F src/Settings.h 1ff8bb71e19949150e8caa4f5e5f13f8810e496b
+F src/SettingsDialog.cpp 25be4c351dd21ea9132321944f42dc0bc22fb128
+F src/SettingsDialog.h b324dfd77ca3ad24fd83588aaf79a7e4c291e716
+F src/Utils.cpp c48e3316f3a0a5d735e8d4953710500358985e32
+F src/Utils.h c2a28611bd77fb35ea3dcf65fb60ed585f68aa3c
+F src/Workspace.cpp f68a4ca05d1b7c5c345fbd89527691813593c663
+F src/Workspace.h d6649a3ae1cd0fbad55237030313e85530417271
+F src/main.cpp d8c65ea5e54102e4989fef9fd8cfd4f13ef8a8f0
F tools/git-push.sh 62cc58434cae5b7bcd6bd9d4cce8b08739f31cd7 x
F tools/pack.sh d7f38a498c4e9327fecd6a6e5ac27be270d43008 x
-F ui/BrowserWidget.ui 5ad98b13773afadb20a1a2c22148aaebe5dbd95d
+F ui/BrowserWidget.ui 994ad9ea0e9f5815d6b1a27acc2f6f39164c507f
F ui/CloneDialog.ui 4886e7d4f258ea8b852b5eefc860396e35145712
-F ui/CommitDialog.ui 6200f6cabdcf40a20812e811be28e0793f82516f
+F ui/CommitDialog.ui aea77347eef82b6b591f31fb058a1bb96193c728
F ui/FileActionDialog.ui 89bb4dc2d0b8adcd41adcb11ec65f2028a09a12d
-F ui/MainWindow.ui 8677f5c8bca5bf7561d5f64bfdd0cef5157c6ac7
-F ui/SettingsDialog.ui 2b7c2870e0054b0f4106f495d85d02c0b814df8b
-P 964b28f34fae482959ce8a96cbc6106f8702fb33
-R 52b73271a5ba6efcdf8cd8ac7e8910df
+F ui/FslSettingsDialog.ui 042717833d8efea905b4fc380bad580be809717d
+F ui/MainWindow.ui 2d36b1ba9886356802f2cddf12c4cabb5ee1acde
+F ui/RevisionDialog.ui 27c3b98c665fec014a50cbf3352c0627f75e68cd
+F ui/SettingsDialog.ui 4c480cd595a32664d01c85bf74845c4282fc0068
+P e2ba8f0aba679a1c52d36daf3041cd139bd49579 7ebac37b3255e6721889c070b7327a96c63b6c97
+R 8f806c4029c039de40ed9c8c7d5c5dca
+T +closed 7ebac37b3255e6721889c070b7327a96c63b6c97
U kostas
-Z e4adddbe2917b1cd621d67e57ca27d42
+Z 0150f2a9d78e86830f0a9f5b26438204
diff --git a/manifest.uuid b/manifest.uuid
index 887f359..0442ec3 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-e2ba8f0aba679a1c52d36daf3041cd139bd49579
\ No newline at end of file
+322729110f48bfb8c007dd611d47e411b389044a
\ No newline at end of file
diff --git a/rsrc/resources.qrc b/rsrc/resources.qrc
index 8c17330..779203f 100644
--- a/rsrc/resources.qrc
+++ b/rsrc/resources.qrc
@@ -1,163 +1,167 @@
-
- icons/Address Book-01.png
- icons/Adobe Illustrator CS3 Document-01.png
- icons/Adobe PDF Document-01.png
- icons/Adobe Photoshop CS3 Document-01.png
- icons/Battery-01.png
- icons/Binoculars-01.png
- icons/Book-01.png
- icons/Briefcase-01.png
- icons/Button Add-01.png
- icons/Button Blank Blue-01.png
- icons/Button Blank Gray-01.png
- icons/Button Blank Green-01.png
- icons/Button Blank Red-01.png
- icons/Button Blank Yellow-01.png
- icons/Button Cancel-01.png
- icons/Button Close-01.png
- icons/Button Delete-01.png
- icons/Button Download-01.png
- icons/Button Favorite-01.png
- icons/Button Forward-01.png
- icons/Button Help-01.png
- icons/Button Info-01.png
- icons/Button Log Off-01.png
- icons/Button Next-01.png
- icons/Button Pause-01.png
- icons/Button Play-01.png
- icons/Button Previous-01.png
- icons/Button Refresh-01.png
- icons/Button Reload-01.png
- icons/Button Reminder-01.png
- icons/Button Rewind-01.png
- icons/Button Talk Balloon-01.png
- icons/Button Turn Off-01.png
- icons/Button Turn On-01.png
- icons/Button Upload-01.png
- icons/Button Warning-01.png
- icons/Calculator-01.png
- icons/Calendar Blue-01.png
- icons/Calendar Green-01.png
- icons/Calendar Red-01.png
- icons/Clipboard-01.png
- icons/Clipboard Paste-01.png
- icons/Clock-01.png
- icons/Coin-01.png
- icons/Compressed File RAR-01.png
- icons/Compressed File SIT-01.png
- icons/Compressed File Zip-01.png
- icons/Computer Monitor-01.png
- icons/Computer Network-01.png
- icons/Document-01.png
- icons/Document Attach-01.png
- icons/Document Blank-01.png
- icons/Document Chart-01.png
- icons/Document Copy-01.png
- icons/Document Flow Chart-01.png
- icons/Document Gant Chart-01.png
- icons/Document Help-01.png
- icons/Document Line Chart-01.png
- icons/Document Microsoft Excel-01.png
- icons/Document Microsoft PowerPoint-01.png
- icons/Document Microsoft Word-01.png
- icons/Document Organization Chart-01.png
- icons/Document Preview-01.png
- icons/Document-Revert-icon.png
- icons/Document Text-01.png
- icons/Edit Document-01.png
- icons/Email-01.png
- icons/Email Attachment-01.png
- icons/Email Delete-01.png
- icons/Email Download-01.png
- icons/Email Forward-01.png
- icons/Email Inbox-01.png
- icons/Email Reply-01.png
- icons/File Audio-01.png
- icons/File Audio AIFF-01.png
- icons/File Audio MP3-01.png
- icons/File Audio WAV-01.png
- icons/File Audio WMA-01.png
- icons/File Delete-01.png
- icons/File History-01.png
- icons/File New-01.png
- icons/File Open-01.png
- icons/File Video 3GP-01.png
- icons/File Video-01.png
- icons/File Video AVI-01.png
- icons/File Video MOV-01.png
- icons/File Video MPEG-01.png
- icons/File Video WMV-01.png
- icons/Folder-01.png
- icons/Folder Add-01.png
- icons/Folder Compressed-01.png
- icons/Folder Delete-01.png
- icons/Folder Explorer-01.png
- icons/Folder Generic Blue-01.png
- icons/Folder Generic Green-01.png
- icons/Folder Generic Red-01.png
- icons/Folder Generic Silver-01.png
- icons/Folder Open-01.png
- icons/Folder RAR-01.png
- icons/Games-01.png
- icons/Gear-01.png
- icons/Highlighter Blue-01.png
- icons/Highlighter Green-01.png
- icons/Highlighter Yellow-01.png
- icons/Image BMP-01.png
- icons/Image GIF-01.png
- icons/Image JPEG-01.png
- icons/Image PNG-01.png
- icons/Image TIFF-01.png
- icons/Lock Lock-01.png
- icons/Lock Unlock-01.png
- icons/My Documents-01.png
- icons/My Ebooks-01.png
- icons/My Music-01.png
- icons/My Pictures.png
- icons/My Videos-01.png
- icons/My Websites-01.png
- icons/Network Firewall-01.png
- icons/Network MAC-01.png
- icons/Network PC-01.png
- icons/Network Refresh-01.png
- icons/Pen Blue-01.png
- icons/Pen Green-01.png
- icons/Pen Red-01.png
- icons/Save-01.png
- icons/Text Edit.png
- icons/USB-01.png
- icons/User Administrator Blue-01.png
- icons/User Administrator Green-01.png
- icons/User Administrator Red-01.png
- icons/User Chat-01.png
- icons/User Clients-01.png
- icons/User Coat Blue-01.png
- icons/User Coat Green-01.png
- icons/User Coat Red-01.png
- icons/User Executive Blue-01.png
- icons/User Executive Green-01.png
- icons/User Executive Red-01.png
- icons/User Group-01.png
- icons/User Preppy Blue-01.png
- icons/User Preppy Green-01.png
- icons/User Preppy Red-01.png
- icons/Web HTML-01.png
- icons/Web XML-01.png
- icons/Window-01.png
- icons/Window Refresh-01.png
- icons/Windows-01.png
- icons/Windows Cascade-01.png
- icons/Zoom-01.png
- icons/Zoom In-01.png
- icons/Zoom Out-01.png
-
-
- intl/el_GR.qm
- intl/de_DE.qm
- intl/es_ES.qm
- intl/fr_FR.qm
- intl/ru_RU.qm
- intl/pt_PT.qm
-
+
+ icons/Address Book-01.png
+ icons/Adobe Illustrator CS3 Document-01.png
+ icons/Adobe PDF Document-01.png
+ icons/Adobe Photoshop CS3 Document-01.png
+ icons/Battery-01.png
+ icons/Binoculars-01.png
+ icons/Book-01.png
+ icons/Briefcase-01.png
+ icons/Button Add-01.png
+ icons/Button Blank Blue-01.png
+ icons/Button Blank Gray-01.png
+ icons/Button Blank Green-01.png
+ icons/Button Blank Red-01.png
+ icons/Button Blank Yellow-01.png
+ icons/Button Cancel-01.png
+ icons/Button Close-01.png
+ icons/Button Close-01.png
+ icons/Button Close-01.png
+ icons/Button Delete-01.png
+ icons/Button Download-01.png
+ icons/Button Favorite-01.png
+ icons/Button Forward-01.png
+ icons/Button Help-01.png
+ icons/Button Info-01.png
+ icons/Button Log Off-01.png
+ icons/Button Next-01.png
+ icons/Button Pause-01.png
+ icons/Button Play-01.png
+ icons/Button Previous-01.png
+ icons/Button Refresh-01.png
+ icons/Button Reload-01.png
+ icons/Button Reload-01.png
+ icons/Button Reminder-01.png
+ icons/Button Rewind-01.png
+ icons/Button Talk Balloon-01.png
+ icons/Button Turn Off-01.png
+ icons/Button Turn On-01.png
+ icons/Button Upload-01.png
+ icons/Button Warning-01.png
+ icons/Calculator-01.png
+ icons/Calendar Blue-01.png
+ icons/Calendar Green-01.png
+ icons/Calendar Red-01.png
+ icons/Clipboard-01.png
+ icons/Clipboard Paste-01.png
+ icons/Clock-01.png
+ icons/Coin-01.png
+ icons/Compressed File RAR-01.png
+ icons/Compressed File SIT-01.png
+ icons/Compressed File Zip-01.png
+ icons/Computer Monitor-01.png
+ icons/Computer Network-01.png
+ icons/Document-01.png
+ icons/Document Attach-01.png
+ icons/Document Blank-01.png
+ icons/Document Chart-01.png
+ icons/Document Copy-01.png
+ icons/Document Flow Chart-01.png
+ icons/Document Gant Chart-01.png
+ icons/Document Help-01.png
+ icons/Document Line Chart-01.png
+ icons/Document Microsoft Excel-01.png
+ icons/Document Microsoft PowerPoint-01.png
+ icons/Document Microsoft Word-01.png
+ icons/Document Organization Chart-01.png
+ icons/Document Preview-01.png
+ icons/Document-Revert-icon.png
+ icons/Document Text-01.png
+ icons/Edit Document-01.png
+ icons/Email-01.png
+ icons/Email Attachment-01.png
+ icons/Email Delete-01.png
+ icons/Email Download-01.png
+ icons/Email Forward-01.png
+ icons/Email Inbox-01.png
+ icons/Email Reply-01.png
+ icons/File Audio-01.png
+ icons/File Audio AIFF-01.png
+ icons/File Audio MP3-01.png
+ icons/File Audio WAV-01.png
+ icons/File Audio WMA-01.png
+ icons/File Delete-01.png
+ icons/File History-01.png
+ icons/File New-01.png
+ icons/File Open-01.png
+ icons/File Video 3GP-01.png
+ icons/File Video-01.png
+ icons/File Video AVI-01.png
+ icons/File Video MOV-01.png
+ icons/File Video MPEG-01.png
+ icons/File Video WMV-01.png
+ icons/Folder-01.png
+ icons/Folder Add-01.png
+ icons/Folder Compressed-01.png
+ icons/Folder Delete-01.png
+ icons/Folder Explorer-01.png
+ icons/Folder Explorer-01.png
+ icons/Folder Generic Blue-01.png
+ icons/Folder Generic Green-01.png
+ icons/Folder Generic Red-01.png
+ icons/Folder Generic Silver-01.png
+ icons/Folder Open-01.png
+ icons/Folder RAR-01.png
+ icons/Games-01.png
+ icons/Gear-01.png
+ icons/Highlighter Blue-01.png
+ icons/Highlighter Green-01.png
+ icons/Highlighter Yellow-01.png
+ icons/Image BMP-01.png
+ icons/Image GIF-01.png
+ icons/Image JPEG-01.png
+ icons/Image PNG-01.png
+ icons/Image TIFF-01.png
+ icons/Lock Lock-01.png
+ icons/Lock Unlock-01.png
+ icons/My Documents-01.png
+ icons/My Ebooks-01.png
+ icons/My Music-01.png
+ icons/My Pictures.png
+ icons/My Videos-01.png
+ icons/My Websites-01.png
+ icons/Network Firewall-01.png
+ icons/Network MAC-01.png
+ icons/Network PC-01.png
+ icons/Network Refresh-01.png
+ icons/Pen Blue-01.png
+ icons/Pen Green-01.png
+ icons/Pen Red-01.png
+ icons/Save-01.png
+ icons/Text Edit.png
+ icons/USB-01.png
+ icons/User Administrator Blue-01.png
+ icons/User Administrator Green-01.png
+ icons/User Administrator Red-01.png
+ icons/User Chat-01.png
+ icons/User Clients-01.png
+ icons/User Coat Blue-01.png
+ icons/User Coat Green-01.png
+ icons/User Coat Red-01.png
+ icons/User Executive Blue-01.png
+ icons/User Executive Green-01.png
+ icons/User Executive Red-01.png
+ icons/User Group-01.png
+ icons/User Preppy Blue-01.png
+ icons/User Preppy Green-01.png
+ icons/User Preppy Red-01.png
+ icons/Web HTML-01.png
+ icons/Web XML-01.png
+ icons/Window-01.png
+ icons/Window Refresh-01.png
+ icons/Windows-01.png
+ icons/Windows Cascade-01.png
+ icons/Zoom-01.png
+ icons/Zoom In-01.png
+ icons/Zoom Out-01.png
+
+
+ intl/el_GR.qm
+ intl/de_DE.qm
+ intl/es_ES.qm
+ intl/fr_FR.qm
+ intl/ru_RU.qm
+ intl/pt_PT.qm
+
diff --git a/src/CloneDialog.cpp b/src/CloneDialog.cpp
index 0f6003d..ff1024b 100644
--- a/src/CloneDialog.cpp
+++ b/src/CloneDialog.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include "Utils.h"
//-----------------------------------------------------------------------------
CloneDialog::CloneDialog(QWidget *parent) :
diff --git a/src/CloneDialog.h b/src/CloneDialog.h
index b2fdae3..884db5a 100644
--- a/src/CloneDialog.h
+++ b/src/CloneDialog.h
@@ -3,10 +3,6 @@
#include
-#define FOSSIL_CHECKOUT1 "_FOSSIL_"
-#define FOSSIL_CHECKOUT2 ".fslckout"
-#define FOSSIL_EXT "fossil"
-
namespace Ui {
class CloneDialog;
}
diff --git a/src/CommitDialog.cpp b/src/CommitDialog.cpp
index 0322c39..4d0de48 100644
--- a/src/CommitDialog.cpp
+++ b/src/CommitDialog.cpp
@@ -4,7 +4,7 @@
#include "ui_CommitDialog.h"
#include "MainWindow.h" // Ugly. I know.
-CommitDialog::CommitDialog(QWidget *parent, QString title, QStringList &files, const QStringList *history, bool singleLineEntry, const QString *checkBoxText, bool *checkBoxValue) :
+CommitDialog::CommitDialog(QWidget *parent, const QString &title, QStringList &files, const QStringList *history, bool stashMode) :
QDialog(parent, Qt::Sheet),
ui(new Ui::CommitDialog)
{
@@ -15,16 +15,13 @@ CommitDialog::CommitDialog(QWidget *parent, QString title, QStringList &files, c
setWindowTitle(title);
// Activate the appropriate control based on mode
- ui->plainTextEdit->setVisible(!singleLineEntry);
- ui->lineEdit->setVisible(singleLineEntry);
+ ui->plainTextEdit->setVisible(!stashMode);
+ ui->lineEdit->setVisible(stashMode);
// Activate the checkbox if we have some text
- ui->checkBox->setVisible(checkBoxText!=0);
- if(checkBoxText && checkBoxValue)
- {
- ui->checkBox->setText(*checkBoxText);
- ui->checkBox->setCheckState(*checkBoxValue ? Qt::Checked : Qt::Unchecked);
- }
+ ui->chkRevertFiles->setVisible(stashMode);
+
+ ui->widgetBranchOptions->setVisible(!stashMode);
// Activate the combo if we have history
ui->comboBox->setVisible(history!=0);
@@ -77,16 +74,12 @@ CommitDialog::~CommitDialog()
}
//------------------------------------------------------------------------------
-bool CommitDialog::run(QWidget *parent, QString title, QStringList &files, QString &commitMsg, const QStringList *history, bool singleLineEntry, const QString *checkBoxText, bool *checkBoxValue)
+bool CommitDialog::runCommit(QWidget* parent, QStringList& files, QString& commitMsg, const QStringList& commitMsgHistory, QString &branchName, bool &privateBranch)
{
- CommitDialog dlg(parent, title, files, history, singleLineEntry, checkBoxText, checkBoxValue);
+ CommitDialog dlg(parent, tr("Commit Changes"), files, &commitMsgHistory, false);
int res = dlg.exec();
- if(singleLineEntry)
- commitMsg = dlg.ui->lineEdit->text();
- else
- commitMsg = dlg.ui->plainTextEdit->toPlainText();
-
+ commitMsg = dlg.ui->plainTextEdit->toPlainText();
if(res!=QDialog::Accepted)
return false;
@@ -100,15 +93,40 @@ bool CommitDialog::run(QWidget *parent, QString title, QStringList &files, QStri
files.append(si->text());
}
- if(checkBoxText)
+ branchName.clear();
+ if(dlg.ui->chkNewBranch->isChecked())
{
- Q_ASSERT(checkBoxValue);
- *checkBoxValue = dlg.ui->checkBox->checkState() == Qt::Checked;
+ branchName = dlg.ui->lineBranchName->text().trimmed();
+ privateBranch = dlg.ui->chkPrivateBranch->isChecked();
}
return true;
}
+//------------------------------------------------------------------------------
+bool CommitDialog::runStashNew(QWidget* parent, QStringList& stashedFiles, QString& stashName, bool& revertFiles)
+{
+ CommitDialog dlg(parent, tr("Stash Changes"), stashedFiles, NULL, true);
+ int res = dlg.exec();
+
+ stashName = dlg.ui->lineEdit->text();
+
+ if(res!=QDialog::Accepted)
+ return false;
+
+ stashedFiles.clear();
+ for(int i=0; icheckState()!=Qt::Checked)
+ continue;
+ stashedFiles.append(si->text());
+ }
+
+ revertFiles = dlg.ui->chkRevertFiles->checkState() == Qt::Checked;
+ return true;
+}
+
//------------------------------------------------------------------------------
void CommitDialog::on_comboBox_activated(int index)
{
@@ -142,3 +160,10 @@ void CommitDialog::on_listView_clicked(const QModelIndex &)
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(num_selected>0);
}
+
+//------------------------------------------------------------------------------
+void CommitDialog::on_chkNewBranch_toggled(bool checked)
+{
+ ui->chkPrivateBranch->setEnabled(checked);
+ ui->lineBranchName->setEnabled(checked);
+}
diff --git a/src/CommitDialog.h b/src/CommitDialog.h
index 4825d11..f52b928 100644
--- a/src/CommitDialog.h
+++ b/src/CommitDialog.h
@@ -5,26 +5,28 @@
#include
namespace Ui {
- class CommitDialog;
+ class CommitDialog;
}
class CommitDialog : public QDialog
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit CommitDialog(QWidget *parent, QString title, QStringList &files, const QStringList *history=0, bool singleLineEntry=false, const QString *checkBoxText=0, bool *checkBoxValue=0);
- ~CommitDialog();
+ explicit CommitDialog(QWidget *parent, const QString &title, QStringList &files, const QStringList *history, bool stashMode);
+ ~CommitDialog();
- static bool run(QWidget *parent, QString title, QStringList &files, QString &commitMsg, const QStringList *history=0, bool singleLineEntry=false, const QString *checkBoxText=0, bool *checkBoxValue=0);
+ static bool runStashNew(QWidget* parent, QStringList& stashedFiles, QString& stashName, bool &revertFiles);
+ static bool runCommit(QWidget* parent, QStringList& files, QString& commitMsg, const QStringList &commitMsgHistory, QString& branchName, bool& privateBranch);
private slots:
void on_comboBox_activated(int index);
void on_listView_doubleClicked(const QModelIndex &index);
void on_listView_clicked(const QModelIndex &index);
+ void on_chkNewBranch_toggled(bool checked);
private:
- Ui::CommitDialog *ui;
+ Ui::CommitDialog *ui;
QStandardItemModel itemModel;
QStringList commitMessages;
};
diff --git a/src/Fossil.cpp b/src/Fossil.cpp
new file mode 100644
index 0000000..148502e
--- /dev/null
+++ b/src/Fossil.cpp
@@ -0,0 +1,1087 @@
+#include "Fossil.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "Utils.h"
+
+static const unsigned char UTF8_BOM[] = { 0xEF, 0xBB, 0xBF };
+
+// 19: [5c46757d4b9765] on 2012-04-22 04:41:15
+static const QRegExp REGEX_STASH("\\s*(\\d+):\\s+\\[(.*)\\] on (\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)", Qt::CaseInsensitive);
+
+// Listening for HTTP requests on TCP port 8081
+static const QRegExp REGEX_PORT(".*TCP port ([0-9]+)\\n", Qt::CaseSensitive);
+
+///////////////////////////////////////////////////////////////////////////////
+RepoStatus Fossil::getRepoStatus()
+{
+ QStringList res;
+ int exit_code = EXIT_FAILURE;
+
+ // We need to determine the reason why fossil has failed
+ // so we delay processing of the exit_code
+ if(!runFossilRaw(QStringList() << "info", &res, &exit_code, RUNFLAGS_SILENT_ALL))
+ return REPO_NOT_FOUND;
+
+ bool run_ok = exit_code == EXIT_SUCCESS;
+
+ for(QStringList::iterator it=res.begin(); it!=res.end(); ++it)
+ {
+ int col_index = it->indexOf(':');
+ if(col_index==-1)
+ continue;
+
+ QString key = it->left(col_index).trimmed();
+ QString value = it->mid(col_index+1).trimmed();
+
+ if(key=="fossil")
+ {
+ if(value=="incorrect repository schema version")
+ return REPO_OLD_SCHEMA;
+ else if(value=="not within an open checkout")
+ return REPO_NOT_FOUND;
+ }
+
+ if(run_ok)
+ {
+ if(key=="project-name")
+ projectName = value;
+ else if(key=="repository")
+ repositoryFile = value;
+ else if(key=="checkout")
+ {
+ // f2121dad5e4565f55ed9ef882484dd5934af565f 2015-04-26 17:27:39 UTC
+ QStringList tokens = value.split(' ', QString::SkipEmptyParts);
+ Q_ASSERT(tokens.length()>0);
+ currentRevision = tokens[0].trimmed();
+ }
+ else if(key=="tags")
+ {
+ currentTags.clear();
+ QStringList tokens = value.split(',', QString::SkipEmptyParts);
+ foreach(const QString &tag, tokens)
+ currentTags.append(tag);
+ currentTags.sort();
+ }
+ }
+ }
+
+ return run_ok ? REPO_OK : REPO_NOT_FOUND;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::openRepository(const QString& repositoryPath, const QString& workspacePath)
+{
+ QFileInfo fi(repositoryPath);
+
+ if(!QDir::setCurrent(workspacePath) || !fi.isFile())
+ return false;
+
+ QString abspath = fi.absoluteFilePath();
+ setCurrentWorkspace(workspacePath);
+ setRepositoryFile(abspath);
+
+ if(!runFossil(QStringList() << "open" << QuotePath(abspath)))
+ return false;
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::newRepository(const QString& repositoryPath)
+{
+ QFileInfo fi(repositoryPath);
+
+ if(fi.exists())
+ return false;
+
+ if(!runFossil(QStringList() << "new" << QuotePath(fi.absoluteFilePath())))
+ return false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::closeRepository()
+{
+ if(!runFossil(QStringList() << "close"))
+ return false;
+
+ stopUI();
+ setCurrentWorkspace("");
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::listFiles(QStringList &files)
+{
+ return runFossil(QStringList() << "ls" << "-l", &files, RUNFLAGS_SILENT_ALL);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::status(QStringList &result)
+{
+ return runFossil(QStringList() << "status", &result, RUNFLAGS_SILENT_ALL);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::pushRepository()
+{
+ return runFossil(QStringList() << "push");
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::pullRepository()
+{
+ return runFossil(QStringList() << "pull");
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::cloneRepository(const QString& repository, const QUrl& url, const QUrl& proxyUrl)
+{
+ // Actual command
+ QStringList cmd = QStringList() << "clone";
+
+ // Log Command
+ QStringList logcmd = QStringList() << "fossil" << "clone";
+
+ QString source = url.toString();
+ QString logsource = url.toString(QUrl::RemovePassword);
+ if(url.isLocalFile())
+ {
+ source = url.toLocalFile();
+ logsource = source;
+ }
+ cmd << source << repository;
+ logcmd << logsource << repository;
+
+ if(!proxyUrl.isEmpty())
+ {
+ cmd << "--proxy" << proxyUrl.toString();
+ logcmd << "--proxy" << proxyUrl.toString(QUrl::RemovePassword);
+ }
+
+ log(">"+logcmd.join(" ")+"
", true);
+
+ // Clone Repo
+ if(!runFossil(cmd, 0, RUNFLAGS_SILENT_INPUT))
+ return false;
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::getFossilVersion(QString& version)
+{
+ QStringList res;
+ if(!runFossil(QStringList() << "version", &res, RUNFLAGS_SILENT_ALL) && res.length()==1)
+ return false;
+
+ if(res.length()==0)
+ return false;
+
+ int off = res[0].indexOf("version ");
+ if(off==-1)
+ return false;
+
+ version = res[0].mid(off+8);
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::diffFile(const QString &repoFile)
+{
+ // Run the diff detached
+ return runFossil(QStringList() << "gdiff" << QuotePath(repoFile), 0, RUNFLAGS_DETACHED);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::commitFiles(const QStringList& fileList, const QString& comment, const QString &newBranchName, bool isPrivateBranch)
+{
+ // Do commit
+ QString comment_fname;
+ {
+ QTemporaryFile temp_file;
+ if(!temp_file.open())
+ return false;
+
+ comment_fname = temp_file.fileName();
+ }
+
+ QFile comment_file(comment_fname);
+ if(!comment_file.open(QIODevice::WriteOnly))
+ return false;
+
+ // Write BOM
+ comment_file.write(reinterpret_cast(UTF8_BOM), sizeof(UTF8_BOM));
+
+ // Write Comment
+ comment_file.write(comment.toUtf8());
+ comment_file.close();
+
+ // Generate fossil parameters.
+ QStringList params;
+ params << "commit" << "--message-file" << QuotePath(comment_fname);
+
+ // Commit to new branch
+ if(!newBranchName.isEmpty())
+ {
+ params << "--branch" << newBranchName.trimmed();
+
+ // Private branches are not synced with remotes
+ if(isPrivateBranch)
+ params << "--private";
+ }
+
+ params << QuotePaths(fileList);
+
+ runFossil(params);
+ QFile::remove(comment_fname);
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::addFiles(const QStringList& fileList)
+{
+ if(fileList.empty())
+ return false;
+
+ // Do Add
+ return runFossil(QStringList() << "add" << QuotePaths(fileList));
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::removeFiles(const QStringList& fileList, bool deleteLocal)
+{
+ if(fileList.empty())
+ return false;
+
+ // Do Delete
+ if(!runFossil(QStringList() << "delete" << QuotePaths(fileList)))
+ return false;
+
+ if(deleteLocal)
+ {
+ for(int i=0; i0)
+ url = out[0].trimmed();
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::stashNew(const QStringList& fileList, const QString& name, bool revert)
+{
+ // Do Stash
+ // Snapshot just records the changes into the stash
+ QString command = "snapshot";
+
+ // Save also reverts the stashed files
+ if(revert)
+ command = "save";
+
+ return runFossil(QStringList() << "stash" << command << "-m" << name << QuotePaths(fileList));
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::stashList(stashmap_t& stashes)
+{
+ stashes.clear();
+ QStringList res;
+
+ if(!runFossil(QStringList() << "stash" << "ls", &res, RUNFLAGS_SILENT_ALL))
+ return false;
+
+ for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); )
+ {
+ QString line = *line_it;
+
+ int index = REGEX_STASH.indexIn(line);
+ if(index==-1)
+ break;
+
+ QString id = REGEX_STASH.cap(1);
+ ++line_it;
+
+ QString name;
+ // Finish at an anonymous stash or start of a new stash ?
+ if(line_it==res.end() || REGEX_STASH.indexIn(*line_it)!=-1)
+ name = line.trimmed();
+ else // Named stash
+ {
+ // Parse stash name
+ name = (*line_it);
+ name = name.trimmed();
+ ++line_it;
+ }
+
+ stashes.insert(name, id);
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::stashApply(const QString& name)
+{
+ return runFossil(QStringList() << "stash" << "apply" << name);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::stashDrop(const QString& name)
+{
+ return runFossil(QStringList() << "stash" << "drop" << name);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::stashDiff(const QString& name)
+{
+ return runFossil(QStringList() << "stash" << "diff" << name, 0);
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::tagList(QStringMap& tags)
+{
+ tags.clear();
+ QStringList tagnames;
+
+ if(!runFossil(QStringList() << "tag" << "ls", &tagnames, RUNFLAGS_SILENT_ALL))
+ return false;
+
+ QStringList info;
+ foreach(const QString &line, tagnames)
+ {
+ QString tag = line.trimmed();
+
+ if(tag.isEmpty())
+ continue;
+
+ info.clear();
+
+ if(!runFossil(QStringList() << "info" << "tag:"+tag, &info, RUNFLAGS_SILENT_ALL))
+ return false;
+
+ QStringMap props;
+ ParseProperties(props, info, ':');
+ Q_ASSERT(props.contains("uuid"));
+
+ // uuid: 0e29a46f036d2e0cc89727190ad34c2dfdc5737c 2015-04-27 15:41:45 UTC
+ QStringList uuid = props["uuid"].trimmed().split(' ');
+ Q_ASSERT(uuid.length()>0);
+
+ QString revision = uuid[0].trimmed();
+ tags.insert(tag, revision);
+ }
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::tagNew(const QString& name, const QString& revision)
+{
+ QStringList res;
+
+ if(!runFossil(QStringList() << "tag" << "add" << name << revision, &res))
+ return false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::tagDelete(const QString& name, const QString &revision)
+{
+ QStringList res;
+
+ if(!runFossil(QStringList() << "tag" << "cancel" << "tag:"+name << revision, &res))
+ return false;
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::branchList(QStringList& branches, QStringList& activeBranches)
+{
+ branches.clear();
+ activeBranches.clear();
+ QStringList res;
+
+ if(!runFossil(QStringList() << "branch" , &res, RUNFLAGS_SILENT_ALL))
+ return false;
+
+ foreach(const QString &line, res)
+ {
+ QString name = line.trimmed();
+
+ if(name.isEmpty())
+ continue;
+
+ // Active branches start with a start
+ int active_index = name.indexOf('*');
+ bool is_active = (active_index != -1) && active_index==0;
+
+ // Strip
+ if(is_active)
+ {
+ name = name.mid(is_active+1).trimmed();
+ activeBranches.append(name);
+ }
+ else
+ branches.append(name);
+ }
+
+ branches.sort();
+ activeBranches.sort();
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::branchNew(const QString& name, const QString& revisionBasis, bool isPrivate)
+{
+ QStringList params;
+
+ params <<"branch" << "new" << name << revisionBasis;
+
+ if(isPrivate)
+ params << "--private";
+
+ QStringList res;
+
+ if(!runFossil(params, &res))
+ return false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::branchMerge(QStringList &res, const QString& revision, bool integrate, bool force, bool testOnly)
+{
+ QStringList params;
+
+ params <<"merge";
+
+ if(integrate)
+ params << "--integrate";
+
+ if(force)
+ params << "--force";
+
+ if(testOnly)
+ params << "--dry-run";
+
+ params << revision;
+
+ if(!runFossil(params, &res))
+ return false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+static QString ParseFossilQuery(QString line)
+{
+ // Extract question
+ int qend = line.lastIndexOf('(');
+ if(qend == -1)
+ qend = line.lastIndexOf('[');
+ Q_ASSERT(qend!=-1);
+ line = line.left(qend);
+ line = line.trimmed();
+ line += "?";
+ line[0]=QString(line[0]).toUpper()[0];
+ return line;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::runFossil(const QStringList &args, QStringList *output, int runFlags)
+{
+ int exit_code = EXIT_FAILURE;
+ if(!runFossilRaw(args, output, &exit_code, runFlags))
+ return false;
+
+ return exit_code == EXIT_SUCCESS;
+}
+
+//------------------------------------------------------------------------------
+// Run fossil. Returns true if execution was successful regardless if fossil
+// issued an error
+bool Fossil::runFossilRaw(const QStringList &args, QStringList *output, int *exitCode, int runFlags)
+{
+ bool silent_input = (runFlags & RUNFLAGS_SILENT_INPUT) != 0;
+ bool silent_output = (runFlags & RUNFLAGS_SILENT_OUTPUT) != 0;
+ bool detached = (runFlags & RUNFLAGS_DETACHED) != 0;
+
+ if(!silent_input)
+ {
+ QString params;
+ foreach(QString p, args)
+ {
+ if(p.indexOf(' ')!=-1)
+ params += '"' + p + "\" ";
+ else
+ params += p + ' ';
+ }
+ log("> fossil "+params+"
", true);
+ }
+
+ QString wkdir = getCurrentWorkspace();
+
+ QString fossil = getFossilPath();
+
+ // Detached processes use the command-line only, to avoid having to wait
+ // for the temporary args file to be released before returing
+ if(detached)
+ return QProcess::startDetached(fossil, args, wkdir);
+
+ // Make status message
+ QString status_msg = QObject::tr("Running Fossil");
+ if(args.length() > 0)
+ status_msg = QString("Fossil %0").arg(args[0].toCaseFolded());
+ ScopedStatus status(uiCallback, status_msg);
+
+ // Generate args file
+ const QStringList *final_args = &args;
+ QTemporaryFile args_file;
+ if(!args_file.open())
+ {
+ log(QObject::tr("Could not generate command line file"));
+ return false;
+ }
+
+ // Write BOM
+ args_file.write(reinterpret_cast(UTF8_BOM), sizeof(UTF8_BOM));
+
+ // Write Args
+ foreach(const QString &arg, args)
+ {
+ args_file.write(arg.toUtf8());
+ args_file.write("\n");
+ }
+ args_file.close();
+
+ // Replace args with args filename
+ QStringList run_args;
+ run_args.append("--args");
+ run_args.append(args_file.fileName());
+ final_args = &run_args;
+
+ // Create fossil process
+ // FIXME: when we are sure this works delete this
+ // LoggedProcess process(parentWidget*/);
+ LoggedProcess process(0);
+
+ process.setWorkingDirectory(wkdir);
+
+ process.start(fossil, *final_args);
+ if(!process.waitForStarted())
+ {
+ log(QObject::tr("Could not start Fossil executable '%0'").arg(fossil)+"\n");
+ return false;
+ }
+ const QChar EOL_MARK('\n');
+ QString ans_yes = 'y' + EOL_MARK;
+ QString ans_no = 'n' + EOL_MARK;
+ QString ans_always = 'a' + EOL_MARK;
+ QString ans_convert = 'c' + EOL_MARK;
+
+ operationAborted = false;
+ QString buffer;
+
+#ifdef Q_OS_WIN
+ QTextCodec *codec = QTextCodec::codecForName("UTF-8");
+#else
+ QTextCodec *codec = QTextCodec::codecForLocale();
+#endif
+
+ Q_ASSERT(codec);
+ QTextDecoder *decoder = codec->makeDecoder();
+ Q_ASSERT(decoder);
+
+ Q_ASSERT(uiCallback);
+#ifdef QT_DEBUG
+ size_t input_index = 0;
+#endif
+
+ while(true)
+ {
+ QProcess::ProcessState state = process.state();
+ qint64 bytes_avail = process.logBytesAvailable();
+
+ if(state!=QProcess::Running && bytes_avail<1)
+ break;
+
+ if(operationAborted)
+ {
+ log("\n* "+QObject::tr("Terminated")+" *\n");
+ #ifdef Q_OS_WIN // Verify this is still true on Qt5
+ process.kill(); // QT on windows cannot terminate console processes with QProcess::terminate
+ #else
+ process.terminate();
+ #endif
+ break;
+ }
+
+ QByteArray input;
+ process.getLogAndClear(input);
+
+ #ifdef QT_DEBUG // Log fossil output in debug builds
+ if(!input.isEmpty() && (runFlags & RUNFLAGS_DEBUG) )
+ qDebug() << "[" << ++input_index << "] '" << input.data() << "'\n";
+ #endif
+
+ buffer += decoder->toUnicode(input);
+#if 0 // Keep this for now to simulate bad parses
+ if(runFlags & RUNFLAGS_DEBUG)
+ {
+ static int debug=1;
+ if(debug==1)
+ {
+ buffer = " 4: [df2233aabe09ef] on 2013-02-03 02:51:33\n"
+ " History\n"
+ " 5: [df2233aabe09ef] on 2013-02-15 03:55:37\n"
+ " ";
+ debug++;
+ }
+ else if(debug==2)
+ {
+ buffer = " Diff\n"
+ " ";
+ debug++;
+ }
+ else
+ buffer="";
+ }
+#endif
+ #ifdef QT_DEBUG // breakpoint
+ //if(buffer.indexOf("SQLITE_CANTOPEN")!=-1)
+ // qDebug() << "Breakpoint\n";
+ #endif
+
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+
+ if(buffer.isEmpty())
+ continue;
+
+ // Normalize line endings
+ buffer = buffer.replace("\r\n", "\n");
+ buffer = buffer.replace("\r", "\n");
+
+ // Extract the last line
+ int last_line_start = buffer.lastIndexOf(EOL_MARK);
+
+ QString last_line;
+ QString before_last_line;
+ if(last_line_start != -1)
+ {
+ last_line = buffer.mid(last_line_start+1); // Including the EOL
+
+ // Detect previous line
+ if(last_line_start>0)
+ {
+ int before_last_line_start = buffer.lastIndexOf(EOL_MARK, last_line_start-1);
+
+ // No new-line before ?
+ if(before_last_line_start==-1)
+ before_last_line_start = 0; // Use entire line
+ else
+ ++before_last_line_start; // Skip new-line
+
+ // Extract previous line
+ before_last_line = buffer.mid(before_last_line_start, last_line_start-before_last_line_start);
+ }
+ }
+ else
+ last_line = buffer;
+
+ last_line = last_line.trimmed();
+
+ // Check if we have a query
+ bool ends_qmark = !last_line.isEmpty() && last_line[last_line.length()-1]=='?';
+ bool have_yn_query = last_line.toLower().indexOf("y/n")!=-1;
+ bool have_yna_query = last_line.toLower().indexOf("a=always/y/n")!=-1 || last_line.toLower().indexOf("yes/no/all")!=-1 || last_line.toLower().indexOf("a=all/y/n")!=-1;
+ bool have_an_query = last_line.toLower().indexOf("a=always/n")!=-1;
+ bool have_acyn_query = last_line.toLower().indexOf("a=all/c=convert/y/n")!=-1;
+
+ bool have_query = ends_qmark && (have_yn_query || have_yna_query || have_an_query || have_acyn_query);
+
+ // Flush all complete lines to the log and output
+ QStringList log_lines = buffer.left(last_line_start).split(EOL_MARK);
+ for(int l=0; lappend(line);
+
+ if(!silent_output)
+ log(line+"\n");
+ }
+
+ // Remove everything we processed (including the EOL)
+ buffer = buffer.mid(last_line_start+1) ;
+
+ // Now process any query
+ if(have_query && (have_yna_query || have_acyn_query))
+ {
+ log(last_line);
+ QString query = ParseFossilQuery(last_line);
+ QMessageBox::StandardButtons buttons = QMessageBox::YesToAll|QMessageBox::Yes|QMessageBox::No;
+
+ // Add any extra text available to the query
+ before_last_line = before_last_line.trimmed();
+ if(!before_last_line.isEmpty())
+ query = before_last_line + "\n" + query;
+
+ // Map the Convert option to the Apply button
+ if(have_acyn_query)
+ buttons |= QMessageBox::Apply;
+
+ QMessageBox::StandardButton res = uiCallback->Query("Fossil", query, buttons);
+ if(res==QMessageBox::Yes)
+ {
+ process.write(ans_yes.toLatin1());
+ log("Y\n");
+ }
+ else if(res==QMessageBox::YesAll)
+ {
+ process.write(ans_always.toLatin1());
+ log("A\n");
+ }
+ else if(res==QMessageBox::Apply)
+ {
+ process.write(ans_convert.toLatin1());
+ log("C\n");
+ }
+ else
+ {
+ process.write(ans_no.toLatin1());
+ log("N\n");
+ }
+ buffer.clear();
+ }
+ else if(have_query && have_yn_query)
+ {
+ log(last_line);
+ QString query = ParseFossilQuery(last_line);
+ QMessageBox::StandardButton res = uiCallback->Query("Fossil", query, QMessageBox::Yes|QMessageBox::No);
+
+ if(res==QMessageBox::Yes)
+ {
+ process.write(ans_yes.toLatin1());
+ log("Y\n");
+ }
+ else
+ {
+ process.write(ans_no.toLatin1());
+ log("N\n");
+ }
+
+ buffer.clear();
+ }
+ else if(have_query && have_an_query)
+ {
+ log(last_line);
+ QString query = ParseFossilQuery(last_line);
+ QMessageBox::StandardButton res = uiCallback->Query("Fossil", query, QMessageBox::YesToAll|QMessageBox::No);
+ if(res==QMessageBox::YesAll)
+ {
+ process.write(ans_always.toLatin1());
+ log("A\n");
+ }
+ else
+ {
+ process.write(ans_no.toLatin1());
+ log("N\n");
+ }
+ buffer.clear();
+ }
+ }
+
+ delete decoder;
+
+ // Must be finished by now
+ Q_ASSERT(process.state()==QProcess::NotRunning);
+
+ QProcess::ExitStatus es = process.exitStatus();
+
+ if(es!=QProcess::NormalExit)
+ return false;
+
+ if(exitCode)
+ *exitCode = process.exitCode();
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+QString Fossil::getFossilPath()
+{
+ // Use the user-specified fossil if available
+ QString fossil_path = fossilPath;
+ if(!fossil_path.isEmpty())
+ return QDir::toNativeSeparators(fossil_path);
+
+ QString fossil_exe = "fossil";
+#ifdef Q_OS_WIN
+ fossil_exe += ".exe";
+#endif
+ // Use our fossil if available
+ QString fuel_fossil = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + fossil_exe);
+
+ if(QFile::exists(fuel_fossil))
+ return fuel_fossil;
+
+ // Otherwise assume there is a "fossil" executable in the path
+ return fossil_exe;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::isWorkspace(const QString &path)
+{
+ if(path.length()==0)
+ return false;
+
+ QFileInfo fi(path);
+ QString wkspace = path;
+ wkspace = fi.absoluteDir().absolutePath();
+ QString checkout_file1 = wkspace + PATH_SEPARATOR + FOSSIL_CHECKOUT1;
+ QString checkout_file2 = wkspace + PATH_SEPARATOR + FOSSIL_CHECKOUT2;
+
+ return (QFileInfo(checkout_file1).exists() || QFileInfo(checkout_file2).exists());
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::uiRunning() const
+{
+ return fossilUI.state() == QProcess::Running;
+}
+
+//------------------------------------------------------------------------------
+bool Fossil::startUI(const QString &httpPort)
+{
+ if(uiRunning())
+ {
+ log(QObject::tr("Fossil UI is already running")+"\n");
+ return true;
+ }
+
+ // FIXME: when we are sure this works delete this
+ //fossilUI.setParent(parentWidget);
+
+ fossilUI.setProcessChannelMode(QProcess::MergedChannels);
+ fossilUI.setWorkingDirectory(getCurrentWorkspace());
+
+ log("> fossil ui
", true);
+ log(QObject::tr("Starting Fossil browser UI. Please wait.")+"\n");
+ QString fossil = getFossilPath();
+
+ QStringList params;
+ params << "server" << "--localauth";
+
+ if(!httpPort.isEmpty())
+ params << "-P" << httpPort;
+
+ fossilUI.start(getFossilPath(), params);
+
+ if(!fossilUI.waitForStarted() || fossilUI.state()!=QProcess::Running)
+ {
+ log(QObject::tr("Could not start Fossil executable '%s'").arg(fossil)+"\n");
+ return false;
+ }
+
+#ifdef Q_OS_WIN
+ QTextCodec *codec = QTextCodec::codecForName("UTF-8");
+#else
+ QTextCodec *codec = QTextCodec::codecForLocale();
+#endif
+
+ Q_ASSERT(codec);
+ QTextDecoder *decoder = codec->makeDecoder();
+ Q_ASSERT(decoder);
+
+ fossilUIPort.clear();
+
+ // Wait for fossil to report the http port
+ QString buffer;
+ while(true)
+ {
+ QProcess::ProcessState state = fossilUI.state();
+ qint64 bytes_avail = fossilUI.logBytesAvailable();
+
+ if(state!=QProcess::Running && bytes_avail<1)
+ break;
+
+ QByteArray input;
+ fossilUI.getLogAndClear(input);
+
+ buffer += decoder->toUnicode(input);
+
+ // Normalize line endings
+ buffer = buffer.replace("\r\n", "\n");
+ buffer = buffer.replace("\r", "\n");
+
+ int index = REGEX_PORT.indexIn(buffer);
+ if(index==-1)
+ {
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ continue;
+ }
+
+ // Extract port
+ fossilUIPort = REGEX_PORT.cap(1).trimmed();
+
+ // Done parsing
+ break;
+ }
+ return true;
+}
+
+//------------------------------------------------------------------------------
+void Fossil::stopUI()
+{
+ if(uiRunning())
+ {
+#ifdef Q_WS_WIN
+ fossilUI.kill(); // QT on windows cannot terminate console processes with QProcess::terminate
+#else
+ fossilUI.terminate();
+#endif
+ }
+ fossilUI.close();
+ fossilUIPort.clear();
+}
+
+//------------------------------------------------------------------------------
+QString Fossil::getUIHttpAddress() const
+{
+ if(fossilUIPort.isEmpty())
+ return QString();
+ return "http://127.0.0.1:"+fossilUIPort;
+}
diff --git a/src/Fossil.h b/src/Fossil.h
new file mode 100644
index 0000000..1925f3d
--- /dev/null
+++ b/src/Fossil.h
@@ -0,0 +1,153 @@
+#ifndef FOSSIL_H
+#define FOSSIL_H
+
+class QStringList;
+#include
+#include
+#include
+#include "LoggedProcess.h"
+#include "Utils.h"
+
+typedef QMap stashmap_t;
+
+#define PATH_SEPARATOR "/"
+
+enum RunFlags
+{
+ RUNFLAGS_NONE = 0<<0,
+ RUNFLAGS_SILENT_INPUT = 1<<0,
+ RUNFLAGS_SILENT_OUTPUT = 1<<1,
+ RUNFLAGS_SILENT_ALL = RUNFLAGS_SILENT_INPUT | RUNFLAGS_SILENT_OUTPUT,
+ RUNFLAGS_DETACHED = 1<<2,
+ RUNFLAGS_DEBUG = 1<<3,
+};
+
+enum RepoStatus
+{
+ REPO_OK,
+ REPO_NOT_FOUND,
+ REPO_OLD_SCHEMA
+};
+
+class Fossil
+{
+public:
+ Fossil()
+ : operationAborted(false)
+ , uiCallback(0)
+ {
+ }
+
+ void Init(UICallback *callback)
+ {
+ uiCallback = callback;
+ fossilPath.clear();
+ currentWorkspace.clear();
+ }
+
+ bool runFossil(const QStringList &args, QStringList *output=0, int runFlags=RUNFLAGS_NONE);
+ bool runFossilRaw(const QStringList &args, QStringList *output, int *exitCode, int runFlags);
+
+ static bool isWorkspace(const QString &path);
+
+ RepoStatus getRepoStatus();
+
+ void setCurrentWorkspace(const QString &workspace)
+ {
+ currentWorkspace = workspace;
+ }
+
+ const QString &getCurrentWorkspace() const
+ {
+ return currentWorkspace;
+ }
+
+
+ const QString &getProjectName() const
+ {
+ return projectName;
+ }
+
+ const QString &getRepositoryFile() const
+ {
+ return repositoryFile;
+ }
+
+ void setRepositoryFile(const QString &filename)
+ {
+ repositoryFile = filename;
+ }
+
+ bool openRepository(const QString &repositoryPath, const QString& workspacePath);
+ bool newRepository(const QString &repositoryPath);
+ bool closeRepository();
+ bool pushRepository();
+ bool pullRepository();
+ bool cloneRepository(const QString &repository, const QUrl &url, const QUrl &proxyUrl);
+ bool undoRepository(QStringList& result, bool explainOnly);
+ bool updateRepository(QStringList& result, const QString& revision, bool explainOnly);
+ bool getFossilVersion(QString &version);
+
+ bool uiRunning() const;
+ bool startUI(const QString &httpPort);
+ void stopUI();
+
+ bool listFiles(QStringList &files);
+ bool status(QStringList& result);
+
+ bool diffFile(const QString &repoFile);
+ bool commitFiles(const QStringList &fileList, const QString &comment, const QString& newBranchName, bool isPrivateBranch);
+ bool addFiles(const QStringList& fileList);
+ bool removeFiles(const QStringList& fileList, bool deleteLocal);
+ bool revertFiles(const QStringList& fileList);
+ bool renameFile(const QString& beforePath, const QString& afterPath, bool renameLocal);
+ bool getFossilSettings(QStringList& result);
+ bool setFossilSetting(const QString &name, const QString &value, bool global);
+ bool setRemoteUrl(const QString &url);
+ bool getRemoteUrl(QString &url);
+
+ bool stashNew(const QStringList& fileList, const QString& name, bool revert);
+ bool stashList(stashmap_t &stashes);
+ bool stashApply(const QString& name);
+ bool stashDrop(const QString& name);
+ bool stashDiff(const QString& name);
+
+ void abortOperation() { operationAborted = true; }
+
+ bool tagList(QStringMap& tags);
+ bool tagNew(const QString& name, const QString& revision);
+ bool tagDelete(const QString& name, const QString& revision);
+
+ bool branchList(QStringList& branches, QStringList& activeBranches);
+ bool branchNew(const QString& name, const QString& revisionBasis, bool isPrivate=false);
+ bool branchMerge(QStringList& res, const QString& revision, bool integrate, bool force, bool testOnly);
+
+ const QString &getCurrentRevision() const { return currentRevision; }
+ const QStringList &getCurrentTags() const { return currentTags; }
+
+ const QString &getUIHttpPort() const { return fossilUIPort; }
+ QString getUIHttpAddress() const;
+
+private:
+ void log(const QString &text, bool isHTML=false)
+ {
+ if(uiCallback)
+ uiCallback->logText(text, isHTML);
+ }
+
+ QString getFossilPath();
+
+ bool operationAborted;
+ UICallback *uiCallback;
+ QString currentWorkspace;
+ QString fossilPath; // The value from the settings
+ QString repositoryFile;
+ QString projectName;
+ QString currentRevision;
+ QStringList currentTags;
+ LoggedProcess fossilUI;
+ QString fossilUIPort;
+};
+
+
+#endif // FOSSIL_H
diff --git a/src/FslSettingsDialog.cpp b/src/FslSettingsDialog.cpp
new file mode 100644
index 0000000..62de753
--- /dev/null
+++ b/src/FslSettingsDialog.cpp
@@ -0,0 +1,72 @@
+#include "FslSettingsDialog.h"
+#include "ui_FslSettingsDialog.h"
+#include "Utils.h"
+
+#include
+
+///////////////////////////////////////////////////////////////////////////////
+FslSettingsDialog::FslSettingsDialog(QWidget *parent, Settings &_settings) :
+ QDialog(parent, Qt::Sheet),
+ ui(new Ui::FslSettingsDialog),
+ settings(&_settings)
+{
+ ui->setupUi(this);
+
+ ui->lineUIPort->setText(settings->GetValue(FOSSIL_SETTING_HTTP_PORT).toString());
+
+ // Global Settings
+ ui->lineGDiffCommand->setText(settings->GetFossilValue(FOSSIL_SETTING_GDIFF_CMD).toString());
+ ui->lineGMergeCommand->setText(settings->GetFossilValue(FOSSIL_SETTING_GMERGE_CMD).toString());
+ ui->lineProxy->setText(settings->GetFossilValue(FOSSIL_SETTING_PROXY_URL).toString());
+
+ // Repository Settings
+ ui->lineRemoteURL->setText(settings->GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString());
+ ui->lineIgnore->setText(settings->GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString());
+ ui->lineIgnoreCRNL->setText(settings->GetFossilValue(FOSSIL_SETTING_CRNL_GLOB).toString());
+}
+
+//-----------------------------------------------------------------------------
+FslSettingsDialog::~FslSettingsDialog()
+{
+ delete ui;
+}
+
+//-----------------------------------------------------------------------------
+bool FslSettingsDialog::run(QWidget *parent, Settings &settings)
+{
+ FslSettingsDialog dlg(parent, settings);
+ return dlg.exec() == QDialog::Accepted;
+}
+
+//-----------------------------------------------------------------------------
+void FslSettingsDialog::on_buttonBox_accepted()
+{
+ settings->SetValue(FOSSIL_SETTING_HTTP_PORT, ui->lineUIPort->text());
+
+ settings->SetFossilValue(FOSSIL_SETTING_GDIFF_CMD, ui->lineGDiffCommand->text());
+ settings->SetFossilValue(FOSSIL_SETTING_GMERGE_CMD, ui->lineGMergeCommand->text());
+ settings->SetFossilValue(FOSSIL_SETTING_PROXY_URL, ui->lineProxy->text());
+
+ settings->SetFossilValue(FOSSIL_SETTING_REMOTE_URL, ui->lineRemoteURL->text());
+ settings->SetFossilValue(FOSSIL_SETTING_IGNORE_GLOB, ui->lineIgnore->text());
+ settings->SetFossilValue(FOSSIL_SETTING_CRNL_GLOB, ui->lineIgnoreCRNL->text());
+
+ settings->ApplyEnvironment();
+}
+
+//-----------------------------------------------------------------------------
+void FslSettingsDialog::on_btnSelectFossilGDiff_clicked()
+{
+ QString path = SelectExe(this, tr("Select Graphical Diff application"));
+ if(!path.isEmpty())
+ ui->lineGDiffCommand->setText(QDir::toNativeSeparators(path));
+}
+
+//-----------------------------------------------------------------------------
+void FslSettingsDialog::on_btnSelectGMerge_clicked()
+{
+ QString path = SelectExe(this, tr("Select Graphical Merge application"));
+ if(!path.isEmpty())
+ ui->lineGMergeCommand->setText(path);
+}
+
diff --git a/src/FslSettingsDialog.h b/src/FslSettingsDialog.h
new file mode 100644
index 0000000..ed71046
--- /dev/null
+++ b/src/FslSettingsDialog.h
@@ -0,0 +1,33 @@
+#ifndef FSLSETTINGSDIALOG_H
+#define FSLSETTINGSDIALOG_H
+
+#include
+#include "Settings.h"
+
+namespace Ui {
+ class FslSettingsDialog;
+}
+
+class FslSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit FslSettingsDialog(QWidget *parent, Settings &_settings);
+ ~FslSettingsDialog();
+
+ static bool run(QWidget *parent, Settings &_settings);
+
+
+private slots:
+ void on_buttonBox_accepted();
+ void on_btnSelectFossilGDiff_clicked();
+ void on_btnSelectGMerge_clicked();
+
+private:
+
+ Ui::FslSettingsDialog *ui;
+ Settings *settings;
+};
+
+#endif // FSLSETTINGSDIALOG_H
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 3a7e9ff..09896a2 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -7,32 +7,21 @@
#include
#include
#include
-#include
-#include
#include
-#include
#include
+#include
#include
#include
-#include
-#include
-#include
-#include
+#include "SettingsDialog.h"
+#include "FslSettingsDialog.h"
+#include "SearchBox.h"
#include "CommitDialog.h"
#include "FileActionDialog.h"
#include "CloneDialog.h"
+#include "RevisionDialog.h"
#include "Utils.h"
-#include "LoggedProcess.h"
-
-#define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
-
-#define PATH_SEP "/"
-
-static const unsigned char UTF8_BOM[] = { 0xEF, 0xBB, 0xBF };
-
-// 19: [5c46757d4b9765] on 2012-04-22 04:41:15
-static const QRegExp REGEX_STASH("\\s*(\\d+):\\s+\\[(.*)\\] on (\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)", Qt::CaseInsensitive);
+#define REVISION_LATEST "Latest revision"
//-----------------------------------------------------------------------------
enum
@@ -52,70 +41,51 @@ enum
enum
{
- REPODIRMODEL_ROLE_PATH = Qt::UserRole+1
+ ROLE_WORKSPACE_ITEM = Qt::UserRole+1
};
-//-----------------------------------------------------------------------------
-static QString QuotePath(const QString &path)
+struct WorkspaceItem
{
- return path;
-}
-
-//-----------------------------------------------------------------------------
-static QStringList QuotePaths(const QStringList &paths)
-{
- QStringList res;
- for(int i=0; i QStringMap;
-static QStringMap MakeKeyValues(QStringList lines)
-{
- QStringMap res;
-
- foreach(QString l, lines)
+ enum
{
- l = l.trimmed();
- int index = l.indexOf(' ');
+ TYPE_UNKNOWN,
+ TYPE_WORKSPACE,
+ TYPE_FOLDER,
+ TYPE_STASHES,
+ TYPE_STASH,
+ TYPE_BRANCHES,
+ TYPE_BRANCH,
+ TYPE_TAGS,
+ TYPE_TAG,
+ TYPE_REMOTES,
+ TYPE_SETTINGS
+ };
- QString key;
- QString value;
- if(index!=-1)
- {
- key = l.left(index).trimmed();
- value = l.mid(index).trimmed();
- }
- else
- key = l;
-
- res.insert(key, value);
- }
- return res;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-class ScopedStatus
-{
-public:
- ScopedStatus(const QString &text, Ui::MainWindow *mw, QProgressBar *bar) : ui(mw), progressBar(bar)
+ WorkspaceItem()
+ : Type(TYPE_UNKNOWN)
{
- ui->statusBar->showMessage(text);
- progressBar->setHidden(false);
}
- ~ScopedStatus()
+ WorkspaceItem(int type, const QString &value)
+ : Type(type), Value(value)
{
- ui->statusBar->clearMessage();
- progressBar->setHidden(true);
}
-private:
- Ui::MainWindow *ui;
- QProgressBar *progressBar;
+
+ WorkspaceItem(const WorkspaceItem &other)
+ {
+ Type = other.Type;
+ Value = other.Value;
+ }
+
+ int Type;
+ QString Value;
+
+ operator QVariant() const
+ {
+ return QVariant::fromValue(*this);
+ }
};
+Q_DECLARE_METATYPE(WorkspaceItem)
///////////////////////////////////////////////////////////////////////////////
MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspacePath) :
@@ -128,59 +98,81 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
QAction *separator = new QAction(this);
separator->setSeparator(true);
- // TableView
- ui->tableView->setModel(&repoFileModel);
+ // fileTableView
+ ui->fileTableView->setModel(&getWorkspace().getFileModel());
- ui->tableView->addAction(ui->actionDiff);
- ui->tableView->addAction(ui->actionHistory);
- ui->tableView->addAction(ui->actionOpenFile);
- ui->tableView->addAction(ui->actionOpenContaining);
- ui->tableView->addAction(separator);
- ui->tableView->addAction(ui->actionAdd);
- ui->tableView->addAction(ui->actionRevert);
- ui->tableView->addAction(ui->actionRename);
- ui->tableView->addAction(ui->actionDelete);
- connect( ui->tableView,
+ ui->fileTableView->addAction(ui->actionDiff);
+ ui->fileTableView->addAction(ui->actionHistory);
+ ui->fileTableView->addAction(ui->actionOpenFile);
+ ui->fileTableView->addAction(ui->actionOpenContaining);
+ ui->fileTableView->addAction(separator);
+ ui->fileTableView->addAction(ui->actionAdd);
+ ui->fileTableView->addAction(ui->actionRevert);
+ ui->fileTableView->addAction(ui->actionRename);
+ ui->fileTableView->addAction(ui->actionDelete);
+ connect( ui->fileTableView,
SIGNAL( dragOutEvent() ),
SLOT( onFileViewDragOut() ),
Qt::DirectConnection );
QStringList header;
header << tr("Status") << tr("File") << tr("Extension") << tr("Modified") << tr("Path");
- repoFileModel.setHorizontalHeaderLabels(header);
- repoFileModel.horizontalHeaderItem(COLUMN_STATUS)->setTextAlignment(Qt::AlignCenter);
+ getWorkspace().getFileModel().setHorizontalHeaderLabels(header);
+ getWorkspace().getFileModel().horizontalHeaderItem(COLUMN_STATUS)->setTextAlignment(Qt::AlignCenter);
// Needed on OSX as the preset value from the GUI editor is not always reflected
- ui->tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
+ ui->fileTableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
- ui->tableView->horizontalHeader()->setMovable(true);
+ ui->fileTableView->horizontalHeader()->setMovable(true);
#else
- ui->tableView->horizontalHeader()->setSectionsMovable(true);
+ ui->fileTableView->horizontalHeader()->setSectionsMovable(true);
#endif
- ui->tableView->horizontalHeader()->setStretchLastSection(true);
+ ui->fileTableView->horizontalHeader()->setStretchLastSection(true);
- // TreeView
- ui->treeView->setModel(&repoDirModel);
- connect( ui->treeView->selectionModel(),
+ // workspaceTreeView
+ ui->workspaceTreeView->setModel(&getWorkspace().getTreeModel());
+
+ header.clear();
+ header << tr("Workspace");
+ getWorkspace().getTreeModel().setHorizontalHeaderLabels(header);
+
+ connect( ui->workspaceTreeView->selectionModel(),
SIGNAL( selectionChanged(const QItemSelection &, const QItemSelection &) ),
- SLOT( onTreeViewSelectionChanged(const QItemSelection &, const QItemSelection &) ),
+ SLOT( onWorkspaceTreeViewSelectionChanged(const QItemSelection &, const QItemSelection &) ),
Qt::DirectConnection );
- ui->treeView->addAction(ui->actionCommit);
- ui->treeView->addAction(ui->actionOpenFolder);
- ui->treeView->addAction(ui->actionAdd);
- ui->treeView->addAction(ui->actionRevert);
- ui->treeView->addAction(ui->actionDelete);
- ui->treeView->addAction(separator);
- ui->treeView->addAction(ui->actionRenameFolder);
- ui->treeView->addAction(ui->actionOpenFolder);
+ // Workspace Menus
+ menuWorkspace = new QMenu(this);
+ menuWorkspace->addAction(ui->actionCommit);
+ menuWorkspace->addAction(ui->actionOpenFolder);
+ menuWorkspace->addAction(ui->actionAdd);
+ menuWorkspace->addAction(ui->actionRevert);
+ menuWorkspace->addAction(ui->actionDelete);
+ menuWorkspace->addAction(separator);
+ menuWorkspace->addAction(ui->actionRenameFolder);
+ menuWorkspace->addAction(ui->actionOpenFolder);
- // StashView
- ui->tableViewStash->setModel(&repoStashModel);
- ui->tableViewStash->addAction(ui->actionApplyStash);
- ui->tableViewStash->addAction(ui->actionDiffStash);
- ui->tableViewStash->addAction(ui->actionDeleteStash);
- ui->tableViewStash->horizontalHeader()->setSortIndicatorShown(false);
+ // StashMenu
+ menuStashes = new QMenu(this);
+ menuStashes->addAction(ui->actionCreateStash);
+ menuStashes->addAction(separator);
+ menuStashes->addAction(ui->actionApplyStash);
+ menuStashes->addAction(ui->actionDiffStash);
+ menuStashes->addAction(ui->actionDeleteStash);
+
+ // TagsMenu
+ menuTags = new QMenu(this);
+ menuTags->addAction(ui->actionCreateTag);
+ menuTags->addAction(separator);
+ menuTags->addAction(ui->actionDeleteTag);
+ menuTags->addAction(ui->actionUpdate);
+
+ // BranchesMenu
+ menuBranches = new QMenu(this);
+ menuBranches->addAction(ui->actionCreateBranch);
+ menuBranches->addAction(separator);
+ menuBranches->addAction(ui->actionMergeBranch);
+ menuBranches->addAction(ui->actionUpdate);
// Recent Workspaces
// Locate a sequence of two separator actions in file menu
@@ -207,6 +199,15 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
// TabWidget
ui->tabWidget->setCurrentIndex(TAB_LOG);
+ lblRevision = new QLabel();
+ ui->statusBar->insertPermanentWidget(0, lblRevision);
+ lblRevision->setVisible(true);
+
+ lblTags = new QLabel();
+ ui->statusBar->insertPermanentWidget(1, lblTags);
+ lblTags->setVisible(true);
+
+
// Construct ProgressBar
progressBar = new QProgressBar();
progressBar->setMinimum(0);
@@ -214,9 +215,10 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
progressBar->setMaximumSize(170, 16);
progressBar->setAlignment(Qt::AlignCenter);
progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
- ui->statusBar->insertPermanentWidget(0, progressBar);
+ ui->statusBar->insertPermanentWidget(2, progressBar);
progressBar->setVisible(false);
+
#ifdef Q_OS_MACX
// Native applications on OSX don't have menu icons
foreach(QAction *a, ui->menuBar->actions())
@@ -230,15 +232,43 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
abortShortcut->setEnabled(false);
connect(abortShortcut, SIGNAL(activated()), this, SLOT(onAbort()));
+ // Searchbox
+ // Add spacer to pad to right
+ QWidget* spacer = new QWidget();
+ spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ ui->mainToolBar->addWidget(spacer);
+
+ // Search shortcut
+ searchShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this);
+ searchShortcut->setContext(Qt::ApplicationShortcut);
+ searchShortcut->setEnabled(true);
+ connect(searchShortcut, SIGNAL(activated()), this, SLOT(onSearch()));
+
+
+ // Create SearchBox
+ searchBox = new SearchBox(this);
+ searchBox->setPlaceholderText(tr("Find (%0)").arg(searchShortcut->key().toString()));
+ searchBox->setMaximumWidth(450);
+ ui->mainToolBar->addWidget(searchBox);
+
+ connect( searchBox,
+ SIGNAL( textChanged(const QString&)),
+ SLOT( onSearchBoxTextChanged(const QString&)),
+ Qt::DirectConnection );
+
viewMode = VIEWMODE_TREE;
+ uiCallback.init(this);
+ // Need to be before applySettings which sets the last workspace
+ fossil().Init(&uiCallback);
+
applySettings();
// Apply any explicit workspace path if available
if(workspacePath && !workspacePath->isEmpty())
openWorkspace(*workspacePath);
- abortOperation = false;
+ operationAborted = false;
rebuildRecent();
}
@@ -249,16 +279,13 @@ MainWindow::~MainWindow()
stopUI();
updateSettings();
- // Dispose RepoFiles
- for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it)
- delete *it;
-
delete ui;
}
+
//-----------------------------------------------------------------------------
const QString &MainWindow::getCurrentWorkspace()
{
- return currentWorkspace;
+ return fossil().getCurrentWorkspace();
}
//-----------------------------------------------------------------------------
@@ -266,13 +293,14 @@ void MainWindow::setCurrentWorkspace(const QString &workspace)
{
if(workspace.isEmpty())
{
- currentWorkspace.clear();
+ fossil().setCurrentWorkspace("");
return;
}
QString new_workspace = QFileInfo(workspace).absoluteFilePath();
- currentWorkspace = new_workspace;
+ fossil().setCurrentWorkspace(new_workspace);
+
addWorkspace(new_workspace);
if(!QDir::setCurrent(new_workspace))
@@ -313,8 +341,8 @@ bool MainWindow::openWorkspace(const QString &path)
if(fi.isFile())
{
wkspace = fi.absoluteDir().absolutePath();
- QString checkout_file1 = wkspace + PATH_SEP + FOSSIL_CHECKOUT1;
- QString checkout_file2 = wkspace + PATH_SEP + FOSSIL_CHECKOUT2;
+ QString checkout_file1 = wkspace + PATH_SEPARATOR + FOSSIL_CHECKOUT1;
+ QString checkout_file2 = wkspace + PATH_SEPARATOR + FOSSIL_CHECKOUT2;
if(!(QFileInfo(checkout_file1).exists() || QFileInfo(checkout_file2).exists()) )
{
@@ -329,21 +357,13 @@ bool MainWindow::openWorkspace(const QString &path)
return false;
}
- // Ok open the fossil
- setCurrentWorkspace(wkspace);
- if(!QDir::setCurrent(wkspace))
- {
- QMessageBox::critical(this, tr("Error"), tr("Could not change current directory"), QMessageBox::Ok );
- return false;
- }
-
- repositoryFile = fi.absoluteFilePath();
-
- if(!runFossil(QStringList() << "open" << QuotePath(repositoryFile)))
+ // Ok open the repository file
+ if(!fossil().openRepository(fi.absoluteFilePath(), wkspace))
{
QMessageBox::critical(this, tr("Error"), tr("Could not open repository."), QMessageBox::Ok );
return false;
}
+
}
else
{
@@ -379,6 +399,7 @@ bool MainWindow::openWorkspace(const QString &path)
// Select the Root of the tree to update the file view
selectRootDir();
+ searchBox->clear();
return true;
}
@@ -440,20 +461,18 @@ void MainWindow::on_actionNewRepository_triggered()
stopUI();
on_actionClearLog_triggered();
- repositoryFile = repo_path_info.absoluteFilePath();
-
// Create repository
- if(!runFossil(QStringList() << "new" << QuotePath(repositoryFile)))
+ QString repo_abs_path = repo_path_info.absoluteFilePath();
+
+ if(!fossil().newRepository(repo_abs_path))
{
QMessageBox::critical(this, tr("Error"), tr("Could not create repository."), QMessageBox::Ok );
return;
}
- // Create workspace
- setCurrentWorkspace(wkdir);
- if(!QDir::setCurrent(wkdir))
+ if(!fossil().openRepository(repo_abs_path, wkdir))
{
- QMessageBox::critical(this, tr("Error"), tr("Could not change current directory"), QMessageBox::Ok );
+ QMessageBox::critical(this, tr("Error"), tr("Could not open repository."), QMessageBox::Ok );
return;
}
@@ -461,26 +480,20 @@ void MainWindow::on_actionNewRepository_triggered()
if(!ui->actionViewUnknown->isChecked())
ui->actionViewUnknown->setChecked(true);
- // Open repo
- if(!runFossil(QStringList() << "open" << QuotePath(repositoryFile)))
- {
- QMessageBox::critical(this, tr("Error"), tr("Could not open repository."), QMessageBox::Ok );
- return;
- }
refresh();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionCloseRepository_triggered()
{
- if(getRepoStatus()!=REPO_OK)
+ if(fossil().getRepoStatus()!=REPO_OK)
return;
if(QMessageBox::Yes !=DialogQuery(this, tr("Close Workspace"), tr("Are you sure you want to close this workspace?")))
return;
// Close Repo
- if(!runFossil(QStringList() << "close"))
+ if(!fossil().closeRepository())
{
QMessageBox::critical(this, tr("Error"), tr("Cannot close the workspace.\nAre there still uncommitted changes available?"), QMessageBox::Ok );
return;
@@ -502,32 +515,7 @@ void MainWindow::on_actionCloneRepository_triggered()
stopUI();
- // Actual command
- QStringList cmd = QStringList() << "clone";
-
- // Log Command
- QStringList logcmd = QStringList() << "fossil" << "clone";
-
- QString source = url.toString();
- QString logsource = url.toString(QUrl::RemovePassword);
- if(url.isLocalFile())
- {
- source = url.toLocalFile();
- logsource = source;
- }
- cmd << source << repository;
- logcmd << logsource << repository;
-
- if(!url_proxy.isEmpty())
- {
- cmd << "--proxy" << url_proxy.toString();
- logcmd << "--proxy" << url_proxy.toString(QUrl::RemovePassword);
- }
-
- log(">"+logcmd.join(" ")+"
", true);
-
- // Clone Repo
- if(!runFossil(cmd, 0, RUNFLAGS_SILENT_INPUT))
+ if(!fossil().cloneRepository(repository, url, url_proxy))
{
QMessageBox::critical(this, tr("Error"), tr("Could not clone the repository"), QMessageBox::Ok);
return;
@@ -552,7 +540,6 @@ void MainWindow::rebuildRecent()
recentWorkspaceActs[i]->setData(workspaceHistory[i]);
recentWorkspaceActs[i]->setVisible(true);
}
-
}
//------------------------------------------------------------------------------
@@ -567,86 +554,67 @@ void MainWindow::onOpenRecent()
openWorkspace(workspace);
}
-//------------------------------------------------------------------------------
-bool MainWindow::scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool &abort)
-{
- QDir dir(dirPath);
-
- setStatus(dirPath);
- QCoreApplication::processEvents();
-
- QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
- for (int i=0; iactionCommit->setEnabled(on);
- ui->actionDiff->setEnabled(on);
- ui->actionAdd->setEnabled(on);
- ui->actionDelete->setEnabled(on);
- ui->actionPush->setEnabled(on);
- ui->actionPull->setEnabled(on);
- ui->actionRename->setEnabled(on);
- ui->actionHistory->setEnabled(on);
- ui->actionFossilUI->setEnabled(on);
- ui->actionRevert->setEnabled(on);
- ui->actionTimeline->setEnabled(on);
- ui->actionOpenFile->setEnabled(on);
- ui->actionOpenContaining->setEnabled(on);
- ui->actionUndo->setEnabled(on);
- ui->actionUpdate->setEnabled(on);
- ui->actionOpenFolder->setEnabled(on);
- ui->actionRenameFolder->setEnabled(on);
- ui->actionNewStash->setEnabled(on);
- ui->actionDeleteStash->setEnabled(on);
- ui->actionDiffStash->setEnabled(on);
- ui->actionApplyStash->setEnabled(on);
+ QAction *actions[] = {
+ ui->actionCommit,
+ ui->actionDiff,
+ ui->actionAdd,
+ ui->actionDelete,
+ ui->actionPush,
+ ui->actionPull,
+ ui->actionRename,
+ ui->actionHistory,
+ ui->actionFossilUI,
+ ui->actionRevert,
+ ui->actionTimeline,
+ ui->actionOpenFile,
+ ui->actionOpenContaining,
+ ui->actionUndo,
+ ui->actionUpdate,
+ ui->actionOpenFolder,
+ ui->actionRenameFolder,
+ ui->actionCreateStash,
+ ui->actionDeleteStash,
+ ui->actionDiffStash,
+ ui->actionApplyStash,
+ ui->actionDeleteStash,
+ ui->actionCreateTag,
+ ui->actionDeleteTag,
+ ui->actionCreateBranch,
+ ui->actionMergeBranch,
+ };
+
+ for(size_t i=0; isetEnabled(on);
+
}
//------------------------------------------------------------------------------
bool MainWindow::refresh()
{
+ QString title = "Fuel";
+
// Load repository info
- RepoStatus st = getRepoStatus();
+ RepoStatus st = fossil().getRepoStatus();
if(st==REPO_NOT_FOUND)
{
setStatus(tr("No workspace detected."));
enableActions(false);
- repoFileModel.removeRows(0, repoFileModel.rowCount());
- repoDirModel.clear();
+ getWorkspace().getFileModel().removeRows(0, getWorkspace().getFileModel().rowCount());
+ getWorkspace().getTreeModel().removeRows(0, getWorkspace().getFileModel().rowCount());
+ setWindowTitle(title);
return false;
}
else if(st==REPO_OLD_SCHEMA)
{
setStatus(tr("Old repository schema detected. Consider running 'fossil rebuild'"));
enableActions(false);
- repoFileModel.removeRows(0, repoFileModel.rowCount());
- repoDirModel.clear();
+ getWorkspace().getFileModel().removeRows(0, getWorkspace().getFileModel().rowCount());
+ getWorkspace().getTreeModel().removeRows(0, getWorkspace().getFileModel().rowCount());
+ setWindowTitle(title);
return true;
}
@@ -655,189 +623,45 @@ bool MainWindow::refresh()
setStatus("");
enableActions(true);
- QString title = "Fuel";
- if(!projectName.isEmpty())
- title += " - "+projectName;
+ if(!fossil().getProjectName().isEmpty())
+ title += " - " + fossil().getProjectName();
setWindowTitle(title);
return true;
}
+
+
//------------------------------------------------------------------------------
void MainWindow::scanWorkspace()
{
- // Scan all workspace files
- QFileInfoList all_files;
- QString wkdir = getCurrentWorkspace();
-
- if(wkdir.isEmpty())
- return;
-
- // Retrieve the status of files tracked by fossil
- QStringList res;
- if(!runFossil(QStringList() << "ls" << "-l", &res, RUNFLAGS_SILENT_ALL))
- return;
-
- bool scan_files = ui->actionViewUnknown->isChecked();
-
- setStatus(tr("Scanning Workspace..."));
setBusy(true);
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-
- // Dispose RepoFiles
- for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it)
- delete *it;
-
- workspaceFiles.clear();
- pathSet.clear();
-
- abortOperation = false;
-
- if(scan_files)
- {
- QCoreApplication::processEvents();
-
- QString ignore;
- // If we should not be showing ignored files, fill in the ignored spec
- if(!ui->actionViewIgnored->isChecked())
- {
- // QDir expects multiple specs being separated by a semicolon
- ignore = settings.GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString().replace(',',';');
- }
-
- if(!scanDirectory(all_files, wkdir, wkdir, ignore, abortOperation))
- goto _done;
-
- for(QFileInfoList::iterator it=all_files.begin(); it!=all_files.end(); ++it)
- {
- QString filename = it->fileName();
- QString fullpath = it->absoluteFilePath();
-
- // Skip fossil files
- if(filename == FOSSIL_CHECKOUT1 || filename == FOSSIL_CHECKOUT2 || (!repositoryFile.isEmpty() && QFileInfo(fullpath) == QFileInfo(repositoryFile)))
- continue;
-
- RepoFile *rf = new RepoFile(*it, RepoFile::TYPE_UNKNOWN, wkdir);
- workspaceFiles.insert(rf->getFilePath(), rf);
- pathSet.insert(rf->getPath());
- }
- }
-
- setStatus(tr("Updating..."));
- QCoreApplication::processEvents();
-
- // Update Files and Directories
-
- for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); ++line_it)
- {
- QString line = (*line_it).trimmed();
- if(line.length()==0)
- continue;
-
- QString status_text = line.left(10).trimmed();
- QString fname = line.right(line.length() - 10).trimmed();
- RepoFile::EntryType type = RepoFile::TYPE_UNKNOWN;
-
- // Generate a RepoFile for all non-existant fossil files
- // or for all files if we skipped scanning the workspace
- bool add_missing = !scan_files;
-
- if(status_text=="EDITED")
- type = RepoFile::TYPE_EDITTED;
- else if(status_text=="ADDED")
- type = RepoFile::TYPE_ADDED;
- else if(status_text=="DELETED")
- {
- type = RepoFile::TYPE_DELETED;
- add_missing = true;
- }
- else if(status_text=="MISSING")
- {
- type = RepoFile::TYPE_MISSING;
- add_missing = true;
- }
- else if(status_text=="RENAMED")
- type = RepoFile::TYPE_RENAMED;
- else if(status_text=="UNCHANGED")
- type = RepoFile::TYPE_UNCHANGED;
- else if(status_text=="CONFLICT")
- type = RepoFile::TYPE_CONFLICTED;
-
- // Filter unwanted file types
- if( ((type & RepoFile::TYPE_MODIFIED) && !ui->actionViewModified->isChecked()) ||
- ((type & RepoFile::TYPE_UNCHANGED) && !ui->actionViewUnchanged->isChecked() ))
- {
- workspaceFiles.remove(fname);
- continue;
- }
- else
- add_missing = true;
-
- filemap_t::iterator it = workspaceFiles.find(fname);
-
- RepoFile *rf = 0;
- if(add_missing && it==workspaceFiles.end())
- {
- QFileInfo info(wkdir+QDir::separator()+fname);
- rf = new RepoFile(info, type, wkdir);
- workspaceFiles.insert(rf->getFilePath(), rf);
- }
-
- if(!rf)
- {
- it = workspaceFiles.find(fname);
- Q_ASSERT(it!=workspaceFiles.end());
- rf = *it;
- }
-
- rf->setType(type);
-
- QString path = rf->getPath();
- pathSet.insert(path);
- }
-
- // Load the stash
- stashMap.clear();
- res.clear();
- if(!runFossil(QStringList() << "stash" << "ls", &res, RUNFLAGS_SILENT_ALL))
- goto _done;
-
- for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); )
- {
- QString line = *line_it;
-
- int index = REGEX_STASH.indexIn(line);
- if(index==-1)
- break;
-
- QString id = REGEX_STASH.cap(1);
- ++line_it;
-
- QString name;
- // Finish at an anonymous stash or start of a new stash ?
- if(line_it==res.end() || REGEX_STASH.indexIn(*line_it)!=-1)
- name = line.trimmed();
- else // Named stash
- {
- // Parse stash name
- name = (*line_it);
- name = name.trimmed();
- ++line_it;
- }
-
- stashMap.insert(name, id);
- }
-
-
- // Update the file item model
-_done:
- updateDirView();
+ getWorkspace().scanWorkspace(ui->actionViewUnknown->isChecked(),
+ ui->actionViewIgnored->isChecked(),
+ ui->actionViewModified->isChecked(),
+ ui->actionViewUnchanged->isChecked(),
+ settings.GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString(),
+ uiCallback,
+ operationAborted
+ );
+ updateWorkspaceView();
updateFileView();
- updateStashView();
+
+ // Build default versions list
+ versionList.clear();
+ versionList += getWorkspace().getBranches();
+ versionList += getWorkspace().getTags().keys();
+
+ selectedTags.clear();
+ selectedBranches.clear();
setBusy(false);
setStatus("");
- QApplication::restoreOverrideCursor();
+ lblRevision->setText(tr("Revision: %0").arg(fossil().getCurrentRevision()));
+ lblRevision->setVisible(true);
+
+ lblTags->setText(tr("Tags: %0").arg(fossil().getCurrentTags().join(' ')));
+ lblTags->setVisible(true);
}
//------------------------------------------------------------------------------
@@ -868,7 +692,7 @@ static void addPathToTree(QStandardItem &root, const QString &path, const QIcon
if(!found) // Generate it
{
QStandardItem *child = new QStandardItem(folderIcon, dir);
- child->setData(fullpath); // keep the full path to simplify selection
+ child->setData(WorkspaceItem(WorkspaceItem::TYPE_FOLDER, fullpath), ROLE_WORKSPACE_ITEM);
parent->appendRow(child);
parent = child;
}
@@ -877,169 +701,230 @@ static void addPathToTree(QStandardItem &root, const QString &path, const QIcon
}
//------------------------------------------------------------------------------
-void MainWindow::updateDirView()
+void MainWindow::updateWorkspaceView()
{
- // Directory View
- repoDirModel.clear();
+ // Record expanded tree-node names, and selection
+ name_modelindex_map_t name_map;
+ BuildNameToModelIndex(name_map, getWorkspace().getTreeModel());
+ stringset_t expanded_items;
+ stringset_t selected_items;
- QStringList header;
- header << tr("Folders");
- repoDirModel.setHorizontalHeaderLabels(header);
+ const QItemSelection selection = ui->workspaceTreeView->selectionModel()->selection();
- QStandardItem *root = new QStandardItem(getInternalIcon(":icons/icons/My Documents-01.png"), projectName);
- root->setData(""); // Empty Path
- root->setEditable(false);
-
- repoDirModel.appendRow(root);
- for(stringset_t::iterator it = pathSet.begin(); it!=pathSet.end(); ++it)
+ for(name_modelindex_map_t::const_iterator it=name_map.begin(); it!=name_map.end(); ++it)
{
- const QString &dir = *it;
- if(dir.isEmpty())
- continue;
+ const QModelIndex mi = it.value();
+ if(ui->workspaceTreeView->isExpanded(mi))
+ expanded_items.insert(it.key());
- addPathToTree(*root, dir, getInternalIcon(":icons/icons/Folder-01.png"));
+ if(selection.contains(mi))
+ selected_items.insert(it.key());
+ }
+
+ // Clear content except headers
+ getWorkspace().getTreeModel().removeRows(0, getWorkspace().getTreeModel().rowCount());
+
+ QStandardItem *workspace = new QStandardItem(getInternalIcon(":icons/icon-item-folder"), tr("Files") );
+ workspace->setData(WorkspaceItem(WorkspaceItem::TYPE_WORKSPACE, ""), ROLE_WORKSPACE_ITEM);
+ workspace->setEditable(false);
+
+ getWorkspace().getTreeModel().appendRow(workspace);
+ if(viewMode == VIEWMODE_TREE)
+ {
+ for(stringset_t::iterator it = getWorkspace().getPaths().begin(); it!=getWorkspace().getPaths().end(); ++it)
+ {
+ const QString &dir = *it;
+ if(dir.isEmpty())
+ continue;
+
+ addPathToTree(*workspace, dir, getInternalIcon(":icons/icon-item-folder"));
+ }
+
+ // Expand root folder
+ ui->workspaceTreeView->setExpanded(workspace->index(), true);
+ }
+
+ // Branches
+ QStandardItem *branches = new QStandardItem(getInternalIcon(":icons/icon-item-branch"), tr("Branches"));
+ branches->setData(WorkspaceItem(WorkspaceItem::TYPE_BRANCHES, ""), ROLE_WORKSPACE_ITEM);
+ branches->setEditable(false);
+ getWorkspace().getTreeModel().appendRow(branches);
+ foreach(const QString &branch_name, getWorkspace().getBranches())
+ {
+ QStandardItem *branch = new QStandardItem(getInternalIcon(":icons/icon-item-branch"), branch_name);
+ branch->setData(WorkspaceItem(WorkspaceItem::TYPE_BRANCH, branch_name), ROLE_WORKSPACE_ITEM);
+
+ bool active = fossil().getCurrentTags().contains(branch_name);
+ if(active)
+ {
+ QFont font = branch->font();
+ font.setBold(true);
+ branch->setFont(font);
+ }
+ branches->appendRow(branch);
+ }
+
+ // Tags
+ QStandardItem *tags = new QStandardItem(getInternalIcon(":icons/icon-item-tag"), tr("Tags"));
+ tags->setData(WorkspaceItem(WorkspaceItem::TYPE_TAGS, ""), ROLE_WORKSPACE_ITEM);
+ tags->setEditable(false);
+ getWorkspace().getTreeModel().appendRow(tags);
+ for(QStringMap::const_iterator it=getWorkspace().getTags().begin(); it!=getWorkspace().getTags().end(); ++it)
+ {
+ const QString &tag_name = it.key();
+
+
+ QStandardItem *tag = new QStandardItem(getInternalIcon(":icons/icon-item-tag"), tag_name);
+ tag->setData(WorkspaceItem(WorkspaceItem::TYPE_TAG, tag_name), ROLE_WORKSPACE_ITEM);
+
+ bool active = fossil().getCurrentTags().contains(tag_name);
+ if(active)
+ {
+ QFont font = tag->font();
+ font.setBold(true);
+ tag->setFont(font);
+ }
+ tags->appendRow(tag);
+ }
+
+ // FIXME: Unique Icon name
+ // Stashes
+ QStandardItem *stashes = new QStandardItem(getInternalIcon(":icons/icon-action-repo-open"), tr("Stashes"));
+ stashes->setData(WorkspaceItem(WorkspaceItem::TYPE_STASHES, ""), ROLE_WORKSPACE_ITEM);
+ stashes->setEditable(false);
+ getWorkspace().getTreeModel().appendRow(stashes);
+ for(stashmap_t::const_iterator it= getWorkspace().getStashes().begin(); it!=getWorkspace().getStashes().end(); ++it)
+ {
+ QStandardItem *stash = new QStandardItem(getInternalIcon(":icons/icon-action-repo-open"), it.key());
+ stash->setData(WorkspaceItem(WorkspaceItem::TYPE_STASH, it.value()), ROLE_WORKSPACE_ITEM);
+ stashes->appendRow(stash);
+ }
+
+#if 0
+ // Remotes
+ QStandardItem *remotes = new QStandardItem(getInternalIcon(":icons/icon-item-remote"), tr("Remotes"));
+ remotes->setData(WorkspaceItem(WorkspaceItem::TYPE_REMOTES, ""), ROLE_WORKSPACE_ITEM);
+ remotes->setEditable(false);
+ getWorkspace().getTreeModel().appendRow(remotes);
+#endif
+
+#if 0 // Unimplemented for now
+ // Settings
+ QStandardItem *settings = new QStandardItem(getInternalIcon(":icons/icon-action-settings"), tr("Settings"));
+ settings->setData(WorkspaceItem(WorkspaceItem::TYPE_SETTINGS, ""), ROLE_WORKSPACE_ITEM);
+ settings->setEditable(false);
+ getWorkspace().getTreeModel().appendRow(settings);
+#endif
+
+ // Expand previously selected nodes
+ name_map.clear();
+ BuildNameToModelIndex(name_map, getWorkspace().getTreeModel());
+
+ for(stringset_t::const_iterator it=expanded_items.begin(); it!=expanded_items.end(); ++it)
+ {
+ name_modelindex_map_t::const_iterator mi_it = name_map.find(*it);
+ if(mi_it!=name_map.end())
+ ui->workspaceTreeView->setExpanded(mi_it.value(), true);
+ }
+
+ // Select previous selected item
+ for(stringset_t::const_iterator it=selected_items.begin(); it!=selected_items.end(); ++it)
+ {
+ name_modelindex_map_t::const_iterator mi_it = name_map.find(*it);
+ if(mi_it!=name_map.end())
+ {
+ const QModelIndex &mi = mi_it.value();
+ ui->workspaceTreeView->selectionModel()->select(mi, QItemSelectionModel::Select);
+ }
}
- ui->treeView->expandToDepth(0);
- ui->treeView->sortByColumn(0, Qt::AscendingOrder);
}
//------------------------------------------------------------------------------
void MainWindow::updateFileView()
{
// Clear content except headers
- repoFileModel.removeRows(0, repoFileModel.rowCount());
+ getWorkspace().getFileModel().removeRows(0, getWorkspace().getFileModel().rowCount());
- struct { RepoFile::EntryType type; QString text; const char *icon; }
+ struct { WorkspaceFile::Type type; QString text; const char *icon; }
stats[] =
{
- { RepoFile::TYPE_EDITTED, tr("Edited"), ":icons/icons/Button Blank Yellow-01.png" },
- { RepoFile::TYPE_UNCHANGED, tr("Unchanged"), ":icons/icons/Button Blank Green-01.png" },
- { RepoFile::TYPE_ADDED, tr("Added"), ":icons/icons/Button Add-01.png" },
- { RepoFile::TYPE_DELETED, tr("Deleted"), ":icons/icons/Button Close-01.png" },
- { RepoFile::TYPE_RENAMED, tr("Renamed"), ":icons/icons/Button Reload-01.png" },
- { RepoFile::TYPE_MISSING, tr("Missing"), ":icons/icons/Button Help-01.png" },
- { RepoFile::TYPE_CONFLICTED, tr("Conflicted"), ":icons/icons/Button Blank Red-01.png" },
+ { WorkspaceFile::TYPE_EDITTED, tr("Edited"), ":icons/icon-item-edited" },
+ { WorkspaceFile::TYPE_UNCHANGED, tr("Unchanged"), ":icons/icon-item-unchanged" },
+ { WorkspaceFile::TYPE_ADDED, tr("Added"), ":icons/icon-item-added" },
+ { WorkspaceFile::TYPE_DELETED, tr("Deleted"), ":icons/icon-item-deleted" },
+ { WorkspaceFile::TYPE_RENAMED, tr("Renamed"), ":icons/icon-item-renamed" },
+ { WorkspaceFile::TYPE_MISSING, tr("Missing"), ":icons/icon-item-missing" },
+ { WorkspaceFile::TYPE_CONFLICTED, tr("Conflicted"), ":icons/icon-item-conflicted" },
+ { WorkspaceFile::TYPE_MERGED, tr("Merged"), ":icons/icon-item-edited" },
};
bool display_path = viewMode==VIEWMODE_LIST || selectedDirs.count() > 1;
+ const QString &status_unknown = QString(tr("Unknown"));
+ const QString &search_text = searchBox->text();
+
size_t item_id=0;
- for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it)
+ for(Workspace::filemap_t::iterator it = getWorkspace().getFiles().begin(); it!=getWorkspace().getFiles().end(); ++it)
{
- const RepoFile &e = *it.value();
- QString path = e.getPath();
+ const WorkspaceFile &e = *it.value();
+ const QString &path = e.getPath();
+ const QString &file_path = e.getFilePath();
+ QString native_file_path = QDir::toNativeSeparators(file_path);
+
+ // Apply filter if available
+ if(!search_text.isEmpty() && !native_file_path.contains(search_text, Qt::CaseInsensitive))
+ continue;
// In Tree mode, filter all items not included in the current dir
if(viewMode==VIEWMODE_TREE && !selectedDirs.contains(path))
continue;
// Status Column
- QString status_text = QString(tr("Unknown"));
- const char *status_icon_path= ":icons/icons/Button Blank Gray-01.png"; // Default icon
+ const QString *status_text = &status_unknown;
+ const char *status_icon_path= ":icons/icon-item-unknown"; // Default icon
for(size_t t=0; tsetToolTip(status_text);
- repoFileModel.setItem(item_id, COLUMN_STATUS, status);
+ QStandardItem *status = new QStandardItem(getInternalIcon(status_icon_path), *status_text);
+ status->setToolTip(*status_text);
+ getWorkspace().getFileModel().setItem(item_id, COLUMN_STATUS, status);
QFileInfo finfo = e.getFileInfo();
QString icon_type = iconProvider.type(finfo);
-
if(!iconCache.contains(icon_type))
iconCache.insert(icon_type, iconProvider.icon(finfo));
const QIcon *icon = &iconCache[icon_type];
QStandardItem *filename_item = 0;
- repoFileModel.setItem(item_id, COLUMN_PATH, new QStandardItem(path));
+ getWorkspace().getFileModel().setItem(item_id, COLUMN_PATH, new QStandardItem(path));
if(display_path)
- filename_item = new QStandardItem(*icon, QDir::toNativeSeparators(e.getFilePath()));
+ filename_item = new QStandardItem(*icon, native_file_path);
else
filename_item = new QStandardItem(*icon, e.getFilename());
Q_ASSERT(filename_item);
// Keep the path in the user data
- filename_item->setData(e.getFilePath());
- repoFileModel.setItem(item_id, COLUMN_FILENAME, filename_item);
+ filename_item->setData(file_path);
+ getWorkspace().getFileModel().setItem(item_id, COLUMN_FILENAME, filename_item);
- repoFileModel.setItem(item_id, COLUMN_EXTENSION, new QStandardItem(finfo.suffix()));
- repoFileModel.setItem(item_id, COLUMN_MODIFIED, new QStandardItem(finfo.lastModified().toString(Qt::SystemLocaleShortDate)));
+ getWorkspace().getFileModel().setItem(item_id, COLUMN_EXTENSION, new QStandardItem(finfo.suffix()));
+ getWorkspace().getFileModel().setItem(item_id, COLUMN_MODIFIED, new QStandardItem(finfo.lastModified().toString(Qt::SystemLocaleShortDate)));
++item_id;
}
- ui->tableView->resizeRowsToContents();
-}
-
-//------------------------------------------------------------------------------
-MainWindow::RepoStatus MainWindow::getRepoStatus()
-{
- QStringList res;
- int exit_code = EXIT_FAILURE;
-
- // We need to determine the reason why fossil has failed
- // so we delay processing of the exit_code
- if(!runFossilRaw(QStringList() << "info", &res, &exit_code, RUNFLAGS_SILENT_ALL))
- return REPO_NOT_FOUND;
-
- bool run_ok = exit_code == EXIT_SUCCESS;
-
- for(QStringList::iterator it=res.begin(); it!=res.end(); ++it)
- {
- int col_index = it->indexOf(':');
- if(col_index==-1)
- continue;
-
- QString key = it->left(col_index).trimmed();
- QString value = it->mid(col_index+1).trimmed();
-
- if(key=="fossil")
- {
- if(value=="incorrect repository schema version")
- return REPO_OLD_SCHEMA;
- else if(value=="not within an open checkout")
- return REPO_NOT_FOUND;
- }
-
- if(run_ok)
- {
- if(key=="project-name")
- projectName = value;
- else if(key=="repository")
- repositoryFile = value;
- }
- }
-
- return run_ok ? REPO_OK : REPO_NOT_FOUND;
-}
-//------------------------------------------------------------------------------
-void MainWindow::updateStashView()
-{
- repoStashModel.clear();
-
- QStringList header;
- header << tr("Stashes");
- repoStashModel.setHorizontalHeaderLabels(header);
-
- for(stashmap_t::iterator it=stashMap.begin(); it!=stashMap.end(); ++it)
- {
- QStandardItem *item = new QStandardItem(it.key());
- item->setToolTip(it.key());
- repoStashModel.appendRow(item);
- }
- ui->tableViewStash->resizeColumnsToContents();
- ui->tableViewStash->resizeRowsToContents();
+ ui->fileTableView->resizeRowsToContents();
}
//------------------------------------------------------------------------------
@@ -1067,333 +952,6 @@ void MainWindow::on_actionClearLog_triggered()
ui->textBrowser->clear();
}
-//------------------------------------------------------------------------------
-bool MainWindow::runFossil(const QStringList &args, QStringList *output, int runFlags)
-{
- int exit_code = EXIT_FAILURE;
- if(!runFossilRaw(args, output, &exit_code, runFlags))
- return false;
-
- return exit_code == EXIT_SUCCESS;
-}
-
-//------------------------------------------------------------------------------
-static QString ParseFossilQuery(QString line)
-{
- // Extract question
- int qend = line.lastIndexOf('(');
- if(qend == -1)
- qend = line.lastIndexOf('[');
- Q_ASSERT(qend!=-1);
- line = line.left(qend);
- line = line.trimmed();
- line += "?";
- line[0]=QString(line[0]).toUpper()[0];
- return line;
-}
-
-//------------------------------------------------------------------------------
-// Run fossil. Returns true if execution was successful regardless if fossil
-// issued an error
-bool MainWindow::runFossilRaw(const QStringList &args, QStringList *output, int *exitCode, int runFlags)
-{
- bool silent_input = (runFlags & RUNFLAGS_SILENT_INPUT) != 0;
- bool silent_output = (runFlags & RUNFLAGS_SILENT_OUTPUT) != 0;
- bool detached = (runFlags & RUNFLAGS_DETACHED) != 0;
-
- if(!silent_input)
- {
- QString params;
- foreach(QString p, args)
- {
- if(p.indexOf(' ')!=-1)
- params += '"' + p + "\" ";
- else
- params += p + ' ';
- }
- log("> fossil "+params+"
", true);
- }
-
- QString wkdir = getCurrentWorkspace();
-
- QString fossil = getFossilPath();
-
- // Detached processes use the command-line only, to avoid having to wait
- // for the temporary args file to be released before returing
- if(detached)
- return QProcess::startDetached(fossil, args, wkdir);
-
- // Make StatusBar message
- QString status_msg = tr("Running Fossil");
- if(args.length() > 0)
- status_msg = QString("Fossil %0").arg(args[0].toCaseFolded());
- ScopedStatus status(status_msg, ui, progressBar);
-
- // Generate args file
- const QStringList *final_args = &args;
- QTemporaryFile args_file;
- if(!args_file.open())
- {
- QMessageBox::critical(this, tr("Error"), tr("Could not generate command line file"), QMessageBox::Ok );
- return false;
- }
-
- // Write BOM
- args_file.write(reinterpret_cast(UTF8_BOM), sizeof(UTF8_BOM));
-
- // Write Args
- foreach(const QString &arg, args)
- {
- args_file.write(arg.toUtf8());
- args_file.write("\n");
- }
- args_file.close();
-
- // Replace args with args filename
- QStringList run_args;
- run_args.append("--args");
- run_args.append(args_file.fileName());
- final_args = &run_args;
-
- // Create fossil process
- LoggedProcess process(this);
- process.setWorkingDirectory(wkdir);
-
- process.start(fossil, *final_args);
- if(!process.waitForStarted())
- {
- log(tr("Could not start Fossil executable '%0'").arg(fossil)+"\n");
- return false;
- }
- const QChar EOL_MARK('\n');
- QString ans_yes = 'y' + EOL_MARK;
- QString ans_no = 'n' + EOL_MARK;
- QString ans_always = 'a' + EOL_MARK;
- QString ans_convert = 'c' + EOL_MARK;
-
- abortOperation = false;
- QString buffer;
-
-#ifdef Q_OS_WIN
- QTextCodec *codec = QTextCodec::codecForName("UTF-8");
-#else
- QTextCodec *codec = QTextCodec::codecForLocale();
-#endif
-
- Q_ASSERT(codec);
- QTextDecoder *decoder = codec->makeDecoder();
- Q_ASSERT(decoder);
-
- while(true)
- {
- QProcess::ProcessState state = process.state();
- qint64 bytes_avail = process.logBytesAvailable();
-
- if(state!=QProcess::Running && bytes_avail<1)
- break;
-
- if(abortOperation)
- {
- #ifdef Q_OS_WIN // Verify this is still true on Qt5
- process.kill(); // QT on windows cannot terminate console processes with QProcess::terminate
- #else
- process.terminate();
- #endif
- break;
- }
-
- QByteArray input;
- process.getLogAndClear(input);
-
- #ifdef QT_DEBUG // Log fossil output in debug builds
- if(!input.isEmpty())
- qDebug() << input;
- #endif
-
- buffer += decoder->toUnicode(input);
-
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
-
- if(buffer.isEmpty())
- continue;
-
- // Normalize line endings
- buffer = buffer.replace("\r\n", "\n");
- buffer = buffer.replace("\r", "\n");
-
- // Extract the last line
- int last_line_start = buffer.lastIndexOf(EOL_MARK);
-
- QString last_line;
- QString before_last_line;
- if(last_line_start != -1)
- {
- last_line = buffer.mid(last_line_start+1); // Including the EOL
-
- // Detect previous line
- if(last_line_start>0)
- {
- int before_last_line_start = buffer.lastIndexOf(EOL_MARK, last_line_start-1);
- // No line before ?
- if(before_last_line_start==-1)
- before_last_line_start = 0; // Use entire line
-
- // Extract previous line
- before_last_line = buffer.mid(before_last_line_start, last_line_start-before_last_line_start);
- }
- }
- else
- last_line = buffer;
-
- last_line = last_line.trimmed();
-
- // Check if we have a query
- bool ends_qmark = !last_line.isEmpty() && last_line[last_line.length()-1]=='?';
- bool have_yn_query = last_line.toLower().indexOf("y/n")!=-1;
- bool have_yna_query = last_line.toLower().indexOf("a=always/y/n")!=-1 || last_line.toLower().indexOf("yes/no/all")!=-1 || last_line.toLower().indexOf("a=all/y/n")!=-1;
- bool have_an_query = last_line.toLower().indexOf("a=always/n")!=-1;
- bool have_acyn_query = last_line.toLower().indexOf("a=all/c=convert/y/n")!=-1;
-
- bool have_query = ends_qmark && (have_yn_query || have_yna_query || have_an_query || have_acyn_query);
-
- // Flush all complete lines to the log and output
- QStringList log_lines = buffer.left(last_line_start).split(EOL_MARK);
- for(int l=0; lappend(line);
-
- if(!silent_output)
- log(line+"\n");
- }
-
- // Remove everything we processed (including the EOL)
- buffer = buffer.mid(last_line_start+1) ;
-
- // Now process any query
- if(have_query && (have_yna_query || have_acyn_query))
- {
- log(last_line);
- QString query = ParseFossilQuery(last_line);
- QMessageBox::StandardButtons buttons = QMessageBox::YesToAll|QMessageBox::Yes|QMessageBox::No;
-
- // Add any extra text available to the query
- before_last_line = before_last_line.trimmed();
- if(!before_last_line.isEmpty())
- query = before_last_line + "\n" + query;
-
- // Map the Convert option to the Apply button
- if(have_acyn_query)
- buttons |= QMessageBox::Apply;
-
- QMessageBox::StandardButton res = DialogQuery(this, "Fossil", query, buttons);
- if(res==QMessageBox::Yes)
- {
- process.write(ans_yes.toLatin1());
- log("Y\n");
- }
- else if(res==QMessageBox::YesAll)
- {
- process.write(ans_always.toLatin1());
- log("A\n");
- }
- else if(res==QMessageBox::Apply)
- {
- process.write(ans_convert.toLatin1());
- log("C\n");
- }
- else
- {
- process.write(ans_no.toLatin1());
- log("N\n");
- }
- buffer.clear();
- }
- else if(have_query && have_yn_query)
- {
- log(last_line);
- QString query = ParseFossilQuery(last_line);
- QMessageBox::StandardButton res = DialogQuery(this, "Fossil", query);
-
- if(res==QMessageBox::Yes)
- {
- process.write(ans_yes.toLatin1());
- log("Y\n");
- }
- else
- {
- process.write(ans_no.toLatin1());
- log("N\n");
- }
-
- buffer.clear();
- }
- else if(have_query && have_an_query)
- {
- log(last_line);
- QString query = ParseFossilQuery(last_line);
- QMessageBox::StandardButton res = DialogQuery(this, "Fossil", query, QMessageBox::YesToAll|QMessageBox::No);
- if(res==QMessageBox::YesAll)
- {
- process.write(ans_always.toLatin1());
- log("A\n");
- }
- else
- {
- process.write(ans_no.toLatin1());
- log("N\n");
- }
- buffer.clear();
- }
- }
-
- delete decoder;
-
- // Must be finished by now
- Q_ASSERT(process.state()==QProcess::NotRunning);
-
- QProcess::ExitStatus es = process.exitStatus();
-
- if(es!=QProcess::NormalExit)
- return false;
-
- if(exitCode)
- *exitCode = process.exitCode();
-
- return true;
-}
-
-
-//------------------------------------------------------------------------------
-QString MainWindow::getFossilPath()
-{
- // Use the user-specified fossil if available
- QString fossil_path = settings.GetValue(FUEL_SETTING_FOSSIL_PATH).toString();
- if(!fossil_path.isEmpty())
- return QDir::toNativeSeparators(fossil_path);
-
- QString fossil_exe = "fossil";
-#ifdef Q_OS_WIN
- fossil_exe += ".exe";
-#endif
- // Use our fossil if available
- QString fuel_fossil = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + fossil_exe);
-
- if(QFile::exists(fuel_fossil))
- return fuel_fossil;
-
- // Otherwise assume there is a "fossil" executable in the path
- return fossil_exe;
-}
//------------------------------------------------------------------------------
void MainWindow::applySettings()
{
@@ -1417,20 +975,20 @@ void MainWindow::applySettings()
store->endArray();
store->beginReadArray("FileColumns");
- for(int i=0; isetArrayIndex(i);
if(store->contains("Width"))
{
int width = store->value("Width").toInt();
- ui->tableView->setColumnWidth(i, width);
+ ui->fileTableView->setColumnWidth(i, width);
}
if(store->contains("Index"))
{
int index = store->value("Index").toInt();
- int cur_index = ui->tableView->horizontalHeader()->visualIndex(i);
- ui->tableView->horizontalHeader()->moveSection(cur_index, index);
+ int cur_index = ui->fileTableView->horizontalHeader()->visualIndex(i);
+ ui->fileTableView->horizontalHeader()->moveSection(cur_index, index);
}
}
@@ -1464,14 +1022,10 @@ void MainWindow::applySettings()
if(store->contains("ViewAsList"))
{
ui->actionViewAsList->setChecked(store->value("ViewAsList").toBool());
+ ui->actionViewAsFolders->setChecked(!store->value("ViewAsList").toBool());
viewMode = store->value("ViewAsList").toBool()? VIEWMODE_LIST : VIEWMODE_TREE;
}
- ui->treeView->setVisible(viewMode == VIEWMODE_TREE);
-
- if(store->contains("ViewStash"))
- ui->actionViewStash->setChecked(store->value("ViewStash").toBool());
- ui->tableViewStash->setVisible(ui->actionViewStash->isChecked());
-
+ //ui->workspaceTreeView->setVisible(viewMode == VIEWMODE_TREE);
}
//------------------------------------------------------------------------------
@@ -1491,12 +1045,12 @@ void MainWindow::updateSettings()
}
store->endArray();
- store->beginWriteArray("FileColumns", repoFileModel.columnCount());
- for(int i=0; ibeginWriteArray("FileColumns", getWorkspace().getFileModel().columnCount());
+ for(int i=0; isetArrayIndex(i);
- store->setValue("Width", ui->tableView->columnWidth(i));
- int index = ui->tableView->horizontalHeader()->visualIndex(i);
+ store->setValue("Width", ui->fileTableView->columnWidth(i));
+ int index = ui->fileTableView->horizontalHeader()->visualIndex(i);
store->setValue("Index", index);
}
store->endArray();
@@ -1510,16 +1064,16 @@ void MainWindow::updateSettings()
store->setValue("ViewUnchanged", ui->actionViewUnchanged->isChecked());
store->setValue("ViewIgnored", ui->actionViewIgnored->isChecked());
store->setValue("ViewAsList", ui->actionViewAsList->isChecked());
- store->setValue("ViewStash", ui->actionViewStash->isChecked());
}
//------------------------------------------------------------------------------
void MainWindow::selectRootDir()
{
+ // FIXME: KKK
if(viewMode==VIEWMODE_TREE)
{
- QModelIndex root_index = ui->treeView->model()->index(0, 0);
- ui->treeView->selectionModel()->select(root_index, QItemSelectionModel::Select);
+ QModelIndex root_index = ui->workspaceTreeView->model()->index(0, 0);
+ ui->workspaceTreeView->selectionModel()->select(root_index, QItemSelectionModel::Select);
}
}
@@ -1531,7 +1085,7 @@ void MainWindow::fossilBrowse(const QString &fossilUrl)
bool use_internal = settings.GetValue(FUEL_SETTING_WEB_BROWSER).toInt() == 1;
- QUrl url = QUrl(getFossilHttpAddress()+fossilUrl);
+ QUrl url = QUrl(fossil().getUIHttpAddress()+fossilUrl);
if(use_internal)
{
@@ -1544,7 +1098,7 @@ void MainWindow::fossilBrowse(const QString &fossilUrl)
//------------------------------------------------------------------------------
void MainWindow::getSelectionFilenames(QStringList &filenames, int includeMask, bool allIfEmpty)
{
- if(QApplication::focusWidget() == ui->treeView)
+ if(QApplication::focusWidget() == ui->workspaceTreeView)
getDirViewSelection(filenames, includeMask, allIfEmpty);
else
getFileViewSelection(filenames, includeMask, allIfEmpty);
@@ -1554,21 +1108,26 @@ void MainWindow::getSelectionFilenames(QStringList &filenames, int includeMask,
void MainWindow::getSelectionPaths(stringset_t &paths)
{
// Determine the directories selected
- QModelIndexList selection = ui->treeView->selectionModel()->selectedIndexes();
- for(QModelIndexList::iterator mi_it = selection.begin(); mi_it!=selection.end(); ++mi_it)
+ QModelIndexList selection = ui->workspaceTreeView->selectionModel()->selectedIndexes();
+ foreach(const QModelIndex &mi, selection)
{
- const QModelIndex &mi = *mi_it;
- QVariant data = repoDirModel.data(mi, REPODIRMODEL_ROLE_PATH);
- paths.insert(data.toString());
+ QVariant data = mi.model()->data(mi, ROLE_WORKSPACE_ITEM);
+ Q_ASSERT(data.isValid());
+
+ WorkspaceItem tv = data.value();
+ if(tv.Type != WorkspaceItem::TYPE_FOLDER)
+ continue;
+
+ paths.insert(tv.Value);
}
}
//------------------------------------------------------------------------------
// Select all workspace files that match the includeMask
void MainWindow::getAllFilenames(QStringList &filenames, int includeMask)
{
- for(filemap_t::iterator it=workspaceFiles.begin(); it!=workspaceFiles.end(); ++it)
+ for(Workspace::filemap_t::iterator it=getWorkspace().getFiles().begin(); it!=getWorkspace().getFiles().end(); ++it)
{
- const RepoFile &e = *(*it);
+ const WorkspaceFile &e = *(*it);
// Skip unwanted file types
if(!(includeMask & e.getType()))
@@ -1583,16 +1142,16 @@ void MainWindow::getDirViewSelection(QStringList &filenames, int includeMask, bo
// Determine the directories selected
stringset_t paths;
- QModelIndexList selection = ui->treeView->selectionModel()->selectedIndexes();
+ QModelIndexList selection = ui->workspaceTreeView->selectionModel()->selectedIndexes();
if(!(selection.empty() && allIfEmpty))
{
getSelectionPaths(paths);
}
// Select the actual files form the selected directories
- for(filemap_t::iterator it=workspaceFiles.begin(); it!=workspaceFiles.end(); ++it)
+ for(Workspace::filemap_t::iterator it=getWorkspace().getFiles().begin(); it!=getWorkspace().getFiles().end(); ++it)
{
- const RepoFile &e = *(*it);
+ const WorkspaceFile &e = *(*it);
// Skip unwanted file types
if(!(includeMask & e.getType()))
@@ -1627,12 +1186,12 @@ void MainWindow::getDirViewSelection(QStringList &filenames, int includeMask, bo
//------------------------------------------------------------------------------
void MainWindow::getFileViewSelection(QStringList &filenames, int includeMask, bool allIfEmpty)
{
- QModelIndexList selection = ui->tableView->selectionModel()->selectedIndexes();
+ QModelIndexList selection = ui->fileTableView->selectionModel()->selectedIndexes();
if(selection.empty() && allIfEmpty)
{
- ui->tableView->selectAll();
- selection = ui->tableView->selectionModel()->selectedIndexes();
- ui->tableView->clearSelection();
+ ui->fileTableView->selectAll();
+ selection = ui->fileTableView->selectionModel()->selectedIndexes();
+ ui->fileTableView->clearSelection();
}
for(QModelIndexList::iterator mi_it = selection.begin(); mi_it!=selection.end(); ++mi_it)
@@ -1644,11 +1203,11 @@ void MainWindow::getFileViewSelection(QStringList &filenames, int includeMask, b
if(mi.column()!=COLUMN_FILENAME)
continue;
- QVariant data = repoFileModel.data(mi, Qt::UserRole+1);
+ QVariant data = getWorkspace().getFileModel().data(mi, Qt::UserRole+1);
QString filename = data.toString();
- filemap_t::iterator e_it = workspaceFiles.find(filename);
- Q_ASSERT(e_it!=workspaceFiles.end());
- const RepoFile &e = *e_it.value();
+ Workspace::filemap_t::iterator e_it = getWorkspace().getFiles().find(filename);
+ Q_ASSERT(e_it!=getWorkspace().getFiles().end());
+ const WorkspaceFile &e = *e_it.value();
// Skip unwanted files
if(!(includeMask & e.getType()))
@@ -1658,39 +1217,36 @@ void MainWindow::getFileViewSelection(QStringList &filenames, int includeMask, b
}
}
//------------------------------------------------------------------------------
-void MainWindow::getStashViewSelection(QStringList &stashNames, bool allIfEmpty)
+void MainWindow::getSelectionStashes(QStringList &stashNames)
{
- QModelIndexList selection = ui->tableViewStash->selectionModel()->selectedIndexes();
- if(selection.empty() && allIfEmpty)
- {
- ui->tableViewStash->selectAll();
- selection = ui->tableViewStash->selectionModel()->selectedIndexes();
- ui->tableViewStash->clearSelection();
- }
+ QModelIndexList selection = ui->workspaceTreeView->selectionModel()->selectedIndexes();
- for(QModelIndexList::iterator mi_it = selection.begin(); mi_it!=selection.end(); ++mi_it)
+ foreach(const QModelIndex &mi, selection)
{
- const QModelIndex &mi = *mi_it;
+ QVariant data = mi.model()->data(mi, ROLE_WORKSPACE_ITEM);
+ Q_ASSERT(data.isValid());
+ WorkspaceItem tv = data.value();
- if(mi.column()!=0)
+ if(tv.Type != WorkspaceItem::TYPE_STASH)
continue;
- QString name = repoStashModel.data(mi).toString();
+
+ QString name = mi.model()->data(mi, Qt::DisplayRole).toString();
stashNames.append(name);
}
+
}
//------------------------------------------------------------------------------
-bool MainWindow::diffFile(QString repoFile)
+bool MainWindow::diffFile(const QString &repoFile)
{
- // Run the diff detached
- return runFossil(QStringList() << "gdiff" << QuotePath(repoFile), 0, RUNFLAGS_DETACHED);
+ return fossil().diffFile(repoFile);
}
//------------------------------------------------------------------------------
void MainWindow::on_actionDiff_triggered()
{
QStringList selection;
- getSelectionFilenames(selection, RepoFile::TYPE_REPO);
+ getSelectionFilenames(selection, WorkspaceFile::TYPE_REPO);
for(QStringList::iterator it = selection.begin(); it!=selection.end(); ++it)
if(!diffFile(*it))
@@ -1700,51 +1256,24 @@ void MainWindow::on_actionDiff_triggered()
//------------------------------------------------------------------------------
bool MainWindow::startUI()
{
- if(uiRunning())
- {
- log(tr("Fossil UI is already running")+"\n");
- return true;
- }
-
- fossilUI.setParent(this);
- fossilUI.setProcessChannelMode(QProcess::MergedChannels);
- fossilUI.setWorkingDirectory(getCurrentWorkspace());
-
- log("> fossil ui
", true);
- log(tr("Starting Fossil browser UI. Please wait.")+"\n");
- QString fossil = getFossilPath();
-
- QString port = settings.GetValue(FUEL_SETTING_HTTP_PORT).toString();
-
- fossilUI.start(fossil, QStringList() << "server" << "--localauth" << "-P" << port );
-
- if(!fossilUI.waitForStarted() || fossilUI.state()!=QProcess::Running)
- {
- log(tr("Could not start Fossil executable '%s'").arg(fossil)+"\n");
- ui->actionFossilUI->setChecked(false);
- return false;
- }
-
- ui->actionFossilUI->setChecked(true);
- return true;
+ bool started = fossil().startUI("");
+ ui->actionFossilUI->setChecked(started);
+ return started;
}
-
//------------------------------------------------------------------------------
void MainWindow::stopUI()
{
- if(uiRunning())
- {
-#ifdef Q_OS_WIN
- fossilUI.kill(); // QT on windows cannot terminate console processes with QProcess::terminate
-#else
- fossilUI.terminate();
-#endif
- }
- fossilUI.close();
-
+ fossil().stopUI();
+ ui->webView->load(QUrl("about:blank"));
ui->actionFossilUI->setChecked(false);
}
+//------------------------------------------------------------------------------
+bool MainWindow::uiRunning() const
+{
+ return fossil().uiRunning();
+}
+
//------------------------------------------------------------------------------
void MainWindow::on_actionFossilUI_triggered()
{
@@ -1780,7 +1309,7 @@ void MainWindow::on_actionHistory_triggered()
}
//------------------------------------------------------------------------------
-void MainWindow::on_tableView_doubleClicked(const QModelIndex &/*index*/)
+void MainWindow::on_fileTableView_doubleClicked(const QModelIndex &/*index*/)
{
int action = settings.GetValue(FUEL_SETTING_FILE_DBLCLICK).toInt();
if(action==FILE_DLBCLICK_ACTION_DIFF)
@@ -1814,7 +1343,7 @@ void MainWindow::on_actionPush_triggered()
return;
}
- runFossil(QStringList() << "push");
+ fossil().pushRepository();
}
//------------------------------------------------------------------------------
@@ -1828,22 +1357,24 @@ void MainWindow::on_actionPull_triggered()
return;
}
- runFossil(QStringList() << "pull");
+ fossil().pullRepository();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionCommit_triggered()
{
QStringList commit_files;
- getSelectionFilenames(commit_files, RepoFile::TYPE_MODIFIED, true);
+ getSelectionFilenames(commit_files, WorkspaceFile::TYPE_MODIFIED, true);
- if(commit_files.empty())
+ if(commit_files.empty() && !getWorkspace().otherChanges())
return;
QStringList commit_msgs = settings.GetValue(FUEL_SETTING_COMMIT_MSG).toStringList();
QString msg;
- bool aborted = !CommitDialog::run(this, tr("Commit Changes"), commit_files, msg, &commit_msgs);
+ QString branch_name="";
+ bool private_branch = false;
+ bool aborted = !CommitDialog::runCommit(this, commit_files, msg, commit_msgs, branch_name, private_branch);
// Aborted or not we always keep the commit messages.
// (This has saved me way too many times on TortoiseSVN)
@@ -1857,52 +1388,23 @@ void MainWindow::on_actionCommit_triggered()
return;
// Since via the commit dialog the user can deselect all files
- if(commit_files.empty())
+ if(commit_files.empty() && !getWorkspace().otherChanges())
return;
// Do commit
- QString comment_fname;
- {
- QTemporaryFile temp_file;
- if(!temp_file.open())
- {
- QMessageBox::critical(this, tr("Error"), tr("Could not generate comment file"), QMessageBox::Ok );
- return;
- }
- comment_fname = temp_file.fileName();
- }
-
- QFile comment_file(comment_fname);
- if(!comment_file.open(QIODevice::WriteOnly))
- {
- QMessageBox::critical(this, tr("Error"), tr("Could not generate comment file"), QMessageBox::Ok );
- return;
- }
-
- // Write BOM
- comment_file.write(reinterpret_cast(UTF8_BOM), sizeof(UTF8_BOM));
-
- // Write Comment
- comment_file.write(msg.toUtf8());
- comment_file.close();
-
- // Generate fossil parameters.
- QStringList params;
- params << "commit" << "--message-file" << QuotePath(comment_fname);
+ QStringList files;
// When a subset of files has been selected, explicitely specify each file.
// Otherwise all files will be implicitly committed by fossil. This is necessary
// when committing after a merge where fossil thinks that we are trying to do
// a partial commit which is not permitted.
QStringList all_modified_files;
- getAllFilenames(all_modified_files, RepoFile::TYPE_MODIFIED);
+ getAllFilenames(all_modified_files, WorkspaceFile::TYPE_MODIFIED);
if(commit_files.size() != all_modified_files.size())
- params << QuotePaths(commit_files);
-
- runFossil(params);
- QFile::remove(comment_fname);
+ files = commit_files;
+ fossil().commitFiles(files, msg, branch_name, private_branch);
refresh();
}
@@ -1911,7 +1413,7 @@ void MainWindow::on_actionAdd_triggered()
{
// Get unknown files only
QStringList selection;
- getSelectionFilenames(selection, RepoFile::TYPE_UNKNOWN);
+ getSelectionFilenames(selection, WorkspaceFile::TYPE_UNKNOWN);
if(selection.empty())
return;
@@ -1920,8 +1422,7 @@ void MainWindow::on_actionAdd_triggered()
return;
// Do Add
- runFossil(QStringList() << "add" << QuotePaths(selection) );
-
+ fossil().addFiles(selection);
refresh();
}
@@ -1929,10 +1430,10 @@ void MainWindow::on_actionAdd_triggered()
void MainWindow::on_actionDelete_triggered()
{
QStringList repo_files;
- getSelectionFilenames(repo_files, RepoFile::TYPE_REPO);
+ getSelectionFilenames(repo_files, WorkspaceFile::TYPE_REPO);
QStringList unknown_files;
- getSelectionFilenames(unknown_files, RepoFile::TYPE_UNKNOWN);
+ getSelectionFilenames(unknown_files, WorkspaceFile::TYPE_UNKNOWN);
QStringList all_files = repo_files+unknown_files;
@@ -1944,18 +1445,16 @@ void MainWindow::on_actionDelete_triggered()
if(!FileActionDialog::run(this, tr("Remove files"), tr("The following files will be removed from the repository.")+"\n"+tr("Are you sure?"), all_files, tr("Also delete the local files"), &remove_local ))
return;
+ // Remove repository files
if(!repo_files.empty())
- {
- // Do Delete
- if(!runFossil(QStringList() << "delete" << QuotePaths(repo_files)))
- return;
- }
+ fossil().removeFiles(repo_files, remove_local);
+ // Remove unknown local files if selected
if(remove_local)
{
- for(int i=0; i0 && res[0]=="No undo or redo is available")
return;
@@ -2053,7 +1546,7 @@ void MainWindow::on_actionUndo_triggered()
return;
// Do Undo
- runFossil(QStringList() << "undo" );
+ fossil().undoRepository(res, false);
refresh();
}
@@ -2062,14 +1555,9 @@ void MainWindow::on_actionUndo_triggered()
void MainWindow::on_actionAbout_triggered()
{
QString fossil_ver;
- QStringList res;
- if(runFossil(QStringList() << "version", &res, RUNFLAGS_SILENT_ALL) && res.length()==1)
- {
- int off = res[0].indexOf("version ");
- if(off!=-1)
- fossil_ver = tr("Fossil version %0").arg(res[0].mid(off+8)) + "\n";
- }
+ if(fossil().getFossilVersion(fossil_ver))
+ fossil_ver = tr("Fossil version %0").arg(fossil_ver) + "\n";
QString qt_ver = tr("QT version %0").arg(QT_VERSION_STR) + "\n\n";
@@ -2094,21 +1582,13 @@ void MainWindow::on_actionAbout_triggered()
//------------------------------------------------------------------------------
void MainWindow::on_actionUpdate_triggered()
{
- QStringList res;
+ QStringList selected = selectedBranches + selectedTags;
- if(!runFossil(QStringList() << "update" << "--nochange", &res, RUNFLAGS_SILENT_ALL))
- return;
+ QString revision;
+ if(!selected.isEmpty())
+ revision = selected.first();
- if(res.length()==0)
- return;
-
- if(!FileActionDialog::run(this, tr("Update"), tr("The following files will be updated.")+"\n"+tr("Are you sure?"), res))
- return;
-
- // Do Update
- runFossil(QStringList() << "update" );
-
- refresh();
+ updateRevision(revision);
}
//------------------------------------------------------------------------------
@@ -2116,10 +1596,12 @@ void MainWindow::loadFossilSettings()
{
// Also retrieve the fossil global settings
QStringList out;
- if(!runFossil(QStringList() << "settings", &out, RUNFLAGS_SILENT_ALL))
+
+ if(!fossil().getFossilSettings(out))
return;
- QStringMap kv = MakeKeyValues(out);
+ QStringMap kv;
+ ParseProperties(kv, out);
for(Settings::mappings_t::iterator it=settings.GetMappings().begin(); it!=settings.GetMappings().end(); ++it)
{
@@ -2127,16 +1609,18 @@ void MainWindow::loadFossilSettings()
Settings::Setting::SettingType type = it->Type;
// Command types we issue directly on fossil
- if(type == Settings::Setting::TYPE_FOSSIL_COMMAND)
+
+ if(name == FOSSIL_SETTING_REMOTE_URL)
{
// Retrieve existing url
- QStringList out;
- if(runFossil(QStringList() << name, &out, RUNFLAGS_SILENT_ALL) && out.length()==1)
- it.value().Value = out[0].trimmed();
-
+ QString url;
+ if(fossil().getRemoteUrl(url))
+ it.value().Value = url;
continue;
}
+ Q_ASSERT(type == Settings::Setting::TYPE_FOSSIL_GLOBAL || type == Settings::Setting::TYPE_FOSSIL_LOCAL);
+
// Otherwise it must be a fossil setting
if(!kv.contains(name))
continue;
@@ -2159,11 +1643,19 @@ void MainWindow::loadFossilSettings()
//------------------------------------------------------------------------------
void MainWindow::on_actionSettings_triggered()
+{
+ // Run the dialog
+ if(!SettingsDialog::run(this, settings))
+ return;
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::on_actionFossilSettings_triggered()
{
loadFossilSettings();
// Run the dialog
- if(!SettingsDialog::run(this, settings))
+ if(!FslSettingsDialog::run(this, settings))
return;
// Apply settings
@@ -2173,28 +1665,20 @@ void MainWindow::on_actionSettings_triggered()
Settings::Setting::SettingType type = it.value().Type;
// Command types we issue directly on fossil
- if(type == Settings::Setting::TYPE_FOSSIL_COMMAND)
+ // FIXME: major uglyness with settings management
+ if(name == FOSSIL_SETTING_REMOTE_URL)
{
// Run as silent to avoid displaying credentials in the log
- runFossil(QStringList() << "remote-url" << QuotePath(it.value().Value.toString()), 0, RUNFLAGS_SILENT_INPUT);
+ fossil().setRemoteUrl(it.value().Value.toString());
continue;
}
Q_ASSERT(type == Settings::Setting::TYPE_FOSSIL_GLOBAL || type == Settings::Setting::TYPE_FOSSIL_LOCAL);
QString value = it.value().Value.toString();
- QStringList params;
-
- if(value.isEmpty())
- params << "unset" << name;
- else
- params << "settings" << name << value;
-
- if(type == Settings::Setting::TYPE_FOSSIL_GLOBAL)
- params << "-global";
-
- runFossil(params);
+ fossil().setFossilSetting(name, value, type == Settings::Setting::TYPE_FOSSIL_GLOBAL);
}
+
}
//------------------------------------------------------------------------------
@@ -2221,60 +1705,93 @@ void MainWindow::on_actionViewIgnored_triggered()
refresh();
}
+//------------------------------------------------------------------------------
+void MainWindow::on_actionViewAll_triggered()
+{
+ ui->actionViewModified->setChecked(true);
+ ui->actionViewUnchanged->setChecked(true);
+ ui->actionViewUnknown->setChecked(true);
+ ui->actionViewIgnored->setChecked(true);
+ refresh();
+}
+
//------------------------------------------------------------------------------
void MainWindow::on_actionViewAsList_triggered()
{
- viewMode = ui->actionViewAsList->isChecked() ? VIEWMODE_LIST : VIEWMODE_TREE;
- ui->treeView->setVisible(viewMode == VIEWMODE_TREE);
+ ui->actionViewAsFolders->setChecked(!ui->actionViewAsList->isChecked());
+ viewMode = ui->actionViewAsList->isChecked() ? VIEWMODE_LIST : VIEWMODE_TREE;
+
+ updateWorkspaceView();
updateFileView();
}
//------------------------------------------------------------------------------
-QString MainWindow::getFossilHttpAddress()
+void MainWindow::on_actionViewAsFolders_triggered()
{
- QString port = settings.GetValue(FUEL_SETTING_HTTP_PORT).toString();
- return "http://127.0.0.1:"+port;
+ ui->actionViewAsList->setChecked(!ui->actionViewAsFolders->isChecked());
+ viewMode = ui->actionViewAsList->isChecked() ? VIEWMODE_LIST : VIEWMODE_TREE;
+ updateWorkspaceView();
+ updateFileView();
}
//------------------------------------------------------------------------------
-void MainWindow::onTreeViewSelectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
+void MainWindow::onWorkspaceTreeViewSelectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{
- QModelIndexList selection = ui->treeView->selectionModel()->selectedIndexes();
- int num_selected = selection.count();
+ QModelIndexList indices = ui->workspaceTreeView->selectionModel()->selectedIndexes();
// Do not modify the selection if nothing is selected
- if(num_selected==0)
+ if(indices.empty())
return;
- selectedDirs.clear();
+ stringset_t new_dirs;
+ selectedTags.clear();
+ selectedBranches.clear();
- for(int i=0; idata(id, ROLE_WORKSPACE_ITEM);
+ Q_ASSERT(data.isValid());
+ WorkspaceItem tv = data.value();
+
+ if(tv.Type == WorkspaceItem::TYPE_FOLDER || tv.Type == WorkspaceItem::TYPE_WORKSPACE)
+ new_dirs.insert(tv.Value);
+ else if(tv.Type == WorkspaceItem::TYPE_TAG)
+ selectedTags.append(tv.Value);
+ else if(tv.Type == WorkspaceItem::TYPE_BRANCH)
+ selectedBranches.append(tv.Value);
}
- updateFileView();
+ // Update the selection if we have any new folders
+ if(!new_dirs.empty() && viewMode == VIEWMODE_TREE)
+ {
+ selectedDirs = new_dirs;
+ updateFileView();
+ }
}
//------------------------------------------------------------------------------
void MainWindow::on_actionOpenFolder_triggered()
{
- const QItemSelection &selection = ui->treeView->selectionModel()->selection();
+ const QItemSelection &selection = ui->workspaceTreeView->selectionModel()->selection();
if(selection.indexes().count()!=1)
return;
QModelIndex index = selection.indexes().at(0);
- on_treeView_doubleClicked(index);
+ on_workspaceTreeView_doubleClicked(index);
}
//------------------------------------------------------------------------------
-void MainWindow::on_treeView_doubleClicked(const QModelIndex &index)
+void MainWindow::on_workspaceTreeView_doubleClicked(const QModelIndex &index)
{
- QString target = repoDirModel.data(index, REPODIRMODEL_ROLE_PATH).toString();
- target = getCurrentWorkspace() + PATH_SEP + target;
+ QVariant data = index.model()->data(index, ROLE_WORKSPACE_ITEM);
+ Q_ASSERT(data.isValid());
+ WorkspaceItem tv = data.value();
+
+ if(tv.Type!=WorkspaceItem::TYPE_FOLDER && tv.Type!=WorkspaceItem::TYPE_WORKSPACE)
+ return;
+
+ QString target = getCurrentWorkspace() + PATH_SEPARATOR + tv.Value;
QUrl url = QUrl::fromLocalFile(target);
QDesktopServices::openUrl(url);
@@ -2299,7 +1816,7 @@ void MainWindow::on_actionRenameFolder_triggered()
return;
}
- int dir_start = old_path.lastIndexOf(PATH_SEP);
+ int dir_start = old_path.lastIndexOf(PATH_SEPARATOR);
if(dir_start==-1)
dir_start = 0;
else
@@ -2327,17 +1844,17 @@ void MainWindow::on_actionRenameFolder_triggered()
QString new_path = old_path.left(dir_start) + new_name;
- if(pathSet.contains(new_path))
+ if(getWorkspace().getPaths().contains(new_path))
{
QMessageBox::critical(this, tr("Error"), tr("Cannot rename folder.")+"\n" +tr("This folder exists already."));
return;
}
// Collect the files to be moved
- filelist_t files_to_move;
+ Workspace::filelist_t files_to_move;
QStringList new_paths;
QStringList operations;
- foreach(RepoFile *r, workspaceFiles)
+ foreach(WorkspaceFile *r, getWorkspace().getFiles())
{
if(r->getPath().indexOf(old_path)!=0)
continue;
@@ -2345,7 +1862,7 @@ void MainWindow::on_actionRenameFolder_triggered()
files_to_move.append(r);
QString new_dir = new_path + r->getPath().mid(old_path.length());
new_paths.append(new_dir);
- QString new_file_path = new_dir + PATH_SEP + r->getFilename();
+ QString new_file_path = new_dir + PATH_SEPARATOR + r->getFilename();
operations.append(r->getFilePath() + " -> " + new_file_path);
}
@@ -2364,10 +1881,10 @@ void MainWindow::on_actionRenameFolder_triggered()
Q_ASSERT(files_to_move.length() == new_paths.length());
for(int i=0; igetFilename();
+ WorkspaceFile *r = files_to_move[i];
+ const QString &new_file_path = new_paths[i] + PATH_SEPARATOR + r->getFilename();
- if(!runFossil(QStringList() << "mv" << QuotePath(r->getFilePath()) << QuotePath(new_file_path)))
+ if(!fossil().renameFile(r->getFilePath(), new_file_path, false))
{
log(tr("Move aborted due to errors")+"\n");
goto _exit;
@@ -2380,7 +1897,7 @@ void MainWindow::on_actionRenameFolder_triggered()
// First ensure that the target directories exist, and if not make them
for(int i=0; igetFilename();
+ WorkspaceFile *r = files_to_move[i];
+ QString new_file_path = new_paths[i] + PATH_SEPARATOR + r->getFilename();
if(QFile::exists(new_file_path))
{
@@ -2421,7 +1938,7 @@ void MainWindow::on_actionRenameFolder_triggered()
// Finally delete old files
for(int i=0; igetFilePath())+"\n");
@@ -2461,24 +1978,18 @@ const QIcon &MainWindow::getInternalIcon(const char* name)
}
//------------------------------------------------------------------------------
-void MainWindow::on_actionViewStash_triggered()
-{
- ui->tableViewStash->setVisible(ui->actionViewStash->isChecked());
-}
-
-//------------------------------------------------------------------------------
-void MainWindow::on_actionNewStash_triggered()
+void MainWindow::on_actionCreateStash_triggered()
{
QStringList stashed_files;
- getSelectionFilenames(stashed_files, RepoFile::TYPE_MODIFIED, true);
+ getSelectionFilenames(stashed_files, WorkspaceFile::TYPE_MODIFIED, true);
if(stashed_files.empty())
return;
QString stash_name;
bool revert = false;
- QString checkbox_text = tr("Revert stashed files");
- if(!CommitDialog::run(this, tr("Stash Changes"), stashed_files, stash_name, 0, true, &checkbox_text, &revert) || stashed_files.empty())
+
+ if(!CommitDialog::runStashNew(this, stashed_files, stash_name, revert) || stashed_files.empty())
return;
stash_name = stash_name.trimmed();
@@ -2490,7 +2001,7 @@ void MainWindow::on_actionNewStash_triggered()
}
// Check that this stash does not exist
- for(stashmap_t::iterator it=stashMap.begin(); it!=stashMap.end(); ++it)
+ for(stashmap_t::iterator it=getWorkspace().getStashes().begin(); it!=getWorkspace().getStashes().end(); ++it)
{
if(stash_name == it.key())
{
@@ -2500,11 +2011,8 @@ void MainWindow::on_actionNewStash_triggered()
}
// Do Stash
- QString command = "snapshot";
- if(revert)
- command = "save";
+ fossil().stashNew(stashed_files, stash_name, revert);
- runFossil(QStringList() << "stash" << command << "-m" << stash_name << QuotePaths(stashed_files) );
refresh();
}
@@ -2512,7 +2020,10 @@ void MainWindow::on_actionNewStash_triggered()
void MainWindow::on_actionApplyStash_triggered()
{
QStringList stashes;
- getStashViewSelection(stashes);
+ getSelectionStashes(stashes);
+
+ if(stashes.empty())
+ return;
bool delete_stashes = false;
if(!FileActionDialog::run(this, tr("Apply Stash"), tr("The following stashes will be applied.")+"\n"+tr("Are you sure?"), stashes, tr("Delete after applying"), &delete_stashes))
@@ -2521,10 +2032,10 @@ void MainWindow::on_actionApplyStash_triggered()
// Apply stashes
for(QStringList::iterator it=stashes.begin(); it!=stashes.end(); ++it)
{
- stashmap_t::iterator id_it = stashMap.find(*it);
- Q_ASSERT(id_it!=stashMap.end());
+ stashmap_t::iterator id_it = getWorkspace().getStashes().find(*it);
+ Q_ASSERT(id_it!=getWorkspace().getStashes().end());
- if(!runFossil(QStringList() << "stash" << "apply" << *id_it))
+ if(!fossil().stashApply(*id_it))
{
log(tr("Stash application aborted due to errors")+"\n");
return;
@@ -2534,10 +2045,10 @@ void MainWindow::on_actionApplyStash_triggered()
// Delete stashes
for(QStringList::iterator it=stashes.begin(); delete_stashes && it!=stashes.end(); ++it)
{
- stashmap_t::iterator id_it = stashMap.find(*it);
- Q_ASSERT(id_it!=stashMap.end());
+ stashmap_t::iterator id_it = getWorkspace().getStashes().find(*it);
+ Q_ASSERT(id_it!=getWorkspace().getStashes().end());
- if(!runFossil(QStringList() << "stash" << "drop" << *id_it))
+ if(!fossil().stashDrop(*id_it))
{
log(tr("Stash deletion aborted due to errors")+"\n");
return;
@@ -2551,7 +2062,7 @@ void MainWindow::on_actionApplyStash_triggered()
void MainWindow::on_actionDeleteStash_triggered()
{
QStringList stashes;
- getStashViewSelection(stashes);
+ getSelectionStashes(stashes);
if(stashes.empty())
return;
@@ -2562,10 +2073,10 @@ void MainWindow::on_actionDeleteStash_triggered()
// Delete stashes
for(QStringList::iterator it=stashes.begin(); it!=stashes.end(); ++it)
{
- stashmap_t::iterator id_it = stashMap.find(*it);
- Q_ASSERT(id_it!=stashMap.end());
+ stashmap_t::iterator id_it = getWorkspace().getStashes().find(*it);
+ Q_ASSERT(id_it!=getWorkspace().getStashes().end());
- if(!runFossil(QStringList() << "stash" << "drop" << *id_it))
+ if(!fossil().stashDrop(*id_it))
{
log(tr("Stash deletion aborted due to errors")+"\n");
return;
@@ -2579,16 +2090,16 @@ void MainWindow::on_actionDeleteStash_triggered()
void MainWindow::on_actionDiffStash_triggered()
{
QStringList stashes;
- getStashViewSelection(stashes);
+ getSelectionStashes(stashes);
if(stashes.length() != 1)
return;
- stashmap_t::iterator id_it = stashMap.find(*stashes.begin());
- Q_ASSERT(id_it!=stashMap.end());
+ stashmap_t::iterator id_it = getWorkspace().getStashes().find(*stashes.begin());
+ Q_ASSERT(id_it!=getWorkspace().getStashes().end());
// Run diff
- runFossil(QStringList() << "stash" << "diff" << *id_it, 0);
+ fossil().stashDiff(*id_it);
}
//------------------------------------------------------------------------------
@@ -2622,19 +2133,19 @@ void MainWindow::on_textBrowser_customContextMenuRequested(const QPoint &pos)
}
//------------------------------------------------------------------------------
-void MainWindow::on_tableView_customContextMenuRequested(const QPoint &pos)
+void MainWindow::on_fileTableView_customContextMenuRequested(const QPoint &pos)
{
- QPoint gpos = QCursor::pos();
+ QPoint gpos = QCursor::pos() + QPoint(1, 1);
#ifdef Q_OS_WIN
if(qApp->keyboardModifiers() & Qt::SHIFT)
{
- ui->tableView->selectionModel()->select(ui->tableView->indexAt(pos), QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Rows);
+ ui->fileTableView->selectionModel()->select(ui->fileTableView->indexAt(pos), QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Rows);
QStringList fnames;
getSelectionFilenames(fnames);
if(fnames.size()==1)
{
- QString fname = getCurrentWorkspace() + PATH_SEP + fnames[0];
+ QString fname = getCurrentWorkspace() + PATH_SEPARATOR + fnames[0];
fname = QDir::toNativeSeparators(fname);
if(ShowExplorerMenu((HWND)winId(), fname, gpos))
refresh();
@@ -2646,12 +2157,44 @@ void MainWindow::on_tableView_customContextMenuRequested(const QPoint &pos)
#endif
{
QMenu *menu = new QMenu(this);
- menu->addActions(ui->tableView->actions());
+ menu->addActions(ui->fileTableView->actions());
menu->popup(gpos);
}
}
+//------------------------------------------------------------------------------
+void MainWindow::on_workspaceTreeView_customContextMenuRequested(const QPoint &)
+{
+ QModelIndexList indices = ui->workspaceTreeView->selectionModel()->selectedIndexes();
+
+ if(indices.empty())
+ return;
+
+ QMenu *menu = 0;
+
+ // Get first selected item
+ const QModelIndex &mi = indices.first();
+ QVariant data = getWorkspace().getTreeModel().data(mi, ROLE_WORKSPACE_ITEM);
+ Q_ASSERT(data.isValid());
+ WorkspaceItem tv = data.value();
+
+ if(tv.Type == WorkspaceItem::TYPE_FOLDER || tv.Type == WorkspaceItem::TYPE_WORKSPACE)
+ menu = menuWorkspace;
+ else if (tv.Type == WorkspaceItem::TYPE_STASH || tv.Type == WorkspaceItem::TYPE_STASHES)
+ menu = menuStashes;
+ else if (tv.Type == WorkspaceItem::TYPE_TAG || tv.Type == WorkspaceItem::TYPE_TAGS)
+ menu = menuTags;
+ else if (tv.Type == WorkspaceItem::TYPE_BRANCH || tv.Type == WorkspaceItem::TYPE_BRANCHES)
+ menu = menuBranches;
+
+ if(menu)
+ {
+ QPoint pos = QCursor::pos() + QPoint(1, 1);
+ menu->popup(pos);
+ }
+}
+
//------------------------------------------------------------------------------
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
@@ -2685,11 +2228,11 @@ void MainWindow::dropEvent(QDropEvent *event)
QString abspath = finfo.absoluteFilePath();
// Within the current workspace ?
- if(abspath.indexOf(currentWorkspace)!=0)
+ if(abspath.indexOf(getCurrentWorkspace())!=0)
continue; // skip
// Remove workspace from full path
- QString wkpath = abspath.right(abspath.length()-currentWorkspace.length()-1);
+ QString wkpath = abspath.right(abspath.length()-getCurrentWorkspace().length()-1);
newfiles.append(wkpath);
}
@@ -2701,7 +2244,7 @@ void MainWindow::dropEvent(QDropEvent *event)
return;
// Do Add
- runFossil(QStringList() << "add" << QuotePaths(newfiles) );
+ fossil().addFiles(newfiles);
refresh();
}
@@ -2711,6 +2254,11 @@ void MainWindow::dropEvent(QDropEvent *event)
//------------------------------------------------------------------------------
void MainWindow::setBusy(bool busy)
{
+ if(busy)
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ else
+ QApplication::restoreOverrideCursor();
+
abortShortcut->setEnabled(busy);
bool enabled = !busy;
ui->menuBar->setEnabled(enabled);
@@ -2721,7 +2269,8 @@ void MainWindow::setBusy(bool busy)
//------------------------------------------------------------------------------
void MainWindow::onAbort()
{
- abortOperation = true;
+ operationAborted = true;
+ fossil().abortOperation();
// FIXME: Rename this to something better, Operation Aborted
log("
* "+tr("Terminated")+" *
", true);
}
@@ -2733,3 +2282,198 @@ void MainWindow::fullRefresh()
// Select the Root of the tree to update the file view
selectRootDir();
}
+
+//------------------------------------------------------------------------------
+void MainWindow::MainWinUICallback::logText(const QString& text, bool isHTML)
+{
+ Q_ASSERT(mainWindow);
+ mainWindow->log(text, isHTML);
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::MainWinUICallback::beginProcess(const QString& text)
+{
+ Q_ASSERT(mainWindow);
+ mainWindow->ui->statusBar->showMessage(text);
+ mainWindow->lblTags->setHidden(true);
+ mainWindow->lblRevision->setHidden(true);
+ mainWindow->progressBar->setHidden(false);
+ QCoreApplication::processEvents();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::MainWinUICallback::updateProcess(const QString& text)
+{
+ Q_ASSERT(mainWindow);
+ mainWindow->ui->statusBar->showMessage(text);
+ QCoreApplication::processEvents();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::MainWinUICallback::endProcess()
+{
+ Q_ASSERT(mainWindow);
+ mainWindow->ui->statusBar->clearMessage();
+ mainWindow->lblTags->setHidden(false);
+ mainWindow->lblRevision->setHidden(false);
+ mainWindow->progressBar->setHidden(true);
+ QCoreApplication::processEvents();
+}
+
+//------------------------------------------------------------------------------
+QMessageBox::StandardButton MainWindow::MainWinUICallback::Query(const QString &title, const QString &query, QMessageBox::StandardButtons buttons)
+{
+ return DialogQuery(mainWindow, title, query, buttons);
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::updateRevision(const QString &revision)
+{
+ const QString latest = tr(REVISION_LATEST);
+ QString defaultval = latest;
+
+ if(!revision.isEmpty())
+ defaultval = revision;
+
+ QString selected_revision = RevisionDialog::runUpdate(this, tr("Update workspace"), versionList, defaultval).trimmed();
+
+ if(selected_revision.isEmpty())
+ return;
+ else if(selected_revision == latest)
+ selected_revision = ""; // Empty revision is "latest"
+
+ QStringList res;
+
+ // Do test update
+ if(!fossil().updateRepository(res, selected_revision, true))
+ return;
+
+ if(res.length()==0)
+ return;
+
+ QStringMap kv;
+ ParseProperties(kv, res, ':');
+ // If no changes exit
+ if(kv.contains("changes") && kv["changes"].indexOf("None.")!=-1)
+ return;
+
+ if(!FileActionDialog::run(this, tr("Update"), tr("The following files will be updated.")+"\n"+tr("Are you sure?"), res))
+ return;
+
+ // Do update
+ fossil().updateRepository(res, selected_revision, false);
+ refresh();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::on_actionCreateTag_triggered()
+{
+ // Default to current revision
+ QString revision = fossil().getCurrentRevision();
+
+ QString name;
+ if(!RevisionDialog::runNewTag(this, tr("Create Tag"), versionList, revision, revision, name))
+ return;
+
+ if(name.isEmpty() || getWorkspace().getTags().contains(name) || getWorkspace().getBranches().contains(name))
+ {
+ QMessageBox::critical(this, tr("Error"), tr("Invalid name."), QMessageBox::Ok );
+ return;
+ }
+
+ fossil().tagNew(name, revision);
+ refresh();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::on_actionDeleteTag_triggered()
+{
+ if(selectedTags.size()!=1)
+ return;
+
+ const QString &tagname = selectedTags.first();
+
+ if(QMessageBox::Yes != DialogQuery(this, tr("Delete Tag"), tr("Are you sure want to delete the tag '%0' ?").arg(tagname)))
+ return;
+
+ Q_ASSERT(getWorkspace().getTags().contains(tagname));
+
+ const QString &revision = getWorkspace().getTags()[tagname];
+
+ fossil().tagDelete(tagname, revision);
+ refresh();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::on_actionCreateBranch_triggered()
+{
+ // Default to current revision
+ QString revision = fossil().getCurrentRevision();
+
+ QString branch_name;
+ if(!RevisionDialog::runNewTag(this, tr("Create Branch"), versionList, revision, revision, branch_name))
+ return;
+
+ if(branch_name.isEmpty() || getWorkspace().getTags().contains(branch_name) || getWorkspace().getBranches().contains(branch_name))
+ {
+ QMessageBox::critical(this, tr("Error"), tr("Invalid name."), QMessageBox::Ok );
+ return;
+ }
+
+ if(!fossil().branchNew(branch_name, revision, false))
+ return;
+
+ // Update to this branch.
+ updateRevision(branch_name);
+}
+//------------------------------------------------------------------------------
+void MainWindow::MergeRevision(const QString &defaultRevision)
+{
+ QStringList res;
+ QString revision = defaultRevision;
+
+ bool integrate = false;
+ bool force = false;
+ revision = RevisionDialog::runMerge(this, tr("Merge Branch"), versionList, revision, integrate, force);
+
+ if(revision.isEmpty())
+ return;
+
+ // Do test merge
+ if(!fossil().branchMerge(res, revision, integrate, force, true))
+ return;
+
+ if(!FileActionDialog::run(this, tr("Merge"), tr("The following changes will be applied.")+"\n"+tr("Are you sure?"), res))
+ return;
+
+ // Do update
+ fossil().branchMerge(res, revision, integrate, force, false);
+
+ log(tr("Merge completed. Don't forget to commit!")+"\n");
+
+ refresh();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::on_actionMergeBranch_triggered()
+{
+ QString revision;
+
+ if(!selectedBranches.isEmpty())
+ revision = selectedBranches.first();
+ MergeRevision(revision);
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::onSearchBoxTextChanged(const QString &)
+{
+ updateFileView();
+}
+
+//------------------------------------------------------------------------------
+void MainWindow::onSearch()
+{
+ searchBox->selectAll();
+ searchBox->setFocus();
+}
+
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 4d4208c..dc7eb64 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -2,114 +2,15 @@
#define MAINWINDOW_H
#include
-#include
#include
-#include
-#include
-#include
-#include
#include
-#include
-#include "SettingsDialog.h"
+#include "Settings.h"
+#include "Workspace.h"
namespace Ui {
class MainWindow;
}
-
-class QStringList;
-
-//////////////////////////////////////////////////////////////////////////
-// RepoFile
-//////////////////////////////////////////////////////////////////////////
-struct RepoFile
-{
- enum EntryType
- {
- TYPE_UNKNOWN = 1<<0,
- TYPE_UNCHANGED = 1<<1,
- TYPE_EDITTED = 1<<2,
- TYPE_ADDED = 1<<3,
- TYPE_DELETED = 1<<4,
- TYPE_MISSING = 1<<5,
- TYPE_RENAMED = 1<<6,
- TYPE_CONFLICTED = 1<<7,
- TYPE_MODIFIED = TYPE_EDITTED|TYPE_ADDED|TYPE_DELETED|TYPE_MISSING|TYPE_RENAMED|TYPE_CONFLICTED,
- TYPE_REPO = TYPE_UNCHANGED|TYPE_MODIFIED,
- TYPE_ALL = TYPE_UNKNOWN|TYPE_REPO
- };
-
- RepoFile(QFileInfo &info, EntryType type, const QString &repoPath)
- {
- FileInfo = info;
- Type = type;
- FilePath = getRelativeFilename(repoPath);
- Path = FileInfo.absolutePath();
-
- // Strip the workspace path from the path
- Q_ASSERT(Path.indexOf(repoPath)==0);
- Path = Path.mid(repoPath.length()+1);
- }
-
- bool isType(EntryType t) const
- {
- return Type == t;
- }
-
- void setType(EntryType t)
- {
- Type = t;
- }
-
- EntryType getType() const
- {
- return Type;
- }
-
- QFileInfo getFileInfo() const
- {
- return FileInfo;
- }
-
- bool isRepo() const
- {
- return Type == TYPE_UNCHANGED || Type == TYPE_EDITTED;
- }
-
- const QString &getFilePath() const
- {
- return FilePath;
- }
-
- QString getFilename() const
- {
- return FileInfo.fileName();
- }
-
- const QString &getPath() const
- {
- return Path;
- }
-
- QString getRelativeFilename(const QString &path)
- {
- QString abs_base_dir = QDir(path).absolutePath();
-
- QString relative = FileInfo.absoluteFilePath();
- int index = relative.indexOf(abs_base_dir);
- if(index<0)
- return QString("");
-
- return relative.right(relative.length() - abs_base_dir.length()-1);
- }
-
-private:
- QFileInfo FileInfo;
- EntryType Type;
- QString FilePath;
- QString Path;
-};
-
//////////////////////////////////////////////////////////////////////////
// MainWindow
//////////////////////////////////////////////////////////////////////////
@@ -120,38 +21,26 @@ class MainWindow : public QMainWindow
public:
explicit MainWindow(Settings &_settings, QWidget *parent = 0, QString *workspacePath = 0);
~MainWindow();
- bool diffFile(QString repoFile);
+ bool diffFile(const QString& repoFile);
void fullRefresh();
-private:
- typedef QSet stringset_t;
- enum RunFlags
- {
- RUNFLAGS_NONE = 0<<0,
- RUNFLAGS_SILENT_INPUT = 1<<0,
- RUNFLAGS_SILENT_OUTPUT = 1<<1,
- RUNFLAGS_SILENT_ALL = RUNFLAGS_SILENT_INPUT | RUNFLAGS_SILENT_OUTPUT,
- RUNFLAGS_DETACHED = 1<<2
- };
-
private:
bool refresh();
void scanWorkspace();
- bool runFossil(const QStringList &args, QStringList *output=0, int runFlags=RUNFLAGS_NONE);
- bool runFossilRaw(const QStringList &args, QStringList *output=0, int *exitCode=0, int runFlags=RUNFLAGS_NONE);
void applySettings();
void updateSettings();
+ void updateRevision(const QString& revision);
const QString &getCurrentWorkspace();
void setCurrentWorkspace(const QString &workspace);
void log(const QString &text, bool isHTML=false);
void setStatus(const QString &text);
- bool uiRunning() const { return fossilUI.state() == QProcess::Running; }
- void getSelectionFilenames(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false);
- void getFileViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false);
- void getDirViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false);
- void getStashViewSelection(QStringList &stashNames, bool allIfEmpty=false);
+ bool uiRunning() const;
+ void getSelectionFilenames(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
+ void getFileViewSelection(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
+ void getDirViewSelection(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
+ void getSelectionStashes(QStringList &stashNames);
void getSelectionPaths(stringset_t &paths);
- void getAllFilenames(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL);
+ void getAllFilenames(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL);
bool startUI();
void stopUI();
void enableActions(bool on);
@@ -159,13 +48,11 @@ private:
void rebuildRecent();
bool openWorkspace(const QString &path);
void loadFossilSettings();
- QString getFossilPath();
- QString getFossilHttpAddress();
- bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort);
- void updateDirView();
+ void updateWorkspaceView();
void updateFileView();
- void updateStashView();
void selectRootDir();
+ void MergeRevision(const QString& defaultRevision);
+
void fossilBrowse(const QString &fossilUrl);
void dragEnterEvent(class QDragEnterEvent *event);
void dropEvent(class QDropEvent *event);
@@ -173,15 +60,6 @@ private:
virtual QMenu *createPopupMenu();
const QIcon& getInternalIcon(const char *name);
- enum RepoStatus
- {
- REPO_OK,
- REPO_NOT_FOUND,
- REPO_OLD_SCHEMA
- };
-
- RepoStatus getRepoStatus();
-
enum ViewMode
{
VIEWMODE_LIST,
@@ -192,9 +70,11 @@ private slots:
// Manual slots.
// Use a different naming scheme to prevent warnings from Qt's automatic signaling
void onOpenRecent();
- void onTreeViewSelectionChanged(const class QItemSelection &selected, const class QItemSelection &deselected);
+ void onWorkspaceTreeViewSelectionChanged(const class QItemSelection &selected, const class QItemSelection &deselected);
void onFileViewDragOut();
void onAbort();
+ void onSearchBoxTextChanged(const QString &text);
+ void onSearch();
// Designer slots
void on_actionRefresh_triggered();
@@ -204,8 +84,8 @@ private slots:
void on_actionTimeline_triggered();
void on_actionHistory_triggered();
void on_actionClearLog_triggered();
- void on_tableView_doubleClicked(const QModelIndex &index);
- void on_treeView_doubleClicked(const QModelIndex &index);
+ void on_fileTableView_doubleClicked(const QModelIndex &index);
+ void on_workspaceTreeView_doubleClicked(const QModelIndex &index);
void on_actionOpenFile_triggered();
void on_actionPush_triggered();
void on_actionPull_triggered();
@@ -219,26 +99,57 @@ private slots:
void on_actionAbout_triggered();
void on_actionUpdate_triggered();
void on_actionSettings_triggered();
+ void on_actionFossilSettings_triggered();
void on_actionViewUnchanged_triggered();
void on_actionViewModified_triggered();
void on_actionViewUnknown_triggered();
void on_actionViewIgnored_triggered();
+ void on_actionViewAll_triggered();
void on_actionViewAsList_triggered();
+ void on_actionViewAsFolders_triggered();
void on_actionOpenFolder_triggered();
void on_actionRenameFolder_triggered();
void on_actionNewRepository_triggered();
void on_actionOpenRepository_triggered();
void on_actionCloseRepository_triggered();
void on_actionCloneRepository_triggered();
- void on_actionViewStash_triggered();
- void on_actionNewStash_triggered();
+ void on_actionCreateStash_triggered();
void on_actionApplyStash_triggered();
void on_actionDeleteStash_triggered();
void on_actionDiffStash_triggered();
void on_textBrowser_customContextMenuRequested(const QPoint &pos);
- void on_tableView_customContextMenuRequested(const QPoint &pos);
+ void on_fileTableView_customContextMenuRequested(const QPoint &pos);
+ void on_workspaceTreeView_customContextMenuRequested(const QPoint &pos);
+ void on_actionCreateTag_triggered();
+ void on_actionDeleteTag_triggered();
+ void on_actionCreateBranch_triggered();
+ void on_actionMergeBranch_triggered();
private:
+ class MainWinUICallback : public UICallback
+ {
+ public:
+ MainWinUICallback() : mainWindow(0)
+ {}
+
+ void init(class MainWindow *mainWindow)
+ {
+ this->mainWindow = mainWindow;
+ }
+
+ virtual void logText(const QString& text, bool isHTML);
+ virtual void beginProcess(const QString& text);
+ virtual void updateProcess(const QString& text);
+ virtual void endProcess();
+ virtual QMessageBox::StandardButton Query(const QString &title, const QString &query, QMessageBox::StandardButtons buttons);
+
+
+ private:
+ class MainWindow *mainWindow;
+ };
+
+ friend class MainWinUICallback;
+
enum
{
MAX_RECENT=5
@@ -249,30 +160,36 @@ private:
Ui::MainWindow *ui;
QFileIconProvider iconProvider;
icon_map_t iconCache;
- QStandardItemModel repoFileModel;
- QStandardItemModel repoDirModel;
- QStandardItemModel repoStashModel;
- QProcess fossilUI;
class QAction *recentWorkspaceActs[MAX_RECENT];
class QProgressBar *progressBar;
+ class QLabel *lblRevision;
+ class QLabel *lblTags;
class QShortcut *abortShortcut;
- bool abortOperation;
+ class SearchBox *searchBox;
+ class QShortcut *searchShortcut;
+ QMenu *menuWorkspace;
+ QMenu *menuStashes;
+ QMenu *menuTags;
+ QMenu *menuBranches;
+
+ bool operationAborted;
+ stringset_t selectedDirs; // The directory selected in the tree
+ QStringList selectedTags;
+ QStringList selectedBranches;
+ QStringList versionList;
+
+ Workspace workspace;
+ Workspace & getWorkspace() { return workspace; }
+
+ Fossil & fossil() { return workspace.fossil(); }
+ const Fossil & fossil() const { return workspace.fossil(); }
Settings &settings;
- QString projectName;
- QString repositoryFile;
QStringList workspaceHistory;
- QString currentWorkspace;
- ViewMode viewMode;
- stringset_t selectedDirs; // The directory selected in the tree
- // Repository State
- typedef QList filelist_t;
- typedef QMap filemap_t;
- typedef QMap stashmap_t;
- filemap_t workspaceFiles;
- stringset_t pathSet;
- stashmap_t stashMap;
+ MainWinUICallback uiCallback;
+
+ ViewMode viewMode;
};
#endif // MAINWINDOW_H
diff --git a/src/RevisionDialog.cpp b/src/RevisionDialog.cpp
new file mode 100644
index 0000000..0f2ca8b
--- /dev/null
+++ b/src/RevisionDialog.cpp
@@ -0,0 +1,92 @@
+#include "RevisionDialog.h"
+#include "ui_RevisionDialog.h"
+#include "Utils.h"
+
+//-----------------------------------------------------------------------------
+RevisionDialog::RevisionDialog(QWidget *parent, const QStringList &completions, const QString &defaultValue) :
+ QDialog(parent),
+ ui(new Ui::RevisionDialog),
+ completer(completions, parent)
+{
+ ui->setupUi(this);
+ ui->cmbRevision->setCompleter(&completer);
+
+ ui->cmbRevision->addItems(completions);
+
+ if(!defaultValue.isEmpty())
+ ui->cmbRevision->setCurrentText(defaultValue);
+}
+
+//-----------------------------------------------------------------------------
+RevisionDialog::~RevisionDialog()
+{
+ delete ui;
+}
+
+//-----------------------------------------------------------------------------
+QString RevisionDialog::runUpdate(QWidget *parent, const QString &title, const QStringList &completions, const QString &defaultValue)
+{
+ RevisionDialog dlg(parent, completions, defaultValue);
+ dlg.setWindowTitle(title);
+ dlg.ui->lblName->setVisible(false);
+ dlg.ui->lineName->setVisible(false);
+ dlg.ui->lblIntegrate->setVisible(false);
+ dlg.ui->chkIntegrate->setVisible(false);
+ dlg.ui->lblForce->setVisible(false);
+ dlg.ui->chkForce->setVisible(false);
+
+ dlg.adjustSize();
+
+ if(dlg.exec() != QDialog::Accepted)
+ return QString("");
+ return dlg.ui->cmbRevision->currentText().trimmed();
+}
+
+//-----------------------------------------------------------------------------
+QString RevisionDialog::runMerge(QWidget *parent, const QString &title, const QStringList &completions, const QString &defaultValue, bool &integrate, bool &force)
+{
+ RevisionDialog dlg(parent, completions, defaultValue);
+ dlg.setWindowTitle(title);
+ dlg.ui->lblName->setVisible(false);
+ dlg.ui->lineName->setVisible(false);
+ dlg.ui->lblIntegrate->setVisible(true);
+ dlg.ui->chkIntegrate->setVisible(true);
+ dlg.ui->chkIntegrate->setChecked(integrate);
+ dlg.ui->lblForce->setVisible(true);
+ dlg.ui->chkForce->setVisible(true);
+ dlg.ui->chkForce->setChecked(force);
+
+ dlg.adjustSize();
+
+ if(dlg.exec() != QDialog::Accepted)
+ return QString("");
+
+ integrate = dlg.ui->chkIntegrate->checkState() == Qt::Checked;
+ force = dlg.ui->chkForce->checkState() == Qt::Checked;
+
+ return dlg.ui->cmbRevision->currentText().trimmed();
+}
+
+
+//-----------------------------------------------------------------------------
+bool RevisionDialog::runNewTag(QWidget *parent, const QString &title, const QStringList &completions, const QString &defaultValue, QString &revision, QString &name)
+{
+ RevisionDialog dlg(parent, completions, defaultValue);
+ dlg.setWindowTitle(title);
+
+ dlg.ui->lblName->setVisible(true);
+ dlg.ui->lineName->setVisible(true);
+ dlg.ui->lblIntegrate->setVisible(false);
+ dlg.ui->chkIntegrate->setVisible(false);
+ dlg.ui->lblForce->setVisible(false);
+ dlg.ui->chkForce->setVisible(false);
+
+ dlg.adjustSize();
+
+ if(dlg.exec() != QDialog::Accepted)
+ return false;
+
+ revision = dlg.ui->cmbRevision->currentText().trimmed();
+ name = dlg.ui->lineName->text().trimmed();
+ return true;
+}
diff --git a/src/RevisionDialog.h b/src/RevisionDialog.h
new file mode 100644
index 0000000..42f678a
--- /dev/null
+++ b/src/RevisionDialog.h
@@ -0,0 +1,28 @@
+#ifndef REVISIONDIALOG_H
+#define REVISIONDIALOG_H
+
+#include
+#include
+
+namespace Ui {
+ class RevisionDialog;
+}
+
+class RevisionDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RevisionDialog(QWidget *parent, const QStringList &completions, const QString &defaultValue);
+ ~RevisionDialog();
+
+ static QString runUpdate(QWidget *parent, const QString &title, const QStringList &completions, const QString &defaultValue);
+ static QString runMerge(QWidget* parent, const QString& title, const QStringList& completions, const QString& defaultValue, bool& integrate, bool& force);
+ static bool runNewTag(QWidget *parent, const QString &title, const QStringList &completions, const QString &defaultValue, QString &revision, QString &name);
+
+private:
+ Ui::RevisionDialog *ui;
+ QCompleter completer;
+};
+
+#endif // REVISIONDIALOG_H
diff --git a/src/SearchBox.cpp b/src/SearchBox.cpp
new file mode 100644
index 0000000..0cbf495
--- /dev/null
+++ b/src/SearchBox.cpp
@@ -0,0 +1,24 @@
+#include "SearchBox.h"
+#include
+
+SearchBox::SearchBox(QWidget *parent) : QLineEdit(parent)
+{
+}
+
+SearchBox::~SearchBox()
+{
+
+}
+
+void SearchBox::keyPressEvent(QKeyEvent *event)
+{
+ // Clear text on escape
+ if(event->key() == Qt::Key_Escape)
+ {
+ setText("");
+ clearFocus();
+ }
+ else
+ QLineEdit::keyPressEvent(event);
+}
+
diff --git a/src/SearchBox.h b/src/SearchBox.h
new file mode 100644
index 0000000..53b7487
--- /dev/null
+++ b/src/SearchBox.h
@@ -0,0 +1,23 @@
+#ifndef SEARCHBOX_H
+#define SEARCHBOX_H
+
+#include
+
+class SearchBox : public QLineEdit
+{
+ Q_OBJECT
+public:
+ explicit SearchBox(QWidget* parent=0);
+ ~SearchBox();
+
+signals:
+
+public slots:
+
+protected:
+ void keyPressEvent(QKeyEvent *event);
+
+};
+
+
+#endif // SEARCHBOX_H
diff --git a/src/Settings.cpp b/src/Settings.cpp
new file mode 100644
index 0000000..95ce3cf
--- /dev/null
+++ b/src/Settings.cpp
@@ -0,0 +1,124 @@
+#include "Settings.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+///////////////////////////////////////////////////////////////////////////////
+Settings::Settings(bool portableMode) : store(0)
+{
+ Mappings.insert(FOSSIL_SETTING_GDIFF_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
+ Mappings.insert(FOSSIL_SETTING_GMERGE_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
+ Mappings.insert(FOSSIL_SETTING_PROXY_URL, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
+ Mappings.insert(FOSSIL_SETTING_HTTP_PORT, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
+
+ Mappings.insert(FOSSIL_SETTING_IGNORE_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
+ Mappings.insert(FOSSIL_SETTING_CRNL_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
+ Mappings.insert(FOSSIL_SETTING_REMOTE_URL, Setting("off", Setting::TYPE_FOSSIL_COMMAND));
+
+ // Go into portable mode when explicitly requested or if a config file exists next to the executable
+ QString ini_path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + QCoreApplication::applicationName() + ".ini");
+ if( portableMode || QFile::exists(ini_path))
+ store = new QSettings(ini_path, QSettings::IniFormat);
+ else
+ {
+ // Linux: ~/.config/organizationName/applicationName.conf
+ // Windows: HKEY_CURRENT_USER\Software\organizationName\Fuel
+ store = new QSettings(QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ }
+ Q_ASSERT(store);
+
+ if(!HasValue(FUEL_SETTING_FILE_DBLCLICK))
+ SetValue(FUEL_SETTING_FILE_DBLCLICK, 0);
+ if(!HasValue(FUEL_SETTING_LANGUAGE) && SupportsLang(QLocale::system().name()))
+ SetValue(FUEL_SETTING_LANGUAGE, QLocale::system().name());
+ if(!HasValue(FUEL_SETTING_WEB_BROWSER))
+ SetValue(FUEL_SETTING_WEB_BROWSER, 0);
+
+ ApplyEnvironment();
+}
+
+//-----------------------------------------------------------------------------
+Settings::~Settings()
+{
+ Q_ASSERT(store);
+ delete store;
+}
+
+//-----------------------------------------------------------------------------
+void Settings::ApplyEnvironment()
+{
+ QString lang_id = GetValue(FUEL_SETTING_LANGUAGE).toString();
+#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
+ QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
+#endif
+ if(!InstallLang(lang_id))
+ SetValue(FUEL_SETTING_LANGUAGE, "en_US");
+}
+
+//-----------------------------------------------------------------------------
+bool Settings::InstallLang(const QString &langId)
+{
+ if(langId == "en_US")
+ {
+ QCoreApplication::instance()->removeTranslator(&translator);
+ return true;
+ }
+
+ QString locale_path = QString(":intl/intl/%0.qm").arg(langId);
+ if(!translator.load(locale_path))
+ return false;
+
+ Q_ASSERT(!translator.isEmpty());
+ QCoreApplication::instance()->installTranslator(&translator);
+
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool Settings::HasValue(const QString &name) const
+{
+ return store->contains(name);
+}
+
+//-----------------------------------------------------------------------------
+const QVariant Settings::GetValue(const QString &name)
+{
+ if(!HasValue(name))
+ return QVariant();
+ return store->value(name);
+}
+
+//-----------------------------------------------------------------------------
+void Settings::SetValue(const QString &name, const QVariant &value)
+{
+ store->setValue(name, value);
+}
+
+//-----------------------------------------------------------------------------
+QVariant &Settings::GetFossilValue(const QString &name)
+{
+ mappings_t::iterator it=Mappings.find(name);
+ Q_ASSERT(it!=Mappings.end());
+ return it.value().Value;
+}
+
+//-----------------------------------------------------------------------------
+void Settings::SetFossilValue(const QString &name, const QVariant &value)
+{
+ mappings_t::iterator it=Mappings.find(name);
+ Q_ASSERT(it!=Mappings.end());
+ it->Value = value;
+}
+
+//-----------------------------------------------------------------------------
+bool Settings::SupportsLang(const QString &langId) const
+{
+ QString locale_path = QString(":intl/intl/%0.qm").arg(langId);
+ QResource res(locale_path);
+ return res.isValid();
+}
diff --git a/src/Settings.h b/src/Settings.h
new file mode 100644
index 0000000..f0535ee
--- /dev/null
+++ b/src/Settings.h
@@ -0,0 +1,75 @@
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include
+#include
+#include
+
+#define FUEL_SETTING_FOSSIL_PATH "FossilPath"
+#define FUEL_SETTING_COMMIT_MSG "CommitMsgHistory"
+#define FUEL_SETTING_FILE_DBLCLICK "FileDblClickAction"
+#define FUEL_SETTING_LANGUAGE "Language"
+#define FUEL_SETTING_WEB_BROWSER "WebBrowser"
+
+#define FOSSIL_SETTING_GDIFF_CMD "gdiff-command"
+#define FOSSIL_SETTING_GMERGE_CMD "gmerge-command"
+#define FOSSIL_SETTING_PROXY_URL "proxy"
+#define FOSSIL_SETTING_IGNORE_GLOB "ignore-glob"
+#define FOSSIL_SETTING_CRNL_GLOB "crnl-glob"
+#define FOSSIL_SETTING_REMOTE_URL "remote-url"
+#define FOSSIL_SETTING_HTTP_PORT "http-port"
+
+
+enum FileDblClickAction
+{
+ FILE_DLBCLICK_ACTION_DIFF,
+ FILE_DLBCLICK_ACTION_OPEN,
+ FILE_DLBCLICK_ACTION_OPENCONTAINING,
+ FILE_DLBCLICK_ACTION_MAX
+};
+
+struct Settings
+{
+ struct Setting
+ {
+ enum SettingType
+ {
+ TYPE_FOSSIL_GLOBAL,
+ TYPE_FOSSIL_LOCAL,
+ TYPE_FOSSIL_COMMAND
+ };
+
+ Setting(QVariant value, SettingType type) : Value(value), Type(type)
+ {}
+ QVariant Value;
+ SettingType Type;
+ };
+ typedef QMap mappings_t;
+
+
+ Settings(bool portableMode = false);
+ ~Settings();
+
+ void ApplyEnvironment();
+
+ // App configuration access
+ class QSettings * GetStore() { return store; }
+ bool HasValue(const QString &name) const; // store->contains(FUEL_SETTING_FOSSIL_PATH)
+ const QVariant GetValue(const QString &name); // settings.store->value
+ void SetValue(const QString &name, const QVariant &value); // settings.store->value
+
+ // Fossil configuration access
+ QVariant & GetFossilValue(const QString &name);
+ void SetFossilValue(const QString &name, const QVariant &value);
+ mappings_t & GetMappings() { return Mappings; }
+
+ bool SupportsLang(const QString &langId) const;
+ bool InstallLang(const QString &langId);
+private:
+ mappings_t Mappings;
+ class QSettings *store;
+ QTranslator translator;
+};
+
+
+#endif // SETTINGS_H
diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp
index 3cf5810..59b86d7 100644
--- a/src/SettingsDialog.cpp
+++ b/src/SettingsDialog.cpp
@@ -1,37 +1,8 @@
#include "SettingsDialog.h"
#include "ui_SettingsDialog.h"
-#include
#include "Utils.h"
-#include
-#include
#include
-#include
-#include
-#include
-
-
-///////////////////////////////////////////////////////////////////////////////
-QString SettingsDialog::SelectExe(QWidget *parent, const QString &description)
-{
- QString filter(tr("Applications"));
-#ifdef Q_OS_WIN
- filter += " (*.exe)";
-#else
- filter += " (*)";
-#endif
- QString path = QFileDialog::getOpenFileName(
- parent,
- description,
- QString(),
- filter,
- &filter);
-
- if(!QFile::exists(path))
- return QString();
-
- return path;
-}
///////////////////////////////////////////////////////////////////////////////
SettingsDialog::SettingsDialog(QWidget *parent, Settings &_settings) :
@@ -54,7 +25,6 @@ SettingsDialog::SettingsDialog(QWidget *parent, Settings &_settings) :
ui->lineFossilPath->setText(QDir::toNativeSeparators(settings->GetValue(FUEL_SETTING_FOSSIL_PATH).toString()));
ui->cmbDoubleClickAction->setCurrentIndex(settings->GetValue(FUEL_SETTING_FILE_DBLCLICK).toInt());
ui->cmbFossilBrowser->setCurrentIndex(settings->GetValue(FUEL_SETTING_WEB_BROWSER).toInt());
- ui->lineUIPort->setText(settings->GetValue(FUEL_SETTING_HTTP_PORT).toString());
// Initialize language combo
foreach(const LangMap &m, langMap)
@@ -66,15 +36,6 @@ SettingsDialog::SettingsDialog(QWidget *parent, Settings &_settings) :
ui->cmbActiveLanguage->findText(
LangIdToName(lang)));
- // Global Settings
- ui->lineGDiffCommand->setText(settings->GetFossilValue(FOSSIL_SETTING_GDIFF_CMD).toString());
- ui->lineGMergeCommand->setText(settings->GetFossilValue(FOSSIL_SETTING_GMERGE_CMD).toString());
- ui->lineProxy->setText(settings->GetFossilValue(FOSSIL_SETTING_PROXY_URL).toString());
-
- // Repository Settings
- ui->lineRemoteURL->setText(settings->GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString());
- ui->lineIgnore->setText(settings->GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString());
- ui->lineIgnoreCRNL->setText(settings->GetFossilValue(FOSSIL_SETTING_CRNL_GLOB).toString());
}
//-----------------------------------------------------------------------------
@@ -97,7 +58,6 @@ void SettingsDialog::on_buttonBox_accepted()
Q_ASSERT(ui->cmbDoubleClickAction->currentIndex()>=FILE_DLBCLICK_ACTION_DIFF && ui->cmbDoubleClickAction->currentIndex()SetValue(FUEL_SETTING_FILE_DBLCLICK, ui->cmbDoubleClickAction->currentIndex());
settings->SetValue(FUEL_SETTING_WEB_BROWSER, ui->cmbFossilBrowser->currentIndex());
- settings->SetValue(FUEL_SETTING_HTTP_PORT, ui->lineUIPort->text());
Q_ASSERT(settings->HasValue(FUEL_SETTING_LANGUAGE));
QString curr_langid = settings->GetValue(FUEL_SETTING_LANGUAGE).toString();
@@ -108,15 +68,6 @@ void SettingsDialog::on_buttonBox_accepted()
if(curr_langid != new_langid)
QMessageBox::information(this, tr("Restart required"), tr("The language change will take effect after restarting the application"), QMessageBox::Ok);
- settings->SetFossilValue(FOSSIL_SETTING_GDIFF_CMD, ui->lineGDiffCommand->text());
- settings->SetFossilValue(FOSSIL_SETTING_GMERGE_CMD, ui->lineGMergeCommand->text());
- settings->SetFossilValue(FOSSIL_SETTING_PROXY_URL, ui->lineProxy->text());
-
- settings->SetFossilValue(FOSSIL_SETTING_REMOTE_URL, ui->lineRemoteURL->text());
- settings->SetFossilValue(FOSSIL_SETTING_IGNORE_GLOB, ui->lineIgnore->text());
- settings->SetFossilValue(FOSSIL_SETTING_CRNL_GLOB, ui->lineIgnoreCRNL->text());
-
-
settings->ApplyEnvironment();
}
@@ -128,22 +79,6 @@ void SettingsDialog::on_btnSelectFossil_clicked()
ui->lineFossilPath->setText(QDir::toNativeSeparators(path));
}
-//-----------------------------------------------------------------------------
-void SettingsDialog::on_btnSelectFossilGDiff_clicked()
-{
- QString path = SelectExe(this, tr("Select Graphical Diff application"));
- if(!path.isEmpty())
- ui->lineGDiffCommand->setText(QDir::toNativeSeparators(path));
-}
-
-//-----------------------------------------------------------------------------
-void SettingsDialog::on_btnSelectGMerge_clicked()
-{
- QString path = SelectExe(this, tr("Select Graphical Merge application"));
- if(!path.isEmpty())
- ui->lineGMergeCommand->setText(path);
-}
-
//-----------------------------------------------------------------------------
void SettingsDialog::on_btnClearMessageHistory_clicked()
{
@@ -186,120 +121,3 @@ QString SettingsDialog::LangNameToId(const QString &name)
return "";
}
-
-///////////////////////////////////////////////////////////////////////////////
-Settings::Settings(bool portableMode) : store(0)
-{
- Mappings.insert(FOSSIL_SETTING_GDIFF_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
- Mappings.insert(FOSSIL_SETTING_GMERGE_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
- Mappings.insert(FOSSIL_SETTING_PROXY_URL, Setting("", Setting::TYPE_FOSSIL_GLOBAL));
-
- Mappings.insert(FOSSIL_SETTING_IGNORE_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
- Mappings.insert(FOSSIL_SETTING_CRNL_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
- Mappings.insert(FOSSIL_SETTING_REMOTE_URL, Setting("off", Setting::TYPE_FOSSIL_COMMAND));
-
- // Go into portable mode when explicitly requested or if a config file exists next to the executable
- QString ini_path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + QCoreApplication::applicationName() + ".ini");
- if( portableMode || QFile::exists(ini_path))
- store = new QSettings(ini_path, QSettings::IniFormat);
- else
- {
- // Linux: ~/.config/organizationName/applicationName.conf
- // Windows: HKEY_CURRENT_USER\Software\organizationName\Fuel
- store = new QSettings(QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
- }
- Q_ASSERT(store);
-
- if(!HasValue(FUEL_SETTING_FILE_DBLCLICK))
- SetValue(FUEL_SETTING_FILE_DBLCLICK, 0);
- if(!HasValue(FUEL_SETTING_LANGUAGE) && SupportsLang(QLocale::system().name()))
- SetValue(FUEL_SETTING_LANGUAGE, QLocale::system().name());
- if(!HasValue(FUEL_SETTING_WEB_BROWSER))
- SetValue(FUEL_SETTING_WEB_BROWSER, 0);
- if(!HasValue(FUEL_SETTING_HTTP_PORT))
- SetValue(FUEL_SETTING_HTTP_PORT, "8090");
-
- ApplyEnvironment();
-}
-
-//-----------------------------------------------------------------------------
-Settings::~Settings()
-{
- Q_ASSERT(store);
- delete store;
-}
-
-//-----------------------------------------------------------------------------
-void Settings::ApplyEnvironment()
-{
- QString lang_id = GetValue(FUEL_SETTING_LANGUAGE).toString();
-#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
- QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
-#endif
- if(!InstallLang(lang_id))
- SetValue(FUEL_SETTING_LANGUAGE, "en_US");
-}
-
-//-----------------------------------------------------------------------------
-bool Settings::InstallLang(const QString &langId)
-{
- if(langId == "en_US")
- {
- QCoreApplication::instance()->removeTranslator(&translator);
- return true;
- }
-
- QString locale_path = QString(":intl/intl/%0.qm").arg(langId);
- if(!translator.load(locale_path))
- return false;
-
- Q_ASSERT(!translator.isEmpty());
- QCoreApplication::instance()->installTranslator(&translator);
-
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-bool Settings::HasValue(const QString &name) const
-{
- return store->contains(name);
-}
-
-//-----------------------------------------------------------------------------
-const QVariant Settings::GetValue(const QString &name)
-{
- if(!HasValue(name))
- return QVariant();
- return store->value(name);
-}
-
-//-----------------------------------------------------------------------------
-void Settings::SetValue(const QString &name, const QVariant &value)
-{
- store->setValue(name, value);
-}
-
-//-----------------------------------------------------------------------------
-QVariant &Settings::GetFossilValue(const QString &name)
-{
- mappings_t::iterator it=Mappings.find(name);
- Q_ASSERT(it!=Mappings.end());
- return it.value().Value;
-}
-
-//-----------------------------------------------------------------------------
-void Settings::SetFossilValue(const QString &name, const QVariant &value)
-{
- mappings_t::iterator it=Mappings.find(name);
- Q_ASSERT(it!=Mappings.end());
- it->Value = value;
-}
-
-//-----------------------------------------------------------------------------
-bool Settings::SupportsLang(const QString &langId) const
-{
- QString locale_path = QString(":intl/intl/%0.qm").arg(langId);
- QResource res(locale_path);
- return res.isValid();
-}
diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h
index b57ba3b..418f2ea 100644
--- a/src/SettingsDialog.h
+++ b/src/SettingsDialog.h
@@ -2,82 +2,12 @@
#define SETTINGSDIALOG_H
#include
-#include
-#include
-#include
-
+#include "Settings.h"
namespace Ui {
class SettingsDialog;
}
-#define FUEL_SETTING_FOSSIL_PATH "FossilPath"
-#define FUEL_SETTING_COMMIT_MSG "CommitMsgHistory"
-#define FUEL_SETTING_FILE_DBLCLICK "FileDblClickAction"
-#define FUEL_SETTING_LANGUAGE "Language"
-#define FUEL_SETTING_WEB_BROWSER "WebBrowser"
-#define FUEL_SETTING_HTTP_PORT "HTTPPort"
-
-#define FOSSIL_SETTING_GDIFF_CMD "gdiff-command"
-#define FOSSIL_SETTING_GMERGE_CMD "gmerge-command"
-#define FOSSIL_SETTING_PROXY_URL "proxy"
-#define FOSSIL_SETTING_IGNORE_GLOB "ignore-glob"
-#define FOSSIL_SETTING_CRNL_GLOB "crnl-glob"
-#define FOSSIL_SETTING_REMOTE_URL "remote-url"
-
-
-enum FileDblClickAction
-{
- FILE_DLBCLICK_ACTION_DIFF,
- FILE_DLBCLICK_ACTION_OPEN,
- FILE_DLBCLICK_ACTION_OPENCONTAINING,
- FILE_DLBCLICK_ACTION_MAX
-};
-
-struct Settings
-{
- struct Setting
- {
- enum SettingType
- {
- TYPE_FOSSIL_GLOBAL,
- TYPE_FOSSIL_LOCAL,
- TYPE_FOSSIL_COMMAND
- };
-
- Setting(QVariant value, SettingType type) : Value(value), Type(type)
- {}
- QVariant Value;
- SettingType Type;
- };
- typedef QMap mappings_t;
-
-
- Settings(bool portableMode = false);
- ~Settings();
-
- void ApplyEnvironment();
-
- // App configuration access
- class QSettings * GetStore() { return store; }
- bool HasValue(const QString &name) const; // store->contains(FUEL_SETTING_FOSSIL_PATH)
- const QVariant GetValue(const QString &name); // settings.store->value
- void SetValue(const QString &name, const QVariant &value); // settings.store->value
-
- // Fossil configuration access
- QVariant & GetFossilValue(const QString &name);
- void SetFossilValue(const QString &name, const QVariant &value);
- mappings_t & GetMappings() { return Mappings; }
-
- bool SupportsLang(const QString &langId) const;
- bool InstallLang(const QString &langId);
-private:
- mappings_t Mappings;
- class QSettings *store;
- QTranslator translator;
-};
-
-
class SettingsDialog : public QDialog
{
Q_OBJECT
@@ -92,12 +22,9 @@ public:
private slots:
void on_btnSelectFossil_clicked();
void on_buttonBox_accepted();
- void on_btnSelectFossilGDiff_clicked();
- void on_btnSelectGMerge_clicked();
void on_btnClearMessageHistory_clicked();
private:
- static QString SelectExe(QWidget *parent, const QString &description);
QString LangIdToName(const QString &id);
QString LangNameToId(const QString &name);
void CreateLangMap();
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 18ccce2..4dedf39 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -1,6 +1,7 @@
#include "Utils.h"
#include
#include
+#include
///////////////////////////////////////////////////////////////////////////////
QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons)
@@ -14,6 +15,42 @@ QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, c
return res;
}
+//-----------------------------------------------------------------------------
+QString QuotePath(const QString &path)
+{
+ return path;
+}
+
+//-----------------------------------------------------------------------------
+QStringList QuotePaths(const QStringList &paths)
+{
+ QStringList res;
+ for(int i=0; i
@@ -158,7 +195,7 @@ bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint)
// IShellFolder interface.
//
bool bResult = false;
-
+
LPMALLOC pMalloc;
if (!SUCCEEDED (SHGetMalloc (&pMalloc)))
return bResult;
@@ -291,3 +328,64 @@ bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint)
#endif
+//-----------------------------------------------------------------------------
+void ParseProperties(QStringMap &properties, const QStringList &lines, QChar separator)
+{
+ properties.clear();
+ foreach(QString l, lines)
+ {
+ l = l.trimmed();
+ int index = l.indexOf(separator);
+
+ QString key;
+ QString value;
+ if(index!=-1)
+ {
+ key = l.left(index).trimmed();
+ value = l.mid(index+1).trimmed();
+ }
+ else
+ key = l;
+
+ properties.insert(key, value);
+ }
+}
+
+
+//------------------------------------------------------------------------------
+void GetStandardItemTextRecursive(QString &name, const QStandardItem &item, const QChar &separator)
+{
+ if(item.parent())
+ {
+ GetStandardItemTextRecursive(name, *item.parent());
+ name.append(separator);
+ }
+
+ name.append(item.data(Qt::DisplayRole).toString());
+}
+
+//------------------------------------------------------------------------------
+void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItem &item)
+{
+ QString name;
+ GetStandardItemTextRecursive(name, item);
+ map.insert(name, item.index());
+
+ for(int i=0; i
#include
+#include
+#include
+
+#define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
+#define FOSSIL_CHECKOUT1 "_FOSSIL_"
+#define FOSSIL_CHECKOUT2 ".fslckout"
+#define FOSSIL_EXT "fossil"
+
QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons = QMessageBox::Yes|QMessageBox::No);
+QString QuotePath(const QString &path);
+QStringList QuotePaths(const QStringList &paths);
+QString SelectExe(QWidget *parent, const QString &description);
+
+
+typedef QMap name_modelindex_map_t;
+void GetStandardItemTextRecursive(QString &name, const QStandardItem &item, const QChar &separator='/');
+void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItem &item);
+void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel &model);
+
+
+typedef QMap QStringMap;
+void ParseProperties(QStringMap &properties, const QStringList &lines, QChar separator=' ');
#ifdef Q_OS_WIN
bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint);
#endif
+class UICallback
+{
+public:
+ virtual void logText(const QString &text, bool isHTML)=0;
+ virtual void beginProcess(const QString &text)=0;
+ virtual void updateProcess(const QString &text)=0;
+ virtual void endProcess()=0;
+ virtual QMessageBox::StandardButton Query(const QString &title, const QString &query, QMessageBox::StandardButtons buttons)=0;
+};
+
+
+class ScopedStatus
+{
+public:
+ ScopedStatus(UICallback *callback, const QString &text) : uiCallback(callback)
+ {
+ uiCallback->beginProcess(text);
+ }
+
+ ~ScopedStatus()
+ {
+ uiCallback->endProcess();
+ }
+
+private:
+ UICallback *uiCallback;
+};
#endif // UTILS_H
diff --git a/src/Workspace.cpp b/src/Workspace.cpp
new file mode 100644
index 0000000..fb5c467
--- /dev/null
+++ b/src/Workspace.cpp
@@ -0,0 +1,218 @@
+#include "Workspace.h"
+#include
+#include "Utils.h"
+
+//-----------------------------------------------------------------------------
+Workspace::~Workspace()
+{
+ clearState();
+}
+
+//------------------------------------------------------------------------------
+void Workspace::clearState()
+{
+ // Dispose RepoFiles
+ foreach(WorkspaceFile *r, getFiles())
+ delete r;
+
+ getFiles().clear();
+ getPaths().clear();
+ stashMap.clear();
+ branchList.clear();
+ tags.clear();
+ isIntegrated = false;
+}
+
+//------------------------------------------------------------------------------
+bool Workspace::scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool &abort, UICallback &uiCallback)
+{
+ QDir dir(dirPath);
+
+ uiCallback.updateProcess(dirPath);
+
+ QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
+ for (int i=0; ifileName();
+ QString fullpath = it->absoluteFilePath();
+
+ // Skip fossil files
+ if(filename == FOSSIL_CHECKOUT1 || filename == FOSSIL_CHECKOUT2 || (!fossil().getRepositoryFile().isEmpty() && QFileInfo(fullpath) == QFileInfo(fossil().getRepositoryFile())))
+ continue;
+
+ WorkspaceFile *rf = new WorkspaceFile(*it, WorkspaceFile::TYPE_UNKNOWN, wkdir);
+ getFiles().insert(rf->getFilePath(), rf);
+ getPaths().insert(rf->getPath());
+ }
+ }
+ uiCallback.endProcess();
+
+ uiCallback.beginProcess(QObject::tr("Updating..."));
+
+ // Update Files and Directories
+
+ for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); ++line_it)
+ {
+ QString line = (*line_it).trimmed();
+ if(line.length()==0)
+ continue;
+
+ int space_index = line.indexOf(' ');
+ if(space_index==-1)
+ continue;
+
+ QString status_text = line.left(space_index);
+ QString fname = line.right(line.length() - space_index).trimmed();
+ WorkspaceFile::Type type = WorkspaceFile::TYPE_UNKNOWN;
+
+ // Generate a RepoFile for all non-existant fossil files
+ // or for all files if we skipped scanning the workspace
+ bool add_missing = !scan_files;
+
+ if(status_text=="EDITED")
+ type = WorkspaceFile::TYPE_EDITTED;
+ else if(status_text=="ADDED")
+ type = WorkspaceFile::TYPE_ADDED;
+ else if(status_text=="DELETED")
+ {
+ type = WorkspaceFile::TYPE_DELETED;
+ add_missing = true;
+ }
+ else if(status_text=="MISSING")
+ {
+ type = WorkspaceFile::TYPE_MISSING;
+ add_missing = true;
+ }
+ else if(status_text=="RENAMED")
+ type = WorkspaceFile::TYPE_RENAMED;
+ else if(status_text=="UNCHANGED")
+ type = WorkspaceFile::TYPE_UNCHANGED;
+ else if(status_text=="CONFLICT")
+ type = WorkspaceFile::TYPE_CONFLICTED;
+ else if(status_text=="UPDATED_BY_MERGE" || status_text=="ADDED_BY_MERGE" || status_text=="ADDED_BY_INTEGRATE" || status_text=="UPDATED_BY_INTEGRATE")
+ type = WorkspaceFile::TYPE_MERGED;
+
+ // Filter unwanted file types
+ if( ((type & WorkspaceFile::TYPE_MODIFIED) && !scanModified) ||
+ ((type & WorkspaceFile::TYPE_UNCHANGED) && !scanUnchanged))
+ {
+ getFiles().remove(fname);
+ continue;
+ }
+ else
+ add_missing = true;
+
+ Workspace::filemap_t::iterator it = getFiles().find(fname);
+
+ WorkspaceFile *rf = 0;
+ if(add_missing && it==getFiles().end())
+ {
+ QFileInfo info(wkdir+QDir::separator()+fname);
+ rf = new WorkspaceFile(info, type, wkdir);
+ getFiles().insert(rf->getFilePath(), rf);
+ }
+
+ if(!rf)
+ {
+ it = getFiles().find(fname);
+ Q_ASSERT(it!=getFiles().end());
+ rf = *it;
+ }
+
+ rf->setType(type);
+
+ QString path = rf->getPath();
+ getPaths().insert(path);
+ }
+
+ // Check if the repository needs integration
+ res.clear();
+ fossil().status(res);
+ isIntegrated = false;
+ foreach(const QString &l, res)
+ {
+ if(l.trimmed().indexOf("INTEGRATE")==0)
+ {
+ isIntegrated = true;
+ break;
+ }
+ }
+
+ // Load the stashes, branches and tags
+ fossil().stashList(getStashes());
+
+ fossil().branchList(branchList, branchList);
+
+ fossil().tagList(tags);
+ // Fossil includes the branches in the tag list
+ // So remove them
+ foreach(const QString &name, branchList)
+ tags.remove(name);
+
+_done:
+ uiCallback.endProcess();
+}
+
diff --git a/src/Workspace.h b/src/Workspace.h
new file mode 100644
index 0000000..68dfa2c
--- /dev/null
+++ b/src/Workspace.h
@@ -0,0 +1,145 @@
+#ifndef WORKSPACE_H
+#define WORKSPACE_H
+
+#include
+#include
+#include
+#include
+#include
+#include "Utils.h"
+#include "Fossil.h"
+
+//////////////////////////////////////////////////////////////////////////
+// WorkspaceFile
+//////////////////////////////////////////////////////////////////////////
+struct WorkspaceFile
+{
+ enum Type
+ {
+ TYPE_UNKNOWN = 1<<0,
+ TYPE_UNCHANGED = 1<<1,
+ TYPE_EDITTED = 1<<2,
+ TYPE_ADDED = 1<<3,
+ TYPE_DELETED = 1<<4,
+ TYPE_MISSING = 1<<5,
+ TYPE_RENAMED = 1<<6,
+ TYPE_CONFLICTED = 1<<7,
+ TYPE_MERGED = 1<<8,
+ TYPE_MODIFIED = TYPE_EDITTED|TYPE_ADDED|TYPE_DELETED|TYPE_MISSING|TYPE_RENAMED|TYPE_CONFLICTED|TYPE_MERGED,
+ TYPE_REPO = TYPE_UNCHANGED|TYPE_MODIFIED,
+ TYPE_ALL = TYPE_UNKNOWN|TYPE_REPO
+ };
+
+ WorkspaceFile(const QFileInfo &info, Type type, const QString &repoPath)
+ {
+ FileInfo = info;
+ FileType = type;
+ FilePath = getRelativeFilename(repoPath);
+ Path = FileInfo.absolutePath();
+
+ // Strip the workspace path from the path
+ Q_ASSERT(Path.indexOf(repoPath)==0);
+ Path = Path.mid(repoPath.length()+1);
+ }
+
+ bool isType(Type t) const
+ {
+ return FileType == t;
+ }
+
+ void setType(Type t)
+ {
+ FileType = t;
+ }
+
+ Type getType() const
+ {
+ return FileType;
+ }
+
+ QFileInfo getFileInfo() const
+ {
+ return FileInfo;
+ }
+
+ const QString &getFilePath() const
+ {
+ return FilePath;
+ }
+
+ QString getFilename() const
+ {
+ return FileInfo.fileName();
+ }
+
+ const QString &getPath() const
+ {
+ return Path;
+ }
+
+ QString getRelativeFilename(const QString &path)
+ {
+ QString abs_base_dir = QDir(path).absolutePath();
+
+ QString relative = FileInfo.absoluteFilePath();
+ int index = relative.indexOf(abs_base_dir);
+ if(index<0)
+ return QString("");
+
+ return relative.right(relative.length() - abs_base_dir.length()-1);
+ }
+
+private:
+ QFileInfo FileInfo;
+ Type FileType;
+ QString FilePath;
+ QString Path;
+};
+
+
+typedef QSet stringset_t;
+
+//////////////////////////////////////////////////////////////////////////
+// Workspace
+//////////////////////////////////////////////////////////////////////////
+
+class Workspace
+{
+public:
+ ~Workspace();
+
+ typedef QList filelist_t;
+ typedef QMap filemap_t;
+
+ void clearState();
+
+ Fossil & fossil() { return bridge; }
+ const Fossil & fossil() const { return bridge; }
+
+ static bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort, UICallback &uiCallback);
+ void scanWorkspace(bool scanLocal, bool scanIgnored, bool scanModified, bool scanUnchanged, const QString &ignoreGlob, UICallback &uiCallback, bool &operationAborted);
+
+ QStandardItemModel &getFileModel() { return repoFileModel; }
+ QStandardItemModel &getTreeModel() { return repoTreeModel; }
+
+ filemap_t &getFiles() { return workspaceFiles; }
+ stringset_t &getPaths() { return pathSet; }
+ stashmap_t &getStashes() { return stashMap; }
+ QStringMap &getTags() { return tags; }
+ QStringList &getBranches() { return branchList; }
+ bool otherChanges() const { return isIntegrated; }
+
+private:
+ Fossil bridge;
+ filemap_t workspaceFiles;
+ stringset_t pathSet;
+ stashmap_t stashMap;
+ QStringList branchList;
+ QStringMap tags;
+ bool isIntegrated;
+
+ QStandardItemModel repoFileModel;
+ QStandardItemModel repoTreeModel;
+};
+
+#endif // WORKSPACE_H
diff --git a/src/main.cpp b/src/main.cpp
index 7688571..98cbd88 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,7 +5,7 @@ int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setApplicationName("Fuel");
- app.setApplicationVersion("1.0.1");
+ app.setApplicationVersion("2.0.0");
app.setOrganizationDomain("fuel-scm.org");
app.setOrganizationName("Fuel-SCM");
diff --git a/ui/BrowserWidget.ui b/ui/BrowserWidget.ui
index c651f9f..6ef556c 100644
--- a/ui/BrowserWidget.ui
+++ b/ui/BrowserWidget.ui
@@ -14,7 +14,16 @@
0
-
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
0
-
@@ -44,7 +53,7 @@
- :/icons/icons/Button Previous-01.png:/icons/icons/Button Previous-01.png
+ :/icons/icon-action-previous:/icons/icon-action-previous
Back
@@ -56,7 +65,7 @@
- :/icons/icons/Button Next-01.png:/icons/icons/Button Next-01.png
+ :/icons/icon-action-next:/icons/icon-action-next
Forward
@@ -68,7 +77,7 @@
- :/icons/icons/Button Refresh-01.png:/icons/icons/Button Refresh-01.png
+ :/icons/icon-action-refresh:/icons/icon-action-refresh
Refresh
@@ -80,7 +89,7 @@
- :/icons/icons/Button Close-01.png:/icons/icons/Button Close-01.png
+ :/icons/icon-action-stop:/icons/icon-action-stop
Stop
diff --git a/ui/CommitDialog.ui b/ui/CommitDialog.ui
index da07022..0907856 100644
--- a/ui/CommitDialog.ui
+++ b/ui/CommitDialog.ui
@@ -10,7 +10,7 @@
0
0
400
- 300
+ 394
@@ -62,9 +62,72 @@
-
-
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ New branch
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Branch name
+
+
+
+ -
+
+
+ Private branch
+
+
+
+
+
+
+ -
+
-
+ Revert stashed files
diff --git a/ui/FslSettingsDialog.ui b/ui/FslSettingsDialog.ui
new file mode 100644
index 0000000..db9ea78
--- /dev/null
+++ b/ui/FslSettingsDialog.ui
@@ -0,0 +1,300 @@
+
+
+ FslSettingsDialog
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 457
+ 266
+
+
+
+ Fossil Settings
+
+
+
+ :/icons/icon-application:/icons/icon-application
+
+
+ true
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 100
+ 0
+
+
+
+ Graphical Diff
+
+
+
+ -
+
+
-
+
+
+ Path to graphical diff tool
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 24
+ 24
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Graphical Merge
+
+
+
+ -
+
+
-
+
+
+ Path to the graphical merge tool
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 24
+ 24
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ HTTP Proxy
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ The URL of the HTTP proxy
+
+
+
+ -
+
+
+ HTTP Port
+
+
+
+ -
+
+
+ HTTP port to use for the Fossil web interface
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Ignore CR/NL
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ A comma separated list of glob-style file/path patterns ignored in Fossil file operations
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Ignore List
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ The remote url used to push/pull changes.
+URL style user names and passwords are also supported.
+For example http://username:password@server.com/fossil
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Remote Url
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ FslSettingsDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ FslSettingsDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui
index 6652298..7774bde 100644
--- a/ui/MainWindow.ui
+++ b/ui/MainWindow.ui
@@ -18,7 +18,7 @@
- :/icons/icons/Battery-01.png:/icons/icons/Battery-01.png
+ :/icons/icon-application:/icons/icon-application
true
@@ -58,7 +58,7 @@
Qt::Horizontal
-
+
20
@@ -66,7 +66,7 @@
- Qt::ActionsContextMenu
+ Qt::CustomContextMenu
QAbstractItemView::NoEditTriggers
@@ -78,16 +78,13 @@
QAbstractItemView::SelectItems
- true
+ false
-
- true
-
false
-
+
80
@@ -140,44 +137,6 @@
30
-
-
-
- 20
- 0
-
-
-
- Qt::ActionsContextMenu
-
-
- QAbstractItemView::NoEditTriggers
-
-
- true
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
- true
-
-
- false
-
-
- false
-
-
- true
-
-
- false
-
-
@@ -190,7 +149,7 @@
QTabWidget::South
- 1
+ 0
@@ -263,7 +222,7 @@
0
0
865
- 22
+ 23
+
+
@@ -343,8 +323,6 @@
-
-
@@ -357,10 +335,10 @@
- :/icons/icons/Button Refresh-01.png:/icons/icons/Button Refresh-01.png
+ :/icons/icon-action-refresh:/icons/icon-action-refresh
- Refresh
+ &Refresh
Refresh the views
@@ -375,10 +353,10 @@
- :/icons/icons/Save-01.png:/icons/icons/Save-01.png
+ :/icons/icon-action-commit:/icons/icon-action-commit
- Commit
+ &Commit
Commit modifications
@@ -393,7 +371,7 @@
- :/icons/icons/Document Copy-01.png:/icons/icons/Document Copy-01.png
+ :/icons/icon-item-diff:/icons/icon-item-diff
Diff
@@ -411,7 +389,7 @@
- :/icons/icons/File New-01.png:/icons/icons/File New-01.png
+ :/icons/icon-item-add:/icons/icon-item-add
Add
@@ -429,7 +407,7 @@
- :/icons/icons/File Delete-01.png:/icons/icons/File Delete-01.png
+ :/icons/icon-item-delete:/icons/icon-item-delete
Delete
@@ -447,7 +425,7 @@
- :/icons/icons/Document Blank-01.png:/icons/icons/Document Blank-01.png
+ :/icons/icon-action-repo-new:/icons/icon-action-repo-new
&New...
@@ -465,7 +443,7 @@
- :/icons/icons/My Documents-01.png:/icons/icons/My Documents-01.png
+ :/icons/icon-action-repo-open:/icons/icon-action-repo-open
&Open...
@@ -497,10 +475,10 @@
- :/icons/icons/My Websites-01.png:/icons/icons/My Websites-01.png
+ :/icons/icon-action-repo-clone:/icons/icon-action-repo-clone
- Clone...
+ C&lone...
Clone a remote repository
@@ -509,10 +487,10 @@
- :/icons/icons/Button Upload-01.png:/icons/icons/Button Upload-01.png
+ :/icons/icon-action-push:/icons/icon-action-push
- Push
+ &Push
Push changes to the remote repository
@@ -527,10 +505,10 @@
- :/icons/icons/Button Download-01.png:/icons/icons/Button Download-01.png
+ :/icons/icon-action-pull:/icons/icon-action-pull
- Pull
+ Pu&ll
Pull changes from the remote repository
@@ -545,7 +523,7 @@
- :/icons/icons/File Open-01.png:/icons/icons/File Open-01.png
+ :/icons/icon-item-rename:/icons/icon-item-rename
Rename
@@ -563,7 +541,7 @@
- :/icons/icons/Button Turn Off-01.png:/icons/icons/Button Turn Off-01.png
+ :/icons/icon-action-quit:/icons/icon-action-quit
&Quit
@@ -584,7 +562,7 @@
- :/icons/icons/File History-01.png:/icons/icons/File History-01.png
+ :/icons/icon-item-history:/icons/icon-item-history
History
@@ -605,7 +583,7 @@
- :/icons/icons/Network MAC-01.png:/icons/icons/Network MAC-01.png
+ :/icons/icon-webview:/icons/icon-webview
Fossil UI
@@ -620,7 +598,7 @@
- :/icons/icons/Document-Revert-icon.png:/icons/icons/Document-Revert-icon.png
+ :/icons/icon-item-revert:/icons/icon-item-revert
Revert
@@ -635,7 +613,7 @@
- :/icons/icons/Text Edit.png:/icons/icons/Text Edit.png
+ :/icons/icon-clear-log:/icons/icon-clear-log
Clear Log
@@ -650,7 +628,7 @@
- :/icons/icons/Clock-01.png:/icons/icons/Clock-01.png
+ :/icons/icon-action-timeline:/icons/icon-action-timeline
Timeline
@@ -665,7 +643,7 @@
- :/icons/icons/Document-01.png:/icons/icons/Document-01.png
+ :/icons/icon-item-file:/icons/icon-item-file
Open File
@@ -683,7 +661,7 @@
- :/icons/icons/Folder-01.png:/icons/icons/Folder-01.png
+ :/icons/icon-action-folder-explore:/icons/icon-action-folder-explore
Open Containing
@@ -701,10 +679,10 @@
- :/icons/icons/Button Reload-01.png:/icons/icons/Button Reload-01.png
+ :/icons/icon-action-undo:/icons/icon-action-undo
- Undo
+ U&ndo
Undo the last Fossil action
@@ -719,7 +697,7 @@
- :/icons/icons/Battery-01.png:/icons/icons/Battery-01.png
+ :/icons/icon-application:/icons/icon-application
&About...
@@ -734,13 +712,13 @@
- :/icons/icons/Button Play-01.png:/icons/icons/Button Play-01.png
+ :/icons/icon-action-update:/icons/icon-action-update
- Update
+ &Update
- Update the workspace to the latest version
+ Update the workspace to a revision
Update the workspace to the latest version
@@ -752,7 +730,7 @@
- :/icons/icons/Gear-01.png:/icons/icons/Gear-01.png
+ :/icons/icon-action-settings:/icons/icon-action-settings
&Preferences...
@@ -840,7 +818,7 @@
- :/icons/icons/Folder-01.png:/icons/icons/Folder-01.png
+ :/icons/icon-item-folder:/icons/icon-item-folder
Open Folder
@@ -855,7 +833,7 @@
- :/icons/icons/Folder Open-01.png:/icons/icons/Folder Open-01.png
+ :/icons/icon-action-folder-rename:/icons/icon-action-folder-rename
Rename Folder
@@ -867,13 +845,13 @@
Rename the selected folder
-
+
- :/icons/icons/Folder Add-01.png:/icons/icons/Folder Add-01.png
+ :/icons/icon-action-stash-new:/icons/icon-action-stash-new
- Stash changes
+ &Stash Changes
Stash changes
@@ -882,7 +860,7 @@
- :/icons/icons/Folder Open-01.png:/icons/icons/Folder Open-01.png
+ :/icons/icon-action-stash-apply:/icons/icon-action-stash-apply
Apply Stash
@@ -894,27 +872,10 @@
Apply stashed changes
-
-
- true
-
-
- &Stashed Changes
-
-
- View Stashed Changes
-
-
- View Stashed Changes
-
-
- Show the list of stashed changes
-
-
- :/icons/icons/Folder Delete-01.png:/icons/icons/Folder Delete-01.png
+ :/icons/icon-action-stash-delete:/icons/icon-action-stash-delete
Delete Stash
@@ -923,12 +884,100 @@
- :/icons/icons/Folder Explorer-01.png:/icons/icons/Folder Explorer-01.png
+ :/icons/icon-action-stash-diff:/icons/icon-action-stash-diff
Diff Stash
+
+
+
+ :/icons/icon-action-tag-new:/icons/icon-action-tag-new
+
+
+ Create &Tag
+
+
+ Create a tag for a revision
+
+
+ Create a tag for a revision
+
+
+
+
+
+ :/icons/icon-action-tag-delete:/icons/icon-action-tag-delete
+
+
+ Delete Tag
+
+
+ Delete Tag
+
+
+
+
+
+ :/icons/icon-item-branch:/icons/icon-item-branch
+
+
+ Create &Branch
+
+
+ Create a branch from a revision
+
+
+ Create a branch from a revision
+
+
+
+
+
+ :/icons/icon-action-merge:/icons/icon-action-merge
+
+
+ Merge Branch
+
+
+ Merge with a branch
+
+
+ Merge with a branch
+
+
+
+
+ true
+
+
+ Files and F&olders
+
+
+ View files and folders
+
+
+ View the workspace as files and folders
+
+
+
+
+ &All
+
+
+ Show all files
+
+
+
+
+
+ :/icons/icon-action-settings:/icons/icon-action-settings
+
+
+ F&ossil Settings
+
+
diff --git a/ui/RevisionDialog.ui b/ui/RevisionDialog.ui
new file mode 100644
index 0000000..f2bb9a1
--- /dev/null
+++ b/ui/RevisionDialog.ui
@@ -0,0 +1,129 @@
+
+
+ RevisionDialog
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 478
+ 177
+
+
+
+
+ 400
+ 0
+
+
+
+ true
+
+
+ -
+
+
-
+
+
+ Revision
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Integrate
+
+
+
+ -
+
+
+ Force
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ RevisionDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ RevisionDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/ui/SettingsDialog.ui b/ui/SettingsDialog.ui
index 0117ecc..0685355 100644
--- a/ui/SettingsDialog.ui
+++ b/ui/SettingsDialog.ui
@@ -10,396 +10,177 @@
0
0
457
- 352
+ 204
Settings
+
+
+ :/icons/icon-application:/icons/icon-application
+
true
-
-
-
- 0
-
-
-
-
- :/icons/icons/Battery-01.png:/icons/icons/Battery-01.png
-
-
- Application
-
-
-
-
-
-
-
- 100
- 0
-
-
-
- Fossil Path
-
-
-
- -
-
-
-
-
-
- Path to the Fossil executable. Leave blank to use the default Fossil
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 24
- 24
-
-
-
- ...
-
-
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Graphical Diff
-
-
-
- -
-
-
-
-
-
- Path to graphical diff tool
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 24
- 24
-
-
-
- ...
-
-
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Graphical Merge
-
-
-
- -
-
-
-
-
-
- Path to the graphical merge tool
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 24
- 24
-
-
-
- ...
-
-
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- HTTP Proxy
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- The URL of the HTTP proxy
-
-
-
- -
-
-
- HTTP Port
-
-
-
- -
-
-
- HTTP port to use for the Fossil web interface
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Commit Messages
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Clear the commit message history
-
-
- Clear
-
-
-
- -
-
-
- Web Browser
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Web browser to use for the Fossil web interface
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Double-click Action
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Action to perfom when double-clicking a file
-
-
- -1
-
-
-
- -
-
-
- Language
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Language for the user interface
-
-
-
-
-
-
-
-
- :/icons/icons/Book-01.png:/icons/icons/Book-01.png
-
-
- Repository
-
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
- -
-
-
-
- 100
- 0
-
-
-
- Remote Url
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- The remote url used to push/pull changes.
-URL style user names and passwords are also supported.
-For example http://username:password@server.com/fossil
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Ignore List
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- A comma separated list of glob-style file/path patterns ignored in Fossil file operations
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Ignore CR/NL
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking
-
-
-
-
-
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Fossil Path
+
+
+
+ -
+
+
-
+
+
+ Path to the Fossil executable. Leave blank to use the default Fossil
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 24
+ 24
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Commit Messages
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Clear the commit message history
+
+
+ Clear
+
+
+
+ -
+
+
+ Web Browser
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Web browser to use for the Fossil web interface
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Double-click Action
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Action to perfom when double-clicking a file
+
+
+ -1
+
+
+
+ -
+
+
+ Language
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Language for the user interface
+
+
+
+
-