summaryrefslogtreecommitdiffstats
path: root/libkdegames
diff options
context:
space:
mode:
Diffstat (limited to 'libkdegames')
-rw-r--r--libkdegames/Makefile.am23
-rw-r--r--libkdegames/README25
-rw-r--r--libkdegames/TODO10
-rw-r--r--libkdegames/carddecks/Makefile.am24
-rw-r--r--libkdegames/carddecks/README12
-rw-r--r--libkdegames/carddecks/cards-aisleriot/1.pngbin0 -> 1261 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/10.pngbin0 -> 1933 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/11.pngbin0 -> 2422 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/12.pngbin0 -> 2387 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/13.pngbin0 -> 1966 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/14.pngbin0 -> 1794 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/15.pngbin0 -> 2368 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/16.pngbin0 -> 2343 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/17.pngbin0 -> 2790 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/18.pngbin0 -> 2092 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/19.pngbin0 -> 2597 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/2.pngbin0 -> 1125 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/20.pngbin0 -> 2465 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/21.pngbin0 -> 2503 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/22.pngbin0 -> 1888 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/23.pngbin0 -> 2470 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/24.pngbin0 -> 2258 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/25.pngbin0 -> 2063 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/26.pngbin0 -> 1845 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/27.pngbin0 -> 2226 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/28.pngbin0 -> 2144 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/29.pngbin0 -> 1980 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/3.pngbin0 -> 1505 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/30.pngbin0 -> 1788 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/31.pngbin0 -> 2091 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/32.pngbin0 -> 2097 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/33.pngbin0 -> 1810 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/34.pngbin0 -> 1685 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/35.pngbin0 -> 1917 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/36.pngbin0 -> 2006 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/37.pngbin0 -> 1654 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/38.pngbin0 -> 1737 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/39.pngbin0 -> 2014 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/4.pngbin0 -> 1559 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/40.pngbin0 -> 1885 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/41.pngbin0 -> 1449 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/42.pngbin0 -> 1577 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/43.pngbin0 -> 1798 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/44.pngbin0 -> 1763 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/45.pngbin0 -> 1619 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/46.pngbin0 -> 1334 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/47.pngbin0 -> 1768 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/48.pngbin0 -> 1738 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/49.pngbin0 -> 1312 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/5.pngbin0 -> 2129 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/50.pngbin0 -> 1126 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/51.pngbin0 -> 1570 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/52.pngbin0 -> 1584 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/6.pngbin0 -> 1877 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/7.pngbin0 -> 2403 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/8.pngbin0 -> 2359 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/9.pngbin0 -> 2143 bytes
-rw-r--r--libkdegames/carddecks/cards-aisleriot/COPYRIGHT10
-rw-r--r--libkdegames/carddecks/cards-aisleriot/index.desktop20
-rw-r--r--libkdegames/carddecks/cards-default/1.pngbin0 -> 266 bytes
-rw-r--r--libkdegames/carddecks/cards-default/10.pngbin0 -> 1002 bytes
-rw-r--r--libkdegames/carddecks/cards-default/11.pngbin0 -> 1048 bytes
-rw-r--r--libkdegames/carddecks/cards-default/12.pngbin0 -> 1028 bytes
-rw-r--r--libkdegames/carddecks/cards-default/13.pngbin0 -> 952 bytes
-rw-r--r--libkdegames/carddecks/cards-default/14.pngbin0 -> 924 bytes
-rw-r--r--libkdegames/carddecks/cards-default/15.pngbin0 -> 940 bytes
-rw-r--r--libkdegames/carddecks/cards-default/16.pngbin0 -> 896 bytes
-rw-r--r--libkdegames/carddecks/cards-default/17.pngbin0 -> 415 bytes
-rw-r--r--libkdegames/carddecks/cards-default/18.pngbin0 -> 425 bytes
-rw-r--r--libkdegames/carddecks/cards-default/19.pngbin0 -> 451 bytes
-rw-r--r--libkdegames/carddecks/cards-default/2.pngbin0 -> 418 bytes
-rw-r--r--libkdegames/carddecks/cards-default/20.pngbin0 -> 415 bytes
-rw-r--r--libkdegames/carddecks/cards-default/21.pngbin0 -> 403 bytes
-rw-r--r--libkdegames/carddecks/cards-default/22.pngbin0 -> 410 bytes
-rw-r--r--libkdegames/carddecks/cards-default/23.pngbin0 -> 441 bytes
-rw-r--r--libkdegames/carddecks/cards-default/24.pngbin0 -> 400 bytes
-rw-r--r--libkdegames/carddecks/cards-default/25.pngbin0 -> 349 bytes
-rw-r--r--libkdegames/carddecks/cards-default/26.pngbin0 -> 367 bytes
-rw-r--r--libkdegames/carddecks/cards-default/27.pngbin0 -> 395 bytes
-rw-r--r--libkdegames/carddecks/cards-default/28.pngbin0 -> 395 bytes
-rw-r--r--libkdegames/carddecks/cards-default/29.pngbin0 -> 361 bytes
-rw-r--r--libkdegames/carddecks/cards-default/3.pngbin0 -> 303 bytes
-rw-r--r--libkdegames/carddecks/cards-default/30.pngbin0 -> 373 bytes
-rw-r--r--libkdegames/carddecks/cards-default/31.pngbin0 -> 414 bytes
-rw-r--r--libkdegames/carddecks/cards-default/32.pngbin0 -> 373 bytes
-rw-r--r--libkdegames/carddecks/cards-default/33.pngbin0 -> 336 bytes
-rw-r--r--libkdegames/carddecks/cards-default/34.pngbin0 -> 356 bytes
-rw-r--r--libkdegames/carddecks/cards-default/35.pngbin0 -> 388 bytes
-rw-r--r--libkdegames/carddecks/cards-default/36.pngbin0 -> 357 bytes
-rw-r--r--libkdegames/carddecks/cards-default/37.pngbin0 -> 347 bytes
-rw-r--r--libkdegames/carddecks/cards-default/38.pngbin0 -> 367 bytes
-rw-r--r--libkdegames/carddecks/cards-default/39.pngbin0 -> 395 bytes
-rw-r--r--libkdegames/carddecks/cards-default/4.pngbin0 -> 290 bytes
-rw-r--r--libkdegames/carddecks/cards-default/40.pngbin0 -> 368 bytes
-rw-r--r--libkdegames/carddecks/cards-default/41.pngbin0 -> 308 bytes
-rw-r--r--libkdegames/carddecks/cards-default/42.pngbin0 -> 321 bytes
-rw-r--r--libkdegames/carddecks/cards-default/43.pngbin0 -> 346 bytes
-rw-r--r--libkdegames/carddecks/cards-default/44.pngbin0 -> 332 bytes
-rw-r--r--libkdegames/carddecks/cards-default/45.pngbin0 -> 321 bytes
-rw-r--r--libkdegames/carddecks/cards-default/46.pngbin0 -> 326 bytes
-rw-r--r--libkdegames/carddecks/cards-default/47.pngbin0 -> 361 bytes
-rw-r--r--libkdegames/carddecks/cards-default/48.pngbin0 -> 342 bytes
-rw-r--r--libkdegames/carddecks/cards-default/49.pngbin0 -> 300 bytes
-rw-r--r--libkdegames/carddecks/cards-default/5.pngbin0 -> 890 bytes
-rw-r--r--libkdegames/carddecks/cards-default/50.pngbin0 -> 304 bytes
-rw-r--r--libkdegames/carddecks/cards-default/51.pngbin0 -> 333 bytes
-rw-r--r--libkdegames/carddecks/cards-default/52.pngbin0 -> 314 bytes
-rw-r--r--libkdegames/carddecks/cards-default/6.pngbin0 -> 911 bytes
-rw-r--r--libkdegames/carddecks/cards-default/7.pngbin0 -> 1062 bytes
-rw-r--r--libkdegames/carddecks/cards-default/8.pngbin0 -> 988 bytes
-rw-r--r--libkdegames/carddecks/cards-default/9.pngbin0 -> 1064 bytes
-rw-r--r--libkdegames/carddecks/cards-default/index.desktop115
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/1.pngbin0 -> 7122 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/10.pngbin0 -> 6497 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/11.pngbin0 -> 6381 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/12.pngbin0 -> 6476 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/13.pngbin0 -> 6642 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/14.pngbin0 -> 6278 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/15.pngbin0 -> 6542 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/16.pngbin0 -> 6523 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/17.pngbin0 -> 5118 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/18.pngbin0 -> 5137 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/19.pngbin0 -> 5239 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/2.pngbin0 -> 7128 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/20.pngbin0 -> 4894 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/21.pngbin0 -> 4898 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/22.pngbin0 -> 4867 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/23.pngbin0 -> 5015 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/24.pngbin0 -> 4677 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/25.pngbin0 -> 4964 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/26.pngbin0 -> 5025 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/27.pngbin0 -> 4944 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/28.pngbin0 -> 4820 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/29.pngbin0 -> 4772 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/3.pngbin0 -> 6723 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/30.pngbin0 -> 4844 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/31.pngbin0 -> 4757 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/32.pngbin0 -> 4662 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/33.pngbin0 -> 4607 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/34.pngbin0 -> 4670 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/35.pngbin0 -> 4584 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/36.pngbin0 -> 4501 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/37.pngbin0 -> 4411 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/38.pngbin0 -> 4481 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/39.pngbin0 -> 4410 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/4.pngbin0 -> 6842 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/40.pngbin0 -> 4315 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/41.pngbin0 -> 4220 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/42.pngbin0 -> 4294 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/43.pngbin0 -> 4239 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/44.pngbin0 -> 4185 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/45.pngbin0 -> 4071 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/46.pngbin0 -> 4118 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/47.pngbin0 -> 4075 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/48.pngbin0 -> 4044 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/49.pngbin0 -> 3886 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/5.pngbin0 -> 6779 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/50.pngbin0 -> 3922 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/51.pngbin0 -> 3892 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/52.pngbin0 -> 3866 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/6.pngbin0 -> 6679 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/7.pngbin0 -> 6638 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/8.pngbin0 -> 6642 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/9.pngbin0 -> 6644 bytes
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT12
-rw-r--r--libkdegames/carddecks/cards-dondorf-whist-b/index.desktop17
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/1.pngbin0 -> 1430 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/10.pngbin0 -> 4774 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/11.pngbin0 -> 4670 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/12.pngbin0 -> 4746 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/13.pngbin0 -> 4573 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/14.pngbin0 -> 4580 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/15.pngbin0 -> 4546 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/16.pngbin0 -> 4636 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/17.pngbin0 -> 2402 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/18.pngbin0 -> 2276 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/19.pngbin0 -> 2614 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/2.pngbin0 -> 1408 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/20.pngbin0 -> 2599 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/21.pngbin0 -> 2397 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/22.pngbin0 -> 2293 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/23.pngbin0 -> 2626 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/24.pngbin0 -> 2489 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/25.pngbin0 -> 2227 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/26.pngbin0 -> 2126 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/27.pngbin0 -> 2434 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/28.pngbin0 -> 2253 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/29.pngbin0 -> 2352 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/3.pngbin0 -> 1765 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/30.pngbin0 -> 2204 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/31.pngbin0 -> 2571 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/32.pngbin0 -> 2385 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/33.pngbin0 -> 2152 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/34.pngbin0 -> 2049 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/35.pngbin0 -> 2410 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/36.pngbin0 -> 2251 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/37.pngbin0 -> 2076 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/38.pngbin0 -> 1994 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/39.pngbin0 -> 2367 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/4.pngbin0 -> 1679 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/40.pngbin0 -> 2256 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/41.pngbin0 -> 1870 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/42.pngbin0 -> 1811 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/43.pngbin0 -> 2160 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/44.pngbin0 -> 2131 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/45.pngbin0 -> 1891 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/46.pngbin0 -> 1792 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/47.pngbin0 -> 2191 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/48.pngbin0 -> 2122 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/49.pngbin0 -> 1701 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/5.pngbin0 -> 5611 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/50.pngbin0 -> 1642 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/51.pngbin0 -> 2007 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/52.pngbin0 -> 1989 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/6.pngbin0 -> 5516 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/7.pngbin0 -> 5609 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/8.pngbin0 -> 5592 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/9.pngbin0 -> 4594 bytes
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT13
-rw-r--r--libkdegames/carddecks/cards-gdkcard-bonded/index.desktop37
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/1.pngbin0 -> 4371 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/10.pngbin0 -> 4732 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/11.pngbin0 -> 4488 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/12.pngbin0 -> 4071 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/13.pngbin0 -> 3948 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/14.pngbin0 -> 4105 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/15.pngbin0 -> 3901 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/16.pngbin0 -> 3426 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/17.pngbin0 -> 4134 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/18.pngbin0 -> 4038 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/19.pngbin0 -> 4732 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/2.pngbin0 -> 4013 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/20.pngbin0 -> 4147 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/21.pngbin0 -> 4124 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/22.pngbin0 -> 3459 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/23.pngbin0 -> 4192 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/24.pngbin0 -> 3610 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/25.pngbin0 -> 5800 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/26.pngbin0 -> 5042 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/27.pngbin0 -> 4679 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/28.pngbin0 -> 3785 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/29.pngbin0 -> 4283 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/3.pngbin0 -> 3626 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/30.pngbin0 -> 4421 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/31.pngbin0 -> 5930 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/32.pngbin0 -> 5315 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/33.pngbin0 -> 4687 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/34.pngbin0 -> 4656 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/35.pngbin0 -> 4573 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/36.pngbin0 -> 4321 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/37.pngbin0 -> 4132 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/38.pngbin0 -> 3717 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/39.pngbin0 -> 4222 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/4.pngbin0 -> 3477 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/40.pngbin0 -> 4219 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/41.pngbin0 -> 3946 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/42.pngbin0 -> 4477 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/43.pngbin0 -> 3823 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/44.pngbin0 -> 4917 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/45.pngbin0 -> 3913 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/46.pngbin0 -> 4945 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/47.pngbin0 -> 5243 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/48.pngbin0 -> 4677 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/49.pngbin0 -> 5234 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/5.pngbin0 -> 3864 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/50.pngbin0 -> 4871 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/51.pngbin0 -> 4339 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/52.pngbin0 -> 3983 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/6.pngbin0 -> 4269 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/7.pngbin0 -> 4432 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/8.pngbin0 -> 4201 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/9.pngbin0 -> 5048 bytes
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/COPYRIGHT14
-rw-r--r--libkdegames/carddecks/cards-hard-a-port/index.desktop29
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/1.pngbin0 -> 4366 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/10.pngbin0 -> 4986 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/11.pngbin0 -> 4978 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/12.pngbin0 -> 4982 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/13.pngbin0 -> 4420 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/14.pngbin0 -> 4434 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/15.pngbin0 -> 4282 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/16.pngbin0 -> 4310 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/17.pngbin0 -> 4112 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/18.pngbin0 -> 4114 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/19.pngbin0 -> 4125 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/2.pngbin0 -> 4815 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/20.pngbin0 -> 4116 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/21.pngbin0 -> 4088 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/22.pngbin0 -> 4066 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/23.pngbin0 -> 4068 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/24.pngbin0 -> 4081 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/25.pngbin0 -> 4127 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/26.pngbin0 -> 4126 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/27.pngbin0 -> 4081 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/28.pngbin0 -> 4153 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/29.pngbin0 -> 4202 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/3.pngbin0 -> 4328 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/30.pngbin0 -> 4174 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/31.pngbin0 -> 4193 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/32.pngbin0 -> 4151 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/33.pngbin0 -> 4251 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/34.pngbin0 -> 4215 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/35.pngbin0 -> 4256 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/36.pngbin0 -> 4213 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/37.pngbin0 -> 4294 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/38.pngbin0 -> 4282 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/39.pngbin0 -> 4275 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/4.pngbin0 -> 4297 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/40.pngbin0 -> 4228 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/41.pngbin0 -> 4347 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/42.pngbin0 -> 4329 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/43.pngbin0 -> 4341 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/44.pngbin0 -> 4252 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/45.pngbin0 -> 4273 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/46.pngbin0 -> 4228 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/47.pngbin0 -> 4309 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/48.pngbin0 -> 4105 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/49.pngbin0 -> 4327 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/5.pngbin0 -> 4959 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/50.pngbin0 -> 4024 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/51.pngbin0 -> 4031 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/52.pngbin0 -> 3897 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/6.pngbin0 -> 4973 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/7.pngbin0 -> 4830 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/8.pngbin0 -> 4896 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/9.pngbin0 -> 4952 bytes
-rw-r--r--libkdegames/carddecks/cards-konqi-modern/index.desktop62
-rw-r--r--libkdegames/carddecks/cards-penguins/1.pngbin0 -> 1869 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/10.pngbin0 -> 2801 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/11.pngbin0 -> 3040 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/12.pngbin0 -> 2981 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/13.pngbin0 -> 2161 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/14.pngbin0 -> 2069 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/15.pngbin0 -> 2367 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/16.pngbin0 -> 2274 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/17.pngbin0 -> 2416 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/18.pngbin0 -> 2134 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/19.pngbin0 -> 2381 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/2.pngbin0 -> 3191 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/20.pngbin0 -> 2212 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/21.pngbin0 -> 2330 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/22.pngbin0 -> 2056 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/23.pngbin0 -> 2254 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/24.pngbin0 -> 2035 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/25.pngbin0 -> 2229 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/26.pngbin0 -> 1989 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/27.pngbin0 -> 2140 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/28.pngbin0 -> 2005 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/29.pngbin0 -> 2213 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/3.pngbin0 -> 1739 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/30.pngbin0 -> 1977 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/31.pngbin0 -> 2111 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/32.pngbin0 -> 1872 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/33.pngbin0 -> 2077 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/34.pngbin0 -> 1889 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/35.pngbin0 -> 1991 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/36.pngbin0 -> 1786 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/37.pngbin0 -> 1918 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/38.pngbin0 -> 1762 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/39.pngbin0 -> 1879 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/4.pngbin0 -> 1523 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/40.pngbin0 -> 1741 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/41.pngbin0 -> 1668 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/42.pngbin0 -> 1536 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/43.pngbin0 -> 1640 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/44.pngbin0 -> 1523 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/45.pngbin0 -> 1672 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/46.pngbin0 -> 1563 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/47.pngbin0 -> 1685 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/48.pngbin0 -> 1571 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/49.pngbin0 -> 1432 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/5.pngbin0 -> 2781 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/50.pngbin0 -> 1329 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/51.pngbin0 -> 1462 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/52.pngbin0 -> 1390 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/6.pngbin0 -> 2699 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/7.pngbin0 -> 2923 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/8.pngbin0 -> 2866 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/9.pngbin0 -> 2894 bytes
-rw-r--r--libkdegames/carddecks/cards-penguins/COPYRIGHT10
-rw-r--r--libkdegames/carddecks/cards-penguins/index.desktop67
-rw-r--r--libkdegames/carddecks/cards-spaced/1.pngbin0 -> 5992 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/10.pngbin0 -> 5927 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/11.pngbin0 -> 5976 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/12.pngbin0 -> 6113 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/13.pngbin0 -> 5973 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/14.pngbin0 -> 6036 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/15.pngbin0 -> 5965 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/16.pngbin0 -> 5621 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/17.pngbin0 -> 6906 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/18.pngbin0 -> 6794 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/19.pngbin0 -> 6863 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/2.pngbin0 -> 6041 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/20.pngbin0 -> 6711 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/21.pngbin0 -> 6800 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/22.pngbin0 -> 6749 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/23.pngbin0 -> 6883 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/24.pngbin0 -> 6635 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/25.pngbin0 -> 6736 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/26.pngbin0 -> 6698 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/27.pngbin0 -> 6778 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/28.pngbin0 -> 6515 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/29.pngbin0 -> 6675 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/3.pngbin0 -> 6057 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/30.pngbin0 -> 6594 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/31.pngbin0 -> 6760 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/32.pngbin0 -> 6506 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/33.pngbin0 -> 6554 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/34.pngbin0 -> 6520 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/35.pngbin0 -> 6716 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/36.pngbin0 -> 6444 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/37.pngbin0 -> 6500 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/38.pngbin0 -> 6448 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/39.pngbin0 -> 6636 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/4.pngbin0 -> 5898 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/40.pngbin0 -> 6317 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/41.pngbin0 -> 6381 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/42.pngbin0 -> 6333 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/43.pngbin0 -> 6515 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/44.pngbin0 -> 6247 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/45.pngbin0 -> 6281 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/46.pngbin0 -> 6259 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/47.pngbin0 -> 6378 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/48.pngbin0 -> 6147 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/49.pngbin0 -> 6156 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/5.pngbin0 -> 5995 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/50.pngbin0 -> 6132 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/51.pngbin0 -> 6243 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/52.pngbin0 -> 6043 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/6.pngbin0 -> 6069 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/7.pngbin0 -> 5927 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/8.pngbin0 -> 5866 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/9.pngbin0 -> 6014 bytes
-rw-r--r--libkdegames/carddecks/cards-spaced/COPYRIGHT16
-rw-r--r--libkdegames/carddecks/cards-spaced/index.desktop46
-rw-r--r--libkdegames/carddecks/cards-warwick/0.pngbin0 -> 1606 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/1.pngbin0 -> 500 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/10.pngbin0 -> 1535 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/105.pngbin0 -> 844 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/106.pngbin0 -> 860 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/107.pngbin0 -> 853 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/108.pngbin0 -> 793 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/109.pngbin0 -> 848 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/11.pngbin0 -> 1553 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/110.pngbin0 -> 868 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/111.pngbin0 -> 856 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/112.pngbin0 -> 773 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/113.pngbin0 -> 746 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/114.pngbin0 -> 778 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/115.pngbin0 -> 771 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/116.pngbin0 -> 663 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/12.pngbin0 -> 1500 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/13.pngbin0 -> 1444 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/14.pngbin0 -> 1408 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/15.pngbin0 -> 1452 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/16.pngbin0 -> 1356 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/17.pngbin0 -> 756 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/18.pngbin0 -> 789 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/19.pngbin0 -> 788 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/2.pngbin0 -> 500 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/20.pngbin0 -> 715 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/21.pngbin0 -> 756 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/22.pngbin0 -> 776 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/23.pngbin0 -> 782 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/24.pngbin0 -> 684 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/25.pngbin0 -> 761 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/26.pngbin0 -> 789 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/27.pngbin0 -> 800 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/28.pngbin0 -> 673 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/29.pngbin0 -> 696 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/3.pngbin0 -> 524 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/30.pngbin0 -> 722 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/31.pngbin0 -> 732 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/32.pngbin0 -> 640 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/33.pngbin0 -> 636 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/34.pngbin0 -> 656 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/35.pngbin0 -> 661 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/36.pngbin0 -> 607 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/37.pngbin0 -> 698 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/38.pngbin0 -> 720 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/39.pngbin0 -> 730 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/4.pngbin0 -> 477 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/40.pngbin0 -> 651 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/41.pngbin0 -> 593 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/42.pngbin0 -> 605 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/43.pngbin0 -> 607 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/44.pngbin0 -> 562 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/45.pngbin0 -> 593 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/46.pngbin0 -> 600 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/47.pngbin0 -> 627 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/48.pngbin0 -> 570 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/49.pngbin0 -> 571 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/5.pngbin0 -> 1517 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/50.pngbin0 -> 572 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/51.pngbin0 -> 591 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/52.pngbin0 -> 543 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/6.pngbin0 -> 1477 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/7.pngbin0 -> 1536 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/8.pngbin0 -> 1451 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/9.pngbin0 -> 1617 bytes
-rw-r--r--libkdegames/carddecks/cards-warwick/index.desktop126
-rw-r--r--libkdegames/carddecks/cards-xskat-french/1.pngbin0 -> 2525 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/10.pngbin0 -> 11981 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/11.pngbin0 -> 11495 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/12.pngbin0 -> 11508 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/13.pngbin0 -> 14544 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/14.pngbin0 -> 13192 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/15.pngbin0 -> 12722 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/16.pngbin0 -> 12880 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/17.pngbin0 -> 5891 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/18.pngbin0 -> 4439 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/19.pngbin0 -> 3308 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/2.pngbin0 -> 2244 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/20.pngbin0 -> 3016 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/21.pngbin0 -> 5372 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/22.pngbin0 -> 4037 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/23.pngbin0 -> 3080 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/24.pngbin0 -> 2786 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/25.pngbin0 -> 5005 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/26.pngbin0 -> 3925 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/27.pngbin0 -> 2879 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/28.pngbin0 -> 2777 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/29.pngbin0 -> 4537 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/3.pngbin0 -> 1928 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/30.pngbin0 -> 3505 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/31.pngbin0 -> 2647 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/32.pngbin0 -> 2538 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/33.pngbin0 -> 4139 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/34.pngbin0 -> 3287 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/35.pngbin0 -> 2402 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/36.pngbin0 -> 2227 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/37.pngbin0 -> 4228 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/38.pngbin0 -> 3234 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/39.pngbin0 -> 2528 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/4.pngbin0 -> 1881 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/40.pngbin0 -> 2437 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/41.pngbin0 -> 3653 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/42.pngbin0 -> 2841 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/43.pngbin0 -> 2227 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/44.pngbin0 -> 2123 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/45.pngbin0 -> 3384 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/46.pngbin0 -> 2888 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/47.pngbin0 -> 2382 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/48.pngbin0 -> 2295 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/49.pngbin0 -> 2894 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/5.pngbin0 -> 14410 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/50.pngbin0 -> 2411 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/51.pngbin0 -> 2159 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/52.pngbin0 -> 2043 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/6.pngbin0 -> 13796 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/7.pngbin0 -> 13434 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/8.pngbin0 -> 13344 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/9.pngbin0 -> 12558 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-french/COPYRIGHT8
-rw-r--r--libkdegames/carddecks/cards-xskat-french/index.desktop55
-rw-r--r--libkdegames/carddecks/cards-xskat-german/1.pngbin0 -> 3915 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/10.pngbin0 -> 10814 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/11.pngbin0 -> 10506 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/12.pngbin0 -> 11304 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/13.pngbin0 -> 12477 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/14.pngbin0 -> 12487 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/15.pngbin0 -> 12450 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/16.pngbin0 -> 12614 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/17.pngbin0 -> 7191 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/18.pngbin0 -> 7804 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/19.pngbin0 -> 6331 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/2.pngbin0 -> 3618 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/20.pngbin0 -> 7794 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/21.pngbin0 -> 6694 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/22.pngbin0 -> 7311 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/23.pngbin0 -> 5828 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/24.pngbin0 -> 7572 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/25.pngbin0 -> 6612 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/26.pngbin0 -> 7207 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/27.pngbin0 -> 5627 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/28.pngbin0 -> 6886 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/29.pngbin0 -> 6141 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/3.pngbin0 -> 3816 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/30.pngbin0 -> 6695 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/31.pngbin0 -> 5139 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/32.pngbin0 -> 6181 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/33.pngbin0 -> 5857 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/34.pngbin0 -> 6473 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/35.pngbin0 -> 4789 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/36.pngbin0 -> 5734 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/37.pngbin0 -> 5485 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/38.pngbin0 -> 6006 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/39.pngbin0 -> 4422 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/4.pngbin0 -> 3891 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/40.pngbin0 -> 5223 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/41.pngbin0 -> 5148 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/42.pngbin0 -> 5651 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/43.pngbin0 -> 3821 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/44.pngbin0 -> 4167 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/45.pngbin0 -> 4642 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/46.pngbin0 -> 5223 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/47.pngbin0 -> 3355 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/48.pngbin0 -> 3761 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/49.pngbin0 -> 4505 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/5.pngbin0 -> 13831 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/50.pngbin0 -> 4802 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/51.pngbin0 -> 2589 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/52.pngbin0 -> 2548 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/6.pngbin0 -> 14126 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/7.pngbin0 -> 13910 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/8.pngbin0 -> 14673 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/9.pngbin0 -> 10522 bytes
-rw-r--r--libkdegames/carddecks/cards-xskat-german/COPYRIGHT8
-rw-r--r--libkdegames/carddecks/cards-xskat-german/index.desktop55
-rwxr-xr-xlibkdegames/carddecks/convertpysols69
-rw-r--r--libkdegames/carddecks/decks/deck0.desktop113
-rw-r--r--libkdegames/carddecks/decks/deck0.pngbin0 -> 4918 bytes
-rw-r--r--libkdegames/carddecks/decks/deck1.desktop56
-rw-r--r--libkdegames/carddecks/decks/deck1.pngbin0 -> 4941 bytes
-rw-r--r--libkdegames/carddecks/decks/deck10.desktop66
-rw-r--r--libkdegames/carddecks/decks/deck10.pngbin0 -> 950 bytes
-rw-r--r--libkdegames/carddecks/decks/deck11.desktop66
-rw-r--r--libkdegames/carddecks/decks/deck11.pngbin0 -> 950 bytes
-rw-r--r--libkdegames/carddecks/decks/deck12.desktop41
-rw-r--r--libkdegames/carddecks/decks/deck12.pngbin0 -> 6389 bytes
-rw-r--r--libkdegames/carddecks/decks/deck13.desktop63
-rw-r--r--libkdegames/carddecks/decks/deck13.pngbin0 -> 4296 bytes
-rw-r--r--libkdegames/carddecks/decks/deck14.desktop66
-rw-r--r--libkdegames/carddecks/decks/deck14.pngbin0 -> 2776 bytes
-rw-r--r--libkdegames/carddecks/decks/deck15.desktop19
-rw-r--r--libkdegames/carddecks/decks/deck15.pngbin0 -> 2627 bytes
-rw-r--r--libkdegames/carddecks/decks/deck16.desktop60
-rw-r--r--libkdegames/carddecks/decks/deck16.pngbin0 -> 406 bytes
-rw-r--r--libkdegames/carddecks/decks/deck17.desktop65
-rw-r--r--libkdegames/carddecks/decks/deck17.pngbin0 -> 277 bytes
-rw-r--r--libkdegames/carddecks/decks/deck18.desktop19
-rw-r--r--libkdegames/carddecks/decks/deck18.pngbin0 -> 5217 bytes
-rw-r--r--libkdegames/carddecks/decks/deck19.desktop63
-rw-r--r--libkdegames/carddecks/decks/deck19.pngbin0 -> 5602 bytes
-rw-r--r--libkdegames/carddecks/decks/deck2.desktop49
-rw-r--r--libkdegames/carddecks/decks/deck2.pngbin0 -> 5416 bytes
-rw-r--r--libkdegames/carddecks/decks/deck20.desktop71
-rw-r--r--libkdegames/carddecks/decks/deck20.pngbin0 -> 6011 bytes
-rw-r--r--libkdegames/carddecks/decks/deck21.desktop65
-rw-r--r--libkdegames/carddecks/decks/deck21.pngbin0 -> 6333 bytes
-rw-r--r--libkdegames/carddecks/decks/deck22.desktop58
-rw-r--r--libkdegames/carddecks/decks/deck22.pngbin0 -> 6192 bytes
-rw-r--r--libkdegames/carddecks/decks/deck23.desktop55
-rw-r--r--libkdegames/carddecks/decks/deck23.pngbin0 -> 5964 bytes
-rw-r--r--libkdegames/carddecks/decks/deck24.desktop60
-rw-r--r--libkdegames/carddecks/decks/deck24.pngbin0 -> 4885 bytes
-rw-r--r--libkdegames/carddecks/decks/deck3.desktop60
-rw-r--r--libkdegames/carddecks/decks/deck3.pngbin0 -> 6162 bytes
-rw-r--r--libkdegames/carddecks/decks/deck4.desktop93
-rw-r--r--libkdegames/carddecks/decks/deck4.pngbin0 -> 6355 bytes
-rw-r--r--libkdegames/carddecks/decks/deck5.desktop67
-rw-r--r--libkdegames/carddecks/decks/deck5.pngbin0 -> 4133 bytes
-rw-r--r--libkdegames/carddecks/decks/deck6.desktop66
-rw-r--r--libkdegames/carddecks/decks/deck6.pngbin0 -> 4067 bytes
-rw-r--r--libkdegames/carddecks/decks/deck7.desktop67
-rw-r--r--libkdegames/carddecks/decks/deck7.pngbin0 -> 3464 bytes
-rw-r--r--libkdegames/carddecks/decks/deck8.desktop67
-rw-r--r--libkdegames/carddecks/decks/deck8.pngbin0 -> 3780 bytes
-rw-r--r--libkdegames/carddecks/decks/deck9.desktop7
-rw-r--r--libkdegames/carddecks/decks/deck9.pngbin0 -> 1019 bytes
-rw-r--r--libkdegames/configure.in.in49
-rw-r--r--libkdegames/highscore/INSTALL12
-rw-r--r--libkdegames/highscore/Makefile.am19
-rw-r--r--libkdegames/highscore/kconfigrawbackend.cpp62
-rw-r--r--libkdegames/highscore/kconfigrawbackend.h57
-rw-r--r--libkdegames/highscore/kexthighscore.cpp289
-rw-r--r--libkdegames/highscore/kexthighscore.h367
-rw-r--r--libkdegames/highscore/kexthighscore_gui.cpp552
-rw-r--r--libkdegames/highscore/kexthighscore_gui.h207
-rw-r--r--libkdegames/highscore/kexthighscore_internal.cpp868
-rw-r--r--libkdegames/highscore/kexthighscore_internal.h277
-rw-r--r--libkdegames/highscore/kexthighscore_item.cpp312
-rw-r--r--libkdegames/highscore/kexthighscore_item.h317
-rw-r--r--libkdegames/highscore/kexthighscore_tab.cpp281
-rw-r--r--libkdegames/highscore/kexthighscore_tab.h117
-rw-r--r--libkdegames/highscore/kfilelock.cpp88
-rw-r--r--libkdegames/highscore/kfilelock.h53
-rw-r--r--libkdegames/highscore/khighscore.cpp262
-rw-r--r--libkdegames/highscore/khighscore.h311
-rw-r--r--libkdegames/highscore/kscoredialog.cpp411
-rw-r--r--libkdegames/highscore/kscoredialog.h125
-rw-r--r--libkdegames/kcanvasrootpixmap.cpp39
-rw-r--r--libkdegames/kcanvasrootpixmap.h61
-rw-r--r--libkdegames/kcarddialog.cpp808
-rw-r--r--libkdegames/kcarddialog.h345
-rw-r--r--libkdegames/kcarddialog.pngbin0 -> 34451 bytes
-rw-r--r--libkdegames/kchat.cpp115
-rw-r--r--libkdegames/kchat.h147
-rw-r--r--libkdegames/kchatbase.cpp530
-rw-r--r--libkdegames/kchatbase.h510
-rw-r--r--libkdegames/kchatdialog.cpp253
-rw-r--r--libkdegames/kchatdialog.h119
-rw-r--r--libkdegames/kgame/COMPAT55
-rw-r--r--libkdegames/kgame/DESIGN407
-rw-r--r--libkdegames/kgame/Makefile.am29
-rw-r--r--libkdegames/kgame/README.LIB12
-rw-r--r--libkdegames/kgame/TODO41
-rw-r--r--libkdegames/kgame/dialogs/Makefile.am17
-rw-r--r--libkdegames/kgame/dialogs/kgameconnectdialog.cpp278
-rw-r--r--libkdegames/kgame/dialogs/kgameconnectdialog.h169
-rw-r--r--libkdegames/kgame/dialogs/kgamedebugdialog.cpp548
-rw-r--r--libkdegames/kgame/dialogs/kgamedebugdialog.h149
-rw-r--r--libkdegames/kgame/dialogs/kgamedialog.cpp347
-rw-r--r--libkdegames/kgame/dialogs/kgamedialog.h320
-rw-r--r--libkdegames/kgame/dialogs/kgamedialogconfig.cpp773
-rw-r--r--libkdegames/kgame/dialogs/kgamedialogconfig.h362
-rw-r--r--libkdegames/kgame/dialogs/kgameerrordialog.cpp129
-rw-r--r--libkdegames/kgame/dialogs/kgameerrordialog.h113
-rw-r--r--libkdegames/kgame/kgame.cpp1475
-rw-r--r--libkdegames/kgame/kgame.h932
-rw-r--r--libkdegames/kgame/kgamechat.cpp341
-rw-r--r--libkdegames/kgame/kgamechat.h223
-rw-r--r--libkdegames/kgame/kgameerror.cpp80
-rw-r--r--libkdegames/kgame/kgameerror.h59
-rw-r--r--libkdegames/kgame/kgameio.cpp539
-rw-r--r--libkdegames/kgame/kgameio.h566
-rw-r--r--libkdegames/kgame/kgamemessage.cpp156
-rw-r--r--libkdegames/kgame/kgamemessage.h173
-rw-r--r--libkdegames/kgame/kgamenetwork.cpp516
-rw-r--r--libkdegames/kgame/kgamenetwork.h431
-rw-r--r--libkdegames/kgame/kgameprocess.cpp158
-rw-r--r--libkdegames/kgame/kgameprocess.h242
-rw-r--r--libkdegames/kgame/kgameproperty.cpp211
-rw-r--r--libkdegames/kgame/kgameproperty.h848
-rw-r--r--libkdegames/kgame/kgamepropertyarray.h309
-rw-r--r--libkdegames/kgame/kgamepropertyhandler.cpp407
-rw-r--r--libkdegames/kgame/kgamepropertyhandler.h353
-rw-r--r--libkdegames/kgame/kgamepropertylist.h258
-rw-r--r--libkdegames/kgame/kgamesequence.cpp125
-rw-r--r--libkdegames/kgame/kgamesequence.h87
-rw-r--r--libkdegames/kgame/kgameversion.h54
-rw-r--r--libkdegames/kgame/kmessageclient.cpp373
-rw-r--r--libkdegames/kgame/kmessageclient.h422
-rw-r--r--libkdegames/kgame/kmessageio.cpp482
-rw-r--r--libkdegames/kgame/kmessageio.h416
-rw-r--r--libkdegames/kgame/kmessageserver.cpp515
-rw-r--r--libkdegames/kgame/kmessageserver.h492
-rw-r--r--libkdegames/kgame/kmessageserver.pngbin0 -> 7791 bytes
-rw-r--r--libkdegames/kgame/kplayer.cpp446
-rw-r--r--libkdegames/kgame/kplayer.h471
-rw-r--r--libkdegames/kgame/libkdegames.html187
-rw-r--r--libkdegames/kgame/messages.txt93
-rw-r--r--libkdegames/kgame/scenario0.pngbin0 -> 2413 bytes
-rw-r--r--libkdegames/kgame/scenario1.pngbin0 -> 8074 bytes
-rw-r--r--libkdegames/kgame/scenario2.pngbin0 -> 7398 bytes
-rw-r--r--libkdegames/kgamelcd.cpp250
-rw-r--r--libkdegames/kgamelcd.h249
-rw-r--r--libkdegames/kgamemisc.cpp62
-rw-r--r--libkdegames/kgamemisc.h45
-rw-r--r--libkdegames/kgameprogress.cpp345
-rw-r--r--libkdegames/kgameprogress.h255
-rw-r--r--libkdegames/kgrid2d.h520
-rw-r--r--libkdegames/kstdgameaction.cpp209
-rw-r--r--libkdegames/kstdgameaction.h261
-rw-r--r--libkdegames/pics/Makefile.am4
-rw-r--r--libkdegames/pics/cr16-action-endturn.pngbin0 -> 364 bytes
-rw-r--r--libkdegames/pics/cr16-action-highscore.pngbin0 -> 493 bytes
-rw-r--r--libkdegames/pics/cr16-action-roll.pngbin0 -> 988 bytes
-rw-r--r--libkdegames/pics/cr22-action-roll.pngbin0 -> 1284 bytes
-rw-r--r--libkdegames/pics/cr32-action-endturn.pngbin0 -> 751 bytes
-rw-r--r--libkdegames/pics/cr32-action-highscore.pngbin0 -> 2440 bytes
-rw-r--r--libkdegames/pics/cr32-action-roll.pngbin0 -> 2235 bytes
-rw-r--r--libkdegames/pics/star.pngbin0 -> 1103 bytes
763 files changed, 28717 insertions, 0 deletions
diff --git a/libkdegames/Makefile.am b/libkdegames/Makefile.am
new file mode 100644
index 00000000..89477c44
--- /dev/null
+++ b/libkdegames/Makefile.am
@@ -0,0 +1,23 @@
+
+lib_LTLIBRARIES = libkdegames.la
+libkdegames_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2
+libkdegames_la_LIBADD = highscore/libkhighscore.la kgame/libkgame.la kgame/dialogs/libkgamedialogs.la \
+ $(LIB_KSYCOCA) $(LIB_KDNSSD)
+
+libkdegames_la_SOURCES = kcarddialog.cpp kstdgameaction.cpp \
+ kgamemisc.cpp kchatbase.cpp kchat.cpp \
+ kchatdialog.cpp kgameprogress.cpp \
+ kcanvasrootpixmap.cpp kgamelcd.cpp
+include_HEADERS = kgamemisc.h kcarddialog.h kstdgameaction.h \
+ kchatbase.h kchat.h kchatdialog.h \
+ kgameprogress.h kcanvasrootpixmap.h kgamelcd.h kgrid2d.h
+
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+
+SUBDIRS = carddecks highscore kgame pics
+
+messages:
+ $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot
+
+include ../admin/Doxyfile.am
diff --git a/libkdegames/README b/libkdegames/README
new file mode 100644
index 00000000..fae0e7ee
--- /dev/null
+++ b/libkdegames/README
@@ -0,0 +1,25 @@
+This directory contains the library for the kdegames packege.
+It is a collection of functions used by some games or which
+are useful for other games.
+
+Packagers note: it is recommended to put the directory "carddecks" into a separate
+package, as not all games using libkdegames use the carddecks as well.
+
+Contents:
+ kcarddialog: Access to carddeck selection and administration
+ for cardgames
+ kstdgameaction: just like kstdaction this provides some default action for
+ kde games. games often use different entries than other apps
+ (like "game" instead of "file"), so use this if possible
+ kgamemisc: some static method i didn't know a good class name for. it
+ currently features "randomName()" which will just give you a random name from
+ a list (translators note: happy translating ;) i copied kde-common/accounts
+ for this so there are nearly 300 entries...)
+ kgame: this is a complete network/game handling library. it constists of a lot
+ of classes which are explained in the kgame docu (as soon as it is
+ written)
+if you use libkdegames in your game please also add
+KGlobal::locale()->insertCatalogue("libkdegames");
+to main()
+This will add libkdegames.pot to your game and therefore all libkdegames
+strings get translated.
diff --git a/libkdegames/TODO b/libkdegames/TODO
new file mode 100644
index 00000000..136c4fdd
--- /dev/null
+++ b/libkdegames/TODO
@@ -0,0 +1,10 @@
+- 17.04.2001: change version number of the kdenonbeta one
+- 10.07.2001: scaling has been added to KCard and KCardDialog. Find out if there
+ is a memory leak!!
+- 10.07.2001: better layout for the resize box
+- 10.07.2001: global decks/carddirs need extensive testing
+
+-These pertain to the highscore widget
+The Ok button when adding a name should be enabled when the lineedit is filled with a QString of length() > 0
+
+The lineedit by default should come up with the user name that is loged in.
diff --git a/libkdegames/carddecks/Makefile.am b/libkdegames/carddecks/Makefile.am
new file mode 100644
index 00000000..8effea93
--- /dev/null
+++ b/libkdegames/carddecks/Makefile.am
@@ -0,0 +1,24 @@
+DECKDIRS = cards-aisleriot cards-dondorf-whist-b cards-gdkcard-bonded cards-hard-a-port \
+ cards-penguins cards-spaced cards-xskat-french cards-default decks cards-konqi-modern cards-warwick cards-xskat-german
+
+deckdir = $(kde_datadir)/carddecks
+
+uninstall-local:
+ rm -rf $(DESTDIR)$(deckdir)
+
+install-data-local:
+ @$(mkinstalldirs) $(DESTDIR)$(deckdir)
+ @for i in $(DECKDIRS); do \
+ if test -d $(DESTDIR)$(deckdir)/$$i; then \
+ echo "refreshing and removing $$i" ;\
+ rm -rf $(DESTDIR)$(deckdir)/$$i;\
+ fi ;\
+ echo "installing $$i" ;\
+ mkdir $(DESTDIR)$(deckdir)/$$i ;\
+ files=`cd $(srcdir)/$$i && ls -1d *` ;\
+ for f in $$files; do \
+ if test -f $(srcdir)/$$i/$$f; then \
+ $(INSTALL_DATA) $(srcdir)/$$i/$$f $(DESTDIR)$(deckdir)/$$i/$$f ;\
+ fi \
+ done \
+ done
diff --git a/libkdegames/carddecks/README b/libkdegames/carddecks/README
new file mode 100644
index 00000000..9beb8a87
--- /dev/null
+++ b/libkdegames/carddecks/README
@@ -0,0 +1,12 @@
+This directory contains all carddecks for KDE cardgames.
+
+The backsides of the cards are stored as PNG images in
+the directory decks as deck0.png, deck1.png, ...
+
+The cardsets with 52 cards are stored in the directories
+cards1,cards2,cards3,... where each directory contains 52
+PNG images of the cards labels 1.png,2.png,...52.png
+
+Access to these cards works by using the kcardialog library
+in libkdegames. See for documentation there.
+
diff --git a/libkdegames/carddecks/cards-aisleriot/1.png b/libkdegames/carddecks/cards-aisleriot/1.png
new file mode 100644
index 00000000..d08a67d8
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/10.png b/libkdegames/carddecks/cards-aisleriot/10.png
new file mode 100644
index 00000000..f9f00749
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/11.png b/libkdegames/carddecks/cards-aisleriot/11.png
new file mode 100644
index 00000000..036807ec
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/12.png b/libkdegames/carddecks/cards-aisleriot/12.png
new file mode 100644
index 00000000..2945ea93
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/13.png b/libkdegames/carddecks/cards-aisleriot/13.png
new file mode 100644
index 00000000..86ac688c
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/14.png b/libkdegames/carddecks/cards-aisleriot/14.png
new file mode 100644
index 00000000..e5b64e0f
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/15.png b/libkdegames/carddecks/cards-aisleriot/15.png
new file mode 100644
index 00000000..3522414c
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/16.png b/libkdegames/carddecks/cards-aisleriot/16.png
new file mode 100644
index 00000000..098a50f4
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/17.png b/libkdegames/carddecks/cards-aisleriot/17.png
new file mode 100644
index 00000000..a4abb694
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/18.png b/libkdegames/carddecks/cards-aisleriot/18.png
new file mode 100644
index 00000000..b231456b
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/19.png b/libkdegames/carddecks/cards-aisleriot/19.png
new file mode 100644
index 00000000..d77ff9c8
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/2.png b/libkdegames/carddecks/cards-aisleriot/2.png
new file mode 100644
index 00000000..5d610da7
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/20.png b/libkdegames/carddecks/cards-aisleriot/20.png
new file mode 100644
index 00000000..bf9d586f
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/21.png b/libkdegames/carddecks/cards-aisleriot/21.png
new file mode 100644
index 00000000..7ae9b457
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/22.png b/libkdegames/carddecks/cards-aisleriot/22.png
new file mode 100644
index 00000000..86dee123
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/23.png b/libkdegames/carddecks/cards-aisleriot/23.png
new file mode 100644
index 00000000..0f6aba24
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/24.png b/libkdegames/carddecks/cards-aisleriot/24.png
new file mode 100644
index 00000000..5cf8d35c
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/25.png b/libkdegames/carddecks/cards-aisleriot/25.png
new file mode 100644
index 00000000..a46de767
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/26.png b/libkdegames/carddecks/cards-aisleriot/26.png
new file mode 100644
index 00000000..a21f883d
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/27.png b/libkdegames/carddecks/cards-aisleriot/27.png
new file mode 100644
index 00000000..724146ca
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/28.png b/libkdegames/carddecks/cards-aisleriot/28.png
new file mode 100644
index 00000000..f446432e
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/29.png b/libkdegames/carddecks/cards-aisleriot/29.png
new file mode 100644
index 00000000..81060669
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/3.png b/libkdegames/carddecks/cards-aisleriot/3.png
new file mode 100644
index 00000000..9bc9a017
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/30.png b/libkdegames/carddecks/cards-aisleriot/30.png
new file mode 100644
index 00000000..d506216a
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/31.png b/libkdegames/carddecks/cards-aisleriot/31.png
new file mode 100644
index 00000000..e6643631
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/32.png b/libkdegames/carddecks/cards-aisleriot/32.png
new file mode 100644
index 00000000..225d9c3e
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/33.png b/libkdegames/carddecks/cards-aisleriot/33.png
new file mode 100644
index 00000000..bb5bd4ec
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/34.png b/libkdegames/carddecks/cards-aisleriot/34.png
new file mode 100644
index 00000000..a5a0f38d
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/35.png b/libkdegames/carddecks/cards-aisleriot/35.png
new file mode 100644
index 00000000..72cca325
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/36.png b/libkdegames/carddecks/cards-aisleriot/36.png
new file mode 100644
index 00000000..8c805d93
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/37.png b/libkdegames/carddecks/cards-aisleriot/37.png
new file mode 100644
index 00000000..36746eee
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/38.png b/libkdegames/carddecks/cards-aisleriot/38.png
new file mode 100644
index 00000000..58de88ae
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/39.png b/libkdegames/carddecks/cards-aisleriot/39.png
new file mode 100644
index 00000000..879c0f78
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/4.png b/libkdegames/carddecks/cards-aisleriot/4.png
new file mode 100644
index 00000000..6b723627
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/40.png b/libkdegames/carddecks/cards-aisleriot/40.png
new file mode 100644
index 00000000..e20553ee
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/41.png b/libkdegames/carddecks/cards-aisleriot/41.png
new file mode 100644
index 00000000..9a023831
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/42.png b/libkdegames/carddecks/cards-aisleriot/42.png
new file mode 100644
index 00000000..363e44b2
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/43.png b/libkdegames/carddecks/cards-aisleriot/43.png
new file mode 100644
index 00000000..e2e7b08c
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/44.png b/libkdegames/carddecks/cards-aisleriot/44.png
new file mode 100644
index 00000000..aeb617f6
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/45.png b/libkdegames/carddecks/cards-aisleriot/45.png
new file mode 100644
index 00000000..fee4dba0
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/46.png b/libkdegames/carddecks/cards-aisleriot/46.png
new file mode 100644
index 00000000..f5948439
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/47.png b/libkdegames/carddecks/cards-aisleriot/47.png
new file mode 100644
index 00000000..0079b371
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/48.png b/libkdegames/carddecks/cards-aisleriot/48.png
new file mode 100644
index 00000000..62a487da
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/49.png b/libkdegames/carddecks/cards-aisleriot/49.png
new file mode 100644
index 00000000..87ede197
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/5.png b/libkdegames/carddecks/cards-aisleriot/5.png
new file mode 100644
index 00000000..8c5df649
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/50.png b/libkdegames/carddecks/cards-aisleriot/50.png
new file mode 100644
index 00000000..a8a01853
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/51.png b/libkdegames/carddecks/cards-aisleriot/51.png
new file mode 100644
index 00000000..9f2d83b9
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/52.png b/libkdegames/carddecks/cards-aisleriot/52.png
new file mode 100644
index 00000000..c5c754b3
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/6.png b/libkdegames/carddecks/cards-aisleriot/6.png
new file mode 100644
index 00000000..d399ebbb
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/7.png b/libkdegames/carddecks/cards-aisleriot/7.png
new file mode 100644
index 00000000..754240cd
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/8.png b/libkdegames/carddecks/cards-aisleriot/8.png
new file mode 100644
index 00000000..805854c0
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/9.png b/libkdegames/carddecks/cards-aisleriot/9.png
new file mode 100644
index 00000000..9c032e48
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-aisleriot/COPYRIGHT b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT
new file mode 100644
index 00000000..bfbeeca7
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT
@@ -0,0 +1,10 @@
+This PySol cardset was adapted from gnome-games 0.27 (aisleriot).
+http://www.gnome.org
+
+Copyright (C) 1998 Jonathan Blandford <jrb@mit.edu>
+Copyright (C) 1998 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>
+
+This cardset is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-aisleriot/index.desktop b/libkdegames/carddecks/cards-aisleriot/index.desktop
new file mode 100644
index 00000000..e70c235b
--- /dev/null
+++ b/libkdegames/carddecks/cards-aisleriot/index.desktop
@@ -0,0 +1,20 @@
+[KDE Backdeck]
+Name=AisleRiot
+Name[af]=Aisleriot
+Name[be]=Эслрыёт (пасьянсы)
+Name[bg]=Айсълриот
+Name[bn]=এইসলরায়োট
+Name[cs]=Pozdvižení v uličce
+Name[eo]=Simpla
+Name[hi]=एस्लेरियॉट
+Name[hr]=Pobuna u prolazu
+Name[ne]=आइस्ले रिओट
+Name[pa]=ਇਲਸੀਰਓਟ
+Name[ru]=ЭшлРиот (коллекция пасьянсов)
+Name[sv]=Kyrkouppror
+Name[ta]= அய்ஸில்ரியாட்
+Name[uk]=Заколот на човні
+Name[vi]= AisleRiot
+Name[zu]=I-AisleRiot
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-default/1.png b/libkdegames/carddecks/cards-default/1.png
new file mode 100644
index 00000000..8f5de97f
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/10.png b/libkdegames/carddecks/cards-default/10.png
new file mode 100644
index 00000000..6c7b87ea
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/11.png b/libkdegames/carddecks/cards-default/11.png
new file mode 100644
index 00000000..a5938708
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/12.png b/libkdegames/carddecks/cards-default/12.png
new file mode 100644
index 00000000..4d43a730
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/13.png b/libkdegames/carddecks/cards-default/13.png
new file mode 100644
index 00000000..c7d51207
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/14.png b/libkdegames/carddecks/cards-default/14.png
new file mode 100644
index 00000000..2efb9b18
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/15.png b/libkdegames/carddecks/cards-default/15.png
new file mode 100644
index 00000000..8208b271
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/16.png b/libkdegames/carddecks/cards-default/16.png
new file mode 100644
index 00000000..73c5fce5
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/17.png b/libkdegames/carddecks/cards-default/17.png
new file mode 100644
index 00000000..611b7aff
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/18.png b/libkdegames/carddecks/cards-default/18.png
new file mode 100644
index 00000000..8b7d1a1e
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/19.png b/libkdegames/carddecks/cards-default/19.png
new file mode 100644
index 00000000..0a9dac18
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/2.png b/libkdegames/carddecks/cards-default/2.png
new file mode 100644
index 00000000..b0c67169
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/20.png b/libkdegames/carddecks/cards-default/20.png
new file mode 100644
index 00000000..0657a091
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/21.png b/libkdegames/carddecks/cards-default/21.png
new file mode 100644
index 00000000..fe376ba1
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/22.png b/libkdegames/carddecks/cards-default/22.png
new file mode 100644
index 00000000..2a0e5d30
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/23.png b/libkdegames/carddecks/cards-default/23.png
new file mode 100644
index 00000000..52403931
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/24.png b/libkdegames/carddecks/cards-default/24.png
new file mode 100644
index 00000000..5af94ec7
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/25.png b/libkdegames/carddecks/cards-default/25.png
new file mode 100644
index 00000000..cc437aea
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/26.png b/libkdegames/carddecks/cards-default/26.png
new file mode 100644
index 00000000..d034f06f
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/27.png b/libkdegames/carddecks/cards-default/27.png
new file mode 100644
index 00000000..a23fe6ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/28.png b/libkdegames/carddecks/cards-default/28.png
new file mode 100644
index 00000000..dcccaac2
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/29.png b/libkdegames/carddecks/cards-default/29.png
new file mode 100644
index 00000000..e19070bd
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/3.png b/libkdegames/carddecks/cards-default/3.png
new file mode 100644
index 00000000..1854faba
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/30.png b/libkdegames/carddecks/cards-default/30.png
new file mode 100644
index 00000000..d9b285c0
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/31.png b/libkdegames/carddecks/cards-default/31.png
new file mode 100644
index 00000000..b77a5d8d
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/32.png b/libkdegames/carddecks/cards-default/32.png
new file mode 100644
index 00000000..0e0649bf
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/33.png b/libkdegames/carddecks/cards-default/33.png
new file mode 100644
index 00000000..d1344b0f
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/34.png b/libkdegames/carddecks/cards-default/34.png
new file mode 100644
index 00000000..9ff5f5aa
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/35.png b/libkdegames/carddecks/cards-default/35.png
new file mode 100644
index 00000000..b247d95e
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/36.png b/libkdegames/carddecks/cards-default/36.png
new file mode 100644
index 00000000..f5284e87
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/37.png b/libkdegames/carddecks/cards-default/37.png
new file mode 100644
index 00000000..e4a2fa70
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/38.png b/libkdegames/carddecks/cards-default/38.png
new file mode 100644
index 00000000..859e4a52
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/39.png b/libkdegames/carddecks/cards-default/39.png
new file mode 100644
index 00000000..b4392112
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/4.png b/libkdegames/carddecks/cards-default/4.png
new file mode 100644
index 00000000..136c3100
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/40.png b/libkdegames/carddecks/cards-default/40.png
new file mode 100644
index 00000000..2a30304a
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/41.png b/libkdegames/carddecks/cards-default/41.png
new file mode 100644
index 00000000..2c619dc3
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/42.png b/libkdegames/carddecks/cards-default/42.png
new file mode 100644
index 00000000..5200e878
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/43.png b/libkdegames/carddecks/cards-default/43.png
new file mode 100644
index 00000000..ebd6db3f
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/44.png b/libkdegames/carddecks/cards-default/44.png
new file mode 100644
index 00000000..8cdcd68f
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/45.png b/libkdegames/carddecks/cards-default/45.png
new file mode 100644
index 00000000..9297d188
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/46.png b/libkdegames/carddecks/cards-default/46.png
new file mode 100644
index 00000000..1b47218e
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/47.png b/libkdegames/carddecks/cards-default/47.png
new file mode 100644
index 00000000..8fd3853d
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/48.png b/libkdegames/carddecks/cards-default/48.png
new file mode 100644
index 00000000..01eea71b
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/49.png b/libkdegames/carddecks/cards-default/49.png
new file mode 100644
index 00000000..d6860e04
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/5.png b/libkdegames/carddecks/cards-default/5.png
new file mode 100644
index 00000000..64029ed0
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/50.png b/libkdegames/carddecks/cards-default/50.png
new file mode 100644
index 00000000..79c1ed05
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/51.png b/libkdegames/carddecks/cards-default/51.png
new file mode 100644
index 00000000..f396b946
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/52.png b/libkdegames/carddecks/cards-default/52.png
new file mode 100644
index 00000000..dcdc90e5
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/6.png b/libkdegames/carddecks/cards-default/6.png
new file mode 100644
index 00000000..1749b472
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/7.png b/libkdegames/carddecks/cards-default/7.png
new file mode 100644
index 00000000..2d33965c
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/8.png b/libkdegames/carddecks/cards-default/8.png
new file mode 100644
index 00000000..165bd76b
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/9.png b/libkdegames/carddecks/cards-default/9.png
new file mode 100644
index 00000000..9860c535
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-default/index.desktop b/libkdegames/carddecks/cards-default/index.desktop
new file mode 100644
index 00000000..63894a1b
--- /dev/null
+++ b/libkdegames/carddecks/cards-default/index.desktop
@@ -0,0 +1,115 @@
+[KDE Backdeck]
+Name=Standard
+Name[af]=Standaard
+Name[az]=Standart
+Name[be]=Звычайная
+Name[bg]=Стандарт
+Name[bn]=প্রমিত
+Name[br]=Reoliek
+Name[ca]=Estàndard
+Name[cs]=Standardní
+Name[cy]=Safonol
+Name[eo]=Normala
+Name[es]=Estándar
+Name[eu]=Estandarra
+Name[fa]=استاندارد
+Name[fi]=Normaali
+Name[ga]=Caighdeánach
+Name[gl]=Estándar
+Name[hi]=मानक
+Name[is]=Staðlað
+Name[ja]=標準
+Name[km]=ខ្នាត​គំរូ
+Name[ko]=표준
+Name[lt]=Standartas
+Name[lv]=Standarta
+Name[mk]=Стандард
+Name[ne]=मानक
+Name[nl]=Standaard
+Name[nso]=Lekanetse
+Name[pa]=ਮਿਆਰ
+Name[pt]=Normal
+Name[pt_BR]=Padrão
+Name[ru]=Стандарт
+Name[se]=Standárda
+Name[sk]=Štandard
+Name[sr]=Стандардни
+Name[sr@Latn]=Standardni
+Name[ta]= தரம்
+Name[tg]=Низоммеъёр
+Name[th]=มาตรฐาน
+Name[tr]=Standart
+Name[uk]=Типові
+Name[uz]=Andoza
+Name[uz@cyrillic]=Андоза
+Name[ven]=Murole
+Name[vi]=Tiêu chuẩn
+Name[wa]=Sitandård
+Name[xh]=Umgangatho
+Name[zh_CN]=标准
+Name[zh_TW]=標準
+Name[zu]=Okulingeneyo
+Preview=11.png
+PySol=false
+Comment=Standard KDE card set\nGPL license
+Comment[af]=Standaard Kde kaart stel\nGPL lisensie
+Comment[az]=Standart KDE kart dəstəsi\nGPL license
+Comment[be]=Звычайная калода картаў KDE\nGPL license
+Comment[bg]=Стандартна колода карти KDE\nОПЛ лиценз(GPL)
+Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট\nজি.পি.এল. লাইসেন্স
+Comment[bs]=Standardni KDE špil karata\nGPL license
+Comment[ca]=Joc de cartes estàndard per al KDE sota\nLlicència GPL
+Comment[cs]=Standardní sada karet KDE\nGPL licence
+Comment[cy]=Trwydded GPL safonol\nar gyfer set cerdiau KDE
+Comment[da]=Standard KDE-kortspil\nGPL-licens
+Comment[de]=Standardmäßige KDE-Karten\nGPL-Lizenz
+Comment[el]=Προκαθορισμένο σετ καρτών του KDE\nΆδεια GPL
+Comment[en_GB]=Standard KDE card set\nGPL licence
+Comment[eo]=Normala KDEa kartaro\nGPL licenco
+Comment[es]=Juego de cartas estándar de KDE\nLicencia GPL
+Comment[et]=Standardne KDE kaardikomplekt\nGPL litsents
+Comment[eu]=KDE-ren karta-sorta estandarra\nGPL lizentzia
+Comment[fa]=مجموعه استاندارد کارت KDE\مجوز nGPL
+Comment[fi]=Normaali KDE:n korttipakka\nGPL lisenssi
+Comment[fr]=Jeu de cartes standard de KDE\nLicence GPL
+Comment[gl]=Baralla de cartas estándar de KDE\nGPL license
+Comment[he]=ערכת הקלפים הסטנדרטית של KDE\nרישיון GPL
+Comment[hi]=मानक केडीई ताश सेट\nजीपीएल अनुज्ञापत्र
+Comment[hr]=Standardni KDE komplet karata\nGPL licenca
+Comment[hu]=Standard KDE kártyacsomag\n(GPL licencű)
+Comment[is]=Venjulegi KDE spilastokkurinn\nGPL leyfi
+Comment[it]=Insieme di carte standard di KDE\nLicenza GPL
+Comment[ja]=標準 KDE カードセット\nGPL ライセンス
+Comment[km]=បណ្ដុំ​បៀ KDE ខ្នាត​គំរូ\nអជ្ញាបណ្ណ GPL
+Comment[ko]=표준 KDE 카드 모음\nGPL 라이선스
+Comment[lt]=Standartinis KDE kortų rinkinys\nGPL licencija
+Comment[lv]=Standarta KDE kāršu komplekts\n GPL licenze
+Comment[mk]=Стандарден комплет на карти во KDE\nGPL-лиценца
+Comment[mt]=Mazz karti standard KDE\nliċenzja GPL
+Comment[nb]=Standard KDE sett med kort\nGPL-lisens
+Comment[nds]=KDE-Standardkoorten\nGPL-Lizenz
+Comment[ne]=मानक केडीई कार्ड सेट\nGPL इजाजतपत्र
+Comment[nl]=Standaard KDE-kaartrug\nGPL-licentie
+Comment[nn]=Standard KDE-kortstokk\nGPL-lisens
+Comment[pl]=Standardowy zestaw kart KDE\nLicencja GPL
+Comment[pt]=Baralho de cartas normal do KDE\nLicença GPL
+Comment[pt_BR]=Jogo de cartas padrão do KDE\nLicença GPL
+Comment[ro]=Set de cărţi KDE standard\nLicenţă GPL
+Comment[ru]=Стандартная колода карт KDE\nЛицензия: GPL
+Comment[sk]=Štandardný balíček kariet KDE\nlicencia GPL
+Comment[sl]=Standardni nabor kart v KDE\nLicenca GPL
+Comment[sr]=Стандардни KDE-ов шпил карата\nGPL лиценца
+Comment[sr@Latn]=Standardni KDE-ov špil karata\nGPL licenca
+Comment[sv]=Förvald KDE-kortlek\nGPL-licens
+Comment[ta]=நிலையான கேடிஇ சீட்டு அமைப்பு \nGPL இயக்க ஆணை
+Comment[tg]=Маҷмӯи кортҳои низомии KDE\nлитсензияи GPL
+Comment[th]=ชุดของเกมไพ่ของ KDE\nลิขสิทธิ์แบบ GPL
+Comment[tr]=Standart KDE kart seti\nGPL lisansı
+Comment[uk]=Типовий набір карт KDE\nліцензія GPL
+Comment[ven]=U vhekanya ha magarata a KDE ya Murole\tendelo ya nGPL
+Comment[vi]=Thẻ KDE tiêu chuẩn đặt \nGPL license
+Comment[wa]=Cwårdjeus standård di KDE\ndizo licince GPL
+Comment[xh]=Umgangatho wekhadi le KDE iqela lekhadi \nGPL imvumelwano
+Comment[zh_CN]=标准 KDE 牌面\nGPL 授权
+Comment[zh_TW]=標準 KDE 牌局\nGPL 授權
+Comment[zu]=Iqoqo lamakhadi elilingeneyo le-KDE\n ilayisensi ye-GPL
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/1.png b/libkdegames/carddecks/cards-dondorf-whist-b/1.png
new file mode 100644
index 00000000..89c86909
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/10.png b/libkdegames/carddecks/cards-dondorf-whist-b/10.png
new file mode 100644
index 00000000..5113cad8
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/11.png b/libkdegames/carddecks/cards-dondorf-whist-b/11.png
new file mode 100644
index 00000000..11fc7e05
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/12.png b/libkdegames/carddecks/cards-dondorf-whist-b/12.png
new file mode 100644
index 00000000..a2307ddc
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/13.png b/libkdegames/carddecks/cards-dondorf-whist-b/13.png
new file mode 100644
index 00000000..0b9e20cd
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/14.png b/libkdegames/carddecks/cards-dondorf-whist-b/14.png
new file mode 100644
index 00000000..1c49f750
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/15.png b/libkdegames/carddecks/cards-dondorf-whist-b/15.png
new file mode 100644
index 00000000..ab295e25
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/16.png b/libkdegames/carddecks/cards-dondorf-whist-b/16.png
new file mode 100644
index 00000000..d8bd8402
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/17.png b/libkdegames/carddecks/cards-dondorf-whist-b/17.png
new file mode 100644
index 00000000..d3a3f6ed
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/18.png b/libkdegames/carddecks/cards-dondorf-whist-b/18.png
new file mode 100644
index 00000000..12831991
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/19.png b/libkdegames/carddecks/cards-dondorf-whist-b/19.png
new file mode 100644
index 00000000..e7199dce
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/2.png b/libkdegames/carddecks/cards-dondorf-whist-b/2.png
new file mode 100644
index 00000000..93ec9944
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/20.png b/libkdegames/carddecks/cards-dondorf-whist-b/20.png
new file mode 100644
index 00000000..90b043f0
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/21.png b/libkdegames/carddecks/cards-dondorf-whist-b/21.png
new file mode 100644
index 00000000..905ccd6a
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/22.png b/libkdegames/carddecks/cards-dondorf-whist-b/22.png
new file mode 100644
index 00000000..2eee5bbc
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/23.png b/libkdegames/carddecks/cards-dondorf-whist-b/23.png
new file mode 100644
index 00000000..b717110e
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/24.png b/libkdegames/carddecks/cards-dondorf-whist-b/24.png
new file mode 100644
index 00000000..4c05cd1d
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/25.png b/libkdegames/carddecks/cards-dondorf-whist-b/25.png
new file mode 100644
index 00000000..c6701d0d
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/26.png b/libkdegames/carddecks/cards-dondorf-whist-b/26.png
new file mode 100644
index 00000000..a737f469
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/27.png b/libkdegames/carddecks/cards-dondorf-whist-b/27.png
new file mode 100644
index 00000000..c904309b
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/28.png b/libkdegames/carddecks/cards-dondorf-whist-b/28.png
new file mode 100644
index 00000000..8da263d7
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/29.png b/libkdegames/carddecks/cards-dondorf-whist-b/29.png
new file mode 100644
index 00000000..8ff7822d
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/3.png b/libkdegames/carddecks/cards-dondorf-whist-b/3.png
new file mode 100644
index 00000000..7f5644f8
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/30.png b/libkdegames/carddecks/cards-dondorf-whist-b/30.png
new file mode 100644
index 00000000..c93c64dc
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/31.png b/libkdegames/carddecks/cards-dondorf-whist-b/31.png
new file mode 100644
index 00000000..912cbf79
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/32.png b/libkdegames/carddecks/cards-dondorf-whist-b/32.png
new file mode 100644
index 00000000..9b72443c
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/33.png b/libkdegames/carddecks/cards-dondorf-whist-b/33.png
new file mode 100644
index 00000000..20f863ac
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/34.png b/libkdegames/carddecks/cards-dondorf-whist-b/34.png
new file mode 100644
index 00000000..7763d965
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/35.png b/libkdegames/carddecks/cards-dondorf-whist-b/35.png
new file mode 100644
index 00000000..29eaef82
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/36.png b/libkdegames/carddecks/cards-dondorf-whist-b/36.png
new file mode 100644
index 00000000..fd72b718
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/37.png b/libkdegames/carddecks/cards-dondorf-whist-b/37.png
new file mode 100644
index 00000000..15a2b979
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/38.png b/libkdegames/carddecks/cards-dondorf-whist-b/38.png
new file mode 100644
index 00000000..91551173
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/39.png b/libkdegames/carddecks/cards-dondorf-whist-b/39.png
new file mode 100644
index 00000000..dcb66d00
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/4.png b/libkdegames/carddecks/cards-dondorf-whist-b/4.png
new file mode 100644
index 00000000..1033ffd0
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/40.png b/libkdegames/carddecks/cards-dondorf-whist-b/40.png
new file mode 100644
index 00000000..a1b9b2e9
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/41.png b/libkdegames/carddecks/cards-dondorf-whist-b/41.png
new file mode 100644
index 00000000..8d7ca55b
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/42.png b/libkdegames/carddecks/cards-dondorf-whist-b/42.png
new file mode 100644
index 00000000..4570430b
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/43.png b/libkdegames/carddecks/cards-dondorf-whist-b/43.png
new file mode 100644
index 00000000..c889b795
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/44.png b/libkdegames/carddecks/cards-dondorf-whist-b/44.png
new file mode 100644
index 00000000..b73bd4d1
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/45.png b/libkdegames/carddecks/cards-dondorf-whist-b/45.png
new file mode 100644
index 00000000..de6730cd
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/46.png b/libkdegames/carddecks/cards-dondorf-whist-b/46.png
new file mode 100644
index 00000000..f20edcc9
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/47.png b/libkdegames/carddecks/cards-dondorf-whist-b/47.png
new file mode 100644
index 00000000..50ec6b95
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/48.png b/libkdegames/carddecks/cards-dondorf-whist-b/48.png
new file mode 100644
index 00000000..c0bfdfbd
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/49.png b/libkdegames/carddecks/cards-dondorf-whist-b/49.png
new file mode 100644
index 00000000..c8803ffa
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/5.png b/libkdegames/carddecks/cards-dondorf-whist-b/5.png
new file mode 100644
index 00000000..0fb9ffcf
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/50.png b/libkdegames/carddecks/cards-dondorf-whist-b/50.png
new file mode 100644
index 00000000..6d8c891e
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/51.png b/libkdegames/carddecks/cards-dondorf-whist-b/51.png
new file mode 100644
index 00000000..fc2c76d0
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/52.png b/libkdegames/carddecks/cards-dondorf-whist-b/52.png
new file mode 100644
index 00000000..6aef81aa
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/6.png b/libkdegames/carddecks/cards-dondorf-whist-b/6.png
new file mode 100644
index 00000000..f70072cf
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/7.png b/libkdegames/carddecks/cards-dondorf-whist-b/7.png
new file mode 100644
index 00000000..1dd352dc
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/8.png b/libkdegames/carddecks/cards-dondorf-whist-b/8.png
new file mode 100644
index 00000000..087906ca
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/9.png b/libkdegames/carddecks/cards-dondorf-whist-b/9.png
new file mode 100644
index 00000000..9bb290ee
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT
new file mode 100644
index 00000000..917bc1e3
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT
@@ -0,0 +1,12 @@
+This PySol cardset was created by T. Kirk.
+
+Copyright (C) 2000 T. Kirk <grania@mailcity.com>
+
+This card set uses the court and Ace images from a deck published
+by Dondorf and Co. of Frankfurt in 1896. The cards are double ended
+and have different images on each end. Two card sets were produced.
+
+This card set is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop
new file mode 100644
index 00000000..6626e825
--- /dev/null
+++ b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop
@@ -0,0 +1,17 @@
+[KDE Backdeck]
+Name=Dondorf
+Name[be]=Дандорф
+Name[bg]=Дондроф
+Name[bn]=ডনডর্ফ
+Name[eo]=Bela
+Name[hi]=डॉनड्रॉफ
+Name[lv]=Dondorfa
+Name[mk]=Дондорф
+Name[ne]=डनडोर्फ
+Name[ru]=Дондорф
+Name[sr]=Дондорф
+Name[ta]=டேன்டார்ஃப்
+Name[tg]=Дондорф
+Name[zu]=I-Dondorf
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/1.png b/libkdegames/carddecks/cards-gdkcard-bonded/1.png
new file mode 100644
index 00000000..17c1ac79
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/10.png b/libkdegames/carddecks/cards-gdkcard-bonded/10.png
new file mode 100644
index 00000000..f9fe505b
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/11.png b/libkdegames/carddecks/cards-gdkcard-bonded/11.png
new file mode 100644
index 00000000..9fa6e5ec
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/12.png b/libkdegames/carddecks/cards-gdkcard-bonded/12.png
new file mode 100644
index 00000000..09089e70
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/13.png b/libkdegames/carddecks/cards-gdkcard-bonded/13.png
new file mode 100644
index 00000000..cb6b0e6f
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/14.png b/libkdegames/carddecks/cards-gdkcard-bonded/14.png
new file mode 100644
index 00000000..027217be
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/15.png b/libkdegames/carddecks/cards-gdkcard-bonded/15.png
new file mode 100644
index 00000000..b9054f4f
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/16.png b/libkdegames/carddecks/cards-gdkcard-bonded/16.png
new file mode 100644
index 00000000..7b14d54a
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/17.png b/libkdegames/carddecks/cards-gdkcard-bonded/17.png
new file mode 100644
index 00000000..e4d9b798
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/18.png b/libkdegames/carddecks/cards-gdkcard-bonded/18.png
new file mode 100644
index 00000000..c0fe1954
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/19.png b/libkdegames/carddecks/cards-gdkcard-bonded/19.png
new file mode 100644
index 00000000..7f24d523
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/2.png b/libkdegames/carddecks/cards-gdkcard-bonded/2.png
new file mode 100644
index 00000000..7d8cbbcb
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/20.png b/libkdegames/carddecks/cards-gdkcard-bonded/20.png
new file mode 100644
index 00000000..1a6e01c9
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/21.png b/libkdegames/carddecks/cards-gdkcard-bonded/21.png
new file mode 100644
index 00000000..c217b982
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/22.png b/libkdegames/carddecks/cards-gdkcard-bonded/22.png
new file mode 100644
index 00000000..82ce9196
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/23.png b/libkdegames/carddecks/cards-gdkcard-bonded/23.png
new file mode 100644
index 00000000..03850bef
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/24.png b/libkdegames/carddecks/cards-gdkcard-bonded/24.png
new file mode 100644
index 00000000..990289f6
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/25.png b/libkdegames/carddecks/cards-gdkcard-bonded/25.png
new file mode 100644
index 00000000..04ea602f
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/26.png b/libkdegames/carddecks/cards-gdkcard-bonded/26.png
new file mode 100644
index 00000000..705a22a2
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/27.png b/libkdegames/carddecks/cards-gdkcard-bonded/27.png
new file mode 100644
index 00000000..cf5ea112
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/28.png b/libkdegames/carddecks/cards-gdkcard-bonded/28.png
new file mode 100644
index 00000000..d613413c
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/29.png b/libkdegames/carddecks/cards-gdkcard-bonded/29.png
new file mode 100644
index 00000000..8838c750
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/3.png b/libkdegames/carddecks/cards-gdkcard-bonded/3.png
new file mode 100644
index 00000000..563a6385
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/30.png b/libkdegames/carddecks/cards-gdkcard-bonded/30.png
new file mode 100644
index 00000000..4fb2901b
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/31.png b/libkdegames/carddecks/cards-gdkcard-bonded/31.png
new file mode 100644
index 00000000..8bcd3090
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/32.png b/libkdegames/carddecks/cards-gdkcard-bonded/32.png
new file mode 100644
index 00000000..c5fb66db
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/33.png b/libkdegames/carddecks/cards-gdkcard-bonded/33.png
new file mode 100644
index 00000000..43d4df04
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/34.png b/libkdegames/carddecks/cards-gdkcard-bonded/34.png
new file mode 100644
index 00000000..1bf6c975
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/35.png b/libkdegames/carddecks/cards-gdkcard-bonded/35.png
new file mode 100644
index 00000000..ba85c37e
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/36.png b/libkdegames/carddecks/cards-gdkcard-bonded/36.png
new file mode 100644
index 00000000..c45008af
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/37.png b/libkdegames/carddecks/cards-gdkcard-bonded/37.png
new file mode 100644
index 00000000..05cac5a9
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/38.png b/libkdegames/carddecks/cards-gdkcard-bonded/38.png
new file mode 100644
index 00000000..7c52ecbf
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/39.png b/libkdegames/carddecks/cards-gdkcard-bonded/39.png
new file mode 100644
index 00000000..1404295a
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/4.png b/libkdegames/carddecks/cards-gdkcard-bonded/4.png
new file mode 100644
index 00000000..fbf1b7d2
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/40.png b/libkdegames/carddecks/cards-gdkcard-bonded/40.png
new file mode 100644
index 00000000..b1c0f317
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/41.png b/libkdegames/carddecks/cards-gdkcard-bonded/41.png
new file mode 100644
index 00000000..a59a245c
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/42.png b/libkdegames/carddecks/cards-gdkcard-bonded/42.png
new file mode 100644
index 00000000..104836a5
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/43.png b/libkdegames/carddecks/cards-gdkcard-bonded/43.png
new file mode 100644
index 00000000..90b51009
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/44.png b/libkdegames/carddecks/cards-gdkcard-bonded/44.png
new file mode 100644
index 00000000..66960d0d
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/45.png b/libkdegames/carddecks/cards-gdkcard-bonded/45.png
new file mode 100644
index 00000000..7aabe88f
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/46.png b/libkdegames/carddecks/cards-gdkcard-bonded/46.png
new file mode 100644
index 00000000..adfb2516
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/47.png b/libkdegames/carddecks/cards-gdkcard-bonded/47.png
new file mode 100644
index 00000000..0f48a363
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/48.png b/libkdegames/carddecks/cards-gdkcard-bonded/48.png
new file mode 100644
index 00000000..368c593a
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/49.png b/libkdegames/carddecks/cards-gdkcard-bonded/49.png
new file mode 100644
index 00000000..a783b506
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/5.png b/libkdegames/carddecks/cards-gdkcard-bonded/5.png
new file mode 100644
index 00000000..9f809535
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/50.png b/libkdegames/carddecks/cards-gdkcard-bonded/50.png
new file mode 100644
index 00000000..dacc26cd
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/51.png b/libkdegames/carddecks/cards-gdkcard-bonded/51.png
new file mode 100644
index 00000000..13a6e924
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/52.png b/libkdegames/carddecks/cards-gdkcard-bonded/52.png
new file mode 100644
index 00000000..cce8c9f6
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/6.png b/libkdegames/carddecks/cards-gdkcard-bonded/6.png
new file mode 100644
index 00000000..c679e0c3
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/7.png b/libkdegames/carddecks/cards-gdkcard-bonded/7.png
new file mode 100644
index 00000000..53b35fbf
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/8.png b/libkdegames/carddecks/cards-gdkcard-bonded/8.png
new file mode 100644
index 00000000..389d0069
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/9.png b/libkdegames/carddecks/cards-gdkcard-bonded/9.png
new file mode 100644
index 00000000..59551a1a
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT
new file mode 100644
index 00000000..36052f95
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT
@@ -0,0 +1,13 @@
+This PySol cardset was adapted from gnome-games 1.0.2 (gdk-card-image).
+http://www.gnome.org
+
+Copyright (C) 1994 Heiko Eissfeldt <heiko@colossus.escape.de>
+Copyright (C) 1994 Michael Bischoff <mbi@mo.math.nat.tu-bs.de>
+Copyright (C) 1998 Felix Bellaby <felix@pooh.u-net.com>
+Copyright (C) 1998 Ryu Changwoo <cwryu@eve.kaist.ac.kr>
+Copyright (C) 1999 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>
+
+This cardset is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop
new file mode 100644
index 00000000..68f3ff21
--- /dev/null
+++ b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop
@@ -0,0 +1,37 @@
+[KDE Backdeck]
+Name=Bonded
+Name[af]=Geheg
+Name[az]=Bağlı
+Name[be]=Перапляценні
+Name[bg]=Завързан
+Name[bn]=আবদ্ধ
+Name[ca]=Unides
+Name[cs]=Lepenka
+Name[de]=Verbunden
+Name[eo]=Benda
+Name[es]=Unidas
+Name[eu]=Estekatua
+Name[fi]=Sidottu
+Name[fr]=Compact
+Name[gl]=Unidas
+Name[hi]=बॉन्डेड
+Name[hr]=Povezano
+Name[is]=Bundið
+Name[lv]=Vienkāršs
+Name[mk]=Врзани
+Name[mt]=Mehmuż
+Name[ne]=बन्डेड
+Name[pt]=Unidos
+Name[pt_BR]=Laçado
+Name[ru]=Переплетения
+Name[sr]=Везан
+Name[sr@Latn]=Vezan
+Name[ta]=பிணைக்கப்பட்ட
+Name[tg]=Печдарпечшавӣ
+Name[tr]=Bağlı
+Name[uk]=Бони
+Name[ven]=Zwo khwathiswa
+Name[xh]=Dibeneyo
+Name[zu]=Kuxhumene
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-hard-a-port/1.png b/libkdegames/carddecks/cards-hard-a-port/1.png
new file mode 100644
index 00000000..05c56268
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/10.png b/libkdegames/carddecks/cards-hard-a-port/10.png
new file mode 100644
index 00000000..ee2d2072
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/11.png b/libkdegames/carddecks/cards-hard-a-port/11.png
new file mode 100644
index 00000000..4fd72b89
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/12.png b/libkdegames/carddecks/cards-hard-a-port/12.png
new file mode 100644
index 00000000..7dd80ce7
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/13.png b/libkdegames/carddecks/cards-hard-a-port/13.png
new file mode 100644
index 00000000..62edf27c
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/14.png b/libkdegames/carddecks/cards-hard-a-port/14.png
new file mode 100644
index 00000000..5cfa05ed
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/15.png b/libkdegames/carddecks/cards-hard-a-port/15.png
new file mode 100644
index 00000000..a950a87c
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/16.png b/libkdegames/carddecks/cards-hard-a-port/16.png
new file mode 100644
index 00000000..8b0d76f3
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/17.png b/libkdegames/carddecks/cards-hard-a-port/17.png
new file mode 100644
index 00000000..9625563e
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/18.png b/libkdegames/carddecks/cards-hard-a-port/18.png
new file mode 100644
index 00000000..715c8443
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/19.png b/libkdegames/carddecks/cards-hard-a-port/19.png
new file mode 100644
index 00000000..7a3cf664
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/2.png b/libkdegames/carddecks/cards-hard-a-port/2.png
new file mode 100644
index 00000000..bf4d601f
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/20.png b/libkdegames/carddecks/cards-hard-a-port/20.png
new file mode 100644
index 00000000..331abc3b
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/21.png b/libkdegames/carddecks/cards-hard-a-port/21.png
new file mode 100644
index 00000000..59adfb08
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/22.png b/libkdegames/carddecks/cards-hard-a-port/22.png
new file mode 100644
index 00000000..a1ca8484
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/23.png b/libkdegames/carddecks/cards-hard-a-port/23.png
new file mode 100644
index 00000000..d97de844
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/24.png b/libkdegames/carddecks/cards-hard-a-port/24.png
new file mode 100644
index 00000000..65e3da3c
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/25.png b/libkdegames/carddecks/cards-hard-a-port/25.png
new file mode 100644
index 00000000..14f82b7d
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/26.png b/libkdegames/carddecks/cards-hard-a-port/26.png
new file mode 100644
index 00000000..e2601df6
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/27.png b/libkdegames/carddecks/cards-hard-a-port/27.png
new file mode 100644
index 00000000..d18b29d7
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/28.png b/libkdegames/carddecks/cards-hard-a-port/28.png
new file mode 100644
index 00000000..3dcc0842
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/29.png b/libkdegames/carddecks/cards-hard-a-port/29.png
new file mode 100644
index 00000000..a0cc5866
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/3.png b/libkdegames/carddecks/cards-hard-a-port/3.png
new file mode 100644
index 00000000..b78502b9
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/30.png b/libkdegames/carddecks/cards-hard-a-port/30.png
new file mode 100644
index 00000000..e659dc83
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/31.png b/libkdegames/carddecks/cards-hard-a-port/31.png
new file mode 100644
index 00000000..db6f599c
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/32.png b/libkdegames/carddecks/cards-hard-a-port/32.png
new file mode 100644
index 00000000..9e42582f
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/33.png b/libkdegames/carddecks/cards-hard-a-port/33.png
new file mode 100644
index 00000000..c19f1520
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/34.png b/libkdegames/carddecks/cards-hard-a-port/34.png
new file mode 100644
index 00000000..139dc9b4
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/35.png b/libkdegames/carddecks/cards-hard-a-port/35.png
new file mode 100644
index 00000000..62d8a9bc
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/36.png b/libkdegames/carddecks/cards-hard-a-port/36.png
new file mode 100644
index 00000000..510d150e
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/37.png b/libkdegames/carddecks/cards-hard-a-port/37.png
new file mode 100644
index 00000000..2e03c9e2
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/38.png b/libkdegames/carddecks/cards-hard-a-port/38.png
new file mode 100644
index 00000000..e12131f0
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/39.png b/libkdegames/carddecks/cards-hard-a-port/39.png
new file mode 100644
index 00000000..eae23793
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/4.png b/libkdegames/carddecks/cards-hard-a-port/4.png
new file mode 100644
index 00000000..9d7f80c1
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/40.png b/libkdegames/carddecks/cards-hard-a-port/40.png
new file mode 100644
index 00000000..733be70f
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/41.png b/libkdegames/carddecks/cards-hard-a-port/41.png
new file mode 100644
index 00000000..b3e51b07
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/42.png b/libkdegames/carddecks/cards-hard-a-port/42.png
new file mode 100644
index 00000000..a6f69d9f
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/43.png b/libkdegames/carddecks/cards-hard-a-port/43.png
new file mode 100644
index 00000000..49c20875
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/44.png b/libkdegames/carddecks/cards-hard-a-port/44.png
new file mode 100644
index 00000000..6fed6ead
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/45.png b/libkdegames/carddecks/cards-hard-a-port/45.png
new file mode 100644
index 00000000..81be70a2
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/46.png b/libkdegames/carddecks/cards-hard-a-port/46.png
new file mode 100644
index 00000000..d82c5f60
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/47.png b/libkdegames/carddecks/cards-hard-a-port/47.png
new file mode 100644
index 00000000..f6056465
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/48.png b/libkdegames/carddecks/cards-hard-a-port/48.png
new file mode 100644
index 00000000..6369f53b
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/49.png b/libkdegames/carddecks/cards-hard-a-port/49.png
new file mode 100644
index 00000000..49b5b53e
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/5.png b/libkdegames/carddecks/cards-hard-a-port/5.png
new file mode 100644
index 00000000..6ff98a13
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/50.png b/libkdegames/carddecks/cards-hard-a-port/50.png
new file mode 100644
index 00000000..ce8e46db
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/51.png b/libkdegames/carddecks/cards-hard-a-port/51.png
new file mode 100644
index 00000000..46552bee
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/52.png b/libkdegames/carddecks/cards-hard-a-port/52.png
new file mode 100644
index 00000000..b03db4a0
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/6.png b/libkdegames/carddecks/cards-hard-a-port/6.png
new file mode 100644
index 00000000..24fa84b0
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/7.png b/libkdegames/carddecks/cards-hard-a-port/7.png
new file mode 100644
index 00000000..11561878
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/8.png b/libkdegames/carddecks/cards-hard-a-port/8.png
new file mode 100644
index 00000000..08d124b7
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/9.png b/libkdegames/carddecks/cards-hard-a-port/9.png
new file mode 100644
index 00000000..14962b11
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT
new file mode 100644
index 00000000..f5575c7b
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT
@@ -0,0 +1,14 @@
+This PySol cardset was created by T. Kirk.
+
+Copyright (C) 2000 T. Kirk <grania@mailcity.com>
+
+This transformation card set uses images from a set of trade
+cards produced in the late 19th century in the United States
+of America. Cards of this type were given away as premiums
+with the purchase of various products. Each of the cards
+has a different image.
+
+This card set is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-hard-a-port/index.desktop b/libkdegames/carddecks/cards-hard-a-port/index.desktop
new file mode 100644
index 00000000..d8e49aa5
--- /dev/null
+++ b/libkdegames/carddecks/cards-hard-a-port/index.desktop
@@ -0,0 +1,29 @@
+[KDE Backdeck]
+Name=Hard a Port
+Name[af]=Hard 'n Poort
+Name[be]=Пампушка
+Name[bg]=Дамски
+Name[bn]=হার্ড এ পোর্ট
+Name[de]=Hart Backbord
+Name[eo]=Tenta
+Name[hi]=हार्ड ए पोर्ट
+Name[hr]=Tvrdi na portu
+Name[is]=Hart á bakborða
+Name[it]=Donnine
+Name[km]=ច្រក​រឹង
+Name[lv]=Klasisks
+Name[nds]=Hatt Backboord
+Name[ne]=कडा एउटा पोर्ट
+Name[pt_BR]=Duramente um Porto
+Name[ro]=Licenţioase
+Name[ru]=Пышка
+Name[sr]=Хард-а-порт
+Name[sr@Latn]=Hard-a-port
+Name[sv]=Dikt babord
+Name[ta]=முனையத்தை கடினமாக்கு
+Name[tg]=Даргоҳи Сахт
+Name[ven]=Port yo Khwathaho
+Name[xh]=Izibuko Esport eqinileyo
+Name[zu]=Qinisa isikhumulo
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-konqi-modern/1.png b/libkdegames/carddecks/cards-konqi-modern/1.png
new file mode 100644
index 00000000..68dfb0ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/10.png b/libkdegames/carddecks/cards-konqi-modern/10.png
new file mode 100644
index 00000000..6df3ad8d
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/11.png b/libkdegames/carddecks/cards-konqi-modern/11.png
new file mode 100644
index 00000000..665cb2a6
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/12.png b/libkdegames/carddecks/cards-konqi-modern/12.png
new file mode 100644
index 00000000..1f23d99e
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/13.png b/libkdegames/carddecks/cards-konqi-modern/13.png
new file mode 100644
index 00000000..f1027fdc
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/14.png b/libkdegames/carddecks/cards-konqi-modern/14.png
new file mode 100644
index 00000000..80d11335
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/15.png b/libkdegames/carddecks/cards-konqi-modern/15.png
new file mode 100644
index 00000000..72913f7b
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/16.png b/libkdegames/carddecks/cards-konqi-modern/16.png
new file mode 100644
index 00000000..225f7774
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/17.png b/libkdegames/carddecks/cards-konqi-modern/17.png
new file mode 100644
index 00000000..0444e010
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/18.png b/libkdegames/carddecks/cards-konqi-modern/18.png
new file mode 100644
index 00000000..f325c781
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/19.png b/libkdegames/carddecks/cards-konqi-modern/19.png
new file mode 100644
index 00000000..570e951e
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/2.png b/libkdegames/carddecks/cards-konqi-modern/2.png
new file mode 100644
index 00000000..89b2b4d5
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/20.png b/libkdegames/carddecks/cards-konqi-modern/20.png
new file mode 100644
index 00000000..d4b47c7d
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/21.png b/libkdegames/carddecks/cards-konqi-modern/21.png
new file mode 100644
index 00000000..9ab04cc5
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/22.png b/libkdegames/carddecks/cards-konqi-modern/22.png
new file mode 100644
index 00000000..387c3203
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/23.png b/libkdegames/carddecks/cards-konqi-modern/23.png
new file mode 100644
index 00000000..fcec2343
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/24.png b/libkdegames/carddecks/cards-konqi-modern/24.png
new file mode 100644
index 00000000..48c9ed02
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/25.png b/libkdegames/carddecks/cards-konqi-modern/25.png
new file mode 100644
index 00000000..80bbfdf1
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/26.png b/libkdegames/carddecks/cards-konqi-modern/26.png
new file mode 100644
index 00000000..2a879ec5
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/27.png b/libkdegames/carddecks/cards-konqi-modern/27.png
new file mode 100644
index 00000000..1e6141c0
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/28.png b/libkdegames/carddecks/cards-konqi-modern/28.png
new file mode 100644
index 00000000..1403437e
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/29.png b/libkdegames/carddecks/cards-konqi-modern/29.png
new file mode 100644
index 00000000..415b48cc
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/3.png b/libkdegames/carddecks/cards-konqi-modern/3.png
new file mode 100644
index 00000000..43067744
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/30.png b/libkdegames/carddecks/cards-konqi-modern/30.png
new file mode 100644
index 00000000..8a1b885d
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/31.png b/libkdegames/carddecks/cards-konqi-modern/31.png
new file mode 100644
index 00000000..c7de49ed
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/32.png b/libkdegames/carddecks/cards-konqi-modern/32.png
new file mode 100644
index 00000000..57e0015f
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/33.png b/libkdegames/carddecks/cards-konqi-modern/33.png
new file mode 100644
index 00000000..a502ae94
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/34.png b/libkdegames/carddecks/cards-konqi-modern/34.png
new file mode 100644
index 00000000..524768dc
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/35.png b/libkdegames/carddecks/cards-konqi-modern/35.png
new file mode 100644
index 00000000..d15a240e
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/36.png b/libkdegames/carddecks/cards-konqi-modern/36.png
new file mode 100644
index 00000000..b8dce3f8
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/37.png b/libkdegames/carddecks/cards-konqi-modern/37.png
new file mode 100644
index 00000000..2f193cc1
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/38.png b/libkdegames/carddecks/cards-konqi-modern/38.png
new file mode 100644
index 00000000..097d63ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/39.png b/libkdegames/carddecks/cards-konqi-modern/39.png
new file mode 100644
index 00000000..a94a6009
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/4.png b/libkdegames/carddecks/cards-konqi-modern/4.png
new file mode 100644
index 00000000..b4890c03
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/40.png b/libkdegames/carddecks/cards-konqi-modern/40.png
new file mode 100644
index 00000000..d533469a
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/41.png b/libkdegames/carddecks/cards-konqi-modern/41.png
new file mode 100644
index 00000000..f524f7ea
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/42.png b/libkdegames/carddecks/cards-konqi-modern/42.png
new file mode 100644
index 00000000..9e6ff5af
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/43.png b/libkdegames/carddecks/cards-konqi-modern/43.png
new file mode 100644
index 00000000..59e9e273
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/44.png b/libkdegames/carddecks/cards-konqi-modern/44.png
new file mode 100644
index 00000000..a3114bd9
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/45.png b/libkdegames/carddecks/cards-konqi-modern/45.png
new file mode 100644
index 00000000..2614080b
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/46.png b/libkdegames/carddecks/cards-konqi-modern/46.png
new file mode 100644
index 00000000..a3118981
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/47.png b/libkdegames/carddecks/cards-konqi-modern/47.png
new file mode 100644
index 00000000..423ec06e
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/48.png b/libkdegames/carddecks/cards-konqi-modern/48.png
new file mode 100644
index 00000000..5153c779
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/49.png b/libkdegames/carddecks/cards-konqi-modern/49.png
new file mode 100644
index 00000000..99f8ae96
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/5.png b/libkdegames/carddecks/cards-konqi-modern/5.png
new file mode 100644
index 00000000..5e167b1a
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/50.png b/libkdegames/carddecks/cards-konqi-modern/50.png
new file mode 100644
index 00000000..a9c41757
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/51.png b/libkdegames/carddecks/cards-konqi-modern/51.png
new file mode 100644
index 00000000..b322fc1b
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/52.png b/libkdegames/carddecks/cards-konqi-modern/52.png
new file mode 100644
index 00000000..d614733f
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/6.png b/libkdegames/carddecks/cards-konqi-modern/6.png
new file mode 100644
index 00000000..d614e646
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/7.png b/libkdegames/carddecks/cards-konqi-modern/7.png
new file mode 100644
index 00000000..961ac50f
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/8.png b/libkdegames/carddecks/cards-konqi-modern/8.png
new file mode 100644
index 00000000..1769d3db
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/9.png b/libkdegames/carddecks/cards-konqi-modern/9.png
new file mode 100644
index 00000000..f86dcd48
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-konqi-modern/index.desktop b/libkdegames/carddecks/cards-konqi-modern/index.desktop
new file mode 100644
index 00000000..4dc5fe5c
--- /dev/null
+++ b/libkdegames/carddecks/cards-konqi-modern/index.desktop
@@ -0,0 +1,62 @@
+[KDE Backdeck]
+Name=Konqi
+Name[be]=Конкі
+Name[bg]=Конки
+Name[bn]=কনকি
+Name[cs]=Konqui
+Name[eo]=Konĉja
+Name[hi]=के-ऑन्गी
+Name[it]=Konqui
+Name[lv]=Konvi
+Name[ne]=कोन्क्वी
+Name[ru]=Конки
+Name[sr]=Конки
+Name[sr@Latn]=Konki
+Name[ta]=கான்கி
+Name[tg]=Конки
+Name[uk]=Конкі
+Name[zu]=I-Konqi
+Preview=11.png
+PySol=false
+Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[bn]=আধুনিক কনকি - একটি পারিবারিক তাস খেলা\nডিজাইন: লরা লেল্যান্ড\n <l_layland@hotmail.com>\n Katie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nকনকি: স্টিফেন স্পাজ\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[bs]=Moderni Konqi - igrajte sa porodičnim špilom\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n <l_layland@hotmail.com>\nKatie per Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi per Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n <l_layland@hotmail.com>\nKatie vytvořila Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqiho vytvořil Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n <l_layland@hotmail.com>\nKatie gan Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi gan Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie von Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi von Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[el]=Μοντέρνος Konqi - play the family θέμα καρτών\nΣχεδίαση: Laura Layland\n <l_layland@hotmail.com>\nKatie από Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi από Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n <l_layland@hotmail.com>\nKonjo de Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonĉjo de Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n <l_layland@hotmail.com>\nKatie-ren egilea: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi-ren egilea Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند <l_layland@hotmail.com>\nKatie توسط آگنیسکا زاکووسکا\n <agnieszka@imagegalaxy.de>\nKonqi توسط استفان اسپاتز\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n <l_layland@hotmail.com>a\nKatie Agnieszka Czajkowskaa\n <agnieszka@imagegalaxy.de>a\nKonqi Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n <l_layland@hotmail.com>\nKatie par Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi par Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[is]=Nútíma Konqi - spilastokkur fjölskyldunnar\nHönnun: Laura Layland\n <l_layland@hotmail.com>\nKatie e. Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi e. Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[it]=Konqui moderno - carte familiari\nDesign: Laura Layland\n<l_layland@hotmail.com>\nKatie di Agnieszka Czajkowska\n<agnieszka@imagegalaxy.de>\nKonqi di Stefan Spatz\n<stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ja]=モダン Konqi - ファミリ向けカードデッキ\nデザイン: Laura Layland\n <l_layland@hotmail.com>\nKatie の作者: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi の作者: Stefan Spatz \n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[lt]=Modern Konqi - žaisktie šeimos kortų žaidimą\nDizainas: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[lv]=Modernais Konkvi - spēlēt pie ģimenes kāršu galda\n Dizains: Laura Layland\n <l_layland@hotmail.com>\n Katie no Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\n Konkvi no Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[mk]=Модерен Konqi - играјте со семејниот шпил карти\nДизајн: Laura Layland\n<l_layland@hotmail.com>\nKatie од Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi од Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de>
+Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie vun Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi vun Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ne]=आधुनिक कोन्क्की - परिवारिक कार्डडेक \nडिजाइन प्ले: लाउरा लेल्यान्ड\n <l_layland@hotmail.com>\nKatie, अग्निज्का जज्कोस्काद्वारा\n <agnieszka@imagegalaxy.de>\nKonqi, स्टेफान स्पार्टजद्वारा \n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie door Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi door Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de>
+Comment[pl]=Nowoczesny Konqi - zagraj w grę rodzinną\nProjekt: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>a\nKonqi: Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ru]=Современная колода с семейством Конки\nДизайн: Лаура Лейлэнд (Laura Layland) <l_layland@hotmail.com>\nКэйт нарисована Агниежкой Зайковской (Agnieszka Czajkowska) <agnieszka@imagegalaxy.de>\nКонки нарисован Штефаном Спартцом (Stefan Spatz) <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie od Agnieszky Czajkowskeja\n <agnieszka@imagegalaxy.de>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie od Agnieszke Czajkowske\n <agnieszka@imagegalaxy.de>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sr]=Модеран Конки - играјте са породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n <l_layland@hotmail.com>\nКети: Агњешка Чајковска (Agnieszka Czajkowska)\n <agnieszka@imagegalaxy.de>\nКонки: Штефан Шпац (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sr@Latn]=Moderan Konki - igrajte sa porodičnim špilom\nDizajn: Lora Lejlend (Laura Layland)\n <l_layland@hotmail.com>\nKeti: Agnješka Čajkovska (Agnieszka Czajkowska)\n <agnieszka@imagegalaxy.de>\nKonki: Štefan Špac (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ta]=மார்டன் கான்கி - புதிய குடும்பச் சீட்டுத் தளத்தை விளையாடு\nவடிவமைப்பு: லெளரா லேலேண்டு\n <l_ayland@hotmail.com>\n Katie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[uk]=Сучасний Конкі - зіграйте у сімейні карти\nРозробка: Laura Laylanda\n <l_layland@hotmail.com>\nKatie від Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi від Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[wa]=Modiene Konqi - cwårdjeus des familes\nDessins: Laura Layland\n <l_layland@hotmail.com>\nKatie pa Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi pa Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[zh_TW]=現代 Konqi - 玩家庭牌局\n設計︰Laura Layland...<l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska...<agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz...<stefan.spatz@stud-mail.uni-wuerzburg.de>
diff --git a/libkdegames/carddecks/cards-penguins/1.png b/libkdegames/carddecks/cards-penguins/1.png
new file mode 100644
index 00000000..b59022e8
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/10.png b/libkdegames/carddecks/cards-penguins/10.png
new file mode 100644
index 00000000..d54669fe
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/11.png b/libkdegames/carddecks/cards-penguins/11.png
new file mode 100644
index 00000000..02c2464c
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/12.png b/libkdegames/carddecks/cards-penguins/12.png
new file mode 100644
index 00000000..d179eb99
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/13.png b/libkdegames/carddecks/cards-penguins/13.png
new file mode 100644
index 00000000..135af493
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/14.png b/libkdegames/carddecks/cards-penguins/14.png
new file mode 100644
index 00000000..4a179917
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/15.png b/libkdegames/carddecks/cards-penguins/15.png
new file mode 100644
index 00000000..b5238d00
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/16.png b/libkdegames/carddecks/cards-penguins/16.png
new file mode 100644
index 00000000..33fe6b8e
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/17.png b/libkdegames/carddecks/cards-penguins/17.png
new file mode 100644
index 00000000..99b77ce8
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/18.png b/libkdegames/carddecks/cards-penguins/18.png
new file mode 100644
index 00000000..b88ae958
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/19.png b/libkdegames/carddecks/cards-penguins/19.png
new file mode 100644
index 00000000..532899aa
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/2.png b/libkdegames/carddecks/cards-penguins/2.png
new file mode 100644
index 00000000..090cfd02
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/20.png b/libkdegames/carddecks/cards-penguins/20.png
new file mode 100644
index 00000000..ed129a32
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/21.png b/libkdegames/carddecks/cards-penguins/21.png
new file mode 100644
index 00000000..5bf532f7
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/22.png b/libkdegames/carddecks/cards-penguins/22.png
new file mode 100644
index 00000000..07c61ced
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/23.png b/libkdegames/carddecks/cards-penguins/23.png
new file mode 100644
index 00000000..ecce302b
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/24.png b/libkdegames/carddecks/cards-penguins/24.png
new file mode 100644
index 00000000..feee0a9e
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/25.png b/libkdegames/carddecks/cards-penguins/25.png
new file mode 100644
index 00000000..d42f2b2b
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/26.png b/libkdegames/carddecks/cards-penguins/26.png
new file mode 100644
index 00000000..cc5e930f
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/27.png b/libkdegames/carddecks/cards-penguins/27.png
new file mode 100644
index 00000000..4ae7702a
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/28.png b/libkdegames/carddecks/cards-penguins/28.png
new file mode 100644
index 00000000..ee9b40ea
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/29.png b/libkdegames/carddecks/cards-penguins/29.png
new file mode 100644
index 00000000..ba2dfe7d
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/3.png b/libkdegames/carddecks/cards-penguins/3.png
new file mode 100644
index 00000000..7f53daf9
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/30.png b/libkdegames/carddecks/cards-penguins/30.png
new file mode 100644
index 00000000..1486cf98
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/31.png b/libkdegames/carddecks/cards-penguins/31.png
new file mode 100644
index 00000000..805d39e9
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/32.png b/libkdegames/carddecks/cards-penguins/32.png
new file mode 100644
index 00000000..4a1b12f0
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/33.png b/libkdegames/carddecks/cards-penguins/33.png
new file mode 100644
index 00000000..b8ae8965
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/34.png b/libkdegames/carddecks/cards-penguins/34.png
new file mode 100644
index 00000000..e2ce160c
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/35.png b/libkdegames/carddecks/cards-penguins/35.png
new file mode 100644
index 00000000..580c4d12
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/36.png b/libkdegames/carddecks/cards-penguins/36.png
new file mode 100644
index 00000000..296d4c7c
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/37.png b/libkdegames/carddecks/cards-penguins/37.png
new file mode 100644
index 00000000..fbbefa84
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/38.png b/libkdegames/carddecks/cards-penguins/38.png
new file mode 100644
index 00000000..7d9a34bd
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/39.png b/libkdegames/carddecks/cards-penguins/39.png
new file mode 100644
index 00000000..23ffffaa
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/4.png b/libkdegames/carddecks/cards-penguins/4.png
new file mode 100644
index 00000000..79ced119
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/40.png b/libkdegames/carddecks/cards-penguins/40.png
new file mode 100644
index 00000000..b9bed320
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/41.png b/libkdegames/carddecks/cards-penguins/41.png
new file mode 100644
index 00000000..c5a65385
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/42.png b/libkdegames/carddecks/cards-penguins/42.png
new file mode 100644
index 00000000..aba32e3e
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/43.png b/libkdegames/carddecks/cards-penguins/43.png
new file mode 100644
index 00000000..04623c1e
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/44.png b/libkdegames/carddecks/cards-penguins/44.png
new file mode 100644
index 00000000..7e6069e6
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/45.png b/libkdegames/carddecks/cards-penguins/45.png
new file mode 100644
index 00000000..36b67487
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/46.png b/libkdegames/carddecks/cards-penguins/46.png
new file mode 100644
index 00000000..80216f18
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/47.png b/libkdegames/carddecks/cards-penguins/47.png
new file mode 100644
index 00000000..9fb16882
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/48.png b/libkdegames/carddecks/cards-penguins/48.png
new file mode 100644
index 00000000..12f928e6
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/49.png b/libkdegames/carddecks/cards-penguins/49.png
new file mode 100644
index 00000000..9119c351
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/5.png b/libkdegames/carddecks/cards-penguins/5.png
new file mode 100644
index 00000000..f18a3a57
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/50.png b/libkdegames/carddecks/cards-penguins/50.png
new file mode 100644
index 00000000..e3dacdba
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/51.png b/libkdegames/carddecks/cards-penguins/51.png
new file mode 100644
index 00000000..fa46ff72
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/52.png b/libkdegames/carddecks/cards-penguins/52.png
new file mode 100644
index 00000000..1522ed3f
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/6.png b/libkdegames/carddecks/cards-penguins/6.png
new file mode 100644
index 00000000..9d84eb8d
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/7.png b/libkdegames/carddecks/cards-penguins/7.png
new file mode 100644
index 00000000..2b8d0768
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/8.png b/libkdegames/carddecks/cards-penguins/8.png
new file mode 100644
index 00000000..933be6e7
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/9.png b/libkdegames/carddecks/cards-penguins/9.png
new file mode 100644
index 00000000..669d6e00
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-penguins/COPYRIGHT b/libkdegames/carddecks/cards-penguins/COPYRIGHT
new file mode 100644
index 00000000..cf87594e
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/COPYRIGHT
@@ -0,0 +1,10 @@
+This PySol cardset was adapted from the Ace of Penguins 1.0.
+http://www.delorie.com/store/ace/
+
+Copyright (C) 1998 DJ Delorie <dj@delorie.com>
+Copyright (C) 1998 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>
+
+This cardset is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-penguins/index.desktop b/libkdegames/carddecks/cards-penguins/index.desktop
new file mode 100644
index 00000000..2fe12197
--- /dev/null
+++ b/libkdegames/carddecks/cards-penguins/index.desktop
@@ -0,0 +1,67 @@
+[KDE Backdeck]
+Name=Penguins
+Name[af]=Pikkewyne
+Name[ar]=بطاريق
+Name[az]=Pinqvinlər
+Name[be]=Пінгвіны
+Name[bg]=Пингвин
+Name[bn]=পেঙ্গুইন
+Name[br]=Pennoù-gwenn
+Name[bs]=Pingvini
+Name[ca]=Pingüins
+Name[cs]=Tučňáci
+Name[da]=Pingviner
+Name[de]=Pinguine
+Name[el]=Πιγκουίνοι
+Name[eo]=Pingvenoj
+Name[es]=Pingüinos
+Name[et]=Pingviinid
+Name[eu]=Pinguinoak
+Name[fa]=پنگوئنها
+Name[fi]=Pingviinit
+Name[fr]=Pingouins
+Name[gl]=Pingüíns
+Name[he]=פנגוינים
+Name[hi]=पेंग्विन्स
+Name[hr]=Pingvini
+Name[hu]=Pingvinek
+Name[is]=Mörgæsir
+Name[it]=Pinguini
+Name[km]=ភេនឃ្វីន
+Name[ko]=펭귄
+Name[lt]=Pingvinai
+Name[lv]=Pingvīni
+Name[mk]=Пингвини
+Name[mt]=Pingwini
+Name[nb]=Pingviner
+Name[nds]=Pinguins
+Name[ne]=पेन्गुइन
+Name[nl]=Pinguïns
+Name[nn]=Pingvinar
+Name[pa]=ਪੈਂਗੂਇਨ
+Name[pl]=Pingwiny
+Name[pt]=Pinguins
+Name[pt_BR]=Pingüins
+Name[ro]=Pinguini
+Name[ru]=Пингвины
+Name[se]=Pingviinnat
+Name[sk]=Tučniaci
+Name[sl]=Pingvini
+Name[sr]=Пингвини
+Name[sr@Latn]=Pingvini
+Name[sv]=Pingviner
+Name[ta]=பென்குயின்கள்
+Name[tg]=Пингвинҳо
+Name[th]=เพนกวิน
+Name[tr]=Penguenler
+Name[uk]=Пінгвіни
+Name[uz]=Pingvinlar
+Name[uz@cyrillic]=Пингвинлар
+Name[vi]=Chim cánh cụt
+Name[wa]=Pingwins
+Name[xh]=iintaka zaselwandle
+Name[zh_CN]=企鹅
+Name[zh_TW]=企鵝
+Name[zu]=Izinyoni zasemanzini
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-spaced/1.png b/libkdegames/carddecks/cards-spaced/1.png
new file mode 100644
index 00000000..62220b38
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/10.png b/libkdegames/carddecks/cards-spaced/10.png
new file mode 100644
index 00000000..d0bcd828
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/11.png b/libkdegames/carddecks/cards-spaced/11.png
new file mode 100644
index 00000000..b4e6016e
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/12.png b/libkdegames/carddecks/cards-spaced/12.png
new file mode 100644
index 00000000..45ab5cf5
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/13.png b/libkdegames/carddecks/cards-spaced/13.png
new file mode 100644
index 00000000..37e7c2ce
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/14.png b/libkdegames/carddecks/cards-spaced/14.png
new file mode 100644
index 00000000..97af1f02
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/15.png b/libkdegames/carddecks/cards-spaced/15.png
new file mode 100644
index 00000000..fb2fcc76
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/16.png b/libkdegames/carddecks/cards-spaced/16.png
new file mode 100644
index 00000000..9042b161
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/17.png b/libkdegames/carddecks/cards-spaced/17.png
new file mode 100644
index 00000000..a67b2832
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/18.png b/libkdegames/carddecks/cards-spaced/18.png
new file mode 100644
index 00000000..f4c8a04b
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/19.png b/libkdegames/carddecks/cards-spaced/19.png
new file mode 100644
index 00000000..fac5b199
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/2.png b/libkdegames/carddecks/cards-spaced/2.png
new file mode 100644
index 00000000..af3c76fd
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/20.png b/libkdegames/carddecks/cards-spaced/20.png
new file mode 100644
index 00000000..12d43520
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/21.png b/libkdegames/carddecks/cards-spaced/21.png
new file mode 100644
index 00000000..ea715adc
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/22.png b/libkdegames/carddecks/cards-spaced/22.png
new file mode 100644
index 00000000..13e6d7ae
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/23.png b/libkdegames/carddecks/cards-spaced/23.png
new file mode 100644
index 00000000..f43a745f
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/24.png b/libkdegames/carddecks/cards-spaced/24.png
new file mode 100644
index 00000000..810f59ef
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/25.png b/libkdegames/carddecks/cards-spaced/25.png
new file mode 100644
index 00000000..836b9863
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/26.png b/libkdegames/carddecks/cards-spaced/26.png
new file mode 100644
index 00000000..27bc29bf
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/27.png b/libkdegames/carddecks/cards-spaced/27.png
new file mode 100644
index 00000000..a44baa51
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/28.png b/libkdegames/carddecks/cards-spaced/28.png
new file mode 100644
index 00000000..af480ddd
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/29.png b/libkdegames/carddecks/cards-spaced/29.png
new file mode 100644
index 00000000..622968f4
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/3.png b/libkdegames/carddecks/cards-spaced/3.png
new file mode 100644
index 00000000..5b31e13b
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/30.png b/libkdegames/carddecks/cards-spaced/30.png
new file mode 100644
index 00000000..e43e9bd0
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/31.png b/libkdegames/carddecks/cards-spaced/31.png
new file mode 100644
index 00000000..382d0c11
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/32.png b/libkdegames/carddecks/cards-spaced/32.png
new file mode 100644
index 00000000..969e921d
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/33.png b/libkdegames/carddecks/cards-spaced/33.png
new file mode 100644
index 00000000..e9dbd109
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/34.png b/libkdegames/carddecks/cards-spaced/34.png
new file mode 100644
index 00000000..49d26d66
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/35.png b/libkdegames/carddecks/cards-spaced/35.png
new file mode 100644
index 00000000..f60149e5
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/36.png b/libkdegames/carddecks/cards-spaced/36.png
new file mode 100644
index 00000000..49b7044a
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/37.png b/libkdegames/carddecks/cards-spaced/37.png
new file mode 100644
index 00000000..dafb3402
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/38.png b/libkdegames/carddecks/cards-spaced/38.png
new file mode 100644
index 00000000..f1d7e030
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/39.png b/libkdegames/carddecks/cards-spaced/39.png
new file mode 100644
index 00000000..8e045381
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/4.png b/libkdegames/carddecks/cards-spaced/4.png
new file mode 100644
index 00000000..e8ceab32
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/40.png b/libkdegames/carddecks/cards-spaced/40.png
new file mode 100644
index 00000000..de378577
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/41.png b/libkdegames/carddecks/cards-spaced/41.png
new file mode 100644
index 00000000..0e4f3382
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/42.png b/libkdegames/carddecks/cards-spaced/42.png
new file mode 100644
index 00000000..910524a2
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/43.png b/libkdegames/carddecks/cards-spaced/43.png
new file mode 100644
index 00000000..f9f218bf
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/44.png b/libkdegames/carddecks/cards-spaced/44.png
new file mode 100644
index 00000000..fe64a155
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/45.png b/libkdegames/carddecks/cards-spaced/45.png
new file mode 100644
index 00000000..d914f7b1
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/46.png b/libkdegames/carddecks/cards-spaced/46.png
new file mode 100644
index 00000000..f6953f9d
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/47.png b/libkdegames/carddecks/cards-spaced/47.png
new file mode 100644
index 00000000..bfaa56ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/48.png b/libkdegames/carddecks/cards-spaced/48.png
new file mode 100644
index 00000000..b5d46c34
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/49.png b/libkdegames/carddecks/cards-spaced/49.png
new file mode 100644
index 00000000..f890b5db
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/5.png b/libkdegames/carddecks/cards-spaced/5.png
new file mode 100644
index 00000000..e160f9b3
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/50.png b/libkdegames/carddecks/cards-spaced/50.png
new file mode 100644
index 00000000..488eb5fa
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/51.png b/libkdegames/carddecks/cards-spaced/51.png
new file mode 100644
index 00000000..4d1f41b0
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/52.png b/libkdegames/carddecks/cards-spaced/52.png
new file mode 100644
index 00000000..345579f4
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/6.png b/libkdegames/carddecks/cards-spaced/6.png
new file mode 100644
index 00000000..90e8e998
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/7.png b/libkdegames/carddecks/cards-spaced/7.png
new file mode 100644
index 00000000..374ee771
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/8.png b/libkdegames/carddecks/cards-spaced/8.png
new file mode 100644
index 00000000..987a24c6
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/9.png b/libkdegames/carddecks/cards-spaced/9.png
new file mode 100644
index 00000000..f2855e9f
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-spaced/COPYRIGHT b/libkdegames/carddecks/cards-spaced/COPYRIGHT
new file mode 100644
index 00000000..f54e3a4e
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/COPYRIGHT
@@ -0,0 +1,16 @@
+The backs for these cards came from the U.S. National Aeronautics
+and Space Administration. The original images can be found at:
+http://antwrp.gsfc.nasa.gov/apod/archivepix.html
+along with a lot more just like 'em.
+
+The penguins are by "The PAPA" <papalini@biancaneve.ing.unifi.it>
+and can be found at:
+http://biancaneve.ing.unifi.it/~papalini/
+and there's a lot more of those too.
+
+Copyright (C) 1999 T. Kirk <grania@mailcity.com>
+
+This card set is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-spaced/index.desktop b/libkdegames/carddecks/cards-spaced/index.desktop
new file mode 100644
index 00000000..0e9eadf0
--- /dev/null
+++ b/libkdegames/carddecks/cards-spaced/index.desktop
@@ -0,0 +1,46 @@
+[KDE Backdeck]
+Name=Spaced
+Name[af]=Gespasieer
+Name[az]=Boşluqlu
+Name[be]=Космас
+Name[bg]=Звезден
+Name[bn]=স্পেসযুক্ত
+Name[ca]=Espaiades
+Name[cs]=Vesmír
+Name[de]=Mit Abstand
+Name[eo]=Komika
+Name[es]=Espaciadas
+Name[eu]=Tartea
+Name[fr]=Spatial
+Name[gl]=Espaciadas
+Name[hi]=स्पेस्ड
+Name[hu]=Űr
+Name[is]=Speisað
+Name[it]=Spaziale
+Name[ko]=우주
+Name[lv]=Kosmisks
+Name[mk]=Раздалечени
+Name[mt]=Spazzjat
+Name[nb]=Rom-duell
+Name[nds]=Afstand
+Name[ne]=खाली स्थान
+Name[nl]=Ruimtelijk
+Name[nso]=Beetswe Sekgoba
+Name[pl]=Przestrzenny
+Name[pt]=Espacial
+Name[pt_BR]=Espaçado
+Name[ro]=Spaţiat
+Name[ru]=Космос
+Name[sl]=Vesoljski dvoboj
+Name[sr]=Размакнут
+Name[sr@Latn]=Razmaknut
+Name[sv]=Spejsad
+Name[ta]=இடம் விடப்பட்ட
+Name[tg]=Кайҳонӣ
+Name[tr]=Boşluklu
+Name[uk]=Космічний
+Name[ven]=Hu na zwikala
+Name[xh]=ukugqagqeneyo
+Name[zu]=Kunezikhala
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-warwick/0.png b/libkdegames/carddecks/cards-warwick/0.png
new file mode 100644
index 00000000..e8fc5c76
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/0.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/1.png b/libkdegames/carddecks/cards-warwick/1.png
new file mode 100644
index 00000000..79fc960c
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/10.png b/libkdegames/carddecks/cards-warwick/10.png
new file mode 100644
index 00000000..9b459b5c
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/105.png b/libkdegames/carddecks/cards-warwick/105.png
new file mode 100644
index 00000000..0f49bdd9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/105.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/106.png b/libkdegames/carddecks/cards-warwick/106.png
new file mode 100644
index 00000000..4e2371c1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/106.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/107.png b/libkdegames/carddecks/cards-warwick/107.png
new file mode 100644
index 00000000..c7a19052
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/107.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/108.png b/libkdegames/carddecks/cards-warwick/108.png
new file mode 100644
index 00000000..09ee0a0a
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/108.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/109.png b/libkdegames/carddecks/cards-warwick/109.png
new file mode 100644
index 00000000..a48b134c
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/109.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/11.png b/libkdegames/carddecks/cards-warwick/11.png
new file mode 100644
index 00000000..a78f94b7
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/110.png b/libkdegames/carddecks/cards-warwick/110.png
new file mode 100644
index 00000000..2a3fccd9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/110.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/111.png b/libkdegames/carddecks/cards-warwick/111.png
new file mode 100644
index 00000000..20c9f0f9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/111.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/112.png b/libkdegames/carddecks/cards-warwick/112.png
new file mode 100644
index 00000000..90935873
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/112.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/113.png b/libkdegames/carddecks/cards-warwick/113.png
new file mode 100644
index 00000000..7df5d9b1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/113.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/114.png b/libkdegames/carddecks/cards-warwick/114.png
new file mode 100644
index 00000000..062ae319
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/114.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/115.png b/libkdegames/carddecks/cards-warwick/115.png
new file mode 100644
index 00000000..fd4b83b4
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/115.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/116.png b/libkdegames/carddecks/cards-warwick/116.png
new file mode 100644
index 00000000..7f1c0dcd
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/116.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/12.png b/libkdegames/carddecks/cards-warwick/12.png
new file mode 100644
index 00000000..41d93ef2
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/13.png b/libkdegames/carddecks/cards-warwick/13.png
new file mode 100644
index 00000000..b990c512
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/14.png b/libkdegames/carddecks/cards-warwick/14.png
new file mode 100644
index 00000000..45b94a15
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/15.png b/libkdegames/carddecks/cards-warwick/15.png
new file mode 100644
index 00000000..46c1f3f6
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/16.png b/libkdegames/carddecks/cards-warwick/16.png
new file mode 100644
index 00000000..d8d118f9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/17.png b/libkdegames/carddecks/cards-warwick/17.png
new file mode 100644
index 00000000..e6e0aa93
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/18.png b/libkdegames/carddecks/cards-warwick/18.png
new file mode 100644
index 00000000..eb84255b
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/19.png b/libkdegames/carddecks/cards-warwick/19.png
new file mode 100644
index 00000000..88d4d6d9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/2.png b/libkdegames/carddecks/cards-warwick/2.png
new file mode 100644
index 00000000..98e798a1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/20.png b/libkdegames/carddecks/cards-warwick/20.png
new file mode 100644
index 00000000..4f75de50
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/21.png b/libkdegames/carddecks/cards-warwick/21.png
new file mode 100644
index 00000000..39bba2f2
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/22.png b/libkdegames/carddecks/cards-warwick/22.png
new file mode 100644
index 00000000..733c396d
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/23.png b/libkdegames/carddecks/cards-warwick/23.png
new file mode 100644
index 00000000..ab00c341
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/24.png b/libkdegames/carddecks/cards-warwick/24.png
new file mode 100644
index 00000000..eeb73bdd
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/25.png b/libkdegames/carddecks/cards-warwick/25.png
new file mode 100644
index 00000000..25715b2f
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/26.png b/libkdegames/carddecks/cards-warwick/26.png
new file mode 100644
index 00000000..e248ded1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/27.png b/libkdegames/carddecks/cards-warwick/27.png
new file mode 100644
index 00000000..1bb2ab90
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/28.png b/libkdegames/carddecks/cards-warwick/28.png
new file mode 100644
index 00000000..33085ffb
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/29.png b/libkdegames/carddecks/cards-warwick/29.png
new file mode 100644
index 00000000..3f15d8d9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/3.png b/libkdegames/carddecks/cards-warwick/3.png
new file mode 100644
index 00000000..7f298774
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/30.png b/libkdegames/carddecks/cards-warwick/30.png
new file mode 100644
index 00000000..ce1f2dd9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/31.png b/libkdegames/carddecks/cards-warwick/31.png
new file mode 100644
index 00000000..fadee1b1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/32.png b/libkdegames/carddecks/cards-warwick/32.png
new file mode 100644
index 00000000..4d4391d2
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/33.png b/libkdegames/carddecks/cards-warwick/33.png
new file mode 100644
index 00000000..7d1a106f
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/34.png b/libkdegames/carddecks/cards-warwick/34.png
new file mode 100644
index 00000000..8d2197e7
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/35.png b/libkdegames/carddecks/cards-warwick/35.png
new file mode 100644
index 00000000..e8f5789d
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/36.png b/libkdegames/carddecks/cards-warwick/36.png
new file mode 100644
index 00000000..32bd75b8
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/37.png b/libkdegames/carddecks/cards-warwick/37.png
new file mode 100644
index 00000000..8a85b206
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/38.png b/libkdegames/carddecks/cards-warwick/38.png
new file mode 100644
index 00000000..edc98fa9
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/39.png b/libkdegames/carddecks/cards-warwick/39.png
new file mode 100644
index 00000000..6ac9b1ab
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/4.png b/libkdegames/carddecks/cards-warwick/4.png
new file mode 100644
index 00000000..ba94daeb
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/40.png b/libkdegames/carddecks/cards-warwick/40.png
new file mode 100644
index 00000000..046f281e
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/41.png b/libkdegames/carddecks/cards-warwick/41.png
new file mode 100644
index 00000000..278f7242
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/42.png b/libkdegames/carddecks/cards-warwick/42.png
new file mode 100644
index 00000000..e0c8857d
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/43.png b/libkdegames/carddecks/cards-warwick/43.png
new file mode 100644
index 00000000..126b5d8e
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/44.png b/libkdegames/carddecks/cards-warwick/44.png
new file mode 100644
index 00000000..220f6e45
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/45.png b/libkdegames/carddecks/cards-warwick/45.png
new file mode 100644
index 00000000..0a5742e1
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/46.png b/libkdegames/carddecks/cards-warwick/46.png
new file mode 100644
index 00000000..f4312a3b
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/47.png b/libkdegames/carddecks/cards-warwick/47.png
new file mode 100644
index 00000000..a4495a2e
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/48.png b/libkdegames/carddecks/cards-warwick/48.png
new file mode 100644
index 00000000..039755f8
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/49.png b/libkdegames/carddecks/cards-warwick/49.png
new file mode 100644
index 00000000..dd7b9779
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/5.png b/libkdegames/carddecks/cards-warwick/5.png
new file mode 100644
index 00000000..d881d1a8
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/50.png b/libkdegames/carddecks/cards-warwick/50.png
new file mode 100644
index 00000000..5840d1a7
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/51.png b/libkdegames/carddecks/cards-warwick/51.png
new file mode 100644
index 00000000..04628627
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/52.png b/libkdegames/carddecks/cards-warwick/52.png
new file mode 100644
index 00000000..07efb60a
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/6.png b/libkdegames/carddecks/cards-warwick/6.png
new file mode 100644
index 00000000..dbe64d0a
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/7.png b/libkdegames/carddecks/cards-warwick/7.png
new file mode 100644
index 00000000..16176ad2
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/8.png b/libkdegames/carddecks/cards-warwick/8.png
new file mode 100644
index 00000000..f62b3441
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/9.png b/libkdegames/carddecks/cards-warwick/9.png
new file mode 100644
index 00000000..a773d309
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-warwick/index.desktop b/libkdegames/carddecks/cards-warwick/index.desktop
new file mode 100644
index 00000000..042b6f1b
--- /dev/null
+++ b/libkdegames/carddecks/cards-warwick/index.desktop
@@ -0,0 +1,126 @@
+[KDE Backdeck]
+Name=Blue Balloon
+Name[af]=Blou Ballon
+Name[ar]=بالون أزرق
+Name[az]=Göy Balon
+Name[be]=Паветраны шар
+Name[bg]=Син балон
+Name[bn]=নীল বেলুন
+Name[br]=Boull glas
+Name[bs]=Plavi balon
+Name[ca]=Pilota blava
+Name[cs]=Modrý balón
+Name[cy]=Balwn Glas
+Name[da]=Blå ballon
+Name[de]=Blauer Ballon
+Name[el]=Μπλε μπαλόνι
+Name[eo]=Blua balono
+Name[es]=Globo azul
+Name[et]=Sinine õhupall
+Name[eu]=Globo urdina
+Name[fi]=Sininen pallo
+Name[fr]=Ballon bleu
+Name[gl]=Globo azul
+Name[he]=בלון כחול
+Name[hi]=ब्लू बलून
+Name[hr]=Plavi balon
+Name[hu]=Kék léggömb
+Name[is]=Blá blaðra
+Name[it]=Pallone blu
+Name[ja]=青風船
+Name[km]=បាល់​ខៀវ
+Name[ko]=파란 풍선
+Name[lt]=Mėlynas balionas
+Name[lv]=Zili baloni
+Name[mk]=Син балон
+Name[mt]=Bużżieqa Blu
+Name[nb]=Blå ballong
+Name[nds]=Blaag Ballon
+Name[ne]=निलो बेलुन
+Name[nl]=Blauwe ballon
+Name[nn]=Blå ballong
+Name[pa]=ਨੀਲਾ ਗੁਬਰਾ
+Name[pl]=Niebieski balon
+Name[pt]=Balão Azul
+Name[pt_BR]=Balão Azul
+Name[ro]=Balon albastru
+Name[ru]=Воздушный шар
+Name[se]=Alit balloŋga
+Name[sk]=Modrý balón
+Name[sl]=Modri balon
+Name[sr]=Плави балон
+Name[sr@Latn]=Plavi balon
+Name[sv]=Blå ballong
+Name[ta]=நீல பலூன்
+Name[tg]=Курраҳои Ҳавоӣ
+Name[th]=ลูกโป่งสีฟ้า - K
+Name[tr]=Mavi Balon
+Name[uk]=Блакитна кулька
+Name[ven]=Baloni la muvhala wa Lutombo
+Name[vi]=Bóng bay xanh
+Name[xh]=Ibhaluni eblowu
+Name[zh_CN]=蓝气球
+Name[zh_TW]=藍色氣球
+Name[zu]=Ibhelunde eliluhlaza
+Preview=11.png
+PySol=false
+Comment=Card set supplied by Warwick Allison
+Comment[af]=Kaart stel verskaf deur Warwick Allison
+Comment[az]=Warwick Allison tərəfindən düzəldilən kart dəstəsi
+Comment[be]=Калода картаў ад Ворвіка Элісана (Warwick Allison)
+Comment[bg]=Колода карти от Warwick Allison
+Comment[bn]=কার্ডের সেট সরবরাহ করেছেন ওয়ারউইক এল্লিসন
+Comment[bs]=Špil je dostavio Warwick Allison
+Comment[ca]=Joc de cartes aportat per Warwick Allison
+Comment[cs]=Sada karet od Warwicka Allisona
+Comment[cy]=Set cerdiau wedi ei ddarparu gan Warwick Allison
+Comment[da]=Kortspil fra Warwick Allison
+Comment[de]=Karten von Warwick Allison
+Comment[el]=Σετ καρτών από τον Warwick Allison
+Comment[eo]=Kartaro donita de Warwick Allison
+Comment[es]=Juego de cartas suministrado por Warwick Allison
+Comment[et]=Warwick Allison'i poolt pakutud kaardipakk
+Comment[eu]=Warwick Allison-ek emandako karta-sorta
+Comment[fa]=مجموعه کارت توسط وارویک آلیسون تهیه شد
+Comment[fi]=Warwick Allison toimittama korttipakka
+Comment[fr]=Jeu de cartes fourni par Warwick Allison
+Comment[gl]=Baralla proporcionada por Warwick Allison
+Comment[he]=ערכת הקלפים סופקה על ידי וורויק אליסון
+Comment[hi]=ताश की गड्डी वारविक एलीसन द्वारा प्रदत्त किया गया
+Comment[hr]=Set karata, poklonio Warwick Allison
+Comment[hu]=Warwick Allison kártyacsomagja
+Comment[is]=Spilastokkur eftir Warwick Allison
+Comment[it]=Mazzo di carte fornito da Warwick Allison
+Comment[ja]=Warwick Allison 作のカードセット
+Comment[km]=បៀរ​ត្រូវ​កំណត់​ផ្ដល់​ដោយ Warwick Allison
+Comment[ko]=Warwick Allison이 만든 카드 셋
+Comment[lt]=Warwick Allison kortų rinkinys
+Comment[lv]=Kāršu komplekts no Warwick Allison
+Comment[mk]=Комплетот карти е обезбеден од Ворвик Алисон (Warwick Allison)
+Comment[mt]=Sett ta' karti mogħti minn Warwick Allison
+Comment[nb]=Kortstokk levert av Warwick Allison
+Comment[nds]=Koorten vun Warwick Allison
+Comment[ne]=वारविक एलिसोनद्वारा वितरण गरिएको कार्ड सेट
+Comment[nl]=Kaartset, geleverd door Warwick Allison
+Comment[nn]=Kortstokk frå Warwick Allison
+Comment[pl]=Zestaw kart dostarczony przez Warwicka Allisona
+Comment[pt]=Baralho de cartas fornecido por Warwick Allison
+Comment[pt_BR]=Jogo de cartas fornecido por Warwick Allison
+Comment[ro]=Set de cărţi de joc de Warwick Allison
+Comment[ru]=Колода карт от Warwick Allison
+Comment[sk]=Balíček kariet od Warwicka Allisona
+Comment[sl]=Nabor kart od Warwicka Allisona
+Comment[sr]=Шпил карата кога је обезбедио Варвик Алисон (Warwick Allison)
+Comment[sr@Latn]=Špil karata koga je obezbedio Varvik Alison (Warwick Allison)
+Comment[sv]=Kortuppsättning tillhandahållen av Warwick Allison
+Comment[ta]=சீட்டுக்கட்டு வில்லியம் எலிசனால் அனுப்பப்பட்டது
+Comment[tg]=Маҷмӯи Кортҳо, ки аз тарафи Warwick Allison пешниҳод шудаанд
+Comment[th]=ชุดไพ่สนับสนุนโดย Warwick Allison
+Comment[tr]=Kart kümeleri Warwick Allison tarafından sağlanmıştır
+Comment[uk]=Набір карт від Warwick Allison
+Comment[ven]=Garata yo diswa nga Warwick Allison
+Comment[vi]=BềEthẻ được cung cấp bởi Warwick Allison
+Comment[xh]=Imfano nye yamakhadi inikwe ngu Warwick Allison
+Comment[zh_CN]=牌面由 Warwick Allison 提供
+Comment[zh_TW]=牌局由 Warwick Allison 提供
+Comment[zu]=Iqoqo lamakhadi linikelwe ngu-Warwick Allison
diff --git a/libkdegames/carddecks/cards-xskat-french/1.png b/libkdegames/carddecks/cards-xskat-french/1.png
new file mode 100644
index 00000000..fdc71a26
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/10.png b/libkdegames/carddecks/cards-xskat-french/10.png
new file mode 100644
index 00000000..9dede1c7
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/11.png b/libkdegames/carddecks/cards-xskat-french/11.png
new file mode 100644
index 00000000..4c1dbf2a
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/12.png b/libkdegames/carddecks/cards-xskat-french/12.png
new file mode 100644
index 00000000..e34fc6bb
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/13.png b/libkdegames/carddecks/cards-xskat-french/13.png
new file mode 100644
index 00000000..842935ca
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/14.png b/libkdegames/carddecks/cards-xskat-french/14.png
new file mode 100644
index 00000000..e87706aa
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/15.png b/libkdegames/carddecks/cards-xskat-french/15.png
new file mode 100644
index 00000000..28d82c71
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/16.png b/libkdegames/carddecks/cards-xskat-french/16.png
new file mode 100644
index 00000000..4920f2f4
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/17.png b/libkdegames/carddecks/cards-xskat-french/17.png
new file mode 100644
index 00000000..27658c7c
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/18.png b/libkdegames/carddecks/cards-xskat-french/18.png
new file mode 100644
index 00000000..ea34c973
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/19.png b/libkdegames/carddecks/cards-xskat-french/19.png
new file mode 100644
index 00000000..80a0c22b
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/2.png b/libkdegames/carddecks/cards-xskat-french/2.png
new file mode 100644
index 00000000..de465ef6
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/20.png b/libkdegames/carddecks/cards-xskat-french/20.png
new file mode 100644
index 00000000..4a0aca66
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/21.png b/libkdegames/carddecks/cards-xskat-french/21.png
new file mode 100644
index 00000000..b7c6027f
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/22.png b/libkdegames/carddecks/cards-xskat-french/22.png
new file mode 100644
index 00000000..ae77e460
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/23.png b/libkdegames/carddecks/cards-xskat-french/23.png
new file mode 100644
index 00000000..967385fc
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/24.png b/libkdegames/carddecks/cards-xskat-french/24.png
new file mode 100644
index 00000000..e8b84baf
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/25.png b/libkdegames/carddecks/cards-xskat-french/25.png
new file mode 100644
index 00000000..f933ca25
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/26.png b/libkdegames/carddecks/cards-xskat-french/26.png
new file mode 100644
index 00000000..ab981154
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/27.png b/libkdegames/carddecks/cards-xskat-french/27.png
new file mode 100644
index 00000000..f34477ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/28.png b/libkdegames/carddecks/cards-xskat-french/28.png
new file mode 100644
index 00000000..0d1d71ff
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/29.png b/libkdegames/carddecks/cards-xskat-french/29.png
new file mode 100644
index 00000000..80e86965
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/3.png b/libkdegames/carddecks/cards-xskat-french/3.png
new file mode 100644
index 00000000..e4cc9d63
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/30.png b/libkdegames/carddecks/cards-xskat-french/30.png
new file mode 100644
index 00000000..a935d55e
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/31.png b/libkdegames/carddecks/cards-xskat-french/31.png
new file mode 100644
index 00000000..6c8d2883
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/32.png b/libkdegames/carddecks/cards-xskat-french/32.png
new file mode 100644
index 00000000..5af0268b
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/33.png b/libkdegames/carddecks/cards-xskat-french/33.png
new file mode 100644
index 00000000..7bd0fbed
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/34.png b/libkdegames/carddecks/cards-xskat-french/34.png
new file mode 100644
index 00000000..dc8bb29f
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/35.png b/libkdegames/carddecks/cards-xskat-french/35.png
new file mode 100644
index 00000000..7eb3f06f
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/36.png b/libkdegames/carddecks/cards-xskat-french/36.png
new file mode 100644
index 00000000..01902b33
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/37.png b/libkdegames/carddecks/cards-xskat-french/37.png
new file mode 100644
index 00000000..80599469
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/38.png b/libkdegames/carddecks/cards-xskat-french/38.png
new file mode 100644
index 00000000..fb4a37e4
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/39.png b/libkdegames/carddecks/cards-xskat-french/39.png
new file mode 100644
index 00000000..01fe85ae
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/4.png b/libkdegames/carddecks/cards-xskat-french/4.png
new file mode 100644
index 00000000..dff28353
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/40.png b/libkdegames/carddecks/cards-xskat-french/40.png
new file mode 100644
index 00000000..a9e28170
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/41.png b/libkdegames/carddecks/cards-xskat-french/41.png
new file mode 100644
index 00000000..66e69166
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/42.png b/libkdegames/carddecks/cards-xskat-french/42.png
new file mode 100644
index 00000000..92e21830
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/43.png b/libkdegames/carddecks/cards-xskat-french/43.png
new file mode 100644
index 00000000..b39a4afa
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/44.png b/libkdegames/carddecks/cards-xskat-french/44.png
new file mode 100644
index 00000000..94bf0fc7
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/45.png b/libkdegames/carddecks/cards-xskat-french/45.png
new file mode 100644
index 00000000..bbdb0ad6
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/46.png b/libkdegames/carddecks/cards-xskat-french/46.png
new file mode 100644
index 00000000..ed610b6a
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/47.png b/libkdegames/carddecks/cards-xskat-french/47.png
new file mode 100644
index 00000000..2688622a
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/48.png b/libkdegames/carddecks/cards-xskat-french/48.png
new file mode 100644
index 00000000..0dc1d52e
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/49.png b/libkdegames/carddecks/cards-xskat-french/49.png
new file mode 100644
index 00000000..4499c0fd
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/5.png b/libkdegames/carddecks/cards-xskat-french/5.png
new file mode 100644
index 00000000..702b65bc
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/50.png b/libkdegames/carddecks/cards-xskat-french/50.png
new file mode 100644
index 00000000..9b8b6619
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/51.png b/libkdegames/carddecks/cards-xskat-french/51.png
new file mode 100644
index 00000000..1edf32d5
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/52.png b/libkdegames/carddecks/cards-xskat-french/52.png
new file mode 100644
index 00000000..0fe25caa
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/6.png b/libkdegames/carddecks/cards-xskat-french/6.png
new file mode 100644
index 00000000..642225b1
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/7.png b/libkdegames/carddecks/cards-xskat-french/7.png
new file mode 100644
index 00000000..a16236b0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/8.png b/libkdegames/carddecks/cards-xskat-french/8.png
new file mode 100644
index 00000000..3cb261c4
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/9.png b/libkdegames/carddecks/cards-xskat-french/9.png
new file mode 100644
index 00000000..2b1d9c43
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-french/COPYRIGHT b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT
new file mode 100644
index 00000000..dc217dc3
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT
@@ -0,0 +1,8 @@
+This PySol cardset was adapted from the game XSkat 4.0
+
+Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de)
+
+This cardset is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-xskat-french/index.desktop b/libkdegames/carddecks/cards-xskat-french/index.desktop
new file mode 100644
index 00000000..3dec4a0c
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-french/index.desktop
@@ -0,0 +1,55 @@
+[KDE Backdeck]
+Name=XSkat French
+Name[be]=Французскі XSkat
+Name[bg]=Френски модел
+Name[bn]=ফরাসি এক্স-স্কাট
+Name[br]=XSkat gallek
+Name[bs]=XSkat Francuski
+Name[ca]=XSkat francès
+Name[cs]=Francouzský XSkat
+Name[cy]=XSkat Ffrangeg
+Name[da]=XSkat-fransk
+Name[de]=XSkat Französisch
+Name[el]=XSkat Γαλλικό
+Name[eo]=XSkat Franca
+Name[es]=XSkat francés
+Name[et]=XSkat (Prantsuse)
+Name[eu]=XSkat Frantsesa
+Name[fa]=XSkat فرانسوی
+Name[fi]=XSkat Ranska
+Name[fr]=XSkat français
+Name[he]=XSkat צרפתי
+Name[hr]=Francuski XSkat
+Name[hu]=FRancia XSkat
+Name[is]=XSkat á frönsku
+Name[it]=XSkat francese
+Name[ja]=XSkat フランス語
+Name[km]=XSkat បារាំង
+Name[ko]=프랑스 XSkat
+Name[lt]=XSkat Prancūziškai
+Name[lv]=XSkat Franču
+Name[mk]=Француски XSkat
+Name[nb]=Fransk XSkat
+Name[nds]=XSkat (Franzöösch)
+Name[ne]=एक्स स्क्याट फ्रेन्च
+Name[nl]=XSkat Frans
+Name[nn]=Fransk XSkat
+Name[pa]=XSkat ਫਰੈਂਚ
+Name[pl]=Francuski skat
+Name[pt]=XSkat Francês
+Name[pt_BR]=XSkat Francês
+Name[ru]=Французский XSkat
+Name[se]=Fránskkalaš XSkat
+Name[sk]=XSkat francúzsky
+Name[sl]=Francoski XSkat
+Name[sr]=XSkat француски
+Name[sr@Latn]=XSkat francuski
+Name[sv]=Fransk X-skat
+Name[ta]=XSkat ஃபிரஞ்ச்
+Name[tg]=XSkat Фаронсавӣ
+Name[tr]=XSkat Fransızca
+Name[uk]=Французький XSkat
+Name[zh_CN]=XSkat 法语
+Name[zh_TW]=XSkat 法語
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/cards-xskat-german/1.png b/libkdegames/carddecks/cards-xskat-german/1.png
new file mode 100644
index 00000000..e679884b
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/1.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/10.png b/libkdegames/carddecks/cards-xskat-german/10.png
new file mode 100644
index 00000000..c9590dbb
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/10.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/11.png b/libkdegames/carddecks/cards-xskat-german/11.png
new file mode 100644
index 00000000..d1c6f500
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/11.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/12.png b/libkdegames/carddecks/cards-xskat-german/12.png
new file mode 100644
index 00000000..5c92d83a
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/12.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/13.png b/libkdegames/carddecks/cards-xskat-german/13.png
new file mode 100644
index 00000000..d68f0c00
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/13.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/14.png b/libkdegames/carddecks/cards-xskat-german/14.png
new file mode 100644
index 00000000..324e3756
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/14.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/15.png b/libkdegames/carddecks/cards-xskat-german/15.png
new file mode 100644
index 00000000..9a516e30
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/15.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/16.png b/libkdegames/carddecks/cards-xskat-german/16.png
new file mode 100644
index 00000000..9d9dec03
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/16.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/17.png b/libkdegames/carddecks/cards-xskat-german/17.png
new file mode 100644
index 00000000..5dc10fa2
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/17.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/18.png b/libkdegames/carddecks/cards-xskat-german/18.png
new file mode 100644
index 00000000..90562629
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/18.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/19.png b/libkdegames/carddecks/cards-xskat-german/19.png
new file mode 100644
index 00000000..7e4552ab
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/19.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/2.png b/libkdegames/carddecks/cards-xskat-german/2.png
new file mode 100644
index 00000000..1c1f8118
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/2.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/20.png b/libkdegames/carddecks/cards-xskat-german/20.png
new file mode 100644
index 00000000..a15534b6
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/20.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/21.png b/libkdegames/carddecks/cards-xskat-german/21.png
new file mode 100644
index 00000000..7aca79e7
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/21.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/22.png b/libkdegames/carddecks/cards-xskat-german/22.png
new file mode 100644
index 00000000..b487a1c8
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/22.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/23.png b/libkdegames/carddecks/cards-xskat-german/23.png
new file mode 100644
index 00000000..761d3bfe
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/23.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/24.png b/libkdegames/carddecks/cards-xskat-german/24.png
new file mode 100644
index 00000000..537e590c
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/24.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/25.png b/libkdegames/carddecks/cards-xskat-german/25.png
new file mode 100644
index 00000000..09567171
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/25.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/26.png b/libkdegames/carddecks/cards-xskat-german/26.png
new file mode 100644
index 00000000..761fc42d
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/26.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/27.png b/libkdegames/carddecks/cards-xskat-german/27.png
new file mode 100644
index 00000000..05083aa2
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/27.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/28.png b/libkdegames/carddecks/cards-xskat-german/28.png
new file mode 100644
index 00000000..e885a908
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/28.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/29.png b/libkdegames/carddecks/cards-xskat-german/29.png
new file mode 100644
index 00000000..5b7244a0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/29.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/3.png b/libkdegames/carddecks/cards-xskat-german/3.png
new file mode 100644
index 00000000..978e63c0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/3.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/30.png b/libkdegames/carddecks/cards-xskat-german/30.png
new file mode 100644
index 00000000..3f8b03ef
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/30.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/31.png b/libkdegames/carddecks/cards-xskat-german/31.png
new file mode 100644
index 00000000..9ab830ce
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/31.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/32.png b/libkdegames/carddecks/cards-xskat-german/32.png
new file mode 100644
index 00000000..b87d68d0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/32.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/33.png b/libkdegames/carddecks/cards-xskat-german/33.png
new file mode 100644
index 00000000..078abf39
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/33.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/34.png b/libkdegames/carddecks/cards-xskat-german/34.png
new file mode 100644
index 00000000..94c5297f
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/34.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/35.png b/libkdegames/carddecks/cards-xskat-german/35.png
new file mode 100644
index 00000000..241ba5dc
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/35.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/36.png b/libkdegames/carddecks/cards-xskat-german/36.png
new file mode 100644
index 00000000..339a155a
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/36.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/37.png b/libkdegames/carddecks/cards-xskat-german/37.png
new file mode 100644
index 00000000..106023f4
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/37.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/38.png b/libkdegames/carddecks/cards-xskat-german/38.png
new file mode 100644
index 00000000..848f9696
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/38.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/39.png b/libkdegames/carddecks/cards-xskat-german/39.png
new file mode 100644
index 00000000..43eaba16
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/39.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/4.png b/libkdegames/carddecks/cards-xskat-german/4.png
new file mode 100644
index 00000000..d69848a0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/4.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/40.png b/libkdegames/carddecks/cards-xskat-german/40.png
new file mode 100644
index 00000000..87619dce
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/40.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/41.png b/libkdegames/carddecks/cards-xskat-german/41.png
new file mode 100644
index 00000000..6b09c88c
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/41.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/42.png b/libkdegames/carddecks/cards-xskat-german/42.png
new file mode 100644
index 00000000..04a1993b
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/42.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/43.png b/libkdegames/carddecks/cards-xskat-german/43.png
new file mode 100644
index 00000000..d5b92e29
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/43.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/44.png b/libkdegames/carddecks/cards-xskat-german/44.png
new file mode 100644
index 00000000..6cf2e09b
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/44.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/45.png b/libkdegames/carddecks/cards-xskat-german/45.png
new file mode 100644
index 00000000..b6a6a2a7
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/45.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/46.png b/libkdegames/carddecks/cards-xskat-german/46.png
new file mode 100644
index 00000000..05f686ab
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/46.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/47.png b/libkdegames/carddecks/cards-xskat-german/47.png
new file mode 100644
index 00000000..f31c89f3
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/47.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/48.png b/libkdegames/carddecks/cards-xskat-german/48.png
new file mode 100644
index 00000000..a8e1c51e
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/48.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/49.png b/libkdegames/carddecks/cards-xskat-german/49.png
new file mode 100644
index 00000000..2a473ac3
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/49.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/5.png b/libkdegames/carddecks/cards-xskat-german/5.png
new file mode 100644
index 00000000..66a4b568
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/5.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/50.png b/libkdegames/carddecks/cards-xskat-german/50.png
new file mode 100644
index 00000000..3a7d6ac0
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/50.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/51.png b/libkdegames/carddecks/cards-xskat-german/51.png
new file mode 100644
index 00000000..09e25a6c
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/51.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/52.png b/libkdegames/carddecks/cards-xskat-german/52.png
new file mode 100644
index 00000000..e7d74ae4
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/52.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/6.png b/libkdegames/carddecks/cards-xskat-german/6.png
new file mode 100644
index 00000000..e4b1d7de
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/6.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/7.png b/libkdegames/carddecks/cards-xskat-german/7.png
new file mode 100644
index 00000000..09e99d70
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/7.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/8.png b/libkdegames/carddecks/cards-xskat-german/8.png
new file mode 100644
index 00000000..58fdc56e
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/8.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/9.png b/libkdegames/carddecks/cards-xskat-german/9.png
new file mode 100644
index 00000000..b55ee847
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/9.png
Binary files differ
diff --git a/libkdegames/carddecks/cards-xskat-german/COPYRIGHT b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT
new file mode 100644
index 00000000..dc217dc3
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT
@@ -0,0 +1,8 @@
+This PySol cardset was adapted from the game XSkat 4.0
+
+Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de)
+
+This cardset is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
diff --git a/libkdegames/carddecks/cards-xskat-german/index.desktop b/libkdegames/carddecks/cards-xskat-german/index.desktop
new file mode 100644
index 00000000..3f19d526
--- /dev/null
+++ b/libkdegames/carddecks/cards-xskat-german/index.desktop
@@ -0,0 +1,55 @@
+[KDE Backdeck]
+Name=XSkat German
+Name[be]=Нямецкі XSkat
+Name[bg]=Немски модел
+Name[bn]=জর্মন এক্স-স্কাট
+Name[br]=XSkat alamanek
+Name[bs]=XSkat Njemački
+Name[ca]=XSkat alemany
+Name[cs]=Německý XSkat
+Name[cy]=XSkat Almaeneg
+Name[da]=XSkat-tysk
+Name[de]=XSkat Deutsch
+Name[el]=XSkat Γερμανικό
+Name[eo]=XSkat Germana
+Name[es]=XSkat alemán
+Name[et]=XSkat (Saksa)
+Name[eu]=XSkat alemaniera
+Name[fa]=آلمانی XSkat
+Name[fi]=XSkat Saksa
+Name[fr]=XSkat allemand
+Name[he]=XSkat גרמני
+Name[hr]=Njemački XSkat
+Name[hu]=Német XSkat
+Name[is]=XSkat á þýsku
+Name[it]=XSkat tedesco
+Name[ja]=XSkat ドイツ語
+Name[km]=XSkat អាល្លឺម៉ង់
+Name[ko]=독일 XSkat
+Name[lt]=XSkat Vokiškai
+Name[lv]=XSkat Vācu
+Name[mk]=Германски XSkat
+Name[nb]=Tysk XSkat
+Name[nds]=XSkat (Düütsch)
+Name[ne]=एक्स स्क्याट जर्मन
+Name[nl]=XSkat Duits
+Name[nn]=Tysk XSkat
+Name[pa]=XSkat ਜਰਮਨ
+Name[pl]=Niemiecki skat
+Name[pt]=XSkat Alemã
+Name[pt_BR]=XSkat Alemão
+Name[ru]=Немецкий XSkat
+Name[se]=Duiskkalaš XSkat
+Name[sk]=XScat nemecký
+Name[sl]=Nemški XSkat
+Name[sr]=XSkat немачки
+Name[sr@Latn]=XSkat nemački
+Name[sv]=Tysk X-skat
+Name[ta]=XSkat ஜெர்மன்
+Name[tg]=XSkat Олмонӣ
+Name[tr]=XSkat Almanca
+Name[uk]=Німецький XSkat
+Name[zh_CN]=XSkat 德语
+Name[zh_TW]=XSkat 德語
+Preview=11.png
+PySol=yes
diff --git a/libkdegames/carddecks/convertpysols b/libkdegames/carddecks/convertpysols
new file mode 100755
index 00000000..5ed10b25
--- /dev/null
+++ b/libkdegames/carddecks/convertpysols
@@ -0,0 +1,69 @@
+#! /bin/sh
+
+cd $1
+
+convert -format png -geometry "72x96" 01c.gif 1.png
+convert -format png -geometry "72x96" 01s.gif 2.png
+convert -format png -geometry "72x96" 01h.gif 3.png
+convert -format png -geometry "72x96" 01d.gif 4.png
+
+convert -format png -geometry "72x96" 13c.gif 5.png
+convert -format png -geometry "72x96" 13s.gif 6.png
+convert -format png -geometry "72x96" 13h.gif 7.png
+convert -format png -geometry "72x96" 13d.gif 8.png
+
+convert -format png -geometry "72x96" 12c.gif 9.png
+convert -format png -geometry "72x96" 12s.gif 10.png
+convert -format png -geometry "72x96" 12h.gif 11.png
+convert -format png -geometry "72x96" 12d.gif 12.png
+
+convert -format png -geometry "72x96" 11c.gif 13.png
+convert -format png -geometry "72x96" 11s.gif 14.png
+convert -format png -geometry "72x96" 11h.gif 15.png
+convert -format png -geometry "72x96" 11d.gif 16.png
+
+convert -format png -geometry "72x96" 10c.gif 17.png
+convert -format png -geometry "72x96" 10s.gif 18.png
+convert -format png -geometry "72x96" 10h.gif 19.png
+convert -format png -geometry "72x96" 10d.gif 20.png
+
+convert -format png -geometry "72x96" 09c.gif 21.png
+convert -format png -geometry "72x96" 09s.gif 22.png
+convert -format png -geometry "72x96" 09h.gif 23.png
+convert -format png -geometry "72x96" 09d.gif 24.png
+
+convert -format png -geometry "72x96" 08c.gif 25.png
+convert -format png -geometry "72x96" 08s.gif 26.png
+convert -format png -geometry "72x96" 08h.gif 27.png
+convert -format png -geometry "72x96" 08d.gif 28.png
+
+convert -format png -geometry "72x96" 07c.gif 29.png
+convert -format png -geometry "72x96" 07s.gif 30.png
+convert -format png -geometry "72x96" 07h.gif 31.png
+convert -format png -geometry "72x96" 07d.gif 32.png
+
+convert -format png -geometry "72x96" 06c.gif 33.png
+convert -format png -geometry "72x96" 06s.gif 34.png
+convert -format png -geometry "72x96" 06h.gif 35.png
+convert -format png -geometry "72x96" 06d.gif 36.png
+
+convert -format png -geometry "72x96" 05c.gif 37.png
+convert -format png -geometry "72x96" 05s.gif 38.png
+convert -format png -geometry "72x96" 05h.gif 39.png
+convert -format png -geometry "72x96" 05d.gif 40.png
+
+convert -format png -geometry "72x96" 04c.gif 41.png
+convert -format png -geometry "72x96" 04s.gif 42.png
+convert -format png -geometry "72x96" 04h.gif 43.png
+convert -format png -geometry "72x96" 04d.gif 44.png
+
+convert -format png -geometry "72x96" 03c.gif 45.png
+convert -format png -geometry "72x96" 03s.gif 46.png
+convert -format png -geometry "72x96" 03h.gif 47.png
+convert -format png -geometry "72x96" 03d.gif 48.png
+
+convert -format png -geometry "72x96" 02c.gif 49.png
+convert -format png -geometry "72x96" 02s.gif 50.png
+convert -format png -geometry "72x96" 02h.gif 51.png
+convert -format png -geometry "72x96" 02d.gif 52.png
+
diff --git a/libkdegames/carddecks/decks/deck0.desktop b/libkdegames/carddecks/decks/deck0.desktop
new file mode 100644
index 00000000..0388ea4b
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck0.desktop
@@ -0,0 +1,113 @@
+[KDE Cards]
+Name=Technics
+Name[az]=Texnik
+Name[be]=Тэхніка
+Name[bg]=Техника
+Name[bn]=টেকনিক্স
+Name[bs]=Tehnički
+Name[ca]=Tècniques
+Name[cs]=Technika
+Name[eo]=Tekniko
+Name[es]=Técnicas
+Name[et]=Tehnika
+Name[eu]=Teknikoa
+Name[fi]=Tekniikka
+Name[fr]=Technique
+Name[gl]=Técnicas
+Name[hi]=टेक्निक्स
+Name[hr]=Tehnika
+Name[hu]=Technikai
+Name[id]=Teknik
+Name[is]=Tækni
+Name[it]=Tecnico
+Name[km]=វិធីសាស្ត្រ
+Name[lt]=Technika
+Name[lv]=Tehnika
+Name[mk]=Техника
+Name[mt]=Tekniċi
+Name[nb]=Teknisk
+Name[nds]=Technik
+Name[ne]=उपाय
+Name[nl]=Technisch
+Name[nn]=Teknisk
+Name[pt]=Técnico
+Name[pt_BR]=Técnicas
+Name[ro]=Tehnic
+Name[ru]=Шестерёнки
+Name[se]=Teknihkalaš
+Name[sk]=Technické
+Name[sl]=Tehnika
+Name[sr]=Техникс
+Name[sr@Latn]=Tehniks
+Name[sv]=Teknik
+Name[ta]=நுணுக்கங்கள்
+Name[tg]=Шомгонаҳо
+Name[th]=เทคนิค
+Name[tr]=Teknik
+Name[uk]=Шестерні
+Name[ven]=Madaela
+Name[vi]=Kĩ thuật
+Name[xh]=iindlela zokudlala
+Name[zh_CN]=工艺
+Name[zh_TW]=工藝
+Comment=Standard KDE card deck
+Comment[af]=Standaard Kde kaart pak
+Comment[az]=Standart KDE kart dəstəsi
+Comment[be]=Стандартная калода картаў KDE
+Comment[bg]=Стандартна колода карти за KDE
+Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট
+Comment[bs]=Standardni KDE špil karata
+Comment[ca]=Joc de cartes estàndard del KDE
+Comment[cs]=Standardní sada karet KDE
+Comment[cy]=Set cerdiau safonol KDE
+Comment[da]=Standard KDE-kortspil
+Comment[de]=Standardmäßige KDE-Karten
+Comment[el]=Προκαθορισμένο σύνολο τράπουλας του KDE
+Comment[eo]=Normala KDEa kartaro
+Comment[es]=Baraja de cartas estándar de KDE
+Comment[et]=Standardne KDE kaardipakk
+Comment[eu]=KDE-ren karta-sorta
+Comment[fa]=دسته کارت استاندارد KDE
+Comment[fi]=Normaali KDE:n korttipakka
+Comment[fr]=Jeu de cartes standard de KDE
+Comment[gl]=Baralla de cartas estándar de KDE
+Comment[he]=חפיסת הקלפים הסטנדרטית של KDE
+Comment[hi]=मानक केडीई ताश गड्डी
+Comment[hr]=Standardni KDE komplet karata
+Comment[hu]=Standard KDE kártyacsomag
+Comment[is]=Venjulegi KDE spilastokkurinn
+Comment[it]=Mazzo di carte standard di KDE
+Comment[ja]=KDE 標準カードデッキ
+Comment[km]=ហូ​បៀ KDE ខ្នាត​គំរូ
+Comment[ko]=표준 KDE 카드 모음
+Comment[lt]=Standartinė KDE kortų kaladė
+Comment[lv]=Standarta KDE kāršu galds
+Comment[mk]=Стандарден шпил карти на KDE
+Comment[mt]=Mazz karti standard tal-KDE
+Comment[nb]=Standard KDE-kortstokk
+Comment[nds]=KDE-Standardkoorten
+Comment[ne]=मानक केडीई कार्ड डेक
+Comment[nl]=Standaard KDE-kaartrug
+Comment[nn]=Standard KDE-kortstokk
+Comment[pa]=ਮਿਆਰੀ KDE ਤਾਸ਼ ਪੱਤੇ
+Comment[pl]=Standardowy zestaw kart KDE
+Comment[pt]=Baralho de cartas por omissão do KDE
+Comment[pt_BR]=Baralho padrão do KDE
+Comment[ro]=Set de cărţi de joc KDE standard
+Comment[ru]=Стандартная колода карт KDE
+Comment[sk]=Štandardný balíček kariet KDE
+Comment[sl]=Standardni komplet kart za KDE
+Comment[sr]=Стандардни KDE-ов шпил карата
+Comment[sr@Latn]=Standardni KDE-ov špil karata
+Comment[sv]=Förvald KDE-kortlek
+Comment[ta]=நிலையான கேடிஇ சீட்டின் மேல் தளம்
+Comment[tg]=Маҷмӯи кортҳои муқаррарии KDE
+Comment[th]=ชุดไพ่มาตรฐานของ KDE
+Comment[tr]=Standart KDE kart destesi
+Comment[uk]=Типовий малюнок карт KDE
+Comment[ven]=Tshidzhumba tsha magarata tsha murole wa KDE
+Comment[vi]=KDE card deck chuẩn
+Comment[xh]=Umgangatho womphezulu wamakhadi eKDE
+Comment[zh_CN]=标准 KDE 牌垛
+Comment[zh_TW]=標準 KDE 紙牌花色
+Comment[zu]=Isigaxa samakhadi esilinganisiwe we-KDE
diff --git a/libkdegames/carddecks/decks/deck0.png b/libkdegames/carddecks/decks/deck0.png
new file mode 100644
index 00000000..0a877a51
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck0.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck1.desktop b/libkdegames/carddecks/decks/deck1.desktop
new file mode 100644
index 00000000..0a8c2cf7
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck1.desktop
@@ -0,0 +1,56 @@
+[KDE Cards]
+Name=Fairy
+Name[af]=Fee
+Name[az]=Pəri
+Name[be]=Фея
+Name[bg]=Фея
+Name[bn]=পরী
+Name[ca]=Fada
+Name[cs]=Víla
+Name[cy]=Tylwyth Teg
+Name[da]=Fe
+Name[de]=Elfe
+Name[el]=Νεράιδα
+Name[eo]=Elfo
+Name[es]=Hada
+Name[et]=Haldjas
+Name[eu]=Maitagarria
+Name[fa]=جن و پری
+Name[fi]=Keijukainen
+Name[fr]=Féérie
+Name[gl]=Fada
+Name[hi]=परी
+Name[hr]=Vila
+Name[hu]=Tündéres
+Name[is]=Álfur
+Name[it]=Fata
+Name[km]=ទេវតា
+Name[ko]=요정
+Name[lt]=Fėja
+Name[lv]=Pasaku
+Name[mk]=Самовила
+Name[mt]=Għafrid
+Name[nb]=Fe
+Name[nds]=Fee
+Name[ne]=अप्सरा
+Name[nl]=Fee
+Name[nn]=Fe
+Name[pl]=Wróżkarski
+Name[pt]=Fada
+Name[pt_BR]=Mágico
+Name[ro]=Basm
+Name[ru]=Фея
+Name[sr]=Вила
+Name[sr@Latn]=Vila
+Name[sv]=Älva
+Name[ta]=தேவதை
+Name[tg]=Парӣ
+Name[tr]=Peri
+Name[uk]=Чарівний
+Name[uz]=Pari
+Name[uz@cyrillic]=Пари
+Name[ven]=Zwi a pfesesea
+Name[vi]=Xinh đẹp
+Name[zh_CN]=仙女
+Name[zh_TW]=仙女
+Name[zu]=Okunomlingo
diff --git a/libkdegames/carddecks/decks/deck1.png b/libkdegames/carddecks/decks/deck1.png
new file mode 100644
index 00000000..247875a2
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck1.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck10.desktop b/libkdegames/carddecks/decks/deck10.desktop
new file mode 100644
index 00000000..5e871ede
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck10.desktop
@@ -0,0 +1,66 @@
+[KDE Cards]
+Name=Classic Blue
+Name[af]=Klasieke Blou
+Name[ar]=أزرق كلاسيكي
+Name[az]=Klassik Göy
+Name[be]=Класічны сіні
+Name[bg]=Класическо синьо
+Name[bn]=ক্লাসিক নীল
+Name[bs]=Klasični plavi
+Name[ca]=Blau clàssic
+Name[cs]=Klasická modrá
+Name[cy]=Glas Clasurol
+Name[da]=Klassisk blå
+Name[de]=Klassisches Blau
+Name[el]=Κλασσικό μπλε
+Name[eo]=Klasika bluo
+Name[es]=Azul clásico
+Name[et]=Klassikaline sinine
+Name[eu]=Urdin klasikoa
+Name[fa]=آبی کلاسیک‌
+Name[fi]=Klassinen sininen
+Name[fr]=Bleu classique
+Name[gl]=Azul clásico
+Name[he]=כחול קלסי
+Name[hi]=आदर्श नीला
+Name[hr]=Klasično plava
+Name[hu]=Klasszikus kék
+Name[id]=Biru klasik
+Name[is]=Klassískur blár
+Name[it]=Blu classico
+Name[ja]=クラシックブルー
+Name[km]=ខៀវ​ក្លាស៊ិក
+Name[ko]=고전 파란색
+Name[lt]=Klasikinė mėlyna
+Name[lv]=Klasiski zils
+Name[mk]=Класично сино
+Name[mt]=Blu Klassiku
+Name[nb]=Klassisk Blå
+Name[nds]=Klass'sch Blaag
+Name[ne]=उत्कृष्ट निलो
+Name[nl]=Klassiek blauw
+Name[nn]=Klassisk blå
+Name[pa]=ਟਕਸਾਲੀ ਨੀਲੇ
+Name[pl]=Klasyczny niebieski
+Name[pt]=Azul Clássico
+Name[pt_BR]=Azul Clássico
+Name[ro]=Albastru clasic
+Name[ru]=Классический синий
+Name[se]=Klassihkalaš alit
+Name[sk]=Klasické modré
+Name[sl]=Klasična modra
+Name[sr]=Класични плави
+Name[sr@Latn]=Klasični plavi
+Name[sv]=Klassisk blå
+Name[ta]=சிறந்த நீலம்
+Name[tg]=Кабуди Классикӣ
+Name[th]=ฟ้าคลาสสิค
+Name[tr]=Klasik Mavi
+Name[uk]=Блакитний
+Name[ven]=Muvhala wa Lutombo wa Maimo
+Name[vi]=Màu xanh cềEđiển
+Name[wa]=Bleu classike
+Name[xh]=Umbala oblowu omdala
+Name[zh_CN]=经典蓝
+Name[zh_TW]=古典藍
+Name[zu]=Ubuluhlaza obuhle
diff --git a/libkdegames/carddecks/decks/deck10.png b/libkdegames/carddecks/decks/deck10.png
new file mode 100644
index 00000000..c84dcf73
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck10.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck11.desktop b/libkdegames/carddecks/decks/deck11.desktop
new file mode 100644
index 00000000..8d0fbb44
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck11.desktop
@@ -0,0 +1,66 @@
+[KDE Cards]
+Name=Classic Red
+Name[af]=Klasieke Rooi
+Name[ar]=أحمر كلاسيكي
+Name[az]=Klassik Qırmızı
+Name[be]=Класічны чырвоны
+Name[bg]=Класическо червено
+Name[bn]=ক্লাসিক লাল
+Name[bs]=Klasični crveni
+Name[ca]=Vermell clàssic
+Name[cs]=Klasická červená
+Name[cy]=Coch Clasurol
+Name[da]=Klassisk Rød
+Name[de]=Klassisches Rot
+Name[el]=Κλασσικό κόκκινο
+Name[eo]=Klasika ruĝo
+Name[es]=Rojo clásico
+Name[et]=Klassikaline punane
+Name[eu]=Gorri klasikoa
+Name[fa]=قرمز کلاسیک
+Name[fi]=Klassinen punainen
+Name[fr]=Rouge classique
+Name[gl]=Vermello clásico
+Name[he]=אדום קלסי
+Name[hi]=आदर्श लाल
+Name[hr]=Klasično crvena
+Name[hu]=klasszikus vörös
+Name[id]=Merah klasik
+Name[is]=Klassískur rauður
+Name[it]=Rosso classico
+Name[ja]=クラシックレッド
+Name[km]=ក្រហម​ក្លាស៊ិក
+Name[ko]=고전 빨간색
+Name[lt]=Klasikinė raudona
+Name[lv]=Klasiski sarkans
+Name[mk]=Класично црвено
+Name[mt]=Aħmar Klassiku
+Name[nb]=Klassisk Rød
+Name[nds]=Klass'sch Root
+Name[ne]=उत्कृष्ट रातो
+Name[nl]=Klassiek rood
+Name[nn]=Klassisk raud
+Name[pa]=ਟਕਸਾਲੀ ਲਾਲ
+Name[pl]=Klasyczny czerwony
+Name[pt]=Vermelho Clássico
+Name[pt_BR]=Vermelho Clássico
+Name[ro]=Roşu clasic
+Name[ru]=Классический красный
+Name[se]=Klassihkalaš ruoksat
+Name[sk]=Klasické červené
+Name[sl]=Klasična rdeča
+Name[sr]=Класични црвени
+Name[sr@Latn]=Klasični crveni
+Name[sv]=Klassisk röd
+Name[ta]= சிறந்த சிகப்பு
+Name[tg]=Сурхи Классикӣ
+Name[th]=แดงคลาสสิค
+Name[tr]=Klasik Kırmızı
+Name[uk]=Червоний
+Name[ven]=Muvhala Mutshuku wa Maimo
+Name[vi]=ĐềEcềEđiển
+Name[wa]=Rodje classike
+Name[xh]=Umbala obomvu wakudala
+Name[zh_CN]=经典红
+Name[zh_TW]=古典紅
+Name[zu]=Ububomvu obuhle
diff --git a/libkdegames/carddecks/decks/deck11.png b/libkdegames/carddecks/decks/deck11.png
new file mode 100644
index 00000000..0474a551
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck11.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck12.desktop b/libkdegames/carddecks/decks/deck12.desktop
new file mode 100644
index 00000000..aa0918bd
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck12.desktop
@@ -0,0 +1,41 @@
+[KDE Cards]
+Name=Chin
+Name[af]=Ken
+Name[az]=Çənə
+Name[be]=Памада
+Name[bg]=Чин
+Name[bn]=চিবুক
+Name[cs]=Brada
+Name[cy]=Gên
+Name[eo]=Vizaĝo
+Name[eu]=Kokotsa
+Name[fr]=Visage
+Name[hi]=ठोढ़ी
+Name[hr]=Brada
+Name[is]=Haka
+Name[it]=Mento
+Name[ja]=中国
+Name[km]=ចង្កា
+Name[lt]=Smakras
+Name[lv]=Seja
+Name[mk]=Брада
+Name[nds]=Kinn
+Name[ne]=चिन
+Name[nl]=Kin
+Name[pl]=Podbródek
+Name[ro]=Bărbie
+Name[ru]=Помада
+Name[sl]=Džin
+Name[sr]=Брада
+Name[sr@Latn]=Brada
+Name[sv]=Pingla
+Name[ta]=தாடை
+Name[tg]=Манаҳ
+Name[tr]=Çene
+Name[uk]=Красуня
+Name[uz@cyrillic]=Чин
+Name[ven]=Tshitefu
+Name[xh]=Isilevu
+Name[zh_CN]=头像
+Name[zh_TW]=中國
+Name[zu]=Isilevu
diff --git a/libkdegames/carddecks/decks/deck12.png b/libkdegames/carddecks/decks/deck12.png
new file mode 100644
index 00000000..df9f6b71
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck12.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck13.desktop b/libkdegames/carddecks/decks/deck13.desktop
new file mode 100644
index 00000000..37180379
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck13.desktop
@@ -0,0 +1,63 @@
+[KDE Cards]
+Name=Copy
+Name[af]=Kopie
+Name[ar]=إنسخ
+Name[az]=Köçür
+Name[be]=Скапіяваць
+Name[bg]=Ръка
+Name[bn]=কপি
+Name[br]=Eilañ
+Name[bs]=Kopiraj
+Name[ca]=Còpia
+Name[cs]=Kopie
+Name[cy]=Copio
+Name[da]=Kopi
+Name[de]=Kopie
+Name[eo]=Mano
+Name[es]=Copia
+Name[et]=Koopia
+Name[eu]=Kopiatu
+Name[fi]=Kopio
+Name[fo]=Avrit
+Name[fr]=Copie
+Name[ga]=Cóipeáil
+Name[gl]=Copia
+Name[hi]=नक़ल
+Name[hr]=Kopija
+Name[hu]=Másolás
+Name[id]=Salin
+Name[is]=Afrit
+Name[it]=Impronta
+Name[ja]=コピー
+Name[km]=ចម្លង
+Name[lt]=Kopija
+Name[lv]=Kopija
+Name[mk]=Копија
+Name[mt]=Kopja
+Name[nb]=Kopier
+Name[nds]=Kopie
+Name[ne]=प्रतिलिपि
+Name[nl]=Hand
+Name[nn]=Kopi
+Name[pl]=Kopia
+Name[pt]=Cópia
+Name[pt_BR]=Cópia
+Name[ro]=Copie
+Name[ru]=Отпечаток
+Name[se]=Máŋgus
+Name[sl]=Prepis
+Name[sr]=Копија
+Name[sr@Latn]=Kopija
+Name[sv]=Kopia
+Name[ta]=படியெடு
+Name[tg]=Нақш
+Name[tr]=Kopyala
+Name[uk]=Копія
+Name[uz]=Nusxa
+Name[uz@cyrillic]=Нусха
+Name[ven]=Tshikopololwa
+Name[wa]=Copyî
+Name[xh]=Ukukopa
+Name[zh_CN]=复制
+Name[zh_TW]=復制品
+Name[zu]=Khiphela
diff --git a/libkdegames/carddecks/decks/deck13.png b/libkdegames/carddecks/decks/deck13.png
new file mode 100644
index 00000000..377ec7a1
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck13.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck14.desktop b/libkdegames/carddecks/decks/deck14.desktop
new file mode 100644
index 00000000..fd5df9f8
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck14.desktop
@@ -0,0 +1,66 @@
+[KDE Cards]
+Name=Penguin
+Name[af]=Pikkewyn
+Name[ar]=بطريق
+Name[az]=Pinqvin
+Name[be]=Пінгвін
+Name[bg]=Пингвин
+Name[bn]=পেঙ্গুইন
+Name[br]=Penn-gwenn
+Name[bs]=Pingvin
+Name[ca]=Pingüí
+Name[cs]=Tučňák
+Name[cy]=Pengwin
+Name[da]=Pingvin
+Name[de]=Pinguin
+Name[el]=Πιγκουίνος
+Name[eo]=Pingveno
+Name[es]=Pingüino
+Name[et]=Pingviin
+Name[eu]=Pinguinoa
+Name[fa]=پنگوئن
+Name[fi]=Pingviini
+Name[fr]=Pingouin
+Name[gl]=Pingüín
+Name[he]=פינגווין
+Name[hi]=पेंग्विन
+Name[hr]=Pingvin
+Name[hu]=Pingvin
+Name[is]=Mörgæs
+Name[it]=Pinguino
+Name[km]=ផេនឃ្វីន
+Name[ko]=펭귄
+Name[lt]=Pingvinas
+Name[lv]=Pingvīns
+Name[mk]=Пингвин
+Name[mt]=Pingwin
+Name[nb]=Pingvin
+Name[nds]=Pinguin
+Name[ne]=पेन्गुइन
+Name[nl]=Pinguïn
+Name[nn]=Pingvin
+Name[pa]=ਪੈਂਗੂਇਨ
+Name[pl]=Pingwin
+Name[pt]=Pinguim
+Name[pt_BR]=Pingüim
+Name[ro]=Pinguin
+Name[ru]=Пингвин
+Name[se]=Piŋviidna
+Name[sk]=Tučniak
+Name[sl]=Pingvin
+Name[sr]=Пингвин
+Name[sr@Latn]=Pingvin
+Name[sv]=Pingvin
+Name[ta]=பென்குயின்
+Name[tg]=Пингвин
+Name[th]=เพนกวิน
+Name[tr]=Penguen
+Name[uk]=Пінгвін
+Name[uz]=Pingvin
+Name[uz@cyrillic]=Пингвин
+Name[vi]=Chim cánh cụt
+Name[wa]=Pingwin
+Name[xh]=Ipenguin
+Name[zh_CN]=企鹅
+Name[zh_TW]=企鵝
+Name[zu]=Inyoni yasemanzini
diff --git a/libkdegames/carddecks/decks/deck14.png b/libkdegames/carddecks/decks/deck14.png
new file mode 100644
index 00000000..694632bd
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck14.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck15.desktop b/libkdegames/carddecks/decks/deck15.desktop
new file mode 100644
index 00000000..92015b81
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck15.desktop
@@ -0,0 +1,19 @@
+[KDE Cards]
+Name=Tristan
+Name[be]=Трыстан
+Name[bg]=Тристан
+Name[bn]=ত্রিস্তান
+Name[eo]=Ornamo
+Name[hi]=ट्राईस्टान
+Name[hu]=Trisztán
+Name[lv]=Tristans
+Name[mk]=Тристан
+Name[ne]=ट्रिस्टान
+Name[pt]=Tristão
+Name[ru]=Тристан
+Name[sr]=Тристан
+Name[ta]=ட்ரிஸ்டன்
+Name[tg]=Тристан
+Name[uk]=Трістан
+Name[uz@cyrillic]=Тристан
+Name[zu]=I-Tristan
diff --git a/libkdegames/carddecks/decks/deck15.png b/libkdegames/carddecks/decks/deck15.png
new file mode 100644
index 00000000..73c3b92c
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck15.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck16.desktop b/libkdegames/carddecks/decks/deck16.desktop
new file mode 100644
index 00000000..11ec9761
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck16.desktop
@@ -0,0 +1,60 @@
+[KDE Cards]
+Name=Grandma
+Name[af]=Ouma
+Name[ar]=جدَة
+Name[az]=Nənə
+Name[be]=Бабуля
+Name[bg]=Стар стил
+Name[bn]=ঠাকুরমা, দাদী, নানী ইত্যাদি
+Name[br]=Mamm-goz
+Name[ca]=Àvia
+Name[cs]=Babička
+Name[cy]=Nain
+Name[da]=Bedstemor
+Name[de]=Großmutter
+Name[el]=Γιαγιά
+Name[eo]=Avinjo
+Name[es]=Abuela
+Name[et]=Vanaema
+Name[eu]=Amona
+Name[fi]=Isoäiti
+Name[fo]=Omma
+Name[fr]=Grand-mère
+Name[gl]=Aboa
+Name[he]=סבתא
+Name[hi]=दादी अम्मा
+Name[hr]=Baka
+Name[hu]=Nagyi
+Name[is]=Amma
+Name[it]=Nonna
+Name[km]=យាយ
+Name[ko]=할머니
+Name[lv]=Vecmāmiņa
+Name[mk]=Старовремско
+Name[mt]=Nanna
+Name[nb]=Bestemor
+Name[nds]=Oma
+Name[ne]=हजुरआमा
+Name[nl]=Grootmoeder
+Name[nn]=Bestemor
+Name[pl]=Babcia
+Name[pt]=Avó
+Name[pt_BR]=Vovó
+Name[ro]=Bunica
+Name[ru]=Бабуля
+Name[se]=Áhkku
+Name[sk]=Stará mama
+Name[sl]=Babica
+Name[sr]=Бака
+Name[sr@Latn]=Baka
+Name[sv]=Farmor
+Name[ta]=பாட்டி
+Name[tg]=Модаркалон
+Name[tr]=Büyük anne
+Name[uk]=Бабуся
+Name[ven]=Makhulu vha mukegulu
+Name[wa]=Grand-mere
+Name[xh]=Umakhulu
+Name[zh_CN]=祖母
+Name[zh_TW]=祖母
+Name[zu]=Ugogo
diff --git a/libkdegames/carddecks/decks/deck16.png b/libkdegames/carddecks/decks/deck16.png
new file mode 100644
index 00000000..8310ecbe
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck16.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck17.desktop b/libkdegames/carddecks/decks/deck17.desktop
new file mode 100644
index 00000000..1537939f
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck17.desktop
@@ -0,0 +1,65 @@
+[KDE Cards]
+Name=Modern Red
+Name[af]=Moderne Rooi
+Name[ar]=أحمر حديث
+Name[az]=Çağdaş Qırmızı
+Name[be]=Сучасны чырвоны
+Name[bg]=Модерно червено
+Name[bn]=আধুনিক লাল
+Name[bs]=Moderna crvena
+Name[ca]=Vermell modern
+Name[cs]=Moderní červená
+Name[cy]=Coch Cyfoes
+Name[da]=Moderne rød
+Name[de]=Modernes Rot
+Name[el]=Μοντέρνο κόκκινο
+Name[eo]=Moderna ruĝo
+Name[es]=Rojo moderno
+Name[et]=Modernpunane
+Name[eu]=Gorri modernoa
+Name[fa]=قرمز مدرن‌
+Name[fi]=Moderni punainen
+Name[fr]=Rouge moderne
+Name[gl]=Vermello moderno
+Name[he]=אדום מודרני
+Name[hi]=आधुनिक लाल
+Name[hr]=Suvremeno crvena
+Name[hu]=Modern vörös
+Name[is]=Nútíma rauður
+Name[it]=Rosso moderno
+Name[ja]=モダンレッド
+Name[km]=ពណ៌​ក្រហម​ទំនើប
+Name[ko]=현대적 빨간색
+Name[lt]=Moderni raudona
+Name[lv]=Moderni sarkans
+Name[mk]=Модерно црвено
+Name[mt]=Aħmar modern
+Name[nb]=Moderne Rød
+Name[nds]=Modern Root
+Name[ne]=उन्नत रातो
+Name[nl]=Modern rood
+Name[nn]=Moderne raud
+Name[pa]=ਨਵਾਂ ਲਾਲ
+Name[pl]=Nowoczesny czerwony
+Name[pt]=Vermelho Moderno
+Name[pt_BR]=Vermelho Moderno
+Name[ro]=Roşu modern
+Name[ru]=Современный красный
+Name[se]=Ođđaáigásaš ruoksat
+Name[sk]=Moderné červené
+Name[sl]=Moderna rdeča
+Name[sr]=Модерни црвени
+Name[sr@Latn]=Moderni crveni
+Name[sv]=Modern röd
+Name[ta]=நவீன சிகப்பு
+Name[tg]=Сурхи Замонавӣ
+Name[th]=แดงทันสมัย
+Name[tr]=Modern Kırmızı
+Name[uk]=Плетінь
+Name[ven]=Muvhala Mutshuku wa Tshizwino
+Name[vi]=Màu đềEhiện đại
+Name[wa]=Rodje modiene
+Name[xh]=Umbala omtsha obomvu
+Name[zh_CN]=现代红
+Name[zh_TW]=現代紅
+Name[zu]=Ububomvu besimanje
diff --git a/libkdegames/carddecks/decks/deck17.png b/libkdegames/carddecks/decks/deck17.png
new file mode 100644
index 00000000..71054897
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck17.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck18.desktop b/libkdegames/carddecks/decks/deck18.desktop
new file mode 100644
index 00000000..c3266a98
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck18.desktop
@@ -0,0 +1,19 @@
+[KDE Cards]
+Name=Holstentor
+Name[be]=Палац
+Name[bg]=Замък
+Name[bn]=হোলস্টেনটর
+Name[eo]=Holsten-pordego
+Name[hi]=हॉल्स्टेंटर
+Name[lv]=Holšteina
+Name[mk]=Холстентор
+Name[nds]=Holstendoor
+Name[ne]=होल्सटेन्टर
+Name[ru]=Замок
+Name[sr]=Холстентор
+Name[ta]=ஹால்ஸ்டென்டர்
+Name[tg]=Қулф
+Name[uk]=Замок
+Name[ven]=Vhushavhelo
+Name[zh_CN]=豪斯顿门
+Name[zu]=I-Holstentor
diff --git a/libkdegames/carddecks/decks/deck18.png b/libkdegames/carddecks/decks/deck18.png
new file mode 100644
index 00000000..fc93b02f
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck18.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck19.desktop b/libkdegames/carddecks/decks/deck19.desktop
new file mode 100644
index 00000000..d77c2c98
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck19.desktop
@@ -0,0 +1,63 @@
+[KDE Cards]
+Name=Horizon
+Name[af]=Horison
+Name[ar]=الأفق
+Name[az]=Üfüq
+Name[be]=Гарызонт
+Name[bg]=Хоризонт
+Name[bn]=দিগন্ত
+Name[br]=Dremmwel
+Name[bs]=Horizont
+Name[ca]=Horitzó
+Name[cs]=Horizont
+Name[cy]=Gorwel
+Name[da]=Horisont
+Name[de]=Horizont
+Name[el]=Ορίζοντας
+Name[eo]=Horizonto
+Name[es]=Horizonte
+Name[et]=Horisont
+Name[eu]=Zeruertza
+Name[fa]=افق
+Name[fi]=Horisontti
+Name[ga]=Léaslíne
+Name[gl]=Horizonte
+Name[he]=אופק
+Name[hi]=क्षितिज
+Name[hr]=Horizont
+Name[hu]=Horizont
+Name[is]=Sjóndeildarhringur
+Name[it]=Orizzonte
+Name[km]=ជើង​មេឃ
+Name[ko]=수평선
+Name[lv]=Horizonts
+Name[mk]=Хоризонт
+Name[mt]=Orizzont
+Name[nb]=Horisont
+Name[nds]=Kimm
+Name[ne]=क्षितिज
+Name[nn]=Horisont
+Name[pa]=ਖੇਤਰ
+Name[pl]=Horyzont
+Name[pt]=Horizonte
+Name[pt_BR]=Horizonte
+Name[ro]=Orizont
+Name[ru]=Горизонт
+Name[sk]=Horizont
+Name[sl]=Obzorje
+Name[sr]=Хоризонт
+Name[sr@Latn]=Horizont
+Name[sv]=Horisont
+Name[ta]=ஹாரிஸான்
+Name[tg]=Уфуқ
+Name[tr]=Ufuk
+Name[uk]=Обрій
+Name[uz]=Ufq
+Name[uz@cyrillic]=Уфқ
+Name[ven]=Magumoni a shango
+Name[vi]=Chân trời
+Name[wa]=Roye di cir
+Name[xh]=Ulundi
+Name[zh_CN]=地平线
+Name[zh_TW]=地平線
+Name[zu]=Okusesibhakabhakeni
diff --git a/libkdegames/carddecks/decks/deck19.png b/libkdegames/carddecks/decks/deck19.png
new file mode 100644
index 00000000..1526e0b3
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck19.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck2.desktop b/libkdegames/carddecks/decks/deck2.desktop
new file mode 100644
index 00000000..4e3906b2
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck2.desktop
@@ -0,0 +1,49 @@
+[KDE Cards]
+Name=Starrise
+Name[ar]=ظهور النجوم
+Name[be]=Узыход зоркі
+Name[bg]=Звезди
+Name[bn]=তারার উদয়
+Name[ca]=Estrellat
+Name[cs]=Úsvit
+Name[cy]=Codiad y sêr
+Name[de]=Sternenaufgang
+Name[eo]=Stellumo
+Name[et]=Tähetõus
+Name[eu]=Izarrak
+Name[fa]=پدیدار شدن ستاره
+Name[fi]=Tähdennousu
+Name[fr]=Étoiles
+Name[he]=צאת הכוכבים
+Name[hi]=स्टार-राइज़
+Name[hr]=Uspon zvijezda
+Name[hu]=Csillagok
+Name[is]=Ris stjarna
+Name[it]=Alba spaziale
+Name[ko]=별무리
+Name[lv]=Zvaigžņu lēkts
+Name[mk]=Изгрев
+Name[mt]=SemaStilel
+Name[nb]=Stjerner
+Name[nds]=Steernopgang
+Name[ne]=स्टारराइज
+Name[nl]=Sterrijzenis
+Name[nn]=Stjerner
+Name[pa]=ਚੜਦਾ ਤਾਰਾ
+Name[pl]=Wschód gwiazdy
+Name[ro]=Stelar
+Name[ru]=Восход звезды
+Name[se]=Násttit
+Name[sk]=Východ hviezd
+Name[sl]=Vzhod zvezde
+Name[sr]=Сазвежђе
+Name[sr@Latn]=Sazvežđe
+Name[sv]=Stjärnuppgång
+Name[ta]=நட்சத்திர உதயம்
+Name[tg]=Пайдоиши ситора
+Name[uk]=Схід зірки
+Name[ven]=Vhubva naledzi
+Name[xh]=Inkwenkwezi ephumayo
+Name[zh_CN]=星空
+Name[zh_TW]=星空
+Name[zu]=Ukuvuka kwezinkanyezi
diff --git a/libkdegames/carddecks/decks/deck2.png b/libkdegames/carddecks/decks/deck2.png
new file mode 100644
index 00000000..52f1b67a
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck2.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck20.desktop b/libkdegames/carddecks/decks/deck20.desktop
new file mode 100644
index 00000000..3403b23f
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck20.desktop
@@ -0,0 +1,71 @@
+[KDE Cards]
+Name=Flowers
+Name[af]=Blomme
+Name[ar]=أزهار
+Name[az]=Çiçəklər
+Name[be]=Кветкі
+Name[bg]=Цветя
+Name[bn]=ফুল
+Name[br]=Bleunioù
+Name[bs]=Cvijeće
+Name[ca]=Flors
+Name[cs]=Květiny
+Name[cy]=Blodau
+Name[da]=Blomster
+Name[de]=Blumen
+Name[el]=Λουλούδια
+Name[eo]=Floroj
+Name[es]=Flores
+Name[et]=Lilled
+Name[eu]=Loreak
+Name[fa]=گلها
+Name[fi]=Kukat
+Name[fo]=Blómur
+Name[fr]=Fleurs
+Name[ga]=Bláthanna
+Name[gl]=Flores
+Name[he]=פרחים
+Name[hi]=पुष्प
+Name[hr]=Cvjetovi
+Name[hu]=Virágok
+Name[is]=Blóm
+Name[it]=Fiori
+Name[ja]=花
+Name[km]=ផ្កា
+Name[ko]=꽃
+Name[lt]=Gėlės
+Name[lv]=Puķes
+Name[mk]=Цвеќиња
+Name[mt]=Fjuri
+Name[nb]=Blomster
+Name[nds]=Blomen
+Name[ne]=फूल
+Name[nl]=Bloemen
+Name[nn]=Blomar
+Name[nso]=Maloba
+Name[pa]=ਫੁੱਲ
+Name[pl]=Kwiaty
+Name[pt]=Flores
+Name[pt_BR]=Flores
+Name[ro]=Flori
+Name[ru]=Цветы
+Name[se]=Lieđit
+Name[sk]=Kvety
+Name[sl]=Rože
+Name[sr]=Цвеће
+Name[sr@Latn]=Cveće
+Name[sv]=Blommor
+Name[ta]=மலர்கள்
+Name[tg]=Гулҳо
+Name[th]=ดอกไม้
+Name[tr]=Çiçekler
+Name[uk]=Квіти
+Name[uz]=Gullar
+Name[uz@cyrillic]=Гуллар
+Name[ven]=Maluvha
+Name[vi]=Hoa
+Name[wa]=Fleurs
+Name[xh]=Iintyatyambo
+Name[zh_CN]=鲜花
+Name[zh_TW]=鮮花
+Name[zu]=Izimbali
diff --git a/libkdegames/carddecks/decks/deck20.png b/libkdegames/carddecks/decks/deck20.png
new file mode 100644
index 00000000..73d76887
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck20.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck21.desktop b/libkdegames/carddecks/decks/deck21.desktop
new file mode 100644
index 00000000..b170ee53
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck21.desktop
@@ -0,0 +1,65 @@
+[KDE Cards]
+Name=Carpet
+Name[af]=Tapyt
+Name[ar]=سجادة
+Name[az]=Xalı
+Name[be]=Дыван
+Name[bg]=Килим
+Name[bn]=গালিচা
+Name[bs]=Ćilim
+Name[ca]=Catifa
+Name[cs]=Koberec
+Name[cy]=Carped
+Name[da]=Tæppe
+Name[de]=Teppich
+Name[el]=Μοκέτα
+Name[eo]=Tapiŝo
+Name[es]=Alfombra
+Name[et]=Vaip
+Name[eu]=Tapiza
+Name[fa]=فرش
+Name[fi]=Matto
+Name[fr]=Tapis
+Name[gl]=Alfombra
+Name[he]=שטיח
+Name[hi]=कालीन
+Name[hr]=Tepih
+Name[hu]=Szőnyeg
+Name[is]=Teppi
+Name[it]=Tappeto
+Name[km]=កម្រាល​ព្រំ
+Name[ko]=양탄자
+Name[lt]=Kilimas
+Name[lv]=Paklājs
+Name[mk]=Килим
+Name[mt]=Tapit
+Name[nb]=Teppe
+Name[nds]=Teppich
+Name[ne]=कार्पेट
+Name[nl]=Tapijt
+Name[nn]=Teppe
+Name[pa]=ਦਰੀ
+Name[pl]=Dywan
+Name[pt]=Carpete
+Name[pt_BR]=Carpete
+Name[ro]=Carpetă
+Name[ru]=Ковёр
+Name[se]=Máhttá
+Name[sl]=Preproga
+Name[sr]=Тепих
+Name[sr@Latn]=Tepih
+Name[sv]=Matta
+Name[ta]=கம்பளம்
+Name[tg]=Гилем
+Name[th]=พรม
+Name[tr]=Halı
+Name[uk]=Килим
+Name[uz]=Gilam
+Name[uz@cyrillic]=Гилам
+Name[ven]=Dzithovho
+Name[vi]=Thảm
+Name[wa]=Carpete
+Name[xh]=Ikhaphethi
+Name[zh_CN]=地毯
+Name[zh_TW]=地毯
+Name[zu]=UKhaphethi
diff --git a/libkdegames/carddecks/decks/deck21.png b/libkdegames/carddecks/decks/deck21.png
new file mode 100644
index 00000000..204156ba
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck21.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck22.desktop b/libkdegames/carddecks/decks/deck22.desktop
new file mode 100644
index 00000000..940b4f53
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck22.desktop
@@ -0,0 +1,58 @@
+[KDE Cards]
+Name=Bathing
+Name[af]=Swem
+Name[ar]=يتحمم
+Name[az]=Çimmə
+Name[be]=Купанне
+Name[bg]=Море
+Name[bn]=গোসল, স্নান
+Name[bs]=Kupanje
+Name[ca]=Balneari
+Name[cs]=Koupel
+Name[cy]=Ymolchi
+Name[da]=Badning
+Name[de]=Baden
+Name[eo]=Banloko
+Name[es]=Balneario
+Name[et]=Suplus
+Name[eu]=Bainua
+Name[fi]=Kylpy
+Name[fr]=Baignade
+Name[gl]=Baño
+Name[he]=מקלחת
+Name[hi]=बाथिंग
+Name[hr]=Kupanje
+Name[hu]=Fürdő
+Name[is]=Í baði
+Name[it]=Balneare
+Name[km]=កំពុង​ងូត​ទឹក
+Name[ko]=목욕
+Name[lt]=Maudynės
+Name[lv]=Peldēšanās
+Name[mk]=Капење
+Name[mt]=Għawm
+Name[nb]=Bading
+Name[nds]=Baden
+Name[ne]=नुहाएको
+Name[nl]=Baden
+Name[nn]=Bad
+Name[pl]=Kąpiel
+Name[pt]=Banho
+Name[pt_BR]=Banho
+Name[ro]=Îmbăiere
+Name[ru]=Купание
+Name[se]=Lávgudeapmi
+Name[sl]=Kopel
+Name[sr]=Купање
+Name[sr@Latn]=Kupanje
+Name[sv]=Bad
+Name[ta]=குளியல்
+Name[tg]=Оббозӣ
+Name[tr]=Yüzme
+Name[uk]=Купання
+Name[ven]=Hu khou tambiwa
+Name[vi]=Tắm
+Name[xh]=Ukuhlamba
+Name[zh_CN]=游泳
+Name[zh_TW]=游泳
+Name[zu]=Ukugeza
diff --git a/libkdegames/carddecks/decks/deck22.png b/libkdegames/carddecks/decks/deck22.png
new file mode 100644
index 00000000..ff3f9834
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck22.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck23.desktop b/libkdegames/carddecks/decks/deck23.desktop
new file mode 100644
index 00000000..ef096f15
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck23.desktop
@@ -0,0 +1,55 @@
+[KDE Cards]
+Name=Oasis
+Name[af]=Oase
+Name[ar]=واحة
+Name[az]=Oazis
+Name[be]=Аазіс
+Name[bg]=Оазис
+Name[bn]=মরুদ্যান
+Name[br]=Oazis
+Name[bs]=Oaza
+Name[cs]=Oáza
+Name[da]=Oase
+Name[de]=Oase
+Name[el]=Όαση
+Name[eo]=Oazo
+Name[et]=Oaas
+Name[eu]=Oasia
+Name[fa]=آبادی
+Name[fo]=Oasa
+Name[hi]=ओएसिस
+Name[hr]=Oaza
+Name[hu]=Oázis
+Name[is]=Vin
+Name[it]=Oasi
+Name[ko]=오아시스
+Name[lt]=Oazė
+Name[lv]=Oāze
+Name[mk]=Оаза
+Name[mt]=Oażi
+Name[nb]=Oase
+Name[nds]=Oaas
+Name[ne]=ओसिस
+Name[nl]=Oase
+Name[nn]=Oase
+Name[pl]=Oaza
+Name[pt]=Oásis
+Name[pt_BR]=Oásis
+Name[ru]=Оазис
+Name[sk]=Oáza
+Name[sl]=Oaza
+Name[sr]=Оаза
+Name[sr@Latn]=Oaza
+Name[sv]=Oas
+Name[ta]=ஒயாஸிஸ்
+Name[tg]=Оазис
+Name[th]=โอเอซิส
+Name[tr]=Vaha
+Name[uk]=Оазис
+Name[uz]=Voha
+Name[uz@cyrillic]=Воҳа
+Name[vi]=Ô'c đảo
+Name[xh]=Indawo esentlango enamanzi
+Name[zh_CN]=绿洲
+Name[zh_TW]=綠洲
+Name[zu]=Indawo evundileyo
diff --git a/libkdegames/carddecks/decks/deck23.png b/libkdegames/carddecks/decks/deck23.png
new file mode 100644
index 00000000..a3025212
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck23.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck24.desktop b/libkdegames/carddecks/decks/deck24.desktop
new file mode 100644
index 00000000..002af784
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck24.desktop
@@ -0,0 +1,60 @@
+[KDE Cards]
+Name=Konqi
+Name[be]=Конкі
+Name[bg]=Конки
+Name[bn]=কনকি
+Name[cs]=Konqui
+Name[eo]=Konĉja
+Name[hi]=के-ऑन्गी
+Name[it]=Konqui
+Name[lv]=Konvi
+Name[ne]=कोन्क्वी
+Name[ru]=Конки
+Name[sr]=Конки
+Name[sr@Latn]=Konki
+Name[ta]=கான்கி
+Name[tg]=Конки
+Name[uk]=Конкі
+Name[zu]=I-Konqi
+Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[bn]=আধুনিক কনকি - একটি পারিবারিক তাস খেলা\nডিজাইন: লরা লেল্যান্ড\n <l_layland@hotmail.com>\nকনকি: স্টিফেন স্পাজ\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[bs]=Modern Konqi - igrajte sa porodičnim špilom\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n <l_layland@hotmail.com>\nKonqi per Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n <l_layland@hotmail.com>\nKonqiho vytvořil Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n <l_layland@hotmail.com>\nKonqi gan Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Laylanda\n <l_layland@hotmail.com>\nKonqi von Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[el]=Μοντέρνος Konqi - play the family θέμα καρτών\nΣχεδίαση: Laura Layland\n <l_layland@hotmail.com>\nKonqi από Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n <l_layland@hotmail.com>\nKonĉjo de Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n <l_layland@hotmail.com>\nKonqi-ren egilea Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند <l_layland@hotmail.com>\nKonqi توسط استفان اسپاتز\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n <l_layland@hotmail.com>\nKonqi Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n <l_layland@hotmail.com>\nKonqi par Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[he]=קונקי מודרני - חפיסת קלים לכל השפחה\nעיצוב: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[it]=Konqui Moderno - carte familiari\nDesign: Laura Layland\n<l_layland@hotmail.com>\nKonqui di Stefan Spatz\n<stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ja]=モダン Konqi - ファミリ向けカードデッキ\nデザイン: Laura Layland\n <l_layland@hotmail.com>\nKonqi の作者: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[lt]=Modern Konqi - žaisktie šeimos kortų žaidimą\nDizainas: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\n
+Comment[lv]=Modernais Konkvi - spēlēt pie ģimenes kāršu galda\n Dizains: Laura Layland\n <l_layland@hotmail.com>\n Konkvi no Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[mk]=Модерен Konqi - играјте со семејниот шпил карти\nДизајн: Laura Layland\n<l_layland@hotmail.com>\nKatie од Agnieszka Czajkowska\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de>
+Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi vun Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ne]=आधुनिक क्वोन्की - पारिवारिक कार्डडेक\nडिजाइन प्ले: लाउरा लेल्यान्ड\n <l_layland@hotmail.com>\nKonqi, स्टेफन स्पार्टजद्वारा\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi door Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de>
+Comment[pl]=Nowoczesny Konqi - zagraj w grę rodzinną\nProjekt: Laura Laylanda\n <l_layland@hotmail.com>a\nKonqi: Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ru]=Колода с семейством Конки\nДизайн: Лаура Лейлэнд (Laura Layland) <l_layland@hotmail.com>\nКонки нарисован Штефаном Спартцом (Stefan Spatz) <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n <l_layland@hotmail.com>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Layland\n <l_layland@hotmail.com>\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sr]=Модеран Конки - играјте са породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n <l_layland@hotmail.com>\nКонки: Штефан Шпац (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sr@Latn]=Moderan Konki - igrajte sa porodičnim špilom\nDizajn: Lora Lejlend (Laura Layland)\n <l_layland@hotmail.com>\nKonki: Štefan Špac (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[ta]=மார்டன் கான்கி - குடும்பச் சீட்டுத் தளத்தை விளையாடு\nவடிவமைப்பு: லௌரா லேலாண்டு\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[uk]=Сучасний Конкі - зіграйте у сімейні карти\nРозробка: Laura Layland\n <l_layland@hotmail.com>\nKatie від Agnieszka Czajkowska\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[wa]=Modiene Konqi - cwårdjeus des familes\nDessins: Laura Layland\n <l_layland@hotmail.com>\nKonqi pa Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de>
+Comment[zh_TW]=現代 Konqi - 玩家庭牌局\n設計︰Laura Layland...<l_layland@hotmail.com>\nKonqi by Stefan Spatz...<stefan.spatz@stud-mail.uni-wuerzburg.de>
diff --git a/libkdegames/carddecks/decks/deck24.png b/libkdegames/carddecks/decks/deck24.png
new file mode 100644
index 00000000..0596e919
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck24.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck3.desktop b/libkdegames/carddecks/decks/deck3.desktop
new file mode 100644
index 00000000..a383495e
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck3.desktop
@@ -0,0 +1,60 @@
+[KDE Cards]
+Name=Romantic
+Name[af]=Romantiese
+Name[ar]=رومانسي
+Name[az]=Romantik
+Name[be]=Рамантыка
+Name[bg]=Романтика
+Name[bn]=রোমান্টিক
+Name[bs]=Romantika
+Name[ca]=Romàntic
+Name[cs]=Romantika
+Name[cy]=Rhamantus
+Name[da]=Romantisk
+Name[de]=Romantisch
+Name[el]=Ρομαντικό
+Name[eo]=Romantiko
+Name[es]=Romántico
+Name[et]=Romantika
+Name[eu]=Erromantikoa
+Name[fa]=خیال‌انگیز
+Name[fi]=Romanttinen
+Name[fr]=Romantique
+Name[gl]=Romántica
+Name[he]=רומנטי
+Name[hi]=रूमानी
+Name[hr]=Romantika
+Name[hu]=Romantikus
+Name[is]=Rómantískt
+Name[it]=Romantico
+Name[km]=មនោសញ្ចេតនា
+Name[ko]=로맨틱
+Name[lt]=Romantika
+Name[lv]=Romantika
+Name[mk]=Романтика
+Name[mt]=Romantiku
+Name[nb]=Romantisk
+Name[nds]=Romantsch
+Name[ne]=रोमान्टिक
+Name[nl]=Romantisch
+Name[nn]=Romantisk
+Name[pa]=ਰੁਮਾਂਸਵਾਦੀ
+Name[pl]=Romantyczny
+Name[pt]=Romântico
+Name[pt_BR]=Romântico
+Name[ru]=Романтика
+Name[se]=Romántalaš
+Name[sk]=Romantické
+Name[sl]=Romantično
+Name[sr]=Романтичан
+Name[sr@Latn]=Romantičan
+Name[sv]=Romantisk
+Name[ta]=ரொமான்டிக்
+Name[tg]=Хаёлпарастӣ
+Name[th]=โรแมนติก
+Name[uk]=Романтичний
+Name[vi]=Lãng mạn
+Name[wa]=Romantike
+Name[zh_CN]=浪漫
+Name[zh_TW]=浪漫的
+Name[zu]=I-Romantic
diff --git a/libkdegames/carddecks/decks/deck3.png b/libkdegames/carddecks/decks/deck3.png
new file mode 100644
index 00000000..dced5af7
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck3.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck4.desktop b/libkdegames/carddecks/decks/deck4.desktop
new file mode 100644
index 00000000..c23f4963
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck4.desktop
@@ -0,0 +1,93 @@
+[KDE Cards]
+Name=Panda
+Name[ar]=باندا
+Name[be]=Панда
+Name[bg]=Панда
+Name[bn]=পান্ডা
+Name[el]=Πάντα
+Name[eo]=Pando
+Name[fa]=پاندا
+Name[he]=פנדה
+Name[hi]=पाण्डा
+Name[is]=Pandabjörn
+Name[ko]=팬더
+Name[mk]=Панда
+Name[ne]=पान्डा
+Name[pa]=ਪਾਂਡਾ
+Name[ru]=Панда
+Name[sr]=Панда
+Name[ta]=பாண்டா
+Name[tg]=Панда
+Name[th]=แพนด้า
+Name[uk]=Панда
+Name[uz@cyrillic]=Панда
+Name[ven]=Tshivhingwi
+Name[vi]=Gấu mèo
+Name[xh]=Ibhere
+Name[zh_CN]=熊猫
+Name[zh_TW]=熊貓
+Name[zu]=Ibhele
+Comment=Dedicated to WWF
+Comment[af]=Opgedra na Wwf
+Comment[ar]=إهداء إلى المؤسسة العالمية للحفاظ على البيئة (WWF)
+Comment[az]=WWFyə hasr edilib
+Comment[be]=Прысвячаецца WWF
+Comment[bg]=Посвещава се на WWF
+Comment[bn]=WWF-এর প্রতি উত্‍সর্গীকৃত
+Comment[bs]=Posvećen WWFu
+Comment[ca]=Dedicat a WWF
+Comment[cs]=Věnované WWF
+Comment[cy]=Cyflwynedig i\'r WWF
+Comment[da]=Dedikeret til WWF
+Comment[de]=Dem WWF gewidmet
+Comment[el]=Αφιερωμένο στο WWF
+Comment[eo]=Dediĉita al WWF
+Comment[es]=Dedicado a WWF
+Comment[et]=Pühendatud organisatsioonile WWF
+Comment[eu]=WWF-ren ohorean
+Comment[fa]=مختص WWF
+Comment[fi]=Omistettu WWF:lle
+Comment[fr]=Dédicacé au WWF
+Comment[gl]=Adicado a WWF
+Comment[he]=מוקדש לקרן העולמית
+Comment[hi]=डब्लयूडब्लयूएफ को समर्पित
+Comment[hr]=Posvećeno WWF-u
+Comment[hu]=A WWF-nek dedikálva
+Comment[is]=Tileinkað WWF
+Comment[it]=Dedicato al WWF
+Comment[ja]=WWF に捧ぐ
+Comment[km]=ឧទ្ទិស​ជូន WWF
+Comment[ko]=WWF에 바침
+Comment[lt]=Skirta WWF
+Comment[lv]=Veltīts WWF
+Comment[mk]=Посветено на WWF (светска фондација за дивиот свет)
+Comment[mt]=Dedikat lill-WWF
+Comment[nb]=Dedikert til WWF
+Comment[nds]=Den WWF toeegt
+Comment[ne]=डब्लूडब्लूएफ प्रति समर्पित
+Comment[nl]=Opgedragen aan WWF (WereldNatuurFonds)
+Comment[nn]=Tileigna WWF
+Comment[pa]=WWF ਨੂੰ ਸਮਰਪਤ
+Comment[pl]=Przeznaczony do WWF
+Comment[pt]=Dedicado ao WWF
+Comment[pt_BR]=Dedicado ao WWF
+Comment[ro]=Dedicat lui WWF
+Comment[ru]=Посвящается WWF
+Comment[sk]=Venované WWF
+Comment[sl]=Posvečeno WWF
+Comment[sr]=Посвећено WWF-у
+Comment[sr@Latn]=Posvećeno WWF-u
+Comment[sv]=Tillägnad WWF
+Comment[ta]= WWF-க்கு அர்ப்பணிக்கப்பட்டது
+Comment[tg]=Бахшида шудааст ба WWF
+Comment[tr]=WWF'e adanmış
+Comment[uk]=Присвячено WWF
+Comment[uz]=WWF tashkilotiga bagʻishlangan
+Comment[uz@cyrillic]=WWF ташкилотига бағишланган
+Comment[ven]=Yo livhiswa kha WWF
+Comment[vi]=Dâng tặng WWF
+Comment[wa]=Dicåçté å WWF
+Comment[xh]=Yenzelwe ku WWF
+Comment[zh_CN]=献给 WWF
+Comment[zh_TW]=獻給 WWF
+Comment[zu]=Inikelwe kwi-WWF
diff --git a/libkdegames/carddecks/decks/deck4.png b/libkdegames/carddecks/decks/deck4.png
new file mode 100644
index 00000000..23fa4010
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck4.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck5.desktop b/libkdegames/carddecks/decks/deck5.desktop
new file mode 100644
index 00000000..5cbe2d6f
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck5.desktop
@@ -0,0 +1,67 @@
+[KDE Cards]
+Name=Water
+Name[ar]=ماء
+Name[az]=Su
+Name[be]=Вада
+Name[bg]=Вода
+Name[bn]=পানি
+Name[br]=Dou
+Name[bs]=Voda
+Name[ca]=Aigua
+Name[cs]=Voda
+Name[cy]=Dŵr
+Name[da]=Vand
+Name[de]=Wasser
+Name[el]=Νερό
+Name[eo]=Akvo
+Name[es]=Agua
+Name[et]=Vesi
+Name[eu]=Ura
+Name[fa]=آب
+Name[fi]=Vesi
+Name[fo]=Vatn
+Name[fr]=Eau
+Name[ga]=Uisce
+Name[gl]=Auga
+Name[he]=מים
+Name[hi]=पानी
+Name[hr]=Voda
+Name[hu]=Víz
+Name[is]=Vatn
+Name[it]=Acqua
+Name[ja]=水
+Name[km]=ទឹក
+Name[ko]=물
+Name[lt]=Vanduo
+Name[lv]=Ūdens
+Name[mk]=Вода
+Name[mt]=Ilma
+Name[nb]=Vann
+Name[ne]=पानी
+Name[nn]=Vatn
+Name[nso]=Meetse
+Name[pa]=ਪਾਣੀ
+Name[pl]=Woda
+Name[pt]=Água
+Name[pt_BR]=Água
+Name[ro]=Apă
+Name[ru]=Вода
+Name[se]=Čáhci
+Name[sk]=Voda
+Name[sl]=Voda
+Name[sr]=Вода
+Name[sr@Latn]=Voda
+Name[sv]=Vatten
+Name[ta]=தண்ணீர்
+Name[tg]=Об
+Name[th]=น้ำ
+Name[uk]=Вода
+Name[uz]=Suv
+Name[uz@cyrillic]=Сув
+Name[ven]=Madi
+Name[vi]=Nước
+Name[wa]=Aiwe
+Name[xh]=Amanzi
+Name[zh_CN]=水
+Name[zh_TW]=水
+Name[zu]=Amanzi
diff --git a/libkdegames/carddecks/decks/deck5.png b/libkdegames/carddecks/decks/deck5.png
new file mode 100644
index 00000000..63cb6762
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck5.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck6.desktop b/libkdegames/carddecks/decks/deck6.desktop
new file mode 100644
index 00000000..6d84a8ad
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck6.desktop
@@ -0,0 +1,66 @@
+[KDE Cards]
+Name=Beach
+Name[af]=Strand
+Name[ar]=شاطئ
+Name[az]=Çimərlik
+Name[be]=Пляж
+Name[bg]=Плаж
+Name[bn]=সৈকত
+Name[br]=Traezhenn
+Name[bs]=Plaža
+Name[ca]=Platja
+Name[cs]=Pláž
+Name[cy]=Traeth
+Name[da]=Strand
+Name[de]=Strand
+Name[el]=Παραλία
+Name[eo]=Plaĝo
+Name[es]=Playa
+Name[et]=Rand
+Name[eu]=Hondartza
+Name[fa]=ساحل
+Name[fi]=Ranta
+Name[fr]=Plage
+Name[gl]=Praia
+Name[he]=חוף
+Name[hi]=समुद्र तट
+Name[hr]=Plaža
+Name[hu]=Tengerpart
+Name[is]=Strönd
+Name[it]=Spiaggia
+Name[km]=ឆ្នេរ​ខ្សាច់
+Name[ko]=바닷가
+Name[lt]=Paplūdimys
+Name[lv]=Pludmale
+Name[mk]=Плажа
+Name[mt]=Ramla
+Name[nb]=Strand
+Name[nds]=Strand
+Name[ne]=किनारा
+Name[nl]=Strand
+Name[nn]=Strand
+Name[nso]=Lewatle
+Name[pa]=ਬੀਚ
+Name[pl]=Plaża
+Name[pt]=Praia
+Name[pt_BR]=Praia
+Name[ro]=Plajă
+Name[ru]=Пляж
+Name[se]=Fiervá
+Name[sk]=Pláž
+Name[sl]=Obala
+Name[sr]=Плажа
+Name[sr@Latn]=Plaža
+Name[sv]=Strand
+Name[ta]=கடல்
+Name[tg]=Пляж
+Name[th]=อ่าว
+Name[tr]=Sahil
+Name[uk]=Пляж
+Name[ven]=Bitshini
+Name[vi]=Bãi biển
+Name[wa]=Pladje
+Name[xh]=Ulwande
+Name[zh_CN]=海滩
+Name[zh_TW]=海灘
+Name[zu]=Ulwandle
diff --git a/libkdegames/carddecks/decks/deck6.png b/libkdegames/carddecks/decks/deck6.png
new file mode 100644
index 00000000..1708d784
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck6.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck7.desktop b/libkdegames/carddecks/decks/deck7.desktop
new file mode 100644
index 00000000..3590a15e
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck7.desktop
@@ -0,0 +1,67 @@
+[KDE Cards]
+Name=Sunset
+Name[af]=Sonsondergang
+Name[ar]=غروب الشمس
+Name[az]=Gün doğuşu
+Name[be]=Заход
+Name[bg]=Залез
+Name[bn]=সূর্যাস্ত
+Name[br]=Kuzh-heol
+Name[bs]=Zalazak
+Name[ca]=Posta de sol
+Name[cs]=Západ slunce
+Name[cy]=Machlud
+Name[da]=Solnedgang
+Name[de]=Sonnenuntergang
+Name[el]=Ηλιοβασίλεμα
+Name[eo]=Sunsubiro
+Name[es]=Puesta de sol
+Name[et]=Loojang
+Name[eu]=Eguzki-sartzea
+Name[fa]=غروب
+Name[fi]=Auringonlasku
+Name[fo]=Sólsetur
+Name[fr]=Soleil
+Name[gl]=Entre lusco e fusco
+Name[he]=שקיעה
+Name[hi]=सूर्यास्त
+Name[hr]=Zalazak sunca
+Name[hu]=Naplemente
+Name[is]=Sólsetur
+Name[it]=Tramonto
+Name[km]=សុរិយា​អស្ដង្គត
+Name[ko]=저녁놀
+Name[lt]=Saulėlydis
+Name[lv]=Saullēkts
+Name[mk]=Зајдисонце
+Name[mt]=Għabex
+Name[nb]=Solnedgang
+Name[nds]=Sünnünnergang
+Name[ne]=सुर्यास्त
+Name[nl]=Zonsondergang
+Name[nn]=Solrenning
+Name[pa]=ਡੁੱਬਦਾ ਸੂਰਜ
+Name[pl]=Zachód słońca
+Name[pt]=Pôr-do-Sol
+Name[pt_BR]=Pôr-do-sol
+Name[ro]=Asfinţit
+Name[ru]=Закат
+Name[sk]=Západ slnka
+Name[sl]=Sončni zahod
+Name[sr]=Залазак сунца
+Name[sr@Latn]=Zalazak sunca
+Name[sv]=Solnedgång
+Name[ta]=சூரிய அஸ்தமம்
+Name[tg]=Ғуруби Офтоб
+Name[th]=พระอาทิตย์ตก
+Name[tr]=Gündoğumu
+Name[uk]=Захід сонця
+Name[uz]=Oqshom
+Name[uz@cyrillic]=Оқшом
+Name[ven]=Vhukovhela
+Name[vi]=Hoàng hôn
+Name[wa]=Solea djus
+Name[xh]=Ukutshona kwelanga
+Name[zh_CN]=日落
+Name[zh_TW]=日落
+Name[zu]=Ukushona kwelanga
diff --git a/libkdegames/carddecks/decks/deck7.png b/libkdegames/carddecks/decks/deck7.png
new file mode 100644
index 00000000..c7f043db
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck7.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck8.desktop b/libkdegames/carddecks/decks/deck8.desktop
new file mode 100644
index 00000000..0c68bc9f
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck8.desktop
@@ -0,0 +1,67 @@
+[KDE Cards]
+Name=Road
+Name[af]=Pad
+Name[ar]=طريق
+Name[az]=Yol
+Name[be]=Дарога
+Name[bg]=Път
+Name[bn]=পথ
+Name[bs]=Put
+Name[ca]=Carretera
+Name[cs]=Cesta
+Name[cy]=Ffordd
+Name[da]=Vej
+Name[de]=Straße
+Name[el]=Δρόμος
+Name[eo]=Strato
+Name[es]=Carretera
+Name[et]=Maantee
+Name[eu]=Errepidea
+Name[fa]=جاده
+Name[fi]=Tie
+Name[fo]=Vegur
+Name[fr]=Route
+Name[gl]=Estrada
+Name[he]=בדרכים
+Name[hi]=रास्ता
+Name[hr]=Cesta
+Name[hu]=Országút
+Name[is]=Vegur
+Name[it]=Strada
+Name[km]=ផ្លូវ
+Name[ko]=길
+Name[lt]=Kelias
+Name[lv]=Ceļš
+Name[mk]=Пат
+Name[mt]=Triq
+Name[nb]=Vei
+Name[nds]=Straat
+Name[ne]=बाटो
+Name[nl]=Weg
+Name[nn]=Veg
+Name[nso]=Tsela
+Name[pa]=ਰਾਹ
+Name[pl]=Droga
+Name[pt]=Estrada
+Name[pt_BR]=Estrada
+Name[ro]=Şosea
+Name[ru]=Дорога
+Name[se]=Geaidnu
+Name[sk]=Cesta
+Name[sl]=Cesta
+Name[sr]=Пут
+Name[sr@Latn]=Put
+Name[sv]=Väg
+Name[ta]=சாலை
+Name[tg]=Роҳ
+Name[th]=ถนน
+Name[tr]=Yol
+Name[uk]=Дорога
+Name[uz]=Yoʻl
+Name[uz@cyrillic]=Йўл
+Name[ven]=Bada
+Name[vi]=Con đường
+Name[xh]=Indlela
+Name[zh_CN]=路
+Name[zh_TW]=道路
+Name[zu]=Umgwaqo
diff --git a/libkdegames/carddecks/decks/deck8.png b/libkdegames/carddecks/decks/deck8.png
new file mode 100644
index 00000000..4fed86e6
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck8.png
Binary files differ
diff --git a/libkdegames/carddecks/decks/deck9.desktop b/libkdegames/carddecks/decks/deck9.desktop
new file mode 100644
index 00000000..2541e22a
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck9.desktop
@@ -0,0 +1,7 @@
+[KDE Cards]
+Name=KDE
+Name[af]=Kde
+Name[bn]=কে.ডি.ই.
+Name[hi]=केडीई
+Name[ne]=केडीई
+Name[ta]=கேடிஇ
diff --git a/libkdegames/carddecks/decks/deck9.png b/libkdegames/carddecks/decks/deck9.png
new file mode 100644
index 00000000..35abd0a8
--- /dev/null
+++ b/libkdegames/carddecks/decks/deck9.png
Binary files differ
diff --git a/libkdegames/configure.in.in b/libkdegames/configure.in.in
new file mode 100644
index 00000000..ffcaf9c9
--- /dev/null
+++ b/libkdegames/configure.in.in
@@ -0,0 +1,49 @@
+dnl AB: checking for a system-wide highscore file. If "no" then the default
+dnl (just kapp->config()) is used. See KHighscore for details.
+
+AC_MSG_CHECKING(whether to use system-wide highscores)
+AC_ARG_ENABLE(highscore-dir,
+AC_HELP_STRING([--enable-highscore-dir=DIR], [system-wide highscore table @<:@default=no@:>@]), [use_highscore_dir=yes], [use_highscore_dir=no])
+
+if test "$use_highscore_dir" = "no"; then
+ AC_MSG_RESULT(no)
+ AC_SUBST(HIGHSCORE_DIRECTORY, "")
+else
+ case "${enableval}" in
+ yes) highscore_dir='${localstatedir}/games' ;;
+ no) ;;
+ *) highscore_dir=${enableval} ;;
+ esac
+ AC_DEFINE_UNQUOTED(HIGHSCORE_DIRECTORY, "$highscore_dir", [The system-wide highscore directory])
+ AC_SUBST(HIGHSCORE_DIRECTORY, $highscore_dir)
+ AC_MSG_RESULT($use_highscore_dir)
+fi
+
+AC_MSG_CHECKING(whether to setgid binaries)
+AC_ARG_ENABLE(setgid,
+ [ --enable-setgid Enable the use of setgid binaries],
+ [case "${enableval}" in
+ yes)
+ case "$use_highscore_dir" in
+ yes) setgid=true;;
+ no) setgid=false;;
+ esac ;;
+ no) setgid=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-setgid) ;;
+ esac],[setgid=false])
+AC_SUBST(setgid)
+AC_MSG_RESULT($setgid)
+
+AC_MSG_CHECKING(what group to use for the highscore tables and binaries)
+AC_ARG_WITH(highscore-group,
+[ --with-highscore-group=group Group for the highscore tables and binaries],
+highscore_group="$withval",highscore_group="games")
+AC_SUBST(highscore_group)
+AC_MSG_RESULT($highscore_group)
+
+AC_MSG_CHECKING(what user to use for the highscore tables and binaries)
+AC_ARG_WITH(highscore-user,
+[ --with-highscore-user=user User for the highscore tables],
+highscore_user="$withval",highscore_user="games")
+AC_SUBST(highscore_user)
+AC_MSG_RESULT($highscore_user)
diff --git a/libkdegames/highscore/INSTALL b/libkdegames/highscore/INSTALL
new file mode 100644
index 00000000..a16fa57d
--- /dev/null
+++ b/libkdegames/highscore/INSTALL
@@ -0,0 +1,12 @@
+Installation notes for the highscore files ; this is only relevant if you
+configured libkdegames with option --enable-highscore-dir=DIR (usually DIR is
+/var/games) for using system-wide highscore files.
+
+For each game using the highscore system :
+
+- the game executable "mygame" should be installed sgid "games"
+
+- an empty file "mygame.scores" should be created in the directory pointed by
+the configuration option. It should be owned by group "games" with read and
+write permissions and should -not- be world readable (since it can contains
+possibly sensitive information associating username with game usage).
diff --git a/libkdegames/highscore/Makefile.am b/libkdegames/highscore/Makefile.am
new file mode 100644
index 00000000..6fa18cc0
--- /dev/null
+++ b/libkdegames/highscore/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = libkhighscore.la
+
+INCLUDES = $(all_includes)
+
+libkhighscore_la_SOURCES = kconfigrawbackend.cpp \
+ kfilelock.cpp khighscore.cpp kscoredialog.cpp \
+ kexthighscore_item.cpp kexthighscore_internal.cpp \
+ kexthighscore_tab.cpp kexthighscore_gui.cpp \
+ kexthighscore.cpp
+
+include_HEADERS = khighscore.h kscoredialog.h \
+ kexthighscore_item.h kexthighscore.h
+
+noinst_HEADERS = kconfigrawbackend.h \
+ kfilelock.h kexthighscore_internal.h kexthighscore_tab.h \
+ kexthighscore_gui.h
+
+METASOURCES = kconfigrawbackend.moc khighscore.moc kscoredialog.moc \
+ kexthighscore_tab.moc kexthighscore_gui.moc
diff --git a/libkdegames/highscore/kconfigrawbackend.cpp b/libkdegames/highscore/kconfigrawbackend.cpp
new file mode 100644
index 00000000..a379ba23
--- /dev/null
+++ b/libkdegames/highscore/kconfigrawbackend.cpp
@@ -0,0 +1,62 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kconfigrawbackend.h"
+#include "kconfigrawbackend.moc"
+
+#include <unistd.h>
+#include <qfile.h>
+
+
+KConfigRawBackEnd::KConfigRawBackEnd(KConfigBase *_config, int fd)
+ : KConfigINIBackEnd(_config, QString::null, "config", false),
+ _fd(fd), _stream(0)
+{
+ _file.open(IO_ReadOnly, _fd);
+}
+
+KConfigRawBackEnd::~KConfigRawBackEnd()
+{
+ if (_stream) fclose(_stream);
+}
+
+bool KConfigRawBackEnd::parseConfigFiles()
+{
+ _file.reset();
+ parseSingleConfigFile(_file);
+ return true;
+}
+
+void KConfigRawBackEnd::sync(bool bMerge)
+{
+ // write-sync is only necessary if there are dirty entries
+ if ( !pConfig->isDirty() || pConfig->isReadOnly() ) return;
+
+ _file.reset();
+ KEntryMap aTempMap;
+ getEntryMap(aTempMap, false, bMerge ? &_file : 0);
+
+ if ( _stream==0 ) {
+ _stream = fdopen(_fd, "w");
+ if ( _stream==0 ) return;
+ }
+ ftruncate(_fd, 0);
+ writeEntries(_stream, aTempMap);
+ fflush(_stream);
+}
diff --git a/libkdegames/highscore/kconfigrawbackend.h b/libkdegames/highscore/kconfigrawbackend.h
new file mode 100644
index 00000000..4b780320
--- /dev/null
+++ b/libkdegames/highscore/kconfigrawbackend.h
@@ -0,0 +1,57 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KCONFIGRAWBACKEND_H
+#define _KCONFIGRAWBACKEND_H
+
+#include <qfile.h>
+
+#include <kconfigbackend.h>
+#include <ksimpleconfig.h>
+
+
+class KConfigRawBackEnd : public KConfigINIBackEnd
+{
+public:
+ KConfigRawBackEnd(KConfigBase *_config, int fd);
+ ~KConfigRawBackEnd();
+
+ bool parseConfigFiles();
+
+ void sync(bool bMerge = true);
+
+private:
+ int _fd;
+ FILE *_stream;
+ QFile _file;
+
+ class KConfigRawBackEndPrivate;
+ KConfigRawBackEndPrivate *d;
+};
+
+class KRawConfig : public KSimpleConfig
+{
+ Q_OBJECT
+public:
+ KRawConfig(int fd, bool readOnly)
+ : KSimpleConfig(new KConfigRawBackEnd(this, fd), readOnly) {}
+};
+
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore.cpp b/libkdegames/highscore/kexthighscore.cpp
new file mode 100644
index 00000000..0ad9b3af
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore.cpp
@@ -0,0 +1,289 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore.h"
+
+#include <qlayout.h>
+
+#include <kdebug.h>
+
+#include "kexthighscore_internal.h"
+#include "kexthighscore_gui.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+ManagerPrivate *internal = 0;
+
+uint gameType()
+{
+ internal->checkFirst();
+ return internal->gameType();
+}
+
+void setGameType(uint type)
+{
+ internal->setGameType(type);
+}
+
+bool configure(QWidget *parent)
+{
+ internal->checkFirst();
+ ConfigDialog *cd = new ConfigDialog(parent);
+ cd->exec();
+ bool saved = cd->hasBeenSaved();
+ delete cd;
+ return saved;
+}
+
+void show(QWidget *parent, int rank)
+{
+ HighscoresDialog *hd = new HighscoresDialog(rank, parent);
+ hd->exec();
+ delete hd;
+}
+
+void submitScore(const Score &score, QWidget *widget)
+{
+ int rank = internal->submitScore(score, widget,
+ internal->showMode!=Manager::NeverShow);
+
+ switch (internal->showMode) {
+ case Manager::AlwaysShow:
+ show(widget, -1);
+ break;
+ case Manager::ShowForHigherScore:
+ if ( rank!=-1) show(widget, rank);
+ break;
+ case Manager::ShowForHighestScore:
+ if ( rank==0 ) show(widget, rank);
+ break;
+ case Manager::NeverShow:
+ break;
+ }
+}
+
+void show(QWidget *widget)
+{
+ internal->checkFirst();
+ show(widget, -1);
+}
+
+Score lastScore()
+{
+ internal->checkFirst();
+ internal->hsConfig().readCurrentConfig();
+ uint nb = internal->scoreInfos().maxNbEntries();
+ return internal->readScore(nb-1);
+}
+
+Score firstScore()
+{
+ internal->checkFirst();
+ internal->hsConfig().readCurrentConfig();
+ return internal->readScore(0);
+}
+
+
+//-----------------------------------------------------------------------------
+Manager::Manager(uint nbGameTypes, uint maxNbEntries)
+{
+ Q_ASSERT(nbGameTypes);
+ Q_ASSERT(maxNbEntries);
+ if (internal)
+ kdFatal(11002) << "A highscore object already exists" << endl;
+ internal = new ManagerPrivate(nbGameTypes, *this);
+ internal->init(maxNbEntries);
+}
+
+Manager::~Manager()
+{
+ delete internal;
+ internal = 0;
+}
+
+void Manager::setTrackLostGames(bool track)
+{
+ internal->trackLostGames = track;
+}
+
+void Manager::setTrackDrawGames(bool track)
+{
+ internal->trackDrawGames = track;
+}
+
+void Manager::setShowStatistics(bool show)
+{
+ internal->showStatistics = show;
+}
+
+void Manager::showStatistics(bool show)
+{
+ internal->showStatistics = show;
+}
+
+void Manager::setShowDrawGamesStatistic(bool show)
+{
+ internal->showDrawGames = show;
+}
+
+void Manager::setWWHighscores(const KURL &url, const QString &version)
+{
+ Q_ASSERT( url.isValid() );
+ internal->serverURL = url;
+ const char *HS_WW_URL = "ww hs url";
+ ConfigGroup cg;
+ if ( cg.config()->hasKey(HS_WW_URL) )
+ internal->serverURL = cg.config()->readEntry(HS_WW_URL);
+ else cg.config()->writeEntry(HS_WW_URL, url.url());
+ internal->version = version;
+}
+
+void Manager::setScoreHistogram(const QMemArray<uint> &scores,
+ ScoreTypeBound type)
+{
+ Q_ASSERT( scores.size()>=2 );
+ for (uint i=0; i<scores.size()-1; i++)
+ Q_ASSERT( scores[i]<scores[i+1] );
+ internal->playerInfos().createHistoItems(scores, type==ScoreBound);
+}
+
+void Manager::setShowMode(ShowMode mode)
+{
+ internal->showMode = mode;
+}
+
+void Manager::setScoreType(ScoreType type)
+{
+ switch (type) {
+ case Normal:
+ return;
+ case MinuteTime: {
+ Item *item = createItem(ScoreDefault);
+ item->setPrettyFormat(Item::MinuteTime);
+ setScoreItem(0, item);
+
+ item = createItem(MeanScoreDefault);
+ item->setPrettyFormat(Item::MinuteTime);
+ setPlayerItem(MeanScore, item);
+
+ item = createItem(BestScoreDefault);
+ item->setPrettyFormat(Item::MinuteTime);
+ setPlayerItem(BestScore, item);
+ return;
+ }
+ }
+}
+
+void Manager::submitLegacyScore(const Score &score) const
+{
+ internal->submitLocal(score);
+}
+
+bool Manager::isStrictlyLess(const Score &s1, const Score &s2) const
+{
+ return s1.score()<s2.score();
+}
+
+Item *Manager::createItem(ItemType type)
+{
+ Item *item = 0;
+ switch (type) {
+ case ScoreDefault:
+ item = new Item((uint)0, i18n("Score"), Qt::AlignRight);
+ break;
+ case MeanScoreDefault:
+ item = new Item((double)0, i18n("Mean Score"), Qt::AlignRight);
+ item->setPrettyFormat(Item::OneDecimal);
+ item->setPrettySpecial(Item::DefaultNotDefined);
+ break;
+ case BestScoreDefault:
+ item = new Item((uint)0, i18n("Best Score"), Qt::AlignRight);
+ item->setPrettySpecial(Item::DefaultNotDefined);
+ break;
+ case ElapsedTime:
+ item = new Item((uint)0, i18n("Elapsed Time"), Qt::AlignRight);
+ item->setPrettyFormat(Item::MinuteTime);
+ item->setPrettySpecial(Item::ZeroNotDefined);
+ break;
+ }
+ return item;
+}
+
+void Manager::setScoreItem(uint worstScore, Item *item)
+{
+ item->setDefaultValue(worstScore);
+ internal->scoreInfos().setItem("score", item);
+ internal->playerInfos().item("mean score")
+ ->item()->setDefaultValue(double(worstScore));
+ internal->playerInfos().item("best score")
+ ->item()->setDefaultValue(worstScore);
+}
+
+void Manager::addScoreItem(const QString &name, Item *item)
+{
+ internal->scoreInfos().addItem(name, item, true);
+}
+
+void Manager::setPlayerItem(PlayerItemType type, Item *item)
+{
+ const Item *scoreItem = internal->scoreInfos().item("score")->item();
+ uint def = scoreItem->defaultValue().toUInt();
+ QString name;
+ switch (type) {
+ case MeanScore:
+ name = "mean score";
+ item->setDefaultValue(double(def));
+ break;
+ case BestScore:
+ name = "best score";
+ item->setDefaultValue(def);
+ break;
+ }
+ internal->playerInfos().setItem(name, item);
+}
+
+QString Manager::gameTypeLabel(uint gameType, LabelType type) const
+{
+ if ( gameType!=0 )
+ kdFatal(11002) << "You need to reimplement KExtHighscore::Manager for "
+ << "multiple game types" << endl;
+ switch (type) {
+ case Icon:
+ case Standard:
+ case I18N: break;
+ case WW: return "normal";
+ }
+ return QString::null;
+}
+
+void Manager::addToQueryURL(KURL &url, const QString &item,
+ const QString &content)
+{
+ Q_ASSERT( !item.isEmpty() && url.queryItem(item).isNull() );
+
+ QString query = url.query();
+ if ( !query.isEmpty() ) query += '&';
+ query += item + '=' + KURL::encode_string(content);
+ url.setQuery(query);
+}
+
+} // namescape
diff --git a/libkdegames/highscore/kexthighscore.h b/libkdegames/highscore/kexthighscore.h
new file mode 100644
index 00000000..2484f97b
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore.h
@@ -0,0 +1,367 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_H
+#define KEXTHIGHSCORE_H
+
+#include "kexthighscore_item.h"
+
+#include <kurl.h>
+#include <kdemacros.h>
+
+class QTabWidget;
+
+
+namespace KExtHighscore
+{
+
+class Score;
+class Item;
+
+class ManagerPrivate;
+extern ManagerPrivate *internal;
+
+/**
+ * Get the current game type.
+ */
+KDE_EXPORT uint gameType();
+
+/**
+ * Set the current game type.
+ */
+KDE_EXPORT void setGameType(uint gameType);
+
+/**
+ * Configure the highscores.
+ * @return true if the configuration has been modified and saved
+ */
+KDE_EXPORT bool configure(QWidget *parent);
+
+/**
+ * Show the highscores lists.
+ */
+KDE_EXPORT void show(QWidget *parent);
+
+/**
+ * Submit a score. See @ref Manager for usage example.
+ *
+ * @param widget a widget used as parent for error message box.
+ */
+KDE_EXPORT void submitScore(const Score &score, QWidget *widget);
+
+/**
+ * @return the last score in the local list of highscores. The worst possible
+ * score if there are less items than the maximum number.
+ */
+KDE_EXPORT Score lastScore();
+
+/**
+ * @return the first score in the local list of highscores (the worst possible
+ * score if there is no entry).
+ */
+KDE_EXPORT Score firstScore();
+
+/**
+ * This class manages highscores and players entries (several players can
+ * share the same highscores list if the libkdegame library is built to
+ * support a common highscores file; NOTE that to correctly implement such
+ * feature we probably need a locking mechanism in @ref KHighscore).
+ *
+ * You need one instance of this class during the application lifetime ; in
+ * main() just insert
+ * \code
+ * KExtHighscore::Manager highscoresManager;
+ * \endcode
+ * with the needed arguments. Use the derived class if you need to
+ * reimplement some of the default methods.
+ *
+ * This class has three functions :
+ * <ul>
+ * <li> Update the highscores list when new entries are submitted </li>
+ * <li> Display the highscores list and the players list </li>
+ * <li> Send query to an optionnal web server to support world-wide
+ * highscores </li>
+ * </ul>
+ *
+ * The highscores and the players lists contain several items described by
+ * the @ref Item class.
+ *
+ * The highscores list contains by default :
+ * <ul>
+ * <li> the player name (automatically set from the config value)</li>
+ * <li> the score value </li>
+ * <li> the time and date of the highscore (automatically set) </li>
+ * </ul>
+ * You can replace the score item (for e.g. displaying it differently) with
+ * setScoreItem or add an item with addScoreItem.
+ *
+ * The players list contains :
+ * <ul>
+ * <li> the player name (as defined by the user in the configuration
+ * dialog) </li>
+ * <li> the number of games played </li>
+ * <li> the mean score </li>
+ * <li> the best score </li>
+ * <li> the best score time and date </li>
+ * <li> the player comment (as defined by the user in the
+ * configuration dialog) </li>
+ * </ul>
+ * You can replace the best score and the mean score items
+ * by calling setPlayerItem.
+ *
+ * To submit a new score at game end, just construct a Score, set the
+ * score data and then call submitScore().
+ * \code
+ * KExtHighscore::Score score(KExtHighscore::Won);
+ * score.setScore(myScore);
+ * KExtHighscore::submitScore(score, widget);
+ * \endcode
+ * You only need to set the score value with Score::setScore()
+ * and the value of the items that you have optionnally added
+ * with Score::setData() ; player name and date are set automatically.
+ */
+class KDE_EXPORT Manager
+{
+ public:
+ /**
+ * Constructor
+ *
+ * @param nbGameTypes the number of different game types (usually one).
+ * For example KMines has easy, normal and expert levels.
+ * @param maxNbEntries the maximum numbers of highscores entries (by game
+ * types)
+ */
+ Manager(uint nbGameTypes = 1, uint maxNbEntries = 10);
+ virtual ~Manager();
+
+ /**
+ * Set the world-wide highscores.
+ * By default there is no world-wide highscores.
+ *
+ * Note: should be called at construction time.
+ *
+ * @param url the web server url
+ * @param version the game version which is sent to the web server (it can
+ * be useful for backward compatibility on the server side).
+ */
+ void setWWHighscores(const KURL &url, const QString &version);
+
+ /**
+ * Set if the number of lost games should be track for the world-wide
+ * highscores statistics. By default, there is no tracking.
+ * False by default.
+ *
+ * Note: should be called at construction time.
+ */
+ void setTrackLostGames(bool track);
+
+ /**
+ * @since 3.3
+ * Set if the number of "draw" games should be track for the world-wide
+ * highscores statistics. By default, there is no tracking.
+ * False by default.
+ *
+ * Note: should be called at construction time.
+ */
+ void setTrackDrawGames(bool track);
+
+ /**
+ * @since 3.3
+ * Set if the statistics tab should be shown in the highscores dialog.
+ * You only want to show this tab if it makes sense to lose or to win the
+ * game (for e.g. it makes no sense for a tetris game but it does for a
+ * minesweeper game).
+ * False by default.
+ *
+ * Note: should be called at construction time.
+ */
+ void setShowStatistics(bool show);
+
+ /** @obsolete */
+ // KDE4 remove this
+ void showStatistics(bool show) KDE_DEPRECATED;
+
+ /**
+ * @since 3.3
+ * Set if draw games statistics should be shown (enable this if
+ * draws are possible in your game).
+ * False by default.
+ */
+ void setShowDrawGamesStatistic(bool show);
+
+ enum ScoreTypeBound { ScoreNotBound, ScoreBound };
+ /**
+ * Set the ranges for the score histogram.
+ *
+ * Note: should be called at construction time.
+ */
+ void setScoreHistogram(const QMemArray<uint> &scores, ScoreTypeBound type);
+
+ /**
+ * Enumerate different conditions under which to show the
+ * high score dialog.
+ */
+ enum ShowMode { AlwaysShow, ///< Always show the dialog
+ NeverShow, ///< Never show the dialog
+ ShowForHigherScore, ///< Show if score has improved
+ ShowForHighestScore ///< Only for the top spot
+ };
+ /**
+ * Set how the highscores dialog is shown at game end.
+ * By default, the mode is ShowForHigherScore.
+ *
+ * Note: should be called at construction time.
+ */
+ void setShowMode(ShowMode mode);
+
+ /**
+ * Score type (@see setScoreType).
+ * @p Normal default score (unsigned integer without upper bound)
+ * @p MinuteTime score by time bound at 3599 seconds (for e.g. kmines)
+ */
+ enum ScoreType { Normal, MinuteTime };
+ /**
+ * Set score type. Helper method to quickly set the type of score.
+ * By default the type is Normal.
+ *
+ * Note: should be called at construction time.
+ */
+ void setScoreType(ScoreType type);
+
+ /**
+ * Some predefined item types.
+ * @p ScoreDefault default item for the score in the highscores list.
+ * @p MeanScoreDefault default item for the mean score (only show one decimal and
+ * 0 is shown as "--".
+ * @p BestScoreDefault default item for the best score (0 is shown as "--").
+ * @p ElapsedTime optionnal item for elapsed time (maximum value is 3599 seconds).
+ */
+ enum ItemType { ScoreDefault, MeanScoreDefault, BestScoreDefault,
+ ElapsedTime };
+ /**
+ * Create a predefined item.
+ */
+ static Item *createItem(ItemType type);
+
+ /**
+ * Replace the default score item in the highscores list by the given one.
+ * @p worstScore is the worst possible score. By default it is 0.
+ *
+ * Note : This method should be called at construction time.
+ */
+ void setScoreItem(uint worstScore, Item *item);
+
+ /**
+ * Add an item in the highscores list (it will add a column to this list).
+ *
+ * Note : This method should be called at construction time.
+ */
+ void addScoreItem(const QString &name, Item *item);
+
+ enum PlayerItemType { MeanScore, BestScore };
+ /**
+ * Replace an item in the players list.
+ *
+ * Note : This method should be called at construction time.
+ */
+ void setPlayerItem(PlayerItemType type, Item *item);
+
+ /**
+ * @return true if the first score is strictly worse than the second one.
+ * By default return <pre>s1.score()<s2.score()</pre>. You can reimplement
+ * this method if additional items added to @ref Score can further
+ * differentiate the scores (for e.g. the time spent).
+ *
+ * Note that you do not need to use directly this method, simply write
+ * <pre>s1<s2</pre> since the operator calls this method.
+ */
+ virtual bool isStrictlyLess(const Score &s1, const Score &s2) const;
+
+ /**
+ * Possible type of label (@see gameTypeLabel).
+ * @p Standard label used in config file.
+ * @p I18N label used to display the game type.
+ * @p WW label used when contacting the world-wide highscores server.
+ * @p Icon label used to load the icon corresponding to the game type.
+ */
+ enum LabelType { Standard, I18N, WW, Icon };
+
+ /**
+ * @return the label corresponding to the game type. The default
+ * implementation works only for one game type : you need to reimplement
+ * this method if the number of game types is more than one.
+ */
+ virtual QString gameTypeLabel(uint gameType, LabelType type) const;
+
+ protected:
+ /**
+ * This method is called once for each player (ie for each user). You
+ * can reimplement it to convert old style highscores to the new mechanism
+ * (@see submitLegacyScore). By default this method does nothing.
+ *
+ * @param gameType the game type
+ */
+ virtual void convertLegacy(uint gameType) { Q_UNUSED(gameType); }
+
+ /**
+ * This method should be called from @ref convertLegacy. It is used
+ * to submit an old highscore (it will not be send over the network).
+ * For each score do something like:
+ * \code
+ * Score score(Won);
+ * score.setScore(oldScore);
+ * score.setData("name", name);
+ * submitLegacyScore(score);
+ * \endcode
+ * Note that here you can set the player "name" and the highscore "date"
+ * if they are known.
+ */
+ void submitLegacyScore(const Score &score) const;
+
+ /**
+ * This method is called before submitting a score to the world-wide
+ * highscores server. You can reimplement this method to add an entry
+ * with @ref addToQueryURL. By default this method does nothing.
+ *
+ * @param url the URL to query
+ * @param score the score to be submitted.
+ */
+ virtual void additionalQueryItems(KURL &url, const Score &score) const
+ { Q_UNUSED(url); Q_UNUSED(score); }
+
+ /**
+ * Add an entry to the url to be submitted (@see additionalQueryItems).
+ *
+ * @param url the URL to query
+ * @param item the item name
+ * @param content the item content
+ */
+ static void addToQueryURL(KURL &url, const QString &item,
+ const QString &content);
+
+ friend class ManagerPrivate;
+
+ private:
+ Manager(const Manager &);
+ Manager &operator =(const Manager &);
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_gui.cpp b/libkdegames/highscore/kexthighscore_gui.cpp
new file mode 100644
index 00000000..547a885c
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_gui.cpp
@@ -0,0 +1,552 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_gui.h"
+#include "kexthighscore_gui.moc"
+
+#include <qlayout.h>
+#include <qtextstream.h>
+#include <qheader.h>
+#include <qgrid.h>
+#include <qvgroupbox.h>
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kurllabel.h>
+#include <kopenwith.h>
+#include <krun.h>
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kiconloader.h>
+
+#include "kexthighscore_internal.h"
+#include "kexthighscore.h"
+#include "kexthighscore_tab.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+ShowItem::ShowItem(QListView *list, bool highlight)
+ : KListViewItem(list), _highlight(highlight)
+{}
+
+void ShowItem::paintCell(QPainter *p, const QColorGroup &cg,
+ int column, int width, int align)
+{
+ QColorGroup cgrp(cg);
+ if (_highlight) cgrp.setColor(QColorGroup::Text, red);
+ KListViewItem::paintCell(p, cgrp, column, width, align);
+}
+
+//-----------------------------------------------------------------------------
+ScoresList::ScoresList(QWidget *parent)
+ : KListView(parent)
+{
+ setSelectionMode(QListView::NoSelection);
+ setItemMargin(3);
+ setAllColumnsShowFocus(true);
+ setSorting(-1);
+ header()->setClickEnabled(false);
+ header()->setMovingEnabled(false);
+}
+
+void ScoresList::addHeader(const ItemArray &items)
+{
+ addLineItem(items, 0, 0);
+}
+
+QListViewItem *ScoresList::addLine(const ItemArray &items,
+ uint index, bool highlight)
+{
+ QListViewItem *item = new ShowItem(this, highlight);
+ addLineItem(items, index, item);
+ return item;
+}
+
+void ScoresList::addLineItem(const ItemArray &items,
+ uint index, QListViewItem *line)
+{
+ uint k = 0;
+ for (uint i=0; i<items.size(); i++) {
+ const ItemContainer &container = *items[i];
+ if ( !container.item()->isVisible() ) continue;
+ if (line) line->setText(k, itemText(container, index));
+ else {
+ addColumn( container.item()->label() );
+ setColumnAlignment(k, container.item()->alignment());
+ }
+ k++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+HighscoresList::HighscoresList(QWidget *parent)
+ : ScoresList(parent)
+{}
+
+QString HighscoresList::itemText(const ItemContainer &item, uint row) const
+{
+ return item.pretty(row);
+}
+
+void HighscoresList::load(const ItemArray &items, int highlight)
+{
+ clear();
+ QListViewItem *line = 0;
+ for (int j=items.nbEntries()-1; j>=0; j--) {
+ QListViewItem *item = addLine(items, j, j==highlight);
+ if ( j==highlight ) line = item;
+ }
+ if (line) ensureItemVisible(line);
+}
+
+//-----------------------------------------------------------------------------
+HighscoresWidget::HighscoresWidget(QWidget *parent)
+ : QWidget(parent, "show_highscores_widget"),
+ _scoresUrl(0), _playersUrl(0), _statsTab(0), _histoTab(0)
+{
+ const ScoreInfos &s = internal->scoreInfos();
+ const PlayerInfos &p = internal->playerInfos();
+
+ QVBoxLayout *vbox = new QVBoxLayout(this, KDialogBase::spacingHint());
+
+ _tw = new QTabWidget(this);
+ connect(_tw, SIGNAL(currentChanged(QWidget *)), SLOT(tabChanged()));
+ vbox->addWidget(_tw);
+
+ // scores tab
+ _scoresList = new HighscoresList(_tw);
+ _scoresList->addHeader(s);
+ _tw->addTab(_scoresList, i18n("Best &Scores"));
+
+ // players tab
+ _playersList = new HighscoresList(_tw);
+ _playersList->addHeader(p);
+ _tw->addTab(_playersList, i18n("&Players"));
+
+ // statistics tab
+ if ( internal->showStatistics ) {
+ _statsTab = new StatisticsTab(_tw);
+ _tw->addTab(_statsTab, i18n("Statistics"));
+ }
+
+ // histogram tab
+ if ( p.histogram().size()!=0 ) {
+ _histoTab = new HistogramTab(_tw);
+ _tw->addTab(_histoTab, i18n("Histogram"));
+ }
+
+ // url labels
+ if ( internal->isWWHSAvailable() ) {
+ KURL url = internal->queryURL(ManagerPrivate::Scores);
+ _scoresUrl = new KURLLabel(url.url(),
+ i18n("View world-wide highscores"), this);
+ connect(_scoresUrl, SIGNAL(leftClickedURL(const QString &)),
+ SLOT(showURL(const QString &)));
+ vbox->addWidget(_scoresUrl);
+
+ url = internal->queryURL(ManagerPrivate::Players);
+ _playersUrl = new KURLLabel(url.url(),
+ i18n("View world-wide players"), this);
+ connect(_playersUrl, SIGNAL(leftClickedURL(const QString &)),
+ SLOT(showURL(const QString &)));
+ vbox->addWidget(_playersUrl);
+ }
+}
+
+void HighscoresWidget::changeTab(int i)
+{
+ if ( i!=_tw->currentPageIndex() )
+ _tw->setCurrentPage(i);
+}
+
+void HighscoresWidget::showURL(const QString &url) const
+{
+ (void)new KRun(KURL(url));
+}
+
+void HighscoresWidget::load(int rank)
+{
+ _scoresList->load(internal->scoreInfos(), rank);
+ _playersList->load(internal->playerInfos(), internal->playerInfos().id());
+ if (_scoresUrl)
+ _scoresUrl->setURL(internal->queryURL(ManagerPrivate::Scores).url());
+ if (_playersUrl)
+ _playersUrl->setURL(internal->queryURL(ManagerPrivate::Players).url());
+ if (_statsTab) _statsTab->load();
+ if (_histoTab) _histoTab->load();
+}
+
+//-----------------------------------------------------------------------------
+HighscoresDialog::HighscoresDialog(int rank, QWidget *parent)
+ : KDialogBase(internal->nbGameTypes()>1 ? TreeList : Plain,
+ i18n("Highscores"), Close|User1|User2, Close,
+ parent, "show_highscores", true, true,
+ KGuiItem(i18n("Configure..."), "configure"),
+ KGuiItem(i18n("Export..."))), _rank(rank), _tab(0)
+{
+ _widgets.resize(internal->nbGameTypes(), 0);
+
+ if ( internal->nbGameTypes()>1 ) {
+ for (uint i=0; i<internal->nbGameTypes(); i++) {
+ QString title = internal->manager.gameTypeLabel(i, Manager::I18N);
+ QString icon = internal->manager.gameTypeLabel(i, Manager::Icon);
+ QWidget *w = addVBoxPage(title, QString::null,
+ BarIcon(icon, KIcon::SizeLarge));
+ if ( i==internal->gameType() ) createPage(w);
+ }
+
+ connect(this, SIGNAL(aboutToShowPage(QWidget *)),
+ SLOT(createPage(QWidget *)));
+ showPage(internal->gameType());
+ } else {
+ QVBoxLayout *vbox = new QVBoxLayout(plainPage());
+ createPage(plainPage());
+ vbox->addWidget(_widgets[0]);
+ setMainWidget(_widgets[0]);
+ }
+}
+
+void HighscoresDialog::createPage(QWidget *page)
+{
+ internal->hsConfig().readCurrentConfig();
+ _current = page;
+ bool several = ( internal->nbGameTypes()>1 );
+ int i = (several ? pageIndex(page) : 0);
+ if ( _widgets[i]==0 ) {
+ _widgets[i] = new HighscoresWidget(page);
+ connect(_widgets[i], SIGNAL(tabChanged(int)), SLOT(tabChanged(int)));
+ }
+ uint type = internal->gameType();
+ if (several) internal->setGameType(i);
+ _widgets[i]->load(uint(i)==type ? _rank : -1);
+ if (several) setGameType(type);
+ _widgets[i]->changeTab(_tab);
+}
+
+void HighscoresDialog::slotUser1()
+{
+ if ( KExtHighscore::configure(this) )
+ createPage(_current);
+}
+
+void HighscoresDialog::slotUser2()
+{
+ KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this);
+ if ( url.isEmpty() ) return;
+ if ( KIO::NetAccess::exists(url, true, this) ) {
+ KGuiItem gi = KStdGuiItem::save();
+ gi.setText(i18n("Overwrite"));
+ int res = KMessageBox::warningContinueCancel(this,
+ i18n("The file already exists. Overwrite?"),
+ i18n("Export"), gi);
+ if ( res==KMessageBox::Cancel ) return;
+ }
+ KTempFile tmp;
+ internal->exportHighscores(*tmp.textStream());
+ tmp.close();
+ KIO::NetAccess::upload(tmp.name(), url, this);
+ tmp.unlink();
+}
+
+//-----------------------------------------------------------------------------
+LastMultipleScoresList::LastMultipleScoresList(
+ const QValueVector<Score> &scores, QWidget *parent)
+ : ScoresList(parent), _scores(scores)
+{
+ const ScoreInfos &s = internal->scoreInfos();
+ addHeader(s);
+ for (uint i=0; i<scores.size(); i++) addLine(s, i, false);
+}
+
+void LastMultipleScoresList::addLineItem(const ItemArray &si,
+ uint index, QListViewItem *line)
+{
+ uint k = 1; // skip "id"
+ for (uint i=0; i<si.size()-2; i++) {
+ if ( i==3 ) k = 5; // skip "date"
+ const ItemContainer *container = si[k];
+ k++;
+ if (line) line->setText(i, itemText(*container, index));
+ else {
+ addColumn( container->item()->label() );
+ setColumnAlignment(i, container->item()->alignment());
+ }
+ }
+}
+
+QString LastMultipleScoresList::itemText(const ItemContainer &item,
+ uint row) const
+{
+ QString name = item.name();
+ if ( name=="rank" )
+ return (_scores[row].type()==Won ? i18n("Winner") : QString::null);
+ QVariant v = _scores[row].data(name);
+ if ( name=="name" ) return v.toString();
+ return item.item()->pretty(row, v);
+}
+
+//-----------------------------------------------------------------------------
+TotalMultipleScoresList::TotalMultipleScoresList(
+ const QValueVector<Score> &scores, QWidget *parent)
+ : ScoresList(parent), _scores(scores)
+{
+ const ScoreInfos &s = internal->scoreInfos();
+ addHeader(s);
+ for (uint i=0; i<scores.size(); i++) addLine(s, i, false);
+}
+
+void TotalMultipleScoresList::addLineItem(const ItemArray &si,
+ uint index, QListViewItem *line)
+{
+ const PlayerInfos &pi = internal->playerInfos();
+ uint k = 1; // skip "id"
+ for (uint i=0; i<4; i++) { // skip additional fields
+ const ItemContainer *container;
+ if ( i==2 ) container = pi.item("nb games");
+ else if ( i==3 ) container = pi.item("mean score");
+ else {
+ container = si[k];
+ k++;
+ }
+ if (line) line->setText(i, itemText(*container, index));
+ else {
+ QString label =
+ (i==2 ? i18n("Won Games") : container->item()->label());
+ addColumn(label);
+ setColumnAlignment(i, container->item()->alignment());
+ }
+ }
+}
+
+QString TotalMultipleScoresList::itemText(const ItemContainer &item,
+ uint row) const
+{
+ QString name = item.name();
+ if ( name=="rank" ) return QString::number(_scores.size()-row);
+ if ( name=="nb games" )
+ return QString::number( _scores[row].data("nb won games").toUInt() );
+ QVariant v = _scores[row].data(name);
+ if ( name=="name" ) return v.toString();
+ return item.item()->pretty(row, v);
+}
+
+
+//-----------------------------------------------------------------------------
+ConfigDialog::ConfigDialog(QWidget *parent)
+ : KDialogBase(Swallow, i18n("Configure Highscores"),
+ Ok|Apply|Cancel, Cancel,
+ parent, "configure_highscores", true, true),
+ _saved(false), _WWHEnabled(0)
+{
+ QWidget *page = 0;
+ QTabWidget *tab = 0;
+ if ( internal->isWWHSAvailable() ) {
+ tab = new QTabWidget(this);
+ setMainWidget(tab);
+ page = new QWidget(tab);
+ tab->addTab(page, i18n("Main"));
+ } else {
+ page = new QWidget(this);
+ setMainWidget(page);
+ }
+
+ QGridLayout *pageTop =
+ new QGridLayout(page, 2, 2, spacingHint(), spacingHint());
+
+ QLabel *label = new QLabel(i18n("Nickname:"), page);
+ pageTop->addWidget(label, 0, 0);
+ _nickname = new QLineEdit(page);
+ connect(_nickname, SIGNAL(textChanged(const QString &)),
+ SLOT(modifiedSlot()));
+ connect(_nickname, SIGNAL(textChanged(const QString &)),
+ SLOT(nickNameChanged(const QString &)));
+
+ _nickname->setMaxLength(16);
+ pageTop->addWidget(_nickname, 0, 1);
+
+ label = new QLabel(i18n("Comment:"), page);
+ pageTop->addWidget(label, 1, 0);
+ _comment = new QLineEdit(page);
+ connect(_comment, SIGNAL(textChanged(const QString &)),
+ SLOT(modifiedSlot()));
+ _comment->setMaxLength(50);
+ pageTop->addWidget(_comment, 1, 1);
+
+ if (tab) {
+ _WWHEnabled
+ = new QCheckBox(i18n("World-wide highscores enabled"), page);
+ connect(_WWHEnabled, SIGNAL(toggled(bool)),
+ SLOT(modifiedSlot()));
+ pageTop->addMultiCellWidget(_WWHEnabled, 2, 2, 0, 1);
+
+ // advanced tab
+ QWidget *page = new QWidget(tab);
+ tab->addTab(page, i18n("Advanced"));
+ QVBoxLayout *pageTop =
+ new QVBoxLayout(page, spacingHint(), spacingHint());
+
+ QVGroupBox *group = new QVGroupBox(i18n("Registration Data"), page);
+ pageTop->addWidget(group);
+ QGrid *grid = new QGrid(2, group);
+ grid->setSpacing(spacingHint());
+
+ label = new QLabel(i18n("Nickname:"), grid);
+ _registeredName = new KLineEdit(grid);
+ _registeredName->setReadOnly(true);
+
+ label = new QLabel(i18n("Key:"), grid);
+ _key = new KLineEdit(grid);
+ _key->setReadOnly(true);
+
+ KGuiItem gi = KStdGuiItem::clear();
+ gi.setText(i18n("Remove"));
+ _removeButton = new KPushButton(gi, grid);
+ connect(_removeButton, SIGNAL(clicked()), SLOT(removeSlot()));
+ }
+
+ load();
+ enableButtonOK( !_nickname->text().isEmpty() );
+ enableButtonApply(false);
+}
+
+void ConfigDialog::nickNameChanged(const QString &text)
+{
+ enableButtonOK( !text.isEmpty() );
+}
+
+
+void ConfigDialog::modifiedSlot()
+{
+ enableButtonApply(true && !_nickname->text().isEmpty() );
+}
+
+void ConfigDialog::accept()
+{
+ if ( save() ) {
+ KDialogBase::accept();
+ kapp->config()->sync(); // safer
+ }
+}
+
+void ConfigDialog::removeSlot()
+{
+ KGuiItem gi = KStdGuiItem::clear();
+ gi.setText(i18n("Remove"));
+ int res = KMessageBox::warningContinueCancel(this,
+ i18n("This will permanently remove your "
+ "registration key. You will not be able to use "
+ "the currently registered nickname anymore."),
+ QString::null, gi);
+ if ( res==KMessageBox::Continue ) {
+ internal->playerInfos().removeKey();
+ _registeredName->clear();
+ _key->clear();
+ _removeButton->setEnabled(false);
+ _WWHEnabled->setChecked(false);
+ modifiedSlot();
+ }
+}
+
+void ConfigDialog::load()
+{
+ internal->hsConfig().readCurrentConfig();
+ const PlayerInfos &infos = internal->playerInfos();
+ _nickname->setText(infos.isAnonymous() ? QString::null : infos.name());
+ _comment->setText(infos.comment());
+ if (_WWHEnabled) {
+ _WWHEnabled->setChecked(infos.isWWEnabled());
+ if ( !infos.key().isEmpty() ) {
+ _registeredName->setText(infos.registeredName());
+ _registeredName->home(false);
+ _key->setText(infos.key());
+ _key->home(false);
+ }
+ _removeButton->setEnabled(!infos.key().isEmpty());
+ }
+}
+
+bool ConfigDialog::save()
+{
+ bool enabled = (_WWHEnabled ? _WWHEnabled->isChecked() : false);
+
+ // do not bother the user with "nickname empty" if he has not
+ // messed with nickname settings ...
+ QString newName = _nickname->text();
+ if ( newName.isEmpty() && !internal->playerInfos().isAnonymous()
+ && !enabled ) return true;
+
+ if ( newName.isEmpty() ) {
+ KMessageBox::sorry(this, i18n("Please choose a non empty nickname."));
+ return false;
+ }
+ if ( internal->playerInfos().isNameUsed(newName) ) {
+ KMessageBox::sorry(this, i18n("Nickname already in use. Please "
+ "choose another one"));
+ return false;
+ }
+
+ int res =
+ internal->modifySettings(newName, _comment->text(), enabled, this);
+ if (res) {
+ load(); // needed to update view when "apply" is clicked
+ enableButtonApply(false);
+ }
+ _saved = true;
+ return res;
+}
+
+//-----------------------------------------------------------------------------
+AskNameDialog::AskNameDialog(QWidget *parent)
+ : KDialogBase(Plain, i18n("Enter Your Nickname"), Ok | Cancel, Ok,
+ parent, "ask_name_dialog")
+{
+ internal->hsConfig().readCurrentConfig();
+
+ QVBoxLayout *top =
+ new QVBoxLayout(plainPage(), marginHint(), spacingHint());
+ QLabel *label =
+ new QLabel(i18n("Congratulations, you have won!"), plainPage());
+ top->addWidget(label);
+
+ QHBoxLayout *hbox = new QHBoxLayout(top);
+ label = new QLabel(i18n("Enter your nickname:"), plainPage());
+ hbox->addWidget(label);
+ _edit = new QLineEdit(plainPage());
+ _edit->setFocus();
+ connect(_edit, SIGNAL(textChanged(const QString &)), SLOT(nameChanged()));
+ hbox->addWidget(_edit);
+
+ top->addSpacing(spacingHint());
+ _checkbox = new QCheckBox(i18n("Do not ask again."), plainPage());
+ top->addWidget(_checkbox);
+
+ nameChanged();
+}
+
+void AskNameDialog::nameChanged()
+{
+ enableButtonOK( !name().isEmpty()
+ && !internal->playerInfos().isNameUsed(name()) );
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_gui.h b/libkdegames/highscore/kexthighscore_gui.h
new file mode 100644
index 00000000..e721299a
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_gui.h
@@ -0,0 +1,207 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_GUI_H
+#define KEXTHIGHSCORE_GUI_H
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qvbox.h>
+#include <qtabwidget.h>
+
+#include <klistview.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kdialogbase.h>
+
+#include "kexthighscore.h"
+
+
+namespace KExtHighscore
+{
+
+class ItemContainer;
+class ItemArray;
+class Score;
+class AdditionalTab;
+
+//-----------------------------------------------------------------------------
+class ShowItem : public KListViewItem
+{
+ public:
+ ShowItem(QListView *, bool highlight);
+
+ protected:
+ virtual void paintCell(QPainter *, const QColorGroup &, int column,
+ int width, int align);
+
+ private:
+ bool _highlight;
+};
+
+class ScoresList : public KListView
+{
+ Q_OBJECT
+ public:
+ ScoresList(QWidget *parent);
+
+ void addHeader(const ItemArray &);
+
+ protected:
+ QListViewItem *addLine(const ItemArray &, uint index, bool highlight);
+ virtual QString itemText(const ItemContainer &, uint row) const = 0;
+
+ private:
+ virtual void addLineItem(const ItemArray &, uint index,
+ QListViewItem *item);
+};
+
+//-----------------------------------------------------------------------------
+class HighscoresList : public ScoresList
+{
+ Q_OBJECT
+ public:
+ HighscoresList(QWidget *parent);
+
+ void load(const ItemArray &, int highlight);
+
+ protected:
+ QString itemText(const ItemContainer &, uint row) const;
+};
+
+class HighscoresWidget : public QWidget
+{
+ Q_OBJECT
+ public:
+ HighscoresWidget(QWidget *parent);
+
+ void load(int rank);
+
+ signals:
+ void tabChanged(int i);
+
+ public slots:
+ void changeTab(int i);
+
+ private slots:
+ void showURL(const QString &) const;
+ void tabChanged() { emit tabChanged(_tw->currentPageIndex()); }
+
+ private:
+ QTabWidget *_tw;
+ HighscoresList *_scoresList, *_playersList;
+ KURLLabel *_scoresUrl, *_playersUrl;
+ AdditionalTab *_statsTab, *_histoTab;
+};
+
+class HighscoresDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ HighscoresDialog(int rank, QWidget *parent);
+
+ private slots:
+ void slotUser1();
+ void slotUser2();
+ void tabChanged(int i) { _tab = i; }
+ void createPage(QWidget *);
+
+ private:
+ int _rank, _tab;
+ QWidget *_current;
+ QValueVector<HighscoresWidget *> _widgets;
+};
+
+//-----------------------------------------------------------------------------
+class LastMultipleScoresList : public ScoresList
+{
+ Q_OBJECT
+public:
+ LastMultipleScoresList(const QValueVector<Score> &, QWidget *parent);
+
+private:
+ void addLineItem(const ItemArray &, uint index, QListViewItem *line);
+ QString itemText(const ItemContainer &, uint row) const;
+
+private:
+ const QValueVector<Score> &_scores;
+};
+
+class TotalMultipleScoresList : public ScoresList
+{
+ Q_OBJECT
+public:
+ TotalMultipleScoresList(const QValueVector<Score> &, QWidget *parent);
+
+private:
+ void addLineItem(const ItemArray &, uint index, QListViewItem *line);
+ QString itemText(const ItemContainer &, uint row) const;
+
+private:
+ const QValueVector<Score> &_scores;
+};
+
+//-----------------------------------------------------------------------------
+class ConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ ConfigDialog(QWidget *parent);
+
+ bool hasBeenSaved() const { return _saved; }
+
+ private slots:
+ void modifiedSlot();
+ void removeSlot();
+ void accept();
+ void slotApply() { save(); }
+ void nickNameChanged(const QString &);
+
+ private:
+ bool _saved;
+ QCheckBox *_WWHEnabled;
+ QLineEdit *_nickname, *_comment;
+ KLineEdit *_key, *_registeredName;
+ KPushButton *_removeButton;
+
+ void load();
+ bool save();
+};
+
+//-----------------------------------------------------------------------------
+class AskNameDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ AskNameDialog(QWidget *parent);
+
+ QString name() const { return _edit->text(); }
+ bool dontAskAgain() const { return _checkbox->isChecked(); }
+
+ private slots:
+ void nameChanged();
+
+ private:
+ QLineEdit *_edit;
+ QCheckBox *_checkbox;
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_internal.cpp b/libkdegames/highscore/kexthighscore_internal.cpp
new file mode 100644
index 00000000..a8395753
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_internal.cpp
@@ -0,0 +1,868 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_internal.h"
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <qfile.h>
+#include <qlayout.h>
+#include <qdom.h>
+
+#include <kglobal.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include <kmessagebox.h>
+#include <kmdcodec.h>
+#include <kdebug.h>
+
+#include "config.h"
+#include "kexthighscore.h"
+#include "kexthighscore_gui.h"
+#include "kemailsettings.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+const char ItemContainer::ANONYMOUS[] = "_";
+const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous");
+
+ItemContainer::ItemContainer()
+ : _item(0)
+{}
+
+ItemContainer::~ItemContainer()
+{
+ delete _item;
+}
+
+void ItemContainer::setItem(Item *item)
+{
+ delete _item;
+ _item = item;
+}
+
+QString ItemContainer::entryName() const
+{
+ if ( _subGroup.isEmpty() ) return _name;
+ return _name + "_" + _subGroup;
+}
+
+QVariant ItemContainer::read(uint i) const
+{
+ Q_ASSERT(_item);
+
+ QVariant v = _item->defaultValue();
+ if ( isStored() ) {
+ internal->hsConfig().setHighscoreGroup(_group);
+ v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v);
+ }
+ return _item->read(i, v);
+}
+
+QString ItemContainer::pretty(uint i) const
+{
+ Q_ASSERT(_item);
+ return _item->pretty(i, read(i));
+}
+
+void ItemContainer::write(uint i, const QVariant &value) const
+{
+ Q_ASSERT( isStored() );
+ Q_ASSERT( internal->hsConfig().isLocked() );
+ internal->hsConfig().setHighscoreGroup(_group);
+ internal->hsConfig().writeEntry(i+1, entryName(), value);
+}
+
+uint ItemContainer::increment(uint i) const
+{
+ uint v = read(i).toUInt() + 1;
+ write(i, v);
+ return v;
+}
+
+//-----------------------------------------------------------------------------
+ItemArray::ItemArray()
+ : _group(""), _subGroup("") // no null groups
+{}
+
+ItemArray::~ItemArray()
+{
+ for (uint i=0; i<size(); i++) delete at(i);
+}
+
+int ItemArray::findIndex(const QString &name) const
+{
+ for (uint i=0; i<size(); i++)
+ if ( at(i)->name()==name ) return i;
+ return -1;
+}
+
+const ItemContainer *ItemArray::item(const QString &name) const
+{
+ int i = findIndex(name);
+ if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+ << "\"" << endl;
+ return at(i);
+}
+
+ItemContainer *ItemArray::item(const QString &name)
+{
+ int i = findIndex(name);
+ if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+ << "\"" << endl;
+ return at(i);
+}
+
+void ItemArray::setItem(const QString &name, Item *item)
+{
+ int i = findIndex(name);
+ if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
+ << "\"" << endl;
+ bool stored = at(i)->isStored();
+ bool canHaveSubGroup = at(i)->canHaveSubGroup();
+ _setItem(i, name, item, stored, canHaveSubGroup);
+}
+
+void ItemArray::addItem(const QString &name, Item *item,
+ bool stored, bool canHaveSubGroup)
+{
+ if ( findIndex(name)!=-1 )
+ kdError(11002) << "item already exists \"" << name << "\"" << endl;
+ uint i = size();
+ resize(i+1);
+ at(i) = new ItemContainer;
+ _setItem(i, name, item, stored, canHaveSubGroup);
+}
+
+void ItemArray::_setItem(uint i, const QString &name, Item *item,
+ bool stored, bool canHaveSubGroup)
+{
+ at(i)->setItem(item);
+ at(i)->setName(name);
+ at(i)->setGroup(stored ? _group : QString::null);
+ at(i)->setSubGroup(canHaveSubGroup ? _subGroup : QString::null);
+}
+
+void ItemArray::setGroup(const QString &group)
+{
+ Q_ASSERT( !group.isNull() );
+ _group = group;
+ for (uint i=0; i<size(); i++)
+ if ( at(i)->isStored() ) at(i)->setGroup(group);
+}
+
+void ItemArray::setSubGroup(const QString &subGroup)
+{
+ Q_ASSERT( !subGroup.isNull() );
+ _subGroup = subGroup;
+ for (uint i=0; i<size(); i++)
+ if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup);
+}
+
+void ItemArray::read(uint k, Score &data) const
+{
+ for (uint i=0; i<size(); i++) {
+ if ( !at(i)->isStored() ) continue;
+ data.setData(at(i)->name(), at(i)->read(k));
+ }
+}
+
+void ItemArray::write(uint k, const Score &data, uint nb) const
+{
+ for (uint i=0; i<size(); i++) {
+ if ( !at(i)->isStored() ) continue;
+ for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1));
+ at(i)->write(k, data.data(at(i)->name()));
+ }
+}
+
+void ItemArray::exportToText(QTextStream &s) const
+{
+ for (uint k=0; k<nbEntries()+1; k++) {
+ for (uint i=0; i<size(); i++) {
+ const Item *item = at(i)->item();
+ if ( item->isVisible() ) {
+ if ( i!=0 ) s << '\t';
+ if ( k==0 ) s << item->label();
+ else s << at(i)->pretty(k-1);
+ }
+ }
+ s << endl;
+ }
+}
+
+//-----------------------------------------------------------------------------
+class ScoreNameItem : public NameItem
+{
+ public:
+ ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos)
+ : _score(score), _infos(infos) {}
+
+ QString pretty(uint i, const QVariant &v) const {
+ uint id = _score.item("id")->read(i).toUInt();
+ if ( id==0 ) return NameItem::pretty(i, v);
+ return _infos.prettyName(id-1);
+ }
+
+ private:
+ const ScoreInfos &_score;
+ const PlayerInfos &_infos;
+};
+
+//-----------------------------------------------------------------------------
+ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos)
+ : _maxNbEntries(maxNbEntries)
+{
+ addItem("id", new Item((uint)0));
+ addItem("rank", new RankItem, false);
+ addItem("name", new ScoreNameItem(*this, infos));
+ addItem("score", Manager::createItem(Manager::ScoreDefault));
+ addItem("date", new DateItem);
+}
+
+uint ScoreInfos::nbEntries() const
+{
+ uint i = 0;
+ for (; i<_maxNbEntries; i++)
+ if ( item("score")->read(i)==item("score")->item()->defaultValue() )
+ break;
+ return i;
+}
+
+//-----------------------------------------------------------------------------
+const char *HS_ID = "player id";
+const char *HS_REGISTERED_NAME = "registered name";
+const char *HS_KEY = "player key";
+const char *HS_WW_ENABLED = "ww hs enabled";
+
+PlayerInfos::PlayerInfos()
+{
+ setGroup("players");
+
+ // standard items
+ addItem("name", new NameItem);
+ Item *it = new Item((uint)0, i18n("Games Count"),Qt::AlignRight);
+ addItem("nb games", it, true, true);
+ it = Manager::createItem(Manager::MeanScoreDefault);
+ addItem("mean score", it, true, true);
+ it = Manager::createItem(Manager::BestScoreDefault);
+ addItem("best score", it, true, true);
+ addItem("date", new DateItem, true, true);
+ it = new Item(QString::null, i18n("Comment"), Qt::AlignLeft);
+ addItem("comment", it);
+
+ // statistics items
+ addItem("nb black marks", new Item((uint)0), true, true); // legacy
+ addItem("nb lost games", new Item((uint)0), true, true);
+ addItem("nb draw games", new Item((uint)0), true, true);
+ addItem("current trend", new Item((int)0), true, true);
+ addItem("max lost trend", new Item((uint)0), true, true);
+ addItem("max won trend", new Item((uint)0), true, true);
+
+ struct passwd *pwd = getpwuid(getuid());
+ QString username = pwd->pw_name;
+#ifdef HIGHSCORE_DIRECTORY
+ internal->hsConfig().setHighscoreGroup("players");
+ for (uint i=0; ;i++) {
+ if ( !internal->hsConfig().hasEntry(i+1, "username") ) {
+ _newPlayer = true;
+ _id = i;
+ break;
+ }
+ if ( internal->hsConfig().readEntry(i+1, "username")==username ) {
+ _newPlayer = false;
+ _id = i;
+ return;
+ }
+ }
+#endif
+ internal->hsConfig().lockForWriting();
+ KEMailSettings emailConfig;
+ emailConfig.setProfile(emailConfig.defaultProfileName());
+ QString name = emailConfig.getSetting(KEMailSettings::RealName);
+ if ( name.isEmpty() || isNameUsed(name) ) name = username;
+ if ( isNameUsed(name) ) name= QString(ItemContainer::ANONYMOUS);
+#ifdef HIGHSCORE_DIRECTORY
+ internal->hsConfig().writeEntry(_id+1, "username", username);
+ item("name")->write(_id, name);
+#endif
+
+ ConfigGroup cg;
+ _oldLocalPlayer = cg.config()->hasKey(HS_ID);
+ _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID);
+#ifdef HIGHSCORE_DIRECTORY
+ if (_oldLocalPlayer) { // player already exists in local config file
+ // copy player data
+ QString prefix = QString("%1_").arg(_oldLocalId+1);
+ QMap<QString, QString> entries =
+ cg.config()->entryMap("KHighscore_players");
+ QMap<QString, QString>::const_iterator it;
+ for (it=entries.begin(); it!=entries.end(); ++it) {
+ QString key = it.key();
+ if ( key.find(prefix)==0 ) {
+ QString name = key.right(key.length()-prefix.length());
+ if ( name!="name" || !isNameUsed(it.data()) )
+ internal->hsConfig().writeEntry(_id+1, name, it.data());
+ }
+ }
+ }
+#else
+ _newPlayer = !_oldLocalPlayer;
+ if (_oldLocalPlayer) _id = _oldLocalId;
+ else {
+ _id = nbEntries();
+ cg.config()->writeEntry(HS_ID, _id);
+ item("name")->write(_id, name);
+ }
+#endif
+ _bound = true;
+ internal->hsConfig().writeAndUnlock();
+}
+
+void PlayerInfos::createHistoItems(const QMemArray<uint> &scores, bool bound)
+{
+ Q_ASSERT( _histogram.size()==0 );
+ _bound = bound;
+ _histogram = scores;
+ for (uint i=1; i<histoSize(); i++)
+ addItem(histoName(i), new Item((uint)0), true, true);
+}
+
+bool PlayerInfos::isAnonymous() const
+{
+ return ( name()==ItemContainer::ANONYMOUS );
+}
+
+uint PlayerInfos::nbEntries() const
+{
+ internal->hsConfig().setHighscoreGroup("players");
+ QStringList list = internal->hsConfig().readList("name", -1);
+ return list.count();
+}
+
+QString PlayerInfos::key() const
+{
+ ConfigGroup cg;
+ return cg.config()->readEntry(HS_KEY, QString::null);
+}
+
+bool PlayerInfos::isWWEnabled() const
+{
+ ConfigGroup cg;
+ return cg.config()->readBoolEntry(HS_WW_ENABLED, false);
+}
+
+QString PlayerInfos::histoName(uint i) const
+{
+ const QMemArray<uint> &sh = _histogram;
+ Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) );
+ if ( i==sh.size() )
+ return QString("nb scores greater than %1").arg(sh[sh.size()-1]);
+ return QString("nb scores less than %1").arg(sh[i]);
+}
+
+uint PlayerInfos::histoSize() const
+{
+ return _histogram.size() + (_bound ? 0 : 1);
+}
+
+void PlayerInfos::submitScore(const Score &score) const
+{
+ // update counts
+ uint nbGames = item("nb games")->increment(_id);
+ switch (score.type()) {
+ case Lost:
+ item("nb lost games")->increment(_id);
+ break;
+ case Won: break;
+ case Draw:
+ item("nb draw games")->increment(_id);
+ break;
+ };
+
+ // update mean
+ if ( score.type()==Won ) {
+ uint nbWonGames = nbGames - item("nb lost games")->read(_id).toUInt()
+ - item("nb draw games")->read(_id).toUInt()
+ - item("nb black marks")->read(_id).toUInt(); // legacy
+ double mean = (nbWonGames==1 ? 0.0
+ : item("mean score")->read(_id).toDouble());
+ mean += (double(score.score()) - mean) / nbWonGames;
+ item("mean score")->write(_id, mean);
+ }
+
+ // update best score
+ Score best = score; // copy optionnal fields (there are not taken into account here)
+ best.setScore( item("best score")->read(_id).toUInt() );
+ if ( best<score ) {
+ item("best score")->write(_id, score.score());
+ item("date")->write(_id, score.data("date").toDateTime());
+ }
+
+ // update trends
+ int current = item("current trend")->read(_id).toInt();
+ switch (score.type()) {
+ case Won: {
+ if ( current<0 ) current = 0;
+ current++;
+ uint won = item("max won trend")->read(_id).toUInt();
+ if ( (uint)current>won ) item("max won trend")->write(_id, current);
+ break;
+ }
+ case Lost: {
+ if ( current>0 ) current = 0;
+ current--;
+ uint lost = item("max lost trend")->read(_id).toUInt();
+ uint clost = -current;
+ if ( clost>lost ) item("max lost trend")->write(_id, clost);
+ break;
+ }
+ case Draw:
+ current = 0;
+ break;
+ }
+ item("current trend")->write(_id, current);
+
+ // update histogram
+ if ( score.type()==Won ) {
+ const QMemArray<uint> &sh = _histogram;
+ for (uint i=1; i<histoSize(); i++)
+ if ( i==sh.size() || score.score()<sh[i] ) {
+ item(histoName(i))->increment(_id);
+ break;
+ }
+ }
+}
+
+bool PlayerInfos::isNameUsed(const QString &newName) const
+{
+ if ( newName==name() ) return false; // own name...
+ for (uint i=0; i<nbEntries(); i++)
+ if ( newName.lower()==item("name")->read(i).toString().lower() ) return true;
+ if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) ) return true;
+ return false;
+}
+
+void PlayerInfos::modifyName(const QString &newName) const
+{
+ item("name")->write(_id, newName);
+}
+
+void PlayerInfos::modifySettings(const QString &newName,
+ const QString &comment, bool WWEnabled,
+ const QString &newKey) const
+{
+ modifyName(newName);
+ item("comment")->write(_id, comment);
+ ConfigGroup cg;
+ cg.config()->writeEntry(HS_WW_ENABLED, WWEnabled);
+ if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey);
+ if (WWEnabled) cg.config()->writeEntry(HS_REGISTERED_NAME, newName);
+}
+
+QString PlayerInfos::registeredName() const
+{
+ ConfigGroup cg;
+ return cg.config()->readEntry(HS_REGISTERED_NAME, QString::null);
+}
+
+void PlayerInfos::removeKey()
+{
+ ConfigGroup cg;
+
+ // save old key/nickname
+ uint i = 0;
+ QString str = "%1 old #%2";
+ QString sk;
+ do {
+ i++;
+ sk = str.arg(HS_KEY).arg(i);
+ } while ( !cg.config()->readEntry(sk, QString::null).isEmpty() );
+ cg.config()->writeEntry(sk, key());
+ cg.config()->writeEntry(str.arg(HS_REGISTERED_NAME).arg(i),
+ registeredName());
+
+ // clear current key/nickname
+ cg.config()->deleteEntry(HS_KEY);
+ cg.config()->deleteEntry(HS_REGISTERED_NAME);
+ cg.config()->writeEntry(HS_WW_ENABLED, false);
+}
+
+//-----------------------------------------------------------------------------
+ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m)
+ : manager(m), showStatistics(false), showDrawGames(false),
+ trackLostGames(false), trackDrawGames(false),
+ showMode(Manager::ShowForHigherScore),
+ _first(true), _nbGameTypes(nbGameTypes), _gameType(0)
+{}
+
+void ManagerPrivate::init(uint maxNbEntries)
+{
+ _hsConfig = new KHighscore(false, 0);
+ _playerInfos = new PlayerInfos;
+ _scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos);
+}
+
+ManagerPrivate::~ManagerPrivate()
+{
+ delete _scoreInfos;
+ delete _playerInfos;
+ delete _hsConfig;
+}
+
+KURL ManagerPrivate::queryURL(QueryType type, const QString &newName) const
+{
+ KURL url = serverURL;
+ QString nameItem = "nickname";
+ QString name = _playerInfos->registeredName();
+ bool withVersion = true;
+ bool key = false;
+ bool level = false;
+
+ switch (type) {
+ case Submit:
+ url.addPath("submit.php");
+ level = true;
+ key = true;
+ break;
+ case Register:
+ url.addPath("register.php");
+ name = newName;
+ break;
+ case Change:
+ url.addPath("change.php");
+ key = true;
+ if ( newName!=name )
+ Manager::addToQueryURL(url, "new_nickname", newName);
+ break;
+ case Players:
+ url.addPath("players.php");
+ nameItem = "highlight";
+ withVersion = false;
+ break;
+ case Scores:
+ url.addPath("highscores.php");
+ withVersion = false;
+ if ( _nbGameTypes>1 ) level = true;
+ break;
+ }
+
+ if (withVersion) Manager::addToQueryURL(url, "version", version);
+ if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name);
+ if (key) Manager::addToQueryURL(url, "key", _playerInfos->key());
+ if (level) {
+ QString label = manager.gameTypeLabel(_gameType, Manager::WW);
+ if ( !label.isEmpty() ) Manager::addToQueryURL(url, "level", label);
+ }
+
+ return url;
+}
+
+// strings that needs to be translated (coming from the highscores server)
+const char *DUMMY_STRINGS[] = {
+ I18N_NOOP("Undefined error."),
+ I18N_NOOP("Missing argument(s)."),
+ I18N_NOOP("Invalid argument(s)."),
+
+ I18N_NOOP("Unable to connect to MySQL server."),
+ I18N_NOOP("Unable to select database."),
+ I18N_NOOP("Error on database query."),
+ I18N_NOOP("Error on database insert."),
+
+ I18N_NOOP("Nickname already registered."),
+ I18N_NOOP("Nickname not registered."),
+ I18N_NOOP("Invalid key."),
+ I18N_NOOP("Invalid submit key."),
+
+ I18N_NOOP("Invalid level."),
+ I18N_NOOP("Invalid score.")
+};
+
+const char *UNABLE_TO_CONTACT =
+ I18N_NOOP("Unable to contact world-wide highscore server");
+
+bool ManagerPrivate::doQuery(const KURL &url, QWidget *parent,
+ QDomNamedNodeMap *map)
+{
+ KIO::http_update_cache(url, true, 0); // remove cache !
+
+ QString tmpFile;
+ if ( !KIO::NetAccess::download(url, tmpFile, parent) ) {
+ QString details = i18n("Server URL: %1").arg(url.host());
+ KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
+ return false;
+ }
+
+ QFile file(tmpFile);
+ if ( !file.open(IO_ReadOnly) ) {
+ KIO::NetAccess::removeTempFile(tmpFile);
+ QString details = i18n("Unable to open temporary file.");
+ KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
+ return false;
+ }
+
+ QTextStream t(&file);
+ QString content = t.read().stripWhiteSpace();
+ file.close();
+ KIO::NetAccess::removeTempFile(tmpFile);
+
+ QDomDocument doc;
+ if ( doc.setContent(content) ) {
+ QDomElement root = doc.documentElement();
+ QDomElement element = root.firstChild().toElement();
+ if ( element.tagName()=="success" ) {
+ if (map) *map = element.attributes();
+ return true;
+ }
+ if ( element.tagName()=="error" ) {
+ QDomAttr attr = element.attributes().namedItem("label").toAttr();
+ if ( !attr.isNull() ) {
+ QString msg = i18n(attr.value().latin1());
+ QString caption = i18n("Message from world-wide highscores "
+ "server");
+ KMessageBox::sorry(parent, msg, caption);
+ return false;
+ }
+ }
+ }
+ QString msg = i18n("Invalid answer from world-wide highscores server.");
+ QString details = i18n("Raw message: %1").arg(content);
+ KMessageBox::detailedSorry(parent, msg, details);
+ return false;
+}
+
+bool ManagerPrivate::getFromQuery(const QDomNamedNodeMap &map,
+ const QString &name, QString &value,
+ QWidget *parent)
+{
+ QDomAttr attr = map.namedItem(name).toAttr();
+ if ( attr.isNull() ) {
+ KMessageBox::sorry(parent,
+ i18n("Invalid answer from world-wide "
+ "highscores server (missing item: %1).").arg(name));
+ return false;
+ }
+ value = attr.value();
+ return true;
+}
+
+Score ManagerPrivate::readScore(uint i) const
+{
+ Score score(Won);
+ _scoreInfos->read(i, score);
+ return score;
+}
+
+int ManagerPrivate::rank(const Score &score) const
+{
+ uint nb = _scoreInfos->nbEntries();
+ uint i = 0;
+ for (; i<nb; i++)
+ if ( readScore(i)<score ) break;
+ return (i<_scoreInfos->maxNbEntries() ? (int)i : -1);
+}
+
+bool ManagerPrivate::modifySettings(const QString &newName,
+ const QString &comment, bool WWEnabled,
+ QWidget *widget)
+{
+ QString newKey;
+ bool newPlayer = false;
+
+ if (WWEnabled) {
+ newPlayer = _playerInfos->key().isEmpty()
+ || _playerInfos->registeredName().isEmpty();
+ KURL url = queryURL((newPlayer ? Register : Change), newName);
+ Manager::addToQueryURL(url, "comment", comment);
+
+ QDomNamedNodeMap map;
+ bool ok = doQuery(url, widget, &map);
+ if ( !ok || (newPlayer && !getFromQuery(map, "key", newKey, widget)) )
+ return false;
+ }
+
+ bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking
+ if (ok) {
+ // check again name in case the config file has been changed...
+ // if it has, it is unfortunate because the WWW name is already
+ // committed but should be very rare and not really problematic
+ ok = ( !_playerInfos->isNameUsed(newName) );
+ if (ok)
+ _playerInfos->modifySettings(newName, comment, WWEnabled, newKey);
+ _hsConfig->writeAndUnlock();
+ }
+ return ok;
+}
+
+void ManagerPrivate::convertToGlobal()
+{
+ // read old highscores
+ KHighscore *tmp = _hsConfig;
+ _hsConfig = new KHighscore(true, 0);
+ QValueVector<Score> scores(_scoreInfos->nbEntries());
+ for (uint i=0; i<scores.count(); i++)
+ scores[i] = readScore(i);
+
+ // commit them
+ delete _hsConfig;
+ _hsConfig = tmp;
+ _hsConfig->lockForWriting();
+ for (uint i=0; i<scores.count(); i++)
+ if ( scores[i].data("id").toUInt()==_playerInfos->oldLocalId()+1 )
+ submitLocal(scores[i]);
+ _hsConfig->writeAndUnlock();
+}
+
+void ManagerPrivate::setGameType(uint type)
+{
+ if (_first) {
+ _first = false;
+ if ( _playerInfos->isNewPlayer() ) {
+ // convert legacy highscores
+ for (uint i=0; i<_nbGameTypes; i++) {
+ setGameType(i);
+ manager.convertLegacy(i);
+ }
+
+#ifdef HIGHSCORE_DIRECTORY
+ if ( _playerInfos->isOldLocalPlayer() ) {
+ // convert local to global highscores
+ for (uint i=0; i<_nbGameTypes; i++) {
+ setGameType(i);
+ convertToGlobal();
+ }
+ }
+#endif
+ }
+ }
+
+ Q_ASSERT( type<_nbGameTypes );
+ _gameType = kMin(type, _nbGameTypes-1);
+ QString str = "scores";
+ QString lab = manager.gameTypeLabel(_gameType, Manager::Standard);
+ if ( !lab.isEmpty() ) {
+ _playerInfos->setSubGroup(lab);
+ str += "_" + lab;
+ }
+ _scoreInfos->setGroup(str);
+}
+
+void ManagerPrivate::checkFirst()
+{
+ if (_first) setGameType(0);
+}
+
+int ManagerPrivate::submitScore(const Score &ascore,
+ QWidget *widget, bool askIfAnonymous)
+{
+ checkFirst();
+
+ Score score = ascore;
+ score.setData("id", _playerInfos->id() + 1);
+ score.setData("date", QDateTime::currentDateTime());
+
+ // ask new name if anonymous and winner
+ const char *dontAskAgainName = "highscore_ask_name_dialog";
+ QString newName;
+ KMessageBox::ButtonCode dummy;
+ if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous()
+ && KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) {
+ AskNameDialog d(widget);
+ if ( d.exec()==QDialog::Accepted ) newName = d.name();
+ if ( d.dontAskAgain() )
+ KMessageBox::saveDontShowAgainYesNo(dontAskAgainName,
+ KMessageBox::No);
+ }
+
+ int rank = -1;
+ if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking
+ // check again new name in case the config file has been changed...
+ if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) )
+ _playerInfos->modifyName(newName);
+
+ // commit locally
+ _playerInfos->submitScore(score);
+ if ( score.type()==Won ) rank = submitLocal(score);
+ _hsConfig->writeAndUnlock();
+ }
+
+ if ( _playerInfos->isWWEnabled() )
+ submitWorldWide(score, widget);
+
+ return rank;
+}
+
+int ManagerPrivate::submitLocal(const Score &score)
+{
+ int r = rank(score);
+ if ( r!=-1 ) {
+ uint nb = _scoreInfos->nbEntries();
+ if ( nb<_scoreInfos->maxNbEntries() ) nb++;
+ _scoreInfos->write(r, score, nb);
+ }
+ return r;
+}
+
+bool ManagerPrivate::submitWorldWide(const Score &score,
+ QWidget *widget) const
+{
+ if ( score.type()==Lost && !trackLostGames ) return true;
+ if ( score.type()==Draw && !trackDrawGames ) return true;
+
+ KURL url = queryURL(Submit);
+ manager.additionalQueryItems(url, score);
+ int s = (score.type()==Won ? score.score() : (int)score.type());
+ QString str = QString::number(s);
+ Manager::addToQueryURL(url, "score", str);
+ KMD5 context(QString(_playerInfos->registeredName() + str).latin1());
+ Manager::addToQueryURL(url, "check", context.hexDigest());
+
+ return doQuery(url, widget);
+}
+
+void ManagerPrivate::exportHighscores(QTextStream &s)
+{
+ uint tmp = _gameType;
+
+ for (uint i=0; i<_nbGameTypes; i++) {
+ setGameType(i);
+ if ( _nbGameTypes>1 ) {
+ if ( i!=0 ) s << endl;
+ s << "--------------------------------" << endl;
+ s << "Game type: "
+ << manager.gameTypeLabel(_gameType, Manager::I18N)
+ << endl;
+ s << endl;
+ }
+ s << "Players list:" << endl;
+ _playerInfos->exportToText(s);
+ s << endl;
+ s << "Highscores list:" << endl;
+ _scoreInfos->exportToText(s);
+ }
+
+ setGameType(tmp);
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_internal.h b/libkdegames/highscore/kexthighscore_internal.h
new file mode 100644
index 00000000..3b206877
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_internal.h
@@ -0,0 +1,277 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_INTERNAL_H
+#define KEXTHIGHSCORE_INTERNAL_H
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include "khighscore.h"
+#include "kexthighscore.h"
+
+class QTextStream;
+class QTabWidget;
+class QDomNamedNodeMap;
+
+
+namespace KExtHighscore
+{
+
+class PlayerInfos;
+class Score;
+class Manager;
+
+
+//-----------------------------------------------------------------------------
+class RankItem : public Item
+{
+ public:
+ RankItem()
+ : Item((uint)0, i18n("Rank"), Qt::AlignRight) {}
+
+ QVariant read(uint rank, const QVariant &) const { return rank; }
+ QString pretty(uint rank, const QVariant &) const
+ { return QString::number(rank+1); }
+};
+
+class NameItem : public Item
+{
+ public:
+ NameItem()
+ : Item(QString::null, i18n("Name"), Qt::AlignLeft) {
+ setPrettySpecial(Anonymous);
+ }
+};
+
+class DateItem : public Item
+{
+ public:
+ DateItem()
+ : Item(QDateTime(), i18n("Date"), Qt::AlignRight) {
+ setPrettyFormat(DateTime);
+ }
+};
+
+class SuccessPercentageItem : public Item
+{
+ public:
+ SuccessPercentageItem()
+ : Item((double)-1, i18n("Success"), Qt::AlignRight) {
+ setPrettyFormat(Percentage);
+ setPrettySpecial(NegativeNotDefined);
+ }
+};
+
+//-----------------------------------------------------------------------------
+class ItemContainer
+{
+ public:
+ ItemContainer();
+ ~ItemContainer();
+
+ void setItem(Item *item);
+ const Item *item() const { return _item; }
+ Item *item() { return _item; }
+
+ void setName(const QString &name) { _name = name; }
+ QString name() const { return _name; }
+
+ void setGroup(const QString &group) { _group = group; }
+ bool isStored() const { return !_group.isNull(); }
+
+ void setSubGroup(const QString &subGroup) { _subGroup = subGroup; }
+ bool canHaveSubGroup() const { return !_subGroup.isNull(); }
+
+ static const char ANONYMOUS[]; // name assigned to anonymous players
+ static const char ANONYMOUS_LABEL[];
+
+ QVariant read(uint i) const;
+ QString pretty(uint i) const;
+ void write(uint i, const QVariant &value) const;
+ // for UInt QVariant (return new value)
+ uint increment(uint i) const;
+
+ private:
+ Item *_item;
+ QString _name, _group, _subGroup;
+
+ QString entryName() const;
+
+ ItemContainer(const ItemContainer &);
+ ItemContainer &operator =(const ItemContainer &);
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * Manage a bunch of @ref Item which are saved under the same group
+ * in KHighscores config file.
+ */
+class ItemArray : public QMemArray<ItemContainer *>
+{
+ public:
+ ItemArray();
+ virtual ~ItemArray();
+
+ virtual uint nbEntries() const = 0;
+
+ const ItemContainer *item(const QString &name) const;
+ ItemContainer *item(const QString &name);
+
+ void addItem(const QString &name, Item *, bool stored = true,
+ bool canHaveSubGroup = false);
+ void setItem(const QString &name, Item *);
+ int findIndex(const QString &name) const;
+
+ void setGroup(const QString &group);
+ void setSubGroup(const QString &subGroup);
+
+ void read(uint k, Score &data) const;
+ void write(uint k, const Score &data, uint maxNbLines) const;
+
+ void exportToText(QTextStream &) const;
+
+ private:
+ QString _group, _subGroup;
+
+ void _setItem(uint i, const QString &name, Item *, bool stored,
+ bool canHaveSubGroup);
+
+ ItemArray(const ItemArray &);
+ ItemArray &operator =(const ItemArray &);
+};
+
+//-----------------------------------------------------------------------------
+class ScoreInfos : public ItemArray
+{
+ public:
+ ScoreInfos(uint maxNbEntries, const PlayerInfos &infos);
+
+ uint nbEntries() const;
+ uint maxNbEntries() const { return _maxNbEntries; }
+
+ private:
+ uint _maxNbEntries;
+};
+
+//-----------------------------------------------------------------------------
+class ConfigGroup : public KConfigGroupSaver
+{
+ public:
+ ConfigGroup(const QString &group = QString::null)
+ : KConfigGroupSaver(kapp->config(), group) {}
+};
+
+//-----------------------------------------------------------------------------
+class PlayerInfos : public ItemArray
+{
+ public:
+ PlayerInfos();
+
+ bool isNewPlayer() const { return _newPlayer; }
+ bool isOldLocalPlayer() const { return _oldLocalPlayer; }
+ uint nbEntries() const;
+ QString name() const { return item("name")->read(_id).toString(); }
+ bool isAnonymous() const;
+ QString prettyName() const { return prettyName(_id); }
+ QString prettyName(uint id) const { return item("name")->pretty(id); }
+ QString registeredName() const;
+ QString comment() const { return item("comment")->pretty(_id); }
+ bool isWWEnabled() const;
+ QString key() const;
+ uint id() const { return _id; }
+ uint oldLocalId() const { return _oldLocalId; }
+
+ void createHistoItems(const QMemArray<uint> &scores, bool bound);
+ QString histoName(uint i) const;
+ uint histoSize() const;
+ const QMemArray<uint> &histogram() const { return _histogram; }
+
+ void submitScore(const Score &) const;
+ // return true if the nickname is already used locally
+ bool isNameUsed(const QString &name) const;
+ void modifyName(const QString &newName) const;
+ void modifySettings(const QString &newName, const QString &comment,
+ bool WWEnabled, const QString &newKey) const;
+ void removeKey();
+
+ private:
+ bool _newPlayer, _bound, _oldLocalPlayer;
+ uint _id, _oldLocalId;
+ QMemArray<uint> _histogram;
+};
+
+//-----------------------------------------------------------------------------
+class ManagerPrivate
+{
+ public:
+ ManagerPrivate(uint nbGameTypes, Manager &manager);
+ void init(uint maxNbentries);
+ ~ManagerPrivate();
+
+ bool modifySettings(const QString &newName, const QString &comment,
+ bool WWEnabled, QWidget *widget);
+
+ void setGameType(uint type);
+ void checkFirst();
+ int submitLocal(const Score &score);
+ int submitScore(const Score &score, QWidget *widget, bool askIfAnonymous);
+ Score readScore(uint i) const;
+
+ uint gameType() const { return _gameType; }
+ uint nbGameTypes() const { return _nbGameTypes; }
+ bool isWWHSAvailable() const { return !serverURL.isEmpty(); }
+ ScoreInfos &scoreInfos() { return *_scoreInfos; }
+ PlayerInfos &playerInfos() { return *_playerInfos; }
+ KHighscore &hsConfig() { return *_hsConfig; }
+ enum QueryType { Submit, Register, Change, Players, Scores };
+ KURL queryURL(QueryType type, const QString &newName=QString::null) const;
+
+ void exportHighscores(QTextStream &);
+
+ Manager &manager;
+ KURL serverURL;
+ QString version;
+ bool showStatistics, showDrawGames, trackLostGames, trackDrawGames;
+ Manager::ShowMode showMode;
+
+ private:
+ KHighscore *_hsConfig;
+ PlayerInfos *_playerInfos;
+ ScoreInfos *_scoreInfos;
+ bool _first;
+ const uint _nbGameTypes;
+ uint _gameType;
+
+ // return -1 if not a local best score
+ int rank(const Score &score) const;
+
+ bool submitWorldWide(const Score &score, QWidget *parent) const;
+ static bool doQuery(const KURL &url, QWidget *parent,
+ QDomNamedNodeMap *map = 0);
+ static bool getFromQuery(const QDomNamedNodeMap &map, const QString &name,
+ QString &value, QWidget *parent);
+ void convertToGlobal();
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_item.cpp b/libkdegames/highscore/kexthighscore_item.cpp
new file mode 100644
index 00000000..48556e02
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_item.cpp
@@ -0,0 +1,312 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_item.h"
+
+#include <qlayout.h>
+#include <kglobal.h>
+#include <kdialogbase.h>
+#include <kdebug.h>
+
+#include "khighscore.h"
+#include "kexthighscore_internal.h"
+#include "kexthighscore_gui.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+Item::Item(const QVariant &def, const QString &label, int alignment)
+ : _default(def), _label(label), _alignment(alignment),
+ _format(NoFormat), _special(NoSpecial)
+{}
+
+Item::~Item()
+{}
+
+QVariant Item::read(uint, const QVariant &value) const
+{
+ return value;
+}
+
+void Item::setPrettyFormat(Format format)
+{
+ bool buint = ( _default.type()==QVariant::UInt );
+ bool bdouble = ( _default.type()==QVariant::Double );
+ bool bnum = ( buint || bdouble || _default.type()==QVariant::Int );
+
+ switch (format) {
+ case OneDecimal:
+ case Percentage:
+ Q_ASSERT(bdouble);
+ break;
+ case MinuteTime:
+ Q_ASSERT(bnum);
+ break;
+ case DateTime:
+ Q_ASSERT( _default.type()==QVariant::DateTime );
+ break;
+ case NoFormat:
+ break;
+ }
+
+ _format = format;
+}
+
+void Item::setPrettySpecial(Special special)
+{
+ bool buint = ( _default.type()==QVariant::UInt );
+ bool bnum = ( buint || _default.type()==QVariant::Double
+ || _default.type()==QVariant::Int );
+
+ switch (special) {
+ case ZeroNotDefined:
+ Q_ASSERT(bnum);
+ break;
+ case NegativeNotDefined:
+ Q_ASSERT(bnum && !buint);
+ break;
+ case DefaultNotDefined:
+ break;
+ case Anonymous:
+ Q_ASSERT( _default.type()==QVariant::String );
+ break;
+ case NoSpecial:
+ break;
+ }
+
+ _special = special;
+}
+
+QString Item::timeFormat(uint n)
+{
+ Q_ASSERT( n<=3600 && n!=0 );
+ n = 3600 - n;
+ return QString::number(n / 60).rightJustify(2, '0') + ':'
+ + QString::number(n % 60).rightJustify(2, '0');
+}
+
+QString Item::pretty(uint, const QVariant &value) const
+{
+ switch (_special) {
+ case ZeroNotDefined:
+ if ( value.toUInt()==0 ) return "--";
+ break;
+ case NegativeNotDefined:
+ if ( value.toInt()<0 ) return "--";
+ break;
+ case DefaultNotDefined:
+ if ( value==_default ) return "--";
+ break;
+ case Anonymous:
+ if ( value.toString()==ItemContainer::ANONYMOUS )
+ return i18n(ItemContainer::ANONYMOUS_LABEL);
+ break;
+ case NoFormat:
+ break;
+ }
+
+ switch (_format) {
+ case OneDecimal:
+ return QString::number(value.toDouble(), 'f', 1);
+ case Percentage:
+ return QString::number(value.toDouble(), 'f', 1) + "%";
+ case MinuteTime:
+ return timeFormat(value.toUInt());
+ case DateTime:
+ if ( value.toDateTime().isNull() ) return "--";
+ return KGlobal::locale()->formatDateTime(value.toDateTime());
+ case NoSpecial:
+ break;
+ }
+
+ return value.toString();
+}
+
+//-----------------------------------------------------------------------------
+Score::Score(ScoreType type)
+ : _type(type)
+{
+ const ItemArray &items = internal->scoreInfos();
+ for (uint i=0; i<items.size(); i++)
+ _data[items[i]->name()] = items[i]->item()->defaultValue();
+}
+
+Score::~Score()
+{}
+
+const QVariant &Score::data(const QString &name) const
+{
+ Q_ASSERT( _data.contains(name) );
+ return _data[name];
+}
+
+void Score::setData(const QString &name, const QVariant &value)
+{
+ Q_ASSERT( _data.contains(name) );
+ Q_ASSERT( _data[name].type()==value.type() );
+ _data[name] = value;
+}
+
+bool Score::isTheWorst() const
+{
+ Score s;
+ return score()==s.score();
+}
+
+bool Score::operator <(const Score &score)
+{
+ return internal->manager.isStrictlyLess(*this, score);
+}
+
+QDataStream &operator <<(QDataStream &s, const Score &score)
+{
+ s << (Q_UINT8)score.type();
+ s << score._data;
+ return s;
+}
+
+QDataStream &operator >>(QDataStream &s, Score &score)
+{
+ Q_UINT8 type;
+ s >> type;
+ score._type = (ScoreType)type;
+ s >> score._data;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+MultiplayerScores::MultiplayerScores()
+{}
+
+MultiplayerScores::~MultiplayerScores()
+{}
+
+void MultiplayerScores::clear()
+{
+ Score score;
+ for (uint i=0; i<_scores.size(); i++) {
+ _nbGames[i] = 0;
+ QVariant name = _scores[i].data("name");
+ _scores[i] = score;
+ _scores[i].setData("name", name);
+ _scores[i]._data["mean score"] = double(0);
+ _scores[i]._data["nb won games"] = uint(0);
+ }
+}
+
+void MultiplayerScores::setPlayerCount(uint nb)
+{
+ _nbGames.resize(nb);
+ _scores.resize(nb);
+ clear();
+}
+
+void MultiplayerScores::setName(uint i, const QString &name)
+{
+ _scores[i].setData("name", name);
+}
+
+void MultiplayerScores::addScore(uint i, const Score &score)
+{
+ QVariant name = _scores[i].data("name");
+ double mean = _scores[i].data("mean score").toDouble();
+ uint won = _scores[i].data("nb won games").toUInt();
+ _scores[i] = score;
+ _scores[i].setData("name", name);
+ _nbGames[i]++;
+ mean += (double(score.score()) - mean) / _nbGames[i];
+ _scores[i]._data["mean score"] = mean;
+ if ( score.type()==Won ) won++;
+ _scores[i]._data["nb won games"] = won;
+}
+
+void MultiplayerScores::show(QWidget *parent)
+{
+ // check consistency
+ if ( _nbGames.size()<2 ) kdWarning(11002) << "less than 2 players" << endl;
+ else {
+ bool ok = true;
+ uint nb = _nbGames[0];
+ for (uint i=1; i<_nbGames.size(); i++)
+ if ( _nbGames[i]!=nb ) ok = false;
+ if (!ok)
+ kdWarning(11002) << "players have not same number of games" << endl;
+ }
+
+ // order the players according to the number of won games
+ QValueVector<Score> ordered;
+ for (uint i=0; i<_scores.size(); i++) {
+ uint won = _scores[i].data("nb won games").toUInt();
+ double mean = _scores[i].data("mean score").toDouble();
+ QValueVector<Score>::iterator it;
+ for(it = ordered.begin(); it!=ordered.end(); ++it) {
+ uint cwon = (*it).data("nb won games").toUInt();
+ double cmean = (*it).data("mean score").toDouble();
+ if ( won<cwon || (won==cwon && mean<cmean) ) {
+ ordered.insert(it, _scores[i]);
+ break;
+ }
+ }
+ if ( it==ordered.end() ) ordered.push_back(_scores[i]);
+ }
+
+ // show the scores
+ KDialogBase dialog(KDialogBase::Plain, i18n("Multiplayers Scores"),
+ KDialogBase::Close, KDialogBase::Close,
+ parent, "show_multiplayers_score", true, true);
+ QHBoxLayout *hbox = new QHBoxLayout(dialog.plainPage(),
+ KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBox *vbox = new QVBox(dialog.plainPage());
+ hbox->addWidget(vbox);
+ if ( _nbGames[0]==0 ) (void)new QLabel(i18n("No game played."), vbox);
+ else {
+ (void)new QLabel(i18n("Scores for last game:"), vbox);
+ (void)new LastMultipleScoresList(ordered, vbox);
+ }
+
+ if ( _nbGames[0]>1 ) {
+ vbox = new QVBox(dialog.plainPage());
+ hbox->addWidget(vbox);
+ (void)new QLabel(i18n("Scores for the last %1 games:")
+ .arg(_nbGames[0]), vbox);
+ (void)new TotalMultipleScoresList(ordered, vbox);
+ }
+
+ dialog.enableButtonSeparator(false);
+ dialog.exec();
+}
+
+QDataStream &operator <<(QDataStream &s, const MultiplayerScores &score)
+{
+ s << score._scores;
+ s << score._nbGames;
+ return s;
+}
+
+QDataStream &operator >>(QDataStream &s, MultiplayerScores &score)
+{
+ s >> score._scores;
+ s >> score._nbGames;
+ return s;
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_item.h b/libkdegames/highscore/kexthighscore_item.h
new file mode 100644
index 00000000..0200fabd
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_item.h
@@ -0,0 +1,317 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_ITEM_H
+#define KEXTHIGHSCORE_ITEM_H
+
+#include <qvariant.h>
+#include <qnamespace.h>
+#include <qmap.h>
+#include <qvaluevector.h>
+#include <kdemacros.h>
+class QWidget;
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+/**
+ * This class defines how to convert and how to display
+ * a highscore element (such as the score, the date, ...) or a player
+ * info (such as the player name, the best score, ...).
+ */
+class KDE_EXPORT Item
+{
+ public:
+ /**
+ * Possible display format.
+ * <ul>
+ * <li> @p NoFormat : no formatting (default) </li>
+ * <li> @p OneDecimal : with one decimal (only for Double) </li>
+ * <li> @p Percentage : with one decimal + % (only for Double) </li>
+ * <li> @p MinuteTime : MM:SS ie 3600 is 00:00, 1 is 59:59 and 0 is
+ * undefined (only for UInt, Int and Double) </li>
+ * <li> @p DateTime : date and time according to locale (only for
+ * DateTime) </li>
+ * </ul>
+ */
+ enum Format { NoFormat, OneDecimal, Percentage, MinuteTime,
+ DateTime };
+
+ /**
+ * Possible special value for display format.
+ * <ul>
+ * <li> @p NoSpecial : no special value ; a null DateTime is replaced by
+ * "--" (default) </li>
+ * <li> ZeroNotDefined : 0 is replaced by "--" (only for UInt, Int and
+ * Double) </li>
+ * <li> @p NegativeNotDefined : negative values are replaced by "--" (only
+ * for Int and Double) </li>
+ * <li> @p DefaultNotDefined : default value is replaced by "--" </li>
+ * <li> @p Anonymous : replace the special value ItemBase::ANONYMOUS
+ * by i18n("anonymous") (only for String) </li>
+ * </ul>
+ */
+ enum Special { NoSpecial, ZeroNotDefined, NegativeNotDefined,
+ DefaultNotDefined, Anonymous };
+
+ /**
+ * Constructor.
+ *
+ * @param def default value ; the QVariant also gives the type of data.
+ * Be sure to cast the value to the required type (for e.g. with uint).
+ * @param label the label corresponding to the item. If empty, the item
+ * is not shown.
+ * @param alignment the alignment of the item.
+ */
+ Item(const QVariant &def = QVariant::Invalid,
+ const QString &label = QString::null, int alignment = Qt::AlignRight);
+
+ virtual ~Item();
+
+ /**
+ * Set the display format.
+ * @see Format
+ */
+ void setPrettyFormat(Format format);
+
+ /**
+ * Set the special value for display.
+ * @see Special
+ */
+ void setPrettySpecial(Special special);
+
+ /**
+ * @return if the item is shown.
+ */
+ bool isVisible() const { return !_label.isEmpty(); }
+
+ /**
+ * Set the label.
+ */
+ void setLabel(const QString &label) { _label = label; }
+
+ /**
+ * @return the label.
+ */
+ QString label() const { return _label; }
+
+ /**
+ * @return the alignment.
+ */
+ int alignment() const { return _alignment; }
+
+ /**
+ * Set default value.
+ */
+ void setDefaultValue(const QVariant &value) { _default = value; }
+
+ /**
+ * @return the default value.
+ */
+ const QVariant &defaultValue() const { return _default; }
+
+ /**
+ * @return the converted value (by default the value is left
+ * unchanged). Most of the time you don't need to reimplement this method.
+ *
+ * @param i the element index ("rank" for score / "id" for player)
+ * @param value the value to convert
+ */
+ virtual QVariant read(uint i, const QVariant &value) const;
+
+ /**
+ * @return the string to be displayed. You may need to reimplement this
+ * method for special formatting (different from the standard ones).
+ *
+ * @param i the element index ("rank" for score / "id" for player)
+ * @param value the value to convert
+ */
+ virtual QString pretty(uint i, const QVariant &value) const;
+
+ private:
+ QVariant _default;
+ QString _label;
+ int _alignment;
+ Format _format;
+ Special _special;
+
+ class ItemPrivate;
+ ItemPrivate *d;
+
+ static QString timeFormat(uint);
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * Possible score type.
+ * @p Won the game has been won.
+ * @p Lost the game has been lost or has been aborted.
+ * @p Draw the game is a draw.
+ */
+enum ScoreType { Won = 0, Lost = -1, Draw = -2 };
+
+/**
+ * This class contains data for a score. You should not inherit from
+ * this class but reimplement the methods in Highscores.
+ */
+class KDE_EXPORT Score
+{
+ public:
+ Score(ScoreType type = Won);
+
+ ~Score();
+
+ /**
+ * @return the game type.
+ */
+ ScoreType type() const { return _type; }
+
+ /**
+ * Set the game type.
+ */
+ void setType(ScoreType type) { _type = type; }
+
+ /**
+ * @return the data associated with the named Item.
+ */
+ const QVariant &data(const QString &name) const;
+
+ /**
+ * Set the data associated with the named Item. Note that the
+ * value should have the type of the default value of the
+ * Item.
+ */
+ void setData(const QString &name, const QVariant &value);
+
+ /**
+ * @return the score value.
+ *
+ * Equivalent to <pre>data("score").toUInt()</pre>.
+ */
+ uint score() const { return data("score").toUInt(); }
+
+ /**
+ * Set the score value.
+ *
+ * Equivalent to <pre>setData("score", score)</pre>.
+ */
+ void setScore(uint score) { setData("score", score); }
+
+ /**
+ * @return true if this is the worst possible score (ie the default
+ * argument of ScoreItem).
+ */
+ bool isTheWorst() const;
+
+ /**
+ * Comparison operator.
+ *
+ * @see Manager::isStrictlyLess
+ */
+ bool operator <(const Score &score);
+
+ private:
+ ScoreType _type;
+ QMap<QString, QVariant> _data;
+
+ class ScorePrivate;
+ ScorePrivate *d;
+
+ friend class MultiplayerScores;
+
+ friend QDataStream &operator <<(QDataStream &stream, const Score &score);
+ friend QDataStream &operator >>(QDataStream &stream, Score &score);
+};
+
+KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const Score &score);
+KDE_EXPORT QDataStream &operator >>(QDataStream &stream, Score &score);
+
+/**
+ * This class is used to store and show scores for multiplayer games.
+ *
+ * Example of use:
+ * Initialize the class:
+ * <pre>
+ * KExtHighscore::MultiScore ms(2);
+ * ms.setPlayerName(0, "player 1");
+ * ms.setPlayerName(1, "player 2");
+ * </pre>
+ * At the end of each game, add the score of each players:
+ * <pre>
+ * KExtHighscore::Score score(KExtHighscore::Won);
+ * score.setScore(100);
+ * ms.addScore(0, score);
+ * score.setType(KExtHighscore::Lost);
+ * score.setScore(20);
+ * ms.addScore(1, score);
+ * </pre>
+ */
+class KDE_EXPORT MultiplayerScores
+{
+ public:
+ MultiplayerScores();
+
+ ~MultiplayerScores();
+
+ /**
+ * Set the number of players and clear the scores.
+ */
+ void setPlayerCount(uint nb);
+
+ /**
+ * Set the name of player.
+ */
+ void setName(uint player, const QString &name);
+
+ /**
+ * Add the score of player.
+ */
+ void addScore(uint player, const Score &score);
+
+ /**
+ * Clear all scores.
+ */
+ void clear();
+
+ /**
+ * Show scores.
+ */
+ void show(QWidget *parent);
+
+ private:
+ QValueVector<uint> _nbGames;
+ QValueVector<Score> _scores;
+
+ class MultiplayerScoresPrivate;
+ MultiplayerScoresPrivate *d;
+
+ friend QDataStream &operator <<(QDataStream &stream,
+ const MultiplayerScores &score);
+ friend QDataStream &operator >>(QDataStream &stream,
+ MultiplayerScores &score);
+};
+
+KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const MultiplayerScores &score);
+KDE_EXPORT QDataStream &operator >>(QDataStream &stream, MultiplayerScores &score);
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kexthighscore_tab.cpp b/libkdegames/highscore/kexthighscore_tab.cpp
new file mode 100644
index 00000000..3e9cbe8a
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_tab.cpp
@@ -0,0 +1,281 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2002 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kexthighscore_tab.h"
+#include "kexthighscore_tab.moc"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qvgroupbox.h>
+#include <qgrid.h>
+#include <qheader.h>
+
+#include <kdialogbase.h>
+#include <klistview.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "kexthighscore.h"
+#include "kexthighscore_internal.h"
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+PlayersCombo::PlayersCombo(QWidget *parent, const char *name)
+ : QComboBox(parent, name)
+{
+ const PlayerInfos &p = internal->playerInfos();
+ for (uint i = 0; i<p.nbEntries(); i++)
+ insertItem(p.prettyName(i));
+ insertItem(QString("<") + i18n("all") + '>');
+ connect(this, SIGNAL(activated(int)), SLOT(activatedSlot(int)));
+}
+
+void PlayersCombo::activatedSlot(int i)
+{
+ const PlayerInfos &p = internal->playerInfos();
+ if ( i==(int)p.nbEntries() ) emit allSelected();
+ else if ( i==(int)p.nbEntries()+1 ) emit noneSelected();
+ else emit playerSelected(i);
+}
+
+void PlayersCombo::load()
+{
+ const PlayerInfos &p = internal->playerInfos();
+ for (uint i = 0; i<p.nbEntries(); i++)
+ changeItem(p.prettyName(i), i);
+}
+
+//-----------------------------------------------------------------------------
+AdditionalTab::AdditionalTab(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *top = new QVBoxLayout(this, KDialogBase::marginHint(),
+ KDialogBase::spacingHint());
+
+ QHBoxLayout *hbox = new QHBoxLayout(top);
+ QLabel *label = new QLabel(i18n("Select player:"), this);
+ hbox->addWidget(label);
+ _combo = new PlayersCombo(this);
+ connect(_combo, SIGNAL(playerSelected(uint)),
+ SLOT(playerSelected(uint)));
+ connect(_combo, SIGNAL(allSelected()), SLOT(allSelected()));
+ hbox->addWidget(_combo);
+ hbox->addStretch(1);
+}
+
+void AdditionalTab::init()
+{
+ uint id = internal->playerInfos().id();
+ _combo->setCurrentItem(id);
+ playerSelected(id);
+}
+
+void AdditionalTab::allSelected()
+{
+ display(internal->playerInfos().nbEntries());
+}
+
+QString AdditionalTab::percent(uint n, uint total, bool withBraces)
+{
+ if ( n==0 || total==0 ) return QString::null;
+ QString s = QString("%1%").arg(100.0 * n / total, 0, 'f', 1);
+ return (withBraces ? QString("(") + s + ")" : s);
+}
+
+void AdditionalTab::load()
+{
+ _combo->load();
+}
+
+
+//-----------------------------------------------------------------------------
+const char *StatisticsTab::COUNT_LABELS[Nb_Counts] = {
+ I18N_NOOP("Total:"), I18N_NOOP("Won:"), I18N_NOOP("Lost:"),
+ I18N_NOOP("Draw:")
+};
+const char *StatisticsTab::TREND_LABELS[Nb_Trends] = {
+ I18N_NOOP("Current:"), I18N_NOOP("Max won:"), I18N_NOOP("Max lost:")
+};
+
+StatisticsTab::StatisticsTab(QWidget *parent)
+ : AdditionalTab(parent, "statistics_tab")
+{
+ // construct GUI
+ QVBoxLayout *top = static_cast<QVBoxLayout *>(layout());
+
+ QHBoxLayout *hbox = new QHBoxLayout(top);
+ QVBoxLayout *vbox = new QVBoxLayout(hbox);
+ QVGroupBox *group = new QVGroupBox(i18n("Game Counts"), this);
+ vbox->addWidget(group);
+ QGrid *grid = new QGrid(3, group);
+ grid->setSpacing(KDialogBase::spacingHint());
+ for (uint k=0; k<Nb_Counts; k++) {
+ if ( Count(k)==Draw && !internal->showDrawGames ) continue;
+ (void)new QLabel(i18n(COUNT_LABELS[k]), grid);
+ _nbs[k] = new QLabel(grid);
+ _percents[k] = new QLabel(grid);
+ }
+
+ group = new QVGroupBox(i18n("Trends"), this);
+ vbox->addWidget(group);
+ grid = new QGrid(2, group);
+ grid->setSpacing(KDialogBase::spacingHint());
+ for (uint k=0; k<Nb_Trends; k++) {
+ (void)new QLabel(i18n(TREND_LABELS[k]), grid);
+ _trends[k] = new QLabel(grid);
+ }
+
+ hbox->addStretch(1);
+ top->addStretch(1);
+}
+
+void StatisticsTab::load()
+{
+ AdditionalTab::load();
+ const PlayerInfos &pi = internal->playerInfos();
+ uint nb = pi.nbEntries();
+ _data.resize(nb+1);
+ for (uint i=0; i<_data.size()-1; i++) {
+ _data[i].count[Total] = pi.item("nb games")->read(i).toUInt();
+ _data[i].count[Lost] = pi.item("nb lost games")->read(i).toUInt()
+ + pi.item("nb black marks")->read(i).toUInt(); // legacy
+ _data[i].count[Draw] = pi.item("nb draw games")->read(i).toUInt();
+ _data[i].count[Won] = _data[i].count[Total] - _data[i].count[Lost]
+ - _data[i].count[Draw];
+ _data[i].trend[CurrentTrend] =
+ pi.item("current trend")->read(i).toInt();
+ _data[i].trend[WonTrend] = pi.item("max won trend")->read(i).toUInt();
+ _data[i].trend[LostTrend] =
+ -(int)pi.item("max lost trend")->read(i).toUInt();
+ }
+
+ for (uint k=0; k<Nb_Counts; k++) _data[nb].count[k] = 0;
+ for (uint k=0; k<Nb_Trends; k++) _data[nb].trend[k] = 0;
+ for (uint i=0; i<_data.size()-1; i++) {
+ for (uint k=0; k<Nb_Counts; k++)
+ _data[nb].count[k] += _data[i].count[k];
+ for (uint k=0; k<Nb_Trends; k++)
+ _data[nb].trend[k] += _data[i].trend[k];
+ }
+ for (uint k=0; k<Nb_Trends; k++)
+ _data[nb].trend[k] /= (_data.size()-1);
+
+ init();
+}
+
+QString StatisticsTab::percent(const Data &d, Count count) const
+{
+ if ( count==Total ) return QString::null;
+ return AdditionalTab::percent(d.count[count], d.count[Total], true);
+}
+
+void StatisticsTab::display(uint i)
+{
+ const Data &d = _data[i];
+ for (uint k=0; k<Nb_Counts; k++) {
+ if ( Count(k) && !internal->showDrawGames ) continue;
+ _nbs[k]->setText(QString::number(d.count[k]));
+ _percents[k]->setText(percent(d, Count(k)));
+ }
+ for (uint k=0; k<Nb_Trends; k++) {
+ QString s;
+ if ( d.trend[k]>0 ) s = '+';
+ int prec = (i==internal->playerInfos().nbEntries() ? 1 : 0);
+ _trends[k]->setText(s + QString::number(d.trend[k], 'f', prec));
+ }
+}
+
+//-----------------------------------------------------------------------------
+HistogramTab::HistogramTab(QWidget *parent)
+ : AdditionalTab(parent, "histogram_tab")
+{
+ // construct GUI
+ QVBoxLayout *top = static_cast<QVBoxLayout *>(layout());
+
+ _list = new KListView(this);
+ _list->setSelectionMode(QListView::NoSelection);
+ _list->setItemMargin(3);
+ _list->setAllColumnsShowFocus(true);
+ _list->setSorting(-1);
+ _list->header()->setClickEnabled(false);
+ _list->header()->setMovingEnabled(false);
+ top->addWidget(_list);
+
+ _list->addColumn(i18n("From"));
+ _list->addColumn(i18n("To"));
+ _list->addColumn(i18n("Count"));
+ _list->addColumn(i18n("Percent"));
+ for (uint i=0; i<4; i++) _list->setColumnAlignment(i, AlignRight);
+ _list->addColumn(QString::null);
+
+ const Item *sitem = internal->scoreInfos().item("score")->item();
+ const PlayerInfos &pi = internal->playerInfos();
+ const QMemArray<uint> &sh = pi.histogram();
+ for (uint k=1; k<pi.histoSize(); k++) {
+ QString s1 = sitem->pretty(0, sh[k-1]);
+ QString s2;
+ if ( k==sh.size() ) s2 = "...";
+ else if ( sh[k]!=sh[k-1]+1 ) s2 = sitem->pretty(0, sh[k]);
+ (void)new KListViewItem(_list, s1, s2);
+ }
+}
+
+void HistogramTab::load()
+{
+ AdditionalTab::load();
+ const PlayerInfos &pi = internal->playerInfos();
+ uint n = pi.nbEntries();
+ uint s = pi.histoSize() - 1;
+ _counts.resize((n+1) * s);
+ _data.fill(0, n+1);
+ for (uint k=0; k<s; k++) {
+ _counts[n*s + k] = 0;
+ for (uint i=0; i<n; i++) {
+ uint nb = pi.item(pi.histoName(k+1))->read(i).toUInt();
+ _counts[i*s + k] = nb;
+ _counts[n*s + k] += nb;
+ _data[i] += nb;
+ _data[n] += nb;
+ }
+ }
+
+ init();
+}
+
+void HistogramTab::display(uint i)
+{
+ const PlayerInfos &pi = internal->playerInfos();
+ QListViewItem *item = _list->firstChild();
+ uint s = pi.histoSize() - 1;
+ for (int k=s-1; k>=0; k--) {
+ uint nb = _counts[i*s + k];
+ item->setText(2, QString::number(nb));
+ item->setText(3, percent(nb, _data[i]));
+ uint width = (_data[i]==0 ? 0 : qRound(150.0 * nb / _data[i]));
+ QPixmap pixmap(width, 10);
+ pixmap.fill(blue);
+ item->setPixmap(4, pixmap);
+ item = item->nextSibling();
+ }
+}
+
+} // namespace
diff --git a/libkdegames/highscore/kexthighscore_tab.h b/libkdegames/highscore/kexthighscore_tab.h
new file mode 100644
index 00000000..ce47a75f
--- /dev/null
+++ b/libkdegames/highscore/kexthighscore_tab.h
@@ -0,0 +1,117 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2002 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXTHIGHSCORE_TAB_H
+#define KEXTHIGHSCORE_TAB_H
+
+#include <qcombobox.h>
+#include <qmemarray.h>
+
+class QLabel;
+class KListView;
+
+
+namespace KExtHighscore
+{
+
+//-----------------------------------------------------------------------------
+class PlayersCombo : public QComboBox
+{
+ Q_OBJECT
+ public:
+ PlayersCombo(QWidget *parent = 0, const char *name = 0);
+
+ void load();
+
+ signals:
+ void playerSelected(uint i);
+ void allSelected();
+ void noneSelected();
+
+ private slots:
+ void activatedSlot(int i);
+};
+
+//-----------------------------------------------------------------------------
+class AdditionalTab : public QWidget
+{
+ Q_OBJECT
+ public:
+ AdditionalTab(QWidget *parent, const char *name);
+
+ virtual void load();
+
+ private slots:
+ void playerSelected(uint i) { display(i) ; }
+ void allSelected();
+
+ protected:
+ void init();
+ static QString percent(uint n, uint total, bool withBraces = false);
+ virtual void display(uint i) = 0;
+
+ private:
+ PlayersCombo *_combo;
+};
+
+//-----------------------------------------------------------------------------
+class StatisticsTab : public AdditionalTab
+{
+ Q_OBJECT
+ public:
+ StatisticsTab(QWidget *parent);
+
+ void load();
+
+ private:
+ enum Count { Total = 0, Won, Lost, Draw, Nb_Counts };
+ static const char *COUNT_LABELS[Nb_Counts];
+ enum Trend { CurrentTrend = 0, WonTrend, LostTrend, Nb_Trends };
+ static const char *TREND_LABELS[Nb_Trends];
+ struct Data {
+ uint count[Nb_Counts];
+ double trend[Nb_Trends];
+ };
+ QMemArray<Data> _data;
+ QLabel *_nbs[Nb_Counts], *_percents[Nb_Counts], *_trends[Nb_Trends];
+
+ QString percent(const Data &, Count) const;
+ void display(uint i);
+};
+
+//-----------------------------------------------------------------------------
+class HistogramTab : public AdditionalTab
+{
+ Q_OBJECT
+ public:
+ HistogramTab(QWidget *parent);
+
+ void load();
+
+ private:
+ QMemArray<uint> _counts;
+ QMemArray<uint> _data;
+ KListView *_list;
+
+ void display(uint i);
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/highscore/kfilelock.cpp b/libkdegames/highscore/kfilelock.cpp
new file mode 100644
index 00000000..7dc83b96
--- /dev/null
+++ b/libkdegames/highscore/kfilelock.cpp
@@ -0,0 +1,88 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kfilelock.h"
+
+#include <unistd.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#include <kdebug.h>
+
+
+KFileLock::KFileLock(int fd)
+ : _fd(fd), _locked(false)
+{}
+
+int KFileLock::lock()
+{
+ kdDebug(11002) << "lock fd=" << _fd << endl;
+#ifdef F_SETLK
+# ifndef SEEK_SET
+# define SEEK_SET 0
+# endif
+ struct flock lock_data;
+ lock_data.l_type = F_WRLCK;
+ lock_data.l_whence = SEEK_SET;
+ lock_data.l_start = lock_data.l_len = 0;
+ if ( fcntl(_fd, F_SETLK, &lock_data)==-1 ) {
+ if ( errno==EAGAIN ) return -2;
+ return -1;
+ }
+#else
+# ifdef LOCK_EX
+ if ( flock (_fd, LOCK_EX|LOCK_NB)==-1 ) {
+ if ( errno==EWOULDBLOCK ) return -2;
+ return -1;
+ }
+# else
+ if ( lockf(_fd, F_TLOCK, 0)==-1 ) {
+ if ( errno==EACCES ) return -2;
+ return -1;
+ }
+# endif
+#endif
+ _locked = true;
+ return 0;
+}
+
+KFileLock::~KFileLock()
+{
+ unlock();
+}
+
+void KFileLock::unlock()
+{
+ if ( !_locked ) return;
+ kdDebug(11002) << "unlock" << endl;
+# ifdef F_SETLK
+ struct flock lock_data;
+ lock_data.l_type = F_UNLCK;
+ lock_data.l_whence = SEEK_SET;
+ lock_data.l_start = lock_data.l_len = 0;
+ (void)fcntl(_fd, F_SETLK, &lock_data);
+# else
+# ifdef F_ULOCK
+ lockf(_fd, F_ULOCK, 0);
+# else
+ flock(_fd, LOCK_UN);
+# endif
+# endif
+ _locked = false;
+}
diff --git a/libkdegames/highscore/kfilelock.h b/libkdegames/highscore/kfilelock.h
new file mode 100644
index 00000000..2e1841ba
--- /dev/null
+++ b/libkdegames/highscore/kfilelock.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef KFILELOCK_H
+#define KFILELOCK_H
+
+
+class KFileLock
+{
+public:
+ KFileLock(int fd);
+
+ /** Call unlock(). */
+ ~KFileLock();
+
+ /** @return the file descriptor. */
+ int fd() const { return _fd; }
+
+ /*
+ * Lock the file.
+ * @return 0 on success, -1 on failure (no permission) and -2 if another
+ * process is currently locking the file.
+ */
+ int lock();
+
+ /** Unlock the file. */
+ void unlock();
+
+ /** @return true if we currently lock the file. */
+ bool isLocked() const { return _locked; }
+
+private:
+ int _fd;
+ bool _locked;
+};
+
+
+#endif
diff --git a/libkdegames/highscore/khighscore.cpp b/libkdegames/highscore/khighscore.cpp
new file mode 100644
index 00000000..4e1f68e5
--- /dev/null
+++ b/libkdegames/highscore/khighscore.cpp
@@ -0,0 +1,262 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include <config.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+#include <kapplication.h>
+#include <ksimpleconfig.h>
+#include <kglobal.h>
+#include <kstdguiitem.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+
+#include "khighscore.h"
+#include "kconfigrawbackend.h"
+#include "kfilelock.h"
+
+#define GROUP "KHighscore"
+
+class KHighscorePrivate
+{
+public:
+ KHighscorePrivate() {}
+
+ QString group;
+ bool global;
+};
+
+KFileLock *KHighscore::_lock = 0;
+KRawConfig *KHighscore::_config = 0;
+static KStaticDeleter<KFileLock> lockSD;
+static KStaticDeleter<KRawConfig> configSD;
+
+
+KHighscore::KHighscore(QObject* parent)
+ : QObject(parent)
+{
+ init(true);
+}
+
+KHighscore::KHighscore(bool forceLocal, QObject* parent)
+ : QObject(parent)
+{
+ init(forceLocal);
+}
+
+void KHighscore::init(bool forceLocal)
+{
+ d = new KHighscorePrivate;
+#ifdef HIGHSCORE_DIRECTORY
+ d->global = !forceLocal;
+ if ( d->global && _lock==0 )
+ kdFatal(11002) << "KHighscore::init should be called before!!" << endl;
+#else
+ d->global = false;
+ Q_UNUSED(forceLocal);
+#endif
+ readCurrentConfig();
+}
+
+bool KHighscore::isLocked() const
+{
+ return (d->global ? _lock->isLocked() : true);
+}
+
+void KHighscore::readCurrentConfig()
+{
+ if ( d->global ) _config->reparseConfiguration();
+}
+
+void KHighscore::init(const char *appname)
+{
+#ifdef HIGHSCORE_DIRECTORY
+ const QString filename = QString::fromLocal8Bit("%1/%2.scores")
+ .arg(HIGHSCORE_DIRECTORY).arg(appname);
+ int fd = open(filename.local8Bit(), O_RDWR);
+ if ( fd<0 ) kdFatal(11002) << "cannot open global highscore file \""
+ << filename << "\"" << endl;
+ lockSD.setObject(_lock, new KFileLock(fd));
+ configSD.setObject(_config, new KRawConfig(fd, true)); // read-only
+
+ // drop the effective gid
+ int gid = getgid();
+ setregid(gid, gid);
+#else
+ Q_UNUSED(appname);
+#endif
+}
+
+bool KHighscore::lockForWriting(QWidget *widget)
+{
+ if ( isLocked() ) return true;
+
+ bool first = true;
+ for (;;) {
+ kdDebug(11002) << "try locking" << endl;
+ // lock the highscore file (it should exist)
+ int result = _lock->lock();
+ bool ok = ( result==0 );
+ kdDebug(11002) << "locking system-wide highscore file res="
+ << result << " (ok=" << ok << ")" << endl;
+ if (ok) {
+ readCurrentConfig();
+ _config->setReadOnly(false);
+ return true;
+ }
+
+ if ( !first ) {
+ KGuiItem item = KStdGuiItem::cont();
+ item.setText(i18n("Retry"));
+ int res = KMessageBox::warningContinueCancel(widget, i18n("Cannot access the highscore file. Another user is probably currently writing to it."), QString::null, item, "ask_lock_global_highscore_file");
+ if ( res==KMessageBox::Cancel ) break;
+ } else sleep(1);
+ first = false;
+ }
+ return false;
+}
+
+void KHighscore::writeAndUnlock()
+{
+ if ( !d->global ) {
+ kapp->config()->sync();
+ return;
+ }
+ if ( !isLocked() ) return;
+
+ kdDebug(11002) << "unlocking" << endl;
+ _config->sync(); // write config
+ _lock->unlock();
+ _config->setReadOnly(true);
+}
+
+KHighscore::~KHighscore()
+{
+ writeAndUnlock();
+ delete d;
+}
+
+KConfig* KHighscore::config() const
+{
+ return (d->global ? _config : kapp->config());
+}
+
+void KHighscore::writeEntry(int entry, const QString& key, const QVariant& value)
+{
+ Q_ASSERT( isLocked() );
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ cg.config()->writeEntry(confKey, value);
+}
+
+void KHighscore::writeEntry(int entry, const QString& key, int value)
+{
+ Q_ASSERT( isLocked() );
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ cg.config()->writeEntry(confKey, value);
+}
+
+void KHighscore::writeEntry(int entry, const QString& key, const QString &value)
+{
+ Q_ASSERT (isLocked() );
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ cg.config()->writeEntry(confKey, value);
+}
+
+QVariant KHighscore::readPropertyEntry(int entry, const QString& key, const QVariant& pDefault) const
+{
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ return cg.config()->readPropertyEntry(confKey, pDefault);
+}
+
+QString KHighscore::readEntry(int entry, const QString& key, const QString& pDefault) const
+{
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ return cg.config()->readEntry(confKey, pDefault);
+}
+
+int KHighscore::readNumEntry(int entry, const QString& key, int pDefault) const
+{
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ return cg.config()->readNumEntry(confKey, pDefault);
+}
+
+bool KHighscore::hasEntry(int entry, const QString& key) const
+{
+ KConfigGroupSaver cg(config(), group());
+ QString confKey = QString("%1_%2").arg(entry).arg(key);
+ return cg.config()->hasKey(confKey);
+}
+
+QStringList KHighscore::readList(const QString& key, int lastEntry) const
+{
+ QStringList list;
+ for (int i = 1; hasEntry(i, key) && ((lastEntry > 0) ? (i <= lastEntry) : true); i++) {
+ list.append(readEntry(i, key));
+ }
+ return list;
+}
+
+void KHighscore::writeList(const QString& key, const QStringList& list)
+{
+ for (int unsigned i = 1; i <= list.count(); i++) {
+ writeEntry(i, key, list[i - 1]);
+ }
+}
+
+void KHighscore::setHighscoreGroup(const QString& group)
+{
+ d->group = group;
+}
+
+const QString& KHighscore::highscoreGroup() const
+{
+ return d->group;
+}
+
+QString KHighscore::group() const
+{
+ if ( highscoreGroup().isNull() )
+ return (d->global ? QString::null : GROUP);
+ return (d->global ? highscoreGroup()
+ : QString("%1_%2").arg(GROUP).arg(highscoreGroup()));
+}
+
+bool KHighscore::hasTable() const
+{ return config()->hasGroup(group()); }
+
+void KHighscore::sync()
+{
+ writeAndUnlock();
+}
+
+#include "khighscore.moc"
diff --git a/libkdegames/highscore/khighscore.h b/libkdegames/highscore/khighscore.h
new file mode 100644
index 00000000..b1e3d25f
--- /dev/null
+++ b/libkdegames/highscore/khighscore.h
@@ -0,0 +1,311 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KHIGHSCORE_H__
+#define __KHIGHSCORE_H__
+
+#include <qstring.h>
+#include <qobject.h>
+#include <kdemacros.h>
+class KConfig;
+class KFileLock;
+class KRawConfig;
+class KHighscorePrivate;
+
+/**
+ * @short Class for managing highscore tables
+ *
+ * This is the KDE class for saving and reading highscore tables. It offers the
+ * possibility for system-wide highscore tables (configure with e.g.
+ * --enable-highscore-dir=/var/games) and a theoretically unlimited number of
+ * entries.
+ *
+ * You can specify different "keys" for an entry - just like the KConfig
+ * keys. But it will be prefixed with the number of the entry. For example you
+ * will probably use something like this to save the name of the player on the
+ * top of the list (ie the winner):
+ * \code
+ * highscore->writeEntry(1, "name", myPlayer->name());
+ * \endcode
+ * Note that it doesn't really matter if you use "0" or "1" as the first entry
+ * of the list as long as your program always uses the same for the first
+ * entry. I recommend to use "1", as several convenience methods use this.
+ *
+ * You can also specify different groups using setHighscoreGroup. Just
+ * like the keys mentioned above the groups behave like groups in KConfig
+ * but are prefixed with "KHighscore_". The default group is just "KHighscore".
+ * You might use this e.g. to create different highscore tables like
+ * \code
+ * table->setHighscoreGroup("Easy");
+ * // write the highscores for level "easy" to the table
+ * writeEasyHighscores(table);
+ *
+ * table->setHighscore("Player_1");
+ * // write player specific highscores to the table
+ * writePlayerHighscores(table);
+ * \endcode
+ * As you can see above you can also use this to write the highscores of a
+ * single player, so the "best times" of a player. To write highscores for a
+ * specific player in a specific level you will have to use a more complex way:
+ * \code
+ * QString group = QString("%1_%2").arg(player).arg(level);
+ * table->setGroup(group);
+ * writeHighscore(table, player, level);
+ * \endcode
+ *
+ * Also note that you MUST NOT mark the key or the group for translation! I.e.
+ * don't use i18n() for the keys or groups! Here is the code to read the above
+ * written entry:
+ * \code
+ * QString firstName = highscore->readEntry(0, "name");
+ * \endcode
+ * Easy, what?
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KHighscore : public QObject
+{
+ Q_OBJECT
+public:
+ /** @obsolete
+ * Constructor. The highscore file is forced to be local to support
+ * games using the old behaviour.
+ */
+ KHighscore(QObject* parent = 0);
+
+ /**
+ * Constructor.
+ *
+ * @param forceLocal if true, the local highscore file is used even
+ * when the configuration has been set to use a system-wide file. This
+ * is convenient for converting highscores from legacy applications.
+ * @param parent parent widget for this widget
+ * @since 3.2
+ */
+ KHighscore(bool forceLocal, QObject *parent);
+
+ /**
+ * Read the current state of the highscore file. Remember that when
+ * it's not locked for writing, this file can change at any time.
+ * (This method is only useful for a system-wide highscore file).
+ * @since 3.2
+ */
+ void readCurrentConfig();
+
+ /** @since 3.2
+ * This method open the system-wide highscore file using the effective
+ * group id of the game executable (which should be "games"). The
+ * effective group id is completely dropped afterwards.
+ *
+ * Note: this method should be called in main() before creating a
+ * KApplication and doing anything else (KApplication checks that the
+ * program is not suid/sgid and will exit the program for security
+ * reason if it is the case).
+ */
+ static void init(const char *appname);
+
+ /** @since 3.2
+ * Lock the system-wide highscore file for writing (does nothing and
+ * return true if the local file is used).
+ * You should perform writing without GUI interaction to avoid
+ * blocking and don't forget to unlock the file as soon as possible
+ * with writeAndUnlock().
+ *
+ * If the config file cannot be locked,
+ * the method waits for 1 second and, if it failed again, displays
+ * a message box asking for retry or cancel.
+ * @param widget used as the parent of the message box.
+ *
+ * @return false on error or if the config file is locked by another
+ * process. In such case, the config stays read-only.
+ */
+ bool lockForWriting(QWidget *widget = 0);
+
+ /**
+ * Effectively write and unlock the system-wide highscore file
+ * (@see lockForWriting).
+ * If using a local highscore file, it will sync the config.
+ * @since 3.2
+ */
+ void writeAndUnlock();
+
+ /**
+ * @return true if the highscore file is locked or if a local
+ * file is used.
+ * @since 3.2
+ */
+ bool isLocked() const;
+
+ /**
+ * Destructor.
+ * If necessary, write and unlock the highscore file.
+ */
+ ~KHighscore();
+
+ /**
+ * @param entry The number of the entry / the placing of the player
+ * @param key A key for this entry. E.g. "name" for the name of the
+ * player. Nearly the same as the usual keys in KConfig - but they
+ * are prefixed with the entry number
+ * @param value The value of this entry
+ **/
+ void writeEntry(int entry, const QString& key, const QString& value);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ void writeEntry(int entry, const QString& key, int value);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ * See KConfigBase documentation for allowed QVariant::Type.
+ **/
+ void writeEntry(int entry, const QString& key, const QVariant &value);
+
+ /**
+ * Reads an entry from the highscore table.
+ * @param entry The number of the entry / the placing to be read
+ * @param key The key of the entry. E.g. "name" for the name of the
+ * player. Nearly the same as the usual keys in KConfig - but they
+ * are prefixed with the entry number
+ * @param pDefault This will be used as default value if the key+pair
+ * entry can't be found.
+ * @return The value of this entry+key pair or pDefault if the entry+key
+ * pair doesn't exist
+ **/
+ QString readEntry(int entry, const QString& key, const QString& pDefault = QString::null) const;
+
+ /**
+ * Read a numeric value.
+ * @param entry The number of the entry / the placing to be read
+ * @param key The key of the entry. E.g. "name" for the name of the
+ * player. Nearly the same as the usual keys in KConfig - but they
+ * are prefixed with the entry number
+ * @param pDefault This will be used as default value if the key+pair
+ * entry can't be found.
+ * @return The value of this entry+key pair or pDefault if the entry+key
+ * pair doesn't exist
+ **/
+ int readNumEntry(int entry, const QString& key, int pDefault = -1) const;
+
+ /**
+ * Read a QVariant entry.
+ * See KConfigBase documentation for allowed QVariant::Type.
+ *
+ * @return the value of this entry+key pair or pDefault if the entry+key
+ * pair doesn't exist or
+ */
+ QVariant readPropertyEntry(int entry, const QString &key, const QVariant &pDefault) const;
+
+ /**
+ * @return True if the highscore table conatins the entry/key pair,
+ * otherwise false
+ **/
+ bool hasEntry(int entry, const QString& key) const;
+
+ /**
+ * Reads a list of entries from the highscore table starting at 1 until
+ * lastEntry. If an entry between those numbers doesn't exist the
+ * function aborts reading even if after the missing entry is an
+ * existing one. The first entry of the list is the first placing, the
+ * last on is the last placing.
+ * @return A list of the entries of this key. You could also call
+ * readEntry(i, key) where i is from 1 to 20. Note that this function
+ * depends on "1" as the first entry!
+ * @param key The key of the entry. E.g. "name" for the name of the
+ * player. Nearly the same as the usual keys in KConfig - but they
+ * are prefixed with the entry number
+ * @param lastEntry the last entry which will be includes into the list.
+ * 1 will include a list with maximal 1 entry - 20 a list with maximal
+ * 20 entries. If lastEntry is <= 0 then rading is only stopped when when an
+ * entry does not exist.
+ **/
+ QStringList readList(const QString& key, int lastEntry = 20) const;
+
+ /**
+ * Writes a list of entries to the highscore table.
+ *
+ * The first entry is prefixed with "1". Using this method is a short
+ * way of calling writeEntry(i, key, list[i]) from i = 1 to
+ * list.count()
+ * @param key A key for the entry. E.g. "name" for the name of the
+ * player. Nearly the same as the usual keys in KConfig - but they
+ * are prefixed with the entry number
+ * @param list The list of values
+ **/
+ void writeList(const QString& key, const QStringList& list);
+
+ /**
+ * @return Whether a highscore table exists. You can use this
+ * function to indicate whether KHighscore created a highscore table
+ * before and - if not - read your old (non-KHighscore) table instead.
+ * This way you can safely read an old table and save it using
+ * KHighscore without losing any data
+ **/
+ bool hasTable() const;
+
+ /** @obsolete
+ * This does the same as writeAndUnlock().
+ */
+ void sync();
+
+ /**
+ * Set the new highscore group. The group is being prefixed with
+ * "KHighscore_" in the table.
+ * @param groupname The new groupname. E.g. use "easy" for the easy
+ * level of your game. If you use QString::null (the default) the
+ * default group is used.
+ **/
+ void setHighscoreGroup(const QString& groupname = QString::null);
+
+ /**
+ * @return The currently used group. This doesn't contain the prefix
+ * ("KHighscore_") but the same as setHighscoreGroup uses. The
+ * default is QString::null
+ **/
+ const QString& highscoreGroup() const;
+
+protected:
+ /**
+ * @return A groupname to be used in KConfig. Used internally to
+ * prefix the value from highscoreGroup() with "KHighscore_"
+ **/
+ QString group() const;
+
+ /**
+ * @return A pointer to the KConfig object to be used. This is
+ * either kapp->config() (default) or a KSimpleConfig object for
+ * a system-wide highscore file.
+ **/
+ KConfig* config() const;
+
+ void init(bool forceLocal);
+
+private:
+ KHighscorePrivate* d;
+
+ static KFileLock *_lock; // lock on system-wide highscore file
+ static KRawConfig *_config; // config for system-wide highscore file
+};
+
+#endif
diff --git a/libkdegames/highscore/kscoredialog.cpp b/libkdegames/highscore/kscoredialog.cpp
new file mode 100644
index 00000000..37155650
--- /dev/null
+++ b/libkdegames/highscore/kscoredialog.cpp
@@ -0,0 +1,411 @@
+/****************************************************************
+Copyright (c) 1998 Sandro Sigala <ssigala@globalnet.it>.
+Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of the author not be used in
+advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+The author disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall the author be liable for any
+special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "config.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qwidgetstack.h>
+#include <qtimer.h>
+#include <qevent.h>
+#include <qptrvector.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kseparator.h>
+
+#include "kscoredialog.h"
+
+class KScoreDialog::KScoreDialogPrivate
+{
+public:
+ QPtrList<FieldInfo> scores;
+ QWidget *page;
+ QGridLayout *layout;
+ QLineEdit *edit;
+ QPtrVector<QWidgetStack> stack;
+ QPtrVector<QLabel> labels;
+ QLabel *commentLabel;
+ QString comment;
+ int fields;
+ int newName;
+ int latest;
+ int nrCols;
+ bool loaded;
+ QString configGroup;
+
+ QMap<int, int> col;
+ QMap<int, QString> header;
+ QMap<int, QString> key;
+ QString player;
+};
+
+
+KScoreDialog::KScoreDialog(int fields, QWidget *parent, const char *oname)
+ : KDialogBase(parent, oname, true, i18n("High Scores"), Ok, Ok, true)
+{
+ d = new KScoreDialogPrivate();
+ d->edit = 0;
+ d->fields = fields;
+ d->newName = -1;
+ d->latest = -1;
+ d->loaded = false;
+ d->nrCols = 0;
+ d->configGroup = "High Score";
+
+ d->scores.setAutoDelete(true);
+ d->header[Name] = i18n("Name");
+ d->key[Name] = "Name";
+
+ d->header[Date] = i18n("Date");
+ d->key[Date] = "Date";
+
+ d->header[Level] = i18n("Level");
+ d->key[Level] = "Level";
+
+ d->header[Score] = i18n("Score");
+ d->key[Score] = "Score";
+ d->page = makeMainWidget();
+
+ connect(this, SIGNAL(okClicked()), SLOT(slotGotName()));
+}
+
+KScoreDialog::~KScoreDialog()
+{
+ delete d;
+}
+
+void KScoreDialog::setConfigGroup(const QString &group)
+{
+ d->configGroup = group;
+ d->loaded = false;
+}
+
+void KScoreDialog::setComment(const QString &comment)
+{
+ d->comment = comment;
+}
+
+void KScoreDialog::addField(int field, const QString &header, const QString &key)
+{
+ d->fields |= field;
+ d->header[field] = header;
+ d->key[field] = key;
+}
+
+void KScoreDialog::setupDialog()
+{
+ d->nrCols = 1;
+
+ for(int field = 1; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ d->col[field] = d->nrCols++;
+ }
+
+ d->layout = new QGridLayout(d->page, 15, d->nrCols, marginHint() + 20, spacingHint());
+ d->layout->addRowSpacing(4, 15);
+
+ d->commentLabel = new QLabel(d->page);
+ d->commentLabel->setAlignment(AlignVCenter | AlignHCenter);
+ d->layout->addMultiCellWidget(d->commentLabel, 1, 1, 0, d->nrCols-1);
+
+ QFont bold = font();
+ bold.setBold(true);
+
+ QLabel *label;
+ d->layout->addColSpacing(0, 50);
+ label = new QLabel(i18n("Rank"), d->page);
+ d->layout->addWidget(label, 3, 0);
+ label->setFont(bold);
+
+ for(int field = 1; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ {
+ d->layout->addColSpacing(d->col[field], 50);
+
+ label = new QLabel(d->header[field], d->page);
+ d->layout->addWidget(label, 3, d->col[field], field <= Name ? AlignLeft : AlignRight);
+ label->setFont(bold);
+ }
+ }
+
+ KSeparator *sep = new KSeparator(Horizontal, d->page);
+ d->layout->addMultiCellWidget(sep, 4, 4, 0, d->nrCols-1);
+
+ d->labels.resize(d->nrCols * 10);
+ d->stack.resize(10);
+
+ QString num;
+ for (int i = 1; i <= 10; ++i) {
+ QLabel *label;
+ num.setNum(i);
+ label = new QLabel(i18n("#%1").arg(num), d->page);
+ d->labels.insert((i-1)*d->nrCols + 0, label);
+ d->layout->addWidget(label, i+4, 0);
+ if (d->fields & Name)
+ {
+ QWidgetStack *stack = new QWidgetStack(d->page);
+ d->stack.insert(i-1, stack);
+ d->layout->addWidget(stack, i+4, d->col[Name]);
+ label = new QLabel(d->page);
+ d->labels.insert((i-1)*d->nrCols + d->col[Name], label);
+ stack->addWidget(label);
+ stack->raiseWidget(label);
+ }
+ for(int field = Name * 2; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ {
+ label = new QLabel(d->page);
+ d->labels.insert((i-1)*d->nrCols + d->col[field], label);
+ d->layout->addWidget(label, i+4, d->col[field], AlignRight);
+ }
+ }
+ }
+}
+
+void KScoreDialog::aboutToShow()
+{
+ if (!d->loaded)
+ loadScores();
+
+ if (!d->nrCols)
+ setupDialog();
+
+ d->commentLabel->setText(d->comment);
+ if (d->comment.isEmpty())
+ {
+ d->commentLabel->setMinimumSize(QSize(1,1));
+ d->commentLabel->hide();
+ d->layout->addRowSpacing(0, -15);
+ d->layout->addRowSpacing(2, -15);
+ }
+ else
+ {
+ d->commentLabel->setMinimumSize(d->commentLabel->sizeHint());
+ d->commentLabel->show();
+ d->layout->addRowSpacing(0, -10);
+ d->layout->addRowSpacing(2, 10);
+ }
+ d->comment = QString::null;
+
+ QFont normal = font();
+ QFont bold = normal;
+ bold.setBold(true);
+
+ QString num;
+ for (int i = 1; i <= 10; ++i) {
+ QLabel *label;
+ num.setNum(i);
+ FieldInfo *score = d->scores.at(i-1);
+ label = d->labels[(i-1)*d->nrCols + 0];
+ if (i == d->latest)
+ label->setFont(bold);
+ else
+ label->setFont(normal);
+
+ if (d->fields & Name)
+ {
+ if (d->newName == i)
+ {
+ QWidgetStack *stack = d->stack[i-1];
+ d->edit = new QLineEdit(d->player, stack);
+ d->edit->setMinimumWidth(40);
+ stack->addWidget(d->edit);
+ stack->raiseWidget(d->edit);
+ d->edit->setFocus();
+ connect(d->edit, SIGNAL(returnPressed()),
+ this, SLOT(slotGotReturn()));
+ }
+ else
+ {
+ label = d->labels[(i-1)*d->nrCols + d->col[Name]];
+ if (i == d->latest)
+ label->setFont(bold);
+ else
+ label->setFont(normal);
+ label->setText((*score)[Name]);
+ }
+
+ }
+ for(int field = Name * 2; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ {
+ label = d->labels[(i-1)*d->nrCols + d->col[field]];
+ if (i == d->latest)
+ label->setFont(bold);
+ else
+ label->setFont(normal);
+ label->setText((*score)[field]);
+ }
+ }
+ }
+ d->latest = -1;
+ setFixedSize(minimumSizeHint());
+}
+
+void KScoreDialog::loadScores()
+{
+ QString key, value;
+ d->loaded = true;
+ d->scores.clear();
+ KConfigGroup config(kapp->config(), d->configGroup.utf8());
+
+ d->player = config.readEntry("LastPlayer");
+
+ QString num;
+ for (int i = 1; i <= 10; ++i) {
+ num.setNum(i);
+ FieldInfo *score = new FieldInfo();
+ for(int field = 1; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ {
+ key = "Pos" + num + d->key[field];
+ (*score)[field] = config.readEntry(key, "-");
+ }
+ }
+ d->scores.append(score);
+ }
+}
+
+void KScoreDialog::saveScores()
+{
+ QString key, value;
+ KConfigGroup config(kapp->config(), d->configGroup.utf8());
+
+ config.writeEntry("LastPlayer", d->player);
+
+ QString num;
+ for (int i = 1; i <= 10; ++i) {
+ num.setNum(i);
+ FieldInfo *score = d->scores.at(i-1);
+ for(int field = 1; field < d->fields; field = field * 2)
+ {
+ if (d->fields & field)
+ {
+ key = "Pos" + num + d->key[field];
+ config.writeEntry(key, (*score)[field]);
+ }
+ }
+ }
+ kapp->config()->sync();
+}
+
+int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName)
+{
+ return addScore(newScore, newInfo, askName, false);
+}
+
+int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore)
+{
+ if (!d->loaded)
+ loadScores();
+ FieldInfo *score = d->scores.first();
+ int i = 1;
+ for(; score; score = d->scores.next(), i++)
+ {
+ bool ok;
+ int num_score = (*score)[Score].toLong(&ok);
+ if (lessIsMore && !ok)
+ num_score = 1 << 30;
+ if (((newScore > num_score) && !lessIsMore) ||
+ ((newScore < num_score) && lessIsMore))
+ {
+ score = new FieldInfo(newInfo);
+ (*score)[Score].setNum(newScore);
+ d->scores.insert(i-1, score);
+ d->scores.remove(10);
+ d->latest = i;
+ if (askName)
+ d->newName = i;
+ else
+ saveScores();
+ if (i == 1)
+ d->comment = i18n("Excellent!\nYou have a new high score!");
+ else
+ d->comment = i18n("Well done!\nYou made it to the high score list!");
+ return i;
+ }
+ }
+ return 0;
+}
+
+void KScoreDialog::show()
+{
+ aboutToShow();
+ KDialogBase::show();
+}
+
+void KScoreDialog::slotGotReturn()
+{
+ QTimer::singleShot(0, this, SLOT(slotGotName()));
+}
+
+void KScoreDialog::slotGotName()
+{
+ if (d->newName == -1) return;
+
+ d->player = d->edit->text();
+
+ (*d->scores.at(d->newName-1))[Name] = d->player;
+ saveScores();
+
+ QFont bold = font();
+ bold.setBold(true);
+
+ QLabel *label = d->labels[(d->newName-1)*d->nrCols + d->col[Name]];
+ label->setFont(bold);
+ label->setText(d->player);
+ d->stack[(d->newName-1)]->raiseWidget(label);
+ delete d->edit;
+ d->edit = 0;
+ d->newName = -1;
+}
+
+int KScoreDialog::highScore()
+{
+ if (!d->loaded)
+ loadScores();
+
+ return (*d->scores.first())[Score].toInt();
+}
+
+void KScoreDialog::keyPressEvent( QKeyEvent *ev)
+{
+ if ((d->newName != -1) && (ev->key() == Key_Return))
+ {
+ ev->ignore();
+ return;
+ }
+ KDialogBase::keyPressEvent(ev);
+}
+
+
+#include "kscoredialog.moc"
diff --git a/libkdegames/highscore/kscoredialog.h b/libkdegames/highscore/kscoredialog.h
new file mode 100644
index 00000000..4d4a76db
--- /dev/null
+++ b/libkdegames/highscore/kscoredialog.h
@@ -0,0 +1,125 @@
+/****************************************************************
+Copyright (c) 1998 Sandro Sigala <ssigala@globalnet.it>.
+Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of the author not be used in
+advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+The author disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall the author be liable for any
+special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#ifndef KSCOREDIALOG_H
+#define KSCOREDIALOG_H
+
+#include <qmap.h>
+#include <qptrlist.h>
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+class QGridLayout;
+class QLineEdit;
+class QWidgetStack;
+
+/**
+ * A simple high score dialog.
+ */
+class KDE_EXPORT KScoreDialog : public KDialogBase {
+ Q_OBJECT
+
+public:
+ enum Fields { Name = 1 << 0,
+ Level = 1 << 1,
+
+ Custom1 = 1 << 10,
+ Custom2 = 1 << 11,
+ Custom3 = 1 << 12,
+
+ Date = 1 << 27,
+ Time = 1 << 28,
+ Score = 1 << 29 };
+
+ typedef QMap<int, QString> FieldInfo;
+
+ /**
+ * @param fields Which fields should be listed.
+ * @param parent passed to parent QWidget constructor
+ * @param name passed to parent QWidget constructor
+ */
+ KScoreDialog(int fields, QWidget *parent=0, const char *name=0);
+
+ ~KScoreDialog();
+
+ /**
+ * @param group to use for reading/writing highscores from/to. By default
+ * the class will use "High Score"
+ */
+ void setConfigGroup(const QString &group);
+
+ /**
+ * @param comment to add when showing high-scores.
+ * The comment is only used once.
+ */
+ void setComment(const QString &comment);
+
+ /**
+ * Define an extra FieldInfo entry.
+ * @param field Id of this field
+ * @param header Header shown in the dialog for this field
+ * @param key used to store this field with.
+ */
+ void addField(int field, const QString &header, const QString &key);
+
+ /**
+ * Adds a new score to the list.
+ *
+ * @param newScore the score of this game.
+ * @param newInfo additional info about the score.
+ * @param askName Whether to prompt for the players name.
+ * @param lessIsMore If true, the lowest score is the best score.
+ *
+ * @returns The highscore position if the score was good enough to
+ * make it into the list (1 being topscore) or 0 otherwise.
+ */
+ int addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore);
+ int addScore(int newScore, const FieldInfo &newInfo, bool askName=true);
+
+ /**
+ * Returns the current best score.
+ */
+ int highScore();
+
+ virtual void show();
+
+private slots:
+ void slotGotReturn();
+ void slotGotName();
+
+private:
+ /* read scores */
+ void loadScores();
+ void saveScores();
+
+ void aboutToShow();
+ void setupDialog();
+ void keyPressEvent( QKeyEvent *ev);
+
+private:
+ class KScoreDialogPrivate;
+ KScoreDialogPrivate *d;
+};
+
+#endif // !KSCOREDIALOG_H
diff --git a/libkdegames/kcanvasrootpixmap.cpp b/libkdegames/kcanvasrootpixmap.cpp
new file mode 100644
index 00000000..14592c66
--- /dev/null
+++ b/libkdegames/kcanvasrootpixmap.cpp
@@ -0,0 +1,39 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kcanvasrootpixmap.h"
+
+#include <qcanvas.h>
+
+
+KCanvasRootPixmap::KCanvasRootPixmap(QCanvasView *view, const char *name)
+ : KRootPixmap(view, name), _view(view)
+{
+ setCustomPainting(true);
+ connect(this, SIGNAL(backgroundUpdated(const QPixmap &)),
+ SLOT(backgroundUpdatedSlot(const QPixmap &)));
+}
+
+void KCanvasRootPixmap::backgroundUpdatedSlot(const QPixmap &pixmap)
+{
+ if ( _view && _view->canvas() )
+ _view->canvas()->setBackgroundPixmap(pixmap);
+}
+
+#include "kcanvasrootpixmap.moc"
diff --git a/libkdegames/kcanvasrootpixmap.h b/libkdegames/kcanvasrootpixmap.h
new file mode 100644
index 00000000..3eedb7e1
--- /dev/null
+++ b/libkdegames/kcanvasrootpixmap.h
@@ -0,0 +1,61 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCANVASROOTPIXMAP_H
+#define KCANVASROOTPIXMAP_H
+
+#include <krootpixmap.h>
+#include <kdemacros.h>
+
+class QCanvasView;
+
+/**
+ * Implement KRootPixmap for a QCanvasView.
+ *
+ * The pixmap will be set as the background of the
+ * QCanvas associated with the view :
+ * <ul>
+ * <li>for correct positioning of the background pixmap, the given
+ * QCanvasView should be positioned at the origin of the canvas.</li>
+ * <li>no other view of the same canvas should use KCanvasRootPixmap.</li>
+ * <li>other views of the canvas will have the same background pixmap.</li>
+ * </ul>
+ */
+class KDE_EXPORT KCanvasRootPixmap : public KRootPixmap
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ KCanvasRootPixmap(QCanvasView *view, const char *name = 0);
+
+ private slots:
+ void backgroundUpdatedSlot(const QPixmap &);
+
+ private:
+ QCanvasView *_view;
+
+ class KCanvasRootPixmapPrivate;
+ KCanvasRootPixmapPrivate *d;
+};
+
+#endif
+
diff --git a/libkdegames/kcarddialog.cpp b/libkdegames/kcarddialog.cpp
new file mode 100644
index 00000000..a4d2ac20
--- /dev/null
+++ b/libkdegames/kcarddialog.cpp
@@ -0,0 +1,808 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2000 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qslider.h>
+#include <qwmatrix.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kiconview.h>
+#include <ksimpleconfig.h>
+
+#include "kcarddialog.h"
+#include <qpushbutton.h>
+#include <kdebug.h>
+
+#define KCARD_DEFAULTDECK QString::fromLatin1("deck0.png")
+#define KCARD_DEFAULTCARD QString::fromLatin1("11.png")
+#define KCARD_DEFAULTCARDDIR QString::fromLatin1("cards-default/")
+
+// values for the resize slider
+#define SLIDER_MIN 400
+#define SLIDER_MAX 3000
+
+// KConfig entries
+#define CONF_GROUP "KCardDialog"
+#define CONF_RANDOMDECK QString::fromLatin1("RandomDeck")
+#define CONF_DECK QString::fromLatin1("Deck")
+#define CONF_CARDDIR QString::fromLatin1("CardDir")
+#define CONF_RANDOMCARDDIR QString::fromLatin1("RandomCardDir")
+#define CONF_USEGLOBALDECK QString::fromLatin1("GlobalDeck")
+#define CONF_USEGLOBALCARDDIR QString::fromLatin1("GlobalCardDir")
+#define CONF_SCALE QString::fromLatin1("Scale")
+
+#define CONF_GLOBAL_GROUP QString::fromLatin1("KCardDialog Settings")
+#define CONF_GLOBAL_DECK QString::fromLatin1("GlobalDeck")
+#define CONF_GLOBAL_CARDDIR QString::fromLatin1("GlobalCardDir")
+#define CONF_GLOBAL_RANDOMDECK QString::fromLatin1("GlobalRandomDeck")
+#define CONF_GLOBAL_RANDOMCARDDIR QString::fromLatin1("GlobalRandomCardDir")
+
+
+class KCardDialogPrivate
+{
+public:
+ KCardDialogPrivate()
+ {
+ deckLabel = 0;
+ cardLabel = 0;
+ deckIconView = 0;
+ cardIconView = 0;
+ randomDeck = 0;
+ randomCardDir = 0;
+ cPreview = 0;
+ scaleSlider = 0;
+ globalDeck = 0;
+ globalCardDir = 0;
+
+ cScale = 1;
+ }
+
+ QLabel* deckLabel;
+ QLabel* cardLabel;
+ KIconView* deckIconView;
+ KIconView* cardIconView;
+ QCheckBox* randomDeck;
+ QCheckBox* randomCardDir;
+ QCheckBox* globalDeck;
+ QCheckBox* globalCardDir;
+
+ QSlider* scaleSlider;
+ QPixmap cPreviewPix;
+ QLabel* cPreview;
+
+ QMap<QIconViewItem*, QString> deckMap;
+ QMap<QIconViewItem*, QString> cardMap;
+ QMap<QString, QString> helpMap;
+
+ //set query variables
+ KCardDialog::CardFlags cFlags;
+ QString cDeck;
+ QString cCardDir;
+ double cScale;
+};
+
+int KCardDialog::getCardDeck(QString &pDeck, QString &pCardDir, QWidget *pParent,
+ CardFlags pFlags, bool* pRandomDeck, bool* pRandomCardDir,
+ double* pScale, KConfig* pConf)
+{
+ KCardDialog dlg(pParent, "dlg", pFlags);
+
+ dlg.setDeck(pDeck);
+ dlg.setCardDir(pCardDir);
+
+ dlg.setupDialog(pScale != 0);
+ dlg.loadConfig(pConf);
+ dlg.showRandomDeckBox(pRandomDeck != 0);
+ dlg.showRandomCardDirBox(pRandomCardDir != 0);
+ int result=dlg.exec();
+ if (result==QDialog::Accepted)
+ {
+ // TODO check for global cards/decks!!!!
+ pDeck=dlg.deck();
+ pCardDir=dlg.cardDir();
+ if (!pCardDir.isNull() && pCardDir.right(1)!=QString::fromLatin1("/"))
+ {
+ pCardDir+=QString::fromLatin1("/");
+ }
+ if (pRandomDeck)
+ {
+ *pRandomDeck = dlg.isRandomDeck();
+ }
+ if (pRandomCardDir)
+ {
+ *pRandomCardDir = dlg.isRandomCardDir();
+ }
+ if (pScale)
+ {
+ *pScale = dlg.cardScale();
+ }
+
+ if (dlg.isGlobalDeck())
+ {
+ kdDebug(11000) << "use global deck" << endl;
+ bool random;
+ getGlobalDeck(pDeck, random);
+ kdDebug(11000) << "use: " << pDeck<< endl;
+ if (pRandomDeck)
+ {
+ *pRandomDeck=random;
+ if (random)
+ kdDebug(11000) << "use random deck" << endl;
+ }
+ }
+ if (dlg.isGlobalCardDir())
+ {
+ kdDebug(11000) << "use global carddir" << endl;
+ bool random;
+ getGlobalCardDir(pCardDir, random);
+ kdDebug(11000) << "use: " << pCardDir << endl;
+ if (pRandomCardDir)
+ {
+ *pRandomCardDir=random;
+ if (random)
+ kdDebug(11000) << "use random carddir" << endl;
+ }
+ }
+ }
+ dlg.saveConfig(pConf);
+ return result;
+}
+
+void KCardDialog::getConfigCardDeck(KConfig* conf, QString &pDeck, QString &pCardDir, double& pScale)
+{
+// TODO check for global cards/decks!!!!
+ if (!conf) {
+ return;
+ }
+ QString origGroup = conf->group();
+
+ conf->setGroup(CONF_GROUP);
+ if (conf->readBoolEntry(CONF_RANDOMDECK) || !conf->hasKey(CONF_DECK)) {
+ pDeck = getRandomDeck();
+ } else {
+ pDeck = conf->readEntry(CONF_DECK);
+ }
+ if (conf->readBoolEntry(CONF_RANDOMCARDDIR) || !conf->hasKey(CONF_CARDDIR)) {
+ pCardDir = getRandomCardDir();
+ } else {
+ pCardDir = conf->readPathEntry(CONF_CARDDIR);
+ }
+ pScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0);
+
+ if (conf->readBoolEntry(CONF_USEGLOBALDECK, false)) {
+ bool random;
+ getGlobalDeck(pCardDir, random);
+ if (random || pDeck.isNull() ) {
+ pDeck = getRandomDeck();
+ }
+ }
+ if (conf->readBoolEntry(CONF_USEGLOBALCARDDIR, false)) {
+ bool random;
+ getGlobalCardDir(pCardDir, random);
+ if (random || pCardDir.isNull() ) {
+ pCardDir = getRandomCardDir();
+ }
+ }
+
+ conf->setGroup(origGroup);
+}
+
+QString KCardDialog::getDefaultDeck()
+{
+ KCardDialog::init();
+ return locate("cards", QString::fromLatin1("decks/") + KCARD_DEFAULTDECK);
+}
+
+QString KCardDialog::getDefaultCardDir()
+{
+ KCardDialog::init();
+
+ QString file = KCARD_DEFAULTCARDDIR + KCARD_DEFAULTCARD;
+ return KGlobal::dirs()->findResourceDir("cards",file) + KCARD_DEFAULTCARDDIR;
+}
+
+QString KCardDialog::getCardPath(const QString &carddir, int index)
+{
+ KCardDialog::init();
+
+ QString entry = carddir + QString::number(index);
+ if (KStandardDirs::exists(entry + QString::fromLatin1(".png")))
+ return entry + QString::fromLatin1(".png");
+
+ // rather theoretical
+ if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm")))
+ return entry + QString::fromLatin1(".xpm");
+
+ return QString::null;
+}
+
+const QString& KCardDialog::deck() const { return d->cDeck; }
+void KCardDialog::setDeck(const QString& file) { d->cDeck=file; }
+const QString& KCardDialog::cardDir() const { return d->cCardDir; }
+void KCardDialog::setCardDir(const QString& dir) { d->cCardDir=dir; }
+KCardDialog::CardFlags KCardDialog::flags() const { return d->cFlags; }
+double KCardDialog::cardScale() const { return d->cScale; }
+bool KCardDialog::isRandomDeck() const
+{ return (d->randomDeck ? d->randomDeck->isChecked() : false); }
+bool KCardDialog::isRandomCardDir() const
+{ return (d->randomCardDir ? d->randomCardDir->isChecked() : false); }
+bool KCardDialog::isGlobalDeck() const
+{ return (d->globalDeck ? d->globalDeck->isChecked() : false); }
+bool KCardDialog::isGlobalCardDir() const
+{ return (d->globalCardDir ? d->globalCardDir->isChecked() : false); }
+
+void KCardDialog::setupDialog(bool showResizeBox)
+{
+ QHBoxLayout* topLayout = new QHBoxLayout(plainPage(), spacingHint());
+ QVBoxLayout* cardLayout = new QVBoxLayout(topLayout);
+ QString path, file;
+ QWMatrix m;
+ m.scale(0.8,0.8);
+
+ setInitialSize(QSize(600,400));
+
+ if (! (flags() & NoDeck))
+ {
+ QHBoxLayout* layout = new QHBoxLayout(cardLayout);
+
+ // Deck iconview
+ QGroupBox* grp1 = new QGroupBox(1, Horizontal, i18n("Choose Backside"), plainPage());
+ layout->addWidget(grp1);
+
+ d->deckIconView = new KIconView(grp1,"decks");
+ d->deckIconView->setSpacing(8);
+ /*
+ deckIconView->setGridX(-1);
+ deckIconView->setGridY(50);
+ */
+ d->deckIconView->setGridX(82);
+ d->deckIconView->setGridY(106);
+ d->deckIconView->setSelectionMode(QIconView::Single);
+ d->deckIconView->setResizeMode(QIconView::Adjust);
+ d->deckIconView->setMinimumWidth(360);
+ d->deckIconView->setMinimumHeight(170);
+ d->deckIconView->setWordWrapIconText(false);
+ d->deckIconView->showToolTips();
+
+ // deck select
+ QVBoxLayout* l = new QVBoxLayout(layout);
+ QGroupBox* grp3 = new QGroupBox(i18n("Backside"), plainPage());
+ grp3->setFixedSize(100, 130);
+ l->addWidget(grp3, 0, AlignTop|AlignHCenter);
+ d->deckLabel = new QLabel(grp3);
+ d->deckLabel->setText(i18n("empty"));
+ d->deckLabel->setAlignment(AlignHCenter|AlignVCenter);
+ d->deckLabel->setGeometry(10, 20, 80, 90);
+
+ d->randomDeck = new QCheckBox(plainPage());
+ d->randomDeck->setChecked(false);
+ connect(d->randomDeck, SIGNAL(toggled(bool)), this,
+ SLOT(slotRandomDeckToggled(bool)));
+ d->randomDeck->setText(i18n("Random backside"));
+ l->addWidget(d->randomDeck, 0, AlignTop|AlignHCenter);
+
+ d->globalDeck = new QCheckBox(plainPage());
+ d->globalDeck->setChecked(false);
+ d->globalDeck->setText(i18n("Use global backside"));
+ l->addWidget(d->globalDeck, 0, AlignTop|AlignHCenter);
+
+ QPushButton* b = new QPushButton(i18n("Make Backside Global"), plainPage());
+ connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalDeck()));
+ l->addWidget(b, 0, AlignTop|AlignHCenter);
+
+ connect(d->deckIconView,SIGNAL(clicked(QIconViewItem *)),
+ this,SLOT(slotDeckClicked(QIconViewItem *)));
+ }
+
+ if (! (flags() & NoCards))
+ {
+ // Cards iconview
+ QHBoxLayout* layout = new QHBoxLayout(cardLayout);
+ QGroupBox* grp2 = new QGroupBox(1, Horizontal, i18n("Choose Frontside"), plainPage());
+ layout->addWidget(grp2);
+
+ d->cardIconView =new KIconView(grp2,"cards");
+ /*
+ cardIconView->setGridX(36);
+ cardIconView->setGridY(50);
+ */
+ d->cardIconView->setGridX(82);
+ d->cardIconView->setGridY(106);
+ d->cardIconView->setResizeMode(QIconView::Adjust);
+ d->cardIconView->setMinimumWidth(360);
+ d->cardIconView->setMinimumHeight(170);
+ d->cardIconView->setWordWrapIconText(false);
+ d->cardIconView->showToolTips();
+
+ // Card select
+ QVBoxLayout* l = new QVBoxLayout(layout);
+ QGroupBox* grp4 = new QGroupBox(i18n("Frontside"), plainPage());
+ grp4->setFixedSize(100, 130);
+ l->addWidget(grp4, 0, AlignTop|AlignHCenter);
+ d->cardLabel = new QLabel(grp4);
+ d->cardLabel->setText(i18n("empty"));
+ d->cardLabel->setAlignment(AlignHCenter|AlignVCenter);
+ d->cardLabel->setGeometry(10, 20, 80, 90 );
+
+ d->randomCardDir = new QCheckBox(plainPage());
+ d->randomCardDir->setChecked(false);
+ connect(d->randomCardDir, SIGNAL(toggled(bool)), this,
+ SLOT(slotRandomCardDirToggled(bool)));
+ d->randomCardDir->setText(i18n("Random frontside"));
+ l->addWidget(d->randomCardDir, 0, AlignTop|AlignHCenter);
+
+ d->globalCardDir = new QCheckBox(plainPage());
+ d->globalCardDir->setChecked(false);
+ d->globalCardDir->setText(i18n("Use global frontside"));
+ l->addWidget(d->globalCardDir, 0, AlignTop|AlignHCenter);
+
+ QPushButton* b = new QPushButton(i18n("Make Frontside Global"), plainPage());
+ connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalCardDir()));
+ l->addWidget(b, 0, AlignTop|AlignHCenter);
+
+ connect(d->cardIconView,SIGNAL(clicked(QIconViewItem *)),
+ this,SLOT(slotCardClicked(QIconViewItem *)));
+ }
+
+ // Insert deck icons
+ // First find the default or alternate path
+ if (! (flags() & NoDeck))
+ {
+ insertDeckIcons();
+ d->deckIconView->arrangeItemsInGrid();
+
+ // Set default icons if given
+ if (!deck().isNull())
+ {
+ file=deck();
+ QPixmap pixmap(file);
+ pixmap=pixmap.xForm(m);
+ d->deckLabel->setPixmap(pixmap);
+ QToolTip::add(d->deckLabel,d->helpMap[file]);
+ }
+ }
+
+ // Insert card icons
+ if (! (flags() & NoCards))
+ {
+ insertCardIcons();
+ d->cardIconView->arrangeItemsInGrid();
+
+ // Set default icons if given
+ if (!cardDir().isNull())
+ {
+ file = cardDir() + KCARD_DEFAULTCARD;
+ QPixmap pixmap(file);
+ pixmap = pixmap.xForm(m);
+ d->cardLabel->setPixmap(pixmap);
+ QToolTip::add(d->cardLabel,d->helpMap[cardDir()]);
+ }
+ }
+
+ // insert resize box
+ if (showResizeBox)
+ {
+ // this part is a little bit...tricky.
+ // i'm sure there is a cleaner way but i cannot find it.
+ // whenever the pixmap is resized (aka scaled) the box is resized, too. This
+ // leads to an always resizing dialog which is *very* ugly. i worked around
+ // this by using a QWidget which is the only child widget of the group box.
+ // The other widget are managed inside this QWidget - a stretch area on the
+ // right ensures that the KIconViews are not resized...
+
+ // note that the dialog is still resized if you you scale the pixmap very
+ // large. This is desired behaviour as i don't want to make the box even
+ // larger but i want the complete pixmap to be displayed. the dialog is not
+ // resized if you make the pixmap smaller again.
+ QVBoxLayout* layout = new QVBoxLayout(topLayout);
+ QGroupBox* grp = new QGroupBox(1, Horizontal, i18n("Resize Cards"), plainPage());
+ layout->setResizeMode(QLayout::Fixed);
+ layout->addWidget(grp);
+ QWidget* box = new QWidget(grp);
+ QHBoxLayout* hbox = new QHBoxLayout(box, 0, spacingHint());
+ QVBoxLayout* boxLayout = new QVBoxLayout(hbox);
+ hbox->addStretch(0);
+
+ d->scaleSlider = new QSlider(1, SLIDER_MAX, 1, (-1000+SLIDER_MIN+SLIDER_MAX), Horizontal, box);
+ d->scaleSlider->setMinValue(SLIDER_MIN);
+ connect(d->scaleSlider, SIGNAL(valueChanged(int)), this, SLOT(slotCardResized(int)));
+ boxLayout->addWidget(d->scaleSlider, 0, AlignLeft);
+
+ QPushButton* b = new QPushButton(i18n("Default Size"), box);
+ connect(b, SIGNAL(pressed()), this, SLOT(slotDefaultSize()));
+ boxLayout->addWidget(b, 0, AlignLeft);
+
+ QLabel* l = new QLabel(i18n("Preview:"), box);
+ boxLayout->addWidget(l);
+ d->cPreviewPix.load(getDefaultDeck());
+ d->cPreview = new QLabel(box);
+ boxLayout->addWidget(d->cPreview, 0, AlignCenter|AlignVCenter);
+
+ slotCardResized(d->scaleSlider->value());
+ }
+}
+
+void KCardDialog::insertCardIcons()
+{
+ QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop", false, true);
+ // kdDebug(11000) << "insert " << list.count() << endl;
+ if (list.isEmpty())
+ return;
+
+ // We shrink the icons a little
+ //
+ QWMatrix m;
+ m.scale(0.8,0.8);
+
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ KSimpleConfig cfg(*it);
+ cfg.setGroup(QString::fromLatin1("KDE Backdeck"));
+ QString path = (*it).left((*it).findRev('/') + 1);
+ assert(path[path.length() - 1] == '/');
+ QPixmap pixmap(path + cfg.readEntry("Preview", "12c.png"));
+
+ if (pixmap.isNull())
+ continue;
+
+ QString name=cfg.readEntry("Name", i18n("unnamed"));
+ QIconViewItem *item= new QIconViewItem(d->cardIconView, name, pixmap);
+
+ item->setDragEnabled(false);
+ item->setDropEnabled(false);
+ item->setRenameEnabled(false);
+ item->setSelectable(true);
+
+ d->cardMap[item] = path;
+ d->helpMap[path] = cfg.readEntry("Comment",name);
+ }
+}
+
+void KCardDialog::insertDeckIcons()
+{
+ QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop", false, true);
+ if (list.isEmpty())
+ return;
+
+ QString label;
+
+ // We shrink the icons a little
+ QWMatrix m;
+ m.scale(0.8,0.8);
+
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ KSimpleConfig cfg(*it);
+ QPixmap pixmap(getDeckName(*it));
+ if (pixmap.isNull())
+ continue;
+
+ // pixmap=pixmap.xForm(m);
+
+ cfg.setGroup(QString::fromLatin1("KDE Cards"));
+ QString name=cfg.readEntry("Name", i18n("unnamed"));
+ QIconViewItem *item= new QIconViewItem(d->deckIconView,name, pixmap);
+
+ item->setDragEnabled(false);
+ item->setDropEnabled(false);
+ item->setRenameEnabled(false);
+
+ d->deckMap[item] = getDeckName(*it);
+ d->helpMap[d->deckMap[item]] = cfg.readEntry("Comment",name);
+ }
+}
+
+
+KCardDialog::~KCardDialog()
+{
+ delete d;
+}
+
+
+// Create the dialog
+KCardDialog::KCardDialog( QWidget *parent, const char *name, CardFlags mFlags)
+ : KDialogBase( Plain, i18n("Carddeck Selection"), Ok|Cancel, Ok, parent, name, true, true)
+{
+ KCardDialog::init();
+
+ d = new KCardDialogPrivate;
+ d->cFlags = mFlags;
+}
+
+void KCardDialog::slotDeckClicked(QIconViewItem *item)
+{
+ if (item && item->pixmap())
+ {
+ d->deckLabel->setPixmap(* (item->pixmap()));
+ QToolTip::remove( d->deckLabel );
+ QToolTip::add(d->deckLabel,d->helpMap[d->deckMap[item]]);
+ setDeck(d->deckMap[item]);
+ }
+}
+void KCardDialog::slotCardClicked(QIconViewItem *item)
+{
+ if (item && item->pixmap())
+ {
+ d->cardLabel->setPixmap(* (item->pixmap()));
+ QString path = d->cardMap[item];
+ QToolTip::remove( d->deckLabel );
+ QToolTip::add(d->cardLabel,d->helpMap[path]);
+ setCardDir(path);
+ }
+}
+
+QString KCardDialog::getDeckName(const QString &desktop)
+{
+ QString entry = desktop.left(desktop.length() - strlen(".desktop"));
+ if (KStandardDirs::exists(entry + QString::fromLatin1(".png")))
+ return entry + QString::fromLatin1(".png");
+
+ // rather theoretical
+ if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm")))
+ return entry + QString::fromLatin1(".xpm");
+ return QString::null;
+}
+
+QString KCardDialog::getRandomDeck()
+{
+ KCardDialog::init();
+
+ QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop");
+ if (list.isEmpty())
+ return QString::null;
+
+ int d = KApplication::random() % list.count();
+ return getDeckName(*list.at(d));
+}
+
+QString KCardDialog::getRandomCardDir()
+{
+ KCardDialog::init();
+
+ QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop");
+ if (list.isEmpty())
+ return QString::null;
+
+ int d = KApplication::random() % list.count();
+ QString entry = *list.at(d);
+ return entry.left(entry.length() - strlen("index.desktop"));
+}
+
+void KCardDialog::showRandomDeckBox(bool s)
+{
+ if (!d->randomDeck)
+ return;
+
+ if (s)
+ d->randomDeck->show();
+ else
+ d->randomDeck->hide();
+}
+
+void KCardDialog::showRandomCardDirBox(bool s)
+{
+ if (!d->randomCardDir)
+ return;
+
+ if (s)
+ d->randomCardDir->show();
+ else
+ d->randomCardDir->hide();
+}
+
+void KCardDialog::slotRandomDeckToggled(bool on)
+{
+ if (on) {
+ d->deckLabel->setText("random");
+ setDeck(getRandomDeck());
+ } else {
+ d->deckLabel->setText("empty");
+ setDeck(0);
+ }
+}
+
+void KCardDialog::slotRandomCardDirToggled(bool on)
+{
+ if (on) {
+ d->cardLabel->setText("random");
+ setCardDir(getRandomCardDir());
+ if (cardDir().length()>0 && cardDir().right(1)!=QString::fromLatin1("/")) {
+ setCardDir(cardDir() + QString::fromLatin1("/"));
+ }
+ } else {
+ d->cardLabel->setText("empty");
+ setCardDir(0);
+ }
+}
+
+void KCardDialog::loadConfig(KConfig* conf)
+{
+ if (!conf) {
+ return;
+ }
+
+ QString origGroup = conf->group();
+
+ conf->setGroup(CONF_GROUP);
+ if (! (flags() & NoDeck)) {
+ if (conf->hasKey(CONF_DECK)) {
+ setDeck(conf->readEntry(CONF_DECK));
+ }
+
+ bool random = conf->readBoolEntry(CONF_RANDOMDECK, false);
+ d->randomDeck->setChecked(random);
+ slotRandomDeckToggled(random);
+
+ if (conf->hasKey(CONF_USEGLOBALDECK) && conf->readBoolEntry(CONF_USEGLOBALDECK)) {
+ d->globalDeck->setChecked(true);
+ } else {
+ d->globalDeck->setChecked(false);
+ }
+ }
+ if (! (flags() & NoCards)) {
+ if (conf->hasKey(CONF_CARDDIR)) {
+ setCardDir(conf->readPathEntry(CONF_CARDDIR));
+ }
+
+ bool random = conf->readBoolEntry(CONF_RANDOMCARDDIR, false);
+ d->randomCardDir->setChecked(random);
+ slotRandomCardDirToggled(random);
+
+ if (conf->hasKey(CONF_USEGLOBALCARDDIR) && conf->readBoolEntry(CONF_USEGLOBALCARDDIR)) {
+ d->globalCardDir->setChecked(true);
+ } else {
+ d->globalCardDir->setChecked(false);
+ }
+ }
+
+ d->cScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0);
+
+ conf->setGroup(origGroup);
+}
+
+void KCardDialog::slotCardResized(int s)
+{
+ if (!d->cPreview) {
+ return;
+ }
+ if (s < SLIDER_MIN || s > SLIDER_MAX) {
+ kdError(11000) << "invalid scaling value!" << endl;
+ return;
+ }
+
+ s *= -1;
+ s += (SLIDER_MIN + SLIDER_MAX);
+
+ QWMatrix m;
+ double scale = (double)1000/s;
+ m.scale(scale, scale);
+ QPixmap pix = d->cPreviewPix.xForm(m);
+ d->cPreview->setPixmap(pix);
+ d->cScale = scale;
+}
+
+void KCardDialog::slotDefaultSize()
+{
+ if (!d->scaleSlider) {
+ return;
+ }
+ d->scaleSlider->setValue(-1000 + SLIDER_MIN + SLIDER_MAX);
+}
+
+void KCardDialog::saveConfig(KConfig* conf)
+{
+ if (!conf) {
+ return;
+ }
+ QString origGroup = conf->group();
+
+ conf->setGroup(CONF_GROUP);
+ if (! (flags() & NoDeck)) {
+ conf->writeEntry(CONF_DECK, deck());
+ conf->writeEntry(CONF_RANDOMDECK, isRandomDeck());
+ conf->writeEntry(CONF_USEGLOBALDECK, d->globalDeck->isChecked());
+ }
+ if (! (flags() & NoCards)) {
+ conf->writePathEntry(CONF_CARDDIR, cardDir());
+ conf->writeEntry(CONF_RANDOMCARDDIR, isRandomCardDir());
+ conf->writeEntry(CONF_USEGLOBALCARDDIR, d->globalCardDir->isChecked());
+ }
+ conf->writeEntry(CONF_SCALE, d->cScale);
+
+ conf->setGroup(origGroup);
+}
+
+void KCardDialog::slotSetGlobalDeck()
+{
+ KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false);
+ conf->setGroup(CONF_GLOBAL_GROUP);
+
+ conf->writeEntry(CONF_GLOBAL_DECK, deck());
+ conf->writeEntry(CONF_GLOBAL_RANDOMDECK, isRandomDeck());
+
+ delete conf;
+}
+
+void KCardDialog::slotSetGlobalCardDir()
+{
+ KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false);
+ conf->setGroup(CONF_GLOBAL_GROUP);
+
+ conf->writePathEntry(CONF_GLOBAL_CARDDIR, cardDir());
+ conf->writeEntry(CONF_GLOBAL_RANDOMCARDDIR, isRandomCardDir());
+
+ delete conf;
+}
+
+void KCardDialog::getGlobalDeck(QString& deck, bool& random)
+{
+ KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true);
+ conf->setGroup(CONF_GLOBAL_GROUP);
+
+ if (!conf->hasKey(CONF_GLOBAL_DECK) || conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false)) {
+ deck = getRandomDeck();
+ random = true;
+ } else {
+ deck = conf->readEntry(CONF_GLOBAL_DECK);
+ random = conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false);
+ }
+
+ delete conf;
+}
+
+void KCardDialog::getGlobalCardDir(QString& dir, bool& random)
+{
+ KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true);
+ conf->setGroup(CONF_GLOBAL_GROUP);
+
+ if (!conf->hasKey(CONF_GLOBAL_CARDDIR) || conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false)) {
+ dir = getRandomCardDir();
+ random = true;
+ } else {
+ dir = conf->readPathEntry(CONF_GLOBAL_CARDDIR);
+ random = conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false);
+ }
+
+ delete conf;
+}
+
+void KCardDialog::init()
+{
+ static bool _inited = false;
+ if (_inited)
+ return;
+ KGlobal::dirs()->addResourceType("cards", KStandardDirs::kde_default("data") + QString::fromLatin1("carddecks/"));
+
+ KGlobal::locale()->insertCatalogue("libkdegames");
+ _inited = true;
+}
+
+#include "kcarddialog.moc"
diff --git a/libkdegames/kcarddialog.h b/libkdegames/kcarddialog.h
new file mode 100644
index 00000000..b32fd636
--- /dev/null
+++ b/libkdegames/kcarddialog.h
@@ -0,0 +1,345 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2000 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __KCARDDIALOG_H_
+#define __KCARDDIALOG_H_
+
+#include <qstring.h>
+#include <kdialogbase.h>
+#include <qmap.h> // TODO: remove - it is in kcarddialog.cpp now; left here for source compatibility
+
+#include <kdemacros.h>
+class QIconViewItem;
+
+class KConfig;
+
+class KCardDialogPrivate;
+
+/**
+ * @short A carddeck selection dialog for card games.
+ *
+ * The KCardDialog provides a dialog for interactive carddeck selection.
+ * It gives cardgames an easy to use interface to select front and
+ * back of the card sets. As card sets the KDE default cardsets are
+ * offered as well as used specified ones.
+ *
+ * In most cases, the simplest
+ * use of this class is the static method KCardDialog::getCardDeck,
+ * which pops up the dialog, allows the user to select a carddeck, and
+ * returns when the dialog is closed. Only if you really need some specific
+ * behaviour or if you overwrite the dialog you need all the other access
+ * functions.
+ *
+ * Example:
+ *
+ * \code
+ * QString deck,card;
+ * int result = KCardDialog::getCardDeck(deck,card );
+ * if ( result == KCardDialog::Accepted )
+ * ...
+ * \endcode
+ *
+ * Here you can see a card dialog in action
+ * @image html "kcarddialog.png" KCarddialog
+ *
+ * KCardDialog::getCardDeck takes a lot of different parameters which are
+ * probably very useful. You can e.g. use the parameters randomDeck and
+ * randomCardDir to give the end-user the ability to choose a random
+ * deck/carddir. You have to save the value of those parameters in your config
+ * file - that's why the parameters are needed.
+ *
+ * You can also provide a KConfig pointer (usually kapp->config()). This
+ * pointer is used to store information about the dialog in an own group
+ * ("KCardDailog").
+ * So you can just ignore the randomCardDir and randomDeck
+ * values and call KCardDialog::getConfigCardDeck instead. The only reson
+ * for this function is to read a previously written configuration and give you
+ * the information about it. This way you don't have to save any configuration
+ * on your own - KCardDialog does this for you.
+ *
+ * Another Parameter for KCardDialog::getCardDeck is scale. This pointer
+ * to a double variable contains the scaling factor the user has chosen in the
+ * dialog (the scale box won't be shown if you don't provide this parameter).
+ * You might want to check out QPixmap::xFrom which gives you access to
+ * scaling. You can e.g. use
+ * \code
+ * QWMatrix m;
+ * m.scale(s,s);
+ * pixmap.xForm(m);
+ * \endcode
+ * to scale your pixmap.
+ *
+ * @author Martin Heni <martin@heni-online.de>
+ * @version $Id$
+ */
+class KDE_EXPORT KCardDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * @li @p Both - both are shown
+ * @li @p NoDeck - The deck (back) selection is not shown
+ * @li @p NoCards - The cards (front) selection is not shown
+ */
+ enum CardFlags { Both=0, NoDeck=0x01, NoCards=0x02 };
+
+ /**
+ * Constructs a card deck selection dialog.
+ *
+ * @param parent The parent widget of the dialog, if any.
+ * @param name The name of the dialog.
+ * @param flags Specifies whether the dialog is modal or not.
+ */
+ KCardDialog (QWidget* parent = NULL,const char* name = NULL,
+ CardFlags flags = Both);
+ /**
+ * Destructs a card deck selection dialog.
+ */
+ ~KCardDialog();
+
+ /**
+ * Creates a modal carddeck dialog, lets the user choose a deck,
+ * and returns when the dialog is closed.
+ *
+ * @param deck a reference to the filename used as backside of the
+ * cards. It is an absolute path and can directly be loaded as
+ * pixmap.
+ *
+ * @param carddir a reference to the directory name used as front of the
+ * cards. The directory contains the card images as 1.png to 52.png
+ *
+ * @param parent an optional pointer to the parent window of the dialog
+ *
+ * @param flags what to show
+ *
+ * @param randomDeck if this pointer is non-zero, *ok is set to TRUE if
+ * the user wants a random deck otherwise to FALSE. Use this in the
+ * config file of your game to load a random deck on startup.
+ * See @ref getRandomDeck()
+ *
+ * @param randomCardDir if this pointer is non-zero, *ok is set to TRUE if
+ * the user wants a random card otherwise to FALSE.
+ * Use this in the config file of your game to load a random card
+ * foregrounds on startup.
+ * See @ref getRandomCardDir()
+ *
+ * @param scale If non-zero a box is shown which provides the possibility to
+ * change the size of the cards. The desired scaling factor is returned to the
+ * game in this variable.
+ *
+ * @param conf If non-zero KCardDialog reads the initial settings for
+ * this dialog from the applications config file and stores them there
+ * when the dialog is closed. You can just use getConfigCardDeck
+ * to get the deck/carddir the user selected before. Note that the
+ * parameters randomDeck and randomCardDir overwrite the initial settings from the
+ * config file.
+ *
+ * @return QDialog::result().
+ */
+ static int getCardDeck(QString &deck,QString &carddir, QWidget *parent=0,
+ CardFlags flags=Both, bool* randomDeck=0,
+ bool* randomCardDir=0, double* scale=0, KConfig* conf=0);
+
+ /**
+ * Read the configuration from the applications rc file and put the
+ * previously chosen deck/frontside in the parameter deck and carddir.
+ *
+ * You probably want to use this function on startup of your program so that
+ * the user gets exactly the card/frontside he/she chose before. Note that
+ * you don't have to care whether the user wants to get a random carddeck or
+ * not as this function takes care of this.
+ * @param conf The config file to read from
+ * @param deck This will contain the chosen deck from the config file (or a
+ * random deck if this is desired according to the config)
+ * @param cardDir This will contain the chosen cardDir from the config file (or a
+ * random cardDir if this is desired according to the config)
+ * @param scale The scaling factor (usually 1)
+ **/
+ static void getConfigCardDeck(KConfig* conf, QString& deck, QString& cardDir, double& scale);
+
+ /**
+ * Returns the default path to the card deck backsides. You want
+ * to use this usually before the user used the card dialog the first
+ * time to get a default deck. You can assume that
+ * \code
+ * getDefaultDeckPath()
+ * \endcode
+ * is a valid deck.
+ *
+ * @return The default path
+ */
+ static QString getDefaultDeck();
+
+ /**
+ * Returns the default path to the card frontsides. You want
+ * to use this usually before the user used the card dialog the first
+ * time to get an default deck. You can assume that
+ * \code
+ * getCardPath(getDefaultCardPath(), *)
+ * \endcode
+ * are valid cards for * from 1 to 52.
+ *
+ * @return returns the path to the card directory
+ */
+ static QString getDefaultCardDir();
+
+ /**
+ * Returns the path to the card frontside specified in dir carddir
+ *
+ * @param index the card to open
+ * @param carddir The carddir which's path shall be searched for
+ * @return returns the path to the card
+ */
+ static QString getCardPath(const QString &carddir, int index);
+
+ /**
+ * Returns a random deck in deckPath()
+ * @return A random deck
+ **/
+ static QString getRandomDeck();
+
+ /**
+ * Returns a random directory of cards
+ * @return A random card dir
+ **/
+ static QString getRandomCardDir();
+
+ /**
+ * Show or hides the "random backside" checkbox
+ * @param s Shows the checkbox if true otherwise hides it
+ **/
+ void showRandomDeckBox(bool s);
+
+ /**
+ * Show or hides the "random foreside" checkbox
+ * @param s Shows the checkbox if true otherwise hides it
+ **/
+ void showRandomCardDirBox(bool s);
+
+ /**
+ * Returns the chosen deck, which is a valid path to a imagefile.
+ *
+ * @return The deck
+ */
+ const QString& deck() const;
+
+ /**
+ * Sets the default deck.
+ * @param file The full path to an image file
+ */
+ void setDeck(const QString& file);
+
+ /**
+ * @return The chosen card directory
+ */
+ const QString& cardDir() const;
+
+ /**
+ * Sets the default card directory.
+ * @param dir The full path to an card directory
+ */
+ void setCardDir(const QString& dir);
+
+ /**
+ * @return the flags set to the dialog
+ */
+ CardFlags flags() const;
+
+ /**
+ * Creates the default widgets in the dialog. Must be called after
+ * all flags are set. This is only needed if you do NOT use the
+ * getCardDeck static function which provides all calls for you.
+ */
+ void setupDialog(bool showResizeBox = false);
+
+ /**
+ * @return TRUE if the selected deck is a random deck (i.e. the user checked
+ * the random checkbox) otherwise FALSE
+ **/
+ bool isRandomDeck() const;
+
+ /**
+ * @return TRUE if the selected carddir is a random dir (i.e. the user
+ * checked the random checkbox) otherwise FALSE
+ **/
+ bool isRandomCardDir() const;
+
+ /**
+ * @return TRUE if the global checkbox was selected
+ **/
+ bool isGlobalDeck() const;
+
+ /**
+ * @return TRUE if the global checkbox was selected
+ **/
+ bool isGlobalCardDir() const;
+
+ /**
+ * @return The scaling factor of the card pixmap
+ **/
+ double cardScale() const;
+
+ /**
+ * Load the default settings into the dialog (e.g. whether the "use random
+ * deck" checkbox is checked or not).
+ **/
+ void loadConfig(KConfig* conf);
+
+ /**
+ * Saves the KCardDialog config into a config file. This should be the
+ * applications config file - KCardDialog creates an own group
+ * ("KCardDialog"). These settings are used by @ref loadConfig and @ref
+ * getConfigCardDeck.
+ **/
+ void saveConfig(KConfig* conf);
+
+
+protected:
+ void insertCardIcons();
+ void insertDeckIcons();
+
+ static void getGlobalDeck(QString& cardDir, bool& random);
+ static void getGlobalCardDir(QString& deck, bool& random);
+
+ static QString getDeckName(const QString& desktop);
+
+ /**
+ * @return the groupname used by functions like @ref saveConfig and @ref
+ * loadConfig.
+ **/
+ static QString group();
+
+protected slots:
+ void slotDeckClicked(QIconViewItem *);
+ void slotCardClicked(QIconViewItem *);
+ void slotRandomCardDirToggled(bool on);
+ void slotRandomDeckToggled(bool on);
+ void slotCardResized(int);
+ void slotDefaultSize();
+ void slotSetGlobalDeck();
+ void slotSetGlobalCardDir();
+
+private:
+ static void init();
+
+ KCardDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kcarddialog.png b/libkdegames/kcarddialog.png
new file mode 100644
index 00000000..3446c461
--- /dev/null
+++ b/libkdegames/kcarddialog.png
Binary files differ
diff --git a/libkdegames/kchat.cpp b/libkdegames/kchat.cpp
new file mode 100644
index 00000000..d4ffd7ec
--- /dev/null
+++ b/libkdegames/kchat.cpp
@@ -0,0 +1,115 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kchat.h"
+
+class KChatPrivate
+{
+public:
+ KChatPrivate()
+ {
+ }
+
+ bool mAutoAddMessages;
+
+ QMap<int, QString> mPlayerMap;
+ int mPlayerId;
+ int mFromId;
+};
+
+KChat::KChat(QWidget* parent, bool twoPlayerGame) : KChatBase(parent, twoPlayerGame)
+{
+ init();
+}
+
+KChat::~KChat()
+{
+ kdDebug(11000) << "DESTRUCT KChat " << this << endl;
+ delete d;
+}
+
+void KChat::init()
+{
+ kdDebug(11001) << "INIT KChat " << this << endl;
+ d = new KChatPrivate;
+ d->mAutoAddMessages = true;
+ d->mPlayerId = 1;
+ d->mFromId = 1;
+}
+
+void KChat::setFromNickname(const QString& n)
+{ d->mFromId = addPlayer(n); }
+const QString& KChat::fromName() const
+{ return player(fromId()); }
+void KChat::setAutoAddMessages(bool add)
+{ d->mAutoAddMessages = add; }
+bool KChat::autoAddMessages() const
+{ return d->mAutoAddMessages; }
+int KChat::uniqueId()
+{ return d->mPlayerId++; }
+int KChat::fromId() const
+{ return d->mFromId; }
+const QString& KChat::player(int id) const
+{ return d->mPlayerMap[id]; }
+
+void KChat::returnPressed(const QString& text)
+{
+ int id = fromId();
+ if (id < 0) {
+ // don't return - just display "unknown" as name
+ kdWarning(11000) << "KChat: no fromNickname has been set!" << endl;
+ }
+ emit signalSendMessage(id, text);
+ if (autoAddMessages()) {
+ QString p = player(id);
+ if (p.isNull()) {
+ p = i18n("Unknown");
+ }
+ kdDebug(11000) << "auto adding message from player " << p << " ;id=" << id << endl;
+ addMessage(p, text);
+ }
+}
+
+int KChat::addPlayer(const QString& nickname)
+{
+ int id = uniqueId();
+ d->mPlayerMap.insert(id, nickname);
+ return id;
+}
+
+void KChat::removePlayer(int id)
+{
+ d->mPlayerMap.remove(id);
+}
+
+void KChat::removePlayer(const QString& nickname)
+{
+ QMap<int, QString>::Iterator it;
+ for (it = d->mPlayerMap.begin(); it != d->mPlayerMap.end(); ++it) {
+ if (it.data() == nickname) {
+ d->mPlayerMap.remove(it);
+ }
+ }
+}
+
+
+#include "kchat.moc"
diff --git a/libkdegames/kchat.h b/libkdegames/kchat.h
new file mode 100644
index 00000000..db479bc0
--- /dev/null
+++ b/libkdegames/kchat.h
@@ -0,0 +1,147 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __KCHAT_H__
+#define __KCHAT_H__
+
+#include <qstring.h>
+
+#include "kchatbase.h"
+#include <kdemacros.h>
+
+class KChatPrivate;
+
+/**
+ * @short A chat widget for non-KGame games
+ *
+ * Docu is TODO
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KChat : public KChatBase
+{
+ Q_OBJECT
+public:
+ /**
+ * @param parent The parent widget for this widget.
+ * @param twoPlayerGame If true the combo box where the player can
+ * choose to send to a single player or to all players will not be added
+ * as you will hardly need it in 2-player games.
+ **/
+ KChat(QWidget* parent, bool twoPlayerGame = false);
+
+ virtual ~KChat();
+
+ /**
+ * Equivalent to player(fromId())
+ * @return The name that will be shown for messages from this widget.
+ * That is the string from @ref setFromNickname
+ **/
+ virtual const QString& fromName() const;
+
+ /**
+ * This sets the name that will be shown on all chat widgets if this
+ * widget sends a message. See signalSendMessage
+ * @param name The name of the player owning this widget
+ **/
+ void setFromNickname(const QString& name);
+
+// TODO:
+// void setPlayerList(QIntDict<QString>);// use this for non-KGame use
+
+ /**
+ * Adds a player nickname.
+ * @return The unique ID of the player
+ **/
+ int addPlayer(const QString& nick);
+
+ /**
+ * Removes all players with this nickname. Better don't use this as it
+ * will remove *all* players with this nickname. Save the id instead and
+ * call removePlayer(id)
+ * @param nick The nickname of the removed players
+ **/
+ void removePlayer(const QString& nick);
+
+ /**
+ * Removes the player with this id, as returned by @ref addPlayer
+ * @param id The id of the player to be removed
+ **/
+ void removePlayer(int id);
+
+
+ /**
+ * @return true if the messages which will be sent from here will be
+ * added automatically using @ref KChatBase::addMessage. See also @ref
+ * setAutoAddMessages
+ **/
+ bool autoAddMessages() const;
+
+ /**
+ * Usually the messages which will be sent from here (see @ref
+ * signalSendMessage) are added autmatically to this widget. But under
+ * some circumstances that would be very unhandy. So you can deactivate
+ * this behaviour here and call @ref KChatBase::addMessage yourself
+ * @param add If true (default) messages sent from here will be added
+ * automatically. Otherwise you will have to add them yourself
+ **/
+ void setAutoAddMessages(bool add);
+
+ /**
+ * @return The nickname of the player which belongs to this id
+ **/
+ const QString& player(int id) const;
+
+ /**
+ * @return The ID that belongs to the local player.
+ * @see setFromNickname
+ **/
+ int fromId() const;
+
+
+signals:
+ /**
+ * This signal is emitted when the player wants to send a message.
+ *
+ * The message is added automatically using @ref KChatBase::addMessage if @ref
+ * autoAddMessages is enabled.
+ * @param id The id of the player who sends the message - see
+ * setFromNickname and player
+ * @param msg The message itself
+ **/
+ void signalSendMessage(int id, const QString& msg);
+
+protected:
+ /**
+ * This emits @ref signalSendMessage and, if @ref autoAddMessages is
+ * true, calls @ref KChatBase::addMessage
+ **/
+ virtual void returnPressed(const QString&);
+
+ /**
+ * The Id of the next player. Incremented after every call.
+ **/
+ int uniqueId();
+
+private:
+ void init();
+
+ KChatPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kchatbase.cpp b/libkdegames/kchatbase.cpp
new file mode 100644
index 00000000..4ccd7b08
--- /dev/null
+++ b/libkdegames/kchatbase.cpp
@@ -0,0 +1,530 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kchatbase.h"
+
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include <qlayout.h>
+#include <qcombobox.h>
+#include <qpainter.h>
+
+class KChatBaseTextPrivate
+{
+public:
+ KChatBaseTextPrivate()
+ {
+ mNameFont = 0;
+ mMessageFont = 0;
+ }
+
+ QString mName;
+ QString mMessage;
+
+ const QFont* mNameFont;
+ const QFont* mMessageFont;
+};
+
+
+KChatBaseText::KChatBaseText(const QString& name, const QString& message) : QListBoxText()
+{
+ init();
+ setName(name);
+ setMessage(message);
+}
+
+KChatBaseText::KChatBaseText(const QString& message) : QListBoxText()
+{
+ init();
+ setMessage(message);
+}
+
+KChatBaseText::~KChatBaseText()
+{
+ delete d;
+}
+
+void KChatBaseText::init()
+{
+ d = new KChatBaseTextPrivate;
+}
+
+void KChatBaseText::setName(const QString& n)
+{
+// d->mName = n;
+ d->mName = QString("%1: ").arg(n);
+ setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting
+}
+
+void KChatBaseText::setMessage(const QString& m)
+{
+ d->mMessage = m;
+ setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting
+}
+
+const QString& KChatBaseText::name() const
+{ return d->mName; }
+
+const QString& KChatBaseText::message() const
+{ return d->mMessage; }
+
+QFont KChatBaseText::nameFont() const
+{
+ if (d->mNameFont) {
+ return *d->mNameFont;
+ } else if (listBox()) {
+ return listBox()->font();
+ } else {
+ return QFont();
+ }
+}
+
+QFont KChatBaseText::messageFont() const
+{
+ if (d->mMessageFont) {
+ return *d->mMessageFont;
+ } else if (listBox()) {
+ return listBox()->font();
+ } else {
+ return QFont();
+ }
+}
+
+void KChatBaseText::setNameFont(const QFont* f)
+{ d->mNameFont = f; }
+
+void KChatBaseText::setMessageFont(const QFont* f)
+{ d->mMessageFont = f; }
+
+void KChatBaseText::paint(QPainter* painter)
+{
+ QFontMetrics fm = painter->fontMetrics();
+ painter->setFont(nameFont());
+ painter->drawText(3, fm.ascent() + fm.leading()/2, name());
+ painter->setFont(messageFont());
+ painter->drawText(3 + QFontMetrics(nameFont()).width(name()), fm.ascent() + fm.leading()/2, message());
+}
+
+int KChatBaseText::width(QListBox* lb) const
+{
+ int w = 0;
+ if (lb) {
+ w += 6;
+ w += QFontMetrics(nameFont()).width(name());
+ w += QFontMetrics(messageFont()).width(message());
+ }
+// int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0; // QT orig
+ return QMAX(w, QApplication::globalStrut().width());
+}
+
+int KChatBaseText::height(QListBox* lb) const
+{
+ int h = 0;
+ if (lb) {
+ h += 2;
+ // AB: is lineSpacing still correct?
+ if (QFontMetrics(nameFont()).lineSpacing() > QFontMetrics(messageFont()).lineSpacing()) {
+ h += QFontMetrics(nameFont()).lineSpacing();
+ } else {
+ h += QFontMetrics(messageFont()).lineSpacing();
+ }
+ }
+// int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; // QT orig
+ return QMAX(h, QApplication::globalStrut().height());
+}
+
+
+
+class KChatBasePrivate
+{
+public:
+ KChatBasePrivate()
+ {
+ mBox = 0;
+ mEdit = 0;
+ mCombo = 0;
+
+ mAcceptMessage = true;
+ mMaxItems = -1;
+ }
+ QListBox* mBox;
+ KLineEdit* mEdit;
+ QComboBox* mCombo;
+ bool mAcceptMessage;
+ int mMaxItems;
+
+ QValueList<int> mIndex2Id;
+
+ QFont mNameFont;
+ QFont mMessageFont;
+ QFont mSystemNameFont;
+ QFont mSystemMessageFont;
+};
+
+KChatBase::KChatBase(QWidget* parent, bool noComboBox) : QFrame(parent)
+{
+ init(noComboBox);
+}
+
+KChatBase::~KChatBase()
+{
+// kdDebug(11000) << "KChatBase: DESTRUCT (" << this << ")" << endl;
+ saveConfig();
+ delete d;
+}
+
+void KChatBase::init(bool noComboBox)
+{
+// kdDebug(11000) << "KChatBase: INIT (" << this << ")" << endl;
+
+ d = new KChatBasePrivate;
+
+ setMinimumWidth(100);
+ setMinimumHeight(150);
+
+ QVBoxLayout* l = new QVBoxLayout(this);
+
+ d->mBox = new QListBox(this);
+ connect(d->mBox, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&)),
+ this, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&)));
+ l->addWidget(d->mBox);
+ d->mBox->setVScrollBarMode(QScrollView::AlwaysOn);
+ d->mBox->setHScrollBarMode(QScrollView::AlwaysOff);
+ d->mBox->setFocusPolicy(QWidget::NoFocus);
+// d->mBox->setSelectionMode(QListBox::NoSelection);
+ d->mBox->setSelectionMode(QListBox::Single);
+
+ l->addSpacing(5);
+
+ QHBoxLayout* h = new QHBoxLayout(l);
+ d->mEdit = new KLineEdit(this);
+ d->mEdit->setHandleSignals(false);
+ d->mEdit->setTrapReturnKey(true);
+ d->mEdit->completionObject(); // add the completion object
+ d->mEdit->setCompletionMode(KGlobalSettings::CompletionNone);
+ connect(d->mEdit, SIGNAL(returnPressed(const QString&)), this, SLOT(slotReturnPressed(const QString&)));
+ h->addWidget(d->mEdit);
+
+ if (!noComboBox) {
+ d->mCombo = new QComboBox(this);
+ h->addWidget(d->mCombo);
+ addSendingEntry(i18n("Send to All Players"), SendToAll);//FIXME: where to put the id?
+ }
+
+ d->mAcceptMessage = true; // by default
+ setMaxItems(-1); // unlimited
+
+ if (kapp) {
+ // kapp might be NULL as well - in case we are in Qt designer.
+ readConfig();
+ }
+}
+
+bool KChatBase::acceptMessage() const
+{ return d->mAcceptMessage; }
+
+void KChatBase::setAcceptMessage(bool a)
+{ d->mAcceptMessage = a; }
+
+bool KChatBase::addSendingEntry(const QString& text, int id)
+{
+//FIXME: is ID used correctly?
+// do we need ID at all?
+// what the hell should be here?
+// d->mCombo->insertItem(i18n("Send to All Players"), SendToAll);
+ return insertSendingEntry(text, id);
+}
+
+bool KChatBase::insertSendingEntry(const QString& text, int id, int index)
+{
+ if (!d->mCombo) {
+ kdWarning(11000) << "KChatBase: Cannot add an entry to the combo box" << endl;
+ return false;
+ }
+ if (d->mIndex2Id.findIndex(id) != -1) {
+ kdError(11000) << "KChatBase: Cannot add more than one entry with the same ID! " << endl;
+ kdError(11000) << "KChatBase: Text="<<text<<endl;
+ return false;
+ }
+ d->mCombo->insertItem(text, index);
+ if (index < 0) {
+ d->mIndex2Id.append(id);
+ } else {
+ d->mIndex2Id.insert(d->mIndex2Id.at(index), id);
+ }
+ if (d->mIndex2Id.count() != (uint)d->mCombo->count()) {
+ kdError(11000) << "KChatBase: internal ERROR - local IDs do not match combo box entries!" << endl;
+ }
+ return true;
+}
+
+int KChatBase::sendingEntry() const
+{
+ if (!d->mCombo) {
+ kdWarning(11001) << "Cannot retrieve index from NULL combo box" << endl;
+ return -1;
+ }
+ int index = d->mCombo->currentItem();
+ if (d->mIndex2Id.at(index) == d->mIndex2Id.end()) {
+ kdWarning(11000) << "could not find the selected sending entry!" << endl;
+ return -1;
+ }
+ return d->mIndex2Id[index];
+}
+
+void KChatBase::removeSendingEntry(int id)
+{
+ if (!d->mCombo) {
+ kdWarning(11000) << "KChatBase: Cannot remove an entry from the combo box" << endl;
+ return;
+ }
+ d->mCombo->removeItem(findIndex(id));
+ d->mIndex2Id.remove(id);
+}
+
+void KChatBase::changeSendingEntry(const QString& text, int id)
+{
+ if (!d->mCombo) {
+ kdWarning(11000) << "KChatBase: Cannot change an entry in the combo box" << endl;
+ return;
+ }
+ int index = findIndex(id);
+ d->mCombo->changeItem(text, index);
+}
+
+void KChatBase::setSendingEntry(int id)
+{
+ if (!d->mCombo) {
+ kdWarning(11000) << "KChatBase: Cannot set an entry in the combo box" << endl;
+ return;
+ }
+ d->mCombo->setCurrentItem(findIndex(id));
+}
+
+int KChatBase::findIndex(int id) const
+{
+ return d->mIndex2Id.findIndex(id);
+}
+
+int KChatBase::nextId() const
+{
+ int i = SendToAll + 1;
+ while (d->mIndex2Id.findIndex(i) != -1) {
+ i++;
+ }
+ return i;
+}
+
+void KChatBase::addItem(const QListBoxItem* text)
+{
+ d->mBox->insertItem(text);
+ int index = d->mBox->count() -1;
+ d->mBox->setBottomItem(index);//FIXME: don't scroll to bottom if user scrolled down manually
+ if (maxItems() >= 0 && d->mBox->count() > (unsigned int)maxItems()) {
+ d->mBox->removeItem(0);
+ }
+}
+
+void KChatBase::addMessage(const QString& fromName, const QString& text)
+{
+//maybe "%1 says: %2" or so
+ addItem(layoutMessage(fromName, text));
+}
+
+void KChatBase::addSystemMessage(const QString& fromName, const QString& text)
+{
+ addItem(layoutSystemMessage(fromName, text));
+}
+
+QListBoxItem* KChatBase::layoutMessage(const QString& fromName, const QString& text)
+{
+ //TODO: KChatBaseConfigure? - e.g. color
+ QListBoxItem* message;
+ if (text.startsWith("/me ")) {
+ // replace "/me" by a nice star. leave one space after the star
+ QPixmap pix;
+ pix.load(locate("data", QString::fromLatin1("kdegames/pics/star.png")));
+
+ //TODO KChatBasePixmap? Should change the font here!
+
+ message = (QListBoxItem*)new QListBoxPixmap(pix, i18n("%1 %2").arg(fromName).arg(text.mid(3)));
+ } else {
+ // the text is not edited in any way. just return an item
+ KChatBaseText* m = new KChatBaseText(fromName, text);
+ m->setNameFont(&d->mNameFont);
+ m->setMessageFont(&d->mMessageFont);
+ message = (QListBoxItem*)m;
+ }
+ return message;
+}
+
+QListBoxItem* KChatBase::layoutSystemMessage(const QString& fromName, const QString& text)
+{
+ //TODO: KChatBaseConfigure? - e.g. color
+
+ // no need to check for /me etc.
+ KChatBaseText* m = new KChatBaseText(i18n("--- %1").arg(fromName), text);
+ m->setNameFont(&d->mSystemNameFont);
+ m->setMessageFont(&d->mSystemMessageFont);
+ return (QListBoxItem*)m;
+}
+
+void KChatBase::slotReturnPressed(const QString& text)
+{
+ if (text.length() <= 0) {
+ // no text entered - probably hit return by accident
+ return;
+ } else if (!acceptMessage()) {
+ return;
+ }
+ d->mEdit->completionObject()->addItem(text);
+// connect(d->mEdit, SIGNAL(returnPressed(const QString&)), comp, SLOT(addItem(const QString&)));
+ d->mEdit->clear();
+ returnPressed(text);
+}
+
+QString KChatBase::comboBoxItem(const QString& name) const
+{ // TODO: such a function for "send to all" and "send to my group"
+ return i18n("Send to %1").arg(name);
+}
+
+void KChatBase::slotClear()
+{
+ d->mBox->clear();
+}
+
+void KChatBase::setCompletionMode(KGlobalSettings::Completion mode)
+{ d->mEdit->setCompletionMode(mode); }
+
+void KChatBase::setNameFont(const QFont& font)
+{
+ d->mNameFont = font;
+ d->mBox->triggerUpdate(false);
+}
+
+void KChatBase::setMessageFont(const QFont& font)
+{
+ d->mMessageFont = font;
+ d->mBox->triggerUpdate(false);
+}
+
+void KChatBase::setBothFont(const QFont& font)
+{
+ setNameFont(font);
+ setMessageFont(font);
+}
+
+const QFont& KChatBase::nameFont() const
+{ return d->mNameFont; }
+
+const QFont& KChatBase::messageFont() const
+{ return d->mMessageFont; }
+
+void KChatBase::setSystemNameFont(const QFont& font)
+{
+ d->mSystemNameFont = font;
+ d->mBox->triggerUpdate(false);
+}
+
+void KChatBase::setSystemMessageFont(const QFont& font)
+{
+ d->mSystemMessageFont = font;
+ d->mBox->triggerUpdate(false);
+}
+
+void KChatBase::setSystemBothFont(const QFont& font)
+{
+ setSystemNameFont(font);
+ setSystemMessageFont(font);
+}
+
+const QFont& KChatBase::systemNameFont() const
+{ return d->mSystemNameFont; }
+
+const QFont& KChatBase::systemMessageFont() const
+{ return d->mSystemMessageFont; }
+
+void KChatBase::saveConfig(KConfig* conf)
+{
+ QString oldGroup;
+ if (!conf) {
+ conf = kapp->config();
+ oldGroup = conf->group();
+ conf->setGroup("KChatBase");
+ }
+
+ conf->writeEntry("NameFont", nameFont());
+ conf->writeEntry("MessageFont", messageFont());
+ conf->writeEntry("SystemNameFont", systemNameFont());
+ conf->writeEntry("SystemMessageFont", systemMessageFont());
+ conf->writeEntry("MaxMessages", maxItems());
+
+ if (!oldGroup.isNull()) {
+ conf->setGroup(oldGroup);
+ }
+}
+
+void KChatBase::readConfig(KConfig* conf)
+{
+ QString oldGroup;
+ if (!conf) {
+ conf = kapp->config();
+ oldGroup = conf->group();
+ conf->setGroup("KChatBase");
+ }
+
+ setNameFont(conf->readFontEntry("NameFont"));
+ setMessageFont(conf->readFontEntry("MessageFont"));
+ setSystemNameFont(conf->readFontEntry("SystemNameFont"));
+ setSystemMessageFont(conf->readFontEntry("SystemMessageFont"));
+ setMaxItems(conf->readNumEntry("MaxMessages", -1));
+
+ if (!oldGroup.isNull()) {
+ conf->setGroup(oldGroup);
+ }
+}
+
+void KChatBase::clear()
+{
+ d->mBox->clear();
+}
+
+void KChatBase::setMaxItems(int maxItems)
+{
+ d->mMaxItems = maxItems;
+ //TODO cut too many messages
+ if (maxItems == 0) {
+ clear();
+ } else if (maxItems > 0) {
+ while (d->mBox->count() > (unsigned int)maxItems) {
+ d->mBox->removeItem(0);
+ }
+ }
+}
+
+int KChatBase::maxItems() const
+{ return d->mMaxItems; }
+
+
+#include "kchatbase.moc"
diff --git a/libkdegames/kchatbase.h b/libkdegames/kchatbase.h
new file mode 100644
index 00000000..07f786f9
--- /dev/null
+++ b/libkdegames/kchatbase.h
@@ -0,0 +1,510 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __KCHATBASE_H__
+#define __KCHATBASE_H__
+
+#include <qframe.h>
+#include <qstring.h>
+#include <qlistbox.h>
+
+#include <kglobalsettings.h>
+#include <kdemacros.h>
+class QListBoxItem;
+
+class KConfig;
+
+
+class KChatBaseTextPrivate;
+
+/**
+ * A QListBoxText implementation for KChatBase.
+ *
+ * It supports different colors, text fonts, ...
+ *
+ * A KChatBaseText consists of two text items: first the player part then the
+ * text part. This honors KChatBase::addMessage which also uses both.
+ * You can leave the player part out if you don't need it - there won't be any
+ * difference.
+ *
+ * You can set different colors and fonts for both parts. In the future there
+ * will probably some kind of KChatBaseDialog which offers the user the ability
+ * to configure things like color and font on the fly.
+ **/
+class KChatBaseText : public QListBoxText
+{
+public:
+
+ /**
+ * Constructs a KChatBaseText object with the player and text part
+ **/
+ KChatBaseText(const QString& player, const QString& text);
+
+ /**
+ * Constructs a KChatBaseText object without player part
+ **/
+ KChatBaseText(const QString& text);
+
+ /**
+ * Destruct a KChatBaseText object.
+ **/
+ virtual ~KChatBaseText();
+
+ /**
+ * Set the name part of a message. A message is usually shown like
+ * "name: text" and you can change both parts independently.
+ *
+ * @see setMessage
+ * @param name The name of the sender (e.g. the player)
+ **/
+ void setName(const QString& name);
+
+ /**
+ * Set the text part of a message. A message is usually shown like
+ * "name: message" and you can change both parts independently.
+ *
+ * See also setName
+ * @param message The message that has been sent
+ **/
+ void setMessage(const QString& message);
+
+ /**
+ * @return The name part of a message.
+ * @see setName
+ **/
+ const QString& name() const;
+
+ /**
+ * @return The message text.
+ * @see setMessage
+ **/
+ const QString& message() const;
+
+ /**
+ * You can set the font of the sender name independently of the message
+ * itself. This font is used as the "name: " part of the message.
+ * @return The font that is used for the name
+ **/
+ QFont nameFont() const;
+
+ /**
+ * You can set the font of the message independently of the sender name.
+ * This font is used as the text part of the message.
+ * @return The font thaz is used for message text
+ **/
+ QFont messageFont() const;
+
+ /**
+ * Set the font for the name.
+ * @see nameFont
+ * @param font A pointer to the name font. Only the pointer is stored so
+ * don't delete the object. This way there is only one object for a lot
+ * of messages in memory.
+ **/
+ void setNameFont(const QFont* font);
+
+ /**
+ * Set the font for the message text.
+ * @see messageFont
+ * @param font A pointer to the message font. Only the pointer is stored so
+ * don't delete the object! This way there is only one object for a lot
+ * of messages in memory.
+ **/
+ void setMessageFont(const QFont* font);
+
+ /**
+ **/
+ virtual int width(QListBox* ) const;
+
+ /**
+ **/
+ virtual int height(QListBox* ) const;
+
+protected:
+ /**
+ **/
+ virtual void paint(QPainter*);
+
+private:
+ void init();
+
+private:
+ KChatBaseTextPrivate* d;
+};
+
+
+class KChatBasePrivate;
+
+/**
+ * @short The base class for chat widgets
+ *
+ * This is the base class for both KChat and KGameChat. KGameChat is the class
+ * you want to use if you write a KGame based game as it will do most things for
+ * you. KChat is more or less the same but not KGame dependant
+ *
+ * KChatBase provides a complete chat widget, featuring different sending means
+ * (e.g. "send to all", "send to player1", "send to group2" and so on - see
+ * addSendingEntry). It also provides full auto-completion capabilities (see
+ * KCompletion and KLineEdit) which defaults to disabled. The user can
+ * change this by right-clicking on the KLineEdit widget and selecting the
+ * desired behaviour. You can also change this manually by calling
+ * setCompletionMode.
+ *
+ * To make KChatBase useful you have to overwrite at least returnPressed.
+ * Here you should send the message to all of your clients (or just some of
+ * them, depending on sendingEntry).
+ *
+ * To add a message just call addMessage with the nickname of the player
+ * who sent the message and the message itself. If you don't want to use
+ * layoutMessage by any reason you can also call addItem directly. But you
+ * should better replace layoutMessage instead.
+ *
+ * You probably don't want to use the abstract class KChatBase directly but use
+ * one of the derived classess KChat or KGameChat. The latter is the
+ * widget of choice if you develop a KGame application as you don't have to
+ * do anything but providing a KGame object.
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KChatBase : public QFrame
+{
+ Q_OBJECT
+public:
+ /**
+ * @param parent The parent widget for this widget.
+ * @param noComboBox If true then the combo box where the player can
+ * choose where to send messages to (either globally or just to some
+ * players) will not be added.
+ **/
+ KChatBase(QWidget* parent, bool noComboBox = false);
+
+ /**
+ * Destruct the KChatBase object
+ *
+ * Also calls saveConfig
+ **/
+ virtual ~KChatBase();
+
+ enum SendingIds {
+ SendToAll = 0
+ };
+
+ /**
+ * @return The name that will be shown for messages from this widget. Either the
+ * string that was set by setFromName or the name of the player
+ * that was set by setFromPlayer
+ **/
+ virtual const QString& fromName() const = 0;
+
+ /**
+ * Adds a new entry in the combo box. The default is "send to all
+ * players" only. This function is provided for convenience. You can
+ * also call inserSendingEntry with index = -1.
+ * See also nextId!
+ * @param text The text of the new entry
+ * @param id An ID for this entry. This must be unique for this
+ * entry. It has nothing to do with the position of the entry in the
+ * combo box. See nextId
+ * @return True if successful, otherwise false (e.g. if the id is already used)
+ **/
+ bool addSendingEntry(const QString& text, int id);
+
+ /**
+ * Inserts a new entry in the combo box.
+ * @param text The entry
+ * @param id An ID for this entry. This must be unique for this
+ * entry. It has nothing to do with the position of the entry in the
+ * combo box!
+ * @see nextId
+ * @param index The position of the entry. If -1 the entry will be added
+ * at the bottom
+ * @return True if successful, otherwise false (e.g. if the id is already used)
+ **/
+ bool insertSendingEntry(const QString& text, int id, int index = -1);
+
+ /**
+ * This changes a combo box entry.
+ * @param text The new text of the entry
+ * @param id The ID of the item to be changed
+ **/
+ void changeSendingEntry(const QString& text, int id);
+
+ /**
+ * This selects a combo box entry.
+ * @param id The ID of the item to be selected
+ **/
+ void setSendingEntry(int id);
+
+ /**
+ * Removes the entry with the ID id from the combo box. Note that id is
+ * _not_ the index of the entry!
+ * @see addSendingEntry
+ * @param id The unique id of the entry
+ **/
+ void removeSendingEntry(int id);
+
+ /**
+ * @return The _unique ID_ of the sending entry that has been selected.
+ * @see addSendingEntry
+ *
+ * Note that the entry "send to all" _always_ uses
+ * KChatBase::SendToAll, i.e. 0 as id!
+ **/
+ int sendingEntry() const;
+
+ /**
+ * @return The index of the combo box entry with the given id
+ **/
+ int findIndex(int id) const;
+
+ /**
+ * @return An ID that has not yet been used in the combo box.
+ * @see addSendingEntry
+ **/
+ int nextId() const;
+
+ /**
+ * @return True if this widget is able to send messages (see
+ * returnPressed) and false if not. The default implementation returns
+ * the value which has been set by setAcceptMessage (true by
+ * default)
+ **/
+ virtual bool acceptMessage() const;
+
+ /**
+ * See KLineEdit::setCompletionMode
+ **/
+ void setCompletionMode(KGlobalSettings::Completion mode);
+
+ /**
+ * Set the font that used used for the name part of a message. See also
+ * nameFont and setBothFont
+ **/
+ void setNameFont(const QFont& font);
+
+ /**
+ * Set the font that used used for the message part of a message.
+ * @see messageFont, setBothFont
+ **/
+ void setMessageFont(const QFont& font);
+
+ /**
+ * This sets both - nameFont and messageFont to font. You
+ * probably want to use this if you don't wish to distinguish between
+ * these parts of a message.
+ * @param font A font used for both nameFont and messageFont
+ **/
+ void setBothFont(const QFont& font);
+
+ /**
+ * Same as setNameFont but applies only to system messages.
+ * @see layoutSystemMessage
+ **/
+ void setSystemNameFont(const QFont& font);
+
+ /**
+ * Same as setMessageFont but applies only to system messages.
+ * @see layoutSystemMessage
+ **/
+ void setSystemMessageFont(const QFont& font);
+
+ /**
+ * Same as setBothFont but applies only to system messages.
+ * @see layoutSystemMessage
+ **/
+ void setSystemBothFont(const QFont& font);
+
+ /**
+ * This font should be used for the name (the "from: " part) of a
+ * message. layoutMessage uses this to set the font using
+ * KChatBaseText::setNameFont but if you want to overwrite
+ * layoutMessage you should do this yourself.
+ * @return The font that is used for the name part of the message.
+ **/
+ const QFont& nameFont() const;
+
+ /**
+ * This font should be used for a message. layoutMessage sets the
+ * font of a message using KChatBaseText::setMessageFont but if ypu
+ * replace layoutMessage with your own function you should use
+ * messageFont() yourself.
+ * @return The font that is used for a message
+ **/
+ const QFont& messageFont() const;
+
+ /**
+ * Same as systemNameFont but applies only to system messages.
+ * @see layoutSystemMessage
+ **/
+ const QFont& systemNameFont() const;
+
+ /**
+ * Same as systemMessageFont but applies only to system messages.
+ * @see layoutSystemMessage
+ **/
+ const QFont& systemMessageFont() const;
+
+ /**
+ * Save the configuration of the dialog to a KConfig object. If
+ * the supplied KConfig pointer is NULL then kapp->config() is used
+ * instead (and the group is changed to "KChatBase") butr the current
+ * group is restored at the end.
+ * @param conf A pointer to the KConfig object to save the config
+ * to. If you use 0 then kapp->config() is used and the group is changed
+ * to "KChatBase" (the current group is restored at the end).
+ **/
+ virtual void saveConfig(KConfig* conf = 0);
+
+ /**
+ * Read the configuration from a KConfig object. If the pointer is
+ * NULL kapp->config() is used and the group is changed to "KChatBase".
+ * The current KConfig::group is restored after this call.
+ **/
+ virtual void readConfig(KConfig* conf = 0);
+
+ /**
+ * Set the maximum number of items in the list. If the number of item
+ * exceeds the maximum as many items are deleted (oldest first) as
+ * necessary. The number of items will never exceed this value.
+ * @param maxItems the maximum number of items. -1 (default) for
+ * unlimited.
+ **/
+ void setMaxItems(int maxItems);
+
+ /**
+ * Clear all messages in the list.
+ **/
+ void clear();
+
+ /**
+ * @return The maximum number of messages in the list. -1 is unlimited. See also
+ * setMaxItems
+ **/
+ int maxItems() const;
+
+
+public slots:
+ /**
+ * Add a text in the listbox. See also signalSendMessage()
+ *
+ * Maybe you want to replace this with a function that creates a nicer text
+ * than "fromName: text"
+ *
+ * Update: the function layoutMessage is called by this now. This
+ * means that you will get user defined outlook on the messages :-)
+ * @param fromName The player who sent this message
+ * @param text The text to be added
+ **/
+ virtual void addMessage(const QString& fromName, const QString& text);
+
+ /**
+ * This works just like addMessage but adds a system message.
+ * layoutSystemMessage is used to generate the displayed item. System
+ * messages will have a different look than player messages.
+ *
+ * You may wish to use this to display status information from your game.
+ **/
+ virtual void addSystemMessage(const QString& fromName, const QString& text);
+
+ /**
+ * This member function is mainly internally used to add a message. It
+ * is called by addMessage which creates a single text from a
+ * player name and a text. You will hardly ever use this - but if you
+ * need it it will be here ;-)
+ *
+ * But you may want to replace this in a derived class to create a
+ * non-default (maybe nicer ;-) ) behaviour
+ * @param item The QListBoxItem that is being added
+ **/
+ virtual void addItem(const QListBoxItem* item);
+
+
+ /**
+ * This clears all messages in the view. Note that only the messages are
+ * cleared, not the sender names in the combo box!
+ **/
+ void slotClear();
+
+ /**
+ * @param a If false this widget cannot send a message until
+ * setAcceptMessage(true) is called
+ **/
+ void setAcceptMessage(bool a);
+
+signals:
+ /**
+ * Emitted when the user right-clicks on a list item.
+ * @see QListBox::rightButtonClicked
+ **/
+ void rightButtonClicked(QListBoxItem*, const QPoint&);
+
+protected:
+ /**
+ * This is called whenever the user pushed return ie wants to send a
+ * message.
+ *
+ * Note that you MUST add the message to the widget when this function
+ * is called as it has already been added to the KCompletion object
+ * of the KLineEdit widget!
+ *
+ * Must be implemented in derived classes
+ * @param text The message to be sent
+ **/
+ virtual void returnPressed(const QString& text) = 0;
+
+ /**
+ * Replace to customise the combo box.
+ *
+ * Default: i18n("Send to %1).arg(name)
+ * @param name The name of the player
+ * @return The string as it will be shown in the combo box
+ **/
+ virtual QString comboBoxItem(const QString& name) const;
+
+ /**
+ * Create a QListBoxItem for this message. This function is not yet
+ * written usefully - currently just a QListBoxTex object is
+ * created which shows the message in this format: "fromName: text".
+ * This should fit most peoples needs but needs further improvements.
+ **/
+ virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text);
+
+ /**
+ * Create a QListBoxItem for this message. This does the same as
+ * layoutMessage but generates a system message. You might want to
+ * use such a message to display e.g. status information from your game.
+ *
+ * The default implementation just prepends "--- ".
+ **/
+ virtual QListBoxItem* layoutSystemMessage(const QString& fromName, const QString& text);
+
+private slots:
+ /**
+ * Check if a text was entered and if acceptMessage returns true.
+ * Then add the message to the KCompletion object of the KLineEdit
+ * widget and call returnPressed
+ **/
+ void slotReturnPressed(const QString&);
+
+private:
+ void init(bool noComboBox);
+
+ KChatBasePrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kchatdialog.cpp b/libkdegames/kchatdialog.cpp
new file mode 100644
index 00000000..bd196e7c
--- /dev/null
+++ b/libkdegames/kchatdialog.cpp
@@ -0,0 +1,253 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kchatdialog.h"
+
+#include "kchatbase.h"
+
+#include <klocale.h>
+#include <kfontdialog.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+class KChatDialogPrivate
+{
+ public:
+ KChatDialogPrivate()
+ {
+ mTextPage = 0;
+
+ mNamePreview = 0;
+ mTextPreview = 0;
+ mSystemNamePreview = 0;
+ mSystemTextPreview = 0;
+
+ mChat = 0;
+ }
+
+ QFrame* mTextPage;
+
+ QLabel* mNamePreview;
+ QLabel* mTextPreview;
+ QLabel* mSystemNamePreview;
+ QLabel* mSystemTextPreview;
+
+ QLineEdit* mMaxMessages;
+
+ KChatBase* mChat;
+};
+
+KChatDialog::KChatDialog(KChatBase* chat, QWidget* parent, bool modal)
+// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true)
+ : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true)
+{
+ init();
+ plugChatWidget(chat);
+}
+
+KChatDialog::KChatDialog(QWidget* parent, bool modal)
+// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true)
+ : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true)
+{
+ init();
+}
+
+KChatDialog::~KChatDialog()
+{
+ delete d;
+}
+
+void KChatDialog::init()
+{
+ d = new KChatDialogPrivate;
+// d->mTextPage = addPage(i18n("&Messages"));// not a good name - game Messages?
+ d->mTextPage = plainPage();
+ QGridLayout* layout = new QGridLayout(d->mTextPage, 7, 2, KDialog::marginHint(), KDialog::spacingHint());
+
+// General fonts
+ QPushButton* nameFont = new QPushButton(i18n("Name Font..."), d->mTextPage);
+ connect(nameFont, SIGNAL(pressed()), this, SLOT(slotGetNameFont()));
+ layout->addWidget(nameFont, 0, 0);
+ QPushButton* textFont = new QPushButton(i18n("Text Font..."), d->mTextPage);
+ connect(textFont, SIGNAL(pressed()), this, SLOT(slotGetTextFont()));
+ layout->addWidget(textFont, 0, 1);
+
+ QFrame* messagePreview = new QFrame(d->mTextPage);
+ messagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ QHBoxLayout* messageLayout = new QHBoxLayout(messagePreview);
+ layout->addMultiCellWidget(messagePreview, 1, 1, 0, 1);
+
+ d->mNamePreview = new QLabel(i18n("Player: "), messagePreview);
+ messageLayout->addWidget(d->mNamePreview, 0);
+ d->mTextPreview = new QLabel(i18n("This is a player message"), messagePreview);
+ messageLayout->addWidget(d->mTextPreview, 1);
+
+ layout->addRowSpacing(2, 10);
+
+// System Message fonts
+ QLabel* systemMessages = new QLabel(i18n("System Messages - Messages directly sent from the game"), d->mTextPage);
+ layout->addMultiCellWidget(systemMessages, 3, 3, 0, 1);
+ QPushButton* systemNameFont = new QPushButton(i18n("Name Font..."), d->mTextPage);
+ connect(systemNameFont, SIGNAL(pressed()), this, SLOT(slotGetSystemNameFont()));
+ layout->addWidget(systemNameFont, 4, 0);
+ QPushButton* systemTextFont = new QPushButton(i18n("Text Font..."), d->mTextPage);
+ connect(systemTextFont, SIGNAL(pressed()), this, SLOT(slotGetSystemTextFont()));
+ layout->addWidget(systemTextFont, 4, 1);
+
+ QFrame* systemMessagePreview = new QFrame(d->mTextPage);
+ systemMessagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ QHBoxLayout* systemMessageLayout = new QHBoxLayout(systemMessagePreview);
+ layout->addMultiCellWidget(systemMessagePreview, 5, 5, 0, 1);
+
+ d->mSystemNamePreview = new QLabel(i18n("--- Game: "), systemMessagePreview);
+ systemMessageLayout->addWidget(d->mSystemNamePreview, 0);
+ d->mSystemTextPreview = new QLabel(i18n("This is a system message"), systemMessagePreview);
+ systemMessageLayout->addWidget(d->mSystemTextPreview, 1);
+
+// message count
+ QLabel* maxMessages = new QLabel(i18n("Maximal number of messages (-1 = unlimited):"), d->mTextPage);
+ layout->addWidget(maxMessages, 6, 0);
+ d->mMaxMessages = new QLineEdit(d->mTextPage);
+ d->mMaxMessages->setText(QString::number(-1));
+ layout->addWidget(d->mMaxMessages, 6, 1);
+}
+
+void KChatDialog::slotGetNameFont()
+{
+ QFont font = nameFont();
+ KFontDialog::getFont(font);
+ setNameFont(font);
+}
+
+void KChatDialog::slotGetTextFont()
+{
+ QFont font = textFont();
+ KFontDialog::getFont(font);
+ setTextFont(font);
+}
+
+void KChatDialog::slotGetSystemNameFont()
+{
+ QFont font = systemNameFont();
+ KFontDialog::getFont(font);
+ setSystemNameFont(font);
+}
+
+void KChatDialog::slotGetSystemTextFont()
+{
+ QFont font = systemTextFont();
+ KFontDialog::getFont(font);
+ setSystemTextFont(font);
+}
+
+QFont KChatDialog::nameFont() const
+{
+ return d->mNamePreview->font();
+}
+
+QFont KChatDialog::textFont() const
+{
+ return d->mTextPreview->font();
+}
+
+QFont KChatDialog::systemNameFont() const
+{
+ return d->mSystemNamePreview->font();
+}
+
+QFont KChatDialog::systemTextFont() const
+{
+ return d->mSystemTextPreview->font();
+}
+
+void KChatDialog::plugChatWidget(KChatBase* widget, bool applyFonts)
+{
+ d->mChat = widget;
+ if (applyFonts && d->mChat) {
+ setNameFont(d->mChat->nameFont());
+ setTextFont(d->mChat->messageFont());
+ setSystemNameFont(d->mChat->systemNameFont());
+ setSystemTextFont(d->mChat->systemMessageFont());
+ setMaxMessages(d->mChat->maxItems());
+ }
+}
+
+void KChatDialog::configureChatWidget(KChatBase* widget)
+{
+ if (!widget) {
+ return;
+ }
+ widget->setNameFont(nameFont());
+ widget->setMessageFont(textFont());
+
+ widget->setSystemNameFont(systemNameFont());
+ widget->setSystemMessageFont(systemTextFont());
+
+ widget->setMaxItems(maxMessages());
+}
+
+void KChatDialog::slotOk()
+{
+ slotApply();
+ KDialogBase::slotOk();
+}
+
+void KChatDialog::slotApply()
+{
+ configureChatWidget(d->mChat);
+}
+
+void KChatDialog::setNameFont(QFont f)
+{
+ d->mNamePreview->setFont(f);
+}
+
+void KChatDialog::setTextFont(QFont f)
+{
+ d->mTextPreview->setFont(f);
+}
+
+void KChatDialog::setSystemNameFont(QFont f)
+{
+ d->mSystemNamePreview->setFont(f);
+}
+
+void KChatDialog::setSystemTextFont(QFont f)
+{
+ d->mSystemTextPreview->setFont(f);
+}
+
+void KChatDialog::setMaxMessages(int max)
+{
+ d->mMaxMessages->setText(QString::number(max));
+}
+
+int KChatDialog::maxMessages() const
+{
+ bool ok;
+ int max = d->mMaxMessages->text().toInt(&ok);
+ if (!ok) {
+ return -1; // unlimited is default
+ }
+ return max;
+}
+
+#include "kchatdialog.moc"
diff --git a/libkdegames/kchatdialog.h b/libkdegames/kchatdialog.h
new file mode 100644
index 00000000..96b3eef2
--- /dev/null
+++ b/libkdegames/kchatdialog.h
@@ -0,0 +1,119 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KCHATDIALOG_H__
+#define __KCHATDIALOG_H__
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+
+class KChatBase;
+
+class KChatDialogPrivate;
+
+class KDE_EXPORT KChatDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a KChatDialog widget
+ **/
+ KChatDialog(QWidget* parent, bool modal = false);
+
+ /**
+ * Construct a KChatDialog widget which automatically configures the
+ * @ref KChatBase widget. You probably want to use this as you don't
+ * have to care about the configuration stuff yourself.
+ **/
+ KChatDialog(KChatBase* chatWidget, QWidget* parent, bool modal = false);
+
+ /**
+ * Destruct the dialog
+ **/
+ ~KChatDialog();
+
+ /**
+ * @return The font that shall be used as the "name: " part of a normal
+ * message.
+ **/
+ QFont nameFont() const;
+
+ /**
+ * @return The font that shall be used for normal messages.
+ **/
+ QFont textFont() const;
+
+ /**
+ * @return The font that shall be used as the "name: " part of a system
+ * (game) message.
+ **/
+ QFont systemNameFont() const;
+
+ /**
+ * @return The font that shall be used for a system (game) message.
+ **/
+ QFont systemTextFont() const;
+
+ /**
+ * Set the widget that will be configured by the dialog. Use this if you
+ * don't want to configure the widget yourself.
+ * @param widget The chat widget that shall be configured
+ * @param Whether you want to have the current @ref KChatBase fonts as
+ * defaults in the dialog
+ **/
+ void plugChatWidget(KChatBase* widget, bool applyFonts = true);
+
+ /**
+ * Used to configure the chat widget according to the user settings.
+ * This is called automatically if @ref plugChatWidget was called
+ * before.
+ * @param widget The chat widget that shall be configured
+ **/
+ void configureChatWidget(KChatBase* widget);
+
+ /**
+ * @return The maximal allowed messages in the chat widget. -1 is
+ * unlimited
+ **/
+ int maxMessages() const;
+
+protected slots:
+ void slotGetNameFont();
+ void slotGetTextFont();
+ void slotGetSystemNameFont();
+ void slotGetSystemTextFont();
+
+ virtual void slotApply();
+ virtual void slotOk();
+
+private:
+ void setNameFont(QFont);
+ void setTextFont(QFont);
+ void setSystemNameFont(QFont);
+ void setSystemTextFont(QFont);
+ void setMaxMessages(int max);
+
+private:
+ void init();
+
+private:
+ KChatDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/COMPAT b/libkdegames/kgame/COMPAT
new file mode 100644
index 00000000..146d3a88
--- /dev/null
+++ b/libkdegames/kgame/COMPAT
@@ -0,0 +1,55 @@
+06.09.2001: replace the signal signalCreatePlayer by the virtual function
+ createPlayer. It has the same arguments but the return value
+ is the new player
+06.09.2001: the KGameConfig dialog changes the parameter initConfigs from bool
+ to long. Use the ConfigOptions to specify what options you want
+ to have enabled. Default is all
+06.09.2001: some int->Q_UINT32 in sender, receiver and player parameters. maybe
+ more will follow.
+06.09.2001: KGameIO::signalPrepareMove(..., bool&) ->
+ KGameIO::signalPrepareMove(..., bool*): don't know why this was
+ necessary but it didn't work anymore...
+16.09.2001: KGamePropertyHandler uses bool* for the sent parameter now. This is
+ because QT3 obviously doesn't honor referneces in signals/slots.
+ This might even be a QT bug. Bad situation - we use references
+ everywhere in KGame... hope nothing else is affecterd by this
+ problem (signalPrepareMove was fixed already by me)
+18.09.2001: bool* for Key/Mouseevents and IOAdded in kgameio.h too
+19.09.2001: Kgame:nextPlayer retunrs the KPlayer *nextplayer instead of bool
+19.09.2001: gameOver() renamed to checkGameOver() !!!!!
+18.09.2001: Question: Should the signal signalPlayerInput(QDataStream &,KPlayer *))
+ be made a virtual function?
+ MH: This is done now. As this is a central function your programs will
+ not run anymore. Fix: rename your slot which is connected to the above
+ signal to playerInput() and return TRUE in it. This will make it 100%
+ compatible to the old version. I think this chagne is necessary especially
+ as a signal is of no use here as you cannot read twice from the same stream.
+ Therefore there can be only one function processing the input. If you really
+ need a signal, you can of course simply emit it in the overwritten playerInput
+ function
+20.09.2001 playerInputFinished(void->KPlayer *)
+--------------------- KGAME_ALPHA_1 ---------------------
+06.10.2001 adding KGameNetwork::signalAdminStatusChanged - needed for
+ KGameDialog
+06.10.2001 KGame::loadGame() doesn't call setPolicy() anymore!
+08.10.2001 KGamePropertyList now honor policies! Use setPolicy(PolicyDirty) to
+ get the old behavior!
+ The behavior of KGamePropertyArray may have changed in this turn,
+ too!
+ The API stays the same.
+11.10.2001 KGameDialogGeneralConfig now doesn't provide setMin/maxPlayers()
+ anymore. The game should manage this internally. layout() is
+ obsolete as well
+18.10.2001 KPlayer::signalNetworkData contained QDataStream& instead of const
+ QByteArray& parameter (oops!). This is fixed now. All apps which
+ used this signal must be changed.
+18.10.2001 KGame::sendProperty(), KGame::sendPlayerProperty(),
+ KPlayer::sendProperty() and related functions contain a "int msgid"
+ parameter. This is the id() of the property handler. This parameter
+ enables us to easily add any number of property handler to a game
+ just by connecting it to existing send slots and call
+ processMessage() in slotNetworkData()
+03.11.2001 KPlayer::signalNetworkData now emits msgid-KGameMessage::IdUser just
+ like KGame::signalNetworkData does
+06.11.2001 KGameDialog has some small improvements - easier and IMHO better
+ constructor code. Most code should be compatible :-)
diff --git a/libkdegames/kgame/DESIGN b/libkdegames/kgame/DESIGN
new file mode 100644
index 00000000..b1c48146
--- /dev/null
+++ b/libkdegames/kgame/DESIGN
@@ -0,0 +1,407 @@
+This document tries to describe the design of KGame - the KDE multiplayer
+library.
+This document has been written by:
+ Andreas Beckermann <b_mann@gmx.de>
+ M. Heni <martin@heni-online.de>
+ Burkhard Lehner <Burkhard.Lehner@gmx.de>
+
+This document is published under the terms of the GNU FDL
+
+!!!
+Note that this is the initial version of this document and has not yet been
+aproved by all core developers (and is far from being complete)
+AB: please remove this comments as soon as all KGame hackers have read the
+document
+!!!
+
+Please refer the API documentation of every KGame class if you want up tp date
+information.
+
+
+0. Contents
+-----------
+
+1. DEFINITIONS
+1.1 Message Server
+1.2 Client or Message Client
+1.3 Master
+1.4 Admin
+1.5 Server
+1.6 Player
+
+2. Game Negotiation (M.Heni 20.05.2001)
+
+AB: 3.x is obsolete!
+3. Game Properties (Andreas Beckermann 28.07.2001) ( not yet completed )
+3.1 Using KGameProperty
+3.2 Custom Classes
+3.3 Concepts
+
+4. KGameIO (Andreas Beckermann 10.08.2001)
+
+5. Debugging (Andreas Beckermann 06.10.2001) TODO!
+5.1 KGameDebugDialog
+5.1.1 Debug KGame
+5.1.3 Debug Messages
+
+---------------------------------------------------------------------
+1. DEFINITIONS
+--------------
+
+First we have to clear some words. The main expressions used in KGame which
+need a definition are
+
+1.1 Message Server
+1.2 Client or Message Client
+1.3 Master
+1.4 Admin
+1.5 Server
+1.6 Player
+
+The most important and confusing ones are Master, Admin and Server. We make
+quite big differerences between those inside KGame.
+
+1.1 Message Server:
+-------------------
+A game has always exactly one object of this class, for local games as well as
+for network games. For network games, this object can be on one of the users
+processes (usually inside KGame), or it can also be on an independant computer,
+that has no idea about what game is played on it.
+
+A KMessageClient object can connect to it. It's main purpose is transmitting
+messages between KMessageClient objects.
+
+The Message Server is the main communication object. It is represented by the
+class KMessageServer. Note that there is also a "Master" and a "Server" which
+both differ heavily from the Message Server!
+
+1.2 Client, Message Client:
+---------------------------
+Each process that wants to take part in the game must have a
+KMessageClient object, that is connected to the Message Server. KGame creates
+this object and connects it to the Messager Server, so that you usually don't
+need to create these of your own. Even in a local game (no network) there
+must be a message server and one message client connected to it. This is usually
+done by the KGame object itself.
+
+Each message client has a unique ID number (a positive integer value, not zero).
+The KMessageClient object, which does the communication with the Message Server
+is called "Message Client" and to simplify the use we call any KGame object (or
+even the game process) that is connected to a game (i.e. even the Master) just
+"Client".
+
+The main purpose of a Client is to connect to a Master (i.e. to a game) and to
+communicate with it. A client has always a KGame object.
+
+1.3 Master:
+-----------
+The process that contains the Message Server is called "Master". In any local
+game this is the game process. The Message Server is started by KGame using
+KGame::setMaster(true) which is automatically done on startup. The Message
+Server is deleted automatically as soon as you connect to another Master.
+So in most cases there is exactly one KGame object / Client which is Master. But
+in one case there can be no KGame object / Client that is Master - if the
+Message Server is started as an own process. This "Message-Server-only" process
+is called "Master" then, although there is no KGame object which is Master. See
+also the definition of Admin!
+
+1.4 Admin:
+----------
+One (and only one) of the Clients is the Admin. He can configure the Message
+Server and the game in general in several ways. He can limit the maximum number
+of connected message clients and can drop the connection to some other clients,
+as well as he can configure game specific ssettings (like max/min players, start
+money, ...). The Admin also initializes newly connected Clients. If the Admin
+himself disconnects, another Client becomes Admin (The Admin can himself elect
+some other Client to become Admin. He himself loses that Admin status then).
+An Admin is *alway* a KGame object. The Admin is usually the same as the Master,
+but if the Master is an own process (i.e. the Message Server has been started
+outside KGame) then Master and Admin differ. An Admin *must* be a KGame object
+while the Master doesn't have to be.
+
+1.5 Server:
+-----------
+The definition of Server differs quite much from the definition of Master.
+A Master just accepts connections and forwards messages. The Server on the other
+side checks these messages, calculates results and sends the results to the
+Clients. That means the Server does all game calculations and doesn't directly
+forward the messages from one Clients to all other Clients.
+KGamer makes it possible to write multiplayer games even without a Server. All
+Clients just send their moves to the Master which forwards them to all Clients.
+Now all Clients calculate the result.
+E.g. in a poker game a player selects two of five cards to be exchanges and
+clicks on "draw" then the client sends the message "Exchange Card-1 and Card-2"
+to the Master. A no-Server solution forwards this to all Clients, and these
+Clients exchange the cards of the player. Note that in a no-Server solution
+(you can also see it as a "every-Client-is-a-Server solution") all Clients must
+have the same random seed and must be of the same version, i.e. the result must
+be the same on all Clients.
+In a Server-Solution on the other hand the Master forwards the Message
+("Exchange Card-1 and Card-2") to the Server only. This Server now calculates
+the result, and sends the new cards back to the Client.
+Both concepts have advantages and disadvantages. It is on you - the game
+developer - to decide which way is better for you.
+E.g. the Server-Solution makes it easier for you to write games. The version
+must not necessarily be the same, you have one central computer which does the
+calcultations. The No-Server-Solution on the other hand decreases network
+traffik as the Clients just send their moves and all Clients can calculate the
+reactions. I'm sure there are a lot of advantages/disadvantages more for both
+concepts.
+
+1.6 Player:
+-----------
+A KPlayer object is always connected to a KGame object and represents a
+player that participates the game. In a network game, every KPlayer object is
+duplicated on every other KGame object connected to the message server with
+virtual KPlayer objects. So at every time in the game, every KGame object has
+the same number of KPlayer objects.
+
+
+2. Game negotiation
+-------------------
+Upon connection of a client the admin and the client try to negotiate
+the game setup. Basically this means the game of the admin is transferred
+(saved) on the client. However, the client's players are added to the game
+as far as possible. If the addition of the client's players would add more
+players than allowed some players are inactivated. Which players are
+inactivated depends on their networkPriority(). This procedure allows
+easy replacement of players in a constant number game (e.g. chess). If
+this feature is of no interest simply keep the priorities equal (all 0)
+and the client will only add only players if the number of players is
+less or equal the maximum player number.
+
+The following is the negotiation procedure as started by the connection
+of a client. It is initiated in the negotiateNetworkGame() virtual function
+of KGame:
+
+admin: client:
+------------ ------------
+IdSetupGame
+ QINT16 Library
+ Version
+ QINT32 Application
+ cookie
+ IdSetupGameContinue;
+ QValueList<int> player id's
+ QValueList<int> network priority's
+
+IdGameLoad
+ all game data
+
+IdGameReactivate
+ QValueList<int> id's
+
+IdSyncRandom
+ int randomseed
+
+
+3. Game Properties
+------------------
+A very hard task in a network game is consistency. You have to achieve that all
+properties of the game and of all players have the same value on all clients
+every time. This is because
+a) the user might be confused if he sees "Player has $0" on client A but
+"Player has $10" on client B and
+b) Often game handling depends on those values, e.g. if the example above
+happens the computer might quit the game for the Player on client A because
+he/she doesn't have enough money. But the game continues on client B.
+Another not that easy task is the network protocol itself. You have to write
+several send() and receive() functions which apply changed values of properties
+to the local property.
+
+KGameProperty is designed to do all of this for you. KGameProperty is
+implemented as a template so you can use it theoretically for every type of data
+- even for your self defined classes.
+
+
+3.1 Using KGameProperty
+-----------------------
+It is basically very easy to use a KGameProperty. You first create your own
+class containing the property, e.g:
+class MyGame : public KGame
+{
+[...]
+protected:
+ KGamePropertyInt money;
+ KGamePropertyQString name;
+ KGameProperty<AntotherClass> myProperty;
+};
+KGamePropertyInt is just a typedef for KGameProperty<int> - just like
+KGamePropertyQString. Now you need to register the properties in the constructor
+of the class to the KGamePropertyHandler:
+MyGame::MyGame() : KGame(myCookie)
+{
+ money.registerData(KGamePropertyBase::IdUser+1, dataHandler(), "Money");
+ name.registerData(KGamePropertyBase::IdUser+2, this, "Name");
+ myProperty.registerData(KGamePropertyBase::IdUser+3, dataHandler(), "MyProperty");
+}
+-> You need to specify a *unique* ID. This ID must be greater than
+KGamePropertyBase::IdUser. IDs below this are reserved for KGame. Probably this
+will be changed so that you cannot use IDs below IdUser in the future. Then you
+have to specify the dataHandler(). You can also use a KGame or KPlayer pointer.
+This will automatically use KGame::dataHandler() or KPlayer::dataHandler().
+Finally you *can* provide a name for the property. This will be used for
+debugging in KGameDebugDialog. If you want to save some memory you can leave
+this out.
+Note that if you use pointers to create the properties dynamically they are
+*not* deleted automatically! You MUST delete them yourself!
+Now you can use the KGameProperty like every variable else. See also Section
+"3.3 Concepts" for restrictions in use.
+
+3.2 Custom Classes
+------------------
+To make custom classes possible you have to implement several operators for your
+them: you need at least << and >> for QDataStream as well as "==" for your own
+class. To overload the "<<" you would e.g. do something like this:
+QDataStream& operator<<(QDataStream& stream, MyData& data)
+{
+ int type = data.type;
+ QString name = data.name;
+ stream << type << name;
+ return stream;
+}
+So you basically just have to split your class to several basic types and stream
+them.
+
+3.3 Concepts
+------------
+You can use KGameProperty basically in two completely different ways. You can
+also use a mixture of both but this is not recommended. The default behaviour
+and therefore also the recommended is the "clean" way:
+a) Always Consistent. This means that a KGameProperty has always the same value
+on *every* client. This is achieved by using KGameProperty::send() whenever you
+want to change the value using "=". You can still use changeValue() or
+setLocal() but send() will be the default. If you use send() then the value of
+the property does *NOT* change immediately. It is just sent to the
+KMessageServer which forwards the value to all clients. As soon as the new value
+is received from the message server the KGamePropertyHandler (a collection class
+for KGameProperty) calls KGameProperty::load() and changes the value of the
+property. So the game first has to go into the event loop, where the message is
+received. This means to you that you cannot do this:
+myIntProperty = 10;
+int value = myIntProperty;
+As myIntPoperty still has the old value when "value = myIntProperty" is called.
+This might seem to be quite complex, but
+KGamePropertyHandler::signalPropertyChanged() is emitted whenever a new value is
+assigned so you can connect to this and work immediately with the new value.
+You gain the certainty that the value is the same on every client every time.
+That will safe you a lot of time debugging!
+Another way is the "dirty" way:
+b) Not Always Consistent. Sometimes you really *want* to do something like
+myIntProperty = 10;
+int value = myIntProperty;
+but this is not possible with the default behaviour. If you call
+KGameProperty::setAlwaysConsistent(false) in the constructor (right after
+registerData()) you get another behaviour. "=" means changeValue() now.
+changeValue() also uses send() to change the value but additionally calls
+setLocal() to create a local copy of the property. This copy now has the value
+you supplied with "=" and is deleted again as soon as any value from the network
+is received.
+
+4. KGameIO
+----------
+The class KGameIO is used to let the players communicate with the server. You
+can plug as many KGameIO objects into a player as you want, e.g. you can plug a
+KGameMouseIO and a KGameKeyIO into a player so that you can control the player
+with the mouse and the keyboard - e.g. in a breakout game.
+You can probably see the advantage: as most of the control stuff is common in a
+lot of games you can use the same IO class in many different games with very
+small adjustments.
+You could also put all the IO stuff directly into your KPlayer object, like
+sendBet(int money) for a poker game. But there is a major disadvantage and I'm
+very sure you don't want to use a KPlayer object for your IO stuff as soon as
+you know which disadvantage:
+KGameIO is designed to be able to switch between different IOs "on the fly". So
+you might have a KGamePlayerIO, derived from KGameIO, for your game. But now
+this player (who "owns"/uses the KGamePlayerIO) leaves the game (e.g. because he
+was a remote player). So now the game would be over for every player as one
+player is now out of order. But with KGameIO you can just let any of the
+remaining clients create a KGameComputerIO and plug this into the player. So the
+player now is controlled by the computer and the game can continue.
+
+Think about it! You don't have to care about removing players when a player
+leaves as you can just replace it! The same works the other way round: imagine a
+game with 10 player (e.g. 5 human and 5 computer players) that has already
+started. You cannot add any further players without restarting. So if there are
+any additional player you can just call KPlayer::removeGameIO() which removes
+the IO of a computer player and then call KPlayer::addGameIO() for the same
+player which adds a GameIO for new human player. That's all!
+
+To achieve this you just have to make sure that you make *all* of your IO
+operations through a KGameIO! So instead of using MyPlayer::sendBet(int money)
+you should use something like MyIO::sendBet(). The amount of money would
+probably be calculated by the game IO itself.
+
+
+
+5. Debugging
+------------
+The general debugging concept (if there is one at all) or general debugging
+hints are not yet written. Feel free to do so
+
+5.1 KGameDebugDialog
+--------------------
+A nice way of debugging a KGame based game is the KGameDebugDialog. Basically
+all you have to do is to add something like "Debug" to your game's menu and add
+a slot like
+slotDebug()
+{
+ KGameDebugDialog* dialog = new KGameDebugDialog(mGame, this);
+ connect(dialog, SIGNAL(finished()), dialog, SLOT(slotDelayedDestruct()));
+ dialog->show();
+}
+that's it.
+You can now click on that menu entry and you get a non-modal dialog where you
+can start to debug :-)
+The dialog consist of several pages. You can easily add your own using
+KDialogBase::addVBoxPage() (for example).
+
+5.1.1 Debug KGame
+-----------------
+The first page, "Debug KGame" shows on the left most or even all status values of
+KGame. That contains e.g. minPlayers(), isAdmin(), gameStatus(), ...
+The right side is probably the more important one. It lists *all* KGameProperties
+which have been inserted to this KGame object (only to this KGame object - not
+the ones that have been added to the players!). Most of the status variables of
+the left side are here again as they are implemented as KGameProperty. You can
+see the name of the property (together with its ID), its value and the policy
+this property uses. Note that "unknwon" will be displayed as name of the
+property if you haven't supplied one. See KGamePropertyBase::registerData() for
+info. You probably always want to supply a name for the property to debug it
+easily. In the future there will be something like
+KGamePropertyHandler::setDebug() so that you can switch off debugging and save
+the memory of the names in a release version.
+For as long as you use standard types for your properties (int, long, bool,
+...) you should always be able to see the value of the property. If you just see
+"unknown" then this type has not been implemented. You can connect to the signal
+KGamePropertyHandler::signalRequestValue() and supply a QString with the value
+yourself. If you do so for a standard type please also submit a bug report!
+
+Currently the dialog does *not* update automatically! So you alway have to click
+the "update" button when you want a current value. There are several reasons for
+this (and one of them is that i'm too lazy to implement the update ;)). E.g.
+often (very often) a property is just in the background - stores e.g. the
+available money in a game. But you don't want it to update whenever the value
+changes (a player receives/pays money) but only when the value on the screen
+changes.
+
+5.1.2 Debug Players
+-------------------
+This page consists of three widgets. On the very left there is a list of all
+players in the game. Only the IDs are displayed to save space. If you click one
+the other widgets are filled with content. These widgets are quite much the same
+as the ones in "Debug KGame" - the left shows the value of the functions and the
+right one displays all KProperties of a player. Not much to say here - except:
+See "Debug KGame".
+
+If you change to another player the value are also updated.
+
+5.1.3 Debug Messages
+--------------------
+This page is probably not as important as the other ones. It displays *every*
+message that is sent through the KGame object. As a KGameProperry also send
+messages you probably get a lot of them...
+You can exclude message IDs from being displayed (e.g. all game properties).
+You can also change the sorting of the list to see all messages of a certain ID.
+The default is to sort by time (which is displayed on the left side).
+
diff --git a/libkdegames/kgame/Makefile.am b/libkdegames/kgame/Makefile.am
new file mode 100644
index 00000000..e0117780
--- /dev/null
+++ b/libkdegames/kgame/Makefile.am
@@ -0,0 +1,29 @@
+
+noinst_LTLIBRARIES = libkgame.la
+
+# compile-order doesn't matter here but maybe we will split these section soon
+
+KGAME = kgame.cpp kplayer.cpp kgamenetwork.cpp kgameproperty.cpp \
+ kgamemessage.cpp kgameio.cpp kgameprocess.cpp kgamechat.cpp \
+ kgamepropertyhandler.cpp kgameerror.cpp kgamesequence.cpp
+KGAME_H = kgame.h kplayer.h kgamenetwork.h kgameproperty.h kgamemessage.h \
+ kgameio.h kgameprocess.h kgamepropertyarray.h \
+ kgamepropertylist.h kgamechat.h kgamepropertyhandler.h \
+ kgameerror.h kgamesequence.h kgameversion.h
+
+KMESSAGE = kmessageio.cpp kmessageserver.cpp kmessageclient.cpp
+KMESSAGE_H = kmessageio.h kmessageserver.h kmessageclient.h
+
+libkgameincludedir=$(includedir)/kgame
+libkgame_la_SOURCES = $(KMESSAGE) $(KGAME)
+
+libkgameinclude_HEADERS = $(KMESSAGE_H) $(KGAME_H)
+
+INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes)
+METASOURCES = AUTO
+
+SUBDIRS = . dialogs
+
+messages:
+# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot
+
diff --git a/libkdegames/kgame/README.LIB b/libkdegames/kgame/README.LIB
new file mode 100644
index 00000000..512edbac
--- /dev/null
+++ b/libkdegames/kgame/README.LIB
@@ -0,0 +1,12 @@
+some thoughts and comments about the lib - usually for KGame hackers
+
+- setMin/MaxPlayers() etc. use KGameProperty::changeValue() which is slightly
+ unclean but as these functions can only called by the ADMIN it doesn't matter.
+- AB: KGamePropertyList && KGamePropertyArray:
+ for PolicyClean||PolicyDirty the values are streamed into a QDataStream as usual
+ for PolicyDirty||PolicyLocal the values are streamed as well but
+ additionally command() is called immediately. The values are read from
+ the stream there. This is some kind of performance loss as it would be
+ faster *not* to stream it but imediately call e.g. insert(). But it will
+ probably save a *lot* of bugs!
+
diff --git a/libkdegames/kgame/TODO b/libkdegames/kgame/TODO
new file mode 100644
index 00000000..2f100b8a
--- /dev/null
+++ b/libkdegames/kgame/TODO
@@ -0,0 +1,41 @@
+- 28.02.2001: Direct computer player for kpoker like games support needs to be
+ improved. UPDATE (01/10/06): but what is needed there?
+- 05.03.2001: Documentation. I am thinking of an explaination of the
+ class + methods and example code for the "key" methods. A sample
+ implementation in a small game (like the current kdenonbeta/kgame
+ but with a real sense - mabye use the QT tic-tac-toe example?)
+ would be very great (this could be stuff of a tutorial instead of
+ KGame documentation)
+ MH: Even better idea
+- 03.06.2001: can KGameNetwork::sendSystemMessage be made protected (maybe using
+ friends)? sendSystenMessage AND sendMessage is very confusing to
+ the user...
+- 03.06.2001: can we translate the group of a KPlayer? Probably not as there are
+ no international connections possible then... maybe a group id?
+- 05.06.2001: KGameDialog::saveConfig(KConfig*) might be useful (as well as
+ KGameDialog::loadConfig(KConfig*). Should set an own group in the
+ config file (setGroup("KGameDialog")). Problem: shalll network
+ settings be saved? Could be used for startup configuration (i.e.
+ load the config of the previous game) otherwise.
+- 21.06.2001: KPlayerPropertyArray does not yet support at() and operator[]
+ assignments. Need to check whether the method from QBitArray
+ can be applied
+- 02.04.2001: VERY DANGEROUS: property1=property2 does NOT assign the values, e.g. int
+ but assignes the whole property, i.e. you have then two properties with
+ the same id and everything is wrong
+ 01/09/09: FIXED! (AB) TODO: check if this behavior also appears in
+ KGamePropertyList and KGamePropertyArray. Althogh this should not
+ be the case
+- 23.09.2001: does the virtual destructor make sense for KGamePropertyBase?
+- 29.09.2001: GGZ integration. I (Andi) already volunteered for this - it's just
+ here so that I don't forget it
+- 06.10.2001: add KGamePropertyHandler::setDebug(false) to clear all debug names
+ (and to not accept new names) of KGameProperty. Will save some
+ memory
+- 06.10.2001: If one kicks a player (in KGameDialog) the client should be kicked
+ as well. Perhaps always disconnect a client when all players from
+ it have disappeared?
+- 07.10.2001: display (List) or (Array) for KGameProperty[List|Array] in
+ KGameDebugDialog as value
+- 08.10.2001: KGamePropertyList|KGamePropertyArray must be ported to new QT3 API
+ (e.g. erase instead of remove, ...)
diff --git a/libkdegames/kgame/dialogs/Makefile.am b/libkdegames/kgame/dialogs/Makefile.am
new file mode 100644
index 00000000..1b9de53c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/Makefile.am
@@ -0,0 +1,17 @@
+
+noinst_LTLIBRARIES = libkgamedialogs.la
+
+# compile-order doesn't matter here but maybe we will split these section soon
+
+
+libkgamedialogs_la_SOURCES = kgamedialog.cpp kgameconnectdialog.cpp kgameerrordialog.cpp kgamedebugdialog.cpp kgamedialogconfig.cpp
+
+libkgamedialogsincludedir=$(includedir)/kgame
+libkgamedialogsinclude_HEADERS = kgamedialog.h kgameconnectdialog.h kgameerrordialog.h kgamedebugdialog.h kgamedialogconfig.h
+
+INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame $(all_includes)
+METASOURCES = AUTO
+
+messages:
+# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot
+
diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.cpp b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp
new file mode 100644
index 00000000..4d2d90e0
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp
@@ -0,0 +1,278 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#include "kgameconnectdialog.h"
+
+#include <knuminput.h>
+#include <klocale.h>
+
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qvbuttongroup.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qlabel.h>
+#include <dnssd/servicebrowser.h>
+#include <qpushbutton.h>
+#include <qgrid.h>
+
+class KGameConnectWidgetPrivate
+{
+ public:
+ KGameConnectWidgetPrivate()
+ {
+ mPort = 0;
+ mHost = 0;
+ mButtonGroup = 0;
+ mBrowser = 0;
+ }
+
+ KIntNumInput* mPort;
+ QLineEdit* mHost; //KLineEdit?
+ QVButtonGroup* mButtonGroup;
+ QComboBox *mClientName;
+ QLabel *mClientNameLabel;
+ DNSSD::ServiceBrowser *mBrowser;
+ QLabel *mServerNameLabel;
+ QLineEdit *mServerName;
+ QString mType;
+};
+
+KGameConnectWidget::KGameConnectWidget(QWidget* parent) : QWidget(parent)
+{
+ d = new KGameConnectWidgetPrivate;
+
+ QVBoxLayout* vb = new QVBoxLayout(this, KDialog::spacingHint());
+ d->mButtonGroup = new QVButtonGroup(this);
+ vb->addWidget(d->mButtonGroup);
+ connect(d->mButtonGroup, SIGNAL(clicked(int)), this, SLOT(slotTypeChanged(int)));
+ (void)new QRadioButton(i18n("Create a network game"), d->mButtonGroup);
+ (void)new QRadioButton(i18n("Join a network game"), d->mButtonGroup);
+
+ QGrid* g = new QGrid(2, this);
+ vb->addWidget(g);
+ g->setSpacing(KDialog::spacingHint());
+ d->mServerNameLabel = new QLabel(i18n("Game name:"), g);
+ d->mServerName = new QLineEdit(g);
+ d->mClientNameLabel = new QLabel(i18n("Network games:"), g);
+ d->mClientName = new QComboBox(g);
+ connect(d->mClientName,SIGNAL(activated(int)),SLOT(slotGameSelected(int)));
+ (void)new QLabel(i18n("Port to connect to:"), g);
+ d->mPort = new KIntNumInput(g);
+ (void)new QLabel(i18n("Host to connect to:"), g);
+ d->mHost = new QLineEdit(g);
+
+ QPushButton *button=new QPushButton(i18n("&Start Network"), this);
+ connect(button, SIGNAL(clicked()), this, SIGNAL(signalNetworkSetup()));
+ vb->addWidget(button);
+ // Hide until type is set
+ d->mClientName->hide();
+ d->mClientNameLabel->hide();
+ d->mServerName->hide();
+ d->mServerNameLabel->hide();
+}
+
+void KGameConnectWidget::showDnssdControls()
+{
+ if (!d->mBrowser) return;
+ if (d->mHost->isEnabled()) { // client
+ d->mClientName->show();
+ d->mClientNameLabel->show();
+ d->mServerName->hide();
+ d->mServerNameLabel->hide();
+ slotGameSelected(d->mClientName->currentItem());
+ } else {
+ d->mClientName->hide();
+ d->mClientNameLabel->hide();
+ d->mServerName->show();
+ d->mServerNameLabel->show();
+ }
+}
+
+void KGameConnectWidget::setType(const QString& type)
+{
+ d->mType = type;
+ delete d->mBrowser;
+ d->mBrowser = new DNSSD::ServiceBrowser(type);
+ connect(d->mBrowser,SIGNAL(finished()),SLOT(slotGamesFound()));
+ d->mBrowser->startBrowse();
+ showDnssdControls();
+}
+
+void KGameConnectWidget::slotGamesFound()
+{
+ bool autoselect=false;
+ if (!d->mClientName->count()) autoselect=true;
+ d->mClientName->clear();
+ QStringList names;
+ QValueList<DNSSD::RemoteService::Ptr>::ConstIterator itEnd = d->mBrowser->services().end();
+ for (QValueList<DNSSD::RemoteService::Ptr>::ConstIterator it = d->mBrowser->services().begin();
+ it!=itEnd; ++it) names << (*it)->serviceName();
+ d->mClientName->insertStringList(names);
+ if (autoselect && d->mClientName->count()) slotGameSelected(0);
+}
+
+void KGameConnectWidget::setName(const QString& name)
+{
+ d->mServerName->setText(name);
+}
+
+QString KGameConnectWidget::gameName() const
+{
+ return d->mServerName->text();
+}
+
+QString KGameConnectWidget::type() const
+{
+ return d->mType;
+}
+
+void KGameConnectWidget::slotGameSelected(int nr)
+{
+ if (nr>=(d->mBrowser->services().count()) || nr<0) return;
+ if (!d->mHost->isEnabled()) return; // this is server mode, do not overwrite host and port controls
+ DNSSD::RemoteService::Ptr srv = d->mBrowser->services()[nr];
+ if (!srv->isResolved() && !srv->resolve()) return;
+ d->mHost->setText(srv->hostName());
+ d->mPort->setValue(srv->port());
+}
+KGameConnectWidget::~KGameConnectWidget()
+{
+ delete d->mBrowser;
+ delete d;
+}
+
+QString KGameConnectWidget::host() const
+{
+ if (d->mHost->isEnabled()) {
+ return d->mHost->text();
+ } else {
+ return QString::null;
+ }
+}
+
+unsigned short int KGameConnectWidget::port() const
+{
+ return d->mPort->value();
+}
+
+void KGameConnectWidget::setHost(const QString& host)
+{
+ d->mHost->setText(host);
+}
+
+void KGameConnectWidget::setPort(unsigned short int port)
+{
+ d->mPort->setValue(port);
+}
+
+void KGameConnectWidget::setDefault(int state)
+{
+ d->mButtonGroup->setButton(state);
+ slotTypeChanged(state);
+}
+
+void KGameConnectWidget::slotTypeChanged(int t)
+{
+ if (t == 0) {
+ d->mHost->setEnabled(false);
+ } else if (t == 1) {
+ d->mHost->setEnabled(true);
+ }
+ showDnssdControls();
+ emit signalServerTypeChanged(t);
+}
+
+class KGameConnectDialogPrivate
+{
+ public:
+ KGameConnectDialogPrivate()
+ {
+ mConnect = 0;
+ }
+
+ KGameConnectWidget* mConnect;
+};
+
+// buttonmask =Ok|Cancel
+KGameConnectDialog::KGameConnectDialog(QWidget* parent,int buttonmask) : KDialogBase(Plain,
+ i18n("Network Game"),buttonmask , Ok, parent, 0, true, buttonmask!=0)
+{
+ d = new KGameConnectDialogPrivate;
+ QVBoxLayout* vb = new QVBoxLayout(plainPage(), spacingHint());
+ d->mConnect = new KGameConnectWidget(plainPage());
+ vb->addWidget(d->mConnect);
+}
+
+KGameConnectDialog::~KGameConnectDialog()
+{
+ delete d;
+}
+
+int KGameConnectDialog::initConnection( unsigned short int& port,
+ QString& host, QWidget* parent, bool server)
+{
+ KGameConnectDialog d(parent);
+ d.setHost(host);
+ d.setPort(port);
+ if (server) {
+ d.setDefault(0);
+ } else {
+ d.setDefault(1);
+ }
+
+ int result = d.exec();
+ if (result == QDialog::Accepted) {
+ host = d.host();
+ port = d.port();
+ }
+ return result;
+}
+
+QString KGameConnectDialog::host() const
+{
+ return d->mConnect->host();
+}
+
+unsigned short int KGameConnectDialog::port() const
+{
+ return d->mConnect->port();
+}
+
+void KGameConnectDialog::setHost(const QString& host)
+{
+ d->mConnect->setHost(host);
+}
+
+void KGameConnectDialog::setPort(unsigned short int port)
+{
+ d->mConnect->setPort(port);
+}
+
+void KGameConnectDialog::setDefault(int state)
+{
+ d->mConnect->setDefault(state);
+}
+
+
+
+#include "kgameconnectdialog.moc"
+
diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.h b/libkdegames/kgame/dialogs/kgameconnectdialog.h
new file mode 100644
index 00000000..acbf21d2
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameconnectdialog.h
@@ -0,0 +1,169 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMECONNECTDIALOG_H__
+#define __KGAMECONNECTDIALOG_H__
+
+#include <kdialogbase.h>
+
+class KGameConnectDialogPrivate;
+class KGameConnectWidgetPrivate;
+
+class KGameConnectWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ KGameConnectWidget(QWidget* parent);
+ virtual ~KGameConnectWidget();
+
+ /**
+ * @param host The host to connect to by default
+ **/
+ void setHost(const QString& host);
+
+ /**
+ * @return The host to connect to or QString::null if the user wants to
+ * be the MASTER
+ **/
+ QString host() const;
+
+ /**
+ * @param port The port that will be shown by default
+ **/
+ void setPort(unsigned short int port);
+
+ /**
+ * @return The port to connect to / to listen
+ **/
+ unsigned short int port() const;
+
+ /**
+ * Specifies which state is the default (0 = server game; 1 = join game)
+ * @param state The default state. 0 For a server game, 1 to join a game
+ **/
+ void setDefault(int state);
+
+ /**
+ * Sets DNS-SD service type, both for publishing and browsing
+ * @param type Service type (something like _kwin4._tcp).
+ * It should be unique for application.
+ * @since 3.4
+ **/
+ void setType(const QString& type);
+
+ /**
+ * @return service type
+ */
+ QString type() const;
+
+ /**
+ * Set game name for publishing.
+ * @param name Game name. Important only for server mode. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return game name.
+ */
+ QString gameName() const;
+
+protected slots:
+ /**
+ * The type has changed, ie the user switched between creating or
+ * joining.
+ **/
+ void slotTypeChanged(int);
+ void slotGamesFound();
+ void slotGameSelected(int);
+
+signals:
+ void signalNetworkSetup();
+ void signalServerTypeChanged(int);
+
+private:
+ void showDnssdControls();
+ KGameConnectWidgetPrivate* d;
+
+};
+
+/**
+ * @short Dialog to ask for host and port
+ *
+ * This Dialog is used to create a game. You call initConnection(port,
+ * QString::null, parent, true) to create a network game (as a server)
+ * or initConnection(port, host, parent) to join a network game.
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameConnectDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameConnectDialog(QWidget* parent = 0,int buttonmask=Ok|Cancel);
+ virtual ~KGameConnectDialog();
+
+ /**
+ * Shows a dialog to either connect to an existing game or to create a
+ * server game, depending on user's choice.
+ * @param port The port the user wants to connect to.
+ * @param host The host the user wants to connect to. Will be
+ * QString::null if server game is chosen
+ * @param parent The parent of the dialog
+ * @param server True to create a network game per default, false to
+ * join a game by default
+ **/
+ static int initConnection(unsigned short int& port, QString& host, QWidget* parent, bool server = false);
+
+ /**
+ * @param host The host to connect to by default
+ **/
+ void setHost(const QString& host);
+
+ /**
+ * @return The host to connect to or QString::null if the user wants to
+ * be the MASTER
+ **/
+ QString host() const;
+
+ /**
+ * @param port The port that will be shown by default
+ **/
+ void setPort(unsigned short int port);
+
+ /**
+ * @return The port to connect to / to listen
+ **/
+ unsigned short int port() const;
+
+ /**
+ * Specifies which state is the default (0 = server game; 1 = join game)
+ * @param state The default state. 0 For a server game, 1 to join a game
+ **/
+ void setDefault(int state);
+
+signals:
+ void signalNetworkSetup();
+
+private:
+ KGameConnectDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.cpp b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp
new file mode 100644
index 00000000..b112b04c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp
@@ -0,0 +1,548 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamedebugdialog.h"
+
+#include "kgamemessage.h"
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamepropertyhandler.h"
+
+#include <klistview.h>
+#include <klistbox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include <qlayout.h>
+#include <qstring.h>
+#include <qintdict.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+
+#include <typeinfo>
+
+
+class KGameDebugDialogPrivate
+{
+public:
+ KGameDebugDialogPrivate()
+ {
+ mGame = 0;
+
+ mGamePage = 0;
+ mGameProperties = 0;
+ mGameAddress = 0;
+ mGameId = 0;
+ mGameCookie = 0;
+ mGameMaster = 0;
+ mGameAdmin = 0;
+ mGameOffering = 0;
+ mGameStatus = 0;
+ mGameRunning = 0;
+ mGameMaxPlayers = 0;
+ mGameMinPlayers = 0;
+ mGamePlayerCount = 0;
+
+ mPlayerPage = 0;
+ mPlayerList = 0;
+ mPlayerProperties = 0;
+ mPlayerAddress = 0;
+ mPlayerId = 0;
+ mPlayerName = 0;
+ mPlayerGroup = 0;
+ mPlayerUserId = 0;
+ mPlayerMyTurn = 0;
+ mPlayerAsyncInput= 0;
+ mPlayerKGameAddress = 0;
+ mPlayerVirtual = 0;
+ mPlayerActive = 0;
+ mPlayerRtti = 0;
+ mPlayerNetworkPriority = 0;
+
+ mMessagePage = 0;
+ mMessageList = 0;
+ mHideIdList = 0;
+ }
+
+ const KGame* mGame;
+
+ QFrame* mGamePage;
+ KListView* mGameProperties;
+ QListViewItem* mGameAddress;
+ QListViewItem* mGameId;
+ QListViewItem* mGameCookie;
+ QListViewItem* mGameMaster;
+ QListViewItem* mGameAdmin;
+ QListViewItem* mGameOffering;
+ QListViewItem* mGameStatus;
+ QListViewItem* mGameRunning;
+ QListViewItem* mGameMaxPlayers;
+ QListViewItem* mGameMinPlayers;
+ QListViewItem* mGamePlayerCount;
+
+ QFrame* mPlayerPage;
+ KListBox* mPlayerList;
+ KListView* mPlayerProperties;
+ QListViewItem* mPlayerAddress;
+ QListViewItem* mPlayerId;
+ QListViewItem* mPlayerName;
+ QListViewItem* mPlayerGroup;
+ QListViewItem* mPlayerUserId;
+ QListViewItem* mPlayerMyTurn;
+ QListViewItem* mPlayerAsyncInput;
+ QListViewItem* mPlayerKGameAddress;
+ QListViewItem* mPlayerVirtual;
+ QListViewItem* mPlayerActive;
+ QListViewItem* mPlayerRtti;
+ QListViewItem* mPlayerNetworkPriority;
+
+ QFrame* mMessagePage;
+ KListView* mMessageList;
+ KListBox* mHideIdList;
+};
+
+KGameDebugDialog::KGameDebugDialog(KGame* g, QWidget* parent, bool modal) :
+ KDialogBase(Tabbed, i18n("KGame Debug Dialog"), Close, Close,
+ parent, 0, modal, true)
+{
+ d = new KGameDebugDialogPrivate;
+
+ initGamePage();
+ initPlayerPage();
+ initMessagePage();
+
+ setKGame(g);
+}
+
+KGameDebugDialog::~KGameDebugDialog()
+{
+ delete d;
+}
+
+void KGameDebugDialog::initGamePage()
+{
+ d->mGamePage = addPage(i18n("Debug &KGame"));
+ QVBoxLayout* topLayout = new QVBoxLayout(d->mGamePage, marginHint(), spacingHint());
+ QHBoxLayout* layout = new QHBoxLayout(topLayout);
+
+ KListView* v = new KListView(d->mGamePage);
+ v->addColumn(i18n("Data"));
+ v->addColumn(i18n("Value"));
+ layout->addWidget(v);
+
+ d->mGameProperties = new KListView(d->mGamePage);
+ d->mGameProperties->addColumn(i18n("Property"));
+ d->mGameProperties->addColumn(i18n("Value"));
+ d->mGameProperties->addColumn(i18n("Policy"));
+ layout->addWidget(d->mGameProperties);
+
+ QPushButton* b = new QPushButton(i18n("Update"), d->mGamePage);
+ connect(b, SIGNAL(pressed()), this, SLOT(slotUpdateGameData()));
+ topLayout->addWidget(b);
+
+// game data
+ d->mGameAddress = new QListViewItem(v, i18n("KGame Pointer"));
+ d->mGameId = new QListViewItem(v, i18n("Game ID"));
+ d->mGameCookie = new QListViewItem(v, i18n("Game Cookie"));
+ d->mGameMaster = new QListViewItem(v, i18n("Is Master"));
+ d->mGameAdmin = new QListViewItem(v, i18n("Is Admin"));
+ d->mGameOffering = new QListViewItem(v, i18n("Is Offering Connections"));
+ d->mGameStatus = new QListViewItem(v, i18n("Game Status"));
+ d->mGameRunning = new QListViewItem(v, i18n("Game is Running"));
+ d->mGameMaxPlayers = new QListViewItem(v, i18n("Maximal Players"));
+ d->mGameMinPlayers = new QListViewItem(v, i18n("Minimal Players"));
+ d->mGamePlayerCount = new QListViewItem(v, i18n("Players"));
+}
+
+void KGameDebugDialog::initPlayerPage()
+{
+ d->mPlayerPage = addPage(i18n("Debug &Players"));
+ QVBoxLayout* topLayout = new QVBoxLayout(d->mPlayerPage, marginHint(), spacingHint());
+ QHBoxLayout* layout = new QHBoxLayout(topLayout);
+
+ //TODO: connect to the KGame signals for joined/removed players!!!
+ QVBoxLayout* listLayout = new QVBoxLayout(layout);
+ QLabel* listLabel = new QLabel(i18n("Available Players"), d->mPlayerPage);
+ listLayout->addWidget(listLabel);
+ d->mPlayerList = new KListBox(d->mPlayerPage);
+ connect(d->mPlayerList, SIGNAL(executed(QListBoxItem*)), this, SLOT(slotUpdatePlayerData(QListBoxItem*)));
+ listLayout->addWidget(d->mPlayerList);
+ d->mPlayerList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
+
+ KListView* v = new KListView(d->mPlayerPage);
+ layout->addWidget(v);
+ v->addColumn(i18n("Data"));
+ v->addColumn(i18n("Value"));
+
+ d->mPlayerProperties = new KListView(d->mPlayerPage);
+ d->mPlayerProperties->addColumn(i18n("Property"));
+ d->mPlayerProperties->addColumn(i18n("Value"));
+ d->mPlayerProperties->addColumn(i18n("Policy"));
+ layout->addWidget(d->mPlayerProperties);
+
+ QPushButton* b = new QPushButton(i18n("Update"), d->mPlayerPage);
+ connect(b, SIGNAL(pressed()), this, SLOT(slotUpdatePlayerList()));
+ topLayout->addWidget(b);
+
+ d->mPlayerAddress = new QListViewItem(v, i18n("Player Pointer"));
+ d->mPlayerId = new QListViewItem(v, i18n("Player ID"));
+ d->mPlayerName = new QListViewItem(v, i18n("Player Name"));
+ d->mPlayerGroup = new QListViewItem(v, i18n("Player Group"));
+ d->mPlayerUserId = new QListViewItem(v, i18n("Player User ID"));
+ d->mPlayerMyTurn = new QListViewItem(v, i18n("My Turn"));
+ d->mPlayerAsyncInput = new QListViewItem(v, i18n("Async Input"));
+ d->mPlayerKGameAddress = new QListViewItem(v, i18n("KGame Address"));
+ d->mPlayerVirtual = new QListViewItem(v, i18n("Player is Virtual"));
+ d->mPlayerActive = new QListViewItem(v, i18n("Player is Active"));
+ d->mPlayerRtti = new QListViewItem(v, i18n("RTTI"));
+ d->mPlayerNetworkPriority = new QListViewItem(v, i18n("Network Priority"));
+}
+
+void KGameDebugDialog::initMessagePage()
+{
+ d->mMessagePage = addPage(i18n("Debug &Messages"));
+ QGridLayout* layout = new QGridLayout(d->mMessagePage, 11, 7, marginHint(), spacingHint());
+ d->mMessageList = new KListView(d->mMessagePage);
+ layout->addMultiCellWidget(d->mMessageList, 0, 9, 0, 3);
+ d->mMessageList->addColumn(i18n("Time"));
+ d->mMessageList->addColumn(i18n("ID"));
+ d->mMessageList->addColumn(i18n("Receiver"));
+ d->mMessageList->addColumn(i18n("Sender"));
+ d->mMessageList->addColumn(i18n("ID - Text"));
+
+ QPushButton* hide = new QPushButton(i18n("&>>"), d->mMessagePage);
+ connect(hide, SIGNAL(pressed()), this, SLOT(slotHideId()));
+ layout->addWidget(hide, 4, 4);
+
+ QPushButton* show = new QPushButton(i18n("&<<"), d->mMessagePage);
+ connect(show, SIGNAL(pressed()), this, SLOT(slotShowId()));
+ layout->addWidget(show, 6, 4);
+
+ QLabel* l = new QLabel(i18n("Do not show IDs:"), d->mMessagePage);
+ layout->addMultiCellWidget(l, 0, 0, 5, 6);
+ d->mHideIdList = new KListBox(d->mMessagePage);
+ layout->addMultiCellWidget(d->mHideIdList, 1, 8, 5, 6);
+
+ QPushButton* clear = new KPushButton(KStdGuiItem::clear(), d->mMessagePage);
+ connect(clear, SIGNAL(pressed()), this, SLOT(slotClearMessages()));
+ layout->addMultiCellWidget(clear, 10, 10, 0, 6);
+ //TODO: "show all but..." and "show nothing but..."
+}
+
+void KGameDebugDialog::clearPlayerData()
+{
+ d->mPlayerAddress->setText(1, "");
+ d->mPlayerId->setText(1, "");
+ d->mPlayerName->setText(1, "");
+ d->mPlayerGroup->setText(1, "");
+ d->mPlayerUserId->setText(1, "");
+ d->mPlayerMyTurn->setText(1, "");
+ d->mPlayerAsyncInput->setText(1, "");
+ d->mPlayerKGameAddress->setText(1, "");
+ d->mPlayerVirtual->setText(1, "");
+ d->mPlayerActive->setText(1, "");
+ d->mPlayerRtti->setText(1, "");
+ d->mPlayerNetworkPriority->setText(1, "");
+
+ d->mPlayerProperties->clear();
+}
+
+void KGameDebugDialog::clearGameData()
+{
+ d->mGameAddress->setText(1, "");
+ d->mGameId->setText(1, "");
+ d->mGameCookie->setText(1, "");
+ d->mGameMaster->setText(1, "");
+ d->mGameAdmin->setText(1, "");
+ d->mGameOffering->setText(1, "");
+ d->mGameStatus->setText(1, "");
+ d->mGameRunning->setText(1, "");
+ d->mGameMaxPlayers->setText(1, "");
+ d->mGameMinPlayers->setText(1, "");
+
+ d->mGameProperties->clear();
+}
+
+void KGameDebugDialog::slotUpdatePlayerData()
+{
+ if (!d->mGame || d->mPlayerList->currentItem() == -1) {
+ return;
+ }
+ slotUpdatePlayerData(d->mPlayerList->item(d->mPlayerList->currentItem()));
+}
+
+void KGameDebugDialog::slotUpdatePlayerList()
+{
+ QListBoxItem* i = d->mPlayerList->firstItem();
+ for (; i; i = d->mPlayerList->firstItem()) {
+ removePlayer(i);
+ }
+
+ QPtrList<KPlayer> list = *d->mGame->playerList();
+ for (KPlayer* p = list.first(); p; p = list.next()) {
+ addPlayer(p);
+ }
+}
+
+void KGameDebugDialog::slotUpdateGameData()
+{
+ if (!d->mGame) {
+ d->mGameAddress->setText(1, i18n("NULL pointer"));
+ return;
+}
+
+ clearGameData();
+
+ QString buf;
+ buf.sprintf("%p", d->mGame);
+ d->mGameAddress->setText(1, buf);
+ d->mGameId->setText(1, QString::number(d->mGame->gameId()));
+ d->mGameCookie->setText(1, QString::number(d->mGame->cookie()));
+ d->mGameMaster->setText(1, d->mGame->isMaster() ? i18n("True") : i18n("False"));
+ d->mGameAdmin->setText(1, d->mGame->isAdmin() ? i18n("True") : i18n("False"));
+ d->mGameOffering->setText(1, d->mGame->isOfferingConnections() ? i18n("True") : i18n("False"));
+ d->mGameStatus->setText(1, QString::number(d->mGame->gameStatus()));
+ d->mGameRunning->setText(1, d->mGame->isRunning() ? i18n("True") : i18n("False"));
+ d->mGameMaxPlayers->setText(1, QString::number(d->mGame->maxPlayers()));
+ d->mGameMinPlayers->setText(1, QString::number(d->mGame->minPlayers()));
+ d->mGamePlayerCount->setText(1, QString::number(d->mGame->playerCount()));
+
+//TODO ios
+
+ KGamePropertyHandler* handler = d->mGame->dataHandler();
+ QIntDictIterator<KGamePropertyBase> it(handler->dict());
+ while (it.current()) {
+ QString policy;
+ switch (it.current()->policy()) {
+ case KGamePropertyBase::PolicyClean:
+ policy = i18n("Clean");
+ break;
+ case KGamePropertyBase::PolicyDirty:
+ policy = i18n("Dirty");
+ break;
+ case KGamePropertyBase::PolicyLocal:
+ policy = i18n("Local");
+ break;
+ case KGamePropertyBase::PolicyUndefined:
+ default:
+ policy = i18n("Undefined");
+ break;
+ }
+ (void) new QListViewItem(d->mGameProperties,
+ handler->propertyName(it.current()->id()),
+ handler->propertyValue(it.current()),
+ policy);
+// kdDebug(11001) << k_funcinfo << ": checking for all game properties: found property name " << name << endl;
+ ++it;
+ }
+}
+
+void KGameDebugDialog::slotUpdatePlayerData(QListBoxItem* item)
+{
+ if (!item || !d->mGame) {
+ return;
+ }
+
+ KPlayer* p = d->mGame->findPlayer(item->text().toInt());
+
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": cannot find player" << endl;
+ return;
+ }
+
+ clearPlayerData();
+
+ QString buf;
+ buf.sprintf("%p", p);
+ d->mPlayerAddress->setText(1, buf);
+ d->mPlayerId->setText(1, QString::number(p->id()));
+ d->mPlayerName->setText(1, p->name());
+ d->mPlayerGroup->setText(1, p->group());
+ d->mPlayerUserId->setText(1, QString::number(p->userId()));
+ d->mPlayerMyTurn->setText(1, p->myTurn() ? i18n("True") : i18n("False"));
+ d->mPlayerAsyncInput->setText(1, p->asyncInput() ? i18n("True") : i18n("False"));
+ buf.sprintf("%p", p->game());
+ d->mPlayerKGameAddress->setText(1, buf);
+ d->mPlayerVirtual->setText(1, p->isVirtual() ? i18n("True") : i18n("False"));
+ d->mPlayerActive->setText(1, p->isActive() ? i18n("True") : i18n("False"));
+ d->mPlayerRtti->setText(1, QString::number(p->rtti()));
+ d->mPlayerNetworkPriority->setText(1, QString::number(p->networkPriority()));
+
+//TODO ios
+
+// Properties
+ KGamePropertyHandler * handler = p->dataHandler();
+ QIntDictIterator<KGamePropertyBase> it((handler->dict()));
+ while (it.current()) {
+ QString policy;
+ switch (it.current()->policy()) {
+ case KGamePropertyBase::PolicyClean:
+ policy = i18n("Clean");
+ break;
+ case KGamePropertyBase::PolicyDirty:
+ policy = i18n("Dirty");
+ break;
+ case KGamePropertyBase::PolicyLocal:
+ policy = i18n("Local");
+ break;
+ case KGamePropertyBase::PolicyUndefined:
+ default:
+ policy = i18n("Undefined");
+ break;
+ }
+ (void)new QListViewItem(d->mPlayerProperties,
+ handler->propertyName(it.current()->id()),
+ handler->propertyValue(it.current()),
+ policy);
+ ++it;
+ }
+}
+
+void KGameDebugDialog::clearPages()
+{
+ clearPlayerData();
+ clearGameData();
+ d->mPlayerList->clear();
+ slotClearMessages();
+}
+
+void KGameDebugDialog::setKGame(const KGame* g)
+{
+ slotUnsetKGame();
+ d->mGame = g;
+ if (g) {
+ //TODO: connect to the KGame signals for joined/removed players!!!
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+// connect();
+
+ QPtrList<KPlayer> list = *d->mGame->playerList();
+ for (KPlayer* p = list.first(); p; p = list.next()) {
+ addPlayer(p);
+ }
+
+ slotUpdateGameData();
+
+ connect(d->mGame, SIGNAL(signalMessageUpdate(int, Q_UINT32, Q_UINT32)), this, SLOT(slotMessageUpdate(int, Q_UINT32, Q_UINT32)));
+ }
+}
+
+void KGameDebugDialog::slotUnsetKGame()
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = 0;
+ clearPages();
+}
+
+void KGameDebugDialog::addPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << "trying to add NULL player" << endl;
+ return;
+ }
+
+ (void) new QListBoxText(d->mPlayerList, QString::number(p->id()));
+ //TODO connect to signals, like deleted/removed, ...
+}
+
+void KGameDebugDialog::removePlayer(QListBoxItem* i)
+{
+ if (!i || !d->mGame) {
+ return;
+ }
+ KPlayer* p = d->mGame->findPlayer(i->text().toInt());
+ if (!p) {
+ return;
+ }
+ disconnect(p, 0, this, 0);
+ if (i->isSelected()) {
+ clearPlayerData();
+ }
+ delete i;
+}
+
+void KGameDebugDialog::slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ if (!showId(msgid)) {
+ return;
+ }
+ QString msgidText = KGameMessage::messageId2Text(msgid);
+ if (msgidText.isNull()) {
+ if (msgid > KGameMessage::IdUser) {
+ emit signalRequestIdName(msgid-KGameMessage::IdUser, true, msgidText);
+ } else {
+ emit signalRequestIdName(msgid, false, msgidText);
+ }
+ if (msgidText.isNull()) {
+ msgidText = i18n("Unknown");
+ }
+ }
+ (void) new QListViewItem( d->mMessageList, QTime::currentTime().toString(),
+ QString::number(msgid), QString::number(receiver),
+ QString::number(sender), msgidText);
+}
+
+void KGameDebugDialog::slotClearMessages()
+{
+ d->mMessageList->clear();
+}
+
+void KGameDebugDialog::slotShowId()
+{
+/* QListBoxItem* i = d->mHideIdList->firstItem();
+ for (; i; i = i->next()) {
+ if (i->selected()) {
+ d->mHideIdList->removeItem(i->);
+ }
+ }*/
+ if (!d->mHideIdList->currentItem()) {
+ return;
+ }
+ d->mHideIdList->removeItem(d->mHideIdList->currentItem());
+}
+
+void KGameDebugDialog::slotHideId()
+{
+ if (!d->mMessageList->currentItem()) {
+ return;
+ }
+ int msgid = d->mMessageList->currentItem()->text(1).toInt();
+ if (!showId(msgid)) {
+ return;
+ }
+ (void)new QListBoxText(d->mHideIdList, QString::number(msgid));
+}
+
+bool KGameDebugDialog::showId(int msgid)
+{
+ QListBoxItem* i = d->mHideIdList->firstItem();
+ for (; i; i = i->next()) {
+ if (i->text().toInt() == msgid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#include "kgamedebugdialog.moc"
diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.h b/libkdegames/kgame/dialogs/kgamedebugdialog.h
new file mode 100644
index 00000000..65afc92a
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedebugdialog.h
@@ -0,0 +1,149 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEDEBUGDIALOG_H__
+#define __KGAMEDEBUGDIALOG_H__
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+
+class KGame;
+class KGameIO;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDebugDialogPrivate;
+
+class KDE_EXPORT KGameDebugDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameDebugDialog(KGame* g, QWidget* parent, bool modal = false);
+ ~KGameDebugDialog();
+
+ /**
+ * Automatically connects the KGame object to all error dependant slots.
+ * Create a KGameErrorDialog object, call this function and forget
+ * everything.
+ * @param g The KGame which will emit the erorrs (or not ;-) )
+ **/
+ void setKGame(const KGame* g);
+
+public slots:
+ /**
+ * Unsets a @ref KGame which has been set using @ref setKGame before.
+ * This is called automatically when the @ref KGame object is destroyed
+ * and you normally don't have to call this yourself.
+ *
+ * Note that @ref setKGame also unsets an already existing @ref KGame
+ * object if exising.
+ **/
+ void slotUnsetKGame();
+
+ /**
+ * Update the data of the @ref KGame object
+ **/
+ void slotUpdateGameData();
+
+ /**
+ * Update the properties of the currently selected player
+ **/
+ void slotUpdatePlayerData();
+
+ /**
+ * Updates the list of players and calls @ref clearPlayerData. Note that
+ * after this call NO player is selected anymore.
+ **/
+ void slotUpdatePlayerList();
+
+ void slotClearMessages();
+
+signals:
+ /**
+ * This signal is emitted when the "debug messages" page couldn't find
+ * the name of a message id. This is usually the case for user-defined
+ * messages. KGameDebugDialog asks you to give the msgid a name.
+ * @param messageid The ID of the message. As given to @ref
+ * KGame::sendMessage
+ * @param userid User defined msgIds are internally increased by
+ * @ref KGameMessage::IdUser. You don't have to care about this but if
+ * this signal is emitted with userid=false (shouldn't happen) then the
+ * name of an internal message as defined in @ref
+ * KGameMessage::GameMessageIds couldn't be found.
+ * @param name The name of the msgid. You have to fill this!
+ **/
+ void signalRequestIdName(int messageid, bool userid, QString& name);
+
+protected:
+ void clearPages();
+
+ /**
+ * Clear the data of the player view. Note that the player list is NOT
+ * cleared.
+ **/
+ void clearPlayerData();
+
+ /**
+ * Clear the data view of the @ref KGame object
+ **/
+ void clearGameData();
+
+ /**
+ * Add a new player to the player list
+ **/
+ void addPlayer(KPlayer* p);
+
+ /**
+ * Remove a player from the list
+ **/
+ void removePlayer(QListBoxItem* item);
+
+ /**
+ * @return Whether messages with this msgid shall be displayed or not
+ **/
+ bool showId(int msgid);
+
+protected slots:
+ /**
+ * Update the data of the player specified in item
+ * @param item The @ref QListBoxItem of the player to be updated. Note
+ * that the text of this item MUST be the ID of the player
+ **/
+ void slotUpdatePlayerData(QListBoxItem* item);
+
+ void slotShowId();
+ void slotHideId();
+
+ /**
+ * A message has been received - see @ref KGame::signalMessageUpdate
+ **/
+ void slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+private:
+ void initGamePage();
+ void initPlayerPage();
+ void initMessagePage();
+
+private:
+ KGameDebugDialogPrivate* d;
+};
+
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedialog.cpp b/libkdegames/kgame/dialogs/kgamedialog.cpp
new file mode 100644
index 00000000..dc564b8e
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialog.cpp
@@ -0,0 +1,347 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qlayout.h>
+#include <qvbox.h>
+
+#include <klocale.h>
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamedialogconfig.h"
+
+#include "kgamedialog.h"
+
+#include "kgamedialog.moc"
+
+class KGameDialogPrivate
+{
+public:
+ KGameDialogPrivate()
+ {
+ mGamePage = 0;
+ mNetworkPage = 0;
+ mMsgServerPage = 0;
+ mTopLayout = 0;
+
+ mNetworkConfig = 0;
+ mGameConfig = 0;
+
+ mOwner = 0;
+ mGame = 0;
+ }
+
+ QVBox* mGamePage;
+ QVBox* mNetworkPage;
+ QVBox* mMsgServerPage;// unused here?
+ QVBoxLayout* mTopLayout;
+ KGameDialogNetworkConfig* mNetworkConfig;
+ KGameDialogGeneralConfig* mGameConfig;
+
+// a list of all config widgets added to this dialog
+ QPtrList<KGameDialogConfig> mConfigWidgets;
+
+// just pointers:
+ KPlayer* mOwner;
+ KGame* mGame;
+};
+
+KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, bool modal)
+ : KDialogBase(Tabbed, title, Ok|Default|Apply,
+ Ok, parent, 0, modal, true)
+{
+ init(g, owner);
+}
+
+KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, long initConfigs, int chatMsgId, bool modal)
+ : KDialogBase(Tabbed, title, Ok|Default|Apply,
+ Ok, parent, 0, modal, true)
+{
+ init(g, owner);
+ if ((ConfigOptions)initConfigs!=NoConfig) {
+ initDefaultDialog((ConfigOptions)initConfigs, chatMsgId);
+ }
+}
+
+void KGameDialog::init(KGame* g, KPlayer* owner)
+{
+//AB: do we need a "Cancel" Button? currently removed
+
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogPrivate;
+
+ setOwner(owner);
+ setKGame(g);
+ if (g) {
+ setAdmin(g->isAdmin());
+ } else {
+ setAdmin(false);
+ }
+}
+
+void KGameDialog::initDefaultDialog(ConfigOptions initConfigs, int chatMsgId)
+{
+ if (initConfigs & GameConfig) {
+ kdDebug() << "add gameconf" << endl;
+ addGameConfig(new KGameDialogGeneralConfig(0));
+ }
+ if (initConfigs & NetworkConfig) {
+ addNetworkConfig(new KGameDialogNetworkConfig(0));
+ }
+ if (initConfigs & (MsgServerConfig) ) {
+ addMsgServerConfig(new KGameDialogMsgServerConfig(0));
+ }
+ if (initConfigs & ChatConfig) {
+ KGameDialogChatConfig * c = new KGameDialogChatConfig(chatMsgId, 0);
+ if (d->mGamePage) {
+ addChatWidget(c, d->mGamePage);
+ } else {
+ addConfigPage(c, i18n("&Chat"));
+ }
+ }
+ if (initConfigs & BanPlayerConfig) {
+ // add the connection management system - ie the widget where the ADMIN can
+ // kick players out
+ if (d->mNetworkPage) {
+ // put it on the network page
+ addConnectionList(new KGameDialogConnectionConfig(0), d->mNetworkPage);
+ } else {
+ // if no network page available put it on an own page
+ addConfigPage(new KGameDialogConnectionConfig(0), i18n("C&onnections"));
+ }
+ }
+}
+
+KGameDialog::~KGameDialog()
+{
+// kdDebug(11001) << "DESTRUCT KGameDialog" << this << endl;
+ d->mConfigWidgets.setAutoDelete(true);
+ d->mConfigWidgets.clear();
+ delete d;
+}
+
+void KGameDialog::addGameConfig(KGameDialogGeneralConfig* conf)
+{
+ if (!conf) {
+ return;
+ }
+ d->mGameConfig = conf;
+ d->mGamePage = addConfigPage(d->mGameConfig, i18n("&Game"));
+}
+
+void KGameDialog::addNetworkConfig(KGameDialogNetworkConfig* netConf)
+{
+ if (!netConf) {
+ return;
+ }
+ d->mNetworkConfig = netConf;
+ d->mNetworkPage = addConfigPage(netConf, i18n("&Network"));
+}
+
+void KGameDialog::addMsgServerConfig(KGameDialogMsgServerConfig* msgConf)
+{
+ if (!msgConf) {
+ return;
+ }
+ d->mMsgServerPage = addConfigPage(msgConf, i18n("&Message Server"));
+}
+
+void KGameDialog::addChatWidget(KGameDialogChatConfig* chat, QVBox* parent)
+{
+ if (!chat) {
+ return;
+ }
+ if (!parent) {
+ parent = d->mGamePage;
+ }
+ if (!parent) {
+ kdError(11001) << "cannot add chat widget without page" << endl;
+ return;
+ }
+ addConfigWidget(chat, parent);
+}
+
+void KGameDialog::addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent)
+{
+ if (!c) {
+ return;
+ }
+ if (!parent) {
+ parent = d->mNetworkPage;
+ }
+ if (!parent) {
+ kdError(11001) << "Cannot add connection list without page" << endl;
+ return;
+ }
+ addConfigWidget(c, parent);
+}
+
+QVBox *KGameDialog::configPage(ConfigOptions which)
+{
+ QVBox *box = 0;
+ switch(which)
+ {
+ case NetworkConfig:
+ box = d->mNetworkPage;
+ break;
+ case GameConfig:
+ box = d->mGamePage;
+ break;
+ case MsgServerConfig:
+ box = d->mMsgServerPage;
+ break;
+ default:
+ kdError(11001) << k_funcinfo << ": Parameter " << which << " not supported" << endl;
+ }
+ return box;
+}
+
+QVBox* KGameDialog::addConfigPage(KGameDialogConfig* widget, const QString& title)
+{
+ if (!widget) {
+ kdError(11001) << "Cannot add NULL config widget" << endl;
+ return 0;
+ }
+ QVBox* page = addVBoxPage(title);
+ addConfigWidget(widget, page);
+ return page;
+}
+
+void KGameDialog::addConfigWidget(KGameDialogConfig* widget, QWidget* parent)
+{
+ if (!widget) {
+ kdError(11001) << "Cannot add NULL config widget" << endl;
+ return;
+ }
+ if (!parent) {
+ kdError(11001) << "Cannot reparent to NULL widget" << endl;
+ return;
+ }
+// kdDebug(11001) << "reparenting widget" << endl;
+ widget->reparent(parent, QPoint(0,0));
+ d->mConfigWidgets.append(widget);
+ connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(slotRemoveConfigWidget(QObject*)));
+ if (!d->mGame) {
+ kdWarning(11001) << "No game has been set!" << endl;
+ } else {
+ widget->setKGame(d->mGame);
+ widget->setAdmin(d->mGame->isAdmin());
+ }
+ if (!d->mOwner) {
+ kdWarning(11001) << "No player has been set!" << endl;
+ } else {
+ widget->setOwner(d->mOwner);
+ }
+ widget->show();
+}
+
+KGameDialogGeneralConfig* KGameDialog::gameConfig() const
+{ return d->mGameConfig; }
+KGameDialogNetworkConfig* KGameDialog::networkConfig() const
+{ return d->mNetworkConfig; }
+
+void KGameDialog::slotApply()
+{
+ submitToKGame();
+}
+
+void KGameDialog::slotDefault()
+{
+ if (!d->mGame) {
+ return;
+ }
+
+//TODO *only* call setKGame/setOwner for the *current* page!!
+ setKGame(d->mGame);
+ setOwner(d->mOwner);
+}
+
+void KGameDialog::slotOk()
+{
+ slotApply();
+ QDialog::accept();
+}
+
+void KGameDialog::setOwner(KPlayer* owner)
+{
+//AB: note: NULL player is ok!
+ d->mOwner = owner;
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ if (d->mConfigWidgets.at(i)) {
+ d->mConfigWidgets.at(i)->setOwner(d->mOwner);
+ //TODO: hide playerName in KGameDialogGeneralConfig
+ } else {
+ kdError(11001) << "NULL widget??" << endl;
+ }
+ }
+}
+
+void KGameDialog::setKGame(KGame* g)
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = g;
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ d->mConfigWidgets.at(i)->setKGame(d->mGame);
+ }
+ if (d->mGame) {
+ setAdmin(d->mGame->isAdmin());
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+ connect(d->mGame, SIGNAL(signalAdminStatusChanged(bool)),
+ this, SLOT(setAdmin(bool)));
+ }
+}
+
+void KGameDialog::setAdmin(bool admin)
+{
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ d->mConfigWidgets.at(i)->setAdmin(admin);
+ }
+}
+
+void KGameDialog::slotUnsetKGame() // called when KGame is destroyed
+{ setKGame(0); }
+
+void KGameDialog::submitToKGame()
+{
+ if (!d->mGame) {
+ kdError(11001) << k_funcinfo << ": no game has been set" << endl;
+ return;
+ }
+ if (!d->mOwner) {
+ kdError(11001) << k_funcinfo << ": no player has been set" << endl;
+ return;
+ }
+
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+// kdDebug(11001) << "submit to kgame " << i << endl;
+ d->mConfigWidgets.at(i)->submitToKGame(d->mGame, d->mOwner);
+// kdDebug(11001) << "done: submit to kgame " << i << endl;
+ }
+}
+
+void KGameDialog::slotRemoveConfigWidget(QObject* configWidget)
+{
+ d->mConfigWidgets.removeRef((KGameDialogConfig*)configWidget);
+}
+
diff --git a/libkdegames/kgame/dialogs/kgamedialog.h b/libkdegames/kgame/dialogs/kgamedialog.h
new file mode 100644
index 00000000..f7a0c6e5
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialog.h
@@ -0,0 +1,320 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// NAMING
+// please follow these naming rules if you add/change classes:
+// the main dialog is named KGameDialog and the base config widget
+// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where
+// XYZ = the name of the config widget, like "general" or "network") and are
+// inherited from KGameDialogConfig.
+
+#ifndef __KGAMEDIALOG_H__
+#define __KGAMEDIALOG_H__
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+class QGridLayout;
+class QVBoxLayout;
+class QListBoxItem;
+
+class KGame;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDialogConfig;
+class KGameDialogGeneralConfig;
+class KGameDialogNetworkConfig;
+class KGameDialogMsgServerConfig;
+class KGameDialogChatConfig;
+class KGameDialogConnectionConfig;
+
+class KGameDialogPrivate;
+/**
+ * TODO: rewrite entire documentation. Nearly nothing is valid anymore.
+ * The main configuration dialog for KGame. Here all players meat each other,
+ * every player can see how many players connected (and their names) and the
+ * ADMIN can even "kick" players out. You can talk to each other (using
+ * KGameChat and the ADMIN can define the maxPlayers/minPlayers as well as the
+ * number of computer players.
+ *
+ *
+ * AB: setDefaultXYZ is obsolete!!
+ * You will usually create an instance of KGameDialog or any derived class and
+ * call setDefaultXYZ methods. Example (maybe
+ * obsoleted parameters - docu is currently changing very fast):
+ * \code
+ * KGameDialog dlg(kgame, i18n("New Game"), localPlayer, this, true,
+ * ID_CHAT);
+ * dlg.setDefaultNetworkInfo(port, host); // AB: obsolete!
+ * dlg.exec();
+ * \endcode
+ * This will create a default modal dialog with the title "New Game". You don't
+ * have to do more than this.
+ *
+ * @short Main configuration dialog for KGame
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KGameDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+
+ enum ConfigOptions
+ {
+ NoConfig = 0,
+ ChatConfig = 1,
+ GameConfig = 2,
+ NetworkConfig = 4,
+ MsgServerConfig = 8,
+ BanPlayerConfig = 16,
+ AllConfig = 0xffff
+ };
+
+ /**
+ * Create an empty KGameDialog. You can add widgets using
+ * addConfigPage.
+ * @param g The KGame object of this game
+ * @param owner The KPlayer object who is responsible for this
+ * dialog, aka "the local player"
+ * @param title The title of the dialog - see KDialog::setCaption
+ * @param parent The parent of the dialog
+ * @param modal Whether the dialog is modal or not
+ **/
+ KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, bool modal = false);
+
+ /**
+ * Create a KGameDialog with the standard configuration widgets. This
+ * creates the following widgets:
+ * <ul>
+ * <li> KGameDialogGeneralConfig
+ * <li> KGameDialogNetworkConfig
+ * <li> KGameDialogMsgServerConfig
+ * <li> KGameDialogChatConfig
+ * <li> KGameDialogConnectionConfig
+ * </ul>
+ * If you want to use your own implementations (or none) of the widgets
+ * above you should subclass KGameDialog. Use addGameConfig,
+ * addNetworkConfig, addMsgConfig, addChatWidget and
+ * addConnectionList in this case.
+ *
+ * If you want to add further configuration widget you can simply use
+ * addConfigPage
+ * @param g The KGame object of this game
+ * @param owner The KPlayer object who is responsible for this
+ * dialog, aka "the local player"
+ * @param title The title of the dialog - see KDialog::setCaption
+ * @param parent The parent of the dialog
+ * @param modal Whether the dialog is modal or not
+ * @param initConfigs whether the default KGameDialogConfig widgets
+ * shall be created using initDefaultDialog. Use false if you want
+ * to use custom widgets.
+ * @param chatMsgId The ID of Chat messages. See KGameChat. Unused
+ * if initConfigs = false
+ **/
+ KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, long initConfigs = AllConfig,
+ int chatMsgId = 15432, bool modal = false);
+
+ virtual ~KGameDialog();
+
+
+ /**
+ * Change the owner of the dialog. This will be used as the fromPlayer in
+ * KGameChat and will receive the entered player name.
+ * @param owner The owner of the dialog. It must already be added to the
+ * KGame object!
+ *
+ * Calls the KGameDialogConfig::setOwner implementation of all
+ * widgets that have been added by addConfigWidget
+ * @param owner The new owner player of this dialog must already be
+ * added to the KGame object. Can even be NULL (then no player
+ * configuration is made)
+ **/
+ void setOwner(KPlayer* owner);
+
+ /**
+ * Change the KGame object this dialog is used for.
+ *
+ * Calls the KGameDialogConfig::setKGame implementation of all
+ * widgets that have been added by addConfigWidget
+ * @param g The new KGame object
+ **/
+ void setKGame(KGame* g);
+
+ /**
+ * This will submit all configuration data to the KGame object.
+ * Automatically called by slotApply and slotOk
+ * There is no need to replace this unless you
+ * want to add widgets which are not derived from those classes
+ **/
+ virtual void submitToKGame();
+
+ /**
+ * Adds a KGameChat to the dialog. If no parent is specified the
+ * game page will be used.
+ * @param chat The chat widget
+ * @param parent The parent of the chat widget. This MUST be an
+ * already added config widget. Note that the game page will be used
+ * if parent is 0.
+ **/
+ void addChatWidget(KGameDialogChatConfig* chat, QVBox* parent = 0);
+
+ /**
+ * Add a connection list to the dialog. The list consists of a
+ * KLisBox containing all players in the current game (see
+ * KGame::playerList). The admin can "ban" players, ie kick them out of
+ * the game.
+ *
+ * This is another not-really-config-config-widget. It just displays the
+ * connections and lets you ban players.
+ * @param c The KGameDialogConnectionConfig object
+ * @param parent The parent of the widget. If 0 the networkConfig
+ * page is used.
+ **/
+ void addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent = 0);
+
+ /**
+ * Add a new page to the dialog. The page will contain you new config
+ * widget and will have your provided title.
+ *
+ * The widget will be reparented to this dialog. This also calls
+ * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner.
+ * @param widget The new config widget
+ * @param title The title of the newly added page.
+ * @return The newly added page which contains your config widget.
+ **/
+ QVBox* addConfigPage(KGameDialogConfig* widget, const QString& title);
+
+ /**
+ * @return The QVBox of the given key, The key is from ConfigOptions
+ * Note that not all are supported yet
+ **/
+ QVBox *configPage(ConfigOptions which);
+
+ /**
+ * @return The default netowrk config. Note that this always returns 0 if
+ * you did not specify NetworkConfig in the constructor!
+ **/
+ KGameDialogNetworkConfig* networkConfig() const;
+
+ /**
+ * @return The default game config. Note that this always returns 0 if
+ * you did not specify GameConfig in the constructor!
+ **/
+ KGameDialogGeneralConfig* gameConfig() const;
+
+ /**
+ * Add a config widget to the specified parent. Usually you call
+ * addConfigPage for one widget and addConfigWidget for another to add
+ * it to the same page. Just use the returned page of
+ * addConfigPage.
+ **/
+ void addConfigWidget(KGameDialogConfig* widget, QWidget* parent);
+
+ /**
+ * Used to add the main network config widget in a new page. Use this to
+ * make networkConfig return something useful.
+ **/
+ void addNetworkConfig(KGameDialogNetworkConfig* netConf);
+
+ /**
+ * Add the main game config widget in a new page. Use this to make
+ * gameConfig return something useful.
+ **/
+ void addGameConfig(KGameDialogGeneralConfig* conf);
+
+ /**
+ * Used to add the message server config widget in a new page.
+ **/
+ void addMsgServerConfig(KGameDialogMsgServerConfig* conf);
+
+protected:
+
+ /**
+ * This is used to create a dialog containing all the default widgets.
+ *
+ * You may want to use this if you just want to use your own
+ * configuration widgets which inherit the standard ones.
+ *
+ * Note that if one of the widgets is NULL the default implementation
+ * will be used! (except the chat widget - you need to create it
+ * yourself as you have to provide a message id)
+ * @param initConfigs The widgets to be created
+ * @param chatMsgId The msgid for the chat config (only if specified in
+ * initConfigs) - see KGameDialogChatConfig
+ **/
+ void initDefaultDialog(ConfigOptions initConfigs, int chatMsgId = 15432);
+
+ /**
+ * Go through all config widgets and call their
+ * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner implementation
+ *
+ * This function could be private and probably will be very soon.
+ * Don't use it yourself
+ **/
+ void configureConfigWidgets();
+
+protected slots:
+ /**
+ * Called when the user clicks on Ok. Calls slotApply and
+ * QDialog::accept()
+ **/
+ virtual void slotOk();
+
+ /**
+ * Just calls submitToKGame()
+ **/
+ virtual void slotApply();
+
+ /**
+ * Sets the default values for the configuration widgets. Set these
+ * values by (e.g.) setDefaultMaxPlayers()
+ * @deprecated
+ **/
+ virtual void slotDefault();
+
+ /**
+ * Called when the KGame object is destroyed. Calls setKGame(0) so
+ * that all widgets can disconnect their slots and so on.
+ **/
+ void slotUnsetKGame();
+
+ /**
+ * Called when the ADMIN status of this KGame client changes. See
+ * KGameNetwork::signalAdminStatusChanged
+ * @param isAdmin TRUE if this client is now the ADMIN otherwise FALSE
+ **/
+ void setAdmin(bool isAdmin);
+
+ /**
+ * Remove a config widget from the widget list.
+ * @see QObject::destroyed
+ **/
+ void slotRemoveConfigWidget(QObject* configWidget);
+
+private:
+ void init(KGame*, KPlayer*);
+
+private:
+ KGameDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.cpp b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp
new file mode 100644
index 00000000..27f91cd1
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp
@@ -0,0 +1,773 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamedialogconfig.h"
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamechat.h"
+#include "kgameconnectdialog.h"
+
+#include <klocale.h>
+#include <knuminput.h>
+#include <kdialog.h>
+#include <klistbox.h>
+#include <kmessagebox.h>
+
+#include <qlayout.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qvbox.h>
+#include <qptrdict.h>
+
+#include "kgamedialogconfig.moc"
+
+class KGameDialogConfigPrivate
+{
+public:
+ KGameDialogConfigPrivate()
+ {
+ mOwner = 0;
+ mGame = 0;
+
+ mAdmin = false;
+ }
+
+ bool mAdmin;
+ KGame* mGame;
+ KPlayer* mOwner;
+};
+
+KGameDialogConfig::KGameDialogConfig(QWidget* parent) : QWidget(parent)
+{
+ d = new KGameDialogConfigPrivate;
+}
+
+KGameDialogConfig::~KGameDialogConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogConfig::setKGame(KGame* g)
+{
+ d->mGame = g;
+}
+
+void KGameDialogConfig::setOwner(KPlayer* p)
+{
+ d->mOwner = p;
+}
+
+void KGameDialogConfig::setAdmin(bool a)
+{
+ d->mAdmin = a;
+}
+
+KGame* KGameDialogConfig::game() const
+{ return d->mGame; }
+bool KGameDialogConfig::admin() const
+{ return d->mAdmin; }
+KPlayer* KGameDialogConfig::owner() const
+{ return d->mOwner; }
+
+/////////////////////////// KGameDialogNetworkConfig /////////////////////////
+class KGameDialogNetworkConfigPrivate
+{
+public:
+ KGameDialogNetworkConfigPrivate()
+ {
+ mInitConnection = 0;
+ mNetworkLabel = 0;
+ mDisconnectButton = 0;
+ mConnect = 0;
+ mDefaultServer=true;
+
+ }
+
+ // QPushButton* mInitConnection;
+ QHGroupBox* mInitConnection;
+ QLabel* mNetworkLabel;
+ QPushButton *mDisconnectButton;
+
+ bool mDefaultServer;
+ QString mDefaultHost;
+ unsigned short int mDefaultPort;
+ KGameConnectWidget *mConnect;
+};
+
+
+KGameDialogNetworkConfig::KGameDialogNetworkConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogNetworkConfigPrivate();
+
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint(), "toplayout");
+
+ QHBoxLayout *hb = new QHBoxLayout(topLayout, KDialog::spacingHint());
+
+ d->mNetworkLabel = new QLabel(this);
+ hb->addWidget(d->mNetworkLabel);
+
+ d->mDisconnectButton=new QPushButton(i18n("Disconnect"),this);
+ connect(d->mDisconnectButton, SIGNAL(clicked()), this, SLOT(slotExitConnection()));
+ hb->addWidget(d->mDisconnectButton);
+
+ d->mInitConnection = new QHGroupBox(i18n("Network Configuration"), this);
+ topLayout->addWidget(d->mInitConnection);
+
+ d->mConnect = new KGameConnectWidget(d->mInitConnection);
+ connect(d->mConnect, SIGNAL(signalNetworkSetup()), this, SLOT(slotInitConnection()));
+ connect(d->mConnect, SIGNAL(signalServerTypeChanged(int)),
+ this, SIGNAL(signalServerTypeChanged(int)));
+
+ // Needs to be AFTER the creation of the dialogs
+ setConnected(false);
+ setDefaultNetworkInfo("localhost", 7654,true);
+}
+
+KGameDialogNetworkConfig::~KGameDialogNetworkConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogNetworkConfig::slotExitConnection()
+{
+ kdDebug(11001) << k_funcinfo << " !!!!!!!!!!!!!!!!!!!!!!!" << endl;
+ if (game()) game()->disconnect();
+ setConnected(false,false);
+}
+
+void KGameDialogNetworkConfig::slotInitConnection()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ bool connected = false;
+ bool master = true;
+ unsigned short int port = d->mConnect->port();
+ QString host = d->mConnect->host();
+
+ if (host.isNull()) {
+ master = true;
+ if (game()) {
+ game()->setDiscoveryInfo(d->mConnect->type(),d->mConnect->gameName());
+ connected = game()->offerConnections(port);
+ }
+ } else {
+ master = false;
+ if (game()) {
+ connected = game()->connectToServer(host, port);
+ }
+ // We need to learn about failed connections
+ if (game()) {
+ connect(game(), SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotConnectionBroken()));
+ }
+ }
+ setConnected(connected, master);
+}
+
+void KGameDialogNetworkConfig::slotConnectionBroken()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ setConnected(false,false);
+ KMessageBox::error(this, i18n("Cannot connect to the network"));
+}
+
+void KGameDialogNetworkConfig::setConnected(bool connected, bool master)
+{
+ if (!connected) {
+ d->mNetworkLabel->setText(i18n("Network status: No Network"));
+ d->mInitConnection->setEnabled(true);
+ d->mDisconnectButton->setEnabled(false);
+ return;
+ }
+ if (master) {
+ d->mNetworkLabel->setText(i18n("Network status: You are MASTER"));
+ } else {
+ d->mNetworkLabel->setText(i18n("Network status: You are connected"));
+ }
+ d->mInitConnection->setEnabled(false);
+ d->mDisconnectButton->setEnabled(true);
+}
+
+void KGameDialogNetworkConfig::submitToKGame(KGame* , KPlayer* )
+{
+}
+
+void KGameDialogNetworkConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ if (!game()) {
+ setConnected(false);
+ return;
+ }
+ setConnected(game()->isNetwork(), game()->isMaster());
+}
+
+void KGameDialogNetworkConfig::setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server)
+{
+ d->mDefaultPort = port;
+ d->mDefaultHost = host;
+ d->mDefaultServer = server;
+
+ d->mConnect->setHost(host);
+ d->mConnect->setPort(port);
+ if (server) {
+ d->mConnect->setDefault(0);
+ } else {
+ d->mConnect->setDefault(1);
+ }
+}
+
+void KGameDialogNetworkConfig::setDiscoveryInfo(const QString& type, const QString& name)
+{
+ d->mConnect->setType(type);
+ d->mConnect->setName(name);
+}
+
+/////////////////////////// KGameDialogGeneralConfig /////////////////////////
+class KGameDialogGeneralConfigPrivate
+{
+public:
+ KGameDialogGeneralConfigPrivate()
+ {
+ mTopLayout = 0;
+ mName = 0;
+ }
+
+ QLineEdit* mName;
+
+ QVBoxLayout* mTopLayout;
+};
+
+KGameDialogGeneralConfig::KGameDialogGeneralConfig(QWidget* parent, bool initializeGUI)
+ : KGameDialogConfig(parent)
+{
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogGeneralConfigPrivate;
+
+ if (initializeGUI) {
+ d->mTopLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ d->mTopLayout->setAutoAdd(true);
+
+ QWidget* nameWidget = new QWidget(this);
+ QHBoxLayout* l = new QHBoxLayout(nameWidget);
+ QLabel* nameLabel = new QLabel(i18n("Your name:"), nameWidget);
+ l->addWidget(nameLabel);
+ d->mName = new QLineEdit(nameWidget);
+ l->addWidget(d->mName);
+ }
+}
+
+KGameDialogGeneralConfig::~KGameDialogGeneralConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogGeneralConfig::setPlayerName(const QString& name)
+{
+ if (d->mName) {
+ d->mName->setText(name);
+ }
+}
+
+QString KGameDialogGeneralConfig::playerName() const
+{
+ return d->mName ? d->mName->text() : QString::null;
+}
+
+void KGameDialogGeneralConfig::setOwner(KPlayer* p)
+{
+ if (owner()) {
+ owner()->disconnect(this);
+ }
+ KGameDialogConfig::setOwner(p);
+ if (!owner()) {
+ // can this config be used at all?
+ // maybe call hide()
+ return;
+ }
+ connect(owner(), SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+ setPlayerName(p->name());
+ //TODO: connect signalPropertyChanged and check for playername changes!
+}
+
+void KGameDialogGeneralConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ if (!g) {
+ // TODO
+ // can this config be used at all?
+ // maybe call hide()
+ return;
+ }
+}
+
+void KGameDialogGeneralConfig::setAdmin(bool admin)
+{
+ KGameDialogConfig::setAdmin(admin);
+// enable/disable widgets
+
+}
+
+void KGameDialogGeneralConfig::submitToKGame(KGame* g, KPlayer* p)
+{
+//FIXME
+ if (p) {
+ p->setName(playerName());
+ }
+ if (g) {
+ }
+}
+
+void KGameDialogGeneralConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p)
+{
+ if (!prop || !p || p != owner()) {
+ return;
+ }
+ switch (prop->id()) {
+ case KGamePropertyBase::IdName:
+ setPlayerName(p->name());
+ break;
+ default:
+ break;
+ }
+}
+
+class KGameDialogMsgServerConfigPrivate
+{
+public:
+ KGameDialogMsgServerConfigPrivate()
+ {
+ senderLayout = 0;
+ localLayout = 0;
+
+ changeMaxClients = 0;
+ changeAdmin= 0;
+ removeClient= 0;
+ noAdmin = 0;
+
+ noMaster = 0;
+ }
+
+ QVBoxLayout* senderLayout;
+ QHBoxLayout* localLayout;
+
+ QPushButton* changeMaxClients;
+ QPushButton* changeAdmin;
+ QPushButton* removeClient;
+ QLabel* noAdmin;
+
+ QLabel* noMaster;
+};
+
+
+// TODO: change ADMIN ID, remove CLIENTS, change MAXCLIENTS
+// we do everything here with QPushButtons as we want to wait a moment before
+// continuing - the message must be sent over network first
+KGameDialogMsgServerConfig::KGameDialogMsgServerConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ d = new KGameDialogMsgServerConfigPrivate;
+
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ d->senderLayout = new QVBoxLayout(topLayout);
+ d->localLayout = new QHBoxLayout(topLayout);
+}
+
+KGameDialogMsgServerConfig::~KGameDialogMsgServerConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogMsgServerConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ //TODO display the ID of the admin if we aren't
+ // connect(g, SIGNAL(signalAdminChanged(int)), this, SLOT(slotChangeIsAdmin(int)));//TODO
+ if (!game()) {
+ // we cannot do anything without a KGame object!
+ setAdmin(false);
+ return;
+ }
+ setAdmin(game()->isAdmin());
+ setHasMsgServer(game()->messageServer());
+}
+
+
+void KGameDialogMsgServerConfig::slotChangeMaxClients()
+{
+ if (!game()) {
+ kdError(11001) << k_funcinfo << ": no valid game object available!" << endl;
+ return;
+ }
+ if (!game()->isAdmin()) {
+ kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ int max;
+// edit->setText(QString::number()); // current max clients! //TODO
+
+ QDialog* dialog = new QDialog();
+ dialog->setCaption(i18n("Maximal Number of Clients"));
+ QHBoxLayout* l = new QHBoxLayout(dialog, KDialog::marginHint(), KDialog::spacingHint());
+ l->setAutoAdd(true);
+
+ (void) new QLabel(i18n("Maximal number of clients (-1 = infinite):"), dialog);
+ QLineEdit* edit = new QLineEdit(dialog);//TODO: use KIntNumInput
+// edit->setText(QString::number(max)); // current max clients! //TODO
+ if (dialog->exec() == QDialog::Accepted) {
+ bool ok;
+ max = edit->text().toInt(&ok);
+ if (ok) {
+ game()->setMaxClients(max);
+ }
+ }
+
+}
+
+void KGameDialogMsgServerConfig::slotRemoveClient()
+{
+}
+
+void KGameDialogMsgServerConfig::slotChangeAdmin()
+{
+ if (!game()) {
+ kdError(11001) << k_funcinfo << ": no valid game object available!" << endl;
+ return;
+ }
+ if (!admin()) {
+ kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ //TODO
+ Q_UINT32 newAdmin = 0;
+// newAdmin = ;
+ game()->electAdmin(newAdmin);
+}
+
+void KGameDialogMsgServerConfig::removeClient(Q_UINT32 /*id*/)
+{
+//TODO
+}
+
+void KGameDialogMsgServerConfig::setAdmin(bool a)
+{
+ if (admin() == a) {
+ // no need to do anything
+ return;
+ }
+ KGameDialogConfig::setAdmin(a);
+ if (admin()) {
+ if (d->noAdmin) {
+ delete d->noAdmin;
+ d->noAdmin = 0;
+ }
+ d->changeMaxClients = new QPushButton(i18n("Change Maximal Number of Clients"), this);
+ connect(d->changeMaxClients, SIGNAL(pressed()), this, SLOT(slotChangeMaxClients()));
+ d->changeAdmin = new QPushButton(i18n("Change Admin"), this);
+ connect(d->changeAdmin, SIGNAL(pressed()), this, SLOT(slotChangeAdmin()));
+ d->removeClient = new QPushButton(i18n("Remove Client with All Players"), this);
+ connect(d->removeClient, SIGNAL(pressed()), this, SLOT(slotRemoveClient()));
+ d->senderLayout->addWidget(d->changeMaxClients);
+ d->senderLayout->addWidget(d->changeAdmin);
+ d->senderLayout->addWidget(d->removeClient);
+ } else {
+ if (d->changeMaxClients) {
+ delete d->changeMaxClients;
+ d->changeMaxClients = 0;
+ }
+ if (d->changeAdmin) {
+ delete d->changeAdmin;
+ d->changeAdmin = 0;
+ }
+ if (d->removeClient) {
+ delete d->removeClient;
+ d->removeClient = 0;
+ }
+ d->noAdmin = new QLabel(i18n("Only the admin can configure the message server!"), this);
+ d->senderLayout->addWidget(d->noAdmin);
+ }
+}
+
+
+void KGameDialogMsgServerConfig::setHasMsgServer(bool has)
+{
+ if (!has) {
+ // delete all inputs
+ if (!d->noMaster) {
+ d->noMaster = new QLabel(i18n("You don't own the message server"), this);
+ d->localLayout->addWidget(d->noMaster);
+ }
+ return;
+ }
+ if (d->noMaster) {
+ delete d->noMaster;
+ d->noMaster = 0;
+ }
+ //TODO
+ // list all connections, data (max clients) and so on
+ // cannot be done above (together with QPushButtons) as it is possible that
+ // this client is ADMIN but not MASTER (i.e. doesn't own the messageserver)
+}
+
+
+class KGameDialogChatConfigPrivate
+{
+public:
+ KGameDialogChatConfigPrivate()
+ {
+ mChat = 0;
+ }
+
+ KGameChat* mChat;
+};
+
+KGameDialogChatConfig::KGameDialogChatConfig(int chatMsgId, QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ d = new KGameDialogChatConfigPrivate;
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ topLayout->setAutoAdd(true);
+ QHGroupBox* b = new QHGroupBox(i18n("Chat"), this);
+ d->mChat = new KGameChat(0, chatMsgId, b);
+}
+
+KGameDialogChatConfig::~KGameDialogChatConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogChatConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ d->mChat->setKGame(game());
+ if (!game()) {
+ hide();
+ } else {
+ show();
+ }
+}
+
+void KGameDialogChatConfig::setOwner(KPlayer* p)
+{
+ KGameDialogConfig::setOwner(p);
+ if (!owner()) {
+ hide();
+ return;
+ }
+ d->mChat->setFromPlayer(owner());
+ show();
+}
+
+
+
+class KGameDialogConnectionConfigPrivate
+{
+public:
+ KGameDialogConnectionConfigPrivate()
+ {
+ mPlayerBox = 0;
+ }
+
+ QPtrDict<KPlayer> mItem2Player;
+ KListBox* mPlayerBox;
+};
+
+KGameDialogConnectionConfig::KGameDialogConnectionConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ //TODO: prevent player to ban himself
+ d = new KGameDialogConnectionConfigPrivate;
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ topLayout->setAutoAdd(true);
+ QHGroupBox* b = new QHGroupBox(i18n("Connected Players"), this);
+ d->mPlayerBox = new KListBox(b);
+ setMinimumHeight(100);
+}
+
+KGameDialogConnectionConfig::~KGameDialogConnectionConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ // d->mIem2Player.clear();
+ delete d;
+}
+
+void KGameDialogConnectionConfig::setKGame(KGame* g)
+{
+ if (game()) {
+ disconnect(game(), 0, this, 0);
+ }
+ KGameDialogConfig::setKGame(g);
+ slotClearPlayers();
+ if (game()) {
+// react to changes in KGame::playerList()
+ connect(game(), SIGNAL(signalPlayerJoinedGame(KPlayer*)),
+ this, SLOT(slotPlayerJoinedGame(KPlayer*)));
+ connect(game(), SIGNAL(signalPlayerLeftGame(KPlayer*)),
+ this, SLOT(slotPlayerLeftGame(KPlayer*)));
+
+ KGame::KGamePlayerList l = *game()->playerList();
+ for (KPlayer* p = l.first(); p; p = l.next()) {
+ slotPlayerJoinedGame(p);
+ }
+ }
+}
+
+void KGameDialogConnectionConfig::setOwner(KPlayer* p)
+{
+ KGameDialogConfig::setOwner(p);
+}
+
+void KGameDialogConnectionConfig::setAdmin(bool a)
+{
+ if (!game()) {// not possible... in theory
+ return;
+ }
+ if (admin()) {
+ disconnect(game(), SIGNAL(executed(QListBoxItem*)), this, 0);
+ }
+ KGameDialogConfig::setAdmin(a);
+ if (admin()) {
+ connect(d->mPlayerBox, SIGNAL(executed(QListBoxItem*)), this,
+ SLOT(slotKickPlayerOut(QListBoxItem*)));
+ }
+}
+
+QListBoxItem* KGameDialogConnectionConfig::item(KPlayer* p) const
+{
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current()) {
+ if (it.current() == p) {
+ return (QListBoxItem*)it.currentKey();
+ }
+ ++it;
+ }
+ return 0;
+}
+
+void KGameDialogConnectionConfig::slotClearPlayers()
+{
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current()) {
+ slotPlayerLeftGame(it.current());
+ ++it;
+ }
+
+ if (d->mItem2Player.count() > 0) {
+ kdWarning(11001) << k_funcinfo << ": itemList wasn't cleared properly" << endl;
+ d->mItem2Player.clear();
+ }
+ if (d->mPlayerBox->count() > 0) {
+ kdWarning(11001) << k_funcinfo << ": listBox wasn't cleared properly" << endl;
+ d->mPlayerBox->clear();
+ }
+
+}
+
+void KGameDialogConnectionConfig::slotPlayerJoinedGame(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": Cannot add NULL player" << endl;
+ }
+ if (d->mItem2Player[p]) {
+ kdError(11001) << k_funcinfo << ": attempt to double add player" << endl;
+ return;
+ }
+ kdDebug(11001) << k_funcinfo << ": add player " << p->id() << endl;
+ QListBoxText* t = new QListBoxText(p->name());
+ d->mItem2Player.insert(t, p);
+ d->mPlayerBox->insertItem(t);
+
+ connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+
+}
+
+void KGameDialogConnectionConfig::slotPlayerLeftGame(KPlayer* p)
+{
+ // disconnect first
+ this->disconnect(p);
+ if (!item(p)) {
+ kdError(11001) << k_funcinfo << ": cannot find " << p->id()
+ << " in list" << endl;
+ return;
+ }
+ d->mPlayerBox->removeItem(d->mPlayerBox->index(item(p)));
+
+}
+
+void KGameDialogConnectionConfig::slotKickPlayerOut(QListBoxItem* item)
+{
+ kdDebug(11001) << "kick player out" << endl;
+ KPlayer* p = d->mItem2Player[item];
+ if (!p) {
+ kdError(11001) << "invalid item selected - no player found" << endl;
+ return;
+ }
+ if (!game()) {
+ kdWarning(11001) << "no game set" << endl;
+ return;
+ }
+ if (!admin()) {
+ kdDebug(11001) << "Only the ADMIN can kick players" << endl;
+ return;
+ }
+ if (p == owner()) { // you wanna ban the ADMIN ??
+ kdDebug(11001) << "you cannot kick the ADMIN" << endl;
+ return;
+ }
+
+ if (KMessageBox::questionYesNo(this, i18n("Do you want to ban player \"%1\" from the game?").arg(
+ p->name()), QString::null, i18n("Ban Player"), i18n("Do Not Ban")) == KMessageBox::Yes) {
+ kdDebug(11001) << "will remove player " << p << endl;
+ game()->removePlayer(p);
+// d->mPlayerBox->removeItem(d->mPlayerBox->index(item)); // should be done by signalPlayerLeftGame
+ } else {
+ kdDebug(11001) << "will NOT remove player " << p << endl;
+ }
+}
+
+void KGameDialogConnectionConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player)
+{
+ if(prop->id() == KGamePropertyBase::IdName) {
+ QListBoxText* old = 0;
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current() && !old) {
+ if (it.current() == player) {
+ old = (QListBoxText*)it.currentKey();
+ }
+ ++it;
+ }
+ QListBoxText* t = new QListBoxText(player->name());
+ d->mPlayerBox->changeItem(t, d->mPlayerBox->index(old));
+ d->mItem2Player.remove(old);
+ d->mItem2Player.insert(t, player);
+ }
+}
+
diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.h b/libkdegames/kgame/dialogs/kgamedialogconfig.h
new file mode 100644
index 00000000..b7d30b23
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialogconfig.h
@@ -0,0 +1,362 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// NAMING
+// please follow these naming rules if you add/change classes:
+// the main dialog is named KGameDialog and the base config widget
+// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where
+// XYZ = the name of the config widget, like "general" or "network") and are
+// inherited from KGameDialogConfig.
+
+#ifndef __KGAMEDIALOGCONFIG_H__
+#define __KGAMEDIALOGCONFIG_H__
+
+#include <qwidget.h>
+#include <kdemacros.h>
+
+class QGridLayout;
+class QVBoxLayout;
+class QListBoxItem;
+
+class KGame;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDialogConfigPrivate;
+/**
+ * Base class for configuration widgets.
+ *
+ * You can inherit from this and implement @ref submitToKGame, @ref
+ * setOwner and @ref setKGame to create your personal @ref KGame configuration widget :-)
+ * @short Base class for configuration widgets
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KGameDialogConfig : public QWidget
+{
+ Q_OBJECT
+public:
+ KGameDialogConfig(QWidget* parent = 0);
+ virtual ~KGameDialogConfig();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p) = 0;
+
+ /**
+ * The owner player of the dialog has been changed. The default
+ * changes the pointer for owner so don't forget to call the
+ * default implementation if you overwrite this!
+ *
+ * You can use this e.g. to change a line edit widget containing the
+ * player name.
+ *
+ * Note: even NULL players are allowed!
+ * @param p The new owner player of the dialog
+ **/
+ virtual void setOwner(KPlayer* p);
+
+ /**
+ * The KGame object of the dialog has been changed. The default
+ * implementation changes the pointer for game so don't forget to
+ * call the default implementation if you overwrite this!
+ *
+ * You can use this e.g. to re-read the min/max player settings.
+ * @param g The KGame object
+ **/
+ virtual void setKGame(KGame* g);
+
+ /**
+ * The admin status has been changed.
+ * If the KGame object of this config widget is the
+ * admin the user is allowed to configure it. Otherwise most
+ * widgets will have to be disabled. Note that you don't necessarily
+ * need to deactivate all widget - e.g. the player name must be
+ * configured by the player. Mainly the KGame configuration can be done
+ * by the admin only.
+ *
+ * By default this does nothing. Changes the value for admin so
+ * don't forget to call the default implementation in derived classes!
+ * @param admin Whether the KGame object of this dialog can be
+ * configured
+ **/
+ virtual void setAdmin(bool admin);
+
+ /**
+ * A pointer to the KGame object that has been set by @ref setKGame.
+ *
+ * Note that NULL is allowed!
+ * @return The KGame object assigned to this dialog
+ **/
+ KGame* game() const;
+
+ /**
+ * A pointer to the KPlayer object that has been set by @ref
+ * setOwner.
+ *
+ * Note that NULL is allowed!
+ * @return The owner of the dialog
+ **/
+ KPlayer* owner() const;
+
+ /**
+ * @return True if the owner is ADMIN otherwise FALSE. See also
+ * @ref setAdmin
+ **/
+ bool admin() const;
+
+protected:
+
+private:
+ KGameDialogConfigPrivate* d;
+};
+
+/**
+ * The main game configuration widget.
+ *
+ * It currently contains a line edit for the name of the player only. You can
+ * add widgets by using the KGameDialogGeneralConfig as parent parameter as it
+ * uses QLayout::autoAdd == true.
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameDialogGeneralConfigPrivate;
+class KGameDialogGeneralConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a KGameDialogGeneralConfig. Currently it contains a line
+ * edit widget to change the player name only.
+ *
+ * If you just want to add more widgets you can just create your widgets
+ * with the KGameDialogGeneralConfig as parent as it uses
+ * QLayout::setAutoAdd(true).
+ *
+ * @param parent Parent widget for this dialog.
+ * @param initializeGUI If you really don't want to use the
+ * predefined widget and/or layout use FALSE here. Note that then none
+ * of the predefined widgets (currently only the name of the player)
+ * will exist anymore.
+ *
+ **/
+ KGameDialogGeneralConfig(QWidget* parent = 0, bool initializeGUI = true);
+ virtual ~KGameDialogGeneralConfig();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p);
+
+ /**
+ * Change the owner of the config widget.
+ *
+ * Changes the playername in the line edit
+ * @param p The new owner player
+ **/
+ virtual void setOwner(KPlayer* p);
+
+ /**
+ * See @ref KGameDialogConfig::setKGame
+ *
+ * Sets the default values of all KGame related predefined widgets
+ * (currently none)
+ **/
+ virtual void setKGame(KGame* g);
+
+ /**
+ * See @ref KGameDialogConfig::setAdmin
+ *
+ * This deactivates the min/max player widgets
+ **/
+ virtual void setAdmin(bool admin);
+
+protected slots:
+ void slotPropertyChanged(KGamePropertyBase*, KPlayer*);
+
+protected:
+ void setPlayerName(const QString& name);
+
+ QString playerName() const;
+
+private:
+ KGameDialogGeneralConfigPrivate* d;
+};
+
+class KGameDialogNetworkConfigPrivate;
+class KDE_EXPORT KGameDialogNetworkConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogNetworkConfig(QWidget* parent = 0);
+ virtual ~KGameDialogNetworkConfig();
+
+
+ void disableInitConnection();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p);
+
+ virtual void setKGame(KGame* g);
+
+ /**
+ * This sets the default port and host used in @ref KGameConnectDialog.
+ * The user will be able to change these defaults!
+ *
+ * If you don't call this then host "localhost" and port "0" is used.
+ * You are strongly encouraged to change at least the port!
+ * @param port The default port to connect to / listen on
+ * @param host The default host to connect to
+ **/
+ void setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server=true);
+
+ /**
+ * Set service type that will be published or browsed for and game name that will be displayed in
+ * server browser. Without this publishing and discovery of LAN servers will not be enabled.
+ * @param name Game name. Important only for server mode. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ * @param type Service type (something like _kwin4._tcp). It should be unique for application.
+ * @since 3.4
+ **/
+ void setDiscoveryInfo(const QString& type, const QString& name=QString::null);
+
+signals:
+ /**
+ * This signal is emmited if the user changes the server type (client/server)
+ * in the network configuration dialog.
+ *
+ * @param t - type type (0/1) of the connection
+ **/
+ void signalServerTypeChanged(int);
+
+
+protected:
+ void setConnected(bool connected, bool master = false);
+
+protected slots:
+ void slotInitConnection();
+ void slotExitConnection();
+ void slotConnectionBroken();
+
+
+private:
+ KGameDialogNetworkConfigPrivate* d;
+};
+
+class KGameDialogMsgServerConfigPrivate;
+class KGameDialogMsgServerConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogMsgServerConfig(QWidget* parent = 0);
+ virtual ~KGameDialogMsgServerConfig();
+
+ virtual void submitToKGame(KGame*, KPlayer*) {}
+
+ void setHasMsgServer(bool);
+
+ virtual void setKGame(KGame* g);
+ virtual void setAdmin(bool);
+
+protected slots:
+ void slotChangeMaxClients();
+ void slotChangeAdmin();
+ void slotRemoveClient();
+
+protected:
+ void removeClient(Q_UINT32 id);
+
+private:
+ KGameDialogMsgServerConfigPrivate* d;
+};
+
+class KGameDialogChatConfigPrivate;
+/**
+ * This is not really a configuration widget but rather a simple chat widget.
+ * This widget does nothing but just providing a @ref KGameChat object.
+ * @short A chat widget inside a @ref KGameDialog
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameDialogChatConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogChatConfig(int chatMsgId, QWidget* parent = 0);
+ virtual ~KGameDialogChatConfig();
+
+ virtual void setKGame(KGame* g);
+ virtual void setOwner(KPlayer* p);
+
+ virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); }
+
+private:
+ KGameDialogChatConfigPrivate* d;
+};
+
+/**
+ * @short Lists all connected players and gives the ability to kick them off the
+ * game
+ **/
+class KGameDialogConnectionConfigPrivate;
+class KGameDialogConnectionConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogConnectionConfig(QWidget* parent = 0);
+ virtual ~KGameDialogConnectionConfig();
+
+ virtual void setKGame(KGame* g);
+ virtual void setOwner(KPlayer* p);
+ virtual void setAdmin(bool admin);
+
+ virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); }
+
+protected:
+ /**
+ * @param p A player
+ * @return The QListBoxItem that belongs to the player @p p
+ **/
+ QListBoxItem* item(KPlayer* p) const;
+
+protected slots:
+ void slotKickPlayerOut(QListBoxItem* item);
+ void slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p);
+ void slotPlayerLeftGame(KPlayer* p);
+ void slotPlayerJoinedGame(KPlayer* p);
+ void slotClearPlayers();
+
+private:
+ KGameDialogConnectionConfigPrivate* d;
+
+};
+#endif
diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.cpp b/libkdegames/kgame/dialogs/kgameerrordialog.cpp
new file mode 100644
index 00000000..1750892c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameerrordialog.cpp
@@ -0,0 +1,129 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kgame.h"
+
+#include "kgameerrordialog.h"
+
+class KGameErrorDialogPrivate
+{
+public:
+ KGameErrorDialogPrivate()
+ {
+ mGame = 0;
+ }
+
+ const KGame* mGame;
+};
+
+KGameErrorDialog::KGameErrorDialog(QWidget* parent) : QObject(parent)
+{
+ d = new KGameErrorDialogPrivate;
+}
+
+KGameErrorDialog::~KGameErrorDialog()
+{
+ delete d;
+}
+
+void KGameErrorDialog::setKGame(const KGame* g)
+{
+ slotUnsetKGame();
+ d->mGame = g;
+
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+
+// the error signals:
+ connect(d->mGame, SIGNAL(signalNetworkErrorMessage(int, QString)),
+ this, SLOT(slotError(int, QString)));
+ connect(d->mGame, SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotServerConnectionLost()));
+ connect(d->mGame, SIGNAL(signalClientDisconnected(Q_UINT32,bool)),
+ this, SLOT(slotClientConnectionLost(Q_UINT32,bool)));
+}
+
+void KGameErrorDialog::slotUnsetKGame()
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = 0;
+}
+
+void KGameErrorDialog::error(const QString& errorText, QWidget* parent)
+{ KMessageBox::error(parent, errorText); }
+
+void KGameErrorDialog::slotServerConnectionLost()
+{
+// TODO: add IP/port of the server
+ QString message = i18n("Connection to the server has been lost!");
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::slotClientConnectionLost(Q_UINT32 /*id*/,bool)
+{
+//TODO: add IP/port of the client
+ QString message;
+// if (c) {
+// message = i18n("Connection to client has been lost!\nID: %1\nIP: %2").arg(c->id()).arg(c->IP());
+// } else {
+// message = i18n("Connection to client has been lost!");
+// }
+ message = i18n("Connection to client has been lost!");
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::slotError(int errorNo, QString text)
+{
+ QString message = i18n("Received a network error!\nError number: %1\nError message: %2").arg(errorNo).arg(text);
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::connectionError(QString s)
+{
+ QString message;
+ if (s.isNull()) {
+ message = i18n("No connection could be created.");
+ } else {
+ message = i18n("No connection could be created.\nThe error message was:\n%1").arg(s);
+ }
+ error(message, (QWidget*)parent());
+}
+
+
+
+// should become the real dialog - currently we just use messageboxes
+// -> maybe unused forever
+KGameErrorMessageDialog::KGameErrorMessageDialog(QWidget* parent)
+ : KDialogBase(Plain, i18n("Error"), Ok, Ok, parent, 0, true, true)
+{
+}
+
+KGameErrorMessageDialog::~KGameErrorMessageDialog()
+{
+}
+
+
+
+#include "kgameerrordialog.moc"
diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.h b/libkdegames/kgame/dialogs/kgameerrordialog.h
new file mode 100644
index 00000000..c1dbd1ca
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameerrordialog.h
@@ -0,0 +1,113 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEERRORDIALOG_H__
+#define __KGAMEERRORDIALOG_H__
+
+#include <kdialogbase.h>
+
+class KGame;
+class KGameErrorDialogPrivate;
+
+/**
+ * Use error(), warning() and information() to display the information about a
+ * network game. Maybe a better solution is to use KMessageBoxes
+ * You can connect to the public slots, too - they will call the static
+ * functions, so that you can always have a KGameErrorDialog object lying around
+ * without losing much memory (a KGameErrorMessageDialog Object will be
+ * created)
+ * @short Error handling for KGame
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameErrorDialog : public QObject
+{
+ Q_OBJECT
+public:
+ KGameErrorDialog(QWidget* parent);
+ ~KGameErrorDialog();
+
+ /**
+ * Automatically connects the KGame object to all error dependant slots.
+ * Create a KGameErrorDialog object, call this function and forget
+ * everything.
+ * @param g The KGame which will emit the erorrs (or not ;-) )
+ **/
+ void setKGame(const KGame* g);
+
+ /**
+ * KGame couldn't establish a connection. Use this if
+ * KGame::initConnection returns false
+ * @param s A string that describes the error further (like port is
+ * already in use). Will be ignored if QString::null
+ **/
+ void connectionError(QString s = QString::null);
+
+public slots:
+ void slotError(int error, QString text);
+
+ /**
+ * The connection to the @ref KMessageServer has been lost
+ *
+ * See @ref KGameNetwork::signalConnectionBroken
+ **/
+ void slotServerConnectionLost();
+
+ /**
+ * The connection to a client has been lost by accident
+ *
+ * See @ref KGameNetwork::signalClientDisconnected
+ **/
+ void slotClientConnectionLost(Q_UINT32 clientID,bool broken);
+
+ /**
+ * Unsets a @ref KGame which has been set using @ref setKGame before.
+ * This is called automatically when the @ref KGame object is destroyed
+ * and you normally don't have to call this yourself.
+ *
+ * Note that @ref setKGame also unsets an already existing @ref KGame
+ * object if exising.
+ **/
+ void slotUnsetKGame();
+
+protected:
+ void error(const QString& errorText, QWidget* parent = 0);
+
+private:
+ KGameErrorDialogPrivate* d;
+};
+
+/**
+ * The real class for error messages. KGameErrorDialog uses this to create error
+ * messages (not yet).
+ * Use @ref KGameErrorDialog instead.
+ * @short Internally used by @ref KGameErrorDialog
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameErrorMessageDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameErrorMessageDialog(QWidget* parent);
+ ~KGameErrorMessageDialog();
+
+private:
+};
+
+#endif
diff --git a/libkdegames/kgame/kgame.cpp b/libkdegames/kgame/kgame.cpp
new file mode 100644
index 00000000..101dbfcc
--- /dev/null
+++ b/libkdegames/kgame/kgame.cpp
@@ -0,0 +1,1475 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgame.h"
+#include "kgame.moc"
+#include "kgamepropertyhandler.h"
+#include "kgameproperty.h"
+#include "kplayer.h"
+#include "kgameio.h"
+#include "kgameerror.h"
+#include "kgamesequence.h"
+
+#include "kgamemessage.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <qbuffer.h>
+#include <qtimer.h>
+#include <qptrqueue.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <krandomsequence.h>
+#include <kdebug.h>
+
+#define KGAME_LOAD_COOKIE 4210
+
+// try to place as much as possible here
+// many things are *not* possible here as KGame has to use some inline function
+class KGamePrivate
+{
+public:
+ KGamePrivate()
+ {
+ mUniquePlayerNumber = 0;
+ mPolicy=KGame::PolicyLocal;
+ mGameSequence = 0;
+ }
+
+ int mUniquePlayerNumber;
+ QPtrQueue<KPlayer> mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu
+ KRandomSequence* mRandom;
+ KGame::GamePolicy mPolicy;
+ KGameSequence* mGameSequence;
+
+
+ KGamePropertyHandler* mProperties;
+
+ // player lists
+ KGame::KGamePlayerList mPlayerList;
+ KGame::KGamePlayerList mInactivePlayerList;
+
+ //KGamePropertys
+ KGamePropertyInt mMaxPlayer;
+ KGamePropertyUInt mMinPlayer;
+ KGamePropertyInt mGameStatus; // Game running?
+ QValueList<int> mInactiveIdList;
+
+};
+
+// ------------------- GAME CLASS --------------------------
+KGame::KGame(int cookie,QObject* parent) : KGameNetwork(cookie,parent)
+{
+ kdDebug(11001) << k_funcinfo << " - " << this << ", sizeof(KGame)=" << sizeof(KGame) << endl;
+ d = new KGamePrivate;
+
+ d->mProperties = new KGamePropertyHandler(this);
+
+ d->mProperties->registerHandler(KGameMessage::IdGameProperty,
+ this,SLOT(sendProperty(int, QDataStream&, bool* )),
+ SLOT(emitSignal(KGamePropertyBase *)));
+ d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers"));
+ d->mMaxPlayer.setLocal(-1); // Infinite
+ d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers"));
+ d->mMinPlayer.setLocal(0); // Always ok
+ d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus"));
+ d->mGameStatus.setLocal(Init);
+ // d->mUniquePlayerNumber = 0;
+ d->mRandom = new KRandomSequence;
+ d->mRandom->setSeed(0);
+
+ connect(this, SIGNAL(signalClientConnected(Q_UINT32)),
+ this, SLOT(slotClientConnected(Q_UINT32)));
+ connect(this, SIGNAL(signalClientDisconnected(Q_UINT32,bool)),
+ this, SLOT(slotClientDisconnected(Q_UINT32,bool)));
+ connect(this, SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotServerDisconnected()));
+
+ setGameSequence(new KGameSequence());
+
+ // BL: FIXME This signal does no longer exist. When we are merging
+ // MH: super....and how do I find out about the lost conenction now?
+ // KGame and KGameNetwork, this could be improved!
+// connect(this,SIGNAL(signalConnectionLost(KGameClient *)),
+// this,SLOT(slotConnectionLost(KGameClient *)));
+}
+
+KGame::~KGame()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+// Debug();
+ reset();
+ delete d->mGameSequence;
+ delete d->mRandom;
+ delete d;
+ kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+bool KGame::reset()
+{
+ deletePlayers();
+ deleteInactivePlayers();
+ return true;
+}
+
+void KGame::deletePlayers()
+{
+// kdDebug(11001) << k_funcinfo << endl;
+ KGamePlayerList tmp = d->mPlayerList; // in case of PolicyClean player=d->mPlayerList.first() is infinite
+ KPlayer *player;
+ while((player=tmp.first()))
+ {
+ delete player; // delete and removes the player
+ tmp.removeFirst();
+ }
+// kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+void KGame::deleteInactivePlayers()
+{
+ KPlayer *player;
+ while((player=d->mInactivePlayerList.first()))
+ {
+ //player->setGame(0); // prevent call backs
+ d->mInactivePlayerList.remove(player);
+ delete player;
+ }
+}
+
+bool KGame::load(QString filename,bool reset)
+{
+ if (filename.isNull())
+ {
+ return false;
+ }
+ QFile f(filename);
+ if (!f.open(IO_ReadOnly))
+ {
+ return false;
+ }
+ QDataStream s( &f );
+ load(s,reset);
+ f.close();
+ return true;
+}
+
+bool KGame::load(QDataStream &stream,bool reset)
+{ return loadgame(stream, false,reset); }
+
+bool KGame::loadgame(QDataStream &stream, bool network,bool resetgame)
+{
+ // Load Game Data
+
+ // internal data
+ Q_INT32 c;
+ stream >> c; // cookie
+
+ if (c!=cookie())
+ {
+ kdWarning(11001) << "Trying to load different game version we="<<cookie() << " saved=" << c << endl;
+ bool result=false;
+ emit signalLoadError(stream,network,(int)c,result);
+ return result;
+ }
+ if (resetgame) reset();
+
+ uint i;
+ stream >> i;
+// setPolicy((GamePolicy)i);
+
+ stream >> d->mUniquePlayerNumber;
+
+ if (gameSequence())
+ {
+ gameSequence()->setCurrentPlayer(0); // TODO !!!
+ }
+ int newseed;
+ stream >> newseed;
+ d->mRandom->setSeed(newseed);
+
+ // Switch off the direct emitting of signals while
+ // loading properties. This can cause inconsistencies
+ // otherwise if a property emits and this emit accesses
+ // a property not yet loaded
+ // Note we habe to have this external locking to prevent the games unlocking
+ // to access the players
+ dataHandler()->lockDirectEmit();
+ KPlayer *player;
+ for ( player=playerList()->first(); player != 0; player=playerList()->next() )
+ {
+ player->dataHandler()->lockDirectEmit();
+ // kdDebug(11001) << "Player "<<player->id() << " to indirect emit" <<endl;
+ }
+
+ // Properties
+ dataHandler()->load(stream);
+
+ // If there is additional data to be loaded before players are loaded then do
+ // this here.
+ emit signalLoadPrePlayers(stream);
+
+ // Load Playerobjects
+ uint playercount;
+ stream >> playercount;
+ kdDebug(11001) << "Loading KGame " << playercount << " KPlayer objects " << endl;
+ for (i=0;i<playercount;i++)
+ {
+ KPlayer *newplayer=loadPlayer(stream,network);
+ systemAddPlayer(newplayer);
+ }
+
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie==KGAME_LOAD_COOKIE) {
+ kdDebug(11001) << " Game loaded propertly"<<endl;
+ } else {
+ kdError(11001) << " Game loading error. probably format error"<<endl;
+ }
+
+ // Switch back on the direct emitting of signals and emit the
+ // queued signals.
+ // Note we habe to have this external locking to prevent the games unlocking
+ // to access the players
+ dataHandler()->unlockDirectEmit();
+ for ( player=playerList()->first(); player != 0; player=playerList()->next() )
+ {
+ player->dataHandler()->unlockDirectEmit();
+ // kdDebug(11001) << "Player "<<player->id() << " to direct emit" <<endl;
+ }
+
+ emit signalLoad(stream);
+ return true;
+}
+
+bool KGame::save(QString filename,bool saveplayers)
+{
+ if (filename.isNull())
+ {
+ return false;
+ }
+ QFile f(filename);
+ if (!f.open(IO_WriteOnly))
+ {
+ return false;
+ }
+ QDataStream s( &f );
+ save(s,saveplayers);
+ f.close();
+ return true;
+}
+
+bool KGame::save(QDataStream &stream,bool saveplayers)
+{ return savegame(stream, false,saveplayers); }
+
+bool KGame::savegame(QDataStream &stream,bool /*network*/,bool saveplayers)
+{
+ // Save Game Data
+
+ // internal variables
+ Q_INT32 c=cookie();
+ stream << c;
+
+ uint p=(uint)policy();
+ stream << p;
+ stream << d->mUniquePlayerNumber;
+ int newseed=(int)d->mRandom->getLong(65535);
+ stream << newseed;
+ d->mRandom->setSeed(newseed);
+
+ // Properties
+ dataHandler()->save(stream);
+
+ // Save all data that need to be saved *before* the players are saved
+ emit signalSavePrePlayers(stream);
+
+ if (saveplayers)
+ {
+ savePlayers(stream,playerList());
+ }
+ else
+ {
+ stream << (uint)0; // no players saved
+ }
+
+ stream << (Q_INT16)KGAME_LOAD_COOKIE;
+
+ emit signalSave(stream);
+ return true;
+}
+
+void KGame::savePlayer(QDataStream &stream,KPlayer* p)
+{
+// this could be in KGameMessage as well
+ stream << (Q_INT32)p->rtti();
+ stream << (Q_INT32)p->id();
+ stream << (Q_INT32)p->calcIOValue();
+ p->save(stream);
+}
+
+void KGame::savePlayers(QDataStream &stream, KGamePlayerList *list)
+{
+ if (!list)
+ {
+ list=playerList();
+ }
+
+ Q_INT32 cnt=list->count();
+ kdDebug(11001) << "Saving KGame " << cnt << " KPlayer objects " << endl;
+ stream << cnt;
+ KPlayer *player;
+ for ( player=list->first(); player != 0; player=list->next() )
+ {
+ savePlayer(stream,player);
+ }
+}
+
+KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/)
+{
+ kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl;
+ return new KPlayer;
+}
+KPlayer *KGame::loadPlayer(QDataStream& stream,bool isvirtual)
+{
+ Q_INT32 rtti,id,iovalue;
+ stream >> rtti >> id >> iovalue;
+ KPlayer *newplayer=findPlayer(id);
+ if (!newplayer)
+ {
+ kdDebug(11001) << k_funcinfo << "Player "<< id << " not found...asking user to create one " << endl;
+ newplayer=createPlayer(rtti,iovalue,isvirtual);
+ //emit signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this);
+ }
+ /*
+ if (!newplayer)
+ {
+ kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl;
+ newplayer=new KPlayer;
+ }
+ else
+ {
+ kdDebug(11001) << " USER Player " << newplayer << " done player->rtti=" << newplayer->rtti() << " rtti=" << rtti << endl;
+ }
+ */
+ newplayer->load(stream);
+ if (isvirtual)
+ {
+ newplayer->setVirtual(true);
+ }
+ return newplayer;
+}
+
+// ----------------- Player handling -----------------------
+
+KPlayer * KGame::findPlayer(Q_UINT32 id) const
+{
+ for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it)
+ {
+ if (it.current()->id() == id)
+ {
+ return it.current();
+ }
+ }
+ for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it)
+ {
+ if (it.current()->id() == id)
+ {
+ return it.current();
+ }
+ }
+ return 0;
+}
+
+// it is necessary that addPlayer and systemAddPlayer are called in the same
+// order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must
+// not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the
+// mAddPlayerList would get confused. Should be no problem as long as comServer
+// and the clients are working correctly.
+// BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar)
+// does, we would be in trouble...
+void KGame::addPlayer(KPlayer* newplayer)
+{
+ kdDebug(11001) << k_funcinfo << ": " << "; maxPlayers=" << maxPlayers() << " playerCount=" << playerCount() << endl;
+ if (!newplayer)
+ {
+ kdFatal(11001) << "trying to add NULL player in KGame::addPlayer()" << endl;
+ return ;
+ }
+
+ if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers())
+ {
+ kdWarning(11001) << "cannot add more than " << maxPlayers() << " players - deleting..." << endl;
+ delete newplayer;
+ return;
+ }
+
+ if (newplayer->id() == 0)
+ {
+ d->mUniquePlayerNumber++;
+ newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId()));
+ kdDebug(11001) << k_funcinfo << "NEW!!! player " << newplayer << " now has id " << newplayer->id() << endl;
+ }
+ else
+ {
+ // this could happen in games which use their own ID management by certain
+ // reasons. that is NOT recommended
+ kdDebug(11001) << k_funcinfo << "player " << newplayer << " already has an id: " << newplayer->id() << endl;
+ }
+
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ // We distinguis here what policy we have
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemAddPlayer(newplayer);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ savePlayer(stream,newplayer);
+ // Store the player for delayed clean adding
+ if (policy()==PolicyClean)
+ {
+ d->mAddPlayerList.enqueue(newplayer);
+ }
+ sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0);
+ }
+}
+
+void KGame::systemAddPlayer(KPlayer* newplayer)
+{
+ if (!newplayer)
+ {
+ kdFatal(11001) << "trying to add NULL player in KGame::systemAddPlayer()" << endl;
+ return ;
+ }
+ if (newplayer->id() == 0)
+ {
+ kdWarning(11001) << k_funcinfo << "player " << newplayer << " has no ID" << endl;
+ }
+
+ if (findPlayer(newplayer->id()))
+ {
+ kdError(11001) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again" << endl;
+ delete newplayer;
+ }
+ else
+ {
+ kdDebug(11001) << "Trying to add player " << newplayer <<" maxPlayers="<<maxPlayers()<<" playerCount="<<playerCount() << endl;
+ // Add the player to the game
+ d->mPlayerList.append(newplayer);
+ newplayer->setGame(this);
+ kdDebug(11001) << "Player: isVirtual=" << newplayer->isVirtual() << endl;
+ kdDebug(11001) << " id=" << newplayer->id() << " #Players="
+ << d->mPlayerList.count() << " added " << newplayer
+ << " (virtual=" << newplayer->isVirtual() << ")" << endl;
+ emit signalPlayerJoinedGame(newplayer);
+ }
+}
+
+// Called by the KPlayer destructor
+void KGame::playerDeleted(KPlayer *player)
+{
+ kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemRemovePlayer(player,false);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (!player->isVirtual())
+ {
+ kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl;
+ sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0);
+ }
+ }
+}
+
+bool KGame::removePlayer(KPlayer * player, Q_UINT32 receiver)
+{//transmit to all clients, or to receiver only
+ if (!player)
+ {
+ kdFatal(11001) << "trying to remove NULL player in KGame::removePlayer()" << endl;
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemRemovePlayer(player,true);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl;
+ sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver);
+ }
+ return true;
+ // we will receive the message in networkTransmission()
+}
+
+void KGame::systemRemovePlayer(KPlayer* player,bool deleteit)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ if (!player)
+ {
+ kdWarning(11001) << "cannot remove NULL player" << endl;
+ return;
+ }
+ if (!systemRemove(player,deleteit))
+ {
+ kdWarning(11001) << "player " << player << "(" << player->id() << ") Could not be found!" << endl;
+ }
+
+ if (gameStatus()==(int)Run && playerCount()<minPlayers())
+ {
+ kdWarning(11001) << k_funcinfo ": not enough players, PAUSING game\n" << endl;
+ setGameStatus(Pause);
+ }
+}
+
+bool KGame::systemRemove(KPlayer* p,bool deleteit)
+{
+ if (!p)
+ {
+ kdWarning(11001) << "cannot remove NULL player" << endl;
+ return false;
+ }
+ bool result;
+ kdDebug(11001) << k_funcinfo << ": Player (" << p->id() << ") to be removed " << p << endl;
+
+ if (d->mPlayerList.count() == 0)
+ {
+ result = false;
+ }
+ else
+ {
+ result = d->mPlayerList.remove(p);
+ }
+
+ emit signalPlayerLeftGame(p);
+
+ p->setGame(0);
+ if (deleteit)
+ {
+ delete p;
+ }
+
+ return result;
+}
+
+bool KGame::inactivatePlayer(KPlayer* player)
+{
+ if (!player)
+ {
+ return false;
+ }
+ kdDebug(11001) << "Inactivate player " << player->id() << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemInactivatePlayer(player);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
+ }
+
+ return true;
+}
+
+bool KGame::systemInactivatePlayer(KPlayer* player)
+{
+ if (!player || !player->isActive())
+ {
+ return false;
+ }
+ kdDebug(11001) << " Inactivate player " << player->id() << endl;
+
+ int pid=player->id();
+ // Virtual players cannot be deactivated. They will be removed
+ if (player->isVirtual())
+ {
+ systemRemovePlayer(player,true);
+ }
+ else
+ {
+ d->mPlayerList.remove(player);
+ d->mInactivePlayerList.prepend(player);
+ player->setActive(false);
+ }
+ emit signalPlayerLeftGame(player);
+ if (isAdmin())
+ {
+ d->mInactiveIdList.prepend(pid);
+ }
+ return true;
+}
+
+bool KGame::activatePlayer(KPlayer * player)
+{
+ if (!player)
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl;
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemActivatePlayer(player);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer);
+ }
+ return true;
+}
+
+bool KGame::systemActivatePlayer(KPlayer* player)
+{
+ if (!player || player->isActive())
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl;
+
+ d->mInactivePlayerList.remove(player);
+ player->setActive(true);
+ addPlayer(player);
+ if (isAdmin())
+ {
+ d->mInactiveIdList.remove(player->id());
+ }
+ return true;
+}
+
+// -------------------- Properties ---------------------------
+
+void KGame::setMaxPlayers(uint maxnumber)
+{ if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } }
+
+void KGame::setMinPlayers(uint minnumber)
+{ if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } }
+
+uint KGame::minPlayers() const
+{ return d->mMinPlayer.value(); }
+
+int KGame::maxPlayers() const
+{ return d->mMaxPlayer.value(); }
+
+uint KGame::playerCount() const
+{ return d->mPlayerList.count(); }
+
+int KGame::gameStatus() const
+{ return d->mGameStatus.value(); }
+
+bool KGame::isRunning() const
+{ return d->mGameStatus.value() == Run; }
+
+KGamePropertyHandler* KGame::dataHandler() const
+{ return d->mProperties; }
+
+
+KGame::KGamePlayerList* KGame::inactivePlayerList()
+{ return &d->mInactivePlayerList; }
+
+const KGame::KGamePlayerList* KGame::inactivePlayerList() const
+{ return &d->mInactivePlayerList; }
+
+KGame::KGamePlayerList* KGame::playerList()
+{ return &d->mPlayerList; }
+
+const KGame::KGamePlayerList* KGame::playerList() const
+{ return &d->mPlayerList; }
+
+KRandomSequence* KGame::random() const
+{ return d->mRandom; }
+
+
+bool KGame::sendPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender)
+{
+ if (!player)
+ {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return false;
+ }
+ if (!isRunning())
+ {
+ kdError(11001) << k_funcinfo << ": game not running" << endl;
+ return false;
+ }
+
+ kdDebug(11001) << k_funcinfo << ": transmitting playerInput over network" << endl;
+ sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender);
+ return true;
+}
+
+bool KGame::systemPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender)
+{
+ if (!player)
+ {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return false;
+ }
+ if (!isRunning())
+ {
+ kdError(11001) << k_funcinfo << ": game not running" << endl;
+ return false;
+ }
+ kdDebug(11001) << "KGame: Got playerInput from messageServer... sender: " << sender << endl;
+ if (playerInput(msg,player))
+ {
+ playerInputFinished(player);
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo<<": switching off player input"<<endl;
+ // TODO: (MH 03-2003): We need an return option from playerInput so that
+ // the player's is not automatically disabled here
+ if (!player->asyncInput())
+ {
+ player->setTurn(false); // in turn based games we have to switch off input now
+ }
+ }
+ return true;
+}
+
+
+KPlayer * KGame::playerInputFinished(KPlayer *player)
+{
+ kdDebug(11001) << k_funcinfo<<"player input finished for "<<player->id()<<endl;
+ // Check for game over and if not allow the next player to move
+ int gameOver = 0;
+ if (gameSequence())
+ {
+ gameSequence()->setCurrentPlayer(player);
+ }
+ // do not call gameSequence()->checkGameOver() to keep backward compatibility!
+ gameOver = checkGameOver(player);
+ if (gameOver!=0)
+ {
+ if (player)
+ {
+ player->setTurn(false);
+ }
+ setGameStatus(End);
+ emit signalGameOver(gameOver,player,this);
+ }
+ else if (!player->asyncInput())
+ {
+ player->setTurn(false); // in turn based games we have to switch off input now
+ if (gameSequence())
+ {
+ QTimer::singleShot(0,this,SLOT(prepareNext()));
+ }
+ }
+ return player;
+}
+
+// Per default we do not do anything
+int KGame::checkGameOver(KPlayer *player)
+{
+ if (gameSequence())
+ {
+ return gameSequence()->checkGameOver(player);
+ }
+ return 0;
+}
+
+void KGame::setGameSequence(KGameSequence* sequence)
+{
+ delete d->mGameSequence;
+ d->mGameSequence = sequence;
+ if (d->mGameSequence)
+ {
+ d->mGameSequence->setGame(this);
+ }
+}
+
+KGameSequence* KGame::gameSequence() const
+{
+ return d->mGameSequence;
+}
+
+void KGame::prepareNext()
+{
+ if (gameSequence())
+ {
+ // we don't call gameSequence->nextPlayer() to keep old code working
+ nextPlayer(gameSequence()->currentPlayer());
+ }
+}
+
+KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive)
+{
+ if (gameSequence())
+ {
+ return gameSequence()->nextPlayer(last, exclusive);
+ }
+ return 0;
+}
+
+void KGame::setGameStatus(int status)
+{
+ kdDebug(11001) << k_funcinfo << ": GAMESTATUS CHANGED to" << status << endl;
+ if (status==(int)Run && playerCount()<minPlayers())
+ {
+ kdDebug(11001) << k_funcinfo << ": not enough players, pausing game\n" << endl;
+ status=Pause;
+ }
+ d->mGameStatus = status;
+}
+
+void KGame::networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 /*clientID*/)
+{//clientID is unused
+ // message targets a playerobject. If we find it we forward the message to the
+ // player. Otherwise we proceed here and hope the best that the user processes
+ // the message
+
+// kdDebug(11001) << k_funcinfo << ": we="<<(int)gameId()<<" id="<<msgid<<" recv=" << receiver << " sender=" << sender << endl;
+
+
+ // *first* notice the game that something has changed - so no return prevents
+ // this
+ emit signalMessageUpdate(msgid, receiver, sender);
+ if (KGameMessage::isPlayer(receiver))
+ {
+ //kdDebug(11001) << "message id " << msgid << " seems to be for a player ("<<active=p->isActive()<<" recv="<< receiver << endl;
+ KPlayer *p=findPlayer(receiver);
+ if (p && p->isActive())
+ {
+ p->networkTransmission(stream,msgid,sender);
+ return;
+ }
+ if (p)
+ {
+ kdDebug(11001) << "player is here but not active" << endl;
+ }
+ else
+ {
+ kdDebug(11001) << "no player found" << endl;
+ }
+ }
+ // If it is not for a player it is meant for us!!!! Otherwise the
+ // gamenetwork would not have passed the message to us!
+
+ // GameProperties processed
+ if (d->mProperties->processMessage(stream, msgid, sender == gameId()))
+ {
+// kdDebug(11001 ) << "KGame: message taken by property - returning" << endl;
+ return ;
+ }
+
+ switch(msgid)
+ {
+ case KGameMessage::IdSetupGame: // Client: First step in setup game
+ {
+ Q_INT16 v;
+ Q_INT32 c;
+ stream >> v >> c;
+ kdDebug(11001) << " ===================> (Client) " << k_funcinfo << ": Got IdSetupGame ================== " << endl;
+ kdDebug(11001) << "our game id is " << gameId() << " Lib version=" << v << " App Cookie=" << c << endl;
+ // Verify identity of the network partners
+ if (c!=cookie())
+ {
+ kdError(11001) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<<cookie()<<" master="<<c<<endl;
+ sendError(KGameError::Cookie, KGameError::errCookie(cookie(), c));
+ disconnect(); // disconnect from master
+ }
+ else if (v!=KGameMessage::version())
+ {
+ sendError(KGameError::Version, KGameError::errVersion(v));
+ disconnect(); // disconnect from master
+ }
+ else
+ {
+ setupGame(sender);
+ }
+ kdDebug(11001) << "========== (Client) Setup game done\n";
+ }
+ break;
+ case KGameMessage::IdSetupGameContinue: // Master: second step in game setup
+ {
+ kdDebug(11001) << "=====>(Master) " << k_funcinfo << " - IdSetupGameContinue" << endl;
+ setupGameContinue(stream, sender);
+ }
+ break;
+ case KGameMessage::IdActivatePlayer: // Activate Player
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << "Got IdActivatePlayer id=" << id << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemActivatePlayer(findPlayer(id));
+ }
+ }
+ break;
+ case KGameMessage::IdInactivatePlayer: // Inactivate Player
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << "Got IdInactivatePlayer id=" << id << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemInactivatePlayer(findPlayer(id));
+ }
+ }
+ break;
+ case KGameMessage::IdAddPlayer:
+ {
+ kdDebug(11001) << k_funcinfo << ": Got IdAddPlayer" << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ KPlayer *newplayer=0;
+ // We sent the message so the player is already available
+ if (sender==gameId())
+ {
+ kdDebug(11001) << "dequeue previously added player" << endl;
+ newplayer = d->mAddPlayerList.dequeue();
+ }
+ else
+ {
+ newplayer=loadPlayer(stream,true);
+ }
+ systemAddPlayer(newplayer);// the final, local, adding
+ //systemAddPlayer(stream);
+ }
+ }
+ break;
+ case KGameMessage::IdRemovePlayer: // Client should delete player id
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << k_funcinfo << ": Got IdRemovePlayer " << id << endl;
+ KPlayer *p=findPlayer(id);
+ if (p)
+ {
+ // Otherwise the player is already removed
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemRemovePlayer(p,true);
+ }
+ }
+ else
+ {
+ kdWarning(11001) << k_funcinfo << "Cannot find player " << id << endl;
+ }
+ }
+ break;
+ case KGameMessage::IdGameLoad:
+ {
+ kdDebug(11001) << "====> (Client) " << k_funcinfo << ": Got IdGameLoad" << endl;
+ loadgame(stream,true,false);
+ }
+ break;
+ case KGameMessage::IdGameSetupDone:
+ {
+ int cid;
+ stream >> cid;
+ kdDebug(11001) << "====> (CLIENT) " << k_funcinfo << ": Got IdGameSetupDone for client "
+ << cid << " we are =" << gameId() << endl;
+ sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0);
+ }
+ break;
+ case KGameMessage::IdGameConnected:
+ {
+ int cid;
+ stream >> cid;
+ kdDebug(11001) << "====> (ALL) " << k_funcinfo << ": Got IdGameConnected for client "<< cid << " we are =" << gameId() << endl;
+ emit signalClientJoinedGame(cid,this);
+ }
+ break;
+
+ case KGameMessage::IdSyncRandom: // Master forces a new random seed on us
+ {
+ int newseed;
+ stream >> newseed;
+ kdDebug(11001) << "CLIENT: setting random seed to " << newseed << endl;
+ d->mRandom->setSeed(newseed);
+ }
+ break;
+ case KGameMessage::IdDisconnect:
+ {
+ // if we disconnect we *always* start a local game.
+ // this could lead into problems if we just change the message server
+ if (sender != gameId())
+ {
+ kdDebug(11001) << "client " << sender << " leaves game" << endl;
+ return;
+ }
+ kdDebug(11001) << "leaving the game" << endl;
+ // start a new local game
+ // no other client is by default connected to this so this call should be
+ // enough
+ setMaster();
+ }
+ break;
+ default:
+ {
+ if (msgid < KGameMessage::IdUser)
+ {
+ kdError(11001) << "incorrect message id " << msgid << " - emit anyway"
+ << endl;
+ }
+ kdDebug(11001) << k_funcinfo << ": User data msgid " << msgid << endl;
+ emit signalNetworkData(msgid - KGameMessage::IdUser,((QBuffer*)stream.device())->readAll(),receiver,sender);
+ }
+ break;
+ }
+
+}
+
+// called by the IdSetupGameContinue Message - MASTER SIDE
+// Here the master needs to decide which players can take part at the game
+// and which will be deactivated
+void KGame::setupGameContinue(QDataStream& stream, Q_UINT32 sender)
+{
+ KPlayer *player;
+ Q_INT32 cnt;
+ int i;
+ stream >> cnt;
+
+ QValueList<int> inactivateIds;
+
+ KGamePlayerList newPlayerList;
+ newPlayerList.setAutoDelete(true);
+ for (i=0;i<cnt;i++)
+ {
+ player=loadPlayer(stream,true);
+ kdDebug(11001) << " Master got player " << player->id() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << " from sender " << sender << endl;
+ if (KGameMessage::rawGameId(player->id()) != sender)
+ {
+ kdError(11001) << "Client tries to add player with wrong game id - cheat possible" << endl;
+ }
+ else
+ {
+ newPlayerList.append(player);
+ kdDebug(11001) << " newplayerlist appended " << player->id() << endl;
+ }
+ }
+
+ newPlayersJoin(playerList(),&newPlayerList,inactivateIds);
+
+
+ kdDebug(11001) << " Master calculates how many players to activate client has cnt=" << cnt << endl;
+ kdDebug(11001) << " The game has " << playerCount() << " active players" << endl;
+ kdDebug(11001) << " The user deactivated "<< inactivateIds.count() << " player already " << endl;
+ kdDebug(11001) << " MaxPlayers for this game is " << maxPlayers() << endl;
+
+ // Do we have too many players? (After the programmer disabled some?)
+ // MH: We cannot use have player here as it CHANGES in the loop
+ // int havePlayers = cnt+playerCount()-inactivateIds.count();
+ kdDebug(11001) << " havePlayers " << cnt+playerCount()-inactivateIds.count() << endl;
+ while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count()))
+ {
+ kdDebug(11001) << " Still to deacticvate "
+ << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers()
+ << endl;
+ KPlayer *currentPlayer=0;
+ int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list
+ // find lowest network priority which is not yet in the newPlayerList
+ // do this for the new players
+ for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() )
+ {
+ // Already in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ if (player->networkPriority()<currentPriority)
+ {
+ currentPriority=player->networkPriority();
+ currentPlayer=player;
+ }
+ }
+
+ // find lowest network priority which is not yet in the newPlayerList
+ // Do this for the network players
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ // Already in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ if (player->networkPriority()<currentPriority)
+ {
+ currentPriority=player->networkPriority();
+ currentPlayer=player;
+ }
+ }
+
+ // add it to inactivateIds
+ if (currentPlayer)
+ {
+ kdDebug(11001) << "Marking player " << currentPlayer->id() << " for inactivation" << endl;
+ inactivateIds.append(currentPlayer->id());
+ }
+ else
+ {
+ kdError(11001) << "Couldn't find a player to dectivate..That is not so good..." << endl;
+ break;
+ }
+ }
+
+ kdDebug(11001) << "Alltogether deactivated " << inactivateIds.count() << " players" << endl;
+
+ QValueList<int>::Iterator it;
+ for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
+ {
+ int pid=*it;
+ kdDebug(11001) << " pid=" << pid << endl;
+ }
+
+ // Now deactivate the network players from the inactivateId list
+ //QValueList<int>::Iterator it;
+ for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
+ {
+ int pid=*it;
+ if (KGameMessage::rawGameId(pid) == sender)
+ {
+ continue; // client's player
+ }
+ kdDebug(11001) << " -> the network needs to deactivate " << pid <<endl;
+ player=findPlayer(pid);
+ if (player)
+ {
+ // We have to make REALLY sure that the player is gone. With any policy
+ systemInactivatePlayer(player);
+ if (policy()!=PolicyLocal)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
+ }
+ }
+ else
+ {
+ kdError(11001) << " We should deactivate a player, but cannot find it...not good." << endl;
+ }
+ }
+
+ // Now send out the player list which the client can activate
+ for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() )
+ {
+ kdDebug(11001) << " newplayerlist contains " << player->id() << endl;
+ // Only activate what is not in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ kdDebug(11001) << " -> the client can ******** reactivate ******** " << player->id() << endl;
+ sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender);
+ }
+
+ // Save the game over the network
+ QByteArray bufferS;
+ QDataStream streamS(bufferS,IO_WriteOnly);
+ // Save game over netowrk and save players
+ savegame(streamS,true,true);
+ sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender);
+
+
+ // Only to the client first , as the client will add players
+ sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender);
+}
+
+// called by the IdSetupGame Message - CLIENT SIDE
+// Client needs to prepare for network transfer
+void KGame::setupGame(Q_UINT32 sender)
+{
+ QByteArray bufferS;
+ QDataStream streamS(bufferS,IO_WriteOnly);
+
+ // Deactivate all players
+ KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes
+ Q_INT32 cnt=mTmpList.count();
+ kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl;
+
+ streamS << cnt;
+
+ QPtrListIterator<KPlayer> it(mTmpList);
+ KPlayer *player;
+ while (it.current())
+ {
+ player=it.current();
+ systemInactivatePlayer(player);
+ // Give the new game id to all players (which are inactivated now)
+ player->setId(KGameMessage::createPlayerId(player->id(),gameId()));
+
+ // Save it for the master to decide what to do
+ savePlayer(streamS,player);
+
+ ++it;
+ --cnt;
+ }
+ if (d->mPlayerList.count() > 0 || cnt!=0)
+ {
+ kdFatal(11001) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <<cnt << endl;
+ }
+
+ sendSystemMessage(streamS,KGameMessage::IdSetupGameContinue,sender);
+}
+
+// unused by KGame
+void KGame::syncRandom()
+{
+ int newseed=(int)d->mRandom->getLong(65535);
+ sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast
+ d->mRandom->setSeed(newseed);
+}
+
+void KGame::Debug()
+{
+ KGameNetwork::Debug();
+ kdDebug(11001) << "------------------- KGAME -------------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "uniquePlayer " << d->mUniquePlayerNumber << endl;
+ kdDebug(11001) << "gameStatus " << gameStatus() << endl;
+ kdDebug(11001) << "MaxPlayers : " << maxPlayers() << endl;
+ kdDebug(11001) << "NoOfPlayers : " << playerCount() << endl;
+ kdDebug(11001) << "NoOfInactive: " << d->mInactivePlayerList.count() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+void KGame::slotClientConnected(Q_UINT32 clientID)
+{
+ if (isAdmin())
+ {
+ negotiateNetworkGame(clientID);
+ }
+}
+
+void KGame::slotServerDisconnected() // Client side
+{
+ kdDebug(11001) << "======= SERVER DISCONNECT ======="<<endl;
+ kdDebug(11001) << "+++ (CLIENT)++++++++" << k_funcinfo << ": our GameID="<<gameId() << endl;
+
+ int oldgamestatus=gameStatus();
+
+ KPlayer *player;
+ KGamePlayerList removeList;
+ kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl;
+ kdDebug(11001) << "Inactive Playerlist of client=" << d->mInactivePlayerList.count() << " count" << endl;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ // TODO: CHECK: id=0, could not connect to server in the first place??
+ if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0)
+ {
+ kdDebug(11001) << "Player " << player->id() << " belongs to a removed game" << endl;
+ removeList.append(player);
+ }
+ }
+
+ for ( player=removeList.first(); player != 0; player=removeList.next() )
+ {
+ bool remove = true;
+ emit signalReplacePlayerIO(player, &remove);
+ if (remove)
+ {
+ kdDebug(11001) << " ---> Removing player " << player->id() << endl;
+ systemRemovePlayer(player,true); // no network necessary
+ }
+ }
+
+ setMaster();
+ kdDebug(11001) << " our game id is after setMaster " << gameId() << endl;
+
+ KGamePlayerList mReList(d->mInactivePlayerList);
+ for ( player=mReList.first(); player != 0; player=mReList.next() )
+ {
+ // TODO ?check for priority? Sequence should be ok
+ if ((int)playerCount()<maxPlayers() || maxPlayers()<0)
+ {
+ systemActivatePlayer(player);
+ }
+ }
+ kdDebug(11001) << " Players activated player-cnt=" << playerCount() << endl;
+
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ int oldid=player->id();
+ d->mUniquePlayerNumber++;
+ player->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber,gameId()));
+ kdDebug(11001) << "Player id " << oldid <<" changed to " << player->id() << " as we are now local" << endl;
+ }
+ // TODO clear inactive lists ?
+ Debug();
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ player->Debug();
+ }
+ kdDebug(11001) << "+++++++++++" << k_funcinfo << " DONE=" << endl;
+ emit signalClientLeftGame(0,oldgamestatus,this);
+}
+
+void KGame::slotClientDisconnected(Q_UINT32 clientID,bool /*broken*/) // server side
+{
+ kdDebug(11001) << "++++(SERVER)+++++++" << k_funcinfo << " clientId=" << clientID << endl;
+
+ int oldgamestatus=gameStatus();
+
+ KPlayer *player;
+ KGamePlayerList removeList;
+ kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ if (KGameMessage::rawGameId(player->id())==clientID)
+ {
+ kdDebug(11001) << "Player " << player->id() << " belongs to the removed game" << endl;
+ removeList.append(player);
+ }
+ }
+
+ for ( player=removeList.first(); player != 0; player=removeList.next() )
+ {
+ // try to replace the KGameIO first
+ bool remove = true;
+ emit signalReplacePlayerIO(player, &remove);
+ if (remove) {
+ // otherwise (no new KGameIO) remove the player
+ kdDebug(11001) << " ---> Removing player " << player->id() << endl;
+ removePlayer(player,0);
+ }
+ }
+
+ // Now add inactive players - sequence should be ok
+ // TODO remove players from removed game
+ for (unsigned int idx=0;idx<d->mInactiveIdList.count();idx++)
+ {
+ QValueList<int>::Iterator it1 = d->mInactiveIdList.at(idx);
+ player = findPlayer(*it1);
+ if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(*it1) != clientID)
+ {
+ activatePlayer(player);
+ }
+ }
+ emit signalClientLeftGame(clientID,oldgamestatus,this);
+}
+
+
+// -------------------- Synchronisation -----------------------
+
+// this initializes a newly connected client.
+// we send the number of players (including type) as well as game status and
+// properties to the client. After the initialization has been completed both
+// clients should have the same status (ie players, properties, etc)
+void KGame::negotiateNetworkGame(Q_UINT32 clientID)
+{
+ kdDebug(11001) << "===========================" << k_funcinfo << ": clientID=" << clientID << " =========================== "<< endl;
+ if (!isAdmin())
+ {
+ kdError(11001) << k_funcinfo << ": Serious WARNING..only gameAdmin should call this" << endl;
+ return ;
+ }
+
+ QByteArray buffer;
+ QDataStream streamGS(buffer,IO_WriteOnly);
+
+ // write Game setup specific data
+ //streamGS << (Q_INT32)maxPlayers();
+ //streamGS << (Q_INT32)minPlayers();
+
+ // send to the newly connected client *only*
+ Q_INT16 v=KGameMessage::version();
+ Q_INT32 c=cookie();
+ streamGS << v << c;
+ sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID);
+}
+
+bool KGame::sendGroupMessage(const QByteArray &msg, int msgid, Q_UINT32 sender, const QString& group)
+{
+// AB: group must not be i18n'ed!! we should better use an id for group and use
+// a groupName() for the name // FIXME
+ KPlayer *player;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ if (player && player->group()==group)
+ {
+ sendMessage(msg,msgid,player->id(), sender);
+ }
+ }
+ return true;
+}
+
+bool KGame::sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group)
+{ return sendGroupMessage(((QBuffer*)msg.device())->buffer(), msgid, sender, group); }
+
+bool KGame::sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ stream << msg;
+ return sendGroupMessage(stream, msgid, sender, group);
+}
+
+bool KGame::addProperty(KGamePropertyBase* data)
+{ return dataHandler()->addProperty(data); }
+
+bool KGame::sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId)
+{ return sendSystemMessage(s, msgid, playerId); }
+
+void KGame::sendProperty(int msgid, QDataStream& stream, bool* sent)
+{
+ bool s = sendSystemMessage(stream, msgid);
+ if (s)
+ {
+ *sent = true;
+ }
+}
+
+void KGame::emitSignal(KGamePropertyBase *me)
+{
+ emit signalPropertyChanged(me,this);
+}
+
+KGamePropertyBase* KGame::findProperty(int id) const
+{ return d->mProperties->find(id); }
+
+KGame::GamePolicy KGame::policy() const
+{
+ return d->mPolicy;
+}
+void KGame::setPolicy(GamePolicy p,bool recursive)
+{
+ // Set KGame policy
+ d->mPolicy=p;
+ if (recursive)
+ {
+ // Set all KGame property policy
+ dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+
+ // Set all KPLayer (active or inactive) property policy
+ for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it)
+ {
+ it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+ }
+ for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it)
+ {
+ it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+ }
+ }
+}
+
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgame.h b/libkdegames/kgame/kgame.h
new file mode 100644
index 00000000..37d8d974
--- /dev/null
+++ b/libkdegames/kgame/kgame.h
@@ -0,0 +1,932 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAME_H_
+#define __KGAME_H_
+
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qvaluelist.h>
+
+#include "kgamenetwork.h"
+#include <kdemacros.h>
+class KRandomSequence;
+
+class KPlayer;
+class KGamePropertyBase;
+class KGamePropertyHandler;
+class KGameSequence;
+
+class KGamePrivate;
+
+/**
+ * @short The main KDE game object
+ *
+ * The KGame class is the central game object. A game basically
+ * consists of following features:
+ * - Player handling (add, remove,...)
+ * - Game status (end,start,pause,...)
+ * - load/save
+ * - Move (and message) handling
+ * - nextPlayer and gameOver()
+ * - Network connection (for KGameNetwork)
+ *
+ * Example:
+ * \code
+ * KGame *game=new KGame;
+ * \endcode
+ *
+ *
+ * @author Martin Heni <martin@heni-online.de>
+ *
+ */
+class KDE_EXPORT KGame : public KGameNetwork
+{
+ Q_OBJECT
+
+public:
+ typedef QPtrList<KPlayer> KGamePlayerList;
+
+ /**
+ * The policy of the property. This can be PolicyClean (setVale uses
+ * send), PolicyDirty (setValue uses changeValue) or
+ * PolicyLocal (setValue uses setLocal).
+ *
+ * A "clean" policy means that the property is always the same on every
+ * client. This is achieved by calling send which actually changes
+ * the value only when the message from the MessageServer is received.
+ *
+ * A "dirty" policy means that as soon as setValue is called the
+ * property is changed immediately. And additionally sent over network.
+ * This can sometimes lead to bugs as the other clients do not
+ * immediately have the same value. For more information see
+ * changeValue.
+ *
+ * PolicyLocal means that a KGameProperty behaves like ever
+ * "normal" variable. Whenever setValue is called (e.g. using "=")
+ * the value of the property is changes immediately without sending it
+ * over network. You might want to use this if you are sure that all
+ * clients set the property at the same time.
+ **/
+ enum GamePolicy
+ {
+ PolicyUndefined = 0,
+ PolicyClean = 1,
+ PolicyDirty = 2,
+ PolicyLocal = 3
+ };
+
+ /**
+ * Create a KGame object. The cookie is used to identify your
+ * game in load/save and network operations. Change this between
+ * games.
+ */
+ KGame(int cookie=42,QObject* parent=0);
+
+ /**
+ * Destructs the game
+ */
+ virtual ~KGame();
+
+ /**
+ * Gives debug output of the game status
+ */
+ virtual void Debug();
+
+ /**
+ * Game status - Use this to Control the game flow.
+ * The KGame e.g. sets the status to Pause when you have
+ * less player than the minimum amount
+ */
+ enum GameStatus
+ {
+ Init = 0,
+ Run = 1,
+ Pause = 2,
+ End = 3,
+ Abort = 4,
+ SystemPause = 5,
+ Intro = 6,
+ UserStatus = 7
+ };
+
+ // Properties
+ /**
+ * Returns a list of all active players
+ *
+ * @return the list of players
+ */
+ KGamePlayerList *playerList();
+
+ /**
+ * The same as @ref playerList but returns a const pointer.
+ **/
+ const KGamePlayerList *playerList() const;
+
+ /**
+ * Returns a list of all inactive players
+ * @return the list of players
+ */
+ KGamePlayerList *inactivePlayerList();
+
+ /**
+ * The same as @ref inactivePlayerList but returns a const pointer.
+ **/
+ const KGamePlayerList *inactivePlayerList() const;
+
+ /**
+ * Returns a pointer to the game's KRandomSequence. This sequence is
+ * identical for all network players!
+ * @return KRandomSequence pointer
+ */
+ KRandomSequence *random() const;
+
+ /**
+ * @return The KGameSequence object that is currently in use.
+ * @see setGameSequence
+ **/
+ KGameSequence *gameSequence() const;
+
+ /**
+ * Is the game running
+ * @return true/false
+ */
+ bool isRunning() const;
+
+ // Player handling
+ /**
+ * Returns the player object for a given player id
+ * @param id Player id
+ * @return player object
+ */
+ KPlayer *findPlayer(Q_UINT32 id) const;
+
+ /**
+ * Set a new @ref KGameSequence to control player management. By default
+ * KGame uses a normal @ref KGameSequence object. You might want to subclass
+ * that and provide your own object.
+ *
+ * The previous sequence will get deleted.
+ * @param sequence The new game sequence object. KGame takes ownership and
+ * will delete it on destruction!
+ **/
+ void setGameSequence(KGameSequence* sequence);
+
+ /**
+ * Note that KPlayer::save must be implemented properly, as well as
+ * KPlayer::rtti
+ * This will only send a message to all clients. The player is _not_ added
+ * directly!
+ * See also playerInput which will be called as soon as the
+ * player really has been added.
+ *
+ * Note that an added player will first get into a "queue" and won't be in
+ * the game. It will be added to the game as soon as systemAddPlayer is
+ * called what will happen as soon as IdAddPlayer is received.
+ *
+ * Note: you probably want to connect to signalPlayerJoinedGame for
+ * further initialization!
+ * @param newplayer The player you want to add. KGame will send a message to
+ * all clients and add the player using systemAddPlayer
+ **/
+ void addPlayer(KPlayer* newplayer);
+
+ /**
+ * Sends a message over the network, msgid=IdRemovePlayer.
+ *
+ * As soon as this message is received by networkTransmission
+ * systemRemovePlayer is called and the player is removed.
+ **/
+ //AB: TODO: make sendMessage to return if the message will be able to be
+ //sent, eg if a socket is connected, etc. If sendMessage returns false
+ //remove the player directly using systemRemovePlayer
+ bool removePlayer(KPlayer * player) { return removePlayer(player, 0); }
+
+ /**
+ * Called by the destructor of KPlayer to remove itself from the game
+ *
+ **/
+ void playerDeleted(KPlayer * player);
+
+ /**
+ * sends activate player: internal use only?
+ */
+ bool activatePlayer(KPlayer *player);
+
+ /**
+ * sends inactivate player: internal use only?
+ */
+ bool inactivatePlayer(KPlayer *player);
+
+ /**
+ * Set the maximal number of players. After this is
+ * reached no more players can be added. You must be ADMIN to call this (@see
+ * isAdmin).
+ * @param maxnumber maximal number of players
+ */
+ void setMaxPlayers(uint maxnumber);
+
+ /**
+ * What is the maximal number of players?
+ * @return maximal number of players
+ */
+ int maxPlayers() const;
+
+ /**
+ * Set the minimal number of players. A game can not be started
+ * with less player resp. is paused when already running. You must be ADMIN
+ * to call this (see @ref isAdmin)!
+ * @param minnumber minimal number of players
+ */
+ void setMinPlayers(uint minnumber);
+
+ /**
+ * What is the minimal number of players?
+ * @return minimal number of players
+ */
+ uint minPlayers() const;
+
+ /**
+ * Returns how many players are plugged into the game
+ * @return number of players
+ */
+ uint playerCount() const;
+
+ /**
+ * @deprecated
+ * Use @ref KGameSequence::nextPlayer instead
+ **/
+ virtual KPlayer * nextPlayer(KPlayer *last,bool exclusive=true);
+
+ // Input events
+ /**
+ * Called by KPlayer to send a player input to the
+ * KMessageServer.
+ **/
+ virtual bool sendPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0);
+
+ /**
+ * Called when a player input arrives from KMessageServer.
+ *
+ * Calls prepareNext (using QTimer::singleShot) if gameOver()
+ * returns 0. This function should normally not be used outside KGame.
+ * It could be made non-virtual,protected in a later version. At the
+ * moment it is a virtual function to give you more control over KGame.
+ *
+ * For documentation see playerInput.
+ **/
+ virtual bool systemPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0);
+
+ /**
+ * This virtual function is called if the KGame needs to create a new player.
+ * This happens only over a network and with load/save. Doing nothing
+ * will create a default KPlayer. If you want to have your own player
+ * you have to create one with the given rtti here.
+ * Note: If your game uses a player class derived from KPlayer you MUST
+ * override this function and create your player here. Otherwise the
+ * game will crash.
+ * Example:
+ * \code
+ * KPlayer *MyGame::createPlayer(int rtti,int io,bool isvirtual)
+ * {
+ * KPlayer *player=new MyPlayer;
+ * if (!isvirtual) // network player ?
+ * {
+ * // Define something like this to add the IO modules
+ * createIO(player,(KGameIO::IOMode)io);
+ * }
+ * return player;
+ * }
+ * \endcode
+ *
+ * @param rtti is the type of the player (0 means default KPlayer)
+ * @param io is the 'or'ed rtti of the KGameIO's
+ * @param isvirtual true if player is virtual
+ */
+ virtual KPlayer *createPlayer(int rtti,int io,bool isvirtual);
+
+ // load/save
+ /**
+ * Load a saved game, from file OR network. This function has
+ * to be overwritten or you need to connect to the load signal
+ * if you have game data other than KGameProperty.
+ * For file load you should reset() the game before any load attempt
+ * to make sure you load into an clear state.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param reset - shall the game be reset before loading
+ *
+ * @return true?
+ */
+ virtual bool load(QDataStream &stream,bool reset=true);
+
+ /**
+ * Same as above function but with different parameters
+ *
+ * @param filename - the filename of the file to be opened
+ * @param reset - shall the game be reset before loading
+ *
+ * @return true?
+ **/
+ virtual bool load(QString filename,bool reset=true);
+
+ /**
+ * Save a game to a file OR to network. Otherwise the same as
+ * the load function
+ *
+ * @param stream a data stream to load the game from
+ * @param saveplayers If true then all players wil be saved too
+ *
+ * @return true?
+ */
+ virtual bool save(QDataStream &stream,bool saveplayers=true);
+
+ /**
+ * Same as above function but with different parameters
+ *
+ * @param filename the filename of the file to be saved
+ * @param saveplayers If true then all players wil be saved too
+ *
+ * @return true?
+ **/
+ virtual bool save(QString filename,bool saveplayers=true);
+
+ /**
+ * Resets the game, i.e. puts it into a state where everything
+ * can be started from, e.g. a load game
+ * Right now it does only need to delete all players
+ *
+ * @return true on success
+ */
+ virtual bool reset();
+
+
+ // Game sequence
+ /**
+ * returns the game status, ie running,pause,ended,...
+ *
+ * @return game status
+ */
+ int gameStatus() const;
+
+ /**
+ * sets the game status
+ *
+ * @param status the new status
+ */
+ void setGameStatus(int status);
+
+ /**
+ * docu: see KPlayer
+ **/
+ bool addProperty(KGamePropertyBase* data);
+
+ /**
+ * This is called by KPlayer::sendProperty only! Internal function!
+ **/
+ bool sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId);
+
+ /**
+ * This function allows to find the pointer to a player
+ * property when you know it's id
+ */
+ KGamePropertyBase* findProperty(int id) const;
+
+ /**
+ * Changes the consistency policy of a property. The
+ * GamePolicy is one of PolicyClean (default), PolicyDirty or PolicyLocal.
+ *
+ * It is up to you to decide how you want to work.
+ **/
+ void setPolicy(GamePolicy p,bool recursive=true);
+
+ /**
+ * @return The default policy of the property
+ **/
+ GamePolicy policy() const;
+
+ /**
+ * See KGameNetwork::sendMessage
+ *
+ * Send a network message msg with a given message ID msgid to all players of
+ * a given group (see KPlayer::group)
+ * @param msg the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message
+ * @param sender the id of the sender
+ * @param group the group of the receivers
+ * @return true if worked
+ */
+ bool sendGroupMessage(const QByteArray& msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(int msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group);
+
+ /**
+ * This will either forward an incoming message to a specified player
+ * (see KPlayer::networkTransmission) or
+ * handle the message directly (e.g. if msgif==IdRemovePlayer it will remove
+ * the (in the stream) specified player). If both is not possible (i.e. the
+ * message is user specified data) the signal signalNetworkData is
+ * emitted.
+ *
+ * This emits signalMessageUpdate <em>before</em> doing anything with
+ * the message. You can use this signal when you want to be notified about
+ * an update/change.
+ * @param msgid Specifies the kind of the message. See messages.txt for
+ * further information
+ * @param stream The message that is being sent
+ * @param receiver The is of the player this message is for. 0 For broadcast.
+ * @param sender
+ * @param clientID the client from which we received the transmission - hardly used
+ **/
+ virtual void networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 clientID);
+
+ /**
+ * Returns a pointer to the KGame property handler
+ **/
+ KGamePropertyHandler* dataHandler() const;
+
+protected slots:
+ /**
+ * Called by KGamePropertyHandler only! Internal function!
+ **/
+ void sendProperty(int msgid, QDataStream& stream, bool* sent);
+
+ /**
+ * Called by KGamePropertyHandler only! Internal function!
+ **/
+ void emitSignal(KGamePropertyBase *me);
+
+ /**
+ * @deprecated
+ * Use KGameSequence::prepareNext() instead
+ **/
+ virtual void prepareNext();
+
+
+ /**
+ * Calls negotiateNetworkGame()
+ * See KGameNetwork::signalClientConnected
+ **/
+ void slotClientConnected(Q_UINT32 clientId);
+
+ /**
+ * This slot is called whenever the connection to a client is lost (ie the
+ * signal KGameNetwork::signalClientDisconnected is emitted) and will remove
+ * the players from that client.
+ * @param clientId The client the connection has been lost to
+ * @param broken (ignore this - not used)
+ **/
+ void slotClientDisconnected(Q_UINT32 clientId,bool broken);
+
+ /**
+ * This slot is called whenever the connection to the server is lost (ie the
+ * signal KGameNetwork::signalConnectionBroken is emitted) and will
+ * switch to local game mode
+ **/
+ void slotServerDisconnected();
+
+signals:
+ /**
+ * When a client disconnects from the game usually all players from that
+ * client are removed. But if you use completely the KGame structure you
+ * probably don't want this. You just want to replace the KGameIO of the
+ * (human) player by a computer KGameIO. So this player continues game but
+ * is from this point on controlled by the computer.
+ *
+ * You achieve this by connecting to this signal. It is emitted as soon as a
+ * client disconnects on <em>all</em> other clients. Make sure to add a new
+ * KGameIO only once! you might want to use @ref isAdmin for this. If you
+ * added a new KGameIO set *remove=false otherwise the player is completely
+ * removed.
+ * @param player The player that is about to be removed. Add your new
+ * KGameIO here - but only on <em>one</em> client!
+ * @param remove Set this to FALSE if you don't want this player to be
+ * removed completely.
+ **/
+ void signalReplacePlayerIO(KPlayer* player, bool* remove);
+
+ /**
+ * The game will be loaded from the given stream. Load from here
+ * the data which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * This signal is emitted <em>before</em> the players are loaded by
+ * KGame. See also signalLoad
+ *
+ * You must load <em>exactly</em> the same data from the stream that you have saved
+ * in signalSavePrePlayers. Otherwise player loading will not work
+ * anymore.
+ *
+ * @param stream the load stream
+ */
+ void signalLoadPrePlayers(QDataStream &stream);
+
+ /**
+ * The game will be loaded from the given stream. Load from here
+ * the data which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * @param stream the load stream
+ */
+ void signalLoad(QDataStream &stream);
+
+ /**
+ * The game will be saved to the given stream. Fill this with data
+ * which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * This signal is emitted <em>before</em> the players are saved by
+ * KGame. See also signalSave
+ *
+ * If you can choose between signalSavePrePlayers and signalSave then
+ * better use signalSave
+ *
+ * @param stream the save stream
+ **/
+ void signalSavePrePlayers(QDataStream &stream);
+
+ /**
+ * The game will be saved to the given stream. Fill this with data
+ * which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * @param stream the save stream
+ */
+ void signalSave(QDataStream &stream);
+
+ /**
+ * Is emmited if a game with a different version cookie is loaded.
+ * Normally this should result in an error. But maybe you do support
+ * loading of older game versions. Here would be a good place to do a
+ * conversion.
+ *
+ * @param stream - the load stream
+ * @param network - true if this is a network connect. False for load game
+ * @param cookie - the saved cookie. It differs from KGame::cookie()
+ * @param result - set this to true if you managed to load the game
+ */
+ void signalLoadError(QDataStream &stream,bool network,int cookie, bool &result);
+
+ /**
+ * We got an user defined update message. This is usually done
+ * by a sendData in a inherited KGame Object which defines its
+ * own methods and has to syncronise them over the network.
+ * Reaction to this is usually a call to a KGame function.
+ */
+ void signalNetworkData(int msgid,const QByteArray& buffer, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * We got an network message. this can be used to notify us that something
+ * changed. What changed can be seen in the message id. Whether this is
+ * the best possible method to do this is unclear...
+ */
+ void signalMessageUpdate(int msgid,Q_UINT32 receiver,Q_UINT32 sender);
+
+ /**
+ * a player left the game because of a broken connection or so!
+ *
+ * Note that when this signal is emitted the player is not part of @ref
+ * playerList anymore but the pointer is still valid. You should do some
+ * final cleanups here since the player is usually deleted after the signal
+ * is emitted.
+ *
+ * @param player the player who left the game
+ */
+ void signalPlayerLeftGame(KPlayer *player);
+
+ /**
+ * a player joined the game
+ *
+ * @param player the player who joined the game
+ */
+ void signalPlayerJoinedGame(KPlayer *player);
+
+
+ /**
+ * This signal is emmited if a player property changes its value and
+ * the property is set to notify this change
+ */
+ void signalPropertyChanged(KGamePropertyBase *property, KGame *me);
+
+ /**
+ * Is emitted after a call to gameOver() returns a non zero
+ * return code. This code is forwarded to this signal as 'status'.
+ *
+ * @param status the return code of gameOver()
+ * @param current the player who did the last move
+ * @param me a pointer to the KGame object
+ */
+ void signalGameOver(int status, KPlayer *current, KGame *me);
+
+ /**
+ * Is emmited after a client is successfully connected to the game.
+ * The client id is the id of the new game client. An easy way to
+ * check whether that's us is
+ * \code
+ * if (clientid==gameid()) .. // we joined
+ * else ... // someone joined the game
+ * \endcode
+ * @param clientid - The id of the new client
+ * @param me - our game pointer
+ */
+ void signalClientJoinedGame(Q_UINT32 clientid,KGame *me);
+
+ /**
+ * This signal is emitted after a network partner left the
+ * game (either by a broken connection or voluntarily).
+ * All changes to the network players have already be done.
+ * If there are not enough players left, the game might have
+ * been paused. To check this you get the old gamestatus
+ * before the disconnection as argument here. The id of the
+ * client who left the game allows to distinguish who left the
+ * game. If it is 0, the server disconnected and you were a client
+ * which has been switched back to local play.
+ * You can use this signal to, e.g. set some menues back to local
+ * player when they were network before.
+ *
+ * @param clientID - 0:server left, otherwise the client who left
+ * @param oldgamestatus - the gamestatus before the loss
+ * @param me - our game pointer
+ **/
+ void signalClientLeftGame(int clientID,int oldgamestatus,KGame *me);
+
+
+protected:
+ /**
+ * A player input occurred. This is the most important function
+ * as the given message will contain the current move made by
+ * the given player.
+ * Note that you HAVE to overwrite this function. Otherwise your
+ * game makes no sense at all.
+ * Generally you have to return TRUE in this function. Only then
+ * the game sequence is proceeded by calling @ref playerInputFinished
+ * which in turn will check for game over or the next player
+ * However, if you have a delayed move, because you e.g. move a
+ * card or a piece you want to return FALSE to pause the game sequence
+ * and then manually call @ref playerInputFinished to resume it.
+ * Example:
+ * \code
+ * bool MyClass::playerInput(QDataStream &msg,KPlayer *player)
+ * {
+ * Q_INT32 move;
+ * msg >> move;
+ * kdDebug() << " Player " << player->id() << " moved to " << move <<
+ * endl;
+ * return true;
+ * }
+ * \endcode
+ *
+ * @param msg the move message
+ * @param player the player who did the move
+ * @return true - input ready, false: input manual
+ */
+ virtual bool playerInput(QDataStream &msg,KPlayer *player)=0;
+
+
+ /**
+ * Called after the player input is processed by the game. Here the
+ * checks for game over and nextPlayer (in the case of turn base games)
+ * are processed.
+ * Call this manually if you have a delayed move, i.e. your playerInput
+ * function returns FALSE. If it returns true you need not do anything
+ * here.
+ *
+ * @return the current player
+ *
+ **/
+ KPlayer *playerInputFinished(KPlayer *player);
+
+
+ /**
+ * This virtual function can be overwritten for your own player management.
+ * It is called when a new game connects to an existing network game or
+ * to the network master. In case you do not want all players of both games
+ * to be present in the new network game, you can deactivate players here.
+ * This is of particular importance if you have a game with fixed number of
+ * player like e.g. chess. A network connect needs to disable one player of
+ * each game to make sense.
+ *
+ * Not overwriting this function will activate a default behaviour which
+ * will deactivate players until the @ref maxPlayers() numebr is reached
+ * according to the KPlayer::networkPriority() value. Players with a low
+ * value will be kicked out first. With equal priority players of the new
+ * client will leave first. This means, not setting this value and not
+ * overwriting this function will never allow a chess game to add client
+ * players!!!
+ * On the other hand setting one player of each game to a networkPriorty of
+ * say 10, already does most of the work for you.
+ *
+ * The parameters of this function are the playerlist of the network game,
+ * which is @ref playerList(). The second argument is the player list of
+ * the new client who wants to join and the third argument serves as return
+ * parameter. All <em>player ID's</em> which are written into this list
+ * will be <em>removed</em> from the created game. You do this by an
+ * \code
+ * inactivate.append(player->id());
+ * \endcode
+ *
+ * @param oldplayer - the list of the network players
+ * @param newplayer - the list of the client players
+ * @param inactivate - the value list of ids to be deactivated
+ *
+ **/
+ virtual void newPlayersJoin(KGamePlayerList *oldplayer,
+ KGamePlayerList *newplayer,
+ QValueList<int> &inactivate) {
+ Q_UNUSED( oldplayer );
+ Q_UNUSED( newplayer );
+ Q_UNUSED( inactivate );
+ }
+
+ /**
+ * Save the player list to a stream. Used for network game and load/save.
+ * Can be overwritten if you know what you are doing
+ *
+ * @param stream is the stream to save the player ot
+ * @param list the optional list is the player list to be saved, default is playerList()
+ *
+ **/
+ void savePlayers(QDataStream &stream,KGamePlayerList *list=0);
+
+ /**
+ * Prepare a player for being added. Put all data about a player into the
+ * stream so that it can be sent to the KGameCommunicationServer using
+ * addPlayer (e.g.)
+ *
+ * This function ensures that the code for adding a player is the same in
+ * addPlayer as well as in negotiateNetworkGame
+ * @param stream is the stream to add the player
+ * @param player The player to add
+ **/
+ void savePlayer(QDataStream& stream,KPlayer* player);
+
+ /**
+ * Load the player list from a stream. Used for network game and load/save.
+ * Can be overwritten if you know what you are doing
+ *
+ * @param stream is the stream to save the player to
+ * @param isvirtual will set the virtual flag true/false
+ *
+ **/
+ KPlayer *loadPlayer(QDataStream& stream,bool isvirtual=false);
+
+
+ /**
+ * inactivates player. Use @ref inactivatePlayer instead!
+ */
+ bool systemInactivatePlayer(KPlayer *player);
+
+ /**
+ * activates player. Use @ref activatePlayer instead!
+ */
+ bool systemActivatePlayer(KPlayer *player);
+
+ /**
+ * Adds a player to the game
+ *
+ * Use @ref addPlayer to send @ref KGameMessage::IdAddPlayer. As soon as
+ * this Id is received this function is called, where the player (see @ref
+ * KPlayer::rtti) is added as well as its properties (see @ref KPlayer::save
+ * and @ref KPlayer::load)
+ *
+ * This method calls the overloaded @ref systemAddPlayer with the created
+ * player as argument. That method will really add the player.
+ * If you need to do some changes to your newly added player just connect to
+ * @ref signalPlayerJoinedGame
+ */
+
+ /**
+ * Finally adds a player to the game and therefore to the list.
+ **/
+ void systemAddPlayer(KPlayer* newplayer);
+
+ /**
+ * Removes a player from the game
+ *
+ * Use removePlayer to send KGameMessage::IdRemovePlayer. As soon
+ * as this Id is received systemRemovePlayer is called and the player is
+ * removed directly.
+ **/
+ void systemRemovePlayer(KPlayer* player,bool deleteit);
+
+ /**
+ * This member function will transmit e.g. all players to that client, as well as
+ * all properties of these players (at least if they have been added by
+ * @ref KPlayer::addProperty) so that the client will finally have the same
+ * status as the master. You want to overwrite this function if you expand
+ * KGame by any properties which have to be known by all clients.
+ *
+ * Only the ADMIN is allowed to call this.
+ * @param clientID The ID of the message client which has connected
+ **/
+ virtual void negotiateNetworkGame(Q_UINT32 clientID);
+
+ /**
+ * syncronise the random numbers with all network clients
+ * not used by KGame - if it should be kept then as public method
+ */
+ void syncRandom();
+
+ void deletePlayers();
+ void deleteInactivePlayers();
+
+ /**
+ * @deprecated
+ * Use @ref KGameSequence instead.
+ *
+ * @param player the player who made the last move
+ * @return anything else but 0 is considered as game over
+ */
+ virtual int checkGameOver(KPlayer *player);
+
+ /**
+ * Load a saved game, from file OR network. Internal.
+ * Warning: loadgame must not rely that all players all already
+ * activated. Actually the network will activate a player AFTER
+ * the loadgame only. This is not true anymore. But be careful
+ * anyway.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param network is it a network call -> make players virtual
+ * @param reset shall the game be reset before loading
+ *
+ * @return true?
+ */
+ virtual bool loadgame(QDataStream &stream, bool network, bool reset);
+
+ /**
+ * Save a game, to file OR network. Internal.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param network is it a call from the network or from a file (unused but informative)
+ * @param saveplayers shall the players be saved too (should be TRUE)
+ *
+ * @return true?
+ */
+ virtual bool savegame(QDataStream &stream, bool network,bool saveplayers);
+
+private:
+ //AB: this is to hide the "receiver" parameter from the user. It shouldn't be
+ //used if possible (except for init).
+ /**
+ * This is an overloaded function. Id differs from the public one only in
+ * its parameters:
+ *
+ * @param receiver The Client that will receive the message. You will hardly
+ * ever need this. It it internally used to initialize a newly connected
+ * client.
+ **/
+ //void addPlayer(KPlayer* newplayer, Q_UINT32 receiver);
+
+ /**
+ * Just the same as the public one except receiver:
+ * @param receiver 0 for broadcast, otherwise the receiver. Should only be
+ * used in special circumstances and not outside KGame.
+ **/
+ bool removePlayer(KPlayer * player, Q_UINT32 receiver);
+
+ /**
+ * Helping function - game negotiation
+ **/
+ void setupGame(Q_UINT32 sender);
+
+ /**
+ * Helping function - game negotiation
+ **/
+ void setupGameContinue(QDataStream& msg, Q_UINT32 sender);
+
+ /**
+ * Removes a player from all lists, removes the @ref KGame pointer from the
+ * @ref KPlayer and deletes the player. Used by (e.g.) @ref
+ * systemRemovePlayer
+ * @return True if the player has been removed, false if the current is not
+ * found
+ **/
+ bool systemRemove(KPlayer* player,bool deleteit);
+
+
+private:
+ KGamePrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamechat.cpp b/libkdegames/kgame/kgamechat.cpp
new file mode 100644
index 00000000..16ec7c18
--- /dev/null
+++ b/libkdegames/kgame/kgamechat.cpp
@@ -0,0 +1,341 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamechat.h"
+#include "kgamechat.moc"
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgameproperty.h"
+#include "kgamemessage.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qmap.h>
+#include <qintdict.h>
+
+//FIXME:
+#define FIRST_ID 2 // first id, that is free of use, aka not defined above
+
+class KGameChatPrivate
+{
+public:
+ KGameChatPrivate()
+ {
+ mFromPlayer = 0;
+ mGame = 0;
+
+ mToMyGroup = -1;
+ }
+
+ KGame* mGame;
+ KPlayer* mFromPlayer;
+ int mMessageId;
+
+
+ QIntDict<KPlayer> mIndex2Player;
+
+ QMap<int, int> mSendId2PlayerId;
+ int mToMyGroup; // just as the above - but for the group, not for players
+};
+
+KGameChat::KGameChat(KGame* g, int msgid, QWidget* parent) : KChatBase(parent)
+{
+ init(g, msgid);
+}
+
+KGameChat::KGameChat(KGame* g, int msgid, KPlayer* fromPlayer, QWidget* parent) : KChatBase(parent)
+{
+ init(g, msgid);
+ setFromPlayer(fromPlayer);
+}
+
+KGameChat::KGameChat(QWidget* parent) : KChatBase(parent)
+{
+ init(0, -1);
+}
+
+KGameChat::~KGameChat()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameChat::init(KGame* g, int msgId)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ d = new KGameChatPrivate;
+ setMessageId(msgId);
+
+ setKGame(g);
+}
+
+void KGameChat::addMessage(int fromId, const QString& text)
+{
+ if (!d->mGame) {
+ kdWarning(11001) << "no KGame object has been set" << endl;
+ addMessage(i18n("Player %1").arg(fromId), text);
+ } else {
+ KPlayer* p = d->mGame->findPlayer(fromId);
+ if (p) {
+ kdDebug(11001) << "adding message of player " << p->name() << "id=" << fromId << endl;
+ addMessage(p->name(), text);
+ } else {
+ kdWarning(11001) << "Could not find player id " << fromId << endl;
+ addMessage(i18n("Unknown"), text);
+ }
+ }
+}
+
+void KGameChat::returnPressed(const QString& text)
+{
+ if (!d->mFromPlayer) {
+ kdWarning(11001) << k_funcinfo << ": You must set a player first!" << endl;
+ return;
+ }
+ if (!d->mGame) {
+ kdWarning(11001) << k_funcinfo << ": You must set a game first!" << endl;
+ return;
+ }
+
+ kdDebug(11001) << "from: " << d->mFromPlayer->id() << "==" << d->mFromPlayer->name() << endl;
+
+ int id = sendingEntry();
+
+ if (isToGroupMessage(id)) {
+ // note: there is currently no support for other groups than the players
+ // group! It might be useful to send to other groups, too
+ QString group = d->mFromPlayer->group();
+ kdDebug(11001) << "send to group " << group << endl;
+ int sender = d->mFromPlayer->id();
+ d->mGame->sendGroupMessage(text, messageId(), sender, group);
+
+ //TODO
+ //AB: this message is never received!! we need to connect to
+ //KPlayer::networkData!!!
+ //TODO
+
+ } else {
+ int toPlayer = 0;
+ if (!isSendToAllMessage(id) && isToPlayerMessage(id)) {
+ toPlayer = playerId(id);
+ if (toPlayer == -1) {
+ kdError(11001) << k_funcinfo << ": don't know that player "
+ << "- internal ERROR" << endl;
+ }
+ }
+ int receiver = toPlayer;
+ int sender = d->mFromPlayer->id();
+ d->mGame->sendMessage(text, messageId(), receiver, sender);
+ }
+}
+
+void KGameChat::setMessageId(int msgid)
+{ d->mMessageId = msgid; }
+
+int KGameChat::messageId() const
+{ return d->mMessageId; }
+
+bool KGameChat::isSendToAllMessage(int id) const
+{ return (id == KChatBase::SendToAll); }
+
+bool KGameChat::isToGroupMessage(int id) const
+{ return (id == d->mToMyGroup); }
+
+bool KGameChat::isToPlayerMessage(int id) const
+{
+return d->mSendId2PlayerId.contains(id); }
+
+QString KGameChat::sendToPlayerEntry(const QString& name) const
+{ return i18n("Send to %1").arg(name); }
+
+int KGameChat::playerId(int id) const
+{
+ if (!isToPlayerMessage(id)) {
+ return -1;
+ }
+
+ return d->mSendId2PlayerId[id];
+}
+
+int KGameChat::sendingId(int playerId) const
+{
+ QMap<int, int>::Iterator it;
+ for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) {
+ if (it.data() == playerId) {
+ return it.key();
+ }
+ }
+ return -1;
+}
+
+const QString& KGameChat::fromName() const
+{ return d->mFromPlayer ? d->mFromPlayer->name() : QString::null; }
+
+bool KGameChat::hasPlayer(int id) const
+{
+ return (sendingId(id) != -1);
+}
+
+void KGameChat::setFromPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ removeSendingEntry(d->mToMyGroup);
+ d->mFromPlayer = 0;
+ return;
+ }
+ if (d->mFromPlayer) {
+ changeSendingEntry(p->group(), d->mToMyGroup);
+ } else {
+ if (d->mToMyGroup != -1) {
+ kdWarning(11001) << "send to my group exists already - removing" << endl;
+ removeSendingEntry(d->mToMyGroup);
+ }
+ d->mToMyGroup = nextId();
+ addSendingEntry(i18n("Send to My Group (\"%1\")").arg(p->group()), d->mToMyGroup);
+ }
+ d->mFromPlayer = p;
+ kdDebug(11001) << k_funcinfo << " player=" << p << endl;
+}
+
+
+void KGameChat::setKGame(KGame* g)
+{
+ if (d->mGame) {
+ slotUnsetKGame();
+ }
+ kdDebug(11001) << k_funcinfo << " game=" << g << endl;
+ d->mGame = g;
+
+ if (d->mGame) {
+ connect(d->mGame, SIGNAL(signalPlayerJoinedGame(KPlayer*)),
+ this, SLOT(slotAddPlayer(KPlayer*)));
+ connect(d->mGame, SIGNAL(signalPlayerLeftGame(KPlayer*)),
+ this, SLOT(slotRemovePlayer(KPlayer*)));
+ connect(d->mGame, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, Q_UINT32)),
+ this, SLOT(slotReceiveMessage(int, const QByteArray&, Q_UINT32, Q_UINT32)));
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+
+ QPtrList<KPlayer> playerList = *d->mGame->playerList();
+ for (int unsigned i = 0; i < playerList.count(); i++) {
+ slotAddPlayer(playerList.at(i));
+ }
+ }
+}
+
+KGame* KGameChat::game() const
+{
+ return d->mGame;
+}
+
+KPlayer* KGameChat::fromPlayer() const
+{
+ return d->mFromPlayer;
+}
+
+void KGameChat::slotUnsetKGame()
+{
+//TODO: test this method!
+
+ if (!d->mGame) {
+ return;
+ }
+ disconnect(d->mGame, 0, this, 0);
+ removeSendingEntry(d->mToMyGroup);
+ QMap<int, int>::Iterator it;
+ for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) {
+ removeSendingEntry(it.data());
+ }
+}
+
+void KGameChat::slotAddPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": cannot add NULL player" << endl;
+ return;
+ }
+ if (hasPlayer(p->id())) {
+ kdError(11001) << k_funcinfo << ": player was added before" << endl;
+ return;
+ }
+
+ int sendingId = nextId();
+ addSendingEntry(comboBoxItem(p->name()), sendingId);
+ d->mSendId2PlayerId.insert(sendingId, p->id());
+ connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+ connect(p, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, KPlayer*)),
+ this, SLOT(slotReceivePrivateMessage(int, const QByteArray&, Q_UINT32, KPlayer*)));
+}
+
+void KGameChat::slotRemovePlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return;
+ }
+ if (!hasPlayer(p->id())) {
+ kdError(11001) << k_funcinfo << ": cannot remove non-existent player" << endl;
+ return;
+ }
+
+ int id = sendingId(p->id());
+ removeSendingEntry(id);
+ p->disconnect(this);
+ d->mSendId2PlayerId.remove(id);
+}
+
+void KGameChat::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player)
+{
+ if (prop->id() == KGamePropertyBase::IdName) {
+// kdDebug(11001) << "new Name" << endl;
+ changeSendingEntry(player->name(), sendingId(player->id()));
+/*
+ mCombo->changeItem(comboBoxItem(player->name()), index);
+ */
+ } else if (prop->id() == KGamePropertyBase::IdGroup) {
+ //TODO
+ }
+}
+
+void KGameChat::slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me)
+{
+ if (!me || me != fromPlayer()) {
+ kdDebug() << k_funcinfo << "nope - not for us!" << endl;
+ return;
+ }
+ slotReceiveMessage(msgid, buffer, me->id(), sender);
+}
+
+void KGameChat::slotReceiveMessage(int msgid, const QByteArray& buffer, Q_UINT32 , Q_UINT32 sender)
+{
+ QDataStream msg(buffer, IO_ReadOnly);
+ if (msgid != messageId()) {
+ return;
+ }
+
+ QString text;
+ msg >> text;
+
+ addMessage(sender, text);
+}
+
diff --git a/libkdegames/kgame/kgamechat.h b/libkdegames/kgame/kgamechat.h
new file mode 100644
index 00000000..6f7ea65d
--- /dev/null
+++ b/libkdegames/kgame/kgamechat.h
@@ -0,0 +1,223 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMECHAT_H__
+#define __KGAMECHAT_H__
+
+#include <qstring.h>
+
+#include "kchatbase.h"
+#include <kdemacros.h>
+class KPlayer;
+class KGame;
+class KGamePropertyBase;
+
+class KGameChatPrivate;
+
+/**
+ * @short A Chat widget for KGame-based games
+ *
+ * Call @ref setFromPlayer() first - this will be used as the "from" part of
+ * every message you will send. Otherwise it won't work! You can also use the
+ * fromPlayer parameter in the constructor though...
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KGameChat : public KChatBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a @ref KGame chat widget on @p game that used @p msgid for
+ * the chat message. The @p fromPlayer is the local player (see @ref
+ * setFromPlayer).
+ **/
+ KGameChat(KGame* game, int msgid, KPlayer* fromPlayer, QWidget * parent);
+
+ /**
+ * @overload
+ * To make use of this widget you need to call @ref setFromPlayer
+ * manually.
+ **/
+ KGameChat(KGame* game, int msgId, QWidget* parent);
+
+ /**
+ * @overload
+ * This constructs a widget that is not usable. You must call at least
+ * setGame, setFromPlayer and setMessageId manually.
+ * @since 3.2
+ **/
+ KGameChat(QWidget* parent);
+
+ virtual ~KGameChat();
+
+ enum SendingIds {
+ SendToGroup = 1
+ };
+
+ /**
+ * This sets the fromPlayer to @p player. The fromPlayer is the
+ * player that will appear as "from" when you send messages through this
+ * widget.
+ * @param player The player of this widget
+ **/
+ void setFromPlayer(KPlayer* player);
+
+ KPlayer* fromPlayer() const;
+
+ /**
+ * Set the @ref KGame object for this chat widget. All messages will be
+ * sent through this object. You don't have to implement any send
+ * functions, just call this function, call @ref setFromPlayer and be
+ * done :-)
+ * @param g The @ref KGame object the messages will be sent through
+ **/
+ void setKGame(KGame* g);
+
+ KGame* game() const;
+
+ /**
+ * @return The id of the messages produced by KGameChat. The id will be
+ * used in @ref KGame as parameter msgid in the method @ref KGame::sendMessage
+ **/
+ int messageId() const;
+
+ /**
+ * Change the message id of the chat widget. It is recommended that you
+ * don't use this but prefer the constructor instead, but in certain
+ * situations (such as using this widget in Qt designer) it may be
+ * useful to change the message id.
+ *
+ * See also @ref messageId
+ * @since 3.2
+ **/
+ void setMessageId(int msgid);
+
+ /**
+ * reimplemented from @ref KChatBase
+ * @return @ref KPlayer::name() for the player set by @ref setFromPlayer
+ **/
+ virtual const QString& fromName() const;
+
+
+public slots:
+ virtual void addMessage(const QString& fromName, const QString& text) { KChatBase::addMessage(fromName, text);}
+ virtual void addMessage(int fromId, const QString& text);
+
+ void slotReceiveMessage(int, const QByteArray&, Q_UINT32 receiver, Q_UINT32 sender);
+
+protected:
+ /**
+ * @param id The ID of the sending entry, as returned by @ref
+ * KChatBase::sendingEntry
+ * @return True if the entry "send to all" was selected, otherwise false
+ **/
+ bool isSendToAllMessage(int id) const;
+
+ /**
+ * Used to indicate whether a message shall be sent to a group of
+ * players. Note that this was not yet implemented when this doc was
+ * written so this description might be wrong. (FIXME)
+ * @param id The ID of the sending entry, as returned by @ref
+ * KChatBase::sendingEntry
+ * @return True if the message is meant to be sent to a group (see @ref
+ * KPlayer::group), e.g. if "send to my group" was selected.
+ **/
+ bool isToGroupMessage(int id) const;
+
+
+ /**
+ * Used to indicate whether the message shall be sent to a single player
+ * only. Note that you can also call @ref isSendToAllMessage and @ref
+ * isToGroupMessage - if both return false it must be a player message.
+ * This behaviour might be changed later - so don't depend on it.
+ *
+ * See also toPlayerId
+ * @param id The ID of the sending entry, as returned by
+ * KChatBase::sendingEntry
+ * @return True if the message shall be sent to a special player,
+ * otherwise false.
+ **/
+ bool isToPlayerMessage(int id) const;
+
+ /**
+ * @param id The ID of the sending entry, as returned by
+ * KChatBase::sendingEntry
+ * @return The ID of the player (see KPlayer::id) the sending entry
+ * belongs to. Note that the parameter id is an id as returned by ref
+ * KChatBase::sendingEntry and the id this method returns is a
+ * KPlayer ID. If isToPlayerMessage returns false this method
+ * returns -1
+ **/
+ int playerId(int id) const;
+
+ /**
+ * @param playerId The ID of the KPlayer object
+ * @return The ID of the sending entry (see KChatBase) or -1 if
+ * the player id was not found.
+ **/
+ int sendingId(int playerId) const;
+
+ /**
+ * @return True if the player with this ID was added before (see
+ * slotAddPlayer)
+ **/
+ bool hasPlayer(int id) const;
+
+ /**
+ * @param name The name of the added player
+ * @return A string that will be added as sending entry in @ref
+ * KChatBase. By default this is "send to name" where name is the name
+ * that you specify. See also KChatBase::addSendingEntry
+ **/
+ virtual QString sendToPlayerEntry(const QString& name) const;
+
+
+protected slots:
+ /**
+ * Unsets a KGame object that has been set using setKGame
+ * before. You don't have to call this - this is usually done
+ * automatically.
+ **/
+ void slotUnsetKGame();
+
+
+ void slotPropertyChanged(KGamePropertyBase*, KPlayer*);
+ void slotAddPlayer(KPlayer*);
+ void slotRemovePlayer(KPlayer*);
+
+ /**
+ * Called when KPlayer::signalNetworkData is emitted. The message
+ * gets forwarded to slotReceiveMessage if @p me equals
+ * fromPlayer.
+ **/
+ void slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me);
+
+protected:
+ virtual void returnPressed(const QString& text);
+
+private:
+ void init(KGame* g, int msgid);
+
+private:
+ KGameChatPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameerror.cpp b/libkdegames/kgame/kgameerror.cpp
new file mode 100644
index 00000000..93f40f93
--- /dev/null
+++ b/libkdegames/kgame/kgameerror.cpp
@@ -0,0 +1,80 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameerror.h"
+#include "kgamemessage.h"
+
+#include <klocale.h>
+
+QByteArray KGameError::errVersion(int remoteVersion)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ s << (Q_INT32)KGameMessage::version();
+ s << (Q_INT32)remoteVersion;
+ return b;
+}
+
+QByteArray KGameError::errCookie(int localCookie, int remoteCookie)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ s << (Q_INT32)localCookie;
+ s << (Q_INT32)remoteCookie;
+ return b;
+}
+
+QString KGameError::errorText(int errorCode, const QByteArray& message)
+{
+ QDataStream s(message, IO_ReadOnly);
+ return errorText(errorCode, s);
+}
+
+QString KGameError::errorText(int errorCode, QDataStream& s)
+{
+ QString text;
+ switch (errorCode) {
+ case Cookie:
+ {
+ Q_INT32 cookie1;
+ Q_INT32 cookie2;
+ s >> cookie1;
+ s >> cookie2;
+ text = i18n("Cookie mismatch!\nExpected Cookie: %1\nReceived Cookie: %2").arg(cookie1).arg(cookie2);
+ break;
+ }
+ case Version:
+ {
+ Q_INT32 version1;
+ Q_INT32 version2;
+ s >> version1;
+ s >> version2;
+ text = i18n("KGame Version mismatch!\nExpected Version: %1\nReceived Version: %2\n").arg(version1).arg(version2);
+ break;
+ }
+ default:
+ text = i18n("Unknown error code %1").arg(errorCode);
+ }
+ return text;
+}
+
diff --git a/libkdegames/kgame/kgameerror.h b/libkdegames/kgame/kgameerror.h
new file mode 100644
index 00000000..2916e891
--- /dev/null
+++ b/libkdegames/kgame/kgameerror.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEERROR_H_
+#define __KGAMEERROR_H_
+
+#include <qstring.h>
+
+
+class KGameError
+{
+public:
+ KGameError() { }
+ ~KGameError() { }
+
+ enum ErrorCodes {
+ Cookie = 0, // Cookie mismatch
+ Version = 1 // Version mismatch
+ };
+
+ /**
+ * Generate an error message with Erorr Code = ErrCookie
+ **/
+ static QByteArray errCookie(int localCookie, int remoteCookie);
+ static QByteArray errVersion(int remoteVersion);
+
+ /**
+ * Create an erorr text using a QDataStream (QByteArray) which was
+ * created using @ref KGameError. This is the opposite function to all
+ * the errXYZ() function (e.g. @ref errVersion).
+ * You want to use this to generate the message that shall be
+ * displayed to the user.
+ * @return an error message
+ **/
+ static QString errorText(int errorCode, QDataStream& message);
+ static QString errorText(int errorCode, const QByteArray& message);
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameio.cpp b/libkdegames/kgame/kgameio.cpp
new file mode 100644
index 00000000..9b3a55e8
--- /dev/null
+++ b/libkdegames/kgame/kgameio.cpp
@@ -0,0 +1,539 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameio.h"
+#include "kgameio.moc"
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamemessage.h"
+#include "kmessageio.h"
+
+#include <kdebug.h>
+
+#include <qwidget.h>
+#include <qbuffer.h>
+#include <qtimer.h>
+
+#include <stdlib.h>
+
+// ----------------------- Generic IO -------------------------
+KGameIO::KGameIO() : QObject(0,0)
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl;
+ mPlayer = 0;
+}
+
+KGameIO::KGameIO(KPlayer* player) : QObject(0,0)
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl;
+ mPlayer = 0;
+ if (player)
+ {
+ player->addGameIO(this);
+ }
+}
+
+KGameIO::~KGameIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ // unregister ourselves
+ if (player())
+ {
+ player()->removeGameIO(this, false);
+ }
+}
+
+void KGameIO::initIO(KPlayer *p)
+{
+ setPlayer(p);
+}
+
+void KGameIO::notifyTurn(bool b)
+{
+ if (!player())
+ {
+ kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl;
+ return;
+ }
+ bool sendit=false;
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ emit signalPrepareTurn(stream, b, this, &sendit);
+ if (sendit)
+ {
+ QDataStream ostream(buffer,IO_ReadOnly);
+ Q_UINT32 sender = player()->id(); // force correct sender
+ kdDebug(11001) << "Prepare turn sendInput" << endl;
+ sendInput(ostream, true, sender);
+ }
+}
+
+KGame* KGameIO::game() const
+{
+ if (!player())
+ {
+ return 0;
+ }
+ return player()->game();
+}
+
+bool KGameIO::sendInput(QDataStream& s, bool transmit, Q_UINT32 sender)
+{
+ if (!player())
+ {
+ return false;
+ }
+ return player()->forwardInput(s, transmit, sender);
+}
+
+void KGameIO::Debug()
+{
+ kdDebug(11001) << "------------------- KGAMEINPUT --------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "rtti : " << rtti() << endl;
+ kdDebug(11001) << "Player: " << player() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+
+// ----------------------- Key IO ---------------------------
+KGameKeyIO::KGameKeyIO(QWidget *parent)
+ : KGameIO()
+{
+ if (parent)
+ {
+ kdDebug(11001) << "Key Event filter installed" << endl;
+ parent->installEventFilter(this);
+ }
+}
+
+KGameKeyIO::~KGameKeyIO()
+{
+ if (parent())
+ {
+ parent()->removeEventFilter(this);
+ }
+}
+
+int KGameKeyIO::rtti() const { return KeyIO; }
+
+bool KGameKeyIO::eventFilter( QObject *o, QEvent *e )
+{
+ if (!player())
+ {
+ return false;
+ }
+
+ // key press/release
+ if ( e->type() == QEvent::KeyPress ||
+ e->type() == QEvent::KeyRelease )
+ {
+ QKeyEvent *k = (QKeyEvent*)e;
+ // kdDebug(11001) << "KGameKeyIO " << this << " key press/release " << k->key() << endl ;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ bool eatevent=false;
+ emit signalKeyEvent(this,stream,k,&eatevent);
+ QDataStream msg(buffer,IO_ReadOnly);
+
+ if (eatevent && sendInput(msg))
+ {
+ return eatevent;
+ }
+ return false; // do not eat otherwise
+ }
+ return QObject::eventFilter( o, e ); // standard event processing
+}
+
+
+// ----------------------- Mouse IO ---------------------------
+KGameMouseIO::KGameMouseIO(QWidget *parent,bool trackmouse)
+ : KGameIO()
+{
+ if (parent)
+ {
+ kdDebug(11001) << "Mouse Event filter installed tracking=" << trackmouse << endl;
+ parent->installEventFilter(this);
+ parent->setMouseTracking(trackmouse);
+ }
+}
+
+KGameMouseIO::~KGameMouseIO()
+{
+ if (parent())
+ {
+ parent()->removeEventFilter(this);
+ }
+}
+
+int KGameMouseIO::rtti() const
+{
+ return MouseIO;
+}
+
+void KGameMouseIO::setMouseTracking(bool b)
+{
+ if (parent())
+ {
+ ((QWidget*)parent())->setMouseTracking(b);
+ }
+}
+
+bool KGameMouseIO::eventFilter( QObject *o, QEvent *e )
+{
+ if (!player())
+ {
+ return false;
+ }
+// kdDebug(11001) << "KGameMouseIO " << this << endl ;
+
+ // mouse action
+ if ( e->type() == QEvent::MouseButtonPress ||
+ e->type() == QEvent::MouseButtonRelease ||
+ e->type() == QEvent::MouseButtonDblClick ||
+ e->type() == QEvent::Wheel ||
+ e->type() == QEvent::MouseMove
+ )
+ {
+ QMouseEvent *k = (QMouseEvent*)e;
+ // kdDebug(11001) << "KGameMouseIO " << this << endl ;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ bool eatevent=false;
+ emit signalMouseEvent(this,stream,k,&eatevent);
+// kdDebug(11001) << "################# eatevent=" << eatevent << endl;
+ QDataStream msg(buffer,IO_ReadOnly);
+ if (eatevent && sendInput(msg))
+ {
+ return eatevent;
+ }
+ return false; // do not eat otherwise
+ }
+ return QObject::eventFilter( o, e ); // standard event processing
+}
+
+
+// ----------------------- KGameProcesPrivate ---------------------------
+class KGameProcessIO::KGameProcessIOPrivate
+{
+public:
+ KGameProcessIOPrivate()
+ {
+ //mMessageServer = 0;
+ //mMessageClient = 0;
+ mProcessIO=0;
+ }
+ //KMessageServer *mMessageServer;
+ //KMessageClient *mMessageClient;
+ KMessageProcess *mProcessIO;
+};
+
+// ----------------------- Process IO ---------------------------
+KGameProcessIO::KGameProcessIO(const QString& name)
+ : KGameIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)=" << sizeof(KGameProcessIO) << endl;
+ d = new KGameProcessIOPrivate;
+
+ //kdDebug(11001) << "================= KMEssageServer ==================== " << endl;
+ //d->mMessageServer=new KMessageServer(0,this);
+ //kdDebug(11001) << "================= KMEssageClient ==================== " << endl;
+ //d->mMessageClient=new KMessageClient(this);
+ kdDebug(11001) << "================= KMEssageProcessIO ==================== " << endl;
+ d->mProcessIO=new KMessageProcess(this,name);
+ kdDebug(11001) << "================= KMEssage Add client ==================== " << endl;
+ //d->mMessageServer->addClient(d->mProcessIO);
+ //kdDebug(11001) << "================= KMEssage SetSErver ==================== " << endl;
+ //d->mMessageClient->setServer(d->mMessageServer);
+ kdDebug(11001) << "================= KMEssage: Connect ==================== " << endl;
+ //connect(d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+ // this, SLOT(clientMessage(const QByteArray&, Q_UINT32)));
+ //connect(d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &)),
+ // this, SLOT(clientMessage(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &)));
+ connect(d->mProcessIO, SIGNAL(received(const QByteArray&)),
+ this, SLOT(receivedMessage(const QByteArray&)));
+ //kdDebug(11001) << "Our client is id="<<d->mMessageClient->id() << endl;
+}
+
+KGameProcessIO::~KGameProcessIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ kdDebug(11001) << "player="<<player() << endl;
+ if (player())
+ {
+ player()->removeGameIO(this,false);
+ }
+ if (d->mProcessIO)
+ {
+ delete d->mProcessIO;
+ d->mProcessIO=0;
+ }
+ delete d;
+}
+
+int KGameProcessIO::rtti() const
+{
+ return ProcessIO;
+}
+
+void KGameProcessIO::initIO(KPlayer *p)
+{
+ KGameIO::initIO(p);
+ // Send 'hello' to process
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ Q_INT16 id = p->userId();
+ stream << id;
+
+ bool sendit=true;
+ if (p)
+ {
+ emit signalIOAdded(this,stream,p,&sendit);
+ if (sendit )
+ {
+ Q_UINT32 sender = p->id();
+ kdDebug(11001) << "Sending IOAdded to process player !!!!!!!!!!!!!! " << endl;
+ sendSystemMessage(stream, KGameMessage::IdIOAdded, 0, sender);
+ }
+ }
+}
+
+void KGameProcessIO::notifyTurn(bool b)
+{
+ if (!player())
+ {
+ kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl;
+ return;
+ }
+ bool sendit=true;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << (Q_INT8)b;
+ emit signalPrepareTurn(stream,b,this,&sendit);
+ if (sendit)
+ {
+ Q_UINT32 sender=player()->id();
+ kdDebug(11001) << "Sending Turn to process player !!!!!!!!!!!!!! " << endl;
+ sendSystemMessage(stream, KGameMessage::IdTurn, 0, sender);
+ }
+}
+
+void KGameProcessIO::sendSystemMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ sendAllMessages(stream, msgid, receiver, sender, false);
+}
+
+void KGameProcessIO::sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ sendAllMessages(stream, msgid, receiver, sender, true);
+}
+
+void KGameProcessIO::sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg)
+{
+ kdDebug(11001) << "==============> KGameProcessIO::sendMessage (usermsg="<<usermsg<<")" << endl;
+ // if (!player()) return ;
+ //if (!player()->isActive()) return ;
+
+ if (usermsg)
+ {
+ msgid+=KGameMessage::IdUser;
+ }
+
+ kdDebug(11001) << "=============* ProcessIO (" << msgid << "," << receiver << "," << sender << ") ===========" << endl;
+
+ QByteArray buffer;
+ QDataStream ostream(buffer,IO_WriteOnly);
+ QBuffer *device=(QBuffer *)stream.device();
+ QByteArray data=device->buffer();;
+
+ KGameMessage::createHeader(ostream,sender,receiver,msgid);
+ // ostream.writeRawBytes(data.data()+device->at(),data.size()-device->at());
+ ostream.writeRawBytes(data.data(),data.size());
+ kdDebug(11001) << " Adding user data from pos="<< device->at() <<" amount= " << data.size() << " byte " << endl;
+ //if (d->mMessageClient) d->mMessageClient->sendBroadcast(buffer);
+ if (d->mProcessIO)
+ {
+ d->mProcessIO->send(buffer);
+ }
+}
+
+//void KGameProcessIO::clientMessage(const QByteArray& receiveBuffer, Q_UINT32 clientID, const QValueList <Q_UINT32> &recv)
+void KGameProcessIO::receivedMessage(const QByteArray& receiveBuffer)
+{
+ QDataStream stream(receiveBuffer,IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender;
+ Q_UINT32 receiver;
+ KGameMessage::extractHeader(stream,sender,receiver,msgid);
+
+ kdDebug(11001) << "************* Got process message sender =" << sender
+ << " receiver=" << receiver << " msgid=" << msgid << endl;
+
+
+ // Cut out the header part...to not confuse network code
+ QBuffer *buf=(QBuffer *)stream.device();
+ QByteArray newbuffer;
+ newbuffer.setRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at());
+ QDataStream ostream(newbuffer,IO_ReadOnly);
+ kdDebug(11001) << "Newbuffer size=" << newbuffer.size() << endl;
+
+
+
+// This is a dummy message which allows us the process to talk with its owner
+ if (msgid==KGameMessage::IdProcessQuery)
+ {
+ emit signalProcessQuery(ostream,this);
+ }
+ else if (player())
+ {
+ sender = player()->id(); // force correct sender
+ if (msgid==KGameMessage::IdPlayerInput)
+ {
+ sendInput(ostream,true,sender);
+ }
+ else
+ {
+ player()->forwardMessage(ostream,msgid,receiver,sender);
+ }
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo << ": Got message from process but no player defined!" << endl;
+ }
+ newbuffer.resetRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at());
+}
+
+
+// ----------------------- Computer IO --------------------------
+class KGameComputerIO::KGameComputerIOPrivate
+{
+//TODO: maybe these should be KGameProperties!!
+public:
+ KGameComputerIOPrivate()
+ {
+ mAdvanceCounter = 0;
+ mReactionPeriod = 0;
+
+ mPauseCounter = 0;
+
+ mAdvanceTimer = 0;
+ }
+ int mAdvanceCounter;
+ int mReactionPeriod;
+
+ int mPauseCounter;
+
+ QTimer* mAdvanceTimer;
+};
+
+KGameComputerIO::KGameComputerIO() : KGameIO()
+{
+ init();
+}
+
+KGameComputerIO::KGameComputerIO(KPlayer* p) : KGameIO(p)
+{
+ init();
+}
+
+void KGameComputerIO::init()
+{
+ d = new KGameComputerIOPrivate;
+}
+
+KGameComputerIO::~KGameComputerIO()
+{
+ if (d->mAdvanceTimer)
+ {
+ delete d->mAdvanceTimer;
+ }
+ delete d;
+}
+
+int KGameComputerIO::rtti() const
+{
+ return ComputerIO;
+}
+
+void KGameComputerIO::setReactionPeriod(int calls)
+{
+ d->mReactionPeriod = calls;
+}
+
+int KGameComputerIO::reactionPeriod() const
+{
+ return d->mReactionPeriod;
+}
+
+void KGameComputerIO::setAdvancePeriod(int ms)
+{
+ stopAdvancePeriod();
+ d->mAdvanceTimer = new QTimer(this);
+ connect(d->mAdvanceTimer, SIGNAL(timeout()), this, SLOT(advance()));
+ d->mAdvanceTimer->start(ms);
+}
+
+void KGameComputerIO::stopAdvancePeriod()
+{
+ if (d->mAdvanceTimer)
+ {
+ d->mAdvanceTimer->stop();
+ delete d->mAdvanceTimer;
+ }
+}
+
+void KGameComputerIO::pause(int calls)
+{
+ d->mPauseCounter = calls;
+}
+
+void KGameComputerIO::unpause()
+{
+ pause(0);
+}
+
+void KGameComputerIO::advance()
+{
+ if (d->mPauseCounter > 0)
+ {
+ d->mPauseCounter--;
+ return;
+ }
+ else if (d->mPauseCounter < 0)
+ {
+ return;
+ }
+ d->mAdvanceCounter++;
+ if (d->mAdvanceCounter >= d->mReactionPeriod)
+ {
+ d->mAdvanceCounter = 0;
+ reaction();
+ }
+}
+
+void KGameComputerIO::reaction()
+{
+ emit signalReaction();
+}
+
+
diff --git a/libkdegames/kgame/kgameio.h b/libkdegames/kgame/kgameio.h
new file mode 100644
index 00000000..4d7e0f5b
--- /dev/null
+++ b/libkdegames/kgame/kgameio.h
@@ -0,0 +1,566 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEIO_H__
+#define __KGAMEIO_H__
+
+#include <qstring.h>
+#include <qobject.h>
+#include <kdemacros.h>
+class KPlayer;
+class KGame;
+class KProcess;
+
+/**
+ * \short Base class for IO devices for games
+ *
+ * This is the master class for
+ * creating IO game devices. You cannot use it directly.
+ * Either take one of the classes derived from it or
+ * you have to create your own IO class derived from it (more probably).
+ *
+ * The idea behind this class is to provide a common interface
+ * for input devices into your game. By programming a KGameIO
+ * device you need not distinguish the actual IO in the game
+ * anymore. All work is done by the IO's. This allows very
+ * easy reuse in other games as well.
+ * A further advantage of using the IO's is that you can exchange
+ * the control of a player at runtime. E.g. you switch a player
+ * to be controlled by the computer or vice versa.
+ *
+ * To achieve this you have to make all of your player inputs through a
+ * KGameIO. You will usually call KGameIO::sendInput to do so.
+ *
+ * @author Martin Heni <martin@heni-online.de>
+ */
+class KDE_EXPORT KGameIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a KGameIO object
+ */
+ KGameIO();
+ KGameIO(KPlayer*);
+ virtual ~KGameIO();
+
+ /**
+ * Gives debug output of the game status
+ */
+ void Debug();
+
+ /**
+ * Identifies the KGameIO via the rtti function
+ */
+ enum IOMode {GenericIO=1,KeyIO=2,MouseIO=4,ProcessIO=8,ComputerIO=16};
+ /**
+ * Run time idendification. Predefined values are from IOMode
+ * You MUST overwrite this in derived classes!
+ *
+ * @return rtti value
+ */
+ virtual int rtti() const = 0; // Computer, network, local, ...
+
+ /**
+ * This function returns the player who owns this IO
+ *
+ * @return the player this IO device is plugged into
+ */
+ KPlayer *player() const {return mPlayer;}
+
+ /**
+ * Equivalent to player()->game()
+ * @return the @ref KGame object of this player
+ **/
+ KGame* game() const;
+
+ /**
+ * Sets the player to which this IO belongs to. This
+ * is done automatically when adding a device to a
+ * player
+ *
+ * @param p the player
+ */
+ void setPlayer(KPlayer *p) {mPlayer=p;}
+
+ /**
+ * Init this device by setting the player and e.g. sending an
+ * init message to the device. This initialisation message is
+ * very useful for computer players as you can transmit the
+ * game status to them and only update this status in the setTurn
+ * commands.
+ *
+ * Called by @ref KPlayer::addGameIO only!
+ */
+ virtual void initIO(KPlayer *p);
+
+ /**
+ * Notifies the IO device that the player's setTurn had been called
+ * Called by KPlayer
+ *
+ * This emits @ref signalPrepareTurn and sends the turn if the send
+ * parameter is set to true.
+ *
+ * @param b turn is true/false
+ */
+ virtual void notifyTurn(bool b);
+
+ /**
+ * Send an input message using @ref KPlayer::forwardInput
+ **/
+ bool sendInput(QDataStream& stream, bool transmit = true, Q_UINT32 sender = 0);
+
+signals:
+ /**
+ * Signal generated when @ref KPlayer::myTurn changes. This can either be
+ * when you get the turn status or when you lose it.
+ *
+ * The datastream has to be filled with a move. If you set (or leave) the
+ * send parameter to FALSE then nothing happens: the datastream will be
+ * ignored. If you set it to TRUE @ref sendInput is used to
+ * send the move.
+ *
+ * Often you want to ignore this signal (leave send=FALSE) and send the
+ * message later. This is usually the case for a human player as he probably
+ * doesn't react immediately. But you can still use this e.g. to notify the
+ * player about the turn change.
+ *
+ * Example:
+ * \code
+ * void GameWindow::slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool * )
+ * {
+ * KPlayer *player=input->player();
+ * if (!player->myTurn()) return ;
+ * if (!b) return ; // only do something on setTurn(true)
+ * stream << 1 << 2 << 3; // Some data for the process
+ * }
+ * \endcode
+ *
+ * @param io the KGameIO object itself
+ * @param stream the stream into which the move will be written
+ * @param turn the argument of setTurn
+ * @param send set this to true to send the generated move using @ref
+ * sendInput
+ **/
+ void signalPrepareTurn(QDataStream & stream, bool turn, KGameIO *io, bool * send);
+
+
+private:
+ KPlayer *mPlayer;
+};
+
+/**
+ * The KGameKeyIO class. It is used to process keyboard input
+ * from a widget and create moves for the player it belongs to.
+ * @author Martin Heni <martin@heni-online.de>
+ */
+class KDE_EXPORT KGameKeyIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a keyboard input devices. All keyboards
+ * inputs of the given widgets are passed through a signal
+ * handler signalKeyEvent and can be used to generate
+ * a valid move for the player.
+ * Note the widget you pass to the constructor must be
+ * the main window of your application, e.g. view->parentWidget()
+ * as QT does not forward your keyevents otherwise. This means
+ * that this might be a different widget comapred to the one you
+ * use for mouse inputs!
+ * Example:
+ * \code
+ * KGameKeyIO *input;
+ * input=new KGameKeyIO(myWidget);
+ * connect(input,SIGNAL(signalKeyEvent(KGameIO *,QDataStream &,QKeyEvent *,bool *)),
+ * this,SLOT(slotKeyInput(KGameIO *,QDataStream &,QKeyEvent *,bool *)));
+ * \endcode
+ *
+ * @param parent The parents widget whose keyboard events * should be grabbed
+ */
+ KGameKeyIO(QWidget *parent);
+ virtual ~KGameKeyIO();
+
+ /**
+ * The idendification of the IO
+ *
+ * @return KeyIO
+ */
+ virtual int rtti() const;
+
+signals:
+ /**
+ * Signal handler for keyboard events. This function is called
+ * on every keyboard event. If appropriate it can generate a
+ * move for the player the device belongs to. If this is done
+ * and the event is eaten eatevent needs to be set to true.
+ * What move you generate (i.e. what you write to the stream)
+ * is totally up to you as it will not be evaluated but forwared
+ * to the player's/game's input move function
+ * Example:
+ * \code
+ * KPlayer *player=input->player(); // Get the player
+ * Q_INT32 key=e->key();
+ * stream << key;
+ * eatevent=true;
+ * \endcode
+ *
+ * @param io the IO device we belong to
+ * @param stream the stream where we write our move into
+ * @param m The QKeyEvent we can evaluate
+ * @param eatevent set this to true if we processed the event
+ */
+ void signalKeyEvent(KGameIO *io,QDataStream &stream,QKeyEvent *m,bool *eatevent);
+
+protected:
+ /**
+ * Internal method to process the events
+ */
+ bool eventFilter( QObject *o, QEvent *e );
+};
+
+/**
+ * The KGameMouseIO class. It is used to process mouse input
+ * from a widget and create moves for the player it belongs to.
+ * @author Martin Heni <martin@heni-online.de>
+ */
+class KDE_EXPORT KGameMouseIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a mouse IO device. It captures all mouse
+ * event of the given widget and forwards them to the
+ * signal handler signalMouseEvent.
+ * Example:
+ * \code
+ * KGameMouseIO *input;
+ * input=new KGameMouseIO(mView);
+ * connect(input,SIGNAL(signalMouseEvent(KGameIO *,QDataStream &,QMouseEvent *,bool *)),
+ * this,SLOT(slotMouseInput(KGameIO *,QDataStream &,QMouseEvent *,bool *)));
+ * \endcode
+ *
+ * @param parent The widget whose events should be captured
+ * @param trackmouse enables mouse tracking (gives mouse move events)
+ */
+ KGameMouseIO(QWidget *parent,bool trackmouse=false);
+ virtual ~KGameMouseIO();
+
+ /**
+ * Manually activate or deactivate mouse tracking
+ *
+ * @param b true = tracking on
+ */
+ void setMouseTracking(bool b);
+ /**
+ * The idendification of the IO
+ *
+ * @return MouseIO
+ */
+ virtual int rtti() const;
+
+signals:
+ /**
+ * Signal handler for mouse events. This function is called
+ * on every mouse event. If appropriate it can generate a
+ * move for the player the device belongs to. If this is done
+ * and the event is eaten eatevent needs to be set to true.
+ * @see signalKeyEvent
+ * Example:
+ * \code
+ * KPlayer *player=input->player(); // Get the player
+ * Q_INT32 button=e->button();
+ * stream << button;
+ * eatevent=true;
+ * \endcode
+ *
+ * @param io the IO device we belong to
+ * @param stream the stream where we write our move into
+ * @param m The QMouseEvent we can evaluate
+ * @param eatevent set this to true if we processed the event
+ */
+ void signalMouseEvent(KGameIO *io,QDataStream &stream,QMouseEvent *m,bool *eatevent);
+
+protected:
+ /**
+ * Internal event filter
+ */
+ bool eventFilter( QObject *o, QEvent *e );
+
+};
+
+
+/**
+ * The KGameProcessIO class. It is used to create a computer player
+ * via a separate process and communicate transparetly with it.
+ * Its counterpart is the @ref KGameProcess class which needs
+ * to be used by the computer player. See its documentation
+ * for the definition of the computer player.
+ * @author Martin Heni <martin@heni-online.de>
+ */
+class KDE_EXPORT KGameProcessIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a computer player via a separate process. The process
+ * name is given as fully qualified filename.
+ * Example:
+ * \code
+ * KGameProcessIO *input;
+ * input=new KGameProcessIO(executable_file);
+ * connect(input,SIGNAL(signalPrepareTurn(QDataStream &,bool,KGameIO *,bool *)),
+ * this,SLOT(slotPrepareTurn(QDataStream &,bool,KGameIO *,bool *)));
+ * connect(input,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)),
+ * this,SLOT(slotProcessQuery(QDataStream &,KGameProcessIO *)));
+ * \endcode
+ *
+ * @param name the filename of the process to start
+ */
+ KGameProcessIO(const QString& name);
+
+ /**
+ * Deletes the process input devices
+ */
+ virtual ~KGameProcessIO();
+
+ /**
+ * The idendification of the IO
+ *
+ * @return ProcessIO
+ */
+ int rtti() const;
+
+ /**
+ * Send a message to the process. This is analogous to the sendMessage
+ * commands of KGame. It will result in a signal of the computer player
+ * on which you can react in the process player.
+ *
+ * @param stream - the actual data
+ * @param msgid - the id of the message
+ * @param receiver - not used
+ * @param sender - who send the message
+ */
+ void sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * Send a system message to the process. This is analogous to the sendMessage
+ * commands of KGame. It will result in a signal of the computer player
+ * on which you can react in the process player.
+ *
+ * @param stream - the actual data
+ * @param msgid - the id of the message
+ * @param receiver - not used
+ * @param sender - who send the message
+ */
+ void sendSystemMessage(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * Init this device by setting the player and e.g. sending an
+ * init message to the device. Calling this function will emit
+ * the IOAdded signal on which you can react and initilise the
+ * computer player.
+ * This function is called automatically when adding the IO to
+ * a player.
+ */
+ void initIO(KPlayer *p);
+
+ /**
+ * Notifies the IO device that the player's setTurn had been called
+ * Called by KPlayer. You can react on the @ref signalPrepareTurn to
+ * prepare a message for the process, i.e. either update it on
+ * the changes made to the game since the last turn or the initIO
+ * has been called or transmit your gamestatus now.
+ *
+ * @param turn is true/false
+ */
+ virtual void notifyTurn(bool turn);
+
+ protected:
+ /**
+ * Internal ~ombined function for all message handling
+ **/
+ void sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg);
+
+ protected slots:
+ /**
+ * Internal message handler to receive data from the process
+ */
+ void receivedMessage(const QByteArray& receiveBuffer);
+
+
+signals:
+ /**
+ * A computer query message is received. This is a 'dummy'
+ * message sent by the process if it needs to communicate
+ * with us. It is not forwarded over the network.
+ * Reacting to this message allows you to 'answer' questions
+ * of the process, e.g. sending addition data which the process
+ * needs to calculate a move.
+ *
+ * Example:
+ * \code
+ * void GameWindow::slotProcessQuery(QDataStream &stream,KGameProcessIO *reply)
+ * {
+ * int no;
+ * stream >> no; // We assume the process sends us an integer question numner
+ * if (no==1) // but YOU have to do this in the process player
+ * {
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * reply->sendSystemMessage(out,4242,0,0); // lets reply something...
+ * }
+ * }
+ * \endcode
+ */
+ void signalProcessQuery(QDataStream &stream,KGameProcessIO *me);
+
+ /**
+ * Signal generated when the computer player is added.
+ * You can use this to communicated with the process and
+ * e.g. send initialisation information to the process.
+ *
+ * @param game the KGameIO object itself
+ * @param stream the stream into which the move will be written
+ * @param p the player itself
+ * @param send set this to false if no move should be generated
+ */
+ void signalIOAdded(KGameIO *game,QDataStream &stream,KPlayer *p,bool *send);
+
+
+protected:
+
+private:
+ class KGameProcessIOPrivate;
+ KGameProcessIOPrivate* d;
+};
+
+/**
+ * \brief KGameIO variant for real-time games
+ *
+ * The KGameComputerIO class. It is used to create a LOCAL computer player
+ * and communicate transparently with it.
+ * Question: Is this needed or is it overwritten anyway for a real game?
+ *
+ * You most probably don't want to use this if you want to design a turn based
+ * game/player. You'll rather use @ref KGameIO directly, i.e. subclass it
+ * yourself. You just need to use @ref KGameIO::signalPrepareTurn and/or @ref
+ * KGameIO::notifyTurn there.
+ *
+ * This is rather meant to be of use in real time games.
+ *
+ * @author <b_mann@gmx.de>
+ */
+class KDE_EXPORT KGameComputerIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a LOCAL computer player
+ *
+ */
+ KGameComputerIO();
+ KGameComputerIO(KPlayer* player);
+ ~KGameComputerIO();
+
+ int rtti() const;
+
+ /**
+ * The number of advance calls until the player (or rather: the IO)
+ * does something (default: 1).
+ **/
+ void setReactionPeriod(int advanceCalls);
+ int reactionPeriod() const;
+
+ /**
+ * Start a QTimer which calls advance every @p ms milli seconds.
+ **/
+ void setAdvancePeriod(int ms);
+
+ void stopAdvancePeriod();
+
+ /**
+ * Ignore calls number of advance calls. if calls is -1 then all
+ * following advance calls are ignored until unpause is called.
+ *
+ * This simply prevents the internal advance counter to be increased.
+ *
+ * You may want to use this to emulate a "thinking" computer player. Note
+ * that this means if you increase the advance period (see
+ * setAdvancePeriod), i.e. if you change the speed of your game, your
+ * computer player thinks "faster".
+ * @param calls Number of advance calls to be ignored
+ **/
+ void pause(int calls = -1);
+
+ /**
+ * Equivalent to pause(0). Immediately continue to increase the internal
+ * advance counter.
+ **/
+ void unpause();
+
+public slots:
+ /**
+ * Works kind of similar to QCanvas::advance. Increase the internal
+ * advance counter. If @p reactionPeriod is reached the counter is set back to
+ * 0 and @ref signalReaction is emitted. This is when the player is meant
+ * to do something (move its units or so).
+ *
+ * This is very useful if you use QCanvas as you can use this in your
+ * QCanvas::advance call. The advantage is that if you change the speed
+ * of the game (i.e. change QCanvas::setAdvancePeriod) the computer
+ * player gets slower as well.
+ *
+ * If you don't use QCanvas you can use setAdvancePeriod to get
+ * the same result. Alternatively you can just use a QTimer.
+ *
+ **/
+ virtual void advance();
+
+signals:
+ /**
+ * This signal is emitted when your computer player is meant to do
+ * something, or better is meant to be allowed to do something.
+ **/
+ void signalReaction();
+
+protected:
+ /**
+ * Default implementation simply emits signalReaction
+ **/
+ virtual void reaction();
+
+private:
+ void init();
+
+private:
+ class KGameComputerIOPrivate;
+ KGameComputerIOPrivate* d;
+};
+
+
+#endif
diff --git a/libkdegames/kgame/kgamemessage.cpp b/libkdegames/kgame/kgamemessage.cpp
new file mode 100644
index 00000000..6464d407
--- /dev/null
+++ b/libkdegames/kgame/kgamemessage.cpp
@@ -0,0 +1,156 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamemessage.h"
+
+#include <klocale.h>
+
+#define MESSAGE_VERSION 2
+
+Q_UINT32 KGameMessage::createPlayerId(int oldplayerid,Q_UINT32 gameid)
+{
+ int p;
+ p = oldplayerid & 0x3ff; // remove game id
+ p |= (gameid << 10);
+ return p;
+}
+
+int KGameMessage::rawPlayerId(Q_UINT32 playerid)
+{
+ return playerid & 0x03ff;
+}
+
+Q_UINT32 KGameMessage::rawGameId(Q_UINT32 playerid)
+{
+ return (playerid & 0xfc00) >> 10;
+}
+
+bool KGameMessage::isPlayer(Q_UINT32 msgid)
+{
+ if (msgid & 0xfc00) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool KGameMessage::isGame(Q_UINT32 msgid)
+{
+ return !isPlayer(msgid);
+}
+
+
+void KGameMessage::createHeader(QDataStream &msg,Q_UINT32 sender,Q_UINT32 receiver,int msgid)
+{
+ msg << (Q_INT16)sender << (Q_INT16)receiver << (Q_INT16)msgid;
+}
+
+void KGameMessage::extractHeader(QDataStream &msg,Q_UINT32 &sender,Q_UINT32 &receiver,int &msgid)
+{
+ Q_INT16 d3,d4,d5;
+ msg >> d3 >> d4 >> d5;
+ sender=d3;receiver=d4;msgid=d5;
+}
+
+void KGameMessage::createPropertyHeader(QDataStream &msg,int id)
+{
+ msg << (Q_INT16)id;
+}
+
+void KGameMessage::extractPropertyHeader(QDataStream &msg,int &id)
+{
+ Q_INT16 d1;
+ msg >> d1;
+ id=d1;
+}
+
+void KGameMessage::createPropertyCommand(QDataStream &msg,int cmdid,int pid,int cmd)
+{
+ createPropertyHeader(msg,cmdid);
+ msg << (Q_INT16)pid ;
+ msg << (Q_INT8)cmd ;
+}
+
+void KGameMessage::extractPropertyCommand(QDataStream &msg,int &pid,int &cmd)
+{
+ Q_INT16 d1;
+ Q_INT8 d2;
+ msg >> d1 >> d2;
+ pid=d1;
+ cmd=d2;
+}
+
+int KGameMessage::version()
+{
+ return MESSAGE_VERSION;
+}
+
+QString KGameMessage::messageId2Text(int msgid)
+{
+// this should contain all KGameMessage::GameMessageIds
+// feel free to add missing ones, to remove obsolete one and even feel free to
+// let it be ;-)
+ switch (msgid) {
+ case KGameMessage::IdSetupGame:
+ return i18n("Setup Game");
+ case KGameMessage::IdSetupGameContinue:
+ return i18n("Setup Game Continue");
+ case KGameMessage::IdGameLoad:
+ return i18n("Load Game");
+ case KGameMessage::IdGameConnected:
+ return i18n("Client game connected");
+ case KGameMessage::IdGameSetupDone:
+ return i18n("Game setup done");
+ case KGameMessage::IdSyncRandom:
+ return i18n("Synchronize Random");
+ case KGameMessage::IdDisconnect:
+ return i18n("Disconnect");
+ case KGameMessage::IdPlayerProperty:
+ return i18n("Player Property");
+ case KGameMessage::IdGameProperty:
+ return i18n("Game Property");
+ case KGameMessage::IdAddPlayer:
+ return i18n("Add Player");
+ case KGameMessage::IdRemovePlayer:
+ return i18n("Remove Player");
+ case KGameMessage::IdActivatePlayer:
+ return i18n("Activate Player");
+ case KGameMessage::IdInactivatePlayer:
+ return i18n("Inactivate Player");
+ case KGameMessage::IdTurn:
+ return i18n("Id Turn");
+ case KGameMessage::IdError:
+ return i18n("Error Message");
+ case KGameMessage::IdPlayerInput:
+ return i18n("Player Input");
+ case KGameMessage::IdIOAdded:
+ return i18n("An IO was added");
+ case KGameMessage::IdProcessQuery:
+ return i18n("Process Query");
+ case KGameMessage::IdPlayerId:
+ return i18n("Player ID");
+ case KGameMessage::IdUser: // IdUser must be unknown for use, too!
+ default:
+ return QString::null;
+ }
+}
diff --git a/libkdegames/kgame/kgamemessage.h b/libkdegames/kgame/kgamemessage.h
new file mode 100644
index 00000000..4394b4fa
--- /dev/null
+++ b/libkdegames/kgame/kgamemessage.h
@@ -0,0 +1,173 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEMSG_H_
+#define __KGAMEMSG_H_
+
+#include <qdatastream.h>
+#include <kdemacros.h>
+
+class KDE_EXPORT KGameMessage
+{
+ public:
+ /**
+ * Creates a fully qualified player ID which contains the original
+ * player id in the lower bits and the game number in the higher bits.
+ * Do not rely on the exact bit positions as they are internal.
+ *
+ * See also @ref rawPlayerId and @ref rawGameId which are the inverse
+ * operations
+ *
+ * @param playerid the player id - can include a gameid (will get removed)
+ * @param gameid The game id (<64). 0 For broadcast.
+ * @return the new player id
+ */
+ static Q_UINT32 createPlayerId(int player, Q_UINT32 game);
+
+ /**
+ * Returns the raw playerid, that is, a id which does not
+ * contain the game number encoded in it. See also @ref createPlayerId which
+ * is the inverse operation.
+ *
+ * @param the player id
+ * @return the raw player id
+ **/
+ static int rawPlayerId(Q_UINT32 playerid);
+
+ /**
+ * Returns the raw game id, that is, the game id the player
+ * belongs to. Se also @ref createPlayerId which is the inverse operation.
+ *
+ * @param the player id
+ * @return the raw game id
+ **/
+ static Q_UINT32 rawGameId(Q_UINT32 playerid);
+
+ /**
+ * Checks whether a message receiver/sender is a player
+ *
+ * @param id The ID of the sender/receiver
+ * @return true/false
+ */
+ static bool isPlayer(Q_UINT32 id);
+
+ /**
+ * Checks whether the sender/receiver of a message is a game
+ *
+ * @param id The ID of the sender/receiver
+ * @return true/false
+ */
+ static bool isGame(Q_UINT32 id);
+
+ /**
+ * Creates a message header given cookie,sender,receiver,...
+ *
+ * Also puts "hidden" header into the stream which are used by KGameClient
+ * (message length and magic cookie). If you don't need them remove them
+ * with @ref dropExternalHeader
+ */
+ static void createHeader(QDataStream &msg, Q_UINT32 sender, Q_UINT32 receiver, int msgid);
+
+ /**
+ * Retrieves the information like cookie,sender,receiver,... from a message header
+ *
+ * Note that it could be necessary to call @ref dropExternalHeader first
+ */
+ static void extractHeader(QDataStream &msg,Q_UINT32 &sender, Q_UINT32 &receiver, int &msgid);
+
+ /**
+ * Creates a property header given the property id
+ */
+ static void createPropertyHeader(QDataStream &msg, int id);
+
+ /**
+ * Retrieves the property id from a property message header
+ */
+ static void extractPropertyHeader(QDataStream &msg, int &id);
+
+ /**
+ * Creates a property header given the property id
+ */
+ static void createPropertyCommand(QDataStream &msg, int cmdid, int pid, int cmd);
+
+ /**
+ * Retrieves the property id from a property message header
+ */
+ static void extractPropertyCommand(QDataStream &msg, int &pid, int &cmd);
+
+ /**
+ * @return Version of the network library
+ */
+ static int version();
+
+ /**
+ * This function takes a @ref GameMessageIds as argument and returns a
+ * suitable string for it. This string can't be used to identify a message
+ * (as it is i18n'ed) but it can make debugging more easy. See also @ref
+ * KGameDebugDialog.
+ * @return Either a i18n'ed string (the name of the id) or QString::null if
+ * the msgid is unknown
+ **/
+ static QString messageId2Text(int msgid);
+
+
+ /**
+ * Message Ids used inside @ref KGame.
+ *
+ * You can use your own custom message Id by adding @p IdUser to it.
+ **/
+// please document every new id with a short comment
+ enum GameMessageIds {
+// game init, game load, disconnect, ...
+ IdSetupGame=1, // sent to a newly connected player
+ IdSetupGameContinue=2, // continue the setup
+ IdGameLoad=3, // load/save the game to the client
+ IdGameConnected=4, // Client successfully connected to master
+ IdSyncRandom=5, // new random seed set - sync games
+ IdDisconnect=6, // KGame object disconnects from game
+ IdGameSetupDone=7, // New game client is now operational
+
+// properties
+ IdPlayerProperty=20, // a player property changed
+ IdGameProperty=21, // a game property changed
+
+// player management
+ IdAddPlayer=30, // add a player
+ IdRemovePlayer=31, // the player will be removed
+ IdActivatePlayer=32, // Activate a player
+ IdInactivatePlayer=33, // Inactivate a player
+ IdTurn=34, // Turn to be prepared
+
+// to-be-categorized
+ IdError=100, // an error occurred
+ IdPlayerInput=101, // a player input occurred
+ IdIOAdded=102, // KGameIO got added to a player...init this IO
+
+// special ids for computer player
+ IdProcessQuery=220, // Process queries data (process only)
+ IdPlayerId=221, // PlayerId got changed (process only)
+
+ IdUser=256 // a user specified message
+ };
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamenetwork.cpp b/libkdegames/kgame/kgamenetwork.cpp
new file mode 100644
index 00000000..9eccb868
--- /dev/null
+++ b/libkdegames/kgame/kgamenetwork.cpp
@@ -0,0 +1,516 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamenetwork.h"
+#include "kgamenetwork.moc"
+#include "kgamemessage.h"
+#include "kgameerror.h"
+
+#include "kmessageserver.h"
+#include "kmessageclient.h"
+#include "kmessageio.h"
+#include <dnssd/publicservice.h>
+
+#include <kdebug.h>
+
+#include <qbuffer.h>
+
+
+class KGameNetworkPrivate
+{
+public:
+ KGameNetworkPrivate()
+ {
+ mMessageClient = 0;
+ mMessageServer = 0;
+ mDisconnectId = 0;
+ mService = 0;
+ }
+
+public:
+ KMessageClient* mMessageClient;
+ KMessageServer* mMessageServer;
+ Q_UINT32 mDisconnectId; // Stores gameId() over a disconnect process
+ DNSSD::PublicService* mService;
+ QString mType;
+ QString mName;
+
+ int mCookie;
+};
+
+// ------------------- NETWORK GAME ------------------------
+KGameNetwork::KGameNetwork(int c, QObject* parent) : QObject(parent, 0)
+{
+ d = new KGameNetworkPrivate;
+ d->mCookie = (Q_INT16)c;
+
+ // Init the game as a local game, i.e.
+ // create your own KMessageServer and a KMessageClient connected to it.
+ setMaster();
+
+ kdDebug(11001) << k_funcinfo << "this=" << this <<", cookie=" << cookie() << " sizeof(this)="<<sizeof(KGameNetwork) << endl;
+}
+
+KGameNetwork::~KGameNetwork()
+{
+ kdDebug(11001) << k_funcinfo << "this=" << this << endl;
+// Debug();
+ delete d->mService;
+ delete d;
+}
+
+// ----------------------------- status methods
+bool KGameNetwork::isNetwork() const
+{ return isOfferingConnections() || d->mMessageClient->isNetwork();}
+
+Q_UINT32 KGameNetwork::gameId() const
+{
+ //return d->mMessageClient->id() ;
+ // Return stored id in the case of disconnect. In any other
+ // case the disconnect id is 0
+ if (d->mMessageClient->id()!=0 ) {
+ return d->mMessageClient->id() ;
+ } else {
+ return d->mDisconnectId;
+ }
+}
+
+int KGameNetwork::cookie() const
+{ return d->mCookie; }
+
+bool KGameNetwork::isMaster() const
+{ return (d->mMessageServer != 0); }
+
+bool KGameNetwork::isAdmin() const
+{ return (d->mMessageClient->isAdmin()); }
+
+KMessageClient* KGameNetwork::messageClient() const
+{ return d->mMessageClient; }
+
+KMessageServer* KGameNetwork::messageServer() const
+{ return d->mMessageServer; }
+
+// ----------------------- network init
+void KGameNetwork::setMaster()
+{
+ if (!d->mMessageServer) {
+ d->mMessageServer = new KMessageServer (cookie(), this);
+ } else {
+ kdWarning(11001) << k_funcinfo << "Server already running!!" << endl;
+ }
+ if (!d->mMessageClient) {
+ d->mMessageClient = new KMessageClient (this);
+ connect (d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+ this, SLOT(receiveNetworkTransmission(const QByteArray&, Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(connectionBroken()),
+ this, SIGNAL(signalConnectionBroken()));
+ connect (d->mMessageClient, SIGNAL(aboutToDisconnect(Q_UINT32)),
+ this, SLOT(aboutToLoseConnection(Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(connectionBroken()),
+ this, SLOT(slotResetConnection()));
+
+ connect (d->mMessageClient, SIGNAL(adminStatusChanged(bool)),
+ this, SLOT(slotAdminStatusChanged(bool)));
+ connect (d->mMessageClient, SIGNAL(eventClientConnected(Q_UINT32)),
+ this, SIGNAL(signalClientConnected(Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(eventClientDisconnected(Q_UINT32, bool)),
+ this, SIGNAL(signalClientDisconnected(Q_UINT32, bool)));
+
+ // broacast and direct messages are treated equally on receive.
+ connect (d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList<Q_UINT32>&)),
+ d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)));
+
+ } else {
+ // should be no problem but still has to be tested
+ kdDebug(11001) << k_funcinfo << "Client already exists!" << endl;
+ }
+ d->mMessageClient->setServer(d->mMessageServer);
+}
+
+void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name)
+{
+ kdDebug() << k_funcinfo << type << ":" << name << endl;
+ d->mType = type;
+ d->mName = name;
+ tryPublish();
+}
+
+void KGameNetwork::tryPublish()
+{
+ if (d->mType.isNull() || !isOfferingConnections()) return;
+ if (!d->mService) d->mService = new DNSSD::PublicService(d->mName,d->mType,port());
+ else {
+ if (d->mType!=d->mService->type()) d->mService->setType(d->mType);
+ if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName);
+ }
+ if (!d->mService->isPublished()) d->mService->publishAsync();
+}
+
+void KGameNetwork::tryStopPublishing()
+{
+ if (d->mService) d->mService->stop();
+}
+
+bool KGameNetwork::offerConnections(Q_UINT16 port)
+{
+ kdDebug (11001) << k_funcinfo << "on port " << port << endl;
+ if (!isMaster()) {
+ setMaster();
+ }
+
+ // Make sure this is 0
+ d->mDisconnectId = 0;
+
+ // FIXME: This debug message can be removed when the program is working correct.
+ if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) {
+ kdDebug (11001) << k_funcinfo << "Already running as server! Changing the port now!" << endl;
+ }
+
+ tryStopPublishing();
+ kdDebug (11001) << k_funcinfo << "before Server->initNetwork" << endl;
+ if (!d->mMessageServer->initNetwork (port)) {
+ kdError (11001) << k_funcinfo << "Unable to bind to port " << port << "!" << endl;
+ // no need to delete - we just cannot listen to the port
+// delete d->mMessageServer;
+// d->mMessageServer = 0;
+// d->mMessageClient->setServer((KMessageServer*)0);
+ return false;
+ }
+ kdDebug (11001) << k_funcinfo << "after Server->initNetwork" << endl;
+ tryPublish();
+ return true;
+}
+
+bool KGameNetwork::connectToServer (const QString& host, Q_UINT16 port)
+{
+ if (host.isEmpty()) {
+ kdError(11001) << k_funcinfo << "No hostname given" << endl;
+ return false;
+ }
+
+ // Make sure this is 0
+ d->mDisconnectId = 0;
+
+// if (!d->mMessageServer) {
+// // FIXME: What shall we do here? Probably must stop a running game.
+// kdWarning (11001) << k_funcinfo << "We are already connected to another server!" << endl;
+/// }
+
+ if (d->mMessageServer) {
+ // FIXME: What shall we do here? Probably must stop a running game.
+ kdWarning(11001) << "we are server but we are trying to connect to another server! "
+ << "make sure that all clients connect to that server! "
+ << "quitting the local server now..." << endl;
+ stopServerConnection();
+ d->mMessageClient->setServer((KMessageIO*)0);
+ delete d->mMessageServer;
+ d->mMessageServer = 0;
+ }
+
+ kdDebug(11001) << " about to set server" << endl;
+ d->mMessageClient->setServer(host, port);
+ emit signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now!
+
+ // OK: We say that we already have connected, but this isn't so yet!
+ // If the connection cannot be established, it will look as being disconnected
+ // again ("slotConnectionLost" is called).
+ // Shall we differ between these?
+ kdDebug(11001) << "connected to " << host << ":" << port << endl;
+ return true;
+}
+
+Q_UINT16 KGameNetwork::port() const
+{
+ if (isNetwork()) {
+ if (isOfferingConnections()) {
+ return d->mMessageServer->serverPort();
+ } else {
+ return d->mMessageClient->peerPort();
+ }
+ }
+ return 0;
+}
+
+QString KGameNetwork::hostName() const
+{
+ return d->mMessageClient->peerName();
+}
+
+bool KGameNetwork::stopServerConnection()
+{
+ // We still are the Master, we just don't accept further connections!
+ tryStopPublishing();
+ if (d->mMessageServer) {
+ d->mMessageServer->stopNetwork();
+ return true;
+ }
+ return false;
+}
+
+bool KGameNetwork::isOfferingConnections() const
+{ return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); }
+
+void KGameNetwork::disconnect()
+{
+ // TODO MH
+ kdDebug(11001) << k_funcinfo << endl;
+ stopServerConnection();
+ if (d->mMessageServer) {
+ QValueList <Q_UINT32> list=d->mMessageServer->clientIDs();
+ QValueList<Q_UINT32>::Iterator it;
+ for( it = list.begin(); it != list.end(); ++it )
+ {
+ kdDebug(11001) << "Client id=" << (*it) << endl;
+ KMessageIO *client=d->mMessageServer->findClient(*it);
+ if (!client)
+ {
+ continue;
+ }
+ kdDebug(11001) << " rtti=" << client->rtti() << endl;
+ if (client->rtti()==2)
+ {
+ kdDebug(11001) << "DIRECT IO " << endl;
+ }
+ else
+ {
+ d->mMessageServer->removeClient(client,false);
+ }
+ }
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo << "before client->disconnect() id="<<gameId()<< endl;
+ //d->mMessageClient->setServer((KMessageIO*)0);
+ kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
+ d->mMessageClient->disconnect();
+
+ kdDebug(11001) << "++++++--------------------------------------------+++++"<<endl;
+ }
+ //setMaster();
+ /*
+ if (d->mMessageServer) {
+ //delete d->mMessageServer;
+ //d->mMessageServer=0;
+ server=true;
+ kdDebug(11001) << " server true" << endl;
+ d->mMessageServer->deleteClients();
+ kdDebug(11001) << " server deleteClients" << endl;
+ }
+ */
+ kdDebug(11001) << k_funcinfo << "DONE" << endl;
+}
+
+void KGameNetwork::aboutToLoseConnection(Q_UINT32 clientID)
+{
+ kdDebug(11001) << "Storing client id of connection "<<clientID<<endl;
+ d->mDisconnectId = clientID;
+}
+
+void KGameNetwork::slotResetConnection()
+{
+ kdDebug(11001) << "Resseting client disconnect id"<<endl;
+ d->mDisconnectId = 0;
+}
+
+void KGameNetwork::electAdmin(Q_UINT32 clientID)
+{
+ if (!isAdmin()) {
+ kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_ADMIN_CHANGE );
+ stream << clientID;
+ d->mMessageClient->sendServerMessage(buffer);
+}
+
+void KGameNetwork::setMaxClients(int max)
+{
+ if (!isAdmin()) {
+ kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_MAX_NUM_CLIENTS );
+ stream << (Q_INT32)max;
+ d->mMessageClient->sendServerMessage(buffer);
+}
+
+void KGameNetwork::lock()
+{
+ if (messageClient()) {
+ messageClient()->lock();
+ }
+}
+
+void KGameNetwork::unlock()
+{
+ if (messageClient()) {
+ messageClient()->unlock();
+ }
+}
+
+// --------------------- send messages ---------------------------
+
+bool KGameNetwork::sendSystemMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << data;
+ return sendSystemMessage(buffer,msgid,receiver,sender);
+}
+
+bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ stream << msg;
+ return sendSystemMessage(buffer, msgid, receiver, sender);
+}
+
+bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); }
+
+bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ if (!sender) {
+ sender = gameId();
+ }
+
+ Q_UINT32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId()
+ int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id()
+
+ KGameMessage::createHeader(stream, sender, receiver, msgid);
+ stream.writeRawBytes(data.data(), data.size());
+
+ /*
+ kdDebug(11001) << "transmitGameClientMessage msgid=" << msgid << " recv="
+ << receiver << " sender=" << sender << " Buffersize="
+ << buffer.size() << endl;
+ */
+
+ if (!d->mMessageClient) {
+ // No client created, this should never happen!
+ // Having a local game means we have our own
+ // KMessageServer and we are the only client.
+ kdWarning (11001) << k_funcinfo << "We don't have a client! Should never happen!" << endl;
+ return false;
+ }
+
+ if (receiverClient == 0 || receiverPlayer != 0)
+ {
+ // if receiverClient == 0 this is a broadcast message. if it is != 0 but
+ // receiverPlayer is also != 0 we have to send broadcast anyway, because the
+ // KPlayer object on all clients needs to receive the message.
+ d->mMessageClient->sendBroadcast(buffer);
+ }
+ else
+ {
+ d->mMessageClient->sendForward(buffer, receiverClient);
+ }
+ return true;
+}
+
+bool KGameNetwork::sendMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); }
+
+bool KGameNetwork::sendMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); }
+
+bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
+
+bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
+
+void KGameNetwork::sendError(int error,const QByteArray& message, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << (Q_INT32) error;
+ stream.writeRawBytes(message.data(), message.size());
+ sendSystemMessage(stream,KGameMessage::IdError,receiver,sender);
+}
+
+
+// ----------------- receive messages from the network
+void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, Q_UINT32 clientID)
+{
+ QDataStream stream(receiveBuffer, IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender; // the id of the KGame/KPlayer who sent the message
+ Q_UINT32 receiver; // the id of the KGame/KPlayer the message is for
+ KGameMessage::extractHeader(stream, sender, receiver, msgid);
+// kdDebug(11001) << k_funcinfo << "id=" << msgid << " sender=" << sender << " recv=" << receiver << endl;
+
+ // No broadcast : receiver==0
+ // No player isPlayer(receiver)
+ // Different game gameId()!=receiver
+ if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) )
+ {
+ // receiver=0 is broadcast or player message
+ kdDebug(11001) << k_funcinfo << "Message not meant for us "
+ << gameId() << "!=" << receiver << " rawid="
+ << KGameMessage::rawGameId(receiver) << endl;
+ return;
+ }
+ else if (msgid==KGameMessage::IdError)
+ {
+ QString text;
+ Q_INT32 error;
+ stream >> error;
+ kdDebug(11001) << k_funcinfo << "Got IdError " << error << endl;
+ text = KGameError::errorText(error, stream);
+ kdDebug(11001) << "Error text: " << text.latin1() << endl;
+ emit signalNetworkErrorMessage((int)error,text);
+ }
+ else
+ {
+ networkTransmission(stream, msgid, receiver, sender, clientID);
+ }
+}
+
+// -------------- slots for the signals of the client
+void KGameNetwork::slotAdminStatusChanged(bool isAdmin)
+{
+ emit signalAdminStatusChanged(isAdmin);
+
+// TODO: I'm pretty sure there are a lot of things that should be done here...
+}
+
+void KGameNetwork::Debug()
+{
+ kdDebug(11001) << "------------------- KNETWORKGAME -------------------------" << endl;
+ kdDebug(11001) << "gameId " << gameId() << endl;
+ kdDebug(11001) << "gameMaster " << isMaster() << endl;
+ kdDebug(11001) << "gameAdmin " << isAdmin() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgamenetwork.h b/libkdegames/kgame/kgamenetwork.h
new file mode 100644
index 00000000..6ff5cf94
--- /dev/null
+++ b/libkdegames/kgame/kgamenetwork.h
@@ -0,0 +1,431 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMENETWORK_H_
+#define __KGAMENETWORK_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <kdemacros.h>
+class KGameIO;
+class KMessageClient;
+class KMessageServer;
+
+class KGameNetworkPrivate;
+
+/**
+ * The KGameNetwork class is the KGame class with network
+ * support. All other features are the same but they are
+ * now network transparent. It is not used directly but
+ * only via a KGame object. So you do not really have
+ * to bother with this object.
+ *
+ * @short The main KDE game object
+ * @author Martin Heni <martin@heni-online.de>
+ * @version $Id$
+ */
+class KDE_EXPORT KGameNetwork : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a KGameNetwork object
+ */
+ KGameNetwork(int cookie=42,QObject* parent=0);
+ virtual ~KGameNetwork();
+
+ /**
+ * Gives debug output of the game status
+ **/
+ virtual void Debug();
+
+ /**
+ * @return TRUE if this is a network game - i.e. you are either MASTER or
+ * connected to a remote MASTER.
+ **/
+ bool isNetwork() const;
+
+ /**
+ * Is this the game MASTER (i.e. has started theKMessageServer). A
+ * game has always exactly one MASTER. This is either a KGame object (i.e. a
+ * Client) or an own MessageServer-process. A KGame object that has the
+ * MASTER status is always admin.
+ *
+ * You probably don't want to use this. It is a mostly internal method which
+ * will probably become protected. Better use isAdmin
+ *
+ * @see isAdmin
+ * @return Whether this client has started the KMessageServer
+ **/
+ bool isMaster() const;
+
+ /**
+ * The admin of a game is the one who initializes newly connected clients
+ * using negotiateNetworkGame and is allowed to configure the game.
+ * E.g. only the admin is allowed to use KGame::setMaxPlayers.
+ *
+ * If one KGame object in the game is MASTER then this client is the admin
+ * as well. isMaster and isAdmin differ only if the KMessageServer
+ * is running in an own process.
+ * @return Whether this client (KGame object) is the admin
+ **/
+ bool isAdmin() const;
+
+ /**
+ * The unique ID of this game
+ *
+ * @return int id
+ **/
+ Q_UINT32 gameId() const;
+
+ /**
+ * Inits a network game as network MASTER. Note that if the
+ * KMessageServer is not yet started it will be started here (see
+ * setMaster). Any existing connection will be disconnected.
+ *
+ * If you already offer connections the port is changed.
+ *
+ * @param port The port on which the service is offered
+ * @return true if it worked
+ **/
+ bool offerConnections (Q_UINT16 port);
+
+ /**
+ * Announces game MASTER on network using DNS-SD. Clients then can discover it using
+ * DNSSD::ServiceBrowser (or KGameConnectWidget) instead of manually entering
+ * IP address.
+ * @param type service type (something like _kwin4._tcp).
+ * It should be unique for application.
+ * @param name game name that will be displayed by clients. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ * @since 3.4
+ **/
+ void setDiscoveryInfo(const QString& type, const QString& name=QString::null);
+
+ /**
+ * Inits a network game as a network CLIENT
+ *
+ * @param host the host to which we want to connect
+ * @param port the port we want to connect to
+ *
+ * @return true if connected
+ **/
+ bool connectToServer(const QString& host, Q_UINT16 port);
+
+ /**
+ * @since 3.2
+ * @return The port we are listening to if offerConnections was called
+ * or the port we are connected to if connectToServer was called.
+ * Otherwise 0.
+ **/
+ Q_UINT16 port() const;
+
+ /**
+ * @since 3.2
+ * @return The name of the host that we are currently connected to is
+ * isNetwork is TRUE and we are not the MASTER, i.e. if connectToServer
+ * was called. Otherwise this will return "localhost".
+ **/
+ QString hostName() const;
+
+ /**
+ * Stops offering server connections - only for game MASTER
+ * @return true
+ **/
+ bool stopServerConnection();
+
+ /**
+ * Changes the maximal connection number of the KMessageServer to max.
+ * -1 Means infinite connections are possible. Note that existing
+ * connections are not affected, so even if you set this to 0 in a running
+ * game no client is being disconnected. You can call this only if you are
+ * the ADMIN!
+ *
+ * @see KMessageServer::setMaxClients
+ * @param max The maximal number of connections possible.
+ **/
+ void setMaxClients(int max);
+
+ //AB: is this now internal only? Can we make it protected (maybe with
+ //friends)? sendSystemMessage AND sendMessage is very confusing to the
+ //user.
+ /**
+ * Sends a network message msg with a given msg id msgid to all clients.
+ * Use this to communicate with KGame (e.g. to add a player ot to configure
+ * the game - usually not necessary).
+ *
+ * For your own messages use sendMessage instead! This is mostly
+ * internal!
+ *
+ * @param buffer the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message. See
+ * KGameMessage::GameMessageIds
+ * @param receiver the KGame / KPlayer this message is for.
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You
+ * probably want to leave this 0, then KGameNetwork will create the correct
+ * value for you. You might want to use this if you send a message from a
+ * specific player.
+ * @return true if worked
+ */
+ // AB: TODO: doc on how "receiver" and "sender" should be created!
+ bool sendSystemMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * Sends a network message
+ * @param error The error code
+ * @param message The error message - use KGameError
+ * @param receiver the KGame / KPlayer this message is for. 0 For
+ * all
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You probably want to leave this 0, then KGameNetwork will create
+ * the correct value for you. You might want to use this if you send a
+ * message from a specific player.
+ **/
+ void sendError(int error, const QByteArray& message, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * Are we still offer offering server connections - only for game MASTER
+ * @return true/false
+ **/
+ bool isOfferingConnections() const;
+
+ /**
+ * Application cookie. this idendifies the game application. It
+ * help to distinguish between e.g. KPoker and KWin4
+ * @return the application cookie
+ **/
+ int cookie() const;
+
+ /**
+ * Send a network message msg with a given message ID msgid to all clients.
+ * You want to use this to send a message to the clients.
+ *
+ * Note that a message is always sent to ALL clients! This is necessary so
+ * that all clients always have the same data and can easily be changed from
+ * network to non-network without restarting the game. If you want a
+ * specific KGame / KPlayer to react to the message use the
+ * receiver and sender parameters. See KGameMessage::calsMessageId
+ *
+ * SendMessage differs from sendSystemMessage only by the msgid parameter.
+ * sendSystemMessage is thought as a KGame only mehtod while
+ * sendMessage is for public use. The msgid parameter will be
+ * +=KGameMessage::IdUser and in KGame::signalNetworkData msgid will
+ * be -= KGameMessage::IdUser again, so that one can easily distinguish
+ * between system and user messages.
+ *
+ * Use sendSystemMessage to comunicate with KGame (e.g. by adding a
+ * player) and sendMessage for your own user message.
+ *
+ * Note: a player should send messages through a KGameIO!
+ *
+ * @param buffer the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message. See KGameMessage::GameMessageIds
+ * @param receiver the KGame / KPlayer this message is for.
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You
+ * probably want to leave this 0, then KGameNetwork will create the correct
+ * value for you. You might want to use this if you send a message from a
+ * specific player.
+ * @return true if worked
+ **/
+ // AB: TODO: doc on how "receiver" and "sender" should be created!
+ bool sendMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+
+ /**
+ * Called by ReceiveNetworkTransmission(). Will be overwritten by
+ * KGame and handle the incoming message.
+ **/
+ virtual void networkTransmission(QDataStream&, int, Q_UINT32, Q_UINT32, Q_UINT32 clientID) = 0;
+
+
+ /**
+ * Disconnect the current connection and establish a new local one.
+ **/
+ void disconnect();
+
+
+ /**
+ * If you are the ADMIN of the game you can give the ADMIN status away to
+ * another client. Use this e.g. if you want to quit the game or if you want
+ * another client to administrate the game (note that disconnect calls
+ * this automatically).
+ * @param clientID the ID of the new ADMIN (note: this is the _client_ID
+ * which has nothing to do with the player IDs. See KMessageServer)
+ **/
+ void electAdmin(Q_UINT32 clientID);
+
+ /**
+ * Don't use this unless you really know what youre doing! You might
+ * experience some strange behaviour if you send your messages directly
+ * through the KMessageClient!
+ *
+ * @return a pointer to the KMessageClient used internally to send the
+ * messages. You should rather use one of the send functions!
+ **/
+ KMessageClient* messageClient() const;
+
+ /**
+ * Don't use this unless you really know what you are doing! You might
+ * experience some strange behaviour if you use the message server directly!
+ *
+ * @return a pointer to the message server if this is the MASTER KGame
+ * object. Note that it might be possible that no KGame object contains
+ * the KMessageServer at all! It might even run stand alone!
+ **/
+ KMessageServer* messageServer() const;
+
+ /**
+ * You should call this before doing thigs like, e.g. qApp->processEvents().
+ * Don't forget to call unlock once you are done!
+ *
+ * @see KMessageClient::lock
+ **/
+ virtual void lock();
+
+ /**
+ * @see KMessageClient::unlock
+ **/
+ virtual void unlock();
+
+signals:
+ /**
+ * A network error occurred
+ * @param error the error code
+ * @param text the error text
+ */
+ void signalNetworkErrorMessage(int error, QString text);
+
+ /**
+ * Our connection to the KMessageServer has broken.
+ * See KMessageClient::connectionBroken
+ **/
+ void signalConnectionBroken();
+
+ /**
+ * This signal is emitted whenever the KMessageServer sends us a message that a
+ * new client connected. KGame uses this to call KGame::negotiateNetworkGame
+ * for the newly connected client if we are admin (see isAdmin)
+ *
+ * @see KMessageClient::eventClientConnected
+ *
+ * @param clientID the ID of the newly connected client
+ **/
+ void signalClientConnected(Q_UINT32 clientID);
+
+ /**
+ * This signal is emitted whenever the KMessageServer sends us a message
+ * that a connection to a client was detached. The second parameter can be used
+ * to distinguish between network errors or removing on purpose.
+ *
+ * @see KMessageClient::eventClientDisconnected
+ *
+ * @param clientID the client that has disconnected
+ * @param broken true if the connection was lost because of a network error, false
+ * if the connection was closed by the message server admin.
+ */
+ void signalClientDisconnected(Q_UINT32 clientID, bool broken);
+
+ /**
+ * This client gets or loses the admin status.
+ * @see KMessageClient::adminStatusChanged
+ * @param isAdmin True if this client gets the ADMIN status otherwise FALSE
+ **/
+ void signalAdminStatusChanged(bool isAdmin);
+
+protected:
+ /**
+ * @internal
+ * Start a KMessageServer object and use it as the MASTER of the game.
+ * Note that you must not call this if there is already another master
+ * running!
+ **/
+ void setMaster();
+
+protected slots:
+ /**
+ * Called by KMessageClient::broadcastReceived() and will check if the
+ * message format is valid. If it is not, it will generate an error (see
+ * signalNetworkVersionError and signalNetworkErorrMessage).
+ * If it is valid, the pure virtual method networkTransmission() is called.
+ * (This one is overwritten in KGame.)
+ **/
+ void receiveNetworkTransmission(const QByteArray& a, Q_UINT32 clientID);
+
+ /**
+ * This KGame object receives or loses the admin status.
+ * @param isAdmin Whether we are admin or not
+ **/
+ void slotAdminStatusChanged(bool isAdmin);
+
+ /**
+ * Called when the network connection is about to terminate. Is used
+ * to store the network parameter like the game id
+ */
+ void aboutToLoseConnection(Q_UINT32 id);
+
+ /**
+ * Called when the network connection is terminated. Used to clean
+ * up the disconnect parameter
+ */
+ void slotResetConnection();
+
+
+private:
+ void tryPublish();
+ void tryStopPublishing();
+ KGameNetworkPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameprocess.cpp b/libkdegames/kgame/kgameprocess.cpp
new file mode 100644
index 00000000..96efe0ce
--- /dev/null
+++ b/libkdegames/kgame/kgameprocess.cpp
@@ -0,0 +1,158 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameprocess.h"
+#include "kplayer.h"
+#include "kgame.h"
+#include "kgamemessage.h"
+#include "kmessageio.h"
+
+#include <krandomsequence.h>
+
+#include <qbuffer.h>
+#include <qdatastream.h>
+#include <qcstring.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define READ_BUFFER_SIZE 1024
+
+// ----------------------- Process Child ---------------------------
+
+KGameProcess::KGameProcess() : QObject(0,0)
+{
+ mTerminate=false;
+ // Check whether a player is set. If not create one!
+ rFile.open(IO_ReadOnly|IO_Raw,stdin);
+ wFile.open(IO_WriteOnly|IO_Raw,stdout);
+ mMessageIO=new KMessageFilePipe(this,&rFile,&wFile);
+// mMessageClient=new KMessageClient(this);
+// mMessageClient->setServer(mMessageIO);
+// connect (mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+// this, SLOT(receivedMessage(const QByteArray&, Q_UINT32)));
+ connect (mMessageIO, SIGNAL(received(const QByteArray&)),
+ this, SLOT(receivedMessage(const QByteArray&)));
+ fprintf(stderr,"KGameProcess::constructor %p %p\n",&rFile,&wFile);
+
+ mRandom = new KRandomSequence;
+ mRandom->setSeed(0);
+}
+KGameProcess::~KGameProcess()
+{
+ delete mRandom;
+ //delete mMessageClient;
+ //delete mMessageServer;
+ delete mMessageIO;
+ rFile.close();
+ wFile.close();
+ fprintf(stderr,"KGameProcess::destructor\n");
+}
+
+
+bool KGameProcess::exec(int argc, char *argv[])
+{
+ // Get id and cookie, ... from command line
+ processArgs(argc,argv);
+ do
+ {
+ mMessageIO->exec();
+ } while(!mTerminate);
+ return true;
+}
+
+// You have to do this to create a message
+// QByteArray buffer;
+// QDataStream wstream(buffer,IO_WriteOnly);
+// then stream data into the stream and call this function
+void KGameProcess::sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver)
+{
+ fprintf(stderr,"KGameProcess::sendMessage id=%d recv=%d",msgid,receiver);
+ QByteArray a;
+ QDataStream outstream(a,IO_WriteOnly);
+
+ QBuffer *device=(QBuffer *)stream.device();
+ QByteArray data=device->buffer();;
+
+ KGameMessage::createHeader(outstream,0,receiver,msgid);
+ outstream.writeRawBytes(data.data(),data.size());
+
+ //if (mMessageClient) mMessageClient->sendBroadcast(a);
+ // TODO: The fixed received 2 will cause problems. But how to address the
+ // proper one?
+// if (mMessageClient) mMessageClient->sendForward(a,2);
+ if (mMessageIO) mMessageIO->send(a);
+}
+
+void KGameProcess::sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver)
+{
+ sendSystemMessage(stream,msgid+KGameMessage::IdUser,receiver);
+}
+
+void KGameProcess::processArgs(int argc, char *argv[])
+{
+ int v=0;
+ if (argc>2)
+ {
+ v=atoi(argv[2]);
+ //kdDebug(11001) << "cookie (unused) " << v << endl;
+ }
+ if (argc>1)
+ {
+ v=atoi(argv[1]);
+ //kdDebug(11001) << "id (unused) " << v << endl;
+ }
+ fprintf(stderr,"processArgs \n");
+ fflush(stderr);
+}
+
+void KGameProcess::receivedMessage(const QByteArray& receiveBuffer)
+{
+ QDataStream stream(receiveBuffer, IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender;
+ Q_UINT32 receiver;
+ KGameMessage::extractHeader(stream, sender, receiver, msgid);
+ fprintf(stderr,"------ receiveNetworkTransmission(): id=%d sender=%d,recv=%d\n",msgid,sender,receiver);
+ switch(msgid)
+ {
+ case KGameMessage::IdTurn:
+ Q_INT8 b;
+ stream >> b;
+ emit signalTurn(stream,(bool)b);
+ break;
+ case KGameMessage::IdIOAdded:
+ Q_INT16 id;
+ stream >> id;
+ emit signalInit(stream,(int)id);
+ break;
+ default:
+ emit signalCommand(stream,msgid-KGameMessage::IdUser,receiver,sender);
+ break;
+ }
+}
+
+#include "kgameprocess.moc"
diff --git a/libkdegames/kgame/kgameprocess.h b/libkdegames/kgame/kgameprocess.h
new file mode 100644
index 00000000..a8db4fcd
--- /dev/null
+++ b/libkdegames/kgame/kgameprocess.h
@@ -0,0 +1,242 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEPROCESS_H_
+#define __KGAMEPROCESS_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qfile.h>
+
+#include "kgameproperty.h"
+#include <krandomsequence.h>
+#include <kdemacros.h>
+class KPlayer;
+class KMessageFilePipe;
+
+/**
+ * This is the process class used on the computer player
+ * side to communicate with its counterpart KProcessIO class.
+ * Using these two classes will give fully transparent communication
+ * via QDataStreams.
+ */
+class KDE_EXPORT KGameProcess: public QObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Creates a KGameProcess class. Done only in the computer
+ * player. To activate the communication you have to call
+ * the exec function of this class which will listen
+ * to the communication and emit signals to notify you of
+ * any incoming messages.
+ * Note: This function will only return after you set
+ * setTerminate(true) in one of the received signals.
+ * So you can not do any computer calculation after the exec function.
+ * Instead you react on the signals which are emitted after a
+ * message is received and perform the calculations there!
+ * Example:
+ * \code
+ * int main(int argc ,char * argv[])
+ * {
+ * KGameProcess proc;
+ * connect(&proc,SIGNAL(signalCommand(QDataStream &,int ,int ,int )),
+ * this,SLOT(slotCommand(QDataStream & ,int ,int ,int )));
+ * connect(&proc,SIGNAL(signalInit(QDataStream &,int)),
+ * this,SLOT(slotInit(QDataStream & ,int )));
+ * connect(&proc,SIGNAL(signalTurn(QDataStream &,bool )),
+ * this,SLOT(slotTurn(QDataStream & ,bool )));
+ * return proc.exec(argc,argv);
+ * }
+ * \endcode
+ */
+ KGameProcess();
+ /**
+ * Destruct the process
+ */
+ ~KGameProcess();
+
+ /**
+ * Enters the event loop of the computer process. Does only
+ * return on setTerminate(true)!
+ */
+ bool exec(int argc, char *argv[]);
+
+ /**
+ * Should the computer process leave its exec function?
+ * Activated if you setTerminate(true);
+ *
+ * @return true/false
+ */
+ bool terminate() const {return mTerminate;}
+
+ /**
+ * Set this to true if the computer process should end, ie
+ * leave its exec function.
+ *
+ * @param b true for exit the exec function
+ */
+ void setTerminate(bool b) {mTerminate=b;}
+
+ /**
+ * Sends a message to the corresponding KGameIO
+ * device. Works like the sendSystemMessage but
+ * for user id's
+ *
+ * @param stream the QDataStream containing the message
+ * @param msgid the message id for the message
+ * @param receiver unused
+ */
+ void sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0);
+
+ /**
+ * Sends a system message to the corresonding KGameIO device.
+ * This will normally be either a performed move or a query
+ * (IdProcessQuery). The query option is a way to communicate
+ * with the KGameIO at the other side and e.g. retrieve some
+ * game relevant data from here.
+ * Exmaple for a query:
+ * \code
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * int msgid=KGameMessage::IdProcessQuery;
+ * out << (int)1;
+ * proc.sendSystemMessage(out,msgid,0);
+ * \endcode
+ *
+ * @param stream the QDataStream containing the message
+ * @param msgid the message id for the message
+ * @param receiver unused
+ */
+ void sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0);
+
+ /**
+ * Returns a pointer to a KRandomSequence. You can generate
+ * random numbers via e.g.
+ * \code
+ * random()->getLong(100);
+ * \endcode
+ *
+ * @return KRandomSequence pointer
+ */
+ KRandomSequence *random() {return mRandom;}
+
+ protected:
+ /**
+ * processes the command line argumens to set up the computer player
+ * Pass the argumens exactely as given by main()
+ */
+ void processArgs(int argc, char *argv[]);
+
+ protected slots:
+ /**
+ * A message is received via the interprocess connection. The
+ * appropriate signals are called.
+ */
+ void receivedMessage(const QByteArray& receiveBuffer);
+
+ signals:
+ /**
+ * The generic communication signal. You have to connect to this
+ * signal to generate a valid computer response onto arbitrary messages.
+ * All signals but IdIOAdded and IdTurn end up here!
+ * Example:
+ * \code
+ * void Computer::slotCommand(int &msgid,QDataStream &in,QDataStream &out)
+ * {
+ * Q_INT32 data,move;
+ * in >> data;
+ * // compute move ...
+ * move=data*2;
+ * out << move;
+ * }
+ * \endcode
+ *
+ * @param inputStream the incoming data stream
+ * @param msgid the message id of the message which got transmitted to the computer
+ * @param receiver the id of the receiver
+ * @param sender the id of the sender
+ */
+ void signalCommand(QDataStream &inputStream,int msgid,int receiver,int sender);
+
+ /**
+ * This signal is emmited if the computer player should perform a turn.
+ * Calculations can be made here and the move can then be send back with
+ * sendSystemMessage with the message id KGameMessage::IdPlayerInput.
+ * These must provide a move which complies to your other move syntax as
+ * e.g. produces by keyboard or mouse input.
+ * Additonal data which have been written into the stream from the
+ * ProcessIO's signal signalPrepareTurn can be retrieved from the
+ * stream here.
+ * Example:
+ * \code
+ * void slotTurn(QDataStream &in,bool turn)
+ * {
+ * int id;
+ * int recv;
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * if (turn)
+ * {
+ * // Create a move - the format is yours to decide
+ * // It arrives exactly as this in the kgame inputMove function!!
+ * Q_INT8 x1,y1,pl;
+ * pl=-1;
+ * x1=proc.random()->getLong(8);
+ * y1=proc.random()->getLong(8);
+ * // Stream it
+ * out << pl << x1 << y1;
+ * id=KGameMessage::IdPlayerInput;
+ * proc.sendSystemMessage(out,id,0);
+ * }
+ * }
+ * \endcode
+ *
+ * @param stream The datastream which contains user data
+ * @param turn True or false whether the turn is activated or deactivated
+ *
+ */
+ void signalTurn(QDataStream &stream,bool turn);
+
+ /**
+ * This signal is emmited when the process is initialized, i.e. added
+ * to a KPlayer. Initial initialisation can be performed here be reacting
+ * to the KProcessIO signal signalIOAdded and retrieving the data here
+ * from the stream.
+ * It works just as the signalTurn() but is only send when the player is
+ * added to the game, i.e. it needs some initialization data
+ *
+ * @param stream The datastream which contains user data
+ * @param userid The userId of the player. (Careful to rely on it yet)
+ */
+ void signalInit(QDataStream &stream,int userid);
+
+ protected:
+ bool mTerminate;
+ KMessageFilePipe *mMessageIO;
+ private:
+ QFile rFile;
+ QFile wFile;
+ KRandomSequence* mRandom;
+};
+#endif
diff --git a/libkdegames/kgame/kgameproperty.cpp b/libkdegames/kgame/kgameproperty.cpp
new file mode 100644
index 00000000..68a33bcb
--- /dev/null
+++ b/libkdegames/kgame/kgameproperty.cpp
@@ -0,0 +1,211 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+#include "kgamemessage.h"
+#include "kplayer.h"
+#include "kgame.h"
+
+#define KPLAYERHANDLER_LOAD_COOKIE 6239
+
+KGamePropertyBase::KGamePropertyBase(int id, KGame* parent)
+{
+ init();
+ registerData(id, parent);
+}
+
+KGamePropertyBase::KGamePropertyBase(int id, KPlayer* parent)
+{
+ init();
+ registerData(id, parent);
+}
+
+KGamePropertyBase::KGamePropertyBase(int id, KGamePropertyHandler* owner)
+{
+ init();
+ registerData(id, owner);
+}
+
+KGamePropertyBase::KGamePropertyBase()
+{
+ init();
+}
+
+KGamePropertyBase::~KGamePropertyBase()
+{
+ unregisterData();
+}
+
+void KGamePropertyBase::init()
+{
+ mOwner = 0;
+ setDirty(false);
+
+ // this is very useful and used by e.g. KGameDialog so
+ // it is activated by default. Big games may profit by deactivating it to get
+ // a better performance.
+ setEmittingSignal(true);
+
+ setOptimized(false);
+
+ //setReadOnly(false);
+ mFlags.bits.locked = false ; // setLocked(false); is NOT possible as it checks whether isLocked() allows to change the status
+
+ // local is default
+ setPolicy(PolicyLocal);
+}
+
+int KGamePropertyBase::registerData(int id, KGame* owner, QString name)
+{ return registerData(id, owner->dataHandler(), name); }
+
+int KGamePropertyBase::registerData(int id, KPlayer* owner, QString name)
+{ return registerData(id, owner->dataHandler(), name); }
+
+int KGamePropertyBase::registerData( KGamePropertyHandler* owner,PropertyPolicy p, QString name)
+{ return registerData(-1, owner,p, name); }
+
+int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner, QString name)
+{ return registerData(id, owner,PolicyUndefined, name); }
+
+int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name)
+{
+// we don't support changing the id
+ if (!owner) {
+ kdWarning(11001) << k_funcinfo << "Resetting owner=0. Sure you want to do this?" << endl;
+ mOwner=0;
+ return -1;
+ }
+ if (!mOwner) {
+ if (id==-1) {
+ id=owner->uniquePropertyId();
+ }
+ mId = id;
+ mOwner = owner;
+ mOwner->addProperty(this, name);
+ if (p!=PolicyUndefined) {
+ setPolicy(p);
+ } else {
+ setPolicy(mOwner->policy());
+ }
+ }
+ return mId;
+}
+
+void KGamePropertyBase::unregisterData()
+{
+ if (!mOwner) {
+ return;
+ }
+ mOwner->removeProperty(this);
+ mOwner = 0;
+}
+
+bool KGamePropertyBase::sendProperty()
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyHeader(s, id());
+ save(s);
+ if (mOwner) {
+ return mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << "Cannot send because there is no receiver defined" << endl;
+ return false;
+ }
+}
+
+bool KGamePropertyBase::sendProperty(const QByteArray& data)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyHeader(s, id());
+ s.writeRawBytes(data.data(), data.size());
+ if (mOwner) {
+ return mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl;
+ return false;
+ }
+}
+
+bool KGamePropertyBase::lock()
+{
+ if (isLocked()) {
+ return false;
+ }
+ setLock(true);
+ return true;
+}
+
+bool KGamePropertyBase::unlock(bool force)
+{
+ if (isLocked() && !force) {
+ return false;
+ }
+ setLock(false);
+ return true;
+}
+
+void KGamePropertyBase::setLock(bool l)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s, IdCommand, id(), CmdLock);
+
+ s << (Q_INT8)l;
+ if (mOwner) {
+ mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl;
+ return ;
+ }
+}
+
+void KGamePropertyBase::emitSignal()
+{
+ //kdDebug(11001) << k_funcinfo << ": mOwnerP="<< mOwner << " id=" << id() << endl;
+ if (mOwner ) {
+ mOwner->emitSignal(this);
+ } else {
+ kdError(11001) << k_funcinfo << ":id="<<id()<<" Cannot emitSignal because there is no handler set" << endl;
+ }
+}
+
+void KGamePropertyBase::command(QDataStream& s, int cmd, bool isSender)
+{
+ switch (cmd) {
+ case CmdLock:
+ {
+ if (!isSender) {
+ Q_INT8 locked;
+ s >> locked;
+ mFlags.bits.locked = (bool)locked ;
+ break;
+ }
+ }
+ default: // probably in derived classes
+ break;
+ }
+}
+
diff --git a/libkdegames/kgame/kgameproperty.h b/libkdegames/kgame/kgameproperty.h
new file mode 100644
index 00000000..c6915606
--- /dev/null
+++ b/libkdegames/kgame/kgameproperty.h
@@ -0,0 +1,848 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTY_H_
+#define __KGAMEPROPERTY_H_
+
+#include <qdatastream.h>
+
+#include <kdebug.h>
+#include <typeinfo>
+#include <kdemacros.h>
+class KGame;
+class KPlayer;
+class KGamePropertyHandler;
+using namespace std;
+
+/**
+ * @short Base class of KGameProperty
+ *
+ * The KGamePropertyBase class is the base class of KGameProperty. See
+ * KGameProperty for further information.
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KDE_EXPORT KGamePropertyBase
+{
+public:
+ enum PropertyDataIds { // these belong to KPlayer/KGame!
+ //KPlayer
+ IdGroup=1,
+ IdUserId=2,
+ IdAsyncInput=3,
+ IdTurn=4,
+ IdName=5,
+
+ //KGame
+ IdGameStatus=6,
+ IdMaxPlayer=7,
+ IdMinPlayer=8,
+
+ // Input Grabbing
+ IdGrabInput=16,
+ IdReleaseInput=17,
+
+ IdCommand, // Reserved for internal use
+ IdUser=256,
+
+ IdAutomatic=0x7000 // Id's from here on are automatically given (16bit)
+ };
+
+ /**
+ * Commands for advanced properties (Q_INT8)
+ **/
+ enum PropertyCommandIds
+ {
+ // General
+ CmdLock=1,
+
+ // Array
+ CmdAt=51,
+ CmdResize=52,
+ CmdFill=53,
+ CmdSort=54,
+ // List (could be the same id's actually)
+ CmdInsert=61,
+ CmdAppend=62,
+ CmdRemove=63,
+ CmdClear=64
+ };
+
+ /**
+ * The policy of the property. This can be PolicyClean (setValue uses
+ * send), PolicyDirty (setValue uses changeValue) or
+ * PolicyLocal (setValue uses setLocal).
+ *
+ * A "clean" policy means that the property is always the same on every
+ * client. This is achieved by calling send which actually changes
+ * the value only when the message from the MessageServer is received.
+ *
+ * A "dirty" policy means that as soon as setValue is called the
+ * property is changed immediately. And additionally sent over network.
+ * This can sometimes lead to bugs as the other clients do not
+ * immediately have the same value. For more information see
+ * changeValue.
+ *
+ * PolicyLocal means that a KGameProperty behaves like ever
+ * "normal" variable. Whenever setValue is called (e.g. using "=")
+ * the value of the property is changes immediately without sending it
+ * over network. You might want to use this if you are sure that all
+ * clients set the property at the same time.
+ **/
+ enum PropertyPolicy
+ {
+ PolicyUndefined = 0,
+ PolicyClean = 1,
+ PolicyDirty = 2,
+ PolicyLocal = 3
+ };
+
+
+ /**
+ * Constructs a KGamePropertyBase object and calls registerData.
+ * @param id The id of this property. MUST be UNIQUE! Used to send and
+ * receive changes in the property of the playere automatically via
+ * network.
+ * @param owner The owner of the object. Must be a KGamePropertyHandler which manages
+ * the changes made to this object, i.e. which will send the new data
+ **/
+ KGamePropertyBase(int id, KGamePropertyHandler* owner);
+
+ KGamePropertyBase(int id, KGame* parent);
+ KGamePropertyBase(int id, KPlayer* parent);
+
+ /**
+ * Creates a KGamePropertyBase object without an owner. Remember to call
+ * registerData!
+ **/
+ KGamePropertyBase();
+
+ virtual ~KGamePropertyBase();
+
+ /**
+ * Changes the consistency policy of a property. The
+ * PropertyPolicy is one of PolicyClean (defaulz), PolicyDirty or PolicyLocal.
+ *
+ * It is up to you to decide how you want to work.
+ **/
+ void setPolicy(PropertyPolicy p) { mFlags.bits.policy = p; }
+
+ /**
+ * @return The default policy of the property
+ **/
+ PropertyPolicy policy() const { return (PropertyPolicy)mFlags.bits.policy; }
+
+ /**
+ * Sets this property to emit a signal on value changed.
+ * As the proerties do not inehrit QObject for optimisation
+ * this signal is emited via the KPlayer or KGame object
+ **/
+ void setEmittingSignal(bool p) { mFlags.bits.emitsignal=p; }
+
+ /**
+ * See also setEmittingSignal
+ * @return Whether this property emits a signal on value change
+ **/
+ bool isEmittingSignal() const { return mFlags.bits.emitsignal; }
+
+ /**
+ * Sets this property to try to optimize signal and network handling
+ * by not sending it out when the property value is not changed.
+ **/
+ void setOptimized(bool p) { mFlags.bits.optimize = p ; }
+
+ /**
+ * See also setOptimize
+ * @return Whether the property optimizes access (signals,network traffic)
+ **/
+ bool isOptimized() const { return mFlags.bits.optimize; }
+
+ /**
+ * @return Whether this property is "dirty". See also setDirty
+ **/
+ bool isDirty() const { return mFlags.bits.dirty; }
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock. See also setLocked
+ * @return Whether this property is currently locked.
+ **/
+ bool isLocked() const { return mFlags.bits.locked; }
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ *
+ * @return returns false if the property can not be locked, i.e. it is already locked
+ *
+ **/
+ bool lock();
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ *
+ * @return returns false if the property can not be locked, i.e. it is already locked
+ *
+ **/
+ bool unlock(bool force=false);
+
+ /**
+ * This will read the value of this property from the stream. You MUST
+ * overwrite this method in order to use this class
+ * @param s The stream to read from
+ **/
+ virtual void load(QDataStream& s) = 0;
+
+ /**
+ * Write the value into a stream. MUST be overwritten
+ **/
+ virtual void save(QDataStream& s) = 0;
+
+ /**
+ * send a command to advanced properties like arrays
+ * @param stream The stream containing the data of the comand
+ * @param msgid The ID of the command - see PropertyCommandIds
+ * @param isSender whether this client is also the sender of the command
+ **/
+ virtual void command(QDataStream &stream, int msgid, bool isSender=false);
+
+ /**
+ * @return The id of this property
+ **/
+ int id() const { return mId; }
+
+ /**
+ * @return a type_info of the data this property contains. This is used
+ * e.g. by KGameDebugDialog
+ **/
+ virtual const type_info* typeinfo() { return &typeid(this); }
+
+ /**
+ * You have to register a KGamePropertyBase before you can use it.
+ *
+ * You MUST call this before you can use KGamePropertyBase!
+ *
+ * @param id the id of this KGamePropertyBase object. The id MUST be
+ * unique, i.e. you cannot have two properties with the same id for one
+ * player, although (currently) nothing prevents you from doing so. But
+ * you will get strange results!
+ *
+ * @param owner The owner of this data. This will send the data
+ * using KPropertyHandler::sendProperty whenever you call send
+ *
+ * @param p If not 0 you can set the policy of the property here
+ *
+ * @param name if not 0 you can assign a name to this property
+ *
+ **/
+ int registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KGamePropertyHandler* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KGame* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KPlayer* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ * In particular you can use this function to create properties which
+ * will have an automatic id assigned. The new id is returned.
+ **/
+ int registerData(KGamePropertyHandler* owner,PropertyPolicy p=PolicyUndefined, QString name=0);
+
+ void unregisterData();
+
+
+protected:
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ * Usually you use lock and unlock to access this property
+ *
+ **/
+ void setLock(bool l);
+
+ /**
+ * Sets the "dirty" flag of the property. If a property is "dirty" i.e.
+ * KGameProperty::setLocal has been called there is no guarantee
+ * that all clients share the same value. You have to ensure this
+ * yourself e.g. by calling KGameProperty::setLocal on every
+ * client. You can also ignore the dirty flag and continue working withe
+ * the property depending on your situation.
+ **/
+ void setDirty(bool d) { mFlags.bits.dirty = d ; }
+
+ /**
+ * Forward the data to the owner of this property which then sends it
+ * over network. save is used to store the data into a stream so
+ * you have to make sure that function is working properly if you
+ * implement your own property!
+ *
+ * Note: this sends the <em>current</em> property!
+ *
+ * Might be obsolete - KGamePropertyArray still uses it. Is this a bug
+ * or correct?
+ **/
+ bool sendProperty();
+
+ /**
+ * Forward the data to the owner of this property which then sends it
+ * over network. save is used to store the data into a stream so
+ * you have to make sure that function is working properly if you
+ * implement your own property!
+ *
+ * This function is used by send to send the data over network.
+ * This does <em>not</em> send the current value but the explicitly
+ * given value.
+ *
+ * @return TRUE if the message could be sent successfully, otherwise
+ * FALSE
+ **/
+ bool sendProperty(const QByteArray& b);
+
+ /**
+ * Causes the parent object to emit a signal on value change
+ **/
+ void emitSignal();
+
+protected:
+ KGamePropertyHandler* mOwner;
+
+ // Having this as a union of the bitfield and the char
+ // allows us to stream this quantity easily (if we need to)
+ // At the moment it is not yet transmitted
+ union Flags {
+ char flag;
+ struct {
+ // unsigned char dosave : 1; // do save this property
+ // unsigned char delaytransmit : 1; // do not send immediately on
+ // change but a KPlayer:QTimer
+ // sends it later on - fast
+ // changing variables
+ unsigned char emitsignal : 1; // KPlayer notifies on variable change (true)
+ //unsigned char readonly : 1; // whether the property can be changed (false)
+ unsigned char optimize : 1; // whether the property tries to optimize send/emit (false)
+ unsigned char dirty: 1; // whether the property dirty (setLocal() was used)
+ unsigned char policy : 2; // whether the property is always consistent (see PropertyPolicy)
+ unsigned char locked: 1; // whether the property is locked (true)
+ } bits;
+ } mFlags;
+
+private:
+ friend class KGamePropertyHandler;
+ void init();
+
+private:
+ int mId;
+
+};
+
+/**
+ * @short A class for network transparent games
+ *
+ * Note: The entire API documentation is obsolete!
+ *
+ * The class KGameProperty can store any form of data and will transmit it via
+ * network whenver you call send. This makes network transparent games
+ * very easy. You first have to register the data to a KGamePropertyHandler
+ * using KGamePropertyBase::registerData (which is called by the
+ * constructor). For the KGamePropertyHandler you can use
+ * KGame::dataHandler or KPlayer::dataHandler but you can also create your
+ * own data handler.
+ *
+ * There are several concepts you can follow when writing network games. These
+ * concepts differ completely from the way how data is transferred so you should
+ * decide which one to use. You can also mix these concepts for a single
+ * property but we do not recommend this. The concepts:
+ * <ul>
+ * <li> Always Consistent (clean)
+ * <li> Not Always Consistent (dirty)
+ * <li> A Mixture (very dirty)
+ * </ul>
+ * I repeat: we do <em>not</em> recommend the third option ("a mixture"). Unless
+ * you have a good reason for this you will probably introduce some hard to find
+ * (and to fix) bugs.
+ *
+ * @section Always consistent (clean):
+ *
+ * This "policy" is default. Whenever you create a KGameProperty it is always
+ * consistent. This means that consistency is the most important thing for the
+ * property. This is achieved by using send to change the value of the
+ * property. send needs a running KMessageServer and therefore
+ * <em>MUST</em> be plugged into a KGamePropertyHandler using either
+ * registerData or the constructor. The parent of the dataHandler must be able
+ * to send messages (see above: the message server must be running). If you use
+ * send to change the value of a property you won't see the effect
+ * immediately: The new value is first transferred to the message server which
+ * queues the message. As soon as <em>all</em> messages in the message server
+ * which are before the changed property have been transferred the message
+ * server delivers the new value of the KGameProperty to all clients. A
+ * QTimer::singleShot is used to queue the messages inside the
+ * KMessageServer.
+ *
+ * This means that if you do the following:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * int value = myProperty.value();
+ * \endcode
+ * then "value" will be "0". initData is used to initialize the property
+ * (e.g. when the KMessageServer is not yet running or can not yet be
+ * reached). This is because "myProperty = 10" or "myProperty.send(10)" send a
+ * message to the KMessageServer which uses QTimer::singleShot to
+ * queue the message. The game first has to go back into the event loop where
+ * the message is received. The KGamePropertyHandler receives the new value
+ * sets the property. So if you need the new value you need to store it in a
+ * different variable (see setLocal which creates one for you until the
+ * message is received). The KGamePropertyHandler emits a signal (unless
+ * you called setEmitSignal with false) when the new value is received:
+ * KGamePropertyHandler::signalPropertyChanged. You can use this to react
+ * to a changed property.
+ *
+ * This may look quite confusing but it has a <em>big</em> advantage: all
+ * KGameProperty objects are ensured to have the same value on all clients in
+ * the game at every time. This way you will save you a lot of trouble as
+ * debugging can be very difficult if the value of a property changes
+ * immediately on client A but only after one or two additianal messages
+ * (function calls, status changes, ...) on client B.
+ *
+ * The only disadvantage of this (clean) concept is that you cannot use a
+ * changed variable immediately but have to wait for the KMessageServer to
+ * change it. You probably want to use
+ * KGamePropertyHandler::signalPropertyChanged for this.
+ *
+ * @section Not Always Consistent (dirty):
+ *
+ * There are a lot of people who don't want to use the (sometimes quite complex)
+ * "clean" way. You can use setAlwaysConsistent to change the default
+ * behaviour of the KGameProperty. If a property is not always consistent
+ * it will use changeValue to send the property. changeValue also uses
+ * send to send the new value over network but it also uses
+ * setLocal to create a local copy of the property. This copy is created
+ * dynamically and is deleted again as soon as the next message from the network
+ * is received. To use the example above again:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.setAlwaysConsistent(false);
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * int value = myProperty.value();
+ * \endcode
+ * Now this example will "work" so value now is 10. Additionally the
+ * KMessageServer receives a message from the local client (just as explained
+ * above in "Always Consistent"). As soon as the message returns to the local
+ * client again the local value is deleted, as the "network value" has the same
+ * value as the local one. So you won't lose the ability to use the always
+ * consistent "clean" value of the property if you use the "dirty" way. Just use
+ * networkValue to access the value which is consistent among all clients.
+ *
+ * The advantage of this concept is clear: you can use a KGameProperty as
+ * every other variable as the changes value takes immediate effect.
+ * Additionally you can be sure that the value is transferred to all clients.
+ * You will usually not experience serious bugs just because you use the "dirty"
+ * way. Several events have to happen at once to get these "strange errors"
+ * which result in inconsistent properties (like "game running" on client A but
+ * "game ended/paused" on client B). But note that there is a very good reason
+ * for the existence of these different concepts of KGameProperty. I have
+ * myself experienced such a "strange error" and it took me several days to find
+ * the reason until I could fix it. So I personally recommend the "clean" way.
+ * On the other hand if you want to port a non-network game to a network game
+ * you will probably start with "dirty" properties as it is you will not have to
+ * change that much code...
+ *
+ * @section A Mixture (very dirty):
+ *
+ * You can also mix the concepts above. Note that we really don't recommend
+ * this. With a mixture I mean something like this:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.setAlwaysConsistent(false);
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * myProperty.setAlwaysConsistent(true);
+ * myProperty = 20;
+ * \endcode
+ * (totally senseless example, btw) I.e. I am speaking of mixing both concepts
+ * for a single property. Things like
+ * \code
+ * KGamePropertyInt myProperty1(id1, dataHandler());
+ * KGamePropertyInt myProperty2(id2, dataHandler());
+ * myProperty1.initData(0);
+ * myProperty2.initData(0);
+ * myProperty1.setAlwaysConsistent(false);
+ * myProperty2.setAlwaysConsistent(true);
+ * myProperty1 = 10;
+ * myProperty2 = 20;
+ * \endcode
+ * are ok. But mixing the concepts for a single property will make it nearly
+ * impossible to you to debug your game.
+ *
+ * So the right thing to do(tm) is to decide in the constructor whether you want
+ * a "clean" or "dirty" property.
+ *
+ * Even if you have decided for one of the concepts you still can manually
+ * follow another concept than the "policy" of your property. So if you use an
+ * always consistent KGameProperty you still can manually call
+ * changeValue as if it was not always consistent. Note that although this is
+ * also kind of a "mixture" as described above this is very useful sometimes. In
+ * contrast to the "mixture" above you don't have the problem that you don't
+ * exactly know which concept you are currently following because you used the
+ * function of the other concept only once.
+ *
+ * @section Custom classes:
+ *
+ * If you want to use a custum class with KGameProperty you have to implement the
+ * operators << and >> for QDataStream:
+ * \code
+ * class Card
+ * {
+ * public:
+ * int type;
+ * int suite;
+ * };
+ * QDataStream& operator<<(QDataStream& stream, Card& card)
+ * {
+ * Q_INT16 type = card.type;
+ * Q_INT16 suite = card.suite;
+ * s << type;
+ * s << suite;
+ * return s;
+ * }
+ * QDataStream& operator>>(QDataStream& stream, Card& card)
+ * {
+ * Q_INT16 type;
+ * Q_INT16 suite;
+ * s >> type;
+ * s >> suite;
+ * card.type = (int)type;
+ * card.suite = (int)suite;
+ * return s;
+ * }
+ *
+ * class Player : KPlayer
+ * {
+ * [...]
+ * KGameProperty<Card> mCards;
+ * };
+ * \endcode
+ *
+ * Note: unlike most QT classes KGameProperty objects are *not* deleted
+ * automatically! So if you create an object using e.g. KGameProperty<int>* data =
+ * new KGameProperty(id, dataHandler()) you have to put a delete data into your
+ * destructor!
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+template<class type>
+class KGameProperty : public KGamePropertyBase
+{
+public:
+ /**
+ * Constructs a KGameProperty object. A KGameProperty object will transmit
+ * any changes to the KMessageServer and then to all clients in the
+ * game (including the one that has sent the new value)
+ * @param id The id of this property. <em>MUST be UNIQUE</em>! Used to send and
+ * receive changes in the property of the playere automatically via
+ * network.
+ * @param owner The parent of the object. Must be a KGame which manages
+ * the changes made to this object, i.e. which will send the new data.
+ * Note that in contrast to most KDE/QT classes KGameProperty objects
+ * are <em>not</em> deleted automatically!
+ **/
+// TODO: ID: Very ugly - better use something like parent()->propertyId() or so which assigns a free id automatically.
+ KGameProperty(int id, KGamePropertyHandler* owner) : KGamePropertyBase(id, owner) { init(); }
+
+ /**
+ * This constructor does nothing. You have to call
+ * KGamePropertyBase::registerData
+ * yourself before using the KGameProperty object.
+ **/
+ KGameProperty() : KGamePropertyBase() { init(); }
+
+ virtual ~KGameProperty() {}
+
+ /**
+ * Set the value depending on the current policy (see
+ * setConsistent). By default KGameProperty just uses send to set
+ * the value of a property. This behaviour can be changed by using
+ * setConsistent.
+ * @param v The new value of the property
+ **/
+ void setValue(type v)
+ {
+ switch (policy()) {
+ case PolicyClean:
+ send(v);
+ break;
+ case PolicyDirty:
+ changeValue(v);
+ break;
+ case PolicyLocal:
+ setLocal(v);
+ break;
+ default: // NEVER!
+ kdError(11001) << "Undefined Policy in property " << id() << endl;
+ return;
+ }
+ }
+
+
+ /**
+ * This function sends a new value over network.
+ *
+ * Note that the value DOES NOT change when you call this function. This
+ * function saves the value into a QDataStream and calls
+ * sendProperty where it gets forwarded to the owner and finally the
+ * value is sent over network. The KMessageServer now sends the
+ * value to ALL clients - even the one who called this function. As soon
+ * as the value from the message server is received load is called
+ * and _then_ the value of the KGameProperty has been set.
+ *
+ * This ensures that a KGameProperty has _always_ the same value on
+ * _every_ client in the network. Note that this means you can NOT do
+ * something like
+ * \code
+ * myProperty.send(1);
+ * doSomething(myProperty);
+ * \endcode
+ * as myProperty has not yet been set when doSomething is being called.
+ *
+ * You are informed about a value change by a singal from the parent of
+ * the property which can be deactivated by setEmittingSignal because of
+ * performance (you probably don't have to deactivate it - except you
+ * want to write a real-time game like Command&Conquer with a lot of
+ * acitvity). See emitSignal
+ *
+ * Note that if there is no KMessageServer accessible - before
+ * the property has been registered to the KGamePropertyHandler (as
+ * it is the case e.g. before a KPlayer has been plugged into the
+ * KGame object) the property is *not* sent but set *locally* (see
+ * setLocal)!
+ *
+ * @param v The new value of the property
+ * @return whether the property could be sent successfully
+ * @see setValue setLocal changeValue value
+ **/
+ bool send(type v)
+ {
+ if (isOptimized() && mData == v) {
+ return true;
+ }
+ if (isLocked()) {
+ return false;
+ }
+ QByteArray b;
+ QDataStream stream(b, IO_WriteOnly);
+ stream << v;
+ if (!sendProperty(b)) {
+ setLocal(v);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This function sets the value of the property directly, i.e. it
+ * doesn't send it to the network.
+ *
+ * Int contrast to @see you change _only_ the local value when using
+ * this function. You do _not_ change the value of any other client. You
+ * probably don't want to use this if you are using a dedicated server
+ * (which is the only "client" which is allowed to change a value) but
+ * rather want to use send().
+ *
+ * But if you use your clients as servers (i.e. all clients receive a
+ * players turn and then calculate the reaction of the game theirselves)
+ * then you probably want to use setLocal as you can do things like
+ * \code
+ * myProperty.setLocal(1);
+ * doSomething(myProperty);
+ * \endcode
+ * on every client.
+ *
+ * If you want to set the value locally AND send it over network you
+ * want to call changeValue!
+ *
+ * You can also use setPolicy to set the default policy to
+ * PolicyLocal.
+ *
+ * @see setValue send changeValue value
+ **/
+ bool setLocal(type v)
+ {
+ if (isOptimized() && mData == v) {
+ return false;
+ }
+ if (isLocked()) {
+ return false;
+ }
+ mData = v;
+ setDirty(true);
+ if (isEmittingSignal()) {
+ emitSignal();
+ }
+ return true;
+ }
+
+ /**
+ * This function does both, change the local value and change the
+ * network value. The value is sent over network first, then changed
+ * locally.
+ *
+ * This function is a convenience function and just calls send
+ * followed by setLocal
+ *
+ * Note that emitSignal is also called twice: once after
+ * setLocal and once when the value from send is received
+ *
+ * @see send setLocal setValue value
+ **/
+ void changeValue(type v)
+ {
+ send(v);
+ setLocal(v);
+ }
+
+ /**
+ * Saves the object to a stream.
+ * @param stream The stream to save to
+ **/
+ virtual void save(QDataStream &stream)
+ {
+ stream << mData;
+ }
+
+ /**
+ * @return The local value (see setLocal) if it is existing,
+ * otherwise the network value which is always consistent on every
+ * client.
+ **/
+ const type& value() const
+ {
+ return mData;
+ }
+
+ /**
+ * Reads from a stream and assigns the read value to this object.
+ *
+ * This function is called automatically when a new value is received
+ * over network (i.e. it has been sent using send on this or any
+ * other client) or when a game is loaded (and maybe on some other
+ * events).
+ *
+ * Also calls emitSignal if isEmittingSignal is TRUE.
+ * @param s The stream to read from
+ **/
+ virtual void load(QDataStream& s)
+ {
+ s >> mData;
+ setDirty(false);
+ if (isEmittingSignal()) {
+ emitSignal();
+ }
+ }
+
+ /**
+ * This calls setValue to change the value of the property. Note
+ * that depending on the policy (see setAlwaysConsistent) the
+ * returned value might be different from the assigned value!!
+ *
+ * So if you use setPolicy(PolicyClean):
+ * \code
+ * int a, b = 10;
+ * myProperty = b;
+ * a = myProperty.value();
+ * \endcode
+ * Here a and b would differ!
+ * The value is actually set as soon as it is received from the
+ * KMessageServer which forwards it to ALL clients in the network.
+ *
+ * If you use a clean policy (see setPolicy) then
+ * the returned value is the assigned value
+ **/
+ const type& operator=(const type& t)
+ {
+ setValue(t);
+ return value();
+ }
+
+ /**
+ * This copies the data of property to the KGameProperty object.
+ *
+ * Equivalent to setValue(property.value());
+ **/
+ const type& operator=(const KGameProperty& property)
+ {
+ setValue(property.value());
+ return value();
+ }
+
+ /**
+ * Yeah, you can do it!
+ * \code
+ * int a = myGamePropertyInt;
+ * \endcode
+ * If you don't see it: you don't have to use integerData.value()
+ **/
+ operator type() const { return value(); }
+
+ virtual const type_info* typeinfo() { return &typeid(type); }
+
+private:
+ void init() { }
+
+private:
+ type mData;
+};
+
+
+typedef KGameProperty<int> KGamePropertyInt;
+typedef KGameProperty<unsigned int> KGamePropertyUInt;
+typedef KGameProperty<QString> KGamePropertyQString;
+typedef KGameProperty<Q_INT8> KGamePropertyBool;
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertyarray.h b/libkdegames/kgame/kgamepropertyarray.h
new file mode 100644
index 00000000..f91bd75c
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyarray.h
@@ -0,0 +1,309 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYARRAY_H_
+#define __KGAMEPROPERTYARRAY_H_
+
+#include <qdatastream.h>
+#include <kdebug.h>
+
+#include "kgamemessage.h"
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+
+
+template<class type>
+class KGamePropertyArray : public QMemArray<type>, public KGamePropertyBase
+{
+public:
+ KGamePropertyArray() :QMemArray<type>(), KGamePropertyBase()
+ {
+ //kdDebug(11001) << "KGamePropertyArray init" << endl;
+ }
+
+ KGamePropertyArray( int size )
+ {
+ resize(size);
+ }
+
+ KGamePropertyArray( const KGamePropertyArray<type> &a ) : QMemArray<type>(a)
+ {
+ }
+
+ bool resize( uint size )
+ {
+ if (size!=QMemArray<type>::size())
+ {
+ bool a=true;
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdResize);
+ s << size ;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+// a=QMemArray<type>::resize(size);// FIXME: return value!
+ }
+ return a;
+ }
+ else return true;
+ }
+
+ void setAt(uint i,type data)
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAt);
+ s << i ;
+ s << data;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+ }
+ //kdDebug(11001) << "KGamePropertyArray setAt send COMMAND for id="<<id() << " type=" << 1 << " at(" << i<<")="<<data << endl;
+ }
+
+ type at( uint i ) const
+ {
+ return QMemArray<type>::at(i);
+ }
+
+ type operator[]( int i ) const
+ {
+ return QMemArray<type>::at(i);
+ }
+
+ KGamePropertyArray<type> &operator=(const KGamePropertyArray<type> &a)
+ {
+ return assign(a);
+ }
+
+ bool truncate( uint pos )
+ {
+ return resize(pos);
+ }
+
+ bool fill( const type &data, int size = -1 )
+ {
+ bool r=true;
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdFill);
+ s << data;
+ s << size ;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+// r=QMemArray<type>::fill(data,size);//FIXME: return value!
+ }
+ return r;
+ }
+
+ KGamePropertyArray<type>& assign( const KGamePropertyArray<type>& a )
+ {
+// note: send() has been replaced by sendProperty so it might be broken now!
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::assign(a);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& assign( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::assign(a,n);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& duplicate( const KGamePropertyArray<type>& a )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::duplicate(a);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& duplicate( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::duplicate(a,n);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& setRawData( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::setRawData(a,n);
+ }
+ return *this;
+ }
+ void sort()
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdSort);
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+ }
+ }
+
+ void load(QDataStream& s)
+ {
+ //kdDebug(11001) << "KGamePropertyArray load " << id() << endl;
+ type data;
+ for (unsigned int i=0; i<QMemArray<type>::size(); i++)
+ {
+ s >> data;
+ QMemArray<type>::at(i)=data;
+ }
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ }
+ void save(QDataStream &s)
+ {
+ //kdDebug(11001) << "KGamePropertyArray save "<<id() << endl;
+ for (unsigned int i=0; i<QMemArray<type>::size(); i++)
+ {
+ s << at(i);
+ }
+ }
+
+ void command(QDataStream &s,int cmd,bool)
+ {
+ KGamePropertyBase::command(s, cmd);
+ //kdDebug(11001) << "Array id="<<id()<<" got command ("<<cmd<<") !!!" <<endl;
+ switch(cmd)
+ {
+ case CmdAt:
+ {
+ uint i;
+ type data;
+ s >> i >> data;
+ QMemArray<type>::at(i)=data;
+ //kdDebug(11001) << "CmdAt:id="<<id()<<" i="<<i<<" data="<<data <<endl;
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ break;
+ }
+ case CmdResize:
+ {
+ uint size;
+ s >> size;
+ //kdDebug(11001) << "CmdResize:id="<<id()<<" oldsize="<<QMemArray<type>::size()<<" newsize="<<size <<endl;
+ if (QMemArray<type>::size() != size)
+ {
+ QMemArray<type>::resize(size);
+ }
+ break;
+ }
+ case CmdFill:
+ {
+ int size;
+ type data;
+ s >> data >> size;
+ //kdDebug(11001) << "CmdFill:id="<<id()<<"size="<<size <<endl;
+ QMemArray<type>::fill(data,size);
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ break;
+ }
+ case CmdSort:
+ {
+ //kdDebug(11001) << "CmdSort:id="<<id()<<endl;
+ QMemArray<type>::sort();
+ break;
+ }
+ default:
+ kdError(11001) << "Error in KPropertyArray::command: Unknown command " << cmd << endl;
+ break;
+ }
+ }
+protected:
+ void extractProperty(const QByteArray& b)
+ {
+ QDataStream s(b, IO_ReadOnly);
+ int cmd;
+ int propId;
+ KGameMessage::extractPropertyHeader(s, propId);
+ KGameMessage::extractPropertyCommand(s, propId, cmd);
+ command(s, cmd, true);
+ }
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertyhandler.cpp b/libkdegames/kgame/kgamepropertyhandler.cpp
new file mode 100644
index 00000000..75b3e7e2
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyhandler.cpp
@@ -0,0 +1,407 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamepropertyhandler.h"
+#include "kgameproperty.h"
+#include "kgamemessage.h"
+
+#include <qmap.h>
+#include <qptrqueue.h>
+
+#include <klocale.h>
+#include <typeinfo>
+
+#define KPLAYERHANDLER_LOAD_COOKIE 6239
+
+//---------------------- KGamePropertyHandler -----------------------------------
+class KGamePropertyHandlerPrivate
+{
+public:
+ KGamePropertyHandlerPrivate()
+ {
+ }
+
+ QMap<int, QString> mNameMap;
+ QIntDict<KGamePropertyBase> mIdDict;
+ int mUniqueId;
+ int mId;
+ KGamePropertyBase::PropertyPolicy mDefaultPolicy;
+ bool mDefaultUserspace;
+ int mIndirectEmit;
+ QPtrQueue<KGamePropertyBase> mSignalQueue;
+};
+
+KGamePropertyHandler::KGamePropertyHandler(int id, const QObject* receiver, const char * sendf, const char *emitf, QObject* parent) : QObject(parent)
+{
+ init();
+ registerHandler(id,receiver,sendf,emitf);
+}
+
+KGamePropertyHandler::KGamePropertyHandler(QObject* parent) : QObject(parent)
+{
+ init();
+}
+
+KGamePropertyHandler::~KGamePropertyHandler()
+{
+ clear();
+ delete d;
+}
+
+void KGamePropertyHandler::init()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGamePropertyHandlerPrivate; // for future use - is BC important to us?
+ d->mId = 0;
+ d->mUniqueId=KGamePropertyBase::IdAutomatic;
+ d->mDefaultPolicy=KGamePropertyBase::PolicyLocal;
+ d->mDefaultUserspace=true;
+ d->mIndirectEmit=0;
+}
+
+
+int KGamePropertyHandler::id() const
+{
+ return d->mId;
+}
+
+void KGamePropertyHandler::setId(int id)
+{
+ d->mId = id;
+}
+
+void KGamePropertyHandler::registerHandler(int id,const QObject * receiver, const char * sendf, const char *emitf)
+{
+ setId(id);
+ if (receiver && sendf) {
+ kdDebug(11001) << "Connecting SLOT " << sendf << endl;
+ connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool*)), receiver, sendf);
+ }
+ if (receiver && emitf) {
+ kdDebug(11001) << "Connecting SLOT " << emitf << endl;
+ connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf);
+ }
+}
+
+bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender)
+{
+// kdDebug(11001) << k_funcinfo << ": id=" << id << " mId=" << d->mId << endl;
+ if (id != d->mId) {
+ return false; // Is the message meant for us?
+ }
+ KGamePropertyBase* p;
+ int propertyId;
+ KGameMessage::extractPropertyHeader(stream, propertyId);
+// kdDebug(11001) << k_funcinfo << ": Got property " << propertyId << endl;
+ if (propertyId==KGamePropertyBase::IdCommand) {
+ int cmd;
+ KGameMessage::extractPropertyCommand(stream, propertyId, cmd);
+//kdDebug(11001) << k_funcinfo << ": Got COMMAND for id= "<<propertyId <<endl;
+ p = d->mIdDict.find(propertyId);
+ if (p) {
+ if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) {
+ p->command(stream, cmd, isSender);
+ }
+ } else {
+ kdError(11001) << k_funcinfo << ": (cmd): property " << propertyId << " not found" << endl;
+ }
+ return true;
+ }
+ p = d->mIdDict.find(propertyId);
+ if (p) {
+ //kdDebug(11001) << k_funcinfo << ": Loading " << propertyId << endl;
+ if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) {
+ p->load(stream);
+ }
+ } else {
+ kdError(11001) << k_funcinfo << ": property " << propertyId << " not found" << endl;
+ }
+ return true;
+}
+
+bool KGamePropertyHandler::removeProperty(KGamePropertyBase* data)
+{
+ if (!data) {
+ return false;
+ }
+ d->mNameMap.erase(data->id());
+ return d->mIdDict.remove(data->id());
+}
+
+bool KGamePropertyHandler::addProperty(KGamePropertyBase* data, QString name)
+{
+ //kdDebug(11001) << k_funcinfo << ": " << data->id() << endl;
+ if (d->mIdDict.find(data->id())) {
+ // this id already exists
+ kdError(11001) << " -> cannot add property " << data->id() << endl;
+ return false;
+ } else {
+ d->mIdDict.insert(data->id(), data);
+ // if here is a check for "is_debug" or so we can add the strings only in debug mode
+ // and save memory!!
+ if (!name.isNull()) {
+ d->mNameMap[data->id()] = name;
+ //kdDebug(11001) << k_funcinfo << ": nid="<< (data->id()) << " inserted in Map name=" << d->mNameMap[data->id()] <<endl;
+ //kdDebug(11001) << "Typeid=" << typeid(data).name() << endl;
+ //kdDebug(11001) << "Typeid call=" << data->typeinfo()->name() << endl;
+ }
+ }
+ return true;
+}
+
+QString KGamePropertyHandler::propertyName(int id) const
+{
+ QString s;
+ if (d->mIdDict.find(id)) {
+ if (d->mNameMap.contains(id)) {
+ s = i18n("%1 (%2)").arg(d->mNameMap[id]).arg(id);
+ } else {
+ s = i18n("Unnamed - ID: %1").arg(id);
+ }
+ } else {
+ // Should _never_ happen
+ s = i18n("%1 unregistered").arg(id);
+ }
+ return s;
+}
+
+bool KGamePropertyHandler::load(QDataStream &stream)
+{
+ // Prevent direct emmiting until all is loaded
+ lockDirectEmit();
+ uint count,i;
+ stream >> count;
+ kdDebug(11001) << k_funcinfo << ": " << count << " KGameProperty objects " << endl;
+ for (i = 0; i < count; i++) {
+ processMessage(stream, id(),false);
+ }
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie == KPLAYERHANDLER_LOAD_COOKIE) {
+ kdDebug(11001) << " KGamePropertyHandler loaded propertly"<<endl;
+ } else {
+ kdError(11001) << "KGamePropertyHandler loading error. probably format error"<<endl;
+ }
+ // Allow direct emmiting (if no other lock still holds)
+ unlockDirectEmit();
+ return true;
+}
+
+bool KGamePropertyHandler::save(QDataStream &stream)
+{
+ kdDebug(11001) << k_funcinfo << ": " << d->mIdDict.count() << " KGameProperty objects " << endl;
+ stream << (uint)d->mIdDict.count();
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ KGamePropertyBase *base=it.current();
+ if (base) {
+ KGameMessage::createPropertyHeader(stream, base->id());
+ base->save(stream);
+ }
+ ++it;
+ }
+ stream << (Q_INT16)KPLAYERHANDLER_LOAD_COOKIE;
+ return true;
+}
+
+KGamePropertyBase::PropertyPolicy KGamePropertyHandler::policy()
+{
+// kdDebug(11001) << k_funcinfo << ": " << d->mDefaultPolicy << endl;
+ return d->mDefaultPolicy;
+}
+void KGamePropertyHandler::setPolicy(KGamePropertyBase::PropertyPolicy p,bool userspace)
+{
+ // kdDebug(11001) << k_funcinfo << ": " << p << endl;
+ d->mDefaultPolicy=p;
+ d->mDefaultUserspace=userspace;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ if (!userspace || it.current()->id()>=KGamePropertyBase::IdUser) {
+ it.current()->setPolicy((KGamePropertyBase::PropertyPolicy)p);
+ }
+ ++it;
+ }
+}
+
+void KGamePropertyHandler::unlockProperties()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ it.current()->unlock();
+ ++it;
+ }
+}
+
+void KGamePropertyHandler::lockProperties()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ it.current()->lock();
+ ++it;
+ }
+}
+
+int KGamePropertyHandler::uniquePropertyId()
+{
+ return d->mUniqueId++;
+}
+
+void KGamePropertyHandler::flush()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ if (it.current()->isDirty()) {
+ it.current()->sendProperty();
+ }
+ ++it;
+ }
+}
+
+/* Fire all property signal changed which are collected in
+ * the queque
+ **/
+void KGamePropertyHandler::lockDirectEmit()
+{
+ d->mIndirectEmit++;
+}
+
+void KGamePropertyHandler::unlockDirectEmit()
+{
+ // If the flag is <=0 we emit the queued signals
+ d->mIndirectEmit--;
+ if (d->mIndirectEmit<=0)
+ {
+ KGamePropertyBase *prop;
+ while((prop=d->mSignalQueue.dequeue()) != 0)
+ {
+ // kdDebug(11001) << "emmiting signal for " << prop->id() << endl;
+ emit signalPropertyChanged(prop);
+ }
+ }
+}
+
+void KGamePropertyHandler::emitSignal(KGamePropertyBase *prop)
+{
+ // If the indirect flag is set (load and network transmit)
+ // we cannot emit the signals directly as it can happend that
+ // a sigal causes an access to a property which is e.g. not
+ // yet loaded or received
+
+ if (d->mIndirectEmit>0)
+ {
+ // Queque the signal
+ d->mSignalQueue.enqueue(prop);
+ }
+ else
+ {
+ // directly emit
+ emit signalPropertyChanged(prop);
+ }
+}
+
+bool KGamePropertyHandler::sendProperty(QDataStream &s)
+{
+ bool sent = false;
+ emit signalSendMessage(id(), s, &sent);
+ return sent;
+}
+
+KGamePropertyBase *KGamePropertyHandler::find(int id)
+{
+ return d->mIdDict.find(id);
+}
+
+void KGamePropertyHandler::clear()
+{
+ kdDebug(11001) << k_funcinfo << id() << endl;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.toFirst()) {
+ KGamePropertyBase* p = it.toFirst();
+ p->unregisterData();
+ if (d->mIdDict.find(p->id())) {
+ // shouldn't happen - but if mOwner in KGamePropertyBase is NULL
+ // this might be possible
+ removeProperty(p);
+ }
+ }
+}
+
+QIntDict<KGamePropertyBase>& KGamePropertyHandler::dict() const
+{
+ return d->mIdDict;
+}
+
+QString KGamePropertyHandler::propertyValue(KGamePropertyBase* prop)
+{
+ if (!prop) {
+ return i18n("NULL pointer");
+ }
+
+ int id = prop->id();
+ QString name = propertyName(id);
+ QString value;
+
+ const type_info* t = prop->typeinfo();
+ if (*t == typeid(int)) {
+ value = QString::number(((KGamePropertyInt*)prop)->value());
+ } else if (*t == typeid(unsigned int)) {
+ value = QString::number(((KGamePropertyUInt *)prop)->value());
+ } else if (*t == typeid(long int)) {
+ value = QString::number(((KGameProperty<long int> *)prop)->value());
+ } else if (*t == typeid(unsigned long int)) {
+ value = QString::number(((KGameProperty<unsigned long int> *)prop)->value());
+ } else if (*t == typeid(QString)) {
+ value = ((KGamePropertyQString*)prop)->value();
+ } else if (*t == typeid(Q_INT8)) {
+ value = ((KGamePropertyBool*)prop)->value() ? i18n("True") : i18n("False");
+ } else {
+ emit signalRequestValue(prop, value);
+ }
+
+ if (value.isNull()) {
+ value = i18n("Unknown");
+ }
+ return value;
+}
+
+void KGamePropertyHandler::Debug()
+{
+ kdDebug(11001) << "-----------------------------------------------------------" << endl;
+ kdDebug(11001) << "KGamePropertyHandler:: Debug this=" << this << endl;
+
+ kdDebug(11001) << " Registered properties: (Policy,Lock,Emit,Optimized, Dirty)" << endl;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ KGamePropertyBase *p=it.current();
+ kdDebug(11001) << " "<< p->id() << ": p=" << p->policy()
+ << " l="<<p->isLocked()
+ << " e="<<p->isEmittingSignal()
+ << " o=" << p->isOptimized()
+ << " d="<<p->isDirty()
+ << endl;
+ ++it;
+ }
+ kdDebug(11001) << "-----------------------------------------------------------" << endl;
+}
+
+#include "kgamepropertyhandler.moc"
diff --git a/libkdegames/kgame/kgamepropertyhandler.h b/libkdegames/kgame/kgamepropertyhandler.h
new file mode 100644
index 00000000..6147c071
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyhandler.h
@@ -0,0 +1,353 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYHANDLER_H_
+#define __KGAMEPROPERTYHANDLER_H_
+
+#include <qobject.h>
+#include <qintdict.h>
+
+#include "kgameproperty.h"
+#include <kdemacros.h>
+
+class QDataStream;
+class KGame;
+class KPlayer;
+//class KGamePropertyBase;
+
+class KGamePropertyHandlerPrivate; // wow - what a name ;-)
+
+/**
+ * @short A collection class for KGameProperty objects
+ *
+ * The KGamePropertyHandler class is some kind of a collection class for
+ * KGameProperty. You usually don't have to create one yourself, as both
+ * KPlayer and KGame provide a handler. In most cases you do not even have
+ * to care about the KGamePropertHandler. KGame and KPlayer implement
+ * all features of KGamePropertyHandler so you will rather use it there.
+ *
+ * You have to use the KGamePropertyHandler as parent for all KGameProperty
+ * objects but you can also use KPlayer or KGame as parent - then
+ * KPlayer::dataHandler or KGame::dataHandler will be used.
+ *
+ * Every KGamePropertyHandler must have - just like every KGameProperty -
+ * a unique ID. This ID is provided either in the constructor or in
+ * registerHandler. The ID is used to assign an incoming message (e.g. a changed
+ * property) to the correct handler. Inside the handler the property ID is used
+ * to change the correct property.
+ *
+ * The constructor or registerHandler takes 3 addittional arguments: a
+ * receiver and two slots. The first slot is connected to
+ * signalSendMessage, the second to signalPropertyChanged. You must provide
+ * these in order to use the KGamePropertyHandler.
+ *
+ * The most important function of KGamePropertyHandler is processMessage
+ * which assigns an incoming value to the correct property.
+ *
+ * A KGamePropertyHandler is also used - indirectly using emitSignal - to
+ * emit a signal when the value of a property changes. This is done this way
+ * because a KGameProperty does not inherit QObject because of memory
+ * advantages. Many games can have dozens or even hundreds of KGameProperty
+ * objects so every additional variable in KGameProperty would be
+ * multiplied.
+ *
+ **/
+class KDE_EXPORT KGamePropertyHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct an unregistered KGamePropertyHandler
+ *
+ * You have to call registerHandler before you can use this
+ * handler!
+ **/
+ KGamePropertyHandler(QObject* parent = 0);
+
+ /**
+ * Construct a registered handler.
+ *
+ * @see registerHandler
+ **/
+ KGamePropertyHandler(int id, const QObject* receiver, const char* sendf, const char* emitf, QObject* parent = 0);
+ ~KGamePropertyHandler();
+
+ /**
+ * Register the handler with a parent. This is to use
+ * if the constructor without arguments has been chosen.
+ * Otherwise you need not call this.
+ *
+ * @param id The id of the message to listen for
+ * @param receiver The object that will receive the signals of
+ * KGamePropertyHandler
+ * @param send A slot that is being connected to signalSendMessage
+ * @param emit A slot that is being connected to signalPropertyChanged
+ **/
+ void registerHandler(int id, const QObject *receiver, const char * send, const char *emit);
+
+ /**
+ * Main message process function. This has to be called by
+ * the parent's message event handler. If the id of the message
+ * agrees with the id of the handler, the message is extracted
+ * and processed. Otherwise false is returned.
+ * Example:
+ * \code
+ * if (mProperties.processMessage(stream,msgid,sender==gameId())) return ;
+ * \endcode
+ *
+ * @param stream The data stream containing the message
+ * @param id the message id of the message
+ * @param isSender Whether the receiver is also the sender
+ * @return true on message processed otherwise false
+ **/
+ bool processMessage(QDataStream &stream, int id, bool isSender );
+
+ /**
+ * @return the id of the handler
+ **/
+ int id() const;
+
+ /**
+ * Adds a KGameProperty property to the handler
+ * @param data the property
+ * @param name A description of the property, which will be returned by
+ * propertyName. This is used for debugging, e.g. in KGameDebugDialog
+ * @return true on success
+ **/
+ bool addProperty(KGamePropertyBase *data, QString name=0);
+
+ /**
+ * Removes a property from the handler
+ * @param data the property
+ * @return true on success
+ **/
+ bool removeProperty(KGamePropertyBase *data);
+
+ /**
+ * returns a unique property ID starting called usually with a base of
+ * KGamePropertyBase::IdAutomatic. This is used internally by
+ * the property base to assign automtic id's. Not much need to
+ * call this yourself.
+ **/
+ int uniquePropertyId();
+
+
+ /**
+ * Loads properties from the datastream
+ *
+ * @param stream the datastream to load from
+ * @return true on success otherwise false
+ **/
+ virtual bool load(QDataStream &stream);
+
+ /**
+ * Saves properties into the datastream
+ *
+ * @param stream the datastream to save to
+ * @return true on success otherwise false
+ **/
+ virtual bool save(QDataStream &stream);
+
+ /**
+ * called by a property to send itself into the
+ * datastream. This call is simply forwarded to
+ * the parent object
+ **/
+ bool sendProperty(QDataStream &s);
+
+ void sendLocked(bool l);
+
+ /**
+ * called by a property to emit a signal
+ * This call is simply forwarded to
+ * the parent object
+ **/
+ void emitSignal(KGamePropertyBase *data);
+
+ /**
+ * @param id The ID of the property
+ * @return A name of the property which can be used for debugging. Don't
+ * depend on this function! It it possible not to provide a name or to
+ * provide the same name for multiple properties!
+ **/
+ QString propertyName(int id) const;
+
+ /**
+ * @param id The ID of the property. See KGamePropertyBase::id
+ * @return The KGameProperty this ID is assigned to
+ **/
+ KGamePropertyBase *find(int id);
+
+ /**
+ * Clear the KGamePropertyHandler. Note that the properties are
+ * <em>not</em> deleted so if you created your KGameProperty
+ * objects dynamically like
+ * \code
+ * KGamePropertyInt* myProperty = new KGamePropertyInt(id, dataHandler());
+ * \endcode
+ * you also have to delete it:
+ * \code
+ * dataHandler()->clear();
+ * delete myProperty;
+ * \endcode
+ **/
+ void clear();
+
+ /**
+ * Use id as new ID for this KGamePropertyHandler. This is used
+ * internally only.
+ **/
+ void setId(int id);//AB: TODO: make this protected in KGamePropertyHandler!!
+
+ /**
+ * Calls KGamePropertyBase::setReadOnly(false) for all properties of this
+ * player. See also lockProperties
+ **/
+ void unlockProperties();
+
+ /**
+ * Set the policy for all kgame variables which are currently registerd in
+ * the KGame proeprty handler. See KGamePropertyBase::setPolicy
+ *
+ * @param p is the new policy for all properties of this handler
+ * @param userspace if userspace is true (default) only user properties are changed.
+ * Otherwise the system properties are also changed.
+ **/
+ void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true);
+
+ /**
+ * Called by the KGame or KPlayer object or the handler itself to delay
+ * emmiting of signals. Lockign keeps a counter and unlock is only achieved
+ * when every lock is canceld by an unlock.
+ * While this is set signals are quequed and only emmited after this
+ * is reset. Its deeper meaning is to prevent inconsistencies in a game
+ * load or network transfer where a emit could access a property not
+ * yet loaded or transmitted. Calling this by yourself you better know
+ * what your are doing.
+ **/
+ void lockDirectEmit();
+
+ /**
+ * Removes the lock from the emitting of property signals. Corresponds to
+ * the lockIndirectEmits
+ **/
+ void unlockDirectEmit();
+
+ /**
+ * Returns the default policy for this property handler. All properties
+ * registered newly, will have this property.
+ **/
+ KGamePropertyBase::PropertyPolicy policy();
+
+ /**
+ * Calls KGamePropertyBase::setReadOnly(true) for all properties of this
+ * handler
+ *
+ * Use with care! This will even lock the core properties, like name,
+ * group and myTurn!!
+ *
+ * @see unlockProperties
+ **/
+ void lockProperties();
+
+ /**
+ * Sends all properties which are marked dirty over the network. This will
+ * make a forced synchornisation of the properties and mark them all not dirty.
+ **/
+ void flush();
+
+ /**
+ * Reference to the internal dictionary
+ **/
+ QIntDict<KGamePropertyBase> &dict() const;
+
+ /**
+ * In several situations you just want to have a QString of a
+ * KGameProperty object. This is the case in the
+ * KGameDebugDialog where the value of all properties is displayed. This
+ * function will provide you with such a QString for all the types
+ * used inside of all KGame classes. If you have a non-standard
+ * property (probably a self defined class or something like this) you
+ * also need to connect to signalRequestValue to make this function
+ * useful.
+ * @param property Return the value of this KGameProperty
+ * @return The value of a KGameProperty
+ **/
+ QString propertyValue(KGamePropertyBase* property);
+
+
+ /**
+ * Writes some debug output to the console.
+ **/
+ void Debug();
+
+
+signals:
+ /**
+ * This is emitted by a property. KGamePropertyBase::emitSignal
+ * calls emitSignal which emits this signal.
+ *
+ * This signal is emitted whenever the property is changed. Note that
+ * you can switch off this behaviour using
+ * KGamePropertyBase::setEmittingSignal in favor of performance. Note
+ * that you won't experience any performance loss using signals unless
+ * you use dozens or hundreds of properties which change very often.
+ **/
+ void signalPropertyChanged(KGamePropertyBase *);
+
+ /**
+ * This signal is emitted when a property needs to be sent. Only the
+ * parent has to react to this.
+ * @param msgid The id of the handler
+ * @param sent set this to true if the property was sent successfully -
+ * otherwise don't touch
+ **/
+ void signalSendMessage(int msgid, QDataStream &, bool* sent); // AB shall we change bool* into bool& again?
+
+ /**
+ * If you call propertyValue with a non-standard KGameProperty
+ * it is possible that the value cannot automatically be converted into a
+ * QString. Then this signal is emitted and asks you to provide the
+ * correct value. You probably want to use something like this to achieve
+ * this:
+ * \code
+ * #include <typeinfo>
+ * void slotRequestValue(KGamePropertyBase* p, QString& value)
+ * {
+ * if (*(p->typeinfo()) == typeid(MyType) {
+ * value = QString(((KGameProperty<MyType>*)p)->value());
+ * }
+ * }
+ * \endcode
+ *
+ * @param property The KGamePropertyBase the value is requested for
+ * @param value The value of this property. You have to set this.
+ **/
+ void signalRequestValue(KGamePropertyBase* property, QString& value);
+
+private:
+ void init();
+
+private:
+ KGamePropertyHandlerPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertylist.h b/libkdegames/kgame/kgamepropertylist.h
new file mode 100644
index 00000000..3a304e70
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertylist.h
@@ -0,0 +1,258 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYLIST_H_
+#define __KGAMEPROPERTYLIST_H_
+
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+
+#include "kgamemessage.h"
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+
+// AB: also see README.LIB!
+
+template<class type>
+class KGamePropertyList : public QValueList<type>, public KGamePropertyBase
+{
+public:
+ /**
+ * Typedefs
+ */
+ typedef QValueListIterator<type> Iterator;
+ typedef QValueListConstIterator<type> ConstIterator;
+
+ KGamePropertyList() :QValueList<type>(), KGamePropertyBase()
+ {
+ }
+
+ KGamePropertyList( const KGamePropertyList<type> &a ) : QValueList<type>(a)
+ {
+ }
+
+ uint findIterator(Iterator me)
+ {
+ Iterator it;
+ uint cnt=0;
+ for( it = this->begin(); it != this->end(); ++it )
+ {
+ if (me==it)
+ {
+ return cnt;
+ }
+ cnt++;
+ }
+ return this->count();
+ }
+
+ Iterator insert( Iterator it, const type& d )
+ {
+ it=QValueList<type>::insert(it,d);
+
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdInsert);
+ int i=findIterator(it);
+ s << i;
+ s << d;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ return it;
+ }
+
+ void prepend( const type& d) { insert(this->begin(),d); }
+
+ void append( const type& d )
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAppend);
+ s << d;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ }
+
+ Iterator erase( Iterator it )
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdRemove);
+ int i=findIterator(it);
+ s << i;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ //TODO: return value - is it correct for PolicyLocal|PolicyDirty?
+// return QValueList<type>::remove(it);
+ return it;
+ }
+
+ Iterator remove( Iterator it )
+ {
+ return erase(it);
+ }
+
+ void remove( const type& d )
+ {
+ Iterator it=find(d);
+ remove(it);
+ }
+
+ void clear()
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdClear);
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ }
+
+ void load(QDataStream& s)
+ {
+ kdDebug(11001) << "KGamePropertyList load " << id() << endl;
+ QValueList<type>::clear();
+ uint size;
+ type data;
+ s >> size;
+
+ for (unsigned int i=0;i<size;i++)
+ {
+ s >> data;
+ QValueList<type>::append(data);
+ }
+ if (isEmittingSignal()) emitSignal();
+ }
+
+ void save(QDataStream &s)
+ {
+ kdDebug(11001) << "KGamePropertyList save "<<id() << endl;
+ type data;
+ uint size=this->count();
+ s << size;
+ Iterator it;
+ for( it = this->begin(); it != this->end(); ++it )
+ {
+ data=*it;
+ s << data;
+ }
+ }
+
+ void command(QDataStream &s,int cmd,bool)
+ {
+ KGamePropertyBase::command(s, cmd);
+ kdDebug(11001) << "---> LIST id="<<id()<<" got command ("<<cmd<<") !!!" <<endl;
+ Iterator it;
+ switch(cmd)
+ {
+ case CmdInsert:
+ {
+ uint i;
+ type data;
+ s >> i >> data;
+ it=this->at(i);
+ QValueList<type>::insert(it,data);
+// kdDebug(11001) << "CmdInsert:id="<<id()<<" i="<<i<<" data="<<data <<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdAppend:
+ {
+ type data;
+ s >> data;
+ QValueList<type>::append(data);
+// kdDebug(11001) << "CmdAppend:id=" << id() << " data=" << data << endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdRemove:
+ {
+ uint i;
+ s >> i;
+ it=this->at(i);
+ QValueList<type>::remove(it);
+ kdDebug(11001) << "CmdRemove:id="<<id()<<" i="<<i <<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdClear:
+ {
+ QValueList<type>::clear();
+ kdDebug(11001) << "CmdClear:id="<<id()<<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ default:
+ kdDebug(11001) << "Error in KPropertyList::command: Unknown command " << cmd << endl;
+ }
+ }
+
+protected:
+ void extractProperty(const QByteArray& b)
+ // this is called for Policy[Dirty|Local] after putting the stuff into the
+ // stream
+ {
+ QDataStream s(b, IO_ReadOnly);
+ int cmd;
+ int propId;
+ KGameMessage::extractPropertyHeader(s, propId);
+ KGameMessage::extractPropertyCommand(s, propId, cmd);
+ command(s, cmd, true);
+ }
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamesequence.cpp b/libkdegames/kgame/kgamesequence.cpp
new file mode 100644
index 00000000..984a9315
--- /dev/null
+++ b/libkdegames/kgame/kgamesequence.cpp
@@ -0,0 +1,125 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2003 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamesequence.h"
+#include "kgamesequence.moc"
+
+#include "kplayer.h"
+#include "kgame.h"
+
+KGameSequence::KGameSequence() : QObject()
+{
+ mGame = 0;
+ mCurrentPlayer = 0;
+}
+
+KGameSequence::~KGameSequence()
+{
+}
+
+void KGameSequence::setGame(KGame* game)
+{
+ mGame = game;
+}
+
+void KGameSequence::setCurrentPlayer(KPlayer* player)
+{
+ mCurrentPlayer = player;
+}
+
+KPlayer *KGameSequence::nextPlayer(KPlayer *last,bool exclusive)
+{
+ kdDebug(11001) << "=================== NEXT PLAYER =========================="<<endl;
+ if (!game())
+ {
+ kdError() << k_funcinfo << "NULL game object" << endl;
+ return 0;
+ }
+ unsigned int minId,nextId,lastId;
+ KPlayer *nextplayer, *minplayer;
+ if (last)
+ {
+ lastId = last->id();
+ }
+ else
+ {
+ lastId = 0;
+ }
+
+ kdDebug(11001) << "nextPlayer: lastId="<<lastId<<endl;
+
+ // remove when this has been checked
+ minId = 0x7fff; // we just need a very large number...properly MAX_UINT or so would be ok...
+ nextId = minId;
+ nextplayer = 0;
+ minplayer = 0;
+
+ KPlayer *player;
+ for (player = game()->playerList()->first(); player != 0; player=game()->playerList()->next() )
+ {
+ // Find the first player for a cycle
+ if (player->id() < minId)
+ {
+ minId=player->id();
+ minplayer=player;
+ }
+ if (player==last)
+ {
+ continue;
+ }
+ // Find the next player which is bigger than the current one
+ if (player->id() > lastId && player->id() < nextId)
+ {
+ nextId=player->id();
+ nextplayer=player;
+ }
+ }
+
+ // Cycle to the beginning
+ if (!nextplayer)
+ {
+ nextplayer=minplayer;
+ }
+
+ kdDebug(11001) << k_funcinfo << " ##### lastId=" << lastId << " exclusive="
+ << exclusive << " minId=" << minId << " nextid=" << nextId
+ << " count=" << game()->playerList()->count() << endl;
+ if (nextplayer)
+ {
+ nextplayer->setTurn(true,exclusive);
+ }
+ else
+ {
+ return 0;
+ }
+ return nextplayer;
+}
+
+// Per default we do not do anything
+int KGameSequence::checkGameOver(KPlayer*)
+{
+ return 0;
+}
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgamesequence.h b/libkdegames/kgame/kgamesequence.h
new file mode 100644
index 00000000..4d26ceee
--- /dev/null
+++ b/libkdegames/kgame/kgamesequence.h
@@ -0,0 +1,87 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2003 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMESEQUENCE_H_
+#define __KGAMESEQUENCE_H_
+
+#include <qobject.h>
+
+class KPlayer;
+class KGame;
+
+/**
+ * This class takes care of round or move management as well of the gameover
+ * condition. It is especially used for round based games. For these games @ref
+ * nextPlayer and @ref checkGameOver are the most important methods.
+ *
+ * You can subclass KGameSequence and use @ref KGame::setGameSequence to use
+ * your own rules. Note that @ref KGame will take ownership and therefore will
+ * delete the object on destruction.
+ * @short Round/move management class
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ **/
+class KGameSequence : public QObject
+{
+ Q_OBJECT
+public:
+ KGameSequence();
+ virtual ~KGameSequence();
+
+ /**
+ * Select the next player in a turn based game. In an asynchronous game this
+ * function has no meaning. Overwrite this function for your own game sequence.
+ * Per default it selects the next player in the playerList
+ */
+ virtual KPlayer* nextPlayer(KPlayer *last, bool exclusive = true);
+
+ virtual void setCurrentPlayer(KPlayer* p);
+
+ /**
+ * @return The @ref KGame object this sequence is for, or NULL if none.
+ **/
+ KGame* game() const { return mGame; }
+
+ KPlayer* currentPlayer() const { return mCurrentPlayer; }
+
+ /**
+ * Set the @ref KGame object for this sequence. This is called
+ * automatically by @ref KGame::setGameSequence and you should not call
+ * it.
+ **/
+ void setGame(KGame* game);
+
+ /**
+ * Check whether the game is over. The default implementation always
+ * returns 0.
+ *
+ * @param player the player who made the last move
+ * @return anything else but 0 is considered as game over
+ **/
+ virtual int checkGameOver(KPlayer *player);
+
+private:
+ KGame* mGame;
+ KPlayer* mCurrentPlayer;
+};
+
+#endif
+
diff --git a/libkdegames/kgame/kgameversion.h b/libkdegames/kgame/kgameversion.h
new file mode 100644
index 00000000..c3147525
--- /dev/null
+++ b/libkdegames/kgame/kgameversion.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de)
+ Copyright (C) 2003 Martin Heni (martin@heni-online.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEVERSION_H__
+#define __KGAMEVERSION_H__
+
+/**
+ * In this file you find a couple of defines that indicate whether a specific
+ * feature or function is present in this version of the KGame library.
+ *
+ * You don't need this for KDE CVS, but for games that live outside of KDE CVS
+ * it may be very helpful and a lot easier than writing configure scripts for
+ * this task.
+ *
+ * All defines are prefixed with KGAME_ to avoid conflicts.
+ **/
+
+// KGame::savegame() didn't exist in KDE 3.0
+#define KGAME_HAVE_KGAME_SAVEGAME 1
+
+// KGameNetwork::port(), KMessageIO::peerPort() and friends were added in KDE 3.2
+#define KGAME_HAVE_KGAME_PORT 1
+
+// KGameNetwork::hostName(), KMessageIO::peerName() and friends were added in KDE 3.2
+#define KGAME_HAVE_KGAME_HOSTNAME 1
+
+// KGameSequence class was added in KDE 3.2
+#define KGAME_HAVE_KGAMESEQUENCE 1
+
+// KGame::addPlayer() needs to assign an ID to new players, otherwise network is
+// broken. this is done in KDE 3.2.
+#define KGAME_HAVE_FIXED_ADDPLAYER_ID 1
+
+#endif
+
diff --git a/libkdegames/kgame/kmessageclient.cpp b/libkdegames/kgame/kmessageclient.cpp
new file mode 100644
index 00000000..ed9cc966
--- /dev/null
+++ b/libkdegames/kgame/kmessageclient.cpp
@@ -0,0 +1,373 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kdebug.h>
+#include <stdio.h>
+
+#include <qbuffer.h>
+#include <qtimer.h>
+
+#include "kmessageio.h"
+#include "kmessageserver.h"
+
+#include "kmessageclient.h"
+
+class KMessageClientPrivate
+{
+public:
+ KMessageClientPrivate ()
+ : adminID (0), connection (0)
+ {}
+
+ ~KMessageClientPrivate ()
+ {
+ delete connection;
+ }
+
+ Q_UINT32 adminID;
+ QValueList <Q_UINT32> clientList;
+ KMessageIO *connection;
+
+ bool isLocked;
+ QValueList <QByteArray> delayedMessages;
+};
+
+KMessageClient::KMessageClient (QObject *parent, const char *name)
+ : QObject (parent, name)
+{
+ d = new KMessageClientPrivate ();
+ d->isLocked = false;
+}
+
+KMessageClient::~KMessageClient ()
+{
+ d->delayedMessages.clear();
+ delete d;
+}
+
+// -- setServer stuff
+
+void KMessageClient::setServer (const QString &host, Q_UINT16 port)
+{
+ setServer (new KMessageSocket (host, port));
+}
+
+void KMessageClient::setServer (KMessageServer *server)
+{
+ KMessageDirect *serverIO = new KMessageDirect ();
+ setServer (new KMessageDirect (serverIO));
+ server->addClient (serverIO);
+}
+
+void KMessageClient::setServer (KMessageIO *connection)
+{
+ if (d->connection)
+ {
+ delete d->connection;
+ kdDebug (11001) << k_funcinfo << ": We are changing the server!" << endl;
+ }
+
+ d->connection = connection;
+ if (connection )
+ {
+ connect (connection, SIGNAL (received(const QByteArray &)),
+ this, SLOT (processIncomingMessage(const QByteArray &)));
+ connect (connection, SIGNAL (connectionBroken()),
+ this, SLOT (removeBrokenConnection ()));
+ }
+}
+
+// -- id stuff
+
+Q_UINT32 KMessageClient::id () const
+{
+ return (d->connection) ? d->connection->id () : 0;
+}
+
+bool KMessageClient::isAdmin () const
+{
+ return id() != 0 && id() == adminId();
+}
+
+Q_UINT32 KMessageClient::adminId () const
+{
+ return d->adminID;
+}
+
+const QValueList <Q_UINT32> &KMessageClient::clientList() const
+{
+ return d->clientList;
+}
+
+bool KMessageClient::isConnected () const
+{
+ return d->connection && d->connection->isConnected();
+}
+
+bool KMessageClient::isNetwork () const
+{
+ return isConnected() ? d->connection->isNetwork() : false;
+}
+
+Q_UINT16 KMessageClient::peerPort () const
+{
+ return d->connection ? d->connection->peerPort() : 0;
+}
+
+QString KMessageClient::peerName () const
+{
+ return d->connection ? d->connection->peerName() : QString::fromLatin1("localhost");
+}
+
+// --------------------- Sending messages
+
+void KMessageClient::sendServerMessage (const QByteArray &msg)
+{
+ if (!d->connection)
+ {
+ kdWarning (11001) << k_funcinfo << ": We have no connection yet!" << endl;
+ return;
+ }
+ d->connection->send (msg);
+}
+
+void KMessageClient::sendBroadcast (const QByteArray &msg)
+{
+ QByteArray sendBuffer;
+ QBuffer buffer (sendBuffer);
+ buffer.open (IO_WriteOnly);
+ QDataStream stream (&buffer);
+
+ stream << static_cast<Q_UINT32> ( KMessageServer::REQ_BROADCAST );
+ buffer.QIODevice::writeBlock (msg);
+ sendServerMessage (sendBuffer);
+}
+
+void KMessageClient::sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients)
+{
+ QByteArray sendBuffer;
+ QBuffer buffer (sendBuffer);
+ buffer.open (IO_WriteOnly);
+ QDataStream stream (&buffer);
+
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_FORWARD ) << clients;
+ buffer.QIODevice::writeBlock (msg);
+ sendServerMessage (sendBuffer);
+}
+
+void KMessageClient::sendForward (const QByteArray &msg, Q_UINT32 client)
+{
+ sendForward (msg, QValueList <Q_UINT32> () << client);
+}
+
+
+// --------------------- Receiving and processing messages
+
+void KMessageClient::processIncomingMessage (const QByteArray &msg)
+{
+ if (d->isLocked)
+ {
+ d->delayedMessages.append(msg);
+ return;
+ }
+ if (d->delayedMessages.count() > 0)
+ {
+ d->delayedMessages.append (msg);
+ QByteArray first = d->delayedMessages.front();
+ d->delayedMessages.pop_front();
+ processMessage (first);
+ }
+ else
+ {
+ processMessage(msg);
+ }
+}
+
+void KMessageClient::processMessage (const QByteArray &msg)
+{
+ if (d->isLocked)
+ { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
+ d->delayedMessages.append(msg);
+ return;
+ }
+ QBuffer in_buffer (msg);
+ in_buffer.open (IO_ReadOnly);
+ QDataStream in_stream (&in_buffer);
+
+
+ bool unknown = false;
+
+ Q_UINT32 messageID;
+ in_stream >> messageID;
+ switch (messageID)
+ {
+ case KMessageServer::MSG_BROADCAST:
+ {
+ Q_UINT32 clientID;
+ in_stream >> clientID;
+ emit broadcastReceived (in_buffer.readAll(), clientID);
+ }
+ break;
+
+ case KMessageServer::MSG_FORWARD:
+ {
+ Q_UINT32 clientID;
+ QValueList <Q_UINT32> receivers;
+ in_stream >> clientID >> receivers;
+ emit forwardReceived (in_buffer.readAll(), clientID, receivers);
+ }
+ break;
+
+ case KMessageServer::ANS_CLIENT_ID:
+ {
+ bool old_admin = isAdmin();
+ Q_UINT32 clientID;
+ in_stream >> clientID;
+ d->connection->setId (clientID);
+ if (old_admin != isAdmin())
+ emit adminStatusChanged (isAdmin());
+ }
+ break;
+
+ case KMessageServer::ANS_ADMIN_ID:
+ {
+ bool old_admin = isAdmin();
+ in_stream >> d->adminID;
+ if (old_admin != isAdmin())
+ emit adminStatusChanged (isAdmin());
+ }
+ break;
+
+ case KMessageServer::ANS_CLIENT_LIST:
+ {
+ in_stream >> d->clientList;
+ }
+ break;
+
+ case KMessageServer::EVNT_CLIENT_CONNECTED:
+ {
+ Q_UINT32 id;
+ in_stream >> id;
+
+ if (d->clientList.contains (id))
+ kdWarning (11001) << k_funcinfo << ": Adding a client that already existed!" << endl;
+ else
+ d->clientList.append (id);
+
+ emit eventClientConnected (id);
+ }
+ break;
+
+ case KMessageServer::EVNT_CLIENT_DISCONNECTED:
+ {
+ Q_UINT32 id;
+ Q_INT8 broken;
+ in_stream >> id >> broken;
+
+ if (!d->clientList.contains (id))
+ kdWarning (11001) << k_funcinfo << ": Removing a client that doesn't exist!" << endl;
+ else
+ d->clientList.remove (id);
+
+ emit eventClientDisconnected (id, bool (broken));
+ }
+ break;
+
+ default:
+ unknown = true;
+ }
+
+ if (!unknown && !in_buffer.atEnd())
+ kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl;
+
+ emit serverMessageReceived (msg, unknown);
+
+ if (unknown)
+ kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl;
+}
+
+void KMessageClient::processFirstMessage()
+{
+ if (d->isLocked)
+ {
+ return;
+ }
+ if (d->delayedMessages.count() == 0)
+ {
+ kdDebug(11001) << k_funcinfo << ": no messages delayed" << endl;
+ return;
+ }
+ QByteArray first = d->delayedMessages.front();
+ d->delayedMessages.pop_front();
+ processMessage (first);
+}
+
+void KMessageClient::removeBrokenConnection ()
+{
+ kdDebug (11001) << k_funcinfo << ": timer single shot for removeBrokenConnection"<<this << endl;
+ // MH We cannot directly delete the socket. otherwise QSocket crashes
+ QTimer::singleShot( 0, this, SLOT(removeBrokenConnection2()) );
+ return;
+}
+
+
+void KMessageClient::removeBrokenConnection2 ()
+{
+ kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object"<<this << endl;
+
+ emit aboutToDisconnect(id());
+ delete d->connection;
+ d->connection = 0;
+ d->adminID = 0;
+ emit connectionBroken();
+ kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object DONE" << endl;
+}
+
+void KMessageClient::disconnect ()
+{
+ kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object" << endl;
+
+ emit aboutToDisconnect(id());
+ delete d->connection;
+ d->connection = 0;
+ d->adminID = 0;
+ emit connectionBroken();
+ kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object DONE" << endl;
+}
+
+void KMessageClient::lock ()
+{
+ d->isLocked = true;
+}
+
+void KMessageClient::unlock ()
+{
+ d->isLocked = false;
+ for (unsigned int i = 0; i < d->delayedMessages.count(); i++)
+ {
+ QTimer::singleShot(0, this, SLOT(processFirstMessage()));
+ }
+}
+
+unsigned int KMessageClient::delayedMessageCount() const
+{
+ return d->delayedMessages.count();
+}
+
+#include "kmessageclient.moc"
diff --git a/libkdegames/kgame/kmessageclient.h b/libkdegames/kgame/kmessageclient.h
new file mode 100644
index 00000000..90364c8d
--- /dev/null
+++ b/libkdegames/kgame/kmessageclient.h
@@ -0,0 +1,422 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KMESSAGECLIENT_H__
+#define __KMESSAGECLIENT_H__
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+class KMessageIO;
+class KMessageServer;
+class KMessageClientPrivate;
+
+/**
+ @short A client to connect to a KMessageServer
+
+ This class implements a client that can connect to a KMessageServer object.
+ It can be used to exchange messages between clients.
+
+ Usually you will connect the signals broadcastReceived and forwardReceived to
+ some specific slots. In these slot methods you can analyse the messages that are
+ sent to you from other clients.
+
+ To send messages to other clients, use the methods sendBroadcast() (to send to all
+ clients) or sendForward() (to send to a list of selected clients).
+
+ If you want to communicate with the KMessageServer object directly (on a more low
+ level base), use the method sendServerMessage to send a command to the server and
+ connect to the signal serverMessageReceived to see all the incoming messages.
+ In that case the messages must be of the format specified in KMessageServer.
+ @author Burkhard Lehner <Burkhard.Lehner@gmx.de>
+*/
+class KMessageClient : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ Constructor.
+ Creates an unconnected KMessageClient object. Use setServer() later to connect to a
+ KMessageServer object.
+ */
+ KMessageClient (QObject *parent = 0, const char *name = 0);
+
+ /**
+ Destructor.
+ Disconnects from the server, if any connection was established.
+ */
+ ~KMessageClient ();
+
+ /**
+ @return The client ID of this client. Every client that is connected to a KMessageServer
+ has a unique ID number.
+
+ NOTE: As long as the object is not yet connected to the server, and as long as the server
+ hasn't sent the client ID, this method returns 0.
+ */
+ Q_UINT32 id () const;
+
+ /**
+ @return Whether or not this client is the server admin.
+ One of the clients connected to the server is the admin and can administrate the server
+ (set maximum number of clients, remove clients, ...).
+
+ If you use admin commands without being the admin, these commands are simply ignored by
+ the server.
+
+ NOTE: As long as you are not connected to a server, this method returns false.
+ */
+ bool isAdmin () const;
+
+ /**
+ @return The ID of the admin client on the message server.
+ */
+ Q_UINT32 adminId() const;
+
+ /**
+ @return The list of the IDs of all the message clients connected to the message server.
+ */
+ const QValueList <Q_UINT32> &clientList() const;
+
+ /**
+ Connects the client to (another) server.
+
+ Tries to connect via a TCP/IP socket to a KMessageServer object
+ on the given host, listening on the specified port.
+
+ If we were already connected, the old connection is closed.
+ @param host The name of the host to connect to. Must be either a hostname which can
+ be resolved to an IP or just an IP
+ @param port The port to connect to
+ */
+ void setServer (const QString &host, Q_UINT16 port);
+
+ /**
+ Connects the client to (another) server.
+
+ Connects to the given server, using KMessageDirect.
+ (The server object has to be in the same process.)
+
+ If we were already connected, the old connection is closed.
+ @param server The KMessageServer to connect to
+ */
+ void setServer (KMessageServer *server);
+
+ /**
+ * Corresponds to setServer(0); but also emits the connectionBroken signal
+ **/
+ void disconnect();
+
+ /**
+ Connects the client to (another) server.
+
+ To use this method, you have to create a KMessageIO object with new (indeed you must
+ create an instance of a subclass of KMessageIO, e.g. KMessageSocket or KMessageDirect).
+ This object must already be connected to the new server.
+
+ Calling this method disconnects any earlier connection, and uses the new KMessageIO
+ object instead. This object gets owned by the KMessageClient object, so don't delete
+ or manipulate it afterwards.
+
+ With this method it is possible to change the server on the fly. But be careful that
+ there are no important messages from the old server not yet delivered.
+
+ NOTE: It is very likely that we will have another client ID on the new server. The
+ value returned by clientID may be a little outdated until the new server tells us
+ our new ID.
+
+ NOTE: The two other setServer methods are for convenience. If you use them, you don't
+ have to create a KMessageIO object yourself.
+ */
+ virtual void setServer (KMessageIO *connection);
+
+ /**
+ @return True, if a connection to a KMessageServer has been started, and if the
+ connection is ready for transferring data. (It will return false e.g. as long as
+ a socket connection hasn't been established, and it will also return false after
+ a socket connection is broken.)
+ */
+ bool isConnected () const;
+
+ /**
+ @return TRUE if isConnected() is true AND this is not a local (like
+ KMessageDirect) connection.
+ */
+ bool isNetwork () const;
+
+ /**
+ @return 0 if isConnected() is FALSE, otherwise the port number this client is
+ connected to. See also KMessageIO::peerPort and QSocket::peerPort.
+ @since 3.2
+ */
+ Q_UINT16 peerPort () const;
+
+ /**
+ @since 3.2
+ @return "localhost" if isConnected() is FALSE, otherwise the hostname this client is
+ connected to. See also KMessageIO::peerName() and QSocket::peerName().
+ */
+ QString peerName() const;
+
+ /**
+ Sends a message to the KMessageServer. If we are not yet connected to one, nothing
+ happens.
+
+ Use this method to send a low level command to the server. It has to be in the
+ format specified in KMessageServer.
+
+ If you want to send messages to other clients, you should use sendBroadcast()
+ and sendForward().
+ @param msg The message to be sent to the server. Must be in the format specified in KMessageServer.
+ */
+ void sendServerMessage (const QByteArray &msg);
+
+ /**
+ Sends a message to all the clients connected to the server, including ourself.
+ The message consists of an arbitrary block of data with arbitrary length.
+
+ All the clients will receive an exact copy of this block of data, which will be
+ processed in their processBroadcast() method.
+ @param msg The message to be sent to the clients
+ */
+ //AB: processBroadcast doesn't exist!! is processIncomingMessage meant?
+ void sendBroadcast (const QByteArray &msg);
+
+ /**
+ Sends a message to all the clients in a list.
+ The message consists of an arbitrary block of data with arbitrary length.
+
+ All clients will receive an exact copy of this block of data, which will be
+ processed in their processForward() method.
+
+ If the list contains client IDs that are not defined, they are ignored. If
+ it contains an ID several times, that client will receive the message several
+ times.
+
+ To send a message to the admin of the KMessageServer, you can use 0 as clientID,
+ instead of using the real client ID.
+ @param msg The message to be sent to the clients
+ @param clients A list of clients the message should be sent to
+ */
+ //AB: processForward doesn't exist!! is processIncomingMessage meant?
+ void sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients);
+
+ /**
+ Sends a message to a single client. This is a convenieance method. It calls
+ sendForward (const QByteArray &msg, const QValueList &ltQ_UINT32> &clients)
+ with a list containing only one client ID.
+
+ To send a message to the admin of the KMessageServer, you can use 0 as clientID,
+ instead of using the real client ID.
+ @param msg The message to be sent to the client
+ @param client The id of the client the message shall be sent to
+ */
+ void sendForward (const QByteArray &msg, Q_UINT32 client);
+
+ /**
+ Once this function is called no message will be received anymore.
+ processIncomingMessage() gets delayed until unlock() is called.
+
+ Note that all messages are still received, but their delivery (like
+ broadcastReceived()) get delayed only.
+ */
+ void lock();
+
+ /**
+ Deliver every message that was delayed by lock() and actually deliver
+ all messages that get received from now on.
+ */
+ void unlock();
+
+ /**
+ @return The number of messages that got delayed since lock() was called
+ */
+ unsigned int delayedMessageCount() const;
+
+signals:
+ /**
+ This signal is emitted when the client receives a broadcast message from the
+ KMessageServer, sent by another client. Connect to this signal to analyse the
+ received message and do the right reaction.
+
+ senderID contains the ID of the client that sent the broadcast message. You can
+ use this e.g. to send a reply message to only that client. Or you can use it
+ to ignore broadcast messages that were sent by yourself:
+
+ \code
+ void myObject::myBroadcastSlot (const QByteArray &msg, Q_UINT32 senderID)
+ {
+ if (senderID == ((KMessageClient *)sender())->id())
+ return;
+ ...
+ }
+ \endcode
+ @param msg The message that has been sent to us
+ @param senderID The ID of the client which sent the message
+ */
+ void broadcastReceived (const QByteArray &msg, Q_UINT32 senderID);
+
+ /**
+ This signal is emitted when the client receives a forward message from the
+ KMessageServer, sent by another client. Connect to this signal to analyse the
+ received message and do the right reaction.
+
+ senderID contains the ID of the client that sent the broadcast message. You can
+ use this e.g. to send a reply message to only that client.
+
+ receivers contains the list of the clients that got the message. (If this list
+ only contains one number, this will be your client ID, and it was exclusivly
+ sent to you.)
+
+ If you don't want to distinguish between broadcast and forward messages and
+ treat them the same, you can connect forwardReceived signal to the
+ broadcastReceived signal. (Yes, that's possible! You can connect a Qt signal to
+ a Qt signal, and the second one can have less parameters.)
+
+ \code
+ KMessageClient *client = new KMessageClient ();
+ connect (client, SIGNAL (forwardReceived (const QByteArray &, Q_UINT32, const QValueList <Q_UINT32>&)),
+ client, SIGNAL (broadcastReceived (const QByteArray &, Q_UINT32)));
+ \endcode
+
+ Then connect the broadcast signal to your slot that analyzes the message.
+ @param msg The message that has been sent to us
+ @param senderID The ID of the client which sent the message
+ @param receivers All clients which receive this message
+ */
+ void forwardReceived (const QByteArray &msg, Q_UINT32 senderID, const QValueList <Q_UINT32> &receivers);
+
+ /**
+ This signal is emitted when the connection to the KMessageServer is broken.
+ Reasons for this can be: a network error, a server breakdown, or you were just kicked
+ from the server.
+
+ When this signal is sent, the connection is already lost and the client is unconnected.
+ You can connect to another server by calling setServer() afterwards. But keep in mind that
+ some important messages might have vanished.
+ */
+ void connectionBroken ();
+
+ /**
+ This signal is emitted right before the client disconnects. It can be used
+ to this store the id of the client which is about to be lost.
+ */
+ void aboutToDisconnect(Q_UINT32 id);
+
+ /**
+ This signal is emitted when this client becomes the admin client or when it loses
+ the admin client status. Connect to this signal if you have to do any initialization
+ or cleanup.
+ @param isAdmin Whether we are now admin or not
+ */
+ void adminStatusChanged (bool isAdmin);
+
+ /**
+ This signal is emitted when another client has connected
+ to the server. Connect to this method if that clients needs initialization.
+ This should usually only be done in one client, e.g. the admin client.
+ @param clientID The ID of the client that has newly connectd.
+ */
+ void eventClientConnected (Q_UINT32 clientID);
+
+ /**
+ This signal is emitted when the server has lost the
+ connection to one of the clients (This could be because of a bad internet connection
+ or because the client disconnected on purpose).
+ @param clientID The ID of the client that has disconnected
+ @param broken true if it was disconnected because of a network error
+ */
+ void eventClientDisconnected (Q_UINT32 clientID, bool broken);
+
+ /**
+ This signal is emitted on every message that came from the server. You can connect to this
+ signal to see the messages directly. They are in the format specified in KMessageServer.
+
+ @param msg The message that has been sent to us
+ @param unknown True when KMessageClient didn't recognize the message, i.e. it contained an unknown
+ message ID. If you want to add additional message types to the client, connect to this signal,
+ and if unknown is true, analyse the message by yourself. If you recognized the message,
+ set unknown to false (Otherwise a debug message will be printed).
+ */
+ //AB: maybe add a setNoEmit() so that the other signals can be deactivated?
+ //Could be a performance benefit (note: KMessageClient is a time critical
+ //class!!!)
+ void serverMessageReceived (const QByteArray &msg, bool &unknown);
+
+protected:
+ /**
+ This slot is called from processIncomingMessage or
+ processFirstMessage, depending on whether the client is locked or a delayed
+ message is still here (see lock)
+
+ It processes the message and analyses it. If it is a broadcast or a forward message from
+ another client, it emits the signal processBroadcast or processForward accordingly.
+
+ If you want to treat additional server messages, you can overwrite this method. Don't
+ forget to call processIncomingMessage of your superclass!
+
+ At the moment, the following server messages are interpreted:
+
+ MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
+ @param msg The incoming message
+ */
+
+ virtual void processMessage (const QByteArray& msg);
+
+protected slots:
+ /**
+ This slot is called from the signal KMessageIO::received whenever a message from the
+ KMessageServer arrives.
+
+ It processes the message and analyses it. If it is a broadcast or a forward message from
+ another client, it emits the signal processBroadcast or processForward accordingly.
+
+ If you want to treat additional server messages, you can overwrite this method. Don't
+ forget to call processIncomingMessage() of your superclass!
+
+ At the moment, the following server messages are interpreted:
+
+ MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
+ @param msg The incoming message
+ */
+ virtual void processIncomingMessage (const QByteArray &msg);
+
+ /**
+ Called from unlock() (using QTimer::singleShot) until all delayed
+ messages are delivered.
+ */
+ void processFirstMessage();
+
+ /**
+ This slot is called from the signal KMessageIO::connectionBroken.
+
+ It deletes the internal KMessageIO object, and resets the client to default
+ values. To connect again to another server, use setServer.
+ */
+ virtual void removeBrokenConnection ();
+ void removeBrokenConnection2 ();
+
+private:
+ KMessageClientPrivate *d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kmessageio.cpp b/libkdegames/kgame/kmessageio.cpp
new file mode 100644
index 00000000..f3353277
--- /dev/null
+++ b/libkdegames/kgame/kmessageio.cpp
@@ -0,0 +1,482 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ KMessageIO class and subclasses KMessageSocket and KMessageDirect
+*/
+
+#include "kmessageio.h"
+#include <qsocket.h>
+#include <kdebug.h>
+#include <kprocess.h>
+#include <qfile.h>
+
+// ----------------------- KMessageIO -------------------------
+
+KMessageIO::KMessageIO (QObject *parent, const char *name)
+ : QObject (parent, name), m_id (0)
+{}
+
+KMessageIO::~KMessageIO ()
+{}
+
+void KMessageIO::setId (Q_UINT32 id)
+{
+ m_id = id;
+}
+
+Q_UINT32 KMessageIO::id ()
+{
+ return m_id;
+}
+
+// ----------------------KMessageSocket -----------------------
+
+KMessageSocket::KMessageSocket (QString host, Q_UINT16 port, QObject *parent,
+const char *name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->connectToHost (host, port);
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (QHostAddress host, Q_UINT16 port, QObject
+*parent, const char *name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->connectToHost (host.toString(), port);
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (QSocket *socket, QObject *parent, const char
+*name)
+ : KMessageIO (parent, name)
+{
+ mSocket = socket;
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (int socketFD, QObject *parent, const char
+*name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->setSocket (socketFD);
+ initSocket ();
+}
+
+KMessageSocket::~KMessageSocket ()
+{
+ delete mSocket;
+}
+
+bool KMessageSocket::isConnected () const
+{
+ return mSocket->state() == QSocket::Connection;
+}
+
+void KMessageSocket::send (const QByteArray &msg)
+{
+ QDataStream str (mSocket);
+ str << Q_UINT8 ('M'); // magic number for begin of message
+ str.writeBytes (msg.data(), msg.size()); // writes the length (as Q_UINT32) and the data
+}
+
+void KMessageSocket::processNewData ()
+{
+ if (isRecursive)
+ return;
+ isRecursive = true;
+
+ QDataStream str (mSocket);
+ while (mSocket->bytesAvailable() > 0)
+ {
+ if (mAwaitingHeader)
+ {
+ // Header = magic number + packet length = 5 bytes
+ if (mSocket->bytesAvailable() < 5)
+ {
+ isRecursive = false;
+ return;
+ }
+
+ // Read the magic number first. If something unexpected is found,
+ // start over again, ignoring the data that was read up to then.
+
+ Q_UINT8 v;
+ str >> v;
+ if (v != 'M')
+ {
+ kdWarning(11001) << k_funcinfo << ": Received unexpected data, magic number wrong!" << endl;
+ continue;
+ }
+
+ str >> mNextBlockLength;
+ mAwaitingHeader = false;
+ }
+ else
+ {
+ // Data not completely read => wait for more
+ if (mSocket->bytesAvailable() < (Q_ULONG) mNextBlockLength)
+ {
+ isRecursive = false;
+ return;
+ }
+
+ QByteArray msg (mNextBlockLength);
+ str.readRawBytes (msg.data(), mNextBlockLength);
+
+ // send the received message
+ emit received (msg);
+
+ // Waiting for the header of the next message
+ mAwaitingHeader = true;
+ }
+ }
+
+ isRecursive = false;
+}
+
+void KMessageSocket::initSocket ()
+{
+ connect (mSocket, SIGNAL (error(int)), SIGNAL (connectionBroken()));
+ connect (mSocket, SIGNAL (connectionClosed()), SIGNAL (connectionBroken()));
+ connect (mSocket, SIGNAL (readyRead()), SLOT (processNewData()));
+ mAwaitingHeader = true;
+ mNextBlockLength = 0;
+ isRecursive = false;
+}
+
+Q_UINT16 KMessageSocket::peerPort () const
+{
+ return mSocket->peerPort();
+}
+
+QString KMessageSocket::peerName () const
+{
+ return mSocket->peerName();
+}
+
+// ----------------------KMessageDirect -----------------------
+
+KMessageDirect::KMessageDirect (KMessageDirect *partner, QObject *parent,
+const char *name)
+ : KMessageIO (parent, name), mPartner (0)
+{
+ // 0 as first parameter leaves the object unconnected
+ if (!partner)
+ return;
+
+ // Check if the other object is already connected
+ if (partner && partner->mPartner)
+ {
+ kdWarning(11001) << k_funcinfo << ": Object is already connected!" << endl;
+ return;
+ }
+
+ // Connect from us to that object
+ mPartner = partner;
+
+ // Connect the other object to us
+ partner->mPartner = this;
+}
+
+KMessageDirect::~KMessageDirect ()
+{
+ if (mPartner)
+ {
+ mPartner->mPartner = 0;
+ emit mPartner->connectionBroken();
+ }
+}
+
+bool KMessageDirect::isConnected () const
+{
+ return mPartner != 0;
+}
+
+void KMessageDirect::send (const QByteArray &msg)
+{
+ if (mPartner)
+ emit mPartner->received (msg);
+ else
+ kdError(11001) << k_funcinfo << ": Not yet connected!" << endl;
+}
+
+
+// ----------------------- KMessageProcess ---------------------------
+
+KMessageProcess::~KMessageProcess()
+{
+ kdDebug(11001) << "@@@KMessageProcess::Delete process" << endl;
+ if (mProcess)
+ {
+ mProcess->kill();
+ delete mProcess;
+ mProcess=0;
+ // Remove not send buffers
+ mQueue.setAutoDelete(true);
+ mQueue.clear();
+ // Maybe todo: delete mSendBuffer
+ }
+}
+KMessageProcess::KMessageProcess(QObject *parent, QString file) : KMessageIO(parent,0)
+{
+ // Start process
+ kdDebug(11001) << "@@@KMessageProcess::Start process" << endl;
+ mProcessName=file;
+ mProcess=new KProcess;
+ int id=0;
+ *mProcess << mProcessName << QString("%1").arg(id);
+ kdDebug(11001) << "@@@KMessageProcess::Init:Id= " << id << endl;
+ kdDebug(11001) << "@@@KMessgeProcess::Init:Processname: " << mProcessName << endl;
+ connect(mProcess, SIGNAL(receivedStdout(KProcess *, char *, int )),
+ this, SLOT(slotReceivedStdout(KProcess *, char * , int )));
+ connect(mProcess, SIGNAL(receivedStderr(KProcess *, char *, int )),
+ this, SLOT(slotReceivedStderr(KProcess *, char * , int )));
+ connect(mProcess, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotProcessExited(KProcess *)));
+ connect(mProcess, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(slotWroteStdin(KProcess *)));
+ mProcess->start(KProcess::NotifyOnExit,KProcess::All);
+ mSendBuffer=0;
+ mReceiveCount=0;
+ mReceiveBuffer.resize(1024);
+}
+bool KMessageProcess::isConnected() const
+{
+ kdDebug(11001) << "@@@KMessageProcess::Is conencted" << endl;
+ if (!mProcess) return false;
+ return mProcess->isRunning();
+}
+void KMessageProcess::send(const QByteArray &msg)
+{
+ kdDebug(11001) << "@@@KMessageProcess:: SEND("<<msg.size()<<") to process" << endl;
+ unsigned int size=msg.size()+2*sizeof(long);
+
+ char *tmpbuffer=new char[size];
+ long *p1=(long *)tmpbuffer;
+ long *p2=p1+1;
+ kdDebug(11001) << "p1="<<p1 << "p2="<< p2 << endl;
+ memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size());
+ *p1=0x4242aeae;
+ *p2=size;
+
+ QByteArray *buffer=new QByteArray();
+ buffer->assign(tmpbuffer,size);
+ // buffer->duplicate(msg);
+ mQueue.enqueue(buffer);
+ writeToProcess();
+}
+void KMessageProcess::writeToProcess()
+{
+ // Previous send ok and item in queue
+ if (mSendBuffer || mQueue.isEmpty()) return ;
+ mSendBuffer=mQueue.dequeue();
+ if (!mSendBuffer) return ;
+
+ // write it out to the process
+ // kdDebug(11001) << " @@@@@@ writeToProcess::SEND to process " << mSendBuffer->size() << " BYTE " << endl;
+ // char *p=mSendBuffer->data();
+ // for (int i=0;i<16;i++) printf("%02x ",(unsigned char)(*(p+i)));printf("\n");
+ mProcess->writeStdin(mSendBuffer->data(),mSendBuffer->size());
+
+}
+void KMessageProcess::slotWroteStdin(KProcess * )
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ if (mSendBuffer)
+ {
+ delete mSendBuffer;
+ mSendBuffer=0;
+ }
+ writeToProcess();
+}
+
+void KMessageProcess::slotReceivedStderr(KProcess * proc, char *buffer, int buflen)
+{
+ int pid=0;
+ int len;
+ char *p;
+ char *pos;
+// kdDebug(11001)<<"############# Got stderr " << buflen << " bytes" << endl;
+
+ if (!buffer || buflen==0) return ;
+ if (proc) pid=proc->pid();
+
+
+ pos=buffer;
+ do
+ {
+ p=(char *)memchr(pos,'\n',buflen);
+ if (!p) len=buflen;
+ else len=p-pos;
+
+ QByteArray a;
+ a.setRawData(pos,len);
+ QString s(a);
+ kdDebug(11001) << "PID" <<pid<< ":" << s << endl;
+ a.resetRawData(pos,len);
+ if (p) pos=p+1;
+ buflen-=len+1;
+ }while(buflen>0);
+}
+
+
+void KMessageProcess::slotReceivedStdout(KProcess * , char *buffer, int buflen)
+{
+ kdDebug(11001) << "$$$$$$ " << k_funcinfo << ": Received " << buflen << " bytes over inter process communication" << endl;
+
+ // TODO Make a plausibility check on buflen to avoid memory overflow
+ while (mReceiveCount+buflen>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024);
+ memcpy(mReceiveBuffer.data()+mReceiveCount,buffer,buflen);
+ mReceiveCount+=buflen;
+
+ // Possbile message
+ while (mReceiveCount>2*sizeof(long))
+ {
+ long *p1=(long *)mReceiveBuffer.data();
+ long *p2=p1+1;
+ unsigned int len;
+ if (*p1!=0x4242aeae)
+ {
+ kdDebug(11001) << k_funcinfo << ": Cookie error...transmission failure...serious problem..." << endl;
+// for (int i=0;i<mReceiveCount;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n");
+ }
+ len=(int)(*p2);
+ if (len<2*sizeof(long))
+ {
+ kdDebug(11001) << k_funcinfo << ": Message size error" << endl;
+ break;
+ }
+ if (len<=mReceiveCount)
+ {
+ kdDebug(11001) << k_funcinfo << ": Got message with len " << len << endl;
+
+ QByteArray msg;
+ // msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ emit received(msg);
+ // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ // Shift buffer
+ if (len<mReceiveCount)
+ {
+ memmove(mReceiveBuffer.data(),mReceiveBuffer.data()+len,mReceiveCount-len);
+ }
+ mReceiveCount-=len;
+ }
+ else break;
+ }
+}
+
+void KMessageProcess::slotProcessExited(KProcess * /*p*/)
+{
+ kdDebug(11001) << "Process exited (slot)" << endl;
+ emit connectionBroken();
+ delete mProcess;
+ mProcess=0;
+}
+
+
+// ----------------------- KMessageFilePipe ---------------------------
+KMessageFilePipe::KMessageFilePipe(QObject *parent,QFile *readfile,QFile *writefile) : KMessageIO(parent,0)
+{
+ mReadFile=readfile;
+ mWriteFile=writefile;
+ mReceiveCount=0;
+ mReceiveBuffer.resize(1024);
+}
+
+KMessageFilePipe::~KMessageFilePipe()
+{
+}
+
+bool KMessageFilePipe::isConnected () const
+{
+ return (mReadFile!=0)&&(mWriteFile!=0);
+}
+
+void KMessageFilePipe::send(const QByteArray &msg)
+{
+ unsigned int size=msg.size()+2*sizeof(long);
+
+ char *tmpbuffer=new char[size];
+ long *p1=(long *)tmpbuffer;
+ long *p2=p1+1;
+ memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size());
+ *p1=0x4242aeae;
+ *p2=size;
+
+ QByteArray buffer;
+ buffer.assign(tmpbuffer,size);
+ mWriteFile->writeBlock(buffer);
+ mWriteFile->flush();
+ /*
+ fprintf(stderr,"+++ KMessageFilePipe:: SEND(%d to parent) realsize=%d\n",msg.size(),buffer.size());
+ for (int i=0;i<buffer.size();i++) fprintf(stderr,"%02x ",buffer[i]);fprintf(stderr,"\n");
+ fflush(stderr);
+ */
+}
+
+void KMessageFilePipe::exec()
+{
+
+ // According to BL: Blocking read is ok
+ // while(mReadFile->atEnd()) { usleep(100); }
+
+ int ch=mReadFile->getch();
+
+ while (mReceiveCount>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024);
+ mReceiveBuffer[mReceiveCount]=(char)ch;
+ mReceiveCount++;
+
+ // Change for message
+ if (mReceiveCount>=2*sizeof(long))
+ {
+ long *p1=(long *)mReceiveBuffer.data();
+ long *p2=p1+1;
+ unsigned int len;
+ if (*p1!=0x4242aeae)
+ {
+ fprintf(stderr,"KMessageFilePipe::exec:: Cookie error...transmission failure...serious problem...\n");
+// for (int i=0;i<16;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n");
+ }
+ len=(int)(*p2);
+ if (len==mReceiveCount)
+ {
+ //fprintf(stderr,"KMessageFilePipe::exec:: Got Message with len %d\n",len);
+
+ QByteArray msg;
+ //msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ emit received(msg);
+ //msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ mReceiveCount=0;
+ }
+ }
+
+
+ return ;
+
+
+}
+
+#include "kmessageio.moc"
diff --git a/libkdegames/kgame/kmessageio.h b/libkdegames/kgame/kmessageio.h
new file mode 100644
index 00000000..37cf35cd
--- /dev/null
+++ b/libkdegames/kgame/kmessageio.h
@@ -0,0 +1,416 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ KMessageIO class and subclasses KMessageSocket and KMessageDirect
+*/
+
+#ifndef _KMESSAGEIO_H_
+#define _KMESSAGEIO_H_
+
+#include <qcstring.h>
+#include <qhostaddress.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qptrqueue.h>
+#include <qfile.h>
+#include <kdebug.h>
+
+class QSocket;
+class KProcess;
+//class QFile;
+
+
+/**
+ This abstract base class represents one end of a message connections
+ between two clients. Each client has one object of a subclass of
+ KMessageIO. Calling /e send() on one object will emit the signal
+ /e received() on the other object, and vice versa.
+
+ For each type of connection (TCP/IP socket, COM port, direct connection
+ within the same class) a subclass of KMessageIO must be defined that
+ implements the pure virtual methods /e send() and /e isConnected(),
+ and sends the signals. (See /e KMessageSocket for an example implementation.)
+
+ Two subclasses are already included: /e KMessageSocket (connection using
+ TCP/IP sockets) and /e KMessageDirect (connection using method calls, both
+ sides must be within the same process).
+*/
+
+class KMessageIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * The usual QObject constructor, does nothing else.
+ **/
+ KMessageIO (QObject *parent = 0, const char *name = 0);
+
+ /**
+ * The usual destructor, does nothing special.
+ **/
+ ~KMessageIO ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 0;}
+
+ /**
+ * @return Whether this KMessageIO is a network IO or not.
+ **/
+ //virtual bool isNetwork () const = 0;
+ virtual bool isNetwork () const
+ {
+ kdError(11001) << "Calling PURE virtual isNetwork...BAD" << endl;
+ return false;
+ }
+
+ /**
+ This method returns the status of the object, whether it is already
+ (or still) connected to another KMessageIO object or not.
+
+ This is a pure virtual method, so it has to be implemented in a subclass
+ of KMessageIO.
+ */
+ //virtual bool isConnected () const = 0;
+ virtual bool isConnected () const
+ {
+ kdError(11001) << "Calling PURE virtual isConencted...BAD" << endl;
+ return false;
+ }
+
+ /**
+ Sets the ID number of this object. This number can for example be used to
+ distinguish several objects in a server.
+
+ NOTE: Sometimes it is useful to let two connected KMessageIO objects
+ have the same ID number. You have to do so yourself, KMessageIO doesn't
+ change this value on its own!
+ */
+ void setId (Q_UINT32 id);
+
+ /**
+ Queries the ID of this object.
+ */
+ Q_UINT32 id ();
+
+ /**
+ @since 3.2
+ @return 0 in the default implementation. Reimplemented in @ref KMessageSocket.
+ */
+ virtual Q_UINT16 peerPort () const { return 0; }
+
+ /**
+ @since 3.2
+ @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket
+ */
+ virtual QString peerName () const { return QString::fromLatin1("localhost"); }
+
+
+signals:
+ /**
+ This signal is emitted when /e send() on the connected KMessageIO
+ object is called. The parameter contains the same data array in /e msg
+ as was used in /e send().
+ */
+ void received (const QByteArray &msg);
+
+ /**
+ This signal is emitted when the connection is closed. This can be caused
+ by a hardware error (e.g. broken internet connection) or because the other
+ object was killed.
+
+ Note: Sometimes a broken connection can be undetected for a long time,
+ or may never be detected at all. So don't rely on this signal!
+ */
+ void connectionBroken ();
+
+public slots:
+
+ /**
+ This slot sends the data block in /e msg to the connected object, that will
+ emit /e received().
+
+ For a concrete class, you have to subclass /e KMessageIO and overwrite this
+ method. In the subclass, implement this method as an ordinary method, not
+ as a slot! (Otherwise another slot would be defined. It would work, but uses
+ more memory and time.) See /e KMessageSocket for an example implementation.
+ */
+ virtual void send (const QByteArray &msg) = 0;
+
+protected:
+ Q_UINT32 m_id;
+};
+
+
+/**
+ This class implements the message communication using a TCP/IP socket. The
+ object can connect to a server socket, or can use an already connected socket.
+*/
+
+class KMessageSocket : public KMessageIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ Connects to a server socket on /e host with /e port. host can be an
+ numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org")
+ IP address. You can immediately use the /e sendSystem() and
+ /e sendBroadcast() methods. The messages are stored and sent to the
+ receiver after the connection is established.
+
+ If the connection could not be established (e.g. unknown host or no server
+ socket at this port), the signal /e connectionBroken is emitted.
+ */
+ KMessageSocket (QString host, Q_UINT16 port, QObject *parent = 0,
+ const char *name = 0);
+
+ /**
+ Connects to a server socket on /e host with /e port. You can immediately
+ use the /e sendSystem() and /e sendBroadcast() methods. The messages are
+ stored and sent to the receiver after the connection is established.
+
+ If the connection could not be established (e.g. unknown host or no server
+ socket at this port), the signal /e connectionBroken is emitted.
+ */
+ KMessageSocket (QHostAddress host, Q_UINT16 port, QObject *parent = 0,
+ const char *name = 0);
+
+ /**
+ Uses /e socket to do the communication.
+
+ The socket should already be connected, or at least be in /e connecting
+ state.
+
+ Note: The /e socket object is then owned by the /e KMessageSocket object.
+ So don't use it otherwise any more and don't delete it. It is deleted
+ together with this KMessageSocket object. (Use 0 as parent for the QSocket
+ object t ensure it is not deleted.)
+ */
+ KMessageSocket (QSocket *socket, QObject *parent = 0, const char *name = 0);
+
+ /**
+ Uses the socket specified by the socket descriptor socketFD to do the
+ communication. The socket must already be connected.
+
+ This constructor can be used with a QServerSocket within the (pure
+ virtual) method /e newConnection.
+
+ Note: The socket is then owned by the /e KMessageSocket object. So don't
+ manipulate the socket afterwards, especially don't close it. The socket is
+ automatically closed when KMessageSocket is deleted.
+ */
+ KMessageSocket (int socketFD, QObject *parent = 0, const char *name = 0);
+
+ /**
+ Destructor, closes the socket.
+ */
+ ~KMessageSocket ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 1;}
+
+ /**
+ @since 3.2
+ @return The port that this object is connected to. See QSocket::peerPort
+ */
+ virtual Q_UINT16 peerPort () const;
+
+ /**
+ @since 3.2
+ @return The hostname this object is connected to. See QSocket::peerName.
+ */
+ virtual QString peerName () const;
+
+ /**
+ @return TRUE as this is a network IO.
+ */
+ bool isNetwork() const { return true; }
+
+ /**
+ Returns true if the socket is in state /e connected.
+ */
+ bool isConnected () const;
+
+ /**
+ Overwritten slot method from KMessageIO.
+
+ Note: It is not declared as a slot method, since the slot is already
+ defined in KMessageIO as a virtual method.
+ */
+ void send (const QByteArray &msg);
+
+protected slots:
+ virtual void processNewData ();
+
+protected:
+ void initSocket ();
+ QSocket *mSocket;
+ bool mAwaitingHeader;
+ Q_UINT32 mNextBlockLength;
+
+ bool isRecursive; // workaround for "bug" in QSocket, Qt 2.2.3 or older
+};
+
+
+/**
+ This class implements the message communication using function calls
+ directly. It can only be used when both sides of the message pipe are
+ within the same process. The communication is very fast.
+
+ To establish a communication, you have to create two instances of
+ KMessageDirect, the first one with no parameters in the constructor,
+ the second one with the first as parameter:
+
+ /code
+ KMessageDirect *peer1, *peer2;
+ peer1 = new KMessageDirect (); // unconnected
+ peer2 = new KMessageDirect (peer1); // connect with peer1
+ /endcode
+
+ The connection is only closed when one of the instances is deleted.
+*/
+
+class KMessageDirect : public KMessageIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ Creates an object and connects it to the object given in the first
+ parameter. Use 0 as first parameter to create an unconnected object,
+ that is later connected.
+
+ If that object is already connected, the object remains unconnected.
+ */
+ KMessageDirect (KMessageDirect *partner = 0, QObject *parent = 0, const char
+*name = 0);
+
+ /**
+ Destructor, closes the connection.
+ */
+ ~KMessageDirect ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 2;}
+
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ Returns true, if the object is connected to another instance.
+
+ If you use the first constructor, the object is unconnected unless another
+ object is created with this one as parameter.
+
+ The connection can only be closed by deleting one of the objects.
+ */
+ bool isConnected () const;
+
+ /**
+ Overwritten slot method from KMessageIO.
+
+ Note: It is not declared as a slot method, since the slot is already
+ defined in KMessageIO as a virtual method.
+ */
+ void send (const QByteArray &msg);
+
+protected:
+ KMessageDirect *mPartner;
+};
+
+class KMessageProcess : public KMessageIO
+{
+ Q_OBJECT
+
+ public:
+ KMessageProcess(QObject *parent, QString file);
+ ~KMessageProcess();
+ bool isConnected() const;
+ void send (const QByteArray &msg);
+ void writeToProcess();
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 3;}
+
+
+
+ public slots:
+ void slotReceivedStdout(KProcess *proc, char *buffer, int buflen);
+ void slotReceivedStderr(KProcess *proc, char *buffer, int buflen);
+ void slotProcessExited(KProcess *p);
+ void slotWroteStdin(KProcess *p);
+
+ private:
+ QString mProcessName;
+ KProcess *mProcess;
+ QPtrQueue <QByteArray> mQueue;
+ QByteArray *mSendBuffer;
+ QByteArray mReceiveBuffer;
+ unsigned int mReceiveCount;
+};
+
+class KMessageFilePipe : public KMessageIO
+{
+ Q_OBJECT
+
+ public:
+ KMessageFilePipe(QObject *parent,QFile *readFile,QFile *writeFile);
+ ~KMessageFilePipe();
+ bool isConnected() const;
+ void send (const QByteArray &msg);
+ void exec();
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 4;}
+
+
+
+ private:
+ QFile *mReadFile;
+ QFile *mWriteFile;
+ QByteArray mReceiveBuffer;
+ unsigned int mReceiveCount;
+};
+
+#endif
diff --git a/libkdegames/kgame/kmessageserver.cpp b/libkdegames/kgame/kmessageserver.cpp
new file mode 100644
index 00000000..e857ea31
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.cpp
@@ -0,0 +1,515 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qiodevice.h>
+#include <qbuffer.h>
+#include <qptrlist.h>
+#include <qptrqueue.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+
+#include "kmessageio.h"
+#include "kmessageserver.h"
+
+// --------------- internal class KMessageServerSocket
+
+KMessageServerSocket::KMessageServerSocket (Q_UINT16 port, QObject *parent)
+ : QServerSocket (port, 0, parent)
+{
+}
+
+KMessageServerSocket::~KMessageServerSocket ()
+{
+}
+
+void KMessageServerSocket::newConnection (int socket)
+{
+ emit newClientConnected (new KMessageSocket (socket));
+}
+
+// ---------------- class for storing an incoming message
+
+class MessageBuffer
+{
+ public:
+ MessageBuffer (Q_UINT32 clientID, const QByteArray &messageData)
+ : id (clientID), data (messageData) { }
+ ~MessageBuffer () {}
+ Q_UINT32 id;
+ QByteArray data;
+};
+
+// ---------------- KMessageServer's private class
+
+class KMessageServerPrivate
+{
+public:
+ KMessageServerPrivate()
+ : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (0)
+ {
+ mClientList.setAutoDelete (true);
+ mMessageQueue.setAutoDelete (true);
+ }
+
+ int mMaxClients;
+ int mGameId;
+ Q_UINT16 mCookie;
+ Q_UINT32 mUniqueClientNumber;
+ Q_UINT32 mAdminID;
+
+ KMessageServerSocket* mServerSocket;
+
+ QPtrList <KMessageIO> mClientList;
+ QPtrQueue <MessageBuffer> mMessageQueue;
+ QTimer mTimer;
+ bool mIsRecursive;
+};
+
+
+// ------------------ KMessageServer
+
+KMessageServer::KMessageServer (Q_UINT16 cookie,QObject* parent)
+ : QObject(parent, 0)
+{
+ d = new KMessageServerPrivate;
+ d->mIsRecursive=false;
+ d->mCookie=cookie;
+ connect (&(d->mTimer), SIGNAL (timeout()),
+ this, SLOT (processOneMessage()));
+ kdDebug(11001) << "CREATE(KMessageServer="
+ << this
+ << ") cookie="
+ << d->mCookie
+ << " sizeof(this)="
+ << sizeof(KMessageServer)
+ << endl;
+}
+
+KMessageServer::~KMessageServer()
+{
+ kdDebug(11001) << k_funcinfo << "this=" << this << endl;
+ Debug();
+ stopNetwork();
+ deleteClients();
+ delete d;
+ kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+//------------------------------------- TCP/IP server stuff
+
+bool KMessageServer::initNetwork (Q_UINT16 port)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+
+ if (d->mServerSocket)
+ {
+ kdDebug (11001) << k_funcinfo << ": We were already offering connections!" << endl;
+ delete d->mServerSocket;
+ }
+
+ d->mServerSocket = new KMessageServerSocket (port);
+ d->mIsRecursive = false;
+
+ if (!d->mServerSocket || !d->mServerSocket->ok())
+ {
+ kdError(11001) << k_funcinfo << ": Serversocket::ok() == false" << endl;
+ delete d->mServerSocket;
+ d->mServerSocket=0;
+ return false;
+ }
+
+ kdDebug (11001) << k_funcinfo << ": Now listening to port "
+ << d->mServerSocket->port() << endl;
+ connect (d->mServerSocket, SIGNAL (newClientConnected (KMessageIO*)),
+ this, SLOT (addClient (KMessageIO*)));
+ return true;
+}
+
+Q_UINT16 KMessageServer::serverPort () const
+{
+ if (d->mServerSocket)
+ return d->mServerSocket->port();
+ else
+ return 0;
+}
+
+void KMessageServer::stopNetwork()
+{
+ if (d->mServerSocket)
+ {
+ delete d->mServerSocket;
+ d->mServerSocket = 0;
+ }
+}
+
+bool KMessageServer::isOfferingConnections() const
+{
+ return d->mServerSocket != 0;
+}
+
+//----------------------------------------------- adding / removing clients
+
+void KMessageServer::addClient (KMessageIO* client)
+{
+ QByteArray msg;
+
+ // maximum number of clients reached?
+ if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount())
+ {
+ kdError (11001) << k_funcinfo << ": Maximum number of clients reached!" << endl;
+ return;
+ }
+
+ // give it a unique ID
+ client->setId (uniqueClientNumber());
+ kdDebug (11001) << k_funcinfo << ": " << client->id() << endl;
+
+ // connect its signals
+ connect (client, SIGNAL (connectionBroken()),
+ this, SLOT (removeBrokenClient()));
+ connect (client, SIGNAL (received (const QByteArray &)),
+ this, SLOT (getReceivedMessage (const QByteArray &)));
+
+ // Tell everyone about the new guest
+ // Note: The new client doesn't get this message!
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_CONNECTED) << client->id();
+ broadcastMessage (msg);
+
+ // add to our list
+ d->mClientList.append (client);
+
+ // tell it its ID
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_ID) << client->id();
+ client->send (msg);
+
+ // Give it the complete list of client IDs
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs();
+ client->send (msg);
+
+
+ if (clientCount() == 1)
+ {
+ // if it is the first client, it becomes the admin
+ setAdmin (client->id());
+ }
+ else
+ {
+ // otherwise tell it who is the admin
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID();
+ client->send (msg);
+ }
+
+ emit clientConnected (client);
+}
+
+void KMessageServer::removeClient (KMessageIO* client, bool broken)
+{
+ Q_UINT32 clientID = client->id();
+ if (!d->mClientList.removeRef (client))
+ {
+ kdError(11001) << k_funcinfo << ": Deleting client that wasn't added before!" << endl;
+ return;
+ }
+
+ // tell everyone about the removed client
+ QByteArray msg;
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (Q_INT8)broken;
+ broadcastMessage (msg);
+
+ // If it was the admin, select a new admin.
+ if (clientID == adminID())
+ {
+ if (!d->mClientList.isEmpty())
+ setAdmin (d->mClientList.first()->id());
+ else
+ setAdmin (0);
+ }
+}
+
+void KMessageServer::deleteClients()
+{
+ d->mClientList.clear();
+ d->mAdminID = 0;
+}
+
+void KMessageServer::removeBrokenClient ()
+{
+ if (!sender()->inherits ("KMessageIO"))
+ {
+ kdError (11001) << k_funcinfo << ": sender of the signal was not a KMessageIO object!" << endl;
+ return;
+ }
+
+ KMessageIO *client = (KMessageIO *) sender();
+
+ emit connectionLost (client);
+ removeClient (client, true);
+}
+
+
+void KMessageServer::setMaxClients(int c)
+{
+ d->mMaxClients = c;
+}
+
+int KMessageServer::maxClients() const
+{
+ return d->mMaxClients;
+}
+
+int KMessageServer::clientCount() const
+{
+ return d->mClientList.count();
+}
+
+QValueList <Q_UINT32> KMessageServer::clientIDs () const
+{
+ QValueList <Q_UINT32> list;
+ for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
+ list.append ((*iter)->id());
+ return list;
+}
+
+KMessageIO* KMessageServer::findClient (Q_UINT32 no) const
+{
+ if (no == 0)
+ no = d->mAdminID;
+
+ QPtrListIterator <KMessageIO> iter (d->mClientList);
+ while (*iter)
+ {
+ if ((*iter)->id() == no)
+ return (*iter);
+ ++iter;
+ }
+ return 0;
+}
+
+Q_UINT32 KMessageServer::adminID () const
+{
+ return d->mAdminID;
+}
+
+void KMessageServer::setAdmin (Q_UINT32 adminID)
+{
+ // Trying to set the the client that is already admin => nothing to do
+ if (adminID == d->mAdminID)
+ return;
+
+ if (adminID > 0 && findClient (adminID) == 0)
+ {
+ kdWarning (11001) << "Trying to set a new admin that doesn't exist!" << endl;
+ return;
+ }
+
+ d->mAdminID = adminID;
+
+ QByteArray msg;
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID;
+
+ // Tell everyone about the new master
+ broadcastMessage (msg);
+}
+
+
+//------------------------------------------- ID stuff
+
+Q_UINT32 KMessageServer::uniqueClientNumber() const
+{
+ return d->mUniqueClientNumber++;
+}
+
+// --------------------- Messages ---------------------------
+
+void KMessageServer::broadcastMessage (const QByteArray &msg)
+{
+ for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
+ (*iter)->send (msg);
+}
+
+void KMessageServer::sendMessage (Q_UINT32 id, const QByteArray &msg)
+{
+ KMessageIO *client = findClient (id);
+ if (client)
+ client->send (msg);
+}
+
+void KMessageServer::sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg)
+{
+ for (QValueListConstIterator <Q_UINT32> iter = ids.begin(); iter != ids.end(); ++iter)
+ sendMessage (*iter, msg);
+}
+
+void KMessageServer::getReceivedMessage (const QByteArray &msg)
+{
+ if (!sender() || !sender()->inherits("KMessageIO"))
+ {
+ kdError (11001) << k_funcinfo << ": slot was not called from KMessageIO!" << endl;
+ return;
+ }
+ //kdDebug(11001) << k_funcinfo << ": size=" << msg.size() << endl;
+ KMessageIO *client = (KMessageIO *) sender();
+ Q_UINT32 clientID = client->id();
+
+ //QByteArray *ta=new QByteArray;
+ //ta->duplicate(msg);
+ //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta));
+
+
+ d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg));
+ if (!d->mTimer.isActive())
+ d->mTimer.start(0); // AB: should be , TRUE i guess
+}
+
+void KMessageServer::processOneMessage ()
+{
+ // This shouldn't happen, since the timer should be stopped before. But only to be sure!
+ if (d->mMessageQueue.isEmpty())
+ {
+ d->mTimer.stop();
+ return;
+ }
+ if (d->mIsRecursive)
+ {
+ return;
+ }
+ d->mIsRecursive = true;
+
+ MessageBuffer *msg_buf = d->mMessageQueue.head();
+
+ Q_UINT32 clientID = msg_buf->id;
+ QBuffer in_buffer (msg_buf->data);
+ in_buffer.open (IO_ReadOnly);
+ QDataStream in_stream (&in_buffer);
+
+ QByteArray out_msg;
+ QBuffer out_buffer (out_msg);
+ out_buffer.open (IO_WriteOnly);
+ QDataStream out_stream (&out_buffer);
+
+ bool unknown = false;
+
+ QByteArray ttt=in_buffer.buffer();
+ Q_UINT32 messageID;
+ in_stream >> messageID;
+ //kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl;
+ switch (messageID)
+ {
+ case REQ_BROADCAST:
+ out_stream << Q_UINT32 (MSG_BROADCAST) << clientID;
+ // FIXME, compiler bug?
+ // this should be okay, since QBuffer is subclass of QIODevice! :
+ // out_buffer.writeBlock (in_buffer.readAll());
+ out_buffer.QIODevice::writeBlock (in_buffer.readAll());
+ broadcastMessage (out_msg);
+ break;
+
+ case REQ_FORWARD:
+ {
+ QValueList <Q_UINT32> clients;
+ in_stream >> clients;
+ out_stream << Q_UINT32 (MSG_FORWARD) << clientID << clients;
+ // see above!
+ out_buffer.QIODevice::writeBlock (in_buffer.readAll());
+ sendMessage (clients, out_msg);
+ }
+ break;
+
+ case REQ_CLIENT_ID:
+ out_stream << Q_UINT32 (ANS_CLIENT_ID) << clientID;
+ sendMessage (clientID, out_msg);
+ break;
+
+ case REQ_ADMIN_ID:
+ out_stream << Q_UINT32 (ANS_ADMIN_ID) << d->mAdminID;
+ sendMessage (clientID, out_msg);
+ break;
+
+ case REQ_ADMIN_CHANGE:
+ if (clientID == d->mAdminID)
+ {
+ Q_UINT32 newAdmin;
+ in_stream >> newAdmin;
+ setAdmin (newAdmin);
+ }
+ break;
+
+ case REQ_REMOVE_CLIENT:
+ if (clientID == d->mAdminID)
+ {
+ QValueList <Q_UINT32> client_list;
+ in_stream >> client_list;
+ for (QValueListIterator <Q_UINT32> iter = client_list.begin(); iter != client_list.end(); ++iter)
+ {
+ KMessageIO *client = findClient (*iter);
+ if (client)
+ removeClient (client, false);
+ else
+ kdWarning (11001) << k_funcinfo << ": removing non-existing clientID" << endl;
+ }
+ }
+ break;
+
+ case REQ_MAX_NUM_CLIENTS:
+ if (clientID == d->mAdminID)
+ {
+ Q_INT32 maximum_clients;
+ in_stream >> maximum_clients;
+ setMaxClients (maximum_clients);
+ }
+ break;
+
+ case REQ_CLIENT_LIST:
+ {
+ out_stream << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs();
+ sendMessage (clientID, out_msg);
+ }
+ break;
+
+ default:
+ unknown = true;
+ }
+
+ // check if all the data has been used
+ if (!unknown && !in_buffer.atEnd())
+ kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl;
+
+ emit messageReceived (msg_buf->data, clientID, unknown);
+
+ if (unknown)
+ kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl;
+
+ // remove the message, since we are ready with it
+ d->mMessageQueue.remove();
+ if (d->mMessageQueue.isEmpty())
+ d->mTimer.stop();
+ d->mIsRecursive = false;
+}
+
+void KMessageServer::Debug()
+{
+ kdDebug(11001) << "------------------ KMESSAGESERVER -----------------------" << endl;
+ kdDebug(11001) << "MaxClients : " << maxClients() << endl;
+ kdDebug(11001) << "NoOfClients : " << clientCount() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+#include "kmessageserver.moc"
diff --git a/libkdegames/kgame/kmessageserver.h b/libkdegames/kgame/kmessageserver.h
new file mode 100644
index 00000000..0049a2e7
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.h
@@ -0,0 +1,492 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KMESSAGESERVER_H__
+#define __KMESSAGESERVER_H__
+
+#include <qobject.h>
+#include <qserversocket.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+class KMessageIO;
+class KMessageServerPrivate;
+
+/**
+ @short A server for message sending and broadcasting, using TCP/IP connections.
+
+ An object of this class listens for incoming connections via TCP/IP sockets and
+ creates KMessageSocket objects for every established connection. It receives
+ messages from the "clients", analyses them and processes an appropriate
+ reaction.
+
+ You can also use other KMessageIO objects with KMessageServer, not only
+ TCP/IP socket based ones. Use addClient to connect via an object of any
+ KMessageIO subclass. (For clients within the same process, you can e.g. use
+ KMessageDirect.) This object already has to be connected.
+
+ The messages are always packages of an arbitrary length. The format of the messages
+ is given below. All the data is stored and received with QDataStream, to be
+ platform independant.
+
+ Setting up a KMessageServer can be done like this:
+
+ \code
+ KMessageServer *server = new KMessageServer ();
+ server->initNetwork (TCP/IP-Portnumber);
+ \endcode
+
+ Usually that is everything you will do. There are a lot of public methods to
+ administrate the object (maximum number of clients, finding clients, removing
+ clients, setting the admin client, ...), but this functionality can also
+ be done by messages from the clients. So you can administrate the object completely
+ on remote.
+
+ If you want to extend the Server for your own needs (e.g. additional message types),
+ you can either create a subclass and overwrite the method processOneMessage.
+ (But don't forget to call the method of the superclass!) Or you can connect to
+ the signal messageReceived, and analyse the messages there.
+
+ Every client has a unique ID, so that messages can be sent to another dedicated
+ client or a list of clients.
+
+ One of the clients (the admin) has a special administration right. Some of the
+ administration messages can only be used with him. The admin can give the admin
+ status to another client. You can send a message to the admin by using clientID 0.
+ This is always interpreted as the admin client, independant of its real clientID.
+
+ Here is a list of the messages the KMessageServer understands:
+ &lt;&lt; means, the value is inserted into the QByteArray using QDataStream. The
+ messageIDs (REQ_BROADCAST, ...) are of type Q_UINT32.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_BROADCAST ) << raw_data
+
+ When the server receives this message, it sends the following message to
+ ALL connected clients (a broadcast), where the raw_data is left unchanged:
+ QByteArray << static_cast &lt;Q_UINT32>( MSG_BROADCAST ) << clientID << raw_data
+ Q_UINT32 clientID; // the ID of the client that sent the broadcast request
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_FORWARD ) << client_list << raw_data
+ QValueList &lt;Q_UINT32> client_list; // list of receivers
+
+ When the server receives this message, it sends the following message to
+ the clients in client_list:
+ QByteArray << static_cast&lt;Q_UINT32>( MSG_FORWARD ) << senderID << client_list << raw_data
+ Q_UINT32 senderID; // the sender of the forward request
+ QValueList &lt;Q_UINT32> client_list; // a copy of the receiver list
+
+ Note: Every client receives the message as many times as he is in the client_list.
+ Note: Since the client_list is sent to all the clients, every client can see who else
+ got the message. If you want to prevent this, send a single REQ_FORWARD
+ message for every receiver.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_CLIENT_ID )
+
+ When the server receives this message, it sends the following message to
+ the asking client:
+ QByteArray << static_cast&lt;Q_UINT32>( ANS_CLIENT_ID ) << clientID
+ Q_UINT32 clientID; // The ID of the client who asked for it
+
+ Note: This answer is also automatically sent to a new connected client, so that he
+ can store his ID. The ID of a client doesn't change during his lifetime, and is
+ unique for this KMessageServer.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_ADMIN_ID )
+
+ When the server receives this message, it sends the following message to
+ the asking client:
+ QByteArray << ANS_ADMIN_ID << adminID
+ Q_UINT32 adminID; // The ID of the admin
+
+ Note: This answer is also automatically sent to a new connected client, so that he
+ can see if he is the admin or not. It will also be sent to all connected clients
+ when a new admin is set (see REQ_ADMIN_CHANGE).
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_ADMIN_CHANGE ) << new_admin
+ Q_UINT32 new_admin; // the ID of the new admin, or 0 for no admin
+
+ When the server receives this message, it sets the admin to the new ID. If no client
+ with that ID exists, nothing happens. With new_admin == 0 no client is a admin.
+ ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE!
+
+ Note: The server sends a ANS_ADMIN_ID message to every connected client.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_REMOVE_CLIENT ) << client_list
+ QValueList &lt;Q_UINT32> client_list; // The list of clients to be removed
+
+ When the server receives this message, it removes the clients with the ids stored in
+ client_list, disconnecting the connection to them.
+ ONLY THE ADMIN CAN USE THIS MESSAGE!
+
+ Note: If one of the clients is the admin himself, he will also be deleted.
+ Another client (if any left) will become the new admin.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients
+ Q_INT32 maximum_clients; // The maximum of clients connected, or infinite if -1
+
+ When the server receives this message, it limits the number of clients to the number given,
+ or sets it unlimited for maximum_clients == -1.
+ ONLY THE ADMIN CAN USE THIS MESSAGE!
+
+ Note: If there are already more clients, they are not affected. It only prevents new Clients
+ to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT)
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_CLIENT_LIST )
+
+ When the server receives this message, it answers by sending a list of IDs of all the clients
+ that are connected at the moment. So it sends the following message to the asking client:
+ QByteArray << static_cast&lt;Q_UINT32>( ANS_CLIENT_LIST ) << clientList
+ QValueList &lt;Q_UINT32> clientList; // The IDs of the connected clients
+
+ Note: This message is also sent to every new connected client, so that he knows the other
+ clients.
+
+ There are two more messages that are sent from the server to the every client automatically
+ when a new client connects or a connection to a client is lost:
+
+ QByteArray << static_cast&lt;Q_UINT32>( EVNT_CLIENT_CONNECTED ) << clientID;
+ Q_UINT32 clientID; // the ID of the new connected client
+
+ QByteArray << static_cast&lt;Q_UINT32>( EVNT_CLIENT_DISCONNECTED ) << clientID;
+ Q_UINT32 clientID; // the ID of the client that lost the connection
+ Q_UINT8 broken; // 1 if the network connection was closed, 0 if it was disconnected
+ // on purpose
+
+
+ @author Andreas Beckermann <b_mann@gmx.de>, Burkhard Lehner <Burkhard.Lehner@gmx.de>
+ @version $Id$
+*/
+class KMessageServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ MessageIDs for messages from a client to the message server.
+ */
+ enum {
+ REQ_BROADCAST = 1,
+ REQ_FORWARD,
+ REQ_CLIENT_ID,
+ REQ_ADMIN_ID,
+ REQ_ADMIN_CHANGE,
+ REQ_REMOVE_CLIENT,
+ REQ_MAX_NUM_CLIENTS,
+ REQ_CLIENT_LIST,
+ REQ_MAX_REQ = 0xffff };
+
+ /**
+ * MessageIDs for messages from the message server to a client.
+ **/
+ enum {
+ MSG_BROADCAST = 101,
+ MSG_FORWARD,
+ ANS_CLIENT_ID,
+ ANS_ADMIN_ID,
+ ANS_CLIENT_LIST,
+ EVNT_CLIENT_CONNECTED,
+ EVNT_CLIENT_DISCONNECTED,
+ EVNT_MAX_EVNT = 0xffff
+ };
+
+ /**
+ * Create a KGameNetwork object
+ **/
+ KMessageServer(Q_UINT16 cookie = 42, QObject* parent = 0);
+
+ ~KMessageServer();
+
+ /**
+ * Gives debug output of the game status
+ **/
+ virtual void Debug();
+
+//---------------------------------- TCP/IP server stuff
+
+ /**
+ * Starts the Communication server to listen for incoming TCP/IP connections.
+ *
+ * @param port The port on which the service is offered, or 0 to let the
+ * system pick a free port
+ * @return true if it worked
+ */
+ bool initNetwork (Q_UINT16 port = 0);
+
+ /**
+ * Returns the TCP/IP port number we are listening to for incoming connections.
+ * (This has to be known by other clients so that they can connect to us. It's
+ * especially necessary if you used 0 as port number in initNetwork().
+ * @return the port number
+ **/
+ Q_UINT16 serverPort () const;
+
+ /**
+ * Stops listening for connections. The already running connections are
+ * not affected.
+ * To listen for connections again call initNetwork again.
+ **/
+ void stopNetwork();
+
+ /**
+ * Are we still offer offering server connections?
+ * @return true, if we are still listening to connections requests
+ **/
+ bool isOfferingConnections() const;
+
+//---------------------------------- adding / removing clients
+
+public slots:
+ /**
+ * Adds a new @ref KMessageIO object to the communication server. This "client"
+ * gets a unique ID.
+ *
+ * This slot method is automatically called for any incoming TCP/IP
+ * connection. You can use it to add other types of connections, e.g.
+ * local connections (KMessageDirect) to the server manually.
+ *
+ * NOTE: The @ref KMessageIO object gets owned by the KMessageServer,
+ * so don't delete or manipulate it afterwards. It is automatically deleted
+ * when the connection is broken or the communication server is deleted.
+ * So, add a @ref KMessageIO object to just ONE KMessageServer.
+ **/
+ void addClient (KMessageIO *);
+
+ /**
+ * Removes the KMessageIO object from the client list and deletes it.
+ * This destroys the connection, if it already was up.
+ * Does NOT emit connectionLost.
+ * Sends an info message to the other clients, that contains the ID of
+ * the removed client and the value of the parameter broken.
+ *
+ * @param io the object to delete and to remove from the client list
+ * @param broken true if the client has lost connection
+ * Mostly used internally. You will probably not need this.
+ **/
+ void removeClient (KMessageIO *io, bool broken);
+
+ /**
+ Deletes all connections to the clients.
+ */
+ void deleteClients();
+
+private slots:
+ /**
+ * Removes the sender object of the signal that called this slot. It is
+ * automatically connected to @ref KMessageIO::connectionBroken.
+ * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object.
+ * Don't call it directly!
+ **/
+ void removeBrokenClient ();
+
+public:
+ /**
+ * sets the maximum number of clients which can connect.
+ * If this number is reached, no more clients can be added.
+ * Setting this number to -1 means unlimited number of clients.
+ *
+ * NOTE: Existing connections are not affected.
+ * So, clientCount > maxClients is possible, if there were already
+ * more clients than allowed before reducing this value.
+ *
+ * @param maxnumber the number of clients
+ **/
+ void setMaxClients(int maxnumber);
+
+ /**
+ * returns the maximum number of clients
+ *
+ * @return the number of clients
+ **/
+ int maxClients() const;
+
+ /**
+ * returns the current number of connected clients.
+ *
+ * @return the number of clients
+ **/
+ int clientCount() const;
+
+ /**
+ * returns a list of the unique IDs of all clients.
+ **/
+ QValueList <Q_UINT32> clientIDs() const;
+
+ /**
+ * Find the @ref KMessageIO object to the given client number.
+ * @param no the client number to look for, or 0 to look for the admin
+ * @return address of the client, or 0 if no client with that number exists
+ **/
+ KMessageIO *findClient (Q_UINT32 no) const;
+
+ /**
+ * Returns the clientID of the admin, if there is a admin, 0 otherwise.
+ *
+ * NOTE: Most often you don't need to know that id, since you can
+ * use clientID 0 to specify the admin.
+ **/
+ Q_UINT32 adminID() const;
+
+ /**
+ * Sets the admin to a new client with the given ID.
+ * The old admin (if existed) and the new admin will get the ANS_ADMIN message.
+ * If you use 0 as new adminID, no client will be admin.
+ **/
+ void setAdmin (Q_UINT32 adminID);
+
+
+//------------------------------ ID stuff
+
+ /*
+ * The unique ID of this game
+ *
+ * @return int id
+ **/
+// int gameId() const;
+
+ /*
+ * Application cookie. this idendifies the game application. It
+ * help to distinguish between e.g. KPoker and KWin4
+ *
+ * @return the application cookie
+ **/
+// int cookie() const;
+
+//------------------------------ Message stuff
+
+public:
+ /**
+ * Sends a message to all connected clients.
+ * The message is NOT translated in any way. This method calls
+ * @ref KMessageIO::send for every client added.
+ **/
+ virtual void broadcastMessage (const QByteArray &msg);
+
+ /**
+ * Sends a message to a single client with the given ID.
+ * The message is NOT translated in any way.
+ * If no client with the given id exists, nothing is done.
+ * This is just a convenience method. You could also call
+ * @ref findClient (id)->send(msg) manually, but this method checks for
+ * errors.
+ **/
+ virtual void sendMessage (Q_UINT32 id, const QByteArray &msg);
+
+ /**
+ * Sends a message to a list of clients. Their ID is given in ids. If
+ * a client id is given more than once in the list, the message is also
+ * sent several times to that client.
+ * This is just a convenience method. You could also iterate over the
+ * list of IDs.
+ **/
+ virtual void sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg);
+
+protected slots:
+ /**
+ * This slot receives all the messages from the @ref KMessageIO::received signals.
+ * It stores the messages in a queue. The messages are later taken out of the queue
+ * by @ref getReceivedMessage.
+ *
+ * NOTE: It is important that this slot may only be called from the signal
+ * @ref KMessageIO::received, since the sender() object is used to find out
+ * the client that sent the message!
+ **/
+ virtual void getReceivedMessage (const QByteArray &msg);
+
+ /**
+ * This slot is called whenever there are elements in the message queue. This queue
+ * is filled by @ref getReceivedMessage.
+ * This slot takes one message out of the queue and analyses processes it,
+ * if it recognizes it. (See message types in the description of the class.)
+ * After that, the signal @ref messageReceived is emitted. Connect to that signal if
+ * you want to process other types of messages.
+ **/
+ virtual void processOneMessage ();
+
+//---------------------------- Signals
+
+signals:
+ /**
+ * A new client connected to the game
+ * @param client the client object that connected
+ **/
+ void clientConnected (KMessageIO *client);
+
+ /**
+ * A network connection got broken. Note that the client will automatically get deleted
+ * after this signal is emitted. The signal is not emitted when the client was removed
+ * regularly.
+ *
+ * @param client the client which left the game
+ **/
+ void connectionLost (KMessageIO *client);
+
+ /**
+ * This signal is always emitted when a message from a client is received.
+ *
+ * You can use this signal to extend the communication server without subclassing.
+ * Just connect to this signal and analyse the message, if unknown is true.
+ * If you recognize a message and process it, set unknown to false, otherwise
+ * a warning message is printed.
+ *
+ * @param data the message data
+ * @param clientID the ID of the KMessageIO object that received the message
+ * @param unknown true, if the message type is not known by the KMessageServer
+ **/
+ void messageReceived (const QByteArray &data, Q_UINT32 clientID, bool &unknown);
+
+protected:
+ /**
+ * @return A unique number which can be used as the id of a @ref KMessageIO. It is
+ * incremented after every call so if you need the id twice you have to save
+ * it anywhere. It's currently used to initialize newly connected clints only.
+ **/
+ Q_UINT32 uniqueClientNumber() const;
+
+private:
+ KMessageServerPrivate* d;
+};
+
+
+/**
+ Internal class of KMessageServer. Creates a server socket and waits for
+ connections.
+
+ NOTE: This has to be here in the header file, because it is a subclass from
+ QObject and has to go through the moc.
+
+ @short An internal class for KServerSocket
+ @author Burkhard Lehner <Burkhard.Lehner@gmx.de>
+*/
+class KMessageServerSocket : public QServerSocket
+{
+ Q_OBJECT
+
+public:
+ KMessageServerSocket (Q_UINT16 port, QObject *parent = 0);
+ ~KMessageServerSocket ();
+
+ void newConnection (int socket);
+
+signals:
+ void newClientConnected (KMessageIO *client);
+};
+
+
+
+#endif
diff --git a/libkdegames/kgame/kmessageserver.png b/libkdegames/kgame/kmessageserver.png
new file mode 100644
index 00000000..07fba6c5
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.png
Binary files differ
diff --git a/libkdegames/kgame/kplayer.cpp b/libkdegames/kgame/kplayer.cpp
new file mode 100644
index 00000000..0f8ea184
--- /dev/null
+++ b/libkdegames/kgame/kplayer.cpp
@@ -0,0 +1,446 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+
+#include "kgame.h"
+#include "kgameio.h"
+#include "kplayer.h"
+#include "kgamemessage.h"
+#include "kgamepropertyhandler.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qbuffer.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+#define KPLAYER_LOAD_COOKIE 7285
+
+class KPlayerPrivate
+{
+public:
+ KPlayerPrivate()
+ {
+ mNetworkPlayer = 0;
+ }
+
+ Q_UINT32 mId;
+ bool mVirtual; // virtual player
+ int mPriority; // tag for replacement
+
+ KPlayer* mNetworkPlayer; // replacement player
+
+ KGamePropertyHandler mProperties;
+
+// Playerdata
+ KGamePropertyQString mName;
+ KGamePropertyQString mGroup;
+};
+
+KPlayer::KPlayer() : QObject(0,0)
+{
+ init();
+}
+
+KPlayer::KPlayer(KGame* game) : QObject(0, 0)
+{
+ init();
+ game->addPlayer(this);
+}
+
+void KPlayer::init()
+{
+// note that NO KGame object exists here! so we cannot use KGameProperty::send!
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)="<<sizeof(KPlayer) << endl;
+ kdDebug(11001) << "sizeof(m_Group)="<<sizeof(d->mGroup)<<endl;
+ d = new KPlayerPrivate;
+
+ d->mProperties.registerHandler(KGameMessage::IdPlayerProperty,
+ this,SLOT(sendProperty(int, QDataStream&, bool*)),
+ SLOT(emitSignal(KGamePropertyBase *)));
+ d->mVirtual=false;
+ mActive=true;
+ mGame=0;
+ d->mId=0; // "0" is always an invalid ID!
+ d->mPriority=0;
+ // I guess we cannot translate the group otherwise no
+ // international conenctions are possible
+
+ mUserId.registerData(KGamePropertyBase::IdUserId, this, i18n("UserId"));
+ mUserId.setLocal(0);
+ d->mGroup.registerData(KGamePropertyBase::IdGroup, this, i18n("Group"));
+ d->mGroup.setLocal(i18n("default"));
+ d->mName.registerData(KGamePropertyBase::IdName, this, i18n("Name"));
+ d->mName.setLocal(i18n("default"));
+
+ mAsyncInput.registerData(KGamePropertyBase::IdAsyncInput, this, i18n("AsyncInput"));
+ mAsyncInput.setLocal(false);
+ mMyTurn.registerData(KGamePropertyBase::IdTurn, this, i18n("myTurn"));
+ mMyTurn.setLocal(false);
+ mMyTurn.setEmittingSignal(true);
+ mMyTurn.setOptimized(false);
+}
+
+KPlayer::~KPlayer()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this <<", id=" << this->id() << endl;
+
+ // Delete IODevices
+ KGameIO *input;
+ while((input=mInputList.first()))
+ {
+ delete input;
+ }
+ if (game())
+ {
+ game()->playerDeleted(this);
+ }
+
+// note: mProperties does not use autoDelete or so - user must delete objects
+// himself
+ d->mProperties.clear();
+ delete d;
+// kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+bool KPlayer::forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver,Q_UINT32 sender)
+{
+ if (!isActive())
+ {
+ return false;
+ }
+ if (!game())
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": to game sender="<<sender<<"" << "recv="<<receiver <<"msgid="<<msgid << endl;
+ return game()->sendSystemMessage(msg,msgid,receiver,sender);
+}
+
+bool KPlayer::forwardInput(QDataStream &msg,bool transmit,Q_UINT32 sender)
+{
+ if (!isActive())
+ {
+ return false;
+ }
+ if (!game())
+ {
+ return false;
+ }
+
+ kdDebug(11001) << k_funcinfo << ": to game playerInput(sender="<<sender<<")" << endl;
+ if (!asyncInput() && !myTurn())
+ {
+ kdDebug(11001) << k_funcinfo << ": rejected cause it is not our turn" << endl;
+ return false;
+ }
+
+ // AB: I hope I remember the usage correctly:
+ // this function is called twice (on sender side) - once with transmit = true
+ // where it sends the input to the comserver and once with transmit = false
+ // where it really looks at the input
+ if (transmit)
+ {
+ kdDebug(11001) << "indirect playerInput" << endl;
+ return game()->sendPlayerInput(msg,this,sender);
+ }
+ else
+ {
+ kdDebug(11001) << "direct playerInput" << endl;
+ return game()->systemPlayerInput(msg,this,sender);
+ }
+}
+
+void KPlayer::setId(Q_UINT32 newid)
+{
+ // Needs to be after the sendProcess
+ d->mId=newid;
+}
+
+
+void KPlayer::setGroup(const QString& group)
+{ d->mGroup = group; }
+
+const QString& KPlayer::group() const
+{ return d->mGroup.value(); }
+
+void KPlayer::setName(const QString& name)
+{ d->mName = name; }
+
+const QString& KPlayer::name() const
+{ return d->mName.value(); }
+
+Q_UINT32 KPlayer::id() const
+{ return d->mId; }
+
+KGamePropertyHandler * KPlayer::dataHandler()
+{ return &d->mProperties; }
+
+void KPlayer::setVirtual(bool v)
+{ d->mVirtual = v; }
+
+bool KPlayer::isVirtual() const
+{ return d->mVirtual;}
+
+void KPlayer::setNetworkPlayer(KPlayer* p)
+{ d->mNetworkPlayer = p; }
+
+KPlayer* KPlayer::networkPlayer() const
+{ return d->mNetworkPlayer; }
+
+int KPlayer::networkPriority() const
+{ return d->mPriority; }
+
+void KPlayer::setNetworkPriority(int p)
+{ d->mPriority = p; }
+
+bool KPlayer::addGameIO(KGameIO *input)
+{
+ if (!input)
+ {
+ return false;
+ }
+ mInputList.append(input);
+ input->initIO(this); // set player and init device
+ return true;
+}
+
+// input=0, remove all
+bool KPlayer::removeGameIO(KGameIO *targetinput,bool deleteit)
+{
+ kdDebug(11001) << k_funcinfo << ": " << targetinput << " delete=" << deleteit<< endl;
+ bool result=true;
+ if (!targetinput) // delete all
+ {
+ KGameIO *input;
+ while((input=mInputList.first()))
+ {
+ if (input) removeGameIO(input,deleteit);
+ }
+ }
+ else
+ {
+// kdDebug(11001) << "remove IO " << targetinput << endl;
+ if (deleteit)
+ {
+ delete targetinput;
+ }
+ else
+ {
+ targetinput->setPlayer(0);
+ result=mInputList.remove(targetinput);
+ }
+ }
+ return result;
+}
+
+KGameIO * KPlayer::findRttiIO(int rtti) const
+{
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ if (it.current()->rtti() == rtti)
+ {
+ return it.current();
+ }
+ ++it;
+ }
+ return 0;
+}
+
+int KPlayer::calcIOValue()
+{
+ int value=0;
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ value|=it.current()->rtti();
+ ++it;
+ }
+ return value;
+}
+
+bool KPlayer::setTurn(bool b, bool exclusive)
+{
+ kdDebug(11001) << k_funcinfo << ": " << id() << " (" << this << ") to " << b << endl;
+ if (!isActive())
+ {
+ return false;
+ }
+
+ // if we get to do an exclusive turn all other players are disallowed
+ if (exclusive && b && game())
+ {
+ KPlayer *player;
+ KGame::KGamePlayerList *list=game()->playerList();
+ for ( player=list->first(); player != 0; player=list->next() )
+ {
+ if (player==this)
+ {
+ continue;
+ }
+ player->setTurn(false,false);
+ }
+ }
+
+ // Return if nothing changed
+ mMyTurn = b;
+
+ return true;
+}
+
+bool KPlayer::load(QDataStream &stream)
+{
+ Q_INT32 id,priority;
+ stream >> id >> priority;
+ setId(id);
+ setNetworkPriority(priority);
+
+ // Load Player Data
+ //FIXME: maybe set all properties setEmitSignal(false) before?
+ d->mProperties.load(stream);
+
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie==KPLAYER_LOAD_COOKIE)
+ {
+ kdDebug(11001) << " Player loaded propertly"<<endl;
+ }
+ else
+ {
+ kdError(11001) << " Player loading error. probably format error"<<endl;
+ }
+
+ // emit signalLoad(stream);
+ return true;
+}
+
+bool KPlayer::save(QDataStream &stream)
+{
+ stream << (Q_INT32)id() << (Q_INT32)networkPriority();
+
+ d->mProperties.save(stream);
+
+ stream << (Q_INT16)KPLAYER_LOAD_COOKIE;
+
+ //emit signalSave(stream);
+ return true;
+}
+
+
+void KPlayer::networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender)
+{
+ //kdDebug(11001) << k_funcinfo ": msgid=" << msgid << " sender=" << sender << " we are=" << id() << endl;
+ // PlayerProperties processed
+ bool issender;
+ if (game())
+ {
+ issender=sender==game()->gameId();
+ }
+ else
+ {
+ issender=true;
+ }
+ if (d->mProperties.processMessage(stream,msgid,issender))
+ {
+ return ;
+ }
+ switch(msgid)
+ {
+ case KGameMessage::IdPlayerInput:
+ {
+ kdDebug(11001) << k_funcinfo << ": Got player move "
+ << "KPlayer (virtual) forwards it to the game object" << endl;
+ forwardInput(stream,false);
+ }
+ break;
+ default:
+ emit signalNetworkData(msgid - KGameMessage::IdUser,
+ ((QBuffer*)stream.device())->readAll(),sender,this);
+ kdDebug(11001) << k_funcinfo << ": "
+ << "User data msgid " << msgid << endl;
+ break;
+ }
+
+}
+
+KGamePropertyBase* KPlayer::findProperty(int id) const
+{
+ return d->mProperties.find(id);
+}
+
+bool KPlayer::addProperty(KGamePropertyBase* data)
+{
+ return d->mProperties.addProperty(data);
+}
+
+void KPlayer::sendProperty(int msgid, QDataStream& stream, bool* sent)
+{
+ if (game())
+ {
+ bool s = game()->sendPlayerProperty(msgid, stream, id());
+ if (s)
+ {
+ *sent = true;
+ }
+ }
+}
+
+void KPlayer::emitSignal(KGamePropertyBase *me)
+{
+ // Notify KGameIO (Process) for a new turn
+ if (me->id()==KGamePropertyBase::IdTurn)
+ {
+ //kdDebug(11001) << k_funcinfo << ": for KGamePropertyBase::IdTurn " << endl;
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ it.current()->notifyTurn(mMyTurn.value());
+ ++it;
+ }
+ }
+ emit signalPropertyChanged(me,this);
+}
+
+// --------------------- DEBUG --------------------
+void KPlayer::Debug()
+{
+ kdDebug(11001) << "------------------- KPLAYER -----------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "rtti: " << rtti() << endl;
+ kdDebug(11001) << "id : " << id() << endl;
+ kdDebug(11001) << "Name : " << name() << endl;
+ kdDebug(11001) << "Group: " << group() << endl;
+ kdDebug(11001) << "Async: " << asyncInput() << endl;
+ kdDebug(11001) << "myTurn: " << myTurn() << endl;
+ kdDebug(11001) << "Virtual: " << isVirtual() << endl;
+ kdDebug(11001) << "Active: " << isActive() << endl;
+ kdDebug(11001) << "Priority:" << networkPriority() << endl;
+ kdDebug(11001) << "Game : " << game() << endl;
+ kdDebug(11001) << "#IOs: " << mInputList.count() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+#include "kplayer.moc"
diff --git a/libkdegames/kgame/kplayer.h b/libkdegames/kgame/kplayer.h
new file mode 100644
index 00000000..0e511ac3
--- /dev/null
+++ b/libkdegames/kgame/kplayer.h
@@ -0,0 +1,471 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni (martin@heni-online.de)
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KPLAYER_H_
+#define __KPLAYER_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "kgameproperty.h"
+#include <kdemacros.h>
+
+class KGame;
+class KGameIO;
+class KGamePropertyBase;
+class KGamePropertyHandler;
+
+class KPlayerPrivate;
+
+/**
+ * @short Base class for a game player
+ *
+ * The KPlayer class is the central player object. It holds
+ * information about the player and is responsible for any
+ * input the player does. For this arbitrary many KGameIO
+ * modules can be plugged into it. Main features are:
+ * - Handling of IO devices
+ * - load/save (mostly handled by KGamePropertyHandler)
+ * - Turn handling (turn based, asynchronous)
+ *
+ * A KPlayer depends on a KGame object. Call KGame::addPlayer() to plug
+ * a KPlayer into a KGame object. Note that you cannot do much with a
+ * KPlayer object before it has been plugged into a KGame. This is because
+ * most properties of KPlayer are KGameProperty which need to send messages
+ * through a KGame object to be changed.
+ *
+ * A KGameIO represents the input methods of a player and you should make all
+ * player inputs through it. So call something like playerInput->move(4);
+ * instead which should call KGameIO::sendInput() to actually move. This way
+ * you gain a *very* big advantage: you can exchange a KGameIO whenever you
+ * want! You can e.g. remove the KGameIO of a local (human) player and just
+ * replace it by a computerIO on the fly! So from that point on all playerInputs
+ * are done by the computerIO instead of the human player. You also can replace
+ * all network players by computer players when the network connection is broken
+ * or a player wants to quit.
+ * So remember: use KGameIO whenever possible! A KPlayer should just
+ * contain all data of the player (KGameIO must not!) and several common
+ * functions which are shared by all of your KGameIOs.
+ *
+ */
+class KDE_EXPORT KPlayer : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef QPtrList<KGameIO> KGameIOList;
+
+ // KPlayer(KGame *,KGameIO * input=0);
+ /**
+ * Create a new player object. It will be automatically
+ * deleted if the game it belongs to is deleted.
+ */
+ KPlayer();
+
+ /**
+ * Create a new player object. It will be automatically
+ * deleted if the game it belongs to is deleted. This constructor
+ * automatically adds the player to the game using KGame::addPlayer()
+ */
+ KPlayer(KGame* game);
+
+ virtual ~KPlayer();
+
+ /**
+ * The idendification of the player. Overwrite this in
+ * classes inherting KPlayer to run time identify them.
+ *
+ * @return 0 for default KPlayer.
+ */
+ virtual int rtti() const {return 0;}
+
+ /**
+ * Gives debug output of the game status
+ */
+ void Debug();
+
+ // properties
+ /**
+ * Returns a list of input devices
+ *
+ * @return list of devices
+ */
+ KGameIOList *ioList() {return &mInputList;}
+
+ /**
+ * sets the game the player belongs to. This
+ * is usually automatically done when adding a
+ * player
+ *
+ * @param game the game
+ */
+ void setGame(KGame *game) {mGame=game;}
+
+ /**
+ * Query to which game the player belongs to
+ *
+ * @return the game
+ */
+ KGame *game() const {return mGame;}
+
+ /**
+ * Set whether this player can make turns/input
+ * all the time (true) or only when it is its
+ * turn (false) as it is used in turn based games
+ *
+ * @param a async=true turn based=false
+ */
+ void setAsyncInput(bool a) {mAsyncInput = a;}
+
+ /**
+ * Query whether this player does asynchronous
+ * input
+ *
+ * @return true/false
+ */
+ bool asyncInput() const {return mAsyncInput.value();}
+
+ /**
+ * Is this player a virtual player, ie is it
+ * created by mirroring a real player from another
+ * network game. This mirroring is done autmatically
+ * as soon as a network connection is build and it affects
+ * all players regardless what type
+ *
+ * @return true/false
+ */
+ bool isVirtual() const;
+
+ /**
+ * @internal
+ * Sets whether this player is virtual. This is internally
+ * called
+ *
+ * @param v virtual true/false
+ */
+ void setVirtual(bool v);
+
+ /**
+ * Is this player an active player. An player is usually
+ * inactivated if it is replaced by a network connection.
+ * But this could also be called manually
+ *
+ * @return true/false
+ */
+ bool isActive() const {return mActive;}
+
+ /**
+ * Set an player as active (true) or inactive (false)
+ *
+ * @param v true=active, false=inactive
+ */
+ void setActive(bool v) {mActive=v;}
+
+ /**
+ * Returns the id of the player
+ *
+ * @return the player id
+ */
+ Q_UINT32 id() const;
+
+ /* Set the players id. This is done automatically by
+ * the game object when adding a new player!
+ *
+ * @param i the player id
+ */
+ void setId(Q_UINT32 i);
+
+ /**
+ * Returns the user defined id of the player
+ * This value can be used arbitrary by you to
+ * have some user idendification for your player,
+ * e.g. 0 for a white chess player, 1 for a black
+ * one. This value is more reliable than the player
+ * id whcih can even change when you make a network
+ * connection.
+ *
+ * @return the user defined player id
+ */
+ int userId() const {return mUserId.value();}
+
+ /* Set the user defined players id.
+ *
+ * @param i the user defined player id
+ */
+ void setUserId(int i) {mUserId = i;}
+
+ /**
+ * Returns whether this player can be replaced by a network
+ * connection player. The name of this function can be
+ * improved ;-) If you do not overwrite the function to
+ * select what players shall play in a network the KGame
+ * does an automatic selection based on the networkPriority
+ * This is not a terrible important function at the moment.
+ *
+ * @return true/false
+ */
+ int networkPriority() const;
+
+ /**
+ * Set whether this player can be replaced by a network
+ * player. There are to possible games. The first type
+ * of game has arbitrary many players. As soon as a network
+ * players connects the game runs with more players (not tagged
+ * situation). The other type is e.g. games like chess which
+ * require a constant player number. In a network game situation
+ * you would tag one or both players of all participants. As
+ * soon as the connect the tagged player will then be replaced
+ * by the network partner and it is then controlled over the network.
+ * On connection loss the old situation is automatically restored.
+ *
+ * The name of this function can be improved;-)
+ *
+ * @param b should this player be tagged
+ */
+ void setNetworkPriority(int b);
+
+ /**
+ * Returns the player which got inactivated to allow
+ * this player to be set up via network. Mostly internal
+ * function
+ */
+ KPlayer *networkPlayer() const;
+
+ /**
+ * Sets this network player replacement. Internal stuff
+ */
+ void setNetworkPlayer(KPlayer *p);
+
+ // A name and group the player belongs to
+ /**
+ * A group the player belongs to. This
+ * Can be set arbitrary by you.
+ */
+ void setGroup(const QString& group);
+
+ /**
+ * Query the group the player belongs to.
+ */
+ virtual const QString& group() const;
+
+ /**
+ * Sets the name of the player.
+ * This can be chosen arbitrary.
+ * @param name The player's name
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return The name of the player.
+ */
+ virtual const QString& name() const;
+
+
+ // set devices
+ /**
+ * Adds an IO device for the player. Possible KGameIO devices
+ * can either be taken from the existing ones or be self written.
+ * Existing are e.g. Keyboard, Mouse, Computerplayer
+ *
+ * @param input the inut device
+ * @return true if ok
+ */
+ bool addGameIO(KGameIO *input);
+
+ /**
+ * remove (and delete) a game IO device
+ *
+ * The remove IO(s) is/are deleted by default. If
+ * you do not want this set the parameter deleteit to false
+ *
+ * @param input the device to be removed or 0 for all devices
+ * @param deleteit true (default) to delete the device otherwisse just remove it
+ * @return true on ok
+ */
+ bool removeGameIO(KGameIO *input=0,bool deleteit=true);
+
+ /**
+ * Finds the KGameIO devies with the given rtti code.
+ * E.g. find the mouse or network device
+ *
+ * @param rtti the rtti code to be searched for
+ * @return the KGameIO device
+ */
+ KGameIO *findRttiIO(int rtti) const;
+
+ /**
+ * Checks whether this player has a IO device of the
+ * given rtti type
+ *
+ * @param rtti the rtti typed to be checked for
+ * @return true if it exists
+ */
+ bool hasRtti(int rtti) const {return findRttiIO(rtti)!=0;}
+
+ // Message exchange
+ /**
+ * Forwards input to the game object..internal use only
+ *
+ * This method is used by KGameIO::sendInput(). Use that function
+ * instead to send player inputs!
+ *
+ * This function forwards a player input (see KGameIO classes) to the
+ * game object, see KGame, either to KGame::sendPlayerInput() (if
+ * transmit=true, ie the message has just been created) or to
+ * KGame::playerInput() (if player=false, ie the message *was* sent through
+ * KGame::sendPlayerInput).
+ */
+ virtual bool forwardInput(QDataStream &msg,bool transmit=true, Q_UINT32 sender=0);
+
+ /**
+ * Forwards Message to the game object..internal use only
+ */
+ virtual bool forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver=0,Q_UINT32 sender=0);
+
+ // Game logic
+ /**
+ * is it my turn to go
+ *
+ * @return true/false
+ */
+ bool myTurn() const {return mMyTurn.value();}
+
+ /**
+ * Sets whether this player is the next to turn.
+ * If exclusive is given all other players are set
+ * to setTurn(false) and only this player can move
+ *
+ * @param b true/false
+ * @param exclusive true (default)/ false
+ * @return should be void
+ */
+ bool setTurn(bool b,bool exclusive=true);
+
+
+ // load/save
+ /**
+ * Load a saved player, from file OR network. By default all
+ * KGameProperty objects in the dataHandler of this player are loaded
+ * and saved when using load or save. If you need to save/load more
+ * you have to replace this function (and save). You will probably
+ * still want to call the default implementation additionally!
+ *
+ * @param stream a data stream where you can stream the player from
+ *
+ * @return true?
+ */
+ virtual bool load(QDataStream &stream);
+
+ /**
+ * Save a player to a file OR to network. See also load
+ *
+ * @param stream a data stream to load the player from
+ *
+ * @return true?
+ */
+ virtual bool save(QDataStream &stream);
+
+ /**
+ * Receives a message
+ * @param msgid The kind of the message. See messages.txt for further
+ * information
+ * @param stream The message itself
+ * @param sender
+ **/
+ void networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender);
+
+ /**
+ * Searches for a property of the player given its id.
+ * @param id The id of the property
+ * @return The property with the specified id
+ **/
+ KGamePropertyBase* findProperty(int id) const;
+
+ /**
+ * Adds a property to a player. You would add all
+ * your player specific game data as KGameProperty and
+ * they are automatically saved and exchanged over network.
+ *
+ * @param data The property to be added. Must have an unique id!
+ * @return false if the given id is not valid (ie another property owns
+ * the id) or true if the property could be added successfully
+ **/
+ bool addProperty(KGamePropertyBase* data);
+
+ /**
+ * Calculates a checksum over the IO devices. Can be used to
+ * restore the IO handlers. The value returned is the 'or'ed
+ * value of the KGameIO rtti's.
+ * this is itnernally used for saving and restorign a player.
+ */
+ int calcIOValue();
+
+ /**
+ * @return the property handler
+ */
+ KGamePropertyHandler* dataHandler();
+
+signals:
+ /**
+ * The player object got a message which was targeted
+ * at it but has no default method to process it. This
+ * means probably a user message. Connecting to this signal
+ * allowed to process it.
+ */
+ void signalNetworkData(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer *me);
+
+ /**
+ * This signal is emmited if a player property changes its value and
+ * the property is set to notify this change. This is an
+ * important signal as you should base the actions on a reaction
+ * to this property changes.
+ */
+ void signalPropertyChanged(KGamePropertyBase *property,KPlayer *me);
+
+protected slots:
+ /**
+ * Called by KGameProperty only! Internal function!
+ **/
+ void sendProperty(int msgid, QDataStream& stream, bool* sent);
+ /**
+ * Called by KGameProperty only! Internal function!
+ **/
+ void emitSignal(KGamePropertyBase *me);
+
+
+private:
+ void init();
+
+private:
+ KGame *mGame;
+ bool mActive; // active player
+ KGameIOList mInputList;
+
+ // GameProperty // AB: I think we can't move them to KPlayerPrivate - inline
+ // makes sense here
+ KGamePropertyBool mAsyncInput; // async input allowed
+ KGamePropertyBool mMyTurn; // Is it my turn to play (only useful if not async)?
+ KGamePropertyInt mUserId; // a user defined id
+
+ KPlayerPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/libkdegames.html b/libkdegames/kgame/libkdegames.html
new file mode 100644
index 00000000..66206c47
--- /dev/null
+++ b/libkdegames/kgame/libkdegames.html
@@ -0,0 +1,187 @@
+<html>
+ <head>
+ <title>Documentation for libkdegames</title>
+ <meta content="">
+ <style></style>
+ </head>
+ <body>
+ <H1>Documentation for the classes in libkdegames</H1>
+<!-------------------------------------------------------------------------------->
+ <H3>Design Principles</H3>
+ The library <em>kdegames</em> contains a collection of classes that can be used
+ to develop games using the KDE environment very easily. There are a few
+ principles that were used when developing the library:<P>
+
+ <UL>
+ <LI><b>usable for a big variety of games</b><br>
+ The class <em>KGame</em> provides many features that are needed in many games.
+ It can be used for board games, card games, maze games, simulation games, strategy games and
+ many more.<br>
+ It does not (yet) include special features for realtime games, but can be used there, too.
+ <LI><b>features one-player and multi-player games</b></br>
+ A game developed with this library can easily support any number of simultaneous players.
+ So use it for one-player games (like Tetris or KSame), for two-player games (like TicTacToe
+ or chess) or for games with an arbitrary number of players.
+ <LI><b>computer players can easily be developed</b><br>
+ The class <em>KPlayer</em> represents an abstract player in a game. This can be a
+ human player that gets the input from the mouse or the keyboard. Or it can be a computer
+ player that makes moves by random or with artificial intelligence. All this can be achieved
+ subclassing KPlayer.
+ <LI><b>support for network games transparently</b><br>
+ The class <em>KGame</em> contains lots of features for network game support. Developing
+ a network game using a TCP/IP connection is very easy this way. But the default
+ case is still the local game. So the user should not need to connect to the internet
+ or to select a game server to play a game.
+ <LI><b>support for turn based and for asynchronous games</b><br>
+ You can use this library for turn based games, when only one player can make a move at a time
+ (like most bord games or card games), but also for asynchronous games, when every player can
+ make a move any time (like many action games).
+ </UL>
+<!-------------------------------------------------------------------------------->
+ <H3>The central game class: <em>KGame</em></H3>
+
+ When you want to develop your own KDE game using the KDE games library, you most likely want
+ to use the <em>KGame</em> class. There are two possible ways to extend it to your own needs:
+ Create a subclass and overwrite the virtual methods, or simply create an instance of KGame
+ and connect to the appropriate signals.<P>
+
+ &lt;&lt;more code about KGame, an easy example&gt;&gt;
+
+<!-------------------------------------------------------------------------------->
+ <H3>Network games and related classes</H3>
+
+ One of the main principles in the design of <em>KGame</em> was to make network games possible
+ with a minimum of effort for the game developer.<P>
+
+ A network game is a game with usually several players, that are on different computers. These
+ computers are usually connected to the internet, and all the moves a player does are exchanged
+ over this network.<P>
+
+ The exchange of moves and other information is done using the class <em>KMessageServer</em>.
+ An object of this class is a server that waits for connections. Someone who wants to take part
+ in the game has to connect to this server - usually using an internet socket connection. He does
+ this by creating a <em>KMessageClient</em> object. This object connects to the message server.<P>
+
+ The transfer of data is realised by subclasses of the abstract class <em>KMessageIO</em>. One object
+ of this class is created on the client side, one on the server side. Different types of networks can
+ be supported by creating new subclasses of KMessageIO. There are already two subclasses of KMessageIO:
+ <em>KMessageSocket</em> uses a internet socket connection to transfer the data from the message client
+ to the message server or vice versa. <em>KMessageDirect</em> can be used if both the message server and
+ the message client are within the same process. The data blocks are copied directly to the other side,
+ so the transfer is faster and needs no network bandwidth.<P>
+
+ A typical network game situation could look like this:<P>
+
+ <IMG SRC="kmessageserver.png"><P>
+
+ Here, three KGame object (called message clients) are connected to the KMessageServer object. One
+ is in the same process, so it uses KMessageDirect. The other two use KMessageSocket, so an internet
+ socket connection is used. KGame doesn't talk directly to the message server, but uses a KMessageClient
+ object instead. One of the KMessageClient objects is the admin of the message server. This client has some
+ priviledges. It may e.g. kill the connection to other message clients, or limit the number of clients
+ that may connect.<P>
+
+ The KGame objects are by default all equal. So the usual approach will be that every KGame object will
+ store the complete status of the game, and any change or move will be broadcasted to the other KGame
+ objects, so that they all change the status in identical ways. Sometimes it may be necessary (or just
+ easier to implement) that one KGame object is a <em>game server</em> (i.e. he is repsonsible for everything,
+ he coordinates the complete game and stores the game status), whereas the other KGame objects are
+ only dumb stubs that are used to contact the <em>game server</em>. You can implement both approaches
+ using the message server structure. If you need to elect the KGame object that shall be
+ the game server, you may e.g. use the one that has the KMessageClient that is the admin of the message
+ server. (Of course this is only a suggestion, you can use other approaches.)<P>
+
+ The main principle when developing the message server/client structure was, that the message server
+ doesn't have <em>any</em> idea of the game and its rules that is played. The message server only forwards
+ messages from one message client to the others without interpreting or manipulating the data. So always
+ keep in mind that the message server is <em>not</em> a game server! It does not store any data about
+ the game status. It is only a server for network connections and message broadcasting, <em>not</em>
+ for game purposes. The reason for this principle is, that <em>any</em> game can be played using a
+ KMessageServer on any computer. The computer being the message server doesn't need to know anything
+ about the game that is played on it. So you don't have to install new versions of the game there. Only
+ the clients need to be game specific.<P>
+
+ Usually you don't need to create <em>KMessageServer</em> or <em>KMessageClient</em> objects in your game,
+ since <em>KGame</em> does this for you. There are three different scenarios fo network games that are
+ all supported in <em>KGame</em>:<P>
+
+ <b>Scenario 1: local game</b><P>
+
+ The local game should always be the default state a game should be in. To avoid having this scenario
+ as a special case, <em>KGame</em> automatically creates a KMessageServer object and a KMessageClient
+ object. So every change and every move is sent to the message server and is returned to the KGame
+ object before it is processed. Since the connection between the message client and the message server
+ uses KMessageDirect the data transfer is very fast and wont hurt in most cases.<P>
+
+ <IMG SRC="scenario0.png"><P>
+
+ This is the default situation right after creating the <em>KGame</em> object.<P>
+
+ <b>Scenario 2: network game, started by one player</b><P>
+
+ If one user is bored of playing alone, he can open his game for connections from the outside world.
+ He listens to a TCP/IP socket port (i.e. a number between 0 and 65535). Other players can create
+ KGame objects of their own and connect to this port. They need to know the IP address of that computer
+ and the port number. This situation will have this structure:
+
+ <IMG SRC="scenario1.png"><P>
+
+ The first player has to do something like:<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // wait for connections on port 12345
+ myGame->offerConnections (12345);
+ </PRE>
+
+ And the other players have to do something like:<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // connect to the message server
+ myGame->connectToServer ("theServer.theDomain.com", 12345);
+ </PRE>
+
+ This automatically removes the message server in these KGame objects and connects to the given
+ one instead.<P>
+
+ <b>Scenario 3: network game, using a stand alone message server</b><P>
+
+ Sometimes it is not possible to let the message server run on one of the players computer. If e.g. all
+ the players have their computer in a local network that uses masquerading to contact the internet,
+ other computers cannot connect to them since the computer doesn't have a IP address to the outside
+ world. Then the only way to play a network game is to have a standalone KMessageServer object on
+ another server computer (somthing like "games.kde.org" e.g.). Since the KMessageServer isn't game
+ specific at all, every game can be played using it. There doesn't have to be any special software
+ installed on that server computer, only the program creating a KMessageServer object.<P>
+
+ This scenario has some more advantages: The message server can be a well known meeting point to
+ start a game. This way one could play games against other players you never knew before. Furthermore
+ the game is stopped brutally when the program that contains the message server in scenario 2 is
+ quitted. (Migration of message servers is not yet implemented, but may be in the future.) Using a
+ stand alone message server, the players may enter and leave the game as they want.
+
+ <IMG SRC="scenario2.png"><P>
+
+ To create this scenario, a special KMessageServer program has to be started on the computer
+ that shall be the stand alone message server:<P>
+
+ <PRE>
+ % kmessageserver -port=12345
+ </PRE>
+
+ The other games that want to connect have to do this (supposed the stand alone message server
+ has the IP address "games.kde.org"):<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // connect to the message server
+ myGame->connectToServer ("games.kde.org", 12345);
+ </PRE>
+
+
+
+
+<!-------------------------------------------------------------------------------->
+ </body>
+</html> \ No newline at end of file
diff --git a/libkdegames/kgame/messages.txt b/libkdegames/kgame/messages.txt
new file mode 100644
index 00000000..151196d5
--- /dev/null
+++ b/libkdegames/kgame/messages.txt
@@ -0,0 +1,93 @@
+Message formats of libkdegames:
+-------------------------------
+
+There are two different communication layers, using their own protocols:
+
+ - the message layer (KMessageIO, KMessageServer, KMessageClient)
+
+ This is used to send messages from one client (i.e. KGame object)
+ to an other one, to a group of other clients, or to all the clients
+ connected to the KMessageServer. The messages are arbitrary blocks
+ of data, of an arbitrary length.
+ This layer is an underlying protocol that isn't game specific at all.
+ You shouldn't need to know the message format of the packets. If you
+ want to extend the protocol, have a look into KMessageServer API
+ reference for a complete list of message types.
+
+ - the game layer (KGame, KGameIO, KPlayer)
+
+ This layer uses the message layer to send its specific message packets
+ between the objects of the game.
+ This layer contains the game specific messages (e.g. addPlayer, setupGame).
+ The rest of this file describes this layer.
+
+
+Game Layer Messages:
+--------------------
+
+ Application Cookie 16 Bit
+ Version 8 Bit
+ MsgId 16 Bit
+ SenderId 16 Bit
+ ReceiverId 16 Bit
+ Userdata
+
+The format of the messages is used internally and there is usually no reason why
+you have to know what it is used for. But as usually != always here are some
+comments on the format. Note that KGame is under development and the content of
+this file could be obsolete. Please result the sourcecode for up-to-date
+information.
+Application Cookie is used to identify the application. This prevents a
+chess game from talking to a poker game.
+Version is the version of KNetworkGame, sender and receiver must be of the same
+version.
+ library note: this could be a limitation, as KGame should be backward
+ compatible. Maybe change to version must be >= KNETWORKGAME or something less
+ restrictive
+MsgId specifies the kind of the message data (see below).
+SenderId is the id of the KGame/KPlayer object which sent the message, it is
+coded like this: the lower 10 bits specify a player and the upper bit
+represent the game id shifted by 10 bits. So we get
+Id=playerId | (gameId<<10);
+ReceiverId is the id of the receiver of the message. It can be either
+a player, a game or a broadcast. For a broadcast the Id is set to 0
+in the other cases the coding is as with the senderId
+Userdata is the data of the user (wow ;-))
+
+
+MsgId UserData
+---------------------------------------------------------
+IdMessage user defined
+
+IdSetupGame Q_INT32 isServer
+ Q_INT32 maxPlayers
+ Q_INT32 newid (id for the new game)
+ Q_INT32 cntR (virtual player nunmber)
+ Q_INT32 cntT (tagged player number)
+ TODO: Changed
+
+IdContinueSetup: TODO
+
+IdSendPlayer Q_INT32 omit how many tagged players for replacement
+ TODO: Changed
+
+IdGameSave Save(msg)->Load(msg)
+
+IdAddPlayer rtti
+ gameid() of the owner
+ player->Save(msg) -> player->Load(msg)
+
+IdRemovePlayer Q_INT16 playerid
+
+IdError Q_INT32 errorcode
+ QString errortext
+
+IdGameStatus Q_INT32 status
+
+IdPlayerProperty Q_INT16 propertyId
+ user defined -> the property
+
+IdGameProperty Q_INT16 propertyId
+ user defined -> the property
+
+IdPlayerInput user defined
diff --git a/libkdegames/kgame/scenario0.png b/libkdegames/kgame/scenario0.png
new file mode 100644
index 00000000..4de99b52
--- /dev/null
+++ b/libkdegames/kgame/scenario0.png
Binary files differ
diff --git a/libkdegames/kgame/scenario1.png b/libkdegames/kgame/scenario1.png
new file mode 100644
index 00000000..74af4f6f
--- /dev/null
+++ b/libkdegames/kgame/scenario1.png
Binary files differ
diff --git a/libkdegames/kgame/scenario2.png b/libkdegames/kgame/scenario2.png
new file mode 100644
index 00000000..14ea0a3c
--- /dev/null
+++ b/libkdegames/kgame/scenario2.png
Binary files differ
diff --git a/libkdegames/kgamelcd.cpp b/libkdegames/kgamelcd.cpp
new file mode 100644
index 00000000..65c436a5
--- /dev/null
+++ b/libkdegames/kgamelcd.cpp
@@ -0,0 +1,250 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamelcd.h"
+#include "kgamelcd.moc"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtimer.h>
+
+#include <kglobal.h>
+
+
+//-----------------------------------------------------------------------------
+KGameLCD::KGameLCD(uint nbDigits, QWidget *parent, const char *name)
+ : QLCDNumber(nbDigits, parent, name), _htime(800)
+{
+ const QPalette &p = palette();
+ _fgColor = p.color(QPalette::Active, QColorGroup::Foreground);
+ _hlColor = p.color(QPalette::Active, QColorGroup::HighlightedText);
+
+ _timer = new QTimer(this);
+ connect(_timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ setFrameStyle(Panel | Plain);
+ setSegmentStyle(Flat);
+
+ displayInt(0);
+}
+
+KGameLCD::~KGameLCD()
+{}
+
+void KGameLCD::setDefaultBackgroundColor(const QColor &color)
+{
+ QPalette p = palette();
+ p.setColor(QColorGroup::Background, color);
+ setPalette(p);
+}
+
+void KGameLCD::setDefaultColor(const QColor &color)
+{
+ _fgColor = color;
+ QPalette p = palette();
+ p.setColor(QColorGroup::Foreground, color);
+ setPalette(p);
+}
+
+void KGameLCD::setHighlightColor(const QColor &color)
+{
+ _hlColor = color;
+}
+
+void KGameLCD::setLeadingString(const QString &s)
+{
+ _lead = s;
+ displayInt(0);
+}
+
+void KGameLCD::setHighlightTime(uint time)
+{
+ _htime = time;
+}
+
+void KGameLCD::resetColor()
+{
+ setColor(QColor());
+}
+
+void KGameLCD::setColor(const QColor &color)
+{
+ const QColor &c = (color.isValid() ? color : _fgColor);
+ QPalette p = palette();
+ p.setColor(QColorGroup::Foreground, c);
+ setPalette(p);
+}
+
+void KGameLCD::displayInt(int v)
+{
+ int n = numDigits() - _lead.length();
+ display(_lead + QString::number(v).rightJustify(n));
+}
+
+void KGameLCD::highlight()
+{
+ highlight(true);
+ _timer->start(_htime, true);
+}
+
+void KGameLCD::highlight(bool light)
+{
+ if (light) setColor(_hlColor);
+ else resetColor();
+}
+
+//-----------------------------------------------------------------------------
+KGameLCDClock::KGameLCDClock(QWidget *parent, const char *name)
+: KGameLCD(5, parent, name)
+{
+ _timerClock = new QTimer(this);
+ connect(_timerClock, SIGNAL(timeout()), SLOT(timeoutClock()));
+}
+
+KGameLCDClock::~KGameLCDClock()
+{}
+
+void KGameLCDClock::timeoutClock()
+{
+ // waiting an hour does not restart timer
+ if ( _min==59 && _sec==59 ) return;
+ _sec++;
+ if (_sec==60) {
+ _min++;
+ _sec = 0;
+ }
+ showTime();
+}
+
+QString KGameLCDClock::pretty() const
+{
+ QString sec = QString::number(_sec).rightJustify(2, '0', true);
+ QString min = QString::number(_min).rightJustify(2, '0', true);
+ return min + ':' + sec;
+}
+
+void KGameLCDClock::showTime()
+{
+ display(pretty());
+}
+
+void KGameLCDClock::reset()
+{
+ _timerClock->stop();
+ _sec = 0;
+ _min = 0;
+ showTime();
+}
+
+void KGameLCDClock::start()
+{
+ _timerClock->start(1000); // 1 second
+}
+
+void KGameLCDClock::stop()
+{
+ _timerClock->stop();
+}
+
+uint KGameLCDClock::seconds() const
+{
+ return _min*60 + _sec;
+}
+
+void KGameLCDClock::setTime(uint sec)
+{
+ Q_ASSERT( sec<3600 );
+ _sec = sec % 60;
+ _min = sec / 60;
+ showTime();
+}
+
+void KGameLCDClock::setTime(const QString &s)
+{
+ Q_ASSERT( s.length()==5 && s[2]==':' );
+ uint min = kMin(s.section(':', 0, 0).toUInt(), uint(59));
+ uint sec = kMin(s.section(':', 1, 1).toUInt(), uint(59));
+ setTime(sec + min*60);
+}
+
+
+//-----------------------------------------------------------------------------
+class KGameLCDList::KGameLCDListPrivate
+{
+public:
+ QValueVector<QLabel *> _leadings;
+};
+
+KGameLCDList::KGameLCDList(const QString &title, QWidget *parent,
+ const char *name)
+ : QWidget(parent, name)
+{
+ init(title);
+}
+
+KGameLCDList::KGameLCDList(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ init(QString::null);
+}
+
+KGameLCDList::~KGameLCDList()
+{
+ delete d;
+}
+
+void KGameLCDList::init(const QString &title)
+{
+ d = new KGameLCDListPrivate;
+
+ QGridLayout *top = new QGridLayout(this, 1, 2, 5);
+ top->setColStretch(1, 1);
+
+ _title = new QLabel(title, this);
+ _title->setAlignment(AlignCenter);
+ top->addMultiCellWidget(_title, 0, 0, 0, 1, AlignCenter);
+}
+
+void KGameLCDList::append(QLCDNumber *lcd)
+{
+ append(QString::null, lcd);
+}
+
+void KGameLCDList::append(const QString &leading, QLCDNumber *lcd)
+{
+ uint i = size();
+ QLabel *label = 0;
+ if ( !leading.isEmpty() ) {
+ label = new QLabel(leading, this);
+ static_cast<QGridLayout *>(layout())->addWidget(label, i+1, 0);
+ }
+ d->_leadings.push_back(label);
+ _lcds.push_back(lcd);
+ static_cast<QGridLayout *>(layout())->addWidget(lcd, i+1, 1);
+}
+
+void KGameLCDList::clear()
+{
+ for (uint i=0; i<_lcds.size(); i++) {
+ delete d->_leadings[i];
+ delete _lcds[i];
+ }
+ d->_leadings.clear();
+ _lcds.clear();
+}
diff --git a/libkdegames/kgamelcd.h b/libkdegames/kgamelcd.h
new file mode 100644
index 00000000..3e6ad33c
--- /dev/null
+++ b/libkdegames/kgamelcd.h
@@ -0,0 +1,249 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001,2002,2003 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMELCD_H
+#define __KGAMELCD_H
+
+#include <qlcdnumber.h>
+#include <qvaluevector.h>
+#include <kdemacros.h>
+
+class QLabel;
+class QTimer;
+
+//-----------------------------------------------------------------------------
+/**
+ * This class is a visually enhanced @ref QLCDNumber:
+ * <ul>
+ * <li> It can show an additional string before the integer being
+ * displayed.</li>
+ * <li> Its foreground and background colors can easily be modified. </li>
+ * <li> It can be highlighted for a short time. </li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+class KDE_EXPORT KGameLCD : public QLCDNumber
+{
+ Q_OBJECT
+public:
+ KGameLCD(uint nbDigits, QWidget *parent = 0, const char *name = 0);
+
+ ~KGameLCD();
+
+ /**
+ * Set the default background color.
+ */
+ void setDefaultBackgroundColor(const QColor &color);
+
+ /**
+ * Set the default foreground color.
+ */
+ void setDefaultColor(const QColor &color);
+
+ /**
+ * Set highlight color.
+ */
+ void setHighlightColor(const QColor &color);
+
+ /**
+ * Set the string that will be displayed before the integer number to be
+ * displayed. By default this string is null.
+ */
+ void setLeadingString(const QString &s);
+
+ /**
+ * Set the highlight duration in milliseconds. The default value is
+ * 800 milliseconds.
+ */
+ void setHighlightTime(uint time);
+
+ /**
+ * Reset the foreground color to the default one.
+ */
+ void resetColor();
+
+ /**
+ * Set the foreground color.
+ */
+ void setColor(const QColor &color);
+
+public slots:
+ /**
+ * Highlight the LCD with the QColorGourp::HighlightedText color
+ * for a small time (setHighlightTime).
+ */
+ void highlight();
+
+ /**
+ * Display the given integer with the (optionnal) leading string.
+ *
+ * Note: we cannot use display(int) since QLCDNumber::display is
+ * not virtual... And you cannot use QLCDNumber::intValue() to retrieve
+ * the given value.
+ */
+ void displayInt(int value);
+
+private slots:
+ void timeout() { highlight(false); }
+
+private:
+ QColor _fgColor, _hlColor;
+ QString _lead;
+ uint _htime;
+ QTimer *_timer;
+
+ class KGameLCDPrivate;
+ KGameLCDPrivate *d;
+
+ void highlight(bool light);
+
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * This class is a digital clock widget. It has a maximum duration of
+ * 3599 seconds (one hour) and it gets updated every second.
+ *
+ * @since 3.2
+ */
+class KDE_EXPORT KGameLCDClock : public KGameLCD
+{
+ Q_OBJECT
+public:
+ KGameLCDClock(QWidget *parent = 0, const char *name = 0);
+
+ ~KGameLCDClock();
+
+ /**
+ * @return the total number of seconds elapsed.
+ */
+ uint seconds() const;
+
+ /**
+ * @return the time as a string to be displayed: "mm:ss".
+ */
+ QString pretty() const;
+
+ /**
+ * Set the time.
+ */
+ void setTime(uint seconds);
+
+ /**
+ * Set the time (format should be "mm:ss").
+ */
+ void setTime(const QString &s);
+
+public slots:
+ /**
+ * Stop the clock and reset it to zero.
+ */
+ virtual void reset();
+
+ /**
+ * Stop the clock but do not reset it to zero.
+ */
+ virtual void stop();
+
+ /**
+ * Start the clock from the current time.
+ */
+ virtual void start();
+
+protected slots:
+ virtual void timeoutClock();
+
+private:
+ QTimer *_timerClock;
+ uint _sec, _min;
+
+ class KGameLCDClockPrivate;
+ KGameLCDClockPrivate *d;
+
+ void showTime();
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * This widget holds a list of @ref QLCDNumber arranged in a vertical layout.
+ * It also shows a label at the top of the list.
+ *
+ * @since 3.2
+ */
+class KDE_EXPORT KGameLCDList : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor.
+ *
+ * @param title is the content of the top label.
+ * @param parent passed to the QWidget constructor
+ * @param name passed to the QWidget constructor
+ */
+ KGameLCDList(const QString &title,
+ QWidget *parent = 0, const char *name = 0);
+ KGameLCDList(QWidget *parent = 0, const char *name = 0);
+
+ ~KGameLCDList();
+
+ /**
+ * Append a QLCDNumber at the bottom of the list.
+ * The QLCDNumber should have the KGameLCDList as parent.
+ */
+ void append(QLCDNumber *lcd);
+
+ /**
+ * Append a QLCDNumber at the bottom of the list.
+ * The QLCDNumber should have the KGameLCDList as parent.
+ */
+ void append(const QString &leading, QLCDNumber *lcd);
+
+ /**
+ * Delete all @ref QLCDNumber and clear the list.
+ */
+ void clear();
+
+ /**
+ * @return the title label.
+ */
+ QLabel *title() const { return _title; }
+
+ /**
+ * @return the QLCDNumber at index @param i
+ */
+ QLCDNumber *lcd(uint i) const { return _lcds[i]; }
+
+ /**
+ * @return the number of QLCDNumber in the list.
+ */
+ uint size() const { return _lcds.size(); }
+
+private:
+ QLabel *_title;
+ QValueVector<QLCDNumber *> _lcds;
+
+ class KGameLCDListPrivate;
+ KGameLCDListPrivate *d;
+
+ void init(const QString &title);
+};
+
+#endif
diff --git a/libkdegames/kgamemisc.cpp b/libkdegames/kgamemisc.cpp
new file mode 100644
index 00000000..bfedd11c
--- /dev/null
+++ b/libkdegames/kgamemisc.cpp
@@ -0,0 +1,62 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include <qstringlist.h>
+
+#include <krandomsequence.h>
+#include <klocale.h>
+
+#include "kgamemisc.h"
+
+class KGameMiscPrivate
+{
+public:
+ KGameMiscPrivate()
+ {
+ }
+
+};
+
+KGameMisc::KGameMisc()
+{
+// not yet used
+// d = new KGamePrivate;
+}
+
+KGameMisc::~KGameMisc()
+{
+ // don't forget to delete it as soon as it is used!
+// delete d;
+}
+
+QString KGameMisc::randomName()// do we need i18n? I think yes
+{
+ QStringList names = QStringList::split( QChar(' '),
+ i18n( "A list of language typical names ( for games ), separated by spaces",
+ "Adam Alex Andreas Andrew Bart Ben Bernd Bill "
+ "Chris Chuck Daniel Don Duncan Ed Emily Eric "
+ "Gary Greg Harry Ian Jean Jeff Jan Kai Keith Ken "
+ "Kirk Marc Mike Neil Paul Rik Robert Sam Sean "
+ "Thomas Tim Walter" ) );
+ KRandomSequence random;
+ return *names.at( random.getLong( names.count() ) );
+}
diff --git a/libkdegames/kgamemisc.h b/libkdegames/kgamemisc.h
new file mode 100644
index 00000000..526bb0ae
--- /dev/null
+++ b/libkdegames/kgamemisc.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEMISC_H__
+#define __KGAMEMISC_H__
+
+#include <qstring.h>
+#include <kdemacros.h>
+class KGameMiscPrivate;
+/**
+ * This class contains several (usually static) functions I really did not know
+ * a class for. If you know a class for any of these member s please drop one of
+ * the above copyright holders a mail (or just kde-games-devel@kde.org)
+ **/
+class KDE_EXPORT KGameMisc
+{
+public:
+ KGameMisc();
+ ~KGameMisc();
+
+ static QString randomName();
+
+private:
+ KGameMiscPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgameprogress.cpp b/libkdegames/kgameprogress.cpp
new file mode 100644
index 00000000..861dd454
--- /dev/null
+++ b/libkdegames/kgameprogress.cpp
@@ -0,0 +1,345 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1996 Martynas Kunigelis
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ * KGameProgress -- a progress indicator widget for KDE.
+ */
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qregexp.h>
+#include <qstyle.h>
+
+#include "kgameprogress.h"
+
+#include <kapplication.h>
+
+KGameProgress::KGameProgress(QWidget *parent, const char *name)
+ : QFrame(parent, name),
+ QRangeControl(0, 100, 1, 10, 0),
+ orient(Horizontal)
+{
+ initialize();
+}
+
+KGameProgress::KGameProgress(Orientation orientation, QWidget *parent, const char *name)
+ : QFrame(parent, name),
+ QRangeControl(0, 100, 1, 10, 0),
+ orient(orientation)
+{
+ initialize();
+}
+
+KGameProgress::KGameProgress(int minValue, int maxValue, int value,
+ Orientation orientation, QWidget *parent, const char *name)
+ : QFrame(parent, name),
+ QRangeControl(minValue, maxValue, 1, 10, value),
+ orient(orientation)
+{
+ initialize();
+}
+
+KGameProgress::~KGameProgress()
+{
+ delete bar_pixmap;
+}
+
+void KGameProgress::advance(int offset)
+{
+ setValue(value() + offset);
+}
+
+void KGameProgress::initialize()
+{
+ format_ = "%p%";
+ use_supplied_bar_color = false;
+ bar_pixmap = 0;
+ bar_style = Solid;
+ text_enabled = TRUE;
+ setBackgroundMode( PaletteBackground );
+ connect(kapp, SIGNAL(appearanceChanged()), this, SLOT(paletteChange()));
+ paletteChange();
+}
+
+void KGameProgress::paletteChange()
+{
+ QPalette p = kapp->palette();
+ const QColorGroup &colorGroup = p.active();
+ if (!use_supplied_bar_color)
+ bar_color = colorGroup.highlight();
+ bar_text_color = colorGroup.highlightedText();
+ text_color = colorGroup.text();
+ setPalette(p);
+
+ adjustStyle();
+}
+
+
+void KGameProgress::setBarPixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return;
+ if (bar_pixmap)
+ delete bar_pixmap;
+
+ bar_pixmap = new QPixmap(pixmap);
+}
+
+void KGameProgress::setBarColor(const QColor &color)
+{
+ bar_color = color;
+ use_supplied_bar_color = true;
+ if (bar_pixmap) {
+ delete bar_pixmap;
+ bar_pixmap = 0;
+ }
+}
+
+void KGameProgress::setBarStyle(BarStyle style)
+{
+ if (bar_style != style) {
+ bar_style = style;
+ update();
+ }
+}
+
+void KGameProgress::setOrientation(Orientation orientation)
+{
+ if (orient != orientation) {
+ orient = orientation;
+ update();
+ }
+}
+
+void KGameProgress::setValue(int value)
+{
+ QRangeControl::setValue(value);
+}
+
+void KGameProgress::setTextEnabled(bool enable)
+{
+ text_enabled = enable;
+}
+
+const QColor & KGameProgress::barColor() const
+{
+ return bar_color;
+}
+
+const QPixmap * KGameProgress::barPixmap() const
+{
+ return bar_pixmap;
+}
+
+bool KGameProgress::textEnabled() const
+{
+ return text_enabled;
+}
+
+QSize KGameProgress::sizeHint() const
+{
+ QSize s( size() );
+
+ if(orientation() == KGameProgress::Vertical) {
+ s.setWidth(24);
+ } else {
+ s.setHeight(24);
+ }
+
+ return s;
+}
+
+QSize KGameProgress::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+QSizePolicy KGameProgress::sizePolicy() const
+{
+ if ( orientation()==KGameProgress::Vertical )
+ return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
+ else
+ return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+}
+
+KGameProgress::Orientation KGameProgress::orientation() const
+{
+ return orient;
+}
+
+KGameProgress::BarStyle KGameProgress::barStyle() const
+{
+ return bar_style;
+}
+
+int KGameProgress::recalcValue(int range)
+{
+ int abs_value = value() - minValue();
+ int abs_range = maxValue() - minValue();
+ return abs_range ? range * abs_value / abs_range : 0;
+}
+
+void KGameProgress::valueChange()
+{
+ repaint(contentsRect(), FALSE);
+ emit percentageChanged(recalcValue(100));
+}
+
+void KGameProgress::rangeChange()
+{
+ repaint(contentsRect(), FALSE);
+ emit percentageChanged(recalcValue(100));
+}
+
+void KGameProgress::styleChange(QStyle&)
+{
+ adjustStyle();
+}
+
+void KGameProgress::adjustStyle()
+{
+ switch (style().styleHint(QStyle::SH_GUIStyle)) {
+ case WindowsStyle:
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ break;
+ case MotifStyle:
+ default:
+ setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ setLineWidth( 2 );
+ break;
+ }
+ update();
+}
+
+void KGameProgress::paletteChange( const QPalette &p )
+{
+ // This never gets called for global color changes
+ // because we call setPalette() ourselves.
+ QFrame::paletteChange(p);
+}
+
+void KGameProgress::drawText(QPainter *p)
+{
+ QRect r(contentsRect());
+ //QColor c(bar_color.rgb() ^ backgroundColor().rgb());
+
+ // Rik: Replace the tags '%p', '%v' and '%m' with the current percentage,
+ // the current value and the maximum value respectively.
+ QString s(format_);
+
+ s.replace(QRegExp(QString::fromLatin1("%p")), QString::number(recalcValue(100)));
+ s.replace(QRegExp(QString::fromLatin1("%v")), QString::number(value()));
+ s.replace(QRegExp(QString::fromLatin1("%m")), QString::number(maxValue()));
+
+ p->setPen(text_color);
+ QFont font = p->font();
+ font.setBold(true);
+ p->setFont(font);
+ //p->setRasterOp(XorROP);
+ p->drawText(r, AlignCenter, s);
+ p->setClipRegion( fr );
+ p->setPen(bar_text_color);
+ p->drawText(r, AlignCenter, s);
+}
+
+void KGameProgress::drawContents(QPainter *p)
+{
+ QRect cr = contentsRect(), er = cr;
+ fr = cr;
+ QBrush fb(bar_color), eb(backgroundColor());
+
+ if (bar_pixmap)
+ fb.setPixmap(*bar_pixmap);
+
+ if (backgroundPixmap())
+ eb.setPixmap(*backgroundPixmap());
+
+ switch (bar_style) {
+ case Solid:
+ if (orient == Horizontal) {
+ fr.setWidth(recalcValue(cr.width()));
+ er.setLeft(fr.right() + 1);
+ } else {
+ fr.setTop(cr.bottom() - recalcValue(cr.height()));
+ er.setBottom(fr.top() - 1);
+ }
+
+ p->setBrushOrigin(cr.topLeft());
+ p->fillRect(fr, fb);
+
+ p->fillRect(er, eb);
+
+ break;
+
+ case Blocked:
+ const int margin = 2;
+ int max, num, dx, dy;
+ if (orient == Horizontal) {
+ fr.setHeight(cr.height() - 2 * margin);
+ fr.setWidth((int)(0.67 * fr.height()));
+ fr.moveTopLeft(QPoint(cr.left() + margin, cr.top() + margin));
+ dx = fr.width() + margin;
+ dy = 0;
+ max = (cr.width() - margin) / (fr.width() + margin) + 1;
+ num = recalcValue(max);
+ } else {
+ fr.setWidth(cr.width() - 2 * margin);
+ fr.setHeight((int)(0.67 * fr.width()));
+ fr.moveBottomLeft(QPoint(cr.left() + margin, cr.bottom() - margin));
+ dx = 0;
+ dy = - (fr.height() + margin);
+ max = (cr.height() - margin) / (fr.height() + margin) + 1;
+ num = recalcValue(max);
+ }
+ p->setClipRect(cr.x() + margin, cr.y() + margin,
+ cr.width() - margin, cr.height() - margin);
+ for (int i = 0; i < num; i++) {
+ p->setBrushOrigin(fr.topLeft());
+ p->fillRect(fr, fb);
+ fr.moveBy(dx, dy);
+ }
+
+ if (num != max) {
+ if (orient == Horizontal)
+ er.setLeft(fr.right() + 1);
+ else
+ er.setBottom(fr.bottom() + 1);
+ if (!er.isNull()) {
+ p->setBrushOrigin(cr.topLeft());
+ p->fillRect(er, eb);
+ }
+ }
+
+ break;
+ }
+
+ if (text_enabled && bar_style != Blocked)
+ drawText(p);
+}
+
+void KGameProgress::setFormat(const QString & format)
+{
+ format_ = format;
+}
+
+QString KGameProgress::format() const
+{
+ return format_;
+}
+
+#include "kgameprogress.moc"
diff --git a/libkdegames/kgameprogress.h b/libkdegames/kgameprogress.h
new file mode 100644
index 00000000..d6a353ac
--- /dev/null
+++ b/libkdegames/kgameprogress.h
@@ -0,0 +1,255 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1996 Martynas Kunigelis
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*****************************************************************************
+* *
+* KGameProgress -- progress indicator widget for KDE by Martynas Kunigelis *
+* *
+*****************************************************************************/
+
+#ifndef _KPROGRES_H
+#define _KPROGRES_H "$Id$"
+
+#include <qframe.h>
+#include <qrangecontrol.h>
+#include <kdemacros.h>
+/**
+ * @short A progress indicator widget.
+ *
+ * KGameProgress is derived from QFrame and QRangeControl, so
+ * you can use all the methods from those classes. The only difference
+ * is that setValue() is now made a slot, so you can connect
+ * stuff to it.
+ *
+ * None of the constructors take line step and page step as arguments,
+ * so by default they're set to 1 and 10 respectively.
+ *
+ * The Blocked style ignores the textEnabled() setting and displays
+ * no text, since it looks truly ugly (and for other reasons). Signal
+ * percentageChanged() is emitted whenever the value changes so you
+ * can set up a different widget to display the current percentage complete
+ * and connect the signal to it.
+ *
+ * @author Martynas Kunigelis
+ * @version $Id$
+ */
+class KDE_EXPORT KGameProgress : public QFrame, public QRangeControl
+{
+ Q_OBJECT
+ Q_ENUMS( BarStyle )
+ Q_PROPERTY( int value READ value WRITE setValue)
+ Q_PROPERTY( BarStyle barStyle READ barStyle WRITE setBarStyle )
+ Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor )
+ Q_PROPERTY( QPixmap barPixmap READ barPixmap WRITE setBarPixmap )
+ Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation )
+ Q_PROPERTY( bool textEnabled READ textEnabled WRITE setTextEnabled )
+
+public:
+ /**
+ * Possible values for bar style.
+ *
+ * @p Solid means one continuous progress bar, @p Blocked means a
+ * progress bar made up of several blocks.
+ */
+ enum BarStyle { Solid, Blocked };
+
+ /**
+ * Construct a horizontal progress bar.
+ */
+ KGameProgress(QWidget *parent=0, const char *name=0);
+
+ /**
+ * Construct a progress bar with orientation @p orient.
+ */
+ KGameProgress(Orientation orient, QWidget *parent=0, const char *name=0);
+
+ /**
+ * Construct a progress bar with minimum, maximum and initial values.
+ */
+ KGameProgress(int minValue, int maxValue, int value, Orientation,
+ QWidget *parent=0, const char *name=0);
+
+ /**
+ * Destruct the progress bar.
+ */
+ ~KGameProgress();
+
+ /**
+ * Set the progress bar style.
+ *
+ * Allowed values are @p Solid and @p Blocked.
+ */
+ void setBarStyle(BarStyle style);
+
+ /**
+ * Set the color of the progress bar.
+ */
+ void setBarColor(const QColor &);
+
+ /**
+ * Set a pixmap to be shown in the progress bar.
+ */
+ void setBarPixmap(const QPixmap &);
+
+ /**
+ * Set the orientation of the progress bar.
+ *
+ * Allowed values are @p Horizontal and @p Vertical.
+ */
+ void setOrientation(Orientation);
+
+ /**
+ * If this is set to @p true, the progress text will be displayed.
+ *
+ */
+ void setTextEnabled(bool);
+
+ /**
+ * Retrieve the bar style.
+ *
+ * @see setBarStyle()
+ */
+ BarStyle barStyle() const;
+
+ /**
+ * Retrieve the bar color.
+ * @see setBarColor()
+ */
+ const QColor &barColor() const;
+
+ /**
+ * Retrieve the bar pixmap.
+ *
+ * @see setBarPixmap()
+ */
+ const QPixmap *barPixmap() const;
+
+ /**
+ * Retrive the current status
+ *
+ * @see setValue()
+ */
+ int value() const { return QRangeControl::value(); }
+ /**
+ * Retrive the orientation of the progress bar.
+ *
+ * @see setOrientation()
+ */
+ Orientation orientation() const;
+
+ /**
+ * Returns @p true if progress text will be displayed,
+ * @p false otherwise.
+ *
+ * @see setFormat()
+ */
+ bool textEnabled() const;
+
+ /**
+ */
+ virtual QSize sizeHint() const;
+
+ /**
+ */
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ */
+ virtual QSizePolicy sizePolicy() const;
+
+ /**
+ * Retrieve the current format for printing status text.
+ * @see setFormat()
+ */
+ QString format() const;
+
+public slots:
+
+ /**
+ * Set the format of the text to use to display status.
+ *
+ * The default format is "%p%" (which looks like "42%".)
+ *
+ * @param format %p is replaced by percentage done, %v is replaced by actual
+ * value, %m is replaced by the maximum value.
+ */
+ void setFormat(const QString & format);
+
+ /**
+ * Set the current value of the progress bar to @p value.
+ *
+ * This must be a number in the range 0..100.
+ */
+ void setValue(int value);
+
+ /**
+ * Advance the progress bar by @p prog.
+ *
+ * This method is
+ * provided for convenience and is equivalent with
+ * setValue(value()+prog).
+ */
+ void advance(int prog);
+
+signals:
+ /**
+ * Emitted when the state of the progress bar changes.
+ */
+ void percentageChanged(int);
+
+protected:
+ /**
+ */
+ void valueChange();
+ /**
+ */
+ void rangeChange();
+ /**
+ */
+ void styleChange( QStyle& );
+ /**
+ */
+ void paletteChange( const QPalette & );
+ /**
+ */
+ void drawContents( QPainter * );
+
+private slots:
+ void paletteChange();
+
+private:
+ QPixmap *bar_pixmap;
+ bool use_supplied_bar_color;
+ QColor bar_color;
+ QColor bar_text_color;
+ QColor text_color;
+ QRect fr;
+ BarStyle bar_style;
+ Orientation orient;
+ bool text_enabled;
+ QString format_;
+ void initialize();
+ int recalcValue(int);
+ void drawText(QPainter *);
+ void adjustStyle();
+
+ class KGameProgressPrivate;
+ KGameProgressPrivate *d;
+};
+
+
+#endif
diff --git a/libkdegames/kgrid2d.h b/libkdegames/kgrid2d.h
new file mode 100644
index 00000000..f9dfd8dd
--- /dev/null
+++ b/libkdegames/kgrid2d.h
@@ -0,0 +1,520 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGRID2D_H_
+#define __KGRID2D_H_
+
+#include <math.h>
+
+#include <qpair.h>
+#include <qvaluelist.h>
+#include <qvaluevector.h>
+
+#include <kglobal.h>
+
+
+//-----------------------------------------------------------------------------
+namespace KGrid2D
+{
+ /**
+ * This type represents coordinates on a bidimensionnal grid.
+ * @since 3.2
+ */
+ typedef QPair<int, int> Coord;
+
+ /**
+ * This type represents a list of @ref Coord.
+ * @since 3.2
+ */
+ typedef QValueList<Coord> CoordList;
+}
+
+inline KGrid2D::Coord
+operator +(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
+ return KGrid2D::Coord(c1.first + c2.first, c1.second + c2.second);
+}
+
+inline KGrid2D::Coord
+operator -(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
+ return KGrid2D::Coord(c1.first - c2.first, c1.second - c2.second);
+}
+
+/**
+ * @return the maximum of both coordinates.
+ * @since 3.2
+ */
+inline KGrid2D::Coord
+maximum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
+ return KGrid2D::Coord(kMax(c1.first, c2.first), kMax(c1.second, c2.second));
+}
+/**
+ * @return the minimum of both coordinates.
+ * @since 3.2
+ */
+inline KGrid2D::Coord
+minimum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
+ return KGrid2D::Coord(kMin(c1.first, c2.first), kMin(c1.second, c2.second));
+}
+
+inline QTextStream &operator <<(QTextStream &s, const KGrid2D::Coord &c) {
+ return s << '(' << c.second << ", " << c.first << ')';
+}
+
+inline QTextStream &operator <<(QTextStream &s, const KGrid2D::CoordList &list)
+{
+ for(KGrid2D::CoordList::const_iterator i=list.begin(); i!=list.end(); ++i)
+ s << *i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+namespace KGrid2D
+{
+/**
+ * This template class represents a generic bidimensionnal grid. Each node
+ * contains an element of the template type.
+ *
+ * @since 3.2
+ */
+template <class Type>
+class Generic
+{
+ public:
+ /**
+ * Constructor.
+ */
+ Generic(uint width = 0, uint height = 0) {
+ resize(width, height);
+ }
+
+ virtual ~Generic() {}
+
+ /**
+ * Resize the grid.
+ */
+ void resize(uint width, uint height) {
+ _width = width;
+ _height = height;
+ _vector.resize(width*height);
+ }
+
+ /**
+ * Fill the nodes with the given value.
+ */
+ void fill(const Type &value) {
+ for (uint i=0; i<_vector.count(); i++) _vector[i] = value;
+ }
+
+ /**
+ * @return the width.
+ */
+ uint width() const { return _width; }
+ /**
+ * @return the height.
+ */
+ uint height() const { return _height; }
+ /**
+ * @return the number of nodes (ie width*height).
+ */
+ uint size() const { return _width*_height; }
+
+ /**
+ * @return the linear index for the given coordinate.
+ */
+ uint index(const Coord &c) const {
+ return c.first + c.second*_width;
+ }
+
+ /**
+ * @return the coordinate corresponding to the linear index.
+ */
+ Coord coord(uint index) const {
+ return Coord(index % _width, index / _width);
+ }
+
+ /**
+ * @return the value at the given coordinate.
+ */
+ const Type &at(const Coord &c) const { return _vector[index(c)]; }
+ /**
+ * @return the value at the given coordinate.
+ */
+ Type &at(const Coord &c) { return _vector[index(c)]; }
+ /**
+ * @return the value at the given coordinate.
+ */
+ const Type &operator [](const Coord &c) const { return _vector[index(c)]; }
+ /**
+ * @return the value at the given coordinate.
+ */
+ Type &operator [](const Coord &c) { return _vector[index(c)]; }
+
+ /**
+ * @return the value at the given linear index.
+ */
+ const Type &at(uint index) const { return _vector[index]; }
+ /**
+ * @return the value at the given linear index.
+ */
+ Type &at(uint index) { return _vector[index]; }
+ /**
+ * @return the value at the given linear index.
+ */
+ const Type &operator [](uint index) const { return _vector[index]; }
+ /**
+ * @return the value at the given linear index.
+ */
+ Type &operator [](uint index) { return _vector[index]; }
+
+ /**
+ * @return if the given coordinate is inside the grid.
+ */
+ bool inside(const Coord &c) const {
+ return ( c.first>=0 && c.first<(int)_width
+ && c.second>=0 && c.second<(int)_height );
+ }
+
+ /**
+ * Bound the given coordinate with the grid dimensions.
+ */
+ void bound(Coord &c) const {
+ c.first = kMax(kMin(c.first, (int)_width-1), 0);
+ c.second = kMax(kMin(c.second, (int)_height-1), 0);
+ }
+
+ protected:
+ uint _width, _height;
+ QValueVector<Type> _vector;
+};
+}
+
+template <class Type>
+QDataStream &operator <<(QDataStream &s, const KGrid2D::Generic<Type> &m) {
+ s << (Q_UINT32)m.width() << (Q_UINT32)m.height();
+ for (uint i=0; i<m.size(); i++) s << m[i];
+ return s;
+}
+
+template <class Type>
+QDataStream &operator >>(QDataStream &s, KGrid2D::Generic<Type> &m) {
+ Q_UINT32 w, h;
+ s >> w >> h;
+ m.resize(w, h);
+ for (uint i=0; i<m.size(); i++) s >> m[i];
+ return s;
+}
+
+
+namespace KGrid2D
+{
+
+//-----------------------------------------------------------------------------
+/**
+ * This class contains static methods to manipulate coordinates for a
+ * square bidimensionnal grid.
+ *
+ * @since 3.2
+ */
+class SquareBase
+{
+ public:
+ /**
+ * Identify the eight neighbours.
+ */
+ enum Neighbour { Left=0, Right, Up, Down, LeftUp, LeftDown,
+ RightUp, RightDown, Nb_Neighbour };
+
+ /**
+ * @return the trigonometric angle in radians for the given neighbour.
+ */
+ static double angle(Neighbour n) {
+ switch (n) {
+ case Left: return M_PI;
+ case Right: return 0;
+ case Up: return M_PI_2;
+ case Down: return -M_PI_2;
+ case LeftUp: return 3.0*M_PI_4;
+ case LeftDown: return -3.0*M_PI_4;
+ case RightUp: return M_PI_4;
+ case RightDown: return -M_PI_4;
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return 0;
+ }
+
+ /**
+ * @return the opposed neighbour.
+ */
+ static Neighbour opposed(Neighbour n) {
+ switch (n) {
+ case Left: return Right;
+ case Right: return Left;
+ case Up: return Down;
+ case Down: return Up;
+ case LeftUp: return RightDown;
+ case LeftDown: return RightUp;
+ case RightUp: return LeftDown;
+ case RightDown: return LeftUp;
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return Nb_Neighbour;
+ }
+
+ /**
+ * @return true if the neighbour is a direct one (ie is one of the four
+ * nearest).
+ */
+ static bool isDirect(Neighbour n) { return n<LeftUp; }
+
+ /**
+ * @return the neighbour for the given coordinate.
+ */
+ static Coord neighbour(const Coord &c, Neighbour n) {
+ switch (n) {
+ case Left: return c + Coord(-1, 0);
+ case Right: return c + Coord( 1, 0);
+ case Up: return c + Coord( 0, -1);
+ case Down: return c + Coord( 0, 1);
+ case LeftUp: return c + Coord(-1, -1);
+ case LeftDown: return c + Coord(-1, 1);
+ case RightUp: return c + Coord( 1, -1);
+ case RightDown: return c + Coord( 1, 1);
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return c;
+ }
+};
+
+/**
+ * This template is a @ref Generic implementation for a square bidimensionnal
+ * grid (@ref SquareBase).
+ *
+ * @since 3.2
+ */
+template <class T>
+class Square : public Generic<T>, public SquareBase
+{
+ public:
+ /**
+ * Constructor.
+ */
+ Square(uint width = 0, uint height = 0)
+ : Generic<T>(width, height) {}
+
+ /**
+ * @return the neighbours of coordinate @param c
+ * to the given set of coordinates
+ * @param c the coordinate to use as the reference point
+ * @param insideOnly only add coordinates that are inside the grid.
+ * @param directOnly only add the four nearest neighbours.
+ */
+ CoordList neighbours(const Coord &c, bool insideOnly = true,
+ bool directOnly = false) const {
+ CoordList neighbours;
+ for (uint i=0; i<(directOnly ? LeftUp : Nb_Neighbour); i++) {
+ Coord n = neighbour(c, (Neighbour)i);
+ if ( insideOnly && !Generic<T>::inside(n) ) continue;
+ neighbours.append(n);
+ }
+ return neighbours;
+ }
+
+ /**
+ * @return the "projection" of the given coordinate on the grid edges.
+ *
+ * @param c the coordinate to use as the reference point
+ * @param n the direction of projection.
+ */
+ Coord toEdge(const Coord &c, Neighbour n) const {
+ switch (n) {
+ case Left: return Coord(0, c.second);
+ case Right: return Coord(Generic<T>::width()-1, c.second);
+ case Up: return Coord(c.first, 0);
+ case Down: return Coord(c.first, Generic<T>::height()-1);
+ case LeftUp: return Coord(0, 0);
+ case LeftDown: return Coord(0, Generic<T>::height()-1);
+ case RightUp: return Coord(Generic<T>::width()-1, 0);
+ case RightDown: return Coord(Generic<T>::width()-1, Generic<T>::height()-1);
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return c;
+ }
+};
+
+//-----------------------------------------------------------------------------
+/**
+ * This class contains static methods to manipulate coordinates on an
+ * hexagonal grid where hexagons form horizontal lines:
+ * <pre>
+ * (0,0) (0,1) (0,2)
+ * (1,0) (1,1) (1,2)
+ * (2,0) (2,1) (2,2)
+ * </pre>
+ *
+ * @since 3.2
+ */
+class HexagonalBase
+{
+ public:
+ /**
+ * Identify the six neighbours.
+ */
+ enum Neighbour { Left = 0, Right, LeftUp, LeftDown,
+ RightUp, RightDown, Nb_Neighbour };
+
+ /**
+ * @return the trigonometric angle in radians for the given neighbour.
+ */
+ static double angle(Neighbour n) {
+ switch (n) {
+ case Left: return M_PI;
+ case Right: return 0;
+ case LeftUp: return 2.0*M_PI/3;
+ case LeftDown: return -2.0*M_PI/3;
+ case RightUp: return M_PI/3;
+ case RightDown: return -M_PI/3;
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return 0;
+ }
+
+ /**
+ * @return the opposed neighbour.
+ */
+ static Neighbour opposed(Neighbour n) {
+ switch (n) {
+ case Left: return Right;
+ case Right: return Left;
+ case LeftUp: return RightDown;
+ case LeftDown: return RightUp;
+ case RightUp: return LeftDown;
+ case RightDown: return LeftUp;
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return Nb_Neighbour;
+ }
+
+ /**
+ * @return the neighbour of the given coordinate.
+ */
+ static Coord neighbour(const Coord &c, Neighbour n) {
+ bool oddRow = c.second%2;
+ switch (n) {
+ case Left: return c + Coord(-1, 0);
+ case Right: return c + Coord( 1, 0);
+ case LeftUp: return c + (oddRow ? Coord( 0, -1) : Coord(-1, -1));
+ case LeftDown: return c + (oddRow ? Coord( 0, 1) : Coord(-1, 1));
+ case RightUp: return c + (oddRow ? Coord( 1, -1) : Coord( 0, -1));
+ case RightDown: return c + (oddRow ? Coord( 1, 1) : Coord( 0, 1));
+ case Nb_Neighbour: Q_ASSERT(false);
+ }
+ return c;
+ }
+
+ /**
+ * @return the distance between the two coordinates in term of hexagons.
+ */
+ static uint distance(const Coord &c1, const Coord &c2) {
+ return kAbs(c1.first - c2.first) + kAbs(c1.second - c2.second)
+ + (c1.first==c2.first || c1.second==c2.second ? 0 : -1);
+ }
+};
+
+/**
+ * This template implements a hexagonal grid
+ * where hexagons form horizontal lines:
+ * <pre>
+ * (0,0) (0,1) (0,2)
+ * (1,0) (1,1) (1,2)
+ * (2,0) (2,1) (2,2)
+ * </pre>
+ *
+ * @ since 3.2
+ */
+template <class Type>
+class Hexagonal : public Generic<Type>, public HexagonalBase
+{
+ public:
+ /**
+ * Constructor.
+ */
+ Hexagonal(uint width = 0, uint height = 0)
+ : Generic<Type>(width, height) {}
+
+ /**
+ * @return the neighbours of coordinate @param c
+ * to the given set of coordinates
+ * @param c the coordiante to use as the reference point
+ * @param insideOnly only add coordinates that are inside the grid.
+ */
+ CoordList neighbours(const Coord &c, bool insideOnly = true) const {
+ CoordList neighbours;
+ for (uint i=0; i<Nb_Neighbour; i++) {
+ Coord n = neighbour(c, (Neighbour)i);
+ if ( insideOnly && !Generic<Type>::inside(n) ) continue;
+ neighbours.append(n);
+ }
+ return neighbours;
+ }
+
+
+ /**
+ * @return the neighbours at distance @param distance of coordinate
+ * @param c the coordinate to use as the reference point
+ * @param distance distance to the neighbour (1 means at contact).
+ * @param insideOnly only add coordinates that are inside the grid.
+ * @param all returns all neighbours at distance equal and less than
+ * @param distance (the original coordinate is not included).
+ */
+ CoordList neighbours(const Coord &c, uint distance, bool all,
+ bool insideOnly = true) const {
+ // brute force algorithm -- you're welcome to make it more efficient :)
+ CoordList ring;
+ if ( distance==0 ) return ring;
+ ring = neighbours(c, insideOnly);
+ if ( distance==1 ) return ring;
+ CoordList center;
+ center.append(c);
+ for (uint i=1; i<distance; i++) {
+ CoordList newRing;
+ CoordList::const_iterator it;
+ for (it=ring.begin(); it!=ring.end(); ++it) {
+ CoordList n = neighbours(*it, insideOnly);
+ CoordList::const_iterator it2;
+ for (it2=n.begin(); it2!=n.end(); ++it2)
+ if ( center.find(*it2)==center.end()
+ && ring.find(*it2)==ring.end()
+ && newRing.find(*it2)==newRing.end() )
+ newRing.append(*it2);
+ center.append(*it);
+ }
+ ring = newRing;
+ }
+ if ( !all ) return ring;
+ CoordList::const_iterator it;
+ for (it=ring.begin(); it!=ring.end(); ++it)
+ center.append(*it);
+ center.remove(c);
+ return center;
+ }
+};
+
+} // namespace
+
+#endif
diff --git a/libkdegames/kstdgameaction.cpp b/libkdegames/kstdgameaction.cpp
new file mode 100644
index 00000000..53b8f16c
--- /dev/null
+++ b/libkdegames/kstdgameaction.cpp
@@ -0,0 +1,209 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kstdgameaction.h"
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kstdaccel.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+
+KStdGameAction::KStdGameAction()
+{}
+
+KStdGameAction::~KStdGameAction()
+{}
+
+KAction *KStdGameAction::action(StdGameAction act_enum, const QObject *recvr,
+ const char *slot, KActionCollection *parent,
+ const char *name)
+{
+ return create( act_enum, name, recvr, slot, parent );
+}
+
+const char* KStdGameAction::stdName(StdGameAction act_enum)
+{
+ return name(act_enum);
+}
+
+struct KStdGameActionInfo
+{
+ KStdGameAction::StdGameAction id;
+ KStdAccel::StdAccel globalAccel; // if we reuse a global accel
+ int shortcut; // specific shortcut (NH: should be configurable)
+ const char* psName;
+ const char* psLabel;
+ const char* psWhatsThis;
+ const char* psIconName;
+};
+
+const KStdGameActionInfo g_rgActionInfo[] = {
+// "game" menu
+ { KStdGameAction::New, KStdAccel::New, 0, "game_new", I18N_NOOP2("new game", "&New"), 0, "filenew" },
+ { KStdGameAction::Load, KStdAccel::Open, 0, "game_load", I18N_NOOP("&Load..."), 0, "fileopen" },
+ { KStdGameAction::LoadRecent, KStdAccel::AccelNone, 0, "game_load_recent", I18N_NOOP("Load &Recent"), 0, 0 },
+ { KStdGameAction::Restart, KStdAccel::Reload, 0, "game_restart", I18N_NOOP("Restart &Game"), 0, "reload" },
+ { KStdGameAction::Save, KStdAccel::Save, 0, "game_save", I18N_NOOP("&Save"), 0, "filesave" },
+ { KStdGameAction::SaveAs, KStdAccel::AccelNone, 0, "game_save_as", I18N_NOOP("Save &As..."), 0, "filesaveas" },
+ { KStdGameAction::End, KStdAccel::End, 0, "game_end", I18N_NOOP("&End Game"), 0, "fileclose" },
+ { KStdGameAction::Pause, KStdAccel::AccelNone, Qt::Key_P, "game_pause", I18N_NOOP("Pa&use"), 0, "player_pause" },
+ { KStdGameAction::Highscores, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_H, "game_highscores", I18N_NOOP("Show &Highscores"), 0, "highscore" },
+ { KStdGameAction::Print, KStdAccel::Print, 0, "game_print", I18N_NOOP("&Print..."), 0, "fileprint" },
+ { KStdGameAction::Quit, KStdAccel::Quit, 0, "game_quit", I18N_NOOP("&Quit"), 0, "exit" },
+// "move" menu
+ { KStdGameAction::Repeat, KStdAccel::AccelNone, 0, "move_repeat", I18N_NOOP("Repeat"), 0, 0 },
+ { KStdGameAction::Undo, KStdAccel::Undo, 0, "move_undo", I18N_NOOP("Und&o"), 0, "undo" },
+ { KStdGameAction::Redo, KStdAccel::Redo, 0, "move_redo", I18N_NOOP("Re&do"), 0, "redo" },
+ { KStdGameAction::Roll, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_R, "move_roll", I18N_NOOP("&Roll Dice"), 0, "roll" },
+ { KStdGameAction::EndTurn, KStdAccel::AccelNone, 0, "move_end_turn", I18N_NOOP("End Turn"), 0, "endturn" },
+ { KStdGameAction::Hint, KStdAccel::AccelNone, Qt::Key_H, "move_hint", I18N_NOOP("&Hint"), 0, "idea" },
+ { KStdGameAction::Demo, KStdAccel::AccelNone, Qt::Key_D, "move_demo", I18N_NOOP("&Demo"), 0, "1rightarrow" },
+ { KStdGameAction::Solve, KStdAccel::AccelNone, 0, "move_solve", I18N_NOOP("&Solve"), 0, "wizard" },
+// "settings" menu
+ { KStdGameAction::ChooseGameType, KStdAccel::AccelNone, 0, "options_choose_game_type", I18N_NOOP("Choose Game &Type"), 0, 0 },
+ { KStdGameAction::Carddecks, KStdAccel::AccelNone, 0, "options_configure_carddecks", I18N_NOOP("Configure &Carddecks..."), 0, 0 },
+ { KStdGameAction::ConfigureHighscores, KStdAccel::AccelNone, 0, "options_configure_highscores", I18N_NOOP("Configure &Highscores..."), 0, 0 },
+
+ { KStdGameAction::ActionNone, KStdAccel::AccelNone, 0, 0, 0, 0, 0 }
+};
+
+static const KStdGameActionInfo* infoPtr( KStdGameAction::StdGameAction id )
+{
+ for (uint i = 0; g_rgActionInfo[i].id!=KStdGameAction::ActionNone; i++) {
+ if( g_rgActionInfo[i].id == id )
+ return &g_rgActionInfo[i];
+ }
+ return 0;
+}
+
+
+KAction* KStdGameAction::create(StdGameAction id, const char *name,
+ const QObject *recvr, const char *slot,
+ KActionCollection* parent )
+{
+ KAction* pAction = 0;
+ const KStdGameActionInfo* pInfo = infoPtr( id );
+ kdDebug(125) << "KStdGameAction::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)0) << ", " << parent << ", " << name << " )" << endl;
+ if( pInfo ) {
+ QString sLabel = i18n(pInfo->psLabel);
+ KShortcut cut = (pInfo->globalAccel==KStdAccel::AccelNone
+ ? KShortcut(pInfo->shortcut)
+ : KStdAccel::shortcut(pInfo->globalAccel));
+ const char *n = name ? name : pInfo->psName;
+ switch( id ) {
+ case LoadRecent:
+ pAction =
+ new KRecentFilesAction(sLabel, cut, recvr, slot, parent, n);
+ break;
+ case Pause:
+ case Demo:
+ pAction = new KToggleAction( sLabel, pInfo->psIconName, cut,
+ recvr, slot, parent, n);
+ break;
+ case ChooseGameType:
+ pAction = new KSelectAction( sLabel, pInfo->psIconName, cut,
+ recvr, slot, parent, n);
+ break;
+ default:
+ pAction = new KAction( sLabel, pInfo->psIconName, cut,
+ recvr, slot, parent, n);
+ break;
+ }
+ }
+ return pAction;
+}
+
+const char* KStdGameAction::name( StdGameAction id )
+{
+ const KStdGameActionInfo* pInfo = infoPtr( id );
+ return (pInfo) ? pInfo->psName : 0;
+}
+
+KAction *KStdGameAction::gameNew(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(New, name, recvr, slot, parent); }
+KAction *KStdGameAction::load(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Load, name, recvr, slot, parent); }
+KRecentFilesAction *KStdGameAction::loadRecent(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return static_cast<KRecentFilesAction *>(KStdGameAction::create(LoadRecent, name, recvr, slot, parent)); }
+KAction *KStdGameAction::save(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Save, name, recvr, slot, parent); }
+KAction *KStdGameAction::saveAs(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(SaveAs, name, recvr, slot, parent); }
+KAction *KStdGameAction::end(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(End, name, recvr, slot, parent); }
+KToggleAction *KStdGameAction::pause(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return static_cast<KToggleAction *>(KStdGameAction::create(Pause, name, recvr, slot, parent)); }
+KAction *KStdGameAction::highscores(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Highscores, name, recvr, slot, parent); }
+KAction *KStdGameAction::print(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Print, name, recvr, slot, parent); }
+KAction *KStdGameAction::quit(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Quit, name, recvr, slot, parent); }
+
+KAction *KStdGameAction::repeat(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Repeat, name, recvr, slot, parent); }
+KAction *KStdGameAction::undo(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Undo, name, recvr, slot, parent); }
+
+KAction *KStdGameAction::redo(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Redo, name, recvr, slot, parent); }
+
+KAction *KStdGameAction::roll(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Roll, name, recvr, slot, parent); }
+KAction *KStdGameAction::endTurn(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(EndTurn, name, recvr, slot, parent); }
+
+KAction *KStdGameAction::carddecks(const QObject *recvr, const char *slot,
+ KActionCollection *parent, const char *name )
+{ return KStdGameAction::create(Carddecks, name, recvr, slot, parent); }
+KAction *KStdGameAction::configureHighscores(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return KStdGameAction::create(ConfigureHighscores, name, recvr, slot, parent); }
+KAction *KStdGameAction::hint(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return KStdGameAction::create(Hint, name, recvr, slot, parent); }
+KToggleAction *KStdGameAction::demo(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return static_cast<KToggleAction *>(KStdGameAction::create(Demo, name, recvr, slot, parent)); }
+KAction *KStdGameAction::solve(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return KStdGameAction::create(Solve, name, recvr, slot, parent); }
+KSelectAction *KStdGameAction::chooseGameType(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return static_cast<KSelectAction *>(KStdGameAction::create(ChooseGameType, name, recvr, slot, parent)); }
+KAction *KStdGameAction::restart(const QObject*recvr, const char *slot,
+ KActionCollection *parent, const char *name)
+{ return KStdGameAction::create(Restart, name, recvr, slot, parent); }
diff --git a/libkdegames/kstdgameaction.h b/libkdegames/kstdgameaction.h
new file mode 100644
index 00000000..a38082af
--- /dev/null
+++ b/libkdegames/kstdgameaction.h
@@ -0,0 +1,261 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// this class was shamelessy stolen from kdelibs/kdeui/kstdction.[cpp|h] and
+// after that just edited for our needs
+#ifndef KSTDGAMEACTION_H
+#define KSTDGAMEACTION_H
+
+class KAction;
+class KToggleAction;
+class QObject;
+class KActionCollection;
+class KRecentFilesAction;
+class KSelectAction;
+#include <kdemacros.h>
+
+//-----------------------------------------------------------------------------
+/**
+ * Replacement for KStdAction for KDE Games
+ *
+ * This class is an extension to the usual KStdAction class which provides
+ * easy access to often used KDE actions
+ *
+ * Games often use different menu entries than other programs, e.g. games use
+ * the menu "game" instead of "file". This class provides the entries which
+ * differ from the usual KStdAction entries.
+ *
+ * @see KStdAction
+ *
+ * @author Andreas Beckermann <b_mann@gmx.de>
+ */
+// #### KDE4: transform in namespace
+class KDE_EXPORT KStdGameAction
+{
+public:
+ /**
+ * The standard menubar and toolbar actions.
+ **/
+ enum StdGameAction {
+ // Game menu
+ New=1, Load, LoadRecent, Save, SaveAs, End, Pause, Highscores,
+ Print, Quit,
+ // Move menu
+ Repeat, Undo, Redo, Roll, EndTurn,
+ // Settings menu
+ Carddecks,
+ ChooseGameType, // @since 3.2
+ ConfigureHighscores, // @since 3.2
+
+ Restart, // @since 3.2
+ Hint, // @since 3.2
+ Demo, // @since 3.2
+ Solve, // @since 3.2
+ ActionNone // @since 3.2
+ };
+
+ KStdGameAction();
+ ~KStdGameAction();
+
+ /**
+ * Creates an action corresponding to the
+ * KStdAction::StdAction enum.
+ * @since 3.2
+ */
+ static KAction* create( StdGameAction id, const char *name,
+ const QObject *recvr, const char *slot,
+ KActionCollection* parent );
+
+ /**
+ * @since 3.2
+ */
+ static KAction* create( StdGameAction id,
+ const QObject *recvr, const char *slot,
+ KActionCollection* parent )
+ { return create( id, 0, recvr, slot, parent ); }
+
+
+ /**
+ * Retrieve the action corresponding to the
+ * KStdGameAction::StdGameAction enum.
+ * @deprecated
+ */
+ static KAction *action(StdGameAction act_enum, const QObject *recvr = 0,
+ const char *slot = 0, KActionCollection *parent = 0,
+ const char *name = 0L );
+
+ /**
+ * This will return the internal name of a given standard action.
+ * @since 3.2
+ */
+ static const char* name( StdGameAction id );
+
+ /**
+ * This will return the internal name of a given standard action.
+ * @deprecated
+ */
+ static const char* stdName(StdGameAction act_enum);
+
+ /**
+ * Start a new game
+ **/
+ static KAction *gameNew(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Load a previousely saved game
+ */
+ static KAction *load(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Load a recently loaded game.
+ */
+ static KRecentFilesAction *loadRecent(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Save the current game.
+ */
+ static KAction *save(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Save the current game under a different filename.
+ */
+ static KAction *saveAs(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Pause the game
+ **/
+ static KToggleAction *pause(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Show the highscores.
+ */
+ static KAction *highscores(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+
+ /**
+ * End the current game, but do not quit the program. Think of a "close"
+ * entry.
+ */
+ static KAction *end(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Print the current screen? Game? Whatever - hardly used in games but there
+ * is at least one example (ktuberling)
+ */
+ static KAction *print(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Quit the game.
+ */
+ static KAction *quit(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+
+
+ /**
+ * Repeat the last move.
+ **/
+ static KAction *repeat(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Undo the last move
+ **/
+ static KAction *undo(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Redo the last move (which has been undone)
+ **/
+ static KAction *redo(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Roll die or dice
+ **/
+ static KAction *roll(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * End the current turn (not the game). Usually to let the next player
+ * start
+ **/
+ static KAction *endTurn(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+
+ /**
+ * Display configure carddecks dialog.
+ */
+ static KAction *carddecks(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Display configure highscores dialog.
+ * @since 3.2
+ */
+ static KAction *configureHighscores(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Give an advice/hint.
+ * @since 3.2
+ */
+ static KAction *hint(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Show a demo.
+ * @since 3.2
+ */
+ static KToggleAction *demo(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Solve the game.
+ * @since 3.2
+ */
+ static KAction *solve(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Choose game type.
+ * @since 3.2
+ */
+ static KSelectAction *chooseGameType(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+ /**
+ * Restart game.
+ * @since 3.2
+ */
+ static KAction *restart(const QObject *recvr = 0, const char *slot = 0,
+ KActionCollection *parent = 0, const char *name = 0L );
+
+};
+
+#endif
diff --git a/libkdegames/pics/Makefile.am b/libkdegames/pics/Makefile.am
new file mode 100644
index 00000000..8f94956a
--- /dev/null
+++ b/libkdegames/pics/Makefile.am
@@ -0,0 +1,4 @@
+picsdir = $(kde_datadir)/kdegames/pics
+pics_DATA = star.png
+
+KDE_ICON = action-roll action-highscore action-endturn
diff --git a/libkdegames/pics/cr16-action-endturn.png b/libkdegames/pics/cr16-action-endturn.png
new file mode 100644
index 00000000..00051f46
--- /dev/null
+++ b/libkdegames/pics/cr16-action-endturn.png
Binary files differ
diff --git a/libkdegames/pics/cr16-action-highscore.png b/libkdegames/pics/cr16-action-highscore.png
new file mode 100644
index 00000000..8eb54762
--- /dev/null
+++ b/libkdegames/pics/cr16-action-highscore.png
Binary files differ
diff --git a/libkdegames/pics/cr16-action-roll.png b/libkdegames/pics/cr16-action-roll.png
new file mode 100644
index 00000000..e41d572a
--- /dev/null
+++ b/libkdegames/pics/cr16-action-roll.png
Binary files differ
diff --git a/libkdegames/pics/cr22-action-roll.png b/libkdegames/pics/cr22-action-roll.png
new file mode 100644
index 00000000..bdf85a8b
--- /dev/null
+++ b/libkdegames/pics/cr22-action-roll.png
Binary files differ
diff --git a/libkdegames/pics/cr32-action-endturn.png b/libkdegames/pics/cr32-action-endturn.png
new file mode 100644
index 00000000..fc6d82bc
--- /dev/null
+++ b/libkdegames/pics/cr32-action-endturn.png
Binary files differ
diff --git a/libkdegames/pics/cr32-action-highscore.png b/libkdegames/pics/cr32-action-highscore.png
new file mode 100644
index 00000000..ad797080
--- /dev/null
+++ b/libkdegames/pics/cr32-action-highscore.png
Binary files differ
diff --git a/libkdegames/pics/cr32-action-roll.png b/libkdegames/pics/cr32-action-roll.png
new file mode 100644
index 00000000..8452f4a8
--- /dev/null
+++ b/libkdegames/pics/cr32-action-roll.png
Binary files differ
diff --git a/libkdegames/pics/star.png b/libkdegames/pics/star.png
new file mode 100644
index 00000000..ea88d160
--- /dev/null
+++ b/libkdegames/pics/star.png
Binary files differ