Compare commits

...

304 Commits

Author SHA1 Message Date
poixen
2e0944b7cc card completion 2018-05-13 14:36:18 -04:00
ctrlaltca
4cdd17945d Servatrice: make listening host configurable; fix #3241 (#3242)
* fix #3241

* clangify
2018-05-12 23:54:03 +02:00
ctrlaltca
c06fc562a1 Cmake scripts refactoring; fix #3225 (#3230)
* fix #3225

* Servatrice: use websockets as an optional component (for qt < 5.3)

* Attempt++
2018-05-12 23:28:22 +02:00
tooomm
73fb9ee03e update file rules (#3232) 2018-05-10 22:07:22 +02:00
tooomm
4e10ce7473 smaller clear pic button (#3220) 2018-05-04 03:48:53 -04:00
ctrlaltca
a7f4aace9c Extract the xml parser from carddatabase (#3223) 2018-05-04 03:46:08 -04:00
David Szabo
9727699e26 braces are only left for split cards (#3217)
* braces are only left for split cards

* condition for braces modified
2018-05-02 21:20:58 -04:00
Zach H
2520d07ef2 log moderation stats in the uptime table (#3215) 2018-05-02 17:31:54 -04:00
tooomm
661e00f563 log cleanup (#3207) 2018-05-02 16:07:43 -04:00
ctrlaltca
2780270911 macOS: fix qt plugin installation (#3216)
* Osx: fix qt plugin installation

* Add make install step to travis debug build

* only install under osx

* Add "styles" plugins (for native look'n'feel)

* Osx: upgrade qt from 5.5 to current (5.10.1)

* Fix osx on travis

The qt homebrew formula now installs three qt* symlinks (qt, qt5, qt5@5.x) so we can't use/don't need bash globbing anymore
2018-05-02 15:57:35 -04:00
tooomm
8a5baa4637 connect dialog tweaks (#3211) 2018-04-23 14:30:46 -04:00
tooomm
f5eb05b682 bump version number (#3205)
* bump version number

* bump
2018-04-18 19:50:26 -04:00
tooomm
f309ddf28a README updates (#3203) 2018-04-16 20:08:26 -04:00
David Szabo
67a2a8cf08 datetime display fixed for OSs with non-English localization (#3204) 2018-04-16 20:07:30 -04:00
Zach H
1fbdea0f35 translation updates (#3200) 2018-04-16 02:42:58 -04:00
David Szabo
cc2a0fa0d0 server site added to connect window (#3190) 2018-04-10 19:05:13 -04:00
tooomm
034e79dd36 typo (#3187) 2018-04-10 12:51:57 -04:00
tooomm
5c03f18c72 add hint about how to contact server admins (#3186) 2018-04-10 12:40:24 -04:00
Zach H
61e509585f Server List from JSON (#3165) 2018-04-09 22:38:21 -04:00
tooomm
0c10b66a2d prettify spoiler settings (#3180) 2018-04-09 17:15:37 -04:00
tooomm
7758145e71 comment explanation out (#3181) 2018-04-09 16:47:44 -04:00
ctrlaltca
37ab7b8fb1 Add a note about bug #3135 (#3184) 2018-04-09 16:47:08 -04:00
tooomm
42d15c39a0 change vcredist install option from "/quiet" to "/passive" (#3175) 2018-04-07 12:32:20 -04:00
tooomm
a0d84114de fix space (#3172) 2018-04-06 15:01:04 -04:00
tooomm
706054ea96 readme: link update (#3173) 2018-04-06 15:00:46 -04:00
tooomm
7d5a33b12c bump version number (#3174) 2018-04-06 15:00:32 -04:00
tooomm
a056a882c3 flip rename (client side) (#3149) 2018-04-05 22:42:54 -04:00
tooomm
f89f6438c9 Update ISSUE_TEMPLATE.md (#3171) 2018-04-05 22:42:36 -04:00
tooomm
e25b7e9eb8 button label wording (#3168) 2018-04-04 04:45:28 -04:00
Zach H
ca2e05952e add rooster ranges, remove woogerworks (#3163) 2018-04-03 18:41:10 -04:00
David Szabo
2fcb02cadd untranslated string comparison fixed (#3164) 2018-04-02 23:04:52 -04:00
David Szabo
e1394bd851 Feature/3139 new features for tips (#3152)
* "Tip of the Day" option added to Help menu

* Tip of the Day setting moved

* If no new tips are availabe, don't show tip of the day again

* list storing try #1

* first unseen tip shown first

* lastShownTip removed

* fixed next/previous buttons

* spaces > tab

* "Show this window on startup" is not checked by default
2018-04-01 04:52:26 -04:00
ctrlaltca
6374d157fc Fix Appveyor builds (#3151)
* fix appveyor builds
2018-03-24 16:05:08 +01:00
tooomm
5df547ee53 update template (#3150) 2018-03-23 23:37:11 -04:00
David Szabo
2a9d76f090 Colors on split cards sorted in WUBRG order (#3128) 2018-03-07 20:26:47 -05:00
Zach H
5859fa2f20 translation updates (#3124) 2018-03-02 16:50:10 -05:00
David Szabo
312caae062 Tip of the Day (#3118)
* Basic tip of the day with sample widget added

* "Show tips on startup" option added to settings

* tip cycling implemented

* Structure of the tipOfTheDay class and resource created

* tip getter function modified

* Resources added, feature works properly

* clangified

* accidental modification rolled back

* zach cleanup

* tips to spaces; cmake list combined

* cleanup img

* fix copy

* remove TOTD as QObject so we can copy construct it

* prevent mem leaks in dlg

* changed order of 'next' and 'previous' buttons

* Date and tip numbers added; content wraps around

* useless sizepolicy removed

* link support added & clangified

* Initial tips & memory management updates
2018-03-02 03:11:18 -05:00
David Szabo
281e52eaa9 power and toughness values displayed after card flip (#3120) 2018-03-01 12:28:30 -05:00
Zach H
66958b5975 clangify only the different files (#3122) 2018-02-28 13:20:53 -05:00
David Szabo
941a06e107 Custom image file path corrected (#3119) 2018-02-25 23:22:41 -05:00
David Szabo
7cfbf114b7 "Show related card" option added to ingame context menu (#3115) 2018-02-19 01:12:25 -05:00
David Szabo
2409eae940 left, right, and S shortcuts modified to use shift (#3116) 2018-02-19 00:38:51 -05:00
Vafthrudnir
3d2c7b6670 Right-click menu added for card database view (#3113) 2018-02-15 03:25:44 -05:00
Vafthrudnir
65ef66cfa7 Braces left in for split mana costs in xml (#3108) 2018-02-13 22:37:24 -05:00
Zach H
019295931a Merge pull request #3109 from Vafthrudnir/hotfix/oracle_button_text_correction 2018-02-13 20:50:30 -05:00
Vafthrudnir
c8a10a9997 Mnemonic created 2018-02-14 02:40:46 +01:00
Vafthrudnir
4e8a09517c Button text changed to "Save" from "Next" 2018-02-14 02:36:54 +01:00
Vafthrudnir
f302154df7 Hotfix/2950 delete filters with keyboard (#3097)
* Shortcut setting checks for invalid keys

* Different message shown for invalid keys and used keys; warning message is displayed on start if conflic is detected

* Only shortcuts related to deck-builder are checked
2018-02-13 02:51:14 -05:00
Vafthrudnir
11ad677fe8 Sending card name to chat on shift+click (#3106) 2018-02-13 02:50:37 -05:00
Vafthrudnir
8084ab605f Tablerow fixed for non-mana generating artifacts (#3104) 2018-02-12 18:04:11 -05:00
Vafthrudnir
af5a04abf1 "Comment" field made resizable in deck builder window (#3105)
* solution with splitter

* Made prettier

* Useless parameter removed

* clangified

* c++11 cleanup
2018-02-11 13:49:58 -05:00
Vafthrudnir
3426a6b201 'Rarity' property added to split cards in xml (#3102) 2018-02-09 23:45:59 +01:00
Vafthrudnir
51eeac0541 Mana cost filtering for split cards (#3098) 2018-02-08 14:15:42 -05:00
Nick Beeuwsaert
2206328406 Add custom QStyle class to tab bar to fix render issue on macOS. (#3095)
* Add custom QStyle class to tab bar to fix render issue on macOS. fixes #3070

* clangify
2018-02-08 15:39:29 +01:00
Vafthrudnir
501e82f712 Split card handling added to cmc filter (#3090) 2018-02-07 11:37:10 -05:00
tooomm
ab3989aeba Consistent use of 'beta' tag across the project (#3089) 2018-02-07 11:33:14 -05:00
Vafthrudnir
a0d6a342d3 Loyalty filter added (#3087) 2018-02-07 11:33:01 -05:00
Zach H
35159ef61a Clang script (#3085) 2018-02-06 08:45:13 -05:00
Zach H
fcfb2b12b7 remove extra copies (#3081) 2018-02-06 05:06:51 -05:00
Vafthrudnir
4cbec71882 Relation checks added to relevant fields in card filter (#3080)
* comments and <=, >=, == support added
2018-02-06 05:05:30 -05:00
Zach H
b58aa459a4 change file names to end with win32/64 instead of x86/x86_64 (#3079) 2018-02-05 23:39:55 +01:00
Zach H
8a8b580501 translations (#3078) 2018-02-05 12:22:49 -05:00
Zach H
c9525af624 this is backwards (#3077) 2018-02-05 11:39:10 -05:00
tooomm
aba47719b2 properly display logical connectors (#3072)
they should not get transalted as well
2018-02-04 13:47:12 -05:00
Vafthrudnir
b6b4d7e4a0 Automatic focus change after filter selection (#3074) 2018-02-04 13:46:35 -05:00
Vafthrudnir
63d0f5af43 Start in maximized window on first start (#3075) 2018-02-04 13:07:37 -05:00
ctrlaltca
517420cccb Replace CardInfo* and Carset* with smart pointers (#3053)
* Replace CardInfo* and Carset* with smart pointers

* fixes to help memory & c++11 stuff
2018-02-02 12:52:47 -05:00
tooomm
3e418ba3c6 travis: temporary downgrade mac builds to qt5.5 (#3066)
`qt@5.7` is no longer available: https://github.com/Homebrew/homebrew-core/pull/23165#issuecomment-362270835
2018-02-01 19:09:46 +01:00
dino572
c1bd50f186 Fix mwDeck edition tag parsing on deck load (#3057)
* Update decklist.cpp

Changed parsing code, line 565 and 567
2018-01-31 23:29:18 +01:00
tooomm
d13cf65a10 contributing: add toc + little tweaks (#3030) 2018-01-27 13:45:27 -05:00
ctrlaltca
994a643d9c Travis: enable ccache usage under macOS; halves compilation time (#3052) 2018-01-27 13:43:48 -05:00
ctrlaltca
e127cb74b6 Fixed most issues from codacy (#3050) 2018-01-27 13:31:44 -05:00
Dave
3c3e6ae68a Domain change (#3051)
.es es no mas

Server and database from .es is now active on cockatrice.dr4ft.com
2018-01-27 17:30:40 +01:00
ctrlaltca
b29bd9e070 Clang-format (#3028)
* 1/3 Add .clang-format file and travis compilation check

* 2/3 Run clang-format

* 3/3 Fix compilation problems due to include reordering

* 3bis/3 AfterControlStatement: false
2018-01-27 10:41:32 +01:00
tooomm
8dbdd24c8e fix cmake 3.10 (#3048) 2018-01-25 20:39:10 +01:00
tooomm
04ab3d7f13 fix release run on tags (#3046) 2018-01-23 12:58:56 -05:00
tooomm
03d0a84ef5 "edit sets" --> "manage sets" rename (#3044) 2018-01-22 12:42:10 -05:00
tooomm
14a0c53fc1 travis update (osx + linux) (#3041) 2018-01-21 17:07:25 -05:00
woogerboy21
da7c7c1f83 Update Servatrice MySQL Search Path (#3039)
Removed the older versions of the mysql library install paths and updated to reflect the 5.7 product paths.
2018-01-21 11:05:13 -05:00
Zach H
261d3ac591 better null check (#3036) 2018-01-20 14:54:34 -05:00
ctrlaltca
55029b6b68 Travis: remove sudo (#3037) 2018-01-20 16:50:06 +01:00
Zach H
bc52882ac4 Fix Bad Gameplay (#3034) 2018-01-19 20:34:14 -05:00
Zach H
d1b95aad16 move to slot (#3032) 2018-01-19 19:04:48 -05:00
Zach H
691bf36fbe Disable sideboard button w/o deck (#3025) 2018-01-19 10:50:18 -05:00
Zach H
5a823becf1 Contributing Doc Updates (#3009) 2018-01-19 08:38:37 -05:00
Zach H
feeaba1d62 2.4.1 beta -> 2.5.0 beta (#3024)
Big changes so we will go to 2.5.0 for next release
2018-01-18 19:28:51 -05:00
Zach H
ebec30dd1c Add No annotations export option w/ shortcut (#3013) 2018-01-18 19:25:30 -05:00
Zach H
5757d60b1d Smarter Mana Cost sorting & small cleanup (#3008) 2018-01-18 19:25:10 -05:00
skwerlman
81fcc3c11a remove builds for 1610 and 1704 (#3023)
ubuntu turned off the repo servers :(
2018-01-18 19:24:43 -05:00
Zach H
34bec90193 Fix deck loader with spaces (#3007) 2018-01-18 19:23:13 -05:00
Zach H
dcc632e0d4 Fix a crash (#3011) 2018-01-18 11:53:57 -05:00
Zach H
7a150c558d Trim all inputs (except password) (#3018)
* Trim all inputs (except password)

* missed )
2018-01-17 18:57:13 +01:00
ctrlaltca
3a11aebb21 windows/appveyor compilation updates (#3020) 2018-01-17 18:20:05 +01:00
Zach H
8825e2932a fix linux crash (#3014) 2018-01-13 04:20:12 -05:00
ctrlaltca
cd558a9722 Even out db collation to utfmb4_unicode_ci; fix #2835 ; fix #2218 (#2915) 2018-01-13 04:02:22 -05:00
tooomm
cc822dd8df card filter: add clear button and placeholder text (#3003) 2018-01-11 16:43:08 -05:00
Kyle Grammer
6fc1aaef90 Add comp architecture (#2968) 2018-01-10 22:19:07 -05:00
tooomm
c8122c94ef adjust custom naming, clickable commit hash in brackets (#2981) 2018-01-10 13:38:51 -05:00
tooomm
e6e6932dbb card filter: alphabetical ordering for keywords (#2987) 2018-01-10 13:37:39 -05:00
tooomm
d124e6ac22 deck editor: add placeholder text to search (#3000) 2018-01-10 13:33:37 -05:00
tooomm
7efab80d9b token creation context menu improvements (#2999) 2018-01-10 13:32:29 -05:00
Zach H
d19744236e Automatic Spoiler Season (#2991)
* oracle now can be run in spoiler or normal mode

* tests for travis

* only run on relaunch

* spoilers in client (not oracle now) and tray icon shows when done

* spoiler status will be checked before downloading spoiler file

* only download if they care about spoilers

* reload db on spoiler download

* manual update button, code cleanup, and fix enabling sets when new

* cleanup, nullchecks, and fixes to spoiler

* reload DB even if not in spoiler season; necessary as we have a check elsewhere to prevent the reload if spoiler check happens

* Implement changes from 2991#issuecomment-356169374

* Change implicit nullptrs, alert on file deletion, minor changes

* make reload thread safe and minor changes from 2991#issuecomment-356450302

* Fix locking

* Disable update now button while process running
2018-01-10 13:27:43 -05:00
tooomm
51ec593759 fix space (#2990) 2018-01-02 22:57:23 -05:00
Zach H
87c978937d replace beta semvar correctly (#2980) 2017-12-28 21:30:17 -05:00
tooomm
dfaa85847c add tag (#2979) 2017-12-28 20:02:30 -05:00
tooomm
74dbf75c5b fix var call (#2978) 2017-12-28 19:21:42 -05:00
tooomm
f0f73c8f77 ci: deployment config for beta+stable (#2976) 2017-12-28 18:33:16 -05:00
ctrlaltca
3af2be4539 removed win xp support; update qt to 5.9 for win 32bit (#2975) 2017-12-19 09:09:20 +01:00
tooomm
5b8e0dc8ec add group key (#2967)
as announced here: https://blog.travis-ci.com/2017-12-01-new-update-schedule-for-linux-build-images
2017-12-18 18:17:31 -05:00
Zach H
b75882b6b9 Allow non-db cards to be moved around (#2960) 2017-12-18 14:17:54 -05:00
Zach H
2abfd3b4a9 Update Rarity Filters & Refactoring (#2962) 2017-12-17 19:42:54 -05:00
Zach H
014b9947fe clear search bar if all filters cleared (#2961) 2017-12-17 17:48:57 -05:00
Zach H
ec4e6d53df allow backspace button to remove a filter (#2959) 2017-12-17 17:39:41 -05:00
Zach H
0eae4dbe54 add checkbox option to clear log (#2963) 2017-12-17 17:32:31 -05:00
Zach H
297f1f2555 remove untap option, combine w/ tap (#2964) 2017-12-17 17:31:31 -05:00
tooomm
7e80f4b4ae oracle: fix wording for token download (#2946) 2017-12-15 15:42:30 -05:00
Kyle Grammer
6e24f59826 Fixing split card from UNH with wrong name in database (fix #2947) (#2949) 2017-12-15 15:37:32 -05:00
Kyle Grammer
5e00faed5d Fix for Aftermath and Split Cards when sending to Tappedout; fix #2942 (#2944) 2017-12-10 17:56:49 -05:00
John Robe
6c038a91e4 Deck export (#2938)
* Added “export deck” option to export deck to decklist.org for viewing/printing. Implemented using a new menu item, added shortcut ability, and functionality to parse the deck correctly and add main/sideboard cards to decklist. Per issue #2931
2017-12-05 22:34:19 -05:00
tooomm
4d641eb0e7 version number bump to next patch (#2920) 2017-11-19 20:22:12 -05:00
tooomm
59824cf4dd update finish (#2923) 2017-11-19 20:22:02 -05:00
tooomm
b615b5b33e Translation Updates (#2918) 2017-11-19 15:11:37 -05:00
tooomm
3dc2526f0a update download wording (#2912) 2017-11-04 20:17:18 -04:00
tooomm
6bc39a4f6f yml config documentation (#2899)
* little fix

* add config hints

* doc link
2017-11-04 20:16:41 -04:00
John Hill
422c899cdb Sorting by P/T numerically (#2901) 2017-11-04 13:11:46 -04:00
Gavin Bisesi
e96a250bf1 Fix easy changes link in readme (#2897)
Using these two labels because github tells contributors to look for them.
2017-11-02 15:17:29 +01:00
TaylorShiells
563e96e051 Update Readme.md (Style Guide) / FMRM feedback (#2905)
This change establishes the Google Developer Documentation Style Guide as the official style guide for the ReadMe. This will provide a starting point for editors or writers looking to work on documentation. This edit also does an initial patch on the readme based on that style guide
2017-11-01 13:08:17 -04:00
ctrlaltca
4baaf978f5 Deck editor: force default file extension on file save; fix #2829 (#2910) 2017-10-31 15:34:56 -04:00
ctrlaltca
6b6ba9a8f1 Update translations (#2890)
* Translations

* update and test codacy change
2017-10-09 19:16:23 +02:00
tooomm
fcb0daf80f more ignore for appveyor (#2892)
* add dockerfile and license to ignorelist

* fixed space
2017-10-09 19:15:36 +02:00
tooomm
a3ca459526 exclude translation files from codacy (#2891)
* exclude translation files from codacy

* update appveyor ignore config

add codacy.yml
2017-10-09 16:40:05 +02:00
tooomm
17202e8dba AppVeyor: prevent unnecessary builds (#2888)
* prevent unnecessarybuilds

* readd qt5 string

sadly this is needed as of now:
ff6b0f86ec/cockatrice/src/releasechannel.cpp (L59-L68)

* wrap asterisks

* fixed with validator

https://ci.appveyor.com/tools/validate-yaml

* complete link

* add helpful links

this should not trigger a appveyor build

* add space in readme

this should not trigger a appveyor build

* highlight c++ types

this should not trigger a appveyor build

* remove empty line

this should not trigger a appveyor build

* declutter dockerignore

this should not trigger a appveyor build
2017-10-09 11:19:56 +02:00
ctrlaltca
ff6b0f86ec Appveyor updates (#2885)
* Appveyor updates

Remove unused stuff (mysql, choco)
Use MSVC 2015
Upgrade Qt to 5.9, protobuf to 3.4.1

* stick qith qt 5.6 for winxp 32bit compatibility; zlib test

* Appveyor is using a randmon clone path for pulls, get it from an env variable
2017-10-05 08:38:09 +02:00
Zach H
96b30da492 fix deckstats.net regex (#2886) 2017-10-04 23:36:42 -04:00
tooomm
9cbdc4195e change to https (#2863) 2017-10-04 12:46:43 -04:00
tooomm
ada262b826 betaXYZ to betaX (#2882) 2017-10-04 12:45:28 -04:00
Alessandro Marzialetti
78e299fdfd Fixed life points not showing (#2868) (#2881) 2017-10-04 12:44:36 -04:00
skwerlman
cf5cd21c50 Update .gitlab-ci.yml (#2876)
Remove support for Fedora 22 (been EOL for a while)
Add support for Ubuntu 17.04 and 17.10

Note that debug builds for 17.04 and 17.10 are still affected by issue #2343, and so always fail.
2017-10-03 15:28:47 +02:00
woogerboy21
b684c12964 Correct VS2017 Compile Issue (#2878)
A line in the servatrice CMAKE file was removed in a prior commit causing a compile error in newer versions of visual studio.  This returns the line.
2017-10-03 09:23:33 -04:00
tooomm
df8c38b649 add link to project roadmap (#2872) 2017-10-01 00:23:06 -04:00
tooomm
1424de2c54 remove old TODO file (#2870) 2017-09-29 20:51:41 -04:00
ctrlaltca
7875407795 servatrice: case-insensitve check for email and token challenge; fix #2857 (#2860) 2017-09-28 15:34:35 -04:00
tooomm
ef89104503 add colourless keyword to deck filter (#2858)
extends #2737 for missing term (add colourless)
2017-09-27 14:03:58 +02:00
ctrlaltca
ccee0aa3fd Cmake: remove old unneeded POLICIES (fix Appveyor) (#2859) 2017-09-22 17:47:05 +02:00
John Hill
a6d3229e74 If playing online, don't change your life/mana counters if you click on your opponent's (#2839) 2017-08-19 00:43:18 -06:00
tooomm
93d8ac10ff Fix broken README links after #2826
copying --> license
2017-08-14 08:25:20 -04:00
tooomm
04d84f9d9a repo cleanup (#2826) 2017-08-11 21:42:45 -04:00
tooomm
2557a2183d .travis folder (#2805) 2017-08-06 23:56:31 -04:00
John Hill
e694cbe854 add ability to clone from stack (#2824) 2017-08-06 23:56:17 -04:00
John Hill
3963570838 Mana counter shortcut (#2821) 2017-08-06 21:09:25 -04:00
John Hill
dd05b86bc9 initial way of getting rid of warning (#2817) 2017-08-06 21:04:03 -04:00
tooomm
af3423e67d split up string and argument (#2822) 2017-08-06 20:51:54 -04:00
John Hill
7c8daf68ea Add version label to Oracle Importer (#2818) 2017-08-06 14:03:19 -04:00
John Hill
6b1ea9186a Double click token (#2815) 2017-07-31 22:50:32 -04:00
tooomm
e90c389212 remove mention-bot config file (#2814) 2017-07-31 18:08:25 -04:00
John Hill
556d19ff70 Clone in exile and graveyard (#2813) 2017-07-31 00:34:24 -04:00
Lee Matos
838ab888db clarify docker build happens inside of the repo folder (#2807)
I intuited that this was the case, but being more explicit might help other docker newbies to understand what's happening.
2017-07-15 13:51:02 -04:00
tooomm
1cb3d88e35 flag updates (#2804) 2017-07-14 21:30:27 -04:00
ctrlaltca
1366e5970e Portable mode for windows (#2794)
* Portable mode for windows

* fix start menu in portable mode

* Make gcc an happy puppy

* Clean old installation if we are installing over an old portable mode installation

* Default to Desktop\CockatricePortable

* Settings dialog fixes

* wording
2017-07-08 11:22:29 +02:00
tooomm
1565309146 enable caching for ci (#2799)
* enable cache

* caching on mac

* fix ci caching on mac

* fix

* add mac cache

* added homebrew cache and pretty print yaml (+ structural ordering)

* cleanup

* fix ccache on mac

* testing workaround

https://github.com/travis-ci/travis-ci/issues/7456#issuecomment-296505058

* try xcode7.3 instead of workaround

* cache timout value

more time for cache to upload
2017-07-07 16:17:18 +02:00
Psithief
b05ed0376a Fixes #2712 (#2797)
When checking unknown sets, consider sets with an empty long name, 
set type and release date as 'incomplete'/'partial' sets.
Do not mark partial sets as known, or ask the user to enable them.
Instead, silently enable partial sets.
When a partial set becomes complete, the user is prompted to enable the
set as before. If they choose not to enable, those partial sets are 
disabled, as we can no longer assume they are disabled by default.
2017-07-07 00:45:46 -04:00
Psithief
61893faf3b Slightly more accurate XSD. (#2787)
XML 1.0 does not provide a method that allows elements with minimum
occurances and variable occurances in any order, so we are stuck
with xs:sequence.
2017-06-29 20:52:44 -04:00
Zach H
e8dbdc3c6d Add token creation for opponent board items (#2744) 2017-06-28 23:09:10 -04:00
tritoch
5abfda49be Import spoiler.xml as spoiler.xml and overwrite existing (#2784) 2017-06-28 22:54:09 -04:00
tritoch
bcda502b46 Loyalty now string, xsd fix (#2785)
Card with `X` loyalty was printed, loyalty now string
2017-06-28 21:42:08 -04:00
Psithief
03bb02f75b Improved XSD (#2781) 2017-06-26 14:42:13 -04:00
Psithief
bfcc48f5bf Check for .xlhq (CCGHQ naming scheme) files (#2778) 2017-06-22 22:59:33 -04:00
Psithief
ae15f5df06 Handling of tokens/related cards improved. (#2773) 2017-06-20 18:35:06 -04:00
tooomm
029a4a39ad readme: https link + layout tweak (#2768) 2017-06-13 15:34:50 +02:00
Zach H
9ddb18a35e Make deck list appear if deck loaded (#2745) 2017-06-12 12:25:52 +02:00
woogerboy21
2094910b7a More descriptive reason for "to many requests" when registering (#2760)
* More descriptive reason for "to many requests" when registering

Currently the only way for a user to get a response of "to many registration attempts"  is by the servers configuration to restrict the number of registration attempts but the error message presented to the user is very vague as to why they have been denied.  This PR updates the reasoning.  I'm open for suggestions on any clarity recommendations.

* Added more clarity

Updated error message to include email address information for even greater clarity.
2017-06-12 12:12:36 +02:00
tooomm
b36c349f9b remove unused define (#2757) 2017-06-11 00:42:09 -04:00
Zach H
99e7458f9b make not set to untap normally more distinct (#2746) 2017-06-04 14:02:35 -04:00
Zach H
0a57229886 keep client version persistant (#2750) 2017-06-04 13:59:37 -04:00
Zach H
f3f83882e0 Model col widths off deck editor & disable button if no tokens in deck (#2751) 2017-06-04 13:58:34 -04:00
Zach H
d970b9e50f fix card not showing correctly in games (#2752) 2017-06-04 13:57:47 -04:00
Zach H
b0d8a31a2e remove invalid connect (#2753) 2017-06-04 13:57:21 -04:00
ctrlaltca
03a7a9fafb Webclient: fix some bugs (#2742)
* Handle room join and leave events + case sensitive userlist sort; fix #2307

* Webclient: add autoscroll, but only if already at the bottom; fix #2306
2017-05-31 23:04:59 -04:00
tooomm
4c953acebc exchange dl stat page (#2743) 2017-05-30 08:32:58 +02:00
tritoch
c5ac61c797 Colorless for color filter (#2737)
* Colorless for color filter

Add 'colorless' as an option for color filter in deck editor / card list.

* Remove empty quote check

Empty quote does not work here. Add 'none' as optional string instead.

* Add C to colorless filters
2017-05-30 08:19:11 +02:00
ctrlaltca
f75caa7245 Webclient overhaul (#2735)
Migrate from protobuf.js 5.x to 6.x (remove long.js and bytebuffer.js)
Upgrade jQuery from 1.x to 3.x
Upgrade jQueryUI to 1.12.x
Use minimized version of js libraries
Disable debug messages
Fix default value for Event_RoomSay’s RoomMessageType field
2017-05-29 14:12:36 +02:00
tooomm
87060dc5c7 created_at to published_at (#2723) 2017-05-22 16:00:38 +02:00
backendr
62681f7f8d contect menu fix (#2719) 2017-05-18 17:03:21 -04:00
Zach H
b53cd33eed Smarter Clipboard Pasting/Parsing (#2706) 2017-05-14 14:35:40 -04:00
backendr
405a719412 Better scrolling in reveal zones (#2716) 2017-05-14 19:01:50 +02:00
Zach H
aca6917d42 Merge pull request #2672 from Cockatrice/tooomm-update_msg_rework
update msg rework
2017-05-13 16:06:53 -04:00
tooomm
73e2c3d32f Publish --> Release 2017-05-13 14:49:35 +02:00
tooomm
7f273b547b add missing tr() 2017-05-13 13:24:04 +02:00
backendr
77e0cddbae counter context sensative (#2714) 2017-05-13 11:48:26 +02:00
backendr
d9230d6c6b Message Log Widget changes (#2709)
* Message Log Widget changes

* Gravard now logs as before

* removed redundant QString()
2017-05-13 11:47:43 +02:00
Zach H
f00bcc9179 Merge pull request #2691 from Cockatrice/tooomm-dl_badges
github dl badges in README
2017-05-10 14:23:33 -04:00
skwerlman
b47e262e7f limit gitlab builds to master (#2711) 2017-05-10 08:53:11 +02:00
woogerboy21
1811bad835 Fix room chat history (#2698)
Looks like an addition to the table shifted things.  We probably  should look into some how use the actual column names (if it can be done).  But for now this should fix the server room chat history.
2017-05-09 23:22:48 -04:00
tooomm
41ebf7a9ad space 2017-05-09 12:47:19 +02:00
tooomm
7f01f921f7 added dl count for latest release 2017-05-09 12:46:41 +02:00
Zach H
d347e54526 Merge pull request #2688 from backendr/ignore_first_server_message
Prevent emitting initial server message event
2017-05-08 19:46:17 -04:00
tooomm
256e40a9ee Merge branch 'master' into tooomm-dl_badges 2017-05-08 10:05:24 +02:00
Zach H
5f4490ec4e Merge pull request #2695 from Cockatrice/tooomm-hq_badges
updated badges (gitter+licence)
2017-05-07 18:54:45 -04:00
tooomm
2fb2481211 updated badges 2017-05-07 12:45:53 +02:00
tooomm
d585b9f1d6 http --> https 2017-05-06 21:47:22 +02:00
Zach H
c745f41cb9 Merge pull request #2692 from Cockatrice/tooomm-cmake_2.3.18
cmake - bump version number
2017-05-06 12:54:50 -04:00
tooomm
5b07f6d6d8 bump version number 2017-05-06 18:21:31 +02:00
tooomm
39779be7bb add alltime dl count 2017-05-06 16:03:27 +02:00
tooomm
70f1c97a27 github dl badges 2017-05-06 15:46:11 +02:00
Zach H
a49373eded Merge pull request #2686 from backendr/log_changes
Log Colors
2017-05-05 21:48:02 -04:00
backendr
55dcf4a845 Log refresh 2017-05-06 00:40:41 +02:00
backendr
b140721d50 Add flag for checking inital server message 2017-05-06 00:18:49 +02:00
Zach H
c96f234b6d Merge pull request #2683 from ctrlaltca/fix_2682
Remove unneeded debug
2017-05-05 13:42:02 -04:00
Fabio Bas
a4e78bbfa2 remove debug 2017-05-05 15:23:59 +02:00
Zach H
fd3d62284d Merge pull request #2670 from ctrlaltca/load_tokens
Improved token loading, removed card price code
2017-05-05 00:24:48 -04:00
Zach H
16bc8b764b remove all of pricing 2017-05-04 23:29:43 -04:00
Fabio Bas
36f6907fa3 Improved token loading 2017-05-04 23:28:51 -04:00
tooomm
339945e089 fix custom build + displaying name (#2668)
* fix custom build name

* remove html and argument from tr()
2017-04-30 16:20:29 +02:00
tooomm
faeb3f8daf fix 2017-04-30 15:17:03 +02:00
tooomm
e04010f00b further tweaks and uniform style 2017-04-30 15:06:32 +02:00
tooomm
cbd9d2c4fc better wording (#2674) 2017-04-29 22:15:43 -04:00
tooomm
fdc82708c6 extend updater description in menu (#2673) 2017-04-29 20:13:33 -04:00
tooomm
8f7691b47a next round 2017-04-29 22:55:49 +02:00
tooomm
ca2e3e5eab trial_error 2017-04-29 22:45:25 +02:00
tooomm
da42d9e049 fix 2017-04-29 22:32:07 +02:00
tooomm
f18679bfff fix attempt 2017-04-29 22:06:53 +02:00
tooomm
970b67cdb6 update message rework 2017-04-29 20:57:52 +02:00
ctrlaltca
3c5e38bd14 Hide some icons for spectators; fix #2589 (#2656)
* fix #2589

* Additional fix
2017-04-29 15:06:30 +02:00
ctrlaltca
b20c60e603 fix #2332 (#2650) 2017-04-29 12:21:37 +02:00
Zach H
12c9e4b81a allow placement below top X cards (#2666)
Close #2666
2017-04-27 20:32:24 -04:00
Zach H
7c1a18da5e Add "Random card from graveyard" (#2652) 2017-04-26 18:12:56 -04:00
ctrlaltca
df211748ca Add clear button to deck editor (#2654) 2017-04-26 15:20:35 -04:00
ctrlaltca
ce77d51a8f Fix crash on logger; make log window modalless (#2659) 2017-04-26 15:05:24 -04:00
tooomm
9dd3a04a08 improve provided info in "edit sets" (#2641) 2017-04-25 15:46:37 -04:00
Zach H
62d8f5a039 change cards from sb<->mb with double-click (#2606) 2017-04-25 14:05:25 -04:00
Fabio Bas
2c551bdd35 Revert "fix #2332"
This reverts commit 0fd77346df.
2017-04-25 19:35:07 +02:00
Fabio Bas
0fd77346df fix #2332 2017-04-25 18:58:01 +02:00
ctrlaltca
a46c7156dd fix_crash (#2649) 2017-04-25 18:13:45 +02:00
ctrlaltca
3c54499a40 sort edit sets better (#2648) 2017-04-25 11:40:47 -04:00
ctrlaltca
160d66d890 show tokens all sets (#2646) 2017-04-25 11:32:51 -04:00
ctrlaltca
3f3839d70a fix deck card backs (#2645) 2017-04-25 11:32:00 -04:00
ctrlaltca
ab94d2c91d Token Dialog Revamp (#2639) 2017-04-25 00:29:57 -04:00
ctrlaltca
8ad448a23c fix #2600 (#2638) 2017-04-25 00:29:32 -04:00
ctrlaltca
acb40bc738 Fix #2608; fix #2609; fix #2611 (#2636) 2017-04-24 22:20:44 +02:00
ctrlaltca
f2a887a6a4 fix #2623 (#2634) 2017-04-24 22:20:19 +02:00
ctrlaltca
29904c49da Specify reason when on game leave; fix #2624 (#2633)
* fix #2624

* feedback++
2017-04-24 22:20:08 +02:00
Fabio Bas
38ad71b06a fix ox compilation 2017-04-24 21:59:09 +02:00
Fabio Bas
7f057612bf possibily fix osx shortcut problem 2017-04-24 20:39:11 +02:00
ctrlaltca
9d2494e6e2 Update README.md adding link to guidelines; fix #2627 (#2635) 2017-04-24 15:52:30 +02:00
tritoch
23d27cff77 Handle 'aftermath' cards like 'split' cards (#2626) 2017-04-22 14:36:22 -04:00
Zach H
176ea444ba revert clear focus (#2619) 2017-04-21 17:52:14 +02:00
ctrlaltca
3e12a5f67f Update CONTRIBUTING.md (#2613)
* Update CONTRIBUTING.md

* Update CONTRIBUTING.md

"wait for CI upload" should be "wait for CI to upload"
"update again the version number" should be "update the version number again"
2017-04-21 14:26:55 +02:00
ctrlaltca
3356b2ede7 Change version in about window and installer name (#2570)
* Change version in about window and installer name

* Change version in about window and installer name

* Cmake get version updates

* dev snapshot format changed

* alignment changes
2017-04-20 13:41:18 +02:00
Zach H
5f3a3f535a clear focus only if needed (#2604) 2017-04-20 05:21:30 -04:00
Zach H
e2e9c5ab96 disable join if spec disabled (#2605) 2017-04-20 03:01:28 -04:00
skwerlman
dd36187864 fix header markdown in TODO.md (#2592) 2017-04-16 21:38:55 -04:00
Zach H
dc6c375220 clear focus when changing phases (#2583) 2017-04-15 14:37:01 -04:00
tritoch
2e9a0bdd4d Fix Pre-release / Dev build link (#2584) 2017-04-11 18:06:13 -04:00
Zach H
1413337b24 Add CONTRIBUTING note on making prereleases (#2576) 2017-04-07 20:59:06 -04:00
Zach H
3b7990b569 fix travis build (#2575)
* fix travis build

* rename dir
2017-04-07 14:41:33 +02:00
Zach H
657e1ac9e6 fix c style casting (#2561) 2017-04-05 15:45:58 -04:00
tooomm
ee154da598 clean transifex translations up (#2566) 2017-04-03 16:35:10 -04:00
Zach H
b3c1a87f60 better bool checking (#2564) 2017-04-01 21:29:23 -04:00
Mark McDonald
9cbae8c707 Decklist testing (#2537) 2017-04-01 01:24:16 -04:00
Zach H
ef7670a1e6 translation file updates (#2559)
* translation file updates

* translation updates
2017-03-31 15:29:43 -04:00
Zach H
d7e5b29d41 Dev channel to GitHub (#2557) 2017-03-31 14:47:49 -04:00
skwerlman
6d07709174 add .gitlab-ci.yml (#2345)
* add .gitlab-ci.yml

* Update to fix issues

fix artifact selection
fix rpm builds
mostly fix debian builds (still have cmake issue)
update to conform with #2373
combined rc and debug stages (more efficient as one stage)
add unused deploy section (can be finished/configured later)

* remove old bintray bits

* fix issues

simplify `mkdir` command
remove bintray deploy
2017-03-31 09:53:00 +02:00
Zach H
51230ade1a correct key (#2553) 2017-03-30 03:35:48 -04:00
tooomm
0e6c3c9561 better description for issue template (#2541)
* more general description for issue template

* fix

* rephrase

* remove part about filename

* fix typos
2017-03-29 11:12:31 -04:00
Zach H
56ec219808 appveyor key change (#2551) 2017-03-28 15:02:34 -04:00
Zach H
fab3d24757 Travis/Appveyor publish to GitHub Releases (#2550) 2017-03-28 13:37:59 -04:00
Zach H
87ebea7b0f fix memory leak (#2549) 2017-03-28 13:13:42 -04:00
tooomm
a273f55b6f exchange tr() for QString() (#2547) 2017-03-27 15:07:17 -04:00
Zach H
06c3edf4c6 cancel downloads from updater (#2534)
* cancel downloads from updater - fix #2534

* fix double popup
2017-03-25 16:35:43 -04:00
Gavin Bisesi
6f30304271 Change stable channel downloads to use only github api and links (#2515)
* Change stable channel downloads to use only github api and links

Ref #2502
2017-03-25 16:35:04 -04:00
tooomm
3374576831 fix toc (#2538) 2017-03-25 16:17:47 -04:00
Gavin Bisesi
54911eebc1 Merge pull request #2536 from Cockatrice/ReadmeUpdate-ZZ
Update README.md

[skip ci]
2017-03-24 16:42:22 -04:00
Zach H
b3563a897e Update README.md
Remove bintray
2017-03-24 16:40:08 -04:00
Gavin Bisesi
1879b906e5 Merge pull request #2532 from ZeldaZach/listover
Temp fix for crash on logging - QList instead of QVector hides whatever double-free bug we're causing
2017-03-24 15:51:26 -04:00
Zach H
559d4bd47f use list over vector 2017-03-23 00:45:52 -04:00
Gavin Bisesi
b99ae7f463 Merge pull request #2501 from Cockatrice/refactor-chatview
Remove direct usages of TabSupervisor from Chatview
2017-03-22 22:22:19 -04:00
Gavin Bisesi
f5a5ea0434 ChatView: Initialize fields in declared order 2017-03-22 21:52:15 -04:00
woogerboy21
2f23a9cb2f Smarter rooms (#2484)
* Added VIP only room

* Added DONATOR room.

* Extended Room to include privilege level.

* Updated room join logic

* Updated server tab permissions column display based on perm+privlevel definitions

* Fixed new client -> old server blank permissions column on server tab

Added the ability for registered user - VIP/DONATOR/PRIVILEGED room.
2017-03-22 21:45:16 -04:00
Zach H
317ac05919 logbuffer crash fix (#2524) 2017-03-22 21:39:16 -04:00
Zach H
f688c046ab Size constraints for Settings Dialog (#2513) 2017-03-22 21:25:40 -04:00
Zach H
141e6df50a Updater dialog changes (#2504) 2017-03-22 21:23:25 -04:00
Gavin Bisesi
8ac2a5870d Un-const non-pointer return types 2017-03-22 21:12:42 -04:00
Gavin Bisesi
a3f4012d1a Reduce TabSupervisor direct usage in UserContextMenu 2017-03-22 20:44:05 -04:00
Gavin Bisesi
c5aa75d4d1 Reduce data dependency for GamesProxyModel 2017-03-22 20:44:05 -04:00
Gavin Bisesi
d65a444ac5 Remove direct usages of TabSupervisor from Chatview
- There still might be inherited usages
- It's still used in the ctor

Areas to test
- Mentions
- Chat notifications
- Username clickable links
2017-03-22 20:44:05 -04:00
tooomm
2c3b85aed3 fix translators link (#1987) 2017-03-22 16:26:59 -04:00
Zach H
85985a9433 Add support for multi-selection in set editor (#2441) 2017-03-22 15:49:38 -04:00
woogerboy21
33e8a2ea95 Fix mysql appveyor (#2529) 2017-03-21 16:42:14 -04:00
Zach H
5ebc9ca360 allow auto connect (#2522) 2017-03-20 18:21:20 -04:00
Zach H
4feb43cdcc changes to connection dialog (#2503) 2017-03-19 20:24:58 -04:00
Zach H
b5b9527c13 Font size defaults (#2521) 2017-03-19 20:20:06 -04:00
Zach H
a2a7561613 allow max font size to be defined by user (#2488) 2017-03-19 14:13:20 -04:00
tooomm
1cc50b2793 remove travis deployment to bintray (#2516) 2017-03-19 12:58:31 -04:00
Zach H
2d401e4aba remove bintray deploy (#2511) 2017-03-19 12:56:16 -04:00
Zach H
0da2bdd7aa additional null checks in player, gameselector, remoteclient, log (#2514) 2017-03-19 12:55:55 -04:00
Zach H
212a7d00db better null checking decklist (#2510) 2017-03-18 20:56:49 -04:00
Zach H
3b2eff2551 file filenames so window updater works (#2500)
* file filenames so window updater works
2017-03-18 18:48:40 -04:00
Zach H
fb194db766 additional null checks & parsing (#2506)
* additional null checks & parsing
2017-03-18 18:48:23 -04:00
Zach H
127a91147c rename from update to check (#2505) 2017-03-18 18:46:19 -04:00
Zach H
2048aac387 STDout debugging (#2499) 2017-03-17 20:45:13 -04:00
Zach H
a8a4557738 auto_connect toggle fix (#2485) 2017-03-16 16:59:48 -04:00
Zach H
a7dbc2e1f6 Fix token window resize (#2480) 2017-03-15 15:53:57 -04:00
Zach H
4cfcca33db Server Connect Remade (#2452)
* Save multiple server configurations
* Each server has its own password/port storage

Close #2452
2017-03-14 17:51:51 -04:00
Zach H
37b43f9916 make settings size vary based on sizeHint() (#2472) 2017-03-14 17:48:02 -04:00
452 changed files with 120374 additions and 82220 deletions

135
.appveyor.yml Normal file
View File

@@ -0,0 +1,135 @@
version: 2.5.2-branch-{branch}-build-{build}
# Skipping commits affecting specific files (GitHub only).
# More details here: https://www.appveyor.com/docs/appveyor-yml and https://www.appveyor.com/docs/how-to/filtering-commits
skip_commits:
files:
- .ci/travis-*
- .github/
- .tx/
- webclient/
- .clang-format
- .*ignore
- .codacy.yml
- .gitlab-ci.yml
- .travis.yml
- '**/*.md'
- Dockerfile
- LICENSE
image: Visual Studio 2017
cache:
- c:\openssl-release
- c:\protobuf-release
- c:\zlib-release
environment:
matrix:
- qt_ver: 5.9\msvc2017_64
openssl_ver: 1.0.2n-x64_86-win64
protobuf_ver: 3.5.1
zlib_ver: 1.2.11
cmake_generator: Visual Studio 15 2017 Win64
cmake_toolset: v141,host=x64
target_arch: win64
vc_arch: amd64
- qt_ver: 5.9\msvc2015 # Qt doesn't provide a msvc2017_32
openssl_ver: 1.0.2n-i386-win32
protobuf_ver: 3.5.1
zlib_ver: 1.2.11
cmake_generator: Visual Studio 15 2017
cmake_toolset: v141
target_arch: win32
vc_arch: amd64_x86
install:
- ps: |
if (Test-Path c:\openssl-release) {
echo "using openssl from cache"
} else {
Invoke-WebRequest "https://indy.fulgan.com/SSL/openssl-$env:openssl_ver.zip" -OutFile c:\openssl-$env:openssl_ver.zip
Expand-Archive -Path c:\openssl-$env:openssl_ver.zip -DestinationPath c:\openssl-release
Set-Location -Path C:\openssl-release
}
if (Test-Path c:\protobuf-release) {
echo "using protobuf from cache"
} else {
Invoke-WebRequest "https://github.com/google/protobuf/releases/download/v$env:protobuf_ver/protobuf-cpp-$env:protobuf_ver.zip" -OutFile c:\protobuf-cpp-$env:protobuf_ver.zip
Expand-Archive -Path c:\protobuf-cpp-$env:protobuf_ver.zip -DestinationPath c:\
Set-Location -Path C:\protobuf-$env:protobuf_ver\cmake
cmake . -G "$env:cmake_generator" -T "$env:cmake_toolset" -Dprotobuf_BUILD_TESTS=0 -Dprotobuf_MSVC_STATIC_RUNTIME=0 -DCMAKE_INSTALL_PREFIX=c:/protobuf-release
msbuild INSTALL.vcxproj /p:Configuration=Release
}
if (Test-Path c:\zlib-release) {
echo "using zlib from cache"
} else {
Invoke-WebRequest "https://github.com/madler/zlib/archive/v$env:zlib_ver.zip" -OutFile c:\zlib-$env:zlib_ver.zip
Expand-Archive -Path c:\zlib-$env:zlib_ver.zip -DestinationPath c:\
Set-Location -Path C:\zlib-$env:zlib_ver
cmake . -G "$env:cmake_generator" -T "$env:cmake_toolset" -DCMAKE_INSTALL_PREFIX=c:/zlib-release
msbuild INSTALL.vcxproj /p:Configuration=Release
}
services:
- mysql
build_script:
- ps: |
New-Item -ItemType directory -Path $env:APPVEYOR_BUILD_FOLDER\build
Set-Location -Path $env:APPVEYOR_BUILD_FOLDER\build
$zlibdir = "c:\zlib-release"
$openssldir = "C:\openssl-release"
$protodir = "c:\protobuf-release"
$protoc = "c:\protobuf-release\bin\protoc.exe"
$mysqldll = "c:\Program Files\MySQL\MySQL Server 5.7\lib\libmysql.dll"
cmake .. -G "$env:cmake_generator" -T "$env:cmake_toolset" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver;$protodir;$zlibdir;$openssldir" "-DWITH_SERVER=1" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" "-DMYSQLCLIENT_LIBRARIES=$mysqldll"
- msbuild PACKAGE.vcxproj /p:Configuration=Release
- ps: |
$exe = dir -name *.exe
$new_name = $exe.Replace(".exe", "-${env:target_arch}.exe")
Push-AppveyorArtifact $exe -FileName $new_name
$cmake_name = $exe.Replace(".exe", "-${env:target_arch}.cmake.txt")
Push-AppveyorArtifact CMakeCache.txt -FileName $cmake_name
$json = New-Object PSObject
(New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII
Push-AppveyorArtifact "latest-$env:target_arch"
$version = $matches['content']
test: off
# Builds for pull requests skip the deployment step altogether
deploy:
# Deploy configuration for "beta" releases
- provider: GitHub
auth_token:
secure: p+7wPVry2XEa6TBm9XH8IaQZbBmXQ/J2ldbGmcIxUZD3NkkPrSRRlmE7Of1CBBIO
tag: "$(APPVEYOR_REPO_TAG_NAME)"
release: "Cockatrice $(APPVEYOR_REPO_TAG_NAME)"
description: "Beta release of Cockatrice"
artifact: /.*\.exe/
force_update: true
draft: false
prerelease: true
on:
APPVEYOR_REPO_TAG: true
APPVEYOR_REPO_TAG_NAME: /([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}-beta(\.([2-9]|[1-9][0-9]))?$/ # regex to match semver naming convention for beta pre-releases
# Deploy configuration for "stable" releases
- provider: GitHub
auth_token:
secure: p+7wPVry2XEa6TBm9XH8IaQZbBmXQ/J2ldbGmcIxUZD3NkkPrSRRlmE7Of1CBBIO
tag: "$(APPVEYOR_REPO_TAG_NAME)"
release: "Cockatrice $(APPVEYOR_REPO_TAG_NAME)"
artifact: /.*\.exe/
force_update: true
draft: false
prerelease: false
on:
APPVEYOR_REPO_TAG: true
APPVEYOR_REPO_TAG_NAME: /([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}$/ # regex to match semver naming convention for stable full releases
# official validator for ".appveyor.yml" config file: https://ci.appveyor.com/tools/validate-yaml
# appveyor config documentation: https://www.appveyor.com/docs/build-configuration/

56
.ci/travis-compile.sh Normal file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -e
./servatrice/check_schema_version.sh
mkdir -p build
cd build
prefix=""
if [[ $TRAVIS_OS_NAME == "osx" ]]; then
export PATH="/usr/local/opt/ccache/bin:$PATH"
prefix="-DCMAKE_PREFIX_PATH=$(echo /usr/local/opt/qt5/)"
fi
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
prefix="-DCMAKE_PREFIX_PATH=$(echo /opt/qt5*/lib/cmake/)"
fi
if [[ $BUILDTYPE == "Debug" ]]; then
cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE $prefix -DTEST=1
make -j2
make test
if [[ $TRAVIS_OS_NAME == "osx" ]]; then
make install
fi
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
cd ..
clang-format -i \
common/*.h \
common/*.cpp \
cockatrice/src/*.h \
cockatrice/src/*.cpp \
oracle/src/*.h \
oracle/src/*.cpp \
servatrice/src/*.h \
servatrice/src/*.cpp
git clean -f
git diff --quiet || (
echo "*****************************************************";
echo "*** This PR is not clean against our code style ***";
echo "*** Run clang-format and fix up any differences ***";
echo "*** Check our CONTRIBUTING.md file for details! ***";
echo "*** Thank you ♥ ***";
echo "*****************************************************";
)
git diff --exit-code
fi
fi
if [[ $BUILDTYPE == "Release" ]]; then
cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE $prefix
make package -j2
fi

View File

@@ -0,0 +1,9 @@
#!/bin/bash
if [[ $TRAVIS_OS_NAME == "osx" ]] ; then
brew update
brew install ccache clang-format protobuf qt
fi
if [[ $TRAVIS_OS_NAME == "linux" ]] ; then
echo Skipping... packages are installed with the Travis apt addon for sudo disabled container builds
fi

25
.clang-format Normal file
View File

@@ -0,0 +1,25 @@
IndentWidth: 4
AccessModifierOffset: -4
ColumnLimit: 120
---
Language: Cpp
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
AllowShortFunctionsOnASingleLine: None
BinPackParameters: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentCaseLabels: true
PointerAlignment: Right

5
.codacy.yml Normal file
View File

@@ -0,0 +1,5 @@
---
exclude_paths:
- '**/translations/*.ts'
# codacy config documentation: https://support.codacy.com/hc/en-us/articles/115002130625-Codacy-Configuration-File

View File

@@ -1,11 +1,9 @@
.git/
CONTRIBUTING.md
Dockerfile
TODO.md
build/
.github/
.travis/
.tx/
cockatrice/
doc/
oracle/
sounds/
travis-compile.sh
travis-dependencies.sh
Dockerfile

View File

@@ -1,12 +1,32 @@
# Style Guide #
&nbsp; [Introduction](#contributing-to-cockatrice) | [Code Style Guide](#code-style-guide) | [Translations](#translations) | [Release Management](#release-management)
----
<br>
# Contributing to Cockatrice #
First off, thanks for taking the time to contribute to our project! 🎉 ❤ ️✨
The following is a set of guidelines for contributing to Cockatrice. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
# Recommended Setups #
For those developers who like the Linux or MacOS environment, many of our developers like working with a nifty program called [CLion](https://www.jetbrains.com/clion/). The program's a great asset and one of the best tools you'll find on these systems, but you're welcomed to use any IDE you most enjoy.
Developers who like Windows development tend to find [Visual Studio](https://www.visualstudio.com/) the best tool for the job.
If you have any questions on IDEs, feel free to chat with us on [Gitter](https://gitter.im/Cockatrice/Cockatrice) and we would love to help answer your questions!
# Code Style Guide #
### Compatibility ###
Cockatrice is compiled on all platform using C++11, even if the majority of the
code is written in C++03.
Cockatrice is currently compiled on all platforms using <kbd>C++11</kbd>. You'll notice <kbd>C++03</kbd> code throughout the codebase. Please feel free to help convert it over!
For consistency, use Qt data structures where possible, such as `QString` over
`std::string` or `QList` over `std::vector`.
For consistency, we use Qt data structures where possible. For example, `QString` over
`std::string` and `QList` over `std::vector`.
### Header files ###
@@ -20,13 +40,13 @@ but other functions should be written in the source file.
Keep library includes and project includes grouped together. So this is okay:
```c++
// Good:
// Good
#include <QList>
#include <QString>
#include "card.h"
#include "deck.h"
// Good:
// Good
#include "card.h"
#include "deck.h"
#include <QList>
@@ -38,6 +58,7 @@ Keep library includes and project includes grouped together. So this is okay:
#include <QString>
#include "deck.h"
```
### Naming ###
Use `UpperCamelCase` for classes, structs, enums, etc. and `lowerCamelCase` for
@@ -49,18 +70,19 @@ underscores, etc.
For arguments to constructors which have the same names as member variables,
prefix those arguments with underscores:
```c++
MyClass::MyClass(int _myData)
: myData(_myData)
{}
MyClass::MyClass(int _myData) : myData(_myData)
{
}
```
Pointers and references should be denoted with the `*` or `&` going with the
variable name:
```c++
// Good:
// Good
Foo *foo1 = new Foo;
Foo &foo2 = *foo1;
// Bad:
// Bad
Bar* bar1 = new Bar;
Bar& bar2 = *bar1;
```
@@ -69,32 +91,41 @@ If you find any usage of the old keywords, we encourage you to fix it.
### Braces ###
Use K&R-style braces. Braces for function implementations go on their own
lines, but they go on the same line everywhere else:
Braces should almost always go on their own line:
```c++
int main()
{
if (someCondition) {
if (someCondition)
{
doSomething();
} else {
while (someOtherCondition) {
}
else if (someOtherCondition1)
{
for (int i = 0; i < 100; i++)
{
doSomethingElse();
}
}
else
{
while (someOtherCondition2)
{
doSomethingElse();
}
}
}
```
Braces can be omitted for single-statement if's and the like, as long as it is
still legible.
Braces should never be omitted for single-statement. Keeping the code legibile is a high priority of ours and we hope you share a similar belief :)
### Tabs ###
### Tabs vs Spaces ###
Use only spaces. Four spaces per tab.
We _highly_ encourate the use of spaces. If you use tabs, please readjust them to 4 spaces per tab before submitting.
### Lines ###
Do not have trailing whitespace in your lines.
Do not have trailing whitespace in your lines, if possible. Most IDEs check for this nowadays and clean it up for you.
Lines should be 80 characters or less, as a soft limit.
Lines should be 120 characters or less, but you can exceed this if you find it necessary.
### Memory Management ###
@@ -108,7 +139,7 @@ int main()
Card card;
showCard(card);
}
// Bad: relies on manual memory management and doesn't give us much
// null-safety.
void showCard(const Card *card);
@@ -141,19 +172,25 @@ The migration file should include the sql statements needed to migrate the datab
Cockatrice and Servatrice exchange data using binary messages. The syntax of these messages is defined in the `proto` files in the `common/pb` folder. These files defines the way data contained in each message is serialized using Google's [protocol buffer](https://developers.google.com/protocol-buffers/).
Any change to the `proto` file should be taken with caution and tested intensively before being merged, becaus a change to the protocol could make new clients incompatible to the old server and vice versa.
### Translations: introduction ###
You can find more information on how we use Protobuf on [our wiki!](https://github.com/Cockatrice/Cockatrice/wiki/Client-server-protocol)
Basic workflow for translations:
1. developer adds a `tr("foo")` string in the code;
2. every few days, a maintainer updates the `*_en.ts files` adding the new strings;
# Translations #
**Basic workflow for translations:**
1. Developer adds a `tr("foo")` string in the code;
2. Every few days, a maintainer updates the `*_en.ts files` with the new strings;
3. Transifex picks up the new files from github every 24 hours;
4. translators translate the new untraslated strings on Transifex;
5. before a release, a maintainer fetches the updated translations from Transifex.
4. Translators translate the new untraslated strings on Transifex;
5. Before a release, a maintainer fetches the updated translations from Transifex.
### Translations (for developers) ###
**Step 1: Adding translatable strings to the code (`tr("foo")`)**
All the user-interface strings inside Cockatrice's source code must be written in
english language. Translations to other languages are managed using [Transifex](https://www.transifex.com/projects/p/cockatrice/).
english language.<br>
Translations to other languages are managed using [Transifex](https://www.transifex.com/projects/p/cockatrice/).
If you're about to propose a change that adds or modifies any translatable string
in the code, you don't need to take care of adding the new strings to the
@@ -164,7 +201,7 @@ translators on Transifex.
### Translations (for maintainers) ###
#### Step 2: updating *_en.ts files ####
**Step 2: Updating `*_en.ts` files with new strings**
When new translatable strings have been added to the code, it would be nice to
make them available to translators on Transifex. Every few days, or when a lot
@@ -194,12 +231,14 @@ It's now suggested to disable the parameter using:
```sh
cmake .. -DUPDATE_TRANSLATIONS=OFF
```
Now you are ready to propose your change. Once your change gets merged,
Transifex will pick up the modified files automatically (checks every 24 hours)
and update the interface where translators will be able to translate the new
strings.
Now you are ready to propose your change.
#### Step 5: fetch new translations from Transifex ####
**Step 3: Automatic pushing to Transifex**
Once your change gets merged, Transifex will pick up the modified files automatically (checks every 24 hours)
and update the interface where translators will be able to translate the new strings.
**Step 5: Fetching new translations from Transifex**
Before rushing out a new release, it would be nice to fetch the most up to date
translations from Transifex and commit them into the Cockatrice source code.
@@ -215,4 +254,52 @@ from Transifex to the source code and vice versa.
### Translations (for translators) ###
**Step 4: Editing translations at Transifex**
Please have a look at the specific [FAQ for translators](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ).
# Release Management #
### Publishing A New Beta Release ###
Travis and AppVeyor have been configured to upload files to GitHub Releases whenever a <kbd>tag</kbd> is pushed.<br>
Usually, tags are created through publishing a (pre-)release, but there's a way around that.
To trigger Travis and AppVeyor, simply do the following:
```bash
cd $COCKATRICE_REPO
git checkout master
git remote update -p
git pull
git tag $TAG_NAME
git push upstream $TAG_NAME
```
You should define the variables as such:
```
upstream - git@github.com:Cockatrice/Cockatrice.git
$COCKATRICE_REPO - /Location/of/repository/cockatrice.git
`$TAG_NAME` should be:
- `YYYY-MM-DD-Release-MAJ.MIN.PATCH` for **stable releases**
- `YYYY-MM-DD-Development-MAJ.MIN.PATCH-beta.X` for **beta releases**<br>
With *MAJ.MIN.PATCH* being the NEXT release version!
```
This will cause a tagged release to be established on the GitHub repository, which will then lead to the upload of binaries. If you use this method, the tags (releases) that you create will be marked as a "Pre-release". The `/latest` URL will not be impacted (for stable release downloads) so that's good.
If you accidentally push a tag incorrectly (the tag is outdated, you didn't pull in the latest branch accidentally, you named the tag wrong, etc.) you can revoke the tag by doing the following:
```bash
git push --delete upstream $TAG_NAME
git tag -d $TAG_NAME
```
**NOTE:** Unfortunately, due to the method of how Travis and AppVeyor work, to publish a stable release you will need to make a copy of the release notes locally and then paste them into the GitHub GUI once the binaries have been uploaded by them. These CI services will automatically overwrite the name of the release (to "Cockatrice $TAG_NAME"), the status of the release (to "Pre-release"), and the release body (to "Beta build of Cockatrice").
**NOTE 2:** In the first lines of https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt there's an hardcoded version number used when compiling custom (not tagged) versions. While on tagged versions these numbers are overriden by the version numbers coming from the tag title, it's a good practice to keep them aligned with the real ones.
The preferred flow of operations is:
* just before a release, update the version number in CMakeLists.txt to "next release version";
* tag the release following the previously described syntax in order to get it built by CI;
* wait for CI to upload the binaries, double check if everything is in order
* after the release is complete, update the version number again to "next targeted beta version", typically increasing `PROJECT_VERSION_PATCH` by one.
**NOTE 3:** When releasing a new stable version, all the previous beta versions should be deleted. This is needed for Cockatrice to pick up the stable release also for users that chose the "beta" release channel.

View File

@@ -1,11 +1,14 @@
<b>OS:</b>
*Override this line with the exact operating system you are running! (e.g. "Win 7 SP1", "OS X 10.8.5", "Ubuntu 15.10" ...)*
<b>System Information:</b>
<!-- Go to "Help → View Debug Log" and copy all lines above the separation here! -->
<b>Cockatrice version:</b>
*Put your Cockatrice version number & build date here! You find them inside the app: `Help` --> `About Cockatrice` (e.g. "2d53ce9 (2016-02-18)"). If you can't access this menu for any reason, please include the full filename of the installer you used.*
<!-- If you can't install Cockatrice to access that information, make
sure to include your OS and the app version from the setup file here -->
__________________________________________________________________________________________
___
<br>
<!-- Explain your issue/request/suggestion in detail here! -->
*Explain your Issue/Request/Suggestion in detail here!*
<!-- This repository is ONLY about development of the Cockatrice app.
If you have any problems with a server (e.g. registering, connecting, ban...)
you have to contact that server's owner/admin.
Check this list of public servers with webpage links and contact details:
https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers -->

View File

@@ -9,4 +9,4 @@
- and this
## Screenshots
*(simply drag & drop image files directly into this description!)*
<!-- simply drag & drop image files directly into this description! -->

139
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,139 @@
---
stages:
- build
.artifacts: &artifacts
artifacts:
paths:
- build/
.cache: &cache
cache:
key: "$CI_BUILD_NAME"
paths:
- cache/
.branches: &branches
only:
- master
.tags: &tags
tags:
- linux
- docker
#================================ DEBIAN-BASED ================================
.build_rc_package_deb: &build_rc_package_deb
stage: build
script:
- mkdir -p build
- cd build
- cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=Release -DCPACK_GENERATOR=DEB
- make package -j2
.build_debug_package_deb: &build_debug_package_deb
stage: build
script:
- mkdir -p build
- cd build
- cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=Debug -DCPACK_GENERATOR=DEB
- make package -j2
.deb-artifacts: &artifacts_deb
artifacts:
paths:
- build/*.deb
- build/CMakeFiles/*.log
when: always
#----------------------------------- UBUNTU -----------------------------------
.requirements_16xx: &install_requirements_16xx
before_script:
- apt-get -o dir::cache::archives="cache" update -qq
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
- apt-get -o dir::cache::archives="cache" install -y qt5-default qttools5-dev qttools5-dev-tools
- apt-get -o dir::cache::archives="cache" install -y qtmultimedia5-dev libqt5multimedia5-plugins
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5sql5-mysql
- apt-get -o dir::cache::archives="cache" install -y libqt5websockets5-dev
.requirements_17xx: &install_requirements_17xx
before_script:
- apt-get -o dir::cache::archives="cache" update -qq
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
- apt-get -o dir::cache::archives="cache" install -y qt5-default qttools5-dev qttools5-dev-tools
- apt-get -o dir::cache::archives="cache" install -y qtmultimedia5-dev libqt5multimedia5-plugins
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5sql5-mysql
- apt-get -o dir::cache::archives="cache" install -y libqt5websockets5-dev
.build_1604: &1604
image: ubuntu:16.04
<<: *tags
<<: *branches
<<: *install_requirements_16xx
<<: *artifacts_deb
<<: *cache
.build_1710: &1710
image: ubuntu:17.10
<<: *tags
<<: *branches
<<: *install_requirements_17xx
<<: *artifacts_deb
<<: *cache
build_rc_1604:
<<: *1604
<<: *build_rc_package_deb
when: always
build_debug_1604:
<<: *1604
<<: *build_debug_package_deb
when: always
build_rc_1710:
<<: *1710
<<: *build_rc_package_deb
when: always
build_debug_1710:
<<: *1710
<<: *build_debug_package_deb
when: always
allow_failure: true
#----------------------------------- DEBIAN -----------------------------------
.requirements_stretch: &install_requirements_stretch
before_script:
- apt-get -o dir::cache::archives="cache" update -qq
- apt-get -o dir::cache::archives="cache" install -y build-essential g++ cmake git
- apt-get -o dir::cache::archives="cache" install -y qt5-default qtbase5-dev-tools
- apt-get -o dir::cache::archives="cache" install -y qttools5-dev-tools qtmultimedia5-dev
- apt-get -o dir::cache::archives="cache" install -y libqt5svg5-dev libqt5websockets5-dev
- apt-get -o dir::cache::archives="cache" install -y libprotobuf-dev protobuf-compiler
.build_stretch: &stretch
image: debian:stretch
<<: *tags
<<: *branches
<<: *install_requirements_stretch
<<: *artifacts_deb
<<: *cache
build_rc_stretch:
<<: *stretch
<<: *build_rc_package_deb
when: always
build_debug_stretch:
<<: *stretch
<<: *build_debug_package_deb
when: always
allow_failure: true

View File

@@ -1,5 +0,0 @@
{
"message": "@pullRequester, thanks for contributing! @reviewers are the best people to review the changes.",
"fileBlacklist": ["*.md"],
"userBlacklist": ["@mbruker", "@Psithief"],
}

View File

@@ -1,39 +1,98 @@
language: cpp
cache: ccache
matrix:
fast_finish: true
include:
# linux debug build, trusty + gcc + qt5
- os: linux
dist: trusty
env: BUILDTYPE=Debug DIST=trusty
# osx debug build, osx + clang + qt5
- os: osx
osx_image: xcode6.4
env: BUILDTYPE=Debug
# linux trusty release build, precise + gcc + qt5
- os: linux
dist: trusty
env: BUILDTYPE=Release DIST=trusty
# osx release build, osx + gcc + qt5
- os: osx
osx_image: xcode6.4
env: BUILDTYPE=Release
script: ./travis-compile.sh
install: ./travis-dependencies.sh
cache: apt
- os: linux
dist: trusty
group: stable
env: BUILDTYPE=Debug
- os: linux
dist: trusty
group: stable
env: BUILDTYPE=Release
if: (branch = master AND NOT type = pull_request) OR tag IS present
- os: osx
osx_image: xcode8
env: BUILDTYPE=Debug
- os: osx
osx_image: xcode8
env: BUILDTYPE=Release
if: (branch = master AND NOT type = pull_request) OR tag IS present
#install dependencies for container-based "linux" builds
addons:
apt:
sources:
- sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main'
key_url: 'http://llvm.org/apt/llvm-snapshot.gpg.key'
packages:
- bc
- clang-format-5.0
- cmake
- libprotobuf-dev
- protobuf-compiler
- qt5-default
- qttools5-dev
- qttools5-dev-tools
- qtmultimedia5-dev
- libqt5multimedia5-plugins
- libqt5svg5-dev
- libqt5sql5-mysql
before_install: bash ./.ci/travis-dependencies.sh
script: bash ./.ci/travis-compile.sh
# Builds for pull requests skip the deployment step altogether
deploy:
# Deploy configuration for "beta" releases
- provider: releases
api_key:
secure: mLMF41q7xgOR1sjczsilEy7HQis2PkZCzhfOGbn/8FoOQnmmPOZjrsdhn06ZSl3SFsbfCLuClDYXAbFscQmdgjcGN5AmHV+JYfW650QEuQa/f4/lQFsVRtEqUA1O3FQ0OuRxdpCfJubZBdFVH8SbZ93GLC5zXJbkWQNq+xCX1fU=
skip_cleanup: true
name: "Cockatrice $TRAVIS_TAG"
body: "Beta release of Cockatrice"
file_glob: true
file: "build/Cockatrice-*"
overwrite: true
draft: false
prerelease: true
on:
tags: true
repo: Cockatrice/Cockatrice
condition: $BUILDTYPE = Release && $TRAVIS_TAG =~ ([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}-beta(\.([2-9]|[1-9][0-9]))?$ # regex to match semver naming convention for beta pre-releases
# Deploy configuration for "stable" releases
- provider: releases
api_key:
secure: mLMF41q7xgOR1sjczsilEy7HQis2PkZCzhfOGbn/8FoOQnmmPOZjrsdhn06ZSl3SFsbfCLuClDYXAbFscQmdgjcGN5AmHV+JYfW650QEuQa/f4/lQFsVRtEqUA1O3FQ0OuRxdpCfJubZBdFVH8SbZ93GLC5zXJbkWQNq+xCX1fU=
skip_cleanup: true
file_glob: true
file: "build/Cockatrice-*"
overwrite: true
draft: false
prerelease: false
on:
tags: true
repo: Cockatrice/Cockatrice
condition: $BUILDTYPE = Release && $TRAVIS_TAG =~ ([0-9]|[1-9][0-9])(\.([0-9]|[1-9][0-9])){2}$ # regex to match semver naming convention for stable full releases
notifications:
slack: cockatrice:dNA81maCU8SAHB7pYrCWiQg9
webhooks:
urls:
- https://webhooks.gitter.im/e/d94969c3b01b22cbdcb7
- https://webhooks.gitter.im/e/d94969c3b01b22cbdcb7
on_success: change
on_failure: change
on_start: never
deploy:
provider: bintray
file: "build/bintray_deploy.json"
user: "ctrlaltca"
key:
secure: DtVeeLoi5fZG/RvLTecRnRQGW9fVNS4Oa5iut2vJa14PdKBAJiXACQ0EzcRJFsbtby7MyMc2IVtT5skpvsaSqkpaxoBaL1YtKwJ4CTkYcm2MDWHS7UlijuxxTjI6BnaL3lcCCIeG+NHBZa3dV2YNJ1sWv6Xmiiix1ujPPW8VtnM=
on:
condition: $BUILDTYPE = Release
on_cancel: change
on_error: change
# official validator for ".travis.yml" config file: https://yaml.travis-ci.org
# travis config documentation: https://docs.travis-ci.com/user/customizing-the-build

View File

@@ -10,4 +10,3 @@ source_lang = en
file_filter = oracle/translations/oracle_<lang>.ts
source_file = oracle/translations/oracle_en.ts
source_lang = en

View File

@@ -8,23 +8,13 @@
# Cmake 3.1 is required to enable C++11 support correctly
cmake_minimum_required(VERSION 3.1)
if(POLICY CMP0020)
cmake_policy(SET CMP0020 OLD)
endif()
if(POLICY CMP0043)
cmake_policy(SET CMP0043 OLD)
endif()
if(POLICY CMP0048)
cmake_policy(SET CMP0048 OLD)
endif()
if(POLICY CMP0064)
cmake_policy(SET CMP0064 OLD)
cmake_policy(SET CMP0064 NEW)
endif()
set(PROJECT_NAME "Cockatrice")
if(POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
# Default to "Release" build type
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
@@ -34,8 +24,17 @@ ELSE()
SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build")
ENDIF()
# Early detect ccache
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
# Support Unix Makefiles and Ninja
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
MESSAGE(STATUS "Found CCache ${CCACHE_PROGRAM}")
endif()
# A project name is needed for CPack
PROJECT("${PROJECT_NAME}")
# Version can be overriden by git tags, see cmake/getversion.cmake
PROJECT("Cockatrice" VERSION 2.5.2)
# Use c++11 for all targets
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ ISO Standard")
@@ -56,10 +55,28 @@ include(createversionfile)
# Define a proper install path
if(UNIX)
if(APPLE)
# MacOS X
# macOS
# Due to the special bundle structure ignore
# the prefix eventually set by the user.
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release)
# Force ccache usage if available
get_property(RULE_LAUNCH_COMPILE GLOBAL PROPERTY RULE_LAUNCH_COMPILE)
if(RULE_LAUNCH_COMPILE)
MESSAGE(STATUS "Force enabling CCache usage under macOS")
# Set up wrapper scripts
configure_file(${CMAKE_MODULE_PATH}/launch-c.in launch-c)
configure_file(${CMAKE_MODULE_PATH}/launch-cxx.in launch-cxx)
execute_process(COMMAND chmod a+rx
"${CMAKE_BINARY_DIR}/launch-c"
"${CMAKE_BINARY_DIR}/launch-cxx")
# Set Xcode project attributes to route compilation through our scripts
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
endif()
else()
# Linux / BSD
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
@@ -78,8 +95,6 @@ endif()
# Define proper compilation flags
IF(MSVC)
# Visual Studio:
# Support from Windows XP
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,5.01")
# Maximum optimization
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
# Generate complete debugging information
@@ -115,29 +130,23 @@ OPTION(UPDATE_TRANSLATIONS "Update translations on compile" OFF)
MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}")
IF(WIN32)
FIND_PACKAGE(Qt5Widgets 5.4.0 REQUIRED) # For QSysInfo::buildAbi()
FIND_PACKAGE(Qt5Core 5.4.0 REQUIRED) # For QSysInfo::buildAbi()
ELSE()
FIND_PACKAGE(Qt5Widgets 5.0.3 REQUIRED)
FIND_PACKAGE(Qt5Core 5.0.3 REQUIRED)
ENDIF()
IF(Qt5Widgets_FOUND)
MESSAGE(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
IF(Qt5Core_FOUND)
MESSAGE(STATUS "Found Qt ${Qt5Core_VERSION_STRING}")
# FIX: Qt was built with -reduce-relocations
if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
FIND_PACKAGE(Qt5LinguistTools)
IF(UPDATE_TRANSLATIONS)
IF(NOT Qt5_LUPDATE_EXECUTABLE)
MESSAGE(WARNING "Qt's lupdate not found.")
ENDIF()
ENDIF()
IF(NOT Qt5_LRELEASE_EXECUTABLE)
MESSAGE(WARNING "Qt's lrelease not found.")
ENDIF()
# guess plugins and libraries directory
set(QT_PLUGINS_DIR "${Qt5Core_DIR}/../../../plugins")
get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION)
get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH)
ELSE()
MESSAGE(FATAL_ERROR "No Qt5 found!")
@@ -158,10 +167,11 @@ set(CPACK_PACKAGE_CONTACT "Gavin Bisesi <Daenyth+github@gmail.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_VERSION_FILENAME}")
if(UNIX)
if(APPLE)
@@ -170,7 +180,6 @@ if(UNIX)
set(CPACK_DMG_FORMAT "UDBZ")
set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}")
set(CPACK_SYSTEM_NAME "OSX")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns")
else()
# linux
@@ -186,24 +195,25 @@ if(UNIX)
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
ENDIF()
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
endif()
elseif(WIN32)
set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR})
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
# Configure file with custom definitions for NSIS.
configure_file(
${CMAKE_MODULE_PATH}/NSIS.definitions.nsh.in
${PROJECT_BINARY_DIR}/NSIS.definitions.nsh
)
endif()
# Configure file with build deployment data for travis
configure_file(
${CMAKE_MODULE_PATH}/bintray_deploy.json.in
${PROJECT_BINARY_DIR}/bintray_deploy.json
)
# include vcredist into the package; NSIS will take care of running it
IF(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
INSTALL(FILES "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/redist/MSVC/14.13.26020/vc_redist.x64.exe" DESTINATION ./)
else()
INSTALL(FILES "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/redist/MSVC/14.13.26020/vc_redist.x86.exe" DESTINATION ./)
endif()
ENDIF()
endif()
include(CPack)

View File

@@ -19,7 +19,7 @@ RUN apt-get update && apt-get install -y\
ENV dir /home/servatrice/code
WORKDIR $dir
RUN mkdir oracle
COPY COPYING COPYING
COPY LICENSE LICENSE
COPY CMakeLists.txt CMakeLists.txt
COPY cmake/ cmake/
COPY common/ common/

View File

View File

@@ -2,52 +2,75 @@
---
**Table of Contents** &nbsp;&nbsp; [Cockatrice](#cockatrice) | [Downloads](#downloads) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building--) | [Running](#running) | [License](#license-)
<p align='center'>
<a href="#cockatrice"><b>Cockatrice</b></a> <b>|</b>
<a href="#download-">Download</a> <b>|</b>
<a href="#get-involved-">Get Involved</a> <b>|</b>
<a href="#community-resources">Community</a> <b>|</b>
<a href="#translations-">Translations</a> <b>|</b>
<a href="#build--">Build</a> <b>|</b>
<a href="#run">Run</a> <b>|</b>
<a href="#license-">License</a>
</p>
---
<br><pre>
<b>If you're getting started &#8674; [view our webpage](https://cockatrice.github.io/)</b><br>
<b>If you're trying to get support or suggest changes &#8674; [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket))</b>
<b>To get started, &#8674; [view our webpage](https://cockatrice.github.io/)</b><br>
<b>To get support or suggest changes &#8674; [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b>
<b>To help with development, see how to [get involved](#get-involved-)</b>
</pre><br>
# Cockatrice
Cockatrice is an open-source multiplatform supported program for playing tabletop card games over a network. The program's server design prevents any kind of client modifications to gain an unfair advantage in a game. The client also has a built in single-player mode where you can brew without being connected to a server. This project is written in C++ and is using the Qt5 libraries.<br>
Cockatrice is an open-source, multiplatform program for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline. This project uses C++ and the Qt5 libraries.<br>
# Downloads
# Download [![Cockatrice Eternal Download Count](https://img.shields.io/github/downloads/cockatrice/cockatrice/total.svg)](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
We offer downloads for all full releases (recommended) and the latest development versions. Full releases are checkpoints with major feature or UI enhancements between them, but currently we don't have a set schedule for releasing new updates. The development version contains the most recently added features and bugfixes, but can be more unstable. Downloads for development versions are updated automatically with every change.
Downloads are hosted on [BinTray](https://bintray.com/cockatrice/Cockatrice).
Downloads are available for full releases and the current beta version in development.<br>
Full releases are checkpoints featuring major feature or UI enhancements - we recommend to use those. There is no strict schedule for new full releases.
- Latest full release (**recommended**): [ ![Download](https://api.bintray.com/packages/cockatrice/Cockatrice/Cockatrice/images/download.svg) ](https://bintray.com/cockatrice/Cockatrice/Cockatrice/_latestVersion#files)<br>
The beta release contains the most recently added features and bugfixes, but can be unstable. They are released as we feel need.
- Latest development version: [ ![Download](https://api.bintray.com/packages/cockatrice/Cockatrice/Cockatrice-git/images/download.svg) ](https://bintray.com/cockatrice/Cockatrice/Cockatrice-git/_latestVersion#files)<br>
*Development builds may not be stable or contain several bugs. Especially if from a branch other than `master`!*
- Latest `stable` release (**recommended**): [![Download from GitHub Releases](https://img.shields.io/github/release/cockatrice/cockatrice.svg)](https://github.com/cockatrice/cockatrice/releases/latest) [![DL Count on Latest Release](https://img.shields.io/github/downloads/cockatrice/cockatrice/latest/total.svg)](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)<br>
- Latest `beta` release: [![Download from GitHub Pre-Releases](https://img.shields.io/github/release/cockatrice/cockatrice/all.svg)](https://github.com/cockatrice/cockatrice/releases) [![DL Count on Latest Pre-Release](https://img.shields.io/github/downloads-pre/cockatrice/cockatrice/latest/total.svg)](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice)
- Beta versions may be unstable and contain bugs.
- To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)!
# Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice)
# Get Involved [![Gitter Chat](https://img.shields.io/gitter/room/Cockatrice/Cockatrice.svg)](https://gitter.im/Cockatrice/Cockatrice)
[Chat](https://gitter.im/Cockatrice/Cockatrice) with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
If you'd like to contribute code to the project, we maintain a tag for "easy" changes on our issue tracker. Issues tagged in this way provide a simple way to get started. [Issues tagged as Easy Changes](https://github.com/Cockatrice/Cockatrice/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+Change%22)
To contribute code to the project, please review [the guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md).
We maintain two tags for contributors to find issues to work on:
- [Good first issue](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20): issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on.
- [Help wanted](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Help%20Wanted%22%20): This tag is used for issues that we are looking for a contributor to work on. Often this is for feature suggestions we are willing to accept, but don't have the time to work on ourselves.
We try to be very responsive to new issues. We'll try to give you advice on how a feature should be implemented / advice on places the codebase is doing something similar before you get too far along with a PR.
For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code review for changes they submit.
Read the long-term project **roadmap** to see planned edits and milestones [here](https://docs.google.com/document/d/1Ewe5uSaRE2nR2pNPMaGmP6gVZdqgFbBgwSscGqIr4W0/edit).
We try to be responsive to new issues. We'll provide advice on how best to implement a feature; alternately, we can show you where the codebase is doing something similar before you get too far along.
Cockatrice uses the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
# Community Resources
- [Cockatrice Official Site](https://cockatrice.github.io)
- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
- [Cockatrice Official Discord](https://discord.gg/3Z9yzmA)
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
# Translation Status [![Cockatrice on Transiflex](https://tx-assets.scdn5.secure.raxcdn.com/static/charts/images/tx-logo-micro.c5603f91c780.png)](https://www.transifex.com/projects/p/cockatrice/)
Cockatrice uses Transifex for translations. You can help us bring Cockatrice and Oracle to your language or just edit single wordings right from within your browser by simply visiting our [Transifex project page](https://www.transifex.com/projects/p/cockatrice/).<br>
# Translations [![Cockatrice on Transiflex](https://tx-assets.scdn5.secure.raxcdn.com/static/charts/images/tx-logo-micro.c5603f91c780.png)](https://www.transifex.com/projects/p/cockatrice/)
Cockatrice uses Transifex for translations. You can help us bring Cockatrice and Oracle to your language or just edit single wordings right from within your browser by visiting our [Transifex project page](https://www.transifex.com/projects/p/cockatrice/).<br>
| Cockatrice | Oracle |
|:-:|:-:|
| [![Cockatrice Translation Status](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/chart/image_png/)](https://www.transifex.com/projects/p/cockatrice/) | [![Oracle Translation Status](https://www.transifex.com/projects/p/cockatrice/resource/oracle/chart/image_png/)](https://www.transifex.com/projects/p/cockatrice/) |
@@ -55,7 +78,7 @@ Cockatrice uses Transifex for translations. You can help us bring Cockatrice and
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!<br>
# Building [![Travis Build Status - master](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) [![Appveyor Build Status - master](https://ci.appveyor.com/api/projects/status/lp5h0dhk4mhmeps7/branch/master?svg=true)](https://ci.appveyor.com/project/Daenyth/cockatrice/branch/master)
# Build [![Travis Build Status - master](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) [![Appveyor Build Status - master](https://ci.appveyor.com/api/projects/status/lp5h0dhk4mhmeps7/branch/master?svg=true)](https://ci.appveyor.com/project/Daenyth/cockatrice/branch/master)
**Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
@@ -65,7 +88,7 @@ Dependencies:
- [CMake](https://www.cmake.org/)
Oracle can optionally use zlib to load zipped files:
- [zlib](http://www.zlib.net/) (no https!)
- [zlib](https://www.zlib.net/)
To compile:
@@ -73,7 +96,7 @@ To compile:
cd build
cmake ..
make
You can then run
make install
@@ -89,33 +112,33 @@ The following flags can be passed to `cmake`:
- `-DWITH_SERVER=1` Whether to build the server (default 0 = no).
- `-DWITH_CLIENT=0` Whether to build the client (default 1 = yes).
- `-DWITH_ORACLE=0` Whether to build oracle (default 1 = yes).
- `-DPORTABLE=1` Build portable versions of client & oracle (default 0 = no).
- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings (default `Release`).
- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no).
- `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```.
# Running
# Run
`cockatrice` is the game client
`oracle` fetches card data
`servatrice` is the server<br>
`Cockatrice` is the game client
`Oracle` fetches card data
`Servatrice` is the server<br>
#### Servatrice Docker container
**Servatrice Docker container**
A Dockerfile is provided to run Servatrice (the Cockatrice server) using [Docker](https://www.docker.com/what-docker).<br>
You can run an instance of Servatrice (the Cockatrice server) using [Docker](https://www.docker.com/what-docker) and the Cockatrice Dockerfile.<br>
You just need to create an image from the Dockerfile<br>
First, create an image from the Dockerfile<br>
`cd /path/to/Cockatrice-Repo/`
`docker build -t servatrice .`<br>
And then run it<br>
`docker run -i -p 4747:4747/tcp -t servatrice:latest`<br>
Please note that running this command will expose the TCP port 4747 of the docker container to permit connections to the server.<br>
More infos on how to use Servatrice with Docker can be found in our [wiki](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
>Note: Running this command exposes the TCP port 4747 of the docker container<br>
to permit connections to the server.
Find more information on how to use Servatrice with Docker in our [wiki](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
# License [![GPLv2 License](https://img.shields.io/badge/License-GPLv2-blue.svg)](https://github.com/Cockatrice/Cockatrice/blob/master/COPYING)
Cockatrice is free software, licensed under the [GPLv2](https://github.com/Cockatrice/Cockatrice/blob/master/COPYING).
# License [![GPLv2 License](https://img.shields.io/github/license/Cockatrice/Cockatrice.svg)](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)
Cockatrice is free software, licensed under the [GPLv2](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE).

27
TODO.md
View File

@@ -1,27 +0,0 @@
#TODOs
This is an unordered list of possible todo items for Cockatrice.
Note that "improve" and "write" always also means: "document and comment"
##Improve packaging:
* Improve nsis file git hash extraction, it only works if the build directory is cleared as version_string.cpp does not seem to get updated by git pull/cmake
* Create script/... for creating Linux packages (deb, rpm, ebuild, ...) or at least an official tarball/git tags; package maintainers dislike using git snapshots so much that they rather ignore software without stable tarballs.
##Scripts
* Write example init script for servatrice.
##Create developer documentation:
* Create developer manual
* Add comments to code
* Describe which components exist and how they work and interact
* Describe the *.proto files
* Comment and document servatrice.sql
* Document everything!1!!
##Else
* Update SFMT library (http://www.math.sci.hiroshima-u.ac.jp/~m-mat@math.sci.hiroshima-u.ac.jp/MT/SFMT/) in common/sfmt and adapt common/rng_sfmt.cpp
* Move hardcoded URLs (especially from oracle and cockatrice) into a config file.
* Search git log for useful information/problems/bugs/...

View File

@@ -1,101 +0,0 @@
version: 0.0.1-branch-{branch}-build-{build}
cache:
- c:\protobuf
- c:\protoc
- c:\zlib
environment:
matrix:
- vc_arch: amd64
choco_arch:
nuget_arch: x64
target_arch: x86_64
qt_ver: 5.6\msvc2013_64
bintray_path: Win64
MYSQL_DRIVER_URL: https://dev.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.1.6-winx64.zip
MYSQL_DRIVER_ARCHIVE: mysql-connector-c-6.1.6-winx64.zip
MYSQL_DRIVER_NAME: mysql-connector-c-6.1.6-winx64
- vc_arch: amd64_x86 # cross-compile from amd64 to x86
choco_arch: --x86
nuget_arch: Win32
target_arch: x86
qt_ver: 5.6\msvc2013
bintray_path: Win32
MYSQL_DRIVER_URL: https://dev.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.1.6-win32.zip
MYSQL_DRIVER_ARCHIVE: mysql-connector-c-6.1.6-win32.zip
MYSQL_DRIVER_NAME: mysql-connector-c-6.1.6-win32
install:
- systeminfo
- ps: |
if (Test-Path c:\protoc) {
echo "using protoc from cache"
} else {
Invoke-WebRequest "https://github.com/google/protobuf/releases/download/v2.6.1/protoc-2.6.1-win32.zip" -OutFile c:\protoc-2.6.1-win32.zip
c:\cygwin\bin\bash -lc "cd /cygdrive/c; 7z x -y protoc-2.6.1-win32.zip -oc:\protoc"
}
- ps: |
if (Test-Path c:\protobuf) {
echo "using protobuf from cache"
} else {
nuget install protobuf-v120 -OutputDirectory c:\protobuf
}
- ps: |
if (Test-Path c:\zlib) {
echo "using zlib from cache"
} else {
nuget install zlib -OutputDirectory c:\zlib
}
# install mysql connector
- curl -kLO %MYSQL_DRIVER_URL%
- 7z x %MYSQL_DRIVER_ARCHIVE% -oc:\ >nul
build_script:
- mkdir build
- cd build
- '"c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall" %vc_arch%'
- path
- ps: |
$zlibinc = c:\cygwin\bin\find /cygdrive/c/zlib/ -path '*v120*/zlib.h'
$zlibinc = c:\cygwin\bin\dirname $zlibinc
$zlibinc = c:\cygwin\bin\cygpath -m $zlibinc
$zliblib = c:\cygwin\bin\find /cygdrive/c/zlib/ -path "*v120*/$env:nuget_arch/Release/zlib.lib"
$zliblib = c:\cygwin\bin\cygpath -m $zliblib
$protoinc = c:\cygwin\bin\find /cygdrive/c/protobuf/ -name 'google'
$protoinc = c:\cygwin\bin\dirname $protoinc
$protoinc = c:\cygwin\bin\cygpath -m $protoinc
$protolib = c:\cygwin\bin\find /cygdrive/c/protobuf/ -path "*/lib/$env:nuget_arch/v120/Release/libprotobuf.lib"
$protolib = c:\cygwin\bin\cygpath -m $protolib
$protoc = c:\cygwin\bin\find /cygdrive/c/protoc/ -name "protoc.exe"
$protoc = c:\cygwin\bin\cygpath -m $protoc
$mysqldll = c:\cygwin\bin\find /cygdrive/c/$env:MYSQL_DRIVER_NAME -name "libmysql.dll"
$mysqldll = c:\cygwin\bin\cygpath -m $mysqldll
Write-Output "ZLIBINC = $zlibinc"
Write-Output "ZLIBLIB = $zliblib"
Write-Output "PROTOINC = $protoinc"
Write-Output "PROTOLIB = $protolib"
Write-Output "PROTOC = $protoc"
Write-Output "MYSQLDLL = $mysqldll"
cmake .. "-GNMake Makefiles" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver" "-DWITH_SERVER=1" "-DZLIB_INCLUDE_DIR=$zlibinc" "-DZLIB_LIBRARY=$zliblib" "-DPROTOBUF_INCLUDE_DIR=$protoinc" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARY=$protolib" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" "-DMYSQLCLIENT_LIBRARIES=$mysqldll"
- nmake package
- c:\cygwin\bin\ls -l
- ps: |
$exe = dir -name *.exe
$new_name = $exe.Replace(".exe", "-${env:target_arch}_qt5.exe")
Push-AppveyorArtifact $exe -FileName $new_name
$cmake_name = $exe.Replace(".exe", "-${env:target_arch}_qt5.cmake.txt")
Push-AppveyorArtifact CMakeCache.txt -FileName $cmake_name
$json = New-Object PSObject
(New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII
Push-AppveyorArtifact "latest-$env:target_arch"
$bt_password = ConvertTo-SecureString $Env:BINTRAY_APIKEY -AsPlainText -Force
$bt_credentials = New-Object System.Management.Automation.PSCredential ($Env:BINTRAY_USER, $bt_password)
$exe -match "Cockatrice-(?<content>.*)\.exe"
$version = $matches['content']
$bt_headers = @{
"X-Bintray-Package" = "Cockatrice-git";
"X-Bintray-Version" = $version;
"X-Bintray-Publish" = 1;
"X-Bintray-Override" = 1;
}
$url = "https://api.bintray.com/content/cockatrice/Cockatrice/$env:bintray_path/$new_name"
$result = Invoke-WebRequest -Uri $url -Credential $bt_credentials -Method PUT -Headers $bt_headers -InFile "$exe"
Write-Host $result
test: off

22
clangify.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# This script will run clang-format on all modified, non-3rd-party C++/Header files.
set -e
if hash clang-format 2>/dev/null && hash git 2>/dev/null; then
files_to_clean=($(git diff --name-only $(git merge-base origin/master HEAD)))
printf "%s\n" ${files_to_clean[@]} | \
xargs -I{} find '{}' \( -name "*.cpp" -o -name "*.h" \) \
-not -path "./cockatrice/src/qt-json/*" \
-not -path "./servatrice/src/smtp/*" \
-not -path "./common/sfmt/*" \
-not -path "./oracle/src/zip/*" \
-not -path "./build*/*" \
-exec clang-format -style=file -i {} \;
echo "Successfully formatted following files:"
printf "%s\n" ${files_to_clean[@]}
else
echo "Please install clang-format and git to use this program"
fi

View File

@@ -1,14 +1,24 @@
!include ..\..\..\NSIS.definitions.nsh
!include "MUI2.nsh"
!include "FileFunc.nsh"
Name "@CPACK_PACKAGE_NAME@"
BrandingText "@CPACK_PACKAGE_NAME@-@CPACK_PACKAGE_VERSION_MAJOR@"
BrandingText "@CPACK_PACKAGE_FILE_NAME@"
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
SetCompressor /SOLID lzma
InstallDir "$PROGRAMFILES\Cockatrice"
!define UNINSTKEY "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice"
!define DEFAULTNORMALDESTINATON "$ProgramFiles\Cockatrice"
!define DEFAULTPORTABLEDESTINATON "$Desktop\CockatricePortable"
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
RequestExecutionlevel highest
SetCompressor LZMA
Var NormalDestDir
Var PortableDestDir
Var PortableMode
!include LogicLib.nsh
!include FileFunc.nsh
!include MUI2.nsh
!define MUI_ABORTWARNING
!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp"
@@ -20,7 +30,9 @@ InstallDir "$PROGRAMFILES\Cockatrice"
!define MUI_FINISHPAGE_RUN_TEXT "Run 'Cockatrice' now"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\COPYING"
!insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE"
Page Custom PortableModePageCreate PortableModePageLeave
!define MUI_PAGE_CUSTOMFUNCTION_PRE componentsPagePre
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
@@ -31,46 +43,125 @@ InstallDir "$PROGRAMFILES\Cockatrice"
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE English
Function .onInit
StrCpy $NormalDestDir "${DEFAULTNORMALDESTINATON}"
StrCpy $PortableDestDir "${DEFAULTPORTABLEDESTINATON}"
${GetParameters} $9
ClearErrors
${GetOptions} $9 "/?" $8
${IfNot} ${Errors}
MessageBox MB_ICONINFORMATION|MB_SETFOREGROUND "\
/PORTABLE : Install in portable mode$\n\
/S : Silent install$\n\
/D=%directory% : Specify destination directory$\n"
Quit
${EndIf}
ClearErrors
${GetOptions} $9 "/PORTABLE" $8
${IfNot} ${Errors}
StrCpy $PortableMode 1
StrCpy $0 $PortableDestDir
${Else}
StrCpy $PortableMode 0
StrCpy $0 $NormalDestDir
${If} ${Silent}
Call RequireAdmin
${EndIf}
${EndIf}
${If} $InstDir == ""
; User did not use /D to specify a directory,
; we need to set a default based on the install mode
StrCpy $InstDir $0
${EndIf}
Call SetModeDestinationFromInstdir
FunctionEnd
Function RequireAdmin
UserInfo::GetAccountType
Pop $8
${If} $8 != "admin"
MessageBox MB_ICONSTOP "You need administrator rights to install Cockatrice"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
Abort
${EndIf}
FunctionEnd
Function SetModeDestinationFromInstdir
${If} $PortableMode = 0
StrCpy $NormalDestDir $InstDir
${Else}
StrCpy $PortableDestDir $InstDir
${EndIf}
FunctionEnd
Function PortableModePageCreate
Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory
!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Cockatrice."
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 10u 100% 24u "Select install mode:"
Pop $0
${NSD_CreateRadioButton} 30u 50u -30u 8u "Normal installation"
Pop $1
${NSD_CreateRadioButton} 30u 70u -30u 8u "Portable mode (all files in a single folder)"
Pop $2
${If} $PortableMode = 0
SendMessage $1 ${BM_SETCHECK} ${BST_CHECKED} 0
${Else}
SendMessage $2 ${BM_SETCHECK} ${BST_CHECKED} 0
${EndIf}
nsDialogs::Show
FunctionEnd
Function PortableModePageLeave
${NSD_GetState} $1 $0
${If} $0 <> ${BST_UNCHECKED}
StrCpy $PortableMode 0
StrCpy $InstDir $NormalDestDir
Call RequireAdmin
${Else}
StrCpy $PortableMode 1
StrCpy $InstDir $PortableDestDir
${EndIf}
FunctionEnd
Function componentsPagePre
${If} $PortableMode = 0
SetShellVarContext all
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
StrCmp $R0 "" done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst
Abort
uninst:
ClearErrors
ExecWait "$R0"
done:
${Else}
Abort
${EndIf}
FunctionEnd
Section "Application" SecApplication
SetShellVarContext all
SetOutPath "$INSTDIR"
@CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@
@CPACK_NSIS_FULL_INSTALL@
SetShellVarContext all
SetOutPath "$INSTDIR"
WriteUninstaller "$INSTDIR\uninstall.exe"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "EstimatedSize" "$0"
SectionEnd
Section "Update configuration" SecUpdateConfig
SetShellVarContext current
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "carddatabase" "$LOCALAPPDATA\Cockatrice\cards.xml"
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "tokendatabase" "$LOCALAPPDATA\Cockatrice\tokens.xml"
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "decks" "$LOCALAPPDATA\Cockatrice\decks"
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "pics" "$LOCALAPPDATA\Cockatrice\pics"
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\replays" "pics" "$LOCALAPPDATA\Cockatrice\replays"
WriteRegStr HKCU "Software\Cockatrice\Cockatrice\sound" "path" "$INSTDIR\sounds"
SectionEnd
Section "Start menu item" SecStartMenu
SetShellVarContext all
createDirectory "$SMPROGRAMS\Cockatrice"
createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe"
createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe"
createShortCut "$SMPROGRAMS\Cockatrice\Servatrice.lnk" "$INSTDIR\servatrice.exe"
SectionEnd
Section "un.Application" UnSecApplication
SetShellVarContext all
${If} $PortableMode = 1
${AndIf} ${FileExists} "$INSTDIR\portable.dat"
; upgrade portable mode
RMDir /r "$INSTDIR\plugins"
RMDir /r "$INSTDIR\sounds"
RMDir /r "$INSTDIR\themes"
@@ -90,6 +181,63 @@ Section "un.Application" UnSecApplication
Delete "$INSTDIR\servatrice.ini.example"
Delete "$INSTDIR\zlib*.dll"
RMDir "$INSTDIR"
${EndIf}
@CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@
@CPACK_NSIS_FULL_INSTALL@
${If} $PortableMode = 0
WriteUninstaller "$INSTDIR\uninstall.exe"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "EstimatedSize" "$0"
IfFileExists "$INSTDIR\vc_redist.x86.exe" VcRedist86Exists PastVcRedist86Check
VcRedist86Exists:
ExecWait '"$INSTDIR\vc_redist.x86.exe" /passive /norestart'
PastVcRedist86Check:
IfFileExists "$INSTDIR\vc_redist.x64.exe" VcRedist64Exists PastVcRedist64Check
VcRedist64Exists:
ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart'
PastVcRedist64Check:
${Else}
; Create the file the application uses to detect portable mode
FileOpen $0 "$INSTDIR\portable.dat" w
FileWrite $0 "PORTABLE"
FileClose $0
${EndIf}
SectionEnd
Section "Start menu item" SecStartMenu
${If} $PortableMode = 0
SetShellVarContext all
createDirectory "$SMPROGRAMS\Cockatrice"
createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe"
createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe"
createShortCut "$SMPROGRAMS\Cockatrice\Servatrice.lnk" "$INSTDIR\servatrice.exe"
${EndIf}
SectionEnd
Section "un.Application" UnSecApplication
SetShellVarContext all
RMDir /r "$INSTDIR\plugins"
RMDir /r "$INSTDIR\sounds"
RMDir /r "$INSTDIR\themes"
RMDir /r "$INSTDIR\translations"
Delete "$INSTDIR\*.exe"
Delete "$INSTDIR\*.dll"
Delete "$INSTDIR\qt.conf"
Delete "$INSTDIR\qdebug.txt"
Delete "$INSTDIR\servatrice.sql"
Delete "$INSTDIR\servatrice.ini.example"
RMDir "$INSTDIR"
RMDir "$SMPROGRAMS\Cockatrice"
@@ -105,11 +253,9 @@ Section /o "un.Configurations, decks, cards, pics" UnSecConfiguration
SectionEnd
LangString DESC_SecApplication ${LANG_ENGLISH} "Cockatrice program files"
LangString DESC_SecUpdateConfig ${LANG_ENGLISH} "Update the paths in the application settings according to the installation paths."
LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Cockatrice and Oracle."
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecApplication} $(DESC_SecApplication)
!insertmacro MUI_DESCRIPTION_TEXT ${SecUpdateConfig} $(DESC_SecUpdateConfig)
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
@@ -118,21 +264,4 @@ LangString DESC_UnSecConfiguration ${LANG_ENGLISH} "Configurations, decks, card
!insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${UnSecApplication} $(DESC_UnSecApplication)
!insertmacro MUI_DESCRIPTION_TEXT ${UnSecConfiguration} $(DESC_UnSecConfiguration)
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END
Function .onInit
SetShellVarContext all
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
StrCmp $R0 "" done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst
Abort
uninst:
ClearErrors
ExecWait "$R0"
done:
FunctionEnd
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END

View File

@@ -1,28 +0,0 @@
{
"package": {
"name": "Cockatrice-git",
"repo": "Cockatrice",
"subject": "cockatrice",
"desc": "Cockatrice master branch automated builds",
"website_url": "https://github.com/Cockatrice/Cockatrice",
"issue_tracker_url": "https://github.com/Cockatrice/Cockatrice/issues",
"vcs_url": "https://github.com/Cockatrice/Cockatrice.git",
"github_use_tag_release_notes": true,
"github_release_notes_file": "RELEASE.txt",
"licenses": ["GPL-2.0"],
"labels": ["card", "tabletop", "game"],
"public_download_numbers": false,
"public_stats": true
},
"version": {
"name": "@PROJECT_VERSION@",
"desc": "Unstable builds from master",
"vcs_tag": "@GIT_COMMIT_ID@",
"gpgSign": false
},
"files": [
{ "includePattern": "build/(Cockatrice.*\\.deb)", "uploadPattern": "Ubuntu/$ENV{DIST}/$1" },
{ "includePattern": "build/(Cockatrice.*\\.dmg)", "uploadPattern": "macOS/$1" }
],
"publish": true
}

View File

@@ -1,40 +1,223 @@
find_package(Git)
if(GIT_FOUND)
# HELPER FUNCTIONS
function(get_commit_id)
# get last commit hash
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --abbrev=7 --date=short "--pretty=%h"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_ID
)
if( NOT ${res_var} EQUAL 0 )
set( GIT_COMMIT_ID "unknown")
message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." )
if(NOT ${res_var} EQUAL 0)
message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.")
return()
endif()
string( REPLACE "\n" "" GIT_COMMIT_ID "${GIT_COM_ID}" )
string(REPLACE "\n" "" GIT_COM_ID "${GIT_COM_ID}")
set(GIT_COMMIT_ID "${GIT_COM_ID}" PARENT_SCOPE)
set(PROJECT_VERSION_LABEL "custom(${GIT_COM_ID})" PARENT_SCOPE)
endfunction()
function(get_commit_date)
# get last commit date
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%cd"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_DATE
)
if( NOT ${res_var} EQUAL 0 )
set( GIT_COMMIT_DATE "unknown")
set( GIT_COMMIT_DATE_FRIENDLY "unknown")
message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." )
if(NOT ${res_var} EQUAL 0)
message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.")
return()
endif()
string( REPLACE "\n" "" GIT_COMMIT_DATE_FRIENDLY "${GIT_COM_DATE}" )
string( REPLACE "-" "" GIT_COMMIT_DATE "${GIT_COMMIT_DATE_FRIENDLY}" )
string(REPLACE "\n" "" GIT_COM_DATE "${GIT_COM_DATE}")
set(GIT_COMMIT_DATE_FRIENDLY "${GIT_COM_DATE}" PARENT_SCOPE)
string(REPLACE "-" "" GIT_COM_DATE "${GIT_COM_DATE}")
set(GIT_COMMIT_DATE "${GIT_COM_DATE}" PARENT_SCOPE)
endfunction()
function(clean_release_name name)
# "name": "Cockatrice: Thopter Pie Network, Revision 2"
# Remove all double quotes
STRING(REPLACE "\"" "" name "${name}")
# Remove json prefix "name: "
STRING(REPLACE " name: " "" name "${name}")
# Remove "cockatrice" name
STRING(REPLACE "Cockatrice" "" name "${name}")
# Remove all unwanted chars
STRING(REGEX REPLACE "[^A-Za-z0-9_ ]" "" name "${name}")
# Strip (trim) whitespaces
STRING(STRIP "${name}" name)
# Replace all spaces with underscores
STRING(REPLACE " " "_" name "${name}")
set(GIT_TAG_RELEASENAME "${name}" PARENT_SCOPE)
endfunction()
function(get_tag_name commit)
if(${commit} STREQUAL "unknown")
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags ${commit}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_TAG
ERROR_VARIABLE GIT_TAG_ERR
)
if((NOT ${res_var} EQUAL 0) OR (${GIT_TAG_ERR} MATCHES "fatal: no tag exactly matches.*"))
message(STATUS "Commit is not a release or prerelease (no git tag found)")
return()
endif()
string(REPLACE "\n" "" GIT_TAG "${GIT_TAG}")
message(STATUS "Commit is a release or prerelease, git tag: ${GIT_TAG}")
# Extract information from tag:
# YYYY-MM-DD-Release-MAJ.MIN.PATCH
# YYYY-MM-DD-Development-MAJ.MIN.PATCH-beta.X
string(REPLACE "-" ";" GIT_TAG_EXPLODED "${GIT_TAG}")
string(REPLACE "." ";" GIT_TAG_EXPLODED "${GIT_TAG_EXPLODED}")
# Sanity checks: length
list(LENGTH GIT_TAG_EXPLODED GIT_TAG_LISTCOUNT)
if(${GIT_TAG_LISTCOUNT} LESS 7 OR ${GIT_TAG_LISTCOUNT} GREATER 9)
message(WARNING "Invalid tag format, got ${GIT_TAG_LISTCOUNT} tokens")
return()
endif()
# Year
list(GET GIT_TAG_EXPLODED 0 GIT_TAG_YEAR)
if(${GIT_TAG_YEAR} LESS 2017 OR ${GIT_TAG_LISTCOUNT} GREATER 2100)
message(WARNING "Invalid tag year ${GIT_TAG_YEAR}")
return()
endif()
# Month
list(GET GIT_TAG_EXPLODED 1 GIT_TAG_MONTH)
if(${GIT_TAG_MONTH} LESS 1 OR ${GIT_TAG_MONTH} GREATER 12)
message(WARNING "Invalid tag month ${GIT_TAG_MONTH}")
return()
endif()
# Day
list(GET GIT_TAG_EXPLODED 2 GIT_TAG_DAY)
if(${GIT_TAG_DAY} LESS 1 OR ${GIT_TAG_DAY} GREATER 31)
message(WARNING "Invalid tag day ${GIT_TAG_DAY}")
return()
endif()
# Type
list(GET GIT_TAG_EXPLODED 3 GIT_TAG_TYPE)
if(NOT(${GIT_TAG_TYPE} STREQUAL "Release" OR ${GIT_TAG_TYPE} STREQUAL "Development"))
message(WARNING "Invalid tag type ${GIT_TAG_TYPE}")
return()
endif()
# Major
list(GET GIT_TAG_EXPLODED 4 GIT_TAG_MAJOR)
if(${GIT_TAG_MAJOR} LESS 0 OR ${GIT_TAG_MAJOR} GREATER 99)
message(WARNING "Invalid tag major version ${GIT_TAG_MAJOR}")
return()
endif()
# Minor
list(GET GIT_TAG_EXPLODED 5 GIT_TAG_MINOR)
if(${GIT_TAG_MINOR} LESS 0 OR ${GIT_TAG_MINOR} GREATER 99)
message(WARNING "Invalid tag minor version ${GIT_TAG_MINOR}")
return()
endif()
# Patch
list(GET GIT_TAG_EXPLODED 6 GIT_TAG_PATCH)
if(${GIT_TAG_PATCH} LESS 0 OR ${GIT_TAG_PATCH} GREATER 99)
message(WARNING "Invalid tag patch version ${GIT_TAG_PATCH}")
return()
endif()
# Label
# 7 = Stable release
# 8 = Dev release, first beta so only "beta" attached
# 9 = Dev release, subsequent beta so "beta.N" attached (N>=2)
if(${GIT_TAG_LISTCOUNT} EQUAL 8)
list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL)
elseif(${GIT_TAG_LISTCOUNT} EQUAL 9)
list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL)
list(GET GIT_TAG_EXPLODED 8 GIT_TAG_LABEL_NUM)
set(GIT_TAG_LABEL ${GIT_TAG_LABEL} ${GIT_TAG_LABEL_NUM})
string(REPLACE ";" "." GIT_TAG_LABEL "${GIT_TAG_LABEL}")
else()
SET(GIT_TAG_LABEL "")
endif()
# Override hardcoded version with the informations from the tag
set(PROJECT_VERSION_MAJOR ${GIT_TAG_MAJOR} PARENT_SCOPE)
set(PROJECT_VERSION_MINOR ${GIT_TAG_MINOR} PARENT_SCOPE)
set(PROJECT_VERSION_PATCH ${GIT_TAG_PATCH} PARENT_SCOPE)
set(PROJECT_VERSION_LABEL ${GIT_TAG_LABEL} PARENT_SCOPE)
if(${GIT_TAG_TYPE} STREQUAL "Development")
set(PROJECT_VERSION_LABEL ${GIT_TAG_LABEL} PARENT_SCOPE)
elseif(${GIT_TAG_TYPE} STREQUAL "Release")
set(PROJECT_VERSION_LABEL "" PARENT_SCOPE)
# get version name from github
set(GIT_TAG_TEMP_FILE "${PROJECT_BINARY_DIR}/tag_informations.txt")
set(GIT_TAG_TEMP_URL "https://api.github.com/repos/Cockatrice/Cockatrice/releases/tags/${GIT_TAG}")
message(STATUS "Fetching tag informations from ${GIT_TAG_TEMP_URL}")
file(REMOVE "${GIT_TAG_TEMP_FILE}")
file(DOWNLOAD "${GIT_TAG_TEMP_URL}" "${GIT_TAG_TEMP_FILE}" STATUS status LOG log INACTIVITY_TIMEOUT 30 TIMEOUT 300 SHOW_PROGRESS)
list(GET status 0 err)
list(GET status 1 msg)
if(err)
message(WARNING "Download failed with error ${msg}: ${log}")
return()
endif()
file(STRINGS "${GIT_TAG_TEMP_FILE}" GIT_TAG_RAW_RELEASENAME REGEX "\"name\": \"" LIMIT_COUNT 1)
clean_release_name("${GIT_TAG_RAW_RELEASENAME}")
set(PROJECT_VERSION_RELEASENAME "${GIT_TAG_RELEASENAME}" PARENT_SCOPE)
endif()
endfunction()
# START OF MAIN
# fallback defaults
set(GIT_COMMIT_ID "unknown")
set(GIT_COMMIT_DATE "unknown")
set(GIT_COMMIT_DATE_FRIENDLY "unknown")
set(PROJECT_VERSION_LABEL "custom(unknown)")
set(PROJECT_VERSION_RELEASENAME "")
find_package(Git)
if(GIT_FOUND)
get_commit_id()
get_commit_date()
get_tag_name(${GIT_COMMIT_ID})
else()
set( GIT_COMMIT_ID "unknown")
set( GIT_COMMIT_DATE "unknown")
set( GIT_COMMIT_DATE_FRIENDLY "unknown")
message( WARNING "Git not found. Build will not contain git revision info." )
endif()
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "0")
set(PROJECT_VERSION_PATCH "1~git${GIT_COMMIT_DATE}.${GIT_COMMIT_ID}")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(PROJECT_VERSION_FRIENDLY "${GIT_COMMIT_ID} (${GIT_COMMIT_DATE_FRIENDLY})")
if(PROJECT_VERSION_LABEL)
set(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_LABEL}")
endif()
set(PROJECT_VERSION_FRIENDLY "${PROJECT_VERSION} (${GIT_COMMIT_DATE_FRIENDLY})")
# Format: <program name>[-ReleaseName]-MAJ.MIN.PATCH[-prerelease_label]
set(PROJECT_VERSION_FILENAME "${PROJECT_NAME}")
if(PROJECT_VERSION_RELEASENAME)
set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION_RELEASENAME}")
endif()
set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION}")
message(STATUS "Project version: ${PROJECT_VERSION}")
message(STATUS "Friendly project version: ${PROJECT_VERSION_FRIENDLY}")
message(STATUS "Project version filename: ${PROJECT_VERSION_FILENAME}")

3
cmake/launch-c.in Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
export CCACHE_CPP2=true
exec "${RULE_LAUNCH_COMPILE}" "${CMAKE_C_COMPILER}" "$@"

3
cmake/launch-cxx.in Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
export CCACHE_CPP2=true
exec "${RULE_LAUNCH_COMPILE}" "${CMAKE_CXX_COMPILER}" "$@"

View File

@@ -5,12 +5,12 @@
PROJECT(Cockatrice)
SET(cockatrice_SOURCES
src/abstractcounter.cpp
src/abstractcounter.cpp
src/counter_general.cpp
src/dlg_creategame.cpp
src/dlg_filter_games.cpp
src/dlg_connect.cpp
src/dlg_create_token.cpp
src/dlg_connect.cpp
src/dlg_create_token.cpp
src/dlg_edit_avatar.cpp
src/dlg_edit_password.cpp
src/dlg_edit_tokens.cpp
@@ -18,33 +18,35 @@ SET(cockatrice_SOURCES
src/dlg_forgotpasswordrequest.cpp
src/dlg_forgotpasswordreset.cpp
src/dlg_forgotpasswordchallenge.cpp
src/dlg_register.cpp
src/dlg_register.cpp
src/dlg_tip_of_the_day.cpp
src/tip_of_the_day.cpp
src/dlg_update.cpp
src/dlg_viewlog.cpp
src/abstractclient.cpp
src/remoteclient.cpp
src/main.cpp
src/window_main.cpp
src/gamesmodel.cpp
src/player.cpp
src/playertarget.cpp
src/cardzone.cpp
src/selectzone.cpp
src/cardlist.cpp
src/abstractcarditem.cpp
src/carditem.cpp
src/tablezone.cpp
src/handzone.cpp
src/handcounter.cpp
src/carddatabase.cpp
src/remoteclient.cpp
src/main.cpp
src/window_main.cpp
src/gamesmodel.cpp
src/player.cpp
src/playertarget.cpp
src/cardzone.cpp
src/selectzone.cpp
src/cardlist.cpp
src/abstractcarditem.cpp
src/carditem.cpp
src/tablezone.cpp
src/handzone.cpp
src/handcounter.cpp
src/carddatabase.cpp
src/keysignals.cpp
src/gameview.cpp
src/gameselector.cpp
src/decklistmodel.cpp
src/deck_loader.cpp
src/dlg_load_deck_from_clipboard.cpp
src/dlg_load_remote_deck.cpp
src/cardinfowidget.cpp
src/gameview.cpp
src/gameselector.cpp
src/decklistmodel.cpp
src/deck_loader.cpp
src/dlg_load_deck_from_clipboard.cpp
src/dlg_load_remote_deck.cpp
src/cardinfowidget.cpp
src/cardframe.cpp
src/cardinfopicture.cpp
src/cardinfotext.cpp
@@ -52,52 +54,52 @@ SET(cockatrice_SOURCES
src/cardfilter.cpp
src/filtertreemodel.cpp
src/filtertree.cpp
src/messagelogwidget.cpp
src/zoneviewzone.cpp
src/zoneviewwidget.cpp
src/pilezone.cpp
src/stackzone.cpp
src/carddragitem.cpp
src/carddatabasemodel.cpp
src/setsmodel.cpp
src/window_sets.cpp
src/abstractgraphicsitem.cpp
src/abstractcarddragitem.cpp
src/dlg_settings.cpp
src/phasestoolbar.cpp
src/gamescene.cpp
src/arrowitem.cpp
src/arrowtarget.cpp
src/tab.cpp
src/tab_server.cpp
src/tab_room.cpp
src/tab_message.cpp
src/tab_game.cpp
src/tab_deck_storage.cpp
src/tab_replays.cpp
src/tab_supervisor.cpp
src/tab_admin.cpp
src/tab_userlists.cpp
src/messagelogwidget.cpp
src/zoneviewzone.cpp
src/zoneviewwidget.cpp
src/pilezone.cpp
src/stackzone.cpp
src/carddragitem.cpp
src/carddatabasemodel.cpp
src/setsmodel.cpp
src/window_sets.cpp
src/abstractgraphicsitem.cpp
src/abstractcarddragitem.cpp
src/dlg_settings.cpp
src/phasestoolbar.cpp
src/gamescene.cpp
src/arrowitem.cpp
src/arrowtarget.cpp
src/tab.cpp
src/tab_server.cpp
src/tab_room.cpp
src/tab_message.cpp
src/tab_game.cpp
src/tab_deck_storage.cpp
src/tab_replays.cpp
src/tab_supervisor.cpp
src/tab_admin.cpp
src/tab_userlists.cpp
src/tab_deck_editor.cpp
src/tab_logs.cpp
src/replay_timeline_widget.cpp
src/deckstats_interface.cpp
src/tappedout_interface.cpp
src/chatview.cpp
src/userlist.cpp
src/userinfobox.cpp
src/chatview/chatview.cpp
src/chatview/userlistProxy.h
src/userlist.cpp
src/userinfobox.cpp
src/user_context_menu.cpp
src/remotedecklist_treewidget.cpp
src/remotereplaylist_treewidget.cpp
src/deckview.cpp
src/playerlistwidget.cpp
src/pixmapgenerator.cpp
src/settingscache.cpp
src/thememanager.cpp
src/localserver.cpp
src/localserverinterface.cpp
src/localclient.cpp
src/priceupdater.cpp
src/remotedecklist_treewidget.cpp
src/remotereplaylist_treewidget.cpp
src/deckview.cpp
src/playerlistwidget.cpp
src/pixmapgenerator.cpp
src/settingscache.cpp
src/thememanager.cpp
src/localserver.cpp
src/localserverinterface.cpp
src/localclient.cpp
src/qt-json/json.cpp
src/soundengine.cpp
src/pending_command.cpp
@@ -115,6 +117,10 @@ SET(cockatrice_SOURCES
src/update_downloader.cpp
src/logger.cpp
src/releasechannel.cpp
src/userconnection_information.cpp
src/spoilerbackgroundupdater.cpp
src/handle_public_servers.cpp
src/carddbparser/cockatricexml3.cpp
${VERSION_STRING_CPP}
)
@@ -142,71 +148,33 @@ if(APPLE)
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
ENDIF(APPLE)
set(COCKATRICE_LIBS)
# Qt5
find_package(Qt5 COMPONENTS Concurrent Multimedia Network PrintSupport Svg Widgets REQUIRED)
include_directories(${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Multimedia_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5PrintSupport_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS})
set(COCKATRICE_QT_MODULES Concurrent Multimedia Network PrintSupport Svg Widgets)
# qt5 stuff
if(Qt5Widgets_FOUND)
include_directories(${Qt5Widgets_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS Widgets)
# Qt5LinguistTools
find_package(Qt5LinguistTools)
if(Qt5LinguistTools_FOUND)
include_directories(${Qt5LinguistTools_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS LinguistTools)
# QtConcurrent
find_package(Qt5Concurrent)
if(Qt5Concurrent_FOUND)
include_directories(${Qt5Concurrent_INCLUDE_DIRS})
list(APPEND ORACLE_LIBS Concurrent)
if(NOT Qt5_LRELEASE_EXECUTABLE)
MESSAGE(WARNING "Qt's lrelease not found.")
endif()
# QtNetwork
find_package(Qt5Network)
if(Qt5Network_FOUND)
include_directories(${Qt5Network_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS Network)
endif()
# QtMultimedia
find_package(Qt5Multimedia)
if(Qt5Multimedia_FOUND)
include_directories(${Qt5Multimedia_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS Multimedia)
endif()
# QtPrinter
find_package(Qt5PrintSupport)
if(Qt5PrintSupport_FOUND)
include_directories(${Qt5PrintSupport_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS PrintSupport)
endif()
# QtSvg
find_package(Qt5Svg)
if(Qt5Svg_FOUND)
include_directories(${Qt5Svg_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS Svg)
endif()
# Qt5LinguistTools
find_package(Qt5LinguistTools)
if(Qt5LinguistTools_FOUND)
include_directories(${Qt5LinguistTools_INCLUDE_DIRS})
list(APPEND COCKATRICE_LIBS LinguistTools)
endif()
# Let cmake chew Qt5's translations and resource files
# Note: header files are MOC-ed automatically by cmake
IF(UPDATE_TRANSLATIONS)
if(UPDATE_TRANSLATIONS)
if(NOT Qt5_LUPDATE_EXECUTABLE)
MESSAGE(WARNING "Qt's lupdate not found.")
endif()
QT5_CREATE_TRANSLATION(cockatrice_QM ${translate_SRCS} ${cockatrice_TS})
ELSE()
else()
QT5_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS})
ENDIF()
QT5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
# guess plugins and libraries directory
set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins")
get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION)
get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH)
endif()
endif()
QT5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
# Declare path variables
set(ICONDIR share/icons CACHE STRING "icon dir")
set(DESKTOPDIR share/applications CACHE STRING "desktop file destination")
@@ -220,12 +188,8 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
# Build cockatrice binary and link it
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS})
if(MSVC)
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common Qt5::WinMain)
else()
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common)
endif()
qt5_use_modules(cockatrice ${COCKATRICE_LIBS})
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common)
qt5_use_modules(cockatrice ${COCKATRICE_QT_MODULES})
if(UNIX)
if(APPLE)
@@ -238,7 +202,7 @@ if(UNIX)
set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist)
INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./)
INSTALL(FILES ${cockatrice_QM} DESTINATION ./Cockatrice.app/Contents/Resources/translations)
INSTALL(FILES ${cockatrice_QM} DESTINATION ./cockatrice.app/Contents/Resources/translations)
else()
# Assume linux
INSTALL(TARGETS cockatrice RUNTIME DESTINATION bin/)
@@ -254,14 +218,22 @@ endif()
if(APPLE)
# these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir Cockatrice.app/Contents/Plugins)
set(qtconf_dest_dir Cockatrice.app/Contents/Resources)
set(plugin_dest_dir cockatrice.app/Contents/Plugins)
set(qtconf_dest_dir cockatrice.app/Contents/Resources)
get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE)
# qt5 plugins: audio, iconengines, imageformats, platforms, printsupport
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport)/.*\\.dylib"
REGEX ".*_debug\\.dylib" EXCLUDE)
FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE
PATTERN "*_debug.dylib" EXCLUDE
PATTERN "audio/*.dylib"
PATTERN "iconengines/*.dylib"
PATTERN "imageformats/*.dylib"
PATTERN "platforms/*.dylib"
PATTERN "printsupport/*.dylib"
PATTERN "styles/*.dylib"
)
install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
@@ -275,7 +247,7 @@ Data = Resources\")
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\")
set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
" COMPONENT Runtime)
endif()
@@ -307,8 +279,3 @@ Data = Resources\")
install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./)
endif()
endif()
#Compile a portable version, default off
option(PORTABLE "portable build" OFF)
IF(PORTABLE)
add_definitions(-DPORTABLE_BUILD)
endif()

View File

@@ -342,5 +342,19 @@
<file>resources/userlevels/admin_vip.svg</file>
<file>resources/userlevels/admin_vip_buddy.svg</file>
<!-- ADD TIP OF THE DAY IMAGES HERE -->
<file>resources/tips/tips_of_the_day.xml</file>
<file>resources/tips/images/accounts_tab.png</file>
<file>resources/tips/images/arrows.png</file>
<file>resources/tips/images/cockatrice_register.png</file>
<file>resources/tips/images/cockatrice_wiki.png</file>
<file>resources/tips/images/coin_flip.png</file>
<file>resources/tips/images/face_down.png</file>
<file>resources/tips/images/filter_games.png</file>
<file>resources/tips/images/github_logo.png</file>
<file>resources/tips/images/gitter.png</file>
<file>resources/tips/images/themes.png</file>
<file>resources/tips/images/tip_of_the_day.png</file>
</qresource>
</RCC>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="640"
height="480"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="eu.svg">
<metadata
id="metadata43">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview41"
showgrid="false"
inkscape:zoom="0.35308642"
inkscape:cx="405"
inkscape:cy="229.84832"
inkscape:window-x="695"
inkscape:window-y="86"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<desc
id="desc4">European flag</desc>
<defs
id="defs6">
<g
id="s">
<g
id="c">
<path
id="t"
d="M 0,0 0,1 0.5,1 Z"
transform="matrix(0.95105652,0.30901699,-0.30901699,0.95105652,0,-1)"
inkscape:connector-curvature="0" />
<use
xlink:href="#t"
transform="scale(-1,1)"
id="use11"
x="0"
y="0"
width="100%"
height="100%" />
</g>
<g
id="a">
<use
xlink:href="#c"
transform="matrix(0.30901699,0.95105652,-0.95105652,0.30901699,0,0)"
id="use14"
x="0"
y="0"
width="100%"
height="100%" />
<use
xlink:href="#c"
transform="matrix(-0.80901699,0.58778525,-0.58778525,-0.80901699,0,0)"
id="use16"
x="0"
y="0"
width="100%"
height="100%" />
</g>
<use
xlink:href="#a"
transform="scale(-1,1)"
id="use18"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</defs>
<rect
width="642.9021"
height="483.35666"
id="rect20"
x="2.8321679"
y="2.3076911"
style="fill:#003399" />
<g
transform="matrix(23.811189,0,0,23.981809,322.28322,245.88665)"
id="g22"
style="fill:#ffcc00">
<use
xlink:href="#s"
y="-6"
id="use24"
x="0"
width="100%"
height="100%" />
<use
xlink:href="#s"
y="6"
id="use26"
x="0"
width="100%"
height="100%" />
<g
id="l">
<use
xlink:href="#s"
x="-6"
id="use29"
y="0"
width="100%"
height="100%" />
<use
xlink:href="#s"
transform="matrix(-0.80901699,-0.58778525,0.58778525,-0.80901699,-3,-5.1961524)"
id="use31"
x="0"
y="0"
width="100%"
height="100%" />
<use
xlink:href="#s"
transform="matrix(-0.80901699,0.58778525,-0.58778525,-0.80901699,-5.1961524,-3)"
id="use33"
x="0"
y="0"
width="100%"
height="100%" />
<use
xlink:href="#s"
transform="matrix(0.30901699,0.95105652,-0.95105652,0.30901699,-5.1961524,3)"
id="use35"
x="0"
y="0"
width="100%"
height="100%" />
<use
xlink:href="#s"
transform="matrix(0.30901699,0.95105652,-0.95105652,0.30901699,-3,5.1961524)"
id="use37"
x="0"
y="0"
width="100%"
height="100%" />
</g>
<use
xlink:href="#l"
transform="scale(-1,1)"
id="use39"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -0,0 +1,86 @@
<tips>
<tip>
<title>Tip of the Day</title>
<text>Tip of the Day is a new feature to Cockatrice that allows users to get information about the newest features of the program and some of the most commonly asked questions!</text>
<image>tip_of_the_day.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Suggesting New Tips</title>
<text>You can suggest new Tips of the Day by reaching out to the development team on &lt;a href="https://gitter.im/cockatrice/cockatrice"&gt;Gitter&lt;/a&gt;!</text>
<image>gitter.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Reporting Bugs</title>
<text>If you encounter a bug while using Cockatrice, you can report the bug to the development team via &lt;a href="https://github.com/cockatrice/cockatrice/issues"&gt;GitHub&lt;a&gt;</text>
<image>github_logo.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>FAQ/Troubleshooting Wiki</title>
<text>You can find answers to the most common questions and some helpful Cockatrice toubleshooting over on the &lt;a href="https://github.com/cockatrice/cockatrice/wiki"&gt;GitHub wiki&lt;a&gt;</text>
<image>cockatrice_wiki.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Register for a Server</title>
<text>Click on either Cockatrice (Windows) or Actions (Mac) and then Register to server... When the dialogue appears, fill out the desired server information.</text>
<image>cockatrice_register.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Drawing Arrows</title>
<text>You can draw arrows of different color by holding a combination of keys!
Right Click: Red Arrow
Shift + Right Click: Green Arrow
Alt + Right Click: Blue Arrow
Cmd + Right Click: Yellow Arrow
</text>
<image>arrows.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Filtering Games</title>
<text>Don't see all the active games? Want to see a smaller selection? Use the Game Filters to change your horizon</text>
<image>filter_games.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Upload Custom Avatar</title>
<text>Want to show off your hippo avatar? Need to update your password? Check out the Accounts Tab for more info!</text>
<image>accounts_tab.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Common Shortcuts</title>
<text>You can find a full list of shortcuts &lt;a href="https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts"&gt;on the wiki&lt;/a&gt;, but a short list:
&lt;br&gt;Roll a die: CTRL + I
&lt;br&gt;Mulligan: CTRL + M
&lt;br&gt;Draw a card: CTRL + D
&lt;br&gt;Undo a draw: CTRL + SHIFT + D
&lt;br&gt;View Sideboard: CTRL + F3
&lt;br&gt;Change Life: CTRL + L
&lt;br&gt;All shortcuts can be customized via Settings->Shortcuts!
</text>
<date>2018-03-01</date>
</tip>
<tip>
<title>Changing Themes</title>
<text>Did you know Cockatrice has custom themes? You can either &lt;a href="https://github.com/Cockatrice/Cockatrice/wiki/Themes"&gt;create one yourself&lt;/a&gt; or use one of the several pre-loaded ones! Go to Settings->Appearance and try them out!</text>
<image>themes.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Flip of the Coin</title>
<text>You can flip a coin instead of rolling a die by rolling a 2 sided die instead!</text>
<image>coin_flip.png</image>
<date>2018-03-01</date>
</tip>
<tip>
<title>Face Down Cards</title>
<text>You can hold Shift while dragging or clicking on a card to have it enter play face down</text>
<image>face_down.png</image>
<date>2018-03-01</date>
</tip>
</tips>

View File

@@ -1,15 +1,17 @@
#include "abstractcarddragitem.h"
#include "carddatabase.h"
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
static const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2;
const QColor GHOST_MASK = QColor(255, 255, 255, 50);
AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag)
AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
const QPointF &_hotSpot,
AbstractCardDragItem *parentDrag)
: QGraphicsItem(), item(_item), hotSpot(_hotSpot)
{
if (parentDrag) {
@@ -27,7 +29,10 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, const QPoint
setZValue(2000000007);
}
if (item->getTapped())
setTransform(QTransform().translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF).rotate(90).translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
setTransform(QTransform()
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
.rotate(90)
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
setCacheMode(DeviceCoordinateCache);
}

View File

@@ -7,24 +7,42 @@ class QGraphicsScene;
class CardZone;
class CardInfo;
class AbstractCardDragItem : public QObject, public QGraphicsItem {
class AbstractCardDragItem : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected:
AbstractCardItem *item;
QPointF hotSpot;
QList<AbstractCardDragItem *> childDrags;
public:
enum { Type = typeCardDrag };
int type() const { return Type; }
enum
{
Type = typeCardDrag
};
int type() const
{
return Type;
}
AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0);
~AbstractCardDragItem();
QRectF boundingRect() const { return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); }
QRectF boundingRect() const
{
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
AbstractCardItem *getItem() const { return item; }
QPointF getHotSpot() const { return hotSpot; }
AbstractCardItem *getItem() const
{
return item;
}
QPointF getHotSpot() const
{
return hotSpot;
}
void addChildDrag(AbstractCardDragItem *child);
virtual void updatePosition(const QPointF &cursorScenePos) = 0;
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};

View File

@@ -1,25 +1,27 @@
#include <QPainter>
#include <QGraphicsScene>
#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <algorithm>
#include <cmath>
#ifdef _WIN32
#include "round.h"
#endif /* _WIN32 */
#include "carddatabase.h"
#include "abstractcarditem.h"
#include "carddatabase.h"
#include "gamescene.h"
#include "main.h"
#include "pictureloader.h"
#include "settingscache.h"
#include "main.h"
#include "gamescene.h"
AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id, QGraphicsItem *parent)
: ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0), bgColor(Qt::transparent), isHovered(false), realZValue(0)
: ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0),
bgColor(Qt::transparent), isHovered(false), realZValue(0)
{
setCursor(Qt::OpenHandCursor);
setFlag(ItemIsSelectable);
setCacheMode(DeviceCoordinateCache);
connect(settingsCache, SIGNAL(displayCardNamesChanged()), this, SLOT(callUpdate()));
cardInfoUpdated();
}
@@ -43,8 +45,8 @@ void AbstractCardItem::pixmapUpdated()
void AbstractCardItem::cardInfoUpdated()
{
info = db->getCard(name);
if(info)
connect(info, SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated()));
if (info)
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated()));
cacheBgColor();
update();
@@ -58,18 +60,19 @@ void AbstractCardItem::setRealZValue(qreal _zValue)
QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const
{
return QSizeF(
painter->combinedTransform().map(QLineF(0, 0, boundingRect().width(), 0)).length(),
painter->combinedTransform().map(QLineF(0, 0, 0, boundingRect().height())).length()
);
return QSizeF(painter->combinedTransform().map(QLineF(0, 0, boundingRect().width(), 0)).length(),
painter->combinedTransform().map(QLineF(0, 0, 0, boundingRect().height())).length());
}
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
{
const int MAX_FONT_SIZE = settingsCache->getMaxFontSize();
const int fontSize = std::max(9, MAX_FONT_SIZE);
QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect());
painter->resetTransform();
QTransform pixmapTransform;
pixmapTransform.translate(totalBoundingRect.width() / 2, totalBoundingRect.height() / 2);
pixmapTransform.rotate(angle);
@@ -77,9 +80,6 @@ void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &transla
painter->setTransform(pixmapTransform);
QFont f;
int fontSize = round(translatedSize.height() / 8);
if (fontSize < 9)
fontSize = 9;
f.setPixelSize(fontSize);
painter->setFont(f);
@@ -91,16 +91,14 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
QPixmap translatedPixmap;
bool paintImage = true;
if(facedown)
{
if (facedown || name.isEmpty()) {
// never reveal card color, always paint the card back
PictureLoader::getPixmap(translatedPixmap, nullptr, translatedSize.toSize());
PictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize());
} else {
// don't even spend time trying to load the picture if our size is too small
if(translatedSize.width() > 10)
{
if (translatedSize.width() > 10) {
PictureLoader::getPixmap(translatedPixmap, info, translatedSize.toSize());
if(translatedPixmap.isNull())
if (translatedPixmap.isNull())
paintImage = false;
} else {
paintImage = false;
@@ -108,7 +106,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
}
painter->save();
if (paintImage) {
painter->save();
transformPainter(painter, translatedSize, angle);
@@ -128,7 +126,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
painter->drawRect(QRectF(0, 0, CARD_WIDTH - 1, CARD_HEIGHT - penWidth));
else
painter->drawRect(QRectF(1, 1, CARD_WIDTH - 2, CARD_HEIGHT - 1.5));
if (translatedPixmap.isNull() || settingsCache->getDisplayCardNames() || facedown) {
painter->save();
transformPainter(painter, translatedSize, angle);
@@ -140,10 +138,12 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
nameStr = "# " + QString::number(id);
else
nameStr = name;
painter->drawText(QRectF(3 * scaleFactor, 3 * scaleFactor, translatedSize.width() - 6 * scaleFactor, translatedSize.height() - 6 * scaleFactor), Qt::AlignTop | Qt::AlignLeft | Qt::TextWrapAnywhere, nameStr);
painter->drawText(QRectF(3 * scaleFactor, 3 * scaleFactor, translatedSize.width() - 6 * scaleFactor,
translatedSize.height() - 6 * scaleFactor),
Qt::AlignTop | Qt::AlignLeft | Qt::TextWrapAnywhere, nameStr);
painter->restore();
}
painter->restore();
}
@@ -153,7 +153,7 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
QSizeF translatedSize = getTranslatedSize(painter);
paintPicture(painter, translatedSize, tapAngle);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
transformPainter(painter, translatedSize, tapAngle);
@@ -179,10 +179,10 @@ void AbstractCardItem::setName(const QString &_name)
{
if (name == _name)
return;
emit deleteCardInfoPopup(name);
if(info)
disconnect(info, nullptr, this, nullptr);
if (info)
disconnect(info.data(), nullptr, this, nullptr);
name = _name;
cardInfoUpdated();
@@ -192,7 +192,7 @@ void AbstractCardItem::setHovered(bool _hovered)
{
if (isHovered == _hovered)
return;
if (_hovered)
processHoverEvent();
isHovered = _hovered;
@@ -212,16 +212,14 @@ void AbstractCardItem::setColor(const QString &_color)
void AbstractCardItem::cacheBgColor()
{
QChar colorChar;
if (color.isEmpty())
{
if(info)
if (color.isEmpty()) {
if (info)
colorChar = info->getColorChar();
} else {
colorChar = color.at(0);
}
switch(colorChar.toLower().toLatin1())
{
switch (colorChar.toLower().toLatin1()) {
case 'b':
bgColor = QColor(0, 0, 0);
break;
@@ -250,13 +248,16 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
{
if (tapped == _tapped)
return;
tapped = _tapped;
if (settingsCache->getTapAnimation() && canAnimate)
static_cast<GameScene *>(scene())->registerAnimationItem(this);
else {
tapAngle = tapped ? 90 : 0;
setTransform(QTransform().translate((float) CARD_WIDTH / 2, (float) CARD_HEIGHT / 2).rotate(tapAngle).translate((float) -CARD_WIDTH / 2, (float) -CARD_HEIGHT / 2));
setTransform(QTransform()
.translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2)
.rotate(tapAngle)
.translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2));
update();
}
}
@@ -270,10 +271,11 @@ void AbstractCardItem::setFaceDown(bool _facedown)
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if ((event->modifiers() & Qt::ControlModifier)) {
if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) {
emit cardShiftClicked(name);
} else if ((event->modifiers() & Qt::ControlModifier)) {
setSelected(!isSelected());
}
else if (!isSelected()) {
} else if (!isSelected()) {
scene()->clearSelection();
setSelected(true);
}
@@ -288,7 +290,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::MidButton)
emit deleteCardInfoPopup(name);
// This function ensures the parent function doesn't mess around with our selection.
event->accept();
}
@@ -306,4 +308,3 @@ QVariant AbstractCardItem::itemChange(QGraphicsItem::GraphicsItemChange change,
} else
return QGraphicsItem::itemChange(change, value);
}

View File

@@ -2,17 +2,18 @@
#define ABSTRACTCARDITEM_H
#include "arrowtarget.h"
#include "carddatabase.h"
class CardInfo;
class Player;
const int CARD_WIDTH = 72;
const int CARD_HEIGHT = 102;
class AbstractCardItem : public ArrowTarget {
class AbstractCardItem : public ArrowTarget
{
Q_OBJECT
protected:
CardInfo *info;
CardInfoPtr info;
int id;
QString name;
bool tapped;
@@ -20,44 +21,84 @@ protected:
int tapAngle;
QString color;
QColor bgColor;
private:
bool isHovered;
qreal realZValue;
private slots:
void pixmapUpdated();
void cardInfoUpdated();
void callUpdate() { update(); }
void callUpdate()
{
update();
}
signals:
void hovered(AbstractCardItem *card);
void showCardInfoPopup(QPoint pos, QString cardName);
void deleteCardInfoPopup(QString cardName);
void updateCardMenu(AbstractCardItem *card);
void sigPixmapUpdated();
void cardShiftClicked(QString cardName);
public:
enum { Type = typeCard };
int type() const { return Type; }
enum
{
Type = typeCard
};
int type() const
{
return Type;
}
AbstractCardItem(const QString &_name = QString(), Player *_owner = 0, int _id = -1, QGraphicsItem *parent = 0);
~AbstractCardItem();
QRectF boundingRect() const;
QSizeF getTranslatedSize(QPainter *painter) const;
void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
CardInfo *getInfo() const { return info; }
int getId() const { return id; }
void setId(int _id) { id = _id; }
QString getName() const { return name; }
CardInfoPtr getInfo() const
{
return info;
}
int getId() const
{
return id;
}
void setId(int _id)
{
id = _id;
}
QString getName() const
{
return name;
}
void setName(const QString &_name = QString());
qreal getRealZValue() const { return realZValue; }
qreal getRealZValue() const
{
return realZValue;
}
void setRealZValue(qreal _zValue);
void setHovered(bool _hovered);
QString getColor() const { return color; }
QString getColor() const
{
return color;
}
void setColor(const QString &_color);
bool getTapped() const { return tapped; }
bool getTapped() const
{
return tapped;
}
void setTapped(bool _tapped, bool canAnimate = false);
bool getFaceDown() const { return facedown; }
bool getFaceDown() const
{
return facedown;
}
void setFaceDown(bool _facedown);
void processHoverEvent();
void deleteCardInfoPopup() { emit deleteCardInfoPopup(name); }
void deleteCardInfoPopup()
{
emit deleteCardInfoPopup(name);
}
protected:
void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
@@ -65,5 +106,5 @@ protected:
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value);
void cacheBgColor();
};
#endif

View File

@@ -1,28 +1,27 @@
#include "abstractclient.h"
#include "pending_command.h"
#include "client_metatypes.h"
#include "featureset.h"
#include "get_pb_extension.h"
#include "pb/commands.pb.h"
#include "pb/server_message.pb.h"
#include "pb/event_add_to_list.pb.h"
#include "pb/event_connection_closed.pb.h"
#include "pb/event_game_joined.pb.h"
#include "pb/event_list_rooms.pb.h"
#include "pb/event_notify_user.pb.h"
#include "pb/event_remove_from_list.pb.h"
#include "pb/event_replay_added.pb.h"
#include "pb/event_server_identification.pb.h"
#include "pb/event_server_message.pb.h"
#include "pb/event_server_shutdown.pb.h"
#include "pb/event_connection_closed.pb.h"
#include "pb/event_user_message.pb.h"
#include "pb/event_notify_user.pb.h"
#include "pb/event_list_rooms.pb.h"
#include "pb/event_add_to_list.pb.h"
#include "pb/event_remove_from_list.pb.h"
#include "pb/event_user_joined.pb.h"
#include "pb/event_user_left.pb.h"
#include "pb/event_game_joined.pb.h"
#include "pb/event_replay_added.pb.h"
#include "get_pb_extension.h"
#include "pb/event_user_message.pb.h"
#include "pb/server_message.pb.h"
#include "pending_command.h"
#include <google/protobuf/descriptor.h>
#include "client_metatypes.h"
#include "featureset.h"
AbstractClient::AbstractClient(QObject *parent)
: QObject(parent), nextCmdId(0), status(StatusDisconnected)
AbstractClient::AbstractClient(QObject *parent) : QObject(parent), nextCmdId(0), status(StatusDisconnected)
{
qRegisterMetaType<QVariant>("QVariant");
qRegisterMetaType<CommandContainer>("CommandContainer");
@@ -44,13 +43,13 @@ AbstractClient::AbstractClient(QObject *parent)
qRegisterMetaType<Event_UserMessage>("Event_UserMessage");
qRegisterMetaType<Event_NotifyUser>("Event_NotifyUser");
qRegisterMetaType<ServerInfo_User>("ServerInfo_User");
qRegisterMetaType<QList<ServerInfo_User> >("QList<ServerInfo_User>");
qRegisterMetaType<QList<ServerInfo_User>>("QList<ServerInfo_User>");
qRegisterMetaType<Event_ReplayAdded>("Event_ReplayAdded");
qRegisterMetaType<QList<QString> >("missingFeatures");
qRegisterMetaType<QList<QString>>("missingFeatures");
FeatureSet features;
features.initalizeFeatureList(clientFeatures);
connect(this, SIGNAL(sigQueuePendingCommand(PendingCommand *)), this, SLOT(queuePendingCommand(PendingCommand *)));
}
@@ -64,33 +63,60 @@ void AbstractClient::processProtocolItem(const ServerMessage &item)
case ServerMessage::RESPONSE: {
const Response &response = item.response();
const int cmdId = response.cmd_id();
PendingCommand *pend = pendingCommands.value(cmdId, 0);
if (!pend)
return;
pendingCommands.remove(cmdId);
pend->processResponse(response);
pend->deleteLater();
break;
}
case ServerMessage::SESSION_EVENT: {
const SessionEvent &event = item.session_event();
switch ((SessionEvent::SessionEventType) getPbExtension(event)) {
case SessionEvent::SERVER_IDENTIFICATION: emit serverIdentificationEventReceived(event.GetExtension(Event_ServerIdentification::ext)); break;
case SessionEvent::SERVER_MESSAGE: emit serverMessageEventReceived(event.GetExtension(Event_ServerMessage::ext)); break;
case SessionEvent::SERVER_SHUTDOWN: emit serverShutdownEventReceived(event.GetExtension(Event_ServerShutdown::ext)); break;
case SessionEvent::CONNECTION_CLOSED: emit connectionClosedEventReceived(event.GetExtension(Event_ConnectionClosed::ext)); break;
case SessionEvent::USER_MESSAGE: emit userMessageEventReceived(event.GetExtension(Event_UserMessage::ext)); break;
case SessionEvent::NOTIFY_USER: emit notifyUserEventReceived(event.GetExtension(Event_NotifyUser::ext)); break;
case SessionEvent::LIST_ROOMS: emit listRoomsEventReceived(event.GetExtension(Event_ListRooms::ext)); break;
case SessionEvent::ADD_TO_LIST: emit addToListEventReceived(event.GetExtension(Event_AddToList::ext)); break;
case SessionEvent::REMOVE_FROM_LIST: emit removeFromListEventReceived(event.GetExtension(Event_RemoveFromList::ext)); break;
case SessionEvent::USER_JOINED: emit userJoinedEventReceived(event.GetExtension(Event_UserJoined::ext)); break;
case SessionEvent::USER_LEFT: emit userLeftEventReceived(event.GetExtension(Event_UserLeft::ext)); break;
case SessionEvent::GAME_JOINED: emit gameJoinedEventReceived(event.GetExtension(Event_GameJoined::ext)); break;
case SessionEvent::REPLAY_ADDED: emit replayAddedEventReceived(event.GetExtension(Event_ReplayAdded::ext)); break;
default: break;
switch ((SessionEvent::SessionEventType)getPbExtension(event)) {
case SessionEvent::SERVER_IDENTIFICATION:
emit serverIdentificationEventReceived(event.GetExtension(Event_ServerIdentification::ext));
break;
case SessionEvent::SERVER_MESSAGE:
emit serverMessageEventReceived(event.GetExtension(Event_ServerMessage::ext));
break;
case SessionEvent::SERVER_SHUTDOWN:
emit serverShutdownEventReceived(event.GetExtension(Event_ServerShutdown::ext));
break;
case SessionEvent::CONNECTION_CLOSED:
emit connectionClosedEventReceived(event.GetExtension(Event_ConnectionClosed::ext));
break;
case SessionEvent::USER_MESSAGE:
emit userMessageEventReceived(event.GetExtension(Event_UserMessage::ext));
break;
case SessionEvent::NOTIFY_USER:
emit notifyUserEventReceived(event.GetExtension(Event_NotifyUser::ext));
break;
case SessionEvent::LIST_ROOMS:
emit listRoomsEventReceived(event.GetExtension(Event_ListRooms::ext));
break;
case SessionEvent::ADD_TO_LIST:
emit addToListEventReceived(event.GetExtension(Event_AddToList::ext));
break;
case SessionEvent::REMOVE_FROM_LIST:
emit removeFromListEventReceived(event.GetExtension(Event_RemoveFromList::ext));
break;
case SessionEvent::USER_JOINED:
emit userJoinedEventReceived(event.GetExtension(Event_UserJoined::ext));
break;
case SessionEvent::USER_LEFT:
emit userLeftEventReceived(event.GetExtension(Event_UserLeft::ext));
break;
case SessionEvent::GAME_JOINED:
emit gameJoinedEventReceived(event.GetExtension(Event_GameJoined::ext));
break;
case SessionEvent::REPLAY_ADDED:
emit replayAddedEventReceived(event.GetExtension(Event_ReplayAdded::ext));
break;
default:
break;
}
break;
}
@@ -130,9 +156,9 @@ void AbstractClient::queuePendingCommand(PendingCommand *pend)
// This function is always called from the client thread via signal/slot.
const int cmdId = getNewCmdId();
pend->getCommandContainer().set_cmd_id(cmdId);
pendingCommands.insert(cmdId, pend);
sendCommandContainer(pend->getCommandContainer());
}

View File

@@ -1,11 +1,11 @@
#ifndef ABSTRACTCLIENT_H
#define ABSTRACTCLIENT_H
#include <QObject>
#include <QVariant>
#include <QMutex>
#include "pb/response.pb.h"
#include "pb/serverinfo_user.pb.h"
#include <QMutex>
#include <QObject>
#include <QVariant>
class PendingCommand;
class CommandContainer;
@@ -27,7 +27,8 @@ class Event_ServerShutdown;
class Event_ReplayAdded;
class FeatureSet;
enum ClientStatus {
enum ClientStatus
{
StatusDisconnected,
StatusDisconnecting,
StatusConnecting,
@@ -40,11 +41,12 @@ enum ClientStatus {
StatusSubmitForgotPasswordChallenge,
};
class AbstractClient : public QObject {
class AbstractClient : public QObject
{
Q_OBJECT
signals:
void statusChanged(ClientStatus _status);
// Room events
void roomEventReceived(const RoomEvent &event);
// Game events
@@ -69,8 +71,9 @@ signals:
void registerAccepted();
void registerAcceptedNeedsActivate();
void activateAccepted();
void sigQueuePendingCommand(PendingCommand *pend);
private:
int nextCmdId;
mutable QMutex clientMutex;
@@ -79,23 +82,35 @@ private slots:
void queuePendingCommand(PendingCommand *pend);
protected slots:
void processProtocolItem(const ServerMessage &item);
protected:
QMap<int, PendingCommand *> pendingCommands;
QString userName, password, email, country, realName, token;
int gender;
void setStatus(ClientStatus _status);
int getNewCmdId() { return nextCmdId++; }
int getNewCmdId()
{
return nextCmdId++;
}
virtual void sendCommandContainer(const CommandContainer &cont) = 0;
public:
AbstractClient(QObject *parent = 0);
~AbstractClient();
ClientStatus getStatus() const { QMutexLocker locker(&clientMutex); return status; }
ClientStatus getStatus() const
{
QMutexLocker locker(&clientMutex);
return status;
}
void sendCommand(const CommandContainer &cont);
void sendCommand(PendingCommand *pend);
const QString getUserName() {return userName;}
const QString getUserName()
{
return userName;
}
static PendingCommand *prepareSessionCommand(const ::google::protobuf::Message &cmd);
static PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd, int roomId);
static PendingCommand *prepareModeratorCommand(const ::google::protobuf::Message &cmd);

View File

@@ -1,16 +1,24 @@
#include "abstractcounter.h"
#include "player.h"
#include "settingscache.h"
#include <QPainter>
#include <QMenu>
#include <QAction>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include "pb/command_inc_counter.pb.h"
#include "pb/command_set_counter.pb.h"
#include "player.h"
#include "settingscache.h"
#include <QAction>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QPainter>
AbstractCounter::AbstractCounter(Player *_player, int _id, const QString &_name, bool _shownInCounterArea, int _value, QGraphicsItem *parent)
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value), hovered(false), aDec(0), aInc(0), dialogSemaphore(false), deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
AbstractCounter::AbstractCounter(Player *_player,
int _id,
const QString &_name,
bool _shownInCounterArea,
int _value,
bool _useNameForShortcut,
QGraphicsItem *parent)
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value),
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(0), aInc(0), dialogSemaphore(false),
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
{
setAcceptHoverEvents(true);
@@ -23,9 +31,9 @@ AbstractCounter::AbstractCounter(Player *_player, int _id, const QString &_name,
menu->addAction(aSet);
menu->addSeparator();
for (int i = 10; i >= -10; --i)
if (i == 0)
if (i == 0) {
menu->addSeparator();
else {
} else {
QAction *aIncrement = new QAction(QString(i < 0 ? "%1" : "+%1").arg(i), this);
if (i == -1)
aDec = aIncrement;
@@ -36,9 +44,9 @@ AbstractCounter::AbstractCounter(Player *_player, int _id, const QString &_name,
menu->addAction(aIncrement);
}
} else
menu = 0;
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts()));
menu = nullptr;
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()), this, SLOT(refreshShortcuts()));
refreshShortcuts();
retranslateUi();
}
@@ -65,18 +73,26 @@ void AbstractCounter::retranslateUi()
void AbstractCounter::setShortcutsActive()
{
if (!player->getLocal()) {
return;
}
if (name == "life") {
shortcutActive = true;
aSet->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSet"));
aDec->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDec"));
aInc->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aInc"));
} else if (useNameForShortcut) {
shortcutActive = true;
aSet->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSetCounter_" + name));
aDec->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDecCounter_" + name));
aInc->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aIncCounter_" + name));
}
}
void AbstractCounter::setShortcutsInactive()
{
shortcutActive = false;
if (name == "life") {
if (name == "life" || useNameForShortcut) {
aSet->setShortcut(QKeySequence());
aDec->setShortcut(QKeySequence());
aInc->setShortcut(QKeySequence());
@@ -85,7 +101,7 @@ void AbstractCounter::setShortcutsInactive()
void AbstractCounter::refreshShortcuts()
{
if(shortcutActive)
if (shortcutActive)
setShortcutsActive();
}
@@ -97,7 +113,7 @@ void AbstractCounter::setValue(int _value)
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (isUnderMouse()) {
if (isUnderMouse() && player->getLocal()) {
if (event->button() == Qt::LeftButton) {
Command_IncCounter cmd;
cmd.set_counter_id(id);
@@ -114,8 +130,8 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
if (menu)
menu->exec(event->screenPos());
event->accept();
}
}else
}
} else
event->ignore();
}
@@ -144,8 +160,8 @@ void AbstractCounter::setCounter()
{
bool ok;
dialogSemaphore = true;
int newValue =
QInputDialog::getInt(0, tr("Set counter"), tr("New value for counter '%1':").arg(name), value, -2000000000, 2000000000, 1, &ok);
int newValue = QInputDialog::getInt(0, tr("Set counter"), tr("New value for counter '%1':").arg(name), value,
-2000000000, 2000000000, 1, &ok);
if (deleteAfterDialog) {
deleteLater();
return;
@@ -153,7 +169,7 @@ void AbstractCounter::setCounter()
dialogSemaphore = false;
if (!ok)
return;
Command_SetCounter cmd;
cmd.set_counter_id(id);
cmd.set_value(newValue);

View File

@@ -7,7 +7,8 @@ class Player;
class QMenu;
class QAction;
class AbstractCounter : public QObject, public QGraphicsItem {
class AbstractCounter : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected:
@@ -15,11 +16,12 @@ protected:
int id;
QString name;
int value;
bool hovered;
bool useNameForShortcut, hovered;
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
private:
QAction *aSet, *aDec, *aInc;
QMenu *menu;
@@ -29,20 +31,42 @@ private slots:
void refreshShortcuts();
void incrementCounter();
void setCounter();
public:
AbstractCounter(Player *_player, int _id, const QString &_name, bool _shownInCounterArea, int _value, QGraphicsItem *parent = 0);
AbstractCounter(Player *_player,
int _id,
const QString &_name,
bool _shownInCounterArea,
int _value,
bool _useNameForShortcut = false,
QGraphicsItem *parent = 0);
~AbstractCounter();
QMenu *getMenu() const { return menu; }
QMenu *getMenu() const
{
return menu;
}
void retranslateUi();
int getId() const { return id; }
QString getName() const { return name; }
bool getShownInCounterArea() const { return shownInCounterArea; }
int getValue() const { return value; }
int getId() const
{
return id;
}
QString getName() const
{
return name;
}
bool getShownInCounterArea() const
{
return shownInCounterArea;
}
int getValue() const
{
return value;
}
void setValue(int _value);
void delCounter();
void setShortcutsActive();
void setShortcutsInactive();
bool shortcutActive;

View File

@@ -1,7 +1,12 @@
#include "abstractgraphicsitem.h"
#include <QPainter>
void AbstractGraphicsItem::paintNumberEllipse(int number, int fontSize, const QColor &color, int position, int count, QPainter *painter)
void AbstractGraphicsItem::paintNumberEllipse(int number,
int fontSize,
const QColor &color,
int position,
int count,
QPainter *painter)
{
painter->save();
@@ -9,7 +14,7 @@ void AbstractGraphicsItem::paintNumberEllipse(int number, int fontSize, const QC
QFont font("Serif");
font.setPixelSize(fontSize);
font.setWeight(QFont::Bold);
QFontMetrics fm(font);
double w = fm.width(numStr) * 1.3;
double h = fm.height() * 1.3;
@@ -18,7 +23,7 @@ void AbstractGraphicsItem::paintNumberEllipse(int number, int fontSize, const QC
painter->setPen(QColor(255, 255, 255, 0));
painter->setBrush(QBrush(QColor(color)));
QRectF textRect;
if (position == -1)
textRect = QRectF((boundingRect().width() - w) / 2.0, (boundingRect().height() - h) / 2.0, w, h);
@@ -27,11 +32,15 @@ void AbstractGraphicsItem::paintNumberEllipse(int number, int fontSize, const QC
qreal yOffset = 20;
qreal spacing = 2;
if (position < 2)
textRect = QRectF(count == 1 ? ((boundingRect().width() - w) / 2.0) : (position % 2 == 0 ? xOffset : (boundingRect().width() - xOffset - w)), yOffset, w, h);
textRect = QRectF(count == 1 ? ((boundingRect().width() - w) / 2.0)
: (position % 2 == 0 ? xOffset : (boundingRect().width() - xOffset - w)),
yOffset, w, h);
else
textRect = QRectF(count == 3 ? ((boundingRect().width() - w) / 2.0) : (position % 2 == 0 ? xOffset : (boundingRect().width() - xOffset - w)), yOffset + (spacing + h) * (position / 2), w, h);
textRect = QRectF(count == 3 ? ((boundingRect().width() - w) / 2.0)
: (position % 2 == 0 ? xOffset : (boundingRect().width() - xOffset - w)),
yOffset + (spacing + h) * (position / 2), w, h);
}
painter->drawEllipse(textRect);
painter->setPen(Qt::black);

View File

@@ -3,7 +3,8 @@
#include <QGraphicsItem>
enum GraphicsItemType {
enum GraphicsItemType
{
typeCard = QGraphicsItem::UserType + 1,
typeCardDrag = QGraphicsItem::UserType + 2,
typeZone = QGraphicsItem::UserType + 3,
@@ -12,13 +13,17 @@ enum GraphicsItemType {
typeOther = QGraphicsItem::UserType + 6
};
class AbstractGraphicsItem : public QObject, public QGraphicsItem {
class AbstractGraphicsItem : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected:
void paintNumberEllipse(int number, int radius, const QColor &color, int position, int count, QPainter *painter);
public:
AbstractGraphicsItem(QGraphicsItem *parent = 0) : QObject(), QGraphicsItem(parent) { }
AbstractGraphicsItem(QGraphicsItem *parent = 0) : QObject(), QGraphicsItem(parent)
{
}
};
#endif

View File

@@ -2,16 +2,16 @@
#include <cmath>
#include "arrowitem.h"
#include "playertarget.h"
#include "carditem.h"
#include "carddatabase.h"
#include "carditem.h"
#include "cardzone.h"
#include "player.h"
#include "playertarget.h"
#include "settingscache.h"
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QDebug>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include "color.h"
#include "pb/command_attach_card.pb.h"
@@ -19,7 +19,8 @@
#include "pb/command_delete_arrow.pb.h"
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color), fullColor(true)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color),
fullColor(true)
{
qDebug() << "ArrowItem constructor: startItem=" << static_cast<QGraphicsItem *>(startItem);
setZValue(2000000005);
@@ -60,7 +61,8 @@ void ArrowItem::updatePath()
if (!targetItem)
return;
QPointF endPoint = targetItem->mapToScene(QPointF(targetItem->boundingRect().width() / 2, targetItem->boundingRect().height() / 2));
QPointF endPoint = targetItem->mapToScene(
QPointF(targetItem->boundingRect().width() / 2, targetItem->boundingRect().height() / 2));
updatePath(endPoint);
}
@@ -68,13 +70,15 @@ void ArrowItem::updatePath(const QPointF &endPoint)
{
const double arrowWidth = 15.0;
const double headWidth = 40.0;
const double headLength = headWidth / pow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++
const double headLength =
headWidth / pow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++
const double phi = 15;
if (!startItem)
return;
QPointF startPoint = startItem->mapToScene(QPointF(startItem->boundingRect().width() / 2, startItem->boundingRect().height() / 2));
QPointF startPoint =
startItem->mapToScene(QPointF(startItem->boundingRect().width() / 2, startItem->boundingRect().height() / 2));
QLineF line(startPoint, endPoint);
qreal lineLength = line.length();
@@ -92,10 +96,14 @@ void ArrowItem::updatePath(const QPointF &endPoint)
QPointF arrowBodyEndPoint = centerLine.pointAtPercent(percentage);
QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001));
qreal alpha = testLine.angle() - 90;
QPointF endPoint1 = arrowBodyEndPoint + arrowWidth / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
QPointF endPoint2 = arrowBodyEndPoint + arrowWidth / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
QPointF point1 = endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
QPointF point2 = endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
QPointF endPoint1 =
arrowBodyEndPoint + arrowWidth / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
QPointF endPoint2 =
arrowBodyEndPoint + arrowWidth / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
QPointF point1 =
endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180));
QPointF point2 =
endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180));
path = QPainterPath(-arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180)));
path.quadTo(c, endPoint1);
@@ -229,15 +237,15 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
PlayerTarget *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem);
cmd.set_target_player_id(targetPlayer->getOwner()->getId());
}
if (startZone->getName().compare("hand") == 0) {
if (startZone->getName().compare("hand") == 0) {
startCard->playCard(false);
CardInfo *ci = startCard->getInfo();
CardInfoPtr ci = startCard->getInfo();
if (ci && (((!settingsCache->getPlayToStack() && ci->getTableRow() == 3) ||
((settingsCache->getPlayToStack() && ci->getTableRow() != 0) &&
startCard->getZone()->getName().toStdString() != "stack"))))
((settingsCache->getPlayToStack() && ci->getTableRow() != 0) &&
startCard->getZone()->getName().toStdString() != "stack"))))
cmd.set_start_zone("stack");
else
cmd.set_start_zone(settingsCache->getPlayToStack() ? "stack" :"table");
cmd.set_start_zone(settingsCache->getPlayToStack() ? "stack" : "table");
}
player->sendGameCommand(cmd);
}

View File

@@ -9,12 +9,14 @@ class QMenu;
class Player;
class ArrowTarget;
class ArrowItem : public QObject, public QGraphicsItem {
class ArrowItem : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private:
QPainterPath path;
QMenu *menu;
protected:
Player *player;
int id;
@@ -22,40 +24,70 @@ protected:
QColor color;
bool fullColor;
void mousePressEvent(QGraphicsSceneMouseEvent *event);
public:
ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &color);
~ArrowItem();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QRectF boundingRect() const { return path.boundingRect(); }
QPainterPath shape() const { return path; }
QRectF boundingRect() const
{
return path.boundingRect();
}
QPainterPath shape() const
{
return path;
}
void updatePath();
void updatePath(const QPointF &endPoint);
int getId() const { return id; }
Player *getPlayer() const { return player; }
void setStartItem(ArrowTarget *_item) { startItem = _item; }
void setTargetItem(ArrowTarget *_item) { targetItem = _item; }
ArrowTarget *getStartItem() const { return startItem; }
ArrowTarget *getTargetItem() const { return targetItem; }
int getId() const
{
return id;
}
Player *getPlayer() const
{
return player;
}
void setStartItem(ArrowTarget *_item)
{
startItem = _item;
}
void setTargetItem(ArrowTarget *_item)
{
targetItem = _item;
}
ArrowTarget *getStartItem() const
{
return startItem;
}
ArrowTarget *getTargetItem() const
{
return targetItem;
}
void delArrow();
};
class ArrowDragItem : public ArrowItem {
class ArrowDragItem : public ArrowItem
{
Q_OBJECT
private:
QList<ArrowDragItem *> childArrows;
public:
ArrowDragItem(Player *_owner, ArrowTarget *_startItem, const QColor &_color);
void addChildArrow(ArrowDragItem *childArrow);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
class ArrowAttachItem : public ArrowItem {
class ArrowAttachItem : public ArrowItem
{
Q_OBJECT
public:
ArrowAttachItem(ArrowTarget *_startItem);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

View File

@@ -7,29 +7,56 @@
class Player;
class ArrowItem;
class ArrowTarget : public AbstractGraphicsItem {
class ArrowTarget : public AbstractGraphicsItem
{
Q_OBJECT
protected:
Player *owner;
private:
bool beingPointedAt;
QList<ArrowItem *> arrowsFrom, arrowsTo;
public:
ArrowTarget(Player *_owner, QGraphicsItem *parent = 0);
~ArrowTarget();
Player *getOwner() const { return owner; }
Player *getOwner() const
{
return owner;
}
void setBeingPointedAt(bool _beingPointedAt);
bool getBeingPointedAt() const { return beingPointedAt; }
const QList<ArrowItem *> &getArrowsFrom() const { return arrowsFrom; }
void addArrowFrom(ArrowItem *arrow) { arrowsFrom.append(arrow); }
void removeArrowFrom(ArrowItem *arrow) { arrowsFrom.removeAt(arrowsFrom.indexOf(arrow)); }
const QList<ArrowItem *> &getArrowsTo() const { return arrowsTo; }
void addArrowTo(ArrowItem *arrow) { arrowsTo.append(arrow); }
void removeArrowTo(ArrowItem *arrow) { arrowsTo.removeAt(arrowsTo.indexOf(arrow)); }
bool getBeingPointedAt() const
{
return beingPointedAt;
}
const QList<ArrowItem *> &getArrowsFrom() const
{
return arrowsFrom;
}
void addArrowFrom(ArrowItem *arrow)
{
arrowsFrom.append(arrow);
}
void removeArrowFrom(ArrowItem *arrow)
{
arrowsFrom.removeAt(arrowsFrom.indexOf(arrow));
}
const QList<ArrowItem *> &getArrowsTo() const
{
return arrowsTo;
}
void addArrowTo(ArrowItem *arrow)
{
arrowsTo.append(arrow);
}
void removeArrowTo(ArrowItem *arrow)
{
arrowsTo.removeAt(arrowsTo.indexOf(arrow));
}
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,106 @@
#ifndef CARDDATABASE_H
#define CARDDATABASE_H
#include <QHash>
#include <QPixmap>
#include <QMap>
#include <QBasicMutex>
#include <QDate>
#include <QDataStream>
#include <QHash>
#include <QList>
#include <QXmlStreamReader>
#include <QMap>
#include <QMetaType>
#include <QSharedPointer>
#include <QStringList>
#include <QVector>
class CardDatabase;
class CardInfo;
class CardSet;
class CardRelation;
class ICardDatabaseParser;
typedef QMap<QString, QString> QStringMap;
// If we don't typedef this, CardInfo::CardInfo will refuse to compile on OS X < 10.9
typedef QMap<QString, int> MuidMap;
typedef QSharedPointer<CardInfo> CardInfoPtr;
typedef QSharedPointer<CardSet> CardSetPtr;
class CardSet : public QList<CardInfo *> {
Q_DECLARE_METATYPE(CardInfoPtr)
class CardSet : public QList<CardInfoPtr>
{
private:
QString shortName, longName;
unsigned int sortKey;
QDate releaseDate;
QString setType;
bool enabled, isknown;
public:
CardSet(const QString &_shortName = QString(), const QString &_longName = QString(), const QString &_setType = QString(), const QDate &_releaseDate = QDate());
explicit CardSet(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate());
static CardSetPtr newInstance(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate());
QString getCorrectedShortName() const;
QString getShortName() const { return shortName; }
QString getLongName() const { return longName; }
QString getSetType() const { return setType; }
QDate getReleaseDate() const { return releaseDate; }
void setLongName(QString & _longName) { longName = _longName; }
void setSetType(QString & _setType) { setType = _setType; }
void setReleaseDate(QDate & _releaseDate) { releaseDate = _releaseDate; }
QString getShortName() const
{
return shortName;
}
QString getLongName() const
{
return longName;
}
QString getSetType() const
{
return setType;
}
QDate getReleaseDate() const
{
return releaseDate;
}
void setLongName(const QString &_longName)
{
longName = _longName;
}
void setSetType(const QString &_setType)
{
setType = _setType;
}
void setReleaseDate(const QDate &_releaseDate)
{
releaseDate = _releaseDate;
}
void loadSetOptions();
int getSortKey() const { return sortKey; }
int getSortKey() const
{
return sortKey;
}
void setSortKey(unsigned int _sortKey);
bool getEnabled() const { return enabled; }
bool getEnabled() const
{
return enabled;
}
void setEnabled(bool _enabled);
bool getIsKnown() const { return isknown; }
bool getIsKnown() const
{
return isknown;
}
void setIsKnown(bool _isknown);
// Determine incomplete sets.
bool getIsKnownIgnored() const
{
return longName.length() + setType.length() + releaseDate.toString().length() == 0;
}
};
class SetList : public QList<CardSet *> {
class SetList : public QList<CardSetPtr>
{
private:
class KeyCompareFunctor;
public:
void sortByKey();
void guessSortKeys();
@@ -58,9 +112,11 @@ public:
QStringList getUnknownSetsNames();
};
class CardInfo : public QObject {
class CardInfo : public QObject
{
Q_OBJECT
private:
CardInfoPtr smartThis;
QString name;
/*
@@ -77,15 +133,20 @@ private:
QString powtough;
QString text;
QStringList colors;
// the cards i'm related to
QStringList relatedCards;
QList<CardRelation *> relatedCards;
// the card i'm reverse-related to
QStringList reverseRelatedCards;
QList<CardRelation *> reverseRelatedCards;
// the cards thare are reverse-related to me
QStringList reverseRelatedCardsToMe;
QList<CardRelation *> reverseRelatedCardsToMe;
QString setsNames;
bool upsideDownArt;
int loyalty;
QString loyalty;
QStringMap customPicURLs;
MuidMap muIds;
QStringMap collectorNumbers;
@@ -93,71 +154,204 @@ private:
bool cipt;
int tableRow;
QString pixmapCacheKey;
public:
CardInfo(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
const QString &_cardtype = QString(),
const QString &_powtough = QString(),
const QString &_text = QString(),
const QStringList &_colors = QStringList(),
const QStringList &_relatedCards = QStringList(),
const QStringList &_reverseRelatedCards = QStringList(),
bool _upsideDownArt = false,
int _loyalty = 0,
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
MuidMap muids = MuidMap(),
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap()
);
~CardInfo();
inline const QString &getName() const { return name; }
inline const QString &getSetsNames() const { return setsNames; }
const QString &getSimpleName() const { return simpleName; }
bool getIsToken() const { return isToken; }
const SetList &getSets() const { return sets; }
inline const QString &getManaCost() const { return manacost; }
inline const QString &getCmc() const { return cmc; }
inline const QString &getCardType() const { return cardtype; }
inline const QString &getPowTough() const { return powtough; }
const QString &getText() const { return text; }
const QString &getPixmapCacheKey() const { return pixmapCacheKey; }
const int &getLoyalty() const { return loyalty; }
bool getCipt() const { return cipt; }
void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); }
void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(this); }
void setCardType(const QString &_cardType) { cardtype = _cardType; emit cardInfoChanged(this); }
void setPowTough(const QString &_powTough) { powtough = _powTough; emit cardInfoChanged(this); }
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
explicit CardInfo(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
const QString &_cardtype = QString(),
const QString &_powtough = QString(),
const QString &_text = QString(),
const QStringList &_colors = QStringList(),
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
bool _upsideDownArt = false,
const QString &_loyalty = QString(),
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
MuidMap muids = MuidMap(),
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap());
~CardInfo() override;
static CardInfoPtr newInstance(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
const QString &_cardtype = QString(),
const QString &_powtough = QString(),
const QString &_text = QString(),
const QStringList &_colors = QStringList(),
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
bool _upsideDownArt = false,
const QString &_loyalty = QString(),
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
MuidMap muids = MuidMap(),
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap());
void setSmartPointer(CardInfoPtr _ptr)
{
smartThis = _ptr;
}
inline const QString &getName() const
{
return name;
}
inline const QString &getSetsNames() const
{
return setsNames;
}
const QString &getSimpleName() const
{
return simpleName;
}
bool getIsToken() const
{
return isToken;
}
const SetList &getSets() const
{
return sets;
}
inline const QString &getManaCost() const
{
return manacost;
}
inline const QString &getCmc() const
{
return cmc;
}
inline const QString &getCardType() const
{
return cardtype;
}
inline const QString &getPowTough() const
{
return powtough;
}
const QString &getText() const
{
return text;
}
const QString &getPixmapCacheKey() const
{
return pixmapCacheKey;
}
const QString &getLoyalty() const
{
return loyalty;
}
bool getCipt() const
{
return cipt;
}
// void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(smartThis); }
// void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(smartThis); }
void setCardType(const QString &_cardType)
{
cardtype = _cardType;
emit cardInfoChanged(smartThis);
}
void setPowTough(const QString &_powTough)
{
powtough = _powTough;
emit cardInfoChanged(smartThis);
}
void setText(const QString &_text)
{
text = _text;
emit cardInfoChanged(smartThis);
}
void setColors(const QStringList &_colors)
{
colors = _colors;
emit cardInfoChanged(smartThis);
}
const QChar getColorChar() const;
const QStringList &getColors() const { return colors; }
const QStringList &getRelatedCards() const { return relatedCards; }
const QStringList &getReverseRelatedCards() const { return reverseRelatedCards; }
const QStringList &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; }
void resetReverseRelatedCards2Me() { reverseRelatedCardsToMe = QStringList(); }
void addReverseRelatedCards2Me(QString & cardName) { reverseRelatedCardsToMe.append(cardName); }
bool getUpsideDownArt() const { return upsideDownArt; }
QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); }
int getMuId(const QString &set) const { return muIds.value(set); }
QString getCollectorNumber(const QString &set) const { return collectorNumbers.value(set); }
QString getRarity(const QString &set) const { return rarities.value(set); }
QStringMap getRarities() const { return rarities; }
const QStringList &getColors() const
{
return colors;
}
const QList<CardRelation *> &getRelatedCards() const
{
return relatedCards;
}
const QList<CardRelation *> &getReverseRelatedCards() const
{
return reverseRelatedCards;
}
const QList<CardRelation *> &getReverseRelatedCards2Me() const
{
return reverseRelatedCardsToMe;
}
void resetReverseRelatedCards2Me();
void addReverseRelatedCards2Me(CardRelation *cardRelation)
{
reverseRelatedCardsToMe.append(cardRelation);
}
bool getUpsideDownArt() const
{
return upsideDownArt;
}
QString getCustomPicURL(const QString &set) const
{
return customPicURLs.value(set);
}
int getMuId(const QString &set) const
{
return muIds.value(set);
}
QString getCollectorNumber(const QString &set) const
{
return collectorNumbers.value(set);
}
QString getRarity(const QString &set) const
{
return rarities.value(set);
}
QStringMap getRarities() const
{
return rarities;
}
QString getMainCardType() const;
QString getCorrectedName() const;
int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; }
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); }
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
void setSetNumber(const QString &_set, const QString &_setNumber) { collectorNumbers.insert(_set, _setNumber); }
void setRarity(const QString &_set, const QString &_setNumber) { rarities.insert(_set, _setNumber); }
void addToSet(CardSet *set);
void emitPixmapUpdated() { emit pixmapUpdated(); }
int getTableRow() const
{
return tableRow;
}
void setTableRow(int _tableRow)
{
tableRow = _tableRow;
}
// void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(smartThis); }
// void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set,
// _customPicURL); }
void setMuId(const QString &_set, const int &_muId)
{
muIds.insert(_set, _muId);
}
void setSetNumber(const QString &_set, const QString &_setNumber)
{
collectorNumbers.insert(_set, _setNumber);
}
void setRarity(const QString &_set, const QString &_setNumber)
{
rarities.insert(_set, _setNumber);
}
void addToSet(CardSetPtr set);
void emitPixmapUpdated()
{
emit pixmapUpdated();
}
void refreshCachedSetNames();
/**
@@ -165,17 +359,27 @@ public:
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
signals:
void pixmapUpdated();
void cardInfoChanged(CardInfo *card);
void cardInfoChanged(CardInfoPtr card);
};
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
enum LoadStatus
{
Ok,
VersionTooOld,
Invalid,
NotLoaded,
FileError,
NoCards
};
typedef QHash<QString, CardInfo *> CardNameMap;
typedef QHash<QString, CardSet *> SetNameMap;
typedef QHash<QString, CardInfoPtr> CardNameMap;
typedef QHash<QString, CardSetPtr> SetNameMap;
class CardDatabase : public QObject {
class CardDatabase : public QObject
{
Q_OBJECT
protected:
/*
@@ -194,46 +398,56 @@ protected:
SetNameMap sets;
LoadStatus loadStatus;
private:
static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
QVector<ICardDatabaseParser *> availableParsers;
private:
CardInfoPtr getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
void checkUnknownSets();
void refreshCachedReverseRelatedCards();
public:
static const char* TOKENS_SETNAME;
CardDatabase(QObject *parent = 0);
~CardDatabase();
QBasicMutex *reloadDatabaseMutex = new QBasicMutex(), *clearDatabaseMutex = new QBasicMutex(),
*loadFromFileMutex = new QBasicMutex(), *addCardMutex = new QBasicMutex(),
*removeCardMutex = new QBasicMutex();
public:
static const char *TOKENS_SETNAME;
explicit CardDatabase(QObject *parent = nullptr);
~CardDatabase() override;
void clear();
void addCard(CardInfo *card);
void removeCard(CardInfo *card);
CardInfo *getCard(const QString &cardName) const;
QList <CardInfo *> getCards(const QStringList &cardNames) const;
void removeCard(CardInfoPtr card);
CardInfoPtr getCard(const QString &cardName) const;
QList<CardInfoPtr> getCards(const QStringList &cardNames) const;
/*
* Get a card by its simple name. The name will be simplified in this
* function, so you don't need to simplify it beforehand.
*/
CardInfo *getCardBySimpleName(const QString &cardName) const;
CardInfoPtr getCardBySimpleName(const QString &cardName) const;
CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cards.values(); }
CardSetPtr getSet(const QString &setName);
QList<CardInfoPtr> getCardList() const
{
return cards.values();
}
SetList getSetList() const;
LoadStatus loadFromFile(const QString &fileName);
bool saveToFile(const QString &fileName, bool tokens = false);
bool saveCustomTokensToFile();
QStringList getAllColors() const;
QStringList getAllMainCardTypes() const;
LoadStatus getLoadStatus() const { return loadStatus; }
LoadStatus getLoadStatus() const
{
return loadStatus;
}
void enableAllUnknownSets();
void markAllSetsAsKnown();
void notifyEnabledSetsChanged();
public slots:
LoadStatus loadCardDatabases();
void addCard(CardInfoPtr card);
void addSet(CardSetPtr set);
private slots:
LoadStatus loadCardDatabase(const QString &path);
signals:
@@ -241,8 +455,50 @@ signals:
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
void cardDatabaseAllNewSetsEnabled();
void cardDatabaseEnabledSetsChanged();
void cardAdded(CardInfo *card);
void cardRemoved(CardInfo *card);
void cardAdded(CardInfoPtr card);
void cardRemoved(CardInfoPtr card);
};
#endif
class CardRelation : public QObject
{
Q_OBJECT
private:
QString name;
bool doesAttach;
bool isCreateAllExclusion;
bool isVariableCount;
int defaultCount;
public:
explicit CardRelation(const QString &_name = QString(),
bool _doesAttach = false,
bool _isCreateAllExclusion = false,
bool _isVariableCount = false,
int _defaultCount = 1);
inline const QString &getName() const
{
return name;
}
bool getDoesAttach() const
{
return doesAttach;
}
bool getCanCreateAnother() const
{
return !doesAttach;
}
bool getIsCreateAllExclusion() const
{
return isCreateAllExclusion;
}
bool getIsVariable() const
{
return isVariableCount;
}
int getDefaultCount() const
{
return defaultCount;
}
};
#endif

View File

@@ -3,11 +3,11 @@
#define CARDDBMODEL_COLUMNS 6
CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, QObject *parent)
: QAbstractListModel(parent), db(_db)
CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent)
: QAbstractListModel(parent), db(_db), showOnlyCardsFromEnabledSets(_showOnlyCardsFromEnabledSets)
{
connect(db, SIGNAL(cardAdded(CardInfo *)), this, SLOT(cardAdded(CardInfo *)));
connect(db, SIGNAL(cardRemoved(CardInfo *)), this, SLOT(cardRemoved(CardInfo *)));
connect(db, SIGNAL(cardAdded(CardInfoPtr)), this, SLOT(cardAdded(CardInfoPtr)));
connect(db, SIGNAL(cardRemoved(CardInfoPtr)), this, SLOT(cardRemoved(CardInfoPtr)));
connect(db, SIGNAL(cardDatabaseEnabledSetsChanged()), this, SLOT(cardDatabaseEnabledSetsChanged()));
cardDatabaseEnabledSetsChanged();
@@ -17,35 +17,39 @@ CardDatabaseModel::~CardDatabaseModel()
{
}
int CardDatabaseModel::rowCount(const QModelIndex &/*parent*/) const
int CardDatabaseModel::rowCount(const QModelIndex & /*parent*/) const
{
return cardList.size();
}
int CardDatabaseModel::columnCount(const QModelIndex &/*parent*/) const
int CardDatabaseModel::columnCount(const QModelIndex & /*parent*/) const
{
return CARDDBMODEL_COLUMNS;
}
QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() ||
index.row() >= cardList.size() ||
index.column() >= CARDDBMODEL_COLUMNS ||
if (!index.isValid() || index.row() >= cardList.size() || index.column() >= CARDDBMODEL_COLUMNS ||
(role != Qt::DisplayRole && role != SortRole))
return QVariant();
CardInfo *card = cardList.at(index.row());
switch (index.column()){
case NameColumn: return card->getName();
case SetListColumn: return card->getSetsNames();
case ManaCostColumn: return role == SortRole ?
QString("%1%2").arg(card->getCmc(), 4, QChar('0')).arg(card->getManaCost()) :
card->getManaCost();
case CardTypeColumn: return card->getCardType();
case PTColumn: return card->getPowTough();
case ColorColumn: return card->getColors().join("");
default: return QVariant();
CardInfoPtr card = cardList.at(index.row());
switch (index.column()) {
case NameColumn:
return card->getName();
case SetListColumn:
return card->getSetsNames();
case ManaCostColumn:
return role == SortRole ? QString("%1%2").arg(card->getCmc(), 4, QChar('0')).arg(card->getManaCost())
: card->getManaCost();
case CardTypeColumn:
return card->getCardType();
case PTColumn:
return card->getPowTough();
case ColorColumn:
return card->getColors().join("");
default:
return QVariant();
}
}
@@ -56,30 +60,39 @@ QVariant CardDatabaseModel::headerData(int section, Qt::Orientation orientation,
if (orientation != Qt::Horizontal)
return QVariant();
switch (section) {
case NameColumn: return QString(tr("Name"));
case SetListColumn: return QString(tr("Sets"));
case ManaCostColumn: return QString(tr("Mana cost"));
case CardTypeColumn: return QString(tr("Card type"));
case PTColumn: return QString(tr("P/T"));
case ColorColumn: return QString(tr("Color(s)"));
default: return QVariant();
case NameColumn:
return QString(tr("Name"));
case SetListColumn:
return QString(tr("Sets"));
case ManaCostColumn:
return QString(tr("Mana cost"));
case CardTypeColumn:
return QString(tr("Card type"));
case PTColumn:
return QString(tr("P/T"));
case ColorColumn:
return QString(tr("Color(s)"));
default:
return QVariant();
}
}
void CardDatabaseModel::cardInfoChanged(CardInfo *card)
void CardDatabaseModel::cardInfoChanged(CardInfoPtr card)
{
const int row = cardList.indexOf(card);
if (row == -1)
return;
emit dataChanged(index(row, 0), index(row, CARDDBMODEL_COLUMNS - 1));
}
bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfo *card)
bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfoPtr card)
{
foreach(CardSet * set, card->getSets())
{
if(set->getEnabled())
if (!showOnlyCardsFromEnabledSets)
return true;
for (CardSetPtr set : card->getSets()) {
if (set->getEnabled())
return true;
}
@@ -89,66 +102,63 @@ bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfo *card)
void CardDatabaseModel::cardDatabaseEnabledSetsChanged()
{
// remove all the cards no more present in at least one enabled set
foreach(CardInfo * card, cardList)
{
if(!checkCardHasAtLeastOneEnabledSet(card))
foreach (CardInfoPtr card, cardList) {
if (!checkCardHasAtLeastOneEnabledSet(card))
cardRemoved(card);
}
// re-check all the card currently not shown, maybe their part of a newly-enabled set
foreach(CardInfo * card, db->getCardList())
{
if(!cardList.contains(card))
foreach (CardInfoPtr card, db->getCardList()) {
if (!cardList.contains(card))
cardAdded(card);
}
}
void CardDatabaseModel::cardAdded(CardInfo *card)
void CardDatabaseModel::cardAdded(CardInfoPtr card)
{
if(checkCardHasAtLeastOneEnabledSet(card))
{
if (checkCardHasAtLeastOneEnabledSet(card)) {
// add the card if it's present in at least one enabled set
beginInsertRows(QModelIndex(), cardList.size(), cardList.size());
cardList.append(card);
connect(card, SIGNAL(cardInfoChanged(CardInfo *)), this, SLOT(cardInfoChanged(CardInfo *)));
connect(card.data(), SIGNAL(cardInfoChanged(CardInfoPtr)), this, SLOT(cardInfoChanged(CardInfoPtr)));
endInsertRows();
}
}
void CardDatabaseModel::cardRemoved(CardInfo *card)
void CardDatabaseModel::cardRemoved(CardInfoPtr card)
{
const int row = cardList.indexOf(card);
if (row == -1)
if (row == -1) {
return;
}
beginRemoveRows(QModelIndex(), row, row);
disconnect(card, 0, this, 0);
disconnect(card.data(), nullptr, this, nullptr);
card.clear();
cardList.removeAt(row);
endRemoveRows();
}
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent)
: QSortFilterProxyModel(parent),
isToken(ShowAll)
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) : QSortFilterProxyModel(parent), isToken(ShowAll)
{
filterTree = NULL;
filterTree = nullptr;
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
loadedRowCount = 0;
}
bool CardDatabaseDisplayModel::canFetchMore(const QModelIndex & index) const
bool CardDatabaseDisplayModel::canFetchMore(const QModelIndex &index) const
{
return loadedRowCount < sourceModel()->rowCount(index);
}
void CardDatabaseDisplayModel::fetchMore(const QModelIndex & index)
void CardDatabaseDisplayModel::fetchMore(const QModelIndex &index)
{
int remainder = sourceModel()->rowCount(index) - loadedRowCount;
int itemsToFetch = qMin(100, remainder);
beginInsertRows(QModelIndex(), loadedRowCount, loadedRowCount+itemsToFetch-1);
beginInsertRows(QModelIndex(), loadedRowCount, loadedRowCount + itemsToFetch - 1);
loadedRowCount += itemsToFetch;
endInsertRows();
@@ -159,13 +169,13 @@ int CardDatabaseDisplayModel::rowCount(const QModelIndex &parent) const
return qMin(QSortFilterProxyModel::rowCount(parent), loadedRowCount);
}
bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {
bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
QString leftString = sourceModel()->data(left, CardDatabaseModel::SortRole).toString();
QString rightString = sourceModel()->data(right, CardDatabaseModel::SortRole).toString();
if (!cardName.isEmpty() && left.column() == CardDatabaseModel::NameColumn)
{
if (!cardName.isEmpty() && left.column() == CardDatabaseModel::NameColumn) {
bool isLeftType = leftString.startsWith(cardName, Qt::CaseInsensitive);
bool isRightType = rightString.startsWith(cardName, Qt::CaseInsensitive);
@@ -177,28 +187,112 @@ bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelInd
// same checks for the right string
if (isRightType && (!isLeftType || rightString.size() == cardName.size()))
return false;
} else if (right.column() == CardDatabaseModel::PTColumn && left.column() == CardDatabaseModel::PTColumn) {
QStringList leftList = leftString.split("/");
QStringList rightList = rightString.split("/");
if (leftList.size() == 2 && rightList.size() == 2) {
// cool, have both P/T in list now
int lessThanNum = lessThanNumerically(leftList.at(0), rightList.at(0));
if (lessThanNum != 0) {
return lessThanNum < 0;
} else {
// power equal, check toughness
return lessThanNumerically(leftList.at(1), rightList.at(1)) < 0;
}
}
}
return QString::localeAwareCompare(leftString, rightString) < 0;
}
int CardDatabaseDisplayModel::lessThanNumerically(const QString &left, const QString &right)
{
if (left == right) {
return 0;
}
bool okLeft, okRight;
float leftNum = left.toFloat(&okLeft);
float rightNum = right.toFloat(&okRight);
if (okLeft && okRight) {
if (leftNum < rightNum) {
return -1;
} else if (leftNum > rightNum) {
return 1;
} else {
return 0;
}
}
// try and parsing again, for weird ones like "1+*"
QString leftAfterNum = "";
QString rightAfterNum = "";
if (!okLeft) {
int leftNumIndex = 0;
for (; leftNumIndex < left.length(); leftNumIndex++) {
if (!left.at(leftNumIndex).isDigit()) {
break;
}
}
if (leftNumIndex != 0) {
leftNum = left.left(leftNumIndex).toFloat(&okLeft);
leftAfterNum = left.right(leftNumIndex);
}
}
if (!okRight) {
int rightNumIndex = 0;
for (; rightNumIndex < right.length(); rightNumIndex++) {
if (!right.at(rightNumIndex).isDigit()) {
break;
}
}
if (rightNumIndex != 0) {
rightNum = right.left(rightNumIndex).toFloat(&okRight);
rightAfterNum = right.right(rightNumIndex);
}
}
if (okLeft && okRight) {
if (leftNum != rightNum) {
// both parsed as numbers, but different number
if (leftNum < rightNum) {
return -1;
} else {
return 1;
}
} else {
// both parsed, same number, but at least one has something else
// so compare the part after the number - prefer nothing
return QString::localeAwareCompare(leftAfterNum, rightAfterNum);
}
} else if (okLeft) {
return -1;
} else if (okRight) {
return 1;
}
// couldn't parse it, just return String comparison
return QString::localeAwareCompare(left, right);
}
bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
{
CardInfo const *info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
CardInfoPtr info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
return false;
return rowMatchesCardName(info);
}
bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfo const *info) const {
bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const
{
if (!cardName.isEmpty() && !info->getName().contains(cardName, Qt::CaseInsensitive))
return false;
if (!cardNameSet.isEmpty() && !cardNameSet.contains(info->getName()))
return false;
if (filterTree != NULL)
if (filterTree != nullptr)
return filterTree->acceptsCard(info);
return true;
@@ -210,15 +304,15 @@ void CardDatabaseDisplayModel::clearFilterAll()
cardText.clear();
cardTypes.clear();
cardColors.clear();
if (filterTree != NULL)
if (filterTree != nullptr)
filterTree->clear();
invalidateFilter();
}
void CardDatabaseDisplayModel::setFilterTree(FilterTree *filterTree)
{
if (this->filterTree != NULL)
disconnect(this->filterTree, 0, this, 0);
if (this->filterTree != nullptr)
disconnect(this->filterTree, nullptr, this, nullptr);
this->filterTree = filterTree;
connect(this->filterTree, SIGNAL(changed()), this, SLOT(filterTreeChanged()));
@@ -230,15 +324,13 @@ void CardDatabaseDisplayModel::filterTreeChanged()
invalidate();
}
TokenDisplayModel::TokenDisplayModel(QObject *parent)
: CardDatabaseDisplayModel(parent)
TokenDisplayModel::TokenDisplayModel(QObject *parent) : CardDatabaseDisplayModel(parent)
{
}
bool TokenDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
{
CardInfo const *info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
CardInfoPtr info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
return info->getIsToken() && rowMatchesCardName(info);
}

View File

@@ -1,43 +1,70 @@
#ifndef CARDDATABASEMODEL_H
#define CARDDATABASEMODEL_H
#include "carddatabase.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <QList>
#include <QSet>
#include "carddatabase.h"
#include <QSortFilterProxyModel>
class FilterTree;
class CardDatabaseModel : public QAbstractListModel {
class CardDatabaseModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Columns { NameColumn, SetListColumn, ManaCostColumn, PTColumn, CardTypeColumn, ColorColumn };
enum Role { SortRole=Qt::UserRole };
CardDatabaseModel(CardDatabase *_db, QObject *parent = 0);
enum Columns
{
NameColumn,
SetListColumn,
ManaCostColumn,
PTColumn,
CardTypeColumn,
ColorColumn
};
enum Role
{
SortRole = Qt::UserRole
};
CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent = 0);
~CardDatabaseModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
CardDatabase *getDatabase() const { return db; }
CardInfo *getCard(int index) const { return cardList[index]; }
private:
QList<CardInfo *> cardList;
CardDatabase *db;
CardDatabase *getDatabase() const
{
return db;
}
CardInfoPtr getCard(int index) const
{
return cardList[index];
}
inline bool checkCardHasAtLeastOneEnabledSet(CardInfo *card);
private:
QList<CardInfoPtr> cardList;
CardDatabase *db;
bool showOnlyCardsFromEnabledSets;
inline bool checkCardHasAtLeastOneEnabledSet(CardInfoPtr card);
private slots:
void cardAdded(CardInfo *card);
void cardRemoved(CardInfo *card);
void cardInfoChanged(CardInfo *card);
void cardAdded(CardInfoPtr card);
void cardRemoved(CardInfoPtr card);
void cardInfoChanged(CardInfoPtr card);
void cardDatabaseEnabledSetsChanged();
};
class CardDatabaseDisplayModel : public QSortFilterProxyModel {
class CardDatabaseDisplayModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
enum FilterBool { ShowTrue, ShowFalse, ShowAll };
enum FilterBool
{
ShowTrue,
ShowFalse,
ShowAll
};
private:
FilterBool isToken;
QString cardNameBeginning, cardName, cardText;
@@ -45,34 +72,70 @@ private:
QSet<QString> cardNameSet, cardTypes, cardColors;
FilterTree *filterTree;
int loadedRowCount;
public:
CardDatabaseDisplayModel(QObject *parent = 0);
void setFilterTree(FilterTree *filterTree);
void setIsToken(FilterBool _isToken) { isToken = _isToken; invalidate(); }
void setCardNameBeginning(const QString &_beginning) { cardNameBeginning = _beginning; invalidate(); }
void setCardName(const QString &_cardName) { cardName = _cardName; invalidate(); }
void setCardNameSet(const QSet<QString> &_cardNameSet) { cardNameSet = _cardNameSet; invalidate(); }
void setSearchTerm(const QString &_searchTerm) { searchTerm = _searchTerm; }
void setCardText(const QString &_cardText) { cardText = _cardText; invalidate(); }
void setCardTypes(const QSet<QString> &_cardTypes) { cardTypes = _cardTypes; invalidate(); }
void setCardColors(const QSet<QString> &_cardColors) { cardColors = _cardColors; invalidate(); }
void setIsToken(FilterBool _isToken)
{
isToken = _isToken;
invalidate();
}
void setCardNameBeginning(const QString &_beginning)
{
cardNameBeginning = _beginning;
invalidate();
}
void setCardName(const QString &_cardName)
{
cardName = _cardName;
invalidate();
}
void setCardNameSet(const QSet<QString> &_cardNameSet)
{
cardNameSet = _cardNameSet;
invalidate();
}
void setSearchTerm(const QString &_searchTerm)
{
searchTerm = _searchTerm;
}
void setCardText(const QString &_cardText)
{
cardText = _cardText;
invalidate();
}
void setCardTypes(const QSet<QString> &_cardTypes)
{
cardTypes = _cardTypes;
invalidate();
}
void setCardColors(const QSet<QString> &_cardColors)
{
cardColors = _cardColors;
invalidate();
}
void clearFilterAll();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
static int lessThanNumerically(const QString &left, const QString &right);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
bool rowMatchesCardName(CardInfo const *info) const;
bool rowMatchesCardName(CardInfoPtr info) const;
bool canFetchMore(const QModelIndex &parent) const;
void fetchMore(const QModelIndex &parent);
private slots:
void filterTreeChanged();
};
class TokenDisplayModel : public CardDatabaseDisplayModel {
class TokenDisplayModel : public CardDatabaseDisplayModel
{
Q_OBJECT
public:
TokenDisplayModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
};

View File

@@ -0,0 +1,24 @@
#ifndef CARDDATABASE_PARSER_H
#define CARDDATABASE_PARSER_H
#include <QIODevice>
#include <QString>
#include "../carddatabase.h"
class ICardDatabaseParser : public QObject
{
public:
virtual ~ICardDatabaseParser()
{
}
virtual bool getCanParseFile(const QString &name, QIODevice &device) = 0;
virtual void parseFile(QIODevice &device) = 0;
virtual bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName) = 0;
signals:
virtual void addCard(CardInfoPtr card) = 0;
};
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
#endif

View File

@@ -0,0 +1,396 @@
#include "cockatricexml3.h"
#include <QDebug>
#include <QFile>
#include <QXmlStreamReader>
#define COCKATRICE_XML3_TAGNAME "cockatrice_carddatabase"
#define COCKATRICE_XML3_TAGVER 3
bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device)
{
qDebug() << "[CockatriceXml3Parser] Trying to parse: " << fileName;
if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) {
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong extension";
return false;
}
QXmlStreamReader xml(&device);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() == COCKATRICE_XML3_TAGNAME) {
int version = xml.attributes().value("version").toString().toInt();
if (version == COCKATRICE_XML3_TAGVER) {
return true;
} else {
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong version" << version;
return false;
}
} else {
qDebug() << "[CockatriceXml3Parser] Parsing failed: wrong element tag" << xml.name();
return false;
}
}
}
return true;
}
void CockatriceXml3Parser::parseFile(QIODevice &device)
{
QXmlStreamReader xml(&device);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "sets") {
loadSetsFromXml(xml);
} else if (xml.name() == "cards") {
loadCardsFromXml(xml);
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml3Parser] Unknown item" << xml.name() << ", trying to continue anyway";
xml.skipCurrentElement();
}
}
}
}
}
CardSetPtr CockatriceXml3Parser::internalAddSet(const QString &setName,
const QString &longName,
const QString &setType,
const QDate &releaseDate)
{
if (sets.contains(setName)) {
return sets.value(setName);
}
CardSetPtr newSet = CardSet::newInstance(setName);
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);
sets.insert(setName, newSet);
emit addSet(newSet);
return newSet;
}
void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "set") {
QString shortName, longName, setType;
QDate releaseDate;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "name") {
shortName = xml.readElementText();
} else if (xml.name() == "longname") {
longName = xml.readElementText();
} else if (xml.name() == "settype") {
setType = xml.readElementText();
} else if (xml.name() == "releasedate") {
releaseDate = QDate::fromString(xml.readElementText(), Qt::ISODate);
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml3Parser] Unknown set property" << xml.name()
<< ", trying to continue anyway";
xml.skipCurrentElement();
}
}
internalAddSet(shortName, longName, setType, releaseDate);
}
}
}
void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
{
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "card") {
QString name, manacost, cmc, type, pt, text, loyalty;
QStringList colors;
QList<CardRelation *> relatedCards, reverseRelatedCards;
QStringMap customPicURLs;
MuidMap muids;
QStringMap collectorNumbers, rarities;
SetList sets;
int tableRow = 0;
bool cipt = false;
bool isToken = false;
bool upsideDown = false;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
}
if (xml.name() == "name") {
name = xml.readElementText();
} else if (xml.name() == "manacost") {
manacost = xml.readElementText();
} else if (xml.name() == "cmc") {
cmc = xml.readElementText();
} else if (xml.name() == "type") {
type = xml.readElementText();
} else if (xml.name() == "pt") {
pt = xml.readElementText();
} else if (xml.name() == "text") {
text = xml.readElementText();
} else if (xml.name() == "set") {
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText();
sets.append(internalAddSet(setName));
if (attrs.hasAttribute("muId")) {
muids[setName] = attrs.value("muId").toString().toInt();
}
if (attrs.hasAttribute("picURL")) {
customPicURLs[setName] = attrs.value("picURL").toString();
}
if (attrs.hasAttribute("num")) {
collectorNumbers[setName] = attrs.value("num").toString();
}
if (attrs.hasAttribute("rarity")) {
rarities[setName] = attrs.value("rarity").toString();
}
} else if (xml.name() == "color") {
colors << xml.readElementText();
} else if (xml.name() == "related" || xml.name() == "reverse-related") {
bool attach = false;
bool exclude = false;
bool variable = false;
int count = 1;
QXmlStreamAttributes attrs = xml.attributes();
QString cardName = xml.readElementText();
if (attrs.hasAttribute("count")) {
if (attrs.value("count").toString().indexOf("x=") == 0) {
variable = true;
count = attrs.value("count").toString().remove(0, 2).toInt();
} else if (attrs.value("count").toString().indexOf("x") == 0) {
variable = true;
} else {
count = attrs.value("count").toString().toInt();
}
if (count < 1) {
count = 1;
}
}
if (attrs.hasAttribute("attach")) {
attach = true;
}
if (attrs.hasAttribute("exclude")) {
exclude = true;
}
auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
if (xml.name() == "reverse-related") {
reverseRelatedCards << relation;
} else {
relatedCards << relation;
}
} else if (xml.name() == "tablerow") {
tableRow = xml.readElementText().toInt();
} else if (xml.name() == "cipt") {
cipt = (xml.readElementText() == "1");
} else if (xml.name() == "upsidedown") {
upsideDown = (xml.readElementText() == "1");
} else if (xml.name() == "loyalty") {
loyalty = xml.readElementText();
} else if (xml.name() == "token") {
isToken = static_cast<bool>(xml.readElementText().toInt());
} else if (xml.name() != "") {
qDebug() << "[CockatriceXml3Parser] Unknown card property" << xml.name()
<< ", trying to continue anyway";
xml.skipCurrentElement();
}
}
CardInfoPtr newCard = CardInfo::newInstance(
name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, reverseRelatedCards, upsideDown,
loyalty, cipt, tableRow, sets, customPicURLs, muids, collectorNumbers, rarities);
emit addCard(newCard);
}
}
}
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
{
if (set.isNull()) {
qDebug() << "&operator<< set is nullptr";
return xml;
}
xml.writeStartElement("set");
xml.writeTextElement("name", set->getShortName());
xml.writeTextElement("longname", set->getLongName());
xml.writeTextElement("settype", set->getSetType());
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
xml.writeEndElement();
return xml;
}
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info)
{
if (info.isNull()) {
qDebug() << "operator<< info is nullptr";
return xml;
}
xml.writeStartElement("card");
xml.writeTextElement("name", info->getName());
const SetList &sets = info->getSets();
QString tmpString;
QString tmpSet;
for (int i = 0; i < sets.size(); i++) {
xml.writeStartElement("set");
tmpSet = sets[i]->getShortName();
xml.writeAttribute("rarity", info->getRarity(tmpSet));
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
tmpString = info->getCollectorNumber(tmpSet);
if (!tmpString.isEmpty()) {
xml.writeAttribute("num", info->getCollectorNumber(tmpSet));
}
tmpString = info->getCustomPicURL(tmpSet);
if (!tmpString.isEmpty()) {
xml.writeAttribute("picURL", tmpString);
}
xml.writeCharacters(tmpSet);
xml.writeEndElement();
}
const QStringList &colors = info->getColors();
for (int i = 0; i < colors.size(); i++) {
xml.writeTextElement("color", colors[i]);
}
const QList<CardRelation *> related = info->getRelatedCards();
for (auto i : related) {
xml.writeStartElement("related");
if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach");
}
if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude");
}
if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x");
} else {
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
}
} else if (1 != i->getDefaultCount()) {
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
}
xml.writeCharacters(i->getName());
xml.writeEndElement();
}
const QList<CardRelation *> reverseRelated = info->getReverseRelatedCards();
for (auto i : reverseRelated) {
xml.writeStartElement("reverse-related");
if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach");
}
if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude");
}
if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x");
} else {
xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount()));
}
} else if (1 != i->getDefaultCount()) {
xml.writeAttribute("count", QString::number(i->getDefaultCount()));
}
xml.writeCharacters(i->getName());
xml.writeEndElement();
}
xml.writeTextElement("manacost", info->getManaCost());
xml.writeTextElement("cmc", info->getCmc());
xml.writeTextElement("type", info->getCardType());
if (!info->getPowTough().isEmpty()) {
xml.writeTextElement("pt", info->getPowTough());
}
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
xml.writeTextElement("text", info->getText());
if (info->getMainCardType() == "Planeswalker") {
xml.writeTextElement("loyalty", info->getLoyalty());
}
if (info->getCipt()) {
xml.writeTextElement("cipt", "1");
}
if (info->getIsToken()) {
xml.writeTextElement("token", "1");
}
if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1");
}
xml.writeEndElement(); // card
return xml;
}
bool CockatriceXml3Parser::saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
QXmlStreamWriter xml(&file);
xml.setAutoFormatting(true);
xml.writeStartDocument();
xml.writeStartElement(COCKATRICE_XML3_TAGNAME);
xml.writeAttribute("version", QString::number(COCKATRICE_XML3_TAGVER));
if (sets.count() > 0) {
xml.writeStartElement("sets");
for (CardSetPtr set : sets) {
xml << set;
}
xml.writeEndElement();
}
if (cards.count() > 0) {
xml.writeStartElement("cards");
for (CardInfoPtr card : cards) {
xml << card;
}
xml.writeEndElement();
}
xml.writeEndElement(); // cockatrice_carddatabase
xml.writeEndDocument();
return true;
}

View File

@@ -0,0 +1,36 @@
#ifndef COCKATRICE_XML3_H
#define COCKATRICE_XML3_H
#include <QXmlStreamReader>
#include "carddatabaseparser.h"
class CockatriceXml3Parser : public ICardDatabaseParser
{
Q_OBJECT
Q_INTERFACES(ICardDatabaseParser)
public:
CockatriceXml3Parser() = default;
~CockatriceXml3Parser() = default;
bool getCanParseFile(const QString &name, QIODevice &device);
void parseFile(QIODevice &device);
bool saveToFile(SetNameMap sets, CardNameMap cards, const QString &fileName);
private:
/*
* A cached list of the available sets, needed to cross-reference sets from cards.
*/
SetNameMap sets;
CardSetPtr internalAddSet(const QString &setName,
const QString &longName = "",
const QString &setType = "",
const QDate &releaseDate = QDate());
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
signals:
void addCard(CardInfoPtr card);
void addSet(CardSetPtr set);
};
#endif

View File

@@ -1,14 +1,18 @@
#include "carddragitem.h"
#include "carditem.h"
#include "cardzone.h"
#include "gamescene.h"
#include "tablezone.h"
#include "zoneviewzone.h"
#include "gamescene.h"
#include <QGraphicsSceneMouseEvent>
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
CardDragItem::CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, bool _faceDown, AbstractCardDragItem *parentDrag)
CardDragItem::CardDragItem(CardItem *_item,
int _id,
const QPointF &_hotSpot,
bool _faceDown,
AbstractCardDragItem *parentDrag)
: AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), faceDown(_faceDown), occupied(false), currentZone(0)
{
}
@@ -16,14 +20,16 @@ CardDragItem::CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, bo
void CardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
AbstractCardDragItem::paint(painter, option, widget);
if (occupied)
painter->fillRect(boundingRect(), QColor(200, 0, 0, 100));
}
void CardDragItem::updatePosition(const QPointF &cursorScenePos)
{
QList<QGraphicsItem *> colliding = scene()->items(cursorScenePos, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder, static_cast<GameScene *>(scene())->getViewTransform());
QList<QGraphicsItem *> colliding =
scene()->items(cursorScenePos, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder,
static_cast<GameScene *>(scene())->getViewTransform());
CardZone *cardZone = 0;
ZoneViewZone *zoneViewZone = 0;
@@ -42,18 +48,18 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
if (!cursorZone)
return;
currentZone = cursorZone;
QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos;
QPointF cardTopLeft = cursorPosInZone - hotSpot;
QPointF closestGridPoint = cursorZone->closestGridPoint(cardTopLeft);
QPointF newPos = zonePos + closestGridPoint;
if (newPos != pos()) {
for (int i = 0; i < childDrags.size(); i++)
childDrags[i]->setPos(newPos + childDrags[i]->getHotSpot());
setPos(newPos);
bool newOccupied = false;
TableZone *table = qobject_cast<TableZone *>(cursorZone);
if (table)
@@ -85,7 +91,7 @@ void CardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
}
}
if(currentZone)
if (currentZone)
currentZone->handleDropEvent(dragItemList, startZone, (sp - currentZone->scenePos()).toPoint());
event->accept();

View File

@@ -5,19 +5,32 @@
class CardItem;
class CardDragItem : public AbstractCardDragItem {
class CardDragItem : public AbstractCardDragItem
{
Q_OBJECT
private:
int id;
bool faceDown;
bool occupied;
CardZone *currentZone;
public:
CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, bool _faceDown, AbstractCardDragItem *parentDrag = 0);
int getId() const { return id; }
bool getFaceDown() const { return faceDown; }
CardDragItem(CardItem *_item,
int _id,
const QPointF &_hotSpot,
bool _faceDown,
AbstractCardDragItem *parentDrag = 0);
int getId() const
{
return id;
}
bool getFaceDown() const
{
return faceDown;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void updatePosition(const QPointF &cursorScenePos);
protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

View File

@@ -4,13 +4,13 @@ const char *CardFilter::typeName(Type t)
{
switch (t) {
case TypeAnd:
return "and";
return "AND";
case TypeOr:
return "or";
return "OR";
case TypeAndNot:
return "and not";
return "AND NOT";
case TypeOrNot:
return "or not";
return "OR NOT";
default:
return "";
}
@@ -20,25 +20,27 @@ const char *CardFilter::attrName(Attr a)
{
switch (a) {
case AttrName:
return "name";
return "Name";
case AttrType:
return "type";
return "Type";
case AttrColor:
return "color";
return "Color";
case AttrText:
return "text";
return "Text";
case AttrSet:
return "set";
return "Set";
case AttrManaCost:
return "mana cost";
return "Mana Cost";
case AttrCmc:
return "cmc";
return "CMC";
case AttrRarity:
return "rarity";
return "Rarity";
case AttrPow:
return "power";
return "Power";
case AttrTough:
return "toughness";
return "Toughness";
case AttrLoyalty:
return "Loyalty";
default:
return "";
}

View File

@@ -3,9 +3,11 @@
#include <QString>
class CardFilter {
class CardFilter
{
public:
enum Type {
enum Type
{
TypeAnd = 0,
TypeOr,
TypeAndNot,
@@ -15,17 +17,19 @@ public:
/* if you add an atribute here you also need to
* add its string representation in attrName */
enum Attr {
AttrName = 0,
AttrType,
enum Attr
{
AttrCmc = 0,
AttrColor,
AttrText,
AttrSet,
AttrLoyalty,
AttrManaCost,
AttrCmc,
AttrRarity,
AttrName,
AttrPow,
AttrRarity,
AttrSet,
AttrText,
AttrTough,
AttrType,
AttrEnd
};
@@ -35,11 +39,20 @@ private:
enum Attr a;
public:
CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr) {};
CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr){};
Type type() const { return t; }
const QString &term() const { return trm; }
Attr attr() const { return a; }
Type type() const
{
return t;
}
const QString &term() const
{
return trm;
}
Attr attr() const
{
return a;
}
static const char *typeName(Type t);
static const char *attrName(Attr a);

View File

@@ -1,17 +1,15 @@
#include "cardframe.h"
#include "carditem.h"
#include "carddatabase.h"
#include "main.h"
#include "cardinfopicture.h"
#include "cardinfotext.h"
#include "carditem.h"
#include "main.h"
#include "settingscache.h"
#include <QSplitter>
#include <QVBoxLayout>
CardFrame::CardFrame(const QString &cardName, QWidget *parent)
: QTabWidget(parent), info(nullptr), cardTextOnly(false)
CardFrame::CardFrame(const QString &cardName, QWidget *parent) : QTabWidget(parent), info(nullptr), cardTextOnly(false)
{
setContentsMargins(3, 3, 3, 3);
pic = new CardInfoPicture();
@@ -69,11 +67,10 @@ void CardFrame::retranslateUi()
void CardFrame::setViewMode(int mode)
{
if(currentIndex() != mode)
if (currentIndex() != mode)
setCurrentIndex(mode);
switch(mode)
{
switch (mode) {
case ImageOnlyView:
case TextOnlyView:
tab1Layout->addWidget(pic);
@@ -83,18 +80,24 @@ void CardFrame::setViewMode(int mode)
splitter->addWidget(pic);
splitter->addWidget(text);
break;
default:
break;
}
settingsCache->setCardInfoViewMode(mode);
}
void CardFrame::setCard(CardInfo *card)
void CardFrame::setCard(CardInfoPtr card)
{
if (info)
disconnect(info, nullptr, this, nullptr);
if (info) {
disconnect(info.data(), nullptr, this, nullptr);
}
info = card;
if(info)
connect(info, SIGNAL(destroyed()), this, SLOT(clear()));
if (info) {
connect(info.data(), SIGNAL(destroyed()), this, SLOT(clear()));
}
text->setCard(info);
pic->setCard(info);
@@ -102,15 +105,17 @@ void CardFrame::setCard(CardInfo *card)
void CardFrame::setCard(const QString &cardName)
{
setCard(db->getCard(cardName));
setCard(db->getCardBySimpleName(cardName));
}
void CardFrame::setCard(AbstractCardItem *card)
{
setCard(card->getInfo());
if (card) {
setCard(card->getInfo());
}
}
void CardFrame::clear()
{
setCard((CardInfo*) nullptr);
setCard((CardInfoPtr) nullptr);
}

View File

@@ -3,32 +3,38 @@
#include <QTabWidget>
#include "carddatabase.h"
class AbstractCardItem;
class CardInfo;
class CardInfoPicture;
class CardInfoText;
class QVBoxLayout;
class QSplitter;
class CardFrame : public QTabWidget {
class CardFrame : public QTabWidget
{
Q_OBJECT
private:
CardInfo *info;
CardInfoPtr info;
CardInfoPicture *pic;
CardInfoText *text;
bool cardTextOnly;
QWidget *tab1, *tab2, *tab3;
QVBoxLayout *tab1Layout, *tab2Layout, *tab3Layout;
QSplitter *splitter;
public:
enum ViewMode { ImageOnlyView, TextOnlyView, ImageAndTextView };
CardFrame(const QString &cardName = QString(),
QWidget *parent = 0);
public:
enum ViewMode
{
ImageOnlyView,
TextOnlyView,
ImageAndTextView
};
explicit CardFrame(const QString &cardName = QString(), QWidget *parent = nullptr);
void retranslateUi();
public slots:
void setCard(CardInfo *card);
void setCard(CardInfoPtr card);
void setCard(const QString &cardName);
void setCard(AbstractCardItem *card);
void clear();

View File

@@ -1,29 +1,29 @@
#include "cardinfopicture.h"
#include <QWidget>
#include <QPainter>
#include <QStyle>
#include <QWidget>
#include "carditem.h"
#include "carddatabase.h"
#include "pictureloader.h"
#include "main.h"
#include "pictureloader.h"
CardInfoPicture::CardInfoPicture(QWidget *parent)
: QWidget(parent),
info(nullptr),
pixmapDirty(true)
CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true)
{
setMinimumHeight(100);
}
void CardInfoPicture::setCard(CardInfo *card)
void CardInfoPicture::setCard(CardInfoPtr card)
{
if (info)
disconnect(info, nullptr, this, nullptr);
if (info) {
disconnect(info.data(), nullptr, this, nullptr);
}
info = card;
if(info)
connect(info, SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
if (info) {
connect(info.data(), SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
}
updatePixmap();
}
@@ -41,7 +41,10 @@ void CardInfoPicture::updatePixmap()
void CardInfoPicture::loadPixmap()
{
PictureLoader::getPixmap(resizedPixmap, info, size());
if (info)
PictureLoader::getPixmap(resizedPixmap, info, size());
else
PictureLoader::getCardBackPixmap(resizedPixmap, size());
}
void CardInfoPicture::paintEvent(QPaintEvent *)
@@ -49,7 +52,7 @@ void CardInfoPicture::paintEvent(QPaintEvent *)
if (width() == 0 || height() == 0)
return;
if(pixmapDirty)
if (pixmapDirty)
loadPixmap();
QPainter painter(this);

View File

@@ -3,25 +3,28 @@
#include <QWidget>
class AbstractCardItem;
class CardInfo;
#include "carddatabase.h"
class CardInfoPicture : public QWidget {
class AbstractCardItem;
class CardInfoPicture : public QWidget
{
Q_OBJECT
private:
CardInfo *info;
CardInfoPtr info;
QPixmap resizedPixmap;
bool pixmapDirty;
public:
CardInfoPicture(QWidget *parent = 0);
protected:
void resizeEvent(QResizeEvent *event);
void paintEvent(QPaintEvent *);
void loadPixmap();
void paintEvent(QPaintEvent *);
void loadPixmap();
public slots:
void setCard(CardInfo *card);
void setCard(CardInfoPtr card);
void updatePixmap();
};

View File

@@ -1,14 +1,12 @@
#include "cardinfotext.h"
#include "carditem.h"
#include "main.h"
#include <QGridLayout>
#include <QLabel>
#include <QTextEdit>
#include <QGridLayout>
#include "carditem.h"
#include "carddatabase.h"
#include "main.h"
CardInfoText::CardInfoText(QWidget *parent)
: QFrame(parent), info(nullptr)
CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
{
nameLabel1 = new QLabel;
nameLabel2 = new QLabel;
@@ -57,16 +55,15 @@ CardInfoText::CardInfoText(QWidget *parent)
retranslateUi();
}
void CardInfoText::setCard(CardInfo *card)
void CardInfoText::setCard(CardInfoPtr card)
{
if(card)
{
if (card) {
nameLabel2->setText(card->getName());
manacostLabel2->setText(card->getManaCost());
colorLabel2->setText(card->getColors().join(""));
cardtypeLabel2->setText(card->getCardType());
powtoughLabel2->setText(card->getPowTough());
loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString());
loyaltyLabel2->setText(card->getLoyalty());
textLabel->setText(card->getText());
} else {
nameLabel2->setText("");

View File

@@ -3,11 +3,12 @@
#include <QFrame>
#include "carddatabase.h"
class QLabel;
class QTextEdit;
class CardInfo;
class CardInfoText : public QFrame {
class CardInfoText : public QFrame
{
Q_OBJECT
private:
@@ -19,14 +20,14 @@ private:
QLabel *loyaltyLabel1, *loyaltyLabel2;
QTextEdit *textLabel;
CardInfo *info;
CardInfoPtr info;
public:
CardInfoText(QWidget *parent = 0);
void retranslateUi();
public slots:
void setCard(CardInfo *card);
void setCard(CardInfoPtr card);
};
#endif

View File

@@ -1,16 +1,13 @@
#include <QVBoxLayout>
#include <QDesktopWidget>
#include "cardinfowidget.h"
#include "carditem.h"
#include "carddatabase.h"
#include "cardinfopicture.h"
#include "cardinfotext.h"
#include "carditem.h"
#include "main.h"
#include <QDesktopWidget>
#include <QVBoxLayout>
CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags)
: QFrame(parent, flags)
, aspectRatio((qreal) CARD_HEIGHT / (qreal) CARD_WIDTH)
, info(nullptr)
: QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH), info(nullptr)
{
setContentsMargins(3, 3, 3, 3);
pic = new CardInfoPicture();
@@ -18,7 +15,7 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win
text = new CardInfoText();
text->setObjectName("text");
QVBoxLayout * layout = new QVBoxLayout();
QVBoxLayout *layout = new QVBoxLayout();
layout->setObjectName("layout");
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
@@ -33,20 +30,20 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win
pic->setFixedWidth(pixmapWidth);
pic->setFixedHeight(pixmapHeight);
setFixedWidth(pixmapWidth + 150);
setCard(cardName);
// ensure our parent gets a valid size to position us correctly
resize(width(), sizeHint().height());
}
void CardInfoWidget::setCard(CardInfo *card)
void CardInfoWidget::setCard(CardInfoPtr card)
{
if (info)
disconnect(info, nullptr, this, nullptr);
disconnect(info.data(), nullptr, this, nullptr);
info = card;
if(info)
connect(info, SIGNAL(destroyed()), this, SLOT(clear()));
if (info)
connect(info.data(), SIGNAL(destroyed()), this, SLOT(clear()));
text->setCard(info);
pic->setCard(info);
@@ -64,5 +61,5 @@ void CardInfoWidget::setCard(AbstractCardItem *card)
void CardInfoWidget::clear()
{
setCard((CardInfo *) nullptr);
setCard((CardInfoPtr) nullptr);
}

View File

@@ -1,28 +1,31 @@
#ifndef CARDINFOWIDGET_H
#define CARDINFOWIDGET_H
#include <QComboBox>
#include <QFrame>
#include <QStringList>
#include <QComboBox>
class CardInfo;
#include "carddatabase.h"
class CardInfoPicture;
class CardInfoText;
class AbstractCardItem;
class CardInfoWidget : public QFrame {
class CardInfoWidget : public QFrame
{
Q_OBJECT
private:
qreal aspectRatio;
CardInfo *info;
CardInfoPtr info;
CardInfoPicture *pic;
CardInfoText *text;
public:
CardInfoWidget(const QString &cardName, QWidget *parent = 0, Qt::WindowFlags f = 0);
public slots:
void setCard(CardInfo *card);
void setCard(CardInfoPtr card);
void setCard(const QString &cardName);
void setCard(AbstractCardItem *card);

View File

@@ -1,32 +1,31 @@
#include <QApplication>
#include <QPainter>
#include <QMenu>
#include <QAction>
#include <QGraphicsSceneMouseEvent>
#include "gamescene.h"
#include "carditem.h"
#include "carddragitem.h"
#include "carddatabase.h"
#include "cardzone.h"
#include "zoneviewzone.h"
#include "tablezone.h"
#include "player.h"
#include "arrowitem.h"
#include "carddatabase.h"
#include "carddragitem.h"
#include "cardzone.h"
#include "gamescene.h"
#include "main.h"
#include "pb/serverinfo_card.pb.h"
#include "player.h"
#include "settingscache.h"
#include "tab_game.h"
#include "pb/serverinfo_card.pb.h"
#include "tablezone.h"
#include "zoneviewzone.h"
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QPainter>
CardItem::CardItem(Player *_owner, const QString &_name, int _cardid, bool _revealedCard, QGraphicsItem *parent)
: AbstractCardItem(_name, _owner, _cardid, parent), zone(0), revealedCard(_revealedCard), attacking(false), destroyOnZoneChange(false), doesntUntap(false), dragItem(0), attachedTo(0)
: AbstractCardItem(_name, _owner, _cardid, parent), zone(0), revealedCard(_revealedCard), attacking(false),
destroyOnZoneChange(false), doesntUntap(false), dragItem(0), attachedTo(0)
{
owner->addCard(this);
cardMenu = new QMenu;
ptMenu = new QMenu;
moveMenu = new QMenu;
retranslateUi();
emit updateCardMenu(this);
}
@@ -34,14 +33,14 @@ CardItem::CardItem(Player *_owner, const QString &_name, int _cardid, bool _reve
CardItem::~CardItem()
{
prepareDelete();
if (scene())
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
delete cardMenu;
delete ptMenu;
delete moveMenu;
deleteDragItem();
}
@@ -54,12 +53,12 @@ void CardItem::prepareDelete()
}
owner = 0;
}
while (!attachedCards.isEmpty()) {
attachedCards.first()->setZone(0); // so that it won't try to call reorganizeCards()
attachedCards.first()->setAttachedTo(0);
}
if (attachedTo) {
attachedTo->removeAttachedCard(this);
attachedTo = 0;
@@ -88,30 +87,29 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
{
painter->save();
AbstractCardItem::paint(painter, option, widget);
int i = 0;
QMapIterator<int, int> counterIterator(counters);
while (counterIterator.hasNext()) {
counterIterator.next();
QColor color;
color.setHsv(counterIterator.key() * 60, 150, 255);
paintNumberEllipse(counterIterator.value(), 14, color, i, counters.size(), painter);
++i;
}
QSizeF translatedSize = getTranslatedSize(painter);
qreal scaleFactor = translatedSize.width() / boundingRect().width();
if (!pt.isEmpty()) {
painter->save();
transformPainter(painter, translatedSize, tapAngle);
if(info)
{
if (info) {
QStringList ptSplit = pt.split("/");
QStringList ptDbSplit = info->getPowTough().split("/");
if (getFaceDown() || ptDbSplit.at(0) != ptSplit.at(0) || ptDbSplit.at(1) != ptSplit.at(1))
painter->setPen(QColor(255, 150, 0));
else
@@ -121,10 +119,13 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
}
painter->setBackground(Qt::black);
painter->setBackgroundMode(Qt::OpaqueMode);
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 10 * scaleFactor, translatedSize.height() - 8 * scaleFactor), Qt::AlignRight | Qt::AlignBottom, pt);
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 10 * scaleFactor,
translatedSize.height() - 8 * scaleFactor),
Qt::AlignRight | Qt::AlignBottom, pt);
painter->restore();
}
if (!annotation.isEmpty()) {
painter->save();
@@ -132,13 +133,33 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
painter->setBackground(Qt::black);
painter->setBackgroundMode(Qt::OpaqueMode);
painter->setPen(Qt::white);
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 8 * scaleFactor, translatedSize.height() - 8 * scaleFactor), Qt::AlignCenter | Qt::TextWrapAnywhere, annotation);
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 8 * scaleFactor,
translatedSize.height() - 8 * scaleFactor),
Qt::AlignCenter | Qt::TextWrapAnywhere, annotation);
painter->restore();
}
if (getBeingPointedAt())
if (getBeingPointedAt()) {
painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100)));
painter->restore();
painter->restore();
}
if (doesntUntap) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
transformPainter(painter, translatedSize, tapAngle);
QPen pen;
pen.setColor(Qt::magenta);
const int penWidth = 1;
pen.setWidth(penWidth);
painter->setPen(pen);
painter->drawRect(QRectF(0, 0, translatedSize.width() + penWidth, translatedSize.height() - penWidth));
painter->restore();
}
}
void CardItem::setAttacking(bool _attacking)
@@ -165,6 +186,7 @@ void CardItem::setAnnotation(const QString &_annotation)
void CardItem::setDoesntUntap(bool _doesntUntap)
{
doesntUntap = _doesntUntap;
update();
}
void CardItem::setPT(const QString &_pt)
@@ -177,7 +199,7 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
{
if (attachedTo)
attachedTo->removeAttachedCard(this);
gridPoint.setX(-1);
attachedTo = _attachedTo;
if (attachedTo) {
@@ -190,7 +212,7 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
if (zone)
zone->reorganizeCards();
emit updateCardMenu(this);
}
@@ -218,7 +240,7 @@ void CardItem::processCardInfo(const ServerInfo_Card &info)
const ServerInfo_CardCounter &counterInfo = info.counter_list(i);
counters.insert(counterInfo.id(), counterInfo.value());
}
setId(info.id());
setName(QString::fromStdString(info.name()));
setAttacking(info.attacking());
@@ -245,7 +267,7 @@ CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPoin
void CardItem::deleteDragItem()
{
if(dragItem)
if (dragItem)
dragItem->deleteLater();
dragItem = NULL;
}
@@ -254,12 +276,12 @@ void CardItem::drawArrow(const QColor &arrowColor)
{
if (static_cast<TabGame *>(owner->parent())->getSpectator())
return;
Player *arrowOwner = static_cast<TabGame *>(owner->parent())->getActiveLocalPlayer();
ArrowDragItem *arrow = new ArrowDragItem(arrowOwner, this, arrowColor);
scene()->addItem(arrow);
arrow->grabMouse();
QListIterator<QGraphicsItem *> itemIterator(scene()->selectedItems());
while (itemIterator.hasNext()) {
CardItem *c = qgraphicsitem_cast<CardItem *>(itemIterator.next());
@@ -267,7 +289,7 @@ void CardItem::drawArrow(const QColor &arrowColor)
continue;
if (c->getZone() != zone)
continue;
ArrowDragItem *childArrow = new ArrowDragItem(arrowOwner, c, arrowColor);
scene()->addItem(childArrow);
arrow->addChildArrow(childArrow);
@@ -277,9 +299,10 @@ void CardItem::drawArrow(const QColor &arrowColor)
void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (event->buttons().testFlag(Qt::RightButton)) {
if ((event->screenPos() - event->buttonDownScreenPos(Qt::RightButton)).manhattanLength() < 2 * QApplication::startDragDistance())
if ((event->screenPos() - event->buttonDownScreenPos(Qt::RightButton)).manhattanLength() <
2 * QApplication::startDragDistance())
return;
QColor arrowColor = Qt::red;
if (event->modifiers().testFlag(Qt::ControlModifier))
arrowColor = Qt::yellow;
@@ -287,10 +310,11 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
arrowColor = Qt::blue;
else if (event->modifiers().testFlag(Qt::ShiftModifier))
arrowColor = Qt::green;
drawArrow(arrowColor);
} else if (event->buttons().testFlag(Qt::LeftButton)) {
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() < 2 * QApplication::startDragDistance())
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
2 * QApplication::startDragDistance())
return;
if (zone->getIsView()) {
const ZoneViewZone *const view = static_cast<const ZoneViewZone *const>(zone);
@@ -298,16 +322,16 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return;
} else if (!owner->getLocal())
return;
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
createDragItem(id, event->pos(), event->scenePos(), facedown || forceFaceDown);
dragItem->grabMouse();
QList<QGraphicsItem *> sel = scene()->selectedItems();
int j = 0;
for (int i = 0; i < sel.size(); i++) {
CardItem *c = (CardItem *) sel.at(i);
CardItem *c = static_cast<CardItem *>(sel.at(i));
if ((c == this) || (c->getZone() != zone))
continue;
++j;
@@ -340,21 +364,23 @@ void CardItem::playCard(bool faceDown)
void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
if (cardMenu)
if (!cardMenu->isEmpty())
cardMenu->exec(event->screenPos());
} else if ((event->button() == Qt::LeftButton) && !settingsCache->getDoubleClickToPlay()) {
if (cardMenu && !cardMenu->isEmpty() && owner) {
owner->updateCardMenu(this);
cardMenu->exec(event->screenPos());
}
} else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) &&
(!settingsCache->getDoubleClickToPlay())) {
bool hideCard = false;
if (zone->getIsView()) {
if (zone && zone->getIsView()) {
ZoneViewZone *view = static_cast<ZoneViewZone *>(zone);
if (view->getRevealZone() && !view->getWriteableRevealZone())
hideCard = true;
}
if (hideCard)
if (zone && hideCard) {
zone->removeCard(this);
else
} else {
playCard(event->modifiers().testFlag(Qt::ShiftModifier));
}
}
setCursor(Qt::OpenHandCursor);
@@ -363,7 +389,8 @@ void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (settingsCache->getDoubleClickToPlay() && event->buttons() == Qt::LeftButton) {
if ((event->modifiers() != Qt::AltModifier) && (settingsCache->getDoubleClickToPlay()) &&
(event->buttons() == Qt::LeftButton)) {
if (revealedCard)
zone->removeCard(this);
else
@@ -372,7 +399,6 @@ void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
event->accept();
}
bool CardItem::animationEvent()
{
int rotation = ROTATION_DEGREES_PER_FRAME;
@@ -381,7 +407,10 @@ bool CardItem::animationEvent()
tapAngle += rotation;
setTransform(QTransform().translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF).rotate(tapAngle).translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
setTransform(QTransform()
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
.rotate(tapAngle)
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
setHovered(false);
update();

View File

@@ -16,7 +16,8 @@ const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2;
const int ROTATION_DEGREES_PER_FRAME = 10;
class CardItem : public AbstractCardItem {
class CardItem : public AbstractCardItem
{
Q_OBJECT
private:
CardZone *zone;
@@ -31,56 +32,131 @@ private:
CardDragItem *dragItem;
CardItem *attachedTo;
QList<CardItem *> attachedCards;
QMenu *cardMenu, *ptMenu, *moveMenu;
void prepareDelete();
public slots:
void deleteLater();
public:
enum { Type = typeCard };
int type() const { return Type; }
CardItem(Player *_owner, const QString &_name = QString(), int _cardid = -1, bool revealedCard = false, QGraphicsItem *parent = 0);
enum
{
Type = typeCard
};
int type() const
{
return Type;
}
CardItem(Player *_owner,
const QString &_name = QString(),
int _cardid = -1,
bool revealedCard = false,
QGraphicsItem *parent = 0);
~CardItem();
void retranslateUi();
CardZone *getZone() const { return zone; }
CardZone *getZone() const
{
return zone;
}
void setZone(CardZone *_zone);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QPoint getGridPoint() const { return gridPoint; }
void setGridPoint(const QPoint &_gridPoint) { gridPoint = _gridPoint; }
QPoint getGridPos() const { return gridPoint; }
Player *getOwner() const { return owner; }
void setOwner(Player *_owner) { owner = _owner; }
bool getRevealedCard() const { return revealedCard; }
bool getAttacking() const { return attacking; }
QPoint getGridPoint() const
{
return gridPoint;
}
void setGridPoint(const QPoint &_gridPoint)
{
gridPoint = _gridPoint;
}
QPoint getGridPos() const
{
return gridPoint;
}
Player *getOwner() const
{
return owner;
}
void setOwner(Player *_owner)
{
owner = _owner;
}
bool getRevealedCard() const
{
return revealedCard;
}
bool getAttacking() const
{
return attacking;
}
void setAttacking(bool _attacking);
const QMap<int, int> &getCounters() const { return counters; }
const QMap<int, int> &getCounters() const
{
return counters;
}
void setCounter(int _id, int _value);
QString getAnnotation() const { return annotation; }
QString getAnnotation() const
{
return annotation;
}
void setAnnotation(const QString &_annotation);
bool getDoesntUntap() const { return doesntUntap; }
bool getDoesntUntap() const
{
return doesntUntap;
}
void setDoesntUntap(bool _doesntUntap);
QString getPT() const { return pt; }
QString getPT() const
{
return pt;
}
void setPT(const QString &_pt);
bool getDestroyOnZoneChange() const { return destroyOnZoneChange; }
void setDestroyOnZoneChange(bool _destroy) { destroyOnZoneChange = _destroy; }
CardItem *getAttachedTo() const { return attachedTo; }
bool getDestroyOnZoneChange() const
{
return destroyOnZoneChange;
}
void setDestroyOnZoneChange(bool _destroy)
{
destroyOnZoneChange = _destroy;
}
CardItem *getAttachedTo() const
{
return attachedTo;
}
void setAttachedTo(CardItem *_attachedTo);
void addAttachedCard(CardItem *card) { attachedCards.append(card); }
void removeAttachedCard(CardItem *card) { attachedCards.removeAt(attachedCards.indexOf(card)); }
const QList<CardItem *> &getAttachedCards() const { return attachedCards; }
void addAttachedCard(CardItem *card)
{
attachedCards.append(card);
}
void removeAttachedCard(CardItem *card)
{
attachedCards.removeAt(attachedCards.indexOf(card));
}
const QList<CardItem *> &getAttachedCards() const
{
return attachedCards;
}
void resetState();
void processCardInfo(const ServerInfo_Card &info);
QMenu *getCardMenu() const { return cardMenu; }
QMenu *getPTMenu() const { return ptMenu; }
QMenu *getMoveMenu() const { return moveMenu; }
QMenu *getCardMenu() const
{
return cardMenu;
}
QMenu *getPTMenu() const
{
return ptMenu;
}
QMenu *getMoveMenu() const
{
return moveMenu;
}
bool animationEvent();
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown);
void deleteDragItem();
void drawArrow(const QColor &arrowColor);
void playCard(bool faceDown);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

View File

@@ -1,9 +1,8 @@
#include "cardlist.h"
#include "carditem.h"
#include "carddatabase.h"
#include "carditem.h"
CardList::CardList(bool _contentsKnown)
: QList<CardItem *>(), contentsKnown(_contentsKnown)
CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
{
}
@@ -32,11 +31,13 @@ CardItem *CardList::findCard(const int id, const bool remove, int *position)
return 0;
}
class CardList::compareFunctor {
class CardList::compareFunctor
{
private:
int flags;
public:
compareFunctor(int _flags) : flags(_flags)
explicit compareFunctor(int _flags) : flags(_flags)
{
}
inline bool operator()(CardItem *a, CardItem *b) const

View File

@@ -5,16 +5,26 @@
class CardItem;
class CardList : public QList<CardItem *> {
class CardList : public QList<CardItem *>
{
private:
class compareFunctor;
protected:
bool contentsKnown;
public:
enum SortFlags { SortByName = 1, SortByType = 2 };
enum SortFlags
{
SortByName = 1,
SortByType = 2
};
CardList(bool _contentsKnown);
CardItem *findCard(const int id, const bool remove, int *position = NULL);
bool getContentsKnown() const { return contentsKnown; }
bool getContentsKnown() const
{
return contentsKnown;
}
void sort(int flags = SortByName);
};

View File

@@ -1,16 +1,23 @@
#include <QMenu>
#include <QAction>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include "cardzone.h"
#include "carditem.h"
#include "player.h"
#include "zoneviewzone.h"
#include "pb/command_move_card.pb.h"
#include "pb/serverinfo_user.pb.h"
#include "player.h"
#include "zoneviewzone.h"
#include <QAction>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
CardZone::CardZone(Player *_p, const QString &_name, bool _hasCardAttr, bool _isShufflable, bool _contentsKnown, QGraphicsItem *parent, bool _isView)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), view(NULL), menu(NULL), doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable), isView(_isView)
CardZone::CardZone(Player *_p,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent,
bool _isView)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), view(NULL), menu(NULL),
doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable), isView(_isView)
{
if (!isView)
player->addZone(this);
@@ -32,11 +39,11 @@ void CardZone::retranslateUi()
void CardZone::clearContents()
{
for (int i = 0; i < cards.size(); i++) {
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a player,
// we have to return them to avoid a crash.
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a
// player, we have to return them to avoid a crash.
const QList<CardItem *> &attachedCards = cards[i]->getAttachedCards();
for (int j = 0; j < attachedCards.size(); ++j)
attachedCards[j]->setParentItem(attachedCards[j]->getZone());
for (auto attachedCard : attachedCards)
attachedCard->setParentItem(attachedCard->getZone());
player->deleteCard(cards.at(i));
}
@@ -48,61 +55,37 @@ QString CardZone::getTranslatedName(bool theirOwn, GrammaticalCase gc) const
{
QString ownerName = player->getName();
if (name == "hand")
return (theirOwn
? tr("their hand", "nominative")
: tr("%1's hand", "nominative").arg(ownerName)
);
return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName));
else if (name == "deck")
switch (gc) {
case CaseLookAtZone:
return (theirOwn
? tr("their library", "look at zone")
: tr("%1's library", "look at zone").arg(ownerName)
);
case CaseTopCardsOfZone:
return (theirOwn
? tr("of their library", "top cards of zone,")
: tr("of %1's library", "top cards of zone").arg(ownerName)
);
case CaseRevealZone:
return (theirOwn
? tr("their library", "reveal zone")
: tr("%1's library", "reveal zone").arg(ownerName)
);
case CaseShuffleZone:
return (theirOwn
? tr("their library", "shuffle")
: tr("%1's library", "shuffle").arg(ownerName)
);
default:
return (theirOwn
? tr("their library", "nominative")
: tr("%1's library", "nominative").arg(ownerName)
);
case CaseLookAtZone:
return (theirOwn ? tr("their library", "look at zone")
: tr("%1's library", "look at zone").arg(ownerName));
case CaseTopCardsOfZone:
return (theirOwn ? tr("of their library", "top cards of zone,")
: tr("of %1's library", "top cards of zone").arg(ownerName));
case CaseRevealZone:
return (theirOwn ? tr("their library", "reveal zone")
: tr("%1's library", "reveal zone").arg(ownerName));
case CaseShuffleZone:
return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName));
default:
return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName));
}
else if (name == "grave")
return (theirOwn
? tr("their graveyard", "nominative")
: tr("%1's graveyard", "nominative").arg(ownerName)
);
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
else if (name == "rfg")
return (theirOwn
? tr("their exile", "nominative")
: tr("%1's exile", "nominative").arg(ownerName)
);
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
else if (name == "sb")
switch (gc) {
case CaseLookAtZone:
return (theirOwn
? tr("their sideboard", "look at zone")
: tr("%1's sideboard", "look at zone").arg(ownerName)
);
case CaseNominative:
return (theirOwn
? tr("their sideboard", "nominative")
: tr("%1's sideboard", "nominative").arg(ownerName)
);
default: break;
case CaseLookAtZone:
return (theirOwn ? tr("their sideboard", "look at zone")
: tr("%1's sideboard", "look at zone").arg(ownerName));
case CaseNominative:
return (theirOwn ? tr("their sideboard", "nominative")
: tr("%1's sideboard", "nominative").arg(ownerName));
default:
break;
}
return QString();
}

View File

@@ -1,10 +1,10 @@
#ifndef CARDZONE_H
#define CARDZONE_H
#include <QString>
#include "cardlist.h"
#include "abstractgraphicsitem.h"
#include "cardlist.h"
#include "translation.h"
#include <QString>
class Player;
class ZoneViewZone;
@@ -13,7 +13,8 @@ class QAction;
class QPainter;
class CardDragItem;
class CardZone : public AbstractGraphicsItem {
class CardZone : public AbstractGraphicsItem
{
Q_OBJECT
protected:
Player *player;
@@ -34,36 +35,90 @@ signals:
public slots:
void moveAllToZone();
bool showContextMenu(const QPoint &screenPos);
public:
enum { Type = typeZone };
int type() const { return Type; }
virtual void handleDropEvent(const QList<CardDragItem *> &dragItem, CardZone *startZone, const QPoint &dropPoint) = 0;
CardZone(Player *_player, const QString &_name, bool _hasCardAttr, bool _isShufflable, bool _contentsKnown, QGraphicsItem *parent = 0, bool _isView = false);
enum
{
Type = typeZone
};
int type() const
{
return Type;
}
virtual void
handleDropEvent(const QList<CardDragItem *> &dragItem, CardZone *startZone, const QPoint &dropPoint) = 0;
CardZone(Player *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QGraphicsItem *parent = 0,
bool _isView = false);
~CardZone();
void retranslateUi();
void clearContents();
bool getHasCardAttr() const { return hasCardAttr; }
bool getIsShufflable() const { return isShufflable; }
QMenu *getMenu() const { return menu; }
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0) { menu = _menu; doubleClickAction = _doubleClickAction; }
QString getName() const { return name; }
bool getHasCardAttr() const
{
return hasCardAttr;
}
bool getIsShufflable() const
{
return isShufflable;
}
QMenu *getMenu() const
{
return menu;
}
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0)
{
menu = _menu;
doubleClickAction = _doubleClickAction;
}
QString getName() const
{
return name;
}
QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const;
Player *getPlayer() const { return player; }
bool contentsKnown() const { return cards.getContentsKnown(); }
const CardList &getCards() const { return cards; }
Player *getPlayer() const
{
return player;
}
bool contentsKnown() const
{
return cards.getContentsKnown();
}
const CardList &getCards() const
{
return cards;
}
void addCard(CardItem *card, bool reorganize, int x, int y = -1);
// getCard() finds a card by id.
CardItem *getCard(int cardId, const QString &cardName);
// takeCard() finds a card by position and removes it from the zone and from all of its views.
virtual CardItem *takeCard(int position, int cardId, bool canResize = true);
void removeCard(CardItem *card);
ZoneViewZone *getView() const { return view; }
void setView(ZoneViewZone *_view) { view = _view; }
ZoneViewZone *getView() const
{
return view;
}
void setView(ZoneViewZone *_view)
{
view = _view;
}
virtual void reorganizeCards() = 0;
virtual QPointF closestGridPoint(const QPointF &point);
bool getIsView() const { return isView; }
bool getAlwaysRevealTopCard() const { return alwaysRevealTopCard; }
void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) { alwaysRevealTopCard = _alwaysRevealTopCard; }
bool getIsView() const
{
return isView;
}
bool getAlwaysRevealTopCard() const
{
return alwaysRevealTopCard;
}
void setAlwaysRevealTopCard(bool _alwaysRevealTopCard)
{
alwaysRevealTopCard = _alwaysRevealTopCard;
}
};
#endif

View File

@@ -1,35 +1,35 @@
#include <QTextEdit>
#include <QDateTime>
#include <QScrollBar>
#include <QMouseEvent>
#include <QDesktopServices>
#include <QApplication>
#include <QDebug>
#include "chatview.h"
#include "../pixmapgenerator.h"
#include "../settingscache.h"
#include "../soundengine.h"
#include "../tab_userlists.h"
#include "../user_context_menu.h"
#include "user_level.h"
#include "user_context_menu.h"
#include "pixmapgenerator.h"
#include "settingscache.h"
#include "tab_userlists.h"
#include "soundengine.h"
#include "room_message_type.h"
#include <QApplication>
#include <QDateTime>
#include <QDesktopServices>
#include <QMouseEvent>
#include <QScrollBar>
#include <QTextEdit>
const QColor DEFAULT_MENTION_COLOR = QColor(194, 31, 47);
const QColor OTHER_USER_COLOR = QColor(0, 65, 255); // dark blue
const QString SERVER_MESSAGE_COLOR = "#851515";
ChatView::ChatView(const TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent)
: QTextBrowser(parent), tabSupervisor(_tabSupervisor), game(_game), evenNumber(true), showTimestamps(_showTimestamps), hoveredItemType(HoveredNothing)
ChatView::ChatView(const TabSupervisor *_tabSupervisor,
const UserlistProxy *_userlistProxy,
TabGame *_game,
bool _showTimestamps,
QWidget *parent)
: QTextBrowser(parent), tabSupervisor(_tabSupervisor), game(_game), userlistProxy(_userlistProxy), evenNumber(true),
showTimestamps(_showTimestamps), hoveredItemType(HoveredNothing)
{
document()->setDefaultStyleSheet("a { text-decoration: none; color: blue; }");
userContextMenu = new UserContextMenu(tabSupervisor, this, game);
connect(userContextMenu, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
if(tabSupervisor->getUserInfo())
{
userName = QString::fromStdString(tabSupervisor->getUserInfo()->name());
mention = "@" + userName;
}
userName = userlistProxy->getOwnUsername();
mention = "@" + userName;
mentionFormat.setFontWeight(QFont::Bold);
@@ -52,7 +52,7 @@ void ChatView::retranslateUi()
QTextCursor ChatView::prepareBlock(bool same)
{
lastSender.clear();
QTextCursor cursor(document()->lastBlock());
cursor.movePosition(QTextCursor::End);
if (same) {
@@ -64,7 +64,7 @@ QTextCursor ChatView::prepareBlock(bool same)
blockFormat.setBottomMargin(4);
cursor.insertBlock(blockFormat);
}
return cursor;
}
@@ -80,7 +80,8 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold,
{
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
QString htmlText = "<font color=" + ((optionalFontColor.size() > 0) ? optionalFontColor : SERVER_MESSAGE_COLOR) + ">" + QDateTime::currentDateTime().toString("[hh:mm:ss] ")+ html + "</font>";
QString htmlText = "<font color=" + ((optionalFontColor.size() > 0) ? optionalFontColor : SERVER_MESSAGE_COLOR) +
">" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + html + "</font>";
if (optionalIsBold)
htmlText = "<b>" + htmlText + "</b>";
@@ -98,7 +99,7 @@ void ChatView::appendCardTag(QTextCursor &cursor, const QString &cardName)
anchorFormat.setAnchor(true);
anchorFormat.setAnchorHref("card://" + cardName);
anchorFormat.setFontItalic(true);
cursor.setCharFormat(anchorFormat);
cursor.insertText(cardName);
cursor.setCharFormat(oldFormat);
@@ -108,7 +109,7 @@ void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
{
if (!url.contains("://"))
url.prepend("http://");
QTextCharFormat oldFormat = cursor.charFormat();
QTextCharFormat anchorFormat = oldFormat;
anchorFormat.setForeground(Qt::blue);
@@ -116,19 +117,24 @@ void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
anchorFormat.setAnchorHref(url);
anchorFormat.setUnderlineColor(Qt::blue);
anchorFormat.setFontUnderline(true);
cursor.setCharFormat(anchorFormat);
cursor.insertText(url);
cursor.setCharFormat(oldFormat);
}
void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType, QString sender, UserLevelFlags userLevel, QString UserPrivLevel, bool playerBold)
void ChatView::appendMessage(QString message,
RoomMessageTypeFlags messageType,
QString sender,
UserLevelFlags userLevel,
QString UserPrivLevel,
bool playerBold)
{
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
bool sameSender = (sender == lastSender) && !lastSender.isEmpty();
QTextCursor cursor = prepareBlock(sameSender);
lastSender = sender;
// timestamp
if (showTimestamps && (!sameSender || sender.toLower() == "servatrice") && !sender.isEmpty()) {
QTextCharFormat timeFormat;
@@ -142,8 +148,7 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
// nickname
if (sender.toLower() != "servatrice") {
QTextCharFormat senderFormat;
if (tabSupervisor && tabSupervisor->getUserInfo() &&
(sender == QString::fromStdString(tabSupervisor->getUserInfo()->name()))) {
if (sender == userName) {
senderFormat.setForeground(QBrush(getCustomMentionColor()));
senderFormat.setFontWeight(QFont::Bold);
} else {
@@ -156,10 +161,11 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
if (sameSender) {
cursor.insertText(" ");
} else {
if (!sender.isEmpty() && tabSupervisor->getUserListsTab()) {
if (!sender.isEmpty()) {
const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize();
QMap<QString, UserListTWI *> buddyList = tabSupervisor->getUserListsTab()->getBuddyList()->getUsers();
cursor.insertImage(UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, buddyList.contains(sender), UserPrivLevel).toImage());
bool isBuddy = userlistProxy->isUserBuddy(sender);
cursor.insertImage(
UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, isBuddy, UserPrivLevel).toImage());
cursor.insertText(" ");
}
cursor.setCharFormat(senderFormat);
@@ -169,7 +175,7 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
}
}
// use different color for server messages
// use different color for server messages
defaultFormat = QTextCharFormat();
if (sender.isEmpty()) {
switch (messageType) {
@@ -196,16 +202,14 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
highlightedWords = settingsCache->getHighlightWords().split(' ', QString::SkipEmptyParts);
// parse the message
while (message.size())
{
QChar c = message.at(0);
switch(c.toLatin1())
{
while (message.size()) {
QChar c = message.at(0);
switch (c.toLatin1()) {
case '[':
checkTag(cursor, message);
break;
case '@':
if(mentionEnabled) {
if (mentionEnabled) {
checkMention(cursor, message, sender, userLevel);
} else {
cursor.insertText(c, defaultFormat);
@@ -217,7 +221,7 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
message = message.mid(1);
break;
default:
if(c.isLetterOrNumber()) {
if (c.isLetterOrNumber()) {
checkWord(cursor, message);
} else {
cursor.insertText(c, defaultFormat);
@@ -233,8 +237,7 @@ void ChatView::appendMessage(QString message, RoomMessageTypeFlags messageType,
void ChatView::checkTag(QTextCursor &cursor, QString &message)
{
if (message.startsWith("[card]"))
{
if (message.startsWith("[card]")) {
message = message.mid(6);
int closeTagIndex = message.indexOf("[/card]");
QString cardName = message.left(closeTagIndex);
@@ -242,13 +245,12 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
message.clear();
else
message = message.mid(closeTagIndex + 7);
appendCardTag(cursor, cardName);
return;
}
if (message.startsWith("[["))
{
if (message.startsWith("[[")) {
message = message.mid(2);
int closeTagIndex = message.indexOf("]]");
QString cardName = message.left(closeTagIndex);
@@ -256,13 +258,12 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
message.clear();
else
message = message.mid(closeTagIndex + 2);
appendCardTag(cursor, cardName);
return;
}
if (message.startsWith("[url]"))
{
if (message.startsWith("[url]")) {
message = message.mid(5);
int closeTagIndex = message.indexOf("[/url]");
QString url = message.left(closeTagIndex);
@@ -270,7 +271,7 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
message.clear();
else
message = message.mid(closeTagIndex + 6);
appendUrlTag(cursor, url);
return;
}
@@ -287,30 +288,24 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
QString fullMentionUpToSpaceOrEnd = (firstSpace == -1) ? message.mid(1) : message.mid(1, firstSpace - 1);
QString mentionIntact = fullMentionUpToSpaceOrEnd;
QMap<QString, UserListTWI *> userList = tabSupervisor->getUserListsTab()->getAllUsersList()->getUsers();
while (fullMentionUpToSpaceOrEnd.size())
{
if (isFullMentionAValidUser(userList, fullMentionUpToSpaceOrEnd)) // Is there a user online named this?
while (fullMentionUpToSpaceOrEnd.size()) {
const ServerInfo_User *onlineUser = userlistProxy->getOnlineUser(fullMentionUpToSpaceOrEnd);
if (onlineUser) // Is there a user online named this?
{
if (userName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you?
{
// You have received a valid mention!!
soundEngine->playSound("chat_mention");
mentionFormat.setBackground(QBrush(getCustomMentionColor()));
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white) : QBrush(Qt::black));
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white)
: QBrush(Qt::black));
cursor.insertText(mention, mentionFormat);
message = message.mid(mention.size());
QApplication::alert(this);
if (settingsCache->getShowMentionPopup() && shouldShowSystemPopup())
{
QString ref = sender.left(sender.length() - 2);
showSystemPopup(ref);
}
showSystemPopup(sender);
} else {
QString correctUserName = getNameFromUserList(userList, fullMentionUpToSpaceOrEnd);
UserListTWI *vlu = userList.value(correctUserName);
mentionFormatOtherUser.setAnchorHref("user://" + QString::number(vlu->getUserInfo().user_level()) + "_" + correctUserName);
QString correctUserName = QString::fromStdString(onlineUser->name());
mentionFormatOtherUser.setAnchorHref("user://" + QString::number(onlineUser->user_level()) + "_" +
correctUserName);
cursor.insertText("@" + correctUserName, mentionFormatOtherUser);
message = message.mid(correctUserName.size() + 1);
@@ -324,22 +319,18 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
// Moderator Sending Global Message
soundEngine->playSound("all_mention");
mentionFormat.setBackground(QBrush(getCustomMentionColor()));
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white) : QBrush(Qt::black));
mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white)
: QBrush(Qt::black));
cursor.insertText("@" + fullMentionUpToSpaceOrEnd, mentionFormat);
message = message.mid(fullMentionUpToSpaceOrEnd.size() + 1);
QApplication::alert(this);
if (settingsCache->getShowMentionPopup() && shouldShowSystemPopup())
{
QString ref = sender.left(sender.length() - 2);
showSystemPopup(ref);
}
showSystemPopup(sender);
cursor.setCharFormat(defaultFormat);
return;
}
if (fullMentionUpToSpaceOrEnd.right(1).indexOf(notALetterOrNumber) == -1 || fullMentionUpToSpaceOrEnd.size() < 2)
{
if (fullMentionUpToSpaceOrEnd.right(1).indexOf(notALetterOrNumber) == -1 ||
fullMentionUpToSpaceOrEnd.size() < 2) {
cursor.insertText("@" + mentionIntact, defaultFormat);
message = message.mid(mentionIntact.size() + 1);
cursor.setCharFormat(defaultFormat);
@@ -362,11 +353,9 @@ void ChatView::checkWord(QTextCursor &cursor, QString &message)
// check urls
if (fullWordUpToSpaceOrEnd.startsWith("http://", Qt::CaseInsensitive) ||
fullWordUpToSpaceOrEnd.startsWith("https://", Qt::CaseInsensitive) ||
fullWordUpToSpaceOrEnd.startsWith("www.", Qt::CaseInsensitive))
{
fullWordUpToSpaceOrEnd.startsWith("www.", Qt::CaseInsensitive)) {
QUrl qUrl(fullWordUpToSpaceOrEnd);
if (qUrl.isValid())
{
if (qUrl.isValid()) {
appendUrlTag(cursor, fullWordUpToSpaceOrEnd);
cursor.insertText(rest, defaultFormat);
return;
@@ -374,13 +363,12 @@ void ChatView::checkWord(QTextCursor &cursor, QString &message)
}
// check word mentions
foreach (QString word, highlightedWords)
{
if (fullWordUpToSpaceOrEnd.compare(word, Qt::CaseInsensitive) == 0)
{
foreach (QString word, highlightedWords) {
if (fullWordUpToSpaceOrEnd.compare(word, Qt::CaseInsensitive) == 0) {
// You have received a valid mention of custom word!!
highlightFormat.setBackground(QBrush(getCustomHighlightColor()));
highlightFormat.setForeground(settingsCache->getChatHighlightForeground() ? QBrush(Qt::white) : QBrush(Qt::black));
highlightFormat.setForeground(settingsCache->getChatHighlightForeground() ? QBrush(Qt::white)
: QBrush(Qt::black));
cursor.insertText(fullWordUpToSpaceOrEnd, highlightFormat);
cursor.insertText(rest, defaultFormat);
QApplication::alert(this);
@@ -397,8 +385,7 @@ QString ChatView::extractNextWord(QString &message, QString &rest)
// get the first next space and extract the word
QString word;
int firstSpace = message.indexOf(' ');
if(firstSpace == -1)
{
if (firstSpace == -1) {
word = message;
message.clear();
} else {
@@ -406,11 +393,9 @@ QString ChatView::extractNextWord(QString &message, QString &rest)
message = message.mid(firstSpace);
}
// remove any punctution from the end and pass it separately
for (int len = word.size() - 1; len >= 0; --len)
{
if(word.at(len).isLetterOrNumber())
{
// remove any punctuation from the end and pass it separately
for (int len = word.size() - 1; len >= 0; --len) {
if (word.at(len).isLetterOrNumber()) {
rest = word.mid(len + 1);
return word.mid(0, len + 1);
}
@@ -420,7 +405,6 @@ QString ChatView::extractNextWord(QString &message, QString &rest)
return QString();
}
bool ChatView::isModeratorSendingGlobal(QFlags<ServerInfo_User::UserLevelFlag> userLevelFlag, QString message)
{
int userLevel = QString::number(userLevelFlag).toInt();
@@ -428,63 +412,40 @@ bool ChatView::isModeratorSendingGlobal(QFlags<ServerInfo_User::UserLevelFlag> u
QStringList getAttentionList;
getAttentionList << "/all"; // Send a message to all users
if (getAttentionList.contains(message) && (userLevel & ServerInfo_User::IsModerator || userLevel & ServerInfo_User::IsAdmin))
return true;
return false;
return (getAttentionList.contains(message) &&
(userLevel & ServerInfo_User::IsModerator || userLevel & ServerInfo_User::IsAdmin));
}
void ChatView::actMessageClicked() {
void ChatView::actMessageClicked()
{
emit messageClickedSignal();
}
bool ChatView::shouldShowSystemPopup() {
return QApplication::activeWindow() == 0 || QApplication::focusWidget() == 0 ||tabSupervisor->currentIndex() != tabSupervisor->indexOf(this);
void ChatView::showSystemPopup(QString &sender)
{
QApplication::alert(this);
if (settingsCache->getShowMentionPopup()) {
QString ref = sender.left(sender.length() - 2);
emit showMentionPopup(ref);
}
}
void ChatView::showSystemPopup(QString &sender) {
emit showMentionPopup(sender);
}
QColor ChatView::getCustomMentionColor() {
QColor ChatView::getCustomMentionColor()
{
QColor customColor;
customColor.setNamedColor("#" + settingsCache->getChatMentionColor());
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
}
QColor ChatView::getCustomHighlightColor() {
QColor ChatView::getCustomHighlightColor()
{
QColor customColor;
customColor.setNamedColor("#" + settingsCache->getChatHighlightColor());
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
}
/**
Returns the correct case version of the provided username, if no correct casing version
was found then the provided name is not available and will return an empty QString.
*/
QString ChatView::getNameFromUserList(QMap<QString, UserListTWI *> &userList, QString &userName) {
QMap<QString, UserListTWI *>::iterator i;
QString lowerUserName = userName.toLower();
for (i = userList.begin(); i != userList.end(); ++i) {
if (i.key().toLower() == lowerUserName)
return i.key();
}
return QString();
}
bool ChatView::isFullMentionAValidUser(QMap<QString, UserListTWI *> &userList, QString userNameToMatch)
void ChatView::clearChat()
{
QString userNameToMatchLower = userNameToMatch.toLower();
QMap<QString, UserListTWI *>::iterator i;
for (i = userList.begin(); i != userList.end(); ++i)
if (i.key().toLower() == userNameToMatchLower)
return true;
return false;
}
void ChatView::clearChat() {
document()->clear();
lastSender = "";
}
@@ -536,7 +497,7 @@ void ChatView::mouseMoveEvent(QMouseEvent *event)
hoveredItemType = HoveredNothing;
viewport()->setCursor(Qt::IBeamCursor);
}
QTextBrowser::mouseMoveEvent(event);
}
@@ -552,21 +513,21 @@ void ChatView::mousePressEvent(QMouseEvent *event)
if (event->button() != Qt::MidButton) {
const int delimiterIndex = hoveredContent.indexOf("_");
const QString userName = hoveredContent.mid(delimiterIndex + 1);
switch(event->button()) {
case Qt::RightButton :{
UserLevelFlags userLevel(hoveredContent.left(delimiterIndex).toInt());
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel);
break;
}
case Qt::LeftButton :{
if (event->modifiers() == Qt::ControlModifier) {
emit openMessageDialog(userName, true);
} else
emit addMentionTag("@" + userName);
break;
}
default:
break;
switch (event->button()) {
case Qt::RightButton: {
UserLevelFlags userLevel(hoveredContent.left(delimiterIndex).toInt());
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel);
break;
}
case Qt::LeftButton: {
if (event->modifiers() == Qt::ControlModifier) {
emit openMessageDialog(userName, true);
} else
emit addMentionTag("@" + userName);
break;
}
default:
break;
}
}
break;
@@ -581,7 +542,7 @@ void ChatView::mouseReleaseEvent(QMouseEvent *event)
{
if ((event->button() == Qt::MidButton) || (event->button() == Qt::LeftButton))
emit deleteCardInfoPopup(QString("_"));
QTextBrowser::mouseReleaseEvent(event);
}
@@ -589,6 +550,6 @@ void ChatView::openLink(const QUrl &link)
{
if ((link.scheme() == "card") || (link.scheme() == "user"))
return;
QDesktopServices::openUrl(link);
}

View File

@@ -1,28 +1,38 @@
#ifndef CHATVIEW_H
#define CHATVIEW_H
#include <QTextBrowser>
#include <QTextFragment>
#include <QTextCursor>
#include <QColor>
#include <QAction>
#include "userlist.h"
#include "user_level.h"
#include "../tab_supervisor.h"
#include "../userlist.h"
#include "room_message_type.h"
#include "tab_supervisor.h"
#include "user_level.h"
#include "userlistProxy.h"
#include <QAction>
#include <QColor>
#include <QTextBrowser>
#include <QTextCursor>
#include <QTextFragment>
class QTextTable;
class QMouseEvent;
class UserContextMenu;
class TabGame;
class ChatView : public QTextBrowser {
class ChatView : public QTextBrowser
{
Q_OBJECT
protected:
const TabSupervisor * const tabSupervisor;
TabGame * const game;
const TabSupervisor *const tabSupervisor;
TabGame *const game;
private:
enum HoveredItemType { HoveredNothing, HoveredUrl, HoveredCard, HoveredUser };
enum HoveredItemType
{
HoveredNothing,
HoveredUrl,
HoveredCard,
HoveredUser
};
const UserlistProxy *const userlistProxy;
UserContextMenu *userContextMenu;
QString lastSender;
QString userName;
@@ -41,11 +51,8 @@ private:
QTextCursor prepareBlock(bool same = false);
void appendCardTag(QTextCursor &cursor, const QString &cardName);
void appendUrlTag(QTextCursor &cursor, QString url);
QString getNameFromUserList(QMap<QString, UserListTWI *> &userList, QString &userName);
bool isFullMentionAValidUser(QMap<QString, UserListTWI *> &userList, QString userNameToMatch);
QColor getCustomMentionColor();
QColor getCustomHighlightColor();
bool shouldShowSystemPopup();
void showSystemPopup(QString &sender);
bool isModeratorSendingGlobal(QFlags<ServerInfo_User::UserLevelFlag> userLevelFlag, QString message);
void checkTag(QTextCursor &cursor, QString &message);
@@ -55,13 +62,25 @@ private:
private slots:
void openLink(const QUrl &link);
void actMessageClicked();
public:
ChatView(const TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent = 0);
ChatView(const TabSupervisor *_tabSupervisor,
const UserlistProxy *_userlistProxy,
TabGame *_game,
bool _showTimestamps,
QWidget *parent = 0);
void retranslateUi();
void appendHtml(const QString &html);
void appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString());
void appendMessage(QString message, RoomMessageTypeFlags messageType = 0, QString sender = QString(), UserLevelFlags userLevel = UserLevelFlags(), QString UserPrivLevel = "NONE", bool playerBold = false);
void
appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString());
void appendMessage(QString message,
RoomMessageTypeFlags messageType = 0,
QString sender = QString(),
UserLevelFlags userLevel = UserLevelFlags(),
QString UserPrivLevel = "NONE",
bool playerBold = false);
void clearChat();
protected:
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);

View File

@@ -0,0 +1,21 @@
#ifndef COCKATRICE_USERLISTPROXY_H
#define COCKATRICE_USERLISTPROXY_H
class QString;
class ServerInfo_User;
/**
* Responsible for providing a bare-bones minimal interface into userlist information,
* including your current connection to the server as well as buddy/ignore/alluser lists.
*/
class UserlistProxy
{
public:
virtual bool isOwnUserRegistered() const = 0;
virtual QString getOwnUsername() const = 0;
virtual bool isUserBuddy(const QString &userName) const = 0;
virtual bool isUserIgnored(const QString &userName) const = 0;
virtual const ServerInfo_User *getOnlineUser(const QString &userName) const = 0; // Can return nullptr
};
#endif // COCKATRICE_USERLISTPROXY_H

View File

@@ -2,8 +2,15 @@
#include "pixmapgenerator.h"
#include <QPainter>
GeneralCounter::GeneralCounter(Player *_player, int _id, const QString &_name, const QColor &_color, int _radius, int _value, QGraphicsItem *parent)
: AbstractCounter(_player, _id, _name, true, _value, parent), color(_color), radius(_radius)
GeneralCounter::GeneralCounter(Player *_player,
int _id,
const QString &_name,
const QColor &_color,
int _radius,
int _value,
bool useNameForShortcut,
QGraphicsItem *parent)
: AbstractCounter(_player, _id, _name, true, _value, useNameForShortcut, parent), color(_color), radius(_radius)
{
setCacheMode(DeviceCoordinateCache);
}
@@ -19,14 +26,14 @@ void GeneralCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /
int translatedHeight = mapRect.size().height();
qreal scaleFactor = translatedHeight / boundingRect().height();
QPixmap pixmap = CounterPixmapGenerator::generatePixmap(translatedHeight, name, hovered);
painter->save();
painter->resetTransform();
painter->drawPixmap(QPoint(0, 0), pixmap);
if (value) {
QFont f("Serif");
f.setPixelSize(qMax((int) (radius * scaleFactor), 10));
f.setPixelSize(qMax((int)(radius * scaleFactor), 10));
f.setWeight(QFont::Bold);
painter->setPen(Qt::black);
painter->setFont(f);

View File

@@ -3,13 +3,22 @@
#include "abstractcounter.h"
class GeneralCounter : public AbstractCounter {
class GeneralCounter : public AbstractCounter
{
Q_OBJECT
private:
QColor color;
int radius;
public:
GeneralCounter(Player *_player, int _id, const QString &_name, const QColor &_color, int _radius, int _value, QGraphicsItem *parent = 0);
GeneralCounter(Player *_player,
int _id,
const QString &_name,
const QColor &_color,
int _radius,
int _value,
bool useNameForShortcut = false,
QGraphicsItem *parent = 0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

View File

@@ -1,43 +1,30 @@
#include <QStringList>
#include <QFile>
#include <QDebug>
#include "deck_loader.h"
#include "decklist.h"
#include "carddatabase.h"
#include "decklist.h"
#include "main.h"
#include <QDebug>
#include <QFile>
#include <QStringList>
const QStringList DeckLoader::fileNameFilters = QStringList()
<< QObject::tr("Common deck formats (*.cod *.dec *.txt *.mwDeck)")
<< QObject::tr("All files (*.*)");
const QStringList DeckLoader::fileNameFilters =
QStringList() << QObject::tr("Common deck formats (*.cod *.dec *.txt *.mwDeck)") << QObject::tr("All files (*.*)");
DeckLoader::DeckLoader()
: DeckList(),
lastFileName(QString()),
lastFileFormat(CockatriceFormat),
lastRemoteDeckId(-1)
DeckLoader::DeckLoader() : DeckList(), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(const QString &nativeString)
: DeckList(nativeString),
lastFileName(QString()),
lastFileFormat(CockatriceFormat),
lastRemoteDeckId(-1)
: DeckList(nativeString), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(const DeckList &other)
: DeckList(other),
lastFileName(QString()),
lastFileFormat(CockatriceFormat),
lastRemoteDeckId(-1)
: DeckList(other), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(const DeckLoader &other)
: DeckList(other),
lastFileName(other.lastFileName),
lastFileFormat(other.lastFileFormat),
: DeckList(other), lastFileName(other.lastFileName), lastFileFormat(other.lastFileFormat),
lastRemoteDeckId(other.lastRemoteDeckId)
{
}
@@ -45,13 +32,16 @@ DeckLoader::DeckLoader(const DeckLoader &other)
bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
bool result = false;
switch (fmt) {
case PlainTextFormat: result = loadFromFile_Plain(&file); break;
case CockatriceFormat:
case PlainTextFormat:
result = loadFromFile_Plain(&file);
break;
case CockatriceFormat: {
result = loadFromFile_Native(&file);
qDebug() << "Loaded from" << fileName << "-" << result;
if (!result) {
@@ -61,13 +51,19 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
fmt = PlainTextFormat;
}
break;
}
default:
break;
}
if (result) {
lastFileName = fileName;
lastFileFormat = fmt;
emit deckLoaded();
}
qDebug() << "Deck was loaded -" << result;
return result;
}
@@ -88,14 +84,20 @@ bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
bool result = false;
switch (fmt) {
case PlainTextFormat: result = saveToFile_Plain(&file); break;
case CockatriceFormat: result = saveToFile_Native(&file); break;
case PlainTextFormat:
result = saveToFile_Plain(&file);
break;
case CockatriceFormat:
result = saveToFile_Native(&file);
break;
}
if (result) {
lastFileName = fileName;
lastFileFormat = fmt;
@@ -103,6 +105,72 @@ bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
return result;
}
// This struct is here to support the forEachCard function call, defined in decklist. It
// requires a function to be called for each card, and passes an inner node and a card for
// each card in the decklist.
struct FormatDeckListForExport
{
// Create refrences for the strings that will be passed in.
QString &mainBoardCards;
QString &sideBoardCards;
// create main operator for struct, allowing the foreachcard to work.
FormatDeckListForExport(QString &_mainBoardCards, QString &_sideBoardCards)
: mainBoardCards(_mainBoardCards), sideBoardCards(_sideBoardCards){};
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
// Get the card name
CardInfoPtr dbCard = db->getCard(card->getName());
if (!dbCard || dbCard->getIsToken()) {
// If it's a token, we don't care about the card.
return;
}
// Check if it's a sideboard card.
if (node->getName() == DECK_ZONE_SIDE) {
// Get the number of cards and add the card name
sideBoardCards += QString::number(card->getNumber());
// Add a space between card num and name
sideBoardCards += "%20";
// Add card name
sideBoardCards += card->getName();
// Add a return at the end of the card
sideBoardCards += "%0A";
} else // If it's a mainboard card, do the same thing, but for the mainboard card string
{
mainBoardCards += QString::number(card->getNumber());
mainBoardCards += "%20";
mainBoardCards += card->getName();
mainBoardCards += "%0A";
}
}
};
// Export deck to decklist function, called to format the deck in a way to be sent to a server
QString DeckLoader::exportDeckToDecklist()
{
// Add the base url
QString deckString = "https://www.decklist.org/?";
// Create two strings to pass to function
QString mainBoardCards, sideBoardCards;
// Set up the struct to call.
FormatDeckListForExport formatDeckListForExport(mainBoardCards, sideBoardCards);
// call our struct function for each card in the deck
forEachCard(formatDeckListForExport);
// Remove the extra return at the end of the last cards
mainBoardCards.chop(3);
sideBoardCards.chop(3);
// if after we've called it for each card, and the strings are empty, we know that
// there were no non-token cards in the deck, so show an error message.
if ((QString::compare(mainBoardCards, "", Qt::CaseInsensitive) == 0) &&
(QString::compare(sideBoardCards, "", Qt::CaseInsensitive) == 0)) {
return "";
}
// return a string with the url for decklist export
deckString += "deckmain=" + mainBoardCards + "&deckside=" + sideBoardCards;
return deckString;
}
DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
{
if (fileName.endsWith(".cod", Qt::CaseInsensitive)) {
@@ -111,16 +179,17 @@ DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
return PlainTextFormat;
}
bool DeckLoader::saveToStream_Plain(QTextStream &out)
bool DeckLoader::saveToStream_Plain(QTextStream &out, bool addComments)
{
saveToStream_DeckHeader(out);
if (addComments) {
saveToStream_DeckHeader(out);
}
// loop zones
for (int i = 0; i < getRoot()->size(); i++) {
const InnerDecklistNode *zoneNode =
dynamic_cast<InnerDecklistNode *>(getRoot()->at(i));
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(getRoot()->at(i));
saveToStream_DeckZone(out, zoneNode);
saveToStream_DeckZone(out, zoneNode, addComments);
// end of zone
out << "\n";
@@ -131,68 +200,99 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out)
void DeckLoader::saveToStream_DeckHeader(QTextStream &out)
{
if(!getName().isEmpty())
if (!getName().isEmpty()) {
out << "// " << getName() << "\n\n";
if(!getComments().isEmpty())
{
}
if (!getComments().isEmpty()) {
QStringList commentRows = getComments().split(QRegExp("\n|\r\n|\r"));
foreach(QString row, commentRows)
foreach (QString row, commentRows) {
out << "// " << row << "\n";
}
out << "\n";
}
}
void DeckLoader::saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode)
void DeckLoader::saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode, bool addComments)
{
// group cards by card type and count the subtotals
QMultiMap<QString, DecklistCardNode*> cardsByType;
// group cards by card type and count the subtotals
QMultiMap<QString, DecklistCardNode *> cardsByType;
QMap<QString, int> cardTotalByType;
int cardTotal = 0;
for (int j = 0; j < zoneNode->size(); j++) {
DecklistCardNode *card =
dynamic_cast<DecklistCardNode *>(
zoneNode->at(j)
);
auto *card = dynamic_cast<DecklistCardNode *>(zoneNode->at(j));
CardInfo *info = db->getCard(card->getName());
CardInfoPtr info = db->getCard(card->getName());
QString cardType = info ? info->getMainCardType() : "unknown";
cardsByType.insert(cardType, card);
if(cardTotalByType.contains(cardType))
if (cardTotalByType.contains(cardType)) {
cardTotalByType[cardType] += card->getNumber();
else
} else {
cardTotalByType[cardType] = card->getNumber();
}
cardTotal += card->getNumber();
}
out << "// " << cardTotal << " " << zoneNode->getVisibleName() << "\n";
if (addComments) {
out << "// " << cardTotal << " " << zoneNode->getVisibleName() << "\n";
}
// print cards to stream
foreach(QString cardType, cardsByType.uniqueKeys())
{
foreach (QString cardType, cardsByType.uniqueKeys()) {
if (addComments) {
out << "// " << cardTotalByType[cardType] << " " << cardType << "\n";
}
out << "// " << cardTotalByType[cardType] << " " << cardType << "\n";
QList <DecklistCardNode*> cards = cardsByType.values(cardType);
QList<DecklistCardNode *> cards = cardsByType.values(cardType);
saveToStream_DeckZoneCards(out, zoneNode, cards);
saveToStream_DeckZoneCards(out, zoneNode, cards, addComments);
out << "\n";
if (addComments) {
out << "\n";
}
}
}
void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out, const InnerDecklistNode *zoneNode, QList <DecklistCardNode*> cards)
void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards,
bool addComments)
{
// QMultiMap sorts values in reverse order
for(int i = cards.size() - 1; i >= 0; --i)
{
DecklistCardNode* card = cards[i];
for (int i = cards.size() - 1; i >= 0; --i) {
DecklistCardNode *card = cards[i];
if (zoneNode->getName() == "side")
if (zoneNode->getName() == DECK_ZONE_SIDE && addComments) {
out << "SB: ";
}
out << card->getNumber() << " " << card->getName() << "\n";
out << card->getNumber() << " " << card->getName() << "\n";
}
}
QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName)
{
CardInfoPtr card = db->getCard(cardName);
if (card && card->getIsToken()) {
return DECK_ZONE_TOKENS;
}
return currentZoneName;
}
QString DeckLoader::getCompleteCardName(const QString cardName) const
{
if (db) {
CardInfoPtr temp = db->getCardBySimpleName(cardName);
if (temp) {
return temp->getName();
}
}
return cardName;
}

View File

@@ -3,39 +3,62 @@
#include "decklist.h"
class DeckLoader : public DeckList {
class DeckLoader : public DeckList
{
Q_OBJECT
signals:
void deckLoaded();
public:
enum FileFormat { PlainTextFormat, CockatriceFormat };
enum FileFormat
{
PlainTextFormat,
CockatriceFormat
};
static const QStringList fileNameFilters;
private:
QString lastFileName;
FileFormat lastFileFormat;
int lastRemoteDeckId;
public:
DeckLoader();
DeckLoader(const QString &nativeString);
DeckLoader(const DeckList &other);
DeckLoader(const DeckLoader &other);
const QString &getLastFileName() const { return lastFileName; }
FileFormat getLastFileFormat() const { return lastFileFormat; }
int getLastRemoteDeckId() const { return lastRemoteDeckId; }
const QString &getLastFileName() const
{
return lastFileName;
}
FileFormat getLastFileFormat() const
{
return lastFileFormat;
}
int getLastRemoteDeckId() const
{
return lastRemoteDeckId;
}
static FileFormat getFormatFromName(const QString &fileName);
bool loadFromFile(const QString &fileName, FileFormat fmt);
bool loadFromRemote(const QString &nativeString, int remoteDeckId);
bool saveToFile(const QString &fileName, FileFormat fmt);
QString exportDeckToDecklist();
// overload
bool saveToStream_Plain(QTextStream &out);
bool saveToStream_Plain(QTextStream &out, bool addComments = true);
protected:
void saveToStream_DeckHeader(QTextStream &out);
void saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode);
void saveToStream_DeckZoneCards(QTextStream &out, const InnerDecklistNode *zoneNode, QList <DecklistCardNode*> cards);
void saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode, bool addComments = true);
void saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards,
bool addComments = true);
virtual QString getCardZoneFromName(QString cardName, QString currentZoneName);
virtual QString getCompleteCardName(const QString cardName) const;
};
#endif

View File

@@ -1,17 +1,17 @@
#include <QFile>
#include <QTextStream>
#include <QFont>
#include <QBrush>
#include <QTextCursor>
#include <QTextDocument>
#include <QPrinter>
#include <QTextTable>
#include <QProgressDialog>
#include "main.h"
#include "decklistmodel.h"
#include "carddatabase.h"
#include "settingscache.h"
#include "deck_loader.h"
#include "main.h"
#include "settingscache.h"
#include <QBrush>
#include <QFile>
#include <QFont>
#include <QPrinter>
#include <QProgressDialog>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextStream>
#include <QTextTable>
DeckListModel::DeckListModel(QObject *parent)
: QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder)
@@ -31,23 +31,30 @@ DeckListModel::~DeckListModel()
void DeckListModel::rebuildTree()
{
beginResetModel();
root->clearTree();
InnerDecklistNode *listRoot = deckList->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
InnerDecklistNode *node = new InnerDecklistNode(currentZone->getName(), root);
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
// XXX better sanity checking
if (!currentCard)
continue;
CardInfo *info = db->getCard(currentCard->getName());
InnerDecklistNode *listRoot = deckList->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
auto *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
auto *node = new InnerDecklistNode(currentZone->getName(), root);
for (int j = 0; j < currentZone->size(); j++) {
auto *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
// TODO: better sanity checking
if (currentCard == nullptr) {
continue;
}
CardInfoPtr info = db->getCard(currentCard->getName());
QString cardType = info ? info->getMainCardType() : "unknown";
InnerDecklistNode *cardTypeNode = dynamic_cast<InnerDecklistNode *>(node->findChild(cardType));
if (!cardTypeNode)
auto *cardTypeNode = dynamic_cast<InnerDecklistNode *>(node->findChild(cardType));
if (!cardTypeNode) {
cardTypeNode = new InnerDecklistNode(cardType, node);
}
new DecklistModelCardNode(currentCard, cardTypeNode);
}
@@ -58,34 +65,35 @@ void DeckListModel::rebuildTree()
int DeckListModel::rowCount(const QModelIndex &parent) const
{
// debugIndexInfo("rowCount", parent);
InnerDecklistNode *node = getNode<InnerDecklistNode *>(parent);
if (node)
// debugIndexInfo("rowCount", parent);
auto *node = getNode<InnerDecklistNode *>(parent);
if (node) {
return node->size();
else
} else {
return 0;
}
}
int DeckListModel::columnCount(const QModelIndex &/*parent*/) const
int DeckListModel::columnCount(const QModelIndex & /*parent*/) const
{
if (settingsCache->getPriceTagFeature())
return 3;
else
return 2;
return 2;
}
QVariant DeckListModel::data(const QModelIndex &index, int role) const
{
// debugIndexInfo("data", index);
if (!index.isValid())
return QVariant();
if (index.column() >= columnCount())
// debugIndexInfo("data", index);
if (!index.isValid()) {
return QVariant();
}
AbstractDecklistNode *temp = static_cast<AbstractDecklistNode *>(index.internalPointer());
DecklistModelCardNode *card = dynamic_cast<DecklistModelCardNode *>(temp);
if (!card) {
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(temp);
if (index.column() >= columnCount()) {
return QVariant();
}
auto *temp = static_cast<AbstractDecklistNode *>(index.internalPointer());
auto *card = dynamic_cast<DecklistModelCardNode *>(temp);
if (card == nullptr) {
auto *node = dynamic_cast<InnerDecklistNode *>(temp);
switch (role) {
case Qt::FontRole: {
QFont f;
@@ -93,31 +101,40 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
return f;
}
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::EditRole: {
switch (index.column()) {
case 0: return node->recursiveCount(true);
case 1: return node->getVisibleName();
case 2: return QString().sprintf("$%.2f", node->recursivePrice(true));
default: return QVariant();
case 0:
return node->recursiveCount(true);
case 1:
if (role == Qt::DisplayRole)
return node->getVisibleName();
else
return node->getName();
default:
return QVariant();
}
}
case Qt::BackgroundRole: {
int color = 90 + 60 * node->depth();
return QBrush(QColor(color, 255, color));
}
case Qt::ForegroundRole: {
return QBrush(QColor(0 ,0 ,0));
return QBrush(QColor(0, 0, 0));
}
default: return QVariant();
default:
return QVariant();
}
} else {
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole: {
switch (index.column()) {
case 0: return card->getNumber();
case 1: return card->getName();
case 2: return QString().sprintf("$%.2f", card->getTotalPrice());
default: return QVariant();
case 0:
return card->getNumber();
case 1:
return card->getName();
default:
return QVariant();
}
}
case Qt::BackgroundRole: {
@@ -125,52 +142,59 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
return QBrush(QColor(color, color, color));
}
case Qt::ForegroundRole: {
return QBrush(QColor(0 ,0 ,0));
return QBrush(QColor(0, 0, 0));
}
default: return QVariant();
default:
return QVariant();
}
}
}
QVariant DeckListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal))
if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) {
return QVariant();
if (section >= columnCount())
}
if (section >= columnCount()) {
return QVariant();
}
switch (section) {
case 0: return tr("Number");
case 1: return tr("Card");
case 2: return tr("Price");
default: return QVariant();
case 0:
return tr("Number");
case 1:
return tr("Card");
default:
return QVariant();
}
}
QModelIndex DeckListModel::index(int row, int column, const QModelIndex &parent) const
{
// debugIndexInfo("index", parent);
if (!hasIndex(row, column, parent))
return QModelIndex();
// debugIndexInfo("index", parent);
if (!hasIndex(row, column, parent)) {
return {};
}
InnerDecklistNode *parentNode = getNode<InnerDecklistNode *>(parent);
if (row >= parentNode->size())
return QModelIndex();
return createIndex(row, column, parentNode->at(row));
auto *parentNode = getNode<InnerDecklistNode *>(parent);
return row >= parentNode->size() ? QModelIndex() : createIndex(row, column, parentNode->at(row));
}
QModelIndex DeckListModel::parent(const QModelIndex &ind) const
{
if (!ind.isValid())
return QModelIndex();
if (!ind.isValid()) {
return {};
}
return nodeToIndex(static_cast<AbstractDecklistNode *>(ind.internalPointer())->getParent());
}
Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
if (!index.isValid()) {
return nullptr;
}
Qt::ItemFlags result = Qt::ItemIsEnabled;
result |= Qt::ItemIsSelectable;
@@ -180,24 +204,32 @@ Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const
void DeckListModel::emitRecursiveUpdates(const QModelIndex &index)
{
if (!index.isValid())
if (!index.isValid()) {
return;
}
emit dataChanged(index, index);
emitRecursiveUpdates(index.parent());
}
bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
DecklistModelCardNode *node = getNode<DecklistModelCardNode *>(index);
if (!node || (role != Qt::EditRole))
auto *node = getNode<DecklistModelCardNode *>(index);
if (!node || (role != Qt::EditRole)) {
return false;
}
switch (index.column()) {
case 0: node->setNumber(value.toInt()); break;
case 1: node->setName(value.toString()); break;
case 2: node->setPrice(value.toFloat()); break;
default: return false;
case 0:
node->setNumber(value.toInt());
break;
case 1:
node->setName(value.toString());
break;
default:
return false;
}
emitRecursiveUpdates(index);
deckList->updateDeckHash();
return true;
@@ -205,32 +237,37 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int
bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent)
{
InnerDecklistNode *node = getNode<InnerDecklistNode *>(parent);
if (!node)
auto *node = getNode<InnerDecklistNode *>(parent);
if (!node) {
return false;
if (row + count > node->size())
}
if (row + count > node->size()) {
return false;
}
beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i < count; i++) {
AbstractDecklistNode *toDelete = node->takeAt(row);
if (DecklistModelCardNode *temp = dynamic_cast<DecklistModelCardNode *>(toDelete))
if (auto *temp = dynamic_cast<DecklistModelCardNode *>(toDelete)) {
deckList->deleteNode(temp->getDataNode());
}
delete toDelete;
}
endRemoveRows();
if (!node->size() && (node != root))
if (node->empty() && (node != root)) {
removeRows(parent.row(), 1, parent.parent());
else
} else {
emitRecursiveUpdates(parent);
}
return true;
}
InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerDecklistNode *parent)
{
InnerDecklistNode *newNode = dynamic_cast<InnerDecklistNode *>(parent->findChild(name));
auto *newNode = dynamic_cast<InnerDecklistNode *>(parent->findChild(name));
if (!newNode) {
beginInsertRows(nodeToIndex(parent), parent->size(), parent->size());
newNode = new InnerDecklistNode(name, parent);
@@ -242,21 +279,24 @@ InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerD
DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, const QString &zoneName) const
{
InnerDecklistNode *zoneNode, *typeNode;
CardInfo *info;
CardInfoPtr info;
QString cardType;
zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
if(!zoneNode)
return NULL;
if (!zoneNode) {
return nullptr;
}
info = db->getCard(cardName);
if(!info)
return NULL;
if (!info) {
return nullptr;
}
cardType = info->getMainCardType();
typeNode = dynamic_cast<InnerDecklistNode *>(zoneNode->findChild(cardType));
if(!typeNode)
return NULL;
if (!typeNode) {
return nullptr;
}
return dynamic_cast<DecklistModelCardNode *>(typeNode->findChild(cardName));
}
@@ -266,24 +306,37 @@ QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zone
DecklistModelCardNode *cardNode;
cardNode = findCardNode(cardName, zoneName);
if(!cardNode)
return QModelIndex();
if (!cardNode) {
return {};
}
return nodeToIndex(cardNode);
}
QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName)
QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway)
{
InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root);
CardInfoPtr info = db->getCard(cardName);
if (info == nullptr) {
if (abAddAnyway) {
// We need to keep this card added no matter what
// This is usually called from tab_deck_editor
// So we'll create a new CardInfo with the name
// and default values for all fields
info = CardInfo::newInstance(cardName, false, nullptr, nullptr, "unknown", nullptr, nullptr, QStringList(),
QList<CardRelation *>(), QList<CardRelation *>(), false, 0, false, 0,
SetList(), QStringMap(), MuidMap(), QStringMap(), QStringMap());
} else {
return {};
}
}
CardInfo *info = db->getCard(cardName);
if(!info)
return QModelIndex();
InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root);
QString cardType = info->getMainCardType();
InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode);
QModelIndex parentIndex = nodeToIndex(cardTypeNode);
DecklistModelCardNode *cardNode = dynamic_cast<DecklistModelCardNode *>(cardTypeNode->findChild(cardName));
auto *cardNode = dynamic_cast<DecklistModelCardNode *>(cardTypeNode->findChild(cardName));
if (!cardNode) {
DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName);
beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size());
@@ -300,8 +353,10 @@ QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneN
QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const
{
if (node == root)
return QModelIndex();
if (node == nullptr || node == root) {
return {};
}
return createIndex(node->getParent()->indexOf(node), 0, node);
}
@@ -309,7 +364,7 @@ void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order)
{
// Sort children of node and save the information needed to
// update the list of persistent indexes.
QVector<QPair<int, int> > sortResult = node->sort(order);
QVector<QPair<int, int>> sortResult = node->sort(order);
QModelIndexList from, to;
int columns = columnCount();
@@ -326,9 +381,10 @@ void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order)
// Recursion
for (int i = node->size() - 1; i >= 0; --i) {
InnerDecklistNode *subNode = dynamic_cast<InnerDecklistNode *>(node->at(i));
if (subNode)
auto *subNode = dynamic_cast<InnerDecklistNode *>(node->at(i));
if (subNode) {
sortHelper(subNode, order);
}
}
}
@@ -339,19 +395,17 @@ void DeckListModel::sort(int column, Qt::SortOrder order)
emit layoutAboutToBeChanged();
DeckSortMethod sortMethod;
switch(column) {
case 0:
sortMethod = ByNumber;
break;
case 1:
sortMethod = ByName;
break;
case 2:
sortMethod = ByPrice;
break;
default:
sortMethod = ByName;
switch (column) {
case 0:
sortMethod = ByNumber;
break;
case 1:
sortMethod = ByName;
break;
default:
sortMethod = ByName;
}
root->setSortMethod(sortMethod);
sortHelper(root, order);
emit layoutChanged();
@@ -373,7 +427,7 @@ void DeckListModel::setDeckList(DeckLoader *_deck)
void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node)
{
const int totalColumns = settingsCache->getPriceTagFeature() ? 3 : 2;
const int totalColumns = 2;
if (node->height() == 1) {
QTextBlockFormat blockFormat;
@@ -381,18 +435,14 @@ void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *no
charFormat.setFontPointSize(11);
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QString priceStr;
if (settingsCache->getPriceTagFeature())
priceStr = QString().sprintf(": $%.2f", node->recursivePrice(true));
cursor->insertText(QString("%1: %2").arg(node->getVisibleName()).arg(node->recursiveCount(true)).append(priceStr));
QTextTableFormat tableFormat;
tableFormat.setCellPadding(0);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat);
QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat);
for (int i = 0; i < node->size(); i++) {
AbstractDecklistCardNode *card = dynamic_cast<AbstractDecklistCardNode *>(node->at(i));
auto *card = dynamic_cast<AbstractDecklistCardNode *>(node->at(i));
QTextCharFormat cellCharFormat;
cellCharFormat.setFontPointSize(9);
@@ -406,13 +456,6 @@ void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *no
cell.setFormat(cellCharFormat);
cellCursor = cell.firstCursorPosition();
cellCursor.insertText(card->getName());
if (settingsCache->getPriceTagFeature()) {
cell = table->cellAt(i, 2);
cell.setFormat(cellCharFormat);
cellCursor = cell.firstCursorPosition();
cellCursor.insertText(QString().sprintf("$%.2f ", card->getTotalPrice()));
}
}
} else if (node->height() == 2) {
QTextBlockFormat blockFormat;
@@ -421,18 +464,15 @@ void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *no
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QString priceStr;
if (settingsCache->getPriceTagFeature())
priceStr = QString().sprintf(": $%.2f", node->recursivePrice(true));
cursor->insertText(QString("%1: %2").arg(node->getVisibleName()).arg(node->recursiveCount(true)).append(priceStr));
QTextTableFormat tableFormat;
tableFormat.setCellPadding(10);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QVector<QTextLength> constraints;
for (int i = 0; i < totalColumns; i++)
for (int i = 0; i < totalColumns; i++) {
constraints << QTextLength(QTextLength::PercentageLength, 100.0 / totalColumns);
}
tableFormat.setColumnWidthConstraints(constraints);
QTextTable *table = cursor->insertTable(1, totalColumns, tableFormat);
@@ -441,6 +481,7 @@ void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *no
printDeckListNode(&cellCursor, dynamic_cast<InnerDecklistNode *>(node->at(i)));
}
}
cursor->movePosition(QTextCursor::End);
}
@@ -469,16 +510,11 @@ void DeckListModel::printDeckList(QPrinter *printer)
for (int i = 0; i < root->size(); i++) {
cursor.insertHtml("<br><img src=theme:hr.jpg>");
//cursor.insertHtml("<hr>");
// cursor.insertHtml("<hr>");
cursor.insertBlock(headerBlockFormat, headerCharFormat);
printDeckListNode(&cursor, dynamic_cast<InnerDecklistNode *>(root->at(i)));
}
doc.print(printer);
}
void DeckListModel::pricesUpdated()
{
emit layoutChanged();
}
}

View File

@@ -1,9 +1,9 @@
#ifndef DECKLISTMODEL_H
#define DECKLISTMODEL_H
#include "decklist.h"
#include <QAbstractItemModel>
#include <QList>
#include "decklist.h"
class DeckLoader;
class CardDatabase;
@@ -11,21 +11,40 @@ class QProgressDialog;
class QPrinter;
class QTextCursor;
class DecklistModelCardNode : public AbstractDecklistCardNode {
class DecklistModelCardNode : public AbstractDecklistCardNode
{
private:
DecklistCardNode *dataNode;
public:
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent) : AbstractDecklistCardNode(_parent), dataNode(_dataNode) { }
int getNumber() const { return dataNode->getNumber(); }
void setNumber(int _number) { dataNode->setNumber(_number); }
float getPrice() const { return dataNode->getPrice(); }
void setPrice(const float _price) { dataNode->setPrice(_price); }
QString getName() const { return dataNode->getName(); }
void setName(const QString &_name) { dataNode->setName(_name); }
DecklistCardNode *getDataNode() const { return dataNode; }
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent)
: AbstractDecklistCardNode(_parent), dataNode(_dataNode)
{
}
int getNumber() const
{
return dataNode->getNumber();
}
void setNumber(int _number)
{
dataNode->setNumber(_number);
}
QString getName() const
{
return dataNode->getName();
}
void setName(const QString &_name)
{
dataNode->setName(_name);
}
DecklistCardNode *getDataNode() const
{
return dataNode;
}
};
class DeckListModel : public QAbstractItemModel {
class DeckListModel : public QAbstractItemModel
{
Q_OBJECT
private slots:
void rebuildTree();
@@ -33,25 +52,29 @@ public slots:
void printDeckList(QPrinter *printer);
signals:
void deckHashChanged();
public:
DeckListModel(QObject *parent = 0);
~DeckListModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &index) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex &parent);
QModelIndex findCard(const QString &cardName, const QString &zoneName) const;
QModelIndex addCard(const QString &cardName, const QString &zoneName);
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
QModelIndex addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway = false);
void sort(int column, Qt::SortOrder order);
void cleanList();
DeckLoader *getDeckList() const { return deckList; }
DeckLoader *getDeckList() const
{
return deckList;
}
void setDeckList(DeckLoader *_deck);
void pricesUpdated();
private:
DeckLoader *deckList;
InnerDecklistNode *root;
@@ -65,7 +88,7 @@ private:
void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
template<typename T> T getNode(const QModelIndex &index) const
template <typename T> T getNode(const QModelIndex &index) const
{
if (!index.isValid())
return dynamic_cast<T>(root);

View File

@@ -1,17 +1,15 @@
#include "deckstats_interface.h"
#include "decklist.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QRegExp>
#include <QMessageBox>
#include <QDesktopServices>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRegExp>
#include <QUrlQuery>
DeckStatsInterface::DeckStatsInterface(
CardDatabase &_cardDatabase,
QObject *parent
) : QObject(parent), cardDatabase(_cardDatabase)
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)
{
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
@@ -25,20 +23,20 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply)
deleteLater();
return;
}
QString data(reply->readAll());
reply->deleteLater();
QRegExp rx("<meta property=\"og:url\" content=\"([^\"]+)\"/>");
QRegExp rx("<meta property=\"og:url\" content=\"([^\"]+)\"");
if (-1 == rx.indexIn(data)) {
QMessageBox::critical(0, tr("Error"), tr("The reply from the server could not be parsed."));
deleteLater();
return;
}
QString deckUrl = rx.cap(1);
QDesktopServices::openUrl(deckUrl);
deleteLater();
}
@@ -59,41 +57,33 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
{
QByteArray data;
getAnalyzeRequestData(deck, &data);
QNetworkRequest request(QUrl("http://deckstats.net/index.php"));
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
manager->post(request, data);
}
struct CopyIfNotAToken {
struct CopyIfNotAToken
{
CardDatabase &cardDatabase;
DeckList &destination;
CopyIfNotAToken(
CardDatabase &_cardDatabase,
DeckList &_destination
) : cardDatabase(_cardDatabase), destination(_destination) {};
CopyIfNotAToken(CardDatabase &_cardDatabase, DeckList &_destination)
: cardDatabase(_cardDatabase), destination(_destination){};
void operator()(
const InnerDecklistNode *node,
const DecklistCardNode *card
) const {
CardInfo * dbCard = cardDatabase.getCard(card->getName());
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
{
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(
card->getName(),
node->getName()
);
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
addedCard->setNumber(card->getNumber());
}
}
};
void DeckStatsInterface::copyDeckWithoutTokens(
const DeckList &source,
DeckList &destination
) {
void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination)
{
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
source.forEachCard(copyIfNotAToken);
}

View File

@@ -10,7 +10,8 @@ class QNetworkAccessManager;
class QNetworkReply;
class DeckList;
class DeckStatsInterface : public QObject {
class DeckStatsInterface : public QObject
{
Q_OBJECT
private:
QNetworkAccessManager *manager;
@@ -22,11 +23,12 @@ private:
* closest non-token card instead. So we construct a new deck which has no
* tokens.
*/
void copyDeckWithoutTokens(const DeckList &source, DeckList& destination);
void copyDeckWithoutTokens(const DeckList &source, DeckList &destination);
private slots:
void queryFinished(QNetworkReply *reply);
void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
public:
DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0);
void analyzeDeck(DeckList *deck);

View File

@@ -1,14 +1,17 @@
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <math.h>
#include "deckview.h"
#include "decklist.h"
#include "carddatabase.h"
#include "decklist.h"
#include "main.h"
#include "settingscache.h"
#include "thememanager.h"
#include "main.h"
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QMouseEvent>
#include <math.h>
DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag)
DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item,
const QPointF &_hotSpot,
AbstractCardDragItem *parentDrag)
: AbstractCardDragItem(_item, _hotSpot, parentDrag), currentZone(0)
{
}
@@ -24,7 +27,7 @@ void DeckViewCardDragItem::updatePosition(const QPointF &cursorScenePos)
if (!cursorZone)
return;
currentZone = cursorZone;
QPointF newPos = cursorScenePos;
if (newPos != pos()) {
for (int i = 0; i < childDrags.size(); i++)
@@ -55,10 +58,10 @@ void DeckViewCardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
c->handleDrop(currentZone);
sc->removeItem(c);
}
sc->updateContents();
}
event->accept();
}
@@ -76,12 +79,12 @@ DeckViewCard::~DeckViewCard()
void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
AbstractCardItem::paint(painter, option, widget);
painter->save();
QPen pen;
pen.setWidth(3);
pen.setJoinStyle(Qt::MiterJoin);
pen.setColor(originZone == "main" ? Qt::green : Qt::red);
pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red);
painter->setPen(pen);
painter->drawRect(QRectF(1, 1, CARD_WIDTH - 2, CARD_HEIGHT - 2.5));
painter->restore();
@@ -89,18 +92,19 @@ void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() < 2 * QApplication::startDragDistance())
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
2 * QApplication::startDragDistance())
return;
if (static_cast<DeckViewScene *>(scene())->getLocked())
return;
delete dragItem;
dragItem = new DeckViewCardDragItem(this, event->pos());
scene()->addItem(dragItem);
dragItem->updatePosition(event->scenePos());
dragItem->grabMouse();
QList<QGraphicsItem *> sel = scene()->selectedItems();
int j = 0;
for (int i = 0; i < sel.size(); i++) {
@@ -116,14 +120,45 @@ void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
setCursor(Qt::OpenHandCursor);
}
void DeckView::mouseDoubleClickEvent(QMouseEvent *event)
{
if (static_cast<DeckViewScene *>(scene())->getLocked())
return;
if (event->button() == Qt::LeftButton) {
QList<MoveCard_ToZone> result;
QList<QGraphicsItem *> sel = scene()->selectedItems();
for (int i = 0; i < sel.size(); i++) {
DeckViewCard *c = static_cast<DeckViewCard *>(sel.at(i));
DeckViewCardContainer *zone = static_cast<DeckViewCardContainer *>(c->parentItem());
MoveCard_ToZone m;
m.set_card_name(c->getName().toStdString());
m.set_start_zone(zone->getName().toStdString());
if (zone->getName() == DECK_ZONE_MAIN)
m.set_target_zone(DECK_ZONE_SIDE);
else if (zone->getName() == DECK_ZONE_SIDE)
m.set_target_zone(DECK_ZONE_MAIN);
else // Trying to move from another zone
m.set_target_zone(zone->getName().toStdString());
result.append(m);
}
deckViewScene->applySideboardPlan(result);
deckViewScene->rearrangeItems();
emit deckViewScene->sideboardPlanChanged();
}
}
void DeckViewCard::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
event->accept();
processHoverEvent();
}
DeckViewCardContainer::DeckViewCardContainer(const QString &_name)
: QGraphicsItem(), name(_name), width(0), height(0)
DeckViewCardContainer::DeckViewCardContainer(const QString &_name) : QGraphicsItem(), name(_name), width(0), height(0)
{
setCacheMode(DeviceCoordinateCache);
}
@@ -136,19 +171,20 @@ QRectF DeckViewCardContainer::boundingRect() const
void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
qreal totalTextWidth = getCardTypeTextWidth();
painter->fillRect(boundingRect(), themeManager->getTableBgBrush());
painter->setPen(QColor(255, 255, 255, 100));
painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY));
painter->setPen(QColor(Qt::white));
QFont f("Serif");
f.setStyleHint(QFont::Serif);
f.setPixelSize(24);
f.setWeight(QFont::Bold);
painter->setFont(f);
painter->drawText(10, 0, width - 20, separatorY, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, InnerDecklistNode::visibleNameFromName(name) + QString(": %1").arg(cards.size()));
painter->drawText(10, 0, width - 20, separatorY, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine,
InnerDecklistNode::visibleNameFromName(name) + QString(": %1").arg(cards.size()));
f.setPixelSize(16);
painter->setFont(f);
QList<QString> cardTypeList = cardsByType.uniqueKeys();
@@ -178,12 +214,12 @@ void DeckViewCardContainer::addCard(DeckViewCard *card)
void DeckViewCardContainer::removeCard(DeckViewCard *card)
{
cards.removeAt(cards.indexOf(card));
cardsByType.remove(card->getInfo() ? card->getInfo()->getMainCardType(): "", card);
cardsByType.remove(card->getInfo() ? card->getInfo()->getMainCardType() : "", card);
}
QList<QPair<int, int> > DeckViewCardContainer::getRowsAndCols() const
QList<QPair<int, int>> DeckViewCardContainer::getRowsAndCols() const
{
QList<QPair<int, int> > result;
QList<QPair<int, int>> result;
QList<QString> cardTypeList = cardsByType.uniqueKeys();
for (int i = 0; i < cardTypeList.size(); ++i)
result.append(QPair<int, int>(1, cardsByType.count(cardTypeList[i])));
@@ -197,7 +233,7 @@ int DeckViewCardContainer::getCardTypeTextWidth() const
f.setPixelSize(16);
f.setWeight(QFont::Bold);
QFontMetrics fm(f);
int maxCardTypeWidth = 0;
QMapIterator<QString, DeckViewCard *> i(cardsByType);
while (i.hasNext()) {
@@ -205,56 +241,56 @@ int DeckViewCardContainer::getCardTypeTextWidth() const
if (cardTypeWidth > maxCardTypeWidth)
maxCardTypeWidth = cardTypeWidth;
}
return maxCardTypeWidth + 15;
}
QSizeF DeckViewCardContainer::calculateBoundingRect(const QList<QPair<int, int> > &rowsAndCols) const
QSizeF DeckViewCardContainer::calculateBoundingRect(const QList<QPair<int, int>> &rowsAndCols) const
{
qreal totalHeight = 0;
qreal totalWidth = 0;
// Calculate space needed for cards
for (int i = 0; i < rowsAndCols.size(); ++i) {
totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY;
if (CARD_WIDTH * rowsAndCols[i].second > totalWidth)
totalWidth = CARD_WIDTH * rowsAndCols[i].second;
}
return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY);
}
bool DeckViewCardContainer::sortCardsByName(DeckViewCard * c1, DeckViewCard * c2)
bool DeckViewCardContainer::sortCardsByName(DeckViewCard *c1, DeckViewCard *c2)
{
if (c1 && c2)
return c1->getName() < c2->getName();
return c1->getName() < c2->getName();
return false;
}
void DeckViewCardContainer::rearrangeItems(const QList<QPair<int, int> > &rowsAndCols)
void DeckViewCardContainer::rearrangeItems(const QList<QPair<int, int>> &rowsAndCols)
{
currentRowsAndCols = rowsAndCols;
int totalCols = 0, totalRows = 0;
qreal yUntilNow = separatorY + paddingY;
qreal x = (qreal) getCardTypeTextWidth();
qreal x = (qreal)getCardTypeTextWidth();
for (int i = 0; i < rowsAndCols.size(); ++i) {
const int tempRows = rowsAndCols[i].first;
const int tempCols = rowsAndCols[i].second;
totalRows += tempRows;
if (tempCols > totalCols)
totalCols = tempCols;
QList<QString> cardTypeList = cardsByType.uniqueKeys();
QList<DeckViewCard *> row = cardsByType.values(cardTypeList[i]);
qSort( row.begin(), row.end(), DeckViewCardContainer::sortCardsByName);
qSort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName);
for (int j = 0; j < row.size(); ++j) {
DeckViewCard *card = row[j];
card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT);
}
yUntilNow += tempRows * CARD_HEIGHT + paddingY;
}
prepareGeometryChange();
QSizeF bRect = calculateBoundingRect(rowsAndCols);
width = bRect.width();
@@ -268,8 +304,7 @@ void DeckViewCardContainer::setWidth(qreal _width)
update();
}
DeckViewScene::DeckViewScene(QObject *parent)
: QGraphicsScene(parent), locked(true), deck(0), optimalAspectRatio(1.0)
DeckViewScene::DeckViewScene(QObject *parent) : QGraphicsScene(parent), locked(true), deck(0), optimalAspectRatio(1.0)
{
}
@@ -291,7 +326,7 @@ void DeckViewScene::setDeck(const DeckList &_deck)
{
if (deck)
delete deck;
deck = new DeckList(_deck);
rebuildTree();
applySideboardPlan(deck->getCurrentSideboardPlan());
@@ -301,21 +336,21 @@ void DeckViewScene::setDeck(const DeckList &_deck)
void DeckViewScene::rebuildTree()
{
clearContents();
if (!deck)
return;
InnerDecklistNode *listRoot = deck->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0);
if (!container) {
container = new DeckViewCardContainer(currentZone->getName());
cardContainers.insert(currentZone->getName(), container);
addItem(container);
}
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
@@ -338,7 +373,7 @@ void DeckViewScene::applySideboardPlan(const QList<MoveCard_ToZone> &plan)
DeckViewCardContainer *target = cardContainers.value(QString::fromStdString(m.target_zone()));
if (!start || !target)
continue;
DeckViewCard *card = 0;
const QList<DeckViewCard *> &cardList = start->getCards();
for (int j = 0; j < cardList.size(); ++j)
@@ -348,7 +383,7 @@ void DeckViewScene::applySideboardPlan(const QList<MoveCard_ToZone> &plan)
}
if (!card)
continue;
start->removeCard(card);
target->addCard(card);
card->setParentItem(target);
@@ -359,19 +394,19 @@ void DeckViewScene::rearrangeItems()
{
const int spacing = CARD_HEIGHT / 3;
QList<DeckViewCardContainer *> contList = cardContainers.values();
// Initialize space requirements
QList<QList<QPair<int, int> > > rowsAndColsList;
QList<QList<int> > cardCountList;
QList<QList<QPair<int, int>>> rowsAndColsList;
QList<QList<int>> cardCountList;
for (int i = 0; i < contList.size(); ++i) {
QList<QPair<int, int> > rowsAndCols = contList[i]->getRowsAndCols();
QList<QPair<int, int>> rowsAndCols = contList[i]->getRowsAndCols();
rowsAndColsList.append(rowsAndCols);
cardCountList.append(QList<int>());
for (int j = 0; j < rowsAndCols.size(); ++j)
cardCountList[i].append(rowsAndCols[j].second);
}
qreal totalHeight, totalWidth;
for (;;) {
// Calculate total size before this iteration
@@ -383,11 +418,11 @@ void DeckViewScene::rearrangeItems()
if (contSize.width() > totalWidth)
totalWidth = contSize.width();
}
// We're done when the aspect ratio shifts from too high to too low.
if (totalWidth / totalHeight <= optimalAspectRatio)
break;
// Find category with highest column count
int maxIndex1 = -1, maxIndex2 = -1, maxCols = 0;
for (int i = 0; i < rowsAndColsList.size(); ++i)
@@ -397,13 +432,14 @@ void DeckViewScene::rearrangeItems()
maxIndex2 = j;
maxCols = rowsAndColsList[i][j].second;
}
// Add row to category
const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first;
const int maxCardCount = cardCountList[maxIndex1][maxIndex2];
rowsAndColsList[maxIndex1][maxIndex2] = QPair<int, int>(maxRows + 1, (int) ceil((qreal) maxCardCount / (qreal) (maxRows + 1)));
rowsAndColsList[maxIndex1][maxIndex2] =
QPair<int, int>(maxRows + 1, (int)ceil((qreal)maxCardCount / (qreal)(maxRows + 1)));
}
totalHeight = -spacing;
for (int i = 0; i < contList.size(); ++i) {
DeckViewCardContainer *c = contList[i];
@@ -411,11 +447,11 @@ void DeckViewScene::rearrangeItems()
c->setPos(0, totalHeight + spacing);
totalHeight += c->boundingRect().height() + spacing;
}
totalWidth = totalHeight * optimalAspectRatio;
for (int i = 0; i < contList.size(); ++i)
contList[i]->setWidth(totalWidth);
setSceneRect(QRectF(0, 0, totalWidth, totalHeight));
}
@@ -450,14 +486,13 @@ void DeckViewScene::resetSideboardPlan()
rearrangeItems();
}
DeckView::DeckView(QWidget *parent)
: QGraphicsView(parent)
DeckView::DeckView(QWidget *parent) : QGraphicsView(parent)
{
deckViewScene = new DeckViewScene(this);
setBackgroundBrush(QBrush(QColor(0, 0, 0)));
setDragMode(RubberBandDrag);
setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing/* | QPainter::SmoothPixmapTransform*/);
setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing /* | QPainter::SmoothPixmapTransform*/);
setScene(deckViewScene);
connect(deckViewScene, SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(updateSceneRect(const QRectF &)));
@@ -468,7 +503,7 @@ DeckView::DeckView(QWidget *parent)
void DeckView::resizeEvent(QResizeEvent *event)
{
QGraphicsView::resizeEvent(event);
deckViewScene->setOptimalAspectRatio((qreal) width() / (qreal) height());
deckViewScene->setOptimalAspectRatio((qreal)width() / (qreal)height());
deckViewScene->rearrangeItems();
}

View File

@@ -1,12 +1,12 @@
#ifndef DECKVIEW_H
#define DECKVIEW_H
#include "abstractcarddragitem.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QMap>
#include <QMultiMap>
#include <QPixmap>
#include "abstractcarddragitem.h"
#include "pb/move_card_to_zone.pb.h"
@@ -17,65 +17,90 @@ class DeckViewCardContainer;
class DeckViewCardDragItem;
class MoveCardToZone;
class DeckViewCard : public AbstractCardItem {
class DeckViewCard : public AbstractCardItem
{
private:
QString originZone;
DeckViewCardDragItem *dragItem;
public:
DeckViewCard(const QString &_name = QString(), const QString &_originZone = QString(), QGraphicsItem *parent = 0);
~DeckViewCard();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
const QString &getOriginZone() const { return originZone; }
const QString &getOriginZone() const
{
return originZone;
}
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
};
class DeckViewCardDragItem : public AbstractCardDragItem {
class DeckViewCardDragItem : public AbstractCardDragItem
{
private:
DeckViewCardContainer *currentZone;
void handleDrop(DeckViewCardContainer *target);
public:
DeckViewCardDragItem(DeckViewCard *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0);
void updatePosition(const QPointF &cursorScenePos);
protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
class DeckViewCardContainer : public QGraphicsItem {
class DeckViewCardContainer : public QGraphicsItem
{
private:
static const int separatorY = 20;
static const int paddingY = 10;
static bool sortCardsByName(DeckViewCard * c1, DeckViewCard * c2);
static bool sortCardsByName(DeckViewCard *c1, DeckViewCard *c2);
QString name;
QList<DeckViewCard *> cards;
QMultiMap<QString, DeckViewCard *> cardsByType;
QList<QPair<int, int> > currentRowsAndCols;
QList<QPair<int, int>> currentRowsAndCols;
qreal width, height;
int getCardTypeTextWidth() const;
public:
enum { Type = typeDeckViewCardContainer };
int type() const { return Type; }
enum
{
Type = typeDeckViewCardContainer
};
int type() const
{
return Type;
}
DeckViewCardContainer(const QString &_name);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void addCard(DeckViewCard *card);
void removeCard(DeckViewCard *card);
const QList<DeckViewCard *> &getCards() const { return cards; }
const QString &getName() const { return name; }
const QList<DeckViewCard *> &getCards() const
{
return cards;
}
const QString &getName() const
{
return name;
}
void setWidth(qreal _width);
QList<QPair<int, int> > getRowsAndCols() const;
QSizeF calculateBoundingRect(const QList<QPair<int, int> > &rowsAndCols) const;
void rearrangeItems(const QList<QPair<int, int> > &rowsAndCols);
QList<QPair<int, int>> getRowsAndCols() const;
QSizeF calculateBoundingRect(const QList<QPair<int, int>> &rowsAndCols) const;
void rearrangeItems(const QList<QPair<int, int>> &rowsAndCols);
};
class DeckViewScene : public QGraphicsScene {
class DeckViewScene : public QGraphicsScene
{
Q_OBJECT
signals:
void newCardAdded(AbstractCardItem *card);
void sideboardPlanChanged();
private:
bool locked;
DeckList *deck;
@@ -83,24 +108,36 @@ private:
qreal optimalAspectRatio;
void clearContents();
void rebuildTree();
void applySideboardPlan(const QList<MoveCard_ToZone> &plan);
public:
DeckViewScene(QObject *parent = 0);
~DeckViewScene();
void setLocked(bool _locked) { locked = _locked; }
bool getLocked() const { return locked; }
void setLocked(bool _locked)
{
locked = _locked;
}
bool getLocked() const
{
return locked;
}
void setDeck(const DeckList &_deck);
void setOptimalAspectRatio(qreal _optimalAspectRatio) { optimalAspectRatio = _optimalAspectRatio; }
void setOptimalAspectRatio(qreal _optimalAspectRatio)
{
optimalAspectRatio = _optimalAspectRatio;
}
void rearrangeItems();
void updateContents();
QList<MoveCard_ToZone> getSideboardPlan() const;
void resetSideboardPlan();
void applySideboardPlan(const QList<MoveCard_ToZone> &plan);
};
class DeckView : public QGraphicsView {
class DeckView : public QGraphicsView
{
Q_OBJECT
private:
DeckViewScene *deckViewScene;
protected:
void resizeEvent(QResizeEvent *event);
public slots:
@@ -108,11 +145,19 @@ public slots:
signals:
void newCardAdded(AbstractCardItem *card);
void sideboardPlanChanged();
public:
DeckView(QWidget *parent = 0);
void setDeck(const DeckList &_deck);
void setLocked(bool _locked) { deckViewScene->setLocked(_locked); }
QList<MoveCard_ToZone> getSideboardPlan() const { return deckViewScene->getSideboardPlan(); }
void setLocked(bool _locked)
{
deckViewScene->setLocked(_locked);
}
QList<MoveCard_ToZone> getSideboardPlan() const
{
return deckViewScene->getSideboardPlan();
}
void mouseDoubleClickEvent(QMouseEvent *event);
void resetSideboardPlan();
};

View File

@@ -1,75 +1,90 @@
#include <QLabel>
#include <QCheckBox>
#include <QComboBox>
#include <QRadioButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QDebug>
#include <QEvent>
#include <QKeyEvent>
#include <QMessageBox>
#include <iostream>
#include <QGroupBox>
#include <QPushButton>
#include "dlg_connect.h"
#include "settingscache.h"
#include "userconnection_information.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QDialogButtonBox>
#include <QEvent>
#include <QGridLayout>
#include <QGroupBox>
#include <QKeyEvent>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
DlgConnect::DlgConnect(QWidget *parent)
: QDialog(parent)
#define PUBLIC_SERVERS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers"
DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
{
previousHostButton = new QRadioButton(tr("Previous Host"), this);
previousHostButton = new QRadioButton(tr("Known Hosts"), this);
previousHosts = new QComboBox(this);
previousHosts->installEventFilter(new DeleteHighlightedItemWhenShiftDelPressedEventFilter);
QStringList previousHostList = settingsCache->servers().getPreviousHostList();
if (previousHostList.isEmpty()) {
previousHostList << "cockatrice.woogerworks.com";
previousHostList << "chickatrice.net";
previousHostList << "mtg.tetrarch.co";
previousHostList << "cockatric.es";
}
previousHosts->addItems(previousHostList);
previousHosts->setCurrentIndex(settingsCache->servers().getPrevioushostindex());
hps = new HandlePublicServers(this);
btnRefreshServers = new QPushButton(this);
btnRefreshServers->setIcon(QPixmap("theme:icons/update"));
btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers"));
btnRefreshServers->setFixedWidth(30);
connect(hps, SIGNAL(sigPublicServersDownloadedSuccessfully()), this, SLOT(rebuildComboBoxList()));
connect(hps, SIGNAL(sigPublicServersDownloadedUnsuccessfully(int)), this, SLOT(rebuildComboBoxList(int)));
connect(btnRefreshServers, SIGNAL(released()), this, SLOT(downloadThePublicServers()));
connect(this, SIGNAL(sigPublicServersDownloaded()), this, SLOT(rebuildComboBoxList()));
preRebuildComboBoxList();
newHostButton = new QRadioButton(tr("New Host"), this);
saveLabel = new QLabel(tr("Name:"));
saveEdit = new QLineEdit;
saveLabel->setBuddy(saveEdit);
hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit();
hostEdit->setPlaceholderText(tr("Enter host name"));
hostEdit = new QLineEdit;
hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit(settingsCache->servers().getPort("4747"));
portEdit = new QLineEdit;
portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit(settingsCache->servers().getPlayerName("Player"));
playernameEdit = new QLineEdit;
playernameLabel->setBuddy(playernameEdit);
passwordLabel = new QLabel(tr("P&assword:"));
passwordEdit = new QLineEdit(settingsCache->servers().getPassword());
passwordEdit = new QLineEdit;
passwordLabel->setBuddy(passwordEdit);
passwordEdit->setEchoMode(QLineEdit::Password);
savePasswordCheckBox = new QCheckBox(tr("&Save password"));
savePasswordCheckBox->setChecked(settingsCache->servers().getSavePassword());
autoConnectCheckBox = new QCheckBox(tr("A&uto connect"));
autoConnectCheckBox->setToolTip(tr("Automatically connect to the most recent login when Cockatrice opens"));
if(savePasswordCheckBox->isChecked())
{
autoConnectCheckBox->setChecked(settingsCache->servers().getAutoConnect());
if (settingsCache->servers().getSavePassword()) {
autoConnectCheckBox->setChecked(static_cast<bool>(settingsCache->servers().getAutoConnect()));
autoConnectCheckBox->setEnabled(true);
} else {
settingsCache->servers().setAutoConnect(0);
autoConnectCheckBox->setChecked(0);
autoConnectCheckBox->setChecked(false);
autoConnectCheckBox->setEnabled(false);
}
connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int)));
serverIssuesLabel =
new QLabel(tr("If you have any trouble connecting or registering then contact the server staff for help!"));
serverIssuesLabel->setWordWrap(true);
serverContactLabel = new QLabel(tr("Webpage") + ":");
serverContactLink = new QLabel;
serverContactLink->setTextFormat(Qt::RichText);
serverContactLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
serverContactLink->setOpenExternalLinks(true);
updateDisplayInfo(previousHosts->currentText());
btnForgotPassword = new QPushButton(tr("Forgot password"));
connect(btnForgotPassword, SIGNAL(released()), this, SLOT(actForgotPassword()));
@@ -81,43 +96,58 @@ DlgConnect::DlgConnect(QWidget *parent)
btnCancel->setFixedWidth(100);
connect(btnCancel, SIGNAL(released()), this, SLOT(actCancel()));
QGridLayout *connectionLayout = new QGridLayout;
connectionLayout->addWidget(previousHostButton, 0, 1);
connectionLayout->addWidget(previousHosts, 1, 1);
connectionLayout->addWidget(newHostButton, 2, 1);
connectionLayout->addWidget(hostLabel, 3, 0);
connectionLayout->addWidget(hostEdit, 3, 1);
connectionLayout->addWidget(portLabel, 4, 0);
connectionLayout->addWidget(portEdit, 4, 1);
connectionLayout->addWidget(autoConnectCheckBox, 5, 1);
newHolderLayout = new QHBoxLayout;
newHolderLayout->addWidget(previousHosts);
newHolderLayout->addWidget(btnRefreshServers);
QGridLayout *buttons = new QGridLayout;
connectionLayout = new QGridLayout;
connectionLayout->addWidget(previousHostButton, 0, 1);
connectionLayout->addLayout(newHolderLayout, 1, 1, 1, 2);
connectionLayout->addWidget(newHostButton, 2, 1);
connectionLayout->addWidget(saveLabel, 3, 0);
connectionLayout->addWidget(saveEdit, 3, 1);
connectionLayout->addWidget(hostLabel, 4, 0);
connectionLayout->addWidget(hostEdit, 4, 1);
connectionLayout->addWidget(portLabel, 5, 0);
connectionLayout->addWidget(portEdit, 5, 1);
connectionLayout->addWidget(autoConnectCheckBox, 6, 1);
buttons = new QGridLayout;
buttons->addWidget(btnOk, 0, 0);
buttons->addWidget(btnForgotPassword, 0, 1);
buttons->addWidget(btnCancel, 0, 2);
QGroupBox *restrictionsGroupBox = new QGroupBox(tr("Server"));
restrictionsGroupBox = new QGroupBox(tr("Server"));
restrictionsGroupBox->setLayout(connectionLayout);
QGridLayout *loginLayout = new QGridLayout;
serverInfoLayout = new QGridLayout;
serverInfoLayout->addWidget(serverIssuesLabel, 0, 0, 1, 4, Qt::AlignTop);
serverInfoLayout->addWidget(serverContactLabel, 1, 0);
serverInfoLayout->addWidget(serverContactLink, 1, 1, 1, 3);
loginLayout = new QGridLayout;
loginLayout->addWidget(playernameLabel, 0, 0);
loginLayout->addWidget(playernameEdit, 0, 1);
loginLayout->addWidget(passwordLabel, 1, 0);
loginLayout->addWidget(passwordEdit, 1, 1);
loginLayout->addWidget(savePasswordCheckBox, 2, 1);
QGroupBox *loginGroupBox = new QGroupBox(tr("Login"));
loginGroupBox = new QGroupBox(tr("Login"));
loginGroupBox->setLayout(loginLayout);
QGroupBox *btnGroupBox = new QGroupBox(tr(""));
serverInfoGroupBox = new QGroupBox(tr("Server Contact"));
serverInfoGroupBox->setLayout(serverInfoLayout);
btnGroupBox = new QGroupBox(tr(""));
btnGroupBox->setLayout(buttons);
QGridLayout *grid = new QGridLayout;
grid = new QGridLayout;
grid->addWidget(restrictionsGroupBox, 0, 0);
grid->addWidget(loginGroupBox, 1, 0);
grid->addWidget(btnGroupBox, 2, 0);
QVBoxLayout *mainLayout = new QVBoxLayout;
grid->addWidget(serverInfoGroupBox, 1, 0);
grid->addWidget(loginGroupBox, 2, 0);
grid->addWidget(btnGroupBox, 3, 0);
mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
setLayout(mainLayout);
@@ -128,66 +158,175 @@ DlgConnect::DlgConnect(QWidget *parent)
connect(previousHostButton, SIGNAL(toggled(bool)), this, SLOT(previousHostSelected(bool)));
connect(newHostButton, SIGNAL(toggled(bool)), this, SLOT(newHostSelected(bool)));
if (settingsCache->servers().getPreviousHostLogin())
previousHostButton->setChecked(true);
else
newHostButton->setChecked(true);
previousHostButton->setChecked(true);
connect(previousHosts, SIGNAL(currentIndexChanged(const QString &)), this,
SLOT(updateDisplayInfo(const QString &)));
playernameEdit->setFocus();
}
DlgConnect::~DlgConnect() = default;
void DlgConnect::previousHostSelected(bool state) {
void DlgConnect::actSaveConfig()
{
bool updateSuccess = settingsCache->servers().updateExistingServer(
saveEdit->text().trimmed(), hostEdit->text().trimmed(), portEdit->text().trimmed(),
playernameEdit->text().trimmed(), passwordEdit->text(), savePasswordCheckBox->isChecked());
if (!updateSuccess) {
settingsCache->servers().addNewServer(saveEdit->text().trimmed(), hostEdit->text().trimmed(),
portEdit->text().trimmed(), playernameEdit->text().trimmed(),
passwordEdit->text(), savePasswordCheckBox->isChecked());
}
preRebuildComboBoxList();
}
void DlgConnect::downloadThePublicServers()
{
btnRefreshServers->setDisabled(true);
previousHosts->clear();
previousHosts->addItem(placeHolderText);
hps->downloadPublicServers();
}
void DlgConnect::preRebuildComboBoxList()
{
UserConnection_Information uci;
savedHostList = uci.getServerInfo();
if (savedHostList.size() == 1) {
downloadThePublicServers();
} else {
rebuildComboBoxList();
}
}
void DlgConnect::rebuildComboBoxList(int failure)
{
Q_UNUSED(failure);
previousHosts->clear();
UserConnection_Information uci;
savedHostList = uci.getServerInfo();
int i = 0;
for (auto pair : savedHostList) {
auto tmp = pair.second;
QString saveName = tmp.getSaveName();
if (saveName.size()) {
previousHosts->addItem(saveName);
if (settingsCache->servers().getPrevioushostName() == saveName) {
previousHosts->setCurrentIndex(i);
}
i++;
}
}
btnRefreshServers->setDisabled(false);
}
void DlgConnect::previousHostSelected(bool state)
{
if (state) {
hostLabel->setDisabled(true);
saveEdit->setDisabled(true);
hostEdit->setDisabled(true);
portEdit->setDisabled(true);
previousHosts->setDisabled(false);
btnRefreshServers->setDisabled(false);
}
}
void DlgConnect::newHostSelected(bool state) {
void DlgConnect::updateDisplayInfo(const QString &saveName)
{
if (saveEdit == nullptr || saveName == placeHolderText) {
return;
}
UserConnection_Information uci;
QStringList data = uci.getServerInfo(saveName);
bool savePasswordStatus = (data.at(5) == "1");
saveEdit->setText(data.at(0));
hostEdit->setText(data.at(1));
portEdit->setText(data.at(2));
playernameEdit->setText(data.at(3));
savePasswordCheckBox->setChecked(savePasswordStatus);
if (savePasswordStatus) {
passwordEdit->setText(data.at(4));
}
if (!data.at(6).isEmpty()) {
QString formattedLink = "<a href=\"" + data.at(6) + "\">" + data.at(6) + "</a>";
serverContactLabel->setText(tr("Webpage") + ":");
serverContactLink->setText(formattedLink);
} else {
serverContactLabel->setText("");
serverContactLink->setText("");
}
}
void DlgConnect::newHostSelected(bool state)
{
if (state) {
hostEdit->setDisabled(false);
hostLabel->setDisabled(false);
previousHosts->setDisabled(true);
btnRefreshServers->setDisabled(true);
hostEdit->clear();
hostEdit->setPlaceholderText("Server URL");
hostEdit->setDisabled(false);
portEdit->clear();
portEdit->setPlaceholderText("Communication Port");
portEdit->setDisabled(false);
playernameEdit->clear();
passwordEdit->clear();
saveEdit->clear();
saveEdit->setPlaceholderText("Unique Server Name");
saveEdit->setDisabled(false);
serverContactLabel->setText("");
serverContactLink->setText("");
} else {
preRebuildComboBoxList();
}
}
void DlgConnect::passwordSaved(int state)
{
Q_UNUSED(state);
if(savePasswordCheckBox->isChecked()) {
autoConnectCheckBox->setEnabled(true);
if (savePasswordCheckBox->isChecked()) {
autoConnectCheckBox->setEnabled(true);
} else {
autoConnectCheckBox->setChecked(0);
autoConnectCheckBox->setChecked(false);
autoConnectCheckBox->setEnabled(false);
}
}
void DlgConnect::actOk()
{
settingsCache->servers().setPort(portEdit->text());
settingsCache->servers().setPlayerName(playernameEdit->text());
settingsCache->servers().setPassword(savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString());
settingsCache->servers().setSavePassword(savePasswordCheckBox->isChecked() ? 1 : 0);
settingsCache->servers().setAutoConnect(autoConnectCheckBox->isChecked() ? 1 : 0);
settingsCache->servers().setPreviousHostLogin(previousHostButton->isChecked() ? 1 : 0);
QStringList hostList;
if (newHostButton->isChecked())
if (!hostEdit->text().trimmed().isEmpty())
hostList << hostEdit->text();
for (int i = 0; i < previousHosts->count(); i++)
if(!previousHosts->itemText(i).trimmed().isEmpty())
hostList << previousHosts->itemText(i);
settingsCache->servers().setPreviousHostList(hostList);
settingsCache->servers().setPrevioushostindex(previousHosts->currentIndex());
if (newHostButton->isChecked()) {
if (saveEdit->text().isEmpty()) {
QMessageBox::critical(this, tr("Connection Warning"), tr("You need to name your new connection profile."));
return;
}
if(playernameEdit->text().isEmpty())
{
settingsCache->servers().addNewServer(saveEdit->text().trimmed(), hostEdit->text().trimmed(),
portEdit->text().trimmed(), playernameEdit->text().trimmed(),
passwordEdit->text(), savePasswordCheckBox->isChecked());
} else {
settingsCache->servers().updateExistingServer(saveEdit->text().trimmed(), hostEdit->text().trimmed(),
portEdit->text().trimmed(), playernameEdit->text().trimmed(),
passwordEdit->text(), savePasswordCheckBox->isChecked());
}
settingsCache->servers().setPrevioushostName(saveEdit->text());
settingsCache->servers().setAutoConnect(autoConnectCheckBox->isChecked());
if (playernameEdit->text().isEmpty()) {
QMessageBox::critical(this, tr("Connect Warning"), tr("The player name can't be empty."));
return;
}
@@ -195,29 +334,30 @@ void DlgConnect::actOk()
accept();
}
QString DlgConnect::getHost() const {
return previousHostButton->isChecked() ? previousHosts->currentText() : hostEdit->text();
QString DlgConnect::getHost() const
{
return hostEdit->text().trimmed();
}
void DlgConnect::actCancel()
{
settingsCache->servers().setSavePassword(savePasswordCheckBox->isChecked() ? 1 : 0);
settingsCache->servers().setAutoConnect( autoConnectCheckBox->isChecked() ? 1 : 0);
settingsCache->servers().setSavePassword(savePasswordCheckBox->isChecked());
settingsCache->servers().setAutoConnect(autoConnectCheckBox->isChecked());
reject();
}
bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
auto *keyEvent = dynamic_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Delete) {
QComboBox* combobox = reinterpret_cast<QComboBox *>(obj);
auto *combobox = reinterpret_cast<QComboBox *>(obj);
combobox->removeItem(combobox->currentIndex());
return true;
}
}
return QObject::eventFilter(obj, event);
}

View File

@@ -1,47 +1,79 @@
#ifndef DLG_CONNECT_H
#define DLG_CONNECT_H
#include "handle_public_servers.h"
#include "userconnection_information.h"
#include <QDialog>
#include <QLineEdit>
class QLabel;
class QPushButton;
class QCheckBox;
class QComboBox;
class QGridLayout;
class QGroupBox;
class QHBoxLayout;
class QLabel;
class QPushButton;
class QRadioButton;
class QVBoxLayout;
class DeleteHighlightedItemWhenShiftDelPressedEventFilter : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
bool eventFilter(QObject *obj, QEvent *event) override;
};
class DlgConnect : public QDialog {
class DlgConnect : public QDialog
{
Q_OBJECT
signals :
signals:
void sigStartForgotPasswordRequest();
void sigPublicServersDownloaded();
public:
DlgConnect(QWidget *parent = 0);
explicit DlgConnect(QWidget *parent = nullptr);
~DlgConnect() override;
QString getHost() const;
int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); }
QString getPassword() const { return passwordEdit->text(); }
int getPort() const
{
return portEdit->text().toInt();
}
QString getPlayerName() const
{
return playernameEdit->text();
}
QString getPassword() const
{
return passwordEdit->text();
}
private slots:
void actOk();
void actCancel();
void actSaveConfig();
void passwordSaved(int state);
void previousHostSelected(bool state);
void newHostSelected(bool state);
void actForgotPassword();
void updateDisplayInfo(const QString &saveName);
void preRebuildComboBoxList();
void rebuildComboBoxList(int failure = -1);
void downloadThePublicServers();
private:
QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit;
QGridLayout *newHostLayout, *connectionLayout, *buttons, *loginLayout, *serverInfoLayout, *grid;
QHBoxLayout *newHolderLayout;
QGroupBox *loginGroupBox, *serverInfoGroupBox, *btnGroupBox, *restrictionsGroupBox;
QVBoxLayout *mainLayout;
QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel, *saveLabel, *serverIssuesLabel,
*serverContactLabel, *serverContactLink;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit, *saveEdit;
QCheckBox *savePasswordCheckBox, *autoConnectCheckBox;
QComboBox *previousHosts;
QRadioButton *newHostButton, *previousHostButton;
QPushButton *btnOk, *btnCancel, *btnForgotPassword;
QPushButton *btnOk, *btnCancel, *btnForgotPassword, *btnRefreshServers;
QMap<QString, std::pair<QString, UserConnection_Information>> savedHostList;
HandlePublicServers *hps;
const QString placeHolderText = tr("Downloading...");
};
#endif

View File

@@ -1,25 +1,30 @@
#include <QCheckBox>
#include <QCloseEvent>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QComboBox>
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QTreeView>
#include <QRadioButton>
#include <QHeaderView>
#include <QTreeView>
#include <QVBoxLayout>
#include "carddatabasemodel.h"
#include "cardinfopicture.h"
#include "decklist.h"
#include "dlg_create_token.h"
#include "carddatabasemodel.h"
#include "main.h"
#include "settingscache.h"
DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent)
: QDialog(parent), predefinedTokens(_predefinedTokens)
{
pic = new CardInfoPicture();
pic->setObjectName("pic");
nameLabel = new QLabel(tr("&Name:"));
nameEdit = new QLineEdit(tr("Token"));
nameEdit->selectAll();
@@ -44,7 +49,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
annotationLabel = new QLabel(tr("&Annotation:"));
annotationEdit = new QLineEdit;
annotationLabel->setBuddy(annotationEdit);
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
destroyCheckBox->setChecked(true);
@@ -58,19 +63,21 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
grid->addWidget(annotationLabel, 3, 0);
grid->addWidget(annotationEdit, 3, 1);
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
tokenDataGroupBox->setLayout(grid);
cardDatabaseModel = new CardDatabaseModel(db, this);
cardDatabaseModel = new CardDatabaseModel(db, false, this);
cardDatabaseDisplayModel = new TokenDisplayModel(this);
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
chooseTokenFromAllRadioButton = new QRadioButton(tr("Show &all tokens"));
connect(chooseTokenFromAllRadioButton, SIGNAL(toggled(bool)), this, SLOT(actChooseTokenFromAll(bool)));
chooseTokenFromDeckRadioButton = new QRadioButton(tr("Show tokens from this &deck"));
connect(chooseTokenFromDeckRadioButton, SIGNAL(toggled(bool)), this, SLOT(actChooseTokenFromDeck(bool)));
QTreeView *chooseTokenView = new QTreeView;
QByteArray deckHeaderState = settingsCache->layouts().getDeckEditorDbHeaderState();
chooseTokenView = new QTreeView;
chooseTokenView->setModel(cardDatabaseDisplayModel);
chooseTokenView->setUniformRowHeights(true);
chooseTokenView->setRootIsDecorated(false);
@@ -78,66 +85,78 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
chooseTokenView->setSortingEnabled(true);
chooseTokenView->sortByColumn(0, Qt::AscendingOrder);
chooseTokenView->resizeColumnToContents(0);
chooseTokenView->header()->setStretchLastSection(false);
chooseTokenView->header()->hideSection(1);
chooseTokenView->header()->hideSection(2);
chooseTokenView->setWordWrap(true);
chooseTokenView->setColumnWidth(0, 130);
chooseTokenView->setColumnWidth(3, 178);
chooseTokenView->header()->setSectionResizeMode(4, QHeaderView::ResizeToContents);
connect(chooseTokenView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(tokenSelectionChanged(QModelIndex, QModelIndex)));
if (predefinedTokens.isEmpty())
if (!deckHeaderState.isNull())
chooseTokenView->header()->restoreState(deckHeaderState);
chooseTokenView->header()->setStretchLastSection(false);
chooseTokenView->header()->hideSection(1); // Sets
chooseTokenView->header()->hideSection(2); // Mana Cost
chooseTokenView->header()->setSectionResizeMode(5, QHeaderView::ResizeToContents); // Color(s)
connect(chooseTokenView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this,
SLOT(tokenSelectionChanged(QModelIndex, QModelIndex)));
connect(chooseTokenView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(actOk()));
if (predefinedTokens.isEmpty()) {
chooseTokenFromAllRadioButton->setChecked(true);
else {
chooseTokenFromDeckRadioButton->setDisabled(true); // No tokens in deck = no need for option
} else {
chooseTokenFromDeckRadioButton->setChecked(true);
cardDatabaseDisplayModel->setCardNameSet(QSet<QString>::fromList(predefinedTokens));
}
QVBoxLayout *tokenChooseLayout = new QVBoxLayout;
tokenChooseLayout->addWidget(chooseTokenFromAllRadioButton);
tokenChooseLayout->addWidget(chooseTokenFromDeckRadioButton);
tokenChooseLayout->addWidget(chooseTokenView);
QGroupBox *tokenChooseGroupBox = new QGroupBox(tr("Choose token from list"));
tokenChooseGroupBox->setLayout(tokenChooseLayout);
QVBoxLayout *leftVBox = new QVBoxLayout;
leftVBox->addWidget(tokenDataGroupBox);
leftVBox->addStretch();
QGridLayout *hbox = new QGridLayout;
hbox->addLayout(leftVBox, 0, 0);
hbox->addWidget(tokenChooseGroupBox, 0, 1);
hbox->addWidget(pic, 0, 0, 1, 1);
hbox->addWidget(tokenDataGroupBox, 1, 0, 1, 1);
hbox->addWidget(tokenChooseGroupBox, 0, 1, 2, 1);
hbox->setColumnStretch(1, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actReject()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(hbox);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Create token"));
setFixedHeight(sizeHint().height());
setFixedWidth(width());
resize(600, 500);
restoreGeometry(settingsCache->getTokenDialogGeometry());
}
void DlgCreateToken::closeEvent(QCloseEvent *event)
{
event->accept();
settingsCache->setTokenDialogGeometry(saveGeometry());
}
void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
const CardInfo *cardInfo = current.row() >= 0 ? cardDatabaseModel->getCard(realIndex.row()) : 0;
if(cardInfo)
{
CardInfoPtr cardInfo;
if (current.row() >= 0) {
cardInfo = cardDatabaseModel->getCard(realIndex.row());
}
if (cardInfo) {
updateSearchFieldWithoutUpdatingFilter(cardInfo->getName());
const QChar cardColor = cardInfo->getColorChar();
colorEdit->setCurrentIndex(colorEdit->findData(cardColor, Qt::UserRole, Qt::MatchFixedString));
ptEdit->setText(cardInfo->getPowTough());
if(settingsCache->getAnnotateTokens())
if (settingsCache->getAnnotateTokens())
annotationEdit->setText(cardInfo->getText());
} else {
nameEdit->setText("");
@@ -145,9 +164,12 @@ void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QMo
ptEdit->setText("");
annotationEdit->setText("");
}
pic->setCard(cardInfo);
}
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const {
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const
{
nameEdit->blockSignals(true);
nameEdit->setText(newValue);
nameEdit->blockSignals(false);
@@ -172,9 +194,16 @@ void DlgCreateToken::actChooseTokenFromDeck(bool checked)
void DlgCreateToken::actOk()
{
settingsCache->setTokenDialogGeometry(saveGeometry());
accept();
}
void DlgCreateToken::actReject()
{
settingsCache->setTokenDialogGeometry(saveGeometry());
reject();
}
QString DlgCreateToken::getName() const
{
return nameEdit->text();
@@ -198,4 +227,4 @@ QString DlgCreateToken::getAnnotation() const
bool DlgCreateToken::getDestroy() const
{
return destroyCheckBox->isChecked();
}
}

View File

@@ -10,11 +10,15 @@ class QComboBox;
class QCheckBox;
class QPushButton;
class QRadioButton;
class QCloseEvent;
class QTreeView;
class DeckList;
class CardDatabaseModel;
class TokenDisplayModel;
class CardInfoPicture;
class DlgCreateToken : public QDialog {
class DlgCreateToken : public QDialog
{
Q_OBJECT
public:
DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent = 0);
@@ -23,12 +27,17 @@ public:
QString getPT() const;
QString getAnnotation() const;
bool getDestroy() const;
protected:
void closeEvent(QCloseEvent *event);
private slots:
void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search);
void actChooseTokenFromAll(bool checked);
void actChooseTokenFromDeck(bool checked);
void actOk();
void actReject();
private:
CardDatabaseModel *cardDatabaseModel;
TokenDisplayModel *cardDatabaseDisplayModel;
@@ -38,6 +47,8 @@ private:
QLineEdit *nameEdit, *ptEdit, *annotationEdit;
QCheckBox *destroyCheckBox;
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
CardInfoPicture *pic;
QTreeView *chooseTokenView;
void updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const;
};

View File

@@ -1,23 +1,24 @@
#include "dlg_creategame.h"
#include "settingscache.h"
#include "tab_room.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QPushButton>
#include <QGridLayout>
#include <QRadioButton>
#include <QSpinBox>
#include <QGroupBox>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QSet>
#include <QSpinBox>
#include <QWizard>
#include "dlg_creategame.h"
#include "tab_room.h"
#include "settingscache.h"
#include "pending_command.h"
#include "pb/serverinfo_game.pb.h"
#include "pending_command.h"
void DlgCreateGame::sharedCtor() {
void DlgCreateGame::sharedCtor()
{
rememberGameSettings = new QCheckBox(tr("Re&member settings"));
descriptionLabel = new QLabel(tr("&Description:"));
descriptionEdit = new QLineEdit;
@@ -106,7 +107,8 @@ void DlgCreateGame::sharedCtor() {
}
DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameTypes, QWidget *parent)
: QDialog(parent), room(_room), gameTypes(_gameTypes) {
: QDialog(parent), room(_room), gameTypes(_gameTypes)
{
sharedCtor();
rememberGameSettings->setChecked(settingsCache->getRememberGameSettings());
@@ -139,7 +141,8 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameType
}
DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMap<int, QString> &_gameTypes, QWidget *parent)
: QDialog(parent), room(0), gameTypes(_gameTypes) {
: QDialog(parent), room(0), gameTypes(_gameTypes)
{
sharedCtor();
rememberGameSettings->setEnabled(false);
@@ -180,7 +183,8 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMap<int, QS
setWindowTitle(tr("Game information"));
}
void DlgCreateGame::actReset() {
void DlgCreateGame::actReset()
{
descriptionEdit->setText("");
maxPlayersEdit->setValue(2);
@@ -205,8 +209,8 @@ void DlgCreateGame::actReset() {
descriptionEdit->setFocus();
}
void DlgCreateGame::actOK() {
void DlgCreateGame::actOK()
{
Command_CreateGame cmd;
cmd.set_description(descriptionEdit->text().simplified().toStdString());
cmd.set_password(passwordEdit->text().toStdString());
@@ -247,7 +251,8 @@ void DlgCreateGame::actOK() {
buttonBox->setEnabled(false);
}
void DlgCreateGame::checkResponse(const Response &response) {
void DlgCreateGame::checkResponse(const Response &response)
{
buttonBox->setEnabled(true);
if (response.response_code() == Response::RespOk)
@@ -258,9 +263,9 @@ void DlgCreateGame::checkResponse(const Response &response) {
}
}
void DlgCreateGame::spectatorsAllowedChanged(int state) {
void DlgCreateGame::spectatorsAllowedChanged(int state)
{
spectatorsNeedPasswordCheckBox->setEnabled(state);
spectatorsCanTalkCheckBox->setEnabled(state);
spectatorsSeeEverythingCheckBox->setEnabled(state);
}

View File

@@ -17,7 +17,8 @@ class TabRoom;
class Response;
class ServerInfo_Game;
class DlgCreateGame : public QDialog {
class DlgCreateGame : public QDialog
{
Q_OBJECT
public:
DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameTypes, QWidget *parent = 0);
@@ -27,6 +28,7 @@ private slots:
void actReset();
void checkResponse(const Response &response);
void spectatorsAllowedChanged(int state);
private:
TabRoom *room;
QMap<int, QString> gameTypes;
@@ -37,11 +39,12 @@ private:
QLineEdit *descriptionEdit, *passwordEdit;
QSpinBox *maxPlayersEdit;
QCheckBox *onlyBuddiesCheckBox, *onlyRegisteredCheckBox;
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox, *spectatorsSeeEverythingCheckBox;
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox,
*spectatorsSeeEverythingCheckBox;
QDialogButtonBox *buttonBox;
QPushButton *clearButton;
QCheckBox *rememberGameSettings;
void sharedCtor();
};

View File

@@ -10,27 +10,27 @@
#include "dlg_edit_avatar.h"
DlgEditAvatar::DlgEditAvatar(QWidget *parent)
: QDialog(parent)
DlgEditAvatar::DlgEditAvatar(QWidget *parent) : QDialog(parent)
{
imageLabel = new QLabel(tr("No image chosen."));
imageLabel->setFixedSize(400, 200);
imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
imageLabel->setStyleSheet("border: 1px solid #000");
textLabel = new QLabel(tr("To change your avatar, choose a new image.\nTo remove your current avatar, confirm without choosing a new image."));
textLabel = new QLabel(tr("To change your avatar, choose a new image.\nTo remove your current avatar, confirm "
"without choosing a new image."));
browseButton = new QPushButton(tr("Browse..."));
connect(browseButton, SIGNAL(clicked()), this, SLOT(actBrowse()));
QGridLayout *grid = new QGridLayout;
grid->addWidget(imageLabel, 0, 0, 1, 2);
grid->addWidget(textLabel, 1, 0);
grid->addWidget(browseButton, 1, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
@@ -42,7 +42,7 @@ DlgEditAvatar::DlgEditAvatar(QWidget *parent)
}
void DlgEditAvatar::actOk()
{
{
accept();
}
@@ -53,9 +53,9 @@ void DlgEditAvatar::actCancel()
void DlgEditAvatar::actBrowse()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), QDir::homePath(), tr("Image Files (*.png *.jpg *.bmp)"));
if(fileName.isEmpty())
{
QString fileName =
QFileDialog::getOpenFileName(this, tr("Open Image"), QDir::homePath(), tr("Image Files (*.png *.jpg *.bmp)"));
if (fileName.isEmpty()) {
imageLabel->setText(tr("No image chosen."));
return;
}
@@ -64,8 +64,7 @@ void DlgEditAvatar::actBrowse()
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
imgReader.setFileName(fileName);
if(!imgReader.read(&image))
{
if (!imgReader.read(&image)) {
qDebug() << "Avatar image loading failed for file:" << fileName;
imageLabel->setText(tr("Invalid image chosen."));
return;
@@ -76,11 +75,11 @@ void DlgEditAvatar::actBrowse()
QByteArray DlgEditAvatar::getImage()
{
const QPixmap *pix = imageLabel->pixmap();
if(!pix || pix->isNull())
if (!pix || pix->isNull())
return QByteArray();
QImage image = pix->toImage();
if(image.isNull())
if (image.isNull())
return QByteArray();
QByteArray ba;

Some files were not shown because too many files have changed in this diff Show More