Compare commits

...

271 Commits

Author SHA1 Message Date
tooomm
5c1bb27d5c README: Add code docs + flathub repo links (#6384)
* Add code docs + flathub repo links

* Update README.md
2025-12-05 23:28:25 +01:00
BruebachL
dde36183ce [VDE] Proper parent lookup syncs group-by box again (#6396)
* [VDE] Proper parent lookup syncs group-by box again

* [VDE] Proper lib inclusion.

* [VDE] Lint.
2025-12-05 23:27:27 +01:00
BruebachL
7c7f2dd8d5 [Doxygen] Logging (#6399)
* [Doxygen] Logging

Took 50 minutes

Took 36 seconds

* [Doxygen] Newline.

Took 2 minutes

* [Doxygen] Add another example.

Took 7 minutes

* [Doxygen] \note and \warning

Took 4 minutes

Took 32 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-12-05 18:42:45 +01:00
BruebachL
edb0a954e2 [GameInformation] Check for existence of room for create as judge checkbox (#6398) 2025-12-05 17:26:35 +01:00
BruebachL
0a239712dd [VDD] Add search bar for filters. (#6389)
* [VDD] Add search bar for filters.

* Update cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-12-04 23:14:19 +01:00
RickyRister
95c3434205 [TagDisplayWidget] Refactor to just store tags and use signals (#6395) 2025-12-04 10:26:39 -08:00
RickyRister
f0be6972cc [TagsDisplayWidget] cleanup refactor (#6394)
* Make fields private

* Move method to static

* clean up code

* move code
2025-12-04 09:40:24 -08:00
BruebachL
a799cd097a [PrintingSelector] Sync modified and history state on bulk selection (#6379)
* [PrintingSelector] Emit deckModified when using bulk selection

* [PrintingSelector] Hook up history manager.

* [PrintingSelector] Remember card amount.

* Return early.

Took 18 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-12-03 08:23:34 +01:00
RickyRister
b4e3f2cba9 [Oracle] Support importing tokens and spoilers from local file (#6387) 2025-12-03 00:19:56 -05:00
RickyRister
658ae83157 [DeckList] Make DeckList not a QObject (#6383) 2025-12-03 00:18:46 -05:00
BruebachL
d29e72ce72 [CardInfo] Display set short name and collector number in info widget. (#6378)
* [CardInfo] Display set short name and collector number in info widget.

* Lint.

* Use reference.
2025-12-02 22:24:39 +01:00
BruebachL
30cc8ad6f9 [DeckEditor] Expand DeckDock TreeView when adding card through deck editor (#6388)
* [DeckEditor] Expand DeckDock TreeView when adding card through deck editor

* [DeckEditor] Expand first, then set selection.
2025-12-02 22:19:44 +01:00
BruebachL
f0ebd28148 [VDE] Consolidate statistical analysis into a separate object (#6392)
* [VDE] Consolidate statistical analysis into a separate object so multiple widgets can re-use calculations and calculation is only performed once on data change.

* [VDE] Lint.

* [VDE] Move struct up to not confuse compiler.

* [VDE] NoDiscards

* [VDE] Move variables

* [VDE] Lint.
2025-12-02 13:51:08 +01:00
BruebachL
364d0ca52b [EDHRec] Add background plate and "selection highlight" to card display widgets (#6390) 2025-12-01 09:37:48 +01:00
RickyRister
3ff2df2796 [DeckList] Move metadata into struct (#6380)
* [DeckList] Move metadata into struct

* wipe metadata if preserveMetadata is false
2025-11-30 13:09:09 +01:00
RickyRister
d57bec8ec6 [DeckList] Move decklist node classes into new folder (#6381)
* [DeckList] Move decklist node classes into new folder

* reformat

* fix
2025-11-30 13:05:49 +01:00
BruebachL
2b64e65f45 [CardInfoPictureEnlargedWidget] Fix DPR scaling. (#6382) 2025-11-30 13:03:18 +01:00
BruebachL
eab4d435f8 [Feature] TabArchidekt and Archidekt API integration (#6348)
* TabArchidekt and Archidekt API integration.


Took 37 seconds

Took 4 minutes

Took 40 seconds

Took 4 minutes

* Lint.

* Lont.

* Search bar, fancier display, resolve providerId

* Delegate click to base.

* Be explicit for pedantic compilers.

* Liiint.

* Leave them default I guess

* Leave them default I guess

* Small fixes.

* New utility display widgets.

* New style for deck listing.

* Lint.

* Lont.

* Scale things.

* Delegate paint to base.

* Use default Archidekt preview image for decks without featured.

* Consistent sizes.

* Increase font size, qt version guard.

* More version guards.

* Clean up filter layout, use mana symbols.

* Set content margins.

* Refresh on filter change.

* Lint.

* Better elision.

* Query actual new endpoints, new query parameters.

* Doxygen, reorder fields in constructor, readability.

* Update page size doc to min size.

* Update initial min deck size value.

* Add label to page selection.

* Okay, so, people upload a lot of 1 card decks frequently.

* Whoops.

* Add a selection combobox for sorting logic.

* Debounce and limit searches.

* Include.

* Lint.

* Don't imply that Archidekt supports multiple cards/commander names.

* Let's not lambda it and slot it instead.

* Overload.

* Add button to home tab.

Took 8 minutes

* Adjust to selection model change.

Took 5 minutes

* Cleanup auto-generated comments.

Took 8 minutes

* Remember card sizes.

Took 1 minute

* Initialize with correct size.

Took 3 minutes

* Use correct placeholders.

Took 2 minutes

* Style lint.

Took 16 minutes

* Parse double-faced cards correctly.

* Parse double-faced cards correctly.

* Allow TabArchidekt to use VDE group/sort/display buttons

* Lint.

* Indicate that things are clickable.

* Min treshold for nicer display.

* Lint.

* We have good labels at home.

* We do a little linting.

* Qt version guards.

* Qt5 is the devil.

* Update comments.

* Lint comments.

* More doxys.

* One more doxy.

* Lint.

* Update.

* Small fixes.

Took 7 minutes

Took 13 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-30 08:41:01 +01:00
BruebachL
de13c22552 [Fix-Warnings] Suppress C4100: unreferenced parameter for protobuf files (#6373)
* [Fix-Warnings] Suppress C4100: unreferenced parameter for protobuf files.

* [Fix-Warnings] Compiler specific options.

* [Fix-Warnings] Lint.
2025-11-29 18:58:39 +01:00
BruebachL
8ee7163014 [Printing Selector] Notify deck editor about history changes. (#6364)
Took 44 minutes

Took 2 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-29 18:56:06 +01:00
BruebachL
c5fde071e7 [Cleanup] Unused #includes (#6367)
* [Cleanup] Unused #includes

Took 44 minutes

* [Cleanup] More unused #includes

Took 55 minutes

* [Cleanup] Include QSet

Took 4 minutes

* [Cleanup] Include QDebug in deck_list.cpp

Took 3 minutes

* [Cleanup] Include protocol stuff in servatrice_database_interface.h

Took 3 minutes

* [Cleanup] Include QDialogButtonBox

Took 8 minutes

* [Cleanup] Include QUrl

Took 8 minutes

* [Cleanup] Include QTextOption in header.

Took 3 minutes

* [Cleanup] Include QMap in user_list_manager.h

Took 8 minutes

* [Cleanup] Adjust qjson

Took 8 minutes

* [Cleanup] include button box.

Took 3 minutes

* [Cleanup] Redo fwd declarations.

* [Cleanup] Redo last removed fwd declarations.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-29 18:53:11 +01:00
BruebachL
8abd04dab1 [Fix-Warnings] Remove more redundant empty declarations. (extra semicolons) (#6374) 2025-11-29 14:19:11 +01:00
RickyRister
858361e6d3 [DeckLoader] Refactor last load info into struct (#6366)
* [DeckLoader] Refactor last load info into struct

* Use constant

* [[nodiscard]]

* do discard, I guess.

---------

Co-authored-by: Brübach, Lukas <lukas.bruebach@student.fhws.de>
2025-11-28 23:41:11 +01:00
BruebachL
9ece4bfd9b [Fix-Warnings] Mark const getters as [[nodiscard]] (#6365)
Took 45 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-28 21:38:54 +01:00
BruebachL
a1a3b02d3a Add clearer labeling, more tooltips, condense layout. (#6361)
Took 17 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-28 00:42:46 +01:00
BruebachL
bc2ae6c486 Remember more card sizes. (#6360)
Took 22 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-27 23:41:44 +01:00
BruebachL
587a8bc524 [VDD] Add sorting (#6355)
* [VDD] Add sorting

Took 17 seconds

Took 3 minutes

* Adjust to contents.

Took 13 minutes

* Adjust sort order as well.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-27 23:31:40 +01:00
BruebachL
122926c6cd Deck Editor owns DeckHistoryManager (#6359)
Took 4 minutes
2025-11-27 23:11:43 +01:00
BruebachL
bac6beeb50 [VDE] Allow visual database display to toggle to table based display. (#6357) 2025-11-27 23:03:30 +01:00
BruebachL
c75a483ee6 [VDE] Add selection model (#6354)
Took 22 minutes

Took 1 minute


Took 17 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-27 22:16:12 +01:00
BruebachL
1c5bfdbabe Rebuild tree any time setDeckList is called. (#6353)
Took 2 hours 5 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-27 22:02:27 +01:00
BruebachL
553952132f [Game] Fix CardZoneLogic::clearContents() (#6356)
Took 6 minutes

Took 28 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-27 22:00:35 +01:00
BruebachL
1931eb11a9 [VDD] Change 'filter to most recent sets' default to false (#6358) 2025-11-27 21:56:49 +01:00
ebbit1q
65aef396fb do not allow other players to know which cards are in a player's hand (#6125) 2025-11-26 09:16:50 -05:00
ebbit1q
a21e45ed36 add phase to delete arrows in to protocol (#6159)
* protocol changes

* servatrice changes

* add new setting

* implement client side with static 4 phases

* reading the code explains the code

* add subphases to phase.cpp

* use new subphase definition
2025-11-26 09:16:10 -05:00
dependabot[bot]
adee67115c Bump actions/checkout from 5 to 6 (#6347) 2025-11-24 19:42:21 +01:00
tooomm
aea468bc7f Doxygen: Use newer version (#6345)
* readd properties

* use newer doxygen version + print config update diff

* readd config options

* fix config

* revert cache change

* GITHUB md

* graphviz version

* Add doxygen output to .gitignore
2025-11-23 19:06:00 +01:00
tooomm
621c6a8d73 Doxygen: Improve file structure and includes (#6344) 2025-11-22 19:38:39 +01:00
BruebachL
73591d5d0f [BannerCard] Try to restore by providerId (#6341)
* [BannerCard] Try to restore by providerId

Took 27 minutes

Took 41 seconds

* Style lint.

Took 2 minutes

* Don't look up by providerId if it's empty.

Took 8 minutes

* Add extra name guard to providerId clause.

Took 4 minutes

* Update cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

* Update cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

* Adjust to comments.

Took 11 minutes

* Extract to helper function.

Took 3 minutes

* Make helper static.

Took 5 minutes

* Remove const qualifier.

Took 3 minutes

* Finally.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-11-20 15:44:35 +01:00
BruebachL
846f16ddaa [DeckEditor] Deck List History Manager. (#6340)
* [DeckEditor] Deck List History Manager.

Took 23 minutes

Took 17 minutes

* Add icons.

Took 2 minutes


Took 3 seconds

* Small fixes.

Took 12 minutes

* Style lint.

Took 48 seconds

* tr() things.

Took 5 minutes

* Add tooltips for buttons.

Took 3 minutes

* Add explanation label to history.

Took 3 minutes

* Refactor to .cpp, delegate undo/redo to manager, don't return memento

Took 8 minutes

* Clear history when setting deck.

Took 6 minutes

* Move to value based stacks.

Took 52 seconds

* Default constructor.

Took 31 seconds

Took 3 minutes

Took 4 minutes

Took 2 minutes

* Have it listen to deck editor additions.

Took 18 minutes

* Don't connect buttons *and* actions.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-20 14:54:32 +01:00
RickyRister
c46f6d1178 Support flavorName in PrintingInfo and cache the altNames in CardInfo (#6335)
* Support flavorName property and cache altNames

* update oracleimporter

* update cards.xsd
2025-11-20 14:54:23 +01:00
BruebachL
ab5d6db8a2 [DeckList] Disable copy constructor (#6339)
* [DeckList] Disable copy constructor

Took 1 hour 44 minutes

Took 1 minute

# Commit time for manual adjustment:
# Took 28 seconds


Took 33 seconds

* Revert member to pointer.

Took 19 minutes

* Revert pulling up setters/getters now that getDeckList is no longer const.

Took 6 minutes

* Revert more.

Took 2 minutes

* One more fix.

Took 1 minute

* Update cockatrice/src/interface/deck_loader/deck_loader.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-11-20 13:20:09 +01:00
BruebachL
9957cb20e2 [Refactor] Move AbstractGraphicsItem and GraphicsItemType to game_graphics/board (#6342)
* [Refactor] Move AbstractGraphicsItem and GraphicsItemType to game_graphics/board folder.

Took 3 minutes

* Update CMakeLists.txt

Took 12 minutes

* Update CMakeLists.txt

Took 12 minutes

Took 2 minutes


Took 16 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-20 12:52:14 +01:00
BruebachL
8788a7aada [DeckLoader] Disable copy constructor (#6338)
Took 1 hour 19 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-19 12:16:39 +01:00
RickyRister
16392c28c5 [DeckLoader] Refactor to make some methods static (#6336)
* Make forEachCard const

* Make some DeckLoader methods static

* Update usages

* Update method param documentation in deck_loader.cpp

---------

Co-authored-by: BruebachL <44814898+BruebachL@users.noreply.github.com>
2025-11-17 03:49:45 -08:00
BruebachL
a8ee0d7648 [Doxygen] PrintingSelector Extra Pages (#6334)
* [Doxygen] PrintingSelector extra pages

Took 46 minutes

* Update Doxyfile

* Subgroup it.

Took 5 minutes

* Update editing_decks_printings.md

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-17 10:19:53 +01:00
tooomm
a405758222 doxygen config changes (#6330) 2025-11-16 23:51:27 +01:00
BruebachL
537e29d937 [Game Selector] Add button to join game as judge as well as convenience filters and doxygen (#6325)
* Add button to join game as judge as well as convenience filters.

Took 1 hour 11 minutes

* Change button to filter to games created by buddies, set default filter settings to be very permissive.

Took 45 minutes

* Remove debug.

Took 3 minutes

* Update game_selector.cpp

* Add spacers, rearrange.

Took 20 minutes


Took 20 seconds

* Add explanation tooltip.

Took 39 seconds

* Try layouting.

Took 14 minutes

* Set min size, set spacing for mac os

Took 3 minutes

* Try without the labels.

Took 3 minutes

* Don't use labels.

Took 5 minutes

* Fine-tune.

Took 2 minutes

* AsJudge

Took 4 minutes

* Clear up comment.

Took 37 seconds

* Remove shift hotkey.

Took 4 minutes

* Spectate as judge.

Took 8 minutes

* Add checkBox to create game as judge.

Took 7 minutes

* Fix crash.

Took 12 minutes

* Rename, fix returns.

Took 19 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-16 18:04:42 +01:00
RickyRister
9a3104c5ac [CardInfo] refactor some fields into a UiAttributes struct (#6322)
* refactor CardInfo

* refactor everything else
2025-11-16 17:56:57 +01:00
BruebachL
722344967f [Home Tab] Don't add connect button with stretch (#6333)
Took 4 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-16 17:35:27 +01:00
tooomm
73ce5e051c Add min os and arch inspection (#6259) 2025-11-16 15:09:26 +01:00
tooomm
b8bbe141a0 Update prebuild.js (#6266) 2025-11-16 03:18:32 -05:00
BruebachL
3285596a93 [Doxygen] VDE: Include new pictures. (#6323)
* Include new pictures.

Took 57 minutes

* Update old pictures.

Took 6 minutes

Took 3 minutes

* Update export documentation.

Took 4 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-16 01:39:43 +01:00
BruebachL
73763b5ee6 Mark more functions as [[nodiscard]] (#6320)
* Fix local variable double declaration.

Took 44 seconds

* Mark functions as [[nodiscard]]

Took 31 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-16 01:39:24 +01:00
BruebachL
27708d5964 Adjust to proxy model indices (#6324)
Took 12 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-16 01:37:07 +01:00
BruebachL
827f22ed37 [Doxygen] Card Picture Loader (#6315)
* [Doxygen] Card Picture Loader

Took 25 minutes

Took 16 minutes

# Commit time for manual adjustment:
# Took 12 seconds


Took 14 seconds

* Remove placeholder file description.

Took 1 minute

* ... but do group PictureLoader again

Took 28 seconds

* Link to methods directly.

Took 6 minutes

* Forward declaration.

Took 49 seconds

* Remove redundant .cpp function documentation.

Took 15 minutes

* More fixes.

Took 7 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-15 17:26:32 +01:00
BruebachL
ace4063371 Helper to query deckList for DecklistCardNodes. (#6242)
* Helper to query deckList for DecklistCardNodes.

Took 30 minutes

Took 6 minutes

Took 2 minutes

* Fix unused.

Took 3 minutes


Took 1 minute

* Convert string to string list.

Took 2 minutes

* Adjust to rebase.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-15 17:21:43 +01:00
BruebachL
f62e29f5d5 Give settings managers default groups instead of manually specifying them everywhere. (#6273)
* Give settings managers default groups instead of manually specifying them everywhere.

Took 1 hour 2 minutes


Took 41 seconds

Took 32 seconds

Took 5 minutes

* Fix dbconverter mock.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-15 15:58:25 +01:00
BruebachL
5df00de246 Avoid repeating type by using auto. (#6321)
Took 19 minutes


Took 22 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-15 14:06:53 +01:00
BruebachL
28dfd62163 [Doxygen] More extra-pages for cards/developer documentation and various fixes (#6316)
* Docu stash

Took 1 hour 53 minutes

Took 5 minutes


Took 16 seconds

Took 33 seconds

* Remove file headers.

Took 8 minutes

* Group to card set.

Took 8 seconds

* More extra pages.

Took 28 seconds

* Small fix for now.

Took 3 minutes

* Expand on picture loading.

Took 44 minutes

* Fix line break breaking link.

Took 2 minutes

* Images and user documentation.

Took 1 hour 49 minutes

* Update doc/doxygen-extra-pages/developer_documentation/primer_cards.md

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-11-15 13:07:15 +01:00
RickyRister
1c1599a9f4 [Oracle] Move page classes to separate file (#6314)
* Move pages to separate file

* Fix shadowing complaint
2025-11-15 11:27:53 +01:00
RickyRister
6dff230e10 [Oracle] Use NoOp setting providers (#6312) 2025-11-15 11:26:39 +01:00
BruebachL
0f60824749 Set DeckList parent when ownership changes (#6317)
* Initialize all deck list member variables in constructor.

Took 10 minutes

* Revert "Initialize all deck list member variables in constructor."

This reverts commit fba2455808.

* setParent

Took 1 hour 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-15 10:28:54 +01:00
RickyRister
84e0732fb1 Fix crash when changing shortcut in game (#6318) 2025-11-14 15:29:05 +01:00
RickyRister
ae123587d7 [Oracle] clean up OracleImporter (#6313)
* Move variable declaration closer to usage

* Leave comments

* inline some constants

* make code easier to understand

* Use structured binding to iterate over maps

* move things around

* static const regex

* remove redundant parens

* Can't use asKeyValueRange because of Qt versions
2025-11-12 16:58:09 +01:00
BruebachL
2efcb48b7e Fix mistyped pointer. (#6310)
Took 25 minutes

Took 25 seconds

Took 22 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-12 06:00:45 +01:00
BruebachL
3d9cae717d Adjust min size. (#6311)
Took 2 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-12 05:56:29 +01:00
BruebachL
cc73a8cc85 Every card is legal for now. (#6309)
Took 28 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 22:57:23 -05:00
ebbit1q
648f028a63 fix use of wrong parent when removing arrows for a player (#6308) 2025-11-12 04:48:26 +01:00
ebbit1q
840ee1379f fix crash on force starting, kicking players (#6307)
* fix crash on force starting, kicking players

* Update server_game.cpp

---------

Co-authored-by: Zach H <zahalpern+github@gmail.com>
2025-11-12 04:44:58 +01:00
SlightlyCircuitous
3c85ca9cbc Remove Fedora 41 Build and Add Fedora 43 Build (#6305)
* Create Dockerfile

* Update Release Template

* Delete .ci/Fedora41 directory

* remove F41, add F43
2025-11-11 20:18:20 -05:00
BruebachL
8e88749078 A DeckLoader is not a DeckList. (#6306)
* A DeckLoader is not a DeckList.

Took 2 hours 39 minutes

* Explicitly initialize base class in copy constructor?

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 20:16:44 -05:00
tooomm
4c431e98a6 Update Dockerfile (#6268)
* Update Dockerfile

Took 15 minutes

Took 43 seconds

Took 2 minutes

Took 13 seconds

Took 3 minutes

Took 4 minutes

Took 51 seconds

* Update docker-release.yml

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Multi stage build.

Took 11 minutes

* Add more arguments.

Took 8 minutes

* Remove apt cache.

Took 3 minutes

* Add workdir, undo tcp socket include.

Took 10 minutes

* Change runtime deps.

Took 8 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 15:31:12 +01:00
BruebachL
40cf3ced1a [Doxygen] Include libs in Doxyfile (#6302)
* Include libs in Doxyfile

Took 13 minutes

* Update Doxyfile

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2025-11-11 14:48:22 +01:00
ebbit1q
c9ccab8771 Servatrice build failure (#6243)
* attempts to fix the problem

* add test to show the problem

* fix workflow

* move logger to cockatrice

* more attempts

* undo stuff

* mark different libraries as gui

* fix SC2145

* rename servatrice only build
2025-11-11 14:34:39 +01:00
BruebachL
7d2700ca65 Clean up link targets. (#6304)
Took 38 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 14:04:14 +01:00
BruebachL
bfedc12fa8 Deck loader is a gui class. (#6294)
* Deck loader is a gui class.

Took 31 minutes

Took 3 minutes

* Deck Loader is responsible for printing.

Took 8 minutes


Took 2 seconds

* Style proxy.

Took 14 minutes

Took 6 minutes

Took 1 minute

* Don't need to include QBrush anymore.

Took 3 minutes

Took 7 seconds

* Includes for printer.

Took 5 minutes

* Nuke getDeckList()

Took 9 minutes

* Adjust to rebase.

Took 35 seconds

* Lint.

Took 3 minutes

* Braces for one line return statements.

Took 13 minutes

Took 50 seconds

* Enum for model columns.

Took 9 minutes

* One more single line if.

Took 1 minute

* Another style lint on a sunday night

Took 5 minutes

* Move enum to namespace.

Took 3 minutes

* Fix a critical blocker.

Took 5 minutes

* Update docs.

Took 3 minutes

* Doxygen and namespace enums.

Took 2 minutes

Took 15 seconds

* Adjust to namespace.

Took 4 minutes

Took 1 minute

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 11:57:41 +01:00
BruebachL
c16267e60f Doxygen exact_card.h (#6301)
Took 8 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 06:46:30 +01:00
BruebachL
0bd9b84931 Doxygen printing_info.h (#6300)
Took 4 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 06:46:18 +01:00
BruebachL
e9a9475ed7 [Doxygen] Card Database and related (#6303)
Took 29 minutes


Took 24 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-11 06:46:08 +01:00
BruebachL
f00d415dd7 [Doxygen] card_relation.h (#6298)
* Doxygen card_relation.h

Took 31 minutes

* Doxygen card_relation_type.h

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-10 09:12:14 +01:00
BruebachL
1e7ff3dbdf Doxygen card_set.h and card_set_list.h (#6299)
Took 13 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-10 09:11:58 +01:00
BruebachL
eb1c257484 Fix crash on rejoining game when reconnecting. (#6295)
* Fix crash on rejoining game when reconnecting.

Took 2 minutes


Took 13 minutes

* Proper lib include.

Took 1 minute

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-10 09:11:22 +01:00
RickyRister
4d652210dc Support shortcut for hand reveal actions (#6297)
* Support shortcut for hand reveal actions

* add docs
2025-11-09 12:34:09 +01:00
BruebachL
9f2ac78609 Split filters into libraries where applicable. (#6293)
* Split filters into libraries where applicable.

Took 23 minutes

Took 2 minutes

* Include filter string.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-09 12:19:27 +01:00
RickyRister
484e8e64a6 Create "Hand" shortcut group (#6296)
* Add new Hand ShortcutGroup

* Move hand shortcuts
2025-11-09 10:16:30 +01:00
Gauwal
e5d5dfa8d8 UI: highlight pinned printings in Printing Selector (#6252)
* UI: highlight pinned printings in Printing Selector

Fixes #5930.

Signed-off-by: Gauwal <gauwain2611@hotmail.com>

* Fix: adjust pin highlighting behavior + making it sharp/not blurry

* Using qceil

* Isolating pin badge init to it's own method + adding comments

* Cleaning up unnecessary #includes

---------

Signed-off-by: Gauwal <gauwain2611@hotmail.com>
2025-11-09 09:53:27 +01:00
ebbit1q
0ad31fea46 allow oracle to run in background with direct xml downloads (#6241)
Took 5 minutes
2025-11-09 09:52:46 +01:00
Chase Naples
ec2d8f231d Fix horizontal flip of VIP Moderator icon (#6292)
The VIP Moderator (Head Moderator) star icon was flipped horizontally
and did not match the orientation of other moderator icons. Fixed by
inverting the scaleX value in the transform matrix and adjusting the
translateX value to maintain the star's position.

Fixes #6290
2025-11-09 02:17:15 +01:00
BruebachL
aeec56f800 Sync up Visual Deck Editor group by combo box to Deck Editor Dock Widget combo box. (#6291)
Took 21 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-09 01:18:31 +01:00
BruebachL
7e6cad974f [Doxygen] abstract_tab_deck_editor (#6286)
* Doxygen abstract_tab_deck_editor

Took 15 minutes

Took 15 minutes


Took 4 seconds

* Lint.

Took 28 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 23:25:33 +01:00
RickyRister
757e9f3415 Add more sort options to hand sort (#6279)
* Add more sort options to hand sort


Took 14 minutes

* Move defaultOptions up a level

* Directly pass sort order as param

* fix include

* revert

* fallback expandSortOption
2025-11-08 23:03:44 +01:00
BruebachL
6bc2293292 Doxygen tab_deck_editor (#6287)
Took 21 seconds

Took 4 minutes

Took 4 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 22:30:19 +01:00
BruebachL
55aaca0e0d Introduce additional checks for playerMenu shortcut activation for judges (#6275)
* Introduce additional checks for playerMenu shortcut activation when they are accessed by a judge, only activating them if the player is a local player.

Took 2 hours 7 minutes


Took 17 minutes

* Undo example change.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 22:22:02 +01:00
BruebachL
a8a3fca8c9 Clean up inter-library dependencies with interfaces (#6280)
* Have CardDatabase::getPreferredPrintingInfo respect card provider ID overrides (pinned printings)

Took 13 minutes

Took 37 seconds

Took 10 seconds

Took 10 seconds

# Commit time for manual adjustment:
# Took 30 seconds

Took 15 seconds


Took 8 minutes

Took 21 seconds

* Move settings cache and settings card preference provider out of libcockatrice_settings and into cockatrice

Took 52 minutes

Took 9 minutes

Took 1 minute

* Temp cache.

Took 16 minutes

* Dependency Injection for SettingsCache

* Turn SettingsCache into a QSharedPointer.
* Implement interfaces for settings that need it

Took 2 hours 38 minutes

* Adjust oracle.

Took 5 minutes

* Move abstract/noop interfaces to libcockatrice_interfaces so they can be linked against independently.

Took 52 minutes

* Clean up some links.

Took 3 minutes

* Cleanup two includes.

Took 3 minutes

* More fixes.

Took 7 minutes

* More includes that slipped past.

Took 3 minutes

* Stop mocking and start injecting for tests.

Took 15 minutes

* I don't know why remote_client was including main.

Took 4 minutes

* Include.

Took 3 minutes

* Lint.

Took 2 minutes

* Don't use Qt pointers.

Took 1 hour 7 minutes

* Make parser use CardSettingsInterface

Took 13 minutes

* Also adjust constructor lol.

Took 8 minutes

* Lint.

Took 32 minutes

* Revert "Lint."

This reverts commit ecb596c39e.


Took 3 minutes

* Test.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 22:19:40 +01:00
BruebachL
fb30515f72 Doxygen tab_deck_editor_visual (#6288)
Took 15 seconds

Took 3 minutes

Took 3 minutes


Took 45 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 22:14:17 +01:00
BruebachL
9a39af6da0 Doxygen tab_deck_editor_visual_tab_widget (#6289)
Took 29 seconds


Took 3 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 21:23:10 +01:00
ebbit1q
6d75ce4b1c don't close dm tabs when disconnecting (#6285) 2025-11-08 20:06:54 +01:00
BruebachL
dbd1d30ca8 Forward opponent deck view signals to deck view container as well instead of just the player deck view signals. (#6283)
Took 25 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-08 13:25:40 +01:00
Zach H
8f80996515 Strip dashes in Yahoo addresses (#6284) 2025-11-07 13:35:44 -05:00
ebbit1q
d206a70b8a update format.sh (#6240)
* update format.sh

add shellcheck to format.sh
add statement macros to .clang-format
add no clang format to format.sh
add changed file list to format.sh diff
rename --cf-version to --print-version in format.sh
lint files

* enable --shell on ci runs

* remove useless semicolons

removes the semicolons after empty function definitions
these semicolons are optional, they don't do anything
this will have functions be consistently formatted
if we want to keep the option to have these on the same line like they
were before we should use the option AllowShortFunctionsOnASingleLine: None

* fix script

* update echo line in lint_cpp.sh which doesn't lint cpp only at all
2025-11-07 15:00:39 +01:00
Bruno Alexandre Rosa
bbec4d2c7e ci: unify vcpkg jobs (take 2) (#6263)
* ci: unify vcpkg jobs

* use build matrix variables: package_suffix

simplifying some convoluted logic, one variable at a time.  work in progress.

* use build matrix variables: artifact_name

* use build matrix variables: qt stuff

* display cmake flags in builds

* add type to windows builds

* use build matrix variables: cmake stuff

* use build matrix variables: USE_CCACHE

* formatting

* more formatting

* spaces

* address review comments
2025-11-06 23:20:47 +01:00
BruebachL
f24c36d6b1 Core qt module for libs (#6278)
* Move logger and key signals from libcockatrice_utility to Cockatrice.

Took 9 minutes

* Only link Qt::Core instead of COCKATRICE_QT_MODULES to libraries, if possible.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-05 18:51:08 +01:00
BruebachL
adff828415 Move models to lib (#6274)
* Move models to own library.

Took 35 minutes


Took 22 minutes

* Adjust CMakeLists

Took 20 seconds

* Reformat CMakeLists.

Took 2 minutes

* Revert "Reformat CMakeLists."

This reverts commit db5982ad1c.


Took 55 seconds

* Lint an include

Took 17 minutes

Took 9 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-05 18:33:40 +01:00
ebbit1q
d914667238 fix qt 6.10 debug build (#6271)
* fix qt 6.10 build

* fix games model parameter order and correct parameter names

* add missing include for qt5
2025-11-05 12:10:36 +01:00
RickyRister
1c209b3320 Fix names in qtlogging.ini (#6265) 2025-11-03 18:55:32 +01:00
tooomm
aa61032cdf Delete servatrice/scripts/mk_pypb.sh (#6267)
See https://github.com/Cockatrice/Cockatrice/pull/6212#issuecomment-3447840864
2025-11-03 18:32:35 +01:00
dependabot[bot]
3ae4a7d8a7 Bump actions/setup-node from 5 to 6 (#6250)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-02 17:11:23 +01:00
dependabot[bot]
9fdecf21f2 Bump actions/upload-artifact from 4 to 5 (#6260)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 19:23:46 +01:00
transifex-integration[bot]
e4d256790f Updates for project Cockatrice and language en@pirate (#6256)
* Translate oracle_en@source.ts in en@pirate [Manual Sync]

7% of minimum 4% translated source file: 'oracle_en@source.ts'
on 'en@pirate'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate cockatrice_en@source.ts in en@pirate [Manual Sync]

8% of minimum 4% translated source file: 'cockatrice_en@source.ts'
on 'en@pirate'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-26 13:46:46 +01:00
transifex-integration[bot]
d9f4faf4ec Translate cockatrice_en@source.ts in tr [Manual Sync] (#6258)
4% of minimum 4% translated source file: 'cockatrice_en@source.ts'
on 'tr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-26 13:46:02 +01:00
transifex-integration[bot]
609a364971 Translate cockatrice_en@source.ts in nb [Manual Sync] (#6257)
7% of minimum 4% translated source file: 'cockatrice_en@source.ts'
on 'nb'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-26 13:44:26 +01:00
github-actions[bot]
2152ddd99b Update translation files (#6255)
Co-authored-by: github-actions <github-actions@github.com>
2025-10-26 13:23:02 +01:00
github-actions[bot]
8caaf8515e Update translation source strings (#6254)
Co-authored-by: github-actions <github-actions@github.com>
2025-10-26 13:11:56 +01:00
github-actions[bot]
ac822fa084 Update translation files (#6244)
Co-authored-by: github-actions <github-actions@github.com>
2025-10-25 14:43:59 +02:00
transifex-integration[bot]
a265b865f6 Translate oracle/oracle_en@source.ts in nl (#6246)
100% translated source file: 'oracle/oracle_en@source.ts'
on 'nl'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-25 14:38:10 +02:00
transifex-integration[bot]
8efc4f4817 Translate cockatrice_en@source.ts in en_US (#6245)
100% translated source file: 'cockatrice_en@source.ts'
on 'en_US'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-25 14:35:29 +02:00
Bruno Alexandre Rosa
817a3f979e build: target macos 13 for x86 binaries (#6221)
* build: target older macos

* cleanup

* Align xcode versions

* Simplify --x86-macos

* use cmake flag in compile.sh i.s.o. env var in yml

* more cleanups

* adress initial reviews

* generate triplet file in compile.sh

* fix triplet name

* pass matrix.target as version

* small refactor

* another minor refactor

* ci: fix ccache cleaning

* add more comments

* try passing triplets config as cmake variables

* Revert "try passing triplets config as cmake variables"

This reverts commit 77e83e8590.

* move logic inside runner == macos if

* move logic to env var

* simplify script

* format script

"I just thinks it looks better like this"

* make script work for arm as well, might be useful

* use hyphen

* use DVCPKG_HOST_TRIPLET instead of DVCPKG_TARGET_TRIPLET

* use DVCPKG_HOST_TRIPLET AND DVCPKG_TARGET_TRIPLET

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2025-10-22 01:00:25 +02:00
RickyRister
8ebfc40de5 Consolidate closeReplay action into leaveGame (#6239) 2025-10-09 18:59:53 -07:00
RickyRister
c42e953199 Fix unattach shortcut not working at all in remote games (#6238) 2025-10-09 18:59:10 -07:00
RickyRister
636aa72141 Remove redundant prefix from libcockatrice_card folders (#6237)
Took 28 minutes
2025-10-09 23:09:20 +02:00
github-actions[bot]
14e6e6eff4 Update translation source strings (#6203)
Co-authored-by: github-actions <github-actions@github.com>
2025-10-09 23:05:12 +02:00
BruebachL
474c1d0d89 [Move refactor] Move dialogs to interface/widgets/ (#6234)
* Move dialogs/ underneath interface/widgets since QDialog inherits from QWidget.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-09 15:25:18 +02:00
BruebachL
b8983f27ab [Move refactor] Move tabs to interface/widgets (#6235)
* Move tabs to interface/widgets.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-09 14:51:47 +02:00
BruebachL
d9c65d4ae0 [Move refactor] Reparent orphan classes (#6236)
* Move orphaned classes to their correct parent folders.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-09 14:15:19 +02:00
BruebachL
1ef07309d6 Turn Card, Deck_List, Protocol, RNG, Network (Client, Server), Settings and Utility into libraries and remove cockatrice_common. (#6212)
---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2025-10-09 07:36:12 +02:00
ebbit1q
be1403c920 add hdiutil repeat script (#6231) 2025-10-08 10:10:44 +02:00
transifex-integration[bot]
03e32f0a7c Translate cockatrice/cockatrice_en@source.ts in it (#6228)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-10-08 02:18:33 +02:00
tooomm
f4361d1b43 CI: More workflow paths updates (#6227)
* update trigger paths

* change to include paths trigger pattern

* refine selection

* more paths

* fix

* revert push trigger and add hint
2025-10-08 02:17:02 +02:00
ebbit1q
e1259e67d3 update vcpkg (#6230)
* update vcpkg

* trigger build workflow on vcpkg changes
2025-10-08 01:40:52 +02:00
BruebachL
ca1b9bf75f [PrintingSelector] Sync PrintingSelector availability to OverrideAllCardArtWithPersonalPreference setting. (#6218)
* [PrintingSelector] Clearly warn users about disabling the providerId change, hide and disable the printingSelector, clear the networkCache.

Took 56 minutes

Took 4 seconds

Took 9 minutes

* Defer rollback so the rollback isn't swallowed logically.

Took 7 minutes

* Immediately enable select printing action.

Took 7 minutes

* Remove restart label.

Took 8 seconds

* Clear PixmapCache as well as NetworkCache.

Took 4 minutes

* Lint.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-07 20:59:16 +02:00
ebbit1q
3cff55b0bb add abstract player in expectance of draft players (#6210)
* add abstract player in expectance of draft players
2025-10-07 15:09:30 +02:00
tooomm
c25b153185 CI: Update trigger paths for desktop workflows (#6223)
* update trigger paths

* change to include paths trigger pattern

* refine selection
2025-10-05 16:51:06 +02:00
RickyRister
9c58e6f90f Don't use vcpkg on local macOS (#6225)
* Don't use vcpkg on local macOS

* fix typo
2025-10-05 13:31:46 +02:00
BruebachL
cff16346ef [TabGame] Fix dangling PlayerMenus in gameMenu (#6215)
* Player manager is responsible for deleting players.

Took 21 minutes

* Clean up dangling QAction* wrappers for PlayerMenus in TabGame::processPlayerLeave(Player* leavingPlayer)

Took 37 seconds

* Lint.

Took 11 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-04 01:08:04 +02:00
BruebachL
30e6b52783 [Game] Populate playerLists for menus in their aboutToShow … (#6214)
* Populate playerLists for menus in their aboutToShow so they are always current and do not rely on playerMenu manually tracking them. Also add playerActions for previous playerListActions.

Took 1 hour 35 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-03 15:21:22 +02:00
BruebachL
015570c833 For the automatic card database update process, disconnect signals, terminate and wait for it to finish on window_main destruction. (#6216)
Took 1 minute

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-03 15:03:37 +02:00
BruebachL
7c31197b78 Correctly add filterWidget as a dock widget for VDE, make printingSelector visible in the default layout for DE. (#6217)
Took 18 minutes


Took 24 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-03 14:56:19 +02:00
ebbit1q
a69bfb8cb8 add scroll bar to properties in cardinfotextwidget (#6201)
* add scroll bar to properties in cardinfotextwidget

* remove resizeevent trigger
2025-10-02 22:55:18 -04:00
BruebachL
c5b361e94d [Documentation] Doxygen regroup files and reorder group structure (#6208)
* Regroup a whole bunch of files.

Took 1 hour 38 minutes

* Reorder the structure.

Took 15 minutes

* Link some more things.

Took 18 minutes

* More links.

Took 14 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-02 16:28:08 +02:00
BruebachL
201750c89f Do not log joins on gameStateChanged (resume events) (#6207)
Took 13 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-01 15:13:56 +02:00
BruebachL
89a8d0f6b8 [Refactor] Untangle card_info.cpp and split into individual files. (#6202)
* Untangle the card_info.cpp mess and split into individual files.

Took 53 minutes

* Auto-lint was disabled and my pre-commit hook didn't fire. Oh well.

Took 3 minutes

* Fix oracle.

Took 35 seconds

* Lint!

Took 20 seconds

* Fix tests.

Took 3 minutes

* CMakeLists.txt: The reason why I have to disable auto-lint.

Took 2 minutes

* dbconverter.

Took 3 minutes

* Oracle again.

Took 3 minutes

* dbconverter again.

Took 3 minutes

* dbconverter again again.

Took 2 minutes

* More fixes.

Took 4 minutes

Took 21 seconds

* Everything needs everything.

Took 3 minutes

* Everything means everything.

Took 4 minutes

* All the tests.

Took 4 minutes

* I hate everything about this.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-01 11:49:39 +02:00
BruebachL
835e4af3e4 [Documentation] Add a doxy group for the PictureLoader. (#6204)
* Add a doxy group for the PictureLoader.

Took 26 minutes

* Linting is a fun activity for children and adults of all ages and sizes.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-01 11:45:56 +02:00
BruebachL
c33106eab4 [UI] Remove @ from playerName since only the auto complete list cares about it. (#6205)
Took 5 minutes

Took 37 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-10-01 11:31:16 +02:00
BruebachL
bea8c3dbec Group game scene correctly and replace to-do. (#6199)
Took 35 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-30 22:36:33 +02:00
dependabot[bot]
b51d5d007b Bump peaceiris/actions-gh-pages from 3 to 4 (#6197)
Bumps [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) from 3 to 4.
- [Release notes](https://github.com/peaceiris/actions-gh-pages/releases)
- [Changelog](https://github.com/peaceiris/actions-gh-pages/blob/main/CHANGELOG.md)
- [Commits](https://github.com/peaceiris/actions-gh-pages/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peaceiris/actions-gh-pages
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 12:14:01 +02:00
BruebachL
f8c4f774cf [Documentation] Sort *every* file into a doxygen group. (#6198)
* Sort *every* file into a doxygen group.

Took 7 hours 9 minutes

Took 18 seconds

Took 2 minutes

* Lint some ingroup definitions.

Took 10 minutes


Took 2 seconds

* Just include the groups in the Doxyfile in this commit.

Took 3 minutes

* Update some group comments so they link!

Took 14 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-30 12:13:32 +02:00
BruebachL
22c6756ce0 [Doxygen] Layout adjustments (#6196)
* Layout adjustments.

Took 1 hour 54 minutes

Took 2 minutes


Took 6 minutes

* Remove the empty building page.

Took 7 minutes

* Change to @page

Took 11 minutes

* Change to @page again

Took 52 seconds

* Change to @page again again

Took 2 minutes

* Fence the page declaration in CONTRIBUTING.md

Took 8 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-30 11:45:49 +02:00
ebbit1q
e318815025 nullcheck concede action (#6193) 2025-09-28 23:03:13 -04:00
BruebachL
0833f94502 [Doxygen] Add javascript to toggle call/caller graph visibility. (#6195)
Took 49 minutes

Took 26 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-28 21:22:37 +02:00
tooomm
ddbf5e1457 CI: Doxygen Docs workflow adjustments (#6194)
* Manual trigger, branch name fix, rename yml

* Update documentation-build.yml

* Run on changes to file but only deploy on tags

* Update documentation-build.yml
2025-09-28 19:58:32 +02:00
ebbit1q
2a032f3116 remove building docker images on code changes in prs (#6192) 2025-09-28 18:44:35 +02:00
BruebachL
5381562a5e [TabGame/GameEventHandler] Re-emit spectator addition signals in eventGameStateChanged (#6187)
* [TabGame/GameEventHandler] Re-emit spectator addition signals in eventGameStateChanged.

Took 36 minutes

* Check spectators as a whole.

Took 2 minutes

* Lint.

Took 42 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-28 12:39:40 +02:00
BruebachL
f2ce5e9693 [DeckEditor] Properly check if deck is blank. (#6188)
* [DeckEditor] Properly check if deck is blank.

Took 20 minutes

* Rename.

Took 17 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-28 11:56:45 +02:00
BruebachL
ed50fd98cd Add workflow to generate doxygen on tag push (#6189)
* Add workflow to generate doxygen on tag push.

Took 17 minutes

* Publish correct dir.

Took 3 minutes

* Don't include common/libs.

Took 20 minutes

* Update workflow

Took 1 hour 25 minutes

* Style Doxygen output.

Took 55 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-28 00:18:09 +02:00
BruebachL
14991e1f9e [GameScene] Refactor and doxygen (#6180)
* Clean up game scene code.

Took 18 minutes

* Doxygen.

Took 18 minutes

Took 5 seconds

Took 10 minutes

* Move some methods.

Took 6 minutes

* Restore the original warning, I guess.

Took 3 minutes

* Accidentally some methods.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-27 00:31:30 +02:00
Bruno Alexandre Rosa
5fa06746f1 build: use vcpkg for most dependencies on macos (#6170)
* build: use vcpkg for most dependencies on macos

* factor out common params

* refactor: factor out common parameters in macOS matrix

* use env vars instead of matrix

* add comment about jianmingyong/ccache-action

* Remove unused 'qt_tools' param

* Use system python

* Let ccache caches be handled by ccache-action

* Add comment about why we use install-qt-action

* set unique ccache key

* nit

* fix cache prefix

* pass gh-token

* Revert "pass gh-token"

This reverts commit cadfa253c6.

* Reapply "pass gh-token"

This reverts commit bd15e96e18.

* do not cache qt on macos
2025-09-27 00:29:13 +02:00
BruebachL
d31b044529 [Card DB] Split out database loading and querying from main class (#6175)
* Simplify add card.

Took 25 minutes

Took 8 minutes

# Commit time for manual adjustment:
# Took 16 minutes

Took 7 seconds

* Refactor out db loading from card db.

Took 39 minutes

Took 9 minutes

Took 2 minutes


Took 17 seconds

* Refactor out db queries from card db.

Took 42 minutes

* Lint.

Took 3 minutes

* I guess.

Took 7 minutes

* Tests.

Took 15 minutes

* I don't understand this.

Took 9 minutes

* fix linker errors

* Rename to querier and promote to QObject

Took 39 minutes

* Lint.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2025-09-27 00:27:15 +02:00
BruebachL
754dd904d2 [TabRoom] Re-layout game creation dialog. (#6182)
* [TabRoom] Re-layout game creation dialog.

Took 18 minutes

* Don't squish because then they overlap. Use new layout instead.

Took 8 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-27 00:23:26 +02:00
BruebachL
1503394662 [Game/DB] Have landscape cards enter tapped and not untap normally when played to table. (#6183)
Took 16 minutes


Took 12 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-27 00:22:01 +02:00
BruebachL
436d69b710 Reconnect remote player deck selection signals to game event handler. (#6181)
Took 14 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-27 00:20:55 +02:00
BruebachL
891e7bf6e4 [TabGame/PlayerManager] Handle concession properly. (#6178)
* Handle concession properly.

Took 57 minutes

Took 38 seconds

Took 18 seconds

Took 21 seconds

* Set text and enable/disable on game start/stop. (Does not fix the translation issue but at least disables the button.)

Took 51 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-26 19:58:48 +02:00
BruebachL
fad1280185 [Game] Fix game timer starting twice, not stopping and not resetting correctly. (#6177)
* Fix timer starting twice, not stopping and not resetting correctly.

Took 39 minutes

* Don't stop/start, just start.

Took 29 minutes

* Fix build.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-26 19:28:07 +02:00
RickyRister
6187c7268f [Game] Fix double concede in log (#6179) 2025-09-26 19:12:20 +02:00
BruebachL
762ea47b8e [Card DB] Various little fixes and cleanups (#6174)
* Simplify add card.

Took 25 minutes


Took 6 minutes

* Simplify guessCard.

Took 2 minutes

* Simplify loadCardDatabases.

Took 3 minutes

Took 6 seconds

* Clean up mutexes instead of manually locking/unlocking.

Took 5 minutes

* Fix null/empty check.

Took 3 minutes

* Move some stuff around inside the file.

Took 4 minutes

* Move some more things.

Took 2 minutes

* Clean up refreshCachedReverseRelatedCards.

Took 2 minutes

Took 6 seconds

* Clean up getCardFromSameSet.

Took 2 minutes

* Lint.

Took 5 minutes

* Fix compiler warning.

Took 4 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-25 19:36:46 +02:00
BruebachL
23612ba6ec [Card DB Models] Move refactor (#6172)
* Refactor CardDatabaseDisplayModel, TokenDisplayModel and TokenEditModel out of CardDatabaseModel. Move every model into an appropriate folder.

Took 54 minutes

* No folder for database models.

Took 6 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-25 16:35:29 +02:00
BruebachL
217646f031 Fix a bounds check to load the last page of cards in VDD as well. (#6169)
Took 18 minutes

Took 17 seconds

Took 14 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-23 17:05:30 +02:00
Bruno Alexandre Rosa
91667d9ecd build: update vcpkg (#6168) 2025-09-23 15:40:05 +02:00
RickyRister
3501ee9a9d Sort pb cmake and add comments to proto files (#6163)
* sort cmake

* copy over comments

* clean up comments
2025-09-22 01:57:42 +02:00
ebbit1q
f0c3860032 squash #6156 (#6161)
* move common server files

* update includes with move

* create participant, move code

* fix linker errors

* fix regressions

* mark function as override to make clang happy

* split out spectator to new file

* forgot to add to cmakelists

* autocompleter picking wrong casing for var name

* clean up forwards declarations in player

* fix includes in game
2025-09-20 14:37:12 +02:00
ebbit1q
17dcaf9afa squash #6158 (#6160)
* move message_log_widget to game

* move files

* update headers

* fix cmakelists

* oracle fixes

* split implementation out to cpp

* fix recursive import

* fix main file

* format
2025-09-20 14:35:52 +02:00
transifex-integration[bot]
f484c98152 Translate cockatrice/cockatrice_en@source.ts in de (#6157)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-09-18 22:10:29 +02:00
github-actions[bot]
46f68115b2 Update translation source strings (#6155)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-17 12:58:05 +02:00
RickyRister
7ac22a6ce8 Move cards and filters folder out of game (#6145)
* big move

* also move game_specific_terms

* fix imports

* alphabetize cmake

* fix build failure

* create database folder and move files into it

* fix includes

* run formatter
2025-09-16 12:02:57 +02:00
BruebachL
bed79ef89e Remove unused layers from cardback.svg (#6154)
Took 42 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 22:20:12 +02:00
BruebachL
54095b9a89 [Home Tab] Disable random art crop shuffle if frequency is set to 0. (#6153)
* Disable shuffle if frequency is set to 0.

Took 13 minutes


Took 26 seconds

* Set special value text.

Took 12 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 21:34:42 +02:00
BruebachL
4b58060ab6 Don't set opaque paint event. (#6151)
Took 9 minutes


Took 20 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 21:12:58 +02:00
BruebachL
dbbb554735 Only use normal layout cards as a background source. (#6152)
Took 11 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 21:12:09 +02:00
BruebachL
9c3be1b851 check for null zone during card item teardown (#6149)
* check for null zone during card item teardown

Took 1 hour 32 minutes


Took 24 seconds

* Also check for it in the successful branch.

Took 6 minutes

* Comment.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 20:06:20 +02:00
BruebachL
190ab211e3 More player leaving fixes (#6148)
* Remove unnecessary parentheses.

Took 16 minutes

* Reorder player-left signals

Took 11 seconds

* Connect PlayerManager::playerRemoved signal to TabGame::processPlayerLeave.

Took 21 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 20:03:00 +02:00
RickyRister
f4fbe90a72 Fix segfault when creating token of opponent's card (#6144)
* Fix segfault when creating token of opponent's card

* clean up code
2025-09-15 12:18:51 +02:00
RickyRister
a9cbd5a172 Make TabHome a managed tab (#6147)
* Make TabHome a managed tab

* Add shortcut
2025-09-15 12:07:57 +02:00
RickyRister
94ba1c83c6 Removed unused fields in GameMetaInfo and GameState (#6142)
* Removed unused fields in GameMetaInfo and GameState

* revert parent to AbstractGame

* init activePlayer to -1
2025-09-15 10:22:11 +02:00
BruebachL
9b3756e591 Don't setText() in paintEvent() which causes infinite recursion on MacOs. Use a styleOption to paint base class directly instead. (#6146)
Took 18 minutes

Took 5 seconds


Took 19 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 10:19:12 +02:00
BruebachL
aff775f488 Don't get local ID from playerInfo on concession (#6143)
* Don't get local ID from playerInfo.

Took 39 minutes


Took 39 seconds

* Introduce isLocalPlayer convenience method.

Took 21 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-15 01:09:30 +02:00
BruebachL
4de5274996 Check if card has no PT set yet if dropped on table. (#6137)
* Check if card has no PT set yet if dropped on table.

Took 22 minutes

* Use isEmpty() for comparison.

Took 6 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-14 12:38:17 +02:00
dependabot[bot]
4e57868037 Bump actions/setup-node from 4 to 5 (#6111)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-14 10:50:33 +02:00
BruebachL
ab6b32b8ba Respect default deck editor choice (#6136)
* Respect default deck editor choice.

Took 5 minutes

* Don't force open default deck editor on startup.

Took 1 minute

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 22:03:10 +02:00
BruebachL
46285a499e Add a style for disabled buttons (#6134)
* Disable view card database button until card db is loaded, add a new style for disabled buttons.

Took 4 minutes

Took 21 seconds

* Lint.

Took 8 minutes

* Rename variables, don't disable DB button anymore.

Took 4 minutes

Took 4 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 22:01:49 +02:00
BruebachL
ce6cad5dfe Use a scope disconnect. (#6135)
Took 7 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 16:35:59 +02:00
BruebachL
d5ea86bc81 Guard against not-loaded database by delaying initialization. (#6133)
Took 20 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 15:00:30 +02:00
BruebachL
41ea424359 table_zone_logic now sets the PT of the cardItem to the cardInfos PT, which ensures consistency of this functionality when the card is added from a hidden zone. (#6129)
Took 27 minutes


Took 43 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 13:37:04 +02:00
BruebachL
87b0259b97 Properly delete enlargedPixmapWidget (#6131)
* Properly delete enlargedPixmapWidget.

Took 23 minutes

Took 13 seconds


Took 16 seconds

* Connect to QObject instead of emitting own signal.

Took 12 minutes

Took 7 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 13:35:59 +02:00
BruebachL
2490e97ea0 Switch to replay tab if already open. (#6130)
Took 11 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 12:44:46 +02:00
BruebachL
eecfe9d387 Forward playerActions signals to playerEventHandler (#6126)
Took 54 minutes

Took 7 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 12:44:09 +02:00
BruebachL
9ca5ee52e7 Connect signals correctly. (#6124)
Took 58 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-13 03:31:05 +02:00
BruebachL
fb23cc8c7a Refactor player menus into helper classes (#6121)
* Refactor player menus into helper classes.

Took 2 hours 6 minutes


Took 11 minutes

* Lint.

Took 6 minutes

Took 22 seconds

* Refactor card and move menu.

Took 1 hour 6 minutes

Took 36 seconds

Took 52 seconds

* Set active shortcuts, move player info stuff to card menu.

Took 25 minutes


Took 18 seconds

* Refactor say and utility menu.

Took 54 minutes

Took 2 minutes

Took 5 minutes

Took 11 minutes

* Rename folder.

Took 24 minutes

Took 6 minutes

* Refactor sideboard menu.

Took 26 minutes

* Remove unused variable in constructor.

Took 42 seconds

* Lint.

Took 11 minutes

* Nullptr check

Took 8 minutes

* Use localOrJudge check

Took 6 minutes

* Fix the build.

Took 7 minutes

Took 35 seconds

* PlayerList things.

Took 16 minutes

* Retranslate and set shortcuts for everything.

Took 10 minutes

* Correctly nullptr out sayMenu if not local

Took 3 minutes

* Don't check playerInfo in sbMenu shortcutsActive

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-12 13:52:05 -04:00
RickyRister
ff7ce39841 Fix hand visible (#6122) 2025-09-12 09:46:38 -04:00
RickyRister
0f05d6bd74 Move doc comments to correct place (#6123)
* Move docs for ViewZone

* Update docs

* move docs for updateCardMenu

* Fix link in doc

* format
2025-09-12 09:46:18 -04:00
BruebachL
93c15c8151 Home tab to replace generic deck editor on startup (#6114) 2025-09-11 15:36:34 -04:00
Zach H
22c8268f02 Require min 3.10 for external deps (#6119) 2025-09-11 14:50:31 -04:00
ebbit1q
216cd491cc fix #6115 (#6118)
* fix #6115

* rename all instances of player when relevant
2025-09-11 12:00:49 -04:00
BruebachL
5efc573783 Also nullptr incrementAllCardsCounter (#6117)
Took 34 minutes


Took 41 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-11 11:39:56 -04:00
RickyRister
bca0da6bd4 Fix segfault when drawing arrow (#6116) 2025-09-11 09:49:54 -04:00
BruebachL
9601a1fa4e Player refactor (#6112)
* Player refactor.

Took 1 hour 43 minutes

Took 1 minute


Took 23 seconds

* Tiny lint.

Took 3 minutes

* Hook up tap logic again.

Took 13 minutes

* Fix an include.

Took 3 minutes

* Stuff.

Took 6 minutes

* Fix typo.

Took 7 minutes

* Include.

Took 1 minute

* Reorganize method/variable definitions, remove unused ones.

Took 1 hour 8 minutes


Took 24 seconds

* Clean up some unused imports.

Took 6 minutes

* Player holds the deck, emits deckChanged(), other elements player->getDeck() to respond to changes.

Took 37 minutes

* Connect player->openDeckEditor signal directly in the player constructor

Took 6 minutes

* Emit openDeckEditor signal in player_actions again.

Took 3 minutes

* Do to-do's

Took 3 hours 32 minutes

* Lint.

Took 3 minutes

* Lint again.

Took 2 minutes

* Fix include.

Took 32 minutes

* The stack should ensure card visibility.

Took 21 minutes

* Fine, the game can remember the tab.

Took 10 minutes

Took 21 seconds

Took 9 seconds

* zoneId is a dynamic gameplay property and thus belongs in player.cpp

Took 11 minutes

Took 19 seconds

* Signal view removal, addition.

Took 5 minutes

* Ensure all players are considered local in local game.

Took 10 minutes

* ENSURE they are.

Took 8 minutes

* Bounds check data sent by QAction()

Took 54 minutes

* Move comment.

Took 20 seconds

* Reimplement logging category for game_event_handler.cpp, remove linebreaks.

Took 36 seconds

* PlayerGraphicsItem is responsible for retranslateUi, not Player.


Took 14 seconds

* Set menu for sideboard again, translate some menu titles, reimplement actIncPT action

Took 54 seconds

* Comment spacing.

Took 43 seconds

* Change message_log_widget.cpp slots to take CardZoneLogic parameters as emitted by PlayerEventHandler.

Took 7 minutes

Took 14 seconds

* Remove unused player_logger.cpp

Took 2 minutes

* Query local game state correctly from tab_supervisor again

Took 3 minutes

* Revert Deck legality checker.

Took 3 minutes

* Instantiate menu before graphics item.

Took 1 hour 5 minutes

Took 55 minutes

* Differentiate games and replays.


Took 9 seconds

* Lint.

Took 10 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-10 18:49:33 -04:00
BruebachL
b8e545bfa4 Move game state and event handling out of tab_game and into separate classes (#6090)
* Move game state and event handling out of tab_game and into separate classes.

Took 6 hours 38 minutes

Took 23 seconds

* Meta Info

Took 14 hours 36 minutes

* Properly respond to game started again.

Took 49 minutes

* Hook up the message log widgets to game events again.

Took 33 minutes

Took 7 seconds

* Lint.

Took 4 minutes

* Hook up playerListWidget.

Took 1 hour 2 minutes

Took 10 seconds

* Hook up playerListWidget properly.

Took 1 hour 17 minutes

* Fix regressions.

Took 17 minutes

Took 9 seconds

* Log the local player joining too.

Took 2 minutes

* Connect some player signals unrelated to this refactor again.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-09-10 18:40:29 -04:00
ebbit1q
5c16f0d027 update the address of the fsf in the license (#6113)
the fsf closed its office in august this year, it's now remote only and
the address has been replaced with its url, the license text can be
found here: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
2025-09-10 18:18:34 -04:00
ebbit1q
1b4441baac add libqt6sql6-mysql to debian package recommends (#6110) 2025-09-08 09:59:18 -04:00
ebbit1q
0147a1d41f disallow users on your ignore list to get your current games (#6109) 2025-09-07 11:58:52 -04:00
BruebachL
0f11fbe599 Use pinned printing when adding to deck. (#6108) 2025-09-07 00:26:39 -04:00
BruebachL
9c18e99fe2 Correctly reset banner card if none was set but card appears in new deck. (#6107) 2025-09-07 00:26:07 -04:00
BruebachL
6e0a7de9cc Move quick filters from right to left to be closer to the color filters. (#6106) 2025-09-06 18:11:43 -04:00
BruebachL
b141a65838 Refactor page loading so it's uniform, display every printing from every filtered set instead of just one. (#6105) 2025-09-06 18:11:36 -04:00
BruebachL
7f842bb1e8 Sort VDE groups internally (#6102) 2025-09-06 09:06:59 -04:00
RickyRister
bd65aae81e Fix Logs tab close button not working (#6104) 2025-09-06 11:04:50 +02:00
BruebachL
b8dedb568c Use modelReset from new grouping in deck_list_model in VDE (#6100) 2025-09-05 11:12:30 -04:00
dependabot[bot]
ec94c29ed9 Bump actions/attest-build-provenance from 2 to 3 (#6095)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2 to 3.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 22:54:35 -04:00
github-actions[bot]
c77943d01c Update translation files (#6094)
Co-authored-by: github-actions <github-actions@github.com>
2025-09-04 22:54:27 -04:00
BruebachL
fc5fb956df Properly allow resizing the printing selector (#6097)
* Properly allow resizing the printing selector.
* Don't restrict central widget sizing in regular deck editor, don't size hidden widget in visual deck editor.
* Reset layout in VDE properly and introduce new default layout.
* Gives hover signal correct parameters (fix for exact card refactor issue)
2025-09-04 22:54:07 -04:00
BruebachL
2eba126ed7 Modularize and Doxygen decklist.cpp (#6099) 2025-09-04 22:52:46 -04:00
dependabot[bot]
da52d677c7 Bump actions/checkout from 4 to 5 (#6084) 2025-08-31 09:06:26 +02:00
RickyRister
ab4373d025 Implement replay sharing (#6066)
* new protos

* implement commands on server

* add buttons

* icons

* run formatter

* Message on get replay code failure

* Add new commands to switch statement

* Better failure messages

* Fix permission check query

* Change hash method

* Prevent adding duplicate replays

* Clean up TabReplay ui

* Copy over replay name

* base64 encode the hash

* Shorten hash

* Better failure messages

* change icon back to search icon

* check hash before checking if user already has access

* update share icon

* Update label text
2025-08-24 22:40:44 -04:00
Paul Carroll
5e88a0f0cc Fix multi-word type matching in card filters (#6060)
* Fix multi-word type matching in card filters

Add phrase matching to StringValue before word-based fallback.
Enables searches like t:"time lord" for multi-word creature types.

* Use existing typedef

* Don't inline lambda

* update filter func

* Update card type FilterString unit tests

* refactor string matcher

* update card db test

* fix sets count in test

* Add regex cache in string matcher

* Update cockatrice/src/game/filters/filter_string.cpp

* Revert "Add regex cache in string matcher"

---------

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-08-24 12:37:25 -04:00
BruebachL
ba794c2b60 Add a guard in case the printing info is empty for a related card. (#6087)
Took 4 hours 19 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-08-23 23:34:41 -04:00
SlightlyCircuitous
268559d8de Add Debian 13 'Trixie' build (#6068)
* Create Dockerfile

* Add Debian 13 to release template

* Add Debian 13 to desktop-build

* Add ca-certificates package to build

attempting to fix SSL issues
2025-08-23 23:34:07 -04:00
transifex-integration[bot]
473d147333 Translate cockatrice/cockatrice_en@source.ts in it (#6083)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-08-22 23:26:59 -04:00
RickyRister
d5d9f9bedc Refactor: Remove unused fields in InnerDecklistNode (#6086) 2025-08-22 23:26:16 -04:00
RickyRister
f31d30bf84 [PictureLoader] Remove manual multithreading (#6078) 2025-08-16 20:38:49 -04:00
transifex-integration[bot]
03b216a6b4 Translate cockatrice/cockatrice_en@source.ts in it (#6081)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-08-16 20:36:12 -04:00
BruebachL
3e6510b935 Buffer decklists to display until player processing is done instead of reordering player creation. (#6080)
Took 28 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-08-16 20:35:57 -04:00
transifex-integration[bot]
e87b35e0bb Updates for project Cockatrice and language fr (#6079)
* Translate oracle/oracle_en@source.ts in fr

100% translated source file: 'oracle/oracle_en@source.ts'
on 'fr'.

* Translate cockatrice/cockatrice_en@source.ts in fr

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'fr'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-08-16 14:27:37 -04:00
RickyRister
322fdb14de Fix segfault when selecting card in replay (#6077) 2025-08-16 14:27:28 -04:00
BruebachL
09381575a7 Add option to share decklists on load. (#6029)
* Add option to share decklists on load.

Took 1 hour 58 minutes

Took 9 minutes


Took 39 minutes

* Lint.

Took 14 minutes


Took 2 minutes

* Stuffs

Took 39 minutes

Took 4 seconds

Took 43 minutes

* Process local player first.

Took 45 minutes

* Consider if the setting is set on the game info first.

Took 4 minutes

* Save an indent level.

Took 43 seconds

* Don't commit logging config.

Took 3 minutes

* Remove a debug print.

Took 10 seconds


Took 7 seconds

* Add another optional guard.

Took 5 minutes

* Hide the tab bar if only one (own deck) is visible.

Took 9 minutes

* Rename setting label for clarity

Took 2 minutes

* Capitalization.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-08-15 17:31:05 -04:00
RickyRister
881243da6a Refactor TabReplay creation (#6064) 2025-08-15 01:15:02 -04:00
transifex-integration[bot]
851fad3e3f Updates for project Cockatrice and language it (#6065)
* Translate oracle/oracle_en@source.ts in it

100% translated source file: 'oracle/oracle_en@source.ts'
on 'it'.

* Translate cockatrice/cockatrice_en@source.ts in it

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-08-15 01:14:05 -04:00
RickyRister
46d65f0b7e Refactor: rename and consolidate getSpectator (#6067) 2025-08-15 01:13:53 -04:00
RickyRister
03bebbe4c2 Rework card menu handling (#6069)
* extract cardMenu from CardItem

* move cardMenu saving to TabGame

* delete TabGame::updateCardMenu

* move checking to updateCardMenu

* unset activeCard when all cards are unselected
2025-08-15 01:13:28 -04:00
RickyRister
1649f30389 [PictureLoader] Use thread pool instead of creating new thread (#6072)
* delete threads

* Run reply processing in thread pool
2025-08-15 01:11:39 -04:00
BruebachL
38f76d449a Fix providerId cards getting removed from deckList by convenience replacement function (#6074)
Took 2 hours 25 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-08-15 01:11:02 -04:00
BruebachL
f2cbdae829 Allow tokens to consider providerIds. (#6075)
Tokens created through Ctrl + T use the pinned printing, if available.
Tokens created through a related card menu use a token from the same set, if available.

Took 2 hours 25 minutes

Took 10 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-08-15 01:10:36 -04:00
RickyRister
3a42354efd Refactor: Move files in src/client/game_logic (#6070)
* move abstract_client to src/server

* move key_signals to src/utility
2025-08-07 16:47:48 +02:00
Zach H
fe7853a389 Fix a crash case for aIncrementAllCardCounters access (#6063) 2025-08-02 16:43:24 -04:00
Paul Carroll
06738cae93 Add menu option and hotkey to sort hand (#6057)
* Add sort hand shortcut

* add function to sort hand by type and name

* rig up the sort hand to the player

* fix sorting param

* use getShortcut instead of getSingleShortcut

* use correct method

* change default sorting

---------

Co-authored-by: Zach H <zahalpern+github@gmail.com>
2025-08-02 00:40:17 -04:00
Paul Carroll
d6243a2dd2 Add menu option and shortcut to auto increment counters (#6055)
* Add method to increment all counters on cards on table

* add keyboard shortcut

* register action for menu

* register action for menu

* Change menu text

* Move to Counters submenu

* Change function name

* Change menu, update function to handle selected vs non selected cards

* Use getShortcut instead of getSingleShortcut

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: Paul Carroll <paul.x.carroll@questdiagnostics.com>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
2025-08-02 00:25:36 -04:00
RickyRister
04be0fe634 Refactor: Simplify closeRequest and remove closed signal (#6062)
* Refactor: simplify closeRequest and remove closed signal

* clean up closeRequest usages
2025-08-01 22:45:54 -04:00
RickyRister
fd12a1f6be Fix certain game actions only recognizing first shortcut (#6059)
* doc

* Fix non-counter shortcuts

* Fix counter shortcuts
2025-08-01 22:45:26 -04:00
RickyRister
e10dd4ef42 Refactor: Don't call stop in TabSupervisor dtor (#6061) 2025-08-01 08:53:06 -04:00
RickyRister
62c02e3fce Also clear redirect cache when clearing network cache (#6052) 2025-07-29 09:35:09 -04:00
RickyRister
ae2c55c33b Refactor: use ExactCard to represent specific printings (#6049)
* Create new class

* Update CardInfo and CardDatabase

* Use new class instead of CardInfoPtr

* fix cmake
2025-07-28 21:04:45 -04:00
RickyRister
4a2a646943 Fix server crash from out-of-bounds index when players is empty (#6048) 2025-07-19 00:31:50 -04:00
RickyRister
ae47ee802b Refactor: Add PrintingInfo::getUuid (#6046) 2025-07-17 09:06:34 -04:00
RickyRister
4fd2f1f974 Fix turning cards face-up not having correct printing (#6043) 2025-07-16 13:03:05 +02:00
BruebachL
b9f16e8cce Refactor replay code in TabGame to replayManager (#6026)
* Refactor replay code to replayManager

* Lint.

* Refresh shortcuts.

Took 13 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
2025-07-16 06:33:48 -04:00
RickyRister
70b4843bc4 Refactor: clean up CardDatabase pt2 (#6042)
* findPrintingWithId

* remove a param

* cleanup up usage of getCardInfo
2025-07-16 06:33:38 -04:00
RickyRister
95190c321c Refactor: remove second param from CardZone::getCard (#6041) 2025-07-16 05:54:31 -04:00
RickyRister
a9b3be33e0 Refactor: Represent cardName + providerId with CardRef struct (#6039)
* card_ref.h

* update CardDatabase signatures

* make everything compile

* rename methods

* add docs

* mark stuff const

* set cardRef in CardItem

* cleanup

* fix build failure

* Fix builds on mac

---------

Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2025-07-15 22:14:02 -04:00
BruebachL
e05dad4267 Add the option to load decklists from Archidekt, Deckstats, Moxfield, TappedOut in deck editor and lobby (#6030)
* Add the option to load decklists from Archidekt, Deckstats, Moxfield, TappedOut in deck editor and lobby.

Took 3 hours 34 minutes

Took 9 seconds


Took 12 seconds

* Properly set quantities.

Took 11 minutes

* Warnings.

Took 5 minutes

* Static regexes.

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

* Category loggings and better warnings.

Took 18 minutes


Took 42 seconds

* use loadFromStream_Plain instead of manually adding CardNodes to the DeckList.

Took 30 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2025-07-14 23:12:25 -04:00
github-actions[bot]
83b90d472f Update translation files (#6037)
Co-authored-by: github-actions <github-actions@github.com>
2025-07-14 23:07:14 -04:00
BruebachL
ee4ff6e732 Allow more naming schemes for custom pictures (#6021)
* Allow more naming schemes for custom pictures.

Order is cardName_providerId, cardName_setName_collectorNumber, setName-collectorNumber-cardName and then just generically cardName, if the user has decided to override every printing. Most-to-least specific.


Took 2 minutes

Took 8 seconds

* Fixups.

Took 2 minutes

* Even more naming schemes.

Took 6 minutes

* Finally yeet the bug warning in PrintingSelector

Took 3 minutes

* Adjust to PrintingInfo change.

Took 5 minutes

* Don't use suffixes.

Took 7 minutes

Took 9 seconds

* Comments.

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-07-14 23:07:03 -04:00
RickyRister
2267d38352 [Refactor] Clean up CardDatabase (#6034)
* Inline getCardFromMap

map.value already returns a default-constructed value if the key is not present

* Use for-each instead of iterator

* Add new method

* clean up method order

* fix build failure

* clean up getPreferredPrinting usage

* early returns
2025-07-12 23:29:02 -04:00
transifex-integration[bot]
4fbb47300e Translate cockatrice/cockatrice_en@source.ts in de (#6028)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-07-09 17:55:54 -04:00
BruebachL
836e168a6c Properly and consistently capitalize EDHRec (#6027)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-07-09 17:55:04 -04:00
RickyRister
a9684f67cc Refactor: rename CardInfoPerSet to PrintingInfo (#6024)
* remove unnecessary consts

* removed unused

* rename class

* rename variables and methods

* rename again

* rename variables again

* rename field

* run formatter
2025-07-07 23:41:19 -04:00
RickyRister
686e90d0ed [PictureLoader] Fix status bar not updating (#6023)
* [PictureLoader] Fix status bar not updating

* rename methods
2025-07-05 23:14:46 -04:00
RickyRister
0b9b39fef7 [PictureLoader] Reduce downtime between load attempts (#6020)
* [PictureLoader] Reduce downtime between load attempts

* rename some stuff

* better comments

* Fix segfault from status bar

Pass just the relevant data through the signals to the status bar, instead of passing the entire Work object.
That way the data is detached from the Work object and we won't segfault when Work self-deletes before status bar tries to use that data.

* Rename method
2025-07-05 22:42:54 -04:00
transifex-integration[bot]
67a3b03b07 Translate oracle/oracle_en@source.ts in de (#6022)
100% translated source file: 'oracle/oracle_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-07-05 11:56:02 -04:00
RickyRister
388db4e995 [PictureLoader] Properly run reply processing on Work's thread (#6016)
* [PictureLoader] Properly run reply processing on Work's thread

* emit cachedImageHit first

* Remove unused fields

* Remove unused fields

* Fix double free requests from cache hit

If we hit a cached url, the request already gets to skip the queue.
By sending another free request once the cached request finishes, we're actually sending two free requests on each cache hit.
2025-07-04 23:01:25 -04:00
RickyRister
a28a1aa601 Fix segfault when filtering card view that have blank cards (#6017) 2025-07-04 10:18:07 -04:00
RickyRister
ed82106359 [PictureLoader] Fix double-queueing bug (#6014) 2025-07-02 23:08:31 -04:00
github-actions[bot]
c57b84cb17 Update translation source strings (#6012)
Co-authored-by: github-actions <github-actions@github.com>
2025-07-01 15:29:57 -04:00
BruebachL
2dfe9fcf45 Add as set instance on subsequent loads as well. (#6013)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-07-01 15:29:48 -04:00
1140 changed files with 152071 additions and 83183 deletions

29
.ci/Debian13/Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM debian:13
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libgl-dev \
liblzma-dev \
libmariadb-dev-compat \
libprotobuf-dev \
libqt6multimedia6 \
libqt6sql6-mysql \
ninja-build \
protobuf-compiler \
qt6-image-formats-plugins \
qt6-l10n-tools \
qt6-multimedia-dev \
qt6-svg-dev \
qt6-tools-dev \
qt6-tools-dev-tools \
qt6-websockets-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -1,4 +1,4 @@
FROM fedora:41
FROM fedora:43
RUN dnf install -y \
ccache \

View File

@@ -0,0 +1,21 @@
FROM debian:11
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libmariadb-dev-compat \
libprotobuf-dev \
libqt5sql5-mysql \
libqt5websockets5-dev \
ninja-build \
protobuf-compiler \
qttools5-dev \
qttools5-dev-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -11,7 +11,8 @@
# --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache [<size>] uses ccache and shows stats, optionally provide size
# --dir <dir> sets the name of the build dir, default is "build"
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR
# --target-macos-version <version> sets the min os version - only used for macOS builds
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --ccache <ccache_size> --dir <dir>)
# exitcode: 1 for failure, 3 for invalid arguments
@@ -46,6 +47,10 @@ while [[ $# != 0 ]]; do
MAKE_SERVER=1
shift
;;
'--no-client')
MAKE_NO_CLIENT=1
shift
;;
'--test')
MAKE_TEST=1
shift
@@ -66,6 +71,10 @@ while [[ $# != 0 ]]; do
shift
fi
;;
'--vcpkg')
USE_VCPKG=1
shift
;;
'--dir')
shift
if [[ $# == 0 ]]; then
@@ -75,6 +84,15 @@ while [[ $# != 0 ]]; do
BUILD_DIR="$1"
shift
;;
'--target-macos-version')
shift
if [[ $# == 0 ]]; then
echo "::error file=$0::--target-macos-version expects an argument"
exit 3
fi
TARGET_MACOS_VERSION="$1"
shift
;;
*)
echo "::error file=$0::unrecognized option: $1"
exit 3
@@ -95,11 +113,17 @@ fi
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# Set minimum CMake Version
export CMAKE_POLICY_VERSION_MINIMUM=3.10
# Add cmake flags
flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1")
fi
if [[ $MAKE_NO_CLIENT ]]; then
flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0" "-DWITH_DBCONVERTER=0")
fi
if [[ $MAKE_TEST ]]; then
flags+=("-DTEST=1")
fi
@@ -113,6 +137,9 @@ fi
if [[ $PACKAGE_TYPE ]]; then
flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE")
fi
if [[ $USE_VCPKG ]]; then
flags+=("-DUSE_VCPKG=1")
fi
# Add cmake --build flags
buildflags=(--config "$BUILDTYPE")
@@ -129,9 +156,35 @@ function ccachestatsverbose() {
# Compile
if [[ $RUNNER_OS == macOS ]]; then
if [[ $TARGET_MACOS_VERSION ]]; then
# CMAKE_OSX_DEPLOYMENT_TARGET is a vanilla cmake flag needed to compile to target macOS version
flags+=("-DCMAKE_OSX_DEPLOYMENT_TARGET=$TARGET_MACOS_VERSION")
# vcpkg dependencies need a vcpkg triplet file to compile to the target macOS version
# an easy way is to copy the x64-osx.cmake file and modify it
triplets_dir="/tmp/cmake/triplets"
triplet_version="custom-triplet"
triplet_file="$triplets_dir/$triplet_version.cmake"
arch=$(uname -m)
if [[ $arch == x86_64 ]]; then
arch="x64"
fi
mkdir -p "$triplets_dir"
cp "../vcpkg/triplets/$arch-osx.cmake" "$triplet_file"
echo "set(VCPKG_CMAKE_SYSTEM_VERSION $TARGET_MACOS_VERSION)" >>"$triplet_file"
echo "set(VCPKG_OSX_DEPLOYMENT_TARGET $TARGET_MACOS_VERSION)" >>"$triplet_file"
flags+=("-DVCPKG_OVERLAY_TRIPLETS=$triplets_dir")
flags+=("-DVCPKG_HOST_TRIPLET=$triplet_version")
flags+=("-DVCPKG_TARGET_TRIPLET=$triplet_version")
echo "::group::Generated triplet $triplet_file"
cat "$triplet_file"
echo "::endgroup::"
fi
echo "::group::Signing Certificate"
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]; then
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
echo "$MACOS_CERTIFICATE" | base64 --decode >"certificate.p12"
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain
security set-keychain-settings -t 3600 -l build.keychain
@@ -143,6 +196,29 @@ if [[ $RUNNER_OS == macOS ]]; then
echo "No signing certificate configured. Skipping set up of keychain in macOS environment."
fi
echo "::endgroup::"
if [[ $MAKE_PACKAGE ]]; then
# Workaround https://github.com/actions/runner-images/issues/7522
# have hdiutil repeat the command 10 times in hope of success
hdiutil_script="/tmp/hdiutil.sh"
# shellcheck disable=SC2016
echo '#!/bin/bash
i=0
while ! hdiutil "$@"; do
if (( ++i >= 10 )); then
echo "Error: hdiutil failed $i times!" >&2
break
fi
sleep 1
done' >"$hdiutil_script"
chmod +x "$hdiutil_script"
flags+=(-DCPACK_COMMAND_HDIUTIL="$hdiutil_script")
fi
elif [[ $RUNNER_OS == Windows ]]; then
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
buildflags+=(-- -p:UseMultiToolTask=true -p:EnableClServerMode=true)
fi
if [[ $USE_CCACHE ]]; then
@@ -153,17 +229,13 @@ fi
echo "::group::Configure cmake"
cmake --version
echo "Running cmake with flags: ${flags[*]}"
cmake .. "${flags[@]}"
echo "::endgroup::"
echo "::group::Build project"
if [[ $RUNNER_OS == Windows ]]; then
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
cmake --build . "${buildflags[@]}" -- -p:UseMultiToolTask=true -p:EnableClServerMode=true
else
cmake --build . "${buildflags[@]}"
fi
echo "Running cmake --build with flags: ${buildflags[*]}"
cmake --build . "${buildflags[@]}"
echo "::endgroup::"
if [[ $USE_CCACHE ]]; then
@@ -172,6 +244,19 @@ if [[ $USE_CCACHE ]]; then
echo "::endgroup::"
fi
if [[ $RUNNER_OS == macOS ]]; then
echo "::group::Inspect Mach-O binaries"
for app in cockatrice oracle servatrice dbconverter; do
binary="$GITHUB_WORKSPACE/build/$app/$app.app/Contents/MacOS/$app"
echo "Inspecting $app..."
vtool -show-build "$binary"
file "$binary"
lipo -info "$binary"
echo ""
done
echo "::endgroup::"
fi
if [[ $MAKE_TEST ]]; then
echo "::group::Run tests"
ctest -C "$BUILDTYPE" --output-on-failure
@@ -186,12 +271,6 @@ fi
if [[ $MAKE_PACKAGE ]]; then
echo "::group::Create package"
if [[ $RUNNER_OS == macOS ]]; then
# Workaround https://github.com/actions/runner-images/issues/7522
echo "killing XProtectBehaviorService"; sudo pkill -9 XProtect >/dev/null || true;
echo "waiting for XProtectBehaviorService kill"; while pgrep "XProtect"; do sleep 3; done;
fi
cmake --build . --target package --config "$BUILDTYPE"
echo "::endgroup::"

View File

@@ -137,10 +137,11 @@ if [[ $SAVE ]]; then
fi
# Set compile function, runs the compile script on the image, passes arguments to the script
# shellcheck disable=2120
function RUN ()
{
echo "running image:"
if [[ $(docker images) =~ "$IMAGE_NAME" ]]; then
if [[ $(docker images) =~ $IMAGE_NAME ]]; then
local args=(--mount "type=bind,source=$PWD,target=/src")
args+=(--workdir "/src")
args+=(--user "$(id -u):$(id -g)")
@@ -151,6 +152,7 @@ function RUN ()
if [[ -n "$CMAKE_GENERATOR" ]]; then
args+=(--env "CMAKE_GENERATOR=$CMAKE_GENERATOR")
fi
# shellcheck disable=2086
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
return $?
else
@@ -164,5 +166,6 @@ function RUN ()
if [[ $INTERACTIVE ]]; then
export BUILD_SCRIPT="-i"
export RUN_ARGS="$RUN_ARGS -it"
# shellcheck disable=2119
RUN
fi

View File

@@ -11,11 +11,19 @@ if ! git merge-base origin/master HEAD; then
fi
# Check formatting using format.sh
echo "Checking your code using clang-format/cmake-format..."
echo "Checking your code using format.sh..."
diff="$(./format.sh --diff --cmake --cf-version --branch origin/master)"
diff="$(./format.sh --diff --cmake --shell --print-version --branch origin/master)"
err=$?
sep="
----------
"
used_version="${diff%%"$sep"*}"
diff="${diff#*"$sep"}"
changes_to_make="${diff%%"$sep"*}"
files_to_edit="${diff#*"$sep"}"
case $err in
1)
cat <<EOM
@@ -33,14 +41,13 @@ case $err in
***********************************************************
Used version:
${diff%%
----------
*}
$used_version
Affected files:
$files_to_edit
The following changes should be made:
${diff#*
----------
}
$changes_to_make
Exiting...
EOM
@@ -58,6 +65,9 @@ EOM
*** ***
***********************************************************
Used version:
$used_version
Exiting...
EOM
exit 0

View File

@@ -20,10 +20,11 @@ Available pre-compiled binaries for installation:
<b>Linux</b>
• <kbd>Ubuntu 24.04 LTS</kbd> <sub><i>Noble Numbat</i></sub>
• <kbd>Ubuntu 22.04 LTS</kbd> <sub><i>Jammy Jellyfish</i></sub>
• <kbd>Debian 13</kbd> <sub><i>Trixie</i></sub>
• <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub>
• <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
• <kbd>Fedora 43</kbd>
• <kbd>Fedora 42</kbd>
• <kbd>Fedora 41</kbd>
<sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub>
<sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub>

View File

@@ -25,6 +25,9 @@ IndentCaseLabels: true
PointerAlignment: Right
SortIncludes: true
IncludeBlocks: Regroup
StatementAttributeLikeMacros: [emit]
# requires clang-format 16
# RemoveSemicolon: true
---
Language: Proto
AllowShortFunctionsOnASingleLine: None

View File

@@ -1,3 +1,5 @@
<!--! @page contributing Contributing -->
&nbsp; [Introduction](#contributing-to-cockatrice) | [Code Style Guide](
#code-style-guide) | [Translations](#translations) | [Release Management](
#release-management)

View File

@@ -4,25 +4,39 @@ permissions:
contents: write
id-token: write
attestations: write
actions: write # needed for ccache action to be able to delete gha caches
on:
push:
branches:
- master
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
tags:
- '*'
pull_request:
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
concurrency:
@@ -56,7 +70,7 @@ jobs:
- name: Checkout
if: steps.configure.outputs.tag != null
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -100,19 +114,29 @@ jobs:
- distro: Debian
version: 11
package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Servatrice_Debian
version: 11
package: DEB
test: skip
server_only: yes
- distro: Debian
version: 12
package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Debian
version: 13
package: DEB
- distro: Fedora
version: 41
version: 42
package: RPM
test: skip # Running tests on all distros is superfluous
- distro: Fedora
version: 42
version: 43
package: RPM
- distro: Ubuntu
@@ -138,7 +162,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Restore compiler cache (ccache)
id: ccache_restore
@@ -172,10 +196,11 @@ jobs:
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
package: '${{matrix.package}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
NO_CLIENT: ${{matrix.server_only == 'yes' && '--no-client' || '' }}
run: |
source .ci/docker.sh
RUN --server --release --package "$package" --dir "$BUILD_DIR" \
--ccache "$CCACHE_SIZE"
--ccache "$CCACHE_SIZE" $NO_CLIENT
.ci/name_build.sh
- name: Save compiler cache (ccache)
@@ -188,7 +213,7 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.package != 'skip'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{matrix.distro}}${{matrix.version}}-package
path: ${{steps.build.outputs.path}}
@@ -208,7 +233,7 @@ jobs:
- name: Attest binary provenance
id: attestation
if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{steps.build.outputs.name}}
subject-digest: sha256:${{ steps.upload_artifact.outputs.artifact-digest }}
@@ -220,96 +245,166 @@ jobs:
GH_TOKEN: ${{github.token}}
run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice
build-macos:
build-vcpkg:
strategy:
fail-fast: false
matrix:
include:
- target: 13
- os: macOS
target: 13
runner: macos-15-intel
soc: Intel
os: macos-13
xcode: "14.3.1"
xcode: "16.4"
type: Release
override_target: 13
make_package: 1
package_suffix: "-macOS13_Intel"
artifact_name: macOS13_Intel-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false # qt caches take too much space for macOS (1.1Gi)
cmake_generator: Ninja
use_ccache: 1
- target: 14
- os: macOS
target: 14
runner: macos-14
soc: Apple
os: macos-14
xcode: "15.4"
type: Release
make_package: 1
package_suffix: "-macOS14"
artifact_name: macOS14-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
- target: 15
- os: macOS
target: 15
runner: macos-15
soc: Apple
os: macos-15
xcode: "16.2"
xcode: "16.4"
type: Release
make_package: 1
package_suffix: "-macOS15"
artifact_name: macOS15-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
- target: 15
- os: macOS
target: 15
runner: macos-15
soc: Apple
os: macos-15
xcode: "16.2"
xcode: "16.4"
type: Debug
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
- os: Windows
target: 7
runner: windows-2022
type: Release
make_package: 1
package_suffix: "-Win7"
artifact_name: Windows7-installer
qt_version: 5.15.*
qt_arch: win64_msvc2019_64
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
- os: Windows
target: 10
runner: windows-2022
type: Release
make_package: 1
package_suffix: "-Win10"
artifact_name: Windows10-installer
qt_version: 6.6.*
qt_arch: win64_msvc2019_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure
runs-on: ${{matrix.os}}
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
CCACHE_DIR: ${{github.workspace}}/.ccache/${{matrix.os}}-${{matrix.type}}
CCACHE_SIZE: 500M
DEVELOPER_DIR:
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
CMAKE_GENERATOR: 'Ninja'
runs-on: ${{matrix.runner}}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dependencies using Homebrew
shell: bash
# CMake cannot find the MySQL connector
# Neither of these works: mariadb-connector-c mysql-connector-c++
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
run: |
brew update
brew install ccache protobuf qt --force-bottle
- name: Restore compiler cache (ccache)
id: ccache_restore
uses: actions/cache/restore@v4
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
uses: actions/checkout@v6
with:
path: ${{env.CCACHE_DIR}}
key: ccache-${{matrix.os}}-${{matrix.type}}-${{env.BRANCH_NAME}}
restore-keys: ccache-${{matrix.os}}-${{matrix.type}}-
submodules: recursive
- name: Build on Xcode ${{matrix.xcode}}
shell: bash
- name: Add msbuild to PATH
if: matrix.os == 'Windows'
id: add-msbuild
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
# Using jianmingyong/ccache-action to setup ccache without using brew
# It tries to download a binary of ccache from GitHub Release and falls back to building from source if it fails
- name: Setup ccache
if: matrix.use_ccache == 1
uses: jianmingyong/ccache-action@v1
with:
install-type: "binary"
ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}
max-size: 500M
gh-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Qt ${{matrix.qt_version}}
uses: jurplel/install-qt-action@v4
with:
version: ${{matrix.qt_version}}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
cache: ${{matrix.cache_qt}}
- name: Setup vcpkg cache
id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
# uses environment variables, see compile.sh for more details
- name: Build Cockatrice
id: build
shell: bash
env:
BUILDTYPE: '${{matrix.type}}'
MAKE_PACKAGE: '${{matrix.make_package}}'
PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}'
PACKAGE_SUFFIX: '${{matrix.package_suffix}}'
CMAKE_GENERATOR: ${{matrix.cmake_generator}}
CMAKE_GENERATOR_PLATFORM: ${{matrix.cmake_generator_platform}}
USE_CCACHE: ${{matrix.use_ccache}}
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
# macOS-specific environment variables, will be ignored on Windows
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
run: .ci/compile.sh --server --test --ccache "$CCACHE_SIZE"
- name: Save compiler cache (ccache)
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@v4
with:
path: ${{env.CCACHE_DIR}}
key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
run: .ci/compile.sh --server --test --vcpkg
- name: Sign app bundle
if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
env:
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
@@ -321,7 +416,7 @@ jobs:
fi
- name: Notarize app bundle
if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
env:
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
@@ -332,20 +427,20 @@ jobs:
# Store the notarization credentials so that we can prevent a UI password dialog from blocking the CI
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MACOS_NOTARIZATION_APPLE_ID" --team-id "$MACOS_NOTARIZATION_TEAM_ID" --password "$MACOS_NOTARIZATION_PWD"
# We can't notarize an app bundle directly, but we need to compress it as an archive.
# Therefore, we create a zip file containing our app bundle, so that we can send it to the
# notarization service
echo "Creating temp notarization archive"
ditto -c -k --keepParent ${{steps.build.outputs.path}} "notarization.zip"
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious
echo "Notarize app"
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available.
echo "Attach staple"
@@ -355,110 +450,15 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.make_package
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: macOS${{matrix.target}}${{ matrix.soc == 'Intel' && '_Intel' || '' }}${{ matrix.type == 'Debug' && '_Debug' || '' }}-package
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload to release
id: upload_release
if: matrix.make_package && needs.configure.outputs.tag != null
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}}
asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.build.outputs.name}}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance
id: attestation
if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{steps.build.outputs.name}}
subject-digest: sha256:${{ steps.upload_artifact.outputs.artifact-digest }}
- name: Verify binary attestation
if: steps.attestation.outcome == 'success'
shell: bash
env:
GH_TOKEN: ${{github.token}}
run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice
build-windows:
strategy:
fail-fast: false
matrix:
include:
- target: 7
qt_version: 5.15.*
qt_arch: msvc2019_64
- target: 10
qt_version: 6.6.*
qt_arch: msvc2019_64
qt_modules: "qtimageformats qtmultimedia qtwebsockets"
name: Windows ${{matrix.target}}
needs: configure
runs-on: windows-2022
env:
CMAKE_GENERATOR: 'Visual Studio 17 2022'
steps:
- name: Add msbuild to PATH
id: add-msbuild
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt ${{matrix.qt_version}}
uses: jurplel/install-qt-action@v4
with:
cache: true
setup-python: true
version: ${{matrix.qt_version}}
arch: win64_${{matrix.qt_arch}}
tools: ${{matrix.qt_tools}}
modules: ${{matrix.qt_modules}}
- name: Setup vcpkg cache
id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Cockatrice
id: build
shell: bash
env:
PACKAGE_SUFFIX: '-Win${{matrix.target}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
CMAKE_GENERATOR_PLATFORM: 'x64'
QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}'
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
# No need for --parallel flag, MTT is added in the compile script to let cmake/msbuild manage core count,
# project and process parallelism: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
run: .ci/compile.sh --server --release --test --package
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4
with:
name: Windows${{matrix.target}}-installer
name: ${{matrix.artifact_name}}
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload pdb database
uses: actions/upload-artifact@v4
if: matrix.os == 'Windows'
uses: actions/upload-artifact@v5
with:
name: Windows${{matrix.target}}-debug-pdbs
path: |
@@ -480,7 +480,7 @@ jobs:
- name: Attest binary provenance
id: attestation
if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{steps.build.outputs.name}}
subject-digest: sha256:${{ steps.upload_artifact.outputs.artifact-digest }}

View File

@@ -1,13 +1,22 @@
name: Code Style (C++)
on:
# push trigger not needed for linting, we do not allow direct pushes to master
pull_request:
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.ci/**'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.ci/lint_cpp.sh'
- '.github/workflows/desktop-lint.yml'
- '.clang-format'
- '.cmake-format.json'
- 'format.sh'
jobs:
format:
@@ -15,7 +24,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 20 # should be enough to find merge base
@@ -23,7 +32,7 @@ jobs:
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format cmake-format
sudo apt-get install -y --no-install-recommends clang-format cmake-format shellcheck
- name: Check code formatting
shell: bash

View File

@@ -11,12 +11,7 @@ on:
- master
paths:
- '.github/workflows/docker-release.yml'
- 'CMakeLists.txt'
- 'Dockerfile'
- 'servatrice/**'
- 'common/**'
- 'cmake/**'
- '!**.md'
jobs:
docker:
@@ -28,7 +23,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Docker metadata
id: metadata

View File

@@ -0,0 +1,61 @@
name: Generate Docs
on:
push:
tags:
- '*' # Only re-generate docs when a new tagged version is pushed
pull_request:
paths:
- 'doc/doxygen/**'
- '.github/workflows/documentation-build.yml'
- 'Doxyfile'
workflow_dispatch:
env:
COCKATRICE_REF: ${{ github.ref_name }} # Tag name if the commit is tagged, otherwise branch name
jobs:
docs:
name: Doxygen
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Graphviz
run: |
sudo apt-get install -y graphviz
dot -V
- name: Install Doxygen
uses: ssciwr/doxygen-install@v1
with:
version: "1.14.0"
- name: Update Doxygen Configuration
run: |
git diff Doxyfile
doxygen -u Doxyfile
if git diff --quiet Doxyfile; then
echo "::notice::No config changes in Doxyfile detected."
else
echo "::error::Config changes in Doxyfile detected! Please update the file by running 'doxygen -u Doxyfile'."
echo ""
git diff --color=always Doxyfile
exit 1
fi
- name: Generate Documentation
if: always()
run: doxygen Doxyfile
- name: Deploy to cockatrice.github.io
if: github.event_name != 'pull_request'
uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }}
external_repository: Cockatrice/cockatrice.github.io
publish_branch: master
publish_dir: ./docs/html
destination_dir: docs # Docs will live under https://cockatrice.github.io/docs/

View File

@@ -7,6 +7,7 @@ on:
- cron: '0 0 15 1,4,7,10 *'
pull_request:
paths:
- '.tx/**'
- '.github/workflows/translations-pull.yml'
jobs:
@@ -19,7 +20,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Pull translated strings from Transifex
uses: transifex/cli-action@v2

View File

@@ -7,6 +7,7 @@ on:
- cron: '0 0 1 1,4,7,10 *'
pull_request:
paths:
- '.ci/update_translation_source_strings.sh'
- '.github/workflows/translations-push.yml'
jobs:
@@ -19,7 +20,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install lupdate
shell: bash
@@ -30,10 +31,10 @@ jobs:
- name: Update Cockatrice translation source
id: cockatrice
shell: bash
env:
FILE: 'cockatrice/cockatrice_en@source.ts'
DIRS: 'cockatrice/src common'
run: .ci/update_translation_source_strings.sh
run: |
FILE="cockatrice/cockatrice_en@source.ts"
export DIRS="cockatrice/src $(find . -maxdepth 1 -type d -name 'libcockatrice_*')"
FILE="$FILE" DIRS="$DIRS" .ci/update_translation_source_strings.sh
- name: Update Oracle translation source
id: oracle

View File

@@ -5,14 +5,16 @@ on:
branches:
- master
paths:
- '.github/workflows/web-*.yml'
- '.husky/**'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-build.yml'
pull_request:
paths:
- '.github/workflows/web-*.yml'
- '.husky/**'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-build.yml'
jobs:
build-web:
@@ -33,10 +35,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{matrix.node_version}}
cache: 'npm'

View File

@@ -1,11 +1,12 @@
name: Code Style (TypeScript)
on:
# push trigger not needed for linting, we do not allow direct pushes to master
pull_request:
paths:
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-lint.yml'
jobs:
ESLint:
@@ -17,10 +18,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
cache: 'npm'
cache-dependency-path: 'webclient/package-lock.json'

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ compile_commands.json
.cache
.gdb_history
cockatrice/resources/config/qtlogging.ini
docs/

View File

@@ -24,6 +24,8 @@ option(WITH_ORACLE "build oracle" ON)
option(WITH_DBCONVERTER "build dbconverter" ON)
# Compile tests
option(TEST "build tests" OFF)
# Use vcpkg regardless of OS
option(USE_VCPKG "Use vcpkg regardless of OS" OFF)
# Default to "Release" build type
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
@@ -48,8 +50,8 @@ if(USE_CCACHE)
endif()
endif()
if(WIN32)
# Use vcpkg toolchain on Windows
if(WIN32 OR USE_VCPKG)
# Use vcpkg toolchain on Windows (and on macOS in CI)
set(CMAKE_TOOLCHAIN_FILE
${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file"
@@ -301,6 +303,7 @@ if(UNIX)
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
if(Qt6_FOUND)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins, qt6-image-formats-plugins")
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libqt6sql6-mysql") # for connecting servatrice to a mysql db
elseif(Qt5_FOUND)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
endif()
@@ -325,7 +328,19 @@ endif()
include(CPack)
add_subdirectory(common)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_interfaces ${CMAKE_BINARY_DIR}/libcockatrice_interfaces)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_protocol ${CMAKE_BINARY_DIR}/libcockatrice_protocol)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_deck_list ${CMAKE_BINARY_DIR}/libcockatrice_deck_list)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_rng ${CMAKE_BINARY_DIR}/libcockatrice_rng)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_card ${CMAKE_BINARY_DIR}/libcockatrice_card)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_utility ${CMAKE_BINARY_DIR}/libcockatrice_utility)
if(WITH_ORACLE OR WITH_CLIENT)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_settings ${CMAKE_BINARY_DIR}/libcockatrice_settings)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters)
endif()
if(WITH_SERVER)
add_subdirectory(servatrice)
set(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})

View File

@@ -1,8 +1,9 @@
FROM ubuntu:24.04
# -------- Build Stage --------
FROM ubuntu:24.04 AS build
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y\
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
cmake \
file \
@@ -16,20 +17,28 @@ RUN apt-get update && apt-get install -y\
qt6-tools-dev \
qt6-tools-dev-tools
COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
COPY ./cmake /home/servatrice/code/cmake
COPY ./common /home/servatrice/code/common
COPY ./servatrice /home/servatrice/code/servatrice
WORKDIR /src
COPY . .
RUN mkdir build && cd build && \
cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \
make -j$(nproc) && \
make install
WORKDIR /home/servatrice/code
WORKDIR build
RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 &&\
make &&\
make install
# -------- Runtime Stage (clean) --------
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
libprotobuf32t64 \
libqt6sql6-mysql \
libqt6websockets6 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Only copy installed binaries, not source
COPY --from=build /usr/local /usr/local
WORKDIR /home/servatrice
EXPOSE 4748
ENTRYPOINT [ "servatrice", "--log-to-console" ]

2963
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
with this program; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -329,8 +328,8 @@ necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
<signature of Moe Ghoul>, 1 April 1989
Moe Ghoul, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may

View File

@@ -46,7 +46,8 @@ Latest <kbd>beta</kbd> version:
- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice
- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official webpage of the Cockatrice project
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official Cockatrice webpage
- [Cockatrice @Flathub](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration for our Linux `flatpak` package
# Community Resources [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA)
@@ -54,6 +55,7 @@ Latest <kbd>beta</kbd> version:
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
- [Official Website](https://cockatrice.github.io)
- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
- [Official Code Documentation](https://cockatrice.github.io/docs)
- [Official Discord](https://discord.gg/3Z9yzmA)
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)

View File

@@ -115,4 +115,7 @@ string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" ORACLE_QT_MO
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" DB_CONVERTER_QT_MODULES "${_DBCONVERTER_NEEDED}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" TEST_QT_MODULES "${_TEST_NEEDED}")
# Core-only export (useful for headless libs)
set(QT_CORE_MODULE "${COCKATRICE_QT_VERSION_NAME}::Core")
message(STATUS "Found Qt ${${COCKATRICE_QT_VERSION_NAME}_VERSION} at: ${${COCKATRICE_QT_VERSION_NAME}_DIR}")

View File

@@ -7,254 +7,281 @@ project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${
set(cockatrice_SOURCES
${VERSION_STRING_CPP}
# sort by alphabetical order, so that there is no debate about where to add new sources to the list
src/client/game_logic/abstract_client.cpp
src/client/game_logic/key_signals.cpp
src/client/get_text_with_max.cpp
src/client/menus/deck_editor/deck_editor_menu.cpp
src/client/network/client_update_checker.cpp
src/client/network/release_channel.cpp
src/client/network/replay_timeline_widget.cpp
src/client/network/sets_model.cpp
src/client/network/spoiler_background_updater.cpp
src/client/network/update/client/update_downloader.cpp
src/client/network/interfaces/deck_stats_interface.cpp
src/client/network/interfaces/tapped_out_interface.cpp
src/client/network/parsers/deck_link_to_api_transformer.cpp
src/client/network/update/client/client_update_checker.cpp
src/client/network/update/client/release_channel.cpp
src/client/network/update/card_spoiler/spoiler_background_updater.cpp
src/client/sound_engine.cpp
src/client/tabs/abstract_tab_deck_editor.cpp
src/client/tabs/api/edhrec/tab_edhrec.cpp
src/client/tabs/api/edhrec/tab_edhrec_main.cpp
src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
src/client/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/client/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/client/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/client/tabs/tab.cpp
src/client/tabs/tab_account.cpp
src/client/tabs/tab_admin.cpp
src/client/tabs/tab_deck_editor.cpp
src/client/tabs/tab_deck_storage.cpp
src/client/tabs/tab_game.cpp
src/client/tabs/tab_logs.cpp
src/client/tabs/tab_message.cpp
src/client/tabs/tab_replays.cpp
src/client/tabs/tab_room.cpp
src/client/tabs/tab_server.cpp
src/client/tabs/tab_supervisor.cpp
src/client/tabs/tab_visual_database_display.cpp
src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/client/tapped_out_interface.cpp
src/client/translate_counter_name.cpp
src/client/ui/layouts/flow_layout.cpp
src/client/ui/layouts/overlap_layout.cpp
src/client/ui/line_edit_completer.cpp
src/client/ui/phases_toolbar.cpp
src/client/ui/picture_loader/picture_loader.cpp
src/client/ui/picture_loader/picture_loader_local.cpp
src/client/ui/picture_loader/picture_loader_request_status_display_widget.cpp
src/client/ui/picture_loader/picture_loader_status_bar.cpp
src/client/ui/picture_loader/picture_loader_worker.cpp
src/client/ui/picture_loader/picture_loader_worker_work.cpp
src/client/ui/picture_loader/picture_to_load.cpp
src/client/ui/pixel_map_generator.cpp
src/client/ui/theme_manager.cpp
src/client/ui/tip_of_the_day.cpp
src/client/ui/widgets/cards/additional_info/color_identity_widget.cpp
src/client/ui/widgets/cards/additional_info/mana_cost_widget.cpp
src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
src/client/ui/widgets/cards/card_info_display_widget.cpp
src/client/ui/widgets/cards/card_info_frame_widget.cpp
src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp
src/client/ui/widgets/cards/card_info_picture_widget.cpp
src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
src/client/ui/widgets/cards/card_info_text_widget.cpp
src/client/ui/widgets/cards/card_size_widget.cpp
src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp
src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp
src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp
src/client/ui/widgets/deck_analytics/mana_base_widget.cpp
src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp
src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
src/client/ui/widgets/general/display/banner_widget.cpp
src/client/ui/widgets/general/display/bar_widget.cpp
src/client/ui/widgets/general/display/dynamic_font_size_label.cpp
src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp
src/client/ui/widgets/general/display/labeled_input.cpp
src/client/ui/widgets/general/display/percent_bar_widget.cpp
src/client/ui/widgets/general/display/shadow_background_label.cpp
src/client/ui/widgets/general/layout_containers/flow_widget.cpp
src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp
src/client/ui/widgets/general/layout_containers/overlap_widget.cpp
src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp
src/client/ui/widgets/printing_selector/card_amount_widget.cpp
src/client/ui/widgets/printing_selector/printing_selector.cpp
src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp
src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp
src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp
src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
src/client/ui/widgets/quick_settings/settings_button_widget.cpp
src/client/ui/widgets/quick_settings/settings_popup_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
src/client/ui/widgets/visual_database_display/visual_database_filter_display_widget.cpp
src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
src/client/ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/client/ui/window_main.cpp
src/client/update_downloader.cpp
src/deck/custom_line_edit.cpp
src/deck/deck_list_model.cpp
src/deck/deck_loader.cpp
src/deck/deck_stats_interface.cpp
src/dialogs/dlg_connect.cpp
src/dialogs/dlg_convert_deck_to_cod_format.cpp
src/dialogs/dlg_create_game.cpp
src/dialogs/dlg_create_token.cpp
src/dialogs/dlg_default_tags_editor.cpp
src/dialogs/dlg_edit_avatar.cpp
src/dialogs/dlg_edit_password.cpp
src/dialogs/dlg_edit_tokens.cpp
src/dialogs/dlg_edit_user.cpp
src/dialogs/dlg_filter_games.cpp
src/dialogs/dlg_forgot_password_challenge.cpp
src/dialogs/dlg_forgot_password_request.cpp
src/dialogs/dlg_forgot_password_reset.cpp
src/dialogs/dlg_load_deck.cpp
src/dialogs/dlg_load_deck_from_clipboard.cpp
src/dialogs/dlg_load_remote_deck.cpp
src/dialogs/dlg_manage_sets.cpp
src/dialogs/dlg_move_top_cards_until.cpp
src/dialogs/dlg_register.cpp
src/dialogs/dlg_roll_dice.cpp
src/dialogs/dlg_select_set_for_cards.cpp
src/dialogs/dlg_settings.cpp
src/dialogs/dlg_startup_card_check.cpp
src/dialogs/dlg_tip_of_the_day.cpp
src/dialogs/dlg_update.cpp
src/dialogs/dlg_view_log.cpp
src/client/settings/cache_settings.cpp
src/client/settings/card_counter_settings.cpp
src/client/settings/shortcut_treeview.cpp
src/client/settings/shortcuts_settings.cpp
src/interface/deck_loader/deck_loader.cpp
src/interface/widgets/dialogs/dlg_connect.cpp
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp
src/interface/widgets/dialogs/dlg_default_tags_editor.cpp
src/interface/widgets/dialogs/dlg_edit_avatar.cpp
src/interface/widgets/dialogs/dlg_edit_password.cpp
src/interface/widgets/dialogs/dlg_edit_tokens.cpp
src/interface/widgets/dialogs/dlg_edit_user.cpp
src/interface/widgets/dialogs/dlg_filter_games.cpp
src/interface/widgets/dialogs/dlg_forgot_password_challenge.cpp
src/interface/widgets/dialogs/dlg_forgot_password_request.cpp
src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
src/interface/widgets/dialogs/dlg_load_deck.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp
src/interface/widgets/dialogs/dlg_manage_sets.cpp
src/interface/widgets/dialogs/dlg_register.cpp
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
src/interface/widgets/dialogs/dlg_settings.cpp
src/interface/widgets/dialogs/dlg_startup_card_check.cpp
src/interface/widgets/dialogs/dlg_tip_of_the_day.cpp
src/interface/widgets/dialogs/dlg_update.cpp
src/interface/widgets/dialogs/dlg_view_log.cpp
src/interface/widgets/dialogs/tip_of_the_day.cpp
src/filters/deck_filter_string.cpp
src/filters/filter_builder.cpp
src/filters/filter_tree_model.cpp
src/filters/syntax_help.cpp
src/game/abstract_game.cpp
src/game/board/abstract_card_drag_item.cpp
src/game/board/abstract_card_item.cpp
src/game/board/abstract_counter.cpp
src/game/board/abstract_graphics_item.cpp
src/game/board/arrow_item.cpp
src/game/board/arrow_target.cpp
src/game/board/card_drag_item.cpp
src/game/board/card_item.cpp
src/game/board/card_list.cpp
src/game/board/counter_general.cpp
src/game/cards/card_completer_proxy_model.cpp
src/game/cards/card_database.cpp
src/game/cards/card_database_manager.cpp
src/game/cards/card_database_model.cpp
src/game/cards/card_database_parser/card_database_parser.cpp
src/game/cards/card_database_parser/cockatrice_xml_3.cpp
src/game/cards/card_database_parser/cockatrice_xml_4.cpp
src/game/cards/card_info.cpp
src/game/cards/card_search_model.cpp
src/game/board/translate_counter_name.cpp
src/game/deckview/deck_view.cpp
src/game/deckview/deck_view_container.cpp
src/game/filters/deck_filter_string.cpp
src/game/filters/filter_builder.cpp
src/game/filters/filter_card.cpp
src/game/filters/filter_string.cpp
src/game/filters/filter_tree.cpp
src/game/filters/filter_tree_model.cpp
src/game/filters/syntax_help.cpp
src/game/deckview/tabbed_deck_view_container.cpp
src/game/dialogs/dlg_create_token.cpp
src/game/dialogs/dlg_move_top_cards_until.cpp
src/game/dialogs/dlg_roll_dice.cpp
src/game/game.cpp
src/game/game_event_handler.cpp
src/game/game_meta_info.cpp
src/game/game_scene.cpp
src/game/game_selector.cpp
src/game/game_state.cpp
src/game/game_view.cpp
src/game/games_model.cpp
src/game/hand_counter.cpp
src/game/log/message_log_widget.cpp
src/game/phase.cpp
src/game/phases_toolbar.cpp
src/game/player/menu/card_menu.cpp
src/game/player/menu/custom_zone_menu.cpp
src/game/player/menu/grave_menu.cpp
src/game/player/menu/hand_menu.cpp
src/game/player/menu/library_menu.cpp
src/game/player/menu/move_menu.cpp
src/game/player/menu/player_menu.cpp
src/game/player/menu/pt_menu.cpp
src/game/player/menu/rfg_menu.cpp
src/game/player/menu/say_menu.cpp
src/game/player/menu/sideboard_menu.cpp
src/game/player/menu/utility_menu.cpp
src/game/player/player.cpp
src/game/player/player_actions.cpp
src/game/player/player_area.cpp
src/game/player/player_event_handler.cpp
src/game/player/player_graphics_item.cpp
src/game/player/player_info.cpp
src/game/player/player_list_widget.cpp
src/game/player/player_manager.cpp
src/game/player/player_target.cpp
src/game/replay.cpp
src/game/zones/card_zone.cpp
src/game/zones/hand_zone.cpp
src/game/zones/logic/card_zone_logic.cpp
src/game/zones/logic/hand_zone_logic.cpp
src/game/zones/logic/pile_zone_logic.cpp
src/game/zones/logic/stack_zone_logic.cpp
src/game/zones/logic/table_zone_logic.cpp
src/game/zones/logic/view_zone_logic.cpp
src/game/zones/pile_zone.cpp
src/game/zones/select_zone.cpp
src/game/zones/stack_zone.cpp
src/game/zones/table_zone.cpp
src/game/zones/view_zone.cpp
src/game/zones/view_zone_widget.cpp
src/game_graphics/board/abstract_graphics_item.cpp
src/interface/card_picture_loader/card_picture_loader.cpp
src/interface/card_picture_loader/card_picture_loader_local.cpp
src/interface/card_picture_loader/card_picture_loader_request_status_display_widget.cpp
src/interface/card_picture_loader/card_picture_loader_status_bar.cpp
src/interface/card_picture_loader/card_picture_loader_worker.cpp
src/interface/card_picture_loader/card_picture_loader_worker_work.cpp
src/interface/card_picture_loader/card_picture_to_load.cpp
src/interface/layouts/flow_layout.cpp
src/interface/layouts/overlap_layout.cpp
src/interface/widgets/utility/line_edit_completer.cpp
src/interface/pixel_map_generator.cpp
src/interface/theme_manager.cpp
src/interface/widgets/cards/additional_info/color_identity_widget.cpp
src/interface/widgets/cards/additional_info/mana_cost_widget.cpp
src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp
src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
src/interface/widgets/cards/card_info_display_widget.cpp
src/interface/widgets/cards/card_info_frame_widget.cpp
src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp
src/interface/widgets/cards/card_info_picture_enlarged_widget.cpp
src/interface/widgets/cards/card_info_picture_widget.cpp
src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
src/interface/widgets/cards/card_info_text_widget.cpp
src/interface/widgets/cards/card_size_widget.cpp
src/interface/widgets/cards/deck_card_zone_display_widget.cpp
src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
src/interface/widgets/deck_analytics/deck_analytics_widget.cpp
src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
src/interface/widgets/deck_analytics/mana_base_widget.cpp
src/interface/widgets/deck_analytics/mana_curve_widget.cpp
src/interface/widgets/deck_analytics/mana_devotion_widget.cpp
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
src/interface/widgets/general/background_sources.cpp
src/interface/widgets/general/display/background_plate_widget.cpp
src/interface/widgets/general/display/banner_widget.cpp
src/interface/widgets/general/display/bar_widget.cpp
src/interface/widgets/general/display/color_bar.cpp
src/interface/widgets/general/display/dynamic_font_size_label.cpp
src/interface/widgets/general/display/dynamic_font_size_push_button.cpp
src/interface/widgets/general/display/labeled_input.cpp
src/interface/widgets/general/display/percent_bar_widget.cpp
src/interface/widgets/general/display/shadow_background_label.cpp
src/interface/widgets/general/home_styled_button.cpp
src/interface/widgets/general/home_widget.cpp
src/interface/widgets/general/layout_containers/flow_widget.cpp
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
src/interface/widgets/general/layout_containers/overlap_widget.cpp
src/interface/widgets/menus/deck_editor_menu.cpp
src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
src/interface/widgets/printing_selector/card_amount_widget.cpp
src/interface/widgets/printing_selector/printing_selector.cpp
src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp
src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
src/interface/widgets/printing_selector/printing_selector_card_search_widget.cpp
src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
src/interface/widgets/quick_settings/settings_button_widget.cpp
src/interface/widgets/quick_settings/settings_popup_widget.cpp
src/interface/widgets/replay/replay_manager.cpp
src/interface/widgets/replay/replay_timeline_widget.cpp
src/interface/widgets/server/chat_view/chat_view.cpp
src/interface/widgets/server/game_selector.cpp
src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp
src/interface/widgets/server/games_model.cpp
src/interface/widgets/server/handle_public_servers.cpp
src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp
src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp
src/interface/widgets/server/user/user_context_menu.cpp
src/interface/widgets/server/user/user_info_box.cpp
src/interface/widgets/server/user/user_info_connection.cpp
src/interface/widgets/server/user/user_list_manager.cpp
src/interface/widgets/server/user/user_list_widget.cpp
src/interface/widgets/utility/custom_line_edit.cpp
src/interface/widgets/utility/get_text_with_max.cpp
src/interface/widgets/utility/sequence_edit.cpp
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_widget.cpp
src/interface/widgets/visual_database_display/visual_database_filter_display_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/interface/window_main.cpp
src/main.cpp
src/server/chat_view/chat_view.cpp
src/server/handle_public_servers.cpp
src/server/local_client.cpp
src/server/local_server.cpp
src/server/local_server_interface.cpp
src/server/message_log_widget.cpp
src/server/pending_command.cpp
src/server/remote/remote_client.cpp
src/server/remote/remote_decklist_tree_widget.cpp
src/server/remote/remote_replay_list_tree_widget.cpp
src/server/user/user_context_menu.cpp
src/server/user/user_info_box.cpp
src/server/user/user_info_connection.cpp
src/server/user/user_list_manager.cpp
src/server/user/user_list_widget.cpp
src/settings/cache_settings.cpp
src/settings/card_counter_settings.cpp
src/settings/card_database_settings.cpp
src/settings/card_override_settings.cpp
src/settings/debug_settings.cpp
src/settings/download_settings.cpp
src/settings/game_filters_settings.cpp
src/settings/layouts_settings.cpp
src/settings/message_settings.cpp
src/settings/recents_settings.cpp
src/settings/servers_settings.cpp
src/settings/settings_manager.cpp
src/settings/shortcut_treeview.cpp
src/settings/shortcuts_settings.cpp
src/utility/card_info_comparator.cpp
src/utility/levenshtein.cpp
src/utility/logger.cpp
src/utility/sequence_edit.cpp
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck_category.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_listing_container.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_owner.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/interface/widgets/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
src/interface/widgets/tabs/tab.cpp
src/interface/widgets/tabs/tab_account.cpp
src/interface/widgets/tabs/tab_admin.cpp
src/interface/widgets/tabs/tab_deck_editor.cpp
src/interface/widgets/tabs/tab_deck_storage.cpp
src/interface/widgets/tabs/tab_game.cpp
src/interface/widgets/tabs/tab_home.cpp
src/interface/widgets/tabs/tab_logs.cpp
src/interface/widgets/tabs/tab_message.cpp
src/interface/widgets/tabs/tab_replays.cpp
src/interface/widgets/tabs/tab_room.cpp
src/interface/widgets/tabs/tab_server.cpp
src/interface/widgets/tabs/tab_supervisor.cpp
src/interface/widgets/tabs/tab_visual_database_display.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/interface/key_signals.cpp
src/interface/logger.cpp
)
add_subdirectory(sounds)
@@ -266,15 +293,23 @@ configure_file(
set(cockatrice_RESOURCES cockatrice.qrc)
if(UPDATE_TRANSLATIONS)
# Cockatrice main sources
file(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp
${CMAKE_SOURCE_DIR}/cockatrice/src/*.h
)
file(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h)
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS})
# All libcockatrice_* libraries (recursively)
file(GLOB_RECURSE translate_lib_SRCS ${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.cpp
${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.h
)
# Combine all sources for translation
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_lib_SRCS})
set(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts")
else()
file(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
endif(UPDATE_TRANSLATIONS)
endif()
if(WIN32)
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
@@ -304,12 +339,6 @@ set(DESKTOPDIR
CACHE STRING "desktop file destination"
)
# Include directories
include_directories(../common)
include_directories(${PROTOBUF_INCLUDE_DIR})
include_directories(${CMAKE_BINARY_DIR}/common)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations")
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
@@ -349,9 +378,31 @@ elseif(Qt5_FOUND)
endif()
if(Qt5_FOUND)
target_link_libraries(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES})
target_link_libraries(
cockatrice
libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
else()
target_link_libraries(cockatrice PUBLIC cockatrice_common ${COCKATRICE_QT_MODULES})
target_link_libraries(
cockatrice
PUBLIC libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
endif()
if(UNIX)

View File

@@ -7,11 +7,14 @@
<file>resources/icons/arrow_bottom_green.svg</file>
<file>resources/icons/arrow_down_green.svg</file>
<file>resources/icons/arrow_history.svg</file>
<file>resources/icons/arrow_left_green.svg</file>
<file>resources/icons/arrow_redo.svg</file>
<file>resources/icons/arrow_right_blue.svg</file>
<file>resources/icons/arrow_right_green.svg</file>
<file>resources/icons/arrow_top_green.svg</file>
<file>resources/icons/arrow_up_green.svg</file>
<file>resources/icons/arrow_undo.svg</file>
<file>resources/icons/clearsearch.svg</file>
<file>resources/icons/cogwheel.svg</file>
<file>resources/icons/conceded.svg</file>
@@ -25,6 +28,7 @@
<file>resources/icons/lock.svg</file>
<file>resources/icons/not_ready_start.svg</file>
<file>resources/icons/pencil.svg</file>
<file>resources/icons/pin.svg</file>
<file>resources/icons/player.svg</file>
<file>resources/icons/ready_start.svg</file>
<file>resources/icons/reload.svg</file>
@@ -33,6 +37,7 @@
<file>resources/icons/scales.svg</file>
<file>resources/icons/search.svg</file>
<file>resources/icons/settings.svg</file>
<file>resources/icons/share.svg</file>
<file>resources/icons/spectator.svg</file>
<file>resources/icons/swap.svg</file>
<file>resources/icons/sync.svg</file>
@@ -46,6 +51,8 @@
<file>resources/icons/mana/U.svg</file>
<file>resources/icons/mana/W.svg</file>
<file>resources/backgrounds/home.png</file>
<file>resources/config/general.svg</file>
<file>resources/config/appearance.svg</file>
<file>resources/config/interface.svg</file>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -1,6 +1,10 @@
[Rules]
# The default log level is info
*.debug = false
#*.info = true
#*.warning = true
#*.critical = true
#*.fatal = true
# Uncomment a rule to see debug level logs for that category,
# or set <category> = false to disable logging
@@ -19,6 +23,7 @@
#tab_supervisor = true
#dlg_edit_avatar = true
#dlg_load_deck_from_website = true
#dlg_settings = true
#dlg_tip_of_the_day = true
#dlg_update = true
@@ -36,12 +41,14 @@
#card_zone = true
#view_zone = true
#game_event_handler = true
#user_info_connection = true
#picture_loader = true
#picture_loader.worker = true
#picture_loader.card_back_cache_fail = true
#picture_loader.picture_to_load = true
#card_picture_loader = true
#card_picture_loader.worker = true
#card_picture_loader.card_back_cache_fail = true
#card_picture_loader.picture_to_load = true
#deck_loader = true
#card_database = true
#card_database.loading = true

View File

@@ -1,3 +1,5 @@
@page deck_search_syntax_help Deck Search Syntax Help
## Deck Search Syntax Help
-----
The search bar recognizes a set of special commands.<br>

View File

@@ -1,3 +1,5 @@
@page search_syntax_help Search Syntax Help
## Search Syntax Help
-----
The search bar recognizes a set of special commands similar to some other card databases.<br>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
history
</title>
<path d="M9 6v5h.06l2.48 2.47 1.41-1.41L11 10.11V6H9z"/>
<path d="M10 1a9 9 0 0 0-7.85 13.35L.5 16H6v-5.5l-2.38 2.38A7 7 0 1 1 10 17v2a9 9 0 0 0 0-18z"/>
</svg>

After

Width:  |  Height:  |  Size: 332 B

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="windows-1252"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px"
y="0px" width="485.212px" height="485.212px" viewBox="0 0 485.212 485.212"
style="enable-background:new 0 0 485.212 485.212;" xml:space="preserve">
<g>
<path d="M242.607,424.559c-75.252,0-136.468-61.209-136.468-136.465c0-75.252,61.216-136.466,136.468-136.466v90.978 l151.629-121.302L242.607,0v90.978c-108.687,0-197.117,88.432-197.117,197.117c0,108.691,88.43,197.118,197.117,197.118 c108.687,0,197.114-88.427,197.114-197.118h-60.645C379.077,363.35,317.859,424.559,242.607,424.559z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1018 B

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="windows-1252"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px"
y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;"
xml:space="preserve">
<g>
<path d="M256,448c79.406,0,144-64.594,144-144s-64.594-144-144-144v96L96,128L256,0v96c114.688,0,208,93.313,208,208 c0,114.688-93.312,208-208,208c-114.687,0-208-93.312-208-208h64C112,383.406,176.594,448,256,448z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 874 B

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="64" height="64">
<g transform="matrix(0 1 -1 0 99.465813 0)" opacity="0.7">
<path fill="#000000" fill-rule="evenodd" clip-rule="evenodd"
stroke="#ffffff"
stroke-width="4"
stroke-linejoin="round"
stroke-linecap="round"
d="M65.5 62
L78 49
C73.5 44.5 69.5 42 63 44
L45 31 C47 25 46 22 41.5 18
L19 41.5
C23 45.5 25 46.5 31 45
L44 62.5
C42.3 69 45 73.5 49 78
L61.5 65.5
L84 87
L87 87
L87.5 86.5
L87.5 83.5 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 736 B

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800"
width="800"
version="1.1"
id="_x32_"
viewBox="0 0 512 512"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<style type="text/css"
id="style1">
.st0{fill:#64C0FF;stroke:black;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1}
</style>
<g id="g1"
transform="matrix(0.87097097,0,0,1.0008579,38.609049,-0.21963163)"
style="stroke-width:3.42738;stroke-dasharray:none">
<path class="st0"
d="M 512,255.995 277.045,65.394 v 103.574 c -17.255,0 -36.408,0 -57.542,0 -208.59,0 -249.35,153.44 -201.394,266.128 9.586,-103.098 142.053,-100.701 237.358,-100.701 7.247,0 14.446,0 21.578,0 v 112.211 z"
id="path1"
style="stroke-width:20;stroke:#000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -363,6 +363,6 @@
d="m 38.011063,984.77381 -10.143601,-5.23583 -10.063711,5.38779 1.845023,-11.2651 -8.233948,-7.90624 11.283888,-1.72639 4.974851,-10.27411 5.128803,10.19813 11.308575,1.55649 -8.114112,8.02918 z"
inkscape:transform-center-x="0.094945927"
inkscape:transform-center-y="-3.9764964"
transform="matrix(2.3768784,0,0,2.4799382,-15.920285,-1400.1716)" />
transform="matrix(-2.3768784,0,0,2.4799382,115.920285,-1400.1716)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,14 +1,13 @@
#include "deck_stats_interface.h"
#include "decklist.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)
@@ -70,7 +69,7 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
{
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName(), -1);
addedCard->setNumber(card->getNumber());

View File

@@ -1,10 +1,14 @@
/**
* @file deck_stats_interface.h
* @ingroup ApiInterfaces
* @brief TODO: Document this.
*/
#ifndef DECKSTATS_INTERFACE_H
#define DECKSTATS_INTERFACE_H
#include "../game/cards/card_database.h"
#include "decklist.h"
#include <QObject>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/deck_list/deck_list.h>
class QByteArray;
class QNetworkAccessManager;

View File

@@ -1,14 +1,13 @@
#include "tapped_out_interface.h"
#include "decklist.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)
@@ -95,7 +94,7 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
{
auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
if (!dbCard || dbCard->getIsToken())
return;

View File

@@ -1,11 +1,14 @@
/**
* @file tapped_out_interface.h
* @ingroup ApiInterfaces
* @brief TODO: Document this.
*/
#ifndef TAPPEDOUT_INTERFACE_H
#define TAPPEDOUT_INTERFACE_H
#include "../game/cards/card_database.h"
#include "decklist.h"
#include <QLoggingCategory>
#include <QObject>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/deck_list/deck_list.h>
inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface");

View File

@@ -0,0 +1,61 @@
#include "deck_link_to_api_transformer.h"
#include <QRegularExpression>
namespace DeckLinkToApiTransformer
{
static const QString TAPPEDOUT_BASE = "https://tappedout.net/mtg-decks/";
static const QString TAPPEDOUT_SUFFIX = "/?fmt=txt";
static const QString ARCHIDEKT_BASE = "https://archidekt.com/api/decks/";
static const QString ARCHIDEKT_SUFFIX = "/?format=json";
static const QString MOXFIELD_BASE = "https://api.moxfield.com/v2/decks/all/";
static const QString MOXFIELD_SUFFIX = "/";
static const QString DECKSTATS_SUFFIX = "?include_comments=1&export_mtgarena=1";
bool parseDeckUrl(const QString &url, ParsedDeckInfo &outInfo)
{
static QRegularExpression rxTappedOut("tappedout\\.net/(?:mtg-decks/)?([^/?#]+)");
static QRegularExpression rxArchidekt("archidekt\\.com/decks/(\\d+)");
static QRegularExpression rxMoxfield("moxfield\\.com/decks/([a-zA-Z0-9_-]+)");
static QRegularExpression rxDeckstats("deckstats\\.net/decks/(\\d+/[a-zA-Z0-9_-]+)");
QRegularExpressionMatch match;
if ((match = rxTappedOut.match(url)).hasMatch()) {
QString slug = match.captured(1);
outInfo = ParsedDeckInfo{.baseUrl = TAPPEDOUT_BASE,
.deckID = slug,
.fullUrl = TAPPEDOUT_BASE + slug + TAPPEDOUT_SUFFIX,
.provider = DeckProvider::TappedOut};
return true;
} else if ((match = rxArchidekt.match(url)).hasMatch()) {
QString deckID = match.captured(1);
outInfo = ParsedDeckInfo{.baseUrl = ARCHIDEKT_BASE,
.deckID = deckID,
.fullUrl = ARCHIDEKT_BASE + deckID + ARCHIDEKT_SUFFIX,
.provider = DeckProvider::Archidekt};
return true;
} else if ((match = rxMoxfield.match(url)).hasMatch()) {
QString deckID = match.captured(1);
outInfo = ParsedDeckInfo{.baseUrl = MOXFIELD_BASE,
.deckID = deckID,
.fullUrl = MOXFIELD_BASE + deckID + MOXFIELD_SUFFIX,
.provider = DeckProvider::Moxfield};
return true;
} else if ((match = rxDeckstats.match(url)).hasMatch()) {
QString deckPath = match.captured(1);
outInfo = ParsedDeckInfo{.baseUrl = "https://deckstats.net/decks/",
.deckID = deckPath,
.fullUrl = "https://deckstats.net/decks/" + deckPath + DECKSTATS_SUFFIX,
.provider = DeckProvider::Deckstats};
return true;
}
return false;
}
} // namespace DeckLinkToApiTransformer

View File

@@ -0,0 +1,37 @@
/**
* @file deck_link_to_api_transformer.h
* @ingroup ApiInterfaces
* @brief TODO: Document this.
*/
#ifndef DECK_LINK_TO_API_TRANSFORMER_H
#define DECK_LINK_TO_API_TRANSFORMER_H
#include <QString>
enum class DeckProvider
{
TappedOut,
Archidekt,
Moxfield,
Deckstats,
Unknown
};
struct ParsedDeckInfo
{
QString baseUrl;
QString deckID;
QString fullUrl;
DeckProvider provider;
};
namespace DeckLinkToApiTransformer
{
// Returns true if the input URL is recognized and fills outInfo.
bool parseDeckUrl(const QString &url, ParsedDeckInfo &outInfo);
} // namespace DeckLinkToApiTransformer
#endif // DECK_LINK_TO_API_TRANSFORMER_H

View File

@@ -0,0 +1,118 @@
/**
* @file interface_json_deck_parser.h
* @ingroup ApiInterfaces
* @brief TODO: Document this.
*/
#ifndef INTERFACE_JSON_DECK_PARSER_H
#define INTERFACE_JSON_DECK_PARSER_H
#include "../../../interface/deck_loader/deck_loader.h"
#include <QJsonArray>
#include <QJsonObject>
class IJsonDeckParser
{
public:
virtual ~IJsonDeckParser() = default;
virtual DeckLoader *parse(const QJsonObject &obj) = 0;
};
class ArchidektJsonParser : public IJsonDeckParser
{
public:
DeckLoader *parse(const QJsonObject &obj) override
{
DeckLoader *loader = new DeckLoader(nullptr);
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
for (auto entry : obj.value("cards").toArray()) {
auto quantity = entry.toObject().value("quantity").toInt();
auto card = entry.toObject().value("card").toObject();
auto oracleCard = card.value("oracleCard").toObject();
QString cardName = oracleCard.value("name").toString();
QString setName = card.value("edition").toObject().value("editioncode").toString().toUpper();
QString collectorNumber = card.value("collectorNumber").toString();
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
return loader;
}
};
class MoxfieldJsonParser : public IJsonDeckParser
{
public:
DeckLoader *parse(const QJsonObject &obj) override
{
DeckLoader *loader = new DeckLoader(nullptr);
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
for (auto entry : obj.value("mainboard").toObject()) {
auto quantity = entry.toObject().value("quantity").toInt();
auto card = entry.toObject().value("card").toObject();
QString cardName = card.value("name").toString();
QString setName = card.value("set").toString().toUpper();
QString collectorNumber = card.value("cn").toString();
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
outStream << '\n';
for (auto entry : obj.value("sideboard").toObject()) {
auto quantity = entry.toObject().value("quantity").toInt();
auto card = entry.toObject().value("card").toObject();
QString cardName = card.value("name").toString();
QString setName = card.value("set").toString().toUpper();
QString collectorNumber = card.value("cn").toString();
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
QJsonObject commandersObj = obj.value("commanders").toObject();
if (!commandersObj.isEmpty()) {
for (auto it = commandersObj.begin(); it != commandersObj.end(); ++it) {
QJsonObject cardData = it.value().toObject().value("card").toObject();
QString commanderName = cardData.value("name").toString();
QString setName = cardData.value("set").toString().toUpper();
QString collectorNumber = cardData.value("cn").toString();
QString providerId = cardData.value("scryfall_id").toString();
loader->getDeckList()->setBannerCard({commanderName, providerId});
loader->getDeckList()->addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
}
}
return loader;
}
};
#endif // INTERFACE_JSON_DECK_PARSER_H

View File

@@ -1,21 +1,19 @@
#include "spoiler_background_updater.h"
#include "../../game/cards/card_database.h"
#include "../../game/cards/card_database_manager.h"
#include "../../main.h"
#include "../../settings/cache_settings.h"
#include "../ui/window_main.h"
#include "../../../../interface/window_main.h"
#include "../../../../main.h"
#include "../../../settings/cache_settings.h"
#include <QApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QLocale>
#include <QMessageBox>
#include <QNetworkReply>
#include <QUrl>
#include <QtConcurrent>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled"
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"

View File

@@ -1,3 +1,9 @@
/**
* @file spoiler_background_updater.h
* @ingroup Client
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_SPOILER_DOWNLOADER_H
#define COCKATRICE_SPOILER_DOWNLOADER_H
@@ -16,7 +22,7 @@ public:
inline QString getCardUpdaterBinaryName()
{
return "oracle";
};
}
QByteArray getHash(const QString fileName);
QByteArray getHash(QByteArray data);
static bool deleteSpoilerFile();

View File

@@ -1,6 +1,6 @@
#include "client_update_checker.h"
#include "../../settings/cache_settings.h"
#include "../../../settings/cache_settings.h"
#include "release_channel.h"
ClientUpdateChecker::ClientUpdateChecker(QObject *parent) : QObject(parent)

View File

@@ -1,3 +1,9 @@
/**
* @file client_update_checker.h
* @ingroup ClientUpdate
* @brief TODO: Document this.
*/
#ifndef CLIENT_UPDATE_CHECKER_H
#define CLIENT_UPDATE_CHECKER_H
#include <QObject>

View File

@@ -1,3 +1,9 @@
/**
* @file release_channel.h
* @ingroup ClientUpdate
* @brief TODO: Document this.
*/
#ifndef RELEASECHANNEL_H
#define RELEASECHANNEL_H
@@ -51,27 +57,27 @@ protected:
}
public:
QString getName() const
[[nodiscard]] QString getName() const
{
return name;
}
QString getDescriptionUrl() const
[[nodiscard]] QString getDescriptionUrl() const
{
return descriptionUrl;
}
QString getDownloadUrl() const
[[nodiscard]] QString getDownloadUrl() const
{
return downloadUrl;
}
QString getCommitHash() const
[[nodiscard]] QString getCommitHash() const
{
return commitHash;
}
QDate getPublishDate() const
[[nodiscard]] QDate getPublishDate() const
{
return publishDate;
}
bool isCompatibleVersionFound() const
[[nodiscard]] bool isCompatibleVersionFound() const
{
return compatibleVersionFound;
}
@@ -91,15 +97,15 @@ protected:
protected:
static bool downloadMatchesCurrentOS(const QString &fileName);
virtual QString getReleaseChannelUrl() const = 0;
[[nodiscard]] virtual QString getReleaseChannelUrl() const = 0;
public:
Release *getLastRelease()
{
return lastRelease;
}
virtual QString getManualDownloadUrl() const = 0;
virtual QString getName() const = 0;
[[nodiscard]] virtual QString getManualDownloadUrl() const = 0;
[[nodiscard]] virtual QString getName() const = 0;
void checkForUpdates();
signals:
void finishedCheck(bool needToUpdate, bool isCompatible, Release *release);
@@ -116,12 +122,12 @@ public:
explicit StableReleaseChannel() = default;
~StableReleaseChannel() override = default;
QString getManualDownloadUrl() const override;
[[nodiscard]] QString getManualDownloadUrl() const override;
QString getName() const override;
[[nodiscard]] QString getName() const override;
protected:
QString getReleaseChannelUrl() const override;
[[nodiscard]] QString getReleaseChannelUrl() const override;
protected slots:
void releaseListFinished() override;
@@ -137,12 +143,12 @@ public:
BetaReleaseChannel() = default;
~BetaReleaseChannel() override = default;
QString getManualDownloadUrl() const override;
[[nodiscard]] QString getManualDownloadUrl() const override;
QString getName() const override;
[[nodiscard]] QString getName() const override;
protected:
QString getReleaseChannelUrl() const override;
[[nodiscard]] QString getReleaseChannelUrl() const override;
protected slots:
void releaseListFinished() override;

View File

@@ -1,6 +1,5 @@
#include "update_downloader.h"
#include <QDebug>
#include <QUrl>
UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr)

View File

@@ -1,13 +1,13 @@
//
// Created by miguel on 28/12/15.
//
/**
* @file update_downloader.h
* @ingroup ClientUpdate
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_UPDATEDOWNLOADER_H
#define COCKATRICE_UPDATEDOWNLOADER_H
#include <QDate>
#include <QObject>
#include <QUrl>
#include <QtNetwork>
class UpdateDownloader : public QObject

View File

@@ -1,8 +1,7 @@
#include "cache_settings.h"
#include "../client/network/release_channel.h"
#include "../network/update/client/release_channel.h"
#include "card_counter_settings.h"
#include "card_override_settings.h"
#include <QAbstractListModel>
#include <QApplication>
@@ -12,9 +11,15 @@
#include <QGlobalStatic>
#include <QSettings>
#include <QStandardPaths>
#include <libcockatrice/settings/card_override_settings.h>
#include <utility>
Q_GLOBAL_STATIC(SettingsCache, settingsCache);
Q_GLOBAL_STATIC(SettingsCache, settingsCache)
SettingsCache &SettingsCache::instance()
{
return *settingsCache; // returns a QT managed singleton reference
}
QString SettingsCache::getDataPath()
{
@@ -61,9 +66,9 @@ void SettingsCache::translateLegacySettings()
QStringList setsGroups = legacySetting.childGroups();
for (int i = 0; i < setsGroups.size(); i++) {
legacySetting.beginGroup(setsGroups.at(i));
cardDatabase().setEnabled(setsGroups.at(i), legacySetting.value("enabled").toBool());
cardDatabase().setIsKnown(setsGroups.at(i), legacySetting.value("isknown").toBool());
cardDatabase().setSortKey(setsGroups.at(i), legacySetting.value("sortkey").toUInt());
cardDatabase()->setEnabled(setsGroups.at(i), legacySetting.value("enabled").toBool());
cardDatabase()->setIsKnown(setsGroups.at(i), legacySetting.value("isknown").toBool());
cardDatabase()->setSortKey(setsGroups.at(i), legacySetting.value("sortkey").toUInt());
legacySetting.endGroup();
}
QStringList setsKeys = legacySetting.allKeys();
@@ -217,6 +222,9 @@ SettingsCache::SettingsCache()
themeName = settings->value("theme/name").toString();
homeTabBackgroundSource = settings->value("home/background", "themed").toString();
homeTabBackgroundShuffleFrequency = settings->value("home/background/shuffleTimer", 0).toInt();
tabVisualDeckStorageOpen = settings->value("tabs/visualDeckStorage", true).toBool();
tabServerOpen = settings->value("tabs/server", true).toBool();
tabAccountOpen = settings->value("tabs/account", true).toBool();
@@ -253,6 +261,7 @@ SettingsCache::SettingsCache()
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
clickPlaysAllSelected = settings->value("interface/clickPlaysAllSelected", true).toBool();
playToStack = settings->value("interface/playtostack", true).toBool();
doNotDeleteArrowsInSubPhases = settings->value("interface/doNotDeleteArrowsInSubPhases", true).toBool();
startingHandSize = settings->value("interface/startinghandsize", 7).toInt();
annotateTokens = settings->value("interface/annotatetokens", false).toBool();
tabGameSplitterSizes = settings->value("interface/tabgame_splittersizes").toByteArray();
@@ -301,10 +310,15 @@ SettingsCache::SettingsCache()
settings->value("interface/visualdeckstorageselectionanimation", true).toBool();
defaultDeckEditorType = settings->value("interface/defaultDeckEditorType", 1).toInt();
visualDatabaseDisplayFilterToMostRecentSetsEnabled =
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", true).toBool();
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", false).toBool();
visualDatabaseDisplayFilterToMostRecentSetsAmount =
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsamount", 10).toInt();
visualDeckEditorSampleHandSize = settings->value("interface/visualdeckeditorsamplehandsize", 7).toInt();
visualDeckEditorCardSize = settings->value("interface/visualdeckeditorcardsize", 100).toInt();
visualDatabaseDisplayCardSize = settings->value("interface/visualdatabasedisplaycardsize", 100).toInt();
edhrecCardSize = settings->value("interface/edhreccardsize", 100).toInt();
archidektPreviewSize = settings->value("interface/archidektpreviewsize", 100).toInt();
horizontalHand = settings->value("hand/horizontal", true).toBool();
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt();
@@ -355,6 +369,7 @@ SettingsCache::SettingsCache()
spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool();
createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool();
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool();
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
@@ -549,6 +564,20 @@ void SettingsCache::setThemeName(const QString &_themeName)
emit themeChanged();
}
void SettingsCache::setHomeTabBackgroundSource(const QString &_backgroundSource)
{
homeTabBackgroundSource = _backgroundSource;
settings->setValue("home/background", homeTabBackgroundSource);
emit homeTabBackgroundSourceChanged();
}
void SettingsCache::setHomeTabBackgroundShuffleFrequency(int _frequency)
{
homeTabBackgroundShuffleFrequency = _frequency;
settings->setValue("home/background/shuffleTimer", homeTabBackgroundShuffleFrequency);
emit homeTabBackgroundShuffleFrequencyChanged();
}
void SettingsCache::setTabVisualDeckStorageOpen(bool value)
{
tabVisualDeckStorageOpen = value;
@@ -641,6 +670,12 @@ void SettingsCache::setPlayToStack(QT_STATE_CHANGED_T _playToStack)
settings->setValue("interface/playtostack", playToStack);
}
void SettingsCache::setDoNotDeleteArrowsInSubPhases(QT_STATE_CHANGED_T _doNotDeleteArrowsInSubPhases)
{
doNotDeleteArrowsInSubPhases = static_cast<bool>(_doNotDeleteArrowsInSubPhases);
settings->setValue("interface/doNotDeleteArrowsInSubPhases", doNotDeleteArrowsInSubPhases);
}
void SettingsCache::setStartingHandSize(int _startingHandSize)
{
startingHandSize = _startingHandSize;
@@ -833,6 +868,34 @@ void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T va
emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation);
}
void SettingsCache::setVisualDeckEditorCardSize(int _visualDeckEditorCardSize)
{
visualDeckEditorCardSize = _visualDeckEditorCardSize;
settings->setValue("interface/visualdeckeditorcardsize", visualDeckEditorCardSize);
emit visualDeckEditorCardSizeChanged();
}
void SettingsCache::setVisualDatabaseDisplayCardSize(int _visualDatabaseDisplayCardSize)
{
visualDatabaseDisplayCardSize = _visualDatabaseDisplayCardSize;
settings->setValue("interface/visualdatabasedisplaycardsize", visualDatabaseDisplayCardSize);
emit visualDatabaseDisplayCardSizeChanged();
}
void SettingsCache::setEDHRecCardSize(int _edhrecCardSize)
{
edhrecCardSize = _edhrecCardSize;
settings->setValue("interface/edhreccardsize", edhrecCardSize);
emit edhRecCardSizeChanged();
}
void SettingsCache::setArchidektPreviewCardSize(int _archidektPreviewCardSize)
{
archidektPreviewSize = _archidektPreviewCardSize;
settings->setValue("interface/archidektpreviewsize", archidektPreviewSize);
emit archidektPreviewSizeChanged();
}
void SettingsCache::setDefaultDeckEditorType(int value)
{
defaultDeckEditorType = value;
@@ -1360,7 +1423,13 @@ void SettingsCache::setDefaultStartingLifeTotal(const int _defaultStartingLifeTo
{
defaultStartingLifeTotal = _defaultStartingLifeTotal;
settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal);
};
}
void SettingsCache::setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad)
{
shareDecklistsOnLoad = _shareDecklistsOnLoad;
settings->setValue("game/sharedecklistsonload", shareDecklistsOnLoad);
}
void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value)
{
@@ -1373,6 +1442,7 @@ void SettingsCache::setStartupCardUpdateCheckPromptForUpdate(bool value)
startupCardUpdateCheckPromptForUpdate = value;
settings->setValue("personal/startupCardUpdateCheckPromptForUpdate", startupCardUpdateCheckPromptForUpdate);
}
void SettingsCache::setStartupCardUpdateCheckAlwaysUpdate(bool value)
{
startupCardUpdateCheckAlwaysUpdate = value;
@@ -1475,11 +1545,6 @@ void SettingsCache::resetPaths()
}
}
SettingsCache &SettingsCache::instance()
{
return *settingsCache;
}
CardCounterSettings &SettingsCache::cardCounters() const
{
return *cardCounterSettings;

View File

@@ -1,23 +1,30 @@
/**
* @file cache_settings.h
* @ingroup Settings
* @brief TODO: Document this.
*/
#ifndef SETTINGSCACHE_H
#define SETTINGSCACHE_H
#include "../utility/macros.h"
#include "card_database_settings.h"
#include "card_override_settings.h"
#include "debug_settings.h"
#include "download_settings.h"
#include "game_filters_settings.h"
#include "layouts_settings.h"
#include "message_settings.h"
#include "recents_settings.h"
#include "servers_settings.h"
#include "shortcuts_settings.h"
#include <QDate>
#include <QLoggingCategory>
#include <QObject>
#include <QSize>
#include <QStringList>
#include <libcockatrice/interfaces/interface_card_database_path_provider.h>
#include <libcockatrice/interfaces/interface_network_settings_provider.h>
#include <libcockatrice/settings/card_database_settings.h>
#include <libcockatrice/settings/card_override_settings.h>
#include <libcockatrice/settings/debug_settings.h>
#include <libcockatrice/settings/download_settings.h>
#include <libcockatrice/settings/game_filters_settings.h>
#include <libcockatrice/settings/layouts_settings.h>
#include <libcockatrice/settings/message_settings.h>
#include <libcockatrice/settings/recents_settings.h>
#include <libcockatrice/settings/servers_settings.h>
#include <libcockatrice/utility/macros.h>
inline Q_LOGGING_CATEGORY(SettingsCacheLog, "settings_cache");
@@ -126,15 +133,16 @@ inline QStringList defaultTags = {
class QSettings;
class CardCounterSettings;
class SettingsCache : public QObject
class SettingsCache : public ICardDatabasePathProvider, public INetworkSettingsProvider
{
Q_OBJECT
signals:
void langChanged();
void picsPathChanged();
void cardDatabasePathChanged();
void themeChanged();
void homeTabBackgroundSourceChanged();
void homeTabBackgroundShuffleFrequencyChanged();
void picDownloadChanged();
void showStatusBarChanged(bool state);
void displayCardNamesChanged();
@@ -158,6 +166,10 @@ signals:
void visualDatabaseDisplayFilterToMostRecentSetsEnabledChanged(bool enabled);
void visualDatabaseDisplayFilterToMostRecentSetsAmountChanged(int amount);
void visualDeckEditorSampleHandSizeAmountChanged(int amount);
void visualDeckEditorCardSizeChanged();
void visualDatabaseDisplayCardSizeChanged();
void edhRecCardSizeChanged();
void archidektPreviewSizeChanged();
void horizontalHandChanged();
void handJustificationChanged();
void invertVerticalCoordinateChanged();
@@ -195,7 +207,7 @@ private:
QByteArray setsDialogGeometry;
QString lang;
QString deckPath, filtersPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName;
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName, homeTabBackgroundSource;
bool tabVisualDeckStorageOpen, tabServerOpen, tabAccountOpen, tabDeckStorageOpen, tabReplaysOpen, tabAdminOpen,
tabLogOpen;
bool checkUpdatesOnStartup;
@@ -208,6 +220,7 @@ private:
bool notifyAboutNewVersion;
bool showTipsOnStartup;
QList<int> seenTips;
int homeTabBackgroundShuffleFrequency;
bool mbDownloadSpoilers;
int updateReleaseChannel;
int maxFontSize;
@@ -218,6 +231,7 @@ private:
bool doubleClickToPlay;
bool clickPlaysAllSelected;
bool playToStack;
bool doNotDeleteArrowsInSubPhases;
int startingHandSize;
bool annotateTokens;
QByteArray tabGameSplitterSizes;
@@ -239,6 +253,10 @@ private:
QStringList visualDeckStorageDefaultTagsList;
bool visualDeckStorageSearchFolderNames;
int visualDeckStorageCardSize;
int visualDeckEditorCardSize;
int visualDatabaseDisplayCardSize;
int edhrecCardSize;
int archidektPreviewSize;
bool visualDeckStorageDrawUnusedColorIdentities;
int visualDeckStorageUnusedColorIdentitiesOpacity;
int visualDeckStorageTooltipType;
@@ -302,11 +320,12 @@ private:
bool spectatorsCanSeeEverything;
bool createGameAsSpectator;
int defaultStartingLifeTotal;
bool shareDecklistsOnLoad;
int keepalive;
int timeout;
void translateLegacySettings();
QString getSafeConfigPath(QString configEntry, QString defaultPath) const;
QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const;
[[nodiscard]] QString getSafeConfigPath(QString configEntry, QString defaultPath) const;
[[nodiscard]] QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const;
void loadPaths();
bool rememberGameSettings;
QList<ReleaseChannel *> releaseChannels;
@@ -318,129 +337,137 @@ public:
SettingsCache();
QString getDataPath();
QString getSettingsPath();
QString getCachePath() const;
QString getNetworkCachePath() const;
const QByteArray &getMainWindowGeometry() const
[[nodiscard]] QString getCachePath() const;
[[nodiscard]] QString getNetworkCachePath() const;
[[nodiscard]] const QByteArray &getMainWindowGeometry() const
{
return mainWindowGeometry;
}
const QByteArray &getTokenDialogGeometry() const
[[nodiscard]] const QByteArray &getTokenDialogGeometry() const
{
return tokenDialogGeometry;
}
const QByteArray &getSetsDialogGeometry() const
[[nodiscard]] const QByteArray &getSetsDialogGeometry() const
{
return setsDialogGeometry;
}
QString getLang() const
[[nodiscard]] QString getLang() const
{
return lang;
}
QString getDeckPath() const
[[nodiscard]] QString getDeckPath() const
{
return deckPath;
}
QString getFiltersPath() const
[[nodiscard]] QString getFiltersPath() const
{
return filtersPath;
}
QString getReplaysPath() const
[[nodiscard]] QString getReplaysPath() const
{
return replaysPath;
}
QString getThemesPath() const
[[nodiscard]] QString getThemesPath() const
{
return themesPath;
}
QString getPicsPath() const
[[nodiscard]] QString getPicsPath() const
{
return picsPath;
}
QString getRedirectCachePath() const
[[nodiscard]] QString getRedirectCachePath() const
{
return redirectCachePath;
}
QString getCustomPicsPath() const
[[nodiscard]] QString getCustomPicsPath() const
{
return customPicsPath;
}
QString getCustomCardDatabasePath() const
[[nodiscard]] QString getCustomCardDatabasePath() const override
{
return customCardDatabasePath;
}
QString getCardDatabasePath() const
[[nodiscard]] QString getCardDatabasePath() const override
{
return cardDatabasePath;
}
QString getSpoilerCardDatabasePath() const
[[nodiscard]] QString getSpoilerCardDatabasePath() const override
{
return spoilerDatabasePath;
}
QString getTokenDatabasePath() const
[[nodiscard]] QString getTokenDatabasePath() const override
{
return tokenDatabasePath;
}
QString getThemeName() const
[[nodiscard]] QString getThemeName() const
{
return themeName;
}
bool getTabVisualDeckStorageOpen() const
[[nodiscard]] QString getHomeTabBackgroundSource() const
{
return homeTabBackgroundSource;
}
[[nodiscard]] int getHomeTabBackgroundShuffleFrequency() const
{
return homeTabBackgroundShuffleFrequency;
}
[[nodiscard]] bool getTabVisualDeckStorageOpen() const
{
return tabVisualDeckStorageOpen;
}
bool getTabServerOpen() const
[[nodiscard]] bool getTabServerOpen() const
{
return tabServerOpen;
}
bool getTabAccountOpen() const
[[nodiscard]] bool getTabAccountOpen() const
{
return tabAccountOpen;
}
bool getTabDeckStorageOpen() const
[[nodiscard]] bool getTabDeckStorageOpen() const
{
return tabDeckStorageOpen;
}
bool getTabReplaysOpen() const
[[nodiscard]] bool getTabReplaysOpen() const
{
return tabReplaysOpen;
}
bool getTabAdminOpen() const
[[nodiscard]] bool getTabAdminOpen() const
{
return tabAdminOpen;
}
bool getTabLogOpen() const
[[nodiscard]] bool getTabLogOpen() const
{
return tabLogOpen;
}
QString getChatMentionColor() const
[[nodiscard]] QString getChatMentionColor() const
{
return chatMentionColor;
}
QString getChatHighlightColor() const
[[nodiscard]] QString getChatHighlightColor() const
{
return chatHighlightColor;
}
bool getPicDownload() const
[[nodiscard]] bool getPicDownload() const
{
return picDownload;
}
bool getShowStatusBar() const
[[nodiscard]] bool getShowStatusBar() const
{
return showStatusBar;
}
bool getNotificationsEnabled() const
[[nodiscard]] bool getNotificationsEnabled() const
{
return notificationsEnabled;
}
bool getSpectatorNotificationsEnabled() const
[[nodiscard]] bool getSpectatorNotificationsEnabled() const
{
return spectatorNotificationsEnabled;
}
bool getBuddyConnectNotificationsEnabled() const
[[nodiscard]] bool getBuddyConnectNotificationsEnabled() const
{
return buddyConnectNotificationsEnabled;
}
bool getCheckUpdatesOnStartup() const
[[nodiscard]] bool getCheckUpdatesOnStartup() const
{
return checkUpdatesOnStartup;
}
@@ -452,243 +479,263 @@ public:
{
return startupCardUpdateCheckAlwaysUpdate;
}
int getCardUpdateCheckInterval() const
[[nodiscard]] int getCardUpdateCheckInterval() const
{
return cardUpdateCheckInterval;
}
QDate getLastCardUpdateCheck() const
[[nodiscard]] QDate getLastCardUpdateCheck() const
{
return lastCardUpdateCheck;
}
bool getCardUpdateCheckRequired() const
[[nodiscard]] bool getCardUpdateCheckRequired() const
{
return getLastCardUpdateCheck().daysTo(QDateTime::currentDateTime().date()) >= getCardUpdateCheckInterval() &&
getLastCardUpdateCheck() != QDateTime::currentDateTime().date();
}
bool getNotifyAboutUpdates() const
[[nodiscard]] bool getNotifyAboutUpdates() const override
{
return notifyAboutUpdates;
}
bool getNotifyAboutNewVersion() const
[[nodiscard]] bool getNotifyAboutNewVersion() const
{
return notifyAboutNewVersion;
}
bool getShowTipsOnStartup() const
[[nodiscard]] bool getShowTipsOnStartup() const
{
return showTipsOnStartup;
}
QList<int> getSeenTips() const
[[nodiscard]] QList<int> getSeenTips() const
{
return seenTips;
}
int getUpdateReleaseChannelIndex() const
[[nodiscard]] int getUpdateReleaseChannelIndex() const
{
return updateReleaseChannel;
}
ReleaseChannel *getUpdateReleaseChannel() const
[[nodiscard]] ReleaseChannel *getUpdateReleaseChannel() const
{
return releaseChannels.at(qMax(0, updateReleaseChannel));
}
QList<ReleaseChannel *> getUpdateReleaseChannels() const
[[nodiscard]] QList<ReleaseChannel *> getUpdateReleaseChannels() const
{
return releaseChannels;
}
bool getDoubleClickToPlay() const
[[nodiscard]] bool getDoubleClickToPlay() const
{
return doubleClickToPlay;
}
bool getClickPlaysAllSelected() const
[[nodiscard]] bool getClickPlaysAllSelected() const
{
return clickPlaysAllSelected;
}
bool getPlayToStack() const
[[nodiscard]] bool getPlayToStack() const
{
return playToStack;
}
int getStartingHandSize() const
[[nodiscard]] bool getDoNotDeleteArrowsInSubPhases() const
{
return doNotDeleteArrowsInSubPhases;
}
[[nodiscard]] int getStartingHandSize() const
{
return startingHandSize;
}
bool getAnnotateTokens() const
[[nodiscard]] bool getAnnotateTokens() const
{
return annotateTokens;
}
QByteArray getTabGameSplitterSizes() const
[[nodiscard]] QByteArray getTabGameSplitterSizes() const
{
return tabGameSplitterSizes;
}
bool getShowShortcuts() const
[[nodiscard]] bool getShowShortcuts() const
{
return showShortcuts;
}
bool getDisplayCardNames() const
[[nodiscard]] bool getDisplayCardNames() const
{
return displayCardNames;
}
bool getOverrideAllCardArtWithPersonalPreference() const
[[nodiscard]] bool getOverrideAllCardArtWithPersonalPreference() const
{
return overrideAllCardArtWithPersonalPreference;
}
bool getBumpSetsWithCardsInDeckToTop() const
[[nodiscard]] bool getBumpSetsWithCardsInDeckToTop() const
{
return bumpSetsWithCardsInDeckToTop;
}
int getPrintingSelectorSortOrder() const
[[nodiscard]] int getPrintingSelectorSortOrder() const
{
return printingSelectorSortOrder;
}
int getPrintingSelectorCardSize() const
[[nodiscard]] int getPrintingSelectorCardSize() const
{
return printingSelectorCardSize;
}
bool getIncludeRebalancedCards() const
[[nodiscard]] bool getIncludeRebalancedCards() const
{
return includeRebalancedCards;
}
bool getPrintingSelectorNavigationButtonsVisible() const
[[nodiscard]] bool getPrintingSelectorNavigationButtonsVisible() const
{
return printingSelectorNavigationButtonsVisible;
}
bool getDeckEditorBannerCardComboBoxVisible() const
[[nodiscard]] bool getDeckEditorBannerCardComboBoxVisible() const
{
return deckEditorBannerCardComboBoxVisible;
}
bool getDeckEditorTagsWidgetVisible() const
[[nodiscard]] bool getDeckEditorTagsWidgetVisible() const
{
return deckEditorTagsWidgetVisible;
}
int getVisualDeckStorageSortingOrder() const
[[nodiscard]] int getVisualDeckStorageSortingOrder() const
{
return visualDeckStorageSortingOrder;
}
bool getVisualDeckStorageShowFolders() const
[[nodiscard]] bool getVisualDeckStorageShowFolders() const
{
return visualDeckStorageShowFolders;
}
bool getVisualDeckStorageShowTagFilter() const
[[nodiscard]] bool getVisualDeckStorageShowTagFilter() const
{
return visualDeckStorageShowTagFilter;
}
QStringList getVisualDeckStorageDefaultTagsList() const
[[nodiscard]] QStringList getVisualDeckStorageDefaultTagsList() const
{
return visualDeckStorageDefaultTagsList;
}
bool getVisualDeckStorageSearchFolderNames() const
[[nodiscard]] bool getVisualDeckStorageSearchFolderNames() const
{
return visualDeckStorageSearchFolderNames;
}
bool getVisualDeckStorageShowBannerCardComboBox() const
[[nodiscard]] bool getVisualDeckStorageShowBannerCardComboBox() const
{
return visualDeckStorageShowBannerCardComboBox;
}
bool getVisualDeckStorageShowTagsOnDeckPreviews() const
[[nodiscard]] bool getVisualDeckStorageShowTagsOnDeckPreviews() const
{
return visualDeckStorageShowTagsOnDeckPreviews;
}
int getVisualDeckStorageCardSize() const
[[nodiscard]] int getVisualDeckStorageCardSize() const
{
return visualDeckStorageCardSize;
}
bool getVisualDeckStorageDrawUnusedColorIdentities() const
[[nodiscard]] bool getVisualDeckStorageDrawUnusedColorIdentities() const
{
return visualDeckStorageDrawUnusedColorIdentities;
}
int getVisualDeckStorageUnusedColorIdentitiesOpacity() const
[[nodiscard]] int getVisualDeckStorageUnusedColorIdentitiesOpacity() const
{
return visualDeckStorageUnusedColorIdentitiesOpacity;
}
int getVisualDeckStorageTooltipType() const
[[nodiscard]] int getVisualDeckStorageTooltipType() const
{
return visualDeckStorageTooltipType;
}
bool getVisualDeckStoragePromptForConversion() const
[[nodiscard]] bool getVisualDeckStoragePromptForConversion() const
{
return visualDeckStoragePromptForConversion;
}
bool getVisualDeckStorageAlwaysConvert() const
[[nodiscard]] bool getVisualDeckStorageAlwaysConvert() const
{
return visualDeckStorageAlwaysConvert;
}
bool getVisualDeckStorageInGame() const
[[nodiscard]] bool getVisualDeckStorageInGame() const
{
return visualDeckStorageInGame;
}
bool getVisualDeckStorageSelectionAnimation() const
[[nodiscard]] bool getVisualDeckStorageSelectionAnimation() const
{
return visualDeckStorageSelectionAnimation;
}
int getDefaultDeckEditorType() const
[[nodiscard]] int getVisualDeckEditorCardSize() const
{
return visualDeckEditorCardSize;
}
[[nodiscard]] int getVisualDatabaseDisplayCardSize() const
{
return visualDatabaseDisplayCardSize;
}
[[nodiscard]] int getEDHRecCardSize() const
{
return edhrecCardSize;
}
[[nodiscard]] int getArchidektPreviewSize() const
{
return archidektPreviewSize;
}
[[nodiscard]] int getDefaultDeckEditorType() const
{
return defaultDeckEditorType;
}
bool getVisualDatabaseDisplayFilterToMostRecentSetsEnabled() const
[[nodiscard]] bool getVisualDatabaseDisplayFilterToMostRecentSetsEnabled() const
{
return visualDatabaseDisplayFilterToMostRecentSetsEnabled;
}
int getVisualDatabaseDisplayFilterToMostRecentSetsAmount() const
[[nodiscard]] int getVisualDatabaseDisplayFilterToMostRecentSetsAmount() const
{
return visualDatabaseDisplayFilterToMostRecentSetsAmount;
}
int getVisualDeckEditorSampleHandSize() const
[[nodiscard]] int getVisualDeckEditorSampleHandSize() const
{
return visualDeckEditorSampleHandSize;
}
bool getHorizontalHand() const
[[nodiscard]] bool getHorizontalHand() const
{
return horizontalHand;
}
bool getInvertVerticalCoordinate() const
[[nodiscard]] bool getInvertVerticalCoordinate() const
{
return invertVerticalCoordinate;
}
int getMinPlayersForMultiColumnLayout() const
[[nodiscard]] int getMinPlayersForMultiColumnLayout() const
{
return minPlayersForMultiColumnLayout;
}
bool getTapAnimation() const
[[nodiscard]] bool getTapAnimation() const
{
return tapAnimation;
}
bool getAutoRotateSidewaysLayoutCards() const
[[nodiscard]] bool getAutoRotateSidewaysLayoutCards() const
{
return autoRotateSidewaysLayoutCards;
}
bool getOpenDeckInNewTab() const
[[nodiscard]] bool getOpenDeckInNewTab() const
{
return openDeckInNewTab;
}
int getRewindBufferingMs() const
[[nodiscard]] int getRewindBufferingMs() const
{
return rewindBufferingMs;
}
bool getChatMention() const
[[nodiscard]] bool getChatMention() const
{
return chatMention;
}
bool getChatMentionCompleter() const
[[nodiscard]] bool getChatMentionCompleter() const
{
return chatMentionCompleter;
}
bool getChatMentionForeground() const
[[nodiscard]] bool getChatMentionForeground() const
{
return chatMentionForeground;
}
bool getChatHighlightForeground() const
[[nodiscard]] bool getChatHighlightForeground() const
{
return chatHighlightForeground;
}
/**
* Currently selected index for the `Group by X` QComboBox
*/
int getZoneViewGroupByIndex() const
[[nodiscard]] int getZoneViewGroupByIndex() const
{
return zoneViewGroupByIndex;
}
/**
* Currently selected index for the `Sort by X` QComboBox
*/
int getZoneViewSortByIndex() const
[[nodiscard]] int getZoneViewSortByIndex() const
{
return zoneViewSortByIndex;
}
@@ -696,144 +743,148 @@ public:
Returns if the view should be sorted into pile view.
@return zoneViewPileView if the view should be sorted into pile view.
*/
bool getZoneViewPileView() const
[[nodiscard]] bool getZoneViewPileView() const
{
return zoneViewPileView;
}
bool getSoundEnabled() const
[[nodiscard]] bool getSoundEnabled() const
{
return soundEnabled;
}
QString getSoundThemeName() const
[[nodiscard]] QString getSoundThemeName() const
{
return soundThemeName;
}
bool getIgnoreUnregisteredUsers() const
[[nodiscard]] bool getIgnoreUnregisteredUsers() const
{
return ignoreUnregisteredUsers;
}
bool getIgnoreUnregisteredUserMessages() const
[[nodiscard]] bool getIgnoreUnregisteredUserMessages() const
{
return ignoreUnregisteredUserMessages;
}
int getPixmapCacheSize() const
[[nodiscard]] int getPixmapCacheSize() const
{
return pixmapCacheSize;
}
int getNetworkCacheSizeInMB() const
[[nodiscard]] int getNetworkCacheSizeInMB() const
{
return networkCacheSize;
}
int getRedirectCacheTtl() const
[[nodiscard]] int getRedirectCacheTtl() const
{
return redirectCacheTtl;
}
bool getScaleCards() const
[[nodiscard]] bool getScaleCards() const
{
return scaleCards;
}
int getStackCardOverlapPercent() const
[[nodiscard]] int getStackCardOverlapPercent() const
{
return verticalCardOverlapPercent;
}
bool getShowMessagePopup() const
[[nodiscard]] bool getShowMessagePopup() const
{
return showMessagePopups;
}
bool getShowMentionPopup() const
[[nodiscard]] bool getShowMentionPopup() const
{
return showMentionPopups;
}
bool getRoomHistory() const
[[nodiscard]] bool getRoomHistory() const
{
return roomHistory;
}
bool getLeftJustified() const
[[nodiscard]] bool getLeftJustified() const
{
return leftJustified;
}
int getMasterVolume() const
[[nodiscard]] int getMasterVolume() const
{
return masterVolume;
}
int getCardInfoViewMode() const
[[nodiscard]] int getCardInfoViewMode() const
{
return cardInfoViewMode;
}
QStringList getCountries() const;
QString getHighlightWords() const
[[nodiscard]] QStringList getCountries() const;
[[nodiscard]] QString getHighlightWords() const
{
return highlightWords;
}
QString getGameDescription() const
[[nodiscard]] QString getGameDescription() const
{
return gameDescription;
}
int getMaxPlayers() const
[[nodiscard]] int getMaxPlayers() const
{
return maxPlayers;
}
QString getGameTypes() const
[[nodiscard]] QString getGameTypes() const
{
return gameTypes;
}
bool getOnlyBuddies() const
[[nodiscard]] bool getOnlyBuddies() const
{
return onlyBuddies;
}
bool getOnlyRegistered() const
[[nodiscard]] bool getOnlyRegistered() const
{
return onlyRegistered;
}
bool getSpectatorsAllowed() const
[[nodiscard]] bool getSpectatorsAllowed() const
{
return spectatorsAllowed;
}
bool getSpectatorsNeedPassword() const
[[nodiscard]] bool getSpectatorsNeedPassword() const
{
return spectatorsNeedPassword;
}
bool getSpectatorsCanTalk() const
[[nodiscard]] bool getSpectatorsCanTalk() const
{
return spectatorsCanTalk;
}
bool getSpectatorsCanSeeEverything() const
[[nodiscard]] bool getSpectatorsCanSeeEverything() const
{
return spectatorsCanSeeEverything;
}
int getDefaultStartingLifeTotal() const
[[nodiscard]] int getDefaultStartingLifeTotal() const
{
return defaultStartingLifeTotal;
}
bool getCreateGameAsSpectator() const
[[nodiscard]] bool getShareDecklistsOnLoad() const
{
return shareDecklistsOnLoad;
}
[[nodiscard]] bool getCreateGameAsSpectator() const
{
return createGameAsSpectator;
}
bool getRememberGameSettings() const
[[nodiscard]] bool getRememberGameSettings() const
{
return rememberGameSettings;
}
int getKeepAlive() const
[[nodiscard]] int getKeepAlive() const override
{
return keepalive;
}
int getTimeOut() const
[[nodiscard]] int getTimeOut() const override
{
return timeout;
}
int getMaxFontSize() const
[[nodiscard]] int getMaxFontSize() const
{
return maxFontSize;
}
void setClientID(const QString &clientID);
void setClientVersion(const QString &clientVersion);
void setKnownMissingFeatures(const QString &_knownMissingFeatures);
void setKnownMissingFeatures(const QString &_knownMissingFeatures) override;
void setUseTearOffMenus(bool _useTearOffMenus);
void setCardViewInitialRowsMax(int _cardViewInitialRowsMax);
void setCardViewExpandedRowsMax(int value);
void setCloseEmptyCardView(QT_STATE_CHANGED_T value);
void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value);
QString getClientID()
QString getClientID() override
{
return clientID;
}
@@ -841,7 +892,7 @@ public:
{
return clientVersion;
}
QString getKnownMissingFeatures()
QString getKnownMissingFeatures() override
{
return knownMissingFeatures;
}
@@ -849,73 +900,73 @@ public:
{
return useTearOffMenus;
}
int getCardViewInitialRowsMax() const
[[nodiscard]] int getCardViewInitialRowsMax() const
{
return cardViewInitialRowsMax;
}
int getCardViewExpandedRowsMax() const
[[nodiscard]] int getCardViewExpandedRowsMax() const
{
return cardViewExpandedRowsMax;
}
bool getCloseEmptyCardView() const
[[nodiscard]] bool getCloseEmptyCardView() const
{
return closeEmptyCardView;
}
bool getFocusCardViewSearchBar() const
[[nodiscard]] bool getFocusCardViewSearchBar() const
{
return focusCardViewSearchBar;
}
ShortcutsSettings &shortcuts() const
[[nodiscard]] ShortcutsSettings &shortcuts() const
{
return *shortcutsSettings;
}
CardDatabaseSettings &cardDatabase() const
[[nodiscard]] CardDatabaseSettings *cardDatabase() const
{
return *cardDatabaseSettings;
return cardDatabaseSettings;
}
ServersSettings &servers() const
[[nodiscard]] ServersSettings &servers() const
{
return *serversSettings;
}
MessageSettings &messages() const
[[nodiscard]] MessageSettings &messages() const
{
return *messageSettings;
}
GameFiltersSettings &gameFilters() const
[[nodiscard]] GameFiltersSettings &gameFilters() const
{
return *gameFiltersSettings;
}
LayoutsSettings &layouts() const
[[nodiscard]] LayoutsSettings &layouts() const
{
return *layoutsSettings;
}
DownloadSettings &downloads() const
[[nodiscard]] DownloadSettings &downloads() const
{
return *downloadSettings;
}
RecentsSettings &recents() const
[[nodiscard]] RecentsSettings &recents() const
{
return *recentsSettings;
}
CardOverrideSettings &cardOverrides() const
[[nodiscard]] CardOverrideSettings &cardOverrides() const
{
return *cardOverrideSettings;
}
DebugSettings &debug() const
[[nodiscard]] DebugSettings &debug() const
{
return *debugSettings;
}
CardCounterSettings &cardCounters() const;
[[nodiscard]] CardCounterSettings &cardCounters() const;
bool getIsPortableBuild() const
[[nodiscard]] bool getIsPortableBuild() const
{
return isPortableBuild;
}
bool getDownloadSpoilersStatus() const
[[nodiscard]] bool getDownloadSpoilersStatus() const
{
return mbDownloadSpoilers;
}
bool getRoundCardCorners() const
[[nodiscard]] bool getRoundCardCorners() const
{
return roundCardCorners;
}
@@ -942,6 +993,8 @@ public slots:
void setSpoilerDatabasePath(const QString &_spoilerDatabasePath);
void setTokenDatabasePath(const QString &_tokenDatabasePath);
void setThemeName(const QString &_themeName);
void setHomeTabBackgroundSource(const QString &_backgroundSource);
void setHomeTabBackgroundShuffleFrequency(int _frequency);
void setTabVisualDeckStorageOpen(bool value);
void setTabServerOpen(bool value);
void setTabAccountOpen(bool value);
@@ -959,6 +1012,7 @@ public slots:
void setDoubleClickToPlay(QT_STATE_CHANGED_T _doubleClickToPlay);
void setClickPlaysAllSelected(QT_STATE_CHANGED_T _clickPlaysAllSelected);
void setPlayToStack(QT_STATE_CHANGED_T _playToStack);
void setDoNotDeleteArrowsInSubPhases(QT_STATE_CHANGED_T _doNotDeleteArrowsInSubPhases);
void setStartingHandSize(int _startingHandSize);
void setAnnotateTokens(QT_STATE_CHANGED_T _annotateTokens);
void setTabGameSplitterSizes(const QByteArray &_tabGameSplitterSizes);
@@ -987,6 +1041,10 @@ public slots:
void setVisualDeckStorageAlwaysConvert(bool _visualDeckStorageAlwaysConvert);
void setVisualDeckStorageInGame(QT_STATE_CHANGED_T value);
void setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T value);
void setVisualDeckEditorCardSize(int _visualDeckEditorCardSize);
void setVisualDatabaseDisplayCardSize(int _visualDatabaseDisplayCardSize);
void setEDHRecCardSize(int _EDHRecCardSize);
void setArchidektPreviewCardSize(int _archidektPreviewCardSize);
void setDefaultDeckEditorType(int value);
void setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled);
void setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int _amount);
@@ -1032,6 +1090,7 @@ public slots:
void setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything);
void setCreateGameAsSpectator(const bool _createGameAsSpectator);
void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal);
void setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad);
void setRememberGameSettings(const bool _rememberGameSettings);
void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value);
void setStartupCardUpdateCheckPromptForUpdate(bool value);

View File

@@ -5,7 +5,7 @@
#include <QtMath>
CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent)
: SettingsManager(settingsPath + "global.ini", parent)
: SettingsManager(settingsPath + "global.ini", "cards", "counters", parent)
{
}

View File

@@ -1,9 +1,13 @@
/**
* @file card_counter_settings.h
* @ingroup GameSettings
* @brief TODO: Document this.
*/
#ifndef CARD_COUNTER_SETTINGS_H
#define CARD_COUNTER_SETTINGS_H
#include "settings_manager.h"
#include <QObject>
#include <libcockatrice/settings/settings_manager.h>
class QSettings;
class QColor;
@@ -15,9 +19,9 @@ class CardCounterSettings : public SettingsManager
public:
CardCounterSettings(const QString &settingsPath, QObject *parent = nullptr);
QColor color(int counterId) const;
[[nodiscard]] QColor color(int counterId) const;
QString displayName(int counterId) const;
[[nodiscard]] QString displayName(int counterId) const;
public slots:
void setColor(int counterId, const QColor &color);

View File

@@ -1,7 +1,12 @@
/**
* @file shortcut_treeview.h
* @ingroup CoreSettings
* @brief TODO: Document this.
*/
#ifndef SHORTCUT_TREEVIEW_H
#define SHORTCUT_TREEVIEW_H
#include <QModelIndex>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTreeView>
@@ -16,7 +21,7 @@ public:
explicit ShortcutFilterProxyModel(QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
class ShortcutTreeView : public QTreeView

View File

@@ -115,6 +115,13 @@ ShortcutKey ShortcutsSettings::getShortcut(const QString &name) const
return getDefaultShortcut(name);
}
/**
* Gets the first shortcut for the given action.
*
* NOTE: In most cases you should be using ShortcutsSettings::getShortcut instead,
* as that will return all shortcuts if there are multiple shortcuts.
* The only reason to use this method is if an object does not accept multiple shortcuts, such as with QButtons.
*/
QKeySequence ShortcutsSettings::getSingleShortcut(const QString &name) const
{
return getShortcut(name).at(0);

View File

@@ -1,3 +1,9 @@
/**
* @file shortcuts_settings.h
* @ingroup CoreSettings
* @brief TODO: Document this.
*/
#ifndef SHORTCUTSSETTINGS_H
#define SHORTCUTSSETTINGS_H
@@ -27,6 +33,7 @@ public:
Move_bottom,
Gameplay,
Drawing,
Hand,
Chat_room,
Game_window,
Load_deck,
@@ -65,6 +72,8 @@ public:
return QApplication::translate("shortcutsTab", "Gameplay");
case Drawing:
return QApplication::translate("shortcutsTab", "Drawing");
case Hand:
return QApplication::translate("shortcutsTab", "Hand");
case Chat_room:
return QApplication::translate("shortcutsTab", "Chat Room");
case Game_window:
@@ -90,15 +99,15 @@ public:
void setSequence(const QList &_sequence)
{
QList::operator=(_sequence);
};
QString getName() const
}
[[nodiscard]] QString getName() const
{
return QApplication::translate("shortcutsTab", name.toUtf8().data());
};
QString getGroupName() const
}
[[nodiscard]] QString getGroupName() const
{
return ShortcutGroup::getGroupName(group);
};
}
private:
QString name;
@@ -111,24 +120,24 @@ class ShortcutsSettings : public QObject
public:
explicit ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
ShortcutKey getDefaultShortcut(const QString &name) const;
ShortcutKey getShortcut(const QString &name) const;
QKeySequence getSingleShortcut(const QString &name) const;
QString getDefaultShortcutString(const QString &name) const;
QString getShortcutString(const QString &name) const;
QString getShortcutFriendlyName(const QString &shortcutName) const;
QList<QString> getAllShortcutKeys() const
[[nodiscard]] ShortcutKey getDefaultShortcut(const QString &name) const;
[[nodiscard]] ShortcutKey getShortcut(const QString &name) const;
[[nodiscard]] QKeySequence getSingleShortcut(const QString &name) const;
[[nodiscard]] QString getDefaultShortcutString(const QString &name) const;
[[nodiscard]] QString getShortcutString(const QString &name) const;
[[nodiscard]] QString getShortcutFriendlyName(const QString &shortcutName) const;
[[nodiscard]] QList<QString> getAllShortcutKeys() const
{
return shortCuts.keys();
};
}
void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence);
void setShortcuts(const QString &name, const QKeySequence &Sequence);
void setShortcuts(const QString &name, const QString &sequences);
bool isKeyAllowed(const QString &name, const QString &sequences) const;
bool isValid(const QString &name, const QString &sequences) const;
QStringList findOverlaps(const QString &name, const QString &sequences) const;
[[nodiscard]] bool isKeyAllowed(const QString &name, const QString &sequences) const;
[[nodiscard]] bool isValid(const QString &name, const QString &sequences) const;
[[nodiscard]] QStringList findOverlaps(const QString &name, const QString &sequences) const;
void resetAllShortcuts();
void clearAllShortcuts();
@@ -143,8 +152,8 @@ private:
QString settingsFilePath;
QHash<QString, ShortcutKey> shortCuts;
QString stringifySequence(const QList<QKeySequence> &Sequence) const;
QList<QKeySequence> parseSequenceString(const QString &stringSequence) const;
[[nodiscard]] QString stringifySequence(const QList<QKeySequence> &Sequence) const;
[[nodiscard]] QList<QKeySequence> parseSequenceString(const QString &stringSequence) const;
const QHash<QString, ShortcutKey> defaultShortCuts = {
{"MainWindow/aCheckCardUpdates", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Check for Card Updates..."),
@@ -413,6 +422,10 @@ private:
{"Player/aSetCounter_storm", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Other Counters..."),
parseSequenceString("Ctrl+\\"),
ShortcutGroup::Player_Counters)},
{"Player/aIncrementAllCardCounters",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increment all card counters"),
parseSequenceString("Ctrl+Shift+A"),
ShortcutGroup::Playing_Area)},
{"Player/aIncP", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Power (+1/+0)"),
parseSequenceString("Ctrl++;Ctrl+="),
ShortcutGroup::Power_Toughness)},
@@ -530,6 +543,9 @@ private:
{"Player/aSelectColumn", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Column"),
parseSequenceString("Ctrl+Shift+C"),
ShortcutGroup::Playing_Area)},
{"Player/aRevealToAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Selected Cards to All Players"),
parseSequenceString(""),
ShortcutGroup::Playing_Area)},
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
parseSequenceString("Ctrl+B"),
ShortcutGroup::Move_selected)},
@@ -659,6 +675,22 @@ private:
{"Player/aAlwaysLookAtTopCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Always Look At Top Card"),
parseSequenceString("Ctrl+Shift+N"),
ShortcutGroup::Drawing)},
{"Player/aSortHandByName", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Name"),
parseSequenceString(""),
ShortcutGroup::Hand)},
{"Player/aSortHandByType", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Type"),
parseSequenceString("Ctrl+Shift+H"),
ShortcutGroup::Hand)},
{"Player/aSortHandByManaValue", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Mana Value"),
parseSequenceString(""),
ShortcutGroup::Hand)},
{"Player/aRevealHandToAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Hand to All Players"),
parseSequenceString(""),
ShortcutGroup::Hand)},
{"Player/aRevealRandomHandCardToAll",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Random Card to All Players"),
parseSequenceString(""),
ShortcutGroup::Hand)},
{"Player/aRotateViewCW", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Rotate View Clockwise"),
parseSequenceString(""),
ShortcutGroup::Gameplay)},
@@ -700,6 +732,8 @@ private:
ShortcutGroup::Replays)},
{"Tabs/aTabDeckEditor",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Deck Editor"), parseSequenceString(""), ShortcutGroup::Tabs)},
{"Tabs/aTabHome",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Home"), parseSequenceString(""), ShortcutGroup::Tabs)},
{"Tabs/aTabVisualDeckStorage", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Visual Deck Storage"),
parseSequenceString(""),
ShortcutGroup::Tabs)},

View File

@@ -1,6 +1,6 @@
#include "sound_engine.h"
#include "../settings/cache_settings.h"
#include "settings/cache_settings.h"
#include <QDir>
#include <QMediaPlayer>

View File

@@ -1,3 +1,9 @@
/**
* @file sound_engine.h
* @ingroup Core
* @brief TODO: Document this.
*/
#ifndef SOUNDENGINE_H
#define SOUNDENGINE_H

View File

@@ -1,158 +0,0 @@
#ifndef TAB_GENERIC_DECK_EDITOR_H
#define TAB_GENERIC_DECK_EDITOR_H
#include "../../game/cards/card_info.h"
#include "../menus/deck_editor/deck_editor_menu.h"
#include "../ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
#include "../ui/widgets/deck_editor/deck_editor_database_display_widget.h"
#include "../ui/widgets/deck_editor/deck_editor_deck_dock_widget.h"
#include "../ui/widgets/deck_editor/deck_editor_filter_dock_widget.h"
#include "../ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h"
#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "tab.h"
class CardDatabaseModel;
class CardDatabaseDisplayModel;
class CardInfoFrameWidget;
class DeckLoader;
class DeckEditorMenu;
class DeckEditorCardInfoDockWidget;
class DeckEditorDatabaseDisplayWidget;
class DeckEditorDeckDockWidget;
class DeckEditorFilterDockWidget;
class DeckEditorPrintingSelectorDockWidget;
class DeckPreviewDeckTagsDisplayWidget;
class Response;
class FilterTreeModel;
class FilterBuilder;
class QTreeView;
class QTextEdit;
class QLabel;
class QComboBox;
class QGroupBox;
class QMessageBox;
class QHBoxLayout;
class QVBoxLayout;
class QPushButton;
class QDockWidget;
class QMenu;
class QAction;
class AbstractTabDeckEditor : public Tab
{
Q_OBJECT
friend class DeckEditorMenu;
public:
explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor);
// UI and Navigation
virtual void createMenus() = 0;
[[nodiscard]] virtual QString getTabText() const override = 0;
bool confirmClose();
virtual void retranslateUi() override = 0;
// Deck Management
void openDeck(DeckLoader *deck);
DeckLoader *getDeckList() const;
void setModified(bool _windowModified);
// UI Elements
DeckEditorMenu *deckMenu;
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget;
DeckEditorCardInfoDockWidget *cardInfoDockWidget;
DeckEditorDeckDockWidget *deckDockWidget;
DeckEditorFilterDockWidget *filterDockWidget;
DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget;
public slots:
virtual void onDeckChanged();
virtual void onDeckModified();
void updateCard(CardInfoPtr _card);
void actAddCard(CardInfoPtr info);
void actAddCardToSideboard(CardInfoPtr info);
void actDecrementCard(CardInfoPtr info);
void actDecrementCardFromSideboard(CardInfoPtr info);
void actOpenRecent(const QString &fileName);
void filterTreeChanged(FilterTree *filterTree);
void closeRequest(bool forced = false) override;
virtual void showPrintingSelector() = 0;
virtual void dockTopLevelChanged(bool topLevel) = 0;
signals:
void openDeckEditor(const DeckLoader *deckLoader);
void deckEditorClosing(AbstractTabDeckEditor *tab);
void decrementCard(CardInfoPtr card, QString zoneName);
protected slots:
// Deck Operations
virtual void actNewDeck();
void cleanDeckAndResetModified();
virtual void actLoadDeck();
bool actSaveDeck();
virtual bool actSaveDeckAs();
virtual void actLoadDeckFromClipboard();
void actEditDeckInClipboard();
void actEditDeckInClipboardRaw();
void actSaveDeckToClipboard();
void actSaveDeckToClipboardNoSetInfo();
void actSaveDeckToClipboardRaw();
void actSaveDeckToClipboardRawNoSetInfo();
void actPrintDeck();
void actExportDeckDecklist();
void actExportDeckDecklistXyz();
void actAnalyzeDeckDeckstats();
void actAnalyzeDeckTappedout();
// Remote Save
void saveDeckRemoteFinished(const Response &r);
// UI Layout Management
virtual void loadLayout() = 0;
virtual void restartLayout() = 0;
virtual void freeDocksSize() = 0;
virtual void refreshShortcuts() = 0;
bool eventFilter(QObject *o, QEvent *e) override;
virtual void dockVisibleTriggered() = 0;
virtual void dockFloatingTriggered() = 0;
private:
virtual void setDeck(DeckLoader *_deck);
void editDeckInClipboard(bool annotated);
void exportToDecklistWebsite(DeckLoader::DecklistWebsite website);
protected:
/**
* @brief Enum for selecting deck open location
*/
enum DeckOpenLocation
{
CANCELLED,
SAME_TAB,
NEW_TAB
};
DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true);
QMessageBox *createSaveConfirmationWindow();
bool isBlankNewDeck() const;
// Helper functions for card actions
void addCardHelper(CardInfoPtr info, QString zoneName);
void actSwapCard(CardInfoPtr info, QString zoneName);
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
// UI Menu Elements
QMenu *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu;
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
bool modified = false;
};
#endif // TAB_GENERIC_DECK_EDITOR_H

View File

@@ -1,41 +0,0 @@
#ifndef WINDOW_DECKEDITOR_H
#define WINDOW_DECKEDITOR_H
#include "../../game/cards/card_info.h"
#include "../game_logic/key_signals.h"
#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "abstract_tab_deck_editor.h"
class CardDatabaseModel;
class CardDatabaseDisplayModel;
class DeckListModel;
class QLabel;
class DeckLoader;
class TabDeckEditor : public AbstractTabDeckEditor
{
Q_OBJECT
protected slots:
void loadLayout() override;
void restartLayout() override;
void freeDocksSize() override;
void refreshShortcuts() override;
bool eventFilter(QObject *o, QEvent *e) override;
void dockVisibleTriggered() override;
void dockFloatingTriggered() override;
void dockTopLevelChanged(bool topLevel) override;
public:
explicit TabDeckEditor(TabSupervisor *_tabSupervisor);
void retranslateUi() override;
QString getTabText() const override;
void createMenus() override;
public slots:
void showPrintingSelector() override;
};
#endif

View File

@@ -1,272 +0,0 @@
#ifndef TAB_GAME_H
#define TAB_GAME_H
#include "../../client/tearoff_menu.h"
#include "../../game/player/player.h"
#include "../ui/widgets/visual_deck_storage/visual_deck_storage_widget.h"
#include "pb/event_leave.pb.h"
#include "pb/serverinfo_game.pb.h"
#include "tab.h"
#include <QCompleter>
#include <QLoggingCategory>
#include <QMap>
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
class UserListProxy;
class DeckViewContainer;
class AbstractClient;
class CardDatabase;
class GameView;
class GameScene;
class CardInfoFrameWidget;
class MessageLogWidget;
class QTimer;
class QSplitter;
class QLabel;
class QToolButton;
class QMenu;
class ZoneViewLayout;
class ZoneViewWidget;
class PhasesToolbar;
class PlayerListWidget;
class ReplayTimelineWidget;
class Response;
class GameEventContainer;
class GameEventContext;
class GameCommand;
class CommandContainer;
class Event_GameJoined;
class Event_GameStateChanged;
class Event_PlayerPropertiesChanged;
class Event_Join;
class Event_Leave;
class Event_GameHostChanged;
class Event_GameClosed;
class Event_GameStart;
class Event_SetActivePlayer;
class Event_SetActivePhase;
class Event_Ping;
class Event_GameSay;
class Event_Kicked;
class Event_ReverseTurn;
class CardZone;
class AbstractCardItem;
class CardItem;
class DeckLoader;
class QVBoxLayout;
class QHBoxLayout;
class GameReplay;
class ServerInfo_User;
class PendingCommand;
class LineEditCompleter;
class QDockWidget;
class QStackedWidget;
class TabGame : public Tab
{
Q_OBJECT
private:
QTimer *gameTimer;
int secondsElapsed;
const UserListProxy *userListProxy;
QList<AbstractClient *> clients;
ServerInfo_Game gameInfo;
QMap<int, QString> roomGameTypes;
int hostId;
int localPlayerId;
const bool isLocalGame;
bool spectator;
bool judge;
QMap<int, Player *> players;
QMap<int, ServerInfo_User> spectators;
bool gameStateKnown;
bool resuming;
QStringList phasesList;
int currentPhase;
int activePlayer;
CardItem *activeCard;
bool gameClosed;
QStringList gameTypes;
QCompleter *completer;
QStringList autocompleteUserList;
QStackedWidget *mainWidget;
// Replay related members
GameReplay *replay;
int currentReplayStep;
QList<int> replayTimeline;
ReplayTimelineWidget *timelineWidget;
QToolButton *replayPlayButton, *replayFastForwardButton;
QAction *aReplaySkipForward, *aReplaySkipBackward, *aReplaySkipForwardBig, *aReplaySkipBackwardBig;
CardInfoFrameWidget *cardInfoFrameWidget;
PlayerListWidget *playerListWidget;
QLabel *timeElapsedLabel;
MessageLogWidget *messageLog;
QLabel *sayLabel;
LineEditCompleter *sayEdit;
PhasesToolbar *phasesToolbar;
GameScene *scene;
GameView *gameView;
QMap<int, DeckViewContainer *> deckViewContainers;
QVBoxLayout *deckViewContainerLayout;
QWidget *gamePlayAreaWidget, *deckViewContainerWidget;
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;
QAction *playersSeparator;
QMenu *gameMenu, *viewMenu, *cardInfoDockMenu, *messageLayoutDockMenu, *playerListDockMenu, *replayDockMenu;
TearOffMenu *phasesMenu;
QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextPhaseAction, *aNextTurn,
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aMessageLayoutDockVisible, *aMessageLayoutDockFloating,
*aPlayerListDockVisible, *aPlayerListDockFloating, *aReplayDockVisible, *aReplayDockFloating;
QAction *aFocusChat;
QList<QAction *> phaseActions;
Player *addPlayer(int playerId, const ServerInfo_User &info);
bool isMainPlayerConceded() const;
void startGame(bool resuming);
void stopGame();
void closeGame();
bool leaveGame();
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
void eventGameStateChanged(const Event_GameStateChanged &event, int eventPlayerId, const GameEventContext &context);
void eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context);
void eventJoin(const Event_Join &event, int eventPlayerId, const GameEventContext &context);
void eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
void eventKicked(const Event_Kicked &event, int eventPlayerId, const GameEventContext &context);
void eventGameHostChanged(const Event_GameHostChanged &event, int eventPlayerId, const GameEventContext &context);
void eventGameClosed(const Event_GameClosed &event, int eventPlayerId, const GameEventContext &context);
Player *setActivePlayer(int id);
void eventSetActivePlayer(const Event_SetActivePlayer &event, int eventPlayerId, const GameEventContext &context);
void setActivePhase(int phase);
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext & /*context*/);
void emitUserEvent();
void createMenuItems();
void createReplayMenuItems();
void createViewMenuItems();
void createCardInfoDock(bool bReplay = false);
void createPlayerListDock(bool bReplay = false);
void createMessageDock(bool bReplay = false);
void createPlayAreaWidget(bool bReplay = false);
void createDeckViewContainerWidget(bool bReplay = false);
void createReplayDock();
QString getLeaveReason(Event_Leave::LeaveReason reason);
signals:
void gameClosing(TabGame *tab);
void playerAdded(Player *player);
void playerRemoved(Player *player);
void containerProcessingStarted(const GameEventContext &context);
void containerProcessingDone();
void openMessageDialog(const QString &userName, bool focus);
void openDeckEditor(const DeckLoader *deck);
void notIdle();
private slots:
void replayNextEvent(Player::EventProcessingOptions options);
void replayFinished();
void replayPlayButtonToggled(bool checked);
void replayFastForwardButtonToggled(bool checked);
void replayRewind();
void incrementGameTime();
void adminLockChanged(bool lock);
void newCardAdded(AbstractCardItem *card);
void updateCardMenu(AbstractCardItem *card);
void actGameInfo();
void actConcede();
void actRemoveLocalArrows();
void actRotateViewCW();
void actRotateViewCCW();
void actSay();
void actPhaseAction();
void actNextPhase();
void actNextPhaseAction();
void actNextTurn();
void actReverseTurn();
void addMentionTag(const QString &value);
void linkCardToChat(const QString &cardName);
void commandFinished(const Response &response);
void refreshShortcuts();
void loadLayout();
void actCompleterChanged();
void actResetLayout();
void freeDocksSize();
void hideEvent(QHideEvent *event) override;
bool eventFilter(QObject *o, QEvent *e) override;
void dockVisibleTriggered();
void dockFloatingTriggered();
void dockTopLevelChanged(bool topLevel);
public:
TabGame(TabSupervisor *_tabSupervisor,
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes);
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
~TabGame() override;
void retranslateUi() override;
void updatePlayerListDockTitle();
void closeRequest(bool forced = false) override;
const QMap<int, Player *> &getPlayers() const
{
return players;
}
CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
bool isHost() const
{
return hostId == localPlayerId;
}
bool getIsLocalGame() const
{
return isLocalGame;
}
int getGameId() const
{
return gameInfo.game_id();
}
QString getTabText() const override;
bool getSpectator() const
{
return spectator;
}
bool getSpectatorsSeeEverything() const
{
return gameInfo.spectators_omniscient();
}
bool isSpectator();
Player *getActiveLocalPlayer() const;
AbstractClient *getClientForPlayer(int playerId) const;
void setActiveCard(CardItem *card);
CardItem *getActiveCard() const
{
return activeCard;
}
void processGameEventContainer(const GameEventContainer &cont,
AbstractClient *client,
Player::EventProcessingOptions options);
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
public slots:
void sendGameCommand(PendingCommand *pend, int playerId = -1);
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
void viewCardInfo(const QString &cardName, const QString &providerId = "") const;
};
#endif

View File

@@ -1,26 +0,0 @@
#ifndef TAB_VISUAL_DATABASE_DISPLAY_H
#define TAB_VISUAL_DATABASE_DISPLAY_H
#include "../ui/widgets/visual_database_display/visual_database_display_widget.h"
#include "tab.h"
#include <QVBoxLayout>
class TabVisualDatabaseDisplay : public Tab
{
Q_OBJECT
private:
TabDeckEditor *deckEditor;
VisualDatabaseDisplayWidget *visualDatabaseDisplayWidget;
public:
TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor);
void retranslateUi() override;
QString getTabText() const override
{
return tr("Visual Database Display");
}
};
#endif // TAB_VISUAL_DATABASE_DISPLAY_H

View File

@@ -1,53 +0,0 @@
#ifndef WINDOW_DECKEDITORVISUAL_H
#define WINDOW_DECKEDITORVISUAL_H
#include "../tab.h"
#include "tab_deck_editor_visual_tab_widget.h"
class TabDeckEditorVisual : public AbstractTabDeckEditor
{
Q_OBJECT
protected slots:
void loadLayout() override;
void restartLayout() override;
void freeDocksSize() override;
void refreshShortcuts() override;
bool eventFilter(QObject *o, QEvent *e) override;
void dockVisibleTriggered() override;
void dockFloatingTriggered() override;
void dockTopLevelChanged(bool topLevel) override;
protected:
TabDeckEditorVisualTabWidget *tabContainer;
QVBoxLayout *centralFrame;
QVBoxLayout *searchAndDatabaseFrame;
QHBoxLayout *searchLayout;
QDockWidget *searchAndDatabaseDock;
QDockWidget *deckAnalyticsDock;
QWidget *centralWidget;
QMenu *deckAnalyticsMenu;
QAction *aDeckAnalyticsDockVisible, *aDeckAnalyticsDockFloating;
public:
explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor);
void retranslateUi() override;
QString getTabText() const override;
void changeModelIndexAndCardInfo(const CardInfoPtr &activeCard);
void changeModelIndexToCard(const CardInfoPtr &activeCard);
void createDeckAnalyticsDock();
void createMenus() override;
void createSearchAndDatabaseFrame();
void createCentralFrame();
public slots:
void onDeckChanged() override;
void showPrintingSelector() override;
void
processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
bool actSaveDeckAs() override;
};
#endif

View File

@@ -1,62 +0,0 @@
#ifndef TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
#define TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
#include "../../ui/widgets/printing_selector/printing_selector.h"
#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.h"
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
#include "../abstract_tab_deck_editor.h"
#include <QTabWidget>
#include <QVBoxLayout>
#include <QWidget>
class TabDeckEditorVisualTabWidget : public QTabWidget
{
Q_OBJECT
public:
explicit TabDeckEditorVisualTabWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor,
DeckListModel *_deckModel,
CardDatabaseModel *_cardDatabaseModel,
CardDatabaseDisplayModel *_cardDatabaseDisplayModel);
// Utility functions
void addNewTab(QWidget *widget, const QString &title);
void removeCurrentTab();
void setTabTitle(int index, const QString &title);
QWidget *getCurrentTab() const;
int getTabCount() const;
VisualDeckEditorWidget *visualDeckView;
DeckAnalyticsWidget *deckAnalytics;
VisualDatabaseDisplayWidget *visualDatabaseDisplay;
PrintingSelector *printingSelector;
VisualDeckEditorSampleHandWidget *sampleHandWidget;
public slots:
void onCardChanged(CardInfoPtr activeCard);
void onCardChangedDatabaseDisplay(CardInfoPtr activeCard);
void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
signals:
void cardChanged(CardInfoPtr activeCard);
void cardChangedDatabaseDisplay(CardInfoPtr activeCard);
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
private:
QVBoxLayout *layout; // Layout for the tab widget and other controls
AbstractTabDeckEditor *deckEditor;
DeckListModel *deckModel;
CardDatabaseModel *cardDatabaseModel;
CardDatabaseDisplayModel *cardDatabaseDisplayModel;
private slots:
void handleTabClose(int index); // Slot for closing a tab
};
#endif // TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H

View File

@@ -1,53 +0,0 @@
#ifndef PICTURELOADER_H
#define PICTURELOADER_H
#include "../../../game/cards/card_info.h"
#include "picture_loader_status_bar.h"
#include "picture_loader_worker.h"
#include <QLoggingCategory>
inline Q_LOGGING_CATEGORY(PictureLoaderLog, "picture_loader");
inline Q_LOGGING_CATEGORY(PictureLoaderCardBackCacheFailLog, "picture_loader.card_back_cache_fail");
class PictureLoader : public QObject
{
Q_OBJECT
public:
static PictureLoader &getInstance()
{
static PictureLoader instance;
return instance;
}
private:
explicit PictureLoader();
~PictureLoader() override;
// Singleton - Don't implement copy constructor and assign operator
PictureLoader(PictureLoader const &);
void operator=(PictureLoader const &);
PictureLoaderWorker *worker;
PictureLoaderStatusBar *statusBar;
public:
static void getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size);
static void getCardBackPixmap(QPixmap &pixmap, QSize size);
static void getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
static void getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size);
static void clearPixmapCache(CardInfoPtr card);
static void clearPixmapCache();
static void cacheCardPixmaps(QList<CardInfoPtr> cards);
static bool hasCustomArt();
public slots:
static void clearNetworkCache();
private slots:
void picDownloadChanged();
void picsPathChanged();
public slots:
void imageLoaded(CardInfoPtr card, const QImage &image);
};
#endif

View File

@@ -1,154 +0,0 @@
#include "picture_loader_local.h"
#include "../../../game/cards/card_database_manager.h"
#include "../../../settings/cache_settings.h"
#include "picture_to_load.h"
#include <QDirIterator>
#include <QMovie>
static constexpr int REFRESH_INTERVAL_MS = 10 * 1000;
PictureLoaderLocal::PictureLoaderLocal(QObject *parent)
: QObject(parent), picsPath(SettingsCache::instance().getPicsPath()),
customPicsPath(SettingsCache::instance().getCustomPicsPath()),
overrideAllCardArtWithPersonalPreference(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference())
{
// Hook up signals to settings
connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this, &PictureLoaderLocal::picsPathChanged);
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
&PictureLoaderLocal::setOverrideAllCardArtWithPersonalPreference);
refreshIndex();
refreshTimer = new QTimer(this);
connect(refreshTimer, &QTimer::timeout, this, &PictureLoaderLocal::refreshIndex);
refreshTimer->start(REFRESH_INTERVAL_MS);
}
void PictureLoaderLocal::refreshIndex()
{
customFolderIndex.clear();
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
// Recursively check all subdirectories of the CUSTOM folder
while (it.hasNext()) {
QString thisPath(it.next());
QFileInfo thisFileInfo(thisPath);
if (thisFileInfo.isFile()) {
// We don't know which name is the correctedName because there might be '.'s in the cardName.
// Just add all possibilities to be sure.
customFolderIndex.insert(thisFileInfo.baseName(), thisFileInfo.absoluteFilePath());
customFolderIndex.insert(thisFileInfo.completeBaseName(), thisFileInfo.absoluteFilePath());
}
}
qCDebug(PictureLoaderLocalLog) << "Finished indexing local image folder CUSTOM; map now has"
<< customFolderIndex.size() << "entries.";
}
/**
* Tries to load the card image from the local images.
*
* @param toLoad The card to load
* @return The loaded image, or an empty QImage if loading failed.
*/
QImage PictureLoaderLocal::tryLoad(const CardInfoPtr &toLoad) const
{
CardSetPtr set = PictureToLoad::extractSetsSorted(toLoad).first();
QString setName = set ? set->getCorrectedShortName() : QString();
QString cardName = toLoad->getName();
QString correctedCardName = toLoad->getCorrectedName();
// FIXME: This is a hack so that to keep old Cockatrice behavior
// (ignoring provider ID) when the "override all card art with personal
// preference" is set.
//
// Figure out a proper way to integrate the two systems at some point.
//
// Note: need to go through a member for
// overrideAllCardArtWithPersonalPreference as reading from the
// SettingsCache instance from the PictureLoaderWorker thread could
// cause race conditions.
//
// XXX: Reading from the CardDatabaseManager instance from the
// PictureLoaderWorker thread might not be safe either
bool searchCustomPics =
overrideAllCardArtWithPersonalPreference ||
CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(cardName, toLoad->getPixmapCacheKey());
if (searchCustomPics) {
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << cardName << " set: " << setName << "]: Attempting to load picture from local";
return tryLoadCardImageFromDisk(setName, correctedCardName, searchCustomPics);
}
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << cardName << " set: " << setName << "]: Skipping load picture from local";
return QImage();
}
QImage PictureLoaderLocal::tryLoadCardImageFromDisk(const QString &setName,
const QString &correctedCardName,
const bool searchCustomPics) const
{
QImage image;
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
QList<QString> picsPaths = QList<QString>();
if (searchCustomPics) {
picsPaths << customFolderIndex.values(correctedCardName);
}
if (!setName.isEmpty()) {
picsPaths << picsPath + "/" + setName + "/" + correctedCardName
// We no longer store downloaded images there, but don't just ignore
// stuff that old versions have put there.
<< picsPath + "/downloadedPics/" + setName + "/" + correctedCardName;
}
// Iterates through the list of paths, searching for images with the desired
// name with any QImageReader-supported extension
for (const auto &_picsPath : picsPaths) {
if (!QFileInfo(_picsPath).isFile()) {
continue;
}
imgReader.setFileName(_picsPath);
if (imgReader.read(&image)) {
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "]: Picture found on disk.";
return image;
}
imgReader.setFileName(_picsPath + ".full");
if (imgReader.read(&image)) {
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "]: Picture.full found on disk.";
return image;
}
imgReader.setFileName(_picsPath + ".xlhq");
if (imgReader.read(&image)) {
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "]: Picture.xlhq found on disk.";
return image;
}
}
qCDebug(PictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "]: Picture NOT found on disk.";
return QImage();
}
void PictureLoaderLocal::picsPathChanged()
{
picsPath = SettingsCache::instance().getPicsPath();
customPicsPath = SettingsCache::instance().getCustomPicsPath();
}
void PictureLoaderLocal::setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference)
{
overrideAllCardArtWithPersonalPreference = _overrideAllCardArtWithPersonalPreference;
}

View File

@@ -1,42 +0,0 @@
#ifndef PICTURE_LOADER_LOCAL_H
#define PICTURE_LOADER_LOCAL_H
#include "../../../game/cards/card_info.h"
#include <QLoggingCategory>
#include <QObject>
#include <QTimer>
inline Q_LOGGING_CATEGORY(PictureLoaderLocalLog, "picture_loader.local");
/**
* Handles searching for and loading card images from the local pics and custom image folders.
* This class maintains an index of the CUSTOM folder, to avoid repeatedly searching the entire directory.
*/
class PictureLoaderLocal : public QObject
{
Q_OBJECT
public:
explicit PictureLoaderLocal(QObject *parent);
QImage tryLoad(const CardInfoPtr &toLoad) const;
private:
QString picsPath, customPicsPath;
bool overrideAllCardArtWithPersonalPreference;
QMultiHash<QString, QString> customFolderIndex; // multimap of cardName to picPaths
QTimer *refreshTimer;
void refreshIndex();
QImage
tryLoadCardImageFromDisk(const QString &setName, const QString &correctedCardName, bool searchCustomPics) const;
private slots:
void picsPathChanged();
void setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference);
};
#endif // PICTURE_LOADER_LOCAL_H

View File

@@ -1,32 +0,0 @@
#include "picture_loader_request_status_display_widget.h"
PictureLoaderRequestStatusDisplayWidget::PictureLoaderRequestStatusDisplayWidget(QWidget *parent,
const QUrl &_url,
PictureLoaderWorkerWork *worker)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
if (worker->cardToDownload.getCard()) {
name = new QLabel(this);
name->setText(worker->cardToDownload.getCard()->getName());
setShortname = new QLabel(this);
setShortname->setText(worker->cardToDownload.getSetName());
providerId = new QLabel(this);
providerId->setText(worker->cardToDownload.getCard()->getProperty("uuid"));
layout->addWidget(name);
layout->addWidget(setShortname);
layout->addWidget(providerId);
}
startTime = new QLabel(QDateTime::currentDateTime().toString(), this);
elapsedTime = new QLabel("0", this);
finished = new QLabel("False", this);
url = new QLabel(_url.toString(), this);
layout->addWidget(startTime);
layout->addWidget(elapsedTime);
layout->addWidget(finished);
layout->addWidget(url);
}

View File

@@ -1,66 +0,0 @@
#ifndef PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H
#define PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H
#include "picture_loader_worker_work.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QWidget>
class PictureLoaderRequestStatusDisplayWidget : public QWidget
{
Q_OBJECT
public:
PictureLoaderRequestStatusDisplayWidget(QWidget *parent, const QUrl &url, PictureLoaderWorkerWork *worker);
PictureLoaderWorkerWork *worker;
void setFinished()
{
finished->setText("True");
update();
repaint();
}
bool getFinished() const
{
return finished->text() == "True";
}
void setElapsedTime(const QString &_elapsedTime) const
{
elapsedTime->setText(_elapsedTime);
}
int queryElapsedSeconds()
{
if (!finished) {
int elapsedSeconds = QDateTime::fromString(startTime->text()).secsTo(QDateTime::currentDateTime());
elapsedTime->setText(QString::number(elapsedSeconds));
update();
repaint();
return elapsedSeconds;
}
return elapsedTime->text().toInt();
}
QString getStartTime() const
{
return startTime->text();
}
QString getUrl() const
{
return url->text();
}
private:
QHBoxLayout *layout;
QLabel *name;
QLabel *setShortname;
QLabel *providerId;
QLabel *startTime;
QLabel *elapsedTime;
QLabel *finished;
QLabel *url;
};
#endif // PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H

View File

@@ -1,29 +0,0 @@
#ifndef PICTURE_LOADER_STATUS_BAR_H
#define PICTURE_LOADER_STATUS_BAR_H
#include "../widgets/quick_settings/settings_button_widget.h"
#include "picture_loader_worker_work.h"
#include <QHBoxLayout>
#include <QProgressBar>
#include <QWidget>
class PictureLoaderStatusBar : public QWidget
{
Q_OBJECT
public:
explicit PictureLoaderStatusBar(QWidget *parent);
public slots:
void addQueuedImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker);
void addSuccessfulImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker);
void cleanOldEntries();
private:
QHBoxLayout *layout;
QProgressBar *progressBar;
SettingsButtonWidget *loadLog;
QTimer *cleaner;
};
#endif // PICTURE_LOADER_STATUS_BAR_H

View File

@@ -1,75 +0,0 @@
#ifndef PICTURE_LOADER_WORKER_H
#define PICTURE_LOADER_WORKER_H
#include "../../../game/cards/card_database.h"
#include "../../../game/cards/card_info.h"
#include "picture_loader_local.h"
#include "picture_loader_worker_work.h"
#include "picture_to_load.h"
#include <QLoggingCategory>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QObject>
#include <QQueue>
#include <QTimer>
#define REDIRECT_HEADER_NAME "redirects"
#define REDIRECT_ORIGINAL_URL "original"
#define REDIRECT_URL "redirect"
#define REDIRECT_TIMESTAMP "timestamp"
#define REDIRECT_CACHE_FILENAME "cache.ini"
inline Q_LOGGING_CATEGORY(PictureLoaderWorkerLog, "picture_loader.worker");
class PictureLoaderWorkerWork;
class PictureLoaderWorker : public QObject
{
Q_OBJECT
public:
explicit PictureLoaderWorker();
~PictureLoaderWorker() override;
void enqueueImageLoad(const CardInfoPtr &card); // Starts a thread for the image to be loaded
void queueRequest(const QUrl &url, PictureLoaderWorkerWork *worker); // Queues network requests for load threads
void clearNetworkCache();
public slots:
QNetworkReply *makeRequest(const QUrl &url, PictureLoaderWorkerWork *workThread);
void processQueuedRequests();
void imageLoadedSuccessfully(const CardInfoPtr &card, const QImage &image);
private:
static QStringList md5Blacklist;
QThread *pictureLoaderThread;
QNetworkAccessManager *networkManager;
QNetworkDiskCache *cache;
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
QString cacheFilePath; // Path to persistent storage
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
bool picDownload;
QQueue<QPair<QUrl, PictureLoaderWorkerWork *>> requestLoadQueue;
QTimer requestTimer; // Timer for processing delayed requests
PictureLoaderLocal *localLoader;
QSet<CardInfoPtr> currentlyLoading; // for deduplication purposes
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
QUrl getCachedRedirect(const QUrl &originalUrl) const;
void loadRedirectCache();
void saveRedirectCache() const;
void cleanStaleEntries();
private slots:
void handleImageLoadEnqueued(const CardInfoPtr &card);
signals:
void imageLoadEnqueued(const CardInfoPtr &card);
void imageLoaded(CardInfoPtr card, const QImage &image);
void imageLoadQueued(const QUrl &url, PictureLoaderWorkerWork *worker);
void imageLoadSuccessful(const QUrl &url, PictureLoaderWorkerWork *worker);
};
#endif // PICTURE_LOADER_WORKER_H

View File

@@ -1,58 +0,0 @@
#ifndef PICTURE_LOADER_WORKER_WORK_H
#define PICTURE_LOADER_WORKER_WORK_H
#include "../../../game/cards/card_database.h"
#include "picture_loader_worker.h"
#include "picture_to_load.h"
#include <QLoggingCategory>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QObject>
#include <QThread>
#define REDIRECT_HEADER_NAME "redirects"
#define REDIRECT_ORIGINAL_URL "original"
#define REDIRECT_URL "redirect"
#define REDIRECT_TIMESTAMP "timestamp"
#define REDIRECT_CACHE_FILENAME "cache.ini"
inline Q_LOGGING_CATEGORY(PictureLoaderWorkerWorkLog, "picture_loader.worker");
class PictureLoaderWorker;
class PictureLoaderWorkerWork : public QObject
{
Q_OBJECT
public:
explicit PictureLoaderWorkerWork(const PictureLoaderWorker *worker, const CardInfoPtr &toLoad);
PictureToLoad cardToDownload;
public slots:
void picDownloadFinished(QNetworkReply *reply);
private:
QThread *pictureLoaderThread;
QNetworkAccessManager *networkManager;
bool picDownload, downloadRunning, loadQueueRunning;
void startNextPicDownload();
void picDownloadFailed();
QImage tryLoadImageFromReply(QNetworkReply *reply);
void concludeImageLoad(const QImage &image);
private slots:
void picDownloadChanged();
signals:
/**
* Emitted when this worker has successfully loaded the image or has exhausted all attempts at loading the image.
* Failures are represented by an empty QImage.
* Note that this object will delete itself as this signal is emitted.
*/
void imageLoaded(CardInfoPtr card, const QImage &image);
void requestImageDownload(const QUrl &url, PictureLoaderWorkerWork *instance);
};
#endif // PICTURE_LOADER_WORKER_WORK_H

View File

@@ -1,48 +0,0 @@
#ifndef PICTURE_TO_LOAD_H
#define PICTURE_TO_LOAD_H
#include "../../../game/cards/card_info.h"
#include <QLoggingCategory>
inline Q_LOGGING_CATEGORY(PictureToLoadLog, "picture_loader.picture_to_load");
class PictureToLoad
{
private:
CardInfoPtr card;
QList<CardSetPtr> sortedSets;
QList<QString> urlTemplates;
QList<QString> currentSetUrls;
QString currentUrl;
CardSetPtr currentSet;
public:
explicit PictureToLoad(CardInfoPtr _card = CardInfoPtr());
CardInfoPtr getCard() const
{
return card;
}
void clear()
{
card.clear();
}
QString getCurrentUrl() const
{
return currentUrl;
}
CardSetPtr getCurrentSet() const
{
return currentSet;
}
QString getSetName() const;
QString transformUrl(const QString &urlTemplate) const;
bool nextSet();
bool nextUrl();
void populateSetUrls();
static QList<CardSetPtr> extractSetsSorted(const CardInfoPtr &card);
};
#endif // PICTURE_TO_LOAD_H

View File

@@ -1,110 +0,0 @@
#include "card_group_display_widget.h"
#include "../../../../../deck/deck_list_model.h"
#include "../../../../../game/cards/card_database_manager.h"
#include "../../../../../utility/card_info_comparator.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _cardGroupCategory,
QString _activeGroupCriteria,
QStringList _activeSortCriteria,
int bannerOpacity,
CardSizeWidget *_cardSizeWidget)
: QWidget(parent), deckListModel(_deckListModel), trackedIndex(_trackedIndex), zoneName(_zoneName),
cardGroupCategory(_cardGroupCategory), activeGroupCriteria(_activeGroupCriteria),
activeSortCriteria(_activeSortCriteria), cardSizeWidget(_cardSizeWidget)
{
layout = new QVBoxLayout(this);
setLayout(layout);
setMinimumSize(QSize(0, 0));
banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
layout->addWidget(banner);
CardGroupDisplayWidget::updateCardDisplays();
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
}
QWidget *CardGroupDisplayWidget::constructWidgetForIndex(int rowIndex)
{
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(rowIndex, 0, trackedIndex));
if (indexToWidgetMap.contains(index)) {
return indexToWidgetMap[index];
}
auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString();
auto widget = new CardInfoPictureWithTextOverlayWidget(getLayoutParent(), true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderId));
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this, &CardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &CardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget, &CardInfoPictureWidget::setScaleFactor);
indexToWidgetMap.insert(index, widget);
return widget;
}
void CardGroupDisplayWidget::updateCardDisplays()
{
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
addToLayout(constructWidgetForIndex(i));
}
}
void CardGroupDisplayWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
return;
}
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
insertIntoLayout(constructWidgetForIndex(i), i);
}
}
}
void CardGroupDisplayWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(first);
Q_UNUSED(last);
if (parent == trackedIndex) {
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
}
}
}
void CardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{
emit cardClicked(event, card);
}
void CardGroupDisplayWidget::onHover(CardInfoPtr card)
{
emit cardHovered(card);
}
void CardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
}

View File

@@ -1,105 +0,0 @@
#include "flat_card_group_display_widget.h"
#include "../../../../../deck/deck_list_model.h"
#include "../../../../../game/cards/card_database_manager.h"
#include "../../../../../utility/card_info_comparator.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
#include <utility>
FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QPersistentModelIndex _trackedIndex,
QString _zoneName,
QString _cardGroupCategory,
QString _activeGroupCriteria,
QStringList _activeSortCriteria,
int bannerOpacity,
CardSizeWidget *_cardSizeWidget)
: CardGroupDisplayWidget(parent,
_deckListModel,
std::move(_trackedIndex),
_zoneName,
_cardGroupCategory,
_activeGroupCriteria,
_activeSortCriteria,
bannerOpacity,
_cardSizeWidget)
{
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
banner->setBuddy(flowWidget);
layout->addWidget(flowWidget);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
FlatCardGroupDisplayWidget::removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
FlatCardGroupDisplayWidget::updateCardDisplays();
disconnect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
disconnect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &FlatCardGroupDisplayWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &FlatCardGroupDisplayWidget::onCardRemoval);
}
void FlatCardGroupDisplayWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
return;
}
if (parent == trackedIndex) {
for (int i = first; i <= last; i++) {
insertIntoLayout(constructWidgetForIndex(i), i);
}
}
}
void FlatCardGroupDisplayWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(first);
Q_UNUSED(last);
if (parent == trackedIndex) {
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
removeFromLayout(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
if (!trackedIndex.isValid()) {
emit cleanupRequested(this);
}
}
}
QWidget *FlatCardGroupDisplayWidget::constructWidgetForIndex(int row)
{
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(row, 0, trackedIndex));
if (indexToWidgetMap.contains(index)) {
return indexToWidgetMap[index];
}
auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString();
auto widget = new CardInfoPictureWithTextOverlayWidget(flowWidget, true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderId));
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this, &FlatCardGroupDisplayWidget::onClick);
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &FlatCardGroupDisplayWidget::onHover);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget, &CardInfoPictureWidget::setScaleFactor);
indexToWidgetMap.insert(index, widget);
return widget;
}
void FlatCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
}

View File

@@ -1,80 +0,0 @@
#include "card_info_text_widget.h"
#include "../../../../game/board/card_item.h"
#include "../../../../game/game_specific_terms.h"
#include <QGridLayout>
#include <QLabel>
#include <QTextEdit>
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(nullptr)
{
nameLabel = new QLabel;
nameLabel->setOpenExternalLinks(false);
nameLabel->setWordWrap(true);
connect(nameLabel, SIGNAL(linkActivated(const QString &)), this, SIGNAL(linkActivated(const QString &)));
textLabel = new QTextEdit();
textLabel->setReadOnly(true);
auto *grid = new QGridLayout(this);
grid->addWidget(nameLabel, 0, 0);
grid->addWidget(textLabel, 1, 0, -1, 2);
grid->setRowStretch(1, 1);
grid->setColumnStretch(1, 1);
retranslateUi();
}
void CardInfoTextWidget::setCard(CardInfoPtr card)
{
if (card == nullptr) {
nameLabel->setText("");
textLabel->setText("");
return;
}
QString text = "<table width=\"100%\" border=0 cellspacing=0 cellpadding=0>";
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
.arg(tr("Name:"), card->getName().toHtmlEscaped());
QStringList cardProps = card->getProperties();
for (const QString &key : cardProps) {
if (key.contains("-"))
continue;
QString keyText = Mtg::getNicePropertyName(key).toHtmlEscaped() + ":";
text +=
QString("<tr><td>%1</td><td></td><td>%2</td></tr>").arg(keyText, card->getProperty(key).toHtmlEscaped());
}
auto relatedCards = card->getAllRelatedCards();
if (!relatedCards.empty()) {
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>").arg(tr("Related cards:"));
for (auto *relatedCard : relatedCards) {
QString tmp = relatedCard->getName().toHtmlEscaped();
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
}
text += "</td></tr>";
}
text += "</table>";
nameLabel->setText(text);
textLabel->setText(card->getText());
}
void CardInfoTextWidget::setInvalidCardName(const QString &cardName)
{
nameLabel->setText(tr("Unknown card:") + " " + cardName);
textLabel->setText("");
}
void CardInfoTextWidget::retranslateUi()
{
/*
* There's no way we can really translate the text currently being rendered.
* The best we can do is invalidate the current text.
*/
setInvalidCardName("");
}

View File

@@ -1,135 +0,0 @@
#include "mana_base_widget.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <QHash>
#include <QRegularExpression>
#include <decklist.h>
ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)
{
layout = new QVBoxLayout(this);
setLayout(layout);
bannerWidget = new BannerWidget(this, tr("Mana Base"), Qt::Vertical, 100);
bannerWidget->setMaximumHeight(100);
layout->addWidget(bannerWidget);
barContainer = new QWidget(this);
barLayout = new QHBoxLayout(barContainer);
layout->addWidget(barContainer);
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
retranslateUi();
}
void ManaBaseWidget::retranslateUi()
{
bannerWidget->setText(tr("Mana Base"));
}
void ManaBaseWidget::setDeckModel(DeckListModel *deckModel)
{
deckListModel = deckModel;
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
analyzeManaBase();
}
void ManaBaseWidget::updateDisplay()
{
// Clear the layout first
QLayoutItem *item;
while ((item = barLayout->takeAt(0)) != nullptr) {
item->widget()->deleteLater();
delete item;
}
int highestEntry = 0;
for (auto entry : manaBaseMap) {
if (entry > highestEntry) {
highestEntry = entry;
}
}
// Define color mapping for mana types
QHash<QString, QColor> manaColors;
manaColors.insert("W", QColor(248, 231, 185));
manaColors.insert("U", QColor(14, 104, 171));
manaColors.insert("B", QColor(21, 11, 0));
manaColors.insert("R", QColor(211, 32, 42));
manaColors.insert("G", QColor(0, 115, 62));
manaColors.insert("C", QColor(150, 150, 150));
for (auto manaColor : manaBaseMap.keys()) {
QColor barColor = manaColors.value(manaColor, Qt::gray);
BarWidget *barWidget = new BarWidget(QString(manaColor), manaBaseMap[manaColor], highestEntry, barColor, this);
barLayout->addWidget(barWidget);
}
update();
}
QHash<QString, int> ManaBaseWidget::analyzeManaBase()
{
manaBaseMap.clear();
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
if (info) {
auto devotion = determineManaProduction(info->getText());
mergeManaCounts(manaBaseMap, devotion);
}
}
}
}
updateDisplay();
return manaBaseMap;
}
QHash<QString, int> ManaBaseWidget::determineManaProduction(const QString &rulesText)
{
QHash<QString, int> manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}};
QString text = rulesText.toLower(); // Normalize case for matching
// Quick keyword-based checks for any color and colorless mana
if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) {
for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"),
QStringLiteral("G")}) {
manaCounts[color]++;
}
}
if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) {
manaCounts["C"]++;
}
// Optimized regex for specific mana symbols
static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})");
QRegularExpressionMatch match = specificColorRegex.match(rulesText);
if (match.hasMatch()) {
manaCounts[match.captured(1)]++;
}
return manaCounts;
}
void ManaBaseWidget::mergeManaCounts(QHash<QString, int> &manaCounts1, const QHash<QString, int> &manaCounts2)
{
for (auto it = manaCounts2.constBegin(); it != manaCounts2.constEnd(); ++it) {
manaCounts1[it.key()] += it.value();
}
}

View File

@@ -1,37 +0,0 @@
#ifndef MANA_BASE_WIDGET_H
#define MANA_BASE_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../general/display/banner_widget.h"
#include <QHBoxLayout>
#include <QWidget>
#include <decklist.h>
#include <utility>
class ManaBaseWidget : public QWidget
{
Q_OBJECT
public:
explicit ManaBaseWidget(QWidget *parent, DeckListModel *deckListModel);
QHash<QString, int> analyzeManaBase();
void updateDisplay();
QHash<QString, int> determineManaProduction(const QString &manaString);
void mergeManaCounts(QHash<QString, int> &manaCounts1, const QHash<QString, int> &manaCounts2);
public slots:
void setDeckModel(DeckListModel *deckModel);
void retranslateUi();
private:
DeckListModel *deckListModel;
BannerWidget *bannerWidget;
QHash<QString, int> manaBaseMap;
QVBoxLayout *layout;
QWidget *barContainer;
QHBoxLayout *barLayout;
};
#endif // MANA_BASE_WIDGET_H

View File

@@ -1,99 +0,0 @@
#include "mana_curve_widget.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <decklist.h>
#include <unordered_map>
ManaCurveWidget::ManaCurveWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)
{
layout = new QVBoxLayout(this);
setLayout(layout);
bannerWidget = new BannerWidget(this, tr("Mana Curve"), Qt::Vertical, 100);
bannerWidget->setMaximumHeight(100);
layout->addWidget(bannerWidget);
barContainer = new QWidget(this);
barLayout = new QHBoxLayout(barContainer);
layout->addWidget(barContainer);
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
retranslateUi();
}
void ManaCurveWidget::retranslateUi()
{
bannerWidget->setText(tr("Mana Curve"));
}
void ManaCurveWidget::setDeckModel(DeckListModel *deckModel)
{
deckListModel = deckModel;
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
analyzeManaCurve();
}
std::unordered_map<int, int> ManaCurveWidget::analyzeManaCurve()
{
manaCurveMap.clear();
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
if (info) {
int cmc = info->getCmc().toInt();
manaCurveMap[cmc]++;
}
}
}
}
updateDisplay();
return manaCurveMap;
}
void ManaCurveWidget::updateDisplay()
{
// Clear the layout first
if (barLayout != nullptr) {
QLayoutItem *item;
while ((item = barLayout->takeAt(0)) != nullptr) {
item->widget()->deleteLater();
delete item;
}
}
int highestEntry = 0;
for (const auto &entry : manaCurveMap) {
if (entry.second > highestEntry) {
highestEntry = entry.second;
}
}
// Convert unordered_map to ordered map to ensure sorting by CMC
std::map<int, int> sortedManaCurve(manaCurveMap.begin(), manaCurveMap.end());
// Add new widgets to the layout in sorted order
for (const auto &entry : sortedManaCurve) {
BarWidget *barWidget =
new BarWidget(QString::number(entry.first), entry.second, highestEntry, QColor(122, 122, 122), this);
barLayout->addWidget(barWidget);
}
update(); // Update the widget display
}

View File

@@ -1,146 +0,0 @@
#include "mana_devotion_widget.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../main.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <decklist.h>
#include <iostream>
#include <regex>
#include <string>
#include <unordered_map>
ManaDevotionWidget::ManaDevotionWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)
{
layout = new QVBoxLayout(this);
setLayout(layout);
bannerWidget = new BannerWidget(this, tr("Mana Devotion"), Qt::Vertical, 100);
bannerWidget->setMaximumHeight(100);
layout->addWidget(bannerWidget);
barLayout = new QHBoxLayout();
layout->addLayout(barLayout);
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion);
retranslateUi();
}
void ManaDevotionWidget::retranslateUi()
{
bannerWidget->setText(tr("Mana Devotion"));
}
void ManaDevotionWidget::setDeckModel(DeckListModel *deckModel)
{
deckListModel = deckModel;
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion);
analyzeManaDevotion();
}
std::unordered_map<char, int> ManaDevotionWidget::analyzeManaDevotion()
{
manaDevotionMap.clear();
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k) {
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
if (info) {
auto devotion = countManaSymbols(info->getManaCost());
mergeManaCounts(manaDevotionMap, devotion);
}
}
}
}
updateDisplay();
return manaDevotionMap;
}
void ManaDevotionWidget::updateDisplay()
{
// Clear the layout first
QLayoutItem *item;
while ((item = barLayout->takeAt(0)) != nullptr) {
item->widget()->deleteLater();
delete item;
}
int highestEntry = 0;
for (auto entry : manaDevotionMap) {
if (highestEntry < entry.second) {
highestEntry = entry.second;
}
}
// Define color mapping for devotion bars
std::unordered_map<char, QColor> manaColors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)},
{'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)},
{'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}};
for (auto entry : manaDevotionMap) {
QColor barColor = manaColors.count(entry.first) ? manaColors[entry.first] : Qt::gray;
BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, highestEntry, barColor, this);
barLayout->addWidget(barWidget);
}
update(); // Update the widget display
}
std::unordered_map<char, int> ManaDevotionWidget::countManaSymbols(const QString &manaString)
{
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}};
int len = manaString.length();
for (int i = 0; i < len; ++i) {
if (manaString[i] == '{') {
++i; // Move past '{'
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
char mana1 = manaString[i].toLatin1();
++i; // Move to next character
if (i < len && manaString[i] == '/') {
++i; // Move past '/'
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
char mana2 = manaString[i].toLatin1();
manaCounts[mana1]++;
manaCounts[mana2]++;
} else {
// Handle cases like "{W/}" where second part is invalid
manaCounts[mana1]++;
}
} else {
manaCounts[mana1]++;
}
}
// Ensure we always skip to the closing '}'
while (i < len && manaString[i] != '}') {
++i;
}
}
// Check if the character is a standalone mana symbol (not inside {})
else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
manaCounts[manaString[i].toLatin1()]++;
}
}
return manaCounts;
}
void ManaDevotionWidget::mergeManaCounts(std::unordered_map<char, int> &manaCounts1,
const std::unordered_map<char, int> &manaCounts2)
{
for (const auto &pair : manaCounts2) {
manaCounts1[pair.first] += pair.second; // Add values for matching keys
}
}

View File

@@ -1,36 +0,0 @@
#ifndef MANA_DEVOTION_WIDGET_H
#define MANA_DEVOTION_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../general/display/banner_widget.h"
#include <QHBoxLayout>
#include <QWidget>
#include <decklist.h>
#include <utility>
class ManaDevotionWidget : public QWidget
{
Q_OBJECT
public:
explicit ManaDevotionWidget(QWidget *parent, DeckListModel *deckListModel);
void updateDisplay();
std::unordered_map<char, int> countManaSymbols(const QString &manaString);
void mergeManaCounts(std::unordered_map<char, int> &manaCounts1, const std::unordered_map<char, int> &manaCounts2);
public slots:
void setDeckModel(DeckListModel *deckModel);
std::unordered_map<char, int> analyzeManaDevotion();
void retranslateUi();
private:
DeckListModel *deckListModel;
BannerWidget *bannerWidget;
std::unordered_map<char, int> manaDevotionMap;
QVBoxLayout *layout;
QHBoxLayout *barLayout;
};
#endif // MANA_DEVOTION_WIDGET_H

View File

@@ -1,355 +0,0 @@
#include "visual_deck_editor_widget.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../deck/deck_loader.h"
#include "../../../../game/cards/card_completer_proxy_model.h"
#include "../../../../game/cards/card_database.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../game/cards/card_database_model.h"
#include "../../../../game/cards/card_search_model.h"
#include "../../../../main.h"
#include "../../../../utility/card_info_comparator.h"
#include "../../layouts/overlap_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../cards/deck_card_zone_display_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/layout_containers/overlap_control_widget.h"
#include <QCheckBox>
#include <QCompleter>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QResizeEvent>
#include <qscrollarea.h>
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)
{
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
mainLayout->setContentsMargins(9, 0, 9, 5);
mainLayout->setSpacing(0);
searchContainer = new QWidget(this);
searchLayout = new QHBoxLayout(searchContainer);
searchContainer->setLayout(searchLayout);
searchBar = new QLineEdit(this);
connect(searchBar, &QLineEdit::returnPressed, this, [=, this]() {
if (!searchBar->hasFocus())
return;
CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(searchBar->text());
if (card) {
emit cardAdditionRequested(card);
}
});
setFocusProxy(searchBar);
setFocusPolicy(Qt::ClickFocus);
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this);
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
CardSearchModel *searchModel = new CardSearchModel(cardDatabaseDisplayModel, this);
proxyModel = new CardCompleterProxyModel(this);
proxyModel->setSourceModel(searchModel);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
proxyModel->setFilterRole(Qt::DisplayRole);
completer = new QCompleter(proxyModel, this);
completer->setCompletionRole(Qt::DisplayRole);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setFilterMode(Qt::MatchContains);
completer->setMaxVisibleItems(15);
searchBar->setCompleter(completer);
// Update suggestions dynamically
connect(searchBar, &QLineEdit::textEdited, searchModel, &CardSearchModel::updateSearchResults);
connect(searchBar, &QLineEdit::textEdited, this, [=, this](const QString &text) {
// Ensure substring matching
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
if (!text.isEmpty()) {
completer->complete(); // Force the dropdown to appear
}
});
connect(completer, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), this,
[=, this](const QString &completion) {
// Prevent the text from changing automatically when navigating with arrow keys
if (searchBar->text() != completion) {
searchBar->setText(completion); // Set the completion explicitly
searchBar->setCursorPosition(searchBar->text().length()); // Move cursor to the end
}
});
// Ensure that the text stays consistent during selection
connect(searchBar, &QLineEdit::textEdited, this, [=, this](const QString &text) {
if (searchBar->hasFocus() && !searchBar->completer()->popup()->isVisible()) {
// Allow text to change when typing, but not when navigating the completer
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
proxyModel->setFilterRegularExpression(
QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
}
});
// Search button functionality
searchPushButton = new QPushButton(this);
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() {
CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(searchBar->text());
if (card) {
emit cardAdditionRequested(card);
}
});
searchLayout->addWidget(searchBar);
searchLayout->addWidget(searchPushButton);
mainLayout->addWidget(searchContainer);
groupAndSortContainer = new QWidget(this);
groupAndSortLayout = new QHBoxLayout(groupAndSortContainer);
groupAndSortLayout->setAlignment(Qt::AlignLeft);
groupAndSortContainer->setLayout(groupAndSortLayout);
groupByComboBox = new QComboBox();
QStringList groupProperties = {"maintype", "colors", "cmc", "name"};
groupByComboBox->addItems(groupProperties);
groupByComboBox->setMinimumWidth(300);
connect(groupByComboBox, QOverload<const QString &>::of(&QComboBox::currentTextChanged), this,
&VisualDeckEditorWidget::actChangeActiveGroupCriteria);
actChangeActiveGroupCriteria();
sortCriteriaButton = new SettingsButtonWidget(this);
sortLabel = new QLabel(sortCriteriaButton);
sortLabel->setWordWrap(true);
QStringList sortProperties = {"colors", "cmc", "name", "maintype"};
sortByListWidget = new QListWidget();
sortByListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
sortByListWidget->setDragDropMode(QAbstractItemView::InternalMove);
sortByListWidget->setDefaultDropAction(Qt::MoveAction);
for (const QString &property : sortProperties) {
QListWidgetItem *item = new QListWidgetItem(property, sortByListWidget);
item->setFlags(item->flags() | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
connect(sortByListWidget->model(), &QAbstractItemModel::rowsMoved, this,
&VisualDeckEditorWidget::actChangeActiveSortCriteria);
actChangeActiveSortCriteria();
sortByListWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
sortCriteriaButton->addSettingsWidget(sortLabel);
sortCriteriaButton->addSettingsWidget(sortByListWidget);
displayTypeButton = new QPushButton(this);
connect(displayTypeButton, &QPushButton::clicked, this, &VisualDeckEditorWidget::updateDisplayType);
groupAndSortLayout->addWidget(groupByComboBox);
groupAndSortLayout->addWidget(sortCriteriaButton);
groupAndSortLayout->addWidget(displayTypeButton);
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setMinimumSize(0, 0);
// Set scrollbar policies
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
zoneContainer = new QWidget(scrollArea);
zoneContainerLayout = new QVBoxLayout(zoneContainer);
zoneContainer->setLayout(zoneContainerLayout);
scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter);
scrollArea->setWidget(zoneContainer);
cardSizeWidget = new CardSizeWidget(this);
mainLayout->addWidget(groupAndSortContainer);
mainLayout->addWidget(scrollArea);
mainLayout->addWidget(cardSizeWidget);
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &VisualDeckEditorWidget::onCardAddition);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &VisualDeckEditorWidget::onCardRemoval);
constructZoneWidgetsFromDeckListModel();
retranslateUi();
}
void VisualDeckEditorWidget::retranslateUi()
{
sortLabel->setText(tr("Click and drag to change the sort order within the groups"));
searchPushButton->setText(tr("Quick search and add card"));
searchPushButton->setToolTip(tr("Search for closest match in the database (with auto-suggestions) and add "
"preferred printing to the deck on pressing enter"));
sortCriteriaButton->setToolTip(tr("Configure how cards are sorted within their groups"));
displayTypeButton->setText(tr("Overlap Layout"));
displayTypeButton->setToolTip(
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
}
void VisualDeckEditorWidget::cleanupInvalidZones(DeckCardZoneDisplayWidget *displayWidget)
{
zoneContainerLayout->removeWidget(displayWidget);
for (auto idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
indexToWidgetMap.remove(idx);
}
}
delete displayWidget;
}
void VisualDeckEditorWidget::onCardAddition(const QModelIndex &parent, int first, int last)
{
if (parent == deckListModel->getRoot()) {
for (int i = first; i <= last; i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, deckListModel->getRoot()));
if (indexToWidgetMap.contains(index)) {
continue;
}
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
zoneContainer, deckListModel, index,
deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(), activeGroupCriteria,
activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this,
&VisualDeckEditorWidget::onCardClick);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this,
&VisualDeckEditorWidget::cleanupInvalidZones);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneDisplayWidget->refreshDisplayType(currentDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
indexToWidgetMap.insert(index, zoneDisplayWidget);
}
}
}
void VisualDeckEditorWidget::onCardRemoval(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent);
Q_UNUSED(first);
Q_UNUSED(last);
for (const QPersistentModelIndex &idx : indexToWidgetMap.keys()) {
if (!idx.isValid()) {
zoneContainerLayout->removeWidget(indexToWidgetMap.value(idx));
indexToWidgetMap.value(idx)->deleteLater();
indexToWidgetMap.remove(idx);
}
}
}
void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel()
{
for (int i = 0; i < deckListModel->rowCount(deckListModel->parent(QModelIndex())); i++) {
QPersistentModelIndex index = QPersistentModelIndex(deckListModel->index(i, 0, deckListModel->getRoot()));
if (indexToWidgetMap.contains(index)) {
continue;
}
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
zoneContainer, deckListModel, index,
deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(), activeGroupCriteria,
activeSortCriteria, currentDisplayType, 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::requestCleanup, this,
&VisualDeckEditorWidget::cleanupInvalidZones);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::refreshDisplayType);
zoneContainerLayout->addWidget(zoneDisplayWidget);
indexToWidgetMap.insert(index, zoneDisplayWidget);
}
}
void VisualDeckEditorWidget::updateZoneWidgets()
{
}
void VisualDeckEditorWidget::updateDisplayType()
{
// Toggle the display type
currentDisplayType = (currentDisplayType == DisplayType::Overlap) ? DisplayType::Flat : DisplayType::Overlap;
// Update UI and emit signal
switch (currentDisplayType) {
case DisplayType::Flat:
displayTypeButton->setText(tr("Flat Layout"));
break;
case DisplayType::Overlap:
displayTypeButton->setText(tr("Overlap Layout"));
break;
}
emit displayTypeChanged(currentDisplayType);
}
void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
}
void VisualDeckEditorWidget::actChangeActiveGroupCriteria()
{
activeGroupCriteria = groupByComboBox->currentText();
emit activeGroupCriteriaChanged(activeGroupCriteria);
}
void VisualDeckEditorWidget::actChangeActiveSortCriteria()
{
QStringList selectedCriteria;
for (int i = 0; i < sortByListWidget->count(); ++i) {
QListWidgetItem *item = sortByListWidget->item(i);
selectedCriteria.append(item->text()); // Collect user-defined sort order
}
activeSortCriteria = selectedCriteria;
emit activeSortCriteriaChanged(selectedCriteria);
}
void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight)
{
// Might use these at some point.
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
// Necessary to delay this in this manner else the updateDisplay will nuke widgets while their onClick event
// hasn't returned yet. Interval of 0 means QT will schedule this after the current event loop has finished.
updateZoneWidgets();
}
void VisualDeckEditorWidget::onHover(CardInfoPtr hoveredCard)
{
emit activeCardChanged(hoveredCard);
}
void VisualDeckEditorWidget::onCardClick(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance,
QString zoneName)
{
emit cardClicked(event, instance, zoneName);
}

View File

@@ -1,188 +0,0 @@
#include "deck_preview_deck_tags_display_widget.h"
#include "../../../../../dialogs/dlg_convert_deck_to_cod_format.h"
#include "../../../../../settings/cache_settings.h"
#include "../../../../tabs/tab_deck_editor.h"
#include "../../general/layout_containers/flow_widget.h"
#include "deck_preview_tag_addition_widget.h"
#include "deck_preview_tag_dialog.h"
#include "deck_preview_tag_display_widget.h"
#include "deck_preview_widget.h"
#include <QDirIterator>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
: QWidget(_parent), deckList(nullptr)
{
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
// Create layout
auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
setFixedHeight(100);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
if (_deckList) {
connectDeckList(_deckList);
}
layout->addWidget(flowWidget);
}
void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList)
{
if (deckList) {
disconnect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
}
deckList = _deckList;
connect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
refreshTags();
}
void DeckPreviewDeckTagsDisplayWidget::refreshTags()
{
flowWidget->clearLayout();
for (const QString &tag : deckList->getTags()) {
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
}
auto tagAdditionWidget = new DeckPreviewTagAdditionWidget(this, tr("Edit tags ..."));
connect(tagAdditionWidget, &DeckPreviewTagAdditionWidget::tagClicked, this,
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
flowWidget->addWidget(tagAdditionWidget);
}
/**
* Gets the filepath of all files (no directories) in target directory and all subdirectories
*/
static QStringList getAllFiles(const QString &filePath)
{
QStringList allFiles;
// QDirIterator with QDir::Files ensures only files are listed (no directories)
QDirIterator it(filePath, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (it.hasNext()) {
allFiles << it.next(); // Add each file path to the list
}
return allFiles;
}
bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
{
QFileInfo fileInfo(filePath);
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
if (QFile::exists(newFileName)) {
QMessageBox::StandardButton reply =
QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"),
QObject::tr("A .cod version of this deck already exists. Overwrite it?"),
QMessageBox::Yes | QMessageBox::No);
return reply == QMessageBox::Yes;
}
return true; // Safe to proceed
}
void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
{
if (qobject_cast<DeckPreviewWidget *>(parentWidget())) {
auto *deckPreviewWidget = qobject_cast<DeckPreviewWidget *>(parentWidget());
QStringList knownTags = deckPreviewWidget->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags();
QStringList activeTags = deckList->getTags();
bool canAddTags = true;
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) != DeckLoader::CockatriceFormat) {
canAddTags = false;
// Retrieve saved preference if the prompt is disabled
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
return;
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
deckPreviewWidget->refreshBannerCardText();
canAddTags = true;
}
} else {
// Show the dialog to the user
DialogConvertDeckToCodFormat conversionDialog(parentWidget());
if (conversionDialog.exec() == QDialog::Accepted) {
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
return;
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
deckPreviewWidget->refreshBannerCardText();
canAddTags = true;
if (conversionDialog.dontAskAgain()) {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(true);
}
} else {
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(false);
if (conversionDialog.dontAskAgain()) {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
} else {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(true);
}
}
}
}
if (canAddTags) {
DeckPreviewTagDialog dialog(knownTags, activeTags);
if (dialog.exec() == QDialog::Accepted) {
QStringList updatedTags = dialog.getActiveTags();
deckList->setTags(updatedTags);
deckPreviewWidget->deckLoader->saveToFile(deckPreviewWidget->filePath, DeckLoader::CockatriceFormat);
}
}
} else if (parentWidget()) {
// If we're the child of an AbstractTabDeckEditor, we are buried under a ton of childWidgets in the
// DeckInfoDock.
QWidget *currentParent = parentWidget();
while (currentParent) {
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
break;
}
currentParent = currentParent->parentWidget();
}
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
auto *deckEditor = qobject_cast<AbstractTabDeckEditor *>(currentParent);
QStringList knownTags;
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
DeckLoader loader;
for (const QString &file : allFiles) {
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
QStringList tags = loader.getTags();
knownTags.append(tags);
knownTags.removeDuplicates();
}
QStringList activeTags = deckList->getTags();
DeckPreviewTagDialog dialog(knownTags, activeTags);
if (dialog.exec() == QDialog::Accepted) {
QStringList updatedTags = dialog.getActiveTags();
deckList->setTags(updatedTags);
deckEditor->setModified(true);
}
}
}
}

View File

@@ -1,26 +0,0 @@
#ifndef DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
#define DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
#include "../../../../../deck/deck_loader.h"
#include "deck_preview_widget.h"
#include <QWidget>
inline bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath);
class DeckPreviewWidget;
class DeckPreviewDeckTagsDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList);
void connectDeckList(DeckList *_deckList);
void refreshTags();
DeckList *deckList;
FlowWidget *flowWidget;
public slots:
void openTagEditDlg();
};
#endif // DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H

View File

@@ -0,0 +1,21 @@
#ifndef COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
#define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
#include "../../client/settings/cache_settings.h"
#include <libcockatrice/interfaces/interface_card_preference_provider.h>
class SettingsCardPreferenceProvider : public ICardPreferenceProvider
{
public:
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override
{
return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName);
}
[[nodiscard]] bool getIncludeRebalancedCards() const override
{
return SettingsCache::instance().getIncludeRebalancedCards();
}
};
#endif // COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H

View File

@@ -1,156 +0,0 @@
#ifndef DECKLISTMODEL_H
#define DECKLISTMODEL_H
#include "../game/cards/card_info.h"
#include "decklist.h"
#include <QAbstractItemModel>
#include <QList>
class DeckLoader;
class CardDatabase;
class QPrinter;
class QTextCursor;
enum DeckListModelGroupCriteria
{
MAIN_TYPE,
MANA_COST,
COLOR
};
class DecklistModelCardNode : public AbstractDecklistCardNode
{
private:
DecklistCardNode *dataNode;
public:
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent, int position = -1)
: AbstractDecklistCardNode(_parent, position), dataNode(_dataNode)
{
}
int getNumber() const override
{
return dataNode->getNumber();
}
void setNumber(int _number) override
{
dataNode->setNumber(_number);
}
QString getName() const override
{
return dataNode->getName();
}
void setName(const QString &_name) override
{
dataNode->setName(_name);
}
QString getCardProviderId() const override
{
return dataNode->getCardProviderId();
}
void setCardProviderId(const QString &_cardProviderId) override
{
dataNode->setCardProviderId(_cardProviderId);
}
QString getCardSetShortName() const override
{
return dataNode->getCardSetShortName();
}
void setCardSetShortName(const QString &_cardSetShortName) override
{
dataNode->setCardSetShortName(_cardSetShortName);
}
QString getCardCollectorNumber() const override
{
return dataNode->getCardCollectorNumber();
}
void setCardCollectorNumber(const QString &_cardSetNumber) override
{
dataNode->setCardCollectorNumber(_cardSetNumber);
}
DecklistCardNode *getDataNode() const
{
return dataNode;
}
[[nodiscard]] bool isDeckHeader() const override
{
return false;
}
};
class DeckListModel : public QAbstractItemModel
{
Q_OBJECT
private slots:
void rebuildTree();
public slots:
void printDeckList(QPrinter *printer);
signals:
void deckHashChanged();
public:
explicit DeckListModel(QObject *parent = nullptr);
~DeckListModel() override;
QModelIndex getRoot() const
{
return nodeToIndex(root);
};
QString getGroupCriteriaForCard(CardInfoPtr info) const;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool removeRows(int row, int count, const QModelIndex &parent) override;
QModelIndex findCard(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
QModelIndex addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway);
QModelIndex addCard(const ::QString &cardName,
const CardInfoPerSet &cardInfoSet,
const QString &zoneName,
bool abAddAnyway = false);
int findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const;
void sort(int column, Qt::SortOrder order) override;
void cleanList();
DeckLoader *getDeckList() const
{
return deckList;
}
void setDeckList(DeckLoader *_deck);
QList<CardInfoPtr> getCardsAsCardInfoPtrs() const;
QList<CardInfoPtr> getCardsAsCardInfoPtrsForZone(QString zoneName) const;
QList<QString> *getZones() const;
void setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria);
private:
DeckLoader *deckList;
InnerDecklistNode *root;
DeckListModelGroupCriteria activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE;
int lastKnownColumn;
Qt::SortOrder lastKnownOrder;
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
DecklistModelCardNode *findCardNode(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
void emitRecursiveUpdates(const QModelIndex &index);
void sortHelper(InnerDecklistNode *node, Qt::SortOrder order);
void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
template <typename T> T getNode(const QModelIndex &index) const
{
if (!index.isValid())
return dynamic_cast<T>(root);
return dynamic_cast<T>(static_cast<AbstractDecklistNode *>(index.internalPointer()));
}
};
#endif

View File

@@ -1,106 +0,0 @@
#ifndef DECK_LOADER_H
#define DECK_LOADER_H
#include "decklist.h"
#include <QLoggingCategory>
inline Q_LOGGING_CATEGORY(DeckLoaderLog, "deck_loader")
class DeckLoader : public DeckList
{
Q_OBJECT
signals:
void deckLoaded();
void loadFinished(bool success);
public:
enum FileFormat
{
PlainTextFormat,
CockatriceFormat
};
/**
* Supported file extensions for decklist files
*/
static const QStringList ACCEPTED_FILE_EXTENSIONS;
/**
* For use with `QFileDialog::setNameFilters`
*/
static const QStringList FILE_NAME_FILTERS;
enum DecklistWebsite
{
DecklistOrg,
DecklistXyz
};
private:
QString lastFileName;
FileFormat lastFileFormat;
int lastRemoteDeckId;
public:
DeckLoader();
explicit DeckLoader(const QString &nativeString);
explicit DeckLoader(const DeckList &other);
DeckLoader(const DeckLoader &other);
const QString &getLastFileName() const
{
return lastFileName;
}
void setLastFileName(const QString &_lastFileName)
{
lastFileName = _lastFileName;
}
FileFormat getLastFileFormat() const
{
return lastFileFormat;
}
int getLastRemoteDeckId() const
{
return lastRemoteDeckId;
}
bool hasNotBeenLoaded() const
{
return getLastFileName().isEmpty() && getLastRemoteDeckId() == -1;
}
void clearSetNamesAndNumbers();
static FileFormat getFormatFromName(const QString &fileName);
bool loadFromFile(const QString &fileName, FileFormat fmt, bool userRequest = false);
bool loadFromFileAsync(const QString &fileName, FileFormat fmt, bool userRequest);
bool loadFromRemote(const QString &nativeString, int remoteDeckId);
bool saveToFile(const QString &fileName, FileFormat fmt);
bool updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt);
QString exportDeckToDecklist(DecklistWebsite website);
void setProviderIdToPreferredPrinting();
void resolveSetNameAndNumberToProviderID();
void saveToClipboard(bool addComments = true, bool addSetNameAndNumber = true) const;
// overload
bool saveToStream_Plain(QTextStream &out, bool addComments = true, bool addSetNameAndNumber = true) const;
bool convertToCockatriceFormat(QString fileName);
protected:
void saveToStream_DeckHeader(QTextStream &out) const;
void saveToStream_DeckZone(QTextStream &out,
const InnerDecklistNode *zoneNode,
bool addComments = true,
bool addSetNameAndNumber = true) const;
void saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards,
bool addComments = true,
bool addSetNameAndNumber = true) const;
[[nodiscard]] QString getCardZoneFromName(QString cardName, QString currentZoneName) override;
[[nodiscard]] QString getCompleteCardName(const QString &cardName) const override;
};
#endif

View File

@@ -1,8 +1,9 @@
#include "deck_filter_string.h"
#include "../cards/card_database_manager.h"
#include "filter_string.h"
#include "lib/peglib.h"
#include <QFileInfo>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/utility/peglib.h>
static peg::parser search(R"(
Start <- QueryPartList
@@ -117,8 +118,8 @@ static void setupParserRules()
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool {
int count = 0;
deck->deckLoader->forEachCard([&](InnerDecklistNode *, const DecklistCardNode *node) {
auto cardInfoPtr = CardDatabaseManager::getInstance()->getCard(node->getName());
deck->deckLoader->getDeckList()->forEachCard([&](InnerDecklistNode *, const DecklistCardNode *node) {
auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) {
count += node->getNumber();
}
@@ -136,7 +137,7 @@ static void setupParserRules()
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
return deck->deckLoader->getName().contains(name, Qt::CaseInsensitive);
return deck->deckLoader->getDeckList()->getName().contains(name, Qt::CaseInsensitive);
};
};

View File

@@ -1,13 +1,17 @@
/**
* @file deck_filter_string.h
* @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/
#ifndef DECK_FILTER_STRING_H
#define DECK_FILTER_STRING_H
#include "../../client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h"
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h"
#include <QLoggingCategory>
#include <QMap>
#include <QString>
#include <functional>
#include <utility>
inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string");
@@ -34,7 +38,7 @@ public:
return filter(deck, info);
}
bool valid() const
[[nodiscard]] bool valid() const
{
return _error.isEmpty();
}

View File

@@ -1,11 +1,11 @@
#include "filter_builder.h"
#include "../../deck/custom_line_edit.h"
#include "filter_card.h"
#include "../interface/widgets/utility/custom_line_edit.h"
#include <QComboBox>
#include <QGridLayout>
#include <QPushButton>
#include <libcockatrice/filters/filter_card.h>
FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
{

View File

@@ -1,3 +1,9 @@
/**
* @file filter_builder.h
* @ingroup CardDatabaseModelFilters
* @brief TODO: Document this.
*/
#ifndef FILTERBUILDER_H
#define FILTERBUILDER_H

View File

@@ -1,9 +1,8 @@
#include "filter_tree_model.h"
#include "filter_card.h"
#include "filter_tree.h"
#include <QFont>
#include <libcockatrice/filters/filter_card.h>
#include <libcockatrice/filters/filter_tree.h>
FilterTreeModel::FilterTreeModel(QObject *parent) : QAbstractItemModel(parent)
{

View File

@@ -1,9 +1,14 @@
/**
* @file filter_tree_model.h
* @ingroup CardDatabaseModelFilters
* @brief TODO: Document this.
*/
#ifndef FILTERTREEMODEL_H
#define FILTERTREEMODEL_H
#include "filter_card.h"
#include <QAbstractItemModel>
#include <libcockatrice/filters/filter_card.h>
class FilterTree;
class CardFilter;
@@ -19,8 +24,8 @@ public slots:
void addFilter(const CardFilter *f);
void removeFilter(const CardFilter *f);
void clearFiltersOfType(CardFilter::Attr filterType);
QList<const CardFilter *> getFiltersOfType(CardFilter::Attr filterType) const;
QList<const CardFilter *> allFilters() const;
[[nodiscard]] QList<const CardFilter *> getFiltersOfType(CardFilter::Attr filterType) const;
[[nodiscard]] QList<const CardFilter *> allFilters() const;
private slots:
void proxyBeginInsertRow(const FilterTreeNode *, int);
@@ -29,23 +34,23 @@ private slots:
void proxyEndRemoveRow(const FilterTreeNode *, int);
private:
FilterTreeNode *indexToNode(const QModelIndex &idx) const;
[[nodiscard]] FilterTreeNode *indexToNode(const QModelIndex &idx) const;
QModelIndex nodeIndex(const FilterTreeNode *node, int row, int column) const;
public:
FilterTreeModel(QObject *parent = nullptr);
~FilterTreeModel() override;
FilterTree *filterTree() const
[[nodiscard]] FilterTree *filterTree() const
{
return fTree;
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex parent(const QModelIndex &ind) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
[[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;
[[nodiscard]] QModelIndex parent(const QModelIndex &ind) const override;
[[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent) const override;
bool removeRows(int row, int count, const QModelIndex &parent) override;
void clear();
};

View File

@@ -22,8 +22,11 @@ static QTextBrowser *createBrowser(const QString &helpFile)
QString text = in.readAll();
file.close();
// Poor Markdown Converter
// --- Remove @page declarations at the top of the file ---
auto opts = QRegularExpression::MultilineOption;
text = text.replace(QRegularExpression(R"(^\s*@page[^\n]*\n)", opts), "");
// Poor Markdown Converter
text = text.replace(QRegularExpression("^(###)(.*)", opts), "<h3>\\2</h3>")
.replace(QRegularExpression("^(##)(.*)", opts), "<h2>\\2</h2>")
.replace(QRegularExpression("^(#)(.*)", opts), "<h1>\\2</h1>")

View File

@@ -1,3 +1,10 @@
/**
* @file syntax_help.h
* @ingroup CardDatabaseModelFilters
* @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/
#ifndef SEARCH_SYNTAX_HELP_H
#define SEARCH_SYNTAX_HELP_H

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