156 Commits

Author SHA1 Message Date
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
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
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
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
1372cbb8f6 Updated Arch PKGBUILD
FossilOrigin-Name: e7792ddc67a212767eafe7c59f346d58e71c8d51
2015-07-07 18:30:14 +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
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
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
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
66 changed files with 9144 additions and 2403 deletions

4
dist/arch/PKGBUILD vendored
View File

@ -1,7 +1,7 @@
# Maintainer: Kostas Karanikolas <lastname[at]gmail[dot]com> # Maintainer: Kostas Karanikolas <lastname[at]gmail[dot]com>
pkgname=fuel pkgname=fuel
pkgver=1.0.1 pkgver=1.0.0
pkgrel=1 pkgrel=3
pkgdesc="A GUI front-end to Fossil SCM" pkgdesc="A GUI front-end to Fossil SCM"
arch=(i686 x86_64) arch=(i686 x86_64)
url="https://fuel-scm.org/" url="https://fuel-scm.org/"

View File

@ -1,13 +1,42 @@
Fuel V1.0.1 (2015-08-23) 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: - Added Localisations:
- Italian (Thanks maxxlupi and Zangune) - Italian (Thanks maxxlupi and Zangune)
- Dutch (Thanks Rick Van Lieshout and Fly Man) - Dutch (Thanks Rick Van Lieshout and Fly Man)
- Korean (Thanks ardiefox) - Korean (Thanks ardiefox)
- Reformated Docs into Markdown
Fuel V1.0.0 (2015-03-28) Fuel V1.0.0 (2015-03-28)
================================================================================ --------------------------------------------------------------------------------
- Feature: Long Operations can now be aborted by pressing the Escape key - Feature: Long Operations can now be aborted by pressing the Escape key
- Improvement: Better support for commit messages with international characters - Improvement: Better support for commit messages with international characters
- Improvement: Fossil queries about CR/NL inconsistencies are now handled better - Improvement: Fossil queries about CR/NL inconsistencies are now handled better
@ -17,7 +46,7 @@ Fuel V1.0.0 (2015-03-28)
- Portuguese (Thanks emansije) - Portuguese (Thanks emansije)
Fuel V0.9.7 (Unreleased) Fuel V0.9.7 (Unreleased)
================================================================================ --------------------------------------------------------------------------------
- Feature: Optionally use the internal browser for the Fossil UI - 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: 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 Fossil checkout file or workspace folder on Fuel now opens that workspace
@ -34,7 +63,7 @@ Fuel V0.9.7 (Unreleased)
- Distribution: Fuel is now available in the Arch User Repository - Distribution: Fuel is now available in the Arch User Repository
Fuel V0.9.6 (2012-05-13) Fuel V0.9.6 (2012-05-13)
================================================================================ --------------------------------------------------------------------------------
- Feature: Support for fossil stashes - Feature: Support for fossil stashes
- Feature: Support for dragging and dropping files out of Fuel - Feature: Support for dragging and dropping files out of Fuel
- Feature: Allow for opening workspaces via the checkout file or a workspace folder - Feature: Allow for opening workspaces via the checkout file or a workspace folder

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 # Fuel
#------------------------------------------------- #-------------------------------------------------
QT += core gui webkit QT = core gui webkit
contains(QT_VERSION, ^5\\..*) { contains(QT_VERSION, ^5\\..*) {
QT += widgets webkitwidgets QT += widgets webkitwidgets
QT -= quick multimediawidgets opengl printsupport qml multimedia positioning sensors
} }
TARGET = Fuel TARGET = Fuel
@ -52,34 +53,85 @@ SOURCES += src/main.cpp\
src/CommitDialog.cpp \ src/CommitDialog.cpp \
src/FileActionDialog.cpp \ src/FileActionDialog.cpp \
src/SettingsDialog.cpp \ src/SettingsDialog.cpp \
src/FslSettingsDialog.cpp \
src/CloneDialog.cpp \
src/RevisionDialog.cpp \
src/Utils.cpp \ src/Utils.cpp \
src/FileTableView.cpp \ src/FileTableView.cpp \
src/CloneDialog.cpp \
src/LoggedProcess.cpp \ src/LoggedProcess.cpp \
src/BrowserWidget.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 \ HEADERS += src/MainWindow.h \
src/CommitDialog.h \ src/CommitDialog.h \
src/FileActionDialog.h \ src/FileActionDialog.h \
src/SettingsDialog.h \ src/SettingsDialog.h \
src/FslSettingsDialog.h \
src/CloneDialog.h \
src/RevisionDialog.h \
src/Utils.h \ src/Utils.h \
src/FileTableView.h \ src/FileTableView.h \
src/CloneDialog.h \
src/LoggedProcess.h \ src/LoggedProcess.h \
src/BrowserWidget.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 \ FORMS += ui/MainWindow.ui \
ui/CommitDialog.ui \ ui/CommitDialog.ui \
ui/FileActionDialog.ui \ ui/FileActionDialog.ui \
ui/SettingsDialog.ui \ ui/SettingsDialog.ui \
ui/FslSettingsDialog.ui \
ui/CloneDialog.ui \ ui/CloneDialog.ui \
ui/BrowserWidget.ui ui/BrowserWidget.ui \
ui/RevisionDialog.ui \
ui/RemoteDialog.ui \
ui/AboutDialog.ui
RESOURCES += \ RESOURCES += \
rsrc/resources.qrc 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
}
win32 {
SOURCES += ext/qtkeychain/keychain_win.cpp
LIBS += -lCrypt32
}
CODECFORTR = UTF-8 CODECFORTR = UTF-8
TRANSLATIONS += \ TRANSLATIONS += \

View File

@ -1,12 +1,14 @@
#!/bin/bash #!/bin/sh
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPTDIR="$( cd "$( dirname "$0" )" && pwd )"
PRJDIR=$SCRIPTDIR/.. PRJDIR=$SCRIPTDIR/..
INTLDIR=$SCRIPTDIR INTLDIR=$SCRIPTDIR
# Detect lrelease tool # Detect lrelease tool
if hash lrelease-qt5 2>/dev/null; then if which lrelease-qt5 2>/dev/null; then
LRELEASE="lrelease-qt5" LRELEASE="lrelease-qt5"
elif hash lrelease 2>/dev/null; then elif which lrelease4 2>/dev/null; then
LRELEASE="lrelease4"
elif which lrelease 2>/dev/null; then
LRELEASE="lrelease" LRELEASE="lrelease"
else else
echo "lrelease not found" echo "lrelease not found"

View File

@ -1,5 +1,5 @@
C Updated\sthe\sChanges.md C Custom\sactions\snow\sexpect\smacros\sfor\sfile\sand\sfolder\sexpansion\nCustom\sactions\snow\strigger\seven\swithout\sa\sselection\nUse\snative\spath\sseparators\sin\scustom\sactions\n
D 2015-08-23T14:11:16.693 D 2015-08-21T10:54:20.418
F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35 F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35
F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b
F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b
@ -10,14 +10,36 @@ F debian/menu aa1321fe6597a631df5cc978a3cf7b21ac1a3657
F debian/rules 468914cbcf1bcc252ab3f616e1fdc2b37bc10b5d x F debian/rules 468914cbcf1bcc252ab3f616e1fdc2b37bc10b5d x
F debian/source/format 1064dc0ce263680c076a1005f35ec906a5cf5a32 F debian/source/format 1064dc0ce263680c076a1005f35ec906a5cf5a32
F debian/watch 34f0921ff100a3e16a7ad84dcc303731de830a60 F debian/watch 34f0921ff100a3e16a7ad84dcc303731de830a60
F dist/arch/PKGBUILD c9d706805891dadfab933c703e19dc86f084d328 F dist/arch/PKGBUILD 26623327e467028a883cd13963daa36baf10dfa3
F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e
F doc/Building.md 149d959751ae488829e084a9f88449a08220c1d1 F doc/Building.md 149d959751ae488829e084a9f88449a08220c1d1
F doc/Changes.md 9546f046a1c37ea6011cdae77da24df883a8e0ee F doc/Changes.md af49b873012c6b453fe8084f46b6aadbd6fe6e63
F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c
F fuel.pro d2d1fd2b3a9169a95e25235e07c7fa8f098a8c40 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 7802dc9513da36539818ee1e927ec1e3494ecb54
F intl/convert.bat 357d461ee8c6a7be6d2f60ac77c3232678ffb513 x F intl/convert.bat 357d461ee8c6a7be6d2f60ac77c3232678ffb513 x
F intl/convert.sh 2ca2179ff53e727f241925b75e19182607883c45 x F intl/convert.sh ac6edc8d99b575601cf9c6b85b18c9e25875b6e0 x
F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741 F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741
F intl/el_GR.ts 1b805ee57309d02059d9e3e4cb49d945f9d9ac82 F intl/el_GR.ts 1b805ee57309d02059d9e3e4cb49d945f9d9ac82
F intl/en_US.ts 7917816efedf35d5f4f798d18896d7aa0cb3c71b F intl/en_US.ts 7917816efedf35d5f4f798d18896d7aa0cb3c71b
@ -29,6 +51,8 @@ F intl/nl_NL.ts ff9b6ae9da5b6ffacc74fc1075a14ad80ebc0429
F intl/pt_PT.ts f93bcc3df5447ab1d85407e1dec4cd68c03d2245 F intl/pt_PT.ts f93bcc3df5447ab1d85407e1dec4cd68c03d2245
F intl/ru_RU.ts 74189b3ee2b30b0b47b2db5bd7c9935db84947fc F intl/ru_RU.ts 74189b3ee2b30b0b47b2db5bd7c9935db84947fc
F intl/update.sh 39d4561630ba6681bb27e7beadc225a31469728f x 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.desktop 43145556bc61f5a91b497c38a16aec44af271d29
F rsrc/fuel.rc 8e9ac966f283102c11a77cd7f936cdc09e09bd79 F rsrc/fuel.rc 8e9ac966f283102c11a77cd7f936cdc09e09bd79
F rsrc/icons/Address\sBook-01.png ef2cec80ea5a559b72e8be4a344a1869fe69cbd8 F rsrc/icons/Address\sBook-01.png ef2cec80ea5a559b72e8be4a344a1869fe69cbd8
@ -186,37 +210,57 @@ F rsrc/icons/Zoom-01.png 67ca532922e9166325c5c75fce1ca3fbb0d2b6a6
F rsrc/icons/fuel.icns 81e535004b62db801a02f3e15d0a33afc9d4070b F rsrc/icons/fuel.icns 81e535004b62db801a02f3e15d0a33afc9d4070b
F rsrc/icons/fuel.ico eb529ab3332a17b9302ef3e851db5b9ebce2a038 F rsrc/icons/fuel.ico eb529ab3332a17b9302ef3e851db5b9ebce2a038
F rsrc/icons/fuel.png 40daf53b7f6bdcdd0d6aa5ef433d078ec5ea4342 F rsrc/icons/fuel.png 40daf53b7f6bdcdd0d6aa5ef433d078ec5ea4342
F rsrc/resources.qrc 3dee01bfbf8b9ec67a04b9259f4fd358f33356b7 F rsrc/resources.qrc 21ae6205e27ac989001eb0edc075d7e405b992c8
F src/AboutDialog.cpp fc9e3ba03aa6cb145ace610d9b38a2de157551ba
F src/AboutDialog.h 269f3a0589067c08f19b542e4576b0ef58bc6ec5
F src/BrowserWidget.cpp 8b8f545cdff4a4188edc698a1b4777f5df46f056 F src/BrowserWidget.cpp 8b8f545cdff4a4188edc698a1b4777f5df46f056
F src/BrowserWidget.h 764d66aa9a93b890298bd0301097739cb4e16597 F src/BrowserWidget.h 764d66aa9a93b890298bd0301097739cb4e16597
F src/CloneDialog.cpp 812ef7d361c16da21540b7047c9d4d5e74f18539 F src/CloneDialog.cpp c341622b01d493387d6e4928018b3392d92471e8
F src/CloneDialog.h e9f0fc8e5cc5ea2e7c43d6e77b5c4a9cc850b59e F src/CloneDialog.h 8813d91f893eb3eb86a4ea5e50f9a53a0ea07047
F src/CommitDialog.cpp 5300522ac11bc1096a11a6ce22f8c1665d4afc05 F src/CommitDialog.cpp 3d25ae2aa8af0ab417736a3f2d7f95a8dcb7480a
F src/CommitDialog.h f1ee8db92103164e7db55a8407ccdcff24571b72 F src/CommitDialog.h 921bf27c0c538ab9e9d6bdc750064337d346270b
F src/CustomWebView.cpp b7dd0c41977c2cba005df07ed8967ba6f58d07d9 F src/CustomWebView.cpp b7dd0c41977c2cba005df07ed8967ba6f58d07d9
F src/CustomWebView.h fbc8ee55812d1acb3c3b2bc31be7533e8a112822 F src/CustomWebView.h fbc8ee55812d1acb3c3b2bc31be7533e8a112822
F src/FileActionDialog.cpp fcaebf9986f789b3440d5390b3458ad5f86fe0c8 F src/FileActionDialog.cpp fcaebf9986f789b3440d5390b3458ad5f86fe0c8
F src/FileActionDialog.h 15db1650b3a13d70bc338371e4c033c66e3b79ce F src/FileActionDialog.h 15db1650b3a13d70bc338371e4c033c66e3b79ce
F src/FileTableView.cpp 5ddf8c391c9a3ac449ec61fb1db837b577afeec2 F src/FileTableView.cpp 5ddf8c391c9a3ac449ec61fb1db837b577afeec2
F src/FileTableView.h 03e56d87c2d46411b9762b87f4d301619aaf18df 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.cpp 2a1e5c94bc1e57c8984563e66c210e43a14dc60c
F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261 F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261
F src/MainWindow.cpp 96c1c733e719774038f8f4e4bb93194e358caa40 F src/MainWindow.cpp dfa81f94f04e418bdd22985b898855733842d496
F src/MainWindow.h 77038e9c9fe8a64a1c2dfb8d4c2be7558ab5f372 F src/MainWindow.h f4cffbe4d360d30aa2eeaa25fc6d50d0a39c617f
F src/SettingsDialog.cpp 8fe0aacdca6694fe6711ec2b5ff4e54c7b426769 F src/RemoteDialog.cpp 8540cc5e2e41c4127ed8a028d84691604fa6ecac
F src/SettingsDialog.h 4e2790f581e991c744ae9f86580f1972b8c7ff43 F src/RemoteDialog.h 5e0438c2bd7c79b1bb44bfbd58c2181b544a9e5d
F src/Utils.cpp 9aff456712e4276b49083426301b3b96d3819c77 F src/RevisionDialog.cpp e58c4f8a704f00addebb15d521b76620fdafda79
F src/Utils.h c546e478a1225a28c99cd4c30f70cf9be9804a2a F src/RevisionDialog.h b718c3009342eaabad39c8a11a253a4e4fef7a73
F src/main.cpp e1217b2331f1b0fd30756fc80a72f9676f09cf6b 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 a375d6b641658b49d84462773d1e052de0c716dd
F src/Utils.h c6341ee49a8fc35f215facb196d70bf9b1f2fc0f
F src/Workspace.cpp feab8b238a99cf1a60731aedf07af96010d9795d
F src/Workspace.h 54eef32658b13a34fe78ae26887420e8ff358eaa
F src/main.cpp d8c65ea5e54102e4989fef9fd8cfd4f13ef8a8f0
F tools/git-push.sh 62cc58434cae5b7bcd6bd9d4cce8b08739f31cd7 x F tools/git-push.sh 62cc58434cae5b7bcd6bd9d4cce8b08739f31cd7 x
F tools/pack.sh d7f38a498c4e9327fecd6a6e5ac27be270d43008 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/CloneDialog.ui 4886e7d4f258ea8b852b5eefc860396e35145712
F ui/CommitDialog.ui 6200f6cabdcf40a20812e811be28e0793f82516f F ui/CommitDialog.ui 1e5dafa742e9ae07ec937bcda8cda3297ddc6199
F ui/FileActionDialog.ui 89bb4dc2d0b8adcd41adcb11ec65f2028a09a12d F ui/FileActionDialog.ui 89bb4dc2d0b8adcd41adcb11ec65f2028a09a12d
F ui/MainWindow.ui 8677f5c8bca5bf7561d5f64bfdd0cef5157c6ac7 F ui/FslSettingsDialog.ui eb3d4cb764cab90b01e82922237d8c42d6ce1749
F ui/SettingsDialog.ui 2b7c2870e0054b0f4106f495d85d02c0b814df8b F ui/MainWindow.ui f9774e6dddb9462d8072bffd6c511bee7f470b9d
P f3d5c35a93cf7285b489307240a6b75fbc8cb3b8 F ui/RemoteDialog.ui 95a4750d972ed8c49bb10b95db91ff16cfe2dd0b
R d1cdbc42fc1a9f327c9de335a7e9bf81 F ui/RevisionDialog.ui 27c3b98c665fec014a50cbf3352c0627f75e68cd
U kostas F ui/SettingsDialog.ui 2e1b6ce7a49100088c5649292c1319e62e0302e1
Z 7390102f93cb735aad85da46b9b95e70 P 9b67709b92eae80e70f8ae2b775014092f820fdf
R 53802be5132d1fc255734da4b05ec8a0
U Kostas
Z 11e2710ccd6ed208d70aec7542a52542

View File

@ -1 +1 @@
2c6269f8f1b5cc9334488c2db77672deab813e1a e017a9126cf63ed060dd88accfcabbd9726e9aa7

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 Illustrator CS3 Document-01.png</file>
<file>icons/Adobe PDF Document-01.png</file> <file>icons/Adobe PDF Document-01.png</file>
<file>icons/Adobe Photoshop CS3 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/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/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 Blue-01.png</file>
<file>icons/Button Blank Gray-01.png</file> <file alias="icon-item-unknown">icons/Button Blank Gray-01.png</file>
<file>icons/Button Blank Green-01.png</file> <file alias="icon-item-unchanged">icons/Button Blank Green-01.png</file>
<file>icons/Button Blank Red-01.png</file> <file alias="icon-item-conflicted">icons/Button Blank Red-01.png</file>
<file>icons/Button Blank Yellow-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 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 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 Favorite-01.png</file>
<file>icons/Button Forward-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 Info-01.png</file>
<file>icons/Button Log Off-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 Pause-01.png</file>
<file>icons/Button Play-01.png</file> <file alias="icon-action-update">icons/Button Play-01.png</file>
<file>icons/Button Previous-01.png</file> <file alias="icon-action-previous">icons/Button Previous-01.png</file>
<file>icons/Button Refresh-01.png</file> <file alias="icon-action-refresh">icons/Button Refresh-01.png</file>
<file>icons/Button Reload-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 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 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 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/Button Warning-01.png</file>
<file>icons/Calculator-01.png</file> <file>icons/Calculator-01.png</file>
<file>icons/Calendar Blue-01.png</file> <file>icons/Calendar Blue-01.png</file>
@ -42,28 +45,28 @@
<file>icons/Calendar Red-01.png</file> <file>icons/Calendar Red-01.png</file>
<file>icons/Clipboard-01.png</file> <file>icons/Clipboard-01.png</file>
<file>icons/Clipboard Paste-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/Coin-01.png</file>
<file>icons/Compressed File RAR-01.png</file> <file>icons/Compressed File RAR-01.png</file>
<file>icons/Compressed File SIT-01.png</file> <file>icons/Compressed File SIT-01.png</file>
<file>icons/Compressed File Zip-01.png</file> <file>icons/Compressed File Zip-01.png</file>
<file>icons/Computer Monitor-01.png</file> <file>icons/Computer Monitor-01.png</file>
<file>icons/Computer Network-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 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 Chart-01.png</file>
<file>icons/Document Copy-01.png</file> <file alias="icon-item-diff">icons/Document Copy-01.png</file>
<file>icons/Document Flow Chart-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 Gant Chart-01.png</file>
<file>icons/Document Help-01.png</file> <file>icons/Document Help-01.png</file>
<file>icons/Document Line Chart-01.png</file> <file>icons/Document Line Chart-01.png</file>
<file>icons/Document Microsoft Excel-01.png</file> <file>icons/Document Microsoft Excel-01.png</file>
<file>icons/Document Microsoft PowerPoint-01.png</file> <file>icons/Document Microsoft PowerPoint-01.png</file>
<file>icons/Document Microsoft Word-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 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/Document Text-01.png</file>
<file>icons/Edit Document-01.png</file> <file>icons/Edit Document-01.png</file>
<file>icons/Email-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 MP3-01.png</file>
<file>icons/File Audio WAV-01.png</file> <file>icons/File Audio WAV-01.png</file>
<file>icons/File Audio WMA-01.png</file> <file>icons/File Audio WMA-01.png</file>
<file>icons/File Delete-01.png</file> <file alias="icon-item-delete">icons/File Delete-01.png</file>
<file>icons/File History-01.png</file> <file alias="icon-item-history">icons/File History-01.png</file>
<file>icons/File New-01.png</file> <file alias="icon-item-add">icons/File New-01.png</file>
<file>icons/File Open-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 3GP-01.png</file>
<file>icons/File Video-01.png</file> <file>icons/File Video-01.png</file>
<file>icons/File Video AVI-01.png</file> <file>icons/File Video AVI-01.png</file>
<file>icons/File Video MOV-01.png</file> <file>icons/File Video MOV-01.png</file>
<file>icons/File Video MPEG-01.png</file> <file>icons/File Video MPEG-01.png</file>
<file>icons/File Video WMV-01.png</file> <file>icons/File Video WMV-01.png</file>
<file>icons/Folder-01.png</file> <file alias="icon-item-folder">icons/Folder-01.png</file>
<file>icons/Folder Add-01.png</file> <file alias="icon-action-stash-new">icons/Folder Add-01.png</file>
<file>icons/Folder Compressed-01.png</file> <file alias="icon-action-stash-apply">icons/Folder Compressed-01.png</file>
<file>icons/Folder Delete-01.png</file> <file alias="icon-action-stash-delete">icons/Folder Delete-01.png</file>
<file>icons/Folder Explorer-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 Blue-01.png</file>
<file>icons/Folder Generic Green-01.png</file> <file alias="icon-item-folder-unchanged">icons/Folder Generic Green-01.png</file>
<file>icons/Folder Generic Red-01.png</file> <file alias="icon-item-folder-modified">icons/Folder Generic Red-01.png</file>
<file>icons/Folder Generic Silver-01.png</file> <file alias="icon-item-folder-unknown">icons/Folder Generic Silver-01.png</file>
<file>icons/Folder Open-01.png</file> <file alias="icon-action-folder-rename">icons/Folder Open-01.png</file>
<file>icons/Folder RAR-01.png</file> <file>icons/Folder RAR-01.png</file>
<file>icons/Games-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 Blue-01.png</file>
<file>icons/Highlighter Green-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 BMP-01.png</file>
<file>icons/Image GIF-01.png</file> <file>icons/Image GIF-01.png</file>
<file>icons/Image JPEG-01.png</file> <file>icons/Image JPEG-01.png</file>
@ -111,21 +115,21 @@
<file>icons/Image TIFF-01.png</file> <file>icons/Image TIFF-01.png</file>
<file>icons/Lock Lock-01.png</file> <file>icons/Lock Lock-01.png</file>
<file>icons/Lock Unlock-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 Ebooks-01.png</file>
<file>icons/My Music-01.png</file> <file>icons/My Music-01.png</file>
<file>icons/My Pictures.png</file> <file>icons/My Pictures.png</file>
<file>icons/My Videos-01.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 Firewall-01.png</file>
<file>icons/Network MAC-01.png</file> <file alias="icon-webview">icons/Network MAC-01.png</file>
<file>icons/Network PC-01.png</file> <file alias="icon-item-remote" >icons/Network PC-01.png</file>
<file>icons/Network Refresh-01.png</file> <file>icons/Network Refresh-01.png</file>
<file>icons/Pen Blue-01.png</file> <file>icons/Pen Blue-01.png</file>
<file>icons/Pen Green-01.png</file> <file>icons/Pen Green-01.png</file>
<file>icons/Pen Red-01.png</file> <file>icons/Pen Red-01.png</file>
<file>icons/Save-01.png</file> <file alias="icon-action-commit">icons/Save-01.png</file>
<file>icons/Text Edit.png</file> <file alias="icon-clear-log">icons/Text Edit.png</file>
<file>icons/USB-01.png</file> <file>icons/USB-01.png</file>
<file>icons/User Administrator Blue-01.png</file> <file>icons/User Administrator Blue-01.png</file>
<file>icons/User Administrator Green-01.png</file> <file>icons/User Administrator Green-01.png</file>
@ -163,4 +167,8 @@
<file>intl/nl_NL.qm</file> <file>intl/nl_NL.qm</file>
<file>intl/ko_KR.qm</file> <file>intl/ko_KR.qm</file>
</qresource> </qresource>
<qresource prefix="/docs">
<file>docs/Translators.txt</file>
<file>docs/Licenses.txt</file>
</qresource>
</RCC> </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 <QMessageBox>
#include <QClipboard> #include <QClipboard>
#include <QUrl> #include <QUrl>
#include "Utils.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
CloneDialog::CloneDialog(QWidget *parent) : 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()); dlg.ui->linePassword->setText(nurl.password());
nurl.setUserName(""); nurl.setUserName("");
nurl.setPassword(""); 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,
&filter, &filter,
QFileDialog::DontConfirmOverwrite); 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> #include <QDialog>
#define FOSSIL_CHECKOUT1 "_FOSSIL_"
#define FOSSIL_CHECKOUT2 ".fslckout"
#define FOSSIL_EXT "fossil"
namespace Ui { namespace Ui {
class CloneDialog; class CloneDialog;
} }

View File

@ -4,7 +4,7 @@
#include "ui_CommitDialog.h" #include "ui_CommitDialog.h"
#include "MainWindow.h" // Ugly. I know. #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), QDialog(parent, Qt::Sheet),
ui(new Ui::CommitDialog) ui(new Ui::CommitDialog)
{ {
@ -15,16 +15,13 @@ CommitDialog::CommitDialog(QWidget *parent, QString title, QStringList &files, c
setWindowTitle(title); setWindowTitle(title);
// Activate the appropriate control based on mode // Activate the appropriate control based on mode
ui->plainTextEdit->setVisible(!singleLineEntry); ui->plainTextEdit->setVisible(!stashMode);
ui->lineEdit->setVisible(singleLineEntry); ui->lineEdit->setVisible(stashMode);
// Activate the checkbox if we have some text // Activate the checkbox if we have some text
ui->checkBox->setVisible(checkBoxText!=0); ui->chkRevertFiles->setVisible(stashMode);
if(checkBoxText && checkBoxValue)
{ ui->widgetBranchOptions->setVisible(!stashMode);
ui->checkBox->setText(*checkBoxText);
ui->checkBox->setCheckState(*checkBoxValue ? Qt::Checked : Qt::Unchecked);
}
// Activate the combo if we have history // Activate the combo if we have history
ui->comboBox->setVisible(history!=0); 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(); int res = dlg.exec();
if(singleLineEntry)
commitMsg = dlg.ui->lineEdit->text();
else
commitMsg = dlg.ui->plainTextEdit->toPlainText(); commitMsg = dlg.ui->plainTextEdit->toPlainText();
if(res!=QDialog::Accepted) if(res!=QDialog::Accepted)
return false; return false;
@ -100,15 +93,40 @@ bool CommitDialog::run(QWidget *parent, QString title, QStringList &files, QStri
files.append(si->text()); files.append(si->text());
} }
if(checkBoxText) branchName.clear();
if(dlg.ui->chkNewBranch->isChecked())
{ {
Q_ASSERT(checkBoxValue); branchName = dlg.ui->lineBranchName->text().trimmed();
*checkBoxValue = dlg.ui->checkBox->checkState() == Qt::Checked; privateBranch = dlg.ui->chkPrivateBranch->isChecked();
} }
return true; 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) 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); 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 Q_OBJECT
public: 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(); ~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: private slots:
void on_comboBox_activated(int index); void on_comboBox_activated(int index);
void on_listView_doubleClicked(const QModelIndex &index); void on_listView_doubleClicked(const QModelIndex &index);
void on_listView_clicked(const QModelIndex &index); void on_listView_clicked(const QModelIndex &index);
void on_chkNewBranch_toggled(bool checked);
private: private:
Ui::CommitDialog *ui; 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,114 +2,15 @@
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow> #include <QMainWindow>
#include <QStandardItemModel>
#include <QStringList> #include <QStringList>
#include <QMap>
#include <QFileInfo>
#include <QDir>
#include <QProcess>
#include <QFileIconProvider> #include <QFileIconProvider>
#include <QSet> #include "Settings.h"
#include "SettingsDialog.h" #include "Workspace.h"
namespace Ui { namespace Ui {
class MainWindow; 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 // MainWindow
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -120,67 +21,47 @@ class MainWindow : public QMainWindow
public: public:
explicit MainWindow(Settings &_settings, QWidget *parent = 0, QString *workspacePath = 0); explicit MainWindow(Settings &_settings, QWidget *parent = 0, QString *workspacePath = 0);
~MainWindow(); ~MainWindow();
bool diffFile(QString repoFile); bool diffFile(const QString& repoFile);
void fullRefresh(); 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: private:
bool refresh(); bool refresh();
void scanWorkspace(); 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 applySettings();
void updateSettings(); void updateSettings();
const QString &getCurrentWorkspace(); void updateRevision(const QString& revision);
void setCurrentWorkspace(const QString &workspace); void setCurrentWorkspace(const QString &workspace);
void log(const QString &text, bool isHTML=false); void log(const QString &text, bool isHTML=false);
void setStatus(const QString &text); void setStatus(const QString &text);
bool uiRunning() const { return fossilUI.state() == QProcess::Running; } bool uiRunning() const;
void getSelectionFilenames(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); void getSelectionFilenames(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
void getFileViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); void getFileViewSelection(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
void getDirViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); void getDirViewSelection(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
void getStashViewSelection(QStringList &stashNames, bool allIfEmpty=false); void getSelectionStashes(QStringList &stashNames);
void getSelectionPaths(stringset_t &paths); 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(); bool startUI();
void stopUI(); void stopUI();
void enableActions(bool on); void enableActions(bool on);
void addWorkspace(const QString &dir); void addWorkspaceHistory(const QString &dir);
void rebuildRecent(); void rebuildRecent();
bool openWorkspace(const QString &path); bool openWorkspace(const QString &path);
void loadFossilSettings(); void loadFossilSettings();
QString getFossilPath(); void updateWorkspaceView();
QString getFossilHttpAddress();
bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort);
void updateDirView();
void updateFileView(); void updateFileView();
void updateStashView();
void selectRootDir(); void selectRootDir();
void mergeRevision(const QString& defaultRevision);
void updateCustomActions();
void invokeCustomAction(int actionId);
void fossilBrowse(const QString &fossilUrl); void fossilBrowse(const QString &fossilUrl);
void dragEnterEvent(class QDragEnterEvent *event); void dragEnterEvent(class QDragEnterEvent *event);
void dropEvent(class QDropEvent *event); void dropEvent(class QDropEvent *event);
void setBusy(bool busy); void setBusy(bool busy);
virtual QMenu *createPopupMenu(); virtual QMenu *createPopupMenu();
const QIcon& getInternalIcon(const char *name); const QIcon& getCachedIcon(const char *name);
const QIcon& getCachedFileIcon(const QFileInfo &finfo);
enum RepoStatus
{
REPO_OK,
REPO_NOT_FOUND,
REPO_OLD_SCHEMA
};
RepoStatus getRepoStatus();
enum ViewMode enum ViewMode
{ {
@ -192,9 +73,12 @@ private slots:
// Manual slots. // Manual slots.
// Use a different naming scheme to prevent warnings from Qt's automatic signaling // Use a different naming scheme to prevent warnings from Qt's automatic signaling
void onOpenRecent(); 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 onFileViewDragOut();
void onAbort(); void onAbort();
void onSearchBoxTextChanged(const QString &text);
void onSearch();
void onCustomActionTriggered();
// Designer slots // Designer slots
void on_actionRefresh_triggered(); void on_actionRefresh_triggered();
@ -204,11 +88,13 @@ private slots:
void on_actionTimeline_triggered(); void on_actionTimeline_triggered();
void on_actionHistory_triggered(); void on_actionHistory_triggered();
void on_actionClearLog_triggered(); void on_actionClearLog_triggered();
void on_tableView_doubleClicked(const QModelIndex &index); void on_fileTableView_doubleClicked(const QModelIndex &index);
void on_treeView_doubleClicked(const QModelIndex &index); void on_workspaceTreeView_doubleClicked(const QModelIndex &index);
void on_actionOpenFile_triggered(); void on_actionOpenFile_triggered();
void on_actionPush_triggered(); void on_actionPush_triggered();
void on_actionPull_triggered(); void on_actionPull_triggered();
void on_actionPushRemote_triggered();
void on_actionPullRemote_triggered();
void on_actionCommit_triggered(); void on_actionCommit_triggered();
void on_actionAdd_triggered(); void on_actionAdd_triggered();
void on_actionDelete_triggered(); void on_actionDelete_triggered();
@ -219,26 +105,62 @@ private slots:
void on_actionAbout_triggered(); void on_actionAbout_triggered();
void on_actionUpdate_triggered(); void on_actionUpdate_triggered();
void on_actionSettings_triggered(); void on_actionSettings_triggered();
void on_actionFossilSettings_triggered();
void on_actionViewUnchanged_triggered(); void on_actionViewUnchanged_triggered();
void on_actionViewModified_triggered(); void on_actionViewModified_triggered();
void on_actionViewUnknown_triggered(); void on_actionViewUnknown_triggered();
void on_actionViewIgnored_triggered(); void on_actionViewIgnored_triggered();
void on_actionViewAll_triggered();
void on_actionViewModifedOnly_triggered();
void on_actionViewAsList_triggered(); void on_actionViewAsList_triggered();
void on_actionViewAsFolders_triggered();
void on_actionOpenFolder_triggered(); void on_actionOpenFolder_triggered();
void on_actionRenameFolder_triggered(); void on_actionRenameFolder_triggered();
void on_actionNewRepository_triggered(); void on_actionNewRepository_triggered();
void on_actionOpenRepository_triggered(); void on_actionOpenRepository_triggered();
void on_actionCloseRepository_triggered(); void on_actionCloseRepository_triggered();
void on_actionCloneRepository_triggered(); void on_actionCloneRepository_triggered();
void on_actionViewStash_triggered(); void on_actionCreateStash_triggered();
void on_actionNewStash_triggered();
void on_actionApplyStash_triggered(); void on_actionApplyStash_triggered();
void on_actionDeleteStash_triggered(); void on_actionDeleteStash_triggered();
void on_actionDiffStash_triggered(); void on_actionDiffStash_triggered();
void on_textBrowser_customContextMenuRequested(const QPoint &pos); 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: 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 enum
{ {
MAX_RECENT=5 MAX_RECENT=5
@ -249,30 +171,39 @@ private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
QFileIconProvider iconProvider; QFileIconProvider iconProvider;
icon_map_t iconCache; icon_map_t iconCache;
QStandardItemModel repoFileModel;
QStandardItemModel repoDirModel;
QStandardItemModel repoStashModel;
QProcess fossilUI;
class QAction *recentWorkspaceActs[MAX_RECENT]; class QAction *recentWorkspaceActs[MAX_RECENT];
class QAction *customActions[MAX_CUSTOM_ACTIONS];
class QAction *fileActionSeparator;
class QAction *workspaceActionSeparator;
class QProgressBar *progressBar; class QProgressBar *progressBar;
class QLabel *lblTags;
class QShortcut *abortShortcut; 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; Settings &settings;
QString projectName;
QString repositoryFile;
QStringList workspaceHistory; QStringList workspaceHistory;
QString currentWorkspace;
ViewMode viewMode;
stringset_t selectedDirs; // The directory selected in the tree
// Repository State MainWinUICallback uiCallback;
typedef QList<RepoFile*> filelist_t;
typedef QMap<QString, RepoFile*> filemap_t; ViewMode viewMode;
typedef QMap<QString, QString> stashmap_t;
filemap_t workspaceFiles;
stringset_t pathSet;
stashmap_t stashMap;
}; };
#endif // MAINWINDOW_H #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 "SettingsDialog.h"
#include "ui_SettingsDialog.h" #include "ui_SettingsDialog.h"
#include <QFileDialog>
#include "Utils.h" #include "Utils.h"
#include <QSettings>
#include <QCoreApplication>
#include <QDir> #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) : 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("Diff File"));
ui->cmbDoubleClickAction->addItem(tr("Open File")); ui->cmbDoubleClickAction->addItem(tr("Open File"));
ui->cmbDoubleClickAction->addItem(tr("Open Containing Folder")); 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("System"));
ui->cmbFossilBrowser->addItem(tr("Internal")); 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->lineFossilPath->setText(QDir::toNativeSeparators(settings->GetValue(FUEL_SETTING_FOSSIL_PATH).toString()));
ui->cmbDoubleClickAction->setCurrentIndex(settings->GetValue(FUEL_SETTING_FILE_DBLCLICK).toInt()); ui->cmbDoubleClickAction->setCurrentIndex(settings->GetValue(FUEL_SETTING_FILE_DBLCLICK).toInt());
ui->cmbFossilBrowser->setCurrentIndex(settings->GetValue(FUEL_SETTING_WEB_BROWSER).toInt()); ui->cmbFossilBrowser->setCurrentIndex(settings->GetValue(FUEL_SETTING_WEB_BROWSER).toInt());
ui->lineUIPort->setText(settings->GetValue(FUEL_SETTING_HTTP_PORT).toString());
// Initialize language combo // Initialize language combo
foreach(const LangMap &m, langMap) foreach(const LangMap &m, langMap)
@ -66,15 +37,22 @@ SettingsDialog::SettingsDialog(QWidget *parent, Settings &_settings) :
ui->cmbActiveLanguage->findText( ui->cmbActiveLanguage->findText(
LangIdToName(lang))); 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 lastActionIndex = 0;
ui->lineRemoteURL->setText(settings->GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString()); currentCustomActions = settings->GetCustomActions();
ui->lineIgnore->setText(settings->GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString());
ui->lineIgnoreCRNL->setText(settings->GetFossilValue(FOSSIL_SETTING_CRNL_GLOB).toString()); 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); 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_FILE_DBLCLICK, ui->cmbDoubleClickAction->currentIndex());
settings->SetValue(FUEL_SETTING_WEB_BROWSER, ui->cmbFossilBrowser->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)); Q_ASSERT(settings->HasValue(FUEL_SETTING_LANGUAGE));
QString curr_langid = settings->GetValue(FUEL_SETTING_LANGUAGE).toString(); QString curr_langid = settings->GetValue(FUEL_SETTING_LANGUAGE).toString();
@ -108,14 +85,16 @@ void SettingsDialog::on_buttonBox_accepted()
if(curr_langid != new_langid) if(curr_langid != new_langid)
QMessageBox::information(this, tr("Restart required"), tr("The language change will take effect after restarting the application"), QMessageBox::Ok); 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()); for(int i=0; i<currentCustomActions.size(); ++i)
settings->SetFossilValue(FOSSIL_SETTING_GMERGE_CMD, ui->lineGMergeCommand->text()); {
settings->SetFossilValue(FOSSIL_SETTING_PROXY_URL, ui->lineProxy->text()); CustomAction &a = currentCustomActions[i];
a.Description = a.Description.trimmed();
a.Command = a.Command.trimmed();
}
settings->SetFossilValue(FOSSIL_SETTING_REMOTE_URL, ui->lineRemoteURL->text()); PutCustomAction(ui->cmbCustomAction->currentIndex());
settings->SetFossilValue(FOSSIL_SETTING_IGNORE_GLOB, ui->lineIgnore->text());
settings->SetFossilValue(FOSSIL_SETTING_CRNL_GLOB, ui->lineIgnoreCRNL->text());
settings->GetCustomActions() = currentCustomActions;
settings->ApplyEnvironment(); settings->ApplyEnvironment();
} }
@ -128,22 +107,6 @@ void SettingsDialog::on_btnSelectFossil_clicked()
ui->lineFossilPath->setText(QDir::toNativeSeparators(path)); 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() void SettingsDialog::on_btnClearMessageHistory_clicked()
{ {
@ -190,119 +153,58 @@ QString SettingsDialog::LangNameToId(const QString &name)
return ""; return "";
} }
/////////////////////////////////////////////////////////////////////////////// //-----------------------------------------------------------------------------
Settings::Settings(bool portableMode) : store(0) void SettingsDialog::on_btnSelectCustomFileActionCommand_clicked()
{ {
Mappings.insert(FOSSIL_SETTING_GDIFF_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL)); QString path = SelectExe(this, tr("Select command"));
Mappings.insert(FOSSIL_SETTING_GMERGE_CMD, Setting("", Setting::TYPE_FOSSIL_GLOBAL)); if(path.isEmpty())
Mappings.insert(FOSSIL_SETTING_PROXY_URL, Setting("", Setting::TYPE_FOSSIL_GLOBAL)); return;
Mappings.insert(FOSSIL_SETTING_IGNORE_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL)); // Quote path if it contains spaces
Mappings.insert(FOSSIL_SETTING_CRNL_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL)); if(path.indexOf(' ')!=-1)
Mappings.insert(FOSSIL_SETTING_REMOTE_URL, Setting("off", Setting::TYPE_FOSSIL_COMMAND)); path = '"' + path + '"';
// Go into portable mode when explicitly requested or if a config file exists next to the executable ui->lineCustomActionCommand->setText(QDir::toNativeSeparators(path));
QString ini_path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + QCoreApplication::applicationName() + ".ini");
if( portableMode || QFile::exists(ini_path))
store = new QSettings(ini_path, QSettings::IniFormat);
else
{
// Linux: ~/.config/organizationName/applicationName.conf
// Windows: HKEY_CURRENT_USER\Software\organizationName\Fuel
store = new QSettings(QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
}
Q_ASSERT(store);
if(!HasValue(FUEL_SETTING_FILE_DBLCLICK))
SetValue(FUEL_SETTING_FILE_DBLCLICK, 0);
if(!HasValue(FUEL_SETTING_LANGUAGE) && SupportsLang(QLocale::system().name()))
SetValue(FUEL_SETTING_LANGUAGE, QLocale::system().name());
if(!HasValue(FUEL_SETTING_WEB_BROWSER))
SetValue(FUEL_SETTING_WEB_BROWSER, 0);
if(!HasValue(FUEL_SETTING_HTTP_PORT))
SetValue(FUEL_SETTING_HTTP_PORT, "8090");
ApplyEnvironment();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
Settings::~Settings() void SettingsDialog::GetCustomAction(int index)
{ {
Q_ASSERT(store); Q_ASSERT(index>=0 && index < currentCustomActions.size());
delete store; 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(); Q_ASSERT(index>=0 && index < currentCustomActions.size());
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) CustomAction &action = currentCustomActions[index];
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8")); action.Description = ui->lineCustomActionDescription->text().trimmed();
#endif action.Command = ui->lineCustomActionCommand->text().trimmed();
if(!InstallLang(lang_id)) action.Context = static_cast<CustomActionContext>(ui->cmbCustomActionContext->currentIndex()+1);
SetValue(FUEL_SETTING_LANGUAGE, "en_US"); action.MultipleSelection = ui->chkCustomActionMultipleSelection->isChecked();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool Settings::InstallLang(const QString &langId) void SettingsDialog::on_cmbCustomAction_currentIndexChanged(int index)
{ {
if(langId == "en_US") if(index != lastActionIndex)
{ PutCustomAction(lastActionIndex);
QCoreApplication::instance()->removeTranslator(&translator);
return true;
}
QString locale_path = QString(":intl/intl/%0.qm").arg(langId); GetCustomAction(index);
if(!translator.load(locale_path)) lastActionIndex = index;
return false;
Q_ASSERT(!translator.isEmpty());
QCoreApplication::instance()->installTranslator(&translator);
return true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool Settings::HasValue(const QString &name) const void SettingsDialog::on_cmbCustomActionContext_currentIndexChanged(int index)
{ {
return store->contains(name); int action_index = ui->cmbCustomAction->currentIndex();
} if(action_index<0)
return;
//----------------------------------------------------------------------------- Q_ASSERT(action_index>=0 && action_index < currentCustomActions.size());
const QVariant Settings::GetValue(const QString &name) currentCustomActions[action_index].Context = static_cast<CustomActionContext>(index+1);
{
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();
} }

View File

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

View File

@ -1,6 +1,12 @@
#include "Utils.h" #include "Utils.h"
#include <QMessageBox> #include <QMessageBox>
#include <QDialogButtonBox> #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) QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons)
@ -14,6 +20,42 @@ QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, c
return res; 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();
return path;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if 0 // Unused #if 0 // Unused
#include <QInputDialog> #include <QInputDialog>
@ -291,3 +333,291 @@ bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint)
#endif #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;
Q_ASSERT(!command.isEmpty());
// Split command from embedded params
extraParams.clear();
// Command ends after first space
QChar cmd_char_end = ' ';
int start = 0;
// ...unless it is a quoted command
if(command[0]=='"')
{
cmd_char_end = '"';
start = 1;
}
int cmd_end = command.indexOf(cmd_char_end, start);
if(cmd_end > 0)
{
extraParams = command.mid(cmd_end+1);
command = command.mid(start, cmd_end-1);
}
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 <QString>
#include <QMessageBox> #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); 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 #ifdef Q_OS_WIN
bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint); bool ShowExplorerMenu(HWND hwnd, const QString &path, const QPoint &qpoint);
#endif #endif
#endif // UTILS_H #endif // UTILS_H

429
src/Workspace.cpp Normal file
View File

@ -0,0 +1,429 @@
#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();
// 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);
}
}
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); QApplication app(argc, argv);
app.setApplicationName("Fuel"); app.setApplicationName("Fuel");
app.setApplicationVersion("1.0.1"); app.setApplicationVersion("2.0.0");
app.setOrganizationDomain("fuel-scm.org"); app.setOrganizationDomain("fuel-scm.org");
app.setOrganizationName("Fuel-SCM"); app.setOrganizationName("Fuel-SCM");

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"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </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> <number>0</number>
</property> </property>
<item> <item>
@ -44,7 +53,7 @@
<action name="actionBrowserBack"> <action name="actionBrowserBack">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Back</string> <string>Back</string>
@ -56,7 +65,7 @@
<action name="actionBrowserForward"> <action name="actionBrowserForward">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Forward</string> <string>Forward</string>
@ -68,7 +77,7 @@
<action name="actionBrowserRefresh"> <action name="actionBrowserRefresh">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Refresh</string> <string>Refresh</string>
@ -80,7 +89,7 @@
<action name="actionBrowserStop"> <action name="actionBrowserStop">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Stop</string> <string>Stop</string>

View File

@ -10,7 +10,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>300</height> <height>394</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -21,7 +21,11 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QComboBox" name="comboBox"/> <widget class="QComboBox" name="comboBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="lineEdit"/> <widget class="QLineEdit" name="lineEdit"/>
@ -62,12 +66,99 @@
</widget> </widget>
</item> </item>
<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"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </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> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <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,7 +18,7 @@
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="unifiedTitleAndToolBarOnMac"> <property name="unifiedTitleAndToolBarOnMac">
<bool>true</bool> <bool>true</bool>
@ -58,7 +58,7 @@
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<widget class="QTreeView" name="treeView"> <widget class="QTreeView" name="workspaceTreeView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>20</horstretch> <horstretch>20</horstretch>
@ -66,7 +66,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="contextMenuPolicy"> <property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum> <enum>Qt::CustomContextMenu</enum>
</property> </property>
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
@ -78,16 +78,13 @@
<enum>QAbstractItemView::SelectItems</enum> <enum>QAbstractItemView::SelectItems</enum>
</property> </property>
<property name="sortingEnabled"> <property name="sortingEnabled">
<bool>true</bool> <bool>false</bool>
</property> </property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0"> <attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
</widget> </widget>
<widget class="FileTableView" name="tableView"> <widget class="FileTableView" name="fileTableView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>80</horstretch> <horstretch>80</horstretch>
@ -140,44 +137,6 @@
<number>30</number> <number>30</number>
</attribute> </attribute>
</widget> </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>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy"> <property name="sizePolicy">
@ -190,7 +149,7 @@
<enum>QTabWidget::South</enum> <enum>QTabWidget::South</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tabLog"> <widget class="QWidget" name="tabLog">
<attribute name="title"> <attribute name="title">
@ -263,7 +222,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>865</width> <width>865</width>
<height>22</height> <height>23</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -291,16 +250,39 @@
<property name="title"> <property name="title">
<string>&amp;View</string> <string>&amp;View</string>
</property> </property>
<addaction name="actionViewAll"/>
<addaction name="actionViewModifedOnly"/>
<addaction name="separator"/>
<addaction name="actionViewModified"/> <addaction name="actionViewModified"/>
<addaction name="actionViewUnchanged"/> <addaction name="actionViewUnchanged"/>
<addaction name="actionViewUnknown"/> <addaction name="actionViewUnknown"/>
<addaction name="actionViewIgnored"/> <addaction name="actionViewIgnored"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionViewStash"/>
<addaction name="separator"/>
<addaction name="actionViewAsList"/> <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> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuWorkspace"/>
<addaction name="menuView"/> <addaction name="menuView"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
</widget> </widget>
@ -343,8 +325,6 @@
<addaction name="actionRevert"/> <addaction name="actionRevert"/>
<addaction name="actionDelete"/> <addaction name="actionDelete"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionNewStash"/>
<addaction name="separator"/>
<addaction name="actionDiff"/> <addaction name="actionDiff"/>
<addaction name="actionHistory"/> <addaction name="actionHistory"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -357,10 +337,10 @@
<action name="actionRefresh"> <action name="actionRefresh">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Refresh</string> <string>&amp;Refresh</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Refresh the views</string> <string>Refresh the views</string>
@ -375,10 +355,10 @@
<action name="actionCommit"> <action name="actionCommit">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Commit</string> <string>&amp;Commit</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Commit modifications</string> <string>Commit modifications</string>
@ -393,7 +373,7 @@
<action name="actionDiff"> <action name="actionDiff">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Diff</string> <string>Diff</string>
@ -411,7 +391,7 @@
<action name="actionAdd"> <action name="actionAdd">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Add</string> <string>Add</string>
@ -429,7 +409,7 @@
<action name="actionDelete"> <action name="actionDelete">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Delete</string> <string>Delete</string>
@ -447,7 +427,7 @@
<action name="actionNewRepository"> <action name="actionNewRepository">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>&amp;New...</string> <string>&amp;New...</string>
@ -465,7 +445,7 @@
<action name="actionOpenRepository"> <action name="actionOpenRepository">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>&amp;Open...</string> <string>&amp;Open...</string>
@ -497,10 +477,10 @@
<action name="actionCloneRepository"> <action name="actionCloneRepository">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Clone...</string> <string>C&amp;lone...</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Clone a remote repository</string> <string>Clone a remote repository</string>
@ -509,13 +489,13 @@
<action name="actionPush"> <action name="actionPush">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Push</string> <string>&amp;Push</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Push changes to the remote repository</string> <string>Push changes to the default remote repository</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Push changes to the remote repository</string> <string>Push changes to the remote repository</string>
@ -527,13 +507,13 @@
<action name="actionPull"> <action name="actionPull">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Pull</string> <string>Pu&amp;ll</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Pull changes from the remote repository</string> <string>Pull changes from the default remote repository</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Pull changes from the remote repository</string> <string>Pull changes from the remote repository</string>
@ -542,10 +522,40 @@
<string>Ctrl+L</string> <string>Ctrl+L</string>
</property> </property>
</action> </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"> <action name="actionRename">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Rename</string> <string>Rename</string>
@ -563,7 +573,7 @@
<action name="actionQuit"> <action name="actionQuit">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>&amp;Quit</string> <string>&amp;Quit</string>
@ -584,7 +594,7 @@
<action name="actionHistory"> <action name="actionHistory">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>History</string> <string>History</string>
@ -605,7 +615,7 @@
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Fossil UI</string> <string>Fossil UI</string>
@ -620,7 +630,7 @@
<action name="actionRevert"> <action name="actionRevert">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Revert</string> <string>Revert</string>
@ -635,7 +645,7 @@
<action name="actionClearLog"> <action name="actionClearLog">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Clear Log</string> <string>Clear Log</string>
@ -650,7 +660,7 @@
<action name="actionTimeline"> <action name="actionTimeline">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Timeline</string> <string>Timeline</string>
@ -665,7 +675,7 @@
<action name="actionOpenFile"> <action name="actionOpenFile">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Open File</string> <string>Open File</string>
@ -683,7 +693,7 @@
<action name="actionOpenContaining"> <action name="actionOpenContaining">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Open Containing</string> <string>Open Containing</string>
@ -701,10 +711,10 @@
<action name="actionUndo"> <action name="actionUndo">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Undo</string> <string>U&amp;ndo</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Undo the last Fossil action</string> <string>Undo the last Fossil action</string>
@ -719,7 +729,7 @@
<action name="actionAbout"> <action name="actionAbout">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>&amp;About...</string> <string>&amp;About...</string>
@ -734,13 +744,13 @@
<action name="actionUpdate"> <action name="actionUpdate">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Update</string> <string>&amp;Update</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Update the workspace to the latest version</string> <string>Update the workspace to a revision</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Update the workspace to the latest version</string> <string>Update the workspace to the latest version</string>
@ -752,7 +762,7 @@
<action name="actionSettings"> <action name="actionSettings">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>&amp;Preferences...</string> <string>&amp;Preferences...</string>
@ -775,7 +785,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Modified</string> <string>&amp;Modified Files</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Show modifed files</string> <string>Show modifed files</string>
@ -789,7 +799,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Unchanged</string> <string>&amp;Unchanged Files</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Show unchanged files</string> <string>Show unchanged files</string>
@ -803,7 +813,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Un&amp;known</string> <string>Un&amp;known Files</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Show unknown files</string> <string>Show unknown files</string>
@ -814,7 +824,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Ignored</string> <string>&amp;Ignored Files</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Show ignored files</string> <string>Show ignored files</string>
@ -840,7 +850,7 @@
<action name="actionOpenFolder"> <action name="actionOpenFolder">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Open Folder</string> <string>Open Folder</string>
@ -855,7 +865,7 @@
<action name="actionRenameFolder"> <action name="actionRenameFolder">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Rename Folder</string> <string>Rename Folder</string>
@ -867,13 +877,13 @@
<string>Rename the selected folder</string> <string>Rename the selected folder</string>
</property> </property>
</action> </action>
<action name="actionNewStash"> <action name="actionCreateStash">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Stash changes</string> <string>&amp;Stash Changes</string>
</property> </property>
<property name="statusTip"> <property name="statusTip">
<string>Stash changes</string> <string>Stash changes</string>
@ -882,7 +892,7 @@
<action name="actionApplyStash"> <action name="actionApplyStash">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Apply Stash</string> <string>Apply Stash</string>
@ -894,27 +904,10 @@
<string>Apply stashed changes</string> <string>Apply stashed changes</string>
</property> </property>
</action> </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"> <action name="actionDeleteStash">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Delete Stash</string> <string>Delete Stash</string>
@ -923,12 +916,137 @@
<action name="actionDiffStash"> <action name="actionDiffStash">
<property name="icon"> <property name="icon">
<iconset resource="../rsrc/resources.qrc"> <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>
<property name="text"> <property name="text">
<string>Diff Stash</string> <string>Diff Stash</string>
</property> </property>
</action> </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> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <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> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>457</width> <width>449</width>
<height>352</height> <height>428</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Settings</string> <string>Settings</string>
</property> </property>
<property name="windowIcon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-application</normaloff>:/icons/icon-application</iconset>
</property>
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QWidget" name="widget" native="true">
<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>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
@ -78,132 +71,6 @@
</layout> </layout>
</item> </item>
<item row="1" column="0"> <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"> <widget class="QLabel" name="label_6">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
@ -216,7 +83,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="1" column="1">
<widget class="QPushButton" name="btnClearMessageHistory"> <widget class="QPushButton" name="btnClearMessageHistory">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -232,14 +99,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="2" column="0">
<widget class="QLabel" name="uILanguageLabel_2"> <widget class="QLabel" name="uILanguageLabel_2">
<property name="text"> <property name="text">
<string>Web Browser</string> <string>Web Browser</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="2" column="1">
<widget class="QComboBox" name="cmbFossilBrowser"> <widget class="QComboBox" name="cmbFossilBrowser">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -252,7 +119,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
@ -265,7 +132,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="3" column="1">
<widget class="QComboBox" name="cmbDoubleClickAction"> <widget class="QComboBox" name="cmbDoubleClickAction">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -281,14 +148,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="4" column="0">
<widget class="QLabel" name="uILanguageLabel"> <widget class="QLabel" name="uILanguageLabel">
<property name="text"> <property name="text">
<string>Language</string> <string>Language</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="cmbActiveLanguage"> <widget class="QComboBox" name="cmbActiveLanguage">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -301,79 +168,41 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="5" column="0" colspan="2">
</widget> <widget class="QGroupBox" name="groupBox">
<widget class="QWidget" name="tabRepo"> <property name="title">
<attribute name="icon"> <string>Custom Actions</string>
<iconset resource="../rsrc/resources.qrc"> </property>
<normaloff>:/icons/icons/Book-01.png</normaloff>:/icons/icons/Book-01.png</iconset>
</attribute>
<attribute name="title">
<string>Repository</string>
</attribute>
<layout class="QFormLayout" name="formLayout_2"> <layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy"> <property name="leftMargin">
<enum>QFormLayout::ExpandingFieldsGrow</enum> <number>0</number>
</property> </property>
<property name="labelAlignment"> <property name="rightMargin">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <number>0</number>
</property> </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"> <item row="1" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text"> <property name="text">
<string>Ignore List</string> <string>Action</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="lineIgnore"> <widget class="QComboBox" name="cmbCustomAction">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip"> <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> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_2">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>100</width> <width>100</width>
@ -381,25 +210,88 @@ For example http://username:password@server.com/fossil</string>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>Ignore CR/NL</string> <string>Description</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="3" column="0">
<widget class="QLineEdit" name="lineIgnoreCRNL"> <widget class="QLabel" name="label_3">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>100</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </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"> <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> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>