186 Commits

Author SHA1 Message Date
b148b86311 OSX: Guess actual executable from the app bundle directory
FossilOrigin-Name: 7988e53166771ea71d916a1ab714eb671d07dea8
2015-08-22 17:32:24 +00:00
521e0e7d7b Disabled automatically adding Fossil's remote-url to the list of remotes.
FossilOrigin-Name: cb27c4e2a0777b5c3fa64a07d19cd6b89e05c76f
2015-08-22 17:05:26 +00:00
0200f4b7b4 Somewhat better Log/Browser TabBar on OSX
FossilOrigin-Name: aea2b8b8985d6b40aff4b53eeaa485702c674658
2015-08-22 16:35:10 +00:00
cf66ce260e Added space to the right side of the searchbox
FossilOrigin-Name: 39572229e93c1a8b63a0e7ed48a1bbe14e67c53c
2015-08-22 14:52:43 +00:00
6c3ad4001c Improved the look of the UI on OSX
FossilOrigin-Name: 08496c56dbe49392c45a50d4bac5621d61b96e84
2015-08-22 14:13:47 +00:00
abd223076c Fixed OSX linker errors
FossilOrigin-Name: d9a15280f162ca92b6d65c41033d43890d306779
2015-08-22 11:40:53 +00:00
1efc307718 Fixed incorrect command splitting for custom actions
FossilOrigin-Name: 29c6a41585bad0bf9f506ea3ba148bb3ad3a2dca
2015-08-22 11:04:16 +00:00
8a2c6293c2 Custom actions now expect macros for file and folder expansion
Custom actions now trigger even without a selection
Use native path separators in custom actions


FossilOrigin-Name: e017a9126cf63ed060dd88accfcabbd9726e9aa7
2015-08-21 10:54:20 +00:00
83217d8e26 Updated tool-tips for custom actions
FossilOrigin-Name: 9b67709b92eae80e70f8ae2b775014092f820fdf
2015-08-21 10:05:45 +00:00
2eebb8d8ba Fixed issue where all exe files had the same icon
FossilOrigin-Name: 14ddcd158900cd672a4c3d88e8465f7ff88f14d2
2015-08-21 09:53:34 +00:00
4ac8e89580 Fixed custom action exe path parsing
Renamed custom action macros from '$' to '%' so that they match fossil's
Fixed Release build warnings

FossilOrigin-Name: b9521f3974653bd28a4f422c2f48755a949b9823
2015-08-21 09:42:44 +00:00
9b26352141 Fixed repository active tags parsing
FossilOrigin-Name: 581579c7dac57d016b878c707814491bb0cbe4f6
2015-08-17 08:36:30 +00:00
925ab25ff5 Added more messageboxes for failed fossil operations
FossilOrigin-Name: 2193c91c253a1de2657a19df0aa9a8b6604c7006
2015-08-17 08:29:14 +00:00
af805db1cb More refactoring
Added messageboxes for failed fossil operations

FossilOrigin-Name: 4a23244f2aa93ec132a74c5b3a7c04d467596963
2015-08-17 07:59:11 +00:00
adaee79179 Use the fossil settings to determine whether gdiff or diff should be used for diffing files
FossilOrigin-Name: b3dc68271fb2d6fbbe80060d3ec3d206f5f0097f
2015-08-14 11:09:22 +00:00
0dcea2a332 Minor UI tweaks
FossilOrigin-Name: 49797e154e77b27ff722edee9654e1b7d42a04cd
2015-08-14 10:43:09 +00:00
5b400ead74 Fixed issue with detached execution not returning the correct result status
FossilOrigin-Name: 28d8e8098a1f1d19ce8494c1926dad0490632b82
2015-08-14 10:42:39 +00:00
48ed97dc10 Refactored and unified workspace path functions
FossilOrigin-Name: 5e89247bf2fa2cf6d5720ddf51e091319ab66459
2015-08-14 10:00:31 +00:00
0f4ff6d3c9 Fixed issue with Fossil settings containing spaces
FossilOrigin-Name: bc8becb1aa02ed969a3330a756e6c54ab1a2a67a
2015-08-14 09:53:59 +00:00
cd8186ec61 Made About dialog modal
FossilOrigin-Name: 79337d9af5942ca7ace8ec49610e524a4561d3a0
2015-08-14 08:59:09 +00:00
abd4bbc125 Better layout for the About dialog
FossilOrigin-Name: 196bdb56e3c2d617f1a1410dc61bc22eb6feb242
2015-08-13 11:41:05 +00:00
623a6ad297 Simplified About dialog
FossilOrigin-Name: e5adb715a5bbbce1fc488d7802c2367120c3007f
2015-08-13 11:39:09 +00:00
65dd35a592 - Reimplemented the about dialog
- Added licenses for external libraries
- Translators and license text now live in separate resource files


FossilOrigin-Name: 8b95a9008769c1082936f3083d65428c262385db
2015-08-13 11:16:53 +00:00
5ac1b77727 Merged Korean translation
FossilOrigin-Name: 2c93f1713207d90729d6d1f105560bc0a24e365b
2015-08-04 08:33:55 +00:00
1e41a0e2f2 Added Korean (ko_KR) translation. Thanks ardiefox.
FossilOrigin-Name: 7fa0e987609c9ccb3d1518f21230e26f8a122d5c
2015-08-04 08:22:20 +00:00
b8c6607b56 Fixed Fossil UI http-port setting management
FossilOrigin-Name: 4cb9b13c6b6c9f3a2fcc62de4737ea61e98ed433
2015-07-31 19:34:48 +00:00
fce2e05019 Fixed warnings on Unix build
FossilOrigin-Name: abdefeaa2bae5e084735797b7f18aa4d738a8b56
2015-07-29 18:31:39 +00:00
44b5faa40f Fixed issue with Fossil's default remote-url with password preventing commits
FossilOrigin-Name: c16b5a89043d313fdee1e645edf9d2d3f1600d0f
2015-07-29 09:20:59 +00:00
387cbb57e8 Fall back to textual diff when gdiff fails
Fossil Exe path needs to be set before applySettings

FossilOrigin-Name: a9115a3369a7897bfc665f2a9614f61dbec80493
2015-07-29 09:09:14 +00:00
45db311f63 Fixed issue where the explicit fossil path setting was not being applied
FossilOrigin-Name: c9ad2689d2d946996f03dbdd62016a33cb7ef4a4
2015-07-29 07:34:18 +00:00
d23e35edd7 Apply the default remote url to Fossil as well.
FossilOrigin-Name: faaed64ae5cace45eb2ce7fff9f4c179bfbc5a80
2015-07-23 19:01:06 +00:00
e2307d673a - Keychain data is now stored within a settings group on Windows
- Do not attempt make Fossil's default url the default if one exists already


FossilOrigin-Name: 15f5fde7bf2f69a76b9f8b1e1a7a0530856210bc
2015-07-23 18:46:47 +00:00
e2dd42f40a Fixed Qt runtime warning caused by automatic singals
FossilOrigin-Name: 193d2c862c9eda6235b4360da1130411ee619766
2015-07-23 18:04:18 +00:00
92ccbb10ab Address Fossil weirdness which treats all closed branches as active tags with a special "closed" attribute
FossilOrigin-Name: 51d26d1e0912e6da939e9a48f0357847acd1501a
2015-07-23 17:22:30 +00:00
1607710731 Beautified Changes.md
FossilOrigin-Name: c3d03e6a79a457f42479b2338a5a6dd23b5e2a06
2015-07-23 13:59:15 +00:00
da09539cea Updated Changes.md
FossilOrigin-Name: 708acbaf0b2451802830cfeb4f3a127d991c89e7
2015-07-23 13:52:12 +00:00
35dce82ad4 Merged changes from fossil-v1
FossilOrigin-Name: 1e30709d8ed7635a10080655c65e51ac9cde2706
2015-07-23 13:05:55 +00:00
f6a1cdfbdd Added Dutch localisation (Thanks Rick Van Lieshout and Fly Man)
Reformated Docs into Markdown


FossilOrigin-Name: c2bac983bbd7ac0b30a8911f9491af593a8b8989
2015-07-23 12:54:38 +00:00
9146737d59 Fixed Windows build
FossilOrigin-Name: c2a08cf305f24922fac8c70654d652b6477ee7b4
2015-07-11 16:26:48 +00:00
846938e603 Removed revision label from status bar.
Cleaned-up tag label


FossilOrigin-Name: 945c7455e0fa0198d51933965cc4c332d0096a1e
2015-07-11 15:01:06 +00:00
e0c9080555 Fuel no longer relies on fossil for the default remote.
FossilOrigin-Name: 6f96b3f7c927e06ac740b9df993a3f19d57eb74a
2015-07-11 14:44:13 +00:00
c087569d2b Merged Italian translation
FossilOrigin-Name: bf0f238d67482239b37e46f4eab20782d1275207
2015-07-11 13:49:34 +00:00
b4b7b5986c Added missing translation file.
FossilOrigin-Name: badb3aa4b79f08bf8768219099b8e2928072f047
2015-07-11 13:39:24 +00:00
6c03dda2dc Added Italian translation. Thanks maxxlupi and Zangune
Sorted the language selection combo-box


FossilOrigin-Name: ddb8c7f5e5fd5b341b90ddafef349af789147d88
2015-07-11 13:38:13 +00:00
5412e7660e Renamed getInternal*Icon to getCached*Icon
FossilOrigin-Name: 874b5aa93906b7576998002fce08046d12b65d22
2015-07-11 12:17:46 +00:00
c6a0b4e0df Display a system icon for a custom actions
Refactored Icon cache
Refactored custom command spawning


FossilOrigin-Name: 2dabf7d89cfa79d1ce278c7b083761be5f54a691
2015-07-11 12:12:17 +00:00
2ccd05f476 Merged folder-state branch
FossilOrigin-Name: cca2042b901945b1079faedaa7339578f6192a3f
2015-07-11 11:50:18 +00:00
9494d54d7e Folder state now uses icon aliases
FossilOrigin-Name: 0f8cf57fa2a7b38a7d2a8ca22d7e0fa09b1cd24d
2015-07-11 11:43:28 +00:00
022b436197 Added state tooltips for folders
Minor refactoring

FossilOrigin-Name: 802d22504691be0f770b7e6ce3840a4d1a12dea4
2015-07-11 11:38:33 +00:00
51a74879c8 Folder state generation and propagation now happens during workspace scan.
FossilOrigin-Name: 309756d79851ed8606d748fb226b2eab3d2276a8
2015-07-11 11:11:23 +00:00
a953856ea7 Initial work on workspace folder state visualization
FossilOrigin-Name: a2d5250064d0499d5ceea87b5eadaa8b90057ba8
2015-07-10 19:52:55 +00:00
0191b4ba5b Minor refactoring
FossilOrigin-Name: ccbdbff89e2354c6d8f38d994587328a04112eaa
2015-07-10 18:43:33 +00:00
a69645064c Merged custom actions feature
FossilOrigin-Name: 7cd82ba8f02f659d13a370043f256212f55669db
2015-07-10 18:24:26 +00:00
813443e380 Allow triggering custom actions via double-clicking a file in the file view
FossilOrigin-Name: 4270974c3f9f7fb166a74a9d76c9db8a24428fe5
2015-07-10 18:20:19 +00:00
509a5d2fe1 Custom actions now support explicit single selections
FossilOrigin-Name: e422f84c485ada3ae538712434ae64ea80021f8b
2015-07-10 18:06:40 +00:00
30d6cf3da4 Refactored custom action process invocation into a separate function
FossilOrigin-Name: bf36bf1c733da77a39ab9a8b7c63b892c11ef93c
2015-07-10 15:49:00 +00:00
8b2137a58a Added support for $FILE $FOLDER and $WORKSPACE macros in custom action commands
FossilOrigin-Name: 381aa49e9b6b5d5424ee6dbd69d0890634d5bf08
2015-07-10 15:36:47 +00:00
78276623a4 Support for multiple custom actions
FossilOrigin-Name: 172938d454db50a53cd0e0435991a7fccea59ae7
2015-07-09 18:55:33 +00:00
ba34cd69d6 Add support for FreeBSD
FossilOrigin-Name: d869092a71de1d2294d475f394a17f35740420aa
2015-07-08 20:10:53 +00:00
15fd9e403d Add support for FreeBSD
FossilOrigin-Name: 0987410cc3fd8d64510e762b1845880a416c27b3
2015-07-08 20:04:10 +00:00
1372cbb8f6 Updated Arch PKGBUILD
FossilOrigin-Name: e7792ddc67a212767eafe7c59f346d58e71c8d51
2015-07-07 18:30:14 +00:00
1892d51226 Updated Arch PKGBUILD
FossilOrigin-Name: c446f431e8de6fad9d05dbbfe992e58f256560bb
2015-07-07 18:27:35 +00:00
2429d6ca9e Initial work on user defined file actions
FossilOrigin-Name: c1562dbda3ad33559227bb0d2fd177e35cae6681
2015-06-02 17:28:10 +00:00
d285cc76a8 Reorganized branch options in commit dialog.
FossilOrigin-Name: 1954aea71218df9a36b6059fac19ab416ef470dc
2015-06-02 16:19:59 +00:00
2a5611b38a Fixed excessive minimum size for commit message history combo box. Addresses ticket [f48f1b9f7d]
FossilOrigin-Name: 4e621933a5ddf87127fab8cc0aa94a4a6fd8c964
2015-06-02 16:07:01 +00:00
81c68f61c9 - Added View Modified Files only view option
- Reorganized the View menu.

FossilOrigin-Name: 34f0879b8cf7507c43d0d36de0aae704151aa4f4
2015-06-02 15:36:00 +00:00
d87c651d45 - Cloning now uses the keychain to store credentials, and sets the default remote
- Clone target fossil file now adds the fossil extension if missing
- Fixed clipboard URL parsing for clone url
- Added more actions to disable when a valid workspace is not available


FossilOrigin-Name: e7d9b59c32aa0d0ad5c97b24d7562e448dad16f3
2015-06-02 14:48:51 +00:00
e71c3750cb Fixed localization conversion script for environments without Bash. Thanks Russell
FossilOrigin-Name: 9a06e230f398a3c93247382ba8f37d29201268f8
2015-05-29 20:26:33 +00:00
fab3c0bf42 Ported new features to QT4
FossilOrigin-Name: 28087b899e38a3881855cf1e7272da3fb13b3462
2015-05-29 20:23:48 +00:00
440563b1ea Merged remote-management
FossilOrigin-Name: 4c246e5290d2cb3b1a90127fe953bffbbf77ea53
2015-05-29 19:26:31 +00:00
8c020c7502 Fixed incorrect remote search key
FossilOrigin-Name: 1f10dd85e5d7e95539baaea140623673b19f12f2
2015-05-29 19:11:10 +00:00
422bbafdf6 Fixed workspace remote storage
Added Delete Remote action
Double-cliking a remote triggers a remote editing


FossilOrigin-Name: 6c042af93ec30f09386497c176b9c0736ac47690
2015-05-29 18:45:44 +00:00
06695db9fe Initial work on multiple remotes
FossilOrigin-Name: 9674708cb5f2e8543e01d64211b6768d2cb726d6
2015-05-28 19:29:09 +00:00
fd78c68541 Merged new workspace
FossilOrigin-Name: 322729110f48bfb8c007dd611d47e411b389044a
2015-05-26 18:03:05 +00:00
5d4bd6bd62 Create new branch named "fuel-v1"
FossilOrigin-Name: d27709554e4b5e0bdec5d67061b83cda7e45c997
2015-05-26 17:51:42 +00:00
b1276a7cd1 Working Fuel-managed remote-url
No more FOSSIL_SETTING_REMOTE_URL uglyness


FossilOrigin-Name: 54518e5519ad4d5d478c815cc78859c9120997fe
2015-05-25 19:59:37 +00:00
e6fa4062d0 Implemented remote credential storage via qtkeychain
Imported qtkeychain

FossilOrigin-Name: 7c068aa8acdae1b86dee004d2d4cd7fb42904186
2015-05-25 14:03:38 +00:00
86af187c23 Initial work on remote management
FossilOrigin-Name: 13d3a5222dfe24a33a72efffff0ecb0b5bfefe73
2015-05-25 12:27:27 +00:00
eb564e533f Removed Create Stash from toolbar
Changed "Open Containing" icon
Minor cosmetic changes

FossilOrigin-Name: 7ebac37b3255e6721889c070b7327a96c63b6c97
2015-05-24 18:37:29 +00:00
130df73f35 Separated Application and Fossil settings into separate dialogs.
FossilOrigin-Name: 97d0d1e2408832e1fc8b532e059b2141afd9ac55
2015-05-24 18:19:19 +00:00
7ce75bcc63 Fuel now uses the http port that Fossil reports when starting the web ui.
FossilOrigin-Name: 5c9a7defad9f6c77db2447df1d904e24001ec40e
2015-05-24 17:44:01 +00:00
973399d958 Fossil ui port is now optional
FossilOrigin-Name: 7ffecfc49aeb4ccb8b2972ac5c6eddd265d3b578
2015-05-24 17:08:46 +00:00
32dc453346 Moved Settings to separate files
FossilOrigin-Name: 46641dde6cd75e57b23d7df78b9e06e3c1e38993
2015-05-24 15:55:14 +00:00
c4b8c458fe Moved SelectExe to Utils
FossilOrigin-Name: 5dcb374fdf60b627d8f0452cea8add32a2c900e8
2015-05-24 15:45:51 +00:00
1e111052c6 Added Workspace menu
Some action refactoring

FossilOrigin-Name: 343b1675fb8eb023a04a1b84131f132d382f01dc
2015-05-24 15:28:43 +00:00
9c63e0e0dc Fixed missing workspace tree header
FossilOrigin-Name: c50d8ea3ed378c1f9efbb6d83e93ffd1ce050db0
2015-05-24 14:47:14 +00:00
3bf716dff0 Added View All Files option
FossilOrigin-Name: 239c19ebecb1565f0c9e3319b1ea3d18bce59ab9
2015-05-24 09:00:03 +00:00
65e06d023e Added branch on commit
Refactored CommitDialog


FossilOrigin-Name: 34efdbe98a491079ce2a2ae97c701ed5af118a4c
2015-05-24 08:47:24 +00:00
d07ee31362 Initial work on branching during commit
Refactored CommitDialog usage

FossilOrigin-Name: 4b4c7cdb6d717d381081bbfbd7e9657acf32d089
2015-05-23 11:48:50 +00:00
6a67423d31 - App now uses aliases instead of icon filenames
- Removed unused actionViewStash





FossilOrigin-Name: 999f57bd1e05e99c67a921c09f782da170ae827c
2015-05-22 20:18:08 +00:00
73e230db6a Maintain the workspace state even when refreshing
FossilOrigin-Name: 0eef20c8eb990a24b214a25b630b2786fee010d7
2015-05-17 16:43:55 +00:00
d63f963779 Separated View->File List checked menu option into two separate mutually exclusive options
FossilOrigin-Name: 502ce5e1b68dd97da3b9a779e5683d2f1ddcb61c
2015-05-17 11:19:18 +00:00
cee4c93efc Renamed DirModel to TreeModel
FossilOrigin-Name: b49ff56d2af6aca465d8b2ce3b5a9455341e7a1c
2015-05-17 10:13:46 +00:00
9769de2674 Fixed missing workspace view header when repository a is not available
FossilOrigin-Name: 37e191368a0f6880991fd27fb08ebf1144f6626f
2015-05-17 10:08:19 +00:00
11b012404d Display current tags and branches as bold in workspace view
FossilOrigin-Name: 59372f35e863ab4a2363baf3b0df87cbb4a183ce
2015-05-12 20:44:53 +00:00
91163a8502 Added SearchBox
FossilOrigin-Name: 097bed86191fae72e1bf76078b8e9d8da37b2932
2015-05-12 20:38:16 +00:00
adbd44da3a The header of the workspace view is no longer regenerated every refresh
FossilOrigin-Name: d1823f2de2e505cae5c738292e71f54e54908ea0
2015-05-12 19:13:27 +00:00
7873746e15 Set version to 1.0.1
FossilOrigin-Name: e2ba8f0aba679a1c52d36daf3041cd139bd49579
2015-05-12 18:18:33 +00:00
4252085d9d Renamed organisation name back to "fuel-scm.org"
FossilOrigin-Name: 0bdfeb81b088e0b1476518a320428b235967936f
2015-05-12 18:01:31 +00:00
285e6795f2 Fixed windows build
FossilOrigin-Name: 9eea1bac1b8780266c9eac0e31d9d9246854e6ac
2015-05-05 12:41:04 +00:00
009c3d055d Merged with trunk
Ported the icon cache

FossilOrigin-Name: 713625d58306a081885455e8f3869723fe649fdd
2015-05-03 19:27:50 +00:00
44ea8fe52b Implemented an icon cache to speed-up workspace refresh
FossilOrigin-Name: 964b28f34fae482959ce8a96cbc6106f8702fb33
2015-05-03 19:14:10 +00:00
3b15999117 Fixed bug where a fossil output line would not be output under certain conditions
FossilOrigin-Name: 2744a0db324c05cb0f4e2e73128d8c3837d66479
2015-05-03 17:57:33 +00:00
8afb6c280c Merged UpdateRevision into Update
Disabled unimplemented Remotes and Settings workspace items

FossilOrigin-Name: a297f70942e3dccf9904c054fb9e633edb9c59cb
2015-05-03 15:37:36 +00:00
435d1571e7 Renamed REPODIRMODEL_ROLE_PATH to
Renamed TreeViewItem to WorkspaceItem


FossilOrigin-Name: 07f3a4f2e0f2a67172434d4336bec1dea1fc5856
2015-05-03 15:27:24 +00:00
1a395023d4 Cleanups
FossilOrigin-Name: c179d505cea29fe7e6b9c1b149bbd46eb6a0b009
2015-05-03 14:52:59 +00:00
234f27302c Renamed UpdateDialog to RevisionDialog
FossilOrigin-Name: 9704609d5e9ccbadfce2ed96e74893f97e453e0f
2015-05-03 14:47:53 +00:00
9786af1d2b Made tag references explicit with "tag:" to prevent clashes with filesystem objects with the same name
FossilOrigin-Name: 71aeb47a33d20919a71efc23d973c5f6a3c3bb90
2015-05-03 14:41:54 +00:00
90bc163bea Added tag and revision display on status bar
FossilOrigin-Name: b64d5954b1e1baf7e69d56b2ec8c31584141cebb
2015-05-03 12:43:46 +00:00
894f980a7c Commits are now allowed when a merge integration has occured even when no files are modified
Support for forced merges
Context menus are offset by a pixel to prevent unintended clicks


FossilOrigin-Name: b0f81bc9c4d226d8406f65d9ad02099205d7e067
2015-05-03 12:27:11 +00:00
cdba8c05f6 Added merge support
FossilOrigin-Name: ac65fbf8d33a9e64fb97bd8652a43b4d11c8e538
2015-05-03 11:44:04 +00:00
f9eda348be Fixed up-to-date workspace check
Added icons for tag operations

FossilOrigin-Name: 87468705beaaef842877ab8459e287a61edf4bc3
2015-05-03 10:26:29 +00:00
9777e1e2da Support for deleting tags
When parsing tags, also keep track of the checkin uuid
Fixed key-value parsing
Prevent apply stash when nothing is selected

FossilOrigin-Name: 72ac541c4c28f83c94c371f2d81e116b8f3cf261
2015-05-03 10:05:46 +00:00
8e21224a5e Prevent unnecessary dialog when updating with no new changes
FossilOrigin-Name: f718ab845eb859700e230090f2fcbc7ddb068e62
2015-05-03 09:22:18 +00:00
3987a35083 Added support for tagging revisions
FossilOrigin-Name: 579a9a1fb06478cd37359eb106dcabc9c9c5fa5c
2015-05-02 18:23:46 +00:00
3c1ca98ca6 Minor fixes
FossilOrigin-Name: 48ae56dfa79bc35a51145dd9549e4f3760e76b7f
2015-05-02 18:22:55 +00:00
cc029b674f Parse the workspace active tags and revision
FossilOrigin-Name: 2bf8e2a8aba307659c07044e1e6d155d287ac6b6
2015-05-02 18:22:31 +00:00
4cd2e6e5b4 Added "Update to Revision" Action
FossilOrigin-Name: 203cae37a6553c26a3844dcfdd3386eae9be288f
2015-05-02 16:42:06 +00:00
b3ba622ef2 Renamed updateDirView to updateWorkspaceView
Disabled sorting in the workspace view

FossilOrigin-Name: 330e0819bba257c674afdadb1ab097cd7f7ffe61
2015-05-02 14:37:35 +00:00
729caef32e Added tag and branch list to workspace view
FossilOrigin-Name: 304f9bd6c61babe7abc522c1a1301fe3f1887505
2015-05-02 12:44:09 +00:00
a48f81d1f5 Renamed getStashViewSelection to getSelectionStashes
FossilOrigin-Name: 82357d43ca742466e08d3794bdc9a4ab800ad867
2015-05-01 19:31:56 +00:00
d3b8900afd Goodbye Stash View
Working stash management in Workspace view

