### ScidPocket.
### Copyright (C) 2007  Pascal Georges

image create photo _tick -data {
  R0lGODdhEAAQAKEAAP///wAAAFFR+wAAACwAAAAAEAAQAAACMISPacHtvpQKUSIKsBQiV8V1
  mIaEFWmYJ8Cl7TGynQpH4XtV9/ThuWepZR5ERtBSAAA7
}

image create photo _cross -data {
  R0lGODdhEAAQAKEAAP///wAAAPoTQAAAACwAAAAAEAAQAAACL4SPacHtvpQKUSIKsFA7V9EZ
  YIUBoxliJXqSSeseIKzKXVujyJlbse/JPIYMoKQAADs=
}

image create photo tb_pgn -data {
  R0lGODdhFAAUAKEAANnZ2QAngf///wAAACwAAAAAFAAUAAACSISPmcHtD6OcFIqLs8Zse90N
  IiYOl3kKYYoK5uiqQRy3J2qutfbGel/a5WZBmtBHzLR6rqHRyBzpSMGl7IO9Zj2d7abrzVQo
  BQA7
}

image create photo tb_flip -data {
  R0lGODdhFAAUAKEAAAAAANDAoKCAUIsAACwAAAAAFAAUAAACXoSPqcHiHJyA0tDXcHS2jT9s
  UyYYEgheY2AKXxaSUPuK6gww3/S66MDJBVK9H7AkHKIiywpAotxFa6ynD8ULpaoOY5YZ69hk
  yuBNh9khz1M1EirD4uDjemtFpyv2/AIAOw==
}

image create photo tb_gprev -data {
  R0lGODlhEQARAMIAANnZ2RwyggAAAP///6mpqampqampqampqSwAAAAAEQARAAADQgi63P4w
  wrCEvViFrcT44CcAWwl4mDUIRMl5Ichq1ZquBN3Fck7WKZSPsuPhdCdbbPYr8pjEU/DicxCu
  WKxkywUkAAA7
}

image create photo tb_gnext -data {
  R0lGODlhEQARAMIAANnZ2RwyggAAAP///6mpqampqampqampqSwAAAAAEQARAAADQQi63P4w
  wrCEvXhRJYb/nqBVA2aVhLIBHfgJKbB2Jh3P7nuTNRr8P1YuRAAGaS2dLCgcwlSV2iXmIFiv
  V4l2C0gAADs=
}

image create photo tb_start -data {
  R0lGODdhFAAUAPAAANnZ2VFR+ywAAAAAFAAUAAACPoSPqQGsf5iDT1JoU5hm96Z5kZh123Q6
  ZaqmX+uyKijXs2Szrxnr9HjikUBAVPCHOPJ2oSbyoiFCk9KpFVIAADs=
}

image create photo tb_prev -data {
  R0lGODdhFAAUAPAAANnZ2VFR+ywAAAAAFAAUAAACI4SPqcvtD6MJVAKKI95v++aFS0iO5Mec
  maNqooRaq0XXNl0AADs=
}


image create photo tb_next -data {
  R0lGODdhFAAUAIAAANnZ2VFR+ywAAAAAFAAUAAACI4SPqcvtD1WYEc1bwd1x++eFTUgupCid
  FKOCqItVcUbX9q0UADs=
}

image create photo tb_end -data {
  R0lGODdhFAAUAIAAANnZ2VFR+ywAAAAAFAAUAAACPoSPqcsBztqLEgZ4rnJWZ59Nz4WR2NSR
  hlmKLMeu7kujK/3ao7mretxrpUo+WQpRNN4SoNCJ4oRuRNJodVEAADs=
}

image create photo tb_flip -data {
  R0lGODdhFAAUAKEAAAAAANDAoKCAUIsAACwAAAAAFAAUAAACXoSPqcHiHJyA0tDXcHS2jT9s
  UyYYEgheY2AKXxaSUPuK6gww3/S66MDJBVK9H7AkHKIiywpAotxFa6ynD8ULpaoOY5YZ69hk
  yuBNh9khz1M1EirD4uDjemtFpyv2/AIAOw==
}
set pieceImageData(Merida,45) {
  R0lGODlhHAItAMIAAL+/v39/fwAAAD8/P////////////////yH5BAEKAAcALAAAAAAcAi0A
  AAP+eLrc/jDKSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoAcgLJICSKOSqGx2
  kAEnCHoKCAZMBiAbo5YAUGQWHM6Gxc8A9zAQuAcNcsq6bnlLVqxjS7uPyFBjZ2aDaWttb3FJ
  KHQwAG6QdR9WblELjwKSK4hXeJCVC5SQlgefoBucZpkEj6SicFUCpC2YkZ6nCpiaKpywI6K4
  wLILpsOotgrCrpC+nrMrj1isbbscsm16pQNtXautR7IEBIsHVuIExqUB4lbPFnQE3MkC5+lX
  rOkiSNjkKrWf1TZ8yvap2yd3HpSFMkWqGEIKwhYeJDbxSAB+D0u0OSf+rhOJcLrMRYNhrmPG
  De3YDYCyseNKJOHG5YNAKVvJdrnCNSoXMx2ikxQ4JWIhlFmJYmAGkmQIbiBLUy+FzXxQk4nC
  A/8yzavIBtcxpSxinrNydN20SuwOBgQR7aLWXzGl1mOYFmi7PPMAxBPAoBeDe9+6wgQKcWqK
  YpCOIl6sBpqptRikLp7slSrYiAv8UhylQCjhCThdiE3rKa0bvfhWIsrGiDM4sxxjy+YYOsI2
  0p2RXXor0aM6cfKeGEYx+daoYqrfQBZeeQrl51whFM2sO+eaV5sTJxx+omW9ZiHaqLniHZ3Z
  pL5N1Fp+IeXs97FrSx+Aex54nu7E/zW7bRL+dxNFDTVCLx3RxUpNKaxnHHSUATXdQvfJl9ss
  UPn3mQjRnENNU/S9NxoAiLB3QYYbwhXVGSimqJ8EbaVXCl8NSIhJA+KVyIFmLGR1WlOngYgc
  amfx9sdjr8FnZF0T/AOeGw7IyGRfAHmAI1H0uERCNCAeKRslIlKQIXBC+ndFimSG8ZOX2DhQ
  mXYSOYBNlzTKAtN9KBRFZwc6UiaNaXCyiFifNF0BpJYc+UiYj/et+WSbcbJ2DBQudvfGXJHe
  WOWBhJ6jiwjYxJYmhmhlyicGLrKpgKkvwkidc0AGlkKARoXHjGQ/irOprIvduUGeDFZHQamL
  nhpsqnGyqul/IIj+NxteIMQUoqipyTrosbpqgKiob2awIzE7YUUPb9Go6q24Efjo1DyxGePW
  QIBSp2cIo4DJIGojJftctRmYWyaKymkrpG4K5hTsjGhCZYlIq6hrZ7tdTVtopdqu8xO0YF4I
  gS7w3YprrxCDlt41y5iX2UririjBT2ZWUtJ3NZ0hoAawItdsXByvspfFD+QpGcONEvppBswK
  m44opHACJWEoPyUnPSmNOUoYL2PQypFkDSFLLbDFZ+S2xwwKD7Wv7TsYB5Q0BNNCIp969lYO
  3rNsJMeGKtvPUnP8ASZjTSZVh+iEiUGA6+wNDqHIRlB2dr01xObhJ7u9ZSSPbDGKw3v+4Rso
  4Tg3Sc8jrLy38tuWBzqte8dmLsHn8BUeaHDhGLPSOLCYU7U2/+VhpKEaOv6eje1xbDoDtm+j
  d98GBg00YoEjD1evv/O0aEWaAbPZSbYfqLKtytL3Jjtlo8b7O5du3bzahKIuG9d/hw8GRhx1
  rIH5s6me87eWhNPfb1Xfln8mfv8VPnze0V3qyFUBWimvA74jninm0qeiiOFAwggd+JjHAawJ
  zSOmyI12UiEBN8jrMWD6jmmgUqD3/c9D4yuPkVT4OAk2YDR2og0BhYM5BMKECCDRSg69lTwe
  PsJPWcuYTmxGtf6dzncdiNmfFHiQtLjQf5+IXwb1gURr2IL+SBnMyriGFYchMgYfydOLEvXS
  LQtMzUhlrBvlZHPGrc3wV0GUSRxn55waWmMdnegJTOqxR5l0xY+GGw0a6ZcpCXmJY4DiFWLo
  Qyu+cbECX6TV8hg0PqUwRBkREcoRkzcZMhLSgEM0HTWE+EQtCBCAJ5yNyS4gSDk+Di52FMg6
  IkezlcmCZpEDZKDimDr4eU5+UIJOKXeDSCYusGIaiORiJgmdSgLEIQxByhR3KRPKmKRKz0ne
  7+Qmw2FSh1DmKp/7IHDKcajyjShJ5fnGdxF5qWE82CAPNTzptvtdrJWo5JuW0mgBYSaxZldo
  kBOTuch1FYOZ2WTOZOx0LyBWc3j+nQLlKvhpAWV1Uzhr1NRq1Hk+dAYyow8bn+ZiaUJbveGk
  UEHps9IWgXL+kpe78+gElOjNrXBMiZMq4fFqJtIYVTGdAE1oBzuEU0a2Tk9gqqBF8+YoUtEH
  DLPRF08y1ZxfcTRug9MSMDUnjapwgpOnWQ2SjghSGcKUjVvVAk0BxVPnWbNApgMoj5qp0KAy
  xmPes2bTFmrS3/FjWXRjJR71OSphFTKt4zLLGQpLRZLudCDYcohVy1oXcD6yAjiNlb8Stpgs
  MChvpvtHiqKE0AbV1a6SncBpPlgMV/IVHzKdHxF/2VQ4mo9ubSxi8zJb0yM6NmJQoBgE3VIB
  jBUKRRj+KUNsNDaimtVWtfZbpCmbOVaYebCjvQ2k2MaGQKc88D2AeFCSVhuPQMRHkGS4TTW7
  ZNwjEUywUO2cTA4x20HGtqWvo01wSJBZsFhqUIDYF4DHScx9oHZMIbKWXf01noD6qr80uy85
  T/QUSREuu50S7kWLm1PPbQ+VRmwcZcHGSn1mwlU5Iax9K1hfk/LMTXLarvGSmZygJicDq/ku
  OA1MYOnY9YmtZRwxqVtNFlPmxXEacb2sQVgZe0rC1MECTG4qhsCCb8QhbS5R6WHPFEOLEjfq
  0GLjkV0YYzlaZHuDjm/HY9P5UlSItWlQLfYPvURNkQ4xjaViDLUeXwDCMkv+s0Gda8jCHNg1
  NNbwfoMCkMiZUsVFhPILoTOgMcn4Kb3F52HTeWa0Nu/Qku5FgodMST3LEj50/IB4Lg21TF9S
  bOKNjEO268B0ahiMUhtMer705ct6LAxjMXBPs7PgOw7XgMpNtWCPDasTeTLNh8YZGTS7ReaZ
  +njcG3OcH6DpIiIwvxTzEZPPertF/xnSmeryiPrB615LejMU5vG7W0puVM/bXd5VUaAJijx+
  LfMrB/amHEht7SLzm9LwireMzf3nVAZYDfGr6XWXOwgpGvvWyt6VS/fp6yQNelZIJp9wMw6+
  er903J0+EMMhCep778Guiu04iz6u5pCnCrXCkbf+MLlLUJ3fa8oX1zDJcXzVSFuNZG6xucgp
  NvRfoVucQR+5y1vu8jjN+doaR/r3Znbg7uabTAGy4tf1Pc26mdze8Dr7L0P8cZcZVWJ8juBn
  AGFpVus8c3z2HaQ4nV4GvURyoaU6vGDO2CY5sNWuXDWkzuUlSKn9vIjuJ7i/vHKPTV5Ubgn8
  41/pn1ubBiFtOc+UQwWJEcoJNQYOyF3sXqatSmXhTHnsQfYFOAW33DmEryboqX5kj8VL6pEv
  zObP6+Zuc3ybw5ehvTyve3KenRobV77HmJ8unP115Fa26tXCvS6B3F7VqI35cACKZ8QYmvdb
  nefof66GJbPyxOvXkxjn3kuq5OdtZswXsk/XFRXu7QgTSJBtOVY4byZcW6VmwrU+Mvdylycq
  Cuh9oMZ1V9d8ltErRFB+0REo6HeAvJcv6AdwqFVmNwdqGbENKQMVYxAgJ6JuGoh+mSN3tMcQ
  cAIMCkcmsUd0hyaCLmgbK/gjl6CCZhI6EjVnsgdzM0h1v/NwrNcYk7CEZNInYGA4PKOETlgG
  FbR4euMHaQBoljYEVRgIU/CFhWAtGcGEeCKGKZJIWMgYWkg2NLdQ/SAFcjiHdFiHdniHeJiH
  eriHfNiHfviHgBiIgjiIhFiIhniIiJiIiriIipgAADs=
}
set pieceImageData(Merida,60) {
[encoded data, 5625 characters]
  O7u0W7u2e7sfkAAAOw==
}

image create photo engine_on -data {
  R0lGODlhGAAYAOf4AFFOdGdOTQBk9AVi9ItNEVhVfAVt8FVafwRt91ZbgGhZYVdcgZNTEF1a
  gVtghV1ih15iiGNjg2ZmhlJrpDB01mdnhxV8+J9iFV1rm2lpimZqkGpqi2trjG1tjm5uj7Fl
  CnBwkXFxkrZpAXJyk2Z2oDeB6nNzlHR0lXV1lnZ2l3B4pDKI+LBwJTSJ+Xx4lHl5mjaK+nN7
  p356l397mIB8mYF9mpF8ebx6KECS+4KGoYOHooaKpUmZ/IeLplSW+2KU5omMp8qFK4uOqZmN
  jXSUz4yPqo2Qq1Ki/tCKKJKSqFOj/9mLGFSk/5+Tk5WVq1+j+5SXs2Ck/JeXrZWYtGGl/ZiY
  ruGRFFqq/3Sj5JqasGSn/+WUBpycspSfuWet/o2i05+ftqCgt42l0Gqv/2uw/6OiuYyp2aSj
  up+mu3Sx/YGv8KamvPSgDaqmt+iiJZ6qxG+3/3ez/6unuHC4/3i0/6mpv4mx7JOw4e6mH8Op
  iKurwXq6/5ez13u7/328/7Guv7KvwKizzrOwwYa9/YDC/6W40Za76+6uQYe//4HD/7ezxIjA
  /8Kzrpi+7bm1xrq2yIrF/ru3ybO60IvG/7y4yrS70ajA37i8zI7I/7K92LO+2Y/K/7q+zr+8
  zZbK/rvAz5DO/5HP/6XJ7L/D0pnO//zASZnR/sHF1aTO95rS/8LG1pvT/77K2MfI0p3V/8XJ
  2cLK36LV/L7N4aPW/crL1aXX/7zQ6cjN3KbY/83N1/TLeKfa/87O2c/Q2qfe/q7b/dDR26jf
  /9HS3Kng/9LT3avi/8vX5f/Yb7Pl/7Tm/7Xn/7ro/Lvp/b3q/vzhkL/s/8Tt/PnjpMXu/cbv
  /sfw/8jx//7ti8nz/8n2/cr3/9D1/tzw/dbz/sv5/9H2/9f0/9n1/9T5/9r2/9v3/9L8/efz
  /9/4/dr7/uH5/tz9/+j5/93//+n6/+L//er7/+j+/f/9xu/8/en//uv///H+//L///n///7/
  /P///////////////////////////////yH5BAEKAP8ALAAAAAAYABgAAAj+AP8JFJgFzCdH
  fwYqXKgwh5M3sri5s1eOFa1ODBnWCJOkWjVw6tTF2wbrVsaFNYRg8ugNHbpx4LTJonRyIAlM
  ppA9q8at2rdnzZDlwnjSA5FFoHAla7YTaLJfgUyezFCCDqZauIYpe7bs165Vd4QpOpkCAQIL
  POjMGePlCp1FntS8EnQSggELMLSM6bNoUZ8xT5RgaaXnZAQKJXy89YQL16o+dJR84SSnJoQf
  XkgNexYuXLdfnvpIAlPznwYMaoZVI9cOXbpmsQy1Kf1vgQYVw7ylu2dv3jRRVeoAol17RIou
  tyqhqZJERy8gNqzQbmAix4wkRlz868BhSBA30mlDR8jgwcNABSx0WcMTnrhCAh/YOINnrb17
  gfBLzQd//z0b/evZdx988tHX33si6BdNHgcORAAbh9yQRYMKBQAAhScFBAA7
}

image create photo engine_off -data {
  R0lGODlhGAAYAOfyAOYFO+YGQOgINugJPOcKQVFOdOkOPeAUOWdOTeAUPgBk9AVi9LUpUuEX
  P+AXRItNEeIZQM8hTVhVfOIaRdgeTAVt8FVafwRt9+IdS+UeQuQeR2hZYVdcgZNTEN4nUFtg
  hdUrWM8vVuEqTV5iiOEqUmNjg+MtVGZmhlJrpNY2WjB01mdnhxV8+OYwVp9iFd81WV1rm2lp
  iqtNcd82X2pqi2trjMlCdG1tjm5uj7FlCuQ8Y+Y9X3BwkbZpAb5NhWZ2oDeB6nNzlNxGaHR0
  ldNKdoZtj3Z2l6hdnHB4pDKI+LBwJTSJ+Xl5mjaK+nN7p356l+ROb4B8mdpWc4F9mpF8ebx6
  KECS+61wjIKGoYOHoqt1oIaKpUmZ/K12roeLplSW+2KU5omMp8qFK4uOqZmNjXSUz7J+nY2Q
  q9CKKJKSqNmLGJ+Tk5WVq8eAmV+j+8p+n8SDmpSXs2Ck/GGl/ZiYruGRFNZ/mJqasGSn/+WU
  Bpycsq+UxJ+ftqCgt42l0L6Uqmqv/9OLpGuw/6OiuaSjuqamvPSgDaqmt+iiJW+3/3ez/72b
  wXC4/3i0/6mpv4mx7NGYspOw4e6mH8OpiJWv53q6/9absLGuv7KvwKizzrOwwcSnxYa9/YDC
  /5a76+6uQYHD/4jA/8Kzrpi+7bm1xrq2yIrF/ru3ybO60IvG/7y4yqjA37i8zLK92Nmuw7O+
  2Y/K/7q+zr+8zZbK/rvAz5DO/5HP/6XJ7MW91r/D0ueyxPzASZnR/sHF1aTO95rS/5vT/8bG
  3bjM5cfI0p3V/8LK36LV/L7N4crL1aXX/6bY/83N1/TLeM7O2c/Q2qfe/q7b/dDR26jf/9HS
  3NLT3avi/8vX5f/Yb7Pl/7Tm/7Xn/7vp/eLb5/zhkL/s//njpMXu/cbv/sfw/8jx//7ti8nz
  /9bz/tH2/9f0/9n1/9v3/+fz/9/4/eH5/un6/+r7///9xu/8/f//////////////////////
  /////////////////////////////////yH5BAEKAP8ALAAAAAAYABgAAAj+AP8JFHiHDy1S
  lwYqXKgQCxszL3TEi7cuFzJZDBlO6ZPGDoAD7ty9Q1esWcaFU8aMgiTgALd27dKNO6bq5EAn
  sHwV03DAgCtz3rxhW4bxJI4ynWxJe9PyRVBs0DKZFChAgMIVQBqlUiYthQADm6BJA+Zp2Z+W
  B6oONHKhAgs3eBq1zCAoFaWPX3suHOG2iZxGldIKOBJhQloDVa0qLKGiCZdEnWqJEJxXgAab
  I8AA8iVtnIaqaR2g4mPzXwkYjaQFa1FZyqhDpf9JuPFjMmUNt+IUwlR6QE+0gQLxDIHFWRgq
  dU4KPhBBBpYsXg/gqEFGDKLkDA+kFbiCRo0IajdFKGFGThL22FSrPshhqBs8cudjo/23fpf7
  6+j/JRb4wJD98vGdBNpA67X3Xn7bEdiDfd9Mgh5iC/X3SRV35HcSAgVYiF5AADs=
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges

image create photo wm20 -data {
  R0lGODlhFAAUAMIAAH9/f7+/vz8/PwAAAP///////////////yH5BAEKAAcALAAAAAAUABQA
  AANweLp8cCG02cSwNB8RSACctgBAR3iDqJDd5wlidBLCHGb1CQzzx+yPDYAWyJ1gixpSwOLM
  CClFESSRup7RImF42zxP0Vpg0EE6SGjSCqxTKYxHN4RJ6sYETHxVNa3vM2gDQyBoGkNbhIdq
  FHRBZyAaCQA7}

image create photo bm20 -data {
  R0lGODlhFAAUAMIAAH9/f7+/vwAAAD8/P////////////////yH5BAEKAAcALAAAAAAUABQA
  AANneLp8cCG02YQYgupj+5CbEgyYAAQCuJHlNYZo1wHDo7VyOjSAebQxS69R25UCvVlmMXIp
  TrmhSGgB4S5LzoVQegK+YJtWwLWEFjnzGVL7ftYQMoGQggerZ3CrLealoxomXxJIX1kNCQA7
}

set pieceImageData(Merida,20) {
  R0lGODlh8AAUAMIAAH9/fz8/P7+/vwAAAP///////////////yH5BAEKAAcALAAAAADwABQA
  AAP+eLrc/jDKSau9OOvNu/8VAIBkJAhl2ohqe5xuCgTBGL/oQaMwJew30KwWhAkGA0Vv8gvk
  LALRUxJ9BTSAk40qFXWzkKZWCmQit5Uo2GdDW4ZuiFQ9koZ33mxZAjhjBidJFDNIRBgBhRQD
  Q4t9NH0NP3o1BEgEYjNTDix/SIITfQOIcROIooOFpouekV6PlQMEQ2qaK6QSsZUholGit5GA
  BJeAuMIixgDCnwrJAbKLsMPNsiY0VxeeyxGhnoZm2cTb4OMrP88C0X3NVWF+2CLaELnCUTRm
  6CfDtQuUwv7G1xb8iHUkmSV1lZy0GpErSZR9DbJVUOULCUQl3VRdPDL+rtsKRM8MxuqDjlcr
  FBIflkomK+CdLP8I2Ivg5NIOmxIe5RnygOSzhDKlLGqg01LCGjMhUHQpj1AhmfEYmHIy8JSJ
  jlZXAHIUDWRBojWcFnK1zZk/bw9oBLt09lcuMcpA7eS0CU8WVyIeMTBHD9ARdMjkjPt14BhF
  QEkddDuhSm7MqIUrrgJ0ZhSDvJIr+7o14x9dB3z9zTtCE3A+nHxiUpNXsFKgXj+mHPN3pKa/
  z5cH48LqJJwDVWoT8enYDis4W9k4cjzjliWk0p5ZBn5QcKdvOardXqqXN1nJVjFpxMTNgJw4
  4zypj3V6kRlxecYl7g0+mZtewcLQ/vYMjTb+U6lh5fXfJtmVNcpOj/xnGzL/kHaeO/AZ1xtN
  AaY3nHk9dZOHKnH0th43M1D4T2KXzebEbKKVFcoMhDEz1y8cvUjIPo3AU2MmNI0zYGEU2eiJ
  a3JUqF9PFT6nnnd5GHMdRrScQMeSC3Q23oCdxXaEapAdMI+Sisy1I0YyQslMgOi48iU34AzY
  yxlQJTfUA1hRoJMXYmJkHESOLIXIl1v+A5mAMgE2IkS9qLUGdDH9gIt0fprAaHQRxHeHeIfV
  eEc2CuV0Z6TrNVYcVrdEodp0ZY36WVVsPrPYb/HxmVFykfrYyJfLddTeCx15MZ8ovJlEVHx1
  zoNillrWICgh2zxpeluLfbZVnllK9pefNiiaSopPWLrVD0BZoqnbboOhBexxEQF7bXxuGfdg
  rlTEJxt9YDh1n0Dj7rOmjhtVmmmG6E2ArlRpapGmYsDa6+2qlwYcxAWHyrHwwxAX1h47EVds
  8cUYZ6xxBwkAADs=
}

set pieceImageData(Merida,30) {
  R0lGODlhaAEeAMIAAH9/fwAAAD8/P////7+/v////////////yH5BAEKAAcALAAAAABoAR4A
  AAP+eLrc/jDKSau9OOvNu/9gKI5kaZ5oqq5s62LAK2MEABAjgc9RzP+R2i23CwECAZ8GqRQI
  SARkEYZMKqoBxZFZiQZ2Atvz4J1mbKOtMsNVOKHSzdZ6wGqr62D8IECOyx1oRgIDA2EbX28A
  TmYeAYVZGjYDgo8DkZOCDjULjwRjlpECBJcLQxaLYY2SWHkWVU9bqxx2rG21c64KnFd+vUhu
  VaazD6mnjoWQcgCUfQNRqiBfUcuUWJB4lLpRRzieY2pupFncX6hOuktYkWewWNEfte3r9K0P
  Xuawd3R9wPjEDYyFsKRMwydDSAqFWUSHAyBJzJJJnKitGDM0D8kUAZT+KZ1FEfQ24JvzhaHH
  Lnvm1aunKxeZlDoWcMRzQdOHRQpPRnBGipS2QtA6OHkjaaGNo0iPHtoUZlQnBkSvyEzitIsN
  gDXtaehnLduzAGM29AsLw2jSs0sb/Hu6IGqdqSknCMGKKpTOYqN8Ukzm9oIThWQtQDuL1ty9
  RQwaArsD9ZjchVU9kLzrgCRLwGLfGfyyd2KUWTXIKmZ3pDHdXZBP67kBtBsGS846Y77wKWKh
  RaqnEpLdB6thPmHBtg1LDYKNJEPC3I4RGizlXStz10lyaeUoQ4Ef04ssmAnh478h/Bbeqy27
  4haR41AlhAzDtBQ4e84u4VEf27IbypWvA2j+eAv9WEdXH7vgdgBSZKRFngPoCOEEdc2gc0Mq
  r1j3GiGWrbObfhLgA1aA/1Xw116MUEDgS+aE40Un9AE3YSrINXWfUi2qtRtF3HVIiHISHTRR
  aRXI9xNQNfZA0EQcGncIgkwKsoguPPYIlkJC8lUkdCuFCEEUzdDj4UXsTLAOeL60812SxVgR
  zpqLHQElM0LA6dwnnB1FSl8WdWaTBNftNQlFaKpFkDHJaCnBEZ0FGlA3k2RxVQyPXIVcZUdK
  qUOVEhlqSpaKNgDimB7aRQE+7DEUVwUZajWBLHMcyAUeXriC6KeX1hoqIZpCl+g5vOGXDJCH
  2kbocu0kStlVCxn+wuUlB4nR3qKyIRWtTqGuU1eWNYxpDQWT6TCZSiyh6sccHo7Uj6zM1BPn
  DfVUNMGIEuF5T6VS5qcTTr/iB5+4xgKI3UWxORehspX5iiS9Eu35wKf6jMrpF9tBcu+Ylj23
  aZbPuePHOxqH2GjECTEcKWXwYgegXj0+yNuVdchWSrF7dZrYV30wjA5nYZpyI4kGZ0rydgBy
  KjJsLD+cs7gYV2h0SJXt2C4j9ex4F52Z5ngPhikrh/BtRzfd2b78xpxxGFLY+Ykq3nVdB8o/
  9uxfFzbTFet26V4GrJFJg0tPxksz7QBnDCMVMXpGsv3VvUcl85kWO/+odkBJtCKzcVv+T37F
  Qob3KIban2kuhlEoL74qpxOzi8UOqVJ3t5E73ycHcmdZPt2435m62CaAmzUXUn+J3kPjE4HN
  wFVA+Q6hn7m2hZ/CQWfJMmrP1G4nwVt+SHth1tOVuqppPthPERhbc5qEYljsKfCzOewyoMRA
  k+/n0lKJ1bIu5wq4gW6g73OQgIqlf/rv8o70amYit1EEMQ5bGkCqkK0iDA1Clrve7TAwozM9
  z0Xbgx3LHhg1EWVuPh1iYFhK1q/RhYuCsKvdgoJFkf60jVsGfKGJ+lYkLxAwGOG7xF3+lDDz
  YXCAz1OD9MJhwiESUUzrKxSqqkLCEtYHcw56nHjM8p0T7Qf+RJDhmPYe9I5PoSNIfZOizsDw
  sIhMLnJjEuPfqEgYK4YwiZDQ3tYAtcMY9rAscNQhqo7wIOaJyY7bCtIHpQS3QSpOeJALo/ky
  yASJ7dF6fqxPGF/ByMihpJJugmEeI/mAOdKxYEgJzVxUcZYe8NGIM1rVizbnHDtxUgsXGaK7
  DhVG2f2CdI5cVChxowpSJsWUrRzgpGhJvL0IYXJuYo0xj1PHTVoMX3DkkCqWc5UyrKs1LUqh
  EY9TxPglpQ18ul4by0RLRWbFaGbMAweNls1Grq9VLKRmUvJVwPfN8zYs4+E7nwlIruWhNtMT
  Hzdao5RZeHJXTzQkkdQ4JTi6kXJ35rxWGa3xT05dClviqaUwnkhDhq7THWDsGxVqiS7egWUH
  dGJjBDS60SDYLnbjkttYqjiukC5tpBEdXlJqhtKalTKjLJWL7ZznmC19tKZASKpSTWEnD6wr
  lEuNalSh2oGnNlWqWM2qVrfK1a569atgDatYx2qBBAAAOw==
}

namespace eval utils {
  namespace eval validate {
    
    # ::utils::validate::Integer
    #
    #   Used to check the size of integers in entry boxes.
    #
    proc Integer {maxValue allowQuestionMarks name el op} {
      global $name ${name}_old
      if {[string comp {} $el]} {
        set old  ${name}_old\($el\)
        set name $name\($el\)
      } else {
        set old ${name}_old
      }
      
      if {$allowQuestionMarks > 0} {
        if {[regexp {^\?*$} [set $name]]} {
          # Accept this value:
          set $old [set $name]
          return
        }
      }
      
      # Only non-negative integers up to maxValue are allowed, unless the
      # value is negative:
      set allowNegatives 0
      if {$maxValue < 0} {
        set allowNegatives 1
        set maxValue [expr {0 - $maxValue}]
      }
      
      if {$allowNegatives} {
        if {![regexp {^[-+]?[0-9]*$} [set $name]]} {
          set $name [set $old]
          bell
          return
        }
      } else {
        if {![regexp {^[+]?[0-9]*$} [set $name]]} {
          set $name [set $old]
          bell
          return
        }
      }
      if {[set $name] > $maxValue} {
        set $name [set $old]
        bell
        return
      }
      set $old [set $name]
    }
    
    # ::utils::validate::Date
    #
    #    Used to check the validity of a date string as it is entered.
    #
    proc Date {name el op} {
      global $name ${name}_old
      set old ${name}_old
      if {![sc_info validDate [set $name]]} {
        set $name [set $old]
        bell
        return
      }
      set $old [set $name]
    }
    
    # ::utils::validate::Result
    #
    #    Used to check the validity of a result entrybox value.
    #    Result can be empty, "1", "0", "=", or "*".
    #
    proc Result {name el op} {
      global $name ${name}_old
      set old ${name}_old
      if {![regexp {^[1|0|=|\*]?$} [set $name]]} {
        set $name [set $old]
        bell
        return
      }
      set $old [set $name]
    }
    
    # ::utils::validate::Alpha
    #
    #    Used to check that an entrybox contains only letters.
    #
    proc Alpha {name el op} {
      global $name ${name}_old
      set old ${name}_old
      if {![regexp {^[A-Za-z]*$} [set $name]]} {
        set $name [set $old]
        bell
        return
      }
      set $old [set $name]
      
    }
    
    # ::utils::validate::Regexp
    #
    #    Used to check the validity of an entrybox given a regular expression.
    #    Used to verify a file is "a-h", for example.
    #
    proc Regexp {expression name el op} {
      global $name ${name}_old
      set old ${name}_old
      if {![regexp $expression [set $name]]} {
        set $name [set $old]
        bell
        return
      }
      set $old [set $name]
    }
  }
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges

################################################################################
#                     F O N T S
################################################################################
set fontsize 6 ;# [font configure font_Regular -size]
set family1 "Tahoma"
set family2 "Courier New"
font create font_Bold -family $family2 -size $fontsize -weight bold
font create font_BoldItalic -family $family2 -size $fontsize -weight bold -slant italic
font create font_Italic -family $family2 -size $fontsize -slant italic
font create font_H1 -family $family2 -size [expr {$fontsize + 4} ] -weight bold
font create font_H2 -family $family2 -size [expr {$fontsize + 3} ] -weight bold
font create font_H3 -family $family2 -size [expr {$fontsize + 2} ] -weight bold
font create font_H4 -family $family2 -size [expr {$fontsize + 1} ] -weight bold
font create font_H5 -family $family2 -size [expr {$fontsize + 0} ] -weight bold

font create font_SmallBold -family $family2 -size $fontsize -weight bold
font create font_SmallItalic -family $family2 -size $fontsize -slant italic
font create font_Regular -family "Courier New" -size 8 -weight normal -slant roman
font create font_Menu -family "Courier New" -size 8 -weight normal -slant roman
font create font_Small -family "Courier New" -size 8 -weight normal -slant roman
font create font_Tiny -family "Courier New" -size 7 -weight normal -slant roman
font create font_Fixed -family "Courier New" -size 8 -weight normal -slant roman

################################################################################
# vwait but will timeout after a delay. Var must be fully qualified (::)
################################################################################
proc vwaitTimed { var {delay 0} {warn "warnuser"} } {
  
  proc trigger {var warn} {
    if {$warn == "warnuser"} {
      tk_messageBox -type ok -icon error -parent . -title "Protocol error" -message "vwait timeout for $var"
    }
    set $var 1
  }
  
  if { $delay != 0 } {
    set timerId [after $delay "trigger $var $warn"]
  }
  
  vwait $var
  
  if [info exists timerId] { after cancel $timerId }
  
}
################################################################################
#  Translation
################################################################################
array set transPieces {}

set transPieces(french) { P P K R Q D R T B F N C }
set untransPieces(french) { P P R K D Q T R F B C N }
set transPieces(spanish) { P P K R Q D R T B A N C }
set untransPieces(spanish) { P P R K D Q T R A B C N }

################################################################################
proc trans { msg } {
  if { $::options(language) == "english" } {
    return $msg
  }
  set t [string map $::transPieces($::options(language)) $msg ]
  return $t
}
################################################################################
proc untrans { msg } {
  if { $::options(language) == "english" } {
    return $msg
  }
  set t [string map $::untransPieces($::options(language)) $msg ]
  return $t
}

################################################################################
#
################################################################################

# dialogbuttonframe:
#   Creates a frame that will be shown at the bottom of a
#   dialog window. It takes two parameters: the frame widget
#   name to create, and a list of button args. Each element
#   should contain a widget name, and button arguments.
#
proc dialogbuttonframe {frame buttonlist} {
  frame $frame
  set bnames {}
  set maxlength 0
  foreach buttonargs $buttonlist {
    set bname $frame.[lindex $buttonargs 0]
    set bargs [lrange $buttonargs 1 end]
    eval button $bname $bargs
    set bnames [linsert $bnames 0 $bname]
    set length [string length [$bname cget -text]]
    if {$length > $maxlength} { set length $maxlength}
  }
  if {$maxlength < 7} { set maxlength 7 }
  foreach b $bnames {
    $b configure -width $maxlength -padx 4
    pack $b -side right -padx 4 -pady 4
  }
}

# packbuttons
#   Packs a row of dialog buttons to the left/right of their frame
#   with a standard amount of padding.
#
proc packbuttons {side args} {
  eval pack $args -side $side -padx 5 -pady 3
}

# dialogbutton:
#   Creates a button that will be shown in a dialog box, so it
#   is given a minumin width.
#
proc dialogbutton {w args} {
  set retval [eval button $w $args]
  set length [string length [$w cget -text]]
  if {$length < 7} { set length 7 }
  $w configure -width $length -pady 1
  return retval
}

# autoscrollframe
#   Creates and returns a frame containing a widget which is gridded
#   with scrollbars that automatically hide themselves when they are
#   not needed.
#   The frame and widget may already exist; they are created if needed.
#   Usage:
#      autoscrolltext [-bars none|x|y|both] frame type w args
#
proc autoscrollframe {args} {
  global _autoscroll
  set bars both
  if {[lindex $args 0] == "-bars"} {
    set bars [lindex $args 1]
    if {$bars != "x" && $bars != "y" && $bars != "none" && $bars != "both"} {
      return -code error "Invalid parameter: -bars $bars"
    }
    set args [lrange $args 2 end]
  }
  if {[llength $args] < 3} {
    return -code error "Insufficient number of parameters"
  }
  set frame [lindex $args 0]
  set type [lindex $args 1]
  set w [lindex $args 2]
  set args [lrange $args 3 end]
  
  set retval $frame
  if {! [winfo exists $frame]} { frame $frame }
  $frame configure -relief sunken -borderwidth 2
  if {! [winfo exists $w]} {
    $type $w
  }
  if {[llength $args] > 0} {
    eval $w configure $args
  }
  $w configure -relief flat -borderwidth 0
  grid $w -in $frame -row 0 -column 0 -sticky news
  set setgrid 0
  catch {set setgrid [$w cget -setgrid]}
  
  if {$bars == "y"  ||  $bars == "both"} {
    ttk::scrollbar $frame.ybar -command [list $w yview] -takefocus 0 -borderwidth 1
    $w configure -yscrollcommand [list _autoscroll $frame.ybar]
    grid $frame.ybar -row 0 -column 1 -sticky ns
    set _autoscroll($frame.ybar) 1
    set _autoscroll(time:$frame.ybar) [clock clicks -milli]
    if {! $setgrid} {
      # bind $frame.ybar <Map> [list _autoscrollMap $frame]
    }
  }
  if {$bars == "x"  ||  $bars == "both"} {
    ttk::scrollbar $frame.xbar -command [list $w xview] -takefocus 0 \
        -borderwidth 1 -orient horizontal
    $w configure -xscrollcommand [list _autoscroll $frame.xbar]
    grid $frame.xbar -row 1 -column 0 -sticky we
    set _autoscroll($frame.xbar) 1
    set _autoscroll(time:$frame.xbar) [clock clicks -milli]
    if {! $setgrid} {
      # bind $frame.xbar <Map> [list _autoscrollMap $frame]
    }
  }
  grid rowconfigure $frame 0 -weight 1
  grid columnconfigure $frame 0 -weight 1
  grid rowconfigure $frame 1 -weight 0
  grid columnconfigure $frame 1 -weight 0
  return $retval
}

array set _autoscroll {}

# _autoscroll
#   This is the "set" command called for auto-scrollbars.
#   If the bar is shown but should not be, it is hidden.
#   If the bar is hidden but should be shown, it is redrawn.
#   Note that once a bar is shown, it will not be removed again for
#   at least a few milliseconds; this is to overcome problematic
#   interactions between the x and y scrollbars where hiding one
#   causes the other to be shown etc. This usually happens because
#   the stupid Tcl/Tk text widget doesn't handle scrollbars well.
#
proc _autoscroll {bar args} {
  global _autoscroll
  if {[llength $args] == 2} {
    set min [lindex $args 0]
    set max [lindex $args 1]
    if {$min > 0.0  ||  $max < 1.0} {
      if {! $_autoscroll($bar)} {
        grid configure $bar
        set _autoscroll($bar) 1
        set _autoscroll(time:$bar) [clock clicks -milli]
      }
    } else {
      if {[clock clicks -milli] > [expr {$_autoscroll(time:$bar) + 100}]} {
        grid remove $bar
        set _autoscroll($bar) 0
      }
    }
    # update idletasks
  }
  eval $bar set $args
}

proc _autoscrollMap {frame} {
  # wm geometry [winfo toplevel $frame] [wm geometry [winfo toplevel $frame]]
}


# busyCursor, unbusyCursor:
#   Sets all cursors to watch (indicating busy) or back to their normal
#   setting again.

array set scid_busycursor {}
set scid_busycursorState 0

proc doBusyCursor {w flag} {
  global scid_busycursor
  if {! [winfo exists $w]} { return }
  
  if {$flag} {
    set scid_busycursor($w) [$w cget -cursor]
    catch {$w configure -cursor watch}
  } else {
    catch {$w configure -cursor $scid_busycursor($w)} err
  }
  foreach i [winfo children $w] { doBusyCursor $i $flag }
}

proc busyCursor {w {flag 1}} {
  global scid_busycursor scid_busycursorState
  if {$scid_busycursorState == $flag} { return }
  set scid_busycursorState $flag
  doBusyCursor $w $flag
}

proc unbusyCursor {w} {busyCursor $w 0}


# addHorizontalRule, addVerticalRule
#   Add a horizontal/vertical rule frame to a window.
#   The optional parameters [x/y]padding and sunken allow the spacing and
#   appearance of the rule to be specified.
#
set horizRuleCounter 0
set vertRuleCounter 0

proc addHorizontalRule {w {ypadding 5} {relief sunken} {height 2} } {
  global horizRuleCounter
  set f [ frame $w.line$horizRuleCounter -height $height -borderwidth 2 \
      -relief $relief -background white ]
  pack $f -fill x -pady $ypadding
  incr horizRuleCounter
  return $f
}

proc addVerticalRule {w {xpadding 5} {relief sunken}} {
  global vertRuleCounter
  set f [ frame $w.line$vertRuleCounter -width 2 -borderwidth 2 \
      -relief $relief -background white ]
  pack $f -fill y -padx $xpadding -side left
  incr vertRuleCounter
  return $f
}


# progressWindow:
#   Creates a window with a label, progress bar, and (if specified),
#   a cancel button and cancellation command.
#
proc progressWindow {args} {
  set w .progressWin
  if {[winfo exists $w]} { return }
  toplevel $w
  wm withdraw $w
  wm resizable $w 0 0
  if {[llength $args] == 2} {
    set title [lindex $args 0]
    set text [lindex $args 1]
    set b 0
  } elseif {[llength $args] == 4} {
    set title [lindex $args 0]
    set text [lindex $args 1]
    set button [lindex $args 2]
    set command [lindex $args 3]
    set b 1
  } else { return }
  wm title $w $title
  label $w.t -text $text
  pack $w.t -side top
  canvas $w.c -width 300 -height 20 -bg white -relief solid -border 1
  $w.c create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
  $w.c create text 295 10 -anchor e -font font_Regular -tags time \
      -fill black -text "0:00 / 0:00"
  pack $w.c -side top -pady 10
  if {$b} {
    pack [frame $w.b] -side bottom -fill x
    button $w.b.cancel -text $button -command $command
    pack $w.b.cancel -side right -padx 5 -pady 2
  }
  # Set up geometry for middle of screen:
  set x [winfo screenwidth $w]
  set x [expr {$x - 300} ]
  set x [expr {$x / 2} ]
  set y [winfo screenheight $w]
  set y [expr {$y - 20} ]
  set y [expr {$y / 2} ]
  wm geometry $w +$x+$y
  sc_progressBar $w.c bar 301 21 time
  update idletasks
  wm deiconify $w
  raise $w
  if {$b} {
    grab $w.b.cancel
  } else {
    grab $w
  }
  bind $w <Visibility> "raise $w"
  set ::progressWin_time [clock seconds]
}

proc leftJustifyProgressWindow {} {
  set w .progressWin
  if {! [winfo exists $w]} { return }
  pack configure $w.t -fill x
  $w.t configure -width 1 -anchor w
}

proc changeProgressWindow {newtext} {
  set w .progressWin
  if {[winfo exists $w]} {
    $w.t configure -text $newtext
    update idletasks
  }
}

proc resetProgressWindow {} {
  set w .progressWin
  set ::progressWin_time [clock seconds]
  if {[winfo exists $w]} {
    $w.c coords bar 0 0 0 0
    $w.c itemconfigure time -text "0:00 / 0:00"
    update idletasks
  }
}

proc updateProgressWindow {done total} {
  set w .progressWin
  if {! [winfo exists $w]} { return }
  set elapsed [expr {[clock seconds] - $::progressWin_time}]
  set width 401
  if {$total > 0} {
    set width [expr {int(double($width) * double($done) / double($total))}]
  }
  $w.c coords bar 0 0 $width 21
  set estimated $elapsed
  if {$done != 0} {
    set estimated [expr {int(double($elapsed) * double($total) / double($done))}]
  }
  set t [format "%d:%02d / %d:%02d" \
      [expr {$elapsed / 60}] [expr {$elapsed % 60}] \
      [expr {$estimated / 60}] [expr {$estimated % 60}]]
  $w.c itemconfigure time -text $t
  update
}

proc closeProgressWindow {} {
  set w .progressWin
  if {! [winfo exists $w]} {
    # puts stderr "Hmm, no progress window -- bug?"
    return
  }
  grab release $w
  destroy $w
}
################################################################################
# clock widget (0.1 sec precision) dsec = deci-second
################################################################################
namespace eval gameclock {
  array set data {}
  
  ################################################################################
  proc new { parent n {showfall 0} } {
    global ::gameclock::data
    set data(showfallen$n) $showfall
    set data(id$n) $parent.clock$n
    if {![winfo exists $data(id$n)]} {
      label $data(id$n) -background white -foreground black -font font_Small
      pack $data(id$n) -side top -anchor w
    }
    set data(running$n) 0
    set data(fg$n) "black"
    # egt = elapsed game time (in chunck of 200 ms)
    set data(egt$n) 0
    ::gameclock::reset $n
    ::gameclock::draw $n
    bind $data(id$n) <Button-1> "::gameclock::toggleClock $n"
  }
  ################################################################################
  proc delete { n } {
    global ::gameclock::data
    stop $n
    destroy $data(id$n)
    set w .fEngine.text
    set del [$w tag nextrange clock$n 1.0]
    catch {$w delete [lindex $del 0] [lindex $del 1]}
  }
  ################################################################################
  proc draw { n } {
    global ::gameclock::data
    if {! [winfo exists $data(id$n)]} { return }
    
    set sec [ expr int($data(counter$n) /10)]
    if { $sec < 0 && $data(showfallen$n) } {
      set color "red"
    } else  {
      set color $::gameclock::data(fg$n)
    }
    set m [expr int($sec/60.0)]
    set s [expr abs($sec) % 60 ]
    $data(id$n) configure -text [format "%02d:%02d" $m $s] -foreground $color
    
    # display clocks even in full screen mode
    if { $::options(fullBoard) } {
      set w .fEngine.text
      set del [$w tag nextrange clock$n 1.0]
      catch {$w delete [lindex $del 0] [lindex $del 1]}
      if {$n == 1} {
        $w insert 1.0 [format " %02d:%02d " $m $s] clock1
      } else {
        set loc [lindex [$w tag nextrange clock1 1.0] 1]
        if {$loc == "" } { set loc 1.0 }
        $w insert $loc [format " %02d:%02d " $m $s] clock2
      }
    }
  }
  ################################################################################
  proc every {ms body n} {
    
    set delta 2
    
    if { $::tournamentMode && $::options(engine_timeMode) == "timebonus" } {
      set delta -2
    }
    if { ! $::tournamentMode && $::options(game_timeMode) == "timebonus" } {
      set delta -2
    }
    incr ::gameclock::data(egt$n)
    incr ::gameclock::data(counter$n) $delta
    
    if {[expr { $::gameclock::data(counter$n) % 10 } ] == 0} {
      eval $body
    }
    if {[winfo exists $::gameclock::data(id$n)]} {
      after $ms [info level 0]
    }
  }
  ################################################################################
  proc getdSec { n } {
    return $::gameclock::data(counter$n)
  }
  ################################################################################
  proc setdSec { n value } {
    set ::gameclock::data(counter$n) $value
    ::gameclock::draw $n
  }
  ################################################################################
  # returns EGT in format h:mm:ss
  proc getEgt { n } {
    set egt [ expr $::gameclock::data(egt$n) / 5 ]
    set h [ expr $egt / 3600 ]
    set egt [ expr $egt - $h * 3600 ]
    set m [ expr $egt / 60 ]
    set s [ expr $egt - $m * 60 ]
    set m [ format "%02i" $m ]
    set s [ format "%02i" $s ]    
    return "$h:$m:$s"
  }
  ################################################################################
  # add seconds
  proc add { n value } {
    set ::gameclock::data(counter$n) [ expr $::gameclock::data(counter$n) + $value * 10 ]
    ::gameclock::draw $n
  }
  
  ################################################################################
  proc reset { n } {
    ::gameclock::stop $n
    set ::gameclock::data(counter$n) 0
  }
  ################################################################################
  proc start { n } {
    if {$::gameclock::data(running$n)} { return }
    set ::gameclock::data(running$n) 1
    ::gameclock::every 200 "draw $n" $n
  }
  ################################################################################
  proc stop { n } {
    if {! $::gameclock::data(running$n)} { return }
    set ::gameclock::data(running$n) 0
    after cancel "::gameclock::every 200 \{draw $n\} $n"
  }
  ################################################################################
  proc toggleClock { n } {
    if { $::gameclock::data(running$n) } {
      stop $n
    } else  {
      start $n
    }
  }
  ################################################################################
  proc setColor { n color } {
    if {$color == "white"} {
      set fg "black"
      set bg "white"
    } else {
      set fg "white"
      set bg "black"
    }
    set ::gameclock::data(fg$n) $fg
    $::gameclock::data(id$n) configure -background $bg -foreground $fg
  }
}
################################################################################
# html generation
################################################################################
namespace eval html {
  set data {}
  set idx 0
  set black_square "#7389b6"
  set white_square "#f3f3f3"
  
  ################################################################################
  proc exportCurrentFilter {} {
    # Check that we have some games to export:
    if {![sc_base inUse]} {
      tk_messageBox -title "Scid: Empty database" -type ok -icon info \
          -message "This is an empty database, there are no games to export."
      return
    }
    if {[sc_filter count] == 0} {
      tk_messageBox -title "Scid: Filter empty" -type ok -icon info \
          -message "The filter contains no games."
      return
    }
    set ftype {
      { "HTML files" {".html" ".htm"} }
      { "All files" {"*"} }
    }
    set idir $::initialDir(html)
    set fName [tk_getSaveFile -initialdir $idir -filetypes $ftype -defaultextension ".html" -title "Create an HTML file"]
    if {$fName == ""} { return }
    set prefix [file rootname [file tail $fName] ]
    set dirtarget [file dirname $fName]
    set sourcedir [file join $::scidExeDir html]
    catch {file copy -force [file join $sourcedir bitmaps] $dirtarget}
    catch {file copy -force [file join $sourcedir scid.js] $dirtarget}
    catch {file copy -force [file join $sourcedir scid.css] $dirtarget}
    writeIndex "[file join $dirtarget $prefix].html" $prefix
    progressWindow "Scid" "Exporting games..." "Cancel" "sc_progressBar"
    busyCursor .
    set savedGameNum [sc_game number]
    set gn [sc_filter first]
    set players {}
    set ::html::cancelHTML 0
    set idx 1
    set total [sc_filter count]
    
    while {$gn != 0 && ! $::html::cancelHTML} {
      updateProgressWindow $idx $total
      sc_game load $gn
      fillData
      set pl "[sc_game tags get White] - [sc_game tags get Black]"
      lappend players $pl
      toHtml $::html::data $idx $dirtarget $prefix $pl [sc_game tags get "Event"] [sc_game tags get "ECO"] [sc_game info result] [sc_game tags get "Date"]
      set gn [sc_filter next]
      incr idx
    }
    
    navhtml $dirtarget $players $prefix
    closeProgressWindow
    unbusyCursor .
    exportPGN "[file join $dirtarget $prefix].pgn" "filter"
    sc_game load $savedGameNum
  }
  ################################################################################
  proc sc_progressBar {} {
    set ::html::cancelHTML 1
  }
  ################################################################################
  proc exportCurrentGame {} {
    
    set ftype {
      { "HTML files" {".html" ".htm"} }
      { "All files" {"*"} }
    }
    set idir $::initialDir(html)
    set fName [tk_getSaveFile -initialdir $idir -filetypes $ftype -defaultextension ".html" -title "Create an HTML file"]
    if {$fName == ""} { return }
    set prefix [file rootname [file tail $fName] ]
    set dirtarget [file dirname $fName]
    set sourcedir [file join $::scidExeDir html]
    catch { file copy -force [file join $sourcedir bitmaps] $dirtarget }
    catch { file copy -force [file join $sourcedir scid.js] $dirtarget }
    catch { file copy -force [file join $sourcedir scid.css] $dirtarget }
    writeIndex "[file join $dirtarget $prefix].html" $prefix
    
    fillData
    set players [list "[sc_game tags get White] - [sc_game tags get Black]"]
    navhtml $dirtarget $players $prefix
    toHtml $::html::data 1 $dirtarget $prefix $players \
        [sc_game tags get "Event"] [sc_game tags get "ECO"] \
        [sc_game info result] [sc_game tags get "Date"]
    exportPGN "[file join $dirtarget $prefix].pgn" "current"
  }
  
  ################################################################################
  proc toHtml { dt game dirtarget prefix {players ""} {event ""} {eco "ECO"} {result "*"} {date ""} } {
    set f [open "[file join $dirtarget $prefix]_${game}.html" w]
    # header
    puts $f "<html>"
    puts $f "<head>"
    puts $f "<meta content=\"text/html; charset=ISO-8859-1\" http-equiv=\"content-type\">"
    puts $f "<title>Scid</title>"
    puts $f "<meta content=\"Scid\" name=\"author\">"
    puts $f "<link rel=\"stylesheet\" type=\"text/css\" href=\"scid.css\">"
    puts $f "<script SRC=\"scid.js\" LANGUAGE=\"JavaScript1.1\"></script>"
    puts $f "</head>"
    puts $f "<body ONLOAD=\"doinit()\" TEXT=\"#000000\" LINK=\"#000000\" VLINK=\"#000000\" ALINK=\"#000000\" BGCOLOR=\"#ECECEC\" onKeyDown=\"handlekey(event)\">"
    puts $f "<p>"
    puts $f "<font COLOR=\"#000000\">"
    puts $f "<script LANGUAGE=\"JavaScript1.1\">"
    puts $f "<!--"
    puts $f "movesArray = new Array("
    for {set i 0} {$i<[llength $dt]} {incr i} {
      array set elt [lindex $dt $i]
      puts -nonewline $f "\"$elt(fen) $elt(prev) $elt(next)\""
      if {$i < [expr [llength $dt] -1]} { puts $f "," }
    }
    puts $f ");"
    puts $f "var current = 0;"
    puts $f "var prefix = \"$prefix\";"
    puts $f "//-->"
    puts $f "</script>"
    puts $f "<NOSCRIPT>You need to have Javascript enabled in your browser to see this page.</NOSCRIPT>"
    # game header
    puts $f "<span class=\"hPlayers\">$players</span>"
    puts $f "<span class=\"hEvent\"><br>$event</span>"
    puts $f "<span class=\"hAnnot\"><br>\[$eco\]</span>"
    puts $f "<span class=\"hEvent\"><br>\[$date\]</span>"
    puts $f "<br>"
    
    # link moves
    set prevdepth 0
    set prevvarnumber 0
    for {set i 1} {$i<[llength $dt]} {incr i} {
      array set elt [lindex $dt $i]
      if {$elt(depth) == 0} {
        set class "V0"
      } elseif {$elt(depth) == 1} {
        set class "V1"
      } else  {
        set class "V2"
      }
      if {$prevdepth != $elt(depth) || $prevvarnumber != $elt(var)} {
        if {$prevdepth != 0} { puts $f "\]" }
        puts $f "<br>"
        for {set j 0} {$j<$elt(depth)} {incr j} {puts $f "&nbsp; &nbsp; "}
        if {$elt(depth) != 0} { puts $f "\[" }
      }
      set prevdepth $elt(depth)
      set prevvarnumber $elt(var)
      puts $f "<a href=\"javascript:gotoMove($elt(idx))\" ID=\"$elt(idx)\" class=\"$class\">$elt(move)</a>$elt(nag) $elt(comment)"
      if {$elt(diag)} {
        insertMiniDiag $elt(fen) $f
      }
    }
    if {$prevdepth != 0} {puts $f "\]"}
    
    # <a href="javascript:gotoMove(1)" ID="1" class="V0">1.Rd8</a>
    puts $f "<br><class=\"VH\">$result"
    puts $f "</font>"
    puts $f "</p>"
    puts $f "<font size=-2><a href=\"http://prolinux.free.fr/scid/\" target=_blank>Created with Scid</a></font>"
    puts $f "</body>"
    puts $f "</html>"
    close $f
  }
  ################################################################################
  proc colorSq {sq} {
    if { [expr $sq % 2] == 1 && [expr int($sq / 8) %2 ] == 0 || [expr $sq % 2] == 0 && [expr int($sq / 8) %2 ] == 1 } {
      return $::html::black_square
    } else {
      return $::html::white_square
    }
  }
  ################################################################################
  proc piece2gif {piece} {
    if {$piece == "K"} { return "wk" }
    if {$piece == "k"} { return "bk" }
    if {$piece == "Q"} { return "wq" }
    if {$piece == "q"} { return "bq" }
    if {$piece == "R"} { return "wr" }
    if {$piece == "r"} { return "br" }
    if {$piece == "B"} { return "wb" }
    if {$piece == "b"} { return "bb" }
    if {$piece == "N"} { return "wn" }
    if {$piece == "n"} { return "bn" }
    if {$piece == "P"} { return "wp" }
    if {$piece == "p"} { return "bp" }
    if {$piece == " "} { return "sq" }
  }
  ################################################################################
  proc insertMiniDiag {fen f} {
    
    set square 0
    set space " "
    puts $f "<table Border=0 CellSpacing=0 CellPadding=0><tr>"
    
    for {set i 0} {$i < [string length $fen]} {incr i} {
      set l [string range $fen $i $i ]
      set res [scan $l "%d" c]
      if {$res == 1} {
        if  { $c >= 1 && $c <= 8 } {
          for { set j 0} {$j < $c} {incr j} {
            puts $f "<td bgcolor= [colorSq $square ] ><img border=0 src=bitmaps/mini/[piece2gif $space].gif </td>"
            incr square
          }
        }
      } elseif {$l == "/"}  {
        puts $f "</tr><tr>"
      } else  {
        puts $f "<td bgcolor= [colorSq $square ] ><img border=0 src=bitmaps/mini/[piece2gif $l].gif </td>"
        incr square
      }
    }
    
    puts $f "</tr></table>"
    puts $f "</body></html>"
  }
  
  ################################################################################
  # generate nav.html
  proc navhtml { dirtarget players prefix } {
    set f [open "[file join $dirtarget ${prefix}_nav.html]" w]
    puts $f "<body BGCOLOR=\"#d7d7d7\">"
    puts $f "<table ALIGN='CENTER'>"
    puts $f "<td VALIGN='TOP'>"
    puts $f "<center>"
    puts $f "<form NAME='formgames'>"
    puts $f "<input TYPE='button' VALUE=' o ' ONCLICK='parent.moves.rotate()'>"
    puts $f "<input TYPE='button' VALUE=' |&lt; ' ONCLICK='parent.moves.jump(0)'>"
    puts $f "<input TYPE='button' VALUE=' &lt; '  ONCLICK='parent.moves.moveForward(0)'>"
    puts $f "<input TYPE='button' VALUE=' &gt; '  ONCLICK='parent.moves.moveForward(1)'>"
    puts $f "<input TYPE='button' VALUE=' &gt;| ' ONCLICK='parent.moves.jump(1)'>"
    puts $f "</center>"
    puts $f "</td>"
    puts $f "</table>"
    
    puts $f "<center>"
    puts $f "<select NAME=\"gameselect\" ID=\"gameselect\" SIZE=1 WIDTH=244 ONCHANGE='parent.moves.gotogame()'>"
    set i 1
    foreach l $players {
      puts $f "<option>$i. $l"
      incr i
    }
    puts $f "</select>"
    puts $f "<nobr>"
    puts $f "<input TYPE=\"button\" VALUE=\"&lt;--\" ONCLICK=\"parent.moves.gotoprevgame()\">"
    puts $f "<input TYPE=\"button\" VALUE=\"--&gt;\" ONCLICK=\"parent.moves.gotonextgame()\">"
    puts $f "</nobr>"
    puts $f "</center>"
    puts $f "</form>"
    puts $f "<br><CENTER><a href=\"${prefix}.pgn\">${prefix}.pgn</a></CENTER>"
    puts $f "</body>"
    
    close $f
  }
  ################################################################################
  # fill data with { idx FEN prev next move nag comment depth }
  proc fillData {} {
    sc_info preMoveCmd {}
    set ::html::data {}
    set ::html::idx -1
    sc_move start
    parseGame
    sc_info preMoveCmd preMoveCommand
  }
  
  ################################################################################
  proc parseGame { {prev -2} {dots 0} } {
    global ::html::data ::html::idx
    
    while {1} {
      recordElt $prev $dots
      set prev -2
      set dots 0
      
      # handle variants
      if {[sc_var count]>0} {
        if { ![sc_pos isAt vend]} {
          sc_move forward
          recordElt
          set lastIdx $idx
          sc_move back
        }
        set dots 1
        for {set v 0} {$v<[sc_var count]} {incr v} {
          sc_var enter $v
          parseGame $idx $dots
          sc_var exit
        }
        if { ![sc_pos isAt vend] } { sc_move forward }
        #update the "next" token
        array set elt [lindex $data $lastIdx]
        set elt(next) [expr $idx + 1]
        lset data $lastIdx [array get elt]
        #update the "previous" token
        set prev $lastIdx
      }
      
      if {[sc_pos isAt vend]} { break }
      sc_move forward
    }
  }
  ################################################################################
  proc recordElt { {prev -2} {dots 0} } {
    global ::html::data ::html::idx
    
    array set elt {}
    
    incr idx
    set elt(idx) $idx
    set elt(fen) [lindex [split [sc_pos fen]] 0]
    if {$prev != -2} {
      set elt(prev) $prev
    } else  {
      set elt(prev) [expr $idx-1]
    }
    
    set nag [sc_pos getNags]
    if {$nag == "0"} { set nag "" }
    if {[string match "*D *" $nag] || [string match "*# *" $nag]} {
      set elt(diag) 1
    } else  {
      set elt(diag) 0
    }
    set nag [regsub -all "D " $nag "" ]
    set nag [regsub -all "# " $nag "" ]
    set elt(nag) $nag
    set comment [sc_pos getComment]
    set comment [regsub -all "\[\x5B\]%draw (.)+\[\x5D\]" $comment ""]
    set elt(comment) $comment
    set elt(depth) [sc_var level]
    set elt(var) [sc_var number]
    if {![sc_pos isAt vend]} {
      set elt(next) [expr $idx +1 ]
    } else  {
      set elt(next) -1
    }
    
    set m [sc_game info previousMove]
    set mn [sc_pos moveNumber]
    
    if {[sc_pos side] == "black"} {
      set elt(move) "$mn.$m"
    } else {
      if {$dots} {
        set elt(move) "[expr $mn -1]. ... $m"
      } else  {
        set elt(move) $m
      }
    }
    
    lappend ::html::data [array get elt]
    
  }
  
  ################################################################################
  proc writeIndex {fn prefix} {
    set f [open $fn w]
    puts $f "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
    puts $f "<html>"
    puts $f "<head>"
    puts $f "<meta content=\"text/html; charset=ISO-8859-1\" http-equiv=\"content-type\">"
    puts $f "<title>Scid</title>"
    puts $f "<meta content=\"Scid\" name=\"author\">"
    puts $f "</head>"
    puts $f "<frameset BORDER=\"0\" FRAMEBORDER=\"0\" FRAMESPACING=\"0\" COLS=\"380,*\">"
    puts $f "<frameset BORDER=\"0\" FRAMEBORDER=\"0\" FRAMESPACING=\"0\" ROWS=\"380,*\">"
    puts $f "<frame NAME=\"diagram\" SCROLLING=\"Auto\">"
    puts $f "<frame NAME=\"nav\" SRC=\"${prefix}_nav.html\" SCROLLING=\"Auto\">"
    puts $f "</frameset>"
    puts $f "<frame NAME=\"moves\" SRC=\"${prefix}_1.html\" SCROLLING=\"Auto\">"
    puts $f "</frameset>"
    puts $f "</html>"
    close $f
  }
  ################################################################################
  proc exportPGN { fName selection } {
    if {$selection == "filter"} {
      progressWindow "Scid" "Exporting games..." "Cancel" "sc_progressBar"
    }
    busyCursor .
    sc_base export $selection "PGN" $fName -append 0 -starttext "" -endtext "" -comments 1 -variations 1 \
        -space 1 -symbols 1 -indentC 0 -indentV 0 -column 0 -noMarkCodes 1 -convertNullMoves 1
    unbusyCursor .
    if {$selection == "filter"} {
      closeProgressWindow
    }
  }
  
}
################################################################################
proc saveOptions { } {
  if { [catch { set f [open $::scidConfigFile "w"] } ] } {
    tk_messageBox -title "Error" -type ok -icon warning -message "Scid: Unable to write file $::scidConfigFile"
    return
  }
  foreach i [ array names ::options ] {
    puts $f "set ::options($i) [list [set ::options($i)]]"
  }
  close $f
}

################################################################################
proc addRecentFile { fName } {
  set idx [lsearch $::options(recentBases) $fName]
  if { $idx == -1 } {
    set ::options(recentBases) [linsert $::options(recentBases) 0 $fName]
    if { [llength $::options(recentBases)] > $::options(maxRecentBases) } {
      set ::options(recentBases) [lreplace $::options(recentBases) $::options(maxRecentBases) end ]
    }
  }
}

################################################################################
#              S O U N D S
################################################################################
if {$WindowsCE} {
  namespace eval sound {
    ################################################################################
    proc playwav { f } {
      if { ! $::hasSound || ! $::options(sound) } { return }
      set f [file join $::instalDir "sounds" $f]
      wce play $f
      # evodio play -file $f
    }
    ################################################################################
    proc play {s} {
      playwav "$s.wav"
    }
  }
} else {
  namespace eval sound {
    proc play {s} {}
  }
}
################################################################################
#
################################################################################
# end of misc.tcl
### ScidPocket.
### Copyright (C) 2007  Pascal Georges
###################
# htext.tcl: Online help/hypertext display module for Scid
#
# The htext module implements html-like display in a text widget.
# It is used in Scid for the help and crosstable windows, and for
# the game information area.

namespace eval htext {
  set headingColor "\#990000"
  array set updates {}
  # ##################################################################
  proc updateRate {w rate} {
    set ::htext::updates($w) $rate
  }
  
  # ##################################################################
  proc init {w} {
    set cyan "\#007000"
    set maroon "\#990000"
    set green "darkgreen"
    
    set ::htext::updates($w) 100
    $w tag configure black -foreground black
    $w tag configure white -foreground white
    $w tag configure red -foreground red
    $w tag configure blue -foreground blue
    $w tag configure darkblue -foreground darkBlue
    $w tag configure green -foreground $green
    $w tag configure cyan -foreground $cyan
    $w tag configure yellow -foreground yellow
    $w tag configure maroon -foreground $maroon
    $w tag configure gray -foreground gray20
    
    $w tag configure bgBlack -background black
    $w tag configure bgWhite -background white
    $w tag configure bgRed -background red
    $w tag configure bgBlue -background blue
    $w tag configure bgLightBlue -background lightBlue
    $w tag configure bgGreen -background $green
    $w tag configure bgCyan -background $cyan
    $w tag configure bgYellow -background yellow
    
    $w tag configure tab -lmargin2 20
    $w tag configure li -lmargin2 20
    $w tag configure center -justify center
    
    if {[$w cget -font] == "font_Small"} {
      $w tag configure b -font font_SmallBold
      $w tag configure i -font font_SmallItalic
    } else {
      $w tag configure b -font font_Bold
      $w tag configure i -font font_Italic
    }
    $w tag configure bi -font font_BoldItalic
    $w tag configure tt -font font_Fixed
    $w tag configure u -underline 1
    $w tag configure h1 -font font_H1 -foreground $::htext::headingColor \
        -justify center
    $w tag configure h2 -font font_H2 -foreground $::htext::headingColor
    $w tag configure h3 -font font_H3 -foreground $::htext::headingColor
    $w tag configure h4 -font font_H4 -foreground $::htext::headingColor
    $w tag configure h5 -font font_H5 -foreground $::htext::headingColor
    $w tag configure footer -font font_Small -justify center
    
    $w tag configure term -font font_BoldItalic -foreground $::htext::headingColor
    $w tag configure menu -font font_Bold -foreground $cyan
    
    # PGN-window-specific tags:
    $w tag configure tag -foreground $::pgn::pgnColor(Header)
    if { $::pgn::boldMainLine } {
      $w tag configure nag -foreground $::pgn::pgnColor(Nag) -font font_Regular
      $w tag configure var -foreground $::pgn::pgnColor(Var) -font font_Regular
    } else {
      $w tag configure nag -foreground $::pgn::pgnColor(Nag)
      $w tag configure var -foreground $::pgn::pgnColor(Var)
    }
    $w tag configure ip1 -lmargin1 10 -lmargin2 10
    $w tag configure ip2 -lmargin1 20 -lmargin2 20
  }
  
  proc isStartTag {tagName} {
    return [expr {![strIsPrefix "/" $tagName]} ]
  }
  # ##################################################################
  proc isEndTag {tagName} {
    return [strIsPrefix "/" $tagName]
  }
  
  # ##################################################################
  proc isLinkTag {tagName} {
    return [strIsPrefix "a " $tagName]
  }
  
  # ##################################################################
  proc extractLinkName {tagName} {
    if {[::htext::isLinkTag $tagName]} {
      return [lindex [split [string range $tagName 2 end] " "] 0]
    }
    return ""
  }
  
  # ##################################################################
  proc extractSectionName {tagName} {
    if {[::htext::isLinkTag $tagName]} {
      return [lindex [split [string range $tagName 2 end] " "] 1]
    }
    return ""
  }
  
  set interrupt 0
  
  # ##################################################################
  proc display {w helptext {section ""} {fixed 1}} {
    global helpWin
    # set start [clock clicks -milli]
    set helpWin(Indent) 0
    set ::htext::interrupt 0
    $w mark set insert 0.0
    $w configure -state normal
    set linkName ""
    
    set count 0
    set str $helptext
    if {$fixed} {
      regsub -all "\n\n" $str "<p>" str
      regsub -all "\n" $str " " str
    } else {
      regsub -all "\[ \n\]+" $str " " str
      regsub -all ">\[ \n\]+" $str "> " str
      regsub -all "\[ \n\]+<" $str " <" str
    }
    set tagType ""
    set seePoint ""
    
    if {! [info exists ::htext::updates($w)]} {
      set ::htext::updates($w) 100
    }
    
    # Loop through the text finding the next formatting tag:
    
    while {1} {
      set startPos [string first "<" $str]
      if {$startPos < 0} { break }
      set endPos [string first ">" $str]
      if {$endPos < 1} { break }
      
      set tagName [string range $str [expr {$startPos + 1}] [expr {$endPos - 1}]]
      
      # Check if it is a starting tag (no "/" at the start):
      
      if {![strIsPrefix "/" $tagName]} {
        
        # Check if it is a link tag:
        if {[strIsPrefix "a " $tagName]} {
          set linkName [::htext::extractLinkName $tagName]
          set sectionName [::htext::extractSectionName $tagName]
          set linkTag "link ${linkName} ${sectionName}"
          set tagName "a"
          $w tag configure "$linkTag" -foreground blue -underline 1
          $w tag bind "$linkTag" <ButtonRelease-1> \
              "helpWindow $linkName $sectionName"
          $w tag bind $linkTag <Any-Enter> \
              "$w tag configure \"$linkTag\" -background yellow
          # $w configure -cursor hand2"
          $w tag bind $linkTag <Any-Leave> \
              "$w tag configure \"$linkTag\" -background {}
          # $w configure -cursor {}"
        } elseif {[strIsPrefix "url " $tagName]} {
          # Check if it is a URL tag:
          set urlName [string range $tagName 4 end]
          set urlTag "url $urlName"
          set tagName "url"
          $w tag configure "$urlTag" -foreground red -underline 1
          $w tag bind "$urlTag" <ButtonRelease-1> "openURL {$urlName}"
          $w tag bind $urlTag <Any-Enter> \
              "$w tag configure \"$urlTag\" -background yellow
          $w configure -cursor hand2"
          $w tag bind $urlTag <Any-Leave> \
              "$w tag configure \"$urlTag\" -background {}
          $w configure -cursor {}"
        } elseif {[strIsPrefix "run " $tagName]} {
          # Check if it is a Tcl command tag:
          set runName [string range $tagName 4 end]
          set runTag "run $runName"
          set tagName "run"
          $w tag bind "$runTag" <ButtonRelease-1> "catch {$runName}"
          $w tag bind $runTag <Any-Enter> \
              "$w tag configure \"$runTag\" -foreground yellow
          $w tag configure \"$runTag\" -background darkBlue
          $w configure -cursor hand2"
          $w tag bind $runTag <Any-Leave> \
              "$w tag configure \"$runTag\" -foreground {}
          $w tag configure \"$runTag\" -background {}
          $w configure -cursor {}"
        } elseif {[strIsPrefix "go " $tagName]} {
          # Check if it is a goto tag:
          set goName [string range $tagName 3 end]
          set goTag "go $goName"
          set tagName "go"
          $w tag bind "$goTag" <ButtonRelease-1> \
              "catch {$w see \[lindex \[$w tag nextrange $goName 1.0\] 0\]}"
          $w tag bind $goTag <Any-Enter> \
              "$w tag configure \"$goTag\" -foreground yellow
          $w tag configure \"$goTag\" -background maroon
          # $w configure -cursor hand2"
          $w tag bind $goTag <Any-Leave> \
              "$w tag configure \"$goTag\" -foreground {}
          $w tag configure \"$goTag\" -background {}
          # $w configure -cursor {}"
        } elseif {[strIsPrefix "pi " $tagName]} {
          # Check if it is a player info tag:
          set playerTag $tagName
          set playerName [string range $playerTag 3 end]
          set tagName "pi"
          $w tag configure "$playerTag" -foreground darkBlue
          $w tag bind "$playerTag" <ButtonRelease-1> "playerInfo \"$playerName\""
          $w tag bind $playerTag <Any-Enter> \
              "$w tag configure \"$playerTag\" -foreground yellow
          $w tag configure \"$playerTag\" -background darkBlue
          $w configure -cursor hand2"
          $w tag bind $playerTag <Any-Leave> \
              "$w tag configure \"$playerTag\" -foreground darkBlue
          $w tag configure \"$playerTag\" -background {}
          $w configure -cursor {}"
        } elseif {[strIsPrefix "g_" $tagName]} {
          # Check if it is a game-load tag:
          set gameTag $tagName
          set tagName "g"
          set gnum [string range $gameTag 2 end]
          set glCommand "::game::LoadMenu $w [sc_base current] $gnum %X %Y"
          $w tag bind $gameTag <ButtonPress-1> $glCommand
          $w tag bind $gameTag <ButtonPress-3> \
              "::gbrowser::new [sc_base current] $gnum"
          $w tag bind $gameTag <Any-Enter> \
              "$w tag configure $gameTag -foreground yellow
          $w tag configure $gameTag -background darkBlue
          $w configure -cursor hand2"
          $w tag bind $gameTag <Any-Leave> \
              "$w tag configure $gameTag -foreground {}
          $w tag configure $gameTag -background {}
          $w configure -cursor {}"
        } elseif {[strIsPrefix "m_" $tagName]} {
          # Check if it is a move tag:
          set moveTag $tagName
          set tagName "m"
          $w tag bind $moveTag <ButtonRelease-1> \
              "sc_move pgn [string range $moveTag 2 end]; ::board::updateBoard -nolastmove ; ::pgn::Refresh"
          # invoking contextual menu in PGN window
          $w tag bind $moveTag <ButtonPress-3> \
              "sc_move pgn [string range $moveTag 2 end]; ::board::updateBoard  -nolastmove ; ::pgn::Refresh"
          $w tag bind $moveTag <Any-Enter> \
              "$w tag configure $moveTag -underline 1
          $w configure -cursor hand2"
          $w tag bind $moveTag <Any-Leave> \
              "$w tag configure $moveTag -underline 0
          $w configure -cursor {}"
        } elseif {[strIsPrefix "c_" $tagName]} {
          # Check if it is a comment tag:
          set commentTag $tagName
          set tagName "c"
          if { $::pgn::boldMainLine } {
            $w tag configure $commentTag -foreground $::pgn::pgnColor(Comment) \
                -font font_Regular
          } else {
            $w tag configure $commentTag -foreground $::pgn::pgnColor(Comment)
          }
          $w tag bind $commentTag <ButtonRelease-1> \
              "sc_move pgn [string range $commentTag 2 end]; ::board::updateBoard -nolastmove ; ::pgn::Refresh"
          $w tag bind $commentTag <Any-Enter> \
              "$w tag configure $commentTag -underline 1
          $w configure -cursor hand2"
          $w tag bind $commentTag <Any-Leave> \
              "$w tag configure $commentTag -underline 0
          $w configure -cursor {}"
        }
        
        if {$tagName == "h1"} {$w insert end "\n"}
        
      }
      
      # Now insert the text up to the formatting tag:
      $w insert end [string range $str 0 [expr {$startPos - 1}]]
      
      # Check if it is a name tag matching the section we want:
      if {$section != ""  &&  [strIsPrefix "name " $tagName]} {
        set sect [string range $tagName 5 end]
        if {$section == $sect} { set seePoint [$w index insert] }
      }
      
      if {[string index $tagName 0] == "/"} {
        # Get rid of initial "/" character:
        set tagName [string range $tagName 1 end]
        switch -- $tagName {
          h1 - h2 - h3 - h4 - h5  {$w insert end "\n"}
        }
        if {$tagName == "p"} {$w insert end "\n"}
        #if {$tagName == "h1"} {$w insert end "\n"}
        if {$tagName == "menu"} {$w insert end "\]"}
        if {$tagName == "ul"} {
          incr helpWin(Indent) -4
          $w insert end "\n"
        }
        if {[info exists startIndex($tagName)]} {
          switch -- $tagName {
            a {$w tag add $linkTag $startIndex($tagName) [$w index insert]}
            g  {$w tag add $gameTag $startIndex($tagName) [$w index insert]}
            c  {$w tag add $commentTag $startIndex($tagName) [$w index insert]}
            m  {$w tag add $moveTag $startIndex($tagName) [$w index insert]}
            pi {$w tag add $playerTag $startIndex($tagName) [$w index insert]}
            url {$w tag add $urlTag $startIndex($tagName) [$w index insert]}
            run {$w tag add $runTag $startIndex($tagName) [$w index insert]}
            go {$w tag add $goTag $startIndex($tagName) [$w index insert]}
            default {$w tag add $tagName $startIndex($tagName) [$w index insert]}
          }
          unset startIndex($tagName)
        }
      } else {
        switch -- $tagName {
          ul {incr helpWin(Indent) 4}
          li {
            $w insert end "\n"
            for {set space 0} {$space < $helpWin(Indent)} {incr space} {
              $w insert end " "
            }
          }
          p  {$w insert end "\n"}
          br {$w insert end "\n"}
          q  {$w insert end "\""}
          lt {$w insert end "<"}
          gt {$w insert end ">"}
          h2 - h3 - h4 - h5  {$w insert end "\n"}
        }
        #Set the start index for this type of tag:
        set startIndex($tagName) [$w index insert]
        if {$tagName == "menu"} {$w insert end "\["}
      }
      
      # Check if it is an image or button tag:
      if {[strIsPrefix "img " $tagName]} {
        set imgName [string range $tagName 4 end]
        set winName $w.$imgName
        while {[winfo exists $winName]} { append winName a }
        label $winName -image $imgName -relief flat -borderwidth 0 -background white
        $w window create end -window $winName
      }
      if {[strIsPrefix "button " $tagName]} {
        set imgName [string range $tagName 7 end]
        set winName $w.$imgName
        while {[winfo exists $winName]} { append winName a }
        button $winName -image $imgName
        $w window create end -window $winName
      }
      if {[strIsPrefix "window " $tagName]} {
        set winName [string range $tagName 7 end]
        $w window create end -window $winName
      }
      
      # Now eliminate the processed text from the string:
      set str [string range $str [expr {$endPos + 1}] end]
      incr count
      if {$count == $::htext::updates($w)} { update idletasks; set count 1 }
      if {$::htext::interrupt} {
        $w configure -state disabled
        return
      }
    }
    
    # Now add any remaining text:
    if {! $::htext::interrupt} { $w insert end $str }
    
    if {$seePoint != ""} { $w yview $seePoint }
    $w configure -state disabled
    # set elapsed [expr {[clock clicks -milli] - $start}]
  }
  
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges

if {$WindowsCE} {package require wce}
################################################################################
#                            U I
################################################################################
namespace eval ui { }
################################################################################
proc ::ui::setFullSize {w {topmost 1} } {
  global ::board::width ::board::height
  
  if {$::WindowsCE} {
    bind $w <ConfigureRequest> {::etcl::autofit %W}
    bind $w <Expose> {::etcl::autofit %W}
    ::etcl::autofit $w
    return
  }
  
  $w configure -borderwidth 0 -highlightthickness 0
  
  set  h [expr $height - 20]
  
  wm geometry $w "=${width}x$h"
  if {$topmost} {
    ::ui::setTopmost $w
  }
}

################################################################################
# combobox has a bug when mapped in a topmost window, so inhibit it now
################################################################################
proc ::ui::setTopmost {w} {
  if {$::WindowsCE} {
    # wm attributes $w -topmost 1
  }
}
################################################################################
proc ::ui::CurrentBoard {} {
  ::search::board
}
################################################################################
proc ::ui::Header {} {
  ::search::header
}
################################################################################
proc ::ui::MaterialPattern {} {
  ::search::material
}
################################################################################
proc ::ui::ResetFilter {} {
  ::search::filter::reset
}
################################################################################
proc ::ui::CopyFilter {target} {
  ::base::copyFilter [sc_base current] $target
}
################################################################################
proc ::ui::NegateFilter {} {
  ::search::filter::negate
}
################################################################################
proc ::ui::FilterInfo {} {
  set stats [sc_filter stats all]
  set total [lindex $stats 0]
  set white [lindex $stats 1]
  set draw [lindex $stats 2]
  set black [lindex $stats 3]
  set wsc [lindex $stats 4]
  
  set msg "Filter : $total games\nWhite wins : $white\nBlack wins $black\nDraws : $draw\nWhite score : $wsc\n[sc_base numGames] games in total"
  tk_messageBox -type ok -icon info -parent . -title "Filter info" -message $msg
}
################################################################################
proc ::ui::showTree {} {
  if {$::options(showTree)} {
    ::infopanel::updateTreeText
  } else {
    ::infopanel::clearTreeText
    sc_tree free
  }
}
################################################################################
proc ::ui::updateMenus {} {
  for {set i 1} {$i < [sc_base count total]} {incr i} {
    set n [sc_base filename $i]
    if {$n != "\[empty\]"} {
      set n [ file root [file tail $n]]
    } else  {
      set n "\[[::msgcat::mc empty]\]"
    }
    .menu.switch entryconfigure [expr $i-1] -label "$i.$n"
    .menu.copy entryconfigure [expr $i-1] -label "$i.$n"
  }
  
  .menu.base.recent delete 0 end
  foreach name $::options(recentBases) {
    .menu.base.recent add command -label [file rootname [file tail $name]] -command  "::base::baseOpen [list $name]"
  }
}

################################################################################
proc ::ui::showBook {} {
  if {$::options(showBook)} {
    ::book::scBookOpen $::options(mainBook) $::book::bookSlot
    ::infopanel::updateBookText
    ::book::closeMainBook
  } else {
    ::book::closeMainBook
    ::infopanel::clearBookText
  }
}
################################################################################
proc ::ui::showAnalysis {} {
  if {$::options(showAnalysis)} {
    ::infopanel::updateAnalysis
  } else {
    ::infopanel::clearAnalysisText
  }
}
################################################################################
proc ::ui::showComments {} {
  if {$::options(showComments)} {
    ::infopanel::updateComments
  } else {
    ::infopanel::clearComments
  }
}
#####################################################
# set theme and correct widget size
proc ::ui::setTheme {} {
  
  ttk::style theme use $::options(theme)
  
  if {$::hires} {
    option add *Scrollbar.width 20
    ttk::style configure TScrollbar -width 20 -arrowsize 20
  } else  {
    ttk::style configure TButton -padding { 0 0 } -width 0
  }
}
################################################################################
proc ::ui::About {} {
  # set qinfor [sc_msg info 1 read]
  # set qinfow [sc_msg info 1 write]
  # set msg "read $qinfor\nwrite $qinfow"
  # DEBUG (loads all games of a DB in order to check it)
  # for {set i 0 } {$i < [sc_base numGames]} { incr i} {
  # ::game::gameLoadNextPrev next
  # update
  # while {![sc_pos isAt end]} {
  # ::board::Forward
  # update
  # }
  # }
  
  tk_messageBox -type ok -icon info -parent .fTop -title "About" -message \
      "Engine #1 : [::engine::getName 1]\nEngine #2 : [::engine::getName 2]\n$::scidppc_version\nScid Pocket is based on Scid\nhttp://scid.sourceforge.net\nReleased under GPL (free)\n[sc_info pocket]"
}

################################################################################
#               I N F O P A N E L
################################################################################
namespace eval infopanel { }

################################################################################
proc ::infopanel::init {} {
  set w .fEngine.text
  setFontSize
  $w configure -wrap $::options(wrap) -background grey94 ;#-setgrid 1
  $w tag configure enginescore -foreground red -background black -relief raised -border 1 -font fontanalysis
  $w tag configure enginedepth -foreground black -background white -font fontanalysis
  $w tag configure enginepv -foreground blue -background white -relief sunken -border 1 -font fontanalysis
  $w tag configure enginemultipv -foreground black -background white -font fontanalysis
  $w tag configure engineinfo -foreground yellow -background grey20 -font fontanalysis
  $w tag configure tree -foreground black -background seashell2 -font fontanalysis
  $w tag configure treeScore -foreground red -background seashell2  -font fontanalysis
  $w tag configure treeTitle -foreground seashell2 -background DodgerBlue4 -font fontanalysis
  $w tag configure treeTitleScore -foreground red -background DodgerBlue4 -font fontanalysis
  $w tag configure book -foreground white -background DarkRed -font fontanalysis
  $w tag configure commentstext -foreground black -background azure -font fontanalysis
  $w tag configure commentsnag -foreground blue -background azure -font fontanalysis
  $w tag configure clock1 -font fontanalysis -foreground black -background white
  $w tag configure clock2 -font fontanalysis -foreground black -background white
  
  ::ui::showComments
  ::ui::showBook
  ::ui::showTree
}
################################################################################
proc ::infopanel::setFontSize {} {
  catch { font delete fontanalysis }
  font create fontanalysis -size $::options(fontAnalysisSize) -family Courier
}
################################################################################
proc ::infopanel::updateAnalysis {{n 1}} {
  global ::engine::data
  
  # Don't update if tactical training is running
  if {$::tactics::running} {return}
  
  set w .fEngine.text
  clearAnalysisText
  if {!$::options(showAnalysis)} {
    $w insert end [::msgcat::mc "Analysis hidden"] enginescore
    return
  }
  set l [lindex $data(multipv$n) 0]
  if { $::options(engineExtendedInfo) } {
    set extendedInfo "($data(seldepth$n))-$data(currmove$n)-"
  } else {
    set extendedInfo ""
  }
  $w insert end "[lindex $l 0]$extendedInfo" enginedepth
  if { [lindex $l 1] != "" } {
    if { [expr abs([lindex $l 1])] == 327.0 && [lindex $l 3] != "" } {
      $w insert end " M [lindex $l 3]" enginescore
    } else {
      set sc ""
      catch { set sc [ format "%.1f" [lindex $l 1] ] }
      $w insert end " $sc" enginescore
    }
  }
  if {$::options(scoreOnly)} {
    return
  }
  set extendedInfo ""
  if { $::options(engineExtendedInfo) } {
    set extendedInfo " hash [expr round($data(hashfull$n)/10)]%"
  }
  $w insert end " [expr round($data(nodes$n)/1000)] kn $data(nps$n) nps [expr round($data(time$n)/1000)] s$extendedInfo" engineinfo
  $w insert end "\n" engineinfo
  
  $w insert end "[lindex $l 2]\n" enginepv
  for {set i 1} { $i < $::options(multipv)} {incr i} {
    set l [lindex $data(multipv$n) $i ]
    $w insert end "$i> [lindex $l 0] " enginemultipv
    set sc ""
    catch { set sc [ format "%.1f" [lindex $l 1] ] }
    $w insert end "$sc" enginescore
    $w insert end "[lindex $l 2]\n" enginemultipv
  }
}
################################################################################
proc ::infopanel::clearAnalysisText {} {
  set txt .fEngine.text
  foreach tag {enginescore enginedepth enginepv enginemultipv engineinfo tagonline} {
    while {1} {
      set del [$txt tag nextrange $tag 1.0]
      if {$del == ""} {break}
      catch {$txt delete [lindex $del 0] [lindex $del 1]}
    }
  }
}
################################################################################
if { [catch {package require http} ] } {
  set ::infopanel::online_available 0
} else  {
  set ::infopanel::online_available 1
}
set ::infopanel::url "http://k4it.de/egtb/fetch.php"
################################################################################
proc ::infopanel::onlineTB {} {
  set query [ ::http::formatQuery hook null action egtb fen [sc_pos fen] ]
  if {[catch {::http::geturl $::infopanel::url -timeout 5000 -query $query -command { ::infopanel::httpCallback }}] } {
    tk_messageBox -type ok -icon error -parent . -title "Network error" -message "Network failure"
  }
}
################################################################################
proc ::infopanel::updateOnlineTB {} {
  set txt .fEngine.text
  foreach tag {tagonline} {
    while {1} {
      set del [$txt tag nextrange $tag 1.0]
      if {$del == ""} {break}
      catch {$txt delete [lindex $del 0] [lindex $del 1]}
    }
  }
}
################################################################################
proc ::infopanel::httpCallback { token } {
  upvar #0 $token state
  
  set t .fEngine.text
  
  # delete previous online output
  ::infopanel::updateOnlineTB
  
  if {$state(status) != "ok"} {
    $t insert end $state(status) tagonline
    return
  }
  
  set b $state(body)
  set result ""
  
  if {[sc_pos side] == "black"} {
    set tmp ""
    set found 0
    foreach line [split $b "\n" ] {
      if {$line == "NEXTCOLOR"} {
        set found 1
        continue
      }
      if {$found} {
        append tmp "$line\n"
      }
    }
    set b $tmp
  }
  
  foreach line [split $b "\n" ] {
    if {$line == "NEXTCOLOR"} {
      break
    }
    if { $line == "No information available" } {
      append result "$line\n"
    }
    if {[string match "hook|null|value|*" $line]} {
      append result "Online : [string range $line 16 end ]\n"
      continue
    }
    if {[scan $line "%d-%d:%s" sq1 sq2 tmp] == 3} {
      set p1 [ string toupper [string index [sc_pos board] $sq1 ] ]
      set p2 [string index [sc_pos board] $sq2 ]
      set take ""
      if {$p2 != "."} {
        set take "x"
      }
      append result "$p1[::board::san $sq1]$take[::board::san $sq2] [string range $line [string first : $line] end]\n"
    }
  }
  
  ::http::cleanup state
  $t insert end $result tagonline
}
################################################################################
proc ::infopanel::updateComments {} {
  set txt .fEngine.text
  clearComments
  if {!$::options(showComments)} {
    return
  }
  set nags [sc_pos getNags]
  if {$nags == "0"} {set nags ""}
  set comment [sc_pos getComment]
  if {$nags == "" && $comment == ""} {return}
  $txt insert 1.0 "$nags" commentsnag " $comment\n" commentstext
}
################################################################################
proc ::infopanel::clearComments {} {
  set txt .fEngine.text
  foreach tag {commentstext commentsnag} {
    while {1} {
      set del [$txt tag nextrange $tag 1.0]
      if {$del == ""} {break}
      catch {$txt delete [lindex $del 0] [lindex $del 1]}
    }
  }
}
################################################################################
proc ::infopanel::clearTreeText {} {
  set txt .fEngine.text
  foreach tag {treeTitle tree treeScore treeTitleScore} {
    while {1} {
      set del [$txt tag nextrange $tag 1.0]
      if {$del == ""} {break}
      catch {$txt delete [lindex $del 0] [lindex $del 1]}
    }
  }
}
################################################################################
proc ::infopanel::getTreeLoc {} {
  set txt .fEngine.text
  # warning : enginedepth must be the first tag of analysis text
  set loc [lindex [$txt tag nextrange enginedepth 1.0] 0]
  if {$loc == "" } { set loc end }
  return $loc
}
################################################################################
proc ::infopanel::getBookLoc {} {
  set txt .fEngine.text
  set loc [lindex [$txt tag nextrange commentstext 1.0] 1]
  if {$loc == "" } { set loc [lindex [$txt tag nextrange commentsnag 1.0] 1] }
  if {$loc == "" } { set loc 1.0 }
  return $loc
}
################################################################################
# returns arg with spaces so its length is up to size
proc ::infopanel::padSpaces { arg size } {
  set spc " "
  set count [expr $size - [string length $arg]]
  if {$count < 0 } {set count 0}
  set tmp "$arg[string repeat $spc $count]"
  return $tmp
}
################################################################################
proc ::infopanel::updateTreeText {} {
  clearTreeText
  set txt .fEngine.text
  set base [sc_base current]
  if {![sc_base inUse $base] || [sc_base count total] == $base } {
    return
  }
  
  $txt insert [getTreeLoc] [::msgcat::mc "Move  Freq(games)  "] treeTitle
  $txt insert [getTreeLoc] [::msgcat::mc "Score " ] treeTitleScore
  $txt insert [getTreeLoc] [::msgcat::mc "AvElo Perf Draws\n"] treeTitle
  
  set tree [sc_tree search -hideMoves 0 -sort frequency -epd 0 -list 1 -base $base ]
  foreach l $tree {
    set lnum [lindex $l 0]
    set move [lindex $l 1]
    set mtot [lindex $l 3]
    set freq [lindex $l 4]
    set score [lindex $l 5]
    set averageElo [lindex $l 6]
    set perf [lindex $l 7]
    set year [lindex $l 8]
    set draws [lindex $l 9]
    if {$lnum != 0} {
      $txt insert [getTreeLoc] "$lnum:" tree
    }
    set tmp [padSpaces "$move" 3]
    $txt insert [getTreeLoc] "$tmp " tree
    set tmp [padSpaces "$freq%($mtot)" 12]
    $txt insert [getTreeLoc] "$tmp " tree
    set tmp [padSpaces "$score%" 5]
    $txt insert [getTreeLoc] "$tmp " treeScore
    set tmp [padSpaces "$averageElo" 4]
    $txt insert [getTreeLoc] "$tmp " tree
    set tmp [padSpaces "$perf" 4]
    $txt insert [getTreeLoc] "$tmp " tree
    set tmp [padSpaces "$draws" 2]
    $txt insert [getTreeLoc] "$tmp\n" tree
  }
}
################################################################################
proc ::infopanel::clearBookText {} {
  set txt .fEngine.text
  foreach tag {book} {
    while {1} {
      set del [$txt tag nextrange $tag 1.0]
      if {$del == ""} {break}
      catch {$txt delete [lindex $del 0] [lindex $del 1]}
    }
  }
}
################################################################################
# Book is the first info after (NAG,comment)
proc ::infopanel::updateBookText {} {
  global ::book::bookMoves
  clearBookText
  if { ! $::options(showBook) } {
    return
  }
  ::book::scBookOpen $::options(mainBook) $::book::bookSlot
  
  set txt .fEngine.text
  
  set bookMoves [sc_book moves $::book::bookSlot]
  set tmp ""
  
  for {set i 0} {$i<[llength $bookMoves]} {incr i 2} {
    set line [expr $i /2 +1]
    set m ""
    append m [lindex $bookMoves $i] " " [lindex $bookMoves [expr $i + 1] ] " "
    append tmp $m
  }
  append tmp "\n"
  
  $txt insert [getBookLoc] [::trans $tmp] book
  ::book::closeMainBook
}

################################################################################
#                         B O A R D
################################################################################
namespace eval board {
  set lightSquare "#f3f3f3"
  set darkSquare "#7389b6"
  set highlight "#f3f484"
  set fromSquare -1
  set pSize 0
  set pSize_lowres 20
  set pSize_hires 45
  set pSize_hires_fullsize 60
  set pSize_hires_setup 45
  set pSize_lowres_fullsize 30
  set pSize_lowres_setup 20
  set isFlipped 0
  set height -1
  set width -1
  array set previousBoard ""
  set lastMoveList {}
}
################################################################################
proc ::board::init {} {
  global ::board::height ::board::width ::board::pSize
  
  if {$::hires} {
    set pSize $::board::pSize_hires
    setPieceData Merida $::board::pSize_hires
    setPieceData Merida $::board::pSize_hires_fullsize
    setPieceData Merida $::board::pSize_lowres
  } else  {
    set pSize $::board::pSize_lowres
    setPieceData Merida $::board::pSize_lowres
    setPieceData Merida $::board::pSize_lowres_fullsize
  }
  
  # define screen resolution
  if {$::WindowsCE} {
    set height [lindex [wm maxsize . ] 1]
    set height [ expr $height - [wce captionheight] ]
    set width [lindex [wm maxsize . ] 0]
  } else  {
    if {$::hires} {
      set height 596
      set width 476
    } else  {
      set height 298
      set width 238
    }
  }
  
  ::ui::setFullSize . 0
  update idletasks
  wm title . "Scid"
  
  menuInit
  frame .fTop -borderwidth 0 -highlightthickness 0
  frame .fTop.fBoard -borderwidth 1 -highlightthickness 0 -relief raised
  frame .fTop.fr
  frame .fTop.fr.fCmd
  frame .fTop.fr.fclocks
  
  frame .fEngine
  text .fEngine.text -borderwidth 1 -relief sunken -font font_Tiny -setgrid 0 -yscrollcommand {.fEngine.sc set} -insertofftime 0
  ttk::scrollbar .fEngine.sc -orient vert -command {.fEngine.text yview}
  grid .fEngine.text -column 0 -row 0 -sticky nsew
  grid .fEngine.sc -column 1 -row 0  -sticky ns
  grid rowconfigure .fEngine 0 -weight 1
  grid columnconfigure .fEngine 0 -weight 1
  
  pack .fTop .fEngine -side top -fill x
  
  canvas .fTop.fBoard.bd -width [expr $pSize*8 ] -height [expr $pSize*8 ] -borderwidth 0 -highlightthickness 0 ; #-relief raised
  
  set bd .fTop.fBoard.bd
  
  grid $bd -row 1 -column 1 -rowspan 8 -columnspan 8
  
  # Create empty board:
  for {set i 0} {$i < 64} {incr i} {
    set xi [expr {$i % 8} ]
    set yi [expr { 7 - int($i/8)} ]
    set x1 [expr {$xi * $pSize +1 } ]
    set y1 [expr {$yi * $pSize +1 } ]
    set x2 [expr {$x1 + $pSize } ]
    set y2 [expr {$y1 + $pSize } ]
    $bd create rectangle $x1 $y1 $x2 $y2 -tag sq$i -width 0 -fill [defaultColor $i]
  }
  
  # Set up coordinate labels:
  for {set i 1} {$i <= 8} {incr i} {
    label .fTop.fBoard.lrank$i -text [expr {9 - $i}]
    grid .fTop.fBoard.lrank$i -row $i -column 0 -sticky e
  }
  
  foreach i {1 2 3 4 5 6 7 8} file {a b c d e f g h} {
    label .fTop.fBoard.bfile$file -text $file
    grid .fTop.fBoard.bfile$file -row 9 -column $i -sticky n
  }
  
  # bindings
  for {set i 0} {$i < 64} {incr i} {
    .fTop.fBoard.bd bind sq$i <ButtonPress-1> "::board::pressSquare $i"
    .fTop.fBoard.bd bind piece$i <ButtonPress-1> "::board::pressSquare $i"
  }
  bind . <Right> ".fTop.fr.fCmd.bNext invoke"
  bind . <Left> { ::board::Back }
  bind . <Up> ".fTop.fr.fCmd.bStart invoke"
  bind . <Down> ".fTop.fr.fCmd.bEnd invoke"
  if {$::WindowsCE} {
    bind . <F23> { ::board::setBoardSize 1 }
  }
  # buttons
  if {$::hires} {
    set pad 2
  } else {
    set pad 0
  }
  
  ttk::button .fTop.fr.fCmd.bPrev -image tb_prev -command {::board::Back}
  grid .fTop.fr.fCmd.bPrev -row 1 -column 0 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bNext -image tb_next -command {::board::Forward}
  grid .fTop.fr.fCmd.bNext -row 1 -column 1 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bStart -image tb_start -command {::board::Back 10}
  grid .fTop.fr.fCmd.bStart -row 2 -column 0 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bEnd -image tb_end -command {::board::Forward 10}
  grid .fTop.fr.fCmd.bEnd -row 2 -column 1 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bGprev -image tb_gprev -command {::game::gamePrev}
  grid .fTop.fr.fCmd.bGprev -row 3 -column 0 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bGnext -image tb_gnext -command {::game::gameNext}
  grid .fTop.fr.fCmd.bGnext -row 3 -column 1 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bFlip -image tb_flip -command {::board::toggleFlip}
  grid .fTop.fr.fCmd.bFlip -row 6 -column 1 -padx $pad -pady $pad
  ttk::button .fTop.fr.fCmd.bPgn -image tb_pgn -command {::board::showPgn}
  grid .fTop.fr.fCmd.bPgn -row 6 -column 0 -padx $pad -pady $pad
  
  ttk::button .fTop.fr.fCmd.bAnalyze -image engine_off -command ::board::toggleAnalyze
  grid .fTop.fr.fCmd.bAnalyze -row 7 -column 1 -padx $pad -pady $pad
  # side to move
  canvas .fTop.fr.fCmd.bSide -background white -relief groove -border 1 -width 24 -height 24
  grid .fTop.fr.fCmd.bSide -row 7 -column 0 -padx $pad -pady $pad
  
  pack .fTop.fBoard -side left -anchor s
  pack .fTop.fr -side left -expand 1 -fill both
  
  # display the material balance and last move
  frame .fTop.fr.f2
  label .fTop.fr.f2.lMaterial -relief flat -fg DarkSeaGreen -textvariable ::game::materialBalance
  label .fTop.fr.f2.lLastMove -relief flat -fg Black -textvariable ::game::lastMove -font font_Tiny
  pack .fTop.fr.f2.lMaterial .fTop.fr.f2.lLastMove -side top
  
  pack .fTop.fr.f2 .fTop.fr.fclocks -side top
  pack .fTop.fr.fCmd -side bottom
  
  set ::board::previousBoard(.fTop.fBoard.bd) ""
  setBoard .fTop.fBoard.bd [ sc_pos board ] $::board::pSize
  
  update
}
################################################################################
proc ::board::setBoardSize { {toggle 0} } {
  global ::board::pSize ::board::isFlipped
  
  if {$toggle} {
    if { $::options(fullBoard) } {
      set ::options(fullBoard) 0
    } else {
      set ::options(fullBoard) 1
    }
  }
  
  if { $::options(fullBoard) } {
    if {$::hires} {
      set pSize $::board::pSize_hires_fullsize
    } else {
      set pSize $::board::pSize_lowres_fullsize
    }
    set border 0
    set border_fBoard 0
  } else {
    set border 1
    set border_fBoard 1
    if {$::hires} {
      set pSize $::board::pSize_hires
    } else {
      set pSize $::board::pSize_lowres
    }
  }
  
  set bd .fTop.fBoard.bd
  
  # to force the redraw of pieces
  set ::board::previousBoard($bd) ""
  
  $bd configure -width [expr $pSize*8] -height [expr $pSize*8] -border $border -relief raised
  .fTop.fBoard configure -borderwidth $border_fBoard -relief sunken
  
  for {set i 0} {$i < 64} {incr i} {
    $bd delete sq$i
  }
  
  # Create empty board:
  for {set i 0} {$i < 64} {incr i} {
    set xi [expr {$i % 8} ]
    set yi [expr { 7 - int($i/8)} ]
    set x1 [expr {$xi * $pSize +1} ]
    set y1 [expr {$yi * $pSize +1} ]
    set x2 [expr {$x1 + $pSize } ]
    set y2 [expr {$y1 + $pSize } ]
    $bd create rectangle $x1 $y1 $x2 $y2 -tag sq$i -width 0 -fill [::board::defaultColor $i]
  }
  
  if { ! $::options(fullBoard) } {
    # Set up coordinate labels:
    for {set i 1} {$i <= 8} {incr i} {
      label .fTop.fBoard.lrank$i -text [expr {9 - $i}]
      grid .fTop.fBoard.lrank$i -row $i -column 0 -sticky e
    }
    
    foreach i {1 2 3 4 5 6 7 8} file {a b c d e f g h} {
      label .fTop.fBoard.bfile$file -text $file
      grid .fTop.fBoard.bfile$file -row 9 -column $i -sticky n
    }
    updateCoordinates
    # remove clocks in infopanel
    set w .fEngine.text
    foreach n {1 2} {
      set del [$w tag nextrange clock$n 1.0]
      catch {$w delete [lindex $del 0] [lindex $del 1]}
    }
  } else {
    for {set i 1} {$i <= 8} {incr i} { destroy .fTop.fBoard.lrank$i }
    foreach file {a b c d e f g h} { destroy .fTop.fBoard.bfile$file }
  }
  
  ::board::updateBoard -nolastmove
}
################################################################################
proc ::board::menuInit {} {
  option add *Menu*TearOff 0
  menu .menu
  
  . configure -menu .menu
  foreach menuname { base game switch search options multipv selectBook copy} {
    menu .menu.$menuname
  }
  .menu add cascade -label [::msgcat::mc "Base"] -menu .menu.base
  .menu.base add command -label [::msgcat::mc "Open"] -command ::base::baseOpen
  menu .menu.base.recent
  .menu.base add cascade -label [::msgcat::mc "Recent"] -menu .menu.base.recent
  .menu.base add command -label [::msgcat::mc "New"] -command ::base::baseNew
  .menu.base add command -label [::msgcat::mc "Import"] -command ::base::importPgnFile
  .menu.base add command -label [::msgcat::mc "Export"] -command ::base::exportPgnFile
  
  .menu.base add cascade -label [::msgcat::mc "Switch"] -menu .menu.switch
  for {set i 1} {$i < [sc_base count total]} {incr i} {
    .menu.switch add command -label $i.\[[::msgcat::mc "empty"]\] -command "::base::switch $i"
  }
  .menu.switch add command -label [::msgcat::mc "clipbase"] -command "::base::switch [sc_base count total]"
  
  .menu.base add command -label [::msgcat::mc "Close"] -command ::base::baseClose
  .menu.base add command -label [::msgcat::mc "Compact"] -command ::base::baseCompact
  .menu.base add command -label [::msgcat::mc "Quit"] -command ::appQuit
  .menu add cascade -label [::msgcat::mc "Game"] -menu .menu.game
  .menu.game add command -label [::msgcat::mc "New"] -command ::game::new
  .menu.game add command -label [::msgcat::mc "Save"] -command ::game::save
  .menu.game add command -label [::msgcat::mc "Delete"] -command ::game::delete
  
  menu .menu.game.goto
  .menu.game add cascade -label [::msgcat::mc "Goto"] -menu .menu.game.goto
  .menu.game.goto add command -label [::msgcat::mc "Next"] -command ::game::gameNext
  .menu.game.goto add command -label [::msgcat::mc "Previous"] -command ::game::gamePrev
  .menu.game.goto add command -label [::msgcat::mc "First"] -command { ::game::gameLoad 1 }
  .menu.game.goto add command -label [::msgcat::mc "Last"] -command { ::game::gameLoad [sc_base numGames]}
  .menu.game.goto add command -label [::msgcat::mc "Number"] -command ::game::LoadNumber
  .menu.game.goto add command -label [::msgcat::mc "Random"] -command ::game::random
  
  .menu.game add command -label [::msgcat::mc "Info"] -command ::game::info
  .menu.game add command -label [::msgcat::mc "Undo"] -command { sc_game undo ; ::board::updateBoard -nolastmove }
  .menu.game add command -label [::msgcat::mc "List"] -command ::game::GameList
  .menu.game add command -label [::msgcat::mc "Comment"] -command ::game::MoveComment
  .menu.game add command -label [::msgcat::mc "Board setup"] -command ::game::BoardSetup
  .menu.game add checkbutton -label [::msgcat::mc "Annotate"] -variable ::analyzeMode -command ::game::Annotate
  .menu.game add checkbutton -label [::msgcat::mc "Play"] -variable ::playMode -command ::game::Play
  # .menu.game add command -label [::msgcat::mc "Online TB"] -command ::infopanel::onlineTB
  
  
  # ------- tactics --------
  menu .menu.game.tactics
  .menu.game add cascade -label [::msgcat::mc "Tactics"] -menu .menu.game.tactics
  .menu.game.tactics add checkbutton -label [::msgcat::mc "Training"] -variable ::tactics::running -command ::tactics::start
  .menu.game.tactics add command -label [::msgcat::mc "Reset base"] -command ::tactics::resetScores
  .menu.game.tactics add command -label [::msgcat::mc "Show solution"] -command ::tactics::showSolution
  
  menu .menu.game.tactics.thinktime
  .menu.game.tactics add cascade -label [::msgcat::mc "Analysis time"] -menu .menu.game.tactics.thinktime
  foreach i { 3 5 10 15 30 60 } {
    .menu.game.tactics.thinktime add radiobutton -label "$i" -value $i -variable ::tactics::analysisTime
  }
  
  # ------- fics --------
  menu .menu.game.fics
  .menu.game add cascade -label [::msgcat::mc "FICS"] -menu .menu.game.fics
  .menu.game.fics add checkbutton -label [::msgcat::mc "Connect"] -variable ::fics::connected -command {
    if {$::fics::connected} { ::fics::config } else { ::fics::close }
  }
  .menu.game.fics add command -label [::msgcat::mc "Find opponent"] -command ::fics::findOpponent
  .menu.game.fics add command -label [::msgcat::mc "Show offers"] -command { ::fics::showOffers }
  
  .menu.game.fics add command -label [::msgcat::mc "Games"] -command { ::fics::writechan "games /bs" ; ::fics::showConsole }
  .menu.game.fics add command -label [::msgcat::mc "Draw"] -command { ::fics::writechan "draw"}
  .menu.game.fics add command -label [::msgcat::mc "Resign"] -command { ::fics::writechan "resign"}
  .menu.game.fics add command -label [::msgcat::mc "Takeback"] -command { ::fics::writechan "takeback"}
  .menu.game.fics add command -label [::msgcat::mc "Takeback 2"] -command { ::fics::writechan "takeback 2"}
  .menu.game.fics add command -label [::msgcat::mc "Console"] -command { ::fics::showConsole }
  .menu.game.fics add command -label [::msgcat::mc "Formula"] -command { ::fics::setFormula }
  menu .menu.game.fics.wifi
  .menu.game.fics add cascade -label [::msgcat::mc "Wifi"] -menu .menu.game.fics.wifi
  .menu.game.fics.wifi add command -label [::msgcat::mc "Start"] -command { wce wifi 1 }
  .menu.game.fics.wifi add command -label [::msgcat::mc "Stop"] -command { wce wifi 0 }
  
  # ------- search --------
  .menu add cascade -label [::msgcat::mc "Search"] -menu .menu.search
  .menu.search add command -label [::msgcat::mc "Current board"] -command ::ui::CurrentBoard
  .menu.search add command -label [::msgcat::mc "Header"] -command ::ui::Header
  .menu.search add command -label [::msgcat::mc "Material/Pattern"] -command ::ui::MaterialPattern
  .menu.search add command -label [::msgcat::mc "Reset filter"] -command ::ui::ResetFilter
  .menu.search add command -label [::msgcat::mc "Negate filter"] -command ::ui::NegateFilter
  
  .menu.search add cascade -label [::msgcat::mc "Copy filter to"] -menu .menu.copy
  for {set i 1} {$i < [sc_base count total]} {incr i} {
    .menu.copy add command -label $i.[::msgcat::mc "empty"] -command "::ui::CopyFilter $i"
  }
  .menu.copy add command -label [::msgcat::mc "clipbase"] -command "::ui::CopyFilter [sc_base count total]"
  
  .menu.search add command -label [::msgcat::mc "Filter info"] -command ::ui::FilterInfo
  
  # ------------- Options menu -------------------------
  .menu add cascade -label [::msgcat::mc "Options"] -menu .menu.options
  .menu.options add cascade -label [::msgcat::mc "Best lines"] -menu .menu.multipv
  for {set i 1} {$i <= $::options(multipvmax) } {incr i} {
    .menu.multipv add radiobutton -label "$i" -value $i -variable ::options(multipv) -command "::engine::setMultiPv 1"
  }
  .menu.multipv add checkbutton -label [::msgcat::mc "Word wrap"] -variable ::options(wrap) -onvalue "word" -offvalue "none" \
      -command { .fEngine.text tag configure enginemultipv -wrap $::options(wrap) }
  
  # -- Books
  .menu.options add cascade -label [::msgcat::mc "Book"] -menu .menu.selectBook
  .menu.selectBook add checkbutton -label [::msgcat::mc "Show book"] -variable ::options(showBook) -command ::ui::showBook
  set bookPath [file join $::instalDir "books" ]
  set bookList [  lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ]
  foreach file $bookList {
    set f [ file tail $file ]
    .menu.selectBook add radiobutton -variable ::options(mainBook) -value $f -label $f -command {
      ::book::bookSelect
      ::infopanel::updateBookText
    }
  }
  if {$::options(mainBook) == ""} {
    set ::options(mainBook) [ file tail [lindex $bookList 0] ]
  }
  
  .menu.options add checkbutton -label [::msgcat::mc "Base stats"] -variable ::options(showTree) -command ::ui::showTree
  
  .menu.options add checkbutton -label [::msgcat::mc "Show analysis"] -variable ::options(showAnalysis) -command ::ui::showAnalysis
  .menu.options add checkbutton -label [::msgcat::mc "Show comments"] -variable ::options(showComments) -command ::ui::showComments
  .menu.options add checkbutton -label [::msgcat::mc "Show material"] -variable ::options(showMaterial) -command ::board::updateMaterialBalance
  .menu.options add checkbutton -label [::msgcat::mc "Score only"] -variable ::options(scoreOnly) -command ::infopanel::updateAnalysis
  .menu.options add checkbutton -label [::msgcat::mc "Show last move"] -variable ::options(showLastMove) -command ::board::showLastMove
  .menu.options add checkbutton -label [::msgcat::mc "Full board"] -variable ::options(fullBoard) -command ::board::setBoardSize
  
  # -- UI settings
  menu .menu.options.looknfeel
  .menu.options add cascade -label [::msgcat::mc "Look'n feel"] -menu .menu.options.looknfeel
  # Font size
  menu .menu.options.looknfeel.fontsize
  .menu.options.looknfeel add cascade -label [::msgcat::mc "Font size"] -menu .menu.options.looknfeel.fontsize
  foreach i { 6 7 8 9 10 11 12 } {
    .menu.options.looknfeel.fontsize add radiobutton -label "$i" -value $i -variable ::options(fontAnalysisSize) -command "::infopanel::setFontSize"
  }
  # Themes
  menu .menu.options.looknfeel.themes
  .menu.options.looknfeel add cascade -label [::msgcat::mc "Themes"] -menu .menu.options.looknfeel.themes
  foreach i [ttk::style theme names] {
    .menu.options.looknfeel.themes add radiobutton -label "$i" -value $i -variable ::options(theme) -command "::ui::setTheme"
  }
  
  # -- engines
  menu .menu.options.engine
  .menu.options add cascade -label [::msgcat::mc "Engines"] -menu .menu.options.engine
  menu .menu.options.engine.hash
  .menu.options.engine add cascade -label [::msgcat::mc "Hash memory"] -menu .menu.options.engine.hash
  set k [::msgcat::mc "kB"]
  foreach i { 64 128 256 512 1024 2048 } {
    .menu.options.engine.hash add radiobutton -label "$i $k" -value $i -variable ::options(enginehash) \
        -command "::engine::setHash"
  }
  .menu.options.engine add command -label [::msgcat::mc "Select"] -command ::engine::associate
  .menu.options.engine add checkbutton -label [::msgcat::mc "Extended info"] -variable ::options(engineExtendedInfo)
  .menu.options.engine add checkbutton -label [::msgcat::mc "Optimize speed"] -variable ::options(engineOptimizeSpeed) -command ::engine::OptimizeSpeed
  .menu.options.engine add checkbutton -label [::msgcat::mc "Tournament"] -variable ::tournamentMode -command ::engine::tournament
  menu .menu.options.engine.setresult
  .menu.options.engine.setresult add command -label [::msgcat::mc "White wins"] -command {::engine::setGameResult "White wins"}
  .menu.options.engine.setresult add command -label [::msgcat::mc "Black wins"] -command {::engine::setGameResult "Black wins"}
  .menu.options.engine.setresult add command -label [::msgcat::mc "Draw"] -command {::engine::setGameResult "Draw"}
  
  .menu.options.engine add cascade -label [::msgcat::mc "Set result"] -menu .menu.options.engine.setresult
  .menu.options add checkbutton -label [::msgcat::mc "Sound"] -variable ::options(sound)
  menu .menu.options.lang
  foreach i { english french spanish } {
    .menu.options.lang add radiobutton -label [::msgcat::mc $i] -value $i -variable ::options(language) -command ::board::setLang
  }
  .menu.options add cascade -label [::msgcat::mc "Language"] -menu .menu.options.lang
  
  # ------- novag --------
  menu .menu.options.novag
  .menu.options.novag add command -label [::msgcat::mc "Configure"] -command ::novag::configure
  .menu.options.novag add checkbutton -label [::msgcat::mc "Connect"] -variable ::novag::connected -command ::novag::connect
  .menu.options.novag add command -label [::msgcat::mc "Console"] -command ::novag::showConsole
  .menu.options.novag add command -label [::msgcat::mc "Pos.->Citrine"] -command ::novag::setup
  .menu.options.novag add command -label [::msgcat::mc "Hard Synch."] -command ::novag::synchro
  .menu.options.novag add command -label [::msgcat::mc "Check sync"] -command ::novag::menuCheckBoardSync
  
  menu .menu.options.novag.bluetooth
  .menu.options.novag add cascade -label [::msgcat::mc "Bluetooth"] -menu .menu.options.novag.bluetooth
  .menu.options.novag.bluetooth add command -label [::msgcat::mc "Start"] -command { wce bluetooth 1 }
  .menu.options.novag.bluetooth add command -label [::msgcat::mc "Stop"] -command { wce bluetooth 0 }
  
  .menu.options.novag add checkbutton -label [::msgcat::mc "Referee mode"] -variable ::options(novag_refereeMode) \
      -command { catch ::novag::referee }
  .menu.options.novag add checkbutton -label [::msgcat::mc "Flipped board"] -variable ::novag::flippedBoard
  .menu.options.novag add checkbutton -label [::msgcat::mc "Send move twice"] -variable ::options(novag_sendmovetwice)
  .menu.options add cascade -label [::msgcat::mc "Novag Citrine"] -menu .menu.options.novag
  
  .menu.options add command -label [::msgcat::mc "About"] -command ::ui::About
}

################################################################################
proc ::board::setLang {} {
  switch $::options(language) {
    french  {sc_info language fr}
    spanish {sc_info language es}
    default {sc_info language en}
  }
}
################################################################################
proc ::board::toggleFlip {} {
  global ::board::isFlipped
  
  resetShowLastMove
  
  # to force the redraw of pieces
  set ::board::previousBoard(.fTop.fBoard.bd) ""
  
  set isFlipped [expr 1 - $isFlipped]
  updateCoordinates
  updateBoard -noanalyze -nolastmove
  
  # if a game is in progress, update status
  if {$::playMode} {
    ::game::setPlayHeaders
  }
  
}
################################################################################
proc ::board::updateCoordinates {} {
  global ::board::isFlipped
  set w .fTop.fBoard
  for {set i 1} {$i <= 8} {incr i} {
    if {!$isFlipped} {
      $w.lrank$i configure -text [expr {9 - $i}]
    } else  {
      $w.lrank$i configure -text $i
    }
  }
  
  if {!$isFlipped} {
    foreach file {a b c d e f g h} {
      $w.bfile$file configure -text $file
    }
  } else  {
    foreach file {a b c d e f g h} newfile {h g f e d c b a} {
      $w.bfile$file configure -text $newfile
    }
  }
}

################################################################################
proc ::board::Start {} {
  sc_move start
  updateBoard -nolastmove
}
################################################################################
proc ::board::End {} {
  sc_move end
  updateBoard -nolastmove
}
################################################################################
proc ::board::Back {{count 1}} {
  if {[sc_pos isAt start]} { return }
  if {[sc_pos isAt vstart]} {
    sc_var exit
    return
  }
  
  # if in a variation, exit it
  if {[sc_var level] != 0 && $count != 1} {
    sc_var exit
  } else {
    sc_move back $count
  }
  updateBoard -nolastmove
}

################################################################################
proc ::board::Forward {{count 1}} {
  if {[sc_pos isAt end]  ||  [sc_pos isAt vend]} { return }
  if {$count == 1} {
    if {[sc_var count] != 0 && $::options(showVarPopup)} {
      showVars
    } else  {
      sc_move forward
      updateBoard
    }
  } else {
    sc_move forward $count
    updateBoard -nolastmove
  }
}
################################################################################
proc ::board::showVars {} {
  
  # No need to display an empty menu
  if {[sc_var count] == 0} {
    return
  }
  
  if {[sc_var count] == 1 &&  [sc_game info nextMove] == ""} {
    # There is only one variation and no main line, so enter it
    sc_var moveInto 0
    updateBoard
    return
  }
  
  sc_info preMoveCmd {}
  
  set w .variations
  if {[winfo exists $w]} { return }
  
  set varList [sc_var list]
  set numVars [sc_var count]
  
  # Present a menu of the possible variations
  toplevel $w
  wm title $w [::msgcat::mc "Variations" ]
  ::ui::setTopmost $w
  
  set h [expr $numVars + 1]
  if { $h> 10} { set h 10 }
  listbox $w.lbVar -selectmode browse -height $h -width 20
  pack $w.lbVar -expand yes -fill both -side top
  
  #insert main line
  set move [sc_game info nextMove]
  if {$move == ""} {
    set move "empty"
  } else  {
    $w.lbVar insert end "0: [getNextMoves 5]"
  }
  
  # insert variations
  for {set i 0} {$i < $numVars} {incr i} {
    set move [::trans [lindex $varList $i]]
    if {$move == ""} {
      set move "empty"
    } else  {
      sc_var moveInto $i
      append move [getNextMoves 5]
      sc_var exit
    }
    set str "[expr {$i + 1}]: $move"
    $w.lbVar insert end $str
  }
  $w.lbVar selection set 0
  
  bind .variations <Button-1> {catch { event generate .variations <Right> } }
  bind .variations <Right> {
    set cur [.variations.lbVar curselection]
    destroy .variations
    sc_info preMoveCmd preMoveCommand
    if {$cur == 0} {
      catch { sc_move forward }
    } else  {
      catch { sc_var moveInto [expr $cur - 1] }
    }
    ::board::updateBoard
  }
  bind .variations <Up> { set cur [.variations.lbVar curselection] ; .variations.lbVar selection clear $cur
    set sel [expr $cur - 1]
    if {$sel < 0} { set sel 0 }
    .variations.lbVar selection set $sel ; .variations.lbVar see $sel}
  bind .variations <Down> { set cur [.variations.lbVar curselection] ; .variations.lbVar selection clear $cur
    set sel [expr $cur + 1]
    if {$sel >= [.variations.lbVar index end]} { set sel end }
    .variations.lbVar selection set $sel ; .variations.lbVar see $sel}
  bind .variations <Left> { catch { event generate .variations <Destroy> } }
  #in order to have the window always on top : this does not really work ...
  bind .variations <Visibility> {
    if { "%s" != "VisibilityUnobscured" } {
      focus .variations
      raise .variations
    }
  }
  bind .variations <FocusOut> {
    focus .variations
    raise .variations
  }
  sc_info preMoveCmd preMoveCommand
  update
  focus .variations
  grab set $w
}
################################################################################
# returns the next moves, translated
proc ::board::getNextMoves { {num 4} } {
  set tmp ""
  set count 0
  while { [sc_game info nextMove] != "" && $count < $num} {
    append tmp " [sc_game info nextMove]"
    sc_move forward
    incr count
  }
  sc_move back $count
  return $tmp
}
################################################################################
proc ::board::showPgn {} {
  ::pgn::OpenClose
}

################################################################################
proc ::board::updateEngine {{n 1 }} {
  if {$::engine::data(running$n)} {
    ::engine::stopAnalysis $n
    ::engine::startAnalysis $n
  }
}
################################################################################
proc ::board::toggleAnalyze {{n 1 }} {
  global ::engine::data
  set b .fTop.fr.fCmd.bAnalyze
  if {$data(running$n)} {
    $b configure -image engine_off -state disabled
    ::engine::stopAnalysis $n
    $b configure -state normal
  } else  {
    $b configure -image engine_on -state disabled
    ::engine::startAnalysis $n
    $b configure -state normal
  }
}
################################################################################
proc ::board::pressSquare { square } {
  if { $::fics::playing == -1} { return } ;# not player's turn
  if { $::tactics::isThinking } { return } ;# to prevent engine update
  
  global ::board::fromSquare
  set bd .fTop.fBoard.bd
  
  resetShowLastMove
  
  if {$fromSquare == -1} {
    set fromSquare $square
    if {$::board::isFlipped} { set fromSquare [expr 63-$fromSquare] }
    $bd itemconfigure sq$square -outline $::board::highlight -fill $::board::highlight
  } else {
    set toSquare $square
    if {$::board::isFlipped} { set toSquare [expr 63-$toSquare] }
    if {$fromSquare == $toSquare} {
      set color [defaultColor $square]
      $bd itemconfigure sq$square -outline $color -fill $color
      set fromSquare -1
      return
    } else  {
      makeMove $fromSquare $toSquare
      set color [defaultColor $fromSquare]
      if {$::board::isFlipped} { set fromSquare [expr 63-$fromSquare] }
      $bd itemconfigure sq$fromSquare -outline $color -fill $color
      set fromSquare -1
    }
  }
}
################################################################################
proc ::board::resetShowLastMove {} {
  set bd .fTop.fBoard.bd
  foreach sq $::board::lastMoveList {
    set color [defaultColor $sq]
    # if {$::board::isFlipped} { set sq [expr 63-$sq] }
    $bd itemconfigure sq$sq -outline $color -fill $color
  }
  set ::board::lastMoveList {}
}
################################################################################
proc ::board::resetSquaresColor {} {
  set bd .fTop.fBoard.bd
  for {set i 0} {$i < 64} {incr i} {
    $bd itemconfigure sq$i -fill [defaultColor $i]
  }
}
################################################################################
proc ::board::showLastMove {} {
  set bd .fTop.fBoard.bd
  if { $::options(showLastMove) } {
    foreach sq $::board::lastMoveList {
      # if {$::board::isFlipped} { set sq [expr 63-$sq] }
      $bd itemconfigure sq$sq -outline $::board::highlight -fill $::board::highlight
    }
  } else {
    resetShowLastMove
  }
}
################################################################################
proc ::board::setBoard {bd board size} {
  
  set ::board::lastMoveList {}
  
  for {set sq 0} {$sq<64} {incr sq} {
    if {$::board::isFlipped} {
      set fsq [expr 63-$sq]
    } else {
      set fsq $sq
    }
    if { [ string index $board $fsq ] == [ string index $::board::previousBoard($bd) $fsq ] } { continue }
    
    lappend ::board::lastMoveList $sq
    
    set img [getImg $fsq $size $board]
    $bd delete piece$sq
    if {$img == ""} { continue }
    $bd create image [getMidSq $bd $sq] -image $img -tag piece$sq
  }
  set ::board::previousBoard($bd) $board
}

################################################################################
proc ::board::getMidSq {w sq} {
  set c [$w coords sq$sq]
  set x [expr {([lindex $c 0] + [lindex $c 2]) / 2 } ]
  set y [expr {([lindex $c 1] + [lindex $c 3]) / 2 } ]
  return [list $x $y]
}
################################################################################
proc ::board::getImg {sq size board} {
  set p [string index $board $sq]
  if {$p == "."} { return "" }
  if { [string is lower $p] } {
    set color "b"
  } else  {
    set color "w"
  }
  set ret "$color[string tolower $p]$size"
  return $ret
}
################################################################################
proc ::board::makeMove {from to} {
  set EMPTY 0 ;    set KING 1 ;    set QUEEN 2 ;    set ROOK 3 ;    set BISHOP 4 ;    set KNIGHT 5 ;    set PAWN 6
  
  sc_game undoPoint
  
  set promo $EMPTY
  if {[sc_pos isPromotion $from $to] == 1} {
    # sometimes, addMove is triggered twice
    if { [winfo exists .promoWin] } { return }
    set promo [getPromoPiece]
  }
  
  set action "replace"
  if {![sc_pos isAt vend]} {
    set action [confirmReplaceMove]
  }
  if {$action == "replace"} {
    # nothing
  } elseif {$action == "mainline" || $action == "var"} {
    sc_var create
  } else {
    # Do not add the move at all:
    return
  }
  
  if {[sc_pos isLegal $from $to] == 0} { return }
  
  if { [catch { ::game::moveAdd [list $from $to $promo] } err ] } {
    tk_messageBox -type ok -icon error -parent . -title "Error" -message "catch ::game::moveAdd ( $from $to $promo ) $err"
    return
  }
  
  # check if the engine pondered on the right move
  if {$::playMode} {
    switch -- $promo {
      2 { set p "q"}
      3 { set p "r"}
      4 { set p "b"}
      5 { set p "n"}
      default {set p ""}
    }
    set ::game::lastPlayerMoveUci "[::board::san $from][::board::san $to]$p"
  }
  
  if {$action == "mainline"} {
    sc_var exit
    sc_var promote [expr {[sc_var count] - 1}]
    sc_move forward 1
  }
  if { $::fics::playing == 1} {
    if { $promo != $EMPTY } {
      switch $promo {
        2 {set p "q"}
        3 {set p "r"}
        4 {set p "b"}
        5 {set p "n"}
        default {set p "q"}
      }
      ::fics::writechan "promote $p"
    }
    ::fics::writechan [ string range [sc_game info previousMoveUCI] 0 3 ]
    sc_move back
    sc_game truncatefree
    ::sound::play alert
  } else {
    updateBoard
  }
  
}
################################################################################
#
################################################################################
proc ::board::getPromoPiece {} {
  set w .promoWin
  set ::result 2
  toplevel $w
  wm title $w [::msgcat::mc "Promotion"]
  ::ui::setTopmost $w
  
  # wm resizable $w 0 0
  set col "w"
  if { [sc_pos side] == "black" } { set col "b" }
  ttk::button $w.bq -image ${col}q$::board::pSize -command "set ::result 2 ; destroy $w"
  ttk::button $w.br -image ${col}r$::board::pSize -command "set ::result 3 ; destroy $w"
  ttk::button $w.bb -image ${col}b$::board::pSize -command "set ::result 4 ; destroy $w"
  ttk::button $w.bn -image ${col}n$::board::pSize -command "set ::result 5 ; destroy $w"
  pack $w.bq $w.br $w.bb $w.bn -side left
  update
  grab $w
  tkwait window $w
  return $::result
}
################################################################################
proc ::board::confirmReplaceMove {} {
  set ::board::answer "cancel"
  if {$::options(addVariationWithoutAsking)} { return "var" }
  
  if {! $::options(askToReplaceMoves)} { return "replace" }
  set w .confirmRepMove
  toplevel $w
  wm title $w [::msgcat::mc "Replace move" ]
  ::ui::setTopmost $w
  
  label $w.l -text [::msgcat::mc "Scid: Replace Move ?"] -anchor w
  pack $w.l
  ttk::button $w.b1 -text [::msgcat::mc "Replace move"] -command "set ::board::answer replace "
  ttk::button $w.b2 -text [::msgcat::mc "New main line"] -command "set ::board::answer mainline "
  ttk::button $w.b3 -text [::msgcat::mc "Add variation"] -command "set ::board::answer var "
  ttk::button $w.b4 -text [::msgcat::mc "Cancel"] -command "set ::board::answer cancel "
  
  pack $w.b1 $w.b2 $w.b3 $w.b4 -fill x
  bind $w <Destroy> ""
  update
  grab $w
  vwait ::board::answer
  destroy $w
  return $::board::answer
}
################################################################################
proc ::board::defaultColor {sq} {
  return [expr {($sq + ($sq / 8)) % 2 ? "$::board::lightSquare" : "$::board::darkSquare"}]
}
################################################################################
proc ::board::updateMenuStates {} {
  # Load first/last/random buttons:
  set filtercount [sc_filter count]
  if {$filtercount == 0} {set state disabled} else {set state normal}
  .menu.game.goto entryconfig [::msgcat::mc "Random"] -state $state
  
  # Load previous button:
  if {[sc_filter previous]} {set state normal} else {set state disabled}
  .fTop.fr.fCmd.bGprev configure -state $state
  .menu.game.goto entryconfig [::msgcat::mc "Previous"] -state $state
  
  # Load next button:
  if {[sc_filter next]} {set state normal} else {set state disabled}
  .fTop.fr.fCmd.bGnext configure -state $state
  .menu.game.goto entryconfig [::msgcat::mc "Next"] -state $state
}
################################################################################
proc ::board::updateBoard {args} {
  set updateAnalyze 1
  set updatePgn 0
  set nolastmove 0
  set nobook 0
  foreach arg $args {
    if {! [string compare $arg "-noanalyze"]} { set updateAnalyze 0 }
    if {! [string compare $arg "-pgn"]} { set updatePgn 1 }
    if {! [string compare $arg "-nolastmove"]} { set nolastmove 1 }
    if {! [string compare $arg "-nobook"]} { set nobook 1 }
  }
  set board [sc_pos board]
  resetShowLastMove
  setBoard .fTop.fBoard.bd $board $::board::pSize
  if {$nolastmove} {
    set ::board::lastMoveList {}
    ::board::resetShowLastMove
  } else {
    showLastMove
  }
  
  .fTop.fr.fCmd.bSide configure -background [sc_pos side]
  if {$updateAnalyze} {
    updateEngine 1
  }
  if {$updatePgn} {
    ::pgn::Refresh 1
  }
  if {$::options(showComments)} {
    ::infopanel::updateComments
  }
  if {$::options(showBook) && ! $nobook} {
    ::infopanel::updateBookText
  }
  if {$::options(showTree)} {
    ::infopanel::updateTreeText
  }
  
  ::infopanel::updateOnlineTB
  
  set nag [sc_pos getNags]
  if {$nag == "0"} {set nag ""}
  set num [sc_pos moveNumber]
  if {[sc_pos side] == "white"} { incr num -1 }
  set ::game::lastMove "$num.[sc_game info previousMove]$nag"
  updateMaterialBalance
  updateMenuStates
  
}
################################################################################
proc ::board::updateMaterialBalance {} {
  if {$::options(showMaterial)} {
    set fen [lindex [sc_pos fen] 0]
    set p 0
    set n 0
    set b 0
    set r 0
    set q 0
    for {set i 0} {$i < [string length $fen]} {incr i} {
      set ch [string index $fen $i]
      switch -- $ch {
        p {incr p -1}
        P {incr p}
        n {incr n -1}
        N {incr n}
        b {incr b -1}
        B {incr b}
        r {incr r -1}
        R {incr r}
        q {incr q -1}
        Q {incr q}
      }
    }
    set ::game::materialBalance [expr $p + $n * 3 + $b * 3 + $r * 5 + $q * 9 ]
  } else {
    set ::game::materialBalance ""
  }
}
################################################################################
proc ::board::updateTitle {} {
  set delete ""
  if { [sc_game flag delete [sc_game number] ] == 1 } {set delete " D"}
  if {[sc_base current] == [sc_base count total]} {
    wm title . "clipbase:[sc_game number]/[sc_base numGames]$delete"
  } else  {
    wm title . "[sc_base current]:[sc_game number]/[sc_base numGames]$delete"
  }
}
################################################################################
proc ::board::san {sqno} {
  if {($sqno < 0) || ($sqno > 63)} { return }
  return [format %c%c \
      [expr {($sqno % 8) + [scan a %c]}] \
      [expr {($sqno / 8) + [scan 1 %c]}]]
}
################################################################################
proc ::board::sq {sqname} {
  set squareIndex [list a1 b1 c1 d1 e1 f1 g1 h1 a2 b2 c2 d2 e2 f2 g2 h2 \
      a3 b3 c3 d3 e3 f3 g3 h3 a4 b4 c4 d4 e4 f4 g4 h4 \
      a5 b5 c5 d5 e5 f5 g5 h5 a6 b6 c6 d6 e6 f6 g6 h6 \
      a7 b7 c7 d7 e7 f7 g7 h7 a8 b8 c8 d8 e8 f8 g8 h8]
  return [lsearch -exact $squareIndex $sqname]
}
################################################################################
proc ::board::setPieceData {font size} {
  image create photo e$size -height $size -width $size
  image create photo tempPieces$font$size -data $::pieceImageData($font,$size)
  set x 0
  foreach p {wp wn wb wr wq wk bp bn bb br bq bk} {
    image create photo $p$size -width $size -height $size
    $p$size copy tempPieces$font$size -from $x 0 [expr $x + $size - 1] [expr $size - 1]
    incr x $size
  }
  image delete tempPieces$font$size
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges
################################################################################
#
################################################################################

namespace eval engine {
  array set data {}
  set infoToken { depth seldepth time nodes pv multipv score cp mate lowerbound upperbound \
        currmove currmovenumber hashfull nps tbhits sbhits cpuload string refutation currline }
  set optionToken {name type default min max var }
  set isOpening 0
  set chosenOpening ""
  set openingMovesList {}
  set openingMovesHash {}
  set openingMoves ""
  set isTurn 1
  set priority 251
  
  trace add variable ::tournamentMode write ::engine::traceTournamentMode
  # ##################################################################
  proc traceTournamentMode { name1 name2 op } {
    if { ! $::tournamentMode } {
      catch {
        sendToEngine "stop" 1
        sendToEngine "stop" 2
        ::gameclock::delete 1
        ::gameclock::delete 2
      }
    }
  }
  # ##################################################################
  proc reset {{n 1}} {
    global ::engine::data
    set data(sock$n) ""
    set data(serversock$n) ""
    set data(depth$n) 0
    set data(seldepth$n) 0
    set data(time$n) 0 ;# msec
    set data(bestmove$n) ""
    set data(ponder$n) ""
    set data(pv$n) ""
    set data(score$n) ""
    set data(scoremate$n) ""
    set data(nps$n) 0
    set data(nodes$n) 0
    set data(multipv$n) {}
    set data(multipvnum$n) 0
    set data(uciok$n) 0
    set data(readyok$n) 0
    set data(waitForBestMove$n) 0
    set data(waitForReadyOk$n) 0
    set data(name$n) ""
    set data(currmove$n) ""
    set data(currmovenumber$n) 0
    set data(hashfull$n) 0
    set data(tbhits$n) 0
    set data(sbhits$n) 0
    set data(cpuload$n) 0
    set data(running$n) 0
  }
  # ##################################################################
  proc resetAnalysis { n } {
    global ::engine::data
    set data(depth$n) 0
    set data(bestmove$n) ""
    set data(ponder$n) ""
    set data(pv$n) ""
    set data(score$n) ""
    set data(nps$n) 0
    set data(nodes$n) 0
    set data(multipvnum$n) 0
  }
  # ##################################################################
  proc setMultiPv { n } {
    if {$::options(engine$n) == "" } {return}
    
    if { $::options(multipv) != 1 } {
      set ::options(engineOptimizeSpeed) 0
      set ::options(engineUCIFormat) 1
    }
    
    if { ! [isUci $n] } { set ::options(multipv) 1 }
    
    if { [lindex $::engineconfig($::options(engine$n)) 3] == "MultiPV_NO" && $::options(multipv) != 1} {
      set ::options(multipv) 1
      tk_messageBox -type ok -icon warning -parent . -title [::msgcat::mc "Best lines"] -message "Engine [getName $n]\ndoes not support Multi PV"
      return
    }
    
    set restartAnalysis $::engine::data(running$n)
    stopAnalysis $n
    sendToEngine "setoption name MultiPV value $::options(multipv)" $n
    # resetAnalysis $n
    if {$restartAnalysis} {
      startAnalysis $n
    }
  }
  # ##################################################################
  # will try to set engines hash to the desired value (or /2^n)
  proc setHash { } {
    set running1 $::engine::data(running1)
    stopAnalysis 1
    if {$::options(engine2) != "" } {
      set running2 $::engine::data(running2)
    } else {
      set running2 0
      stopAnalysis 2
    }
    
    for {set h $::options(enginehash)} {$h>=64} { set h [expr $h / 2]} {
      if {[isUci 1]} {
        sendToEngine "setoption name Hash value $::options(enginehash)" 1
      } else {
        sendToEngine "sethash $::options(enginehash)" 1
      }
      if { $::options(engine2) != "" } {
        if { [ isUci 2 ] } {
          sendToEngine "setoption name Hash value $::options(enginehash)" 2
        } else {
          sendToEngine "sethash $::options(enginehash)" 2
        }
      }
      
      after 100
      if {$::options(engine2) != "" } {
        if { ![ isUci 2 ] } { break }
        if { [checkEngineAlive 1] && [checkEngineAlive 2] } {
          break
        } else {
          tk_messageBox -type ok -icon error -parent . -title "Hash" -message "Hash memory too high $h\trying a lower value"
        }
      } else {
        if { ![ isUci 1 ] } { break }
        if { [checkEngineAlive 1] } {
          break
        } else {
          tk_messageBox -type ok -icon error -parent . -title "Hash" -message "Hash memory too high $h\trying a lower value"
        }
      }
      
    }
    
    set ::options(enginehash) $h
    
    if {$running1} { startAnalysis 1 }
    if {$running2} { startAnalysis 2 }
    
  }
  
  # ##################################################################
  proc startAnalysis { n } {
    if {$::engine::data(running$n)} { return }
    
    if { $::engine::data(running$n) == -1 } { return }
    
    set ::engine::data(running$n) -1
    
    if { ! [isUci $n] } {
      sendToEngine "setboard [sc_pos fen]" $n
      sendToEngine "analyze" $n
    } else {
      set ::engine::data(waitForReadyOk$n) 1
      sendToEngine "isready" $n
      ::vwaitTimed ::engine::data(waitForReadyOk$n) 3000 nowarnuser
      ::engine::resetAnalysis $n
      ::infopanel::updateAnalysis $n
      sendToEngine "position fen [sc_pos fen]" $n
      sendToEngine "go infinite" $n
    }
    
    set ::engine::data(running$n) 1
  }
  # ##################################################################
  proc stopAnalysis { n } {
    if { $::options(engine$n) == "" } { return }
    if {!$::engine::data(running$n)} { return }
    
    if { $::engine::data(running$n) == -1 } { return }
    
    set ::engine::data(running$n) -1
    
    set ::engine::data(waitForBestMove$n) 1
    if { ! [isUci $n] } {
      sendToEngine "exit" $n
    } else {
      sendToEngine "stop" $n
    }
    ::vwaitTimed ::engine::data(waitForBestMove$n) 1000 "nowarning"
    
    set ::engine::data(running$n) 0
    set ::engine::data(cpuload$n) 0
    ::infopanel::updateAnalysis $n
  }
  # ##################################################################
  proc associate { } {
    set w .associate
    if {[winfo exists $w]} { return }
    
    toplevel $w
    wm title $w [::msgcat::mc "Engines"]
    ::ui::setTopmost $w
    ttk::frame $w.f1
    ttk::frame $w.f2
    pack $w.f1 $w.f2 -fill x
    
    ttk::label $w.f1.l -text [::msgcat::mc "Configure engines"] -anchor w
    
    set leng {}
    foreach eng [ lsort [ array names ::engineconfig ] ] {
      lappend leng "[lindex $::engineconfig($eng) 0] [lindex $::engineconfig($eng) 1]"
    }
    
    ttk::combobox $w.f1.eng1 -width 20 -values [concat $leng [::msgcat::mc "<none>"]]
    ttk::combobox $w.f1.eng2 -width 20 -values [concat $leng [::msgcat::mc "<none>"]]
    
    $w.f1.eng1 set "[lindex $::engineconfig($::options(engine1)) 0] [lindex $::engineconfig($::options(engine1)) 1]"
    if { [catch {$w.f1.eng2 set "[lindex $::engineconfig($::options(engine2)) 0] [lindex $::engineconfig($::options(engine2)) 1]"}]} {
      $w.f1.eng2 set [::msgcat::mc "<none>"]
    }
    
    $w.f1.eng1 configure -state readonly
    $w.f1.eng2 configure -state readonly
    
    pack $w.f1.l $w.f1.eng1 $w.f1.eng2
    ttk::button $w.f2.ok -text Ok -command { ::engine::doAssociate ; destroy .associate }
    ttk::button $w.f2.cancel -text Cancel -command { destroy .associate }
    pack $w.f2.ok $w.f2.cancel -side left
  }
  # ##################################################################
  proc doAssociate {} {
    set neweng1 [ .associate.f1.eng1 get ]
    set neweng2 [ .associate.f1.eng2 get ]
    
    set eng1 ""
    set eng2 ""
    foreach eng [ array names ::engineconfig ] {
      if { $neweng1 == "[lindex $::engineconfig($eng) 0] [lindex $::engineconfig($eng) 1]" } {
        set eng1 $eng
      }
      if { $neweng2 == "[lindex $::engineconfig($eng) 0] [lindex $::engineconfig($eng) 1]" } {
        set eng2 $eng
      }
    }
    
    if {$eng1 == $eng2} {
      tk_messageBox -type ok -icon error -parent . -title [::msgcat::mc "Engines" ] -message [::msgcat::mc "Engines 1 & 2 must be different"]
      return
    }
    
    set todoInit1 0
    set todoInit2 0
    
    if {$eng1 != $::options(engine1) } {
      unload 1
      set ::options(engine1) $eng1
      set todoInit1 1
    }
    
    if {$eng2 != $::options(engine2) } {
      if { $::options(engine2) != "" } { unload 2 }
      set ::options(engine2) $eng2
      
      if {$::options(engine2) != ""} {
        set todoInit2 1
      }
    }
    
    if { $todoInit1 } { init 1 }
    if { $todoInit2 } { init 2 }
    
    setHash
    
    if { ! [isUci 1] } {
      set ::options(multipv) 1
      ::engine::setMultiPv 1
    }
    
  }
  # ##################################################################
  proc tournament {} {
    if { ! $::tournamentMode } { return }
    
    set w .tournament
    
    if {[winfo exists $w]} {
      focus $w
      return
    }
    
    if {$::options(engine2) == "" } {
      tk_messageBox -type ok -icon info -parent . -title [::msgcat::mc "Tournament"] -message [::msgcat::mc "Engine #2 not configured"]
      set ::tournamentMode 0
      return
    }
    
    toplevel $w
    wm title $w [::msgcat::mc "Tournament"]
    ::ui::setFullSize $w
    
    ttk::frame $w.fnb
    ttk::frame $w.fdown
    pack [ttk::notebook $w.fnb.nb] -fill both -expand 1
    
    # --------------- Time frame ---------------------
    $w.fnb.nb add [ttk::frame $w.fnb.nb.time] -text [::msgcat::mc "time"]
    set nb1 $w.fnb.nb.time
    
    set row 0
    
    ttk::radiobutton $nb1.rb1 -text [::msgcat::mc "Time bonus"] -value "timebonus" -variable ::options(engine_timeMode)
    grid $nb1.rb1 -row $row -column 0 -sticky w
    incr row
    ttk::label $nb1.lb1 -text [::msgcat::mc "White time"]
    grid $nb1.lb1 -row $row -column 0 -sticky w
    incr row
    spinbox $nb1.spW1 -textvar ::options(engine_wtime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    spinbox $nb1.spW2 -textvar ::options(engine_winc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    grid $nb1.spW1 -row $row -column 0 -sticky w
    grid $nb1.spW2  -row $row -column 1 -sticky w
    incr row
    
    ttk::label $nb1.lb2 -text [::msgcat::mc "Black time"]
    grid $nb1.lb2 -row $row -column 0 -sticky w
    incr row
    spinbox $nb1.spB1 -textvar ::options(engine_btime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    spinbox $nb1.spB2 -textvar ::options(engine_binc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    grid $nb1.spB1 -row $row -column 0 -sticky w
    grid $nb1.spB2  -row $row -column 1 -sticky w
    incr row
    
    ttk::radiobutton $nb1.rb2 -text [::msgcat::mc "Fixed depth"] -value "depth" -variable ::options(engine_timeMode)
    spinbox $nb1.sbDepth -background white -width 2 -from 1 -to 20 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    $nb1.sbDepth set 3
    
    ttk::radiobutton $nb1.rb3 -text [::msgcat::mc "Nodes (x1000)"] -value "nodes" -variable ::options(engine_timeMode)
    spinbox $nb1.sbNodes -background white -width 5 -from 5 -to 10000 -increment 5 -validate all -vcmd { regexp {^[0-9]+$} %P }
    $nb1.sbNodes set 10
    
    ttk::radiobutton $nb1.rb4 -text [::msgcat::mc "Sec / move"] -value "movetime" -variable ::options(engine_timeMode)
    spinbox $nb1.sbMoveTime -background white -width 3 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    $nb1.sbMoveTime set 5
    
    grid $nb1.rb2 -row $row -column 0 -sticky w
    grid $nb1.sbDepth -row $row -column 1 -sticky w
    incr row
    grid $nb1.rb3 -row $row -column 0 -sticky w
    grid $nb1.sbNodes -row $row -column 1 -sticky w
    incr row
    grid $nb1.rb4 -row $row -column 0 -sticky w
    grid $nb1.sbMoveTime -row $row -column 1 -sticky w
    incr row
    
    # --------------- Other settings frame -----------
    $w.fnb.nb add [ttk::frame $w.fnb.nb.settings] -text [::msgcat::mc "settings"]
    set nb2 $w.fnb.nb.settings
    
    ttk::frame $nb2.fbook -relief groove -borderwidth 1
    ttk::frame $nb2.fopening -relief groove -borderwidth 1
    ttk::frame $nb2.fgames -relief groove -borderwidth 1
    pack $nb2.fopening -side top -fill both -expand 1
    pack $nb2.fbook -side top -fill both -expand 1
    pack $nb2.fgames -side top -fill both -expand 1
    
    # load book names
    ttk::checkbutton $nb2.fbook.cbUseBook -text [::msgcat::mc "Use book"] -variable ::options(engine_useBook)
    set bookPath [file join $::instalDir "books" ]
    
    set bookList [ lsort -dictionary [ glob -nocomplain -tails -directory $bookPath *.bin ] ]
    set i 0
    set idx 0
    set lbook {}
    foreach file  $bookList {
      set f [file rootname [ file tail $file ] ]
      set lbook [concat $lbook $f]
    }
    
    ttk::combobox $nb2.fbook.combo -state readonly -width 12 -values $lbook
    
    $nb2.fbook.combo set [file rootname $::options(engine_bookToUse) ]
    pack $nb2.fbook.cbUseBook $nb2.fbook.combo -expand yes -fill both -side top
    
    # choose a specific opening
    ttk::checkbutton $nb2.fopening.cbOpening -text [::msgcat::mc "Specific opening"] -variable ::game::isOpening
    ttk::frame $nb2.fopening.fOpeningList -relief raised -borderwidth 1
    listbox $nb2.fopening.fOpeningList.lbOpening -yscrollcommand "$nb2.fopening.fOpeningList.ybar set" -xscrollcommand "$nb2.fopening.fOpeningList.xbar set" \
        -height 5 -list ::game::openingList -exportselection 0 ; # -width 50
    $nb2.fopening.fOpeningList.lbOpening selection set $::options(game_specificOpening)
    
    ttk::scrollbar $nb2.fopening.fOpeningList.ybar -command "$nb2.fopening.fOpeningList.lbOpening yview"
    ttk::scrollbar $nb2.fopening.fOpeningList.xbar -orient horiz -command "$nb2.fopening.fOpeningList.lbOpening xview"
    
    grid $nb2.fopening.fOpeningList.lbOpening -row 0 -column 0 -sticky nsew
    grid $nb2.fopening.fOpeningList.ybar -row 0 -column 1 -sticky ns
    grid $nb2.fopening.fOpeningList.xbar -row 1 -column 0 -sticky ew
    grid rowconfigure $nb2.fopening.fOpeningList 0 -weight 1
    grid columnconfigure $nb2.fopening.fOpeningList 0 -weight 1
    
    pack $nb2.fopening.cbOpening -fill x -side top
    pack $nb2.fopening.fOpeningList -expand yes -fill both -side top
    
    spinbox $nb2.fgames.sp -background yellow -width 3 -from 1 -to 200 -increment 1 -validate all -textvariable ::options(tournamentRounds) -vcmd { regexp {^[0-9]+$} %P }
    pack $nb2.fgames.sp
    
    # ------------- Bottom buttons -----------------
    ttk::button $w.fdown.close -text [::msgcat::mc "Play"] -command {
      focus .
      
      set nb1 .tournament.fnb.nb.time
      set nb2 .tournament.fnb.nb.settings
      
      set ::engine::chosenOpening [$nb2.fopening.fOpeningList.lbOpening curselection]
      if {$::options(engine_useBook)} {
        set ::options(engine_bookToUse) "[$nb2.fbook.combo get].bin"
      }
      set ::engine::data(wtime1) [expr [$nb1.spW1 get]*1000*60]
      set ::engine::data(btime1) [expr [$nb1.spB1 get]*1000*60]
      set ::engine::data(winc1) [expr [$nb1.spW2 get]*1000]
      set ::engine::data(binc1) [expr [$nb1.spB2 get]*1000]
      set ::engine::data(fixeddepth1) [$nb1.sbDepth get]
      set ::engine::data(fixednodes1) [expr [$nb1.sbNodes get]*1000]
      set ::engine::data(movetime1) [expr [$nb1.sbMoveTime get]*1000]
      
      if {$::options(engine_timeMode) == "depth" || $::options(engine_timeMode) == "nodes" || $::options(engine_timeMode) == "movetime"} {
        set ::engine::data(wtime1) 0
        set ::engine::data(btime1) 0
        set ::engine::data(winc1) 0
        set ::engine::data(binc1) 0
      }
      
      set ::engine::data(wtime2) $::engine::data(wtime1)
      set ::engine::data(btime2) $::engine::data(btime1)
      set ::engine::data(winc2) $::engine::data(winc1)
      set ::engine::data(binc2) $::engine::data(binc1)
      set ::engine::data(fixeddepth2) $::engine::data(fixeddepth1)
      set ::engine::data(fixednodes2) $::engine::data(fixednodes1)
      set ::engine::data(movetime2) $::engine::data(movetime1)
      
      destroy .tournament
      update
      ::engine::tournamentRun
    }
    
    ttk::button $w.fdown.cancel -text [::msgcat::mc "Cancel"] -command "focus .; destroy .tournament ; set ::tournamentMode 0"
    
    pack $w.fdown.close $w.fdown.cancel -expand yes -side left -padx 20 -pady 2
    
    pack $w.fnb -fill both -side top -expand 1
    pack $w.fdown -side top -fill x
  }
  # ##################################################################
  proc tournamentRun {} {
    global ::engine::chosenOpening ::engine::isOpening ::game::openingList ::engine::openingMovesList \
        ::engine::openingMovesHash ::engine::openingMoves ::engine::isTurn
    
    ::gameclock::new .fTop.fr.fclocks 2 1
    ::gameclock::new .fTop.fr.fclocks 1 1
    ::gameclock::reset 1
    ::gameclock::reset 2
    ::gameclock::setColor 1 "black"
    ::gameclock::setColor 2 "white"
    
    # if will follow a specific opening line
    if {$isOpening} {
      set fields [split [lindex $openingList $chosenOpening] ":"]
      set openingName [lindex $fields 0]
      set openingMoves [string trim [lindex $fields 1]]
      set openingMovesList ""
      set openingMovesHash ""
      foreach m [split $openingMoves] {
        # in case of multiple adjacent spaces in opening line
        if {$m =={}} {
          continue
        }
        set p [string trim $m]
        lappend openingMovesList [string trim [regsub {^[1-9]+\.} $p ""] ]
      }
      
      sc_game new
      lappend openingMovesHash [sc_pos hash]
      foreach m  $openingMovesList {
        ::game::moveAdd $m
        lappend openingMovesHash [sc_pos hash]
      }
    }
    
    array set ::engine::score {}
    set ::engine::score(1) 0.0
    set ::engine::score(2) 0.0
    for {set ::engine::currentRound 1} {$::engine::currentRound <= $::options(tournamentRounds)} {incr ::engine::currentRound} {
      # create a new game if a DB is opened
      sc_game new
      sc_game tags set -event "$::options(engine1) vs $::options(engine2)"
      sc_game tags set -round "$::engine::currentRound"
      
      if {$::engine::currentRound % 2 == 1} {
        set isTurn 1
        set white [::engine::getName 1]
        set black [::engine::getName 2]
        set sc "$::engine::score(1)-$::engine::score(2)"
      } else {
        set isTurn 2
        set white [::engine::getName 2]
        set black [::engine::getName 1]
        set sc "$::engine::score(2)-$::engine::score(1)"
      }
      
      sc_game tags set -white $white
      sc_game tags set -black $black
      wm title . "$sc $white-$black"
      
      sc_game tags set -date [::utils::date::today]
      if {[sc_base inUse [sc_base current]]} { catch {sc_game save 0}  }
      
      ::board::updateBoard -pgn -nolastmove
      
      # setup clocks
      if {$::options(engine_timeMode) == "timebonus"} {
        ::gameclock::setdSec 2 [expr $::engine::data(wtime1)/100]
        ::gameclock::setdSec 1 [expr $::engine::data(btime1)/100]
      } elseif {$::options(engine_timeMode) == "depth" || $::options(engine_timeMode) == "nodes" || $::options(engine_timeMode) == "movetime"} {
        ::gameclock::setdSec 1 0
        ::gameclock::setdSec 2 0
      }
      set ::engine::wentOutOfBook 0
      set ::engine::gameEnded 0
      ::gameclock::start [getClock 1]
      
      ::game::repetitionReset
      
      while { !$::engine::gameEnded && $::tournamentMode } {
        tournamentEngineGo $isTurn
        # case of game adjudication by user
        if {$::engine::gameEnded} { break }
        
        # if halfmove is resetted, clear list of FEN used by repetition detection
        if { [lindex [split [sc_pos fen]] 4] == 0 } { set ::game::lFen {} }
        
        if { [::game::repetition 0] } {
          set ::engine::score(1) [expr $::engine::score(1) + 0.5]
          set ::engine::score(2) [expr $::engine::score(2) + 0.5]
          set ::engine::gameEnded 1
          sc_game tags set -result "1/2-1/2"
          break
        }
        
        if { [::game::endOfGame] } {
          set ::engine::score($isTurn) [expr $::engine::score($isTurn) + 1.0]
          if {[sc_pos side] == "black" } {
            sc_game tags set -result "1-0"
          } else {
            sc_game tags set -result "0-1"
          }
          set ::engine::gameEnded 1
          break
        }
        
        set isTurn [oppN $isTurn]
      }
      
      sc_game save [sc_game number]
      
      if {! $::tournamentMode} {break}
      
    }
    
    set ::tournamentMode 0
    wm title . "$::engine::score(1)-$::engine::score(2) $::options(engine1) $::options(engine2)"
    tk_messageBox -type ok -icon info -parent . -title [::msgcat::mc "Tournament"] \
        -message "[::msgcat::mc Result]\n[::engine::getName 1] : $::engine::score(1)\n[::engine::getName 2] : $::engine::score(2)"
    
  }
  ################################################################################
  proc setGameResult { op } {
    if { !$::tournamentMode } { return }
    
    set ::engine::gameEnded 1
    sendToEngine "stop" 1
    sendToEngine "stop" 2
    
    # Force bestmove to avoid vwaiting for ever
    set ::engine::data(bestmove1) ""
    set ::engine::data(bestmove2) ""
    
    if { $op == "White wins" } {
      if { [getEngineColor 1] == "white" } {
        set ::engine::score(1) [expr $::engine::score(1) + 1.0]
      } else {
        set ::engine::score(2) [expr $::engine::score(2) + 1.0]
      }
    }
    if { $op == "Black wins" } {
      if { [getEngineColor 1] == "black" } {
        set ::engine::score(1) [expr $::engine::score(1) + 1.0]
      } else {
        set ::engine::score(2) [expr $::engine::score(2) + 1.0]
      }
    }
    if { $op == "Draw" } {
      set ::engine::score(1) [expr $::engine::score(1) + 0.5]
      set ::engine::score(2) [expr $::engine::score(2) + 0.5]
    }
  }
  ################################################################################
  proc getEngineColor { n } {
    if {$::engine::currentRound % 2 == 1} {
      if {$n == 1} {
        return "white"
      } else {
        return "black"
      }
    } else {
      if {$n == 1} {
        return "black"
      } else {
        return "white"
      }
    }
  }
  # ##################################################################
  proc oppN { n } {
    if {$n == 1} {return 2} else {return 1}
  }
  # ##################################################################
  proc getClock { n } {
    if { [getEngineColor $n] == "white"} {
      return 2
    } else {
      return 1
    }
  }
  # ##################################################################
  proc tournamentEngineGo { n } {
    global ::engine::isOpening ::engine::openingMovesList ::engine::openingMovesHash ::engine::openingMoves
    
    if { [::game::endOfGame] } { return }
    
    # make a move corresponding to a specific opening
    if {$isOpening} {
      set index 0
      
      set hpos [sc_pos hash]
      # Find a corresponding position in the opening line
      set length [llength $openingMovesHash]
      for {set i 0}   { $i < [expr $length-1] } { incr i } {
        set h [lindex $openingMovesHash $i]
        if {$h == $hpos} {
          set index [lsearch $openingMovesHash $h]
          set move [lindex $openingMovesList $index]
          # play the move
          ::game::moveAdd $move
          ::board::updateBoard -pgn
          update
          after 300
          ::gameclock::stop [getClock $n]
          ::gameclock::start [oppN [getClock $n]]
          
          if { [::engine::getEngineColor $n] == "white" } {
            ::gameclock::add [getClock $n] $::engine::data(winc$n)/1000
          } else  {
            ::gameclock::add [getClock $n] $::engine::data(binc$n)/1000
          }
          return
        }
      }
    }
    
    # use a book
    
    if {$::options(engine_useBook) && ! $::engine::wentOutOfBook} {
      set move [ ::book::getMove $::options(engine_bookToUse) [sc_pos fen] $::game::bookSlot]
      if {$move == ""} {
        set ::engine::wentOutOfBook 1
      } else  {
        ::game::moveAdd $move
        ::board::updateBoard -pgn
        update
        after 300
        ::gameclock::stop [ getClock $n ]
        ::gameclock::start [ oppN [ getClock $n ] ]
        if {$::options(engine_timeMode) == "timebonus"} {
          if { [::engine::getEngineColor $n] == "white" } {
            ::gameclock::add [getClock $n] $::engine::data(winc$n)/1000
          } else  {
            ::gameclock::add [getClock $n] $::engine::data(binc$n)/1000
          }
        }
        return
      }
    }
    
    if {[isUci $n]} {
      ::engine::sendToEngine "position fen [sc_pos fen]" $n
    } else {
      ::engine::sendToEngine "setboard [sc_pos fen]" $n
    }
    
    set wtime [expr [::gameclock::getdSec 2] * 100 ]
    set btime [expr [::gameclock::getdSec 1] * 100 ]
    
    # Send to the engine values with at least 1 second on each clock, even if the flag is down
    if {$wtime < 1000} {set wtime 1000}
    if {$btime < 1000} {set btime 1000}
    
    if {[isUci $n]} {
      if {$::options(engine_timeMode) == "timebonus"} {
        ::engine::sendToEngine "go wtime $wtime btime $btime winc $::engine::data(winc$n) binc $::engine::data(binc$n)" $n
      } elseif {$::options(engine_timeMode) == "depth"} {
        ::engine::sendToEngine "go depth $::engine::data(fixeddepth$n)" $n
      } elseif {$::options(engine_timeMode) == "movetime"} {
        ::engine::sendToEngine "go movetime $::engine::data(movetime$n)" $n
      } elseif {$::options(engine_timeMode) == "nodes"} {
        ::engine::sendToEngine "go nodes $::engine::data(fixednodes$n)" $n
      }
    } else {
      set wtime [expr $wtime / 1000 ]
      set btime [expr $btime / 1000 ]
      if {$wtime < 60 } {set wtime 60}
      if {$btime < 60 } {set btime 60}
      if { [::engine::getEngineColor $n] == "white" } {
        ::engine::sendToEngine "level 0 [expr int($wtime /60 )] [expr int($::engine::data(winc$n) / 1000)]" $n
      } else {
        ::engine::sendToEngine "level 0 [expr int($btime /60 )] [expr int($::engine::data(binc$n) / 1000)]" $n
      }
      ::engine::sendToEngine "go" $n
    }
    
    set ::engine::data(waitForBestMove$n) 1
    vwait ::engine::data(bestmove$n)
    
    ::game::moveAdd $::engine::data(bestmove$n)
    set ::engine::data(prevscore$n) $::engine::data(score$n)
    ::board::updateBoard -pgn
    
    # add time after a move played
    if {$::options(engine_timeMode) == "timebonus"} {
      if { [::engine::getEngineColor $n] == "white" } {
        ::gameclock::add [getClock $n] $::engine::data(winc$n)/1000
      } else  {
        ::gameclock::add [getClock $n] $::engine::data(binc$n)/1000
      }
    }
    ::gameclock::stop [getClock $n]
    ::gameclock::start [ oppN [ getClock $n ] ]
  }
  # ##################################################################
  proc unload { n } {
    if {!$::WindowsCE} {
      after cancel ::engine::pollSock $n
      chan event $::engine::data(sock$n) readable {}
    }
    ::engine::sendToEngine "stop" $n 0
    ::engine::sendToEngine "quit" $n 0
    
    if {$::WindowsCE} {
      set engine_quit 0
      #wait for Toga to close msg queues
      for {set i 0} {$i < 50} {incr i} {
        set msg_queue_info [sc_msg info $n read]
        if {[lindex $msg_queue_info 3] < 1} {
          set engine_quit 1
          break
        }
        update
        after 50
      }
      if {!$engine_quit} {
        tk_messageBox -type ok -icon error -parent . -title [::msgcat::mc "Error"] -message [::msgcat::mc "Engine may be still in memory !"]
      }
      sc_msg close $n
    } else {
      close $::engine::data(sock$n)
      close $::engine::data(serversock$n)
    }
  }
  
  if { ! $::WindowsCE } {
    # ##################################################################"
    proc Server1 {channel clientaddr clientport} {
      # puts "Connection from $clientaddr registered channel $channel 1"
      set ::engine::data(sock1) $channel
      chan configure $::engine::data(sock1) -blocking 0 -buffering line -buffersize 4096
    }
    proc Server2 {channel clientaddr clientport} {
      # puts "Connection from $clientaddr registered channel $channel 2"
      set ::engine::data(sock2) $channel
      chan configure $::engine::data(sock2) -blocking 0 -buffering line -buffersize 4096
    }
  }
  
  array set ::cacheIsUci ""
  
  # ##################################################################
  proc init { n } {
    if { $::options(engine$n) == "" } { return }
    
    reset $n
    
    if {$::WindowsCE} {
      set name [file join $::instalDir "$::options(engine$n).exe"]
      sc_msg init $n $::options(engine$n)
    } else {
      set name "./$::options(engine$n)/src/$::options(engine$n)"
      set port [ lindex $::engineconfig($::options(engine$n)) 2 ]
      set ::engine::data(serversock$n) [socket -server ::engine::Server$n $port]
    }
    
    if {![file exists $name]} {
      tk_messageBox -type ok -icon error -parent . -title [::msgcat::mc "Engine error"] -message "The engine $name can't be found"
      return
    }
    
    if { [catch { exec $name & } result ] } {
      tk_messageBox -type ok -icon error -parent . -title [::msgcat::mc "Engine error"] -message "exec error $result for $name"
      return
    }
    
    set ::cacheIsUci($n) [ ::engine::isUci $n ]
    
    set count 0
    if {$::WindowsCE} {
      set ready 0
      while {!$ready && $count < 300 } {
        incr count
        set qinfor [sc_msg info $n read]
        set qinfow [sc_msg info $n write]
        if {[lindex $qinfor 3] >= 1 && [lindex $qinfow 2] >= 1} {
          set ready 1
        } else {
          after 10
        }
      }
      if {!$ready} {
        tk_messageBox -type ok -icon error -parent . -title "Fatal Error" \
            -message "Msg queue error\nread [sc_msg info $n read]\nwrite [sc_msg info $n write]"
        return
      }
      if {[isUci $n]} {
        sendToEngine "uci" $n
      } else {
        sendToEngine "xboard" $n
        sendToEngine "protover 2" $n
        sendToEngine "ponder off" $n
        sendToEngine "post" $n
      }
      ::engine::pollSock $n
    } else {
      ::vwaitTimed ::engine::data(sock$n) 5000
      ::engine::pollSock $n
      if {[isUci $n]} {
        sendToEngine "uci" $n
      } else {
        sendToEngine "xboard" $n
        sendToEngine "protover 2" $n
        sendToEngine "ponder off" $n
        sendToEngine "post" $n
      }
    }
    
    ::vwaitTimed ::engine::data(uciok$n) 10000
    
    # no need to set multiPV for engines != 1 (but may lead to comparison problems in tournament)
    if {$n == 1} { setMultiPv $n }
    
    sendToEngine "setpriority $::engine::priority" $n
  }
  
  # ##################################################################
  proc restartEngine { n {warnUser 1}} {
    
    after cancel ::engine::pollSock $n
    
    if {$warnUser} {
      tk_messageBox -type ok -icon warning -parent . -title [::msgcat::mc "Engine error"] -message "The engine #$n probably crashed\nIt will be restarted"
    }
    
    unload $n
    
    if {$::WindowsCE} {
      reset $n
      sc_msg close $n
      sc_msg init $n $::options(engine$n)
    } else {
      set ::engine::data(sock$n) ""
      set ::engine::data(uciok$n) 0
      set ::engine::data(waitForReadyOk$n) 0
      set ::engine::data(waitForBestMove$n) 0
      catch { chan event $::engine::data(sock$n) readable {} }
      catch { close $data(sock$n) }
    }
    init $::cmd $n
    # reset analysis, play status
    set ::engine::data(running$n) 0
    .fTop.fr.fCmd.bAnalyze configure -image engine_off -relief flat -state normal
    resetAnalysis $n
    if {$::playMode} {
      set ::playMode 0
      after cancel ::game::engineGo $n
      set ::options(showAnalysis) $::options(showAnalysisOld)
      set ::options(showBook) $::options(showBookOld)
      ::infopanel::updateAnalysis
      ::infopanel::updateBookText
      ::gameclock::delete 1
      ::gameclock::delete 2
    }
  }
  # ##################################################################
  proc checkEngineAlive { n } {
    if {$::WindowsCE} {
      set qinfor [sc_msg info $n read]
      set qinfow [sc_msg info $n write]
      if {[lindex $qinfor 3] == 1 && [lindex $qinfow 2] == 1} {
        return 1
      } else {
        set msg "read $qinfor\nwrite $qinfow"
        tk_messageBox -type ok -icon warning -parent . -title "Engine dead" -message $msg
        return 0
      }
    } else {
      global ::engine::data
      if {[eof $data(sock$n)]} {
        restartEngine $n
        return 0
      }
      return 1
    }
  }
  # ##################################################################"
  if {!$::WindowsCE} {
    proc checkSocketReady { n } {
      set ::writable$n 0
      chan event $::engine::data(sock$n) writable "set ::writable$n 1"
      ::vwaitTimed ::writable$n 3000
      chan event $::engine::data(sock$n) writable ""
    }
  }
  # ##################################################################"
  proc sendToEngine { msg n {checkAlive 1} } {
    # ::log "send $n $msg"
    # puts ">> $n $msg"
    if {$::WindowsCE} {
      if { $checkAlive } {
        if { ! [checkEngineAlive $n] } {
          tk_messageBox -type ok -icon error -parent . -title "Error" -message "Engine $n dead"
          # restartEngine $n
          return
        }
      }
      sc_msg send $n $msg
    } else {
      if { $checkAlive && ! [checkEngineAlive $n] } { return }
      # checkSocketReady $n
      puts $::engine::data(sock$n) $msg
      flush $::engine::data(sock$n)
    }
  }
  # ##################################################################
  proc pollSock { n } {
    after cancel ::engine::pollSock $n
    set ::interesting 0
    
    while {1} {
      if {$::WindowsCE} {
        set line [sc_msg recv $n]
      } else {
        set line [gets $::engine::data(sock$n)]
      }
      
      if {$line == ""} {break}
      
      #DEBUG
      if {[string match "ERROR*" $line]} {
        tk_messageBox -type ok -icon warning -parent . -title "Engine sends error" -message $line
      }
      # puts "recv $n $line"
      if { $::cacheIsUci($n) } {
        readSock $line $n
      } else {
        readXboard $line $n
      }
    }
    
    if { $::interesting } {
      ::infopanel::updateAnalysis $n
    }
    
    after 300 ::engine::pollSock $n
  }
  # ##################################################################
  proc readSock { line n } {
    global ::engine::data ::engine::infoToken
    
    # To speed up parsing of engine's output. Should be removed if currmove info is used
    if {[string first "info currmove" $line ] == 0} { return }
    
    # if {![string match "info *" $line ]} { puts "readSock $line" }
    
    if {[string match "error*" $line]} {
      tk_messageBox -type ok -icon warning -parent . -title "Engine sends error" -message $line
    }
    if { $line == "uciok" } { set data(uciok$n) 1 ; return  }
    if { $line == "readyok" } {
      if {$data(waitForReadyOk$n)} { set data(waitForReadyOk$n) 0 }
      set data(readyok$n) 1
      return
    }
    if {[string match "bestmove*" $line]} {
      set d [split $line]
      set data(bestmove$n) [lindex $d 1]
      # get ponder move
      if {[lindex $d 2] == "ponder"} {
        set data(ponder$n) [lindex $d 3]
      } else {
        set data(ponder$n) ""
      }
      if { $data(waitForBestMove$n) } {
        set data(waitForBestMove$n) 0
        return
      }
    }
    if {[string match "id *name *" $line]} {
      set data(name$n) [ regsub {id[ ]?name[ ]?} $line "" ]
    }
    
    set toBeFormatted 0
    
    # parse an info line
    if {[string first "info" $line ] == 0} {
      set d [split $line]
      set length [llength $d]
      for {set i 0} {$i < $length } {incr i} {
        set t [lindex $d $i]
        if { $t == "info" } { continue }
        if { $t == "depth" } { incr i ; set data(depth$n) [ lindex $d $i ] ; continue }
        if { $t == "seldepth" } { incr i ; set data(seldepth$n) [ lindex $d $i ] ; continue }
        if { $t == "time" } { incr i ; if {$data(multipvnum$n) == 1} {set data(time$n) [ lindex $d $i ]} ; continue }
        if { $t == "nodes" } { incr i ; if {$data(multipvnum$n) == 1} { set data(nodes$n) [ lindex $d $i ]} ; continue }
        if { $t == "pv" } {
          set ::interesting 1
          incr i
          set data(pv$n) [ lindex $d $i ]
          incr i
          while { [ lsearch -exact $infoToken [ lindex $d $i ] ] == -1 && $i < $length } {
            append data(pv$n) " " [ lindex $d $i ]
            incr i
          }
          set toBeFormatted 1
          
          incr i -1
          continue
        }
        if { $t == "multipv" } {
          incr i
          set data(multipvnum$n) [ lindex $d $i ]
          continue
        }
        if { $t == "score" } {
          set ::interesting 1
          incr i
          set next [ lindex $d $i ]
          if { $next == "cp" } {
            incr i
            set data(score$n) [ lindex $d $i ]
          }
          
          if { $next == "mate" } {
            incr i
            set next [ lindex $d $i ]
            set data(scoremate$n) $next
            if {$next < 0} { set data(score$n) -32700 } else  { set data(score$n) 32700 }
          }
          
          # convert the score to white's perspective (not engine's one)
          if { [sc_pos side] == "black"} {
            set data(score$n) [ expr 0.0 - $data(score$n) ]
            if { $data(scoremate$n) != ""} {
              set data(scoremate$n) [ expr 0 - $data(scoremate$n) ]
            }
          }
          set data(score$n) [expr {double($data(score$n)) / 100.0} ]
          
          # don't consider lowerbound & upperbound score info
          continue
        }
        # if { $t == "currmove" } { incr i ; set data(currmove$n) [ lindex $d $i ] ; continue }
        if { $t == "currmovenumber" } { incr i ; set data(currmovenumber$n) [ lindex $d $i ] ; continue }
        if { $t == "hashfull" } { incr i ; set data(hashfull$n) [ lindex $d $i ] ; continue }
        if { $t == "nps" } { incr i ; set nps [ lindex $d $i ] ; set data(nps$n) $nps ; continue }
        if { $t == "tbhits" } { incr i ; set data(tbhits$n) [ lindex $d $i ] ; continue }
        if { $t == "cpuload" } { incr i ; set data(cpuload$n) [ lindex $d $i ] ; continue }
        if { $t == "string" } { continue }
        if { $t == "refutation" } { continue }
        if { $t == "currline" } { continue }
      };# end for data loop
      
      # handle the case an UCI engine does not send multiPV
      if { $data(multipvnum$n) == 0 } { set data(multipvnum$n) 1 }
      
      # convert to something more readable
      if { $toBeFormatted && $::options(engineUCIFormat) } {
        set data(pv$n) [formatPv $data(pv$n) $n]
        set toBeFormatted 0
      }
      
      if { $data(multipvnum$n) <= $::options(multipv) } {
        if { $data(multipvnum$n) <= [llength $data(multipv$n)] } {
          lset data(multipv$n) [expr $data(multipvnum$n) - 1] "$data(depth$n) $data(score$n) [list $data(pv$n)] $data(scoremate$n)"
        } else  {
          lappend data(multipv$n) "$data(depth$n) $data(score$n) [list $data(pv$n)] $data(scoremate$n)"
        }
      }
    } ;# end of info line
    
  }
  
  # ##################################################################
  proc formatPv { moves n } {
    set tmp ""
    
    sc_info preMoveCmd {}
    set altered [sc_game altered]
    if {![sc_pos isAt vend]} {
      sc_var create
      set tmp [sc_move addUCI $moves]
      set var [sc_var number]
      sc_var exit
      sc_var deletefree $var
    } else {
      set count 0
      set tmp [sc_move addUCI $moves]
      set count [llength $tmp]
      
      if { $count !=0 } {
        sc_move back $count
        sc_game truncatefree
      }
    }
    
    sc_info preMoveCmd preMoveCommand
    set tmp [string trim $tmp]
    sc_game setaltered $altered
    return $tmp
  }
  ################################################################################
  # UCI moves use long notation
  # returns 1 if an error occured when entering a move
  ################################################################################
  proc sc_move_add { moves n } {
    
    # CODE MORT !!
    
    if { ! $::cacheIsUci($n) } {
      return [ catch { sc_move addSan $moves } ]
    }
    
    foreach m $moves {
      # get rid of leading piece : to be removed ??
      # set c [string index $m 0]
      # if {$c == "K" || $c == "Q" || $c == "R" || $c == "B" || $c == "N"} { set m [string range $m 1 end] }
      set s1 [string range $m 0 1]
      set s1 [::board::sq $s1]
      set s2 [string range $m 2 3]
      set s2 [::board::sq $s2]
      if {[string length $m] > 4} {
        set promo [string range $m 4 end]
        # inverse transformation : const char PIECE_CHAR [] = "xKQRBNP.xkqrbnpxMm";
        # it seems capitalisation does not matter (see addMove proc in main.tcl)
        switch -- $promo {
          q { set p 2}
          r { set p 3}
          b { set p 4}
          n { set p 5}
          default {puts "Promo error $promo"}
        }
        if { [catch { sc_move add $s1 $s2 $p } ] } { return 1}
      } else  {
        if { [catch { sc_move add $s1 $s2 0 } ] } { return 1 }
      }
    }
    return 0
  }
  ################################################################################
  # returns "Name Version"
  proc getName {n} {
    if {$::options(engine$n) == ""} {return ""}
    return "[lindex $::engineconfig($::options(engine$n)) 0] [lindex $::engineconfig($::options(engine$n)) 1]"
  }
  ################################################################################
  proc OptimizeSpeed {} {
    if { $::options(engineOptimizeSpeed) } {
      set ::options(multipv_old) $::options(multipv)
      set ::options(multipv) 1
      ::engine::setMultiPv 1
      ::engine::setMultiPv 2
      set ::options(engineUCIFormat) 0
    } else {
      set ::options(multipv) $::options(multipv_old)
      ::engine::setMultiPv 1
      ::engine::setMultiPv 2
      set ::options(engineUCIFormat) 1
    }
  }
  ################################################################################
  proc isUci { n } {
    if { [ lindex $::engineconfig($::options(engine$n)) 4 ] == "uci" } { return 1 } else { return 0 }
  }
  ################################################################################
  #   X B O A R D specific code
  ################################################################################
  proc readXboard { line n } {
    global ::engine::data
    
    # puts "readXboard $line"
    
    set data(uciok$n) 1
    # ply score time nodes pv
    set res [scan $line "%d %d %d %s %\[^\n\]\n" \
        temp_depth temp_score temp_time temp_nodes temp_moves]
    if {$res == 5} {
      # Phalanx inverts score : TODO check this for other ported xboard engines in the future
      if { [sc_pos side] == "black" } {
        set temp_score [expr { 0.0 - $temp_score } ]
      }
      set data(depth$n) $temp_depth
      set data(score$n) $temp_score
      # Convert score to pawns from centipawns:
      set data(score$n) [expr {double($data(score$n)) / 100.0} ]
      set data(pv$n) $temp_moves
      set data(time$n) [ expr $temp_time *10]
      set data(nodes$n) $temp_nodes
      set data(scoremate$n) 0
      if {$data(time$n) != 0} {
        set data(nps$n) [expr int( ($data(nodes$n) * 1000) / $data(time$n)) ]
      } else {
        set data(nps$n) 0
      }
      set data(multipv$n) [ list "$data(depth$n) $data(score$n) [::trans [list $data(pv$n)]] $data(scoremate$n)" ]
      set ::interesting 1
      return
    }
    
    # Check for a "stat01:" line, the reply to the "." command:
    #
    if {! [string compare [string range $line 0 6] "stat01:"]} {
      if {[scan $line "%s %d %s %d" \
            dummy temp_time temp_nodes temp_depth] == 4} {
        set data(depth$n) $temp_depth
        set data(time$n) $temp_time
        set data(nodes$n) $temp_nodes
        set data(time$n) [expr {double($data(time$n)) / 100.0} ]
      }
      return
    }
    # if the engine moves
    set res [ scan $line "my move is %s" move]
    if { $res != 1 } { set res [ scan $line "move %s" move] }
    if { $res == 1 } {
      if { $data(waitForBestMove$n) } {
        set data(waitForBestMove$n) 0
      }
      set ::engine::data(bestmove$n) $move
    }
    
  }
  ################################################################################
  #   end of X B O A R D specific code
  ################################################################################
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges
proc importPgnGame {} {
  if {[winfo exists .importWin]} { return }
  set w [toplevel .importWin]
  wm title $w "Scid: Import PGN game"
  
  wm minsize $w 50 5
  frame $w.b
  pack $w.b -side bottom
  set pane [::utils::pane::Create $w.pane edit err 650 300 0.8]
  pack $pane -side top -expand true -fill both
  set edit $w.pane.edit
  text $edit.text -height 12 -width 80 -wrap none -background white \
      -yscroll "$edit.ybar set" -xscroll "$edit.xbar set"  -setgrid 1
  # Override tab-binding for this widget:
  bind $edit.text <Key-Tab> "[bind all <Key-Tab>]; break"
  ttk::scrollbar $edit.ybar -command "$edit.text yview" -takefocus 0
  ttk::scrollbar $edit.xbar -orient horizontal -command "$edit.text xview" \
      -takefocus 0
  grid $edit.text -row 0 -column 0 -sticky nesw
  grid $edit.ybar -row 0 -column 1 -sticky nesw
  grid $edit.xbar -row 1 -column 0 -sticky nesw
  grid rowconfig $edit 0 -weight 1 -minsize 0
  grid columnconfig $edit 0 -weight 1 -minsize 0
  
  # Right-mouse button cut/copy/paste menu:
  menu $edit.text.rmenu -tearoff 0
  $edit.text.rmenu add command -label "Cut" -command "tk_textCut $edit.text"
  $edit.text.rmenu add command -label "Copy" -command "tk_textCopy $edit.text"
  $edit.text.rmenu add command -label "Paste" -command "tk_textPaste $edit.text"
  $edit.text.rmenu add command -label "Select all" -command \
      "$edit.text tag add sel 1.0 end"
  bind $edit.text <ButtonPress-3> "tk_popup $edit.text.rmenu %X %Y"
  
  text $pane.err.text -height 4 -width 75 -wrap word \
      -yscroll "$pane.err.scroll set"
  $pane.err.text insert end ImportHelp1
  $pane.err.text insert end "\n"
  $pane.err.text insert end ImportHelp2
  $pane.err.text configure -state disabled
  ttk::scrollbar $pane.err.scroll -command "$pane.err.text yview" -takefocus 0
  pack $pane.err.scroll -side right -fill y
  pack $pane.err.text -side left -expand true -fill both
  
  button $w.b.paste -text "PasteCurrentGame" -command {
    .importWin.pane.edit.text delete 1.0 end
    .importWin.pane.edit.text insert end [sc_game pgn -width 70]
    .importWin.pane.err.text configure -state normal
    .importWin.pane.err.text delete 1.0 end
    .importWin.pane.err.text configure -state disabled
  }
  button $w.b.clear -text "Clear" -command {
    .importWin.pane.edit.text delete 1.0 end
    .importWin.pane.err.text configure -state normal
    .importWin.pane.err.text delete 1.0 end
    .importWin.pane.err.text configure -state disabled
  }
  button $w.b.ok -text "Import" -command {
    set err [catch {sc_game import \
          [.importWin.pane.edit.text get 1.0 end]} result]
    .importWin.pane.err.text configure -state normal
    .importWin.pane.err.text delete 1.0 end
    .importWin.pane.err.text insert end $result
    .importWin.pane.err.text configure -state disabled
    if {! $err} {
      updateBoard -pgn
      updateTitle
    }
  }
  button $w.b.cancel -textvar Close -command {
    destroy .importWin; focus .
  }
  frame $w.b.space -width 20
  pack $w.b.paste $w.b.clear $w.b.space -side left -padx 2 -pady 2
  pack $w.b.cancel $w.b.ok -side right -padx 10 -pady 5
  # Paste the current selected text automatically:
  if {[catch {$w.pane.edit.text insert end [selection get]}]} {
    # ?
  }
  # Select all of the pasted text:
  $w.pane.edit.text tag add sel 1.0 end
  
  bind $w <F1> { helpWindow Import }
  bind $w <Alt-i> { .importWin.b.ok invoke }
  bind $w <Alt-p> { .importWin.b.paste invoke }
  bind $w <Alt-c> { .importWin.b.clear invoke }
  bind $w <Escape> { .importWin.b.cancel invoke }
  # bind $w.pane.edit.text <Any-KeyRelease> { .importWin.b.ok invoke }
  focus $w.pane.edit.text
}


proc importClipboardGame {} {
  importPgnGame
  catch {event generate .importWin.pane.edit.text <<Paste>>}
}

proc importPgnLine {line} {
  importPgnGame
  set w .importWin.pane.edit.text
  $w delete 1.0 end
  $w insert end $line
  $w tag add sel 1.0 end
  focus $w
}

proc importMoveList {line} {
  sc_move start
  sc_move addSan $line
  updateBoard
}

set importPgnErrors ""

### Import file of Pgn games:

proc importPgnFile {} {
  global importPgnErrors
  
  set err ""
  if {[sc_base isReadOnly]} { set err "This database is read-only." }
  if {![sc_base inUse]} { set err "This is not an open database." }
  if {$err != ""} {
    tk_messageBox -type ok -icon error -title "Scid: Error" -message $err
    return
  }
  if {[sc_info gzip]} {
    set ftypes {
      { "Portable Game Notation files" {".pgn" ".PGN" ".pgn.gz"} }
      { "Text files" {".txt" ".TXT"} }
      { "All files" {"*"} }
    }
  } else {
    set ftypes {
      { "Portable Game Notation files" {".pgn" ".PGN"} }
      { "Text files" {".txt" ".TXT"} }
      { "All files" {"*"} }
    }
  }
  set fnames [tk_getOpenFile -multiple 1 -filetypes $ftypes -title "Import from PGN files" ]
  if {$fnames == ""} { return }
  foreach fname $fnames {
    doPgnFileImport $fname "" 1
  }
}
################################################################################
#
################################################################################
proc doPgnFileImport {fname text {multiple 0} } {
  set w .ipgnWin
  if {[winfo exists $w] && ! $multiple } { destroy $w }
  if {! [winfo exists $w]} {
    toplevel $w
    wm title $w "Importing PGN file"
    
    canvas $w.progress -width 200 -height 20 -bg white -relief solid -border 1
    $w.progress create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
    $w.progress create text 195 10 -anchor e -font font_Regular -tags time -fill black -text "0:00 / 0:00"
    
    pack $w.progress -side bottom
    
    frame $w.buttons
    pack $w.buttons -side bottom -fill x
    button $w.buttons.stop -text "Stop" -command {sc_progressBar}
    button $w.buttons.close -text "Close" -command "focus .; destroy $w"
    pack $w.buttons.close $w.buttons.stop -side right -ipadx 5 -padx 5 -pady 2
    
    pack [frame $w.tf] -side top -expand yes -fill both
    text $w.text -height 4 -width 30 -background gray90 \
        -wrap none -cursor watch -setgrid 1 -yscrollcommand "$w.ybar set"
    ttk::scrollbar $w.ybar -command "$w.text yview"
    pack $w.ybar -in $w.tf -side right -fill y
    pack $w.text -in $w.tf -side left -fill both -expand yes
  }
  
  sc_progressBar $w.progress bar 201 21 time
  update
  catch {grab $w.buttons.stop}
  bind $w <Escape> "$w.buttons.stop invoke"
  $w.buttons.close configure -state disabled
  $w.text insert end $text
  $w.text insert end "Importing PGN games from [file tail $fname]...\n\n"
  $w.text configure -state disabled
  
  set importPgnErrors ""
  set err [catch {sc_base import file $fname} result]
  set warnings ""
  $w.text configure -state normal
  $w.text configure -cursor top_left_arrow
  if {$err} {
    $w.text insert end $result
  } else {
    set nImported [lindex $result 0]
    set warnings [lindex $result 1]
    set str "Imported $nImported "
    if {$nImported == 1} { append str "game" } else { append str "games" }
    if {$warnings == ""} {
      append str " no errors"
    } else {
      append str ".\nPGN errors:\n$warnings"
    }
    $w.text insert end "$str\n"
  }
  
  $w.text configure -state disabled
  $w.buttons.close configure -state normal
  $w.buttons.stop configure -state disabled
  catch {grab release $w.buttons.stop}
  bind $w <Escape> "$w.buttons.close invoke; break"
  # Auto-close import progress window if there were no errors/warnings?
  if {!$err  &&  $warnings == "" && ! $multiple} { destroy $w }
  
  update
}

###
### End of file: import.tcl
###

### ScidPocket.
### Copyright (C) 2007  Pascal Georges
#
array set informant {}
set informant("!?") 0.5
set informant("?") 1.5
set informant("??") 3.0
set informant("?!") 0.5
set informant("+=") 0.5
set informant("+/-") 1.5
set informant("+-") 3.0
set informant("++-") 5.5

################################################################################
#                        B A S E
################################################################################
namespace eval base { }
################################################################################
proc ::base::baseClose {} {
  
  if {[sc_base inUse]} {
    if {![::game::ConfirmDiscard]} {
      return
    }
    sc_base close
    sc_base switch clipbase
    ::board::updateBoard -nolastmove
    ::board::updateTitle
    ::ui::updateMenus
  }
}
################################################################################
proc ::base::baseIsCompactable {} {
  # Only a database that is in use, not read-only, and not the
  # clipbase, can be compacted:
  if {! [sc_base inUse]} { return 0 }
  if {[sc_base isReadOnly]} { return 0 }
  if {[sc_base current] == [sc_info clipbase]} { return 0 }
  return 1
}
################################################################################
proc ::base::baseCompact {} {
  makeCompactWin
}

################################################################################
# makeCompactWin:
# Opens the database compaction dialog box.
proc ::base::makeCompactWin {} {
  if {! [baseIsCompactable]} {
    tk_messageBox -title "Scid" -type ok -icon info -message "Base not\ncompactable"
    return
  }
  
  set w .compactWin
  toplevel $w
  wm title $w "Scid: Compact database"
  ::ui::setFullSize $w
  
  pack [ttk::notebook $w.nb] -fill both -expand 1
  
  # --------- Names -----------------
  $w.nb add [ttk::frame $w.nb.fnames] -text "Names" -sticky nsew
  set f1 [ttk::frame $w.nb.fnames.f1]
  set f2 [ttk::frame $w.nb.fnames.f2]
  pack $f1 $f2 -fill x
  
  for {set i 0} {$i < 3} {incr i} { grid columnconfigure $f1 $i -weight 1 }
  
  ttk::label $f1.title -text "Name file"
  grid $f1.title -columnspan 3 -row 0 -column 0 -sticky n
  ttk::label $f1.nt -text "  Names"
  grid $f1.nt -row 1 -column 1 -sticky e
  ttk::label $f1.ut -text "  Unused"
  grid $f1.ut -row 1 -column 2 -sticky e
  ttk::frame $f1.h -height 1 -relief solid
  grid $f1.h -columnspan 3 -row 2 -column 0 -sticky we
  set row 3
  set ndata [sc_compact stats names]
  set idx 0
  foreach n {p e s r} name {Players Events Sites Rounds} {
    ttk::label $f1.t$n -text "$name)  "
    ttk::label $f1.n$n -text "  [::utils::thousands [lindex $ndata $idx]]"
    incr idx
    ttk::label $f1.u$n -text "  [::utils::thousands [lindex $ndata $idx]]"
    incr idx
    grid $f1.t$n -row $row -column 0 -sticky w
    grid $f1.n$n -row $row -column 1 -sticky e
    grid $f1.u$n -row $row -column 2 -sticky e
    incr row
  }
  
  ttk::button $f2.n -text "Compact names" -command ::base::compactNames
  ttk::button $f2.cancel -text "Cancel" -command "focus . ; destroy .compactWin"
  pack $f2.n $f2.cancel -side left -fill x
  
  # --------- Games -----------------
  $w.nb add [ttk::frame $w.nb.fgames] -text "Games" -sticky nsew
  
  set f1 [ttk::frame $w.nb.fgames.f1]
  set f2 [ttk::frame $w.nb.fgames.f2]
  pack $f1 $f2 -fill x
  
  for {set i 0} {$i < 3} {incr i} { grid columnconfigure $f1 $i -weight 1 }
  
  ttk::label $f1.title -text "Game file"
  grid $f1.title -columnspan 3 -row 0 -column 0 -sticky n
  ttk::label $f1.gt -text "  [::utils::string::Capital games]"
  grid $f1.gt -row 1 -column 1 -sticky e
  ttk::label $f1.st -text "  Size (kb)"
  grid $f1.st -row 1 -column 2 -sticky e
  ttk::frame $f1.h -height 1 -relief solid
  grid $f1.h -columnspan 3 -row 2 -column 0 -sticky we
  set row 3
  set ndata [sc_compact stats games]
  set idx 0
  foreach g {current compact} name {CurrentState AfterCompaction} {
    ttk::label $f1.t$g -text "$name  "
    ttk::label $f1.g$g -text "  [::utils::thousands [lindex $ndata $idx]]"
    incr idx
    set kbytes [expr {int(([lindex $ndata $idx] + 512) / 1024)} ]
    ttk::label $f1.s$g -text "  [::utils::thousands $kbytes]"
    incr idx
    grid $f1.t$g -row $row -column 0 -sticky w
    grid $f1.g$g -row $row -column 1 -sticky e
    grid $f1.s$g -row $row -column 2 -sticky e
    incr row
  }
  
  ttk::button $f2.n -text "Compact names" -command ::base::compactGames
  ttk::button $f2.cancel -text "Cancel" -command "focus . ; destroy .compactWin"
  pack $f2.n $f2.cancel -side left -fill x
  
}

################################################################################
proc ::base::compactNames {} {
  set w .compactWin
  set stats [sc_compact stats names]
  if {[lindex $stats 1] == 0  &&  [lindex $stats 3] == 0  && \
        [lindex $stats 5] == 0  &&  [lindex $stats 7] == 0} {
    tk_messageBox -type ok -icon info -parent $w -title "Scid: compaction" \
        -message "There are no unused names,\nso the name file is\nalready fully compacted."
    return
  }
  progressWindow "Scid" "Compacting the name file..."
  busyCursor .compactWin
  set err [catch {sc_compact names} result]
  unbusyCursor .compactWin
  closeProgressWindow
  set w .compactWin
  if {$err} {
    tk_messageBox -type ok -icon warning -parent $w \
        -title "Error compacting file" -message $result
  } else {
    tk_messageBox -type ok -icon info -parent $w \
        -title "Name file compacted" \
        -message "The name file for the database\n \"[file tail [sc_base filename]]\"\nwas compacted."
  }
  grab release $w
  destroy $w
  ::board::updateBoard -nolastmove
  ::board::updateTitle
}

################################################################################
proc ::base::compactGames {} {
  set w .compactWin
  set stats [sc_compact stats games]
  if {[lindex $stats 1] == [lindex $stats 3]  && \
        [lindex $stats 0] == [lindex $stats 2]} {
    tk_messageBox -type ok -icon info -parent $w -title "Scid: compaction" \
        -message "The game file is already fully compacted."
    return
  }
  progressWindow "Scid" "Compacting the game file..." "Cancel" "sc_progressBar"
  busyCursor .
  set err [catch {sc_compact games} result]
  unbusyCursor .
  closeProgressWindow
  if {$err} {
    tk_messageBox -type ok -icon warning -parent $w \
        -title "Scid: Error compacting file" -message $result
  } else {
    tk_messageBox -type ok -icon info -parent $w \
        -title "Scid: Game file compacted" \
        -message "The game file for the database\n \"[file tail [sc_base filename]]\"\nwas compacted."
  }
  grab release $w
  destroy $w
  ::board::updateBoard -nolastmove
  ::board::updateTitle
}

################################################################################
proc ::base::baseNew {{fName ""}} {
  if {[sc_base count free] == 0} {
    tk_messageBox -title "Scid" -type ok -icon info -message "Too many databases open\nclose one first"
    return
  }
  set ftype {}
  
  set fName [tk_getSaveFile -initialdir $::initialDir(base) -filetypes $ftype -title "Create database"]
  if {$fName == ""} {
    # do nothing
  } else {
    set fName [file rootname $fName]
    tk_messageBox -title "Scid" -type ok -icon info -message "Creation of $fName"
    if {[catch {sc_base create $fName} result]} {
      tk_messageBox -icon warning -type ok -parent . -title "Unable to create base" -message $result
    }
  }
  ::board::updateBoard -nolastmove
  ::board::updateTitle
  ::ui::updateMenus
}
################################################################################
proc ::base::importPgnFile {} {
  ::importPgnFile
  ::board::updateBoard -nolastmove
  ::board::updateTitle
}
################################################################################
proc ::base::exportPgnFile {} {
  # Check that we have some games to export:
  if {![sc_base inUse]} {
    tk_messageBox -title "Scid: Empty database" -type ok -icon info \
        -message "This is an empty database,\nthere are no games to export."
    return
  }
  if {[sc_filter count] == 0} {
    tk_messageBox -title "Scid: Filter empty" -type ok -icon info -message "The filter contains no games."
    return
  }
  
  set ftype { { "PGN files" {".pgn"} } { "All files" {"*"} } }
  set fName [tk_getSaveFile -initialdir $::initialDir(base) -filetypes $ftype -defaultextension ".pgn" -title "a PGN file"]
  if {$fName == ""} { return }
  
  progressWindow "Scid" "Exporting games..." "Cancel" "sc_progressBar"
  
  sc_base export filter "PGN" $fName -append 0 -starttext "" -endtext "" -comments 1 -variations 1 \
      -space 1 -symbols 1 -indentC 0 -indentV 0 -column 0 -noMarkCodes 0 -convertNullMoves 1
  
  closeProgressWindow
}

################################################################################
proc ::base::baseOpen {{fName ""}} {
  if {[sc_base count free] == 0} {
    tk_messageBox -type ok -icon info -title "Scid" \
        -message "Too many databases are open; close one first"
    return
  }
  
  if {[sc_info gzip]} {
    set ftype {
      { "All Scid files" {".si4" ".si3" ".pgn" ".pgn.gz" ".epd" ".epd.gz"} }
      { "Scid databases, PGN files" {".si4" ".si3" ".pgn" ".PGN" ".pgn.gz"} }
      { "Scid databases" {".si4" ".si3"} }
      { "PGN files" {".pgn" ".PGN" ".pgn.gz"} }
      { "EPD files" {".epd" ".EPD" ".epd.gz"} }
    }
  } else {
    set ftype {
      { "All Scid files" {".si4" ".si3" ".pgn" ".epd"} }
      { "Scid databases, PGN files" {".si4" ".si3" ".pgn" ".PGN"} }
      { "Scid databases" {".si4" ".si3"} }
      { "PGN files" {".pgn" ".PGN"} }
      { "EPD files" {".epd" ".EPD"} }
    }
  }
  if {$fName == ""} {
    set fName [tk_getOpenFile -initialdir $::initialDir(base) -filetypes $ftype -title "Open a Scid file"]
    if {$fName == ""} { return }
  }
  
  if {[file extension $fName] == ".si3"} {
    ::base::upgrade [file rootname $fName]
    return
  }
  
  if {[file extension $fName] == ""} {
    set fName "$fName.si4"
  }
  
  set err 0
  if {[file extension $fName] == ".si4"} {
    set fName [file rootname $fName]
    if {[catch {launchOpenBase $fName} result]} {
      set err 1
      tk_messageBox -icon warning -type ok -parent . \
          -title "Scid: Error opening file" -message $result
    } else {
      set ::initialDir(base) [file dirname $fName]
      if {$::WindowsCE} {
        tk_messageBox -icon info -type ok -parent . -title "Scid: base loading" -message "[sc_base numGames] games loaded"
      }
    }
  } elseif {[string match "*.epd" [string tolower $fName]]} {
    # EPD file:
    newEpdWin open $fName
  } else {
    # PGN file:
    set result "This file is not readable."
    if {(![file readable $fName])  || \
          [catch {sc_base create $fName true} result]} {
      set err 1
      tk_messageBox -icon warning -type ok -parent . -title "Scid: Error opening file" -message $result
    } else {
      doPgnFileImport $fName "Opening [file tail $fName] read-only...\n"
      sc_base type [sc_base current] 3
    }
  }
  
  if {$err == 0} {
    catch {sc_game load auto}
    ::board::updateBoard -nolastmove
    ::board::updateTitle
    ::addRecentFile $fName
    ::ui::updateMenus
  }
}
################################################################################
#   Upgrades an old (version 3) Scid database to version 4.
proc ::base::upgrade {name} {
  if {[file readable "$name.si4"]} {
    set msg "This is an old Scid format (v3).\nDo you wish to use the new format ?"
    set res [tk_messageBox -title "Scid" -type yesno -icon info -message $msg]
    if {$res == "no"} { return }
    ::file::Open "$name.si4"
    return
  }
  
  set msg "This will upgrade base format to Scid v4.\nDo you confirm this ?"
  set res [tk_messageBox -title "Scid" -type yesno -icon info -message $msg]
  if {$res == "no"} { return }
  progressWindow "Scid" "Upgrading: [file tail $name]..."  [::msgcat::mc "Cancel"] "sc_progressBar"
  busyCursor .
  update
  set err [catch {sc_base upgrade $name} res]
  
  unbusyCursor .
  closeProgressWindow
  if {$err} {
    tk_messageBox -title "Scid" -type ok -icon warning -message "Unable to upgrade the database:\n$res"
    return
  } else  {
    # rename game and name files, delete old .si3
    file rename "$name.sg3"  "$name.sg4"
    file rename "$name.sn3"  "$name.sn4"
    file delete "$name.si3"
  }
  ::base::baseOpen "$name.si4"
}
################################################################################
proc ::base::launchOpenBase {name} {
  set bsize 0
  set gfile "[file rootname $name].sg3"
  if {! [catch {file size $gfile} err]} { set bsize $err }
  set showProgress 0
  if {$bsize > 10000} { set showProgress 1 }
  if {$showProgress} {
    progressWindow "Scid" "Opening [file tail $name]"
  }
  set err [catch {sc_base open $name} result]
  if {$showProgress} { closeProgressWindow }
  if {$err} { return -code error $result }
  return $result
}
################################################################################
proc ::base::switch {base} {
  sc_base switch $base
  ::board::updateBoard -nolastmove
  ::board::updateTitle
  ::board::updateMenuStates
}
################################################################################
proc ::base::copyFilter {frombaseNum tobaseNum} {
  # Check status of source and target bases:
  set currentBaseNum [sc_base current]
  sc_base switch $frombaseNum
  set nGamesToCopy [sc_filter count]
  set fromInUse [sc_base inUse]
  set fromName [file tail [sc_base filename]]
  sc_base switch $tobaseNum
  set targetInUse [sc_base inUse]
  set targetName [file tail [sc_base filename]]
  set targetReadOnly [sc_base isReadOnly]
  sc_base switch $currentBaseNum
  set err ""
  if {$nGamesToCopy == 0} {
    set err "[::msgcat::mc [list Copy error source]] [::msgcat::mc [list Copy error no game]]"
  }
  if {$targetReadOnly} {
    set err "[::msgcat::mc [list Copy error target]] ($targetName) [::msgcat::mc [list copy error read only]]."
  }
  if {! $targetInUse} {set err "[::msgcat::mc [list Copy error target]] [::msgcat::mc [list Copy error not open]]."}
  if {! $fromInUse} {set err "[::msgcat::mc [list Copy error source]] [::msgcat::mc [list Copy error not open]]."}
  if {$frombaseNum == $tobaseNum} {
    set err "[::msgcat::mc [list Copy error source]] == [::msgcat::mc [list Copy error target]]."
  }
  
  if {$err != ""} {
    tk_messageBox -type ok -icon info -title "Scid" -message "[::msgcat::mc [list Copy error]] \n\"$fromName\" -> \"$targetName\": \n$err"
    return
  }
  
  # If copying to the clipbase, do not bother asking for confirmation:
  if {$tobaseNum == [sc_info clipbase]} {
    progressWindow "Scid" "[::msgcat::mc [list Copy games]]" [::msgcat::mc Cancel] "sc_progressBar"
    set copyErr [catch {sc_filter copy $frombaseNum $tobaseNum} result]
    closeProgressWindow
    if {$copyErr} {
      tk_messageBox -type ok -icon info -title "Scid" -message $result
    }
    return
  }
  
  set w [toplevel .fcopyWin]
  wm title $w "Scid: [::msgcat::mc [list Copy games]]"
  label $w.text -text "[::msgcat::mc [list Copy confirm]]"
  frame $w.b
  dialogbutton $w.b.go -text [::msgcat::mc [list Copy games]] -command "
  $w.b.cancel configure -command sc_progressBar
  $w.b.cancel configure -text [::msgcat::mc Stop]
  sc_progressBar $w.bar bar 301 21 time
  grab $w.b.cancel
  if {[catch {sc_filter copy $frombaseNum $tobaseNum} result]} {
    tk_messageBox -type ok -icon info -title Scid -message $result
  }
  focus .
  destroy $w
  updateStatusBar
  "
  dialogbutton $w.b.cancel -text [::msgcat::mc Cancel] -command "focus .; destroy $w"
  canvas $w.bar -width 200 -height 20 -bg white -relief solid -border 1
  $w.bar create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
  $w.bar create text 195 10 -anchor e -font font_Regular -tags time -fill black -text "0:00 / 0:00"
  
  pack $w.text $w.b -side top -pady 5
  pack $w.bar -side bottom
  pack $w.b.go $w.b.cancel -side left -padx 10 -pady 10
  grab $w
  focus $w.b.go
}

################################################################################
#                   G A M E
################################################################################
namespace eval game {
  
  set bookSlot 2
  set minElo 1200
  set maxElo 2000
  set isOpening 0
  set openingMovesList {}
  set openingMovesHash {}
  set openingMoves ""
  set outOfOpening 0
  set startFromCurrent 0
  set coachIsWatching 0
  set ponder 0
  set lFen {}
  set materialBalance ""
  set lastMove ""
  set lastPlayerMoveUci ""
}
################################################################################
# like "sc_move add" but replicates to external board
proc ::game::moveAdd { move } {
  ::sound::play move
  # ::novag::checkBoardSync
  
  if { [ llength $move ] == 3 } {
    sc_move add [lindex $move 0] [lindex $move 1] [lindex $move 2]
  } else {
    # beware of promote (should be e2e1=Q instead of e2e1q)
    if { [string length $move] == 5 && [lsearch { "Q" "q" "R" "r" "B" "b" "N" "n" } [string index $move end]] != -1 } {
      set move "[string range $move 0 3]=[string toupper [string range $move end end]]"
    }
    # code pour DEBUG Greko (coup a8a8 après la fin de biblio ?)
    if { [ catch { sc_move addSan $move } err ] } {
      tk_messageBox -type ok -icon error -title "Scid" -message "sc_move addSan $move\nerror $err"
    }
  }
  
  if {$::novag::connected} {
    ::novag::addMove [sc_game info previousMoveUCI]
  }
}
################################################################################
proc ::game::gameLoad { selection } {
  # If an invalid game number, just return:
  if {$selection < 1} { return }
  if {$selection > [sc_base numGames]} { return }
  if {![::game::ConfirmDiscard]} { return }
  if {[catch {sc_game load $selection} result]} {
    tk_messageBox -type ok -icon info -title "Scid" -message $result
  }
  ::board::updateTitle
  ::board::updateBoard -nolastmove
}
################################################################################
#   Loads the next or previous filtered game in the database.
#   The parameter <action> should be "previous" or "next".
proc ::game::gameLoadNextPrev {action} {
  if {![sc_base inUse]} {
    return
  }
  set number [sc_filter $action]
  if {$number == 0} {
    return
  }
  gameLoad $number
}
################################################################################
# ::game::ConfirmDiscard
#   Prompts the user if they want to discard the changes to the
#   current game. Returns 1 if they selected yes, 0 otherwise.
proc ::game::ConfirmDiscard {} {
  if {[sc_base isReadOnly]} { return 1 }
  if {! [sc_game altered]} { return 1 }
  set answer [ tk_dialog .cgDialog  [::msgcat::mc "Game new"] \
      [::msgcat::mc "Game modified,\ncontinue and discard\nthe changes ?"] questhead 0 [::msgcat::mc "Yes"] [::msgcat::mc "No"] ]
  if {$answer == 1} { return  0 }
  return 1
}
################################################################################
proc ::game::new {} {
  if {![::game::ConfirmDiscard]} { return }
  sc_game new
  # if in play mode, rewrite game header
  if {$::playMode} {
    setPlayHeaders
  }
  ::board::updateTitle
  ::board::updateBoard -nolastmove
}
################################################################################
proc ::game::setPlayHeaders {} {
  if { $::options(game_limitElo) && $::options(engine1) == "phalanx" } {
    sc_game tags set -event "Game $::options(engine1) $::options(game_Elo)"
  } else {
    sc_game tags set -event "Game $::options(engine1)"
  }
  
  if { $::board::isFlipped } {
    set engine_color w
    set human_color b
  } else {
    set engine_color b
    set human_color w
  }
  
  set t$human_color ""
  
  if {$::options(game_timeMode) == "timebonus"} {
    set tw "[expr $::engine::data(wtime1) / (1000 * 60)]/[expr $::engine::data(winc1) / 1000]"
    set tb "[expr $::engine::data(btime1) / (1000 * 60)]/[expr $::engine::data(binc1) / 1000]"
  } elseif {$::options(game_timeMode) == "depth"} {
    set t$engine_color "depth $::engine::data(fixeddepth1)"
  } elseif {$::options(game_timeMode) == "movetime"} {
    set t$engine_color "[expr $::engine::data(movetime1) / 1000 ] s"
  } elseif {$::options(game_timeMode) == "nodes"} {
    set t$engine_color "nodes $::engine::data(fixednodes1)"
  }
  
  if { $::board::isFlipped } {
    sc_game tags set -white "$::engine::data(name1) ($tw)"
    sc_game tags set -black "[::msgcat::mc Human] ($tb)"
  } else {
    sc_game tags set -white "[::msgcat::mc Human] ($tw)"
    sc_game tags set -black "$::engine::data(name1) ($tb)"
  }
  
  sc_game tags set -date [::utils::date::today]
}
################################################################################
proc ::game::save {} {
  if { [catch {sc_game save [sc_game number] } result ] } {
    tk_messageBox -type ok -icon error -parent . -title "Scid" -message "Error saving game\n$result"
  }
  ::board::updateTitle
}
################################################################################
proc ::game::delete {} {
  set glNumber [sc_game number]
  set flag delete
  # If an invalid game number, just return:
  if {$glNumber < 1} { return }
  if {$glNumber > [sc_base numGames]} { return }
  catch {sc_game flag $flag $glNumber invert}
  ::board::updateTitle
}
################################################################################
proc ::game::gamePrev {} {
  gameLoadNextPrev previous
}
################################################################################
proc ::game::gameNext {} {
  gameLoadNextPrev next
}
################################################################################
proc ::game::random {} {
  set ngames [sc_filter size]
  if {$ngames == 0} { return }
  set r [expr {(int (rand() * $ngames)) + 1} ]
  set gnumber [sc_filter index $r]
  gameLoad $gnumber
}

trace variable ::game::entryLoadNumber w {::utils::validate::Regexp {^[0-9]*$}}
################################################################################
proc ::game::LoadNumber {} {
  set ::game::entryLoadNumber ""
  if {![sc_base inUse]} { return }
  # if {![::game::ConfirmDiscard]} { return }
  if {[sc_base numGames] < 1} { return }
  set w [toplevel .glnumDialog]
  wm title $w [::msgcat::mc "Game number"]
  grab $w
  
  label $w.label -text [::msgcat::mc "Load game number"]
  pack $w.label -side top -pady 5 -padx 5
  
  entry $w.entry -background white -width 10 -textvariable ::game::entryLoadNumber
  pack $w.entry -side top -pady 5
  
  set b [frame $w.buttons]
  pack $b -side top -fill x
  dialogbutton $b.load -text "OK" -command {
    grab release .glnumDialog
    ::game::gameLoad $::game::entryLoadNumber
    focus .
    destroy .glnumDialog
  }
  dialogbutton $b.cancel -text [::msgcat::mc "Cancel"] -command {
    focus .
    grab release .glnumDialog
    destroy .glnumDialog
    focus .
  }
  packbuttons right $b.cancel $b.load
  
  set x [ expr {[winfo width .] / 4 + [winfo rootx .] }]
  set y [ expr {[winfo height .] / 4 + [winfo rooty .] }]
  wm geometry $w "+$x+$y"
  
  focus $w.entry
}

################################################################################
set ::game::gameListHeight 50
set ::game::glOffset 0
################################################################################
proc ::game::refreshGameList {} {
  global ::game::glOffset ::game::gameListHeight
  set lb .gameList.f1.list
  
  if {$glOffset == 0} {
    set glOffset [sc_filter first]
  }
  if {$glOffset == 0} { return }
  set f .gameList.f1.sf.scrolled
  
  $lb delete 0 end
  
  set l1 [split [sc_game list $glOffset $gameListHeight "g\n"] "\n"]
  set l2 [split [sc_game list $glOffset $gameListHeight "w12\n"] "\n"]
  set l3 [split [sc_game list $glOffset $gameListHeight "b12\n"] "\n"]
  set l4 [split [sc_game list $glOffset $gameListHeight "r*\n"] "\n"]
  set l5 [split [sc_game list $glOffset $gameListHeight "o*\n"] "\n"]
  
  for {set i 0} {$i < [expr [llength $l1] -1 ] } {incr i} {
    set white "[lindex $l2 $i]"
    set black "[lindex $l3 $i]"
    $lb insert end "[lindex $l1 $i]: $white - $black [lindex $l4 $i] \[[lindex $l5 $i]\]"
    if { [lindex $l1 $i] == [sc_game number] } {
      $lb selection set $i
    }
  }
}
################################################################################
proc ::game::GameList {} {
  set w .gameList
  if {[winfo exists $w]} {
    focus $w
    return
  }
  toplevel $w
  ::ui::setFullSize $w 0
  
  ttk::frame $w.f1
  
  listbox $w.f1.list -xscrollcommand {.gameList.f1.hs set} -yscrollcommand {.gameList.f1.vs set}
  ttk::scrollbar $w.f1.vs -command {.gameList.f1.list yview}
  ttk::scrollbar $w.f1.hs -command {.gameList.f1.list xview} -orient horizontal
  grid $w.f1.list -row 0 -column 0 -sticky nsew
  grid $w.f1.vs -row 0 -column 1 -sticky ns
  grid $w.f1.hs -row 1 -column 0 -sticky ew
  grid rowconfigure $w.f1 0 -weight 1
  grid columnconfigure $w.f1 0 -weight 1
  
  set ::game::glOffset [sc_game number]
  
  ttk::frame $w.f2
  ttk::button $w.f2.bPrev -image tb_prev -command { ::game::setglOffset -10 }
  ttk::button $w.f2.bNext -image tb_next -command { ::game::setglOffset 10 }
  ttk::button $w.f2.bStart -image tb_start -command { ::game::setglOffset [expr 0 - $::game::gameListHeight] }
  ttk::button $w.f2.bEnd -image tb_end -command { ::game::setglOffset $::game::gameListHeight }
  
  ttk::button $w.f2.bLoad -text Load -command "::game::GameListLoad ; destroy .gameList"
  ttk::button $w.f2.bClose -text Close -command "destroy .gameList"
  pack $w.f2.bStart $w.f2.bPrev $w.f2.bNext $w.f2.bEnd $w.f2.bLoad $w.f2.bClose -side left -fill x
  pack $w.f1 -fill both -expand 1
  pack $w.f2 -fill x
  
  refreshGameList
}
# ----------------------------------------------------------------------------------
proc ::game::setglOffset {delta} {
  incr ::game::glOffset $delta
  if {$::game::glOffset > [expr [sc_base numGames] - $::game::gameListHeight] } {
    set ::game::glOffset [expr [sc_base numGames] - $::game::gameListHeight +1 ]
  }
  if {$::game::glOffset < 1} {
    set ::game::glOffset 1
  }
  ::game::refreshGameList
}
# ----------------------------------------------------------------------------------
proc ::game::GameListLoad {} {
  set lb .gameList.f1.list
  set g [$lb get [$lb curselection] ]
  set num [lindex [split $g ":"] 0]
  ::game::gameLoad $num
}
################################################################################
proc ::game::BoardSetup {} {
  ::setupBoard
  set ::board::lastMoveList {}
  ::board::resetSquaresColor
}
################################################################################
proc ::game::Annotate {} {
  if {$::analyzeMode} {
    set w .analyze
    toplevel $w
    ::ui::setFullSize $w
    
    ttk::label $w.l2 -text [::msgcat::mc "Time (sec)"]
    spinbox $w.sb2 -textvariable ::options(analyze_time) -width 3 -from 5 -to 120 -increment 5 -validate all -vcmd { regexp {^[0-9]+$} %P }
    ttk::label $w.l1 -text [::msgcat::mc "Error threshold"]
    spinbox $w.sb1 -textvariable ::options(analyze_threshold) -width 3 -from 0.1 -to 3.0 -increment 0.1 -validate all -vcmd { regexp {^[0-9]\.[0-9]$} %P }
    ttk::checkbutton $w.cbBook -text [::msgcat::mc "Use book"] -variable ::options(analyze_usebook)
    ttk::button $w.analyze -text [::msgcat::mc Annotate] -command { destroy .analyze ; ::game::analyzeRun }
    ttk::button $w.cancel -text [::msgcat::mc Cancel] -command { set ::analyzeMode 0 ; destroy .analyze }
    
    grid $w.l2 -column 0 -row 0 -sticky ew
    grid $w.sb2 -column 1 -row 0 -sticky ew
    grid $w.l1 -column 0 -row 1 -sticky ew
    grid $w.sb1 -column 1 -row 1 -sticky ew
    grid $w.cbBook -column 0 -row 2 -columnspan 2 -sticky ew
    grid $w.analyze -column 0 -row 3 -sticky ew
    grid $w.cancel -column 1 -row 3 -sticky ew
  } else {
    after cancel ::game::analyzeMove 1
    if { $::engine::data(running1) } { ::board::toggleAnalyze 1 }
  }
}
################################################################################
proc ::game::analyzeRun {} {
  
  sc_move start
  ::board::updateBoard -nolastmove
  
  # book
  if { $::options(analyze_usebook) } {
    
    global ::book::bookMoves
    ::book::scBookOpen $::options(mainBook) $::book::bookSlot
    
    while { ! [sc_pos isAt end] } {
      set bookMoves [sc_book moves $::book::bookSlot]
      set next [sc_game info nextMoveNT]
      if {[lsearch $bookMoves $next] == -1} {
        sc_pos setComment $bookMoves
        break
      }
      sc_move forward
      ::board::updateBoard -nolastmove -nobook
    }
    ::book::closeMainBook
    ::board::updateBoard -nolastmove
  }
  
  # engine
  set n 1
  set ::engine::data(prevscore$n) 0.0
  # set ::engine::data(score$n) 0.0
  set ::engine::data(prevpv$n) {}
  if {! $::engine::data(running$n) } { ::board::toggleAnalyze $n }
  
  after [expr { $::options(analyze_time) * 1000 } ] ::game::analyzeMove $n
  
}
################################################################################
proc ::game::analyzeMove {n} {
  global ::engine::data ::informant
  set blunder 0
  
  set score [lindex [lindex $data(multipv$n) 0] 1]
  
  # white moved
  if { [sc_pos side] == "black" && [expr { $score - $data(prevscore$n)} ] < [expr { 0.0 - $::options(analyze_threshold) } ]} {
    set blunder 1
  }
  # black moved
  if { [sc_pos side] == "white" && [expr { $score - $data(prevscore$n) } ] > $::options(analyze_threshold)} {
    set blunder 1
  }
  
  # if the game is clearly won or lost, do nothing
  if { $score > $informant("++-") && $data(prevscore$n) > $informant("++-") || \
        $score < [ expr { 0.0 - $informant("++-") } ] && $data(prevscore$n) < [ expr { 0.0 - $informant("++-") } ] } {
    set blunder 0
  }
  
  if { $blunder } {
    set delta [expr { abs( $score - $data(prevscore$n) ) } ]
    set nag ""
    if { $delta > $informant("??") } {
      set nag "??"
    } elseif { $delta > $informant("?") } {
      set nag "?"
    } elseif { $delta > $informant("?!") } {
      set nag "?!"
    }
    if {$nag != ""} {sc_pos addNag $nag}
    
    sc_pos setComment "$score ($data(prevscore$n)) [sc_pos getComment]"
    
    sc_move back
    sc_var create
    sc_move addSan $data(prevpv$n)
    sc_var exit
    sc_move forward
  }
  
  set data(prevscore$n) $score
  set data(prevpv$n) [ ::untrans [ lindex [lindex $data(multipv$n) 0] 2 ] ]
  
  sc_move forward
  ::board::updateBoard -nolastmove
  
  if { ! [sc_pos isAt end] && $::analyzeMode } {
    after [expr { $::options(analyze_time) * 1000 } ] ::game::analyzeMove $n
  } else {
    if { $::engine::data(running$n) } { ::board::toggleAnalyze $n }
    set ::analyzeMode 0
  }
}
################################################################################
proc ::game::Play {} {
  if {$::playMode} {
    if { [::engine::isUci 1] } {
      PlayConfig
    } else {
      PlayConfigXBoard
    }
  } else {
    stopPlay 1
  }
}
################################################################################
proc ::game::stopPlay { n } {
  if { [::engine::isUci $n] } {
    after cancel ::game::engineGo $n
  } else {
    after cancel ::game::engineGoXBoard $n
  }
  
  ::engine::sendToEngine "stop" $n
  set ::engine::data(bestmove$n) ""
  
  set ::options(showAnalysis) $::options(showAnalysisOld)
  set ::options(showBook) $::options(showBookOld)
  set ::options(multipv) $::options(multipvOld)
  ::engine::setMultiPv 1
  
  ::infopanel::updateAnalysis
  ::infopanel::updateBookText
  ::gameclock::delete 1
  ::gameclock::delete 2
  # if the opponent was Phalanx, put it back at full strength
  if {$::options(game_limitElo) && $::options(engine1) == "phalanx"} {
    ::engine::sendToEngine "elevel 0" $n
  }
  wm title . "Scid"
}
################################################################################
proc ::game::PlayConfig {} {
  set w ".configSerGameWin"
  if {[winfo exists $w]} {
    focus $w
    return
  }
  
  toplevel $w
  wm title $w [::msgcat::mc "Configure Game"]
  ::ui::setFullSize $w
  
  set ::options(limitElo) 0
  
  ttk::frame $w.fnb
  ttk::frame $w.fdown
  
  pack [ttk::notebook $w.fnb.nb] -fill both -expand 1
  
  # --------------- Time frame ---------------------
  $w.fnb.nb add [ttk::frame $w.fnb.nb.time] -text [::msgcat::mc "time"]
  set nb1 $w.fnb.nb.time
  
  set row 0
  
  ttk::radiobutton $nb1.rb1 -text [::msgcat::mc "Time bonus"] -value "timebonus" -variable ::options(game_timeMode)
  grid $nb1.rb1 -row $row -column 0 -sticky w
  incr row
  ttk::label $nb1.lb1 -text [::msgcat::mc "White time"]
  grid $nb1.lb1 -row $row -column 0 -sticky w
  incr row
  spinbox $nb1.spW1 -textvar ::options(game_wtime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  spinbox $nb1.spW2 -textvar ::options(game_winc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  grid $nb1.spW1 -row $row -column 0 -sticky w
  grid $nb1.spW2  -row $row -column 1 -sticky w
  incr row
  
  ttk::label $nb1.lb2 -text [::msgcat::mc "Black time"]
  grid $nb1.lb2 -row $row -column 0 -sticky w
  incr row
  spinbox $nb1.spB1 -textvar ::options(game_btime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  spinbox $nb1.spB2 -textvar ::options(game_binc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  grid $nb1.spB1 -row $row -column 0 -sticky w
  grid $nb1.spB2  -row $row -column 1 -sticky w
  incr row
  
  ttk::radiobutton $nb1.rb2 -text [::msgcat::mc "Fixed depth"] -value "depth" -variable ::options(game_timeMode)
  spinbox $nb1.sbDepth -textvar ::options(game_fixeddepth) -background white -width 2 -from 1 -to 20 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  
  ttk::radiobutton $nb1.rb3 -text [::msgcat::mc "Nodes (x1000)"] -value "nodes" -variable ::options(game_timeMode)
  spinbox $nb1.sbNodes -textvar ::options(game_fixednodes) -background white -width 5 -from 5 -to 10000 -increment 5 -validate all -vcmd { regexp {^[0-9]+$} %P }
  
  ttk::radiobutton $nb1.rb4 -text [::msgcat::mc "Sec / move"] -value "movetime" -variable ::options(game_timeMode)
  spinbox $nb1.sbMoveTime -textvar ::options(game_movetime) -background white -width 3 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  
  grid $nb1.rb2 -row $row -column 0 -sticky w
  grid $nb1.sbDepth -row $row -column 1 -sticky w
  incr row
  grid $nb1.rb3 -row $row -column 0 -sticky w
  grid $nb1.sbNodes -row $row -column 1 -sticky w
  incr row
  grid $nb1.rb4 -row $row -column 0 -sticky w
  grid $nb1.sbMoveTime -row $row -column 1 -sticky w
  incr row
  
  ttk::checkbutton $nb1.cbEgt -text [::msgcat::mc "Add elapsed game time"] -variable ::options(game_elapsedGameTime)
  grid $nb1.cbEgt -row $row -column 0 -sticky w -columnspan 2
  incr row
  
  # --------------- Other settings frame -----------
  $w.fnb.nb add [ttk::frame $w.fnb.nb.settings] -text [::msgcat::mc "settings"]
  set nb2 $w.fnb.nb.settings
  
  ttk::frame $nb2.fbook -relief groove -borderwidth 1
  ttk::frame $nb2.ftime -relief groove -borderwidth 1
  ttk::frame $nb2.fopening -relief groove -borderwidth 1
  
  pack $nb2.fbook $nb2.ftime -side top -fill x
  pack $nb2.fopening -side top -fill both -expand 1
  
  # load book names
  ttk::checkbutton $nb2.fbook.cbUseBook -text [::msgcat::mc "Use book"] -variable ::options(game_useBook)
  set bookPath [file join $::instalDir "books" ]
  
  set bookList [ lsort -dictionary [ glob -nocomplain -tails -directory $bookPath *.bin ] ]
  set i 0
  set idx 0
  set lbook {}
  foreach file  $bookList {
    set f [file rootname [ file tail $file ] ]
    set lbook [concat $lbook $f]
  }
  
  ttk::combobox $nb2.fbook.combo -state readonly -width 12 -values $lbook
  
  $nb2.fbook.combo set [file rootname $::options(game_bookToUse) ]
  pack $nb2.fbook.cbUseBook $nb2.fbook.combo -expand yes -fill both -side top
  
  set row 0
  # New game or use current position ?
  ttk::checkbutton $nb2.ftime.cbPosition -text [::msgcat::mc "From current position"] -variable ::game::startFromCurrent
  grid $nb2.ftime.cbPosition  -row $row -column 0 -sticky w
  incr row
  
  # Warn if the user makes weak/bad moves
  ttk::checkbutton $nb2.ftime.cbCoach -text [::msgcat::mc "Coach is watching"] -variable ::game::coachIsWatching
  grid $nb2.ftime.cbCoach  -row $row -column 0 -sticky w
  incr row
  
  # Ponder
  ttk::checkbutton $nb2.ftime.cbPonder -text [::msgcat::mc "Permanent thinking"] -variable ::game::ponder
  grid $nb2.ftime.cbPonder  -row $row -column 0 -sticky w
  incr row
  
  # if Glaurung, tweak some uci options which may be interesting for the game
  if { $::options(engine1) == "glaurung" } {
    $w.fnb.nb add [ttk::frame $w.fnb.nb.extra] -text [::msgcat::mc "extra"]
    set nb3 $w.fnb.nb.extra
    ttk::label $nb3.lagg -text [::msgcat::mc "Aggressiveness\n(default 130)"]
    spinbox $nb3.sbagg -background yellow -width 3 -from 0 -to 300 -increment 10 -validate all \
        -textvariable ::options(glaurungAggressiveness) -vcmd { regexp {^[0-9]+$} %P }
    ttk::label $nb3.lcow -text [::msgcat::mc "Cowardice\n(default 100)"]
    spinbox $nb3.sbcow -background yellow -width 3 -from 0 -to 300 -increment 10 -validate all \
        -textvariable ::options(glaurungCowardice) -vcmd { regexp {^[0-9]+$} %P }
    
    grid $nb3.lagg -column 0 -row $row -sticky w
    grid $nb3.sbagg -column 1 -row $row -sticky w
    incr row
    grid $nb3.lcow -column 0 -row $row -sticky w
    grid $nb3.sbcow -column 1 -row $row -sticky w
    incr row
  }
  
  # choose a specific opening
  ttk::checkbutton $nb2.fopening.cbOpening -text [::msgcat::mc "Specific opening"] -variable ::game::isOpening
  ttk::frame $nb2.fopening.fOpeningList -relief raised -borderwidth 1
  listbox $nb2.fopening.fOpeningList.lbOpening -yscrollcommand "$nb2.fopening.fOpeningList.ybar set" -xscrollcommand "$nb2.fopening.fOpeningList.xbar set" \
      -height 5 -list ::game::openingList -exportselection 0 ; # -width 50
  $nb2.fopening.fOpeningList.lbOpening selection set $::options(game_specificOpening)
  
  ttk::scrollbar $nb2.fopening.fOpeningList.ybar -command "$nb2.fopening.fOpeningList.lbOpening yview"
  ttk::scrollbar $nb2.fopening.fOpeningList.xbar -orient horiz -command "$nb2.fopening.fOpeningList.lbOpening xview"
  
  grid $nb2.fopening.fOpeningList.lbOpening -row 0 -column 0 -sticky nsew
  grid $nb2.fopening.fOpeningList.ybar -row 0 -column 1 -sticky ns
  grid $nb2.fopening.fOpeningList.xbar -row 1 -column 0 -sticky ew
  grid rowconfigure $nb2.fopening.fOpeningList 0 -weight 1
  grid columnconfigure $nb2.fopening.fOpeningList 0 -weight 1
  
  pack $nb2.fopening.cbOpening -fill x -side top
  pack $nb2.fopening.fOpeningList -expand yes -fill both -side top
  
  # ------------- Bottom buttons -----------------
  ttk::button $w.fdown.close -text [::msgcat::mc "Play"] -command {
    focus .
    set nb1 .configSerGameWin.fnb.nb.time
    set nb2 .configSerGameWin.fnb.nb.settings
    set ::options(game_specificOpening) [$nb2.fopening.fOpeningList.lbOpening curselection]
    if {$::options(game_useBook)} {
      set ::options(game_bookToUse) "[$nb2.fbook.combo get].bin"
    }
    set ::engine::data(wtime1) [expr [$nb1.spW1 get]*1000*60]
    set ::engine::data(btime1) [expr [$nb1.spB1 get]*1000*60]
    set ::engine::data(winc1) [expr [$nb1.spW2 get]*1000]
    set ::engine::data(binc1) [expr [$nb1.spB2 get]*1000]
    set ::engine::data(fixeddepth1) [$nb1.sbDepth get]
    set ::engine::data(fixednodes1) [expr [$nb1.sbNodes get]*1000]
    set ::engine::data(movetime1) [expr [$nb1.sbMoveTime get]*1000]
    
    if {$::options(game_timeMode) == "depth" || $::options(game_timeMode) == "nodes" || $::options(game_timeMode) == "movetime"} {
      set ::engine::data(wtime1) 0
      set ::engine::data(btime1) 0
      set ::engine::data(winc1) 0
      set ::engine::data(binc1) 0
    }
    destroy .configSerGameWin
    ::game::playStart
  }
  ttk::button $w.fdown.cancel -text [::msgcat::mc "Cancel"] -command "focus .; destroy .configSerGameWin"
  
  pack $w.fdown.close $w.fdown.cancel -expand yes -side left -padx 20 -pady 2
  
  pack $w.fnb -fill both -side top -expand 1
  pack $w.fdown -side top -fill x
  
  bind $w <Destroy> { set ::playMode 0 }
}
################################################################################
proc ::game::PlayConfigXBoard {} {
  set w ".configXBoardGameWin"
  if {[winfo exists $w]} {
    focus $w
    return
  }
  
  toplevel $w
  wm title $w "Configure Game"
  ::ui::setFullSize $w
  ttk::frame $w.fnb
  ttk::frame $w.fdown
  
  pack [ttk::notebook $w.fnb.nb] -fill both -expand 1
  
  # --------------- Time frame ---------------------
  $w.fnb.nb add [ttk::frame $w.fnb.nb.time] -text "time"
  set nb1 $w.fnb.nb.time
  
  set row 0
  set ::options(game_timeMode) "timebonus"
  
  ttk::label $nb1.lb1 -text [::msgcat::mc "White time"]
  grid $nb1.lb1 -row $row -column 0 -sticky w
  incr row
  spinbox $nb1.spW1 -textvar ::options(game_wtime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  spinbox $nb1.spW2 -textvar ::options(game_winc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  grid $nb1.spW1 -row $row -column 0 -sticky w
  grid $nb1.spW2  -row $row -column 1 -sticky w
  incr row
  
  ttk::label $nb1.lb2 -text [::msgcat::mc "Black time"]
  grid $nb1.lb2 -row $row -column 0 -sticky w
  incr row
  spinbox $nb1.spB1 -textvar ::options(game_btime) -background white -width 4 -from 1 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  spinbox $nb1.spB2 -textvar ::options(game_binc) -background white -width 4 -from 0 -to 60 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
  grid $nb1.spB1 -row $row -column 0 -sticky w
  grid $nb1.spB2  -row $row -column 1 -sticky w
  incr row
  
  ttk::checkbutton $nb1.cbEgt -text [::msgcat::mc "Add elapsed game time"] -variable ::options(game_elapsedGameTime)
  grid $nb1.cbEgt -row $row -column 0 -sticky w -columnspan 2
  incr row
  
  # --------------- Other settings frame -----------
  $w.fnb.nb add [ttk::frame $w.fnb.nb.settings] -text "settings"
  set nb2 $w.fnb.nb.settings
  
  ttk::frame $nb2.fbook -relief groove -borderwidth 1
  ttk::frame $nb2.ftime -relief groove -borderwidth 1
  ttk::frame $nb2.fopening -relief groove -borderwidth 1
  
  # load book names
  ttk::checkbutton $nb2.fbook.cbUseBook -text [::msgcat::mc "Use book"] -variable ::options(game_useBook)
  set bookPath [file join $::instalDir "books" ]
  
  set bookList [ lsort -dictionary [ glob -nocomplain -tails -directory $bookPath *.bin ] ]
  set i 0
  set idx 0
  set lbook {}
  foreach file  $bookList {
    set f [ file rootname [ file tail $file ] ]
    set lbook [concat $lbook $f]
  }
  
  ttk::combobox $nb2.fbook.combo -state readonly -width 12 -values $lbook
  
  $nb2.fbook.combo set [file rootname $::options(game_bookToUse)]
  pack $nb2.fbook.cbUseBook $nb2.fbook.combo -expand yes -fill both -side top
  
  set row 0
  # New game or use current position ?
  ttk::checkbutton $nb2.ftime.cbPosition -text [::msgcat::mc "From current position"] -variable ::game::startFromCurrent
  grid $nb2.ftime.cbPosition  -row $row -column 0 -sticky w
  incr row
  
  # make the engine simulate an opponent with a given ELO
  if {$::options(engine1) == "phalanx"} {
    $w.fnb.nb add [ttk::frame $w.fnb.nb.extra] -text "extra"
    set nb3 $w.fnb.nb.extra
    ttk::checkbutton $nb3.cbElo -text [::msgcat::mc "Opponent limited Elo"] -variable ::options(game_limitElo)
    scale $nb3.scale -from $::game::minElo -to $::game::maxElo -resolution 50 -showvalue 1 -variable ::options(game_Elo) -orient horiz ;#-length 250
    # ttk::scale $nb3.scale -from $::game::minElo -to $::game::maxElo -variable ::options(game_Elo) -orient horiz
    pack $nb3.cbElo $nb3.scale -expand yes -fill x -side top
  } else {
    set ::options(limitElo) 0
  }
  
  # choose a specific opening
  ttk::checkbutton $nb2.fopening.cbOpening -text [::msgcat::mc "Specific opening"] -variable ::game::isOpening
  ttk::frame $nb2.fopening.fOpeningList -relief raised -borderwidth 1
  listbox $nb2.fopening.fOpeningList.lbOpening -yscrollcommand "$nb2.fopening.fOpeningList.ybar set" -xscrollcommand "$nb2.fopening.fOpeningList.xbar set" \
      -height 5 -list ::game::openingList -exportselection 0 ; # -width 50
  $nb2.fopening.fOpeningList.lbOpening selection set $::options(game_specificOpening)
  
  ttk::scrollbar $nb2.fopening.fOpeningList.ybar -command "$nb2.fopening.fOpeningList.lbOpening yview"
  ttk::scrollbar $nb2.fopening.fOpeningList.xbar -orient horiz -command "$nb2.fopening.fOpeningList.lbOpening xview"
  
  grid $nb2.fopening.fOpeningList.lbOpening -row 0 -column 0 -sticky nsew
  grid $nb2.fopening.fOpeningList.ybar -row 0 -column 1 -sticky ns
  grid $nb2.fopening.fOpeningList.xbar -row 1 -column 0 -sticky ew
  grid rowconfigure $nb2.fopening.fOpeningList 0 -weight 1
  grid columnconfigure $nb2.fopening.fOpeningList 0 -weight 1
  
  pack $nb2.fopening.cbOpening -fill x -side top
  pack $nb2.fopening.fOpeningList -expand yes -fill both -side top
  
  pack $nb2.fbook $nb2.ftime -side top -fill x
  pack $nb2.fopening -side top -fill both -expand 1
  
  
  button $w.fdown.close -text [::msgcat::mc "Play"] -command {
    focus .
    set w ".configXBoardGameWin"
    set nb1 $w.fnb.nb.time
    set nb2 $w.fnb.nb.settings
    set ::options(game_specificOpening) [$nb2.fopening.fOpeningList.lbOpening curselection]
    if {$::options(game_useBook)} {
      set ::options(game_bookToUse) "[$nb2.fbook.combo get].bin"
    }
    set ::engine::data(wtime1) [expr [$nb1.spW1 get]*1000*60]
    set ::engine::data(btime1) [expr [$nb1.spB1 get]*1000*60]
    set ::engine::data(winc1) [expr [$nb1.spW2 get]*1000]
    set ::engine::data(binc1) [expr [$nb1.spB2 get]*1000]
    destroy .configXBoardGameWin
    ::game::playStart
  }
  button $w.fdown.cancel -text [::msgcat::mc "Cancel"] -command "focus .; destroy .configXBoardGameWin"
  
  pack $w.fdown.close $w.fdown.cancel -expand yes -side left -padx 20 -pady 2
  
  pack $w.fnb -fill both -side top -expand 1
  pack $w.fdown -side top -fill x
  
  bind $w <Destroy> { set ::playMode 0 }
}

################################################################################
# n => 1 for engine
# n => 2 for player
proc ::game::playStart {} {
  global ::game::isOpening ::game::openingList ::game::openingMovesList \
      ::game::openingMovesHash ::game::openingMoves ::game::outOfOpening
  set n 1
  set ::playMode 1
  
  repetitionReset
  
  ::engine::stopAnalysis $n
  
  set ::options(multipvOld) $::options(multipv)
  
  if { $::novag::connected } {
    # ::novag::writechan "U ON"
    set ::options(novag_refereeMode) 1
    ::novag::newGame
  }
  # ponder
  if {$::game::ponder && $::cacheIsUci($n)} {
    ::engine::sendToEngine "setoption name Ponder value true" $n
  } else {
    ::engine::sendToEngine "setoption name Ponder value false" $n
  }
  
  # Glaurung special options
  if { $::options(engine$n) == "glaurung" } {
    ::engine::sendToEngine "setoption name Aggressiveness value $::options(glaurungAggressiveness)" $n
    ::engine::sendToEngine "setoption name Cowardice value $::options(glaurungCowardice)" $n
  }
  
  if {$::options(game_limitElo)} {
    if {$::options(engine1) == "phalanx"} {
      set level [expr int(100-(100*($::options(game_Elo) - $::game::minElo)/($::game::maxElo-$::game::minElo)))]
      ::engine::sendToEngine "elevel $level" $n
    }
  }
  
  set title $::options(engine1)
  if {$::options(game_limitElo) && $::options(engine1) == "phalanx"} {
    set title "$title $::options(game_Elo)"
  }
  wm title . $title
  
  set ::options(showAnalysisOld) $::options(showAnalysis)
  set ::options(showAnalysis) 0
  ::infopanel::updateAnalysis
  set ::options(showBookOld) $::options(showBook)
  set ::options(showBook) 0
  ::infopanel::updateBookText
  
  set ::engine::data(prevscore$n) 0.0
  
  if {$::game::startFromCurrent} {
    set isOpening 0
  }
  # clock 1 is white
  ::gameclock::new .fTop.fr.fclocks 1 1
  ::gameclock::new .fTop.fr.fclocks 2 1
  ::gameclock::setColor 1 "white"
  ::gameclock::setColor 2 "black"
  ::gameclock::reset 1
  ::gameclock::start 1
  
  # if will follow a specific opening line
  if { $isOpening && ! $::game::startFromCurrent } {
    set fields [split [lindex $openingList $::options(game_specificOpening)] ":"]
    set openingName [lindex $fields 0]
    set openingMoves [string trim [lindex $fields 1]]
    set openingMovesList ""
    set openingMovesHash ""
    set outOfOpening 0
    foreach m [split $openingMoves] {
      # in case of multiple adjacent spaces in opening line
      if {$m =={}} {
        continue
      }
      set p [string trim $m]
      lappend openingMovesList [string trim [regsub {^[1-9]+\.} $p ""] ]
    }
    
    sc_game new
    lappend openingMovesHash [sc_pos hash]
    foreach m  $openingMovesList {
      if {[catch {sc_move addSan $m}]} { }
      lappend openingMovesHash [sc_pos hash]
    }
    sc_game new
  }
  
  if {!$::game::startFromCurrent} {
    # create a new game if a DB is opened
    sc_game new
    
    setPlayHeaders
    
    if {[sc_base inUse [sc_base current]]} { catch {sc_game save 0}  }
  }
  
  ::board::updateBoard -pgn -nolastmove
  
  # setup clocks
  if {$::options(game_timeMode) == "timebonus"} {
    ::gameclock::setdSec 1 [expr $::engine::data(wtime$n)/100]
    ::gameclock::setdSec 2 [expr $::engine::data(btime$n)/100]
  } elseif {$::options(game_timeMode) == "depth" || $::options(game_timeMode) == "nodes" || $::options(game_timeMode) == "movetime"} {
    ::gameclock::setdSec 1 0
    ::gameclock::setdSec 2 0
  }
  
  set ::game::wentOutOfBook 0
  if { [ ::engine::isUci 1 ] } {
    ::game::engineGo 1
  } else  {
    ::game::engineGoXBoard 1
  }
}
################################################################################
proc ::game::endOfGame {} {
  set move_done [sc_game info previousMove]
  if { [string index [sc_game info previousMove] end ] == "#"} {
    ::gameclock::stop 1
    ::gameclock::stop 2
    return 1
  }
  return 0
}
################################################################################
proc ::game::engineGoXBoard { n } {
  global ::game::isOpening ::game::openingMovesList ::game::openingMovesHash ::game::openingMoves ::game::outOfOpening
  
  after cancel ::game::engineGoXBoard $n
  
  if { [::game::endOfGame] } {
    tk_messageBox -type ok -title [::msgcat::mc "End of game"] -message [::msgcat::mc "Mate"] -parent .fTop -icon info
    return
  }
  
  if { [sc_pos side] != [::game::getEngineColor] } {
    after 1000 ::game::engineGoXBoard $n
    return
  }
  
  # The player moved : add clock time
  if {!([::game::getEngineColor] == "black" && [sc_pos moveNumber] == 1)} {
    if { [::game::getEngineColor] == "white" } {
      ::gameclock::add 2 $::engine::data(binc$n)/1000
    } else {
      ::gameclock::add 1 $::engine::data(winc$n)/1000
    }
  }
  
  clockStart "engine"
  repetition
  
  # make a move corresponding to a specific opening, (it is engine's turn)
  if {$isOpening && !$outOfOpening} {
    set index 0
    # Warn if the user went out of the opening line chosen
    if { !$outOfOpening } {
      set ply [ expr [sc_pos moveNumber] * 2 - 1]
      if { [sc_pos side] == "white" } {
        set ply [expr $ply - 1]
      }
      
      if { [lsearch $openingMovesHash [sc_pos hash]] == -1 && [llength $openingMovesList] >= $ply} {
        set answer [tk_messageBox -icon question -parent .fTop -title "Out of opening" -type yesno \
            -message "You did not follow the line\n$openingMoves\nDo you want to continue ?" ]
        if {$answer == no} {
          sc_move back 1
          ::board::updateBoard -pgn
          clockStart "player"
          
          after 1000 ::game::engineGoXBoard $n
          return
        }  else  {
          set outOfOpening 1
        }
      }
    }
    
    set hpos [sc_pos hash]
    # Find a corresponding position in the opening line
    set length [llength $openingMovesHash]
    for {set i 0}   { $i < [expr $length-1] } { incr i } {
      set h [lindex $openingMovesHash $i]
      if {$h == $hpos} {
        set index [lsearch $openingMovesHash $h]
        set move [lindex $openingMovesList $index]
        # play the move
        set action "replace"
        if {![sc_pos isAt vend]} { set action [confirmReplaceMove] }
        if {$action == "replace"} {
          ::game::moveAdd $move
        } elseif {$action == "var"} {
          sc_var create
          ::game::moveAdd $move
        } elseif {$action == "mainline"} {
          sc_var create
          ::sound::play move
          ::game::moveAdd $move
          sc_var exit
          sc_var promote [expr {[sc_var count] - 1}]
          sc_move forward 1
        }
        
        ::board::updateBoard -pgn
        
        repetition
        if { [::game::getEngineColor] == "white" } {
          ::gameclock::add 1 $::engine::data(winc$n)/1000
        } else  {
          ::gameclock::add 2 $::engine::data(binc$n)/1000
        }
        clockStart "player"
        after 1000 ::game::engineGoXBoard $n
        return
      }
    }
  }
  
  # use a book
  # if the opponent has a limited ELO, use book corresponding to its level
  if { $::options(game_limitElo) } {
    set mn [sc_pos moveNumber]
    if {$::options(game_Elo) < 2000 && [expr ($::options(game_Elo) - $::game::minElo)/100] < $mn} {
      set ::game::wentOutOfBook 1
    }
  }
  if {$::options(game_useBook) && ! $::game::wentOutOfBook} {
    set move [ ::book::getMove $::options(game_bookToUse) [sc_pos fen] $::game::bookSlot]
    if {$move == ""} {
      set ::game::wentOutOfBook 1
    } else  {
      ::game::moveAdd $move
      ::board::updateBoard -pgn
      repetition
      if {$::options(game_timeMode) == "timebonus"} {
        if { [::game::getEngineColor] == "white" } {
          ::gameclock::add 1 $::engine::data(winc$n)/1000
        } else  {
          ::gameclock::add 2 $::engine::data(binc$n)/1000
        }
      }
      clockStart "player"
      after 1000 ::game::engineGoXBoard $n
      return
    }
  }
  
  set wtime [::gameclock::getdSec 1]
  set btime [::gameclock::getdSec 2]
  
  # Send to the engine values with at least 1 minute on each clock, even if the flag is down
  if {$wtime < 600 } {set wtime 600}
  if {$btime < 600 } {set btime 600}
  
  ::engine::sendToEngine "setboard [sc_pos fen]" $n
  
  if { [::game::getEngineColor] == "white" } {
    ::engine::sendToEngine "level 0 [expr int($wtime /600 )] [expr int($::engine::data(winc$n) / 1000)]" $n
  } else {
    ::engine::sendToEngine "level 0 [expr int($btime /600 )] [expr int($::engine::data(binc$n) / 1000)]" $n
  }
  
  ::engine::sendToEngine "go" $n
  
  set ::engine::data(bestmove$n) ""
  vwait ::engine::data(bestmove$n)
  # check if the game is stopped
  if {! $::playMode} { return }
  
  ::game::moveAdd $::engine::data(bestmove$n)
  
  set ::engine::data(prevscore$n) $::engine::data(score$n)
  ::board::updateBoard -pgn
  
  # add time after a move played
  if { [::game::getEngineColor] == "white" } {
    ::gameclock::add 1 $::engine::data(winc$n)/1000
  } else  {
    ::gameclock::add 2 $::engine::data(binc$n)/1000
  }
  
  clockStart "player"
  
  repetition
  after 1000 ::game::engineGoXBoard $n
  
}
################################################################################
# UCI engine
proc ::game::engineGo { n } {
  global ::game::isOpening ::game::openingMovesList ::game::openingMovesHash ::game::openingMoves ::game::outOfOpening
  
  after cancel ::game::engineGo $n
  
  if { [::game::endOfGame] } {
    tk_messageBox -type ok -title [::msgcat::mc "End of game"] -message [::msgcat::mc "Mate"] -parent .fTop -icon info
    return
  }
  
  if { [sc_pos side] != [::game::getEngineColor] } {
    after 1000 ::game::engineGo $n
    return
  }
  
  # The player moved : add clock time
  if {!([::game::getEngineColor] == "black" && [sc_pos moveNumber] == 1)} {
    if { [::game::getEngineColor] == "white" } {
      ::gameclock::add 2 $::engine::data(binc$n)/1000
    } else  {
      ::gameclock::add 1 $::engine::data(winc$n)/1000
    }
  }
  # if halfmove is resetted, clear list of FEN used by repetition detection
  if { [lindex [split [sc_pos fen]] 4] == 0 } { set ::game::lFen {} }
  
  clockStart "engine"
  
  repetition
  # -------------------------------------------------------------
  # make a move corresponding to a specific opening, (it is engine's turn)
  if {$isOpening && !$outOfOpening} {
    set index 0
    # Warn if the user went out of the opening line chosen
    if { !$outOfOpening } {
      set ply [ expr [sc_pos moveNumber] * 2 - 1]
      if { [sc_pos side] == "white" } {
        set ply [expr $ply - 1]
      }
      
      if { [lsearch $openingMovesHash [sc_pos hash]] == -1 && [llength $openingMovesList] >= $ply} {
        set answer [tk_messageBox -icon question -parent .fTop -title "Out of opening" -type yesno \
            -message "You did not follow the line\n$openingMoves\nDo you want to continue ?" ]
        if {$answer == no} {
          sc_move back 1
          ::board::updateBoard -pgn
          
          clockStart "player"
          after 1000 ::game::engineGo $n
          return
        }  else  {
          set outOfOpening 1
        }
      }
    }
    
    set hpos [sc_pos hash]
    # Find a corresponding position in the opening line
    set length [llength $openingMovesHash]
    for {set i 0}   { $i < [expr $length-1] } { incr i } {
      set h [lindex $openingMovesHash $i]
      if {$h == $hpos} {
        set index [lsearch $openingMovesHash $h]
        set move [lindex $openingMovesList $index]
        # play the move
        set action "replace"
        if {![sc_pos isAt vend]} { set action [confirmReplaceMove] }
        if {$action == "replace"} {
          ::game::moveAdd $move
        } elseif {$action == "var"} {
          sc_var create
          ::game::moveAdd $move
        } elseif {$action == "mainline"} {
          sc_var create
          ::game::moveAdd $move
          sc_var exit
          sc_var promote [expr {[sc_var count] - 1}]
          sc_move forward 1
        }
        
        ::board::updateBoard -pgn
        clockStart "player"
        
        repetition
        if { [::game::getEngineColor] == "white" } {
          ::gameclock::add 1 $::engine::data(winc$n)/1000
        } else  {
          ::gameclock::add 2 $::engine::data(binc$n)/1000
        }
        after 1000 ::game::engineGo $n
        return
      }
    }
  }
  # -------------------------------------------------------------
  # use a book
  if {$::options(game_useBook) && ! $::game::wentOutOfBook} {
    set move [ ::book::getMove $::options(game_bookToUse) [sc_pos fen] $::game::bookSlot]
    if {$move == ""} {
      set ::game::wentOutOfBook 1
    } else  {
      
      if { [ catch {::game::moveAdd $move} ] } {
        tk_messageBox -type ok -message "Error book move $move" -icon error
        set ::game::wentOutOfBook 1
      } else {
        
        ::board::updateBoard -pgn
        clockStart "player"
        
        repetition
        if {$::options(game_timeMode) == "timebonus"} {
          if { [::game::getEngineColor] == "white" } {
            ::gameclock::add 1 $::engine::data(winc$n)/1000
          } else  {
            ::gameclock::add 2 $::engine::data(binc$n)/1000
          }
        }
        after 1000 ::game::engineGo $n
        return
      }
    }
  }
  
  # -------------------------------------------------------------
  # check if the engine pondered on the right move
  if { $::game::ponder && $::engine::data(ponder$n) == $::game::lastPlayerMoveUci } {
    ::engine::sendToEngine "ponderhit" $n
  } else {
    
    if { $::game::ponder && [sc_pos moveNumber] != 1} {
      set ::engine::data(waitForBestMove$n) 1
      ::engine::sendToEngine "stop" $n
      vwaitTimed ::engine::data(waitForBestMove$n) 3000 nowarn
    }
    
    set ::engine::data(waitForReadyOk$n) 1
    ::engine::sendToEngine "isready" $n
    vwait ::engine::data(waitForReadyOk$n)
    
    ::engine::sendToEngine "position fen [sc_pos fen]" $n
    
    set wtime [expr [::gameclock::getdSec 1] * 100 ]
    set btime [expr [::gameclock::getdSec 2] * 100 ]
    
    # Send to the engine values with at least 1 second on each clock, even if the flag is down
    if {$wtime < 1000} {set wtime 1000}
    if {$btime < 1000} {set btime 1000}
    
    if {$::options(game_timeMode) == "timebonus"} {
      ::engine::sendToEngine "go wtime $wtime btime $btime winc $::engine::data(winc$n) binc $::engine::data(binc$n)" $n
    } elseif {$::options(game_timeMode) == "depth"} {
      ::engine::sendToEngine "go depth $::engine::data(fixeddepth$n)" $n
    } elseif {$::options(game_timeMode) == "movetime"} {
      ::engine::sendToEngine "go movetime $::engine::data(movetime$n)" $n
    } elseif {$::options(game_timeMode) == "nodes"} {
      ::engine::sendToEngine "go nodes $::engine::data(fixednodes$n)" $n
    }
  }
  # -------------------------------------------------------------
  
  set ::engine::data(bestmove$n) ""
  vwait ::engine::data(bestmove$n)
  # check if the game is stopped
  if {! $::playMode} { return }
  
  # if halfmove is resetted, clear list of FEN used by repetition detection
  if { [lindex [split [sc_pos fen]] 4] == 0 } { set ::game::lFen {} }
  
  # -------------------------------------------------------------
  # if weak move detected, propose the user to take back
  if { $::game::coachIsWatching && $::engine::data(prevscore$n) != "" } {
    set blunder 0
    set delta [expr $::engine::data(score$n) - $::engine::data(prevscore$n)]
    
    if {$delta > $::informant("?!") && [getEngineColor] == "white" ||
      $delta < [expr 0.0 - $::informant("?!")] && [getEngineColor] == "black" } {
      set blunder 1
    }
    
    if {$delta > $::informant("?") && [getEngineColor] == "white" ||
      $delta < [expr 0.0 - $::informant("?")] && [getEngineColor] == "black" } {
      set blunder 2
    }
    
    if {$delta > $::informant("??") && [getEngineColor] == "white" ||
      $delta < [expr 0.0 - $::informant("??")] && [getEngineColor] == "black" } {
      set blunder 3
    }
    
    if {$blunder == 1} {
      set tBlunder "Dubious move played,\ndo you want to take back ?"
    } elseif {$blunder == 2} {
      set tBlunder "Weak move played,\ndo you want to take back ?"
    } elseif {$blunder == 3} {
      set tBlunder "Bad move played,\ndo you want to take back ?"
    }
    
    if {$blunder != 0} {
      set answer [tk_messageBox -icon question -parent .fTop -title "Scid" -type yesno -message $tBlunder ]
      if {$answer == yes} {
        sc_move back 1
        ::board::updateBoard -pgn
        clockStart "player"
        
        after 1000 ::game::engineGo $n
        return
      }
    }
  }
  # -------------------------------------------------------------
  if { [ catch { ::game::moveAdd $::engine::data(bestmove$n) } ] } {
    tk_messageBox -type ok -message "Error in call\n::game::moveAdd (l. 1631) $::engine::data(bestmove$n)" -icon error
  }
  
  set ::engine::data(prevscore$n) $::engine::data(score$n)
  ::board::updateBoard -pgn
  
  # add time after a move played
  if {$::options(game_timeMode) == "timebonus"} {
    if { [::game::getEngineColor] == "white" } {
      ::gameclock::add 1 $::engine::data(winc$n)/1000
    } else  {
      ::gameclock::add 2 $::engine::data(binc$n)/1000
    }
  }
  clockStart "player"
  
  repetition
  
  # -------------------------------------------------------------
  # ponder mode (the engine just played its move)
  if {$::game::ponder && $::engine::data(ponder$n) != ""} {
    ::engine::sendToEngine "position fen [sc_pos fen] moves $::engine::data(ponder$n)" $n
    set wtime [expr [::gameclock::getdSec 1] * 100 ]
    set btime [expr [::gameclock::getdSec 2] * 100 ]
    if {$::options(game_timeMode) == "timebonus"} {
      ::engine::sendToEngine "go ponder wtime $wtime btime $btime winc $::engine::data(winc$n) binc $::engine::data(binc$n)" $n
    } elseif {$::options(game_timeMode) == "depth"} {
      ::engine::sendToEngine "go ponder depth $::engine::data(fixeddepth$n)" $n
    } elseif {$::options(game_timeMode) == "movetime"} {
      ::engine::sendToEngine "go ponder movetime $::engine::data(movetime$n)" $n
    } elseif {$::options(game_timeMode) == "nodes"} {
      ::engine::sendToEngine "go ponder nodes $::engine::data(fixednodes$n)" $n
    }
  }
  
  after 1000 ::game::engineGo $n
}
################################################################################
#  start the clock of "engine" or "player" and stops the other one
# clock 1 is always for white
################################################################################
proc ::game::clockStart { arg } {
  if {$arg == "engine"} {
    if { [::game::getEngineColor] == "white" } {
      set clockPlayer 2
      ::gameclock::stop 2
      ::gameclock::start 1
    } else {
      set clockPlayer 1
      ::gameclock::stop 1
      ::gameclock::start 2
    }
    # add elapsed game time as comment like {[%egt 1:25:42]}
    if { $::options(game_elapsedGameTime) } {
      set egt [::gameclock::getEgt $clockPlayer]
      sc_pos setComment "\{\[%egt $egt\]\}"
    }
  } else {
    if { [::game::getEngineColor] == "white" } {
      ::gameclock::stop 1
      ::gameclock::start 2
    } else {
      ::gameclock::stop 2
      ::gameclock::start 1
    }
  }
}
################################################################################
# check if the game is a draw by 50 moves rule
################################################################################
proc ::game::draw50moves { { showMessage 1 } } {
  if { [lindex [split [sc_pos fen]] 4] >= 50 } {
    if { $showMessage } {
      tk_messageBox -type ok -message [::msgcat::mc "Draw"] -parent .fTop -icon info
    }
    return 1
  }
  return 0
}
################################################################################
# check if the game is a draw by insufficient material
################################################################################
proc ::game::drawInsufficientMaterial { { showMessage 1 } } {
  set fen [ lindex [sc_pos fen] 0]
  
  set wp 0 ; set wb 0 ; set wr 0 ; set wn 0 ; set wq 0
  set bp 0 ; set bb 0 ; set br 0 ; set bn 0 ; set bq 0
  
  for {set i 0} {$i < [string length $fen]} {incr i} {
    switch --  [string index $fen $i] {
      P { incr wp }
      p { incr bp }
      B { incr wb }
      b { incr bb }
      R { incr wr }
      r { incr br }
      N { incr wn }
      n { incr bn }
      Q { incr wq }
      q { incr bq }
    }
  }
  
  if { $wp == 0 && $bp == 0 && $wr == 0 && $br == 0 && $wq == 0 && $bq == 0} {
    if { [ expr $wb + $wn ] <= 1 && [ expr $bb + $bn ] <= 1 } {
      if { $showMessage } {
        tk_messageBox -type ok -message [::msgcat::mc "Draw"] -parent .fTop -icon info
      }
      return 1
    }
  }
  
  return 0
}
################################################################################
#   add current position for 3fold repetition detection and returns 1 if
# the position is a repetion
# It also calls draw50moves and drawInsufficientMaterial
################################################################################
proc ::game::repetition { { showMessage 1 } } {
  set elt [lrange [split [sc_pos fen]] 0 2]
  lappend ::game::lFen $elt
  if { [llength [lsearch -all $::game::lFen $elt] ] >=3 } {
    if { $showMessage } {
      tk_messageBox -type ok -message [::msgcat::mc "Draw"] -parent .fTop -icon info
    }
    return 1
  }
  
  if { [ draw50moves $showMessage ] == 1} {
    return 1
  }
  
  if { [ drawInsufficientMaterial $showMessage ] == 1} {
    return 1
  }
  
  return 0
}

proc ::game::repetitionReset {} {
  set ::game::lFen {}
}
################################################################################
#
################################################################################
proc ::game::getEngineColor {} {
  # Engine always plays for the upper side
  if { $::board::isFlipped == 0 } {
    return "black"
  } else  {
    return "white"
  }
}
################################################################################
proc ::game::MoveComment {} {
  set oldNag [sc_pos getNags]
  if {$oldNag == "0"} {set oldNag ""}
  set oldComment [sc_pos getComment]
  
  set ::game::newComment ""
  set ::game::newNag ""
  
  set w .moveComment
  if {[winfo exists $w]} {
    focus $w
    return
  }
  toplevel $w
  ::ui::setFullSize $w
  ttk::frame $w.fNagDisp
  text $w.fNagDisp.text -height 1
  $w.fNagDisp.text insert end $oldNag
  ttk::button $w.fNagDisp.bClear -text "Clear" -command { .moveComment.fNagDisp.text delete 1.0 end }
  pack $w.fNagDisp.bClear -side right
  pack $w.fNagDisp.text -side right -expand 1
  
  ttk::frame $w.fNagButton
  set idx 1
  foreach nag { ! ? !! ?? !? ?! = ~ += =+ +/- -/+ +- -+ } {
    ttk::button $w.fNagButton.b$idx -text $nag -command " .moveComment.fNagDisp.text insert end \" $nag\" "
    incr idx
  }
  
  for {set i 1} {$i<=5} {incr i} { grid $w.fNagButton.b$i -row 0 -column [expr $i - 1] -sticky ew }
  for {set i 6} {$i<=10} {incr i} { grid $w.fNagButton.b$i -row 1 -column [expr $i - 6] -sticky ew }
  for {set i 11} {$i<=14} {incr i} { grid $w.fNagButton.b$i -row 2 -column [expr $i - 11] -sticky ew }
  
  ttk::frame $w.fCommentDisp
  text $w.fCommentDisp.text
  $w.fCommentDisp.text insert end $oldComment
  pack $w.fCommentDisp.text -fill both
  
  ttk::frame $w.fCommentButton
  ttk::button $w.fCommentButton.b0 -text "Clear" -command { .moveComment.fCommentDisp.text delete 1.0 end }
  pack $w.fCommentButton.b0
  
  ttk::frame $w.fClose
  ttk::button $w.fClose.bClose -text "OK" -command { sc_game undoPoint ; ::game::updateComment; destroy .moveComment}
  ttk::button $w.fClose.bCancel -text [::msgcat::mc "Cancel"] -command {destroy .moveComment}
  pack $w.fClose.bClose $w.fClose.bCancel -side left
  
  pack $w.fNagDisp $w.fNagButton -side top -fill both
  pack $w.fClose $w.fCommentButton -side bottom -fill x
  pack $w.fCommentDisp -side bottom -expand 1
  
}
################################################################################
proc ::game::updateComment {} {
  set nag [.moveComment.fNagDisp.text get 1.0 end]
  set comment [.moveComment.fCommentDisp.text get 1.0 end]
  set nag [string trim $nag]
  set comment [string trim $comment]
  
  sc_pos setComment $comment
  sc_pos clearNags
  foreach n $nag {
    sc_pos addNag $n
  }
  ::infopanel::updateComments
}
################################################################################
proc ::game::info {} {
  set w .gameInfo
  toplevel $w
  ::ui::setFullSize $w
  
  set row 0
  set f [ ttk::frame $w.tags ]
  grid columnconfigure $f 1 -weight 5
  
  ttk::label $f.labelWhite -text White
  ttk::entry $f.white
  $f.white insert end [sc_game tag get White]
  grid $f.labelWhite -row $row -column 0 -sticky w
  grid $f.white -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelWhiteElo -text Elo
  ttk::entry $f.whiteElo  -validate all -validatecommand { string is integer %P }
  $f.whiteElo insert end [sc_game tag get WhiteElo]
  grid $f.labelWhiteElo -row $row -column 0 -sticky w
  grid $f.whiteElo -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelBlack -text Black
  ttk::entry $f.black
  $f.black insert end [sc_game tag get Black]
  grid $f.labelBlack -row $row -column 0 -sticky w
  grid $f.black -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelBlackElo -text Elo
  ttk::entry $f.blackElo -validate all -validatecommand { string is integer %P }
  $f.blackElo insert end [sc_game tag get BlackElo]
  grid $f.labelBlackElo -row $row -column 0 -sticky w
  grid $f.blackElo -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelEvent -text Event
  ttk::entry $f.event
  $f.event insert end [sc_game tag get Event]
  grid $f.labelEvent -row $row -column 0 -sticky w
  grid $f.event -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelSite -text Site
  ttk::entry $f.site
  $f.site insert end [sc_game tag get Site]
  grid $f.labelSite -row $row -column 0 -sticky w
  grid $f.site -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelDate -text Date
  ttk::entry $f.date
  $f.date insert end [sc_game tag get Date]
  grid $f.labelDate -row $row -column 0 -sticky w
  grid $f.date -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelRound -text Round
  ttk::entry $f.round -validate all -validatecommand { string is integer %P }
  $f.round insert end [sc_game tag get Round]
  grid $f.labelRound -row $row -column 0 -sticky w
  grid $f.round -row $row -column 1 -sticky w
  incr row
  
  
  ttk::label $f.labelEco -text ECO
  ttk::entry $f.eco
  $f.eco insert end [sc_game tag get ECO]
  grid $f.labelEco -row $row -column 0 -sticky w
  grid $f.eco -row $row -column 1 -sticky w
  incr row
  
  ttk::label $f.labelResult -text Result
  ttk::combobox $f.result -state readonly -width 4 -values { "1-0" "0-1" "=" "*" }
  set result [sc_game info result]
  if {$result == "=-="} {set result "=" }
  $f.result set $result
  grid $f.labelResult -row $row -column 0 -sticky w
  grid $f.result -row $row -column 1 -sticky w
  incr row
  
  ttk::frame $w.buttons
  ttk::button $w.buttons.save -text "Save" -command {
    set f .gameInfo.tags
    sc_game tag set -white [$f.white get]
    sc_game tag set -black [$f.black get]
    sc_game tag set -whiteElo [$f.whiteElo get]
    sc_game tag set -blackElo [$f.blackElo get]
    sc_game tag set -event [$f.event get]
    sc_game tag set -site [$f.site get]
    sc_game tag set -date [$f.date get]
    sc_game tag set -round [$f.round get]
    sc_game tag set -eco [$f.eco get]
    set result [$f.result get]
    if {$result == "1-0"} {  set result 1 }
    if {$result == "0-1"} {  set result 0 }
    sc_game tag set -result $result
  }
  ttk::button $w.buttons.close -text "Close" -command " focus . ; destroy $w "
  pack $w.buttons.save $w.buttons.close -fill x -side left
  
  pack $f -fill both -expand 1
  pack $w.buttons -fill x -side bottom
}

################################################################################
set ::game::openingList [ list \
    "[::msgcat::mc Reti]: 1.Nf3" \
    "[::msgcat::mc English]: 1.c4" \
    "[::msgcat::mc [list 1.d4 Nf6 Miscellaneous]]: 1.d4 Nf6" \
    "[::msgcat::mc Trompowsky]: 1.d4 Nf6 2.Bg5" \
    "[::msgcat::mc Budapest]: 1.d4 Nf6 2.c4 e5" \
    "[::msgcat::mc [list Old Indian]]: 1.d4 Nf6 2.c4 d6" \
    "[::msgcat::mc [list Benko Gambit]]: 1.d4 Nf6 2.c4 c5 3.d5 b5" \
    "[::msgcat::mc [list Modern Benoni]]: 1.d4 Nf6 2.c4 c5 3.d5 e6" \
    "[::msgcat::mc [list Dutch Defence]]: 1.d4 f5" \
    "1.e4" \
    "[::msgcat::mc [list Scandinavian]]: 1.e4 d5" \
    "[::msgcat::mc [list Alekhine Defence]]: 1.e4 Nf6" \
    "[::msgcat::mc [list Pirc]]: 1.e4 d6" \
    "[::msgcat::mc [list Caro-Kann]]: 1.e4 c6" \
    "[::msgcat::mc [list Caro-Kann Advance]]: 1.e4 c6 2.d4 d5 3.e5" \
    "[::msgcat::mc [list Sicilian]]: 1.e4 c5" \
    "[::msgcat::mc [list Sicilian Alapin]]: 1.e4 c5 2.c3" \
    "[::msgcat::mc [list Sicilian Closed]]: 1.e4 c5 2.Nc3" \
    "[::msgcat::mc [list Sicilian]]: 1.e4 c5 2.Nf3 Nc6" \
    "[::msgcat::mc [list Sicilian]]: 1.e4 c5 2.Nf3 e6" \
    "[::msgcat::mc [list Sicilian Rauzer]]: 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 Nc6" \
    "[::msgcat::mc [list Sicilian Dragon]]: 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 g6 " \
    "[::msgcat::mc [list Sicilian Scheveningen]]: 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 e6" \
    "[::msgcat::mc [list Sicilian Najdorf]]: 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 a6" \
    "[::msgcat::mc [list Open Game]]: 1.e4 e5" \
    "[::msgcat::mc [list Vienna]]: 1.e4 e5 2.Nc3" \
    "[::msgcat::mc [list King's Gambit]]: 1.e4 e5 2.f4" \
    "[::msgcat::mc [list Russian Game]]: 1.e4 e5 2.Nf3 Nf6" \
    "[::msgcat::mc [list Open Game]]: 1.e4 e5 2.Nf3 Nc6" \
    "[::msgcat::mc [list Italian/Two Knights]]: 1.e4 e5 2.Nf3 Nc6 3.Bc4" \
    "[::msgcat::mc [list Spanish]]: 1.e4 e5 2.Nf3 Nc6 3.Bb5" \
    "[::msgcat::mc [list Spanish Exchange]]: 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Bxc6" \
    "[::msgcat::mc [list Spanish Open]]: 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4 Nf6 5.O-O Nxe4" \
    "[::msgcat::mc [list Spanish Closed]]: 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4 Nf6 5.O-O Be7" \
    "[::msgcat::mc [list French Defence]]: 1.e4 e6" \
    "[::msgcat::mc [list French Advance]]: 1.e4 e6 2.d4 d5 3.e5" \
    "[::msgcat::mc [list French Tarrasch]]: 1.e4 e6 2.d4 d5 3.Nd2" \
    "[::msgcat::mc [list French Winawer]]: 1.e4 e6 2.d4 d5 3.Nc3 Bb4" \
    "[::msgcat::mc [list French Exchange]]: 1.e4 e6 2.d4 d5 3.exd5 exd5" \
    "[::msgcat::mc [list Queen's Pawn]]: 1.d4 d5" \
    "[::msgcat::mc [list Slav]]: 1.d4 d5 2.c4 c6" \
    "[::msgcat::mc [list QGA]]: 1.d4 d5 2.c4 dxc4" \
    "[::msgcat::mc [list QGD]]: 1.d4 d5 2.c4 e6" \
    "[::msgcat::mc [list QGD Exchange]]: 1.d4 d5 2.c4 e6 3.cxd5 exd5" \
    "[::msgcat::mc [list Semi-Slav]]: 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Nf3 c6" \
    "[::msgcat::mc [list QGD with Bg5]]: 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5" \
    "[::msgcat::mc [list QGD Orthodox]]: 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5 Be7 5.e3 O-O 6.Nf3 Nbd7" \
    "[::msgcat::mc [list Gruenfeld]]: 1.d4 Nf6 2.c4 g6 3.Nc3 d5" \
    "[::msgcat::mc [list Gruenfeld Exchange]]: 1.d4 Nf6 2.c4 g6 3.Nc3 d5 4.cxd5" \
    "[::msgcat::mc [list Gruenfeld Russian]]: 1.d4 Nf6 2.c4 g6 3.Nc3 d5 4.Nf3 Bg7 5.Qb3" \
    "[::msgcat::mc [list Catalan]]: 1.d4 Nf6 2.c4 e6 3.g3 " \
    "[::msgcat::mc [list Catalan Open]]: 1.d4 Nf6 2.c4 e6 3.g3 d5 4.Bg2 dxc4" \
    "[::msgcat::mc [list Catalan Closed]]: 1.d4 Nf6 2.c4 e6 3.g3 d5 4.Bg2 Be7" \
    "[::msgcat::mc [list Queen's Indian]]: 1.d4 Nf6 2.c4 e6 3.Nf3 b6" \
    "[::msgcat::mc [list Nimzo-Indian]]: 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4" \
    "[::msgcat::mc [list Nimzo-Indian Classical]]: 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4 4.Qc2" \
    "[::msgcat::mc [list Nimzo-Indian Rubinstein]]: 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4 4.e3" \
    "[::msgcat::mc [list King's Indian]]: 1.d4 Nf6 2.c4 g6" \
    "[::msgcat::mc [list King's Indian Sämisch]]: 1.d4 Nf6 2.c4 g6 4.e4 d6 5.f3" \
    "[::msgcat::mc [list King's Indian Main Line]]: 1.d4 Nf6 2.c4 g6 4.e4 d6 5.Nf3" \
    ]

### ScidPocket.
### Copyright (C) 2008  Pascal Georges
#
################################################################################
#                         N O V A G
################################################################################
namespace eval novag {
  
  set connected 0
  set fd 0
  set waitBetweenMessages 0
  set consoleList {}
  set setupCmd ""
  set synchroPending 0
  set flippedBoard 0
}
################################################################################
proc ::novag::connect {} {
  if {$::novag::connected} {
    
    if { [catch {set ::novag::fd [open $::options(novag_port) RDWR]} ] } {
      tk_messageBox -title "Novag Citrine" -type ok -icon error -message "Connection failed"
      set ::novag::connected 0
      return
    }
    
    fconfigure $::novag::fd -buffering line -blocking 0 -translation auto -mode 57600,n,8,1
    fileevent $::novag::fd readable ::novag::readchan
    set ::novag::consoleList {}
    # by default start in referee mode
    wait 200
    ::novag::referee
  } else {
    close $::novag::fd
  }
}
################################################################################
proc ::novag::configure {} {
  set w .serialConfig
  if { [winfo exists $w]} { return }
  toplevel $w
  wm title $w "Set COM port"
  label $w.l -text "COM7: , COM8 , ..."
  entry $w.e -width 15 -textvariable ::options(novag_port)
  button $w.bOk -text OK -command "destroy $w"
  pack $w.l $w.e $w.bOk
}
################################################################################
proc ::novag::showConsole {} {
  set w .novagconsole
  if {[winfo exists $w]} { focus $w ; return }
  toplevel $w
  
  ::ui::setFullSize $w 0
  
  frame $w.fcmd
  entry $w.fcmd.cmd -width 25
  button $w.fcmd.send -text Send -command {
    ::novag::writechan [.novagconsole.fcmd.cmd get]
    .novagconsole.fcmd.cmd delete 0 end
  }
  bind $w.fcmd.cmd <Return> {.novagconsole.fcmd.send invoke}
  pack $w.fcmd.cmd $w.fcmd.send -side left -fill x
  pack $w.fcmd
  focus $w.fcmd.cmd
  
  frame $w.top
  pack $w.top -fill both -expand 1
  frame $w.bottom
  pack $w.bottom
  
  ttk::scrollbar $w.top.ysc -command { .novagconsole.top.console yview }
  text $w.top.console -height 10 -width 40 -wrap word -yscrollcommand "$w.top.ysc set"
  pack $w.top.ysc -side left -fill y -side right
  pack $w.top.console -side left -fill both -expand 1 -side right
  
  button $w.bottom.close -text Close -command "destroy $w"
  pack $w.bottom.close -fill x
  foreach line $::novag::consoleList {
    $w.top.console insert end "$line\n"
  }
  
  update idletasks
  $w.top.console yview moveto 1
}
##########################################################
proc ::novag::newGame {} {
  if {! $::novag::connected} {return}
  writechan "N"
  wait 100
  ::novag::referee
}
##########################################################
proc ::novag::addMove {san} {
  # if promotion add "/"
  if {[string length $san] == 5} {
    set san "[string range $san 0 3]/[string range $san 4 end]"
  }
  
  ::novag::writechan "M $san"
  if { $::options(novag_sendmovetwice) } {
    if { [ string first "x" [sc_game info previousMove] ] != -1 } {
      wait 2000
    } else {
      wait 1000
    }
    ::novag::writechan "M $san"
  }
}
################################################################################
proc ::novag::checkBoardSync {} {
  if { ! $::novag::connected } { return }
  set novagfen [::novag::getFen]
  set fen [sc_pos fen]
  set fen "[lindex $fen 0] [lindex $fen 1]"
  
  if { $novagfen != $fen } {
    set lret [::novag::fenDiff $fen $novagfen ]
    if {[llength $lret] > 6} {
      set msg "$fen\n$novagfen"
    } else {
      set msg "\tScid\tNovag\n"
      foreach l $lret {
        append msg "[lindex $l 0]\t[lindex $l 1]\t[lindex $l 2]\n"
      }
    }
    
    tk_messageBox -title "Novag Citrine" -type ok -icon warning -message "Out of sync\n$msg"
    return 0
  }
  return 1
}
################################################################################
proc ::novag::fenDiff { fen1 fen2 } {
  
  set lcol { a b c d e f g h }
  set row 8
  set lret {}
  
  foreach e1 [split $fen1 "/" ] e2 [ split $fen2 "/" ] {
    
    if {$e1 != $e2 } {
      
      # remplace les chiffres par autant de 0
      set e1 [string map { 1 0 2 00 3 000 4 0000 5 00000 6 000000 7 0000000 8 00000000 } $e1]
      set e2 [string map { 1 0 2 00 3 000 4 0000 5 00000 6 000000 7 0000000 8 00000000 } $e2]
      for {set i 0} { $i < 8 } { incr i } {
        set c1 [string index $e1 $i]
        set c2 [string index $e2 $i]
        if {$c1 != $c2} {
          if {$c1 == "0"} {
            set c1 "<>"
          } else {
            if {[string is lower $c1]} { set c1 "b$c1" } else {set c1 "W$c1"}
          }
          if {$c2 == "0"} {
            set c2 "<>"
          } else {
            if {[string is lower $c2]} { set c2 "b$c2" } else {set c2 "W$c2"}
          }
          lappend lret "[lindex $lcol $i]$row $c1 $c2"
        }
      }
    }
    incr row -1
  }
  return $lret
  
}
################################################################################
proc ::novag::menuCheckBoardSync {} {
  if { [::novag::checkBoardSync] } {
    tk_messageBox -title "Novag Citrine" -type ok -icon info -message "Synchronization OK"
  }
}
################################################################################
# try to sync Citrine when it looks locked
################################################################################
# Perte de synchro :
#
#   1. ->Déconnecter
#   2. Mettre pièces en position de départ
#   3. ->Connecter
#   4. ->Setup
#   5. Mettre la position en place
#
# Cas de figure :
# 1. setup
# 2. Mettre en place pièces
# 3. setup
#
# Etapes 1 et 3 facultatives puisque normalement New game demande confirmation
################################################################################
proc ::novag::synchro {} {
  
  if { ! $::novag::connected } { return }
  
  if {[winfo exists .novagsynchro]} {
    focus .novagsynchro
    return
  }
  
  set w .novagsynchro
  toplevel $w
  wm title $w "Scid: Sync Novag"
  
  set ::novag::synchroPending 1
  
  label $w.inst1 -text [::msgcat::mc "In order to unlock Citrine,\nput pieces at start position"] -fg MidnightBlue
  
  button $w.cancel -text [::msgcat::mc "Close" ] -command {
    set ::novag::synchroPending 0
    # it seems that position must be resent
    ::novag::setup
    destroy .novagsynchro
  }
  pack $w.inst1 $w.cancel
  
  bind $w <Destroy> { set ::novag::synchroPending 0 }
  wm geometry $w +50+100
  
}
################################################################################
# send to Novag the current board
proc ::novag::setup {} {
  
  set cmd "pc"
  set board [sc_pos board]
  
  for {set i 0} {$i<64} {incr i} {
    set c [string index $board $i]
    if { $c != "." && [string is upper $c]} {
      append cmd "[string tolower $c][::board::san $i]"
    }
  }
  # scan black pieces
  append cmd "b"
  for {set i 0} {$i<64} {incr i} {
    set c [string index $board $i]
    if { $c != "." && [string is lower $c]} {
      append cmd "[string tolower $c][::board::san $i]"
    }
  }
  
  # set color
  if {[string index $board 65] == "w"} {
    append cmd "+"
  } else {
    append cmd "-"
  }
  writechan $cmd
  set ::novag::setupCmd $cmd
  set ::novag::waitForSetupOk 1
  ::vwaitTimed ::novag::waitForSetupOk 1000 nowarnuser
  if { $::novag::waitForSetupOk } {
    tk_messageBox -type ok -icon error -parent . -title "Setup" -message "Setup failed after timeout\nBoard locked !?"
  }
}
################################################################################
# returns the position part of fen and w or b
proc ::novag::getFen {} {
  writechan "P"
  set ::novag::fen ""
  vwaitTimed ::novag::fen 2000
  return $::novag::fen
}
################################################################################
proc ::novag::writechan {line} {
  # puts ">$line"
  set line "$line\r\n"
  puts -nonewline $::novag::fd $line
}
################################################################################
proc ::novag::readchan {} {
  global ::novag::fd
  set l [gets $fd]
  if { $l == "" } { return }
  
  # puts "<$l"
  
  updateConsole $l
  
  if {[string match -nocase "New Game*" $l]} {
    if { $::novag::synchroPending } {
      .novagsynchro.inst1 configure -text [::msgcat::mc "Set pieces back to the position\nand press button when finished"] -fg SeaGreen
      
      # the board should be ok, send it the position
      ::novag::setup
      wait 200
      ::novag::referee
      return
    }
    ::game::new
    ::board::updateBoard -pgn -nolastmove
    # new game resets board flip
    if {$::novag::flippedBoard} {
      wait 100
      writechan "F"
    }
    wait 100
    ::novag::referee
    return
  }
  # move
  if {[lindex $l 0] == "M"} {
    
    if {[sc_pos side] == "white" && [string index [lindex $l 1] end ] == ","} {  return }
    if {[sc_pos side] == "black" && [string index [lindex $l 1] end ] != ","} {  return }
    
    set m [lindex $l 2]
    
    if { $m == "O-O" || $m == "O-O-O"} {
      if { [ catch { sc_move addSan $m } err ] } {
        tk_messageBox -type ok -icon error -parent . -title "Novag" -message "sc_move addSan $m\n$err"
        return
      }
    } else {
      set m [string map { "-" "" "/" "" "x" "" } $m ]
      if { [ catch { sc_move addUCI $m } err ] } {
        tk_messageBox -type ok -icon error -parent . -title "Novag" -message "sc_move addUCI $m\n$err"
        return
      }
    }
    
    if { $::fics::playing == 1} {
      ::fics::writechan [ sc_game info previousMoveNT ]
    }
    
    ::board::updateBoard -pgn
    # ::novag::checkBoardSync
    return
  }
  
  # takeback
  if {[lindex $l 0] == "T"} {
    sc_move back
    ::board::updateBoard -pgn
    return
  }
  
  # position
  if {$l == ".  aa bb cc dd ee ff gg hh"} {
    set f ""
    for {set i 8} { $i > 0} {incr i -1} {
      set l [gets $fd]
      if {$l == ""} {
        after 100
        set l [gets $fd]
      }
      updateConsole $l
      if { [string range $l 0 1] != ".$i" || [string range $l end end] != "$i"} {
      }
      set f "$f[fenline $l]"
      if {$i != 1} {
        set f "$f/"
      } else {
        if {[string index $l end] == "+"} { set color "w" } else { set color "b" }
        set f "$f $color"
      }
    }
    set l [gets $fd] ;# last ".  aa bb cc dd ee ff gg hh"
    
    set ::novag::fen $f
  }
  
  # setup confirmation
  if {[string match "pc*" $l]} {
    if { $::novag::synchroPending } {
      if { $l == $::novag::setupCmd } {
        .novagsynchro.inst1 configure -fg SeaGreen
      } else {
        .novagsynchro.inst1 configure -fg red
      }
      set ::novag::waitForSetupOk 0
      return
    }
    if { $l == $::novag::setupCmd } {
      tk_messageBox -type ok -icon info -parent . -title "Setup" -message "Setup successfull"
    } else {
      tk_messageBox -type ok -icon error -parent . -title "Setup" -message "Setup failed"
    }
    set ::novag::waitForSetupOk 0
  }
}
################################################################################
proc ::novag::fenline {l} {
  set s ""
  set empty 0
  for {set i 3} { $i <= 24} {incr i 3} {
    set elt [string range $l $i [expr $i +1]]
    if {$elt == "  " || $elt == "::"} {
      incr empty
    } else {
      if {$empty != 0} {
        set s "$s$empty"
        set empty 0
      }
      set s "$s[string index $elt 1]"
    }
  }
  if {$empty != 0} {
    set s "$s$empty"
  }
  return $s
}
################################################################################
proc ::novag::updateConsole {line} {
  lappend ::novag::consoleList $line
  if {[winfo exists .novagconsole]} {
    set t .novagconsole.top.console
    $t insert end "$line\n"
    $t yview moveto 1
  }
}
##########################################################
proc ::novag::wait {ms} {
  after $ms {set ::novag::waitBetweenMessages 1}
  vwait ::novag::waitBetweenMessages
}
##########################################################
proc ::novag::referee {} {
  if { $::options(novag_refereeMode) } {
    ::novag::writechan "U ON"
  } else  {
    ::novag::writechan "U OFF"
  }
}
### ScidPocket.
### Copyright (C) 2008  Pascal Georges
#
################################################################################
#            T A C T I C A L   T R A I N I N G
################################################################################
namespace eval tactics {
  set prevScore ""
  set prevLine ""
  set nextMove ""
  set matePending 0
  set analysisTime 5
  set isThinking 0
  set running 0
  set lastGameLoaded 0
  set solved "problem solved"
  set failed "problem failed"
  set winWonGame 0
}
##########################################################
proc ::tactics::config {} {
}
##########################################################
proc ::tactics::start {} {
  if { $::tactics::running } {
    # check at least a base is opened for current slot
    if { [sc_base numGames] < 1 } {
      tk_messageBox -title "Scid" -icon warning -type ok -message [::msgcat::mc "Open first a base with tactical exercises"]
      set ::tactics::running 0
      return
    }
    set ::options(addVariationWithoutAsking) 1
    set ::options(showAnalysisOld) $::options(showAnalysis)
    set ::options(showAnalysis) 0
    ::infopanel::updateAnalysis
    set ::tactics::lastGameLoaded 0
    ::tactics::loadNextGame
    ::tactics::mainLoop
  } else {
    setInfo ""
    set ::options(addVariationWithoutAsking) 0
    after cancel ::tactics::mainLoop
    set ::options(showAnalysis) $::options(showAnalysisOld)
    ::infopanel::updateAnalysis
  }
}
##########################################################
proc ::tactics::resetValues {} {
  set ::tactics::prevScore 0
  set ::tactics::prevLine ""
  set ::tactics::nextMove ""
  set ::tactics::matePending 0
  set ::tactics::showSolution 0
  set ::tactics::labelSolution ""
  set ::tactics::prevFen ""
}
##########################################################
proc ::tactics::showSolution {} {
  set l [lindex $::engine::data(multipv1) 0]
  set score [lindex $l 1]
  set pv [lindex $l 2]
  
  if { $score != "" } {
    if { [expr abs($score)] == 327.0 && [lindex $l 3] != "" } {
      set score "M [lindex $l 3]"
    }
  }
  
  tk_messageBox -title "Scid" -icon info -type ok -message "$score\n$pv"
}
################################################################################
# waits for the user to play and check the move played
proc ::tactics::mainLoop {} {
  global ::tactics::prevScore ::tactics::prevLine ::tactics::nextMove
  
  after cancel ::tactics::mainLoop
  
  if {[sc_pos fen] != $::tactics::prevFen && [sc_pos isAt start]} {
    ::tactics::abnormalContinuation
    return
  }
  
  # is this player's turn (which always plays from bottom of the board) ?
  if { [::tactics::isPlayerTurn] } {
    after 1000 ::tactics::mainLoop
    return
  }
  
  set ::tactics::prevFen [sc_pos fen]
  
  # check if player's move is a direct mate : no need to wait for engine analysis in this case
  set move_done [sc_game info previousMove]
  if { [string index $move_done end] == "#"} { ::tactics::exSolved; return }
  
  # if the engine is still analyzing, wait the end of it
  if {$::tactics::isThinking} { vwait ::tactics::isThinking }
  if {[sc_pos fen] != $::tactics::prevFen  && [sc_pos isAt start]} {
    ::tactics::abnormalContinuation
    return
  }
  
  # the player moved and analysis is over : check if his move was as good as expected
  set l [lindex $::engine::data(multipv1) 0]
  set prevScore [lindex $l 1]
  set prevLine [lindex $l 2]
  
  ::tactics::startAnalyze
  
  # now wait for the end of analyzis
  if {$::tactics::isThinking} { vwait ::tactics::isThinking }
  if {[sc_pos fen] != $::tactics::prevFen  && [sc_pos isAt start]} {
    ::tactics::abnormalContinuation
    return
  }
  
  # compare results
  set res [::tactics::foundBestLine]
  
  if {  $res != ""} {
    tk_messageBox -title "Scid" -icon info -type ok -message "[::msgcat::mc [list Best solution not found]]\n$res"
    # take back last move so restore engine status
    # TODO : à virer ou utile ??
    #  ****************************************************************************************
    # set analysisEngine(score) $prevScore
    # set analysisEngine(moves) $prevLine
    sc_game tags set -site $::tactics::failed
    sc_game save [sc_game number]
    sc_move back
    ::board::updateBoard -pgn
    set ::tactics::prevFen [sc_pos fen]
  } else  {
    catch { sc_move addSan [::untrans $nextMove] }
    set ::tactics::prevFen [sc_pos fen]
    ::board::updateBoard -pgn
    if { $::tactics::matePending } {
      # continue until end of game
    } else  {
      sc_game tags set -site $::tactics::solved
      sc_game save [sc_game number]
      tk_messageBox -title "Scid" -icon info -type ok -message [::msgcat::mc "Good move"]
      loadNextGame
      return
    }
  }
  
  after 1000 ::tactics::mainLoop
}
################################################################################
proc ::tactics::isPlayerTurn {} {
  if { [sc_pos side] == "white" &&  ! $::board::isFlipped || [sc_pos side] == "black" &&  $::board::isFlipped } {
    return 1
  }
  return 0
}
################################################################################
proc ::tactics::exSolved {} {
  ::tactics::stopAnalyze
  tk_messageBox -title "Scid" -icon info -type ok -message [::msgcat::mc "Mate found"]
  sc_game tags set -site $::tactics::solved
  sc_game save [sc_game number]
  ::tactics::loadNextGame
}
################################################################################
proc ::tactics::loadNextGame {} {
  ::tactics::resetValues
  
  set newGameFound 0
  # find a game with site tag != problem solved
  for {set g [ expr $::tactics::lastGameLoaded +1 ] } { $g <= [sc_base numGames]} { incr g} {
    # sc_game load $g
    ::game::gameLoad $g
    set tag [sc_game tags get "Site"]
    if {$tag != $::tactics::solved} { set newGameFound 1 ; break }
  }
  # it seems we finished the serial
  if {! $newGameFound } {
    tk_messageBox -title "Scid" -icon info -type ok -message [::msgcat::mc "All exercises done"]
    return
  }
  set ::tactics::lastGameLoaded $g
  
  if { [sc_pos side] == "white" && $::board::isFlipped || [sc_pos side] == "black" &&  ! $::board::isFlipped } {
    ::board::toggleFlip
  }
  
  ::board::updateBoard -pgn
  set ::tactics::prevFen [sc_pos fen]
  
  ::tactics::startAnalyze
  ::tactics::mainLoop
}
##########################################################
proc ::tactics::startAnalyze {} {
  ::engine::startAnalysis 1
  set ::tactics::isThinking 1
  setInfo [::msgcat::mc "Thinking, wait before entering your move"] IndianRed
  after [expr 1000 * $::tactics::analysisTime] ::tactics::stopAnalyze
}
##########################################################
proc ::tactics::stopAnalyze {} {
  ::engine::stopAnalysis 1
  set ::tactics::isThinking 0
  setInfo [::msgcat::mc "Analyze done"] ForestGreen
}
##########################################################
proc ::tactics::foundBestLine {} {
  global ::tactics::prevScore ::tactics::prevLine ::tactics::nextMove ::tactics::matePending
  
  set l [lindex $::engine::data(multipv1) 0]
  set score [lindex $l 1]
  set line [lindex $l 2]
  
  set s [ regsub -all "\[\.\]{3} " $line "" ]
  set s [ regsub -all "\[0-9\]+\[\.\] " $s "" ]
  set nextMove [ lindex [ split $s ] 0 ]
  set ply [ llength [split $s] ]
  
  # check if the player played the same move predicted by Engine
  set s [ regsub -all "\[\.\]{3} " $prevLine "" ]
  set s [ regsub -all "\[0-9\]+\[\.\] " $s "" ]
  set prevBestMove [ lindex [ split $s ] 1 ]
  
  if { [sc_game info previousMoveNT] == $prevBestMove} {
    return ""
  }
  
  # Case of mate
  if { [string index $prevLine end] == "#"} {
    set matePending 1
    #  Engine may find a mate then put a score != 300 but rather 10
    if {[string index $line end] != "#"} {
      # Engine line does not end with a # but the score is a mate (we can't count plies here)
      if {[sc_pos side] == "white" && $score < -300 || [sc_pos side] == "black" && $score > 300} {
        return ""
      }
      if {! $::tactics::winWonGame } {
        return [::msgcat::mc "Mate not found"]
      } else  {
        # win won game but still have to find a mate
        if {[sc_pos side] == "white" && $score < -300 || [sc_pos side] == "black" && $score > 300} {
          return ""
        } else  {
          return [::msgcat::mc "Mate not found"]
        }
      }
    }
    # Engine found a mate, search in how many plies
    set s [ regsub -all "\[\.\]{3} " $prevLine "" ]
    set s [ regsub -all "\[0-9\]+\[\.\] " $s "" ]
    set prevPly [ llength [ split $s ] ]
    if { $ply > [ expr $prevPly - 1 ] && ! $::tactics::winWonGame } {
      return [::msgcat::mc "Shorter mate exists"]
    } else  {
      return ""
    }
  } else  {
    # no mate case
    set matePending 0
    set threshold 0.5
    if {$::tactics::winWonGame} {
      # Only alert when the advantage clearly changes side
      if {[sc_pos side] == "white" && $prevScore < 0 && $score >= $threshold  || \
            [sc_pos side] == "black" &&  $prevScore >= 0 && $score < [expr 0 - $threshold]  } {
        return "[::msgcat::mc [list Score played]] $score\n$[::msgcat::mc Expected] $prevScore"
      } else  {
        return ""
      }
    }
    if {[ expr abs($prevScore) ] > 3.0 } { set threshold 1.0 }
    if {[ expr abs($prevScore) ] > 5.0 } { set threshold 1.5 }
    # the player moved : score is from opponent side
    if {[sc_pos side] == "white" && $score < [ expr $prevScore + $threshold ] || \
          [sc_pos side] == "black" && $score > [ expr $prevScore - $threshold ] } {
      return ""
    } else  {
      return "[::msgcat::mc [list Score played]] $score\n[::msgcat::mc Expected] $prevScore"
    }
  }
}
################################################################################
# Handle the case where position was changed not during normal play but certainly with
# move back / forward / rewind commands
################################################################################
proc ::tactics::abnormalContinuation {} {
  ::tactics::stopAnalyze
  ::tactics::resetValues
  
  ::board::updateBoard -pgn
  if { [sc_pos side] == "white" && $::board::isFlipped || [sc_pos side] == "black" &&  ! $::board::isFlipped } {
    ::board::toggleFlip
  }
  set ::tactics::prevFen [sc_pos fen]
  ::tactics::startAnalyze
  ::tactics::mainLoop
}
################################################################################
proc ::tactics::resetScores {} {
  global ::tactics::cancelScoreReset
  
  #reset site tag for each game
  progressWindow "Scid" "Resetting score" "Cancel" ::tactics::sc_progressBar
  set numGames [sc_base numGames]
  set cancelScoreReset 0
  for {set g 1} { $g <= $numGames } { incr g} {
    if { $cancelScoreReset } { break }
    sc_game load $g
    if { [sc_game tags get "Site"] != ""} {
      sc_game tags set -site ""
      sc_game save [sc_game number]
    }
    if { [expr $g % 10] == 0 } {
      updateProgressWindow $g $numGames
    }
  }
  closeProgressWindow
  
}
################################################################################
# cancel score reset loading
################################################################################
proc ::tactics::sc_progressBar {} {
  set ::tactics::cancelScoreReset 1
}
################################################################################
proc ::tactics::setInfo { msg {bg white} } {
  # $w tag configure enginescore -foreground red -background black -relief raised -border 1 -font fontanalysis
  set w .fEngine.text
  $w delete 1.0 end
  $w configure -bg $bg
  $w insert end $msg enginescore
}

### ScidPocket.
### Copyright (C) 2007  Pascal Georges
############################################################
### PGN window

namespace eval pgn {
  set pgnColor(Header) "\#00008b"
  set pgnColor(Main) "\#000000"
  set pgnColor(Var) "\#0000ee"
  set pgnColor(Nag) "\#ee0000"
  set pgnColor(Comment) "\#008b00"
  set pgnColor(Current) lightSteelBlue
  set pgnColor(NextMove) "\#fefe80"
  set pgnColor(Background) "\#ffffff"
  
  ################################################################################
  #
  ################################################################################
  proc OpenClose {} {
    global pgnWin
    
    if {[winfo exists .pgnWin]} {
      focus .
      destroy .pgnWin
      set pgnWin 0
      return
    }
    set w [toplevel .pgnWin]
    busyCursor .pgnWin
    ::ui::setFullSize $w 0
    
    frame .pgnWin.menu -borderwidth 1 -relief raised
    pack .pgnWin.menu -side top -fill x
    $w configure -menu $w.menu
    menubutton $w.menu.file -text File -menu $w.menu.file.m -underline 0
    menubutton $w.menu.opt -text Options -menu $w.menu.opt.m -underline 0
    foreach i {file opt} {
      menu $w.menu.$i.m -tearoff 0
      pack $w.menu.$i -side left
    }
    
    $w.menu.file.m add command -label "File copy" -command {
      set pgnStr [sc_game pgn -width 25 -indentComments $::pgn::indentComments \
          -indentVariations $::pgn::indentVars -space $::options(pgn_moveNumberSpaces)]
      set wt .tempFEN
      if {! [winfo exists $wt]} { text $wt }
      $wt delete 1.0 end
      $wt insert end $pgnStr sel
      clipboard clear
      clipboard append $pgnStr
      selection own $wt
      selection get
    }
    
    $w.menu.file.m add command -label "File save" -command {
      set ftype {
        { "PGN files"  {".pgn"} }
        { "Text files" {".txt"} }
        { "All files"  {"*"}    }
      }
      set fname [tk_getSaveFile -initialdir [pwd] -filetypes $ftype -title "Save PGN file" -parent .pgnWin]
      if {$fname != ""} {
        if {[catch {set tempfile [open $fname w]}]} {
          tk_messageBox -title "Scid: Error saving file" -type ok -icon warning \
              -message "Unable to save the file: $fname\n\n"
        } else {
          puts $tempfile \
              [sc_game pgn -width 25 -symbols $::options(pgn_symbolicNags) \
              -indentVar $::pgn::indentVars -indentCom $::pgn::indentComments \
              -space $::options(pgn_moveNumberSpaces) -format plain -column $::options(pgn_columnFormat) \
              -markCodes $::::pgn::stripMarks]
          close $tempfile
        }
      }
    }
    $w.menu.file.m add separator
    $w.menu.file.m add command -label "Close window" -command "focus .; destroy $w"
    
    $w.menu.opt.m add checkbutton -label "Color Display" \
        -variable ::options(pgn_showColor) -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Short (3-line) Header" \
        -variable ::pgn::shortHeader -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Symbolic Annotations" \
        -variable ::options(pgn_symbolicNags) -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Indent Comments" \
        -variable ::pgn::indentComments -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Indent Variations" \
        -variable ::pgn::indentVars -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Column Style (one move per line)" \
        -variable ::options(pgn_columnFormat) -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Strip out Colored Square/Arrow Codes" \
        -variable ::pgn::stripMarks -command {::board::updateBoard -pgn -nolastmove}
    $w.menu.opt.m add checkbutton -label "Use Bold Text for Main Line Moves" \
        -variable ::pgn::boldMainLine -command {::board::updateBoard -pgn -nolastmove}
    
    text $w.text -wrap word -background white -yscrollcommand "$w.scroll set" -setgrid 0 -tabs {1c right 1c 1c}
    
    if { $::pgn::boldMainLine } {
      $w.text configure -font font_Bold
    }
    ttk::scrollbar $w.scroll -command "$w.text yview" -takefocus 0
    pack [frame $w.buttons] -side bottom -fill x
    pack $w.scroll -side right -fill y
    pack $w.text -fill both ;# -expand yes
    button $w.buttons.close -text "Close" -command { focus . ; destroy .pgnWin }
    pack $w.buttons.close -side right -padx 5 -pady 2
    set pgnWin 1
    bind $w <Destroy> { set pgnWin 0 }
    
    # Bind left button to close ctxt menu:
    bind $w <ButtonPress-1> {
      if {[winfo exists .pgnWin.text.ctxtMenu]} { destroy .pgnWin.text.ctxtMenu; focus .pgnWin }
    }
    
    # Bind right button to popup a contextual menu:
    if {$::WindowsCE} {
      wce tapandhold .pgnWin.text on
    }
    
    bind $w.text <ButtonPress-3> "::pgn::contextMenu .pgnWin.text 5 %x %y %X %Y"
    
    # set the same arrow key, etc bindings that the main window has:
    # bind $w <Up>    {::move::Back 10}
    # bind $w <Left>  ::move::Back
    # bind $w <Down>  {::move::Forward 10}
    # bind $w <Right> ::move::Forward
    
    $w.text tag add Current 0.0 0.0
    ::pgn::ResetColors
    unbusyCursor .pgnWin
  }
  
  ################################################################################
  #
  ################################################################################
  proc contextMenu {win startLine x y xc yc} {
    
    update idletasks
    
    set mctxt $win.ctxtMenu
    if { [winfo exists $mctxt] } { destroy $mctxt }
    if {[sc_var level] == 0} {
      set state disabled
    } else  {
      set state normal
    }
    menu $mctxt
    $mctxt add command -label [::msgcat::mc "Delete var"] -state $state -command "::pgn::deleteVar [sc_var number]"
    $mctxt add command -label [::msgcat::mc "To first var"] -state $state -command "::pgn::firstVar [sc_var number]"
    $mctxt add command -label [::msgcat::mc "Main line"] -state $state -command "::pgn::mainVar [sc_var number]"
    $mctxt add separator
    $mctxt add command -label [::msgcat::mc "Strip begin"] -command {::pgn::TruncateBegin}
    $mctxt add command -label [::msgcat::mc "Strip end"] -command {::pgn::Truncate}
    $mctxt add separator
    $mctxt add command -label [::msgcat::mc "Strip comments"] -command {::pgn::Strip comments}
    $mctxt add command -label [::msgcat::mc "Strip vars"] -command {::pgn::Strip variations}
    
    $mctxt post [winfo pointerx .] [winfo pointery .]
    
  }
  
  #   Strips all comments or variations from a game
  proc Strip {type} {
    if {[catch {sc_game strip $type} result]} {
      tk_messageBox -parent . -type ok -icon info -title "Scid" -message $result
      return
    }
    ::board::updateBoard -pgn -nolastmove
    ::board::updateTitle
  }
  
  # ::game::TruncateBegin
  proc TruncateBegin {} {
    if {[catch {sc_game truncate -start} result]} {
      tk_messageBox -parent . -type ok -icon info -title "Scid" -message $result
      return
    }
    ::board::updateBoard -pgn -nolastmove
    ::board::updateTitle
  }
  
  # ::game::Truncate
  proc Truncate {} {
    if {[catch {sc_game truncate} result]} {
      tk_messageBox -parent . -type ok -icon info -title "Scid" -message $result
      return
    }
    ::board::updateBoard -pgn -nolastmove
    ::board::updateTitle
  }
  
  proc deleteVar { var } {
    sc_var exit
    sc_var delete $var
    ::board::updateBoard -pgn -nolastmove
  }
  
  proc firstVar { var } {
    sc_var exit
    sc_var first $var
    ::board::updateBoard -pgn -nolastmove
  }
  
  proc mainVar { var } {
    sc_var exit
    sc_var promote $var
    ::board::updateBoard -pgn -nolastmove
  }
  
  ################################################################################
  # # ::pgn::ResetColors
  #
  #    Reconfigures the pgn Colors, after a color is changed by the user
  #
  ################################################################################
  proc ResetColors {} {
    global pgnColor
    if {![winfo exists .pgnWin]} { return }
    .pgnWin.text configure -background $::pgn::pgnColor(Background)
    .pgnWin.text tag configure Current -background $::pgn::pgnColor(Current)
    .pgnWin.text tag configure NextMove -background $::pgn::pgnColor(NextMove)
    ::htext::init .pgnWin.text
    ::htext::updateRate .pgnWin.text 60
    ::pgn::Refresh 1
  }
  ################################################################################
  # ::pgn::Refresh
  #
  #    Updates the PGN window. If $pgnNeedsUpdate == 0, then the
  #    window text is not regenerated; only the current and next move
  #    tags will be updated.
  ################################################################################
  proc Refresh {{pgnNeedsUpdate 0}} {
    
    if {![winfo exists .pgnWin]} { return }
    
    set format plain
    if {$::options(pgn_showColor)} {set format color}
    
    set pgnStr [sc_game pgn -symbols $::options(pgn_symbolicNags) \
        -indentVar $::pgn::indentVars -indentCom $::pgn::indentComments \
        -space $::options(pgn_moveNumberSpaces) -format $format -column $::options(pgn_columnFormat) \
        -short $::pgn::shortHeader -markCodes $::pgn::stripMarks]

    if {$pgnNeedsUpdate} {
      set windowTitle [format Pgn [sc_game number]]
      wm title .pgnWin "Scid: $windowTitle"
      .pgnWin.text configure -state normal
      .pgnWin.text delete 1.0 end
      if {$::options(pgn_showColor)} {
        #set start [clock clicks -milli]
        ::htext::display .pgnWin.text $pgnStr
        #set end [clock clicks -milli]
        #puts "PGN: [expr $end - $start] ms"
      } else {
        .pgnWin.text insert 1.0 $pgnStr
      }
    }
    
    if {$::options(pgn_showColor)} {
      if { $::pgn::boldMainLine } {
        .pgnWin.text configure -font font_Bold
      } else {
        .pgnWin.text configure -font font_Regular
      }
      # Now update Current and NextMove tags:
      .pgnWin.text tag remove Current 1.0 end
      set offset [sc_pos pgnOffset]
      set moveRange [.pgnWin.text tag nextrange "m_$offset" 1.0]
      if {[llength $moveRange] == 2} {
        .pgnWin.text tag add Current [lindex $moveRange 0] [lindex $moveRange 1]
        .pgnWin.text see [lindex $moveRange 0]
      }
      
      .pgnWin.text tag remove NextMove 1.0 end
      set noffset [sc_pos pgnOffset next]
      if {$noffset == $offset} {set noffset 0}
      set moveRange [.pgnWin.text tag nextrange "m_$noffset" 1.0]
      if {[llength $moveRange] == 2} {
        .pgnWin.text tag add NextMove [lindex $moveRange 0] [lindex $moveRange 1]
      }
      .pgnWin.text configure -state disabled
    }
    return
  }
  ################################################################################
  #
  ################################################################################
  
}
### ScidPocket.
### Copyright (C) 2007  Pascal Georges
################################################################################
#
################################################################################

namespace eval fics {
  set port 5000
  set login ""
  set password ""
  set sockchan 0
  set server "freechess.org"
  set seeklist {}
  set observedGame -1
  set playing 0
  set waitForRating ""
  set consoleList {}
  set connected 0
  variable logged 0
  variable isGuestLogin 0
  
  ################################################################################
  #
  ################################################################################
  proc config {} {
    set w ".ficsConfig"
    
    if {[winfo exists $w]} {
      focus $w
      return
    }
    
    toplevel $w
    wm title $w "ConfigureFics"
    label $w.lLogin -text [::msgcat::mc "Login:"]
    entry $w.login -width 20 -textvariable ::options(fics_login)
    label $w.lPwd -text [::msgcat::mc "Password:"]
    entry $w.passwd -width 20 -textvariable ::options(fics_password)
    button $w.connect -text [::msgcat::mc "Connect"] -command {
      ::fics::connect [.ficsConfig.login get] [.ficsConfig.passwd get]
      destroy .ficsConfig
    }
    button $w.guest -text [::msgcat::mc "Login as guest"] -command {
      ::fics::connect "guest" ""
      destroy .ficsConfig
    }
    button $w.cancel -text [::msgcat::mc "Cancel"] -command { destroy .ficsConfig }
    
    grid $w.lLogin -column 0 -row 0 -sticky ew
    grid $w.login -column 1 -row 0 -sticky ew
    grid $w.lPwd -column 0 -row 1 -sticky ew
    grid $w.passwd -column 1 -row 1 -sticky ew
    grid $w.guest -column 0 -row 2 -columnspan 2 -sticky ew
    grid $w.connect -column 0 -row 3 -sticky ew
    grid $w.cancel -column 1 -row 3 -sticky ew
    
  }
  ################################################################################
  #
  ################################################################################
  proc connect {login passwd} {
    global ::fics::sockchan
    
    set isGuestLogin [string match -nocase "guest" $login]
    
    set ::options(fics_login) $login
    set ::options(fics_password) $passwd
    set ::fics::login $login
    
    set ::options(novag_refereeMode) 1
    
    if { [catch { set sockchan [socket $::fics::server $::options(fics_port)] } ] } {
      tk_messageBox -title "Error" -icon error -type ok \
          -message "Network error\nCan't connect to $::fics::server $::options(fics_port)\nTrying other port"
      # try the other port (23 or 5000)
      if { $::options(fics_port) == 5000 } {
        set port 23
      } else {
        set port 5000
      }
      if { [catch { set sockchan [socket $::fics::server $port] } ] } {
        tk_messageBox -title "Error" -icon error -type ok -message "Network error\nCan't connect to $::fics::server $port"
        set ::fics::connected 0
        return
      }
      
    }
    
    chan configure $sockchan -blocking 0 -buffering line -translation auto ;#-encoding iso8859-1 -translation crlf
    chan event $sockchan readable ::fics::readchan
    
    # clock 1 is white
    set ::options(game_timeMode) "timebonus"
    ::gameclock::new .fTop.fr.fclocks 2 1
    ::gameclock::new .fTop.fr.fclocks 1 1
    ::gameclock::setColor 1 white
    ::gameclock::setColor 2 black
    
  }
  ################################################################################
  #
  ################################################################################
  proc setFormula {} {
    set w .ficsformula
    if {[winfo exists $w]} { focus $w ; return }
    toplevel $w
    entry $w.formula -width 25 -textvariable ::options(fics_formula)
    button $w.save -text Save -command { ::fics::writechan "set formula $::options(fics_formula)" ; destroy .ficsformula }
    button $w.cancel -text Cancel -command { destroy .ficsformula }
    
    grid $w.formula -column 0 -row 0 -columnspan 2 -sticky ew
    grid $w.save  -column 0 -row 1 -sticky ew
    grid $w.cancel  -column 1 -row 1 -sticky ew
  }
  ################################################################################
  #
  ################################################################################
  proc showConsole {} {
    set w .ficsconsole
    if {[winfo exists $w]} { focus $w ; return }
    toplevel $w
    
    ::ui::setFullSize $w 0
    
    frame $w.fcmd
    entry $w.fcmd.cmd -width 25
    button $w.fcmd.send -text Send -command {
      ::fics::writechan [.ficsconsole.fcmd.cmd get]
      .ficsconsole.fcmd.cmd delete 0 end
    }
    bind $w.fcmd.cmd <Return> {.ficsconsole.fcmd.send invoke}
    pack $w.fcmd.cmd $w.fcmd.send -side left -fill x
    pack $w.fcmd
    focus $w.fcmd.cmd
    
    frame $w.top
    pack $w.top -fill both -expand 1
    frame $w.bottom
    pack $w.bottom
    
    ttk::scrollbar $w.top.ysc -command { .ficsconsole.top.console yview }
    text $w.top.console -height 10 -width 40 -wrap word -yscrollcommand "$w.top.ysc set"
    pack $w.top.ysc -side left -fill y -side right
    pack $w.top.console -side left -fill both -expand 1 -side right
    
    button $w.bottom.close -text Close -command "destroy $w"
    pack $w.bottom.close -fill x
    set lines ""
    foreach line $::fics::consoleList {
      append lines "$line\n"
    }
    $w.top.console insert end $lines
    
    update idletasks
    $w.top.console yview moveto 1
  }
  ################################################################################
  #
  ################################################################################
  proc findOpponent {} {
    set w .ficsfindopp
    if {[winfo exists $w]} {
      focus $w
      return
    }
    toplevel $w
    
    label $w.linit -text [::msgcat::mc "Initial time (min)"]
    spinbox $w.sbTime1 -background white -width 3 -textvariable ::options(fics_initTime) -from 0 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    label $w.linc -text [::msgcat::mc "Increment (sec)"]
    spinbox $w.sbTime2 -background white -width 3 -textvariable ::options(fics_incTime) -from 0 -to 120 -increment 1 -validate all -vcmd { regexp {^[0-9]+$} %P }
    grid $w.linit -column 0 -row 0 -sticky ew
    grid $w.sbTime1 -column 1 -row 0 -sticky ew
    grid $w.linc -column 0 -row 1 -sticky ew
    grid $w.sbTime2 -column 1 -row 1 -sticky ew
    
    checkbutton $w.cbrated -text [::msgcat::mc "Rated game"] -onvalue "rated" -offvalue "unrated" -variable ::options(fics_rated)
    grid $w.cbrated -column 0 -row 2 -columnspan 2 -sticky ew
    
    label $w.color -text [::msgcat::mc "Color"]
    grid $w.color -column 0 -row 3 -columnspan 3 -sticky ew
    radiobutton $w.rb1 -text [::msgcat::mc "Automatic"] -value "auto" -variable ::options(fics_color)
    radiobutton $w.rb2 -text [::msgcat::mc "White"] -value "white" -variable ::options(fics_color)
    radiobutton $w.rb3 -text [::msgcat::mc "Black"] -value "black" -variable ::options(fics_color)
    grid $w.rb1 -column 0 -row 4 -sticky ew
    grid $w.rb2 -column 1 -row 4 -sticky ew
    grid $w.rb3 -column 2 -row 4 -sticky ew
    
    checkbutton $w.cblimitrating -text [::msgcat::mc "Limit rating between"] -variable ::options(fics_limitrating)
    spinbox $w.sbrating1 -background white -width 4 -textvariable ::options(fics_rating1) -from 1000 -to 3000 -increment 50 -validate all -vcmd { regexp {^[0-9]+$} %P }
    spinbox $w.sbrating2 -background white -width 4 -textvariable ::options(fics_rating2) -from 1000 -to 3000 -increment 50 -validate all -vcmd { regexp {^[0-9]+$} %P }
    grid $w.cblimitrating -column 0 -row 5 -columnspan 2 -sticky ew
    grid $w.sbrating1 -column 0 -row 6 -sticky ew
    grid $w.sbrating2 -column 1 -row 6 -sticky ew
    
    checkbutton $w.cbmanual -text [::msgcat::mc "Confirm manually"] -onvalue "manual" -offvalue "auto" -variable ::options(fics_manual)
    grid $w.cbmanual -column 0 -row 7 -columnspan 2 -sticky ew
    checkbutton $w.cbformula -text [::msgcat::mc "Filter with formula"] -onvalue "formula" -offvalue "" -variable ::options(fics_useformula)
    grid $w.cbformula -column 0 -row 8 -columnspan 2 -sticky ew
    
    button $w.seek -text [::msgcat::mc "Issue seek"] -command {
      set range ""
      if {$::options(fics_limitrating) } {
        set range "$::options(fics_rating1)-$::options(fics_rating2)"
      }
      if { $::options(fics_color) == "auto" } {
        set fcolor ""
      } else {
        set fcolor $::options(fics_color)
      }
      set cmd "seek $::options(fics_initTime) $::options(fics_incTime) $::options(fics_rated) \
          $fcolor $::options(fics_manual) $::options(fics_useformula) $range"
      ::fics::writechan $cmd
      destroy .ficsfindopp
    }
    button $w.cancel -text [::msgcat::mc "Cancel"] -command "destroy $w"
    grid $w.seek -column 0 -row 9 -sticky ew
    grid $w.cancel -column 1 -row 9 -sticky ew
  }
  ################################################################################
  #
  ################################################################################
  proc readchan {} {
    # gets $::fics::sockchan line
    variable logged
    
    if {[eof $::fics::sockchan]} {
      tk_messageBox -title "FICS" -icon error -type ok -message "Network error"
      return
    }
    
    # switch from read to gets in case a read is done at the middle of a line
    if {! $logged} {
      set line [read $::fics::sockchan]
      foreach l [split $line "\n"] {
        readparse $l
      }
    } else  {
      set line [gets $::fics::sockchan]
      set line [string map {"\a" ""} $line]
      readparse $line
    }
    
  }
  ################################################################################
  #
  ################################################################################
  proc readparse {line} {
    variable logged
    variable isGuestLogin
    
    if {$line == "" || [string match "fics*" $line] } {return}
    
    if {[string match "login: " $line]} {
      writechan $::options(fics_login)
      if { $isGuestLogin} {
        set logged 1
      }
      return
    }
    if {[string match "password: " $line]} {
      writechan $::options(fics_password)
      set logged 1
      return
    }
    if {[string match "<sc>*" $line]} {
      set ::fics::seeklist {}
      refreshOffers
      return
    }
    if {[string match "<s>*" $line]} {
      parseSeek $line
      return
    }
    if {[string match "<sr>*" $line]} {
      removeSeek $line
      return
    }
    
    if {[string match "<12>*" $line]} {
      parseStyle12 $line
      return
    }
    
    updateConsole $line
    
    if {[string match "Creating: *" $line]} {
      sc_game new
      ::novag::newGame
      
      set idx1 [string first "(" $line]
      set white [string trim [string range $line 10 [expr $idx1 -1]] ]
      set idx2 [string first ")" $line]
      set whiteElo [string trim [string range $line [expr $idx1 +1] [expr $idx2 -1]] ]
      
      set idx1 [expr $idx2 +1]
      set idx2 [string first "(" $line $idx1]
      set black [string trim [string range $line $idx1 [expr $idx2 -1]] ]
      
      set idx1 [expr $idx2 +1]
      set idx2 [string first ")" $line $idx1]
      set blackElo [string trim [string range $line $idx1 [expr $idx2 -1]] ]
      
      if { $whiteElo == "++++"} { set whiteElo 0 }
      if { $blackElo == "++++"} { set blackElo 0 }
      
      sc_game tags set -white $white
      sc_game tags set -whiteElo $whiteElo
      sc_game tags set -black $black
      sc_game tags set -blackElo $blackElo
      
      sc_game tags set -event "Fics [lrange $line 5 end]"
      if {$::board::isFlipped} {
        if { [ string match -nocase $white $::fics::login ] } { ::board::toggleFlip }
      } else {
        if { [ string match -nocase $black $::fics::login ] } { ::board::toggleFlip }
      }
      ::sound::play alert
      ::board::updateBoard -pgn
      return
    }
    
    if {[string match "\{Game *" $line]} {
      set num [lindex [lindex $line 0] 1]
      set res [lindex $line end]
      if {$num == $::fics::observedGame} {
        if {[string match "1/2*" $res]} {
          ::sound::play draw
          tk_messageBox -title "Game result" -icon info -type ok -message "Draw"
        } else {
          ::sound::play checkmate
          tk_messageBox -title "Game result" -icon info -type ok -message "$res"
        }
        sc_game tags set -result $res
        ::board::updateBoard -pgn
        set ::fics::playing 0
        set ::fics::observedGame -1
        ::gameclock::stop 1
        ::gameclock::stop 2
      }
      return
    }
    
    if { [string match "You are now observing game*" $line] } {
      scan $line "You are now observing game %d." ::fics::observedGame
    }
    
    if {[string match "*Starting FICS session*" $line]} {
      # init commands
      writechan "iset seekremove 1"
      writechan "iset seekinfo 1"
      writechan "set seek 1"
      writechan "set silence 1"
      writechan "set chanoff 1"
      writechan "set cshout 0"
      writechan "style 12"
      writechan "iset nowrap 1"
      writechan "iset nohighlight 1"
      writechan "set formula $::options(fics_formula)"
      ::sound::play connect
      return
    }
    
    if { $::fics::waitForRating == "wait" } {
      if {[lindex $line 0] == "Standard"} {
        set ::fics::waitForRating [lindex $line 1]
        return
      }
    }
    
    if {[string match "Challenge:*" $line]} {
      ::sound::play alert
      set ans [tk_dialog .challenge "Challenge" $line "" 0 "accept" "decline"]
      if {$ans == 0} {
        writechan "accept"
      } else {
        writechan "decline"
      }
    }
    
    # abort request
    if {[string match "* would like to abort the game;*" $line]} {
      ::sound::play alert
      set ans [tk_messageBox -title "Abort" -icon question -type yesno -message "$line\nDo you accept ?" ]
      if {$ans == yes} {
        writechan "accept"
      } else {
        writechan "decline"
      }
    }
    
    # takeback
    if {[string match "* would like to take back *" $line]} {
      ::sound::play takeback
      set ans [tk_messageBox -title "Abort" -icon question -type yesno -message "$line\nDo you accept ?" ]
      if {$ans == yes} {
        writechan "accept"
      } else {
        writechan "decline"
      }
    }
    
    # draw
    if {[string match "*offers you a draw*" $line]} {
      ::sound::play alert
      set ans [tk_messageBox -title "Abort" -icon question -type yesno -message "$line\nDo you accept ?" ]
      if {$ans == yes} {
        writechan "accept"
      } else {
        writechan "decline"
      }
    }
    
    # adjourn
    if {[string match "*would like to adjourn the game*" $line]} {
      ::sound::play alert
      set ans [tk_messageBox -title "Abort" -icon question -type yesno -message "$line\nDo you accept ?" ]
      if {$ans == yes} {
        writechan "accept"
      } else {
        writechan "decline"
      }
    }
    
    # guest logging
    if {[string match "Logging you in as*" $line]} {
      set line [string map {"\"" "" ";" ""} $line ]
      set ::fics::login [lindex $line 4]
    }
    if {[string match "Press return to enter the server as*" $line]} {
      writechan "\n"
    }
  }
  ################################################################################
  #
  ################################################################################
  proc updateConsole {line} {
    lappend ::fics::consoleList $line
    if {[winfo exists .ficsconsole]} {
      set t .ficsconsole.top.console
      $t insert end "$line\n"
      $t yview moveto 1
    }
  }
  ################################################################################
  #
  ################################################################################
  proc removeSeek {line} {
    global ::fics::seeklist
    foreach l $line {
      if { $l == "<sr>" } {continue}
      for {set i 0} {$i < [llength $seeklist]} {incr i} {
        array set a [lindex $seeklist $i]
        if {$a(index) == $l} {
          set seeklist [lreplace $seeklist $i $i]
          break
        }
      }
    }
    refreshOffers
  }
  ################################################################################
  #
  ################################################################################
  proc parseStyle12 {line} {
    set color [lindex $line 9]
    set gameNumber [lindex $line 16]
    set white [lindex $line 17]
    set black [lindex $line 18]
    set relation [lindex $line 19]
    set initialTime [lindex $line 20]
    set increment [lindex $line 21]
    set whiteMaterial [lindex $line 22]
    set blackMaterial [lindex $line 23]
    set whiteRemainingTime  [lindex $line 24]
    set blackRemainingTime  [lindex $line 25]
    set moveNumber [lindex $line 26]
    set verbose_move [lindex $line 27]
    set moveTime [lindex $line 28]
    set moveSan [lindex $line 29]
    
    set ::fics::playing $relation
    set ::fics::observedGame $gameNumber
    
    ::gameclock::setdSec 1 [expr $whiteRemainingTime * 10]
    ::gameclock::setdSec 2 [expr $blackRemainingTime * 10]
    if {$color == "W"} {
      ::gameclock::start 1
      ::gameclock::stop 2
    } else {
      ::gameclock::start 2
      ::gameclock::stop 1
    }
    
    set fen ""
    for {set i 1} {$i <=8} { incr i} {
      set l [lindex $line $i]
      set count 0
      
      for { set col 0 } { $col < 8 } { incr col } {
        set c [string index $l $col]
        if { $c == "-"} {
          incr count
        } else {
          if {$count != 0} {
            set fen "$fen$count"
            set count 0
          }
          set fen "$fen$c"
        }
      }
      
      if {$count != 0} { set fen "$fen$count" }
      if {$i != 8} { set fen "$fen/" }
    }
    
    set fen "$fen [string tolower $color]"
    set f [lindex $line 10]
    
    # en passant
    if { $f == "-1" || $verbose_move == "none"} {
      set enpassant "-"
    } else {
      set enpassant "-"
      set conv "abcdefgh"
      set fl [string index $conv $f]
      if {$color == "W"} {
        if { [ string index [lindex $line 4] [expr $f - 1]] == "P" || [ string index [lindex $line 4] [expr $f + 1]] == "P" } {
          set enpassant "${fl}6"
        }
      } else {
        if { [ string index [lindex $line 5] [expr $f - 1]] == "p" || [ string index [lindex $line 5] [expr $f + 1]] == "p" } {
          set enpassant "${fl}3"
        }
      }
    }
    
    set castle ""
    if {[lindex $line 11] == "1"} {set castle "${castle}K"}
    if {[lindex $line 12] == "1"} {set castle "${castle}Q"}
    if {[lindex $line 13] == "1"} {set castle "${castle}k"}
    if {[lindex $line 14] == "1"} {set castle "${castle}q"}
    if {$castle == ""} {set castle "-"}
    
    set fen "$fen $castle $enpassant [lindex $line 15] $moveNumber"
    
    # try to play the move and check if fen corresponds. If not this means the position needs to be set up.
    if {$moveSan != "none"} {
      # if the move is an echo of the move previously entered with an external board, do nothing
      if { $::novag::connected && $relation == -1} {
        return
      }
      if { [catch { ::game::moveAdd $moveSan } err ] } {
        # tk_messageBox -title "FICS error" -icon error -type ok -message "FICS error\n$err"
      } else {
        # check that Scid has the same board as FICS
        if {$fen != [sc_pos fen]} {
          tk_messageBox -title "FICS error" -icon error -type ok -message "FICS move entered but\nfen different.\nNew game Creation"
        } else {
          ::board::updateBoard -pgn
          return
        }
      }
    }
    
    if {$fen != [sc_pos fen]} {
      sc_game new
      
      set ::fics::waitForRating "wait"
      writechan "finger $white /s"
      vwaitTimed ::fics::waitForRating 2000 "nowarn"
      if {$::fics::waitForRating == "wait"} { set ::fics::waitForRating "0" }
      sc_game tags set -white $white
      sc_game tags set -whiteElo $::fics::waitForRating
      
      set ::fics::waitForRating "wait"
      writechan "finger $black /s"
      vwaitTimed ::fics::waitForRating 2000 "nowarn"
      if {$::fics::waitForRating == "wait"} { set ::fics::waitForRating "0" }
      sc_game tags set -black $black
      sc_game tags set -blackElo $::fics::waitForRating
      
      set ::fics::waitForRating ""
      
      sc_game tags set -event "Fics game $gameNumber $initialTime/$increment"
      sc_game startBoard $fen
      ::board::updateBoard -pgn -nolastmove
    }
  }
  ################################################################################
  #
  ################################################################################
  proc parseSeek {line} {
    array set seekelt {}
    set seekelt(index) [lindex $line 1]
    foreach m [split $line] {
      if {[string match "w=*" $m]} { set seekelt(name_from) [string range $m 2 end] ; continue }
      if {[string match "ti=*" $m]} { set seekelt(titles) [string range $m 3 end] ; continue }
      if {[string match "rt=*" $m]} { set seekelt(rating) [string range $m 3 end] ; continue }
      if {[string match "t=*" $m]} { set seekelt(time) [string range $m 2 end] ; continue }
      if {[string match "i=*" $m]} { set seekelt(increment) [string range $m 2 end] ; continue }
      if {[string match "r=*" $m]} { set seekelt(rated) [string range $m 2 end] ; continue }
      if {[string match "tp=*" $m]} { set seekelt(type) [string range $m 3 end] ; continue }
      if {[string match "c=*" $m]} { set seekelt(color) [string range $m 2 end] ; continue }
      if {[string match "rr=*" $m]} { set seekelt(rating_range) [string range $m 3 end] ; continue }
      if {[string match "a=*" $m]} { set seekelt(automatic) [string range $m 2 end] ; continue }
      if {[string match "f=*" $m]} { set seekelt(formula_checked) [string range $m 2 end] ; continue }
    }
    lappend ::fics::seeklist [array get seekelt]
    refreshOffers
  }
  ################################################################################
  #
  ################################################################################
  proc refreshOffers {} {
    global ::fics::seeklist
    if {[winfo exists .ficsoffers]} {
      set ws .ficsoffers.sf.scrolled
      foreach c [winfo child $ws] {
        destroy $c
      }
      set count 0
      foreach l $seeklist {
        array set a $l
        button $ws.play$count -text "$a(time)/$a(increment) $a(rating) $a(rated)" -command "::fics::play $a(index)"
        pack $ws.play$count -fill x
        incr count
      }
    }
  }
  ################################################################################
  #
  ################################################################################
  proc showOffers {} {
    global ::fics::seeklist
    
    set w .ficsoffers
    
    if {[winfo exists .ficsoffers]} {
      focus .ficsoffers
      return
    }
    
    toplevel $w
    ::ui::setFullSize $w 0
    ::scrolledframe::scrolledframe $w.sf -yscrollcommand {.ficsoffers.vs set}  -fill both
    ttk::scrollbar $w.vs -command {.ficsoffers.sf yview}
    grid $w.sf -row 0 -column 0 -sticky nsew
    grid $w.vs -row 0 -column 1 -sticky ns
    grid rowconfigure $w 0 -weight 1
    grid columnconfigure $w 0 -weight 1
    
    refreshOffers
    
    button $w.close -text "Close" -command "destroy .ficsoffers"
    grid $w.close -row 1 -column 0 -columnspan 2 -sticky ew
  }
  ################################################################################
  #
  ################################################################################
  proc play { index } {
    writechan "play $index"
    # set ::fics::playing 1
    set ::fics::observedGame $index
  }
  ################################################################################
  #
  ################################################################################
  proc writechan {line} {
    if {$line == ""} { return }
    puts $::fics::sockchan $line
    updateConsole "->>$line"
  }
  
  ################################################################################
  #
  ################################################################################
  proc close {} {
    writechan "exit"
    set ::fics::playing 0
    set ::fics::observedGame -1
    set ::fics::consoleList {}
    ::close $::fics::sockchan
    ::gameclock::delete 1
    ::gameclock::delete 2
    ::sound::play disconnect
  }
}

###
### End of file: fics.tcl
###
# Scid Pocket (C) 2007 Pascal Georges
### Based on Scid (C) Shane Hudson
#

############################################################
### Board setup window:

set setupBd {}
set setupFen {}

# makeSetupFen:
#    Reconstructs the FEN string from the current settings in the
#    setupBoard dialog. Check to see if the position is
#    acceptable (a position can be unacceptable by not having exactly
#    one King per side, or by having more than 16 pieces per side).
#
proc makeSetupFen {} {
  global setupFen setupBd moveNum toMove castling epFile
  set fenStr ""
  set errorStr [validateSetup]
  if {$errorStr != ""} {
    set fenStr [::msgcat::mc "Invalid board: "]
    append fenStr $errorStr
    return $fenStr
  }
  for {set bRow 56} {$bRow >= 0} {incr bRow -8} {
    if {$bRow < 56} { append fenStr "/" }
    set emptyRun 0
    for {set bCol 0} {$bCol < 8} {incr bCol} {
      set sq [expr {$bRow + $bCol} ]
      set piece [string index $setupBd $sq]
      if {$piece == "."} {
        incr emptyRun
      } else {
        if {$emptyRun > 0} {
          append fenStr $emptyRun
          set emptyRun 0
        }
        append fenStr $piece
      }
    }
    if {$emptyRun > 0} { append fenStr $emptyRun }
  }
  append fenStr " " [string tolower [string index $toMove 0]] " "
  if {$castling == ""} {
    append fenStr "- "
  } else {
    append fenStr $castling " "
  }
  if {$epFile == ""  ||  $epFile == "-"} {
    append fenStr "-"
  } else {
    append fenStr $epFile
    if {$toMove == "White"} {
      append fenStr "6"
    } else {
      append fenStr "3"
    }
  }
  # We assume a halfmove clock of zero:
  append fenStr " 0 " $moveNum
  set setupFen $fenStr
  return $fenStr
}
############################################################
# validateSetup:
#   Called by makeSetupFen to check that the board is sensible: that is,
#   that there is one king per side and there are at most 16 pieces per
#   side.
#
proc validateSetup {} {
  global setupBd
  set wkCount 0; set bkCount 0; set wCount 0; set bCount 0
  set wpCount 0; set bpCount 0
  for {set i 0} {$i < 64} {incr i} {
    set p [string index $setupBd $i]
    if {$p == "."} {
    } elseif {$p == "P"} { incr wCount; incr wpCount
    } elseif {$p == "p"} { incr bCount; incr bpCount
    } elseif {$p == "N" || $p == "B" || $p == "R" || $p == "Q"} {
      incr wCount
    } elseif {$p == "n" || $p == "b" || $p == "r" || $p == "q"} {
      incr bCount
    } elseif {$p == "K"} { incr wCount; incr wkCount
    } elseif {$p == "k"} { incr bCount; incr bkCount
    } else { return "Invalid piece: $p" }
  }
  if {$wkCount != 1} { return "There must be one white king"
  } elseif {$bkCount != 1} { return "There must be one black king"
  } elseif {$wCount > 16} { return "Too many white pieces"
  } elseif {$bCount > 16} { return "Too many black pieces"
  } elseif {$wpCount > 8} { return "Too many white pawns"
  } elseif {$bpCount > 8} { return "Too many black pawns" }
  return ""
}
############################################################
# setupBoardPiece:
#    Called by setupBoard to set or clear a square when it is clicked on.
#    Sets that square to containing the active piece (stored in pastePiece)
#    unless it already contains that piece, in which case the square is
#    cleared to be empty.
#
proc setupBoardPiece { square } {
  global setupBd pastePiece  setupFen
  set oldState $setupBd
  set setupBd {}
  set piece $pastePiece
  if {$::hires} {
    set pSize $::board::pSize_hires_setup
  } else {
    set pSize $::board::pSize_lowres_setup
  }
  if {[string index $oldState $square] == $pastePiece} {
    set piece "."
  }
  if {$piece == "P"  ||  $piece == "p"} {
    if {$square < 8  ||  $square >= 56} {
      set setupBd $oldState
      unset oldState
      return
    }
  }
  append setupBd \
      [string range $oldState 0 [expr {$square - 1} ]] \
      $piece \
      [string range $oldState [expr {$square + 1} ] 63]
  unset oldState
  ::board::setBoard .setup.fTop.bd $setupBd $pSize
  set setupFen [makeSetupFen]
}

# Global variables for entry of the start position:
set epFile {}          ;# legal values are empty, or "a"-"h".
set moveNum 1          ;# legal values are 1-999.
set castling KQkq      ;# will be empty or some combination of KQkq letters.
set toMove White       ;# side to move, "White" or "Black".
set pastePiece K       ;# Piece being pasted, "K", "k", "Q", "q", etc.

# Traces to keep entry values sensible:

trace variable moveNum w {::utils::validate::Integer 999 0}
trace variable epFile w {::utils::validate::Regexp {^(-|[a-h])?$}}
trace variable castling w {::utils::validate::Regexp {^(-|[KQkq]*)$}}

############################################################
# setupBoard:
#   The main procedure for creating the dialog for setting the start board.
#   Calls makeSetupFen.
#   On "Setup" button press, calls sc_pos startBoard to try to set the
#   starting board.
#
proc setupBoard {} {
  set lite $::board::lightSquare
  set dark $::board::darkSquare
  
  global setupBd pastePiece toMove epFile moveNum
  global castling setupFen
  if {[winfo exists .setup]} { return }
  set setupBd [sc_pos board]
  toplevel .setup
  wm title .setup [::msgcat::mc "Set position" ]
  
  ::ui::setFullSize .setup 0
  
  if {$::hires} {
    set pSize $::board::pSize_hires_setup
  } else {
    set pSize $::board::pSize_lowres_setup
  }
  
  frame .setup.fTop
  pack .setup.fTop -side top
  canvas .setup.fTop.bd -width [expr $pSize*8] -height [expr $pSize*8]
  set sbd .setup.fTop.bd
  
  # Create empty board:
  for {set i 0} {$i < 64} {incr i} {
    set xi [expr {$i % 8} ]
    set yi [expr { 7 - int($i/8)} ]
    set x1 [expr {$xi * $pSize} ]
    set y1 [expr {$yi * $pSize} ]
    set x2 [expr {$x1 + $pSize} ]
    set y2 [expr {$y1 + $pSize} ]
    $sbd create rectangle $x1 $y1 $x2 $y2 -tag sq$i
    set color [::board::defaultColor $i]
    $sbd itemconfigure sq$i -outline $color -fill $color
  }
  # bindings
  for {set i 0} {$i < 64} {incr i} {
    if {!$::board::isFlipped} {
      $sbd bind sq$i <ButtonPress-1> "setupBoardPiece $i"
      $sbd bind piece$i <ButtonPress-1> "setupBoardPiece $i"
    } else {
      set fsq [expr 63-$i]
      $sbd bind sq$i <ButtonPress-1> "setupBoardPiece $fsq"
      $sbd bind piece$i <ButtonPress-1> "setupBoardPiece $fsq"
    }
  }
  
  pack $sbd -side left
  
  set ::board::previousBoard($sbd) ""
  ::board::setBoard $sbd $setupBd $pSize
  
  ### Piece Buttons
  set sr .setup.fTop
  frame $sr.sw; frame $sr.sb
  
  foreach i {k q r b n p} {
    set i2 [string toupper $i]
    radiobutton $sr.sw.$i -image w${i}$pSize -indicatoron 0 -variable pastePiece -value $i2
    pack $sr.sw.$i -side top
    radiobutton $sr.sb.$i -image b${i}$pSize -indicatoron 0 -variable pastePiece -value $i
    pack $sr.sb.$i -side top
  }
  pack $sr.sw $sr.sb -side left
  
  ### Quick Board Setup buttons: Clear Board and Initial Board.
  frame .setup.fBottom
  pack .setup.fBottom -side top
  set sr .setup.fBottom
  
  frame $sr.b
  button $sr.b.clear -text [::msgcat::mc "Empty"] -command {
    set setupBd "................................................................"
    if {$::hires} {
      ::board::setBoard .setup.fTop.bd $setupBd $::board::pSize_hires_setup
    } else {
      ::board::setBoard .setup.fTop.bd $setupBd $::board::pSize_lowres_setup
    }
    set castling {}
    set setupFen [makeSetupFen]
  }
  button $sr.b.initial -text [::msgcat::mc "Start pos."] -command {
    set setupBd "RNBQKBNRPPPPPPPP................................pppppppprnbqkbnr"
    if {$::hires} {
      ::board::setBoard .setup.fTop.bd $setupBd $::board::pSize_hires_setup
    } else {
      ::board::setBoard .setup.fTop.bd $setupBd $::board::pSize_lowres_setup
    }
    set castling KQkq
    set setupFen [makeSetupFen]
  }
  label $sr.b.label -text [::msgcat::mc "Move:"]
  entry $sr.b.e -width 3 -background white -textvariable moveNum
  pack $sr.b.clear $sr.b.initial $sr.b.label $sr.b.e -side left
  
  ### Side to move frame.
  frame $sr.tomove
  label $sr.tomove.label -text [::msgcat::mc "Side:"]
  radiobutton $sr.tomove.w -text [::msgcat::mc "White" ] -variable toMove -value White -command {set setupFen [makeSetupFen]}
  radiobutton $sr.tomove.b -text [::msgcat::mc "Black" ] -variable toMove -value Black -command {set setupFen [makeSetupFen]}
  pack $sr.tomove.label $sr.tomove.w $sr.tomove.b -side left
  
  ### Entry boxes: Move number, Castling and En Passant file.
  
  frame $sr.castle
  label $sr.castle.label -text [::msgcat::mc "Castling:"]
  ttk::combobox $sr.castle.e -width 5 -textvariable castling -values {KQkq KQ kq -}
  # foreach c {KQkq KQ kq -} {
    # $sr.castle.e list insert end $c
  # }
  
  label $sr.castle.label2 -text "En passant"
  ttk::combobox $sr.castle.e2 -width 2 -textvariable epFile -values {- a b c d e f g h}
  set epFile {}
  # foreach f {- a b c d e f g h} {
    # $sr.castle.e2 list insert end $f
  # }
  pack $sr.castle.label $sr.castle.e $sr.castle.label2 $sr.castle.e2 -side left
  
  ### Buttons: Setup and Cancel.
  
  frame $sr.b2
  button $sr.b2.setup -text "OK" -command {
    if {[catch {sc_game startBoard [makeSetupFen]} err]} {
      tk_messageBox -icon info -type ok -title "Scid" -parent .setup -message $err
    } else {
      destroy .setup
      ::board::updateBoard -pgn -nolastmove
    }
  }
  button $sr.b2.cancel -text [::msgcat::mc "Cancel" ] -command {
    destroy .setup
  }
  pack $sr.b2.setup $sr.b2.cancel -side left
  
  pack $sr.b $sr.tomove $sr.castle -side top
  pack $sr.b2 -side top
  
  set setupFen [makeSetupFen]
}
# Scid Pocket (C) 2007 Pascal Georges
### Based on Scid (C) Shane Hudson
#
###
### book.tcl: part of Scid.
### Copyright (C) 2007  Pascal Georges
###
######################################################################
### Book window

namespace eval book {
  set isOpen 0
  set bookList ""
  set bookPath ""
  set currentBook "" ; # book in form abc.bin
  set bookMoves ""
  set cancelBookExport 0
  set exportCount 0
  set exportMax 3000
  set hashList ""
  set bookSlot 0
  
  ################################################################################
  # open a book, closing any previously opened one (called by annotation analysis)
  # arg name : gm2600.bin for example
  ################################################################################
  proc scBookOpen { name slot } {
    
    if {$slot == $::book::bookSlot} {
      if {$::book::currentBook != ""} {
        sc_book close $::book::bookSlot
      }
      set ::book::currentBook $name
    }
    
    set bn [ file join  $::instalDir "books" $name ]
    sc_book load $bn $slot
  }
  
  ################################################################################
  # Return a move in book for position fen. If there is no move in book, returns ""
  # Is used by engines, not book windows
  ################################################################################
  proc getMove { book fen slot} {
    set tprob 0
    ::book::scBookOpen $book $slot
    set bookmoves [sc_book moves $slot]
    if {[llength $bookmoves] == 0} {
      return ""
    }

    set r [expr {(int (rand() * 100))} ]
    for {set i 0} {$i<[llength $bookmoves]} {incr i 2} {
      set m [lindex $bookmoves $i]
      set prob [string range [lindex $bookmoves [expr $i + 1] ] 0 end-1 ]
      incr tprob $prob
      if { $tprob >= $r } {
        break
      }
    }
    sc_book close $slot
    return $m
  }
  
  ################################################################################
  #
  ################################################################################
  proc closeMainBook {} {
    if { $::book::currentBook == "" } { return }
    sc_book close $::book::bookSlot
    set ::book::isOpen 0
    set ::book::currentBook ""
  }
  ################################################################################
  #
  ################################################################################
  proc makeBookMove { move } {
    set action "replace"
    if {![sc_pos isAt vend]} { set action [confirmReplaceMove] }
    if {$action == "replace"} {
      sc_move addSan $move
    } elseif {$action == "var"} {
      sc_var create
      sc_move addSan $move
    } elseif {$action == "mainline"} {
      sc_var create
      sc_move addSan $move
      sc_var exit
      sc_var promote [expr {[sc_var count] - 1}]
      sc_move forward 1
    }
    ::board::updateBoard -pgn
    destroy .bookWin
  }
  ################################################################################
  #
  ################################################################################
  proc bookSelect { { n "" }  { v  0} } {
    scBookOpen $::options(mainBook) $::book::bookSlot    
  }
}
###
### End of file: book.tcl
###### ScidPocket.
### Copyright (C) 2007  Pascal Georges
###
### search/header.tcl: Header Search routines for Scid.
###

namespace eval ::search::header {}

set sWhite "";  set sBlack "";  set sEvent ""; set sSite "";  set sRound ""
set sWhiteEloMin 0; set sWhiteEloMax [sc_info limit elo]
set sBlackEloMin 0; set sBlackEloMax [sc_info limit elo]
set sEloDiffMin "-[sc_info limit elo]"; set sEloDiffMax "+[sc_info limit elo]"
set sTitleList [list gm im fm none wgm wim wfm w]
foreach i $sTitleList {
  set sTitles(w:$i) 1
  set sTitles(b:$i) 1
}
set sGlMin 0; set sGlMax 999
set sEcoMin "A00";  set sEcoMax "E99"; set sEco Yes
set sDateMin "0000.00.00"; set sDateMax "[sc_info limit year].12.31"
set sResWin 1; set sResLoss 1; set sResDraw 1; set sResOther 1
set sGnumMin 1; set sGnumMax -1
set sIgnoreCol No
set sSideToMove wb
set sHeaderFlagList {StdStart Promotions Comments Variations Annotations \
      DeleteFlag WhiteOpFlag BlackOpFlag MiddlegameFlag EndgameFlag \
      NoveltyFlag PawnFlag TacticsFlag KsideFlag QsideFlag \
      BrilliancyFlag BlunderFlag UserFlag
}
foreach i $sHeaderFlagList {
  set sHeaderFlags($i) both
}
set sPgntext(1) ""
set sPgntext(2) ""
set sPgntext(3) ""

trace variable sDateMin w ::utils::validate::Date
trace variable sDateMax w ::utils::validate::Date

foreach i {sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax} {
  trace variable $i w [list ::utils::validate::Integer [sc_info limit elo] 0]
}
trace variable sEloDiffMin w [list ::utils::validate::Integer "-[sc_info limit elo]" 0]
trace variable sEloDiffMax w [list ::utils::validate::Integer "-[sc_info limit elo]" 0]

trace variable sGlMin w {::utils::validate::Integer 9999 0}
trace variable sGlMax w {::utils::validate::Integer 9999 0}

trace variable sGnumMin w {::utils::validate::Integer -9999999 0}
trace variable sGnumMax w {::utils::validate::Integer -9999999 0}

# Forcing ECO entry to be valid ECO codes:
foreach i {sEcoMin sEcoMax} {
  trace variable $i w {::utils::validate::Regexp {^$|^[A-Ea-e]$|^[A-Ea-e][0-9]$|^[A-Ea-e][0-9][0-9]$|^[A-Ea-e][0-9][0-9][a-z]$|^[A-Ea-e][0-9][0-9][a-z][1-4]$}}
}
################################################################################
#
################################################################################
# checkDates:
#    Checks minimum/maximum search dates in header search window and
#    extends them if necessary.
proc checkDates {} {
  global sDateMin sDateMax
  if {[string length $sDateMin] == 0} { set sDateMin "0000" }
  if {[string length $sDateMax] == 0} { set sDateMax [sc_info limit year]}
  if {[string length $sDateMin] == 4} { append sDateMin ".??.??" }
  if {[string length $sDateMax] == 4} { append sDateMax ".12.31" }
  if {[string length $sDateMin] == 7} { append sDateMin ".??" }
  if {[string length $sDateMax] == 7} { append sDateMax ".31" }
}

proc ::search::header::defaults {} {
  global sWhite sBlack sEvent sSite sRound sDateMin sDateMax sIgnoreCol
  global sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax
  global sEloDiffMin sEloDiffMax
  global sEco sEcoMin sEcoMax sHeaderFlags sGlMin sGlMax
  global sGnumMin sGnumMax
  global sResWin sResLoss sResDraw sResOther glstart
  global sPgntext sTitles
  
  set sWhite "";  set sBlack ""
  set sEvent ""; set sSite "";  set sRound ""
  set sWhiteEloMin 0; set sWhiteEloMax [sc_info limit elo]
  set sBlackEloMin 0; set sBlackEloMax [sc_info limit elo]
  set sEloDiffMin "-[sc_info limit elo]"
  set sEloDiffMax "+[sc_info limit elo]"
  set sGlMin 0; set sGlMax 999
  set sEcoMin "A00";  set sEcoMax "E99"; set sEco Yes
  set sGnumMin 1; set sGnumMax -1
  set sDateMin "0000.00.00"; set sDateMax "[sc_info limit year].12.31"
  set sResWin 1; set sResLoss 1; set sResDraw 1; set sResOther 1
  set sIgnoreCol No
  set sSideToMove wb
  foreach flag $::sHeaderFlagList { set sHeaderFlags($flag) both }
  foreach i [array names sPgntext] { set sPgntext($i) "" }
  foreach i $::sTitleList {
    set sTitles(w:$i) 1
    set sTitles(b:$i) 1
  }
}

::search::header::defaults

set sHeaderFlagFrame 0
################################################################################
#
################################################################################
# ::search::header
#
#   Opens the window for searching by header information.
#
proc search::header {} {
  global sWhite sBlack sEvent sSite sRound sDateMin sDateMax sIgnoreCol
  global sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax
  global sEloDiffMin sEloDiffMax sSideToMove
  global sEco sEcoMin sEcoMax sHeaderFlags sGlMin sGlMax sTitleList sTitles
  global sResWin sResLoss sResDraw sResOther glstart sPgntext
  
  set w .sh
  if {[winfo exists $w]} {
    wm deiconify $w
    raiseWin $w
    return
  }
  
  toplevel $w
  wm title $w [::msgcat::mc "Header search"]
  ::ui::setFullSize $w
  
  pack [ttk::notebook $w.nb] -fill both -expand 1
  
  # --------- Exec search -----------------
  $w.nb add [ttk::frame $w.nb.frun] -text [::msgcat::mc "run"]
  
  ttk::button $w.nb.frun.defaults -text [::msgcat::mc "Defaults"] -command ::search::header::defaults
  ttk::button $w.nb.frun.stop -text [::msgcat::mc "Stop"] -command sc_progressBar
  ttk::button $w.nb.frun.search -text [::msgcat::mc "Search"] -command {
    set w .sh
    ::utils::history::AddEntry HeaderSearchWhite $sWhite
    ::utils::history::AddEntry HeaderSearchBlack $sBlack
    ::utils::history::AddEntry HeaderSearchEvent $sEvent
    ::utils::history::AddEntry HeaderSearchSite $sSite
    
    set sPgnlist {}
    foreach i {1 2} {
      set temp [string trim $sPgntext($i)]
      if {$temp != ""} { lappend sPgnlist $temp }
    }
    busyCursor .sh
    grid $w.nb.frun.stop -column 0 -row 4 -sticky ew
    grab $w.nb.frun.stop
    sc_progressBar $w.nb.frun.progress bar 201 21 time
    set wtitles {}
    set btitles {}
    foreach i $sTitleList {
      if $sTitles(w:$i) { lappend wtitles $i }
      if $sTitles(b:$i) { lappend btitles $i }
    }
    
    set str [sc_search header -white $sWhite -black $sBlack \
        -event $sEvent -site $sSite -round $sRound \
        -date [list $sDateMin $sDateMax] \
        -results [list $sResWin $sResDraw $sResLoss $sResOther] \
        -welo [list $sWhiteEloMin $sWhiteEloMax] \
        -belo [list $sBlackEloMin $sBlackEloMax] \
        -delo [list $sEloDiffMin $sEloDiffMax] \
        -eco [list $sEcoMin $sEcoMax $sEco] \
        -length [list $sGlMin $sGlMax] \
        -toMove $sSideToMove \
        -gameNumber [list $sGnumMin $sGnumMax] \
        -flip $sIgnoreCol -filter $::search::filter::operation \
        -fStdStart $sHeaderFlags(StdStart) \
        -fPromotions $sHeaderFlags(Promotions) \
        -fComments $sHeaderFlags(Comments) \
        -fVariations $sHeaderFlags(Variations) \
        -fAnnotations $sHeaderFlags(Annotations) \
        -fDelete $sHeaderFlags(DeleteFlag) \
        -fWhiteOp $sHeaderFlags(WhiteOpFlag) \
        -fBlackOp $sHeaderFlags(BlackOpFlag) \
        -fMiddlegame $sHeaderFlags(MiddlegameFlag) \
        -fEndgame $sHeaderFlags(EndgameFlag) \
        -fNovelty $sHeaderFlags(NoveltyFlag) \
        -fPawnStruct $sHeaderFlags(PawnFlag) \
        -fTactics $sHeaderFlags(TacticsFlag) \
        -fKingside $sHeaderFlags(KsideFlag) \
        -fQueenside $sHeaderFlags(QsideFlag) \
        -fBrilliancy $sHeaderFlags(BrilliancyFlag) \
        -fBlunder $sHeaderFlags(BlunderFlag) \
        -fUser $sHeaderFlags(UserFlag) \
        -pgn $sPgnlist -wtitles $wtitles -btitles $btitles \
        ]
    
    grab release $w.nb.frun.stop
    grid forget $w.nb.frun.stop
    unbusyCursor .sh
    
    tk_messageBox -type ok -icon info -parent .sh -title [::msgcat::mc "Search result"] -message $str
    
    set glstart 1
  }
  
  ttk::button $w.nb.frun.cancel -text [::msgcat::mc "Close"] -command {focus .; destroy .sh}
  grid $w.nb.frun.defaults -column 0 -row 0 -sticky ew
  grid $w.nb.frun.cancel -column 0 -row 1 -sticky ew
  grid $w.nb.frun.search -column 1 -row 1 -sticky ew
  
  canvas $w.nb.frun.progress -height 20 -width 200 -bg white -relief solid -border 1
  $w.nb.frun.progress create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
  $w.nb.frun.progress create text 195 10 -anchor e -tags time -fill black -text "0:00 / 0:00"
  grid $w.nb.frun.progress -column 0 -row 2 -columnspan 2 -pady 2
  
  # ---------------- Players ------------------------
  $w.nb add [ttk::frame $w.nb.fplayer] -text [::msgcat::mc "player"]
  
  foreach frame {cWhite cBlack ignore} { ;# eventsite dateround res gl ends eco
    ttk::frame $w.nb.fplayer.$frame
  }
  
  foreach color {White Black} {
    pack $w.nb.fplayer.c$color -side top -fill x
    ttk::label $w.nb.fplayer.c$color.lab -text "[::msgcat::mc $color]:" -anchor w
    ttk::combobox $w.nb.fplayer.c$color.e -text "s$color"
    ::utils::history::SetCombobox HeaderSearch$color $w.nb.fplayer.c$color.e
    ttk::label $w.nb.fplayer.c$color.elo1 -text "ELO:"
    ttk::entry $w.nb.fplayer.c$color.elomin -text s${color}EloMin -width 6 -justify right
    ttk::label $w.nb.fplayer.c$color.elo2 -text "-"
    ttk::entry $w.nb.fplayer.c$color.elomax -text s${color}EloMax -width 6 -justify right
    
    grid $w.nb.fplayer.c$color.lab -column 0 -row 0
    grid $w.nb.fplayer.c$color.e -column 1 -row 0 -columnspan 3
    
    grid $w.nb.fplayer.c$color.elo1 -column 0 -row 1
    grid $w.nb.fplayer.c$color.elomin -column 1 -row 1
    grid $w.nb.fplayer.c$color.elo2 -column 2 -row 1
    grid $w.nb.fplayer.c$color.elomax -column 3 -row 1
  }
  
  pack $w.nb.fplayer.ignore -side top -fill x
  ttk::label $w.nb.fplayer.ignore.l -text [::msgcat::mc "Ignore colors"]
  ttk::radiobutton $w.nb.fplayer.ignore.yes -variable sIgnoreCol -value Yes -text [::msgcat::mc "Yes"]
  ttk::radiobutton $w.nb.fplayer.ignore.no  -variable sIgnoreCol -value No -text [::msgcat::mc "No"]
  grid $w.nb.fplayer.ignore.l -column 0 -row 0
  grid $w.nb.fplayer.ignore.yes -column 1 -row 0
  grid $w.nb.fplayer.ignore.no -column 2 -row 0
  
  ttk::label $w.nb.fplayer.ignore.rdiff -text [::msgcat::mc "ELO diff:"]
  ttk::entry $w.nb.fplayer.ignore.rdmin -width 6 -text sEloDiffMin -justify right
  ttk::label $w.nb.fplayer.ignore.rdto -text "-"
  ttk::entry $w.nb.fplayer.ignore.rdmax -width 6 -text sEloDiffMax -justify right
  
  grid $w.nb.fplayer.ignore.rdiff -column 0 -row 1
  grid $w.nb.fplayer.ignore.rdmin -column 1 -row 1
  grid $w.nb.fplayer.ignore.rdto -column 2 -row 1
  grid $w.nb.fplayer.ignore.rdmax -column 3 -row 1
  
  # ------------------ Game info (event , site) -------------------------
  $w.nb add [ttk::frame $w.nb.fgame] -text [::msgcat::mc "game"]
  
  foreach frame {eventsite dateround res gl eco} {
    ttk::frame $w.nb.fgame.$frame
  }
  
  set f $w.nb.fgame.eventsite
  pack $f -side top -fill x
  
  foreach i {Event Site} {
    ttk::label $f.l$i -text "[::msgcat::mc $i]:"
    ttk::combobox $f.e$i -text s$i -width 15
    ::utils::history::SetCombobox HeaderSearch$i $f.e$i
  }
  grid $f.lEvent -column 0 -row 0
  grid $f.eEvent -column 1 -row 0
  grid $f.lSite -column 0 -row 1
  grid $f.eSite -column 1 -row 1
  
  set f $w.nb.fgame.dateround
  pack $f -side top -fill x
  pack [ttk::frame $f.f1] [ttk::frame $f.f2] -side top -fill x
  ttk::label $f.f1.l1 -text "[::msgcat::mc Date]:"
  ttk::label $f.f2.l2 -text "-"
  ttk::entry $f.f2.emin -text sDateMin -width 10
  ttk::button $f.f2.eminCal -image ::utils::date::calendar -command {
    regsub -all {[.]} $sDateMin "-" newdate
    set ndate [::utils::date::chooser $newdate]
    if {[llength $ndate] == 3} {
      set sDateMin "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
    }
  }
  ttk::entry $f.f2.emax -text sDateMax -width 10
  ttk::button $f.f2.emaxCal -image ::utils::date::calendar -command {
    regsub -all {[.]} $sDateMax "-" newdate
    set ndate [::utils::date::chooser $newdate]
    if {[llength $ndate] == 3} {
      set sDateMax "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
    }
  }
  bind $f.f2.emin <FocusOut> +checkDates
  bind $f.f2.emax <FocusOut> +checkDates
  ttk::button $f.f1.lyear -text [::msgcat::mc "Last year"] -command {
    set sDateMin "[expr [::utils::date::today year]-1].[::utils::date::today month].[::utils::date::today day]"
    set sDateMax [::utils::date::today]
  }
  
  pack $f.f1.l1 $f.f1.lyear -side left
  pack $f.f2.emin $f.f2.eminCal $f.f2.l2 $f.f2.emax $f.f2.emaxCal -side left
  
  # label $f.lRound -text Round:
  # entry $f.eRound -text sRound -width 10
  # pack $f.eRound $f.lRound -side right
  
  pack $w.nb.fgame.res -side top -fill x
  ttk::label $w.nb.fgame.res.l1 -text "[::msgcat::mc Result]:"
  pack $w.nb.fgame.res.l1 -side left
  foreach i {win draw loss other} v {sResWin sResDraw sResLoss sResOther} text {"1-0 "  "1/2-1/2 "  "0-1 "  "* "} {
    ttk::checkbutton $w.nb.fgame.res.e$i -text $text -variable $v -offvalue 0 -onvalue 1
    pack $w.nb.fgame.res.e$i -side left
  }
  
  ttk::label $w.nb.fgame.gl.l1 -text [::msgcat::mc "Game length:"]
  ttk::label $w.nb.fgame.gl.l2 -text "-"
  ttk::label $w.nb.fgame.gl.l3 -text [::msgcat::mc "Half moves"]
  ttk::entry $w.nb.fgame.gl.emin -textvariable sGlMin -justify right -width 4
  ttk::entry $w.nb.fgame.gl.emax -textvariable sGlMax -justify right -width 4
  pack $w.nb.fgame.gl -side top -fill x
  pack $w.nb.fgame.gl.l1 $w.nb.fgame.gl.emin $w.nb.fgame.gl.l2 $w.nb.fgame.gl.emax $w.nb.fgame.gl.l3 -side left
  
  # label $w.nb.fgame.ends.label -text "End side to move:"
  # frame $w.nb.fgame.ends.sep1 -width 5
  # frame $w.nb.fgame.ends.sep2 -width 5
  # radiobutton $w.nb.fgame.ends.white -text White -variable sSideToMove -value w
  # radiobutton $w.nb.fgame.ends.black -text Black -variable sSideToMove -value b
  # radiobutton $w.nb.fgame.ends.both -text Both -variable sSideToMove -value wb
  # pack $w.nb.fgame.ends.label $w.nb.fgame.ends.white $w.nb.fgame.ends.sep1 \
  # $w.nb.fgame.ends.black $w.nb.fgame.ends.sep2 $w.nb.fgame.ends.both -side left
  # pack $w.nb.fgame.ends -side top -fill x
  
  pack $w.nb.fgame.eco -side top -fill x
  pack [ttk::frame $w.nb.fgame.eco2] -side top -fill x
  
  ttk::label $w.nb.fgame.eco.l1 -text [::msgcat::mc "ECO Code:"]
  ttk::label $w.nb.fgame.eco.l2 -text "-"
  ttk::entry $w.nb.fgame.eco.emin -textvariable sEcoMin -width 5
  ttk::entry $w.nb.fgame.eco.emax -textvariable sEcoMax -width 5
  ttk::button $w.nb.fgame.eco.range -text "..." -command {
    set tempResult [chooseEcoRange]
    if {[scan $tempResult "%\[A-E0-9a-z\]-%\[A-E0-9a-z\]" sEcoMin_tmp sEcoMax_tmp] == 2} {
      set sEcoMin $sEcoMin_tmp
      set sEcoMax $sEcoMax_tmp
    }
    unset tempResult
  }
  ttk::label $w.nb.fgame.eco2.l4 -text [::msgcat::mc "Games without ECO:"]
  ttk::radiobutton $w.nb.fgame.eco2.yes -variable sEco -value Yes -text [::msgcat::mc Yes]
  ttk::radiobutton $w.nb.fgame.eco2.no -variable sEco -value No -text [::msgcat::mc No]
  pack $w.nb.fgame.eco.l1 $w.nb.fgame.eco.emin $w.nb.fgame.eco.l2 $w.nb.fgame.eco.emax $w.nb.fgame.eco.range -side left
  pack $w.nb.fgame.eco2.l4 $w.nb.fgame.eco2.yes $w.nb.fgame.eco2.no -side left
  
  set f [ttk::frame $w.nb.fgame.gnum]
  pack $f -side top -fill x
  ttk::label $f.l1 -text [::msgcat::mc "Game number:"]
  ttk::entry $f.emin -textvariable sGnumMin -width 6 -justify right
  ttk::label $f.l2 -text "-"
  ttk::entry $f.emax -textvariable sGnumMax -width 6 -justify right
  
  ttk::button $f.all -text [::utils::string::Capital all] -command {set sGnumMin 1; set sGnumMax -1}
  pack $f.l1 $f.emin $f.l2 $f.emax $f.all -side left
  
  # menubutton $f.first -text First... -pady 2 -menu $f.first.m -indicatoron 0 -relief raised
  # menubutton $f.last -text Last... -pady 2 -menu $f.last.m -indicatoron 0 -relief raised
  # menu $f.first.m
  # menu $f.last.m
  # foreach x {10 50 100 500 1000 5000 10000} {
  # $f.first.m add command -label $x \
  # -command "set sGnumMin 1; set sGnumMax $x"
  # $f.last.m add command -label $x \
  # -command "set sGnumMin -$x; set sGnumMax -1"
  # }
  # pack $f.first $f.last -side left -padx 2
  
  set f [ttk::frame $w.nb.fgame.pgntext]
  pack $f -side top -fill x
  
  ttk::label $f.l1 -text [::msgcat::mc "Pgn contains:"]
  ttk::entry $f.e1 -textvariable sPgntext(1) -width 20
  ttk::label $f.l2 -text "+"
  ttk::entry $f.e2 -textvariable sPgntext(2) -width 20
  
  grid $f.l1 -column 0 -row 0
  grid $f.e1 -column 1 -row 0
  grid $f.l2 -column 0 -row 1 -sticky e
  grid $f.e2 -column 1 -row 1
  
  # button $w.flagslabel -text "Find games with flags:" -command {
  # set w .sh.sf.scrolled
  # if {$sHeaderFlagFrame} {
  # set sHeaderFlagFrame 0
  # pack forget $w.flags
  # } else {
  # set sHeaderFlagFrame 1
  # pack $w.flags -side top -after $w.flagslabel
  # }
  # }
  # pack $w.flagslabel -side top
  #
  # frame $w.flags
  # if {$::sHeaderFlagFrame} {
  # pack $w.flags -side top
  # }
  #
  # set count 0
  # set row 0
  # set col 0
  #
  # foreach var $::sHeaderFlagList {
  # set lab [label $w.flags.l$var -text $var ]
  # grid $lab -row $row -column $col -sticky e
  # incr col
  # grid [radiobutton $w.flags.yes$var -variable sHeaderFlags($var) \
  # -ind 0 -value yes -text Yes -padx 2 -pady 0 ] \
  # -row $row -column $col
  # incr col
  # grid [radiobutton $w.flags.no$var -variable sHeaderFlags($var) \
  # -ind 0 -value no -text No -padx 2 -pady 0 ] \
  # -row $row -column $col
  # incr col
  # grid [radiobutton $w.flags.both$var -variable sHeaderFlags($var) \
  # -ind 0 -value both -text Both -padx 2 -pady 0 ] \
  # -row $row -column $col
  # incr count
  # incr col -3
  # incr row
  # if {$count == 6} { set col 5; set row 0 }
  # if {$count == 12} { set col 10; set row 0 }
  # }
  # grid [label $w.flags.space -text "" ] -row 0 -column 4
  # grid [label $w.flags.space2 -text "" ] -row 0 -column 9
  
  # ----------------- Filter operation -------------
  ::search::addFilterOpNoteBook $w.nb
  
  # update
  ::search::Config
  
  # focus $w.cWhite.e
}

##############################
### Selecting common ECO ranges

set ecoCommonRanges {
  {A04-A09  Reti: 1.Nf3}
  {A10-A39  English: 1.c4}
  {A40-A49  1.d4, 1.d4 Nf6 Miscellaneous}
  {A45l-A45z  Trompowsky: 1.d4 Nf6 2.Bg5}
  {A51-A52  Budapest: 1.d4 Nf6 2.c4 e5}
  {A53-A55  Old Indian: 1.d4 Nf6 2.c4 d6}
  {A57-A59  Benko Gambit: 1.d4 Nf6 2.c4 c5 3.d5 b5}
  {A60-A79  Modern Benoni: 1.d4 Nf6 2.c4 c5 3.d5 e6}
  {A80-A99  Dutch Defence: 1.d4 f5}
  {____________________________________________________________}
  {B00-C99  1.e4}
  {B01-B01     Scandinavian: 1.e4 d5}
  {B02-B05     Alekhine Defence: 1.e4 Nf6}
  {B07-B09     Pirc: 1.e4 d6}
  {B10-B19     Caro-Kann: 1.e4 c6}
  {B12i-B12z      Caro-Kann: Advance: 1.e4 c6 2.d4 d5 3.e5}
  {B20-B99  Sicilian: 1.e4 c5}
  {B22-B22     Sicilian: Alapin: 1.e4 c5 2.c3}
  {B23-B26     Sicilian: Closed: 1.e4 c5 2.Nc3}
  {B30-B39     Sicilian: 1.e4 c5 2.Nf3 Nc6}
  {B40-B49     Sicilian: 1.e4 c5 2.Nf3 e6}
  {B50-B59     Sicilian Rauzer: 1.e4 c5 2.Nf3 d6 ... 5.Nc3 Nc6}
  {B70-B79     Sicilian Dragon: 1.e4 c5 2.Nf3 d6 ... 5.Nc3 g6}
  {B80-B89     Sicilian Scheveningen: 1.e4 c5 2.Nf3 d6 ... 5.Nc3 e6}
  {B90-B99     Sicilian Najdorf: 1.e4 c5 2.Nf3 d6 ... 5.Nc3 a6}
  {____________________________________________________________}
  {C00-C19  French Defence: 1.e4 e6}
  {C02-C02     French: Advance: 1.e4 e6 2.d4 d5 3.e5}
  {C03-C09     French: Tarrasch: 1.e4 e6 2.d4 d5 3.Nd2}
  {C15-C19     French: Winawer: 1.e4 e6 2.d4 d5 3.Nc3 Bb4}
  {C20-C99  Open Game: 1.e4 e5}
  {C25-C29     Vienna: 1.e4 e5 2.Nc3}
  {C30-C39     King's Gambit: 1.e4 e5 2.f4}
  {C42-C43     Russian Game: 1.e4 e5 2.Nf3 Nf6}
  {C44-C49     Open Game: 1.e4 e5 2.Nf3 Nc6}
  {C50-C59     Italian/Two Knights: 1.e4 e5 2.Nf3 Nc6 3.Bc4}
  {C60-C99  Spanish: 1.e4 e5 2.Nf3 Nc6 3.Bb5}
  {C68-C69      Spanish: Exchange: 3.Bb5 a6 4.Bxc6}
  {C80-C83      Spanish: Open: 3.Bb5 a6 4.Ba4 Nf6 5.O-O Nxe4}
  {C84-C99      Spanish: Closed: 3.Bb5 a6 4.Ba4 Nf6 5.O-O Be7}
  {____________________________________________________________}
  {D00-D99  Queen's Pawn: 1.d4 d5}
  {D10-D19  Slav: 1.d4 d5 2.c4 c6}
  {D20-D29  QGA: 1.d4 d5 2.c4 dxc4}
  {D30-D69  QGD: 1.d4 d5 2.c4 e6}
  {D35-D36     QGD: Exchange: 1.d4 d5 2.c4 e6 3.cxd5 exd5}
  {D43-D49     Semi-Slav: 3.Nc3 Nf6 4.Nf3 c6}
  {D50-D69     QGD with Bg5: 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5}
  {D60-D69     QGD: Orthodox: 4.Bg5 Be7 5.e3 O-O 6.Nf3 Nbd7}
  {D70-D99  Grünfeld: 1.d4 Nf6 2.c4 g6 with 3...d5}
  {D85-D89     Grünfeld: Exchange: 3.Nc3 d5 4.e4 Nxc3 5.bxc3}
  {D96-D99     Grünfeld: Russian: 3.Nc3 d5 4.Nf3 Bg7 5.Qb3}
  {____________________________________________________________}
  {E00-E09  Catalan: 1.d4 Nf6 2.c4 e6 3.g3/...}
  {E02-E05     Catalan: Open: 3.g3 d5 4.Bg2 dxc4}
  {E06-E09     Catalan: Closed: 3.g3 d5 4.Bg2 Be7}
  {E12-E19  Queen's Indian: 1.d4 Nf6 2.c4 e6 3.Nf3 b6}
  {E20-E59  Nimzo-Indian: 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4}
  {E32-E39     Nimzo-Indian: Classical: 4.Qc2}
  {E40-E59     Nimzo-Indian: Rubinstein: 4.e3}
  {E60-E99  King's Indian: 1.d4 Nf6 2.c4 g6}
  {E80-E89     King's Indian: Sämisch: 4.e4 d6 5.f3}
  {E90-E99     King's Indian: Main Line: 4.e4 d6 5.Nf3}
}

set scid_ecoRangeChosen ""
################################################################################
#
################################################################################
proc chooseEcoRange {} {
  global ecoCommonRanges scid_ecoRangeChosen
  if {[winfo exists .ecoRangeWin]} { return }
  set w .ecoRangeWin
  toplevel $w
  wm title $w [::msgcat::mc "Choose ECO Range"]
  ::ui::setFullSize $w
  
  listbox $w.list -yscrollcommand "$w.ybar set" -xscrollcommand "$w.xbar set"  ;#-height 20 -width 60 -background white -setgrid 0
  foreach i $ecoCommonRanges { $w.list insert end $i }
  ttk::scrollbar $w.xbar -command "$w.list xview" -takefocus 0  -orient horizontal
  ttk::scrollbar $w.ybar -command "$w.list yview" -takefocus 0
  
  pack [ttk::frame $w.b] -side bottom -fill x
  pack $w.xbar -side bottom -fill x
  pack $w.ybar -side right -fill y
  pack $w.list -side left -fill both -expand yes
  
  ttk::button $w.b.ok -text [::msgcat::mc "OK"] -command {
    set sel [.ecoRangeWin.list curselection]
    if {[llength $sel] > 0} {
      set scid_ecoRangeChosen [lindex $ecoCommonRanges [lindex $sel 0]]
    }
    # focus .
    destroy .ecoRangeWin
  }
  ttk::button $w.b.cancel -text [::msgcat::mc Cancel] -command "destroy $w"
  pack $w.b.cancel $w.b.ok -side right -padx 5 -pady 2
  bind $w <Return> "$w.b.ok invoke; break"
  bind $w.list <Double-ButtonRelease-1> "$w.b.ok invoke; break"
  focus $w.list
  grab $w
  tkwait window $w
  return $scid_ecoRangeChosen
}


###
### End of file: search.tcl

### ScidPocket.
### Copyright (C) 2007  Pascal Georges
###
### search/material.tcl: Material Search routine for Scid.
###

namespace eval ::search::material {}

array set letterToPiece [list \
    "R" wr "r" br "N" wn "n" bn "B" wb "b" bb \
    "Q" wq "q" bq "K" wk "k" bk "P" wp "p" bp "." e \
    ]

image create photo button_oneplus -data {
  R0lGODlhFAAUAIAAAAAAAP///yH5BAEKAAEALAAAAAAUABQAAAIpjI+py+0P
  FwCSzVnlzZaaC3oJNooadyqmun4OGR1wHMxQ2HYgzfd+UgAAOw==
}

set ignoreColors 0
set minMoveNum 1
set maxMoveNum 999
set minHalfMoves 1
set oppBishops "Either"
set minMatDiff -40
set maxMatDiff +40

trace variable minMoveNum w {::utils::validate::Integer 999 0}
trace variable maxMoveNum w {::utils::validate::Integer 999 0}
trace variable minHalfMoves w {::utils::validate::Integer 99 0}
trace variable minMatDiff w {::utils::validate::Integer -99 0}
trace variable maxMatDiff w {::utils::validate::Integer -99 0}

set nPatterns 10

array set pMin [list wq 0 bq 0 wr 0 br 0 wb 0 bb 0 wn 0 bn 0 wm 0 bm 0 wp 0 bp 0]
array set pMax [list wq 2 bq 2 wr 2 br 2 wb 2 bb 2 wn 2 bn 2 wm 4 bm 4 wp 8 bp 8]
for { set i 1 } { $i <= $nPatterns } { incr i } {
  set pattPiece($i) "?";  set pattFyle($i) "?";  set pattRank($i) "?"
}

proc checkPieceCounts {name el op} {
  global pMin pMax
  ::utils::validate::Integer 9 0 $name $el $op
  # Now make sure minor piece counts fit with bishop/knight counts:
  set wmMin [expr {$pMin(wn) + $pMin(wb)} ]
  set wmMax [expr {$pMax(wn) + $pMax(wb)} ]
  set bmMin [expr {$pMin(bn) + $pMin(bb)} ]
  set bmMax [expr {$pMax(bn) + $pMax(bb)} ]
  if {$pMin(wm) < $wmMin} { set pMin(wm) $wmMin }
  if {$pMax(wm) > $wmMax} { set pMax(wm) $wmMax }
  if {$pMin(bm) < $bmMin} { set pMin(bm) $bmMin }
  if {$pMax(bm) > $bmMax} { set pMax(bm) $bmMax }
  foreach p {wq wr wb wn wm wp bq br bb bn bm bp} {
    if {$pMax($p) != ""  &&  $pMax($p) < $pMin($p)} { set pMax($p) $pMin($p) }
  }
}

trace variable pMin w checkPieceCounts
trace variable pMax w checkPieceCounts


proc makeBoolMenu {w varName} {
  upvar #0 $varName var
  if {![info exists var]} { set var "Yes" }
  menubutton $w -textvariable $varName -indicatoron 0 -menu $w.menu \
      -relief raised -bd 2 -highlightthickness 2 -anchor w -image ""
  menu $w.menu -tearoff 0
  $w.menu add radiobutton -label Yes -image _tick \
      -variable $varName -value Yes \
      -command "$w configure -image _tick" ;# -hidemargin 1
  $w.menu add radiobutton -label No -image _cross \
      -variable $varName -value No \
      -command "$w configure -image _cross" ;# -hidemargin 1
  return $w.menu
}

proc makePieceMenu {w varName} {
  global dark
  upvar #0 $varName var
  if {![info exists var]} { set var "?" }
  menubutton $w -textvariable $varName -indicatoron 0 -menu $w.menu \
      -relief raised -bd 2 -highlightthickness 2 -anchor w -image ""
  menu $w.menu -tearoff 0
  $w.menu add radiobutton -label " ? " -variable $varName -value "?" \
      -command "$w configure -image e20" ;# -hidemargin 1
  foreach i {wk wq wr wb wn wp bk bq br bb bn bp} {
    $w.menu add radiobutton -label $i -image ${i}20 -value $i \
        -variable $varName \
        -command "$w configure -image ${i}20" ;# -hidemargin 1
  }
  foreach i {" ? " wk bk} {
    $w.menu entryconfigure $i -columnbreak 1
  }
  return $w.menu
}

proc updatePatternImages {} {
  global pattPiece nPatterns pattBool
  if {! [winfo exists .sm]} { return }
  set w .sm.sf.scrolled
  for {set i 1} {$i <= $nPatterns} {incr i} {
    if {$pattBool($i) == "Yes"} {
      $w.mp.patt.grid.b$i configure -image _tick
    } else {
      $w.mp.patt.grid.b$i configure -image _cross
    }
    if {$pattPiece($i) == "?"} {
      $w.mp.patt.grid.p$i configure -image e20
    } else {
      $w.mp.patt.grid.p$i configure -image "$pattPiece($i)20"
    }
  }
}

# ::search::material::zero
#
#   Called to clear all material minumum/maximum values to zero.
#
proc ::search::material::zero {} {
  global pMin pMax
  array set pMin {wq 0 bq 0 wr 0 br 0 wb 0 bb 0 wn 0 bn 0 wm 0 bm 0 wp 0 bp 0}
  array set pMax {wq 0 bq 0 wr 0 br 0 wb 0 bb 0 wn 0 bn 0 wm 0 bm 0 wp 0 bp 0}
}

proc ::search::material::any {} {
  global pMin pMax
  array set pMin {wq 0 bq 0 wr 0 br 0 wb 0 bb 0 wn 0 bn 0 wm 0 bm 0 wp 0 bp 0}
  array set pMax {wq 2 bq 2 wr 2 br 2 wb 2 bb 2 wn 2 bn 2 wm 4 bm 4 wp 8 bp 8}
  set ::minMatDiff -40
  set maxMatDiff +40
}

proc clearPatterns {} {
  global pattPiece pattFyle pattRank pattBool nPatterns
  
  for { set i 1 } { $i <= $nPatterns } { incr i } {
    set pattPiece($i) "?";  set pattFyle($i) "?";  set pattRank($i) "?"
    set pattBool($i) "Yes"
  }
  updatePatternImages
}

proc setPatterns {pattlist} {
  global pattPiece pattFyle pattRank pattBool nPatterns
  
  clearPatterns
  set count 1
  foreach patt $pattlist {
    if {$count <= $nPatterns  &&  [llength $patt] == 4} {
      set pattPiece($count) [lindex $patt 0]
      set pattFyle($count) [lindex $patt 1]
      set pattRank($count) [lindex $patt 2]
      set pattBool($count) [lindex $patt 3]
      incr count
    }
  }
  updatePatternImages
}

set smDisplayed(Material) 1
set smDisplayed(Patterns) 0


# ::search::material
#
#   Opens the window for searching by material or patterns.
#
proc ::search::material {} {
  global glstart dark pMin pMax ignoreColors minMoveNum maxMoveNum
  global pattPiece pattFyle pattRank pattBool oppBishops nPatterns
  global minHalfMoves smDisplayed
  
  set w .sm
  if {[winfo exists $w]} {
    wm deiconify $w
    raiseWin $w
    return
  }
  
  toplevel $w
  wm title $w "Scid:Material search)"
  ::ui::setFullSize $w
  
  ::scrolledframe::scrolledframe .sm.sf -xscrollcommand {.sm.hs set} -yscrollcommand {.sm.vs set}  -fill both ;# try both, x, y or none
  scrollbar .sm.vs -command {.sm.sf yview}
  scrollbar .sm.hs -command {.sm.sf xview} -orient horizontal
  grid .sm.sf -row 0 -column 0 -sticky nsew
  grid .sm.vs -row 0 -column 1 -sticky ns
  grid .sm.hs -row 1 -column 0 -sticky ew
  grid rowconfigure .sm 0 -weight 1
  grid columnconfigure .sm 0 -weight 1
  
  set w .sm.sf.scrolled
  
  pack [frame $w.mp] -side top
  pack [frame $w.mp.material] -side left
  
  label $w.mp.material.title -textvar Material:
  pack $w.mp.material.title -side top -pady 3
  
  foreach piece {q r b n m p} {
    frame $w.mp.material.$piece
    pack $w.mp.material.$piece -side top ;# -padx 2
  }
  
  foreach i {q r b n m p} {
    set f $w.mp.material.$i
    button $f.w0 -text "0" -command "set pMin(w$i) 0; set pMax(w$i) 0"
    button $f.w1 -text "1" -command "set pMin(w$i) 1; set pMax(w$i) 1"
    button $f.w2 -text "2" -command "set pMin(w$i) 2; set pMax(w$i) 2"
    button $f.wa -text "0+" -command "set pMin(w$i) 0; set pMax(w$i) 2"
    button $f.w1p -text "1+" -command "set pMin(w$i) 1; set pMax(w$i) 2"
    label $f.wi -image w${i}20
    label $f.wto -text "-" -padx 0
    entry $f.wmin -width 2 -relief sunken -textvar pMin(w$i) -justify right
    entry $f.wmax -width 2 -relief sunken -textvar pMax(w$i) -justify right
    pack $f.w0 $f.w1 $f.w2 $f.wa $f.w1p $f.wi $f.wmin $f.wto $f.wmax -side left -pady 1
    
    pack [frame $f.space -width 20] -side left
    button $f.b0 -text "0" -command "set pMin(b$i) 0; set pMax(b$i) 0"
    button $f.b1 -text "1" -command "set pMin(b$i) 1; set pMax(b$i) 1"
    button $f.b2 -text "2" -command "set pMin(b$i) 2; set pMax(b$i) 2"
    button $f.ba -text "0+" -command "set pMin(b$i) 0; set pMax(b$i) 2"
    button $f.b1p -text "1+" -command "set pMin(b$i) 1; set pMax(b$i) 2"
    label $f.bi -image b${i}20
    label $f.bto -text "-"
    entry $f.bmin -width 2 -relief sunken -textvar pMin(b$i) -justify right
    entry $f.bmax -width 2 -relief sunken -textvar pMax(b$i) -justify right
    pack $f.b0 $f.b1 $f.b2 $f.ba $f.b1p $f.bi $f.bmin $f.bto $f.bmax -side left -pady 1
    
    foreach b {0 1 2 a 1p} {
      $f.w$b configure -width 2 -pady 0 -padx 1 -takefocus 0
      $f.b$b configure -width 2 -pady 0 -padx 1 -takefocus 0
    }
    
    if {$i == "p"} {
      $f.w1p configure -command "set pMin(wp) 1; set pMax(wp) 8"
      $f.wa configure -command "set pMin(wp) 0; set pMax(wp) 8"
      $f.b1p configure -command "set pMin(bp) 1; set pMax(bp) 8"
      $f.ba configure -command "set pMin(bp) 0; set pMax(bp) 8"
    }
    if {$i == "m"} {
      $f.w1p configure -command "set pMin(wm) 1; set pMax(wm) 4"
      $f.wa configure -command "set pMin(wm) 0; set pMax(wm) 4"
      $f.b1p configure -command "set pMin(bm) 1; set pMax(bm) 4"
      $f.ba configure -command "set pMin(bm) 0; set pMax(bm) 4"
    }
  }
  
  # Buttons that manipulate material settings:
  set f $w.mp.material.b1
  pack [frame $f] -side top -ipady 2
  
  dialogbutton $f.zero -text Zero -command ::search::material::zero
  dialogbutton $f.reset -text Any -command ::search::material::any
  dialogbutton $f.current -text "Current board" -command {
    ::search::material::zero
    set bd [sc_pos board]
    for {set i 0} {$i < 64} {incr i} {
      set piece $::letterToPiece([ string index $bd $i ])
      if {$piece != "e"  &&  $piece != "wk"  &&  $piece != "bk"} {
        incr pMin($piece); set pMax($piece) $pMin($piece)
      }
    }
  }
  
  pack $f.zero $f.reset $f.current -side left -pady 5 -padx 10
  
  set f $w.mp.material.mdiff
  pack [frame $f] -side top
  label $f.label -text "Material diff:"
  entry $f.min -width 3 -relief sunken -textvar minMatDiff -justify right
  label $f.sep -text "-"
  entry $f.max -width 3 -relief sunken -text maxMatDiff -justify right
  label $f.sep2 -text " "
  button $f.any -text Any -padx 1 -pady 1 -command {set minMatDiff -40; set maxMatDiff +40}
  button $f.w1 -text " + " -padx 1 -pady 1 -command {set minMatDiff +1; set maxMatDiff +40}
  button $f.equal -text " = " -padx 1 -pady 1 -command {set minMatDiff 0; set maxMatDiff 0}
  button $f.b1 -text " - " -padx 1 -pady 1 -command {set minMatDiff -40; set maxMatDiff -1}
  pack $f.label $f.min $f.sep $f.max -side left
  pack $f.sep2 $f.any $f.w1 $f.equal $f.b1 -side left
  set f [frame $w.mp.material.mdiff2]
  pack $f -side top
  label $f.explan -text "(Material diff = White - Black; Q=9 R=5 B=3 N=3 P=1)"
  pack $f.explan -side top
  
  addVerticalRule $w.mp
  
  set f [frame $w.mp.patt]
  pack $f -side top
  
  label $w.mp.patt.title -text Patterns:
  pack $w.mp.patt.title -side top -pady 3
  
  pack [frame $f.grid] -side top
  for { set i 1 } { $i <= $nPatterns } { incr i } {
    makeBoolMenu $f.grid.b$i pattBool($i)
    set menuPiece1 [ makePieceMenu $f.grid.p$i pattPiece($i) ]
    tk_optionMenu $f.grid.f$i pattFyle($i) "?" a b c d e f g h
    tk_optionMenu $f.grid.r$i pattRank($i) "?" 1 2 3 4 5 6 7 8
    $f.grid.b$i configure -indicatoron 0 ;# -width 4
    $f.grid.f$i configure -indicatoron 0 -width 1 -pady 1
    $f.grid.r$i configure -indicatoron 0 -width 1 -pady 1
    set column [expr {5 * (($i - 1) / 5)} ]
    set row [expr {($i - 1) % 5} ]
    grid $f.grid.b$i -row $row -column $column -padx 0; incr column
    grid $f.grid.p$i -row $row -column $column -padx 0; incr column
    grid $f.grid.f$i -row $row -column $column -padx 0; incr column
    grid $f.grid.r$i -row $row -column $column -padx 0; incr column
    if {$column == 4  ||  $column == 9} {
      label $f.grid.sp_$i -text "  "
      grid $f.grid.sp_$i -row $row -column $column
    }
  }
  
  updatePatternImages
  
  ### Buttons that manipulate patterns:
  set f $w.mp.patt.b2
  frame $f
  dialogbutton $f.clearPat -text Clear -command clearPatterns
  
  pack $f -side top
  pack $f.clearPat -side left -pady 5 -padx 10
  #if {! $smDisplayed(Patterns)} {
  #  pack forget $w.patt $w.b2
  #}
  updatePatternImages
  
  addHorizontalRule $w
  
  ### Now the move counter:
  
  set f $w.bishops
  pack [frame $f] -side top
  label $f.t1 -text "1"
  label $f.t2 -image wb20
  label $f.t3 -text "- 1"
  label $f.t4 -image bb20
  label $f.t5 -text squares:
  radiobutton $f.same -text "Same color" -variable oppBishops \
      -value "Same" -padx 5 -pady 4
  radiobutton $f.opp -text "Opp color" -variable oppBishops \
      -value "Opposite" -padx 5 -pady 4
  radiobutton $f.either -text Either -variable oppBishops \
      -value "Either" -padx 5 -pady 4
  foreach i {t1 t2 t3 t4 t5 same opp either} { pack $f.$i -side left }
  
  set f $w.move
  pack [frame $f] -side top -ipady 5
  label $f.fromlab -text "Move number range:"
  entry $f.from -width 4 -relief sunken -textvar minMoveNum -justify right
  label $f.tolab -text "-"
  entry $f.to -width 4 -relief sunken -textvar maxMoveNum -justify right
  label $f.space -text "  "
  label $f.label1 -textvar MatchForAtLeast
  entry $f.hmoves -width 3 -relief sunken -textvar minHalfMoves -justify right
  label $f.label2 -textvar HalfMoves
  pack $f.fromlab $f.from $f.tolab $f.to $f.space \
      $f.label1 $f.hmoves $f.label2 -side left
  
  addHorizontalRule $w
  ::search::addFilterOpFrame $w 1
  addHorizontalRule $w
  
  ### Progress bar:
  
  canvas $w.progress -height 20 -width 200 -bg white -relief solid -border 1
  $w.progress create rectangle 0 0 0 0 -outline blue -fill blue -tags bar
  $w.progress create text 295 10 -anchor e -tags time -fill black -text "0:00 / 0:00"
  
  ### Last of all, the buttons frame:
  
  set f $w.b3
  pack [frame $f] -side top -ipady 5 -fill x
  checkbutton $f.ignorecol -textvar IgnoreColors \
      -variable ignoreColors -padx 4
  
  dialogbutton $f.stop -text [::msgcat::mc "Stop"] -command sc_progressBar
  $f.stop configure -state disabled
  
  dialogbutton $f.search -text [::msgcat::mc "Search"] -command {
    busyCursor .sm
    set w .sm.sf.scrolled
    $w.b3.stop configure -state normal
    grab $w.b3.stop
    sc_progressBar $w.progress bar 201 21 time
    set str [sc_search material \
        -wq [list $pMin(wq) $pMax(wq)] -bq [list $pMin(bq) $pMax(bq)] \
        -wr [list $pMin(wr) $pMax(wr)] -br [list $pMin(br) $pMax(br)] \
        -wb [list $pMin(wb) $pMax(wb)] -bb [list $pMin(bb) $pMax(bb)] \
        -wn [list $pMin(wn) $pMax(wn)] -bn [list $pMin(bn) $pMax(bn)] \
        -wm [list $pMin(wm) $pMax(wm)] -bm [list $pMin(bm) $pMax(bm)] \
        -wp [list $pMin(wp) $pMax(wp)] -bp [list $pMin(bp) $pMax(bp)] \
        -flip $ignoreColors -filter $::search::filter::operation \
        -range [list $minMoveNum $maxMoveNum] \
        -length $minHalfMoves -bishops $oppBishops \
        -diff [list $minMatDiff $maxMatDiff] \
        -patt "$pattBool(1) $pattPiece(1) $pattFyle(1) $pattRank(1)" \
        -patt "$pattBool(2) $pattPiece(2) $pattFyle(2) $pattRank(2)" \
        -patt "$pattBool(3) $pattPiece(3) $pattFyle(3) $pattRank(3)" \
        -patt "$pattBool(4) $pattPiece(4) $pattFyle(4) $pattRank(4)" \
        -patt "$pattBool(5) $pattPiece(5) $pattFyle(5) $pattRank(5)" \
        -patt "$pattBool(6) $pattPiece(6) $pattFyle(6) $pattRank(6)" \
        -patt "$pattBool(7) $pattPiece(7) $pattFyle(7) $pattRank(7)" \
        -patt "$pattBool(8) $pattPiece(8) $pattFyle(8) $pattRank(8)" \
        -patt "$pattBool(9) $pattPiece(9) $pattFyle(9) $pattRank(9)" \
        -patt "$pattBool(10) $pattPiece(10) $pattFyle(10) $pattRank(10)" ]
    grab release $w.b3.stop
    $w.b3.stop configure -state disabled
    unbusyCursor .sm
    tk_messageBox -type ok -title "Search results" -parent .sm -message $str
    set glstart 1
  }
  
  dialogbutton $f.cancel -text Close -command { destroy .sm  }
  
  pack $w.b3.cancel $w.b3.search $w.b3.stop -side right -pady 5 -padx 5
  pack $w.progress -side top -pady 2
  
  label $w.status -text "" -width 1 -relief sunken -anchor w
  pack $w.status -side bottom -fill x
  
  # update
  ::search::Config
  focus $f.search
}

### ScidPocket.
### Copyright (C) 2007  Pascal Georges
###
### search.tcl: Search routines for Scid.
###

namespace eval ::search {}
namespace eval ::search::filter {}

# searchType: set to Header or Material in a SearchOptions file
set searchType 0

set ::search::filter::operation 2


# TODO: Rename to ::search::filter::text
# filterText: returns text describing state of filter for specified
#   database, e.g. "no games" or "all / 400" or "1,043 / 2,057"
#
proc filterText {{base 0} {kilo 0}} {
  # Default to current base if no base specified:
  if {$base == 0} { set base [sc_base current] }
  set filterCount [sc_filter count $base]
  set gameCount [sc_base numGames $base]
  if {$gameCount == 0} { return noGames }
  if {$gameCount == $filterCount} {
    return "all / [::utils::thousands $gameCount $kilo]"
  }
  return "[::utils::thousands $filterCount $kilo] / [::utils::thousands $gameCount $kilo]"
}


# search::filter::reset
#   Resets the filter to contain all games. Calls sc_filter reset and
#   updates relevant windows.
#
proc ::search::filter::reset {} {
  global glstart
  sc_filter reset
  set glstart 1
}

# ::search::filter::negate
#
#   Negates the filter, to include only excluded games.
#
proc ::search::filter::negate {} {
  global glstart
  sc_filter negate
  set glstart 1
}



# ::search::addFilterOpFrame
#
#   Adds a search filter operation selection frame to the window.
#   Adds a frame of radiobuttons allowing the filter operation
#   (AND with current filter, OR with current filter, or RESET filter)
#   to be chosen.
#   The default value for the first search is RESET:
proc ::search::addFilterOpFrame {w {small 0}} {
  frame $w.filterop
  set f $w.filterop
  pack $f -side top
  # set regular font_Regular
  # set bold font_Bold
  # if {$small} {
  # set regular font_Small
  # set bold font_SmallBold
  # }
  
  label $f.title -text [::msgcat::mc "Operation on\ncurrent filter:"] ;# -font $bold
  radiobutton $f.and -text [::msgcat::mc "AND\n(Restrict filter)"] -variable ::search::filter::operation \
      -value 0 -pady 5 -padx 5 ;# -font $regular
  radiobutton $f.or -text [::msgcat::mc "OR \n(Add to filter)"] -variable ::search::filter::operation \
      -value 1 -pady 5 -padx 5 ;# -font $regular
  radiobutton $f.ignore -text [::msgcat::mc "IGNORE\n(Reset filter)"] -variable ::search::filter::operation \
      -value 2 -pady 5 -padx 5 ;# -font $regular
  pack $f.title -side top
  pack $f.and $f.or $f.ignore -side left
}

# ::search::addFilterOpNoteBook
#
#   Adds a search filter operation selection frame to notebook.
#   Adds a frame of radiobuttons allowing the filter operation
#   (AND with current filter, OR with current filter, or RESET filter)
#   to be chosen.
#   The default value for the first search is RESET:
proc ::search::addFilterOpNoteBook { nb } {
  
  set f $nb.filterop
  $nb add [ ttk::frame $f ] -text [::msgcat::mc "filter"]
  
  ttk::label $f.title -text [::msgcat::mc "Operation on\ncurrent filter:"]
  ttk::radiobutton $f.and -text [::msgcat::mc "AND\n(Restrict filter)"] -variable ::search::filter::operation -value 0
  ttk::radiobutton $f.or -text [::msgcat::mc "OR \n(Add to filter)"] -variable ::search::filter::operation -value 1
  ttk::radiobutton $f.ignore -text [::msgcat::mc "IGNORE\n(Reset filter)"] -variable ::search::filter::operation -value 2
  pack $f.title -side top
  pack $f.and $f.or $f.ignore -side top -anchor w
}

# ::search::Config
#
#   Sets state of Search button in Header, Board and Material windows
#
proc ::search::Config {{state ""}} {
  if {$state == ""} {
    set state disabled
    if {[sc_base inUse]} { set state normal }
  }
  catch {.sh.b.search configure -state $state }
  catch {.sb.b.search configure -state $state }
  catch {.sm.b3.search configure -state $state }
  catch {.spprep.b.search configure -state $state }
}

### ScidPocket.
### Copyright (C) 2007  Pascal Georges
###
### search/board.tcl: Board Search routines for Scid.
###

set searchInVars 0
set sBoardIgnoreCols 0
set sBoardSearchType Exact

# ::search::board
#   Opens the search window for the current board position.
#
proc ::search::board {} {
  global glstart searchInVars sBoardType sBoardIgnoreCols
  
  set w .sb
  if {[winfo exists $w]} {
    wm deiconify $w
    raiseWin $w
    return
  }
  
  toplevel $w
  wm title $w "Scid: Board search"
  ::ui::setFullSize $w
  
  pack [ttk::notebook $w.nb] -fill both -expand 1
  
  # --------- Exec search -----------------
  $w.nb add [ttk::frame $w.nb.frun] -text [::msgcat::mc "run"] -sticky nsew
  
  ### Progress bar:
  
  canvas $w.nb.frun.progress -height 20 -width 200 -bg white -relief solid -border 1
  $w.nb.frun.progress create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
  $w.nb.frun.progress create text 195 10 -anchor e -tags time -fill black -text "0:00 / 0:00"
  
  ttk::frame $w.nb.frun.b2
  pack $w.nb.frun.b2 -side top -fill x
  ttk::frame $w.nb.frun.b
  pack $w.nb.frun.b -side top -fill x
  
  ttk::checkbutton $w.nb.frun.b2.vars -text [::msgcat::mc "Look in variations"] -onvalue 1 -offvalue 0 -variable searchInVars
  ttk::checkbutton $w.nb.frun.b2.flip -text [::msgcat::mc "Ignore colors"] -onvalue 1 -offvalue 0 -variable sBoardIgnoreCols
  
  ttk::button $w.nb.frun.b.stop -text [::msgcat::mc Stop] -command sc_progressBar
  $w.nb.frun.b.stop configure -state disabled
  
  ttk::button $w.nb.frun.b.search -text [::msgcat::mc Search] -command {
    set w .sb
    busyCursor $w
    $w.nb.frun.b.stop configure -state normal
    grab $w.nb.frun.b.stop
    sc_progressBar $w.nb.frun.progress bar 201 21 time
    set str [sc_search board $::search::filter::operation $sBoardSearchType $searchInVars $sBoardIgnoreCols]
    unbusyCursor $w
    grab release $w.nb.frun.b.stop
    $w.nb.frun.b.stop configure -state disabled
    tk_messageBox -type ok -title "Search results" -parent .sb -message $str
    set glstart 1
  }
  ttk::button $w.nb.frun.b.cancel -text [::msgcat::mc Close] -command "focus .; destroy .sb"
  pack $w.nb.frun.b2.vars $w.nb.frun.b2.flip -side left
  
  grid $w.nb.frun.b.search -column 0 -row 0 -sticky ew
  grid $w.nb.frun.b.stop   -column 1 -row 0 -sticky ew
  grid $w.nb.frun.b.cancel -column 0 -row 1 -sticky ew
  pack $w.nb.frun.progress -side top 
  
  # --------- Search type -----------------
  $w.nb add [ttk::frame $w.nb.ftype] -text [::msgcat::mc "type"] -sticky nsew
  
  ttk::label $w.nb.ftype.type -text [::msgcat::mc "Search type:"]
  pack $w.nb.ftype.type -side top -fill x
  pack [ttk::frame $w.nb.ftype.g] -side top -fill both
  ttk::radiobutton $w.nb.ftype.g.exact -text [::msgcat::mc "Exact position (all\npieces on same squares)"] -variable sBoardSearchType -value Exact
  ttk::radiobutton $w.nb.ftype.g.pawns -text [::msgcat::mc "Pawns (same material,\n all pawns on same squares)"] -variable sBoardSearchType -value Pawns
  ttk::radiobutton $w.nb.ftype.g.files -text [::msgcat::mc "Files (same material,\n all pawns on same files)"] -variable sBoardSearchType -value Fyles
  ttk::radiobutton $w.nb.ftype.g.material -text [::msgcat::mc "Any (same material,\n pawns and pieces anywhere)"] -variable sBoardSearchType -value Material
  set row 0
  foreach i {exact pawns files material} {
    grid $w.nb.ftype.g.$i -row $row -column 0 -sticky ew
    incr row
  }

  # ----------------- Filter operation -------------  
  ::search::addFilterOpNoteBook $w.nb
  
  ::search::Config
  focus $w.nb.frun.b.search
}
### history.tcl
### Text entry history functions for Scid.
### Copyright (C) 2004 Shane Hudson.

namespace eval ::utils::history {}


set ::utils::history::defaultListLength 10
array set ::utils::history::listLength {}
array set ::utils::history::comboboxWidget {}

if {! [info exists ::utils::history::listData]} {
  array set ::utils::history::listData {}
}

# Load any history lists that were saved in the last session:
catch {source [scidConfigFile history]}


proc ::utils::history::SetList {key list} {
  set ::utils::history::listData($key) $list
}


proc ::utils::history::GetList {key} {
  variable listData
  if {[info exists listData($key)]} {
    return $listData($key)
  }
  return {}
}


#  ::utils::history::AddEntry
#
#   Inserts an entry at the top of the list of the specified key and
#   ensures that only one copy of the entry exists in the list.
#   The list is then pruned to the limit size is necessary.
#
proc ::utils::history::AddEntry {key entry} {
  variable listData
  
  # We do not add the empty string to a history list:
  if {$entry == ""} { return }
  
  if {[info exists listData($key)]} {
    # Take out this entry if it exists, so it will not appear twice:
    set index [lsearch -exact $listData($key) $entry]
    if {$index == 0} {
      # The entry is already at the start of the list; nothing to do
      return
    } elseif {$index > 0} {
      set listData($key) [lreplace $listData($key) $index $index]
    }
    set listData($key) [linsert $listData($key) 0 $entry]
    ::utils::history::PruneList $key
  } else {
    set listData($key) [list $entry]
  }
  RefillCombobox $key
}


proc ::utils::history::SetLimit {key length} {
  set ::utils::history::listLength($key) $length
  ::utils::history::PruneList $key
}


proc ::utils::history::GetLimit {key} {
  variable listLength
  variable defaultListLength
  if {[info exists ::utils::history::listLength($key)]} {
    return $::utils::history::listLength($key)
  }
  return $defaultListLength
}


proc ::utils::history::PruneList {key {length -1}} {
  variable data
  if {! [info exists data($key)]} { return }
  if {$length < 0} {
    set length [::utils::history::GetLimit $key]
  }
  set data($key) [lrange $data($key) 0 [expr {$length - 1}]]
}



# ::utils::history::SetCombobox
#
#   Returns the combobox widget associated with this history key.
#
proc ::utils::history::GetCombobox {key} {
  variable comboboxWidget
  if {[info exists comboboxWidget($key)]} {
    return $comboboxWidget($key)
  }
  return ""
}


# ::utils::history::SetCombobox
#
#   Associates the specified widget (which must be a megawidget created
#   with ::combobox::combobox, see contrib/combobox.tcl) with the specifiec
#   history key. Whenever the list for this history key is modified, the
#   combobox widget will be updated.
#
proc ::utils::history::SetCombobox {key cbWidget} {
  set ::utils::history::comboboxWidget($key) $cbWidget
  RefillCombobox $key
}


# ::utils::history::SetCombobox
#
#   Refills the history list of the combobox associated with the specified
#   history key, if there is one.
#
proc ::utils::history::RefillCombobox {key} {
  variable comboboxWidget
  
  set cbWidget [GetCombobox $key]
  if {$cbWidget == ""} { return }
  
  # If the combobox widget is part of a dialog which is generated as needed,
  # it may not exist right now:
  if {! [winfo exists $cbWidget]} { return }
  
  # $cbWidget list delete 0 end
  
  set entries [GetList $key]
  $cbWidget configure -values $entries
  
  # foreach entry $entries {
  # $cbWidget list insert end $entry
  # }
}


# ::utils::history::Save
#   Saves the combobox history file, reporting any error in a message box
#   if reportError is true.
#
proc ::utils::history::Save {{reportError 0}} {
  variable listData
  
  set f {}
  set filename [scidConfigFile history]
  
  if  {[catch {open $filename w} f]} {
    if {$reportError} {
      tk_messageBox -title "Scid" -type ok -icon warning \
          -message "Unable to write file: $filename\n$f"
    }
    return
  }
  
  puts $f "# Scid [sc_info version] combobox history lists"
  puts $f ""
  foreach i [lsort [array names listData]] {
    puts $f "set ::utils::history::listData($i) [list $listData($i)]"
  }
  close $f
}

namespace eval ::utils::date {}

# ::utils::date::today:
#   Returns todays date, in "yyyy.mm.dd" format.
#   The optional parameter "year", "month" or "day" can be used to
#   limit the returned value to just the year, month or day.
#
proc ::utils::date::today {{type all}} {
  set timeNow [clock seconds]
  set year [clock format $timeNow -format "%Y"]
  set month [clock format $timeNow -format "%m"]
  set day [clock format $timeNow -format "%d"]
  switch -- $type {
    "all"   { return [format "%s.%s.%s" $year $month $day] }
    "year"  { return $year }
    "month" { return $month }
    "day"   { return $day }
    default { error "Unrecognised parameter: $type" }
  }
}

image create photo ::utils::date::calendar -data {
R0lGODdhFgAUAMIAANnZ2VFR+wAAAP////oTQP//AAAAAAAAACwAAAAAFgAUAAADTwi63A4h
yklrVAFruDO0lCCO5NMIw4CqqWAya9ySdG3LbI7He+vrsxthSLiJfitCoUBAzpwDJRNqFBCL
RqpW1QN6q+DRdrfomsvh2mvtSAAAOw==
}

# ::utils::date::chooser
#
#   Produce a date-selection dialog box.
#   Originally based on code from Effective Tcl/Tk Programming by
#   Mark Harrison, but with lots of changes and improvements.
#
proc ::utils::date::chooser {{date "now"}} {
  set time [clock seconds]
  if {$date != "now"} {
    catch {set time [clock scan $date]}
  }
  set ::utils::date::_time $time
  set ::utils::date::_selected [clock format $time -format "%Y-%m-%d"]

  set win .dateChooser
  toplevel $win
  ::ui::setFullSize $win
  canvas $win.cal ;#-width 300 -height 220
  pack [frame $win.b] -side bottom -fill x
  button $win.b.ok -text "OK" -command "destroy $win"
  button $win.b.cancel -text Cancel -command "
    set ::utils::date::_selected {}
    destroy $win"
  pack $win.b.cancel $win.b.ok -side right -padx 5 -pady 5
  pack $win.cal -side top -expand yes -fill both

  button $win.cal.prevY -image tb_start -command "::utils::date::_month $win -12"
  button $win.cal.prev -image tb_prev -command "::utils::date::_month $win -1"
  button $win.cal.next -image tb_next -command "::utils::date::_month $win +1"
  button $win.cal.nextY -image tb_end -command "::utils::date::_month $win +12"
  bind $win.cal <Configure> "::utils::date::_redraw $win"
  bind $win.cal <Double-Button-1> "destroy $win"
  bind $win <Escape> "$win.b.cancel invoke"
  bind $win <Return> "$win.b.ok invoke"
  bind $win <Prior> "$win.cal.prev invoke"
  bind $win <Next> "$win.cal.next invoke"
  bind $win <Shift-Prior> "$win.cal.prevY invoke"
  bind $win <Shift-Next> "$win.cal.nextY invoke"
  bind $win <Up> "::utils::date::_day $win -7"
  bind $win <Down> "::utils::date::_day $win +7"
  bind $win <Left> "::utils::date::_day $win -1"
  bind $win <Right> "::utils::date::_day $win +1"

  # wm minsize $win 250 200
  wm title $win "Scid: Choose Date"
  focus $win
  grab $win
  tkwait window $win
  if {$::utils::date::_selected == ""} { return {} }
  set time [clock scan $::utils::date::_selected]
  return [list \
          [clock format $time -format "%Y"] \
          [clock format $time -format "%m"] \
          [clock format $time -format "%d"] \
         ]
}

proc ::utils::date::_day {win delta} {
  set unit "day"
  if {$delta < 0} {set unit "day ago"}
  set time [clock scan "[expr abs($delta)] $unit" -base $::utils::date::_time]
  set day [string trimleft [clock format $time -format "%d"] 0]
  set month [string trimleft [clock format $time -format "%m"] 0]
  set year [clock format $time -format "%Y"]
  ::utils::date::_select $win "$year-$month-$day"
}

proc ::utils::date::_month {win delta} {
  set dir [expr {($delta > 0) ? 1 : -1} ]
  set day [string trimleft [clock format $::utils::date::_time -format "%d"] 0]
  set month [string trimleft [clock format $::utils::date::_time -format "%m"] 0]
  set year [clock format $::utils::date::_time -format "%Y"]

  for {set i 0} {$i < abs($delta)} {incr i} {
    incr month $dir
    if {$month < 1} {
      set month 12
      incr year -1
    } elseif {$month > 12} {
      set month 1
      incr year 1
    }
  }
  if {[catch {::date::_select $win "$year-$month-$day"}]} {
    ::utils::date::_select $win "$year-$month-28"
  }
}

proc ::utils::date::_redraw {win} {
  $win.cal delete all
  set time $::utils::date::_time
  set wmax [winfo width $win.cal]
  set hmax [winfo height $win.cal]

  $win.cal create window 3 3 -anchor nw -window $win.cal.prevY
  $win.cal create window 40 3 -anchor nw -window $win.cal.prev
  $win.cal create window [expr {$wmax-43} ] 3 -anchor ne -window $win.cal.next
  $win.cal create window [expr {$wmax-3} ] 3 -anchor ne -window $win.cal.nextY
  set bottom [lindex [$win.cal bbox all] 3]

  set month [string trimleft [clock format $time -format "%m"] 0]
  set year [clock format $time -format "%Y"]
  $win.cal create text [expr {$wmax/2} ] $bottom -anchor s -font font_Bold \
    -text "[lindex Months [expr $month - 1]] $year"

  incr bottom 3
  $win.cal create line 0 $bottom $wmax $bottom -width 2
  incr bottom 25

  set current ""

  set layout [::utils::date::_layout $time]
  set weeks [expr {[lindex $layout end]+1} ]

  for {set day 0} {$day < 7} {incr day} {
    set x0 [expr {$day*($wmax-7)/7+3} ]
    set x1 [expr {($day+1)*($wmax-7)/7+3} ]
    $win.cal create text [expr {($x1+$x0)/2} ] $bottom -anchor s \
      -text [lindex Days $day] -font font_Small
  }
  incr bottom 3

  foreach {day date dcol wrow} $layout {
    set x0 [expr {$dcol*($wmax-7)/7+3} ]
    set y0 [expr {$wrow*($hmax-$bottom-4)/$weeks+$bottom} ]
    set x1 [expr {($dcol+1)*($wmax-7)/7+3} ]
    set y1 [expr {($wrow+1)*($hmax-$bottom-4)/$weeks+$bottom} ]

    if {$date == $::utils::date::_selected} {set current $date}

    $win.cal create rectangle $x0 $y0 $x1 $y1 -outline black -fill white

    $win.cal create text [expr {$x0+4} ] [expr {$y0+2} ] -anchor nw -text "$day" \
      -fill black -font font_Small -tags [list $date-text all-text]

    $win.cal create rectangle $x0 $y0 $x1 $y1 \
      -outline "" -fill "" -tags [list $date-sensor all-sensor]

    $win.cal bind $date-sensor <ButtonPress-1> "::utils::date::_select $win $date"
  }

  if {$current != ""} {
    $win.cal itemconfigure $current-sensor -outline red -width 3
    $win.cal raise $current-sensor
  } elseif {$::utils::date::_selected == ""} {
    set date [clock format $time -format "%Y-%m-%d"]
    ::utils::date::_select $win $date
  }
}

proc ::utils::date::_layout {time} {
  set month [string trimleft [clock format $time -format "%m"] 0]
  set year  [clock format $time -format "%Y"]

  foreach lastday {31 30 29 28} {
    if {[catch {clock scan "$year-$month-$lastday"}] == 0} { break }
  }
  set seconds [clock scan "$year-$month-1"]
  set firstday [clock format $seconds -format %w]
  set weeks [expr {ceil(double($lastday+$firstday)/7)} ]

  set rlist ""
  for {set day 1} {$day <= $lastday} {incr day} {
    set seconds [clock scan "$year-$month-$day"]
    set date [clock format $seconds -format "%Y-%m-%d"]
    set daycol [clock format $seconds -format %w]
    set weekrow [expr {($firstday+$day-1)/7} ]
    lappend rlist $day $date $daycol $weekrow
  }
  return $rlist
}

proc ::utils::date::_select {win date} {
  set time [clock scan $date]
  set date [clock format $time -format "%Y-%m-%d"]

  set currentMonth [clock format $::utils::date::_time -format "%m %Y"]
  set selectedMonth [clock format $time -format "%m %Y"]
  set ::utils::date::_time $time
  set ::utils::date::_selected $date

  if {$currentMonth == $selectedMonth} {
    $win.cal itemconfigure all-sensor -outline "" -width 1
    $win.cal itemconfigure $date-sensor -outline red -width 3
    $win.cal raise $date-sensor
  } else {
    ::utils::date::_redraw $win
  }
}
namespace eval ::utils::string {
  # ::utils::string::Surname
  #
  #   Returns the surname of a player name.
  #
  proc Surname {name} {
    set idx [string first "," $name]
    if {$idx > 0} { set name [string range $name 0 [expr {$idx - 1} ]] }
    return $name
  }
  
  
  proc CityName {siteName} {
    regsub { [A-Z][A-Z][A-Z]$} $siteName "" siteName
    return [string trim [::utils::string::Surname $siteName]]
  }
  
  
  # ::utils::string::Capital
  #
  #    Returns a string with the first character capitalised.
  #
  proc Capital {str} {
    set s [string toupper [string index $str 0]]
    append s [string range $str 1 end]
    return $s
  }
  
  # PadLeft
  #
  #   Given a string and a length, pads the string with padChar to have
  #   the required length.
  #
  proc PadLeft {str length {padChar " "}} {
    set s $str
    for {set actual [string length $s]} {$actual < $length} {incr actual} {
      append s $padChar
    }
    return $s
  }
  
  # Pad
  #
  #   Same as PadLeft.
  #
  proc Pad {str length {padChar " "}} {
    return [::utils::string::PadLeft $str $length $padChar]
  }
  
  # PadRight
  #
  #   Like PadLeft, but adds the padding characters to the start of the string.
  #
  proc PadRight {str length {padChar " "}} {
    set s $str
    for {set actual [string length $s]} {$actual < $length} {incr actual} {
      set s "$padChar$s"
    }
    return $s
  }
  
  # PadCenter
  #
  #   Like PadLeft and PadRight, but centers the specified string.
  #
  proc PadCenter {str length {padChar " "}} {
    set pre 1
    set s $str
    for {set actual [string length $s]} {$actual < $length} {incr actual} {
      if {$pre} {
        set s "$padChar$s"
        set pre 0
      } else {
        append s $padChar
        set pre 1
      }
    }
    return $s
  }
  
}
if {[info exists ::scrolledframe::version]} { return }
namespace eval ::scrolledframe {
  # beginning of ::scrolledframe namespace definition
  
  namespace export scrolledframe
  
  # ==============================
  #
  # scrolledframe
  set version 0.9.1
  set (debug,place) 0
  #
  # a scrolled frame
  #
  # (C) 2003, ulis
  #
  # NOL licence (No Obligation Licence)
  #
  # Changes (C) 2004, KJN
  #
  # NOL licence (No Obligation Licence)
  # ==============================
  #
  # Hacked package, no documentation, sorry
  # See example at bottom
  #
  # ------------------------------
  # v 0.9.1
  #  automatic scroll on resize
  # ==============================
  
  package provide Scrolledframe $version
  
  # --------------
  #
  # create a scrolled frame
  #
  # --------------
  # parm1: widget name
  # parm2: options key/value list
  # --------------
  proc scrolledframe {w args} \
      {
        variable {}
        # create a scrolled frame
        frame $w
        # trap the reference
        rename $w ::scrolledframe::_$w
        # redirect to dispatch
        interp alias {} $w {} ::scrolledframe::dispatch $w
        # create scrollable internal frame
        frame $w.scrolled -highlightt 0 -padx 0 -pady 0
        # place it
        place $w.scrolled -in $w -x 0 -y 0
        if {$(debug,place)} { puts "place $w.scrolled -in $w -x 0 -y 0" } ;#DEBUG
        # init internal data
        set ($w:vheight) 0
        set ($w:vwidth) 0
        set ($w:vtop) 0
        set ($w:vleft) 0
        set ($w:xscroll) ""
        set ($w:yscroll) ""
        set ($w:width)    0
        set ($w:height)   0
        set ($w:fillx)    0
        set ($w:filly)    0
        # configure
        if {$args != ""} { uplevel 1 ::scrolledframe::config $w $args }
        # bind <Configure>
        bind $w <Configure> [namespace code [list resize $w]]
        bind $w.scrolled <Configure> [namespace code [list resize $w]]
        # return widget ref
        return $w
      }
  
  # --------------
  #
  # dispatch the trapped command
  #
  # --------------
  # parm1: widget name
  # parm2: operation
  # parm2: operation args
  # --------------
  proc dispatch {w cmd args} \
      {
        variable {}
        switch -glob -- $cmd \
        {
          con*    { uplevel 1 [linsert $args 0 ::scrolledframe::config $w] }
          xvi*    { uplevel 1 [linsert $args 0 ::scrolledframe::xview  $w] }
          yvi*    { uplevel 1 [linsert $args 0 ::scrolledframe::yview  $w] }
          default { uplevel 1 [linsert $args 0 ::scrolledframe::_$w    $cmd] }
        }
      }
  
  # --------------
  # configure operation
  #
  # configure the widget
  # --------------
  # parm1: widget name
  # parm2: options
  # --------------
  proc config {w args} \
      {
        variable {}
        set options {}
        set flag 0
        foreach {key value} $args \
        {
          switch -glob -- $key \
          {
            -fill   \
            {
              # new fill option: what should the scrolled object do if it is smaller than the viewing window?
              if {$value == "none"} {
                set ($w:fillx) 0
                set ($w:filly) 0
              } elseif {$value == "x"} {
                set ($w:fillx) 1
                set ($w:filly) 0
              } elseif {$value == "y"} {
                set ($w:fillx) 0
                set ($w:filly) 1
              } elseif {$value == "both"} {
                set ($w:fillx) 1
                set ($w:filly) 1
              } else {
                error "invalid value: should be \"$w configure -fill value\", where \"value\" is \"x\", \"y\", \"none\", or \"both\""
              }
              resize $w force
              set flag 1
            }
            -xsc*   \
            {
              # new xscroll option
              set ($w:xscroll) $value
              set flag 1
            }
            -ysc*   \
            {
              # new yscroll option
              set ($w:yscroll) $value
              set flag 1
            }
            default { lappend options $key $value }
          }
        }
        # check if needed
        if {!$flag || $options != ""} \
        {
          # call frame config
          uplevel 1 [linsert $options 0 ::scrolledframe::_$w config]
        }
      }
  
  # --------------
  # resize proc
  #
  # Update the scrollbars if necessary, in response to a change in either the viewing window
  # or the scrolled object.
  # Replaces the old resize and the old vresize
  # A <Configure> call may mean any change to the viewing window or the scrolled object.
  # We only need to resize the scrollbars if the size of one of these objects has changed.
  # Usually the window sizes have not changed, and so the proc will not resize the scrollbars.
  # --------------
  # parm1: widget name
  # parm2: pass anything to force resize even if dimensions are unchanged
  # --------------
  proc resize {w args} \
      {
        variable {}
        set force [llength $args]
        
        set _vheight     $($w:vheight)
        set _vwidth      $($w:vwidth)
        # compute new height & width
        set ($w:vheight) [winfo reqheight $w.scrolled]
        set ($w:vwidth)  [winfo reqwidth  $w.scrolled]
        
        # The size may have changed, e.g. by manual resizing of the window
        set _height     $($w:height)
        set _width      $($w:width)
        set ($w:height) [winfo height $w] ;# gives the actual height of the viewing window
        set ($w:width)  [winfo width  $w] ;# gives the actual width of the viewing window
        
        if {$force || $($w:vheight) != $_vheight || $($w:height) != $_height} {
          # resize the vertical scroll bar
          yview $w scroll 0 unit
          # yset $w
        }
        
        if {$force || $($w:vwidth) != $_vwidth || $($w:width) != $_width} {
          # resize the horizontal scroll bar
          xview $w scroll 0 unit
          # xset $w
        }
      } ;# end proc resize
  
  # --------------
  # xset proc
  #
  # resize the visible part
  # --------------
  # parm1: widget name
  # --------------
  proc xset {w} \
      {
        variable {}
        # call the xscroll command
        set cmd $($w:xscroll)
        if {$cmd != ""} { catch { eval $cmd [xview $w] } }
      }
  
  # --------------
  # yset proc
  #
  # resize the visible part
  # --------------
  # parm1: widget name
  # --------------
  proc yset {w} \
      {
        variable {}
        # call the yscroll command
        set cmd $($w:yscroll)
        if {$cmd != ""} { catch { eval $cmd [yview $w] } }
      }
  
  # -------------
  # xview
  #
  # called on horizontal scrolling
  # -------------
  # parm1: widget path
  # parm2: optional moveto or scroll
  # parm3: fraction if parm2 == moveto, count unit if parm2 == scroll
  # -------------
  # return: scrolling info if parm2 is empty
  # -------------
  proc xview {w {cmd ""} args} \
      {
        variable {}
        # check args
        set len [llength $args]
        switch -glob -- $cmd \
        {
          ""      {set args {}}
          mov*    \
          { if {$len != 1} { error "wrong # args: should be \"$w xview moveto fraction\"" } }
          scr*    \
          { if {$len != 2} { error "wrong # args: should be \"$w xview scroll count unit\"" } }
          default \
          { error "unknown operation \"$cmd\": should be empty, moveto or scroll" }
        }
        # save old values:
        set _vleft $($w:vleft)
        set _vwidth $($w:vwidth)
        set _width  $($w:width)
        # compute new vleft
        set count ""
        switch $len \
        {
          0       \
          {
            # return fractions
            if {$_vwidth == 0} { return {0 1} }
            set first [expr {double($_vleft) / $_vwidth}]
            set last [expr {double($_vleft + $_width) / $_vwidth}]
            if {$last > 1.0} { return {0 1} }
            return [list [format %g $first] [format %g $last]]
          }
          1       \
          {
            # absolute movement
            set vleft [expr {int(double($args) * $_vwidth)}]
          }
          2       \
          {
            # relative movement
            foreach {count unit} $args break
            if {[string match p* $unit]} { set count [expr {$count * 9}] }
            set vleft [expr {$_vleft + $count * 0.1 * $_width}]
          }
        }
        if {$vleft + $_width > $_vwidth} { set vleft [expr {$_vwidth - $_width}] }
        if {$vleft < 0} { set vleft 0 }
        if {$vleft != $_vleft || $count == 0} \
        {
          set ($w:vleft) $vleft
          xset $w
          if {$($w:fillx) && ($_vwidth < $_width || $($w:xscroll) == "") } {
            # "scrolled object" is not scrolled, because it is too small or because no scrollbar was requested
            # fillx means that, in these cases, we must tell the object what its width should be
            place $w.scrolled -in $w -x [expr {-$vleft}] -width $_width
            if {$(debug,place)} { puts "place $w.scrolled -in $w -x [expr {-$vleft}] -width $_width" } ;#DEBUG
          } else {
            place $w.scrolled -in $w -x [expr {-$vleft}] -width {}
            if {$(debug,place)} { puts "place $w.scrolled -in $w -x [expr {-$vleft}] -width {}" } ;#DEBUG
          }
          
        }
      }
  
  # -------------
  # yview
  #
  # called on vertical scrolling
  # -------------
  # parm1: widget path
  # parm2: optional moveto or scroll
  # parm3: fraction if parm2 == moveto, count unit if parm2 == scroll
  # -------------
  # return: scrolling info if parm2 is empty
  # -------------
  proc yview {w {cmd ""} args} \
      {
        variable {}
        # check args
        set len [llength $args]
        switch -glob -- $cmd \
        {
          ""      {set args {}}
          mov*    \
          { if {$len != 1} { error "wrong # args: should be \"$w yview moveto fraction\"" } }
          scr*    \
          { if {$len != 2} { error "wrong # args: should be \"$w yview scroll count unit\"" } }
          default \
          { error "unknown operation \"$cmd\": should be empty, moveto or scroll" }
        }
        # save old values
        set _vtop $($w:vtop)
        set _vheight $($w:vheight)
        #    set _height [winfo height $w]
        set _height $($w:height)
        # compute new vtop
        set count ""
        switch $len \
        {
          0       \
          {
            # return fractions
            if {$_vheight == 0} { return {0 1} }
            set first [expr {double($_vtop) / $_vheight}]
            set last [expr {double($_vtop + $_height) / $_vheight}]
            if {$last > 1.0} { return {0 1} }
            return [list [format %g $first] [format %g $last]]
          }
          1       \
          {
            # absolute movement
            set vtop [expr {int(double($args) * $_vheight)}]
          }
          2       \
          {
            # relative movement
            foreach {count unit} $args break
            if {[string match p* $unit]} { set count [expr {$count * 9}] }
            set vtop [expr {$_vtop + $count * 0.1 * $_height}]
          }
        }
        if {$vtop + $_height > $_vheight} { set vtop [expr {$_vheight - $_height}] }
        if {$vtop < 0} { set vtop 0 }
        if {$vtop != $_vtop || $count == 0} \
        {
          set ($w:vtop) $vtop
          yset $w
          if {$($w:filly) && ($_vheight < $_height || $($w:yscroll) == "")} {
            # "scrolled object" is not scrolled, because it is too small or because no scrollbar was requested
            # filly means that, in these cases, we must tell the object what its height should be
            place $w.scrolled -in $w -y [expr {-$vtop}] -height $_height
            if {$(debug,place)} { puts "place $w.scrolled -in $w -y [expr {-$vtop}] -height $_height" } ;#DEBUG
          } else {
            place $w.scrolled -in $w -y [expr {-$vtop}] -height {}
            if {$(debug,place)} { puts "place $w.scrolled -in $w -y [expr {-$vtop}] -height {}" } ;#DEBUG
          }
        }
      }
  
  # end of ::scrolledframe namespace definition
}
set locale(numeric) ".,"

# thousands, percentFormat:
#   Functions to format integer numbers.
#   thousands inserts the thousands separator (usually "," or ".") for
#   every three digits before the decimal separator in the number.
#   percentFormat does the same as thousands, but also adds a percentage.
#   If "kilo" is true, numbers >= 100,000 are divided by 1000 and have
#   the unit "K" appended while values over 1 million appear as "1.00M"
#
proc ::utils::thousands {n {kilo 0}} {
  global locale
  set commaChar [string index $locale(numeric) 1]
  set unit ""
  if {$kilo} {
    if {$n >= 1000000} {
      set decimalChar [string index $locale(numeric) 0]
      set decimalPart [format "%02d" [expr {(int($n / 10000)) % 100}]]
      set n [expr {int($n) / 1000000}]
      set unit "${decimalChar}${decimalPart}M"
    } elseif {$n >= 100000} {
      set unit "K"
      set n [expr {int($n / 1000)} ]
    }
  }
  if {$commaChar == ""} { return "$n$unit" }
  while {[regsub {^([-+]?[0-9]+)([0-9][0-9][0-9])} $n "\\1$commaChar\\2" n]} {}
  return "$n$unit"
}

proc ::utils::percentFormat {num denom} {
  # Ensure denominator is not zero:
  if {$denom == 0} {set denom 1}
  return "[::utils::thousands $num] ([expr $num * 100 / $denom]%)"
}