FossilOrigin-Name: 1b0d47442df7a239ed11a048bde0073603b514f1
2015-05-01 19:29:12 +00:00
e5d5c09f99 Fixed getSelectionPaths
FossilOrigin-Name: 268641002b332e82e893dacce6698a2c298c4f98
2015-05-01 19:13:19 +00:00
709d3b90bb Fixed "open containing"
FossilOrigin-Name: 63b2c047630f4f14ee0c1bd398d5df61433827dc
2015-05-01 19:10:16 +00:00
2bf3de6c2b Initial work on new workspace view
FossilOrigin-Name: 64386745ebf2d2fa921a51383bd76241947b879c
2015-05-01 19:04:02 +00:00
b796cde81b Merged refactor
Added missing status UPDATED_BY_MERGE 
Fixed parsing of file status

FossilOrigin-Name: f82fab41453e6ccf28c60e89ce567635ffab25a3
2015-04-30 17:43:40 +00:00
122e0e3b28 Closed branch
FossilOrigin-Name: e54f1cacad13f429891d89053a208cd340386a3b
2015-04-30 16:51:00 +00:00
b897cf7b1e Renamed tableView to fileTableView
Renamed treeView to workspaceTreeView
Renamed tableViewStash to stashTableView

FossilOrigin-Name: 54059126aee6bb232373c1f134cc07ea0a6f4cca
2015-04-30 16:23:15 +00:00
68d9279e28 Create new branch named "new-workspace"
FossilOrigin-Name: c5152d66a73c5f374ae24fa61c7c9e721e7bb821
2015-04-30 15:26:39 +00:00
33d196fbdb Renamed Bridge to Fossil
FossilOrigin-Name: 77abfe4126ef0adf7d0c70df7a2aac918bcc4128
2015-04-30 12:15:14 +00:00
c59df3c7b0 Cosmetic changes
FossilOrigin-Name: c7b15ab434e33f5b73833256e97e2fabed8f1451
2015-04-30 12:00:47 +00:00
dbfc4f10da Moved UICallback and ScopedCallback to Utils
FossilOrigin-Name: 717fb7a390f81af33ad40913960a30ff9c1c7a75
2015-04-30 11:43:31 +00:00
9dff9370ce Added missing Workspace.cpp/h
FossilOrigin-Name: 2e7553cddf1db1c9e22e883276829a8032dc9afb
2015-04-30 11:37:04 +00:00
e4aebbe52c Renamed RepoFile to WorkspaceFile
FossilOrigin-Name: 8527bab97be43b2370eb03045dee3818fa8a2edf
2015-04-30 11:29:21 +00:00
1b2cebdc7f Moved Workspace to separate file
FossilOrigin-Name: 4c84ad9b0b25c9a77d935f9598b6705498d8e0ae
2015-04-30 11:21:29 +00:00
3ae76ce837 Refactored Workspace cleanup
FossilOrigin-Name: 3a653a3761f6f6e565762b8730bf5f7447ca2d2b
2015-04-30 11:10:27 +00:00
2ad805ecb5 Renamed getRepo to getWorkspace
FossilOrigin-Name: 654259ef3bb3e7e2cc43a85637a8320b501a6593
2015-04-30 11:04:43 +00:00
c67746a84d Renamed Repository to Workspace
Workspace member access is now via methods


FossilOrigin-Name: 2c664b7db2644e1434b5e2d9e83a103ca2093cb3
2015-04-30 10:59:25 +00:00
341e945322 Cleanup
FossilOrigin-Name: 0975c824a72cacd0d2fb5ee5d3c38c145712a0b4
2015-04-30 10:36:18 +00:00
d289dee92b Moved scanWorkspace to Repository
FossilOrigin-Name: 61734cf430e041d514222c71f751feead5b7faf6
2015-04-30 10:30:47 +00:00
97f210fd46 Added UICallback::updateProcess
Moved scanDirectory to Repository

FossilOrigin-Name: 4d94a638499c31672bf47de43407b1fa2531e716
2015-04-30 09:50:22 +00:00
a43eac0ae3 Wrapped bridge member via function
FossilOrigin-Name: c48861c93293aafa6b1193a4b6892342e0ca0159
2015-04-30 08:44:45 +00:00
f882d80dc0 Moved repository model data to Repository class
FossilOrigin-Name: 2e2faf9770ecdb644a4e9aaad0cc7dc050ef5f98
2015-04-30 08:34:11 +00:00
c7cfa079a4 Fixed fossil operation termination flag
FossilOrigin-Name: f791fda7d73f7e7b71b28ecbaefd93330090a834
2015-04-29 12:35:33 +00:00
a661606572 Bridge now longer derives from QObject
FossilOrigin-Name: 0017b55685a3fe99a72757e9f6a8c3c261612413
2015-04-27 19:59:29 +00:00
2eb430b6e1 Bridge no longer needs depends on the UI. All interaction occurs via the callback
More cleanups

FossilOrigin-Name: 3d1e269fee5acd4b65025024f701319c3e993e97
2015-04-27 19:54:01 +00:00
46c5fba9d2 Transitioned to new fossil bridge
FossilOrigin-Name: c5b0a1a62a9607f434ac6426401fd40c94d6a2d8
2015-04-27 19:31:43 +00:00
762172f124 More refactoring
FossilOrigin-Name: e2b03b2a965ec50382ea89d374ece4a6a56b72b5
2015-04-27 19:15:43 +00:00
c039be3c8c Cleanups
FossilOrigin-Name: b6473fc40399a46731c8741ceed7d3432d687c5a
2015-04-27 19:04:11 +00:00
5b3333836d Refactored ui callback mechanism
Bridge now has access to the status bar via the new callbacks

FossilOrigin-Name: ee4cf9240e411dfdfc7a4d2b69456fb38a2300c8
2015-04-27 18:59:09 +00:00
906873e7f9 Completed wrapping
FossilOrigin-Name: de84b1fda5b215f404dfafb1d5b95a694707573b
2015-04-27 18:23:00 +00:00
be2856d060 Wrapped "stash" and all remaining calls
We now use explicit calls for non-setting based fossil options (eg remote-url)

FossilOrigin-Name: bbbd2f42e643fbd912b6b3a5cc0b744f14cd56b0
2015-04-27 18:12:04 +00:00
79c351c043 Wrapped "settings"
FossilOrigin-Name: 2b7585554eaec0b930d78171ee9c6855637cd7ab
2015-04-27 15:53:09 +00:00
efe0b61464 - More fossil "mv" wrapping
- stopUI now navigates to "about:blank"

FossilOrigin-Name: 77ff1ae8697fa89d87f3ca03fc50cf40ce537f24
2015-04-27 15:49:05 +00:00
c4e96bea35 Wrapped fossil "update"
FossilOrigin-Name: 11a0f979fc198ad134c95c139547baae81d90050
2015-04-27 15:32:28 +00:00
fc8941b520 Wrapped fossil version
FossilOrigin-Name: 239b8ac44d4dd954633b52d28b68e4cd9937efbc
2015-04-27 15:25:13 +00:00
1f8a533096 Wrapped "revert" "rename" "undo"
FossilOrigin-Name: 09f2c8f44189b490da880f56011b95fae223a5a9
2015-04-26 19:32:41 +00:00
04a5bb2488 Wrapped "add" and "remove"
FossilOrigin-Name: 1284b0abf55c359e79d4953a00da1de2cebb6a31
2015-04-26 19:14:09 +00:00
a7e759e6a6 Wrapped "clone" "diff" "commit"
FossilOrigin-Name: 1207f87a560c9d077c00367a4270c693f7cceee0
2015-04-26 18:55:05 +00:00
895770b14b Wrapped "push" and "pull"
FossilOrigin-Name: ab5fd401a09cc503168a86761b95c28e52d2875a
2015-04-26 18:26:39 +00:00
496224f030 Wrapped "ls" and "stash ls"
FossilOrigin-Name: 8d81e12735d7c999ea5fca07c4c1683b68a5ad66
2015-04-26 18:16:42 +00:00
1c728c9146 - Wrapped "fossil close"
- Fixed invalid window title when closing a workspace


FossilOrigin-Name: c449a826c83c98c4e33e7adb31841684b4ed6084
2015-04-26 17:40:58 +00:00
c43c638b91 Wrapped "fossil open" and "fossil new"
FossilOrigin-Name: edf97efd4a0adc050076296c896e68371ecb0be0
2015-04-26 17:31:26 +00:00
b97434a2a4 Refactored openRepository
FossilOrigin-Name: 02a7b672e1d634b703ee5f30258153c3f4610886
2015-04-26 17:13:29 +00:00
68575b95cd Fixed status bar updates
FossilOrigin-Name: bf0e885bf80d28fda92ed20315d93445bb2b42a6
2015-04-26 13:57:13 +00:00
a1d60fa19c Refactored repositoryFile
FossilOrigin-Name: 880213d7d5ad220ae0a8c453e367d87f069c67b7
2015-04-26 12:27:51 +00:00
e8c8f9e506 Fixed currentWorkspace
Refactored  projectName

FossilOrigin-Name: 1d1ec089cdb02738d99efcc66c1b32bb0e3208e0
2015-04-26 11:33:07 +00:00
17736bd719 Moved currentWorkspace to Bridge
FossilOrigin-Name: a7ac992f6f87abee63c74696cb372a1564250103
2015-04-26 11:13:24 +00:00
39745ab55a Refactored logging
FossilOrigin-Name: 9c198b023e2df1b5cfe64dd745d070732b5ab843
2015-04-26 10:58:36 +00:00
f261fbaa66 Ported uiRunning
FossilOrigin-Name: bc822e7b940bf0e3eceaf99d6a47921af1a361f0
2015-04-26 10:41:44 +00:00
de78beb1b5 Started moving fossil operations into Bridge
FossilOrigin-Name: 7a4ea959c64034b6e20dec41a4a8c16a09397bfa
2015-04-26 10:36:55 +00:00
d17850e07c Create new branch named "refactor"
FossilOrigin-Name: 7f876a0b9e2c3c802938bd843e3e387e5e7e027a
2015-04-26 09:57:06 +00:00
07ec39ec5f Fixed formatting in building instructions
FossilOrigin-Name: 6b51d631f61ea685cf4ce84b3c34954dcd6338e2
2015-04-25 15:10:40 +00:00
28e4bca233 OSX: Localized menus now integrate better in OSX
FossilOrigin-Name: 9314ddc02dd302435a4aea987f16cb847a1076b6
2015-04-25 14:46:36 +00:00
28f07f22a7 Updated build instructions
FossilOrigin-Name: 3c5332bad121fe233106059c3217d61bf1741a73
2015-04-25 14:45:38 +00:00
156ebb5654 Added Travis-CI build
FossilOrigin-Name: d25b1871b284843ab9abfc0635415ff2373c5ee1
2015-04-24 11:54:07 +00:00
914d238940 Added fossil -> git synchronization script
FossilOrigin-Name: f7f4a3c45d4eefbc9577821d83a47ccdbb495cc0
2015-04-24 10:16:22 +00:00
a221c8f90a Fixed Localization conversion on windows which incorrectly generated Russian for the Portuguese text
FossilOrigin-Name: bde553c9433cc6aebffeb7fb7a06f91e9f13ff61
2015-04-18 19:34:38 +00:00
94f7518b5b Updated localization conversion script
FossilOrigin-Name: ab222d5e30ea8069959b10e083edbad6c5fb8848
2015-04-18 19:08:37 +00:00
db0956be3e Update debian changelog
FossilOrigin-Name: 0305679852d0394c1d40b837fb7449b3f880b418
2015-04-09 11:47:20 +00:00
2fb1d55af4 Covered more cases with an error message when opening a repository failed. Fixes [6e08966e80]
FossilOrigin-Name: d2e3f8c14dafd2ba1b13b6d3b97e12acca400199
2015-04-09 11:46:34 +00:00
c1a3f78fd7 Fossil command-lines are now passed via a temporary file and the new "--args <Filename>" option.
FossilOrigin-Name: 25812c7fba2c2c125e461f59c8c5577252072e99
2015-04-09 11:24:46 +00:00
0dff445061 Allow added files to be reverted. Fixes [72d5fb6fec]
FossilOrigin-Name: d8b0af9783cc0822f0ad32a11446b22b394f0959
2015-04-09 10:49:42 +00:00
342f553703 Localisation conversion script now detects which version lrelease is available in the system
FossilOrigin-Name: 4286422aff75481222f2855360bdc6c18c588e38
2015-03-28 09:56:25 +00:00
a540921de5 Corrected PKGBUILD dependency versions
FossilOrigin-Name: b6bc371c5d50ce549cd2fa53889cc2395b35d810
2015-03-27 18:43:04 +00:00
cee1bf071e Updated PKGBUILD
FossilOrigin-Name: 28586a5428d5c5b0aab1c14a2c858e4eb79e4bed
2015-03-27 18:29:39 +00:00
75 changed files with 13903 additions and 2793 deletions

10
.travis.yml Normal file
View File

@ -0,0 +1,10 @@
before_install:
- sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa
- sudo apt-get update -qq
- sudo apt-get install qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev qt5-default qttools5-dev-tools
script:
- intl/convert.sh
- qmake -project
- qmake fuel.pro
- make

11
debian/changelog vendored
View File

@ -1,5 +1,8 @@
fuel (0.9.6-1) unstable; urgency=low
fuel (1.0.0-1) unstable; urgency=low
* Feature: Long Operations can now be aborted by pressing the Escape key
* Improvement: Better support for commit messages with international characters
* Improvement: Fossil queries about CR/NL inconsistencies are now handled better
* Improvement: Files in Conflicted state are now shown
* Added localisations: Russia, Portuguese
* Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
-- Kostas <kostas@unknown> Wed, 08 Oct 2014 12:10:32 +0300
-- Kostas <karanikolas@gmail.com> Sat, 28 Mar 2015 12:00:00 +0200

10
dist/arch/PKGBUILD vendored
View File

@ -1,15 +1,15 @@
# Maintainer: Kostas Karanikolas <lastname[at]gmail[dot]com>
pkgname=fuel
pkgver=1.0.0
pkgrel=1
pkgrel=3
pkgdesc="A GUI front-end to Fossil SCM"
arch=(i686 x86_64)
url="http://code.google.com/p/fuel-scm/"
url="https://fuel-scm.org/"
license=('GPL2')
depends=('qt5-base>=5.4.0', 'qt5-webkit>=5.2.0' 'fossil')
source=("http://fuelscm.org/files/releases/${pkgname}-${pkgver}.tar.gz")
depends=('qt5-base>=5.4.0', 'qt5-webkit>=5.4.0' 'fossil')
source=("https://fuel-scm.org/files/releases/${pkgname}-${pkgver}.tar.gz")
sha256sums=('905faee12029eafaec9715e6bd5706c57514efa016389e78b78254a7cca0abaa') # Generate with 'makepkg -g'
sha256sums=('034593d16eba9e30a73d1b40bfd4f1a7f9ba438a04dc07cc7bb2cd2202da40fc') # Generate with 'makepkg -g'
build() {
cd "${srcdir}"

View File

@ -3,69 +3,89 @@ Building from Source
Prerequisites
-------------------------------------------------------------------------------
Building Fuel from source requires Qt version 4. Qt is available at:
http://qt.nokia.com/downloads
Building Fuel from source requires Qt version 4 or 5. Qt is available at:
http://www.qt.io/download-open-source/
To run Fuel a compiled binary of Fossil must be available either in the system
path or in the same folder as the Fuel executable. You can find the latest
Fossil binaries from the Fossil homepage at:
http://www.fossil-scm.org/download.html
https://www.fossil-scm.org/download.html
Retrieving the source
-------------------------------------------------------------------------------
The source is available as a tar.gz archive at the following location
https://fuel-scm.googlecode.com/files/fuel-{VERSION}.tar.gz
So for version 0.9.6 the package name would be fuel-0.9.6.tar.gz
Additionally you can clone the source code directly from Chisel using fossil
https://fuel-scm.org/fossil/wiki?name=Downloads
Additionally you can clone the source code directly from our site using fossil
mkdir fuel
cd fuel
fossil clone https://chiselapp.com/user/karanik/repository/fuel fuel.fossil
fossil clone https://fuel-scm.org/fossil fuel.fossil
fossil open fuel.fossil
Windows
Windows (Qt4 / MinGW)
-------------------------------------------------------------------------------
1. Open a Command Prompt and cd into the folder containing the Fuel source code
cd fuel
2. Make a build folder and cd into it
md build
cd build
3.1 Build with MinGW
3.1.1 Generate the makefile with qmake
3. Generate the makefile with qmake
C:\QtSDK\Desktop\Qt\4.8.1\mingw\bin\qmake ..\fuel.pro -r -spec win32-g++ CONFIG+=release
3.1.2 Build the project
4. Build the project
c:\QtSDK\mingw\bin\mingw32-make
3.1.3 Copy the Qt DLLs
5. Copy the Qt DLLs
copy C:\QtSDK\Desktop\Qt\4.8.1\mingw\bin\QtCore4.dll release
copy C:\QtSDK\Desktop\Qt\4.8.1\mingw\bin\QtGui4.dll release
3.1.4 Copy the MinGW DLLs
6. Copy the MinGW DLLs
copy C:\QtSDK\mingw\bin\libgcc_s_dw2-1.dll release
copy C:\QtSDK\mingw\bin\mingwm10.dll release
3.2 Build with Visual Studio 2010
3.2.1 Generate the Visual Studio project makefile with qmake
Windows (Qt4 / MSVC)
-------------------------------------------------------------------------------
1. Open a Command Prompt and cd into the folder containing the Fuel source code
cd fuel
2. Make a build folder and cd into it
md build
cd build
3. Generate the Visual Studio project makefile with qmake
C:\QtSDK\Desktop\Qt\4.8.1\msvc2010\bin\qmake ..\fuel.pro -tp vc -spec win32-msvc2010
3.2.2 Open the generated project
4. Open the generated project
start fuel.vcxproj
3.2.3 Build the project
5. Build the project
Use the IDE to build the project or alternatively you can use via MSBuild
Alternatively you can build via MSBuild
c:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild Fuel.vcxproj /t:build /p:Configuration=Release
3.2.4 Copy the Qt DLLs
6. Copy the Qt DLLs
copy C:\QtSDK\Desktop\Qt\4.8.1\msvc2010\bin\QtCore4.dll release
copy C:\QtSDK\Desktop\Qt\4.8.1\msvc2010\bin\QtGui4.dll release
4. Enjoy
release\Fuel.exe
@ -73,38 +93,60 @@ Mac OS X
-------------------------------------------------------------------------------
Build Steps:
1. Open a Terminal and cd into the folder containing the Fuel source code
1. Open a Terminal and add your Qt bin folder to the path
export PATH=$PATH:/path/to/qt/version/clang_64/bin
2. Go into the folder containing the Fuel source code
cd fuel
2. Generate the makefile with qmake
/path/to/qt/bin/qmake fuel.pro -spec macx-g++ CONFIG+=release
3. Generate localization files
intl/convert.sh
4. Generate the makefile with qmake
qmake fuel.pro -spec macx-clang CONFIG+=release
5. Build the project
3. Build the project
make
4. (Optional) Include the Fossil executable within the Fuel application bundle
6. (Optional) Include the Fossil executable within the Fuel application bundle
cp /location/to/fossil Fuel.app/Contents/MacOS
5. Package Qt dependencies into Fuel to make a standalone application bundle
/path/to/qt/bin/macdeployqt Fuel.app
7. Package Qt dependencies into Fuel to make a standalone application bundle
macdeployqt Fuel.app
8. Enjoy
6. Enjoy
open Fuel.app
Unix-based OSs
Unix-based OS
-------------------------------------------------------------------------------
Build Steps:
1. cd into the folder containing the Fuel source code
cd fuel
2. Generate the makefile with qmake
2. Generate localization files
intl/convert.sh
3. Generate the makefile with qmake
qmake fuel.pro
3. Build the project
4. Build the project
make
4. Enjoy
5. Enjoy
./Fuel

82
doc/Changes.md Normal file
View File

@ -0,0 +1,82 @@
Changes
================================================================================
Fuel V2.0.0 (2015-XX-XX)
--------------------------------------------------------------------------------
- Feature: New workspace view to visualise branches, tags, stashes and remotes
- Feature: Support for creating, merging and integrating branches
- Feature: Support for creating and deleting tags
- Feature: Support for updating the workspace to a particular revisions including
specific branches or tags
- Feature: Support for creating a branch during commit, including private branches
- Feature: Added Search Box for filtering and searching within the list of files
- Feature: Display the active tags and branches in the status bar
- Feature: Fuel now automatically detects the http port of "fossil ui"
- Feature: Added menu shortcut to display only the modified files of the workspace
- Feature: Support for arbitrary number of remote urls, including pushing and
pulling to specific remotes.
- Feature: Remote URL credentials are stored in the user keychain for systems that
support one. Thanks to the QtKeychain project (https://github.com/frankosterfeld/qtkeychain)
- Feature: Folders now have different appereance depending on the modification
status of their contents, including missing folders
- Feature: Support for multiple file user-actions
- Feature: Double-Click action also allows user actions
- Misc: Reorganised menu structure.
- Misc: Separated Fuel and Fossil settings
- Bug Fix: Retain the folder tree state when refreshing the workspace
- Bug Fix: Fixed issue with the expanding width of the commit dialog
- Major internal refactoring
Fuel V1.0.1 (2015-08-XX)
--------------------------------------------------------------------------------
- Reformated Docs into Markdown
- Added Localisations:
- Italian (Thanks maxxlupi and Zangune)
- Dutch (Thanks Rick Van Lieshout and Fly Man)
- Korean (Thanks ardiefox)
Fuel V1.0.0 (2015-03-28)
--------------------------------------------------------------------------------
- Feature: Long Operations can now be aborted by pressing the Escape key
- Improvement: Better support for commit messages with international characters
- Improvement: Fossil queries about CR/NL inconsistencies are now handled better
- Improvement: Files in Conflicted state are now shown
- Localisations:
- Russian (Thanks Mouse166)
- Portuguese (Thanks emansije)
Fuel V0.9.7 (Unreleased)
--------------------------------------------------------------------------------
- Feature: Optionally use the internal browser for the Fossil UI
- Feature: Support for persisting the state (Column order and sizes) of the File View
- Feature: Dropping a Fossil checkout file or workspace folder on Fuel now opens that workspace
- Feature: Dropping a file on Fuel now adds that file to Fossil
- Feature: Commit Dialog: Pressing Ctrl-Enter within the comment box commences the commit,
and Escape aborts it
- Feature: Support for localization
- Localisations:
- French (Thanks Fringale)
- German (Thanks stayawake)
- Greek
- Spanish (Thanks djnavas)
- Feature: Support for QT5
- Distribution: Fuel is now available in the Arch User Repository
Fuel V0.9.6 (2012-05-13)
--------------------------------------------------------------------------------
- Feature: Support for fossil stashes
- Feature: Support for dragging and dropping files out of Fuel
- Feature: Allow for opening workspaces via the checkout file or a workspace folder
- Feature: Display the actual file icons
- Feature: Windows: Shift-Right-Click invokes the Explorer file context menu
- Feature: Allow starting Fuel into an existing fossil workspace via the command line (Thanks Chris)
- Feature: Portable mode. When starting Fuel with the "--portable" option all settings
will be stored in a Fuel.ini file. If a settings file already exists, Fuel will start
into portable mode automatically. (Thanks Chris)
- Improvement: Always show unknown files when starting a new repository
- Bug Fix: Avoid specifying filenames explicitly when all modified files are selected.
This addresses an issue preventing commits after a merge
- Bug Fix: Fixed issue where a complete repository would be committed even when
the user has a specific set of files marked for commit
- Misc: Minor GUI bug fixes and usability enhancements

View File

@ -1,44 +0,0 @@
Fuel V1.0.0 (2015-03-28)
============
- Feature: Long Operations can now be aborted by pressing the Escape key
- Improvement: Better support for commit messages with international characters
- Improvement: Fossil queries about CR/NL inconsistencies are now handled better
- Improvement: Files in Conflicted state are now shown
- Localisations:
Russian (Thanks Mouse166)
Portuguese (Thanks emansije)
Fuel V0.9.7 (Unreleased)
============
- Feature: Optionally use the internal browser for the Fossil UI
- Feature: Support for persisting the state (Column order and sizes) of the File View
- Feature: Dropping a Fossil checkout file or workspace folder on Fuel now opens that workspace
- Feature: Dropping a file on Fuel now adds that file to Fossil
- Feature: Commit Dialog: Pressing Ctrl-Enter within the comment box commences the commit,
and Escape aborts it
- Feature: Support for localization
- Localisations:
French (Thanks Fringale)
German (Thanks stayawake)
Greek
Spanish (Thanks djnavas)
- Feature: Support for QT5
- Distribution: Fuel is now available in the Arch User Repository
Fuel V0.9.6 (2012-05-13)
============
- Feature: Support for fossil stashes
- Feature: Support for dragging and dropping files out of Fuel
- Feature: Allow for opening workspaces via the checkout file or a workspace folder
- Feature: Display the actual file icons
- Feature: Windows: Shift-Right-Click invokes the Explorer file context menu
- Feature: Allow starting Fuel into an existing fossil workspace via the command line (Thanks Chris)
- Feature: Portable mode. When starting Fuel with the "--portable" option all settings
will be stored in a Fuel.ini file. If a settings file already exists, Fuel will start
into portable mode automatically. (Thanks Chris)
- Improvement: Always show unknown files when starting a new repository
- Bug Fix: Avoid specifying filenames explicitly when all modified files are selected.
This addresses an issue preventing commits after a merge
- Bug Fix: Fixed issue where a complete repository would be committed even when
the user has a specific set of files marked for commit
- Misc: Minor GUI bug fixes and usability enhancements

View File

@ -0,0 +1,195 @@
cmake_minimum_required(VERSION 2.8)
project(qtkeychain)
###
set(QTKEYCHAIN_VERSION 0.5.90)
set(QTKEYCHAIN_SOVERSION 0)
###
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
include(GNUInstallDirs)
option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF)
if( NOT BUILD_WITH_QT4 )
# try Qt5 first, and prefer that if found
find_package(Qt5Core QUIET)
endif()
if (Qt5Core_FOUND)
set(QTKEYCHAIN_VERSION_INFIX 5)
if(UNIX AND NOT APPLE)
find_package(Qt5DBus REQUIRED)
include_directories(${Qt5DBus_INCLUDE_DIRS})
set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES})
macro(qt_add_dbus_interface)
qt5_add_dbus_interface(${ARGN})
endmacro()
endif()
find_package(Qt5LinguistTools REQUIRED)
macro(qt_add_translation)
qt5_add_translation(${ARGN})
endmacro(qt_add_translation)
macro(qt_create_translation)
qt5_create_translation(${ARGN})
endmacro(qt_create_translation)
macro(qt_wrap_cpp)
qt5_wrap_cpp(${ARGN})
endmacro()
set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES})
include_directories(${Qt5Core_INCLUDE_DIRS})
if (Qt5_POSITION_INDEPENDENT_CODE)
if (CMAKE_VERSION VERSION_LESS 2.8.9) # TODO remove once we increase the cmake requirement
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
else()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
endif()
else()
set(QTKEYCHAIN_VERSION_INFIX "")
if(UNIX AND NOT APPLE)
find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED)
set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY})
macro(qt_add_dbus_interface)
qt4_add_dbus_interface(${ARGN})
endmacro()
else()
find_package(Qt4 COMPONENTS QtCore REQUIRED)
endif()
include_directories(${QT_INCLUDES})
set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY})
macro(qt_add_translation)
qt4_add_translation(${ARGN})
endmacro(qt_add_translation)
macro(qt_create_translation)
qt4_create_translation(${ARGN})
endmacro(qt_create_translation)
macro(qt_wrap_cpp)
qt4_wrap_cpp(${ARGN})
endmacro()
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES})
set(qtkeychain_SOURCES
keychain.cpp
)
ADD_DEFINITIONS( -Wall )
if(WIN32)
list(APPEND qtkeychain_SOURCES keychain_win.cpp)
list(APPEND qtkeychain_LIBRARIES crypt32)
#FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there
if(MINGW)
add_definitions( -O2 )
endif()
endif()
if(APPLE)
list(APPEND qtkeychain_SOURCES keychain_mac.cpp)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY})
find_library(SECURITY_LIBRARY Security)
list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY})
endif()
if(UNIX AND NOT APPLE)
list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp)
qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES})
endif()
QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h)
set(qtkeychain_TR_FILES
translations/qtkeychain_de.ts
translations/qtkeychain_ro.ts
)
file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui)
qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES})
qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES})
add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES})
add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES})
if(NOT QT_TRANSLATIONS_DIR)
# If this directory is missing, we are in a Qt5 environment.
# Extract the qmake executable location
get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
# Ask Qt5 where to put the translations
execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS
OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE )
# make sure we have / and not \ as qmake gives on windows
FILE(TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir)
SET(QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH "The
location of the Qt translations" FORCE)
endif()
install(FILES ${qtkeychain_QM_FILES}
DESTINATION ${QT_TRANSLATIONS_DIR})
set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain)
if(NOT QTKEYCHAIN_STATIC)
add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_BUILD_QKEYCHAIN_LIB)
target_link_libraries(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_LIBRARIES})
else()
add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_STATICLIB)
endif()
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES
VERSION ${QTKEYCHAIN_VERSION}
SOVERSION ${QTKEYCHAIN_SOVERSION}
MACOSX_RPATH 1
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
)
install(FILES keychain.h qkeychain_export.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/
)
install(TARGETS ${QTKEYCHAIN_TARGET_NAME}
EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
add_executable( testclient testclient.cpp )
target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME})
###
### CMake config file
###
export(TARGETS ${QTKEYCHAIN_TARGET_NAME} FILE "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends.cmake")
export(PACKAGE Qt${QTKEYCHAIN_VERSION_INFIX}Keychain)
configure_file(QtKeychainBuildTreeSettings.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainBuildTreeSettings.cmake" @ONLY)
configure_file(QtKeychainConfig.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" @ONLY)
configure_file(QtKeychainConfigVersion.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" @ONLY)
install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain
)

20
ext/qtkeychain/COPYING Normal file
View File

@ -0,0 +1,20 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

23
ext/qtkeychain/ChangeLog Normal file
View File

@ -0,0 +1,23 @@
ChangeLog
=========
version 0.5.0 (release 2015-05-04)
* Added support for KWallet5 (KDE5/KF)
version 0.4.0 (release 2014-09-01)
* KWallet: Handle case where no wallet exists yet (Liviu Cristian Mirea Ghiban <contact@liviucmg.com>)
* Improved desktop environment detection at runtime (Daniel Molkentin <daniel@molkentin.de>)
version 0.3.0 (release 2014-03-13)
* Gnome Keyring supported added (Francois Ferrand <thetypz@gmail.com>)
* Improved Qt 5 support
* KWallet: Distinguish empty passwords from non-existing entries
* KWallet: Do not use hardcoded wallet name
* German translation (Daniel Molkentin <daniel@molkentin.de>)
* Romanian translation (Arthur Țițeică <arthur@psw.ro>)
version 0.2.0: no official release
version 0.1.0 (release 2013-01-16)
* Initial release

View File

@ -0,0 +1,4 @@
set(QTKEYCHAIN_INCLUDE_DIRS
"@PROJECT_SOURCE_DIR@"
"@PROJECT_BINARY_DIR@"
)

View File

@ -0,0 +1,21 @@
# - Config file for the QtKeychain package
# It defines the following variables
# QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain
# QTKEYCHAIN_LIBRARIES - libraries to link against
# Compute paths
get_filename_component(QTKEYCHAIN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
if(EXISTS "${QTKEYCHAIN_CMAKE_DIR}/CMakeCache.txt")
# In build tree
include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainBuildTreeSettings.cmake")
else()
set(QTKEYCHAIN_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
endif()
# Our library dependencies (contains definitions for IMPORTED targets)
include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake")
# These are IMPORTED targets created by FooBarLibraryDepends.cmake
set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@")
set(QTKEYCHAIN_FOUND TRUE)

View File

@ -0,0 +1,11 @@
set(PACKAGE_VERSION "@QTKEYCHAIN_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -0,0 +1,15 @@
QtKeychain
==========
QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
* **Mac OS X:** Passwords are stored in the OS X Keychain.
* **Linux/Unix:** If running, GNOME Keyring is used, otherwise
qtkeychain tries to use KWallet (via D-Bus), if available.
* **Windows:** Windows does not provide a service for secure storage. QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings.
In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )).
**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details.

15
ext/qtkeychain/ReadMe.txt Normal file
View File

@ -0,0 +1,15 @@
QtKeychain
==========
QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
* **Mac OS X:** Passwords are stored in the OS X Keychain.
* **Linux/Unix:** If running, GNOME Keyring is used, otherwise
qtkeychain tries to use KWallet (via D-Bus), if available.
* **Windows:** Windows does not provide a service for secure storage. QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings.
In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )).
**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details.

View File

@ -0,0 +1,188 @@
# - Define GNU standard installation directories
# Provides install directory variables as defined for GNU software:
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
# Inclusion of this module defines the following variables:
# CMAKE_INSTALL_<dir> - destination for files of a given type
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
# where <dir> is one of:
# BINDIR - user executables (bin)
# SBINDIR - system admin executables (sbin)
# LIBEXECDIR - program executables (libexec)
# SYSCONFDIR - read-only single-machine data (etc)
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
# LOCALSTATEDIR - modifiable single-machine data (var)
# LIBDIR - object code libraries (lib or lib64 or lib/<multiarch-tuple> on Debian)
# INCLUDEDIR - C header files (include)
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
# DATAROOTDIR - read-only architecture-independent data root (share)
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
# INFODIR - info documentation (DATAROOTDIR/info)
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
# MANDIR - man documentation (DATAROOTDIR/man)
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
# install() commands for the corresponding file type. If the includer does
# not define a value the above-shown default will be used and the value will
# appear in the cache for editing by the user.
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
# from the corresponding destination by prepending (if necessary) the value
# of CMAKE_INSTALL_PREFIX.
#=============================================================================
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
# Copyright 2011 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
# Installation directories
#
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(_LIBDIR_DEFAULT "lib")
# Override this default 'lib' with 'lib64' iff:
# - we are on Linux system but NOT cross-compiling
# - we are NOT on debian
# - we are on a 64 bits system
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
# For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
# CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu"
# See http://wiki.debian.org/Multiarch
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT CMAKE_CROSSCOMPILING)
if (EXISTS "/etc/debian_version") # is this a debian system ?
if(CMAKE_LIBRARY_ARCHITECTURE)
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
endif()
else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
message(AUTHOR_WARNING
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
"Please enable at least one language before including GNUInstallDirs.")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIBDIR_DEFAULT "lib64")
endif()
endif()
endif()
endif()
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
endif()
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
endif()
#-----------------------------------------------------------------------------
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
# the cache and store the defaults in local variables if the cache values are
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
if(NOT CMAKE_INSTALL_DATADIR)
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
endif()
if(NOT CMAKE_INSTALL_INFODIR)
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
endif()
if(NOT CMAKE_INSTALL_LOCALEDIR)
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
endif()
if(NOT CMAKE_INSTALL_MANDIR)
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
endif()
if(NOT CMAKE_INSTALL_DOCDIR)
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
endif()
#-----------------------------------------------------------------------------
mark_as_advanced(
CMAKE_INSTALL_BINDIR
CMAKE_INSTALL_SBINDIR
CMAKE_INSTALL_LIBEXECDIR
CMAKE_INSTALL_SYSCONFDIR
CMAKE_INSTALL_SHAREDSTATEDIR
CMAKE_INSTALL_LOCALSTATEDIR
CMAKE_INSTALL_LIBDIR
CMAKE_INSTALL_INCLUDEDIR
CMAKE_INSTALL_OLDINCLUDEDIR
CMAKE_INSTALL_DATAROOTDIR
CMAKE_INSTALL_DATADIR
CMAKE_INSTALL_INFODIR
CMAKE_INSTALL_LOCALEDIR
CMAKE_INSTALL_MANDIR
CMAKE_INSTALL_DOCDIR
)
# Result directories
#
foreach(dir
BINDIR
SBINDIR
LIBEXECDIR
SYSCONFDIR
SHAREDSTATEDIR
LOCALSTATEDIR
LIBDIR
INCLUDEDIR
OLDINCLUDEDIR
DATAROOTDIR
DATADIR
INFODIR
LOCALEDIR
MANDIR
DOCDIR
)
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
else()
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
endif()
endforeach()

View File

@ -0,0 +1,71 @@
#include "gnomekeyring_p.h"
const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = NULL;
bool GnomeKeyring::isAvailable()
{
const GnomeKeyring& keyring = instance();
return keyring.isLoaded() &&
keyring.NETWORK_PASSWORD &&
keyring.is_available &&
keyring.find_password &&
keyring.store_password &&
keyring.delete_password &&
keyring.is_available();
}
GnomeKeyring::gpointer GnomeKeyring::store_network_password( const gchar* keyring, const gchar* display_name,
const gchar* user, const gchar* server, const gchar* password,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data )
{
if ( !isAvailable() )
return 0;
return instance().store_password( instance().NETWORK_PASSWORD,
keyring, display_name, password, callback, data, destroy_data,
"user", user, "server", server, static_cast<char*>(0) );
}
GnomeKeyring::gpointer GnomeKeyring::find_network_password( const gchar* user, const gchar* server,
OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data )
{
if ( !isAvailable() )
return 0;
return instance().find_password( instance().NETWORK_PASSWORD,
callback, data, destroy_data,
"user", user, "server", server, static_cast<char*>(0) );
}
GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user,
const gchar* server,
OperationDoneCallback callback,
gpointer data,
GDestroyNotify destroy_data )
{
if ( !isAvailable() )
return 0;
return instance().delete_password( instance().NETWORK_PASSWORD,
callback, data, destroy_data,
"user", user, "server", server, static_cast<char*>(0) );
}
GnomeKeyring::GnomeKeyring()
: QLibrary("gnome-keyring", 0)
{
static const PasswordSchema schema = {
ITEM_NETWORK_PASSWORD,
{{ "user", ATTRIBUTE_TYPE_STRING },
{ "server", ATTRIBUTE_TYPE_STRING },
{ 0, static_cast<AttributeType>( 0 ) }}
};
NETWORK_PASSWORD = &schema;
is_available = reinterpret_cast<is_available_fn*>( resolve( "gnome_keyring_is_available" ) );
find_password = reinterpret_cast<find_password_fn*>( resolve( "gnome_keyring_find_password" ) );
store_password = reinterpret_cast<store_password_fn*>( resolve( "gnome_keyring_store_password" ) );
delete_password = reinterpret_cast<delete_password_fn*>( resolve( "gnome_keyring_delete_password" ) );
}
GnomeKeyring& GnomeKeyring::instance() {
static GnomeKeyring keyring;
return keyring;
}

View File

@ -0,0 +1,88 @@
#ifndef QTKEYCHAIN_GNOME_P_H
#define QTKEYCHAIN_GNOME_P_H
#include <QLibrary>
class GnomeKeyring : private QLibrary {
public:
enum Result {
RESULT_OK,
RESULT_DENIED,
RESULT_NO_KEYRING_DAEMON,
RESULT_ALREADY_UNLOCKED,
RESULT_NO_SUCH_KEYRING,
RESULT_BAD_ARGUMENTS,
RESULT_IO_ERROR,
RESULT_CANCELLED,
RESULT_KEYRING_ALREADY_EXISTS,
RESULT_NO_MATCH
};
enum ItemType {
ITEM_GENERIC_SECRET = 0,
ITEM_NETWORK_PASSWORD,
ITEM_NOTE,
ITEM_CHAINED_KEYRING_PASSWORD,
ITEM_ENCRYPTION_KEY_PASSWORD,
ITEM_PK_STORAGE = 0x100
};
enum AttributeType {
ATTRIBUTE_TYPE_STRING,
ATTRIBUTE_TYPE_UINT32
};
typedef char gchar;
typedef void* gpointer;
typedef bool gboolean;
typedef struct {
ItemType item_type;
struct {
const gchar* name;
AttributeType type;
} attributes[32];
} PasswordSchema;
typedef void ( *OperationGetStringCallback )( Result result, const char* string, gpointer data );
typedef void ( *OperationDoneCallback )( Result result, gpointer data );
typedef void ( *GDestroyNotify )( gpointer data );
static const char* GNOME_KEYRING_DEFAULT;
static bool isAvailable();
static gpointer store_network_password( const gchar* keyring, const gchar* display_name,
const gchar* user, const gchar* server, const gchar* password,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
static gpointer find_network_password( const gchar* user, const gchar* server,
OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data );
static gpointer delete_network_password( const gchar* user, const gchar* server,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
private:
GnomeKeyring();
static GnomeKeyring& instance();
const PasswordSchema* NETWORK_PASSWORD;
typedef gboolean ( is_available_fn )( void );
typedef gpointer ( store_password_fn )( const PasswordSchema* schema, const gchar* keyring,
const gchar* display_name, const gchar* password,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
... );
typedef gpointer ( find_password_fn )( const PasswordSchema* schema,
OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data,
... );
typedef gpointer ( delete_password_fn )( const PasswordSchema* schema,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
... );
is_available_fn* is_available;
find_password_fn* find_password;
store_password_fn* store_password;
delete_password_fn* delete_password;
};
#endif

229
ext/qtkeychain/keychain.cpp Normal file
View File

@ -0,0 +1,229 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain.h"
#include "keychain_p.h"
using namespace QKeychain;
Job::Job( const QString& service, QObject *parent )
: QObject( parent )
, d ( new JobPrivate( service ) ) {
}
Job::~Job() {
delete d;
}
QString Job::service() const {
return d->service;
}
QSettings* Job::settings() const {
return d->settings;
}
void Job::setSettings( QSettings* settings ) {
d->settings = settings;
}
void Job::start() {
QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
}
bool Job::autoDelete() const {
return d->autoDelete;
}
void Job::setAutoDelete( bool autoDelete ) {
d->autoDelete = autoDelete;
}
bool Job::insecureFallback() const {
return d->insecureFallback;
}
void Job::setInsecureFallback( bool insecureFallback ) {
d->insecureFallback = insecureFallback;
}
void Job::emitFinished() {
emit finished( this );
if ( d->autoDelete )
deleteLater();
}
void Job::emitFinishedWithError( Error error, const QString& errorString ) {
d->error = error;
d->errorString = errorString;
emitFinished();
}
Error Job::error() const {
return d->error;
}
QString Job::errorString() const {
return d->errorString;
}
void Job::setError( Error error ) {
d->error = error;
}
void Job::setErrorString( const QString& errorString ) {
d->errorString = errorString;
}
ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new ReadPasswordJobPrivate( this ) )
{}
ReadPasswordJob::~ReadPasswordJob() {
delete d;
}
QString ReadPasswordJob::textData() const {
return QString::fromUtf8( d->data );
}
QByteArray ReadPasswordJob::binaryData() const {
return d->data;
}
QString ReadPasswordJob::key() const {
return d->key;
}
void ReadPasswordJob::setKey( const QString& key ) {
d->key = key;
}
void ReadPasswordJob::doStart() {
JobExecutor::instance()->enqueue( this );
}
WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new WritePasswordJobPrivate( this ) ) {
}
WritePasswordJob::~WritePasswordJob() {
delete d;
}
QString WritePasswordJob::key() const {
return d->key;
}
void WritePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void WritePasswordJob::setBinaryData( const QByteArray& data ) {
d->binaryData = data;
d->mode = WritePasswordJobPrivate::Binary;
}
void WritePasswordJob::setTextData( const QString& data ) {
d->textData = data;
d->mode = WritePasswordJobPrivate::Text;
}
void WritePasswordJob::doStart() {
JobExecutor::instance()->enqueue( this );
}
DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new DeletePasswordJobPrivate( this ) ) {
}
DeletePasswordJob::~DeletePasswordJob() {
delete d;
}
void DeletePasswordJob::doStart() {
//Internally, to delete a password we just execute a write job with no data set (null byte array).
//In all current implementations, this deletes the entry so this is sufficient
WritePasswordJob* job = new WritePasswordJob( service(), this );
connect( job, SIGNAL(finished(QKeychain::Job*)), d, SLOT(jobFinished(QKeychain::Job*)) );
job->setInsecureFallback(true);
job->setSettings(settings());
job->setKey( d->key );
job->doStart();
}
QString DeletePasswordJob::key() const {
return d->key;
}
void DeletePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void DeletePasswordJobPrivate::jobFinished( Job* job ) {
q->setError( job->error() );
q->setErrorString( job->errorString() );
q->emitFinished();
}
JobExecutor::JobExecutor()
: QObject( 0 )
, m_runningJob( 0 )
{
}
void JobExecutor::enqueue( Job* job ) {
m_queue.append( job );
startNextIfNoneRunning();
}
void JobExecutor::startNextIfNoneRunning() {
if ( m_queue.isEmpty() || m_runningJob )
return;
QPointer<Job> next;
while ( !next && !m_queue.isEmpty() ) {
next = m_queue.first();
m_queue.pop_front();
}
if ( next ) {
connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) );
connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) );
m_runningJob = next;
if ( ReadPasswordJob* rpj = qobject_cast<ReadPasswordJob*>( m_runningJob ) )
rpj->d->scheduledStart();
else if ( WritePasswordJob* wpj = qobject_cast<WritePasswordJob*>( m_runningJob) )
wpj->d->scheduledStart();
}
}
void JobExecutor::jobDestroyed( QObject* object ) {
Q_UNUSED( object ) // for release mode
Q_ASSERT( object == m_runningJob );
m_runningJob->disconnect( this );
m_runningJob = 0;
startNextIfNoneRunning();
}
void JobExecutor::jobFinished( Job* job ) {
Q_UNUSED( job ) // for release mode
Q_ASSERT( job == m_runningJob );
m_runningJob->disconnect( this );
m_runningJob = 0;
startNextIfNoneRunning();
}
JobExecutor* JobExecutor::s_instance = 0;
JobExecutor* JobExecutor::instance() {
if ( !s_instance )
s_instance = new JobExecutor;
return s_instance;
}

145
ext/qtkeychain/keychain.h Normal file
View File

@ -0,0 +1,145 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#ifndef KEYCHAIN_H
#define KEYCHAIN_H
#include "qkeychain_export.h"
#include <QtCore/QObject>
#include <QtCore/QString>
class QSettings;
#define QTKEYCHAIN_VERSION 0x000100
namespace QKeychain {
/**
* Error codes
*/
enum Error {
NoError=0, /**< No error occurred, operation was successful */
EntryNotFound, /**< For the given key no data was found */
CouldNotDeleteEntry, /**< Could not delete existing secret data */
AccessDeniedByUser, /**< User denied access to keychain */
AccessDenied, /**< Access denied for other reasons */
NoBackendAvailable, /**< No platform-specific keychain service available */
NotImplemented, /**< Not implemented on platform */
OtherError /**< Something else went wrong (errorString() might provide details) */
};
class JobExecutor;
class JobPrivate;
class QKEYCHAIN_EXPORT Job : public QObject {
Q_OBJECT
public:
explicit Job( const QString& service, QObject* parent=0 );
~Job();
QSettings* settings() const;
void setSettings( QSettings* settings );
void start();
QString service() const;
Error error() const;
QString errorString() const;
bool autoDelete() const;
void setAutoDelete( bool autoDelete );
bool insecureFallback() const;
void setInsecureFallback( bool insecureFallback );
Q_SIGNALS:
void finished( QKeychain::Job* );
protected:
Q_INVOKABLE virtual void doStart() = 0;
void setError( Error error );
void setErrorString( const QString& errorString );
void emitFinished();
void emitFinishedWithError(Error, const QString& errorString);
private:
JobPrivate* const d;
};
class ReadPasswordJobPrivate;
class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
Q_OBJECT
public:
explicit ReadPasswordJob( const QString& service, QObject* parent=0 );
~ReadPasswordJob();
QString key() const;
void setKey( const QString& key );
QByteArray binaryData() const;
QString textData() const;
protected:
void doStart();
private:
friend class QKeychain::ReadPasswordJobPrivate;
friend class QKeychain::JobExecutor;
ReadPasswordJobPrivate* const d;
};
class WritePasswordJobPrivate;
class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
Q_OBJECT
public:
explicit WritePasswordJob( const QString& service, QObject* parent=0 );
~WritePasswordJob();
QString key() const;
void setKey( const QString& key );
void setBinaryData( const QByteArray& data );
void setTextData( const QString& data );
protected:
void doStart();
private:
friend class QKeychain::JobExecutor;
friend class QKeychain::WritePasswordJobPrivate;
friend class DeletePasswordJob;
WritePasswordJobPrivate* const d;
};
class DeletePasswordJobPrivate;
class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
Q_OBJECT
public:
explicit DeletePasswordJob( const QString& service, QObject* parent=0 );
~DeletePasswordJob();
QString key() const;
void setKey( const QString& key );
protected:
void doStart();
private:
friend class QKeychain::DeletePasswordJobPrivate;
DeletePasswordJobPrivate* const d;
};
} // namespace QtKeychain
#endif

View File

@ -0,0 +1,161 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <QDebug>
using namespace QKeychain;
template <typename T>
struct Releaser {
explicit Releaser( const T& v ) : value( v ) {}
~Releaser() {
CFRelease( value );
}
const T value;
};
static QString strForStatus( OSStatus os ) {
const Releaser<CFStringRef> str( SecCopyErrorMessageString( os, 0 ) );
const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 );
if ( !buf )
return QObject::tr( "%1 (OSStatus %2)" )
.arg( "OSX Keychain Error" ).arg( os );
return QObject::tr( "%1 (OSStatus %2)" )
.arg( QString::fromUtf8( buf, strlen( buf ) ) ).arg( os );
}
static OSStatus readPw( QByteArray* pw,
const QString& service,
const QString& account,
SecKeychainItemRef* ref ) {
Q_ASSERT( pw );
pw->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
void* data = 0;
UInt32 len = 0;
const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
&len,
&data,
ref );
if ( ret == noErr ) {
*pw = QByteArray( reinterpret_cast<const char*>( data ), len );
const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data );
if ( ret2 != noErr )
qWarning() << "Could not free item content: " << strForStatus( ret2 );
}
return ret;
}
void ReadPasswordJobPrivate::scheduledStart()
{
QString errorString;
Error error = NoError;
const OSStatus ret = readPw( &data, q->service(), q->key(), 0 );
switch ( ret ) {
case noErr:
break;
case errSecItemNotFound:
errorString = tr("Password not found");
error = EntryNotFound;
break;
default:
errorString = strForStatus( ret );
error = OtherError;
break;
}
q->emitFinishedWithError( error, errorString );
}
static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) {
SecKeychainItemRef ref;
QByteArray pw;
const OSStatus ret1 = readPw( &pw, service, account, &ref );
if ( ret1 == errSecItemNotFound )
return NoError; // No item stored, we're done
if ( ret1 != noErr ) {
*err = strForStatus( ret1 );
//TODO map error code, set errstr
return OtherError;
}
const Releaser<SecKeychainItemRef> releaser( ref );
const OSStatus ret2 = SecKeychainItemDelete( ref );
if ( ret2 == noErr )
return NoError;
//TODO map error code
*err = strForStatus( ret2 );
return CouldNotDeleteEntry;
}
static QKeychain::Error writeEntryImpl( const QString& service,
const QString& account,
const QByteArray& data,
QString* err ) {
Q_ASSERT( err );
err->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
data.size(),
data.constData(),
NULL //item reference
);
if ( ret != noErr ) {
switch ( ret ) {
case errSecDuplicateItem:
{
Error derr = deleteEntryImpl( service, account, err );
if ( derr != NoError )
return CouldNotDeleteEntry;
else
return writeEntryImpl( service, account, data, err );
}
default:
*err = strForStatus( ret );
return OtherError;
}
}
return NoError;
}
void WritePasswordJobPrivate::scheduledStart()
{
QString errorString;
Error error = NoError;
if ( mode == Delete ) {
const Error derr = deleteEntryImpl( q->service(), key, &errorString );
if ( derr != NoError )
error = CouldNotDeleteEntry;
q->emitFinishedWithError( error, errorString );
return;
}
const QByteArray data = mode == Text ? textData.toUtf8() : binaryData;
error = writeEntryImpl( q->service(), key, data, &errorString );
q->emitFinishedWithError( error, errorString );
}

163
ext/qtkeychain/keychain_p.h Normal file
View File

@ -0,0 +1,163 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#ifndef KEYCHAIN_P_H
#define KEYCHAIN_P_H
#include <QCoreApplication>
#include <QObject>
#include <QPointer>
#include <QSettings>
#include <QVector>
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
#include <QDBusPendingCallWatcher>
#include "kwallet_interface.h"
#else
class QDBusPendingCallWatcher;
#endif
#include "keychain.h"
namespace QKeychain {
class JobExecutor;
class JobPrivate : public QObject {
Q_OBJECT
public:
JobPrivate( const QString& service_ )
: error( NoError )
, service( service_ )
, autoDelete( true )
, insecureFallback( false ) {}
QKeychain::Error error;
QString errorString;
QString service;
bool autoDelete;
bool insecureFallback;
QPointer<QSettings> settings;
};
class ReadPasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit ReadPasswordJobPrivate( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {}
void scheduledStart();
ReadPasswordJob* const q;
QByteArray data;
QString key;
int walletHandle;
enum DataType {
Binary,
Text
};
DataType dataType;
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
org::kde::KWallet* iface;
static void gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* data );
friend class QKeychain::JobExecutor;
void fallbackOnError(const QDBusError& err);
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher );
void kwalletReadFinished( QDBusPendingCallWatcher* watcher );
#else //moc's too dumb to respect above macros, so just define empty slot implementations
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
void kwalletReadFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class WritePasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit WritePasswordJobPrivate( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {}
void scheduledStart();
enum Mode {
Delete,
Text,
Binary
};
static QString modeToString(Mode m);
static Mode stringToMode(const QString& s);
WritePasswordJob* const q;
Mode mode;
QString key;
QByteArray binaryData;
QString textData;
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
org::kde::KWallet* iface;
static void gnomeKeyring_cb( int result, WritePasswordJobPrivate* self );
friend class QKeychain::JobExecutor;
void fallbackOnError(const QDBusError& err);
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletWriteFinished( QDBusPendingCallWatcher* watcher );
#else
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletWriteFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class DeletePasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit DeletePasswordJobPrivate( DeletePasswordJob* qq ) : q( qq ) {}
void doStart();
DeletePasswordJob* const q;
QString key;
private Q_SLOTS:
void jobFinished( QKeychain::Job* );
};
class JobExecutor : public QObject {
Q_OBJECT
public:
static JobExecutor* instance();
void enqueue( Job* job );
private:
explicit JobExecutor();
void startNextIfNoneRunning();
private Q_SLOTS:
void jobFinished( QKeychain::Job* );
void jobDestroyed( QObject* object );
private:
static JobExecutor* s_instance;
Job* m_runningJob;
QVector<QPointer<Job> > m_queue;
};
}
#endif // KEYCHAIN_P_H

View File

@ -0,0 +1,510 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include "gnomekeyring_p.h"
#include <QSettings>
#include <QScopedPointer>
using namespace QKeychain;
static QString typeKey( const QString& key )
{
return QString::fromLatin1( "%1/type" ).arg( key );
}
static QString dataKey( const QString& key )
{
return QString::fromLatin1( "%1/data" ).arg( key );
}
enum KeyringBackend {
Backend_GnomeKeyring,
Backend_Kwallet4,
Backend_Kwallet5
};
enum DesktopEnvironment {
DesktopEnv_Gnome,
DesktopEnv_Kde4,
DesktopEnv_Plasma5,
DesktopEnv_Unity,
DesktopEnv_Xfce,
DesktopEnv_Other
};
// the following detection algorithm is derived from chromium,
// licensed under BSD, see base/nix/xdg_util.cc
static DesktopEnvironment getKdeVersion() {
QString value = qgetenv("KDE_SESSION_VERSION");
if ( value == "5" ) {
return DesktopEnv_Plasma5;
} else if (value == "4" ) {
return DesktopEnv_Kde4;
} else {
// most likely KDE3
return DesktopEnv_Other;
}
}
static DesktopEnvironment detectDesktopEnvironment() {
QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
if ( xdgCurrentDesktop == "GNOME" ) {
return DesktopEnv_Gnome;
} else if ( xdgCurrentDesktop == "Unity" ) {
return DesktopEnv_Unity;
} else if ( xdgCurrentDesktop == "KDE" ) {
return getKdeVersion();
}
QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
if ( desktopSession == "gnome" ) {
return DesktopEnv_Gnome;
} else if ( desktopSession == "kde" ) {
return getKdeVersion();
} else if ( desktopSession == "kde4" ) {
return DesktopEnv_Kde4;
} else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) {
return DesktopEnv_Xfce;
}
if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) {
return DesktopEnv_Gnome;
} else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) {
return getKdeVersion();
}
return DesktopEnv_Other;
}
static KeyringBackend detectKeyringBackend()
{
switch (detectDesktopEnvironment()) {
case DesktopEnv_Kde4:
return Backend_Kwallet4;
break;
case DesktopEnv_Plasma5:
return Backend_Kwallet5;
break;
// fall through
case DesktopEnv_Gnome:
case DesktopEnv_Unity:
case DesktopEnv_Xfce:
case DesktopEnv_Other:
default:
if ( GnomeKeyring::isAvailable() ) {
return Backend_GnomeKeyring;
} else {
return Backend_Kwallet4;
}
}
}
static KeyringBackend getKeyringBackend()
{
static KeyringBackend backend = detectKeyringBackend();
return backend;
}
static void kwalletReadPasswordScheduledStartImpl(const char * service, const char * path, ReadPasswordJobPrivate * priv) {
if ( QDBusConnection::sessionBus().isConnected() )
{
priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
}
else
{
// D-Bus is not reachable so none can tell us something about KWalletd
QDBusError err( QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running") );
priv->fallbackOnError( err );
}
}
void ReadPasswordJobPrivate::scheduledStart() {
switch ( getKeyringBackend() ) {
case Backend_GnomeKeyring:
if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(),
reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &ReadPasswordJobPrivate::gnomeKeyring_cb ),
this, 0 ) )
q->emitFinishedWithError( OtherError, tr("Unknown error") );
break;
case Backend_Kwallet4:
kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this);
break;
case Backend_Kwallet5:
kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this);
break;
}
}
void ReadPasswordJobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
{
watcher->deleteLater();
const QDBusPendingReply<QString> reply = *watcher;
const QDBusPendingReply<int> pendingReply = iface->open( reply.value(), 0, q->service() );
QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this );
connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
}
static QPair<Error, QString> mapGnomeKeyringError( int result )
{
Q_ASSERT( result != GnomeKeyring::RESULT_OK );
switch ( result ) {
case GnomeKeyring::RESULT_DENIED:
return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") );
case GnomeKeyring::RESULT_NO_KEYRING_DAEMON:
return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") );
case GnomeKeyring::RESULT_ALREADY_UNLOCKED:
return qMakePair( OtherError, QObject::tr("Already unlocked") );
case GnomeKeyring::RESULT_NO_SUCH_KEYRING:
return qMakePair( OtherError, QObject::tr("No such keyring") );
case GnomeKeyring::RESULT_BAD_ARGUMENTS:
return qMakePair( OtherError, QObject::tr("Bad arguments") );
case GnomeKeyring::RESULT_IO_ERROR:
return qMakePair( OtherError, QObject::tr("I/O error") );
case GnomeKeyring::RESULT_CANCELLED:
return qMakePair( OtherError, QObject::tr("Cancelled") );
case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS:
return qMakePair( OtherError, QObject::tr("Keyring already exists") );
case GnomeKeyring::RESULT_NO_MATCH:
return qMakePair( EntryNotFound, QObject::tr("No match") );
default:
break;
}
return qMakePair( OtherError, QObject::tr("Unknown error") );
}
void ReadPasswordJobPrivate::gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* self )
{
if ( result == GnomeKeyring::RESULT_OK ) {
if ( self->dataType == ReadPasswordJobPrivate::Text )
self->data = string;
else
self->data = QByteArray::fromBase64( string );
self->q->emitFinished();
} else {
const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
self->q->emitFinishedWithError( errorResult.first, errorResult.second );
}
}
void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err )
{
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( q->insecureFallback() && actual->contains( dataKey( key ) ) ) {
const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() );
if (mode == WritePasswordJobPrivate::Binary)
dataType = Binary;
else
dataType = Text;
data = actual->value( dataKey( key ) ).toByteArray();
q->emitFinished();
} else {
if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running
q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") );
else
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
}
}
void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
const QDBusPendingReply<int> reply = *watcher;
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( reply.isError() ) {
fallbackOnError( reply.error() );
return;
}
if ( actual->contains( dataKey( key ) ) ) {
// We previously stored data in the insecure QSettings, but now have KWallet available.
// Do the migration
data = actual->value( dataKey( key ) ).toByteArray();
const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() );
actual->remove( key );
q->emitFinished();
WritePasswordJob* j = new WritePasswordJob( q->service(), 0 );
j->setSettings( q->settings() );
j->setKey( key );
j->setAutoDelete( true );
if ( mode == WritePasswordJobPrivate::Binary )
j->setBinaryData( data );
else if ( mode == WritePasswordJobPrivate::Text )
j->setTextData( QString::fromUtf8( data ) );
else
Q_ASSERT( false );
j->start();
return;
}
walletHandle = reply.value();
if ( walletHandle < 0 ) {
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
return;
}
const QDBusPendingReply<int> nextReply = iface->entryType( walletHandle, q->service(), key, q->service() );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) );
}
//Must be in sync with KWallet::EntryType (kwallet.h)
enum KWalletEntryType {
Unknown=0,
Password,
Stream,
Map
};
void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
if ( watcher->isError() ) {
const QDBusError err = watcher->error();
q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
const QDBusPendingReply<int> reply = *watcher;
const int value = reply.value();
switch ( value ) {
case Unknown:
q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
return;
case Password:
dataType = Text;
break;
case Stream:
dataType = Binary;
break;
case Map:
q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") );
return;
default:
q->emitFinishedWithError( OtherError, tr("Unknown kwallet entry type '%1'").arg( value ) );
return;
}
const QDBusPendingCall nextReply = dataType == Text
? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) )
: QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletReadFinished(QDBusPendingCallWatcher*)) );
}
void ReadPasswordJobPrivate::kwalletReadFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
if ( watcher->isError() ) {
const QDBusError err = watcher->error();
q->emitFinishedWithError( OtherError, tr("Could not read password: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
if ( dataType == Binary ) {
QDBusPendingReply<QByteArray> reply = *watcher;
data = reply.value();
} else {
QDBusPendingReply<QString> reply = *watcher;
data = reply.value().toUtf8();
}
q->emitFinished();
}
static void kwalletWritePasswordScheduledStart( const char * service, const char * path, WritePasswordJobPrivate * priv ) {
if ( QDBusConnection::sessionBus().isConnected() )
{
priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
}
else
{
// D-Bus is not reachable so none can tell us something about KWalletd
QDBusError err( QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running") );
priv->fallbackOnError( err );
}
}
void WritePasswordJobPrivate::scheduledStart() {
switch ( getKeyringBackend() ) {
case Backend_GnomeKeyring:
if ( mode == WritePasswordJobPrivate::Delete ) {
if ( !GnomeKeyring::delete_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(),
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &WritePasswordJobPrivate::gnomeKeyring_cb ),
this, 0 ) )
q->emitFinishedWithError( OtherError, tr("Unknown error") );
} else {
QByteArray password = mode == WritePasswordJobPrivate::Text ? textData.toUtf8() : binaryData.toBase64();
QByteArray service = q->service().toUtf8();
if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT, service.constData(),
key.toUtf8().constData(), service.constData(), password.constData(),
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &WritePasswordJobPrivate::gnomeKeyring_cb ),
this, 0 ) )
q->emitFinishedWithError( OtherError, tr("Unknown error") );
}
break;
case Backend_Kwallet4:
kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this);
break;
case Backend_Kwallet5:
kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this);
break;
}
}
QString WritePasswordJobPrivate::modeToString(Mode m)
{
switch (m) {
case Delete:
return QLatin1String("Delete");
case Text:
return QLatin1String("Text");
case Binary:
return QLatin1String("Binary");
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value");
return QString();
}
WritePasswordJobPrivate::Mode WritePasswordJobPrivate::stringToMode(const QString& s)
{
if (s == QLatin1String("Delete") || s == QLatin1String("0"))
return Delete;
if (s == QLatin1String("Text") || s == QLatin1String("1"))
return Text;
if (s == QLatin1String("Binary") || s == QLatin1String("2"))
return Binary;
qCritical("Unexpected mode string '%s'", qPrintable(s));
return Text;
}
void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err)
{
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( !q->insecureFallback() ) {
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
if ( mode == Delete ) {
actual->remove( key );
actual->sync();
q->emitFinished();
return;
}
actual->setValue( QString::fromLatin1( "%1/type" ).arg( key ), mode );
if ( mode == Text )
actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), textData.toUtf8() );
else if ( mode == Binary )
actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), binaryData );
actual->sync();
q->emitFinished();
}
void WritePasswordJobPrivate::gnomeKeyring_cb( int result, WritePasswordJobPrivate* self )
{
if ( result == GnomeKeyring::RESULT_OK ) {
self->q->emitFinished();
} else {
const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
self->q->emitFinishedWithError( errorResult.first, errorResult.second );
}
}
void WritePasswordJobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
{
watcher->deleteLater();
const QDBusPendingReply<QString> reply = *watcher;
const QDBusPendingReply<int> pendingReply = iface->open( reply.value(), 0, q->service() );
QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this );
connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
}
void WritePasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( reply.isError() ) {
fallbackOnError( reply.error() );
return;
}
if ( actual->contains( key ) )
{
// If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data
actual->remove( key );
actual->sync();
}
const int handle = reply.value();
if ( handle < 0 ) {
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
return;
}
QDBusPendingReply<int> nextReply;
if ( !textData.isEmpty() )
nextReply = iface->writePassword( handle, q->service(), key, textData, q->service() );
else if ( !binaryData.isEmpty() )
nextReply = iface->writeEntry( handle, q->service(), key, binaryData, q->service() );
else
nextReply = iface->removeEntry( handle, q->service(), key, q->service() );
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletWriteFinished(QDBusPendingCallWatcher*)) );
}
void WritePasswordJobPrivate::kwalletWriteFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
if ( reply.isError() ) {
const QDBusError err = reply.error();
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
q->emitFinished();
}

View File

@ -0,0 +1,107 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include <QSettings>
#include <windows.h>
#include <wincrypt.h>
#include <memory>
using namespace QKeychain;
void ReadPasswordJobPrivate::scheduledStart() {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
QByteArray encrypted = actual->value( key ).toByteArray();
if ( encrypted.isNull() ) {
q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
return;
}
DATA_BLOB blob_in, blob_out;
blob_in.pbData = reinterpret_cast<BYTE*>( encrypted.data() );
blob_in.cbData = encrypted.size();
const BOOL ret = CryptUnprotectData( &blob_in,
NULL,
NULL,
NULL,
NULL,
0,
&blob_out );
if ( !ret ) {
q->emitFinishedWithError( OtherError, tr("Could not decrypt data") );
return;
}
data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
SecureZeroMemory( blob_out.pbData, blob_out.cbData );
LocalFree( blob_out.pbData );
q->emitFinished();
}
void WritePasswordJobPrivate::scheduledStart() {
if ( mode == Delete ) {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->remove( key );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString err = actual->status() == QSettings::AccessError
? tr("Could not delete encrypted data from settings: access error")
: tr("Could not delete encrypted data from settings: format error");
q->emitFinishedWithError( OtherError, err );
} else {
q->emitFinished();
}
return;
}
QByteArray data = mode == Binary ? binaryData : textData.toUtf8();
DATA_BLOB blob_in, blob_out;
blob_in.pbData = reinterpret_cast<BYTE*>( data.data() );
blob_in.cbData = data.size();
const BOOL res = CryptProtectData( &blob_in,
L"QKeychain-encrypted data",
NULL,
NULL,
NULL,
0,
&blob_out );
if ( !res ) {
q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available?
return;
}
const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
LocalFree( blob_out.pbData );
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->setValue( key, encrypted );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString errorString = actual->status() == QSettings::AccessError
? tr("Could not store encrypted data in settings: access error")
: tr("Could not store encrypted data in settings: format error");
q->emitFinishedWithError( OtherError, errorString );
return;
}
q->emitFinished();
}

View File

@ -0,0 +1,276 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KWallet">
<signal name="walletListDirty">
</signal>
<signal name="walletCreated">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletOpened">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletAsyncOpened">
<arg name="tId" type="i" direction="out"/>
<arg name="handle" type="i" direction="out"/>
</signal>
<signal name="walletDeleted">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletClosed">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="walletClosed">
<arg name="handle" type="i" direction="out"/>
</signal>
<signal name="allWalletsClosed">
</signal>
<signal name="folderListUpdated">
<arg name="wallet" type="s" direction="out"/>
</signal>
<signal name="folderUpdated">
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
</signal>
<signal name="applicationDisconnected">
<arg name="wallet" type="s" direction="out"/>
<arg name="application" type="s" direction="out"/>
</signal>
<method name="isEnabled">
<arg type="b" direction="out"/>
</method>
<method name="open">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="openPath">
<arg type="i" direction="out"/>
<arg name="path" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="openAsync">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<arg name="handleSession" type="b" direction="in"/>
</method>
<method name="openPathAsync">
<arg type="i" direction="out"/>
<arg name="path" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<arg name="handleSession" type="b" direction="in"/>
</method>
<method name="close">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="force" type="b" direction="in"/>
</method>
<method name="close">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="force" type="b" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="sync">
<arg name="handle" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="deleteWallet">
<arg type="i" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="isOpen">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="isOpen">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
</method>
<method name="users">
<arg type="as" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
</method>
<method name="changePassword">
<arg name="wallet" type="s" direction="in"/>
<arg name="wId" type="x" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="wallets">
<arg type="as" direction="out"/>
</method>
<method name="folderList">
<arg type="as" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="hasFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="createFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="removeFolder">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="entryList">
<arg type="as" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readEntry">
<arg type="ay" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readMap">
<arg type="ay" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readPassword">
<arg type="s" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readEntryList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readMapList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="readPasswordList">
<arg type="a{sv}" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="renameEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="oldName" type="s" direction="in"/>
<arg name="newName" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="entryType" type="i" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writeMap">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="ay" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="writePassword">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="value" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="hasEntry">
<arg type="b" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="entryType">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="removeEntry">
<arg type="i" direction="out"/>
<arg name="handle" type="i" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
<arg name="appid" type="s" direction="in"/>
</method>
<method name="disconnectApplication">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="application" type="s" direction="in"/>
</method>
<method name="reconfigure">
</method>
<method name="folderDoesNotExist">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="folder" type="s" direction="in"/>
</method>
<method name="keyDoesNotExist">
<arg type="b" direction="out"/>
<arg name="wallet" type="s" direction="in"/>
<arg name="folder" type="s" direction="in"/>
<arg name="key" type="s" direction="in"/>
</method>
<method name="closeAllWallets">
</method>
<method name="networkWallet">
<arg type="s" direction="out"/>
</method>
<method name="localWallet">
<arg type="s" direction="out"/>
</method>
<method name="pamOpen">
<arg name="wallet" type="s" direction="in"/>
<arg name="passwordHash" type="ay" direction="in"/>
<arg name="sessionTimeout" type="i" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>

View File

@ -0,0 +1,17 @@
#ifndef QKEYCHAIN_EXPORT_H
#define QKEYCHAIN_EXPORT_H
#include <qglobal.h>
# ifdef QKEYCHAIN_STATICLIB
# undef QKEYCHAIN_SHAREDLIB
# define QKEYCHAIN_EXPORT
# else
# ifdef QKEYCHAIN_BUILD_QKEYCHAIN_LIB
# define QKEYCHAIN_EXPORT Q_DECL_EXPORT
# else
# define QKEYCHAIN_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif

View File

@ -0,0 +1,98 @@
/******************************************************************************
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include <QCoreApplication>
#include <QStringList>
#include "keychain.h"
#include <iostream>
using namespace QKeychain;
static int printUsage() {
std::cerr << "testclient store <account> <password>" << std::endl;
std::cerr << "testclient restore <account>" << std::endl;
std::cerr << "testclient delete <account>" << std::endl;
return 1;
}
int main( int argc, char** argv ) {
QCoreApplication app( argc, argv );
const QStringList args = app.arguments();
if ( args.count() < 2 )
return printUsage();
QStringList::ConstIterator it = args.constBegin();
++it;
if ( *it == QLatin1String("store") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it == args.constEnd() )
return printUsage();
const QString pass = *it;
if ( ++it != args.constEnd() )
return printUsage();
WritePasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
job.setTextData( pass );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
if ( job.error() ) {
std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << "Password stored successfully" << std::endl;
} else if ( *it == QLatin1String("restore") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it != args.constEnd() )
return printUsage();
ReadPasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
const QString pw = job.textData();
if ( job.error() ) {
std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << qPrintable(pw) << std::endl;
} else if ( *it == QLatin1String("delete") ) {
if ( ++it == args.constEnd() )
return printUsage();
const QString acc = *it;
if ( ++it != args.constEnd() )
return printUsage();
DeletePasswordJob job( QLatin1String("qtkeychain-testclient") );
job.setAutoDelete( false );
job.setKey( acc );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
if ( job.error() ) {
std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl;
return 1;
}
std::cout << "Password deleted successfully" << std::endl;
} else {
return printUsage();
}
}

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="140"/>
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="129"/>
<source>D-Bus is not running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="222"/>
<source>No keychain service available</source>
<translation>Kein Schlüsselbund-Dienst verfügbar</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="224"/>
<source>Could not open wallet: %1; %2</source>
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="270"/>
<source>Access to keychain denied</source>
<translation>Zugriff auf Schlüsselbund verweigert</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="291"/>
<source>Could not determine data type: %1; %2</source>
<translation>Datentyp kann nicht ermittelt werden: %1: %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="309"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="312"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="327"/>
<source>Could not read password: %1; %2</source>
<translation>Passwort konnte nicht ausgelesen werden: %1; %2</translation>
</message>
<message>
<location filename="../keychain_mac.cpp" line="76"/>
<source>Password not found</source>
<translation>Passwort nicht gefunden</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="300"/>
<location filename="../keychain_win.cpp" line="27"/>
<source>Entry not found</source>
<translation>Eintrag nicht gefunden</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="44"/>
<source>Could not decrypt data</source>
<translation>Kann Daten nicht entschlüsseln</translation>
</message>
</context>
<context>
<name>QKeychain::WritePasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="364"/>
<location filename="../keychain_unix.cpp" line="372"/>
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="352"/>
<source>D-Bus is not running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="420"/>
<location filename="../keychain_unix.cpp" line="505"/>
<source>Could not open wallet: %1; %2</source>
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="483"/>
<source>Access to keychain denied</source>
<translation>Zugriff auf Schlüsselbund verweigert</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="64"/>
<source>Could not delete encrypted data from settings: access error</source>
<translation>Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="65"/>
<source>Could not delete encrypted data from settings: format error</source>
<translation>Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="85"/>
<source>Encryption failed</source>
<translation>Verschlüsselung fehlgeschlagen</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="100"/>
<source>Could not store encrypted data in settings: access error</source>
<translation>Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="101"/>
<source>Could not store encrypted data in settings: format error</source>
<translation>Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../keychain_unix.cpp" line="167"/>
<source>Access to keychain denied</source>
<translation>Zugriff auf Schlüsselbund verweigert</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="169"/>
<source>No keyring daemon</source>
<translation>Kein Schlüsselbund-Dienst </translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="171"/>
<source>Already unlocked</source>
<translation>Bereits entsperrt</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="173"/>
<source>No such keyring</source>
<translation>Kein solcher Schlüsselbund</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="175"/>
<source>Bad arguments</source>
<translation>Ungültige Argumente</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="177"/>
<source>I/O error</source>
<translation>Ein-/Ausgabe-Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="179"/>
<source>Cancelled</source>
<translation>Abgebrochen</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="181"/>
<source>Keyring already exists</source>
<translation>Schlüsselbund existiert bereits</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="183"/>
<source>No match</source>
<translation>Kein Treffer</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="188"/>
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<location filename="../keychain_mac.cpp" line="31"/>
<location filename="../keychain_mac.cpp" line="33"/>
<source>%1 (OSStatus %2)</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ro_RO">
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="140"/>
<source>Unknown error</source>
<translation>Eroare necunoscută</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="129"/>
<source>D-Bus is not running</source>
<translation>D-Bus nu rulează</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="222"/>
<source>No keychain service available</source>
<translatorcomment>Nu există niciun serviciu de chei disponibil</translatorcomment>
<translation>Kein Schlüsselbund-Dienst verfügbar</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="224"/>
<source>Could not open wallet: %1; %2</source>
<translation>Nu se poate deschide portofelul: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="270"/>
<source>Access to keychain denied</source>
<translation>Acces interzis la serviciul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="291"/>
<source>Could not determine data type: %1; %2</source>
<translation>Nu se poate stabili tipul de date: %1: %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="309"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation>Tip de înregistrare nesuportat &apos;Map&apos;</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="312"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation>Tip de înregistrare kwallet necunoscut &apos;%1&apos;</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="327"/>
<source>Could not read password: %1; %2</source>
<translation>Nu se poate citi parola: %1; %2</translation>
</message>
<message>
<location filename="../keychain_mac.cpp" line="76"/>
<source>Password not found</source>
<translation>Parola nu a fost găsită</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="300"/>
<location filename="../keychain_win.cpp" line="27"/>
<source>Entry not found</source>
<translation>Înregistrarea nu a fost găsită</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="44"/>
<source>Could not decrypt data</source>
<translation>Nu se poate decripta data</translation>
</message>
</context>
<context>
<name>QKeychain::WritePasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="364"/>
<location filename="../keychain_unix.cpp" line="372"/>
<source>Unknown error</source>
<translation>Eroare necunoscută</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="352"/>
<source>D-Bus is not running</source>
<translation>D-Bus nu rulează</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="420"/>
<location filename="../keychain_unix.cpp" line="505"/>
<source>Could not open wallet: %1; %2</source>
<translation>Nu se poate deschide portofelul: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="483"/>
<source>Access to keychain denied</source>
<translation>Acces interzis la serviciul de chei</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="64"/>
<source>Could not delete encrypted data from settings: access error</source>
<translation>Nu se pot șterge datele criptate din setări: eroare de acces</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="65"/>
<source>Could not delete encrypted data from settings: format error</source>
<translation>Nu se pot șterge datele criptate din setări: eroare de format</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="85"/>
<source>Encryption failed</source>
<translation>Criptarea a eșuat</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="100"/>
<source>Could not store encrypted data in settings: access error</source>
<translation>Nu se pot stoca datele criptate în setări: eroare de acces</translation>
</message>
<message>
<location filename="../keychain_win.cpp" line="101"/>
<source>Could not store encrypted data in settings: format error</source>
<translation>Nu se pot stoca datele criptate în setări: eroare de format</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../keychain_unix.cpp" line="167"/>
<source>Access to keychain denied</source>
<translation>Acces interzis la serviciul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="169"/>
<source>No keyring daemon</source>
<translation>Niciun demon pentru inelul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="171"/>
<source>Already unlocked</source>
<translation>Deja deblocat</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="173"/>
<source>No such keyring</source>
<translation>Nu există astfel de inel de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="175"/>
<source>Bad arguments</source>
<translation>Argumente greșite</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="177"/>
<source>I/O error</source>
<translation>Eroare de I/E</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="179"/>
<source>Cancelled</source>
<translation>Anulat</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="181"/>
<source>Keyring already exists</source>
<translation>Inelul de chei deja există</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="183"/>
<source>No match</source>
<translation>Nicio potrivire</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="188"/>
<source>Unknown error</source>
<translation>Eroare necunoscută</translation>
</message>
<message>
<location filename="../keychain_mac.cpp" line="31"/>
<location filename="../keychain_mac.cpp" line="33"/>
<source>%1 (OSStatus %2)</source>
<translation>%1 (OSStatus %2)</translation>
</message>
</context>
</TS>

View File

@ -2,10 +2,11 @@
# Fuel
#-------------------------------------------------
QT += core gui webkit
QT = core gui webkit
contains(QT_VERSION, ^5\\..*) {
QT += widgets webkitwidgets
QT -= quick multimediawidgets opengl printsupport qml multimedia positioning sensors
}
TARGET = Fuel
@ -20,6 +21,12 @@ macx {
ICON = rsrc/icons/fuel.icns
}
# FreeBSD needs explicit paths to Qt install
unix:freebsd {
INCLUDEPATH += /usr/local/include
LIBS += -L/usr/local/lib
}
unix:!macx {
TARGET = fuel
ICON = rsrc/icons/fuel.png
@ -46,34 +53,86 @@ SOURCES += src/main.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/CloneDialog.cpp \
src/LoggedProcess.cpp \
src/BrowserWidget.cpp \
src/CustomWebView.cpp
src/CustomWebView.cpp \
src/Fossil.cpp \
src/Workspace.cpp \
src/SearchBox.cpp \
src/Settings.cpp \
src/RemoteDialog.cpp \
src/AboutDialog.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/CloneDialog.h \
src/LoggedProcess.h \
src/BrowserWidget.h \
src/CustomWebView.h
src/CustomWebView.h \
src/Fossil.h \
src/Workspace.h \
src/SearchBox.h \
src/Settings.h \
src/RemoteDialog.h \
src/AboutDialog.h
FORMS += ui/MainWindow.ui \
ui/CommitDialog.ui \
ui/FileActionDialog.ui \
ui/SettingsDialog.ui \
ui/FslSettingsDialog.ui \
ui/CloneDialog.ui \
ui/BrowserWidget.ui
ui/BrowserWidget.ui \
ui/RevisionDialog.ui \
ui/RemoteDialog.ui \
ui/AboutDialog.ui
RESOURCES += \
rsrc/resources.qrc
# QtKeychain
SOURCES += ext/qtkeychain/keychain.cpp
HEADERS += ext/qtkeychain/keychain.h \
ext/qtkeychain/keychain_p.h \
ext/qtkeychain/qkeychain_export.h
DEFINES += QKEYCHAIN_STATICLIB
unix:!macx {
QT += dbus
SOURCES += ext/qtkeychain/keychain_unix.cpp \
ext/qtkeychain/gnomekeyring.cpp
HEADERS += ext/qtkeychain/gnomekeyring_p.h
DBUS_INTERFACES += ext/qtkeychain/org.kde.KWallet.xml
}
macx {
SOURCES += ext/qtkeychain/keychain_mac.cpp
LIBS += -framework CoreFoundation -framework Security
}
win32 {
SOURCES += ext/qtkeychain/keychain_win.cpp
LIBS += -lCrypt32
}
CODECFORTR = UTF-8
TRANSLATIONS += \
@ -83,5 +142,7 @@ TRANSLATIONS += \
intl/es_ES.ts \
intl/fr_FR.ts \
intl/ru_RU.ts \
intl/pt_PT.ts
intl/pt_PT.ts \
intl/it_IT.ts \
intl/nl_NL.ts \
intl/ko_KO.ts \

View File

@ -2,7 +2,7 @@
setlocal EnableDelayedExpansion
set SCRIPTDIR=%CD%
set PRJDIR=%SCRIPTDIR%\..
set QTPATH=C:\Qt\Qt5.3.1\5.3\mingw482_32
set QTPATH=C:\Qt\5.4\mingw491_32
echo Converting localizations
del /q %PRJDIR%\rsrc\intl\*
@ -15,6 +15,9 @@ REM Convert all except the en_US which is the original text in the code
%QTPATH%\bin\lrelease es_ES.ts -qm ..\rsrc\intl\es_ES.qm
%QTPATH%\bin\lrelease fr_FR.ts -qm ..\rsrc\intl\fr_FR.qm
%QTPATH%\bin\lrelease ru_RU.ts -qm ..\rsrc\intl\ru_RU.qm
%QTPATH%\bin\lrelease ru_RU.ts -qm ..\rsrc\intl\pt_PT.qm
%QTPATH%\bin\lrelease pt_PT.ts -qm ..\rsrc\intl\pt_PT.qm
%QTPATH%\bin\lrelease it_IT.ts -qm ..\rsrc\intl\it_IT.qm
%QTPATH%\bin\lrelease nl_NL.ts -qm ..\rsrc\intl\nl_NL.qm
%QTPATH%\bin\lrelease ko_KR.ts -qm ..\rsrc\intl\ko_KR.qm
endlocal

View File

@ -1,8 +1,22 @@
#!/bin/bash
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#!/bin/sh
SCRIPTDIR="$( cd "$( dirname "$0" )" && pwd )"
PRJDIR=$SCRIPTDIR/..
INTLDIR=$SCRIPTDIR
# Detect lrelease tool
if which lrelease-qt5 2>/dev/null; then
LRELEASE="lrelease-qt5"
elif which lrelease4 2>/dev/null; then
LRELEASE="lrelease4"
elif which lrelease 2>/dev/null; then
LRELEASE="lrelease"
else
echo "lrelease not found"
exit 1
fi
echo "Using ${LRELEASE}"
echo "Converting localizations"
rm -rf $PRJDIR/rsrc/intl
@ -16,7 +30,7 @@ do
# the original text in the code
if [ "$BASE" != "en_US" ]; then
echo "$TARGET"
lrelease-qt5 $i -qm $PRJDIR/rsrc/intl/$BASE.qm
$LRELEASE $i -qm $PRJDIR/rsrc/intl/$BASE.qm
fi
done

1375
intl/it_IT.ts Normal file

File diff suppressed because it is too large Load Diff

1373
intl/ko_KR.ts Normal file

File diff suppressed because it is too large Load Diff

1375
intl/nl_NL.ts Normal file

File diff suppressed because it is too large Load Diff

105
manifest
View File

@ -1,6 +1,7 @@
C Updated\sapplication\sversion
D 2015-03-27T18:25:42.323
F debian/changelog 19b96029fa5c46944f5c9ffb9a75ce47a19e1dda
C OSX:\sGuess\sactual\sexecutable\sfrom\sthe\sapp\sbundle\sdirectory
D 2015-08-22T17:32:24.250
F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35
F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b
F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b
F debian/control f4f9bbf38a523520eadbb6f66325d2784297e2a0
F debian/copyright 6bcc83e13533b6cc208f3cd423f1f483163b62ad
@ -9,22 +10,49 @@ F debian/menu aa1321fe6597a631df5cc978a3cf7b21ac1a3657
F debian/rules 468914cbcf1bcc252ab3f616e1fdc2b37bc10b5d x
F debian/source/format 1064dc0ce263680c076a1005f35ec906a5cf5a32
F debian/watch 34f0921ff100a3e16a7ad84dcc303731de830a60
F dist/arch/PKGBUILD e08bdd82de34beeba1ef22ce6b6aa1900637581c
F dist/arch/PKGBUILD 26623327e467028a883cd13963daa36baf10dfa3
F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e
F doc/Building.txt 7c0f1060d4a08ed330058d4a3a68905c05228381
F doc/Changes.txt b03302545e4a6c0b16a30d623a7627f8aef65ef6
F doc/Building.md 149d959751ae488829e084a9f88449a08220c1d1
F doc/Changes.md af49b873012c6b453fe8084f46b6aadbd6fe6e63
F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c
F fuel.pro 844a18c3faf5239e0d0025d8b7feac3900c28e71
F intl/convert.bat a4c0198e6df9e39469e81d1d702fccffe74ed691 x
F intl/convert.sh 40ca4d0474063faefb6bd51dd37e0e732a7e82aa x
F ext/qtkeychain/CMakeLists.txt fc1afa05034f2765ba243ce758a7e9d6b6efe2d6
F ext/qtkeychain/COPYING d0f83c8198fdd5464d2373015b7b64ce7cae607e
F ext/qtkeychain/ChangeLog 1703279e17036995806ba1719033d14840a8a7e2
F ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in a50c3b646181124f15b946c3297f13012e959341
F ext/qtkeychain/QtKeychainConfig.cmake.in ac7c87e54854a06c51e00f833f21f8323d1e6884
F ext/qtkeychain/QtKeychainConfigVersion.cmake.in 3b650037d5775f28802c0471afe2cf6dbe51084e
F ext/qtkeychain/ReadMe.markdown 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43
F ext/qtkeychain/ReadMe.txt 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43
F ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake 7a2ccf81f25546e93a6f48c792cdebaae51857e9
F ext/qtkeychain/gnomekeyring.cpp 7fa97bd4ffb7c9df00d25e56cd9173d34109afe6
F ext/qtkeychain/gnomekeyring_p.h 7f6acaf6d00b36bd08f5f31ba9136efa969e9875
F ext/qtkeychain/keychain.cpp 427cbda7c6a76de995b1f1b4caa700cd06a9d19a
F ext/qtkeychain/keychain.h f084c671b481af6ac7ce00bf641055a3cfc9cf9b
F ext/qtkeychain/keychain_mac.cpp a028f6fc5e40f9ab88c94ebe30b8b0ae417f2f34
F ext/qtkeychain/keychain_p.h 36f4caee2cbdbde971a1105ab388681ad2924665
F ext/qtkeychain/keychain_unix.cpp 8e657da9acd9e86b2fdec19dc40f1afa4a1c5191
F ext/qtkeychain/keychain_win.cpp e52877828703650219c1c674e618c7211f588d0d
F ext/qtkeychain/org.kde.KWallet.xml f3729fda9f8fa8031a6f69415dcc29455c3c9ae6
F ext/qtkeychain/qkeychain_export.h d756528188ef9bf3c4461ecc80048f06c362b54b
F ext/qtkeychain/testclient.cpp cb1290a9584b627306a7bfdf1c02a8bbae503f5f
F ext/qtkeychain/translations/qtkeychain_de.ts 0a70c8205c066c30ed8172f0670942de1fc5eede
F ext/qtkeychain/translations/qtkeychain_ro.ts f16939382fd1a047b0692426bc82847347f14b32
F fuel.pro 2de14a4a6bd3e5af9d9e7c78f870453fedb38577
F intl/convert.bat 357d461ee8c6a7be6d2f60ac77c3232678ffb513 x
F intl/convert.sh ac6edc8d99b575601cf9c6b85b18c9e25875b6e0 x
F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741
F intl/el_GR.ts 1b805ee57309d02059d9e3e4cb49d945f9d9ac82
F intl/en_US.ts 7917816efedf35d5f4f798d18896d7aa0cb3c71b
F intl/es_ES.ts fbddd3374f7999e602536de9870ae1fef0c28ea8
F intl/fr_FR.ts 8d2c00ab00927ee5239c1d273f380bbe4cfb2464
F intl/it_IT.ts 54b962cc19ce2e86268da67cea63db53beb751af
F intl/ko_KR.ts fecec68593af8a862f032c0361eaf94a1737736f
F intl/nl_NL.ts ff9b6ae9da5b6ffacc74fc1075a14ad80ebc0429
F intl/pt_PT.ts f93bcc3df5447ab1d85407e1dec4cd68c03d2245
F intl/ru_RU.ts 74189b3ee2b30b0b47b2db5bd7c9935db84947fc
F intl/update.sh 39d4561630ba6681bb27e7beadc225a31469728f x
F rsrc/docs/Licenses.txt 4021c1b126d55c1630ae2b43a8b805f99e39a357
F rsrc/docs/Translators.txt caf04efd7391546adda7da73679156e3ff5d5fcd
F rsrc/fuel.desktop 43145556bc61f5a91b497c38a16aec44af271d29
F rsrc/fuel.rc 8e9ac966f283102c11a77cd7f936cdc09e09bd79
F rsrc/icons/Address\sBook-01.png ef2cec80ea5a559b72e8be4a344a1869fe69cbd8
@ -182,36 +210,57 @@ 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 21ae6205e27ac989001eb0edc075d7e405b992c8
F src/AboutDialog.cpp fc9e3ba03aa6cb145ace610d9b38a2de157551ba
F src/AboutDialog.h 269f3a0589067c08f19b542e4576b0ef58bc6ec5
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 c341622b01d493387d6e4928018b3392d92471e8
F src/CloneDialog.h 8813d91f893eb3eb86a4ea5e50f9a53a0ea07047
F src/CommitDialog.cpp 3d25ae2aa8af0ab417736a3f2d7f95a8dcb7480a
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 e3451ddd8f19f1b6f2d446d9390b336e493da197
F src/Fossil.h 7acbd4a9d43f6a11c183dbffd73b71d54a4c5108
F src/FslSettingsDialog.cpp e00907d493fba469e48a008aecda88426350b5ac
F src/FslSettingsDialog.h dfe2a61884a55a74cbb9206b6f6b482b979725e7
F src/LoggedProcess.cpp 2a1e5c94bc1e57c8984563e66c210e43a14dc60c
F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261
F src/MainWindow.cpp a228d45fde194eace0cb7833d3da8757b49142ab
F src/MainWindow.h dc0a9ed7de8a338e56c38c00ec303796f31bd24d
F src/SettingsDialog.cpp a46cff5e5dd425e3dbdd15632abfd5829f5562b4
F src/SettingsDialog.h 4e2790f581e991c744ae9f86580f1972b8c7ff43
F src/Utils.cpp 9aff456712e4276b49083426301b3b96d3819c77
F src/Utils.h c546e478a1225a28c99cd4c30f70cf9be9804a2a
F src/main.cpp 0bba433f16072096cba1d48733b4e801df144800
F src/MainWindow.cpp 48ed62679d02f918479cd1610309443cd15d57ae
F src/MainWindow.h f4cffbe4d360d30aa2eeaa25fc6d50d0a39c617f
F src/RemoteDialog.cpp 8540cc5e2e41c4127ed8a028d84691604fa6ecac
F src/RemoteDialog.h 5e0438c2bd7c79b1bb44bfbd58c2181b544a9e5d
F src/RevisionDialog.cpp e58c4f8a704f00addebb15d521b76620fdafda79
F src/RevisionDialog.h b718c3009342eaabad39c8a11a253a4e4fef7a73
F src/SearchBox.cpp d4209c575baa9933e1ce5ed376e785b289a145ba
F src/SearchBox.h 0c78d3a68136dab3e0e71b83ae36f22bd2688ab2
F src/Settings.cpp 258d3f466f6a125ce2b8519d6d57a312cbc44a3f
F src/Settings.h 0a10b0b83fe804bdc7dac58eed06b5b6ee422055
F src/SettingsDialog.cpp fa0c70eaf0fa7edb15de302d041cdb552fe523d5
F src/SettingsDialog.h 5eb3ae2cbb00ab5544e1889860f5376f69fe47cd
F src/Utils.cpp cfadf3ee981f5ead8635c395610421f8e04a9a42
F src/Utils.h c6341ee49a8fc35f215facb196d70bf9b1f2fc0f
F src/Workspace.cpp c46aeb616712e42dcee72774a572af3a9803dbf1
F src/Workspace.h 54eef32658b13a34fe78ae26887420e8ff358eaa
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/AboutDialog.ui 77704a7422a59ccdddbdf00979bd20a861d9ee5a
F ui/BrowserWidget.ui 994ad9ea0e9f5815d6b1a27acc2f6f39164c507f
F ui/CloneDialog.ui 4886e7d4f258ea8b852b5eefc860396e35145712
F ui/CommitDialog.ui 6200f6cabdcf40a20812e811be28e0793f82516f
F ui/CommitDialog.ui 1e5dafa742e9ae07ec937bcda8cda3297ddc6199
F ui/FileActionDialog.ui 89bb4dc2d0b8adcd41adcb11ec65f2028a09a12d
F ui/MainWindow.ui 7ede8bbb54513e0771fdf5d5a2566d88c81b73ad
F ui/SettingsDialog.ui 2b7c2870e0054b0f4106f495d85d02c0b814df8b
P ca68fe622ce88483d769d78281f7e98ed0f70e9e
R 0d1dd353890d2cf810a406f276a6938b
F ui/FslSettingsDialog.ui eb3d4cb764cab90b01e82922237d8c42d6ce1749
F ui/MainWindow.ui e2a18caa7482b3ee0dff477592cdc9574b35fe4f
F ui/RemoteDialog.ui 95a4750d972ed8c49bb10b95db91ff16cfe2dd0b
F ui/RevisionDialog.ui 27c3b98c665fec014a50cbf3352c0627f75e68cd
F ui/SettingsDialog.ui 2e1b6ce7a49100088c5649292c1319e62e0302e1
P cb27c4e2a0777b5c3fa64a07d19cd6b89e05c76f
R 19ead9c938bef0606914a30fe37378b3
U kostas
Z a91ed61381f99fa788c67281b0b4d5ce
Z 6b9bc678ab29fb68e3f8be6bb65cb34f

View File

@ -1 +1 @@
f1c343a9ee2e43baf5ba2bc5c2e34a8dd7215ac9
7988e53166771ea71d916a1ab714eb671d07dea8

28
rsrc/docs/Licenses.txt Normal file
View File

@ -0,0 +1,28 @@
Icon Theme by Deleket - Jojo Mendoza
Available under the CC Attribution Noncommercial No Derivative 3.0 License
---------------------------------------
QtKeychain
https://github.com/frankosterfeld/qtkeychain
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------

10
rsrc/docs/Translators.txt Normal file
View File

@ -0,0 +1,10 @@
stayawake: de_DE
djnavas: es_ES
Fringale: fr_FR
mouse166: ru_RU
emansije: pt_PT
maxxlupi: it_IT
Zangune: it_IT
Fly Man: nl_NL
Rick Van Lieshout: nl_NL
ardiefox: ko_KR

View File

@ -4,37 +4,40 @@
<file>icons/Adobe Illustrator CS3 Document-01.png</file>
<file>icons/Adobe PDF Document-01.png</file>
<file>icons/Adobe Photoshop CS3 Document-01.png</file>
<file>icons/Battery-01.png</file>
<file alias="icon-application">icons/Battery-01.png</file>
<file>icons/Binoculars-01.png</file>
<file>icons/Book-01.png</file>
<file alias="icon-item-tag">icons/Book-01.png</file>
<file>icons/Briefcase-01.png</file>
<file>icons/Button Add-01.png</file>
<file alias="icon-item-added">icons/Button Add-01.png</file>
<file>icons/Button Blank Blue-01.png</file>
<file>icons/Button Blank Gray-01.png</file>
<file>icons/Button Blank Green-01.png</file>
<file>icons/Button Blank Red-01.png</file>
<file>icons/Button Blank Yellow-01.png</file>
<file alias="icon-item-unknown">icons/Button Blank Gray-01.png</file>
<file alias="icon-item-unchanged">icons/Button Blank Green-01.png</file>
<file alias="icon-item-conflicted">icons/Button Blank Red-01.png</file>
<file alias="icon-item-edited">icons/Button Blank Yellow-01.png</file>
<file>icons/Button Cancel-01.png</file>
<file>icons/Button Close-01.png</file>
<file alias="icon-item-deleted">icons/Button Close-01.png</file>
<file alias="icon-action-tag-delete">icons/Button Close-01.png</file>
<file alias="icon-action-stop">icons/Button Close-01.png</file>
<file>icons/Button Delete-01.png</file>
<file>icons/Button Download-01.png</file>
<file alias="icon-action-pull">icons/Button Download-01.png</file>
<file>icons/Button Favorite-01.png</file>
<file>icons/Button Forward-01.png</file>
<file>icons/Button Help-01.png</file>
<file alias="icon-item-missing">icons/Button Help-01.png</file>
<file>icons/Button Info-01.png</file>
<file>icons/Button Log Off-01.png</file>
<file>icons/Button Next-01.png</file>
<file alias="icon-action-next">icons/Button Next-01.png</file>
<file>icons/Button Pause-01.png</file>
<file>icons/Button Play-01.png</file>
<file>icons/Button Previous-01.png</file>
<file>icons/Button Refresh-01.png</file>
<file>icons/Button Reload-01.png</file>
<file alias="icon-action-update">icons/Button Play-01.png</file>
<file alias="icon-action-previous">icons/Button Previous-01.png</file>
<file alias="icon-action-refresh">icons/Button Refresh-01.png</file>
<file alias="icon-action-undo">icons/Button Reload-01.png</file>
<file alias="icon-item-renamed">icons/Button Reload-01.png</file>
<file>icons/Button Reminder-01.png</file>
<file>icons/Button Rewind-01.png</file>
<file alias="icon-action-revert">icons/Button Rewind-01.png</file>
<file>icons/Button Talk Balloon-01.png</file>
<file>icons/Button Turn Off-01.png</file>
<file alias="icon-action-quit">icons/Button Turn Off-01.png</file>
<file>icons/Button Turn On-01.png</file>
<file>icons/Button Upload-01.png</file>
<file alias="icon-action-push">icons/Button Upload-01.png</file>
<file>icons/Button Warning-01.png</file>
<file>icons/Calculator-01.png</file>
<file>icons/Calendar Blue-01.png</file>
@ -42,28 +45,28 @@
<file>icons/Calendar Red-01.png</file>
<file>icons/Clipboard-01.png</file>
<file>icons/Clipboard Paste-01.png</file>
<file>icons/Clock-01.png</file>
<file alias="icon-action-timeline">icons/Clock-01.png</file>
<file>icons/Coin-01.png</file>
<file>icons/Compressed File RAR-01.png</file>
<file>icons/Compressed File SIT-01.png</file>
<file>icons/Compressed File Zip-01.png</file>
<file>icons/Computer Monitor-01.png</file>
<file>icons/Computer Network-01.png</file>
<file>icons/Document-01.png</file>
<file alias="icon-item-file">icons/Document-01.png</file>
<file>icons/Document Attach-01.png</file>
<file>icons/Document Blank-01.png</file>
<file alias="icon-action-repo-new">icons/Document Blank-01.png</file>
<file>icons/Document Chart-01.png</file>
<file>icons/Document Copy-01.png</file>
<file>icons/Document Flow Chart-01.png</file>
<file alias="icon-item-diff">icons/Document Copy-01.png</file>
<file alias="icon-action-merge">icons/Document Flow Chart-01.png</file>
<file>icons/Document Gant Chart-01.png</file>
<file>icons/Document Help-01.png</file>
<file>icons/Document Line Chart-01.png</file>
<file>icons/Document Microsoft Excel-01.png</file>
<file>icons/Document Microsoft PowerPoint-01.png</file>
<file>icons/Document Microsoft Word-01.png</file>
<file>icons/Document Organization Chart-01.png</file>
<file alias="icon-item-branch">icons/Document Organization Chart-01.png</file>
<file>icons/Document Preview-01.png</file>
<file>icons/Document-Revert-icon.png</file>
<file alias="icon-item-revert">icons/Document-Revert-icon.png</file>
<file>icons/Document Text-01.png</file>
<file>icons/Edit Document-01.png</file>
<file>icons/Email-01.png</file>
@ -78,32 +81,33 @@
<file>icons/File Audio MP3-01.png</file>
<file>icons/File Audio WAV-01.png</file>
<file>icons/File Audio WMA-01.png</file>
<file>icons/File Delete-01.png</file>
<file>icons/File History-01.png</file>
<file>icons/File New-01.png</file>
<file>icons/File Open-01.png</file>
<file alias="icon-item-delete">icons/File Delete-01.png</file>
<file alias="icon-item-history">icons/File History-01.png</file>
<file alias="icon-item-add">icons/File New-01.png</file>
<file alias="icon-item-rename">icons/File Open-01.png</file>
<file>icons/File Video 3GP-01.png</file>
<file>icons/File Video-01.png</file>
<file>icons/File Video AVI-01.png</file>
<file>icons/File Video MOV-01.png</file>
<file>icons/File Video MPEG-01.png</file>
<file>icons/File Video WMV-01.png</file>
<file>icons/Folder-01.png</file>
<file>icons/Folder Add-01.png</file>
<file>icons/Folder Compressed-01.png</file>
<file>icons/Folder Delete-01.png</file>
<file>icons/Folder Explorer-01.png</file>
<file alias="icon-item-folder">icons/Folder-01.png</file>
<file alias="icon-action-stash-new">icons/Folder Add-01.png</file>
<file alias="icon-action-stash-apply">icons/Folder Compressed-01.png</file>
<file alias="icon-action-stash-delete">icons/Folder Delete-01.png</file>
<file alias="icon-action-stash-diff">icons/Folder Explorer-01.png</file>
<file alias="icon-action-folder-explore">icons/Folder Explorer-01.png</file>
<file>icons/Folder Generic Blue-01.png</file>
<file>icons/Folder Generic Green-01.png</file>
<file>icons/Folder Generic Red-01.png</file>
<file>icons/Folder Generic Silver-01.png</file>
<file>icons/Folder Open-01.png</file>
<file alias="icon-item-folder-unchanged">icons/Folder Generic Green-01.png</file>
<file alias="icon-item-folder-modified">icons/Folder Generic Red-01.png</file>
<file alias="icon-item-folder-unknown">icons/Folder Generic Silver-01.png</file>
<file alias="icon-action-folder-rename">icons/Folder Open-01.png</file>
<file>icons/Folder RAR-01.png</file>
<file>icons/Games-01.png</file>
<file>icons/Gear-01.png</file>
<file alias="icon-action-settings">icons/Gear-01.png</file>
<file>icons/Highlighter Blue-01.png</file>
<file>icons/Highlighter Green-01.png</file>
<file>icons/Highlighter Yellow-01.png</file>
<file alias="icon-action-tag-new">icons/Highlighter Yellow-01.png</file>
<file>icons/Image BMP-01.png</file>
<file>icons/Image GIF-01.png</file>
<file>icons/Image JPEG-01.png</file>
@ -111,21 +115,21 @@
<file>icons/Image TIFF-01.png</file>
<file>icons/Lock Lock-01.png</file>
<file>icons/Lock Unlock-01.png</file>
<file>icons/My Documents-01.png</file>
<file alias="icon-action-repo-open">icons/My Documents-01.png</file>
<file>icons/My Ebooks-01.png</file>
<file>icons/My Music-01.png</file>
<file>icons/My Pictures.png</file>
<file>icons/My Videos-01.png</file>
<file>icons/My Websites-01.png</file>
<file alias="icon-action-repo-clone">icons/My Websites-01.png</file>
<file>icons/Network Firewall-01.png</file>
<file>icons/Network MAC-01.png</file>
<file>icons/Network PC-01.png</file>
<file alias="icon-webview">icons/Network MAC-01.png</file>
<file alias="icon-item-remote" >icons/Network PC-01.png</file>
<file>icons/Network Refresh-01.png</file>
<file>icons/Pen Blue-01.png</file>
<file>icons/Pen Green-01.png</file>
<file>icons/Pen Red-01.png</file>
<file>icons/Save-01.png</file>
<file>icons/Text Edit.png</file>
<file alias="icon-action-commit">icons/Save-01.png</file>
<file alias="icon-clear-log">icons/Text Edit.png</file>
<file>icons/USB-01.png</file>
<file>icons/User Administrator Blue-01.png</file>
<file>icons/User Administrator Green-01.png</file>
@ -159,5 +163,12 @@
<file>intl/fr_FR.qm</file>
<file>intl/ru_RU.qm</file>
<file>intl/pt_PT.qm</file>
<file>intl/it_IT.qm</file>
<file>intl/nl_NL.qm</file>
<file>intl/ko_KR.qm</file>
</qresource>
<qresource prefix="/docs">
<file>docs/Translators.txt</file>
<file>docs/Licenses.txt</file>
</qresource>
</RCC>

41
src/AboutDialog.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "AboutDialog.h"
#include "ui_AboutDialog.h"
#include <QFile>
AboutDialog::AboutDialog(QWidget *parent, const QString &fossilVersion) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
QString banner(QCoreApplication::applicationName() + " " + QCoreApplication::applicationVersion());
ui->lblBanner->setText(banner + "\n" + ui->lblBanner->text());
ui->lblQtVersion->setText(tr("QT version %0").arg(QT_VERSION_STR));
if(!fossilVersion.isEmpty())
ui->lblFossilVersion->setText(tr("Fossil version %0").arg(fossilVersion));
QString additional;
QFile ftrans(":/docs/docs/Translators.txt");
if(ftrans.open(QFile::ReadOnly))
{
additional.append(tr("Translations with the help of:")+"\n");
additional.append(ftrans.readAll());
additional.append("\n\n");
ftrans.close();
}
QFile flicenses(":/docs/docs/Licenses.txt");
if(flicenses.open(QFile::ReadOnly))
{
additional.append(tr("This sofware uses the following open-source libraries and assets:")+"\n");
additional.append(flicenses.readAll());
flicenses.close();
}
ui->txtAdditional->setText(additional);
}
AboutDialog::~AboutDialog()
{
delete ui;
}

22
src/AboutDialog.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent, const QString &fossilVersion);
~AboutDialog();
private:
Ui::AboutDialog *ui;
};
#endif // ABOUTDIALOG_H

View File

@ -5,6 +5,7 @@
#include <QMessageBox>
#include <QClipboard>
#include <QUrl>
#include "Utils.h"
//-----------------------------------------------------------------------------
CloneDialog::CloneDialog(QWidget *parent) :
@ -39,7 +40,7 @@ bool CloneDialog::run(QWidget *parent, QUrl &url, QString &repository, QUrl &url
dlg.ui->linePassword->setText(nurl.password());
nurl.setUserName("");
nurl.setPassword("");
dlg.ui->lineURL->setText(nurl.toString());
dlg.ui->lineURL->setText(UrlToStringNoCredentials(nurl));
}
}
@ -90,6 +91,11 @@ void CloneDialog::GetRepositoryPath(QString &pathResult)
filter,
&filter,
QFileDialog::DontConfirmOverwrite);
// Ensure that it ends in the required extension (On GTK, Qt doesn't seem to add it automatically)
QFileInfo fi(pathResult);
if(fi.suffix().toLower() != ("." FOSSIL_EXT))
pathResult += "." FOSSIL_EXT;
}
//-----------------------------------------------------------------------------

View File

@ -3,10 +3,6 @@
#include <QDialog>
#define FOSSIL_CHECKOUT1 "_FOSSIL_"
#define FOSSIL_CHECKOUT2 ".fslckout"
#define FOSSIL_EXT "fossil"
namespace Ui {
class CloneDialog;
}

View File

@ -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,17 +74,13 @@ 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();
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; i<dlg.itemModel.rowCount(); ++i)
{
QStandardItem *si = dlg.itemModel.item(i);
if(si->checkState()!=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,11 @@ void CommitDialog::on_listView_clicked(const QModelIndex &)
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(num_selected>0);
}
//------------------------------------------------------------------------------
void CommitDialog::on_chkNewBranch_toggled(bool checked)
{
ui->lblPrivateBranch->setEnabled(checked);
ui->chkPrivateBranch->setEnabled(checked);
ui->lineBranchName->setEnabled(checked);
}

View File

@ -13,15 +13,17 @@ class CommitDialog : public QDialog
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);
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;

1175
src/Fossil.cpp Normal file

File diff suppressed because it is too large Load Diff

152
src/Fossil.h Normal file
View File

@ -0,0 +1,152 @@
#ifndef FOSSIL_H
#define FOSSIL_H
class QStringList;
#include <QString>
#include <QStringList>
#include <QUrl>
#include "LoggedProcess.h"
#include "Utils.h"
typedef QMap<QString, QString> stashmap_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,
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();
workspacePath.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 setWorkspacePath(const QString &workspace)
{
workspacePath = workspace;
}
const QString &getWorkspacePath() const
{
return workspacePath;
}
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(const QUrl& url);
bool pullRepository(const QUrl& url);
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 graphical);
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 QUrl& url);
bool getRemoteUrl(QUrl &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 &getActiveTags() const { return activeTags; }
const QString &getUIHttpPort() const { return fossilUIPort; }
QString getUIHttpAddress() const;
void setExecutablePath(const QString &path) { fossilPath = path; }
private:
void log(const QString &text, bool isHTML=false)
{
if(uiCallback)
uiCallback->logText(text, isHTML);
}
QString getFossilPath();
bool operationAborted;
UICallback *uiCallback;
QString workspacePath;
QString fossilPath; // The value from the settings
QString repositoryFile;
QString projectName;
QString currentRevision;
QStringList activeTags;
LoggedProcess fossilUI;
QString fossilUIPort;
};
#endif // FOSSIL_H

70
src/FslSettingsDialog.cpp Normal file
View File

@ -0,0 +1,70 @@
#include "FslSettingsDialog.h"
#include "ui_FslSettingsDialog.h"
#include "Utils.h"
#include <QDir>
///////////////////////////////////////////////////////////////////////////////
FslSettingsDialog::FslSettingsDialog(QWidget *parent, Settings &_settings) :
QDialog(parent, Qt::Sheet),
ui(new Ui::FslSettingsDialog),
settings(&_settings)
{
ui->setupUi(this);
ui->lineUIPort->setText(settings->GetFossilValue(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->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->SetFossilValue(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_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);
}

33
src/FslSettingsDialog.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef FSLSETTINGSDIALOG_H
#define FSLSETTINGSDIALOG_H
#include <QDialog>
#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

File diff suppressed because it is too large Load Diff

View File

@ -2,113 +2,15 @@
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
#include <QStringList>
#include <QMap>
#include <QFileInfo>
#include <QDir>
#include <QProcess>
#include <QSet>
#include "SettingsDialog.h"
#include <QFileIconProvider>
#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
//////////////////////////////////////////////////////////////////////////
@ -119,66 +21,47 @@ 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<QString> 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();
const QString &getCurrentWorkspace();
void updateRevision(const QString& revision);
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 getSelectionRemotes(QStringList& remoteUrls);
void getAllFilenames(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL);
bool startUI();
void stopUI();
void enableActions(bool on);
void addWorkspace(const QString &dir);
void addWorkspaceHistory(const QString &dir);
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 updateCustomActions();
void invokeCustomAction(int actionId);
void fossilBrowse(const QString &fossilUrl);
void dragEnterEvent(class QDragEnterEvent *event);
void dropEvent(class QDropEvent *event);
void setBusy(bool busy);
virtual QMenu *createPopupMenu();
enum RepoStatus
{
REPO_OK,
REPO_NOT_FOUND,
REPO_OLD_SCHEMA
};
RepoStatus getRepoStatus();
const QIcon& getCachedIcon(const char *name);
const QIcon& getCachedFileIcon(const QFileInfo &finfo);
enum ViewMode
{
@ -190,9 +73,12 @@ 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();
void onCustomActionTriggered();
// Designer slots
void on_actionRefresh_triggered();
@ -202,11 +88,13 @@ 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();
void on_actionPushRemote_triggered();
void on_actionPullRemote_triggered();
void on_actionCommit_triggered();
void on_actionAdd_triggered();
void on_actionDelete_triggered();
@ -217,57 +105,105 @@ 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_actionViewModifedOnly_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();
void on_actionEditRemote_triggered();
void on_actionSetDefaultRemote_triggered();
void on_actionAddRemote_triggered();
void on_actionDeleteRemote_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
};
typedef QMap<QString, QIcon> icon_map_t;
Ui::MainWindow *ui;
QStandardItemModel repoFileModel;
QStandardItemModel repoDirModel;
QStandardItemModel repoStashModel;
QProcess fossilUI;
QFileIconProvider iconProvider;
icon_map_t iconCache;
class QAction *recentWorkspaceActs[MAX_RECENT];
class QAction *customActions[MAX_CUSTOM_ACTIONS];
class QAction *fileActionSeparator;
class QAction *workspaceActionSeparator;
class QProgressBar *progressBar;
class QLabel *lblTags;
class QShortcut *abortShortcut;
bool abortOperation;
class SearchBox *searchBox;
class QShortcut *searchShortcut;
QMenu *menuWorkspace;
QMenu *menuStashes;
QMenu *menuTags;
QMenu *menuBranches;
QMenu *menuRemotes;
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<RepoFile*> filelist_t;
typedef QMap<QString, RepoFile*> filemap_t;
typedef QMap<QString, QString> stashmap_t;
filemap_t workspaceFiles;
stringset_t pathSet;
stashmap_t stashMap;
MainWinUICallback uiCallback;
ViewMode viewMode;
};
#endif // MAINWINDOW_H

95
src/RemoteDialog.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "RemoteDialog.h"
#include "ui_RemoteDialog.h"
#include <QFileDialog>
#include <QDir>
#include <QMessageBox>
#include <QClipboard>
#include <QUrl>
#include "Utils.h"
//-----------------------------------------------------------------------------
RemoteDialog::RemoteDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RemoteDialog)
{
ui->setupUi(this);
}
//-----------------------------------------------------------------------------
RemoteDialog::~RemoteDialog()
{
delete ui;
}
//-----------------------------------------------------------------------------
bool RemoteDialog::run(QWidget *parent, QUrl &url, QString &name)
{
RemoteDialog dlg(parent);
// Set URL components
if(!url.isEmpty())
{
QString url_no_credentials = UrlToStringNoCredentials(url);
dlg.ui->lineURL->setText(url_no_credentials);
dlg.ui->lineUserName->setText(url.userName());
dlg.ui->linePassword->setText(url.password());
dlg.ui->lineName->setText(name);
}
if(dlg.exec() != QDialog::Accepted)
return false;
QString urltext = dlg.ui->lineURL->text();
url = QUrl::fromUserInput(urltext);
if(url.isEmpty() || !url.isValid())
{
QMessageBox::critical(parent, tr("Error"), tr("Invalid URL."), QMessageBox::Ok );
return false;
}
if(!dlg.ui->lineUserName->text().trimmed().isEmpty())
url.setUserName(dlg.ui->lineUserName->text());
if(!dlg.ui->linePassword->text().trimmed().isEmpty())
url.setPassword(dlg.ui->linePassword->text());
name =dlg.ui->lineName->text().trimmed();
if(name.isEmpty())
name = UrlToStringNoCredentials(url);
return true;
}
//-----------------------------------------------------------------------------
void RemoteDialog::GetRepositoryPath(QString &pathResult)
{
QString filter(tr("Fossil Repository") + QString(" (*." FOSSIL_EXT ")"));
pathResult = QFileDialog::getSaveFileName(
this,
tr("Select Fossil Repository"),
QDir::toNativeSeparators(pathResult),
filter,
&filter,
QFileDialog::DontConfirmOverwrite);
}
//-----------------------------------------------------------------------------
void RemoteDialog::on_btnSelectSourceRepo_clicked()
{
QString path = ui->lineURL->text();
GetRepositoryPath(path);
if(path.trimmed().isEmpty())
return;
if(!QFile::exists(path))
{
QMessageBox::critical(this, tr("Error"), tr("Invalid Repository File."), QMessageBox::Ok);
return;
}
ui->lineURL->setText(QDir::toNativeSeparators(path));
}

29
src/RemoteDialog.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef REMOTEDIALOG_H
#define REMOTEDIALOG_H
#include <QDialog>
namespace Ui {
class RemoteDialog;
}
class RemoteDialog : public QDialog
{
Q_OBJECT
public:
explicit RemoteDialog(QWidget *parent = 0);
~RemoteDialog();
static bool run(QWidget *parent, class QUrl &url, QString &name);
private slots:
void on_btnSelectSourceRepo_clicked();
private:
void GetRepositoryPath(QString &pathResult);
Ui::RemoteDialog *ui;
};
#endif // REMOTEDIALOG_H

100
src/RevisionDialog.cpp Normal file
View File

@ -0,0 +1,100 @@
#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())
{
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
int index = ui->cmbRevision->findText(defaultValue);
if(index>=0)
ui->cmbRevision->setCurrentIndex(index);
#else
ui->cmbRevision->setCurrentText(defaultValue);
#endif
}
}
//-----------------------------------------------------------------------------
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;
}

28
src/RevisionDialog.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef REVISIONDIALOG_H
#define REVISIONDIALOG_H
#include <QDialog>
#include <QCompleter>
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

24
src/SearchBox.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "SearchBox.h"
#include <QKeyEvent>
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);
}

23
src/SearchBox.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef SEARCHBOX_H
#define SEARCHBOX_H
#include <QLineEdit>
class SearchBox : public QLineEdit
{
Q_OBJECT
public:
explicit SearchBox(QWidget* parent=0);
~SearchBox();
signals:
public slots:
protected:
void keyPressEvent(QKeyEvent *event);
};
#endif // SEARCHBOX_H

131
src/Settings.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "Settings.h"
#include <QSettings>
#include <QCoreApplication>
#include <QDir>
#include <QTranslator>
#include <QResource>
#include <QTextCodec>
///////////////////////////////////////////////////////////////////////////////
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));
// 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);
for(int i=0; i<MAX_CUSTOM_ACTIONS; ++i)
{
CustomAction action;
action.Id = QObject::tr("Custom Action %0").arg(i+1);
customActions.append(action);
}
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();
}

126
src/Settings.h Normal file
View File

@ -0,0 +1,126 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QMap>
#include <QVariant>
#include <QTranslator>
#include <QVector>
#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_HTTP_PORT "http-port"
enum FileDblClickAction
{
FILE_DLBCLICK_ACTION_DIFF,
FILE_DLBCLICK_ACTION_OPEN,
FILE_DLBCLICK_ACTION_OPENCONTAINING,
FILE_DLBCLICK_ACTION_CUSTOM, // Custom Action 1
FILE_DLBCLICK_ACTION_MAX
};
enum CustomActionContext
{
ACTION_CONTEXT_FILES = 1 << 0,
ACTION_CONTEXT_FOLDERS = 1 << 1,
ACTION_CONTEXT_FILES_AND_FOLDERS = ACTION_CONTEXT_FILES|ACTION_CONTEXT_FOLDERS
};
enum
{
MAX_CUSTOM_ACTIONS = 9
};
struct CustomAction
{
QString Id;
QString Description;
QString Command;
CustomActionContext Context;
bool MultipleSelection;
CustomAction()
{
Clear();
}
bool IsValid() const
{
return !(Description.isEmpty() || Command.isEmpty());
}
bool IsActive(CustomActionContext context) const
{
return (Context & context) != 0;
}
void Clear()
{
Description.clear();
Command.clear();
Context = ACTION_CONTEXT_FILES;
MultipleSelection = true;
}
};
struct Settings
{
struct Setting
{
enum SettingType
{
TYPE_FOSSIL_GLOBAL,
TYPE_FOSSIL_LOCAL
};
Setting(QVariant value, SettingType type) : Value(value), Type(type)
{}
QVariant Value;
SettingType Type;
};
typedef QMap<QString, Setting> mappings_t;
typedef QVector<CustomAction> custom_actions_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);
custom_actions_t &GetCustomActions() { return customActions; }
private:
mappings_t Mappings;
class QSettings *store;
QTranslator translator;
custom_actions_t customActions;
};
#endif // SETTINGS_H

View File

@ -1,37 +1,8 @@
#include "SettingsDialog.h"
#include "ui_SettingsDialog.h"
#include <QFileDialog>
#include "Utils.h"
#include <QSettings>
#include <QCoreApplication>
#include <QDir>
#include <QTranslator>
#include <QResource>
#include <QTextCodec>
///////////////////////////////////////////////////////////////////////////////
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) :
@ -46,6 +17,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, Settings &_settings) :
ui->cmbDoubleClickAction->addItem(tr("Diff File"));
ui->cmbDoubleClickAction->addItem(tr("Open File"));
ui->cmbDoubleClickAction->addItem(tr("Open Containing Folder"));
ui->cmbDoubleClickAction->addItem(tr("Custom Action %0").arg(1));
ui->cmbFossilBrowser->addItem(tr("System"));
ui->cmbFossilBrowser->addItem(tr("Internal"));
@ -54,7 +26,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 +37,22 @@ 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());
lastActionIndex = 0;
currentCustomActions = settings->GetCustomActions();
ui->cmbCustomActionContext->addItem(tr("Files"));
ui->cmbCustomActionContext->addItem(tr("Folders"));
ui->cmbCustomActionContext->setCurrentIndex(0);
GetCustomAction(0);
for(int i=0; i<currentCustomActions.size(); ++i)
{
CustomAction &a = currentCustomActions[i];
ui->cmbCustomAction->addItem(a.Id);
}
ui->cmbCustomAction->setCurrentIndex(0);
}
//-----------------------------------------------------------------------------
@ -97,7 +75,6 @@ void SettingsDialog::on_buttonBox_accepted()
Q_ASSERT(ui->cmbDoubleClickAction->currentIndex()>=FILE_DLBCLICK_ACTION_DIFF && ui->cmbDoubleClickAction->currentIndex()<FILE_DLBCLICK_ACTION_MAX);
settings->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,14 +85,16 @@ 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());
for(int i=0; i<currentCustomActions.size(); ++i)
{
CustomAction &a = currentCustomActions[i];
a.Description = a.Description.trimmed();
a.Command = a.Command.trimmed();
}
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());
PutCustomAction(ui->cmbCustomAction->currentIndex());
settings->GetCustomActions() = currentCustomActions;
settings->ApplyEnvironment();
}
@ -128,22 +107,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()
{
@ -154,13 +117,16 @@ void SettingsDialog::on_btnClearMessageHistory_clicked()
//-----------------------------------------------------------------------------
void SettingsDialog::CreateLangMap()
{
langMap.append(LangMap("de_DE", "German (DE)"));
langMap.append(LangMap("el_GR", "Greek"));
langMap.append(LangMap("nl_NL", "Dutch (NL)"));
langMap.append(LangMap("en_US", "English (US)"));
langMap.append(LangMap("es_ES", "Spanish (ES)"));
langMap.append(LangMap("fr_FR", "French (FR)"));
langMap.append(LangMap("ru_RU", "Russian (RU)"));
langMap.append(LangMap("de_DE", "German (DE)"));
langMap.append(LangMap("el_GR", "Greek (GR)"));
langMap.append(LangMap("it_IT", "Italian (IT)"));
langMap.append(LangMap("ko_KR", "Korean (KO)"));
langMap.append(LangMap("pt_PT", "Portuguese (PT)"));
langMap.append(LangMap("ru_RU", "Russian (RU)"));
langMap.append(LangMap("es_ES", "Spanish (ES)"));
}
//-----------------------------------------------------------------------------
@ -187,119 +153,58 @@ QString SettingsDialog::LangNameToId(const QString &name)
return "";
}
///////////////////////////////////////////////////////////////////////////////
Settings::Settings(bool portableMode) : store(0)
//-----------------------------------------------------------------------------
void SettingsDialog::on_btnSelectCustomFileActionCommand_clicked()
{
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));
QString path = SelectExe(this, tr("Select command"));
if(path.isEmpty())
return;
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));
// Quote path if it contains spaces
if(path.indexOf(' ')!=-1)
path = '"' + path + '"';
// 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();
ui->lineCustomActionCommand->setText(QDir::toNativeSeparators(path));
}
//-----------------------------------------------------------------------------
Settings::~Settings()
void SettingsDialog::GetCustomAction(int index)
{
Q_ASSERT(store);
delete store;
Q_ASSERT(index>=0 && index < currentCustomActions.size());
CustomAction &action = currentCustomActions[index];
ui->lineCustomActionDescription->setText(action.Description);
ui->lineCustomActionCommand->setText(action.Command);
ui->cmbCustomActionContext->setCurrentIndex(action.Context-1);
ui->chkCustomActionMultipleSelection->setChecked(action.MultipleSelection);
}
//-----------------------------------------------------------------------------
void Settings::ApplyEnvironment()
void SettingsDialog::PutCustomAction(int index)
{
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");
Q_ASSERT(index>=0 && index < currentCustomActions.size());
CustomAction &action = currentCustomActions[index];
action.Description = ui->lineCustomActionDescription->text().trimmed();
action.Command = ui->lineCustomActionCommand->text().trimmed();
action.Context = static_cast<CustomActionContext>(ui->cmbCustomActionContext->currentIndex()+1);
action.MultipleSelection = ui->chkCustomActionMultipleSelection->isChecked();
}
//-----------------------------------------------------------------------------
bool Settings::InstallLang(const QString &langId)
void SettingsDialog::on_cmbCustomAction_currentIndexChanged(int index)
{
if(langId == "en_US")
{
QCoreApplication::instance()->removeTranslator(&translator);
return true;
}
if(index != lastActionIndex)
PutCustomAction(lastActionIndex);
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;
GetCustomAction(index);
lastActionIndex = index;
}
//-----------------------------------------------------------------------------
bool Settings::HasValue(const QString &name) const
void SettingsDialog::on_cmbCustomActionContext_currentIndexChanged(int index)
{
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();
int action_index = ui->cmbCustomAction->currentIndex();
if(action_index<0)
return;
Q_ASSERT(action_index>=0 && action_index < currentCustomActions.size());
currentCustomActions[action_index].Context = static_cast<CustomActionContext>(index+1);
}

View File

@ -2,82 +2,12 @@
#define SETTINGSDIALOG_H
#include <QDialog>
#include <QMap>
#include <QVariant>
#include <QTranslator>
#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<QString, Setting> 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,15 +22,17 @@ public:
private slots:
void on_btnSelectFossil_clicked();
void on_buttonBox_accepted();
void on_btnSelectFossilGDiff_clicked();
void on_btnSelectGMerge_clicked();
void on_btnClearMessageHistory_clicked();
void on_btnSelectCustomFileActionCommand_clicked();
void on_cmbCustomAction_currentIndexChanged(int index);
void on_cmbCustomActionContext_currentIndexChanged(int index);
private:
static QString SelectExe(QWidget *parent, const QString &description);
QString LangIdToName(const QString &id);
QString LangNameToId(const QString &name);
void CreateLangMap();
void GetCustomAction(int index);
void PutCustomAction(int index);
struct LangMap
{
@ -116,6 +48,8 @@ private:
QList<LangMap> langMap;
Ui::SettingsDialog *ui;
Settings *settings;
Settings::custom_actions_t currentCustomActions;
int lastActionIndex;
};
#endif // SETTINGSDIALOG_H

View File

@ -1,6 +1,12 @@
#include "Utils.h"
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QEventLoop>
#include <QUrl>
#include <QProcess>
#include <QCryptographicHash>
#include "ext/qtkeychain/keychain.h"
///////////////////////////////////////////////////////////////////////////////
QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons)
@ -14,6 +20,54 @@ 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<paths.size(); ++i)
res.append(QuotePath(paths[i]));
return res;
}
//-----------------------------------------------------------------------------
QString SelectExe(QWidget *parent, const QString &description)
{
QString filter(QObject::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();
#ifdef Q_OS_MACX
// Guess actual executable from bundle dir
QFileInfo fi(path);
if(fi.isDir() && path.indexOf(".app")!=-1)
{
path += "/Contents/MacOS/" + fi.baseName();
if(!QFile::exists(path))
return QString();
}
#endif
return path;
}
//-----------------------------------------------------------------------------
#if 0 // Unused
#include <QInputDialog>
@ -291,3 +345,294 @@ 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<item.rowCount(); ++i)
{
const QStandardItem *child = item.child(i);
Q_ASSERT(child);
BuildNameToModelIndex(map, *child);
}
}
//------------------------------------------------------------------------------
void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel &model)
{
for(int i=0; i<model.rowCount(); ++i)
{
const QStandardItem *item = model.item(i);
Q_ASSERT(item);
BuildNameToModelIndex(map, *item);
}
}
//------------------------------------------------------------------------------
bool KeychainSet(QObject *parent, const QUrl &url, QSettings &settings)
{
QEventLoop loop(parent);
QKeychain::WritePasswordJob job(UrlToStringNoCredentials(url));
#ifdef Q_OS_WIN
settings.beginGroup("Keychain");
job.setSettings(&settings);
#else
Q_UNUSED(settings)
#endif
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setKey(url.userName());
job.setTextData(url.password());
job.start();
loop.exec();
#ifdef Q_OS_WIN
settings.endGroup();
#endif
return job.error() == QKeychain::NoError;
}
//------------------------------------------------------------------------------
bool KeychainGet(QObject *parent, QUrl &url, QSettings &settings)
{
QEventLoop loop(parent);
QKeychain::ReadPasswordJob job(UrlToStringNoCredentials(url));
#ifdef Q_OS_WIN
settings.beginGroup("Keychain");
job.setSettings(&settings);
#else
Q_UNUSED(settings)
#endif
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setAutoDelete( false );
job.setKey(url.userName());
job.start();
loop.exec();
#ifdef Q_OS_WIN
settings.endGroup();
#endif
if(job.error() != QKeychain::NoError)
return false;
url.setUserName(job.key());
url.setPassword(job.textData());
return true;
}
//------------------------------------------------------------------------------
bool KeychainDelete(QObject* parent, const QUrl& url, QSettings &settings)
{
QEventLoop loop(parent);
QKeychain::DeletePasswordJob job(UrlToStringNoCredentials(url));
#ifdef Q_OS_WIN
settings.beginGroup("Keychain");
job.setSettings(&settings);
#else
Q_UNUSED(settings)
#endif
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setAutoDelete( false );
job.setKey(url.userName());
job.start();
loop.exec();
#ifdef Q_OS_WIN
settings.endGroup();
#endif
return job.error() == QKeychain::NoError;
}
//------------------------------------------------------------------------------
QString HashString(const QString& str)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
const QByteArray ba(str.toUtf8());
hash.addData(ba.data(), ba.size());
QString str_out(hash.result().toHex());
return str_out;
}
//------------------------------------------------------------------------------
QString UrlToStringDisplay(const QUrl& url)
{
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
return url.toString(QUrl::RemovePassword);
#else
return url.toDisplayString();
#endif
}
//------------------------------------------------------------------------------
QString UrlToStringNoCredentials(const QUrl& url)
{
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
return url.toString(QUrl::RemoveUserInfo);
#else
return url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo);
#endif
}
//------------------------------------------------------------------------------
void SplitCommandLine(const QString &commandLine, QString &command, QString &extraParams)
{
// Process command string
command = commandLine.trimmed();
Q_ASSERT(!command.isEmpty());
// Split command from embedded params
extraParams.clear();
// Command ends after first space
QChar cmd_char_end = ' ';
int cmd_start = 0;
int backtrack = 0;
// ...unless it is a quoted command
if(command[0]=='"')
{
cmd_char_end = '"';
cmd_start = 1;
backtrack = 1;
}
int cmd_end = command.indexOf(cmd_char_end, cmd_start);
if(cmd_end > 0)
{
extraParams = command.mid(cmd_end+1);
command = command.mid(cmd_start, cmd_end-backtrack);
}
command = command.trimmed();
extraParams = extraParams.trimmed();
}
//------------------------------------------------------------------------------
bool SpawnExternalProcess(QObject *processParent, const QString& command, const QStringList& fileList, const stringset_t& pathSet, const QString &workspaceDir, UICallback &uiCallback)
{
static const char* MACRO_FILE = "%FILE";
static const char* MACRO_FOLDER = "%FOLDER";
static const char* MACRO_WORKSPACE = "%WORKSPACE";
QStringList params;
QString cmd, extra_params;
SplitCommandLine(command, cmd, extra_params);
// Push all additional params, except those containing macros
QString macro_file;
QString macro_folder;
if(!extra_params.isEmpty())
{
QStringList extra_param_list = extra_params.split(' ');
foreach(const QString &p, extra_param_list)
{
if(p.indexOf(MACRO_FILE)!=-1)
{
macro_file = p;
continue;
}
else if(p.indexOf(MACRO_FOLDER)!=-1)
{
macro_folder = p;
continue;
}
else if(p.indexOf(MACRO_WORKSPACE)!=-1)
{
// Add in-place
QString n = p;
n.replace(MACRO_WORKSPACE, QDir::toNativeSeparators(workspaceDir), Qt::CaseInsensitive);
params.push_back(n);
continue;
}
params.push_back(p);
}
}
// Build file params
if(!macro_file.isEmpty())
{
foreach(const QString &f, fileList)
{
QString path = QDir::toNativeSeparators(QFileInfo(workspaceDir + PATH_SEPARATOR + f).absoluteFilePath());
QString macro = macro_file;
path = macro.replace(MACRO_FILE, path, Qt::CaseInsensitive);
params.append(path);
}
}
// Build folder params
if(!macro_folder.isEmpty())
{
foreach(const QString &f, pathSet)
{
QString path = QDir::toNativeSeparators(QFileInfo(workspaceDir + PATH_SEPARATOR + f).absoluteFilePath());
QString macro = macro_folder;
path = macro.replace(MACRO_FOLDER, path, Qt::CaseInsensitive);
params.append(path);
}
}
uiCallback.logText("<b>"+cmd + " "+params.join(" ")+"</b><br>", true);
QProcess proc(processParent);
return proc.startDetached(cmd, params);
}

View File

@ -3,12 +3,73 @@
#include <QString>
#include <QMessageBox>
#include <QMap>
#include <QStandardItem>
#include <QSet>
#include <QSettings>
#define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
#define FOSSIL_CHECKOUT1 "_FOSSIL_"
#define FOSSIL_CHECKOUT2 ".fslckout"
#define FOSSIL_EXT "fossil"
#define PATH_SEPARATOR "/"
typedef QSet<QString> stringset_t;
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;
};
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<QString, QModelIndex> 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);
bool KeychainSet(QObject* parent, const QUrl& url, QSettings &settings);
bool KeychainGet(QObject* parent, QUrl& url, QSettings &settings);
bool KeychainDelete(QObject* parent, const QUrl& url, QSettings &settings);
QString HashString(const QString &str);
QString UrlToStringDisplay(const QUrl &url);
QString UrlToStringNoCredentials(const QUrl& url);
void SplitCommandLine(const QString &commandLine, QString &command, QString &extraParams);
bool SpawnExternalProcess(QObject *processParent, const QString& command, const QStringList& fileList, const stringset_t& pathSet, const QString &workspaceDir, UICallback &uiCallback);
typedef QMap<QString, QString> 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
#endif // UTILS_H

431
src/Workspace.cpp Normal file
View File

@ -0,0 +1,431 @@
#include "Workspace.h"
#include <QCoreApplication>
#include "Utils.h"
//-----------------------------------------------------------------------------
Workspace::Workspace()
{
}
//-----------------------------------------------------------------------------
Workspace::~Workspace()
{
clearState();
remotes.clear();
}
//------------------------------------------------------------------------------
void Workspace::clearState()
{
// Dispose RepoFiles
foreach(WorkspaceFile *r, getFiles())
delete r;
getFiles().clear();
getPaths().clear();
pathState.clear();
stashMap.clear();
branchList.clear();
tags.clear();
isIntegrated = false;
}
//------------------------------------------------------------------------------
void Workspace::storeWorkspace(QSettings &store)
{
QString workspace = fossil().getWorkspacePath();
if(workspace.isEmpty())
return;
store.beginGroup("Remotes");
QString workspace_hash = HashString(QDir::toNativeSeparators(workspace));
store.beginWriteArray(workspace_hash);
int index = 0;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it, ++index)
{
store.setArrayIndex(index);
store.setValue("Name", it->name);
QUrl url = it->url;
url.setPassword("");
store.setValue("Url", url);
if(it->isDefault)
store.setValue("Default", it->isDefault);
else
store.remove("Default");
}
store.endArray();
store.endGroup();
}
//------------------------------------------------------------------------------
bool Workspace::switchWorkspace(const QString& workspace, QSettings &store)
{
// Save Remotes
storeWorkspace(store);
clearState();
remotes.clear();
fossil().setWorkspacePath("");
if(workspace.isEmpty())
return true;
QString new_workspace = QFileInfo(workspace).absoluteFilePath();
if(!QDir::setCurrent(new_workspace))
return false;
fossil().setWorkspacePath(new_workspace);
// Load Remotes
QString workspace_hash = HashString(QDir::toNativeSeparators(new_workspace));
QString gr = store.group();
store.beginGroup("Remotes");
gr = store.group();
int num_remotes = store.beginReadArray(workspace_hash);
for(int i=0; i<num_remotes; ++i)
{
store.setArrayIndex(i);
QString name = store.value("Name").toString();
QUrl url = store.value("Url").toUrl();
bool def = store.value("Default", false).toBool();
addRemote(url, name);
if(def)
setRemoteDefault(url);
}
store.endArray();
store.endGroup();
#if 0 // FIXME: Disabled this because if fossil's remote does not match exactly what we have stored (url and username), it will be automatically added every-time
// Add the default url from fossil
QUrl default_remote;
if(fossil().getRemoteUrl(default_remote) && default_remote.isValid() && !default_remote.isEmpty())
{
default_remote.setPassword("");
// Add Default remote if not available already
if(findRemote(default_remote)==NULL)
{
addRemote(default_remote, UrlToStringDisplay(default_remote));
if(getRemoteDefault().isEmpty())
setRemoteDefault(default_remote);
}
}
#endif
return true;
}
//------------------------------------------------------------------------------
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; i<list.count(); ++i)
{
if(abort)
return false;
QFileInfo info = list[i];
QString filepath = info.filePath();
QString rel_path = filepath;
rel_path.remove(baseDir+PATH_SEPARATOR);
// Skip ignored files
if(!ignoreSpec.isEmpty() && QDir::match(ignoreSpec, rel_path))
continue;
if (info.isDir())
{
if(!scanDirectory(entries, filepath, baseDir, ignoreSpec, abort, uiCallback))
return false;
}
else
entries.push_back(info);
}
return true;
}
//------------------------------------------------------------------------------
static bool StringLengthDescending(const QString &l, const QString &r)
{
return l.length() > r.length();
}
//------------------------------------------------------------------------------
void Workspace::scanWorkspace(bool scanLocal, bool scanIgnored, bool scanModified, bool scanUnchanged, const QString &ignoreGlob, UICallback &uiCallback, bool &operationAborted)
{
// Scan all workspace files
QFileInfoList all_files;
QString wkdir = fossil().getWorkspacePath();
if(wkdir.isEmpty())
return;
// Retrieve the status of files tracked by fossil
QStringList res;
if(!fossil().listFiles(res))
return;
bool scan_files = scanLocal;
clearState();
QStringList paths;
operationAborted = false;
uiCallback.beginProcess("");
if(scan_files)
{
QCoreApplication::processEvents();
QString ignore;
// If we should not be showing ignored files, fill in the ignored spec
if(!scanIgnored)
{
// QDir expects multiple specs being separated by a semicolon
ignore = ignoreGlob;
ignore.replace(',',';');
}
if(!scanDirectory(all_files, wkdir, wkdir, ignore, operationAborted, uiCallback))
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 || (!fossil().getRepositoryFile().isEmpty() && QFileInfo(fullpath) == QFileInfo(fossil().getRepositoryFile())))
continue;
WorkspaceFile::Type type = WorkspaceFile::TYPE_UNKNOWN;
WorkspaceFile *rf = new WorkspaceFile(*it, type, wkdir);
const QString &path = rf->getPath();
getFiles().insert(rf->getFilePath(), rf);
getPaths().insert(path);
// Add or merge file state into directory state
pathstate_map_t::iterator state_it = pathState.find(path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | type);
else
{
pathState.insert(path, type);
paths.append(path); // keep path in list for depth sort
}
}
}
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);
// Add or merge file state into directory state
pathstate_map_t::iterator state_it = pathState.find(path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | type);
else
{
pathState.insert(path, type);
paths.append(path); // keep path in list for depth sort
}
}
// Sort paths, so that children (longer path) are before parents (shorter path)
std::sort(paths.begin(), paths.end(), StringLengthDescending);
foreach(const QString &p, paths)
{
pathstate_map_t::iterator state_it = pathState.find(p);
Q_ASSERT(state_it != pathState.end());
WorkspaceFile::Type state = state_it.value();
// Propagate child dir state to parents
QString parent_path = p;
while(!parent_path.isEmpty())
{
// Extract parent path
int sep_index = parent_path.lastIndexOf(PATH_SEPARATOR);
if(sep_index>=0)
parent_path = parent_path.left(sep_index);
else
parent_path = "";
// Merge path of child to parent
pathstate_map_t::iterator state_it = pathState.find(parent_path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | state);
else
pathState.insert(parent_path, state);
}
}
// 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();
}
//------------------------------------------------------------------------------
bool Workspace::addRemote(const QUrl& url, const QString& name)
{
if(remotes.contains(url))
return false;
Q_ASSERT(url.password().isEmpty());
Remote r(name, url);
remotes.insert(url, r);
return true;
}
//------------------------------------------------------------------------------
bool Workspace::removeRemote(const QUrl& url)
{
return remotes.remove(url) > 0;
}
//------------------------------------------------------------------------------
bool Workspace::setRemoteDefault(const QUrl& url)
{
Q_ASSERT(url.password().isEmpty());
bool found = false;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it)
{
if(it->url == url)
{
it->isDefault = true;
found = true;
}
else
it->isDefault = false;
}
return found;
}
//------------------------------------------------------------------------------
QUrl Workspace::getRemoteDefault() const
{
for(remote_map_t::const_iterator it=remotes.begin(); it!=remotes.end(); ++it)
{
if(it->isDefault)
return it->url;
}
return QUrl();
}
//------------------------------------------------------------------------------
Remote * Workspace::findRemote(const QUrl& url)
{
remote_map_t::iterator it = remotes.find(url);
if(it!=remotes.end())
return &(*it);
return NULL;
}

180
src/Workspace.h Normal file
View File

@ -0,0 +1,180 @@
#ifndef WORKSPACE_H
#define WORKSPACE_H
#include <QStandardItemModel>
#include <QFileInfo>
#include <QDir>
#include <QSet>
#include <QMap>
#include <QSettings>
#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;
};
class Remote
{
public:
Remote(const QString &_name, const QUrl &_url, bool _isDefault=false)
: name(_name), url(_url), isDefault(_isDefault)
{
}
QString name;
QUrl url;
bool isDefault;
};
typedef QMap<QUrl, Remote> remote_map_t;
typedef QMap<QString, WorkspaceFile::Type> pathstate_map_t;
//////////////////////////////////////////////////////////////////////////
// Workspace
//////////////////////////////////////////////////////////////////////////
class Workspace
{
public:
Workspace();
~Workspace();
typedef QList<WorkspaceFile*> filelist_t;
typedef QMap<QString, WorkspaceFile*> filemap_t;
void clearState();
Fossil & fossil() { return bridge; }
const Fossil & fossil() const { return bridge; }
const QString & getPath() const { return fossil().getWorkspacePath(); }
bool switchWorkspace(const QString &workspace, QSettings &store);
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; }
pathstate_map_t &getPathState() { return pathState; }
stashmap_t &getStashes() { return stashMap; }
QStringMap &getTags() { return tags; }
QStringList &getBranches() { return branchList; }
bool otherChanges() const { return isIntegrated; }
const QString &getCurrentRevision() const { return fossil().getCurrentRevision(); }
const QStringList &getActiveTags() const { return fossil().getActiveTags(); }
const QString &getProjectName() const { return fossil().getProjectName(); }
// Remotes
const remote_map_t &getRemotes() const { return remotes; }
bool addRemote(const QUrl &url, const QString &name);
bool removeRemote(const QUrl &url);
bool setRemoteDefault(const QUrl& url);
QUrl getRemoteDefault() const;
Remote * findRemote(const QUrl& url);
void storeWorkspace(QSettings &store);
private:
static bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort, UICallback &uiCallback);
private:
Fossil bridge;
filemap_t workspaceFiles;
stringset_t pathSet;
pathstate_map_t pathState;
stashmap_t stashMap;
QStringList branchList;
QStringMap tags;
remote_map_t remotes;
bool isIntegrated;
QStandardItemModel repoFileModel;
QStandardItemModel repoTreeModel;
};
#endif // WORKSPACE_H

View File

@ -5,7 +5,7 @@ int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setApplicationName("Fuel");
app.setApplicationVersion("1.0.0");
app.setApplicationVersion("2.0.0");
app.setOrganizationDomain("fuel-scm.org");
app.setOrganizationName("Fuel-SCM");

17
tools/git-push.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
export MARKS=marks
export DUMP=changes.fastexport
export REPO=fuel.fossil
if [ ! -f $MARKS-fossil ]; then
touch $MARKS-fossil
fi
fossil export --git --import-marks $MARKS-fossil --export-marks $MARKS-fossil $REPO >$DUMP
if [ ! -f $MARKS-git ]; then
touch $MARKS-git
fi
git fast-import --export-marks=$MARKS-git --import-marks=$MARKS-git <$DUMP

132
ui/AboutDialog.ui Normal file
View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>577</width>
<height>349</height>
</rect>
</property>
<property name="windowTitle">
<string>About Fuel...</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblIcon">
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="pixmap">
<pixmap resource="../rsrc/resources.qrc">:/icons/icon-application</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblBanner">
<property name="text">
<string>A GUI front-end for the Fossil SCM by Kostas Karanikolas
Released under the GNU GPL</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblFossilVersion">
<property name="text">
<string notr="true">FOSSIL VERSION</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblQtVersion">
<property name="text">
<string notr="true">QT VERSION</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QTextEdit" name="txtAdditional">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../rsrc/resources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -14,7 +14,16 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -44,7 +53,7 @@
<action name="actionBrowserBack">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Previous-01.png</normaloff>:/icons/icons/Button Previous-01.png</iconset>
<normaloff>:/icons/icon-action-previous</normaloff>:/icons/icon-action-previous</iconset>
</property>
<property name="text">
<string>Back</string>
@ -56,7 +65,7 @@
<action name="actionBrowserForward">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Next-01.png</normaloff>:/icons/icons/Button Next-01.png</iconset>
<normaloff>:/icons/icon-action-next</normaloff>:/icons/icon-action-next</iconset>
</property>
<property name="text">
<string>Forward</string>
@ -68,7 +77,7 @@
<action name="actionBrowserRefresh">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Refresh-01.png</normaloff>:/icons/icons/Button Refresh-01.png</iconset>
<normaloff>:/icons/icon-action-refresh</normaloff>:/icons/icon-action-refresh</iconset>
</property>
<property name="text">
<string>Refresh</string>
@ -80,7 +89,7 @@
<action name="actionBrowserStop">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Close-01.png</normaloff>:/icons/icons/Button Close-01.png</iconset>
<normaloff>:/icons/icon-action-stop</normaloff>:/icons/icon-action-stop</iconset>
</property>
<property name="text">
<string>Stop</string>

View File

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<height>394</height>
</rect>
</property>
<property name="windowTitle">
@ -21,7 +21,11 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboBox"/>
<widget class="QComboBox" name="comboBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
@ -62,12 +66,99 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<widget class="QWidget" name="widgetBranchOptions" native="true">
<layout class="QFormLayout" name="formLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Commit to new branch</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="chkNewBranch">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblPrivateBranch">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Private branch</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="chkPrivateBranch">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Branch name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineBranchName">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkRevertFiles">
<property name="text">
<string>Revert stashed files</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

272
ui/FslSettingsDialog.ui Normal file
View File

@ -0,0 +1,272 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FslSettingsDialog</class>
<widget class="QDialog" name="FslSettingsDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>457</width>
<height>235</height>
</rect>
</property>
<property name="windowTitle">
<string>Fossil Settings</string>
</property>
<property name="windowIcon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-application</normaloff>:/icons/icon-application</iconset>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Graphical Diff </string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="lineGDiffCommand">
<property name="toolTip">
<string>Path to graphical diff tool</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectFossilGDiff">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Graphical Merge</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLineEdit" name="lineGMergeCommand">
<property name="toolTip">
<string>Path to the graphical merge tool</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectGMerge">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_44">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>HTTP Proxy</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineProxy">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The URL of the HTTP proxy</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="uILanguageLabel_3">
<property name="text">
<string>HTTP Port</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineUIPort">
<property name="toolTip">
<string>HTTP port to use for the Fossil web interface</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Ignore CR/NL</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineIgnoreCRNL">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Ignore List</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="lineIgnore">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>A comma separated list of glob-style file/path patterns ignored in Fossil file operations</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../rsrc/resources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FslSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FslSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -18,18 +18,27 @@
</property>
<property name="windowIcon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Battery-01.png</normaloff>:/icons/icons/Battery-01.png</iconset>
<normaloff>:/icons/icon-application</normaloff>:/icons/icon-application</iconset>
</property>
<property name="unifiedTitleAndToolBarOnMac">
<bool>true</bool>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QSplitter" name="splitter_2">
<widget class="QSplitter" name="splitterVertical">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
@ -39,7 +48,7 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QSplitter" name="splitter">
<widget class="QSplitter" name="splitterHorizontal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@ -49,7 +58,7 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeView">
<widget class="QTreeView" name="workspaceTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>20</horstretch>
@ -57,7 +66,7 @@
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
@ -69,16 +78,13 @@
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
</widget>
<widget class="FileTableView" name="tableView">
<widget class="FileTableView" name="fileTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>80</horstretch>
@ -131,44 +137,6 @@
<number>30</number>
</attribute>
</widget>
<widget class="QTableView" name="tableViewStash">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>20</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
@ -181,14 +149,23 @@
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="tabLog">
<attribute name="title">
<string>Log</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -217,7 +194,16 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -264,16 +250,39 @@
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="actionViewAll"/>
<addaction name="actionViewModifedOnly"/>
<addaction name="separator"/>
<addaction name="actionViewModified"/>
<addaction name="actionViewUnchanged"/>
<addaction name="actionViewUnknown"/>
<addaction name="actionViewIgnored"/>
<addaction name="separator"/>
<addaction name="actionViewStash"/>
<addaction name="separator"/>
<addaction name="actionViewAsList"/>
<addaction name="actionViewAsFolders"/>
</widget>
<widget class="QMenu" name="menuWorkspace">
<property name="title">
<string>&amp;Workspace</string>
</property>
<addaction name="actionRefresh"/>
<addaction name="separator"/>
<addaction name="actionCommit"/>
<addaction name="actionUpdate"/>
<addaction name="separator"/>
<addaction name="actionPush"/>
<addaction name="actionPull"/>
<addaction name="separator"/>
<addaction name="actionUndo"/>
<addaction name="separator"/>
<addaction name="actionCreateBranch"/>
<addaction name="actionCreateTag"/>
<addaction name="actionCreateStash"/>
<addaction name="separator"/>
<addaction name="actionFossilSettings"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuWorkspace"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
@ -316,8 +325,6 @@
<addaction name="actionRevert"/>
<addaction name="actionDelete"/>
<addaction name="separator"/>
<addaction name="actionNewStash"/>
<addaction name="separator"/>
<addaction name="actionDiff"/>
<addaction name="actionHistory"/>
<addaction name="separator"/>
@ -330,10 +337,10 @@
<action name="actionRefresh">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Refresh-01.png</normaloff>:/icons/icons/Button Refresh-01.png</iconset>
<normaloff>:/icons/icon-action-refresh</normaloff>:/icons/icon-action-refresh</iconset>
</property>
<property name="text">
<string>Refresh</string>
<string>&amp;Refresh</string>
</property>
<property name="toolTip">
<string>Refresh the views</string>
@ -348,10 +355,10 @@
<action name="actionCommit">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Save-01.png</normaloff>:/icons/icons/Save-01.png</iconset>
<normaloff>:/icons/icon-action-commit</normaloff>:/icons/icon-action-commit</iconset>
</property>
<property name="text">
<string>Commit</string>
<string>&amp;Commit</string>
</property>
<property name="toolTip">
<string>Commit modifications</string>
@ -366,7 +373,7 @@
<action name="actionDiff">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Document Copy-01.png</normaloff>:/icons/icons/Document Copy-01.png</iconset>
<normaloff>:/icons/icon-item-diff</normaloff>:/icons/icon-item-diff</iconset>
</property>
<property name="text">
<string>Diff</string>
@ -384,7 +391,7 @@
<action name="actionAdd">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/File New-01.png</normaloff>:/icons/icons/File New-01.png</iconset>
<normaloff>:/icons/icon-item-add</normaloff>:/icons/icon-item-add</iconset>
</property>
<property name="text">
<string>Add</string>
@ -402,7 +409,7 @@
<action name="actionDelete">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/File Delete-01.png</normaloff>:/icons/icons/File Delete-01.png</iconset>
<normaloff>:/icons/icon-item-delete</normaloff>:/icons/icon-item-delete</iconset>
</property>
<property name="text">
<string>Delete</string>
@ -420,7 +427,7 @@
<action name="actionNewRepository">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Document Blank-01.png</normaloff>:/icons/icons/Document Blank-01.png</iconset>
<normaloff>:/icons/icon-action-repo-new</normaloff>:/icons/icon-action-repo-new</iconset>
</property>
<property name="text">
<string>&amp;New...</string>
@ -438,7 +445,7 @@
<action name="actionOpenRepository">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/My Documents-01.png</normaloff>:/icons/icons/My Documents-01.png</iconset>
<normaloff>:/icons/icon-action-repo-open</normaloff>:/icons/icon-action-repo-open</iconset>
</property>
<property name="text">
<string>&amp;Open...</string>
@ -470,10 +477,10 @@
<action name="actionCloneRepository">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/My Websites-01.png</normaloff>:/icons/icons/My Websites-01.png</iconset>
<normaloff>:/icons/icon-action-repo-clone</normaloff>:/icons/icon-action-repo-clone</iconset>
</property>
<property name="text">
<string>Clone...</string>
<string>C&amp;lone...</string>
</property>
<property name="statusTip">
<string>Clone a remote repository</string>
@ -482,13 +489,13 @@
<action name="actionPush">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Upload-01.png</normaloff>:/icons/icons/Button Upload-01.png</iconset>
<normaloff>:/icons/icon-action-push</normaloff>:/icons/icon-action-push</iconset>
</property>
<property name="text">
<string>Push</string>
<string>&amp;Push</string>
</property>
<property name="toolTip">
<string>Push changes to the remote repository</string>
<string>Push changes to the default remote repository</string>
</property>
<property name="statusTip">
<string>Push changes to the remote repository</string>
@ -500,13 +507,13 @@
<action name="actionPull">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Download-01.png</normaloff>:/icons/icons/Button Download-01.png</iconset>
<normaloff>:/icons/icon-action-pull</normaloff>:/icons/icon-action-pull</iconset>
</property>
<property name="text">
<string>Pull</string>
<string>Pu&amp;ll</string>
</property>
<property name="toolTip">
<string>Pull changes from the remote repository</string>
<string>Pull changes from the default remote repository</string>
</property>
<property name="statusTip">
<string>Pull changes from the remote repository</string>
@ -515,10 +522,40 @@
<string>Ctrl+L</string>
</property>
</action>
<action name="actionPushRemote">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-push</normaloff>:/icons/icon-action-push</iconset>
</property>
<property name="text">
<string>&amp;Push to Remote</string>
</property>
<property name="toolTip">
<string>Push changes to a remote repository</string>
</property>
<property name="statusTip">
<string>Push changes to a remote repository</string>
</property>
</action>
<action name="actionPullRemote">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-pull</normaloff>:/icons/icon-action-pull</iconset>
</property>
<property name="text">
<string>Pu&amp;ll from Remote</string>
</property>
<property name="toolTip">
<string>Pull changes from a remote repository</string>
</property>
<property name="statusTip">
<string>Pull changes from a remote repository</string>
</property>
</action>
<action name="actionRename">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/File Open-01.png</normaloff>:/icons/icons/File Open-01.png</iconset>
<normaloff>:/icons/icon-item-rename</normaloff>:/icons/icon-item-rename</iconset>
</property>
<property name="text">
<string>Rename</string>
@ -536,7 +573,7 @@
<action name="actionQuit">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Turn Off-01.png</normaloff>:/icons/icons/Button Turn Off-01.png</iconset>
<normaloff>:/icons/icon-action-quit</normaloff>:/icons/icon-action-quit</iconset>
</property>
<property name="text">
<string>&amp;Quit</string>
@ -547,6 +584,9 @@
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>
</property>
<property name="iconVisibleInMenu">
<bool>true</bool>
</property>
@ -554,7 +594,7 @@
<action name="actionHistory">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/File History-01.png</normaloff>:/icons/icons/File History-01.png</iconset>
<normaloff>:/icons/icon-item-history</normaloff>:/icons/icon-item-history</iconset>
</property>
<property name="text">
<string>History</string>
@ -575,7 +615,7 @@
</property>
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Network MAC-01.png</normaloff>:/icons/icons/Network MAC-01.png</iconset>
<normaloff>:/icons/icon-webview</normaloff>:/icons/icon-webview</iconset>
</property>
<property name="text">
<string>Fossil UI</string>
@ -590,7 +630,7 @@
<action name="actionRevert">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Document-Revert-icon.png</normaloff>:/icons/icons/Document-Revert-icon.png</iconset>
<normaloff>:/icons/icon-item-revert</normaloff>:/icons/icon-item-revert</iconset>
</property>
<property name="text">
<string>Revert</string>
@ -605,7 +645,7 @@
<action name="actionClearLog">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Text Edit.png</normaloff>:/icons/icons/Text Edit.png</iconset>
<normaloff>:/icons/icon-clear-log</normaloff>:/icons/icon-clear-log</iconset>
</property>
<property name="text">
<string>Clear Log</string>
@ -620,7 +660,7 @@
<action name="actionTimeline">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Clock-01.png</normaloff>:/icons/icons/Clock-01.png</iconset>
<normaloff>:/icons/icon-action-timeline</normaloff>:/icons/icon-action-timeline</iconset>
</property>
<property name="text">
<string>Timeline</string>
@ -635,7 +675,7 @@
<action name="actionOpenFile">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Document-01.png</normaloff>:/icons/icons/Document-01.png</iconset>
<normaloff>:/icons/icon-item-file</normaloff>:/icons/icon-item-file</iconset>
</property>
<property name="text">
<string>Open File</string>
@ -653,7 +693,7 @@
<action name="actionOpenContaining">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder-01.png</normaloff>:/icons/icons/Folder-01.png</iconset>
<normaloff>:/icons/icon-action-folder-explore</normaloff>:/icons/icon-action-folder-explore</iconset>
</property>
<property name="text">
<string>Open Containing</string>
@ -671,10 +711,10 @@
<action name="actionUndo">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Reload-01.png</normaloff>:/icons/icons/Button Reload-01.png</iconset>
<normaloff>:/icons/icon-action-undo</normaloff>:/icons/icon-action-undo</iconset>
</property>
<property name="text">
<string>Undo</string>
<string>U&amp;ndo</string>
</property>
<property name="toolTip">
<string>Undo the last Fossil action</string>
@ -689,7 +729,7 @@
<action name="actionAbout">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Battery-01.png</normaloff>:/icons/icons/Battery-01.png</iconset>
<normaloff>:/icons/icon-application</normaloff>:/icons/icon-application</iconset>
</property>
<property name="text">
<string>&amp;About...</string>
@ -697,17 +737,20 @@
<property name="statusTip">
<string>About Fuel</string>
</property>
<property name="menuRole">
<enum>QAction::AboutRole</enum>
</property>
</action>
<action name="actionUpdate">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Button Play-01.png</normaloff>:/icons/icons/Button Play-01.png</iconset>
<normaloff>:/icons/icon-action-update</normaloff>:/icons/icon-action-update</iconset>
</property>
<property name="text">
<string>Update</string>
<string>&amp;Update</string>
</property>
<property name="toolTip">
<string>Update the workspace to the latest version</string>
<string>Update the workspace to a revision</string>
</property>
<property name="statusTip">
<string>Update the workspace to the latest version</string>
@ -719,7 +762,7 @@
<action name="actionSettings">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Gear-01.png</normaloff>:/icons/icons/Gear-01.png</iconset>
<normaloff>:/icons/icon-action-settings</normaloff>:/icons/icon-action-settings</iconset>
</property>
<property name="text">
<string>&amp;Preferences...</string>
@ -730,6 +773,9 @@
<property name="statusTip">
<string>Fuel Preferences</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>
</property>
</action>
<action name="actionViewModified">
<property name="checkable">
@ -739,7 +785,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Modified</string>
<string>&amp;Modified Files</string>
</property>
<property name="statusTip">
<string>Show modifed files</string>
@ -753,7 +799,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Unchanged</string>
<string>&amp;Unchanged Files</string>
</property>
<property name="statusTip">
<string>Show unchanged files</string>
@ -767,7 +813,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Un&amp;known</string>
<string>Un&amp;known Files</string>
</property>
<property name="statusTip">
<string>Show unknown files</string>
@ -778,7 +824,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Ignored</string>
<string>&amp;Ignored Files</string>
</property>
<property name="statusTip">
<string>Show ignored files</string>
@ -804,7 +850,7 @@
<action name="actionOpenFolder">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder-01.png</normaloff>:/icons/icons/Folder-01.png</iconset>
<normaloff>:/icons/icon-item-folder</normaloff>:/icons/icon-item-folder</iconset>
</property>
<property name="text">
<string>Open Folder</string>
@ -819,7 +865,7 @@
<action name="actionRenameFolder">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder Open-01.png</normaloff>:/icons/icons/Folder Open-01.png</iconset>
<normaloff>:/icons/icon-action-folder-rename</normaloff>:/icons/icon-action-folder-rename</iconset>
</property>
<property name="text">
<string>Rename Folder</string>
@ -831,13 +877,13 @@
<string>Rename the selected folder</string>
</property>
</action>
<action name="actionNewStash">
<action name="actionCreateStash">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder Add-01.png</normaloff>:/icons/icons/Folder Add-01.png</iconset>
<normaloff>:/icons/icon-action-stash-new</normaloff>:/icons/icon-action-stash-new</iconset>
</property>
<property name="text">
<string>Stash changes</string>
<string>&amp;Stash Changes</string>
</property>
<property name="statusTip">
<string>Stash changes</string>
@ -846,7 +892,7 @@
<action name="actionApplyStash">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder Open-01.png</normaloff>:/icons/icons/Folder Open-01.png</iconset>
<normaloff>:/icons/icon-action-stash-apply</normaloff>:/icons/icon-action-stash-apply</iconset>
</property>
<property name="text">
<string>Apply Stash</string>
@ -858,27 +904,10 @@
<string>Apply stashed changes</string>
</property>
</action>
<action name="actionViewStash">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Stashed Changes</string>
</property>
<property name="iconText">
<string>View Stashed Changes</string>
</property>
<property name="toolTip">
<string>View Stashed Changes</string>
</property>
<property name="statusTip">
<string>Show the list of stashed changes</string>
</property>
</action>
<action name="actionDeleteStash">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder Delete-01.png</normaloff>:/icons/icons/Folder Delete-01.png</iconset>
<normaloff>:/icons/icon-action-stash-delete</normaloff>:/icons/icon-action-stash-delete</iconset>
</property>
<property name="text">
<string>Delete Stash</string>
@ -887,12 +916,137 @@
<action name="actionDiffStash">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Folder Explorer-01.png</normaloff>:/icons/icons/Folder Explorer-01.png</iconset>
<normaloff>:/icons/icon-action-stash-diff</normaloff>:/icons/icon-action-stash-diff</iconset>
</property>
<property name="text">
<string>Diff Stash</string>
</property>
</action>
<action name="actionCreateTag">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-tag-new</normaloff>:/icons/icon-action-tag-new</iconset>
</property>
<property name="text">
<string>Create &amp;Tag</string>
</property>
<property name="toolTip">
<string>Create a tag for a revision</string>
</property>
<property name="statusTip">
<string>Create a tag for a revision</string>
</property>
</action>
<action name="actionDeleteTag">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-tag-delete</normaloff>:/icons/icon-action-tag-delete</iconset>
</property>
<property name="text">
<string>Delete Tag</string>
</property>
<property name="toolTip">
<string>Delete Tag</string>
</property>
</action>
<action name="actionCreateBranch">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-item-branch</normaloff>:/icons/icon-item-branch</iconset>
</property>
<property name="text">
<string>Create &amp;Branch</string>
</property>
<property name="toolTip">
<string>Create a branch from a revision</string>
</property>
<property name="statusTip">
<string>Create a branch from a revision</string>
</property>
</action>
<action name="actionMergeBranch">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-merge</normaloff>:/icons/icon-action-merge</iconset>
</property>
<property name="text">
<string>Merge Branch</string>
</property>
<property name="toolTip">
<string>Merge with a branch</string>
</property>
<property name="statusTip">
<string>Merge with a branch</string>
</property>
</action>
<action name="actionViewAsFolders">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Files and F&amp;olders</string>
</property>
<property name="toolTip">
<string>View files and folders</string>
</property>
<property name="statusTip">
<string>View the workspace as files and folders</string>
</property>
</action>
<action name="actionViewAll">
<property name="text">
<string>&amp;All Files</string>
</property>
<property name="statusTip">
<string>Show all files</string>
</property>
</action>
<action name="actionViewModifedOnly">
<property name="text">
<string>Mo&amp;dified Files Only</string>
</property>
<property name="statusTip">
<string>Show modified files only</string>
</property>
</action>
<action name="actionFossilSettings">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-settings</normaloff>:/icons/icon-action-settings</iconset>
</property>
<property name="text">
<string>F&amp;ossil Settings</string>
</property>
</action>
<action name="actionEditRemote">
<property name="text">
<string>Edit Remote</string>
</property>
<property name="toolTip">
<string>Edit Remote URL</string>
</property>
</action>
<action name="actionSetDefaultRemote">
<property name="text">
<string>Set Remote as Default</string>
</property>
<property name="statusTip">
<string>Makes the selected remote </string>
</property>
</action>
<action name="actionAddRemote">
<property name="text">
<string>Add Remote</string>
</property>
<property name="statusTip">
<string>Adds a Remote Url</string>
</property>
</action>
<action name="actionDeleteRemote">
<property name="text">
<string>Delete Remote</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

165
ui/RemoteDialog.ui Normal file
View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RemoteDialog</class>
<widget class="QDialog" name="RemoteDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>478</width>
<height>189</height>
</rect>
</property>
<property name="windowTitle">
<string>Remote Repository</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>URL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLineEdit" name="lineURL">
<property name="toolTip">
<string>The URL of the source repository</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectSourceRepo">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>User Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineUserName">
<property name="toolTip">
<string>The user name used to access the remote repository. Leave blank if not required</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="linePassword">
<property name="toolTip">
<string>The password used to access the remote repository. Leave blank if not required</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineName">
<property name="toolTip">
<string>The password used to access the remote repository. Leave blank if not required</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="Name">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lineURL</tabstop>
<tabstop>btnSelectSourceRepo</tabstop>
<tabstop>lineUserName</tabstop>
<tabstop>linePassword</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RemoteDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RemoteDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

129
ui/RevisionDialog.ui Normal file
View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RevisionDialog</class>
<widget class="QDialog" name="RevisionDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>478</width>
<height>177</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblRevision">
<property name="text">
<string>Revision</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cmbRevision">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblName">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineName"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblIntegrate">
<property name="text">
<string>Integrate</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblForce">
<property name="text">
<string>Force</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="chkIntegrate">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkForce">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RevisionDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RevisionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -9,30 +9,23 @@
<rect>
<x>0</x>
<y>0</y>
<width>457</width>
<height>352</height>
<width>449</width>
<height>428</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<property name="windowIcon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-application</normaloff>:/icons/icon-application</iconset>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabApp">
<attribute name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Battery-01.png</normaloff>:/icons/icons/Battery-01.png</iconset>
</attribute>
<attribute name="title">
<string>Application</string>
</attribute>
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
@ -78,132 +71,6 @@
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Graphical Diff </string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="lineGDiffCommand">
<property name="toolTip">
<string>Path to graphical diff tool</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectFossilGDiff">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Graphical Merge</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLineEdit" name="lineGMergeCommand">
<property name="toolTip">
<string>Path to the graphical merge tool</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectGMerge">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_44">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>HTTP Proxy</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineProxy">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The URL of the HTTP proxy</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="uILanguageLabel_3">
<property name="text">
<string>HTTP Port</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineUIPort">
<property name="toolTip">
<string>HTTP port to use for the Fossil web interface</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="minimumSize">
<size>
@ -216,7 +83,7 @@
</property>
</widget>
</item>
<item row="5" column="1">
<item row="1" column="1">
<widget class="QPushButton" name="btnClearMessageHistory">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -232,14 +99,14 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="2" column="0">
<widget class="QLabel" name="uILanguageLabel_2">
<property name="text">
<string>Web Browser</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="2" column="1">
<widget class="QComboBox" name="cmbFossilBrowser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -252,7 +119,7 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="minimumSize">
<size>
@ -265,7 +132,7 @@
</property>
</widget>
</item>
<item row="7" column="1">
<item row="3" column="1">
<widget class="QComboBox" name="cmbDoubleClickAction">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -281,14 +148,14 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="4" column="0">
<widget class="QLabel" name="uILanguageLabel">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="4" column="1">
<widget class="QComboBox" name="cmbActiveLanguage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -301,79 +168,41 @@
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabRepo">
<attribute name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icons/Book-01.png</normaloff>:/icons/icons/Book-01.png</iconset>
</attribute>
<attribute name="title">
<string>Repository</string>
</attribute>
<item row="5" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Custom Actions</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
<property name="leftMargin">
<number>0</number>
</property>
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
<property name="rightMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Remote Url</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineRemoteURL">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>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</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Ignore List</string>
<string>Action</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineIgnore">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<widget class="QComboBox" name="cmbCustomAction">
<property name="toolTip">
<string>A comma separated list of glob-style file/path patterns ignored in Fossil file operations</string>
<string>Custom action</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineCustomActionDescription">
<property name="toolTip">
<string>Name of custom action</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>100</width>
@ -381,25 +210,88 @@ For example http://username:password@server.com/fossil</string>
</size>
</property>
<property name="text">
<string>Ignore CR/NL</string>
<string>Description</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineIgnoreCRNL">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>0</width>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Command</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLineEdit" name="lineCustomActionCommand">
<property name="toolTip">
<string>A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking</string>
<string>Custom action command-line. Information about the selected items is available via the macros %FILE %FOLDER %WORKSPACE</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectCustomFileActionCommand">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Context</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cmbCustomActionContext">
<property name="toolTip">
<string>The context where this action will be available</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="chkCustomActionMultipleSelection">
<property name="toolTip">
<string>When checked this action supports multiple selected items</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Multiple Selection</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>