update branch webgl
authorkoda
Sun, 27 Jan 2013 00:28:57 +0100
branchwebgl
changeset 8444 75db7bb8dce8
parent 8340 46a9fde631f4 (current diff)
parent 8443 2debc9b9f917 (diff)
child 8446 c18ba8726f5a
update branch
.hgignore
CMakeLists.txt
QTfrontend/AutoUpdater.cpp
QTfrontend/AutoUpdater.h
QTfrontend/CMakeLists.txt
QTfrontend/CocoaInitializer.h
QTfrontend/CocoaInitializer.mm
QTfrontend/InstallController.cpp
QTfrontend/InstallController.h
QTfrontend/M3InstallController.h
QTfrontend/M3InstallController.m
QTfrontend/M3Panel.h
QTfrontend/M3Panel.mm
QTfrontend/NSWorkspace_RBAdditions.h
QTfrontend/NSWorkspace_RBAdditions.m
QTfrontend/SparkleAutoUpdater.h
QTfrontend/SparkleAutoUpdater.mm
QTfrontend/game.cpp
QTfrontend/gameuiconfig.cpp
QTfrontend/gameuiconfig.h
QTfrontend/hwform.cpp
QTfrontend/hwform.h
QTfrontend/net/newnetclient.cpp
QTfrontend/net/newnetclient.h
QTfrontend/ui/dialog/input_password.cpp
QTfrontend/ui/page/pagefeedback.cpp
QTfrontend/ui/page/pagefeedback.h
QTfrontend/ui/page/pagemain.cpp
QTfrontend/ui/page/pagevideos.cpp
QTfrontend/ui/widget/about.cpp
QTfrontend/xfire.cpp
QTfrontend/xfire.h
gameServer/Actions.hs
gameServer/HWProtoInRoomState.hs
hedgewars/ArgParsers.inc
hedgewars/CMakeLists.txt
hedgewars/GSHandlers.inc
hedgewars/hwengine.pas
hedgewars/uAIAmmoTests.pas
hedgewars/uCommandHandlers.pas
hedgewars/uConsts.pas
hedgewars/uCursor.pas
hedgewars/uGears.pas
hedgewars/uGearsHedgehog.pas
hedgewars/uGearsRender.pas
hedgewars/uIO.pas
hedgewars/uInputHandler.pas
hedgewars/uLand.pas
hedgewars/uLocale.pas
hedgewars/uScript.pas
hedgewars/uSound.pas
hedgewars/uStore.pas
hedgewars/uTeams.pas
hedgewars/uTypes.pas
hedgewars/uUtils.pas
hedgewars/uVariables.pas
hedgewars/uVisualGears.pas
hedgewars/uWorld.pas
misc/physfs/CMakeLists.txt
misc/xfire/Xfire Game SDK.url
misc/xfire/license.txt
misc/xfire/xfiregameclient.cpp
misc/xfire/xfiregameclient.h
tools/pas2c/Pas2C.hs
tools/pas2c/PascalParser.hs
--- a/.hgignore	Wed Jan 02 11:11:49 2013 +0100
+++ b/.hgignore	Sun Jan 27 00:28:57 2013 +0100
@@ -14,6 +14,7 @@
 glob:config.inc
 glob:cmake_install.cmake
 glob:QTfrontend/hwconsts.cpp
+glob:QTfrontend/servermessages.h
 glob:CPackConfig.cmake
 glob:CPackSourceConfig.cmake
 glob:tools/cmake_uninstall.cmake
--- a/CMakeLists.txt	Wed Jan 02 11:11:49 2013 +0100
+++ b/CMakeLists.txt	Sun Jan 27 00:28:57 2013 +0100
@@ -28,9 +28,11 @@
 option(NOAUTOUPDATE "Disable OS X Sparkle update checking" OFF)
 option(MINIMAL_FLAGS "Respect system flags as much as possible [default: off]" OFF)
 option(GL2 "Enable OpenGL 2 rendering [default: off]" OFF)
-set(FPFLAGS "" CACHE STRING "Additional Freepascal flags" FORCE)
-set(GHFLAGS "" CACHE STRING "Additional Haskell flags" FORCE)
-
+set(FPFLAGS "" CACHE STRING "Additional Freepascal flags")
+set(GHFLAGS "" CACHE STRING "Additional Haskell flags")
+if(UNIX AND NOT APPLE)
+    set(DATA_INSTALL_DIR "share/hedgewars" CACHE STRING "Resource folder path")
+endif()
 
 #detect Mercurial revision (if present)
 if(NOT ${NOREVISION})
@@ -89,11 +91,7 @@
 if(UNIX AND NOT APPLE)
     set(target_binary_install_dir "bin")
     set(target_library_install_dir "lib")
-    if(DEFINED DATA_INSTALL_DIR)
-        set(SHAREPATH "${DATA_INSTALL_DIR}")
-    else()
-        set(SHAREPATH "share/hedgewars/")
-    endif()
+    set(SHAREPATH "${DATA_INSTALL_DIR}/")
 else()
     set(target_binary_install_dir "./")
 
@@ -149,8 +147,8 @@
         find_package(SDL_mixer REQUIRED)
         set(DYLIB_SMPEG "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg")
         set(DYLIB_MIKMOD "-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod")
-        set(pascal_flags "-k${DYLIB_SMPEG}" "-k${DYLIB_MIKMOD}" ${pascal_flags})
-        set(CMAKE_C_FLAGS "${DYLIB_SMPEG}" "${DYLIB_MIKMOD}" ${CMAKE_C_FLAGS})
+        set(CMAKE_C_FLAGS "${DYLIB_SMPEG} ${DYLIB_MIKMOD}")
+        list(APPEND pascal_flags "-k${DYLIB_SMPEG}" "-k${DYLIB_MIKMOD}")
     endif()
 
     #CMAKE_OSX_ARCHITECTURES and CMAKE_OSX_SYSROOT need to be set for universal binary and correct linking
@@ -180,9 +178,9 @@
     endif()
 
     #add user framework directory, other paths can be passed via FPFLAGS
-    set(pascal_flags "-Ff~/Library/Frameworks" ${pascal_flags})
+    list(APPEND pascal_flags "-Ff~/Library/Frameworks")
     #set deployment target
-    set(pascal_flags "-k-macosx_version_min" "-k${minimum_macosx_version}" "-XR${CMAKE_OSX_SYSROOT}" ${pascal_flags})
+    list(APPEND pascal_flags "-k-macosx_version_min" "-k${minimum_macosx_version}" "-XR${CMAKE_OSX_SYSROOT}")
 
     #silly libav that always brings in VideoDecoderAcceleration, avaible only from 10.6.3
     if(NOT NOVIDEOREC AND ${minimum_macosx_version} VERSION_LESS "10.6")
@@ -203,10 +201,10 @@
 endif (CMAKE_BUILD_TYPE)
 
 #set default flags values for all projects (unless MINIMAL_FLAGS is true)
-if(NOT MINIMAL_FLAGS)
-    set(CMAKE_C_FLAGS "-pipe")
-    set(CMAKE_C_FLAGS_RELEASE "-w -Os -fomit-frame-pointer")
-    set(CMAKE_C_FLAGS_DEBUG "-Wall -O0 -g -DDEBUG")
+if(NOT ${MINIMAL_FLAGS})
+    set(CMAKE_C_FLAGS "-pipe ${CMAKE_C_FLAGS}")
+    set(CMAKE_C_FLAGS_RELEASE "-w -Os -fomit-frame-pointer ${CMAKE_C_FLAGS_RELEASE}")
+    set(CMAKE_C_FLAGS_DEBUG "-Wall -O0 -g -DDEBUG ${CMAKE_C_FLAGS_DEBUG}")
     set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS})
     set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
     set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
@@ -229,7 +227,7 @@
     endif()
 endif()
 
-set(pascal_flags ${fpflags_parsed}              # user flags
+list(APPEND pascal_flags ${fpflags_parsed}              # user flags
                  "-vm4079,4080,4081"            # fpc output format
                  "-B"                           # compile all units
                  "-FE${PROJECT_BINARY_DIR}/bin" # fpc output directory
@@ -237,19 +235,18 @@
                  "-Cs2000000"                   # stack size
                  "-vewnq"                       # fpc output verbosity
                  "-dDEBUGFILE"                  # macro for engine output
-                 ${pascal_flags}                # adding to list
                  )
-set(haskell_flags "-O2" ${ghflags_parsed} ${haskell_flags})
+list(APPEND haskell_flags "-O2" ${ghflags_parsed})
 
 #get BUILD_TYPE and enable/disable optimisation
 message(STATUS "Using ${CMAKE_BUILD_TYPE} configuration")
 if(CMAKE_BUILD_TYPE MATCHES "DEBUG")
-    set(pascal_flags "-O-" "-g" "-gl" "-gv" ${pascal_flags})
-    set(haskell_flags "-Wall" "-debug" "-dcore-lint" "-fno-warn-unused-do-bind" ${haskell_flags})
+    list(APPEND pascal_flags "-O-" "-g" "-gl" "-gv")
+    list(APPEND haskell_flags "-Wall" "-debug" "-dcore-lint" "-fno-warn-unused-do-bind")
 else()
 #    set(pascal_flags "-O3" "-OpPENTIUM4" "-CfSSE3" "-Xs" "-Si" ${pascal_flags})
-    set(pascal_flags "-Os" "-Xs" "-Si" ${pascal_flags})
-    set(haskell_flags "-w" "-fno-warn-unused-do-bind" ${haskell_flags})
+    list(APPEND pascal_flags "-Os" "-Xs" "-Si")
+    list(APPEND haskell_flags "-w" "-fno-warn-unused-do-bind")
 endif()
 
 
@@ -289,14 +286,14 @@
     message(STATUS "Using internal LUA library")
     add_subdirectory(misc/liblua)
     #linking with liblua.a requires system readline
-    set(pascal_flags "-k${EXECUTABLE_OUTPUT_PATH}/lib${LUA_LIBRARY}.a" "-k-lreadline" ${pascal_flags})
+    list(APPEND pascal_flags "-k${EXECUTABLE_OUTPUT_PATH}/lib${LUA_LIBRARY}.a" "-k-lreadline")
 endif()
 
 
 #physfs library (static on unix, dll on win32)
 add_subdirectory(misc/physfs)
 if(NOT WIN32)
-    set(pascal_flags "-k${LIBRARY_OUTPUT_PATH}/libphysfs.a" ${pascal_flags})
+    list(APPEND pascal_flags "-k${LIBRARY_OUTPUT_PATH}/libphysfs.a")
 endif()
 
 
--- a/ChangeLog.txt	Wed Jan 02 11:11:49 2013 +0100
+++ b/ChangeLog.txt	Sun Jan 27 00:28:57 2013 +0100
@@ -28,7 +28,7 @@
  * Fix all knowns bugs which caused network game hang when players close engine or quit
  * Fix drill strike bug when drill's timer gets ridiculously high value instead of explosion
  * Fix some crashes, freezes and memory leaks in frontend and engine
- 
+
 0.9.16 -> 0.9.17:
  + New theme, Cave
  + New voicepack, Hillbilly
@@ -401,7 +401,7 @@
  + AI updates
  + Teams now work in fort mode, i.e. 2v2
  + Ability to attack whilst jumping/rope swinging
- + Some weapons can only be used after a certain number of turns 
+ + Some weapons can only be used after a certain number of turns
  + Lots of new graphics
  * Many network/gameplay bug fixes
 
--- a/QTfrontend/AutoUpdater.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-/*
- * Copyright (C) 2008 Remko Troncon
- */
-
-#include "AutoUpdater.h"
-
-AutoUpdater::~AutoUpdater()
-{
-}
--- a/QTfrontend/AutoUpdater.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-/*
- * Copyright (C) 2008 Remko Troncon
- */
-
-#ifndef AUTOUPDATER_H
-#define AUTOUPDATER_H
-
-class AutoUpdater
-{
-    public:
-        virtual ~AutoUpdater();
-
-        virtual void checkForUpdates() = 0;
-        virtual void checkForUpdatesNow() = 0;
-};
-
-#endif
--- a/QTfrontend/CMakeLists.txt	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/CMakeLists.txt	Sun Jan 27 00:28:57 2013 +0100
@@ -23,7 +23,29 @@
     endif()
 endif()
 
-include_directories(.)
+# server messages localization
+file(GLOB ServerSources ${CMAKE_SOURCE_DIR}/gameServer/*.hs)
+foreach(hsfile ${ServerSources})
+    file(READ ${hsfile} hs)
+    string(REGEX MATCHALL "loc *\"[^\n\"]+\"" locs ${hs})
+    foreach(str ${locs})
+        string(REGEX REPLACE "loc *\"([^\n\"]+)\"" "QT_TRANSLATE_NOOP(\"server\", \"\\1\")" s ${str})
+        list(APPEND serverlocs ${s})
+    endforeach(str)
+endforeach(hsfile)
+
+list(REMOVE_DUPLICATES serverlocs)
+list(GET serverlocs 0 firstline)
+list(REMOVE_AT serverlocs 0)
+set(locsout "const char * serverMessages[] = {\n")
+foreach(l ${serverlocs})
+    list(APPEND locsout ${l} ",\n")
+endforeach(l)
+list(APPEND locsout ${firstline} "\n}\\;\n")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/servermessages.h ${locsout})
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/model)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/net)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ui)
@@ -31,6 +53,7 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ui/page)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ui/widget)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/util)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/util/platform)
 include_directories(${SDL_INCLUDE_DIR})
 include_directories(${SDLMIXER_INCLUDE_DIR})
 include_directories(${FFMPEG_INCLUDE_DIR})
@@ -41,11 +64,12 @@
     include_directories("/usr/local/include")
 endif(UNIX)
 
-#directory for resources, relative to the one above on certain platforms/configurations
-if(DEFINED DATA_INSTALL_DIR OR WIN32 OR APPLE)
+#directory for resources, relative to bindir (on linux an absolute path is always used)
+string(SUBSTRING "${SHAREPATH}" 0 1 sharepath_start)
+if(APPLE OR WIN32 OR ${sharepath_start} MATCHES "/")
     set(HEDGEWARS_DATADIR ${SHAREPATH})
 else()
-    set(HEDGEWARS_DATADIR ${CMAKE_INSTALL_PREFIX}/${SHAREPATH}/)
+    set(HEDGEWARS_DATADIR ${CMAKE_INSTALL_PREFIX}/${SHAREPATH})
 endif()
 
 #only the cocoa version of qt supports building 64 bit apps
@@ -61,7 +85,7 @@
 file(GLOB_RECURSE UIcpp ui/*.cpp)
 file(GLOB UtilCpp util/*.cpp)
 
-set(hwfr_src
+list(APPEND hwfr_src
     ${ModelCpp}
     ${NetCpp}
     ${UIcpp}
@@ -82,7 +106,7 @@
 
 #xfire integration
 if(WIN32)
-    set(hwfr_src ${hwfr_src} xfire.cpp ../misc/xfire/xfiregameclient.cpp)
+    list(APPEND hwfr_src util/platform/xfire.cpp util/platform/xfiregameclient.cpp)
 endif(WIN32)
 
 if(MINGW)
@@ -91,9 +115,9 @@
                        COMMAND windres -I ${CMAKE_CURRENT_SOURCE_DIR}
                                -i ${CMAKE_CURRENT_SOURCE_DIR}/hedgewars.rc
                                -o ${CMAKE_CURRENT_BINARY_DIR}/hedgewars_rc.o)
-    set(hwfr_src ${hwfr_src} ${CMAKE_CURRENT_BINARY_DIR}/hedgewars_rc.o)
+    list(APPEND hwfr_src ${CMAKE_CURRENT_BINARY_DIR}/hedgewars_rc.o)
 else(MINGW)
-    set(hwfr_src ${hwfr_src} hedgewars.rc)
+    list(APPEND hwfr_src hedgewars.rc)
 endif(MINGW)
 
 file(GLOB ModelHdr model/*.h)
@@ -114,6 +138,7 @@
     team.h
     util/DataManager.h
     util/LibavInteraction.h
+    util/MessageDialog.h
     )
 
 set(hwfr_hdrs
@@ -125,6 +150,7 @@
     hwconsts.h
     sdlkeys.h
     campaign.h
+    ${CMAKE_CURRENT_BINARY_DIR}/servermessages.h
     )
 
 set(hwfr_rez hedgewars.qrc)
@@ -132,7 +158,7 @@
 if(${BUILD_ENGINE_LIBRARY})
     add_definitions(-DHWLIBRARY=1)
     set(hwlibname "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}hwengine${CMAKE_SHARED_LIBRARY_SUFFIX}")
-    set(HW_LINK_LIBS ${hwlibname} ${HW_LINK_LIBS})
+    list(APPEND HW_LINK_LIBS ${hwlibname})
 endif()
 
 qt4_add_resources(hwfr_rez_src ${hwfr_rez})
@@ -142,19 +168,20 @@
 
 if(APPLE)
     find_library(iokit_framework NAMES IOKit)
-    set(HW_LINK_LIBS ${iokit_framework} ${HW_LINK_LIBS})
-    set(hwfr_src ${hwfr_src} CocoaInitializer.mm
-                             InstallController.cpp
-                             M3Panel.mm
-                             M3InstallController.m
-                             NSWorkspace_RBAdditions.m
-                             )
+    list(APPEND HW_LINK_LIBS ${iokit_framework})
+    list(APPEND hwfr_src util/platform/CocoaInitializer.mm
+                         util/platform/InstallController.cpp
+                         util/platform/M3Panel.mm
+                         util/platform/M3InstallController.m
+                         util/platform/NSWorkspace_RBAdditions.m
+                         )
     if(NOT NOAUTOUPDATE)
         find_package(Sparkle)
         if(SPARKLE_FOUND)
             add_definitions(-DSPARKLE_ENABLED)
-            set(hwfr_src ${hwfr_src} AutoUpdater.cpp SparkleAutoUpdater.mm)
-            set(HW_LINK_LIBS ${SPARKLE_LIBRARY} ${HW_LINK_LIBS})
+            list(APPEND hwfr_src util/platform/AutoUpdater.cpp
+                                 util/platform/SparkleAutoUpdater.mm)
+            list(APPEND HW_LINK_LIBS ${SPARKLE_LIBRARY})
         endif()
     endif()
 endif()
@@ -181,22 +208,20 @@
     set_target_properties(hedgewars PROPERTIES LINK_FLAGS "-Wl,-rpath,${CMAKE_INSTALL_PREFIX}/${target_library_install_dir}")
 endif()
 
-set(HW_LINK_LIBS
+list(APPEND HW_LINK_LIBS
     physfs
     ${QT_LIBRARIES}
     ${SDL_LIBRARY}
     ${SDLMIXER_LIBRARY}
     ${FFMPEG_LIBRARIES}
-    ${HW_LINK_LIBS}
     )
 
 if(WIN32 AND NOT UNIX)
     if(NOT SDL_LIBRARY)
-        set(HW_LINK_LIBS ${HW_LINK_LIBS} SDL)
+        list(APPEND HW_LINK_LIBS SDL)
     endif()
 
-    set(HW_LINK_LIBS
-        ${HW_LINK_LIBS}
+    list(APPEND HW_LINK_LIBS
         ole32
         oleaut32
         winspool
--- a/QTfrontend/CocoaInitializer.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-// see original example here http://el-tramo.be/blog/mixing-cocoa-and-qt
-
-#ifndef COCOAINITIALIZER_H
-#define COCOAINITIALIZER_H
-
-class CocoaInitializer
-{
-    public:
-        CocoaInitializer();
-        ~CocoaInitializer();
-
-    private:
-        class Private;
-        Private* c;
-};
-
-#endif
--- a/QTfrontend/CocoaInitializer.mm	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-// see original example here http://el-tramo.be/blog/mixing-cocoa-and-qt
-
-#include "CocoaInitializer.h"
-
-#include <AppKit/AppKit.h>
-#include <Cocoa/Cocoa.h>
-#include <QtDebug>
-
-class CocoaInitializer::Private
-{
-    public:
-        NSAutoreleasePool* pool;
-};
-
-CocoaInitializer::CocoaInitializer()
-{
-    c = new CocoaInitializer::Private();
-    c->pool = [[NSAutoreleasePool alloc] init];
-    NSApplicationLoad();
-}
-
-CocoaInitializer::~CocoaInitializer()
-{
-    [c->pool release];
-    delete c;
-}
--- a/QTfrontend/InstallController.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include "InstallController.h"
-
-InstallController::~InstallController()
-{
-}
--- a/QTfrontend/InstallController.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#ifndef INSTALLCONTROLLER_H
-#define INSTALLCONTROLLER_H
-
-class InstallController
-{
-    public:
-        virtual ~InstallController();
-
-        virtual void showInstallController() = 0;
-};
-
-#endif
--- a/QTfrontend/M3InstallController.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*****************************************************************
- M3InstallController.m
-
- Created by Martin Pilkington on 02/06/2007.
-
- Copyright (c) 2006-2009 M Cubed Software
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
-
- *****************************************************************/
-
-#import <Cocoa/Cocoa.h>
-#import <AvailabilityMacros.h>
-
-//#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
-#if __LP64__ || NS_BUILD_32_LIKE_64
-typedef long NSInteger;
-typedef unsigned long NSUInteger;
-#else
-typedef int NSInteger;
-typedef unsigned int NSUInteger;
-#endif
-//#endif
-
-
-@interface M3InstallController :
-NSObject
-{
-    NSAlert *alert;
-}
-
-- (void)displayInstaller;
-- (void)installApp;
-@end
--- a/QTfrontend/M3InstallController.m	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/*****************************************************************
- M3InstallController.m
-
- Created by Martin Pilkington on 02/06/2007.
-
- Copyright (c) 2006-2009 M Cubed Software
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
-
- *****************************************************************/
-
-#import "M3InstallController.h"
-#import "NSWorkspace_RBAdditions.h"
-
-#import <Foundation/Foundation.h>
-
-@implementation M3InstallController
-
-- (id) init {
-        if ((self = [super init])) {
-		NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
-		NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%@ is currently running from a disk image", @"AppName is currently running from a disk image"), appName];
-		NSString *body = [NSString stringWithFormat:NSLocalizedString(@"Would you like to install %@ in your applications folder before quitting?", @"Would you like to install App Name in your applications folder before quitting?"), appName];
-		alert = [[NSAlert alertWithMessageText:title
-								 defaultButton:NSLocalizedString(@"Install", @"Install")
-							   alternateButton:NSLocalizedString(@"Don't Install", @"Don't Install")
-								   otherButton:nil
-					 informativeTextWithFormat:body] retain];
-		//[alert setShowsSuppressionButton:YES];
-	}
-	return self;
-}
-
-- (void)displayInstaller {
-	NSString *imageFilePath = [[[NSWorkspace sharedWorkspace] propertiesForPath:[[NSBundle mainBundle] bundlePath]] objectForKey:NSWorkspace_RBimagefilepath];
-	if (imageFilePath && ![imageFilePath isEqualToString:[NSString stringWithFormat:@"/Users/.%@/%@.sparseimage", NSUserName(), NSUserName()]] && ![[NSUserDefaults standardUserDefaults] boolForKey:@"M3DontAskInstallAgain"]) {
-		NSInteger returnValue = [alert runModal];
-		if (returnValue == NSAlertDefaultReturn) {
-			[self installApp];
-		}
-		if ([[alert suppressionButton] state] == NSOnState) {
-			[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"M3DontAskInstallAgain"];
-		}
-	}
-}
-
-- (void)installApp {
-	NSString *appsPath = [[NSString stringWithString:@"/Applications"] stringByAppendingPathComponent:[[[NSBundle mainBundle] bundlePath] lastPathComponent]];
-	NSString *userAppsPath = [[[NSString stringWithString:@"~/Applications"] stringByAppendingPathComponent:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] stringByExpandingTildeInPath];
-	NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
-
-	//Delete the app that is installed
-	if ([[NSFileManager defaultManager] fileExistsAtPath:appsPath]) {
-		[[NSFileManager defaultManager] removeFileAtPath:appsPath handler:nil];
-	}
-	//Delete the app that is installed
-	if ([[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath] toPath:appsPath
-										  handler:nil]) {
-		NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"%@ installed successfully", @"App Name installed successfully"), appName],
-						[NSString stringWithFormat:NSLocalizedString(@"%@ was installed in /Applications", @"App Name was installed in /Applications"), appName],
-						NSLocalizedString(@"Quit", @"Quit"), nil, nil);
-	} else {
-		if ([[NSFileManager defaultManager] fileExistsAtPath:userAppsPath]) {
-			[[NSFileManager defaultManager] removeFileAtPath:userAppsPath handler:nil];
-		}
-		if ([[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath] toPath:userAppsPath
-												handler:nil]) {
-		NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"%@ installed successfully", @"AppName installed successfully"), appName],
-				[NSString stringWithFormat:NSLocalizedString(@"%@ was installed in %@", @"App Name was installed in %@"), appName, [[NSString stringWithString:@"~/Applications"] stringByExpandingTildeInPath]],
-						NSLocalizedString(@"Quit", @"Quit"), nil, nil);
-		} else {
-			NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"Could not install %@", @"Could not install App Name"), appName],
-							NSLocalizedString(@"An error occurred when installing", @"An error occurred when installing"), NSLocalizedString(@"Quit", @"Quit"), nil, nil);
-		}
-	}
-}
-
-@end
--- a/QTfrontend/M3Panel.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#ifndef M3PANEL_H
-#define M3PANEL_H
-
-#include "InstallController.h"
-
-class M3Panel : public InstallController
-{
-    public:
-        M3Panel(void);
-        ~M3Panel();
-
-        void showInstallController();
-
-    private:
-        class Private;
-        Private* m;
-};
-
-#endif
--- a/QTfrontend/M3Panel.mm	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include "M3Panel.h"
-#include "M3InstallController.h"
-
-#include <Cocoa/Cocoa.h>
-
-class M3Panel::Private
-{
-    public:
-        M3InstallController *install;
-};
-
-M3Panel::M3Panel(void)
-{
-    m = new M3Panel::Private();
-
-    m->install = [[M3InstallController alloc] init];
-    [m->install retain];
-}
-
-M3Panel::~M3Panel()
-{
-    [m->install release];
-    delete m;
-}
-
-void M3Panel::showInstallController()
-{
-    [m->install displayInstaller];
-}
--- a/QTfrontend/NSWorkspace_RBAdditions.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-//
-//  NSWorkspace_RBAdditions.h
-//  PathProps
-//
-//  Created by Rainer Brockerhoff on 10/04/2007.
-//  Copyright 2007 Rainer Brockerhoff. All rights reserved.
-//
-
-#import <Cocoa/Cocoa.h>
-
-extern NSString* NSWorkspace_RBfstypename;
-extern NSString* NSWorkspace_RBmntonname;
-extern NSString* NSWorkspace_RBmntfromname;
-extern NSString* NSWorkspace_RBdeviceinfo;
-extern NSString* NSWorkspace_RBimagefilepath;
-extern NSString* NSWorkspace_RBconnectiontype;
-extern NSString* NSWorkspace_RBpartitionscheme;
-extern NSString* NSWorkspace_RBserverURL;
-
-@interface NSWorkspace (NSWorkspace_RBAdditions)
-
-// This method will return nil if the input path is invalid. Otherwise, the returned NSDictionary may contain
-// the following keys:
-//- NSWorkspace_RBfstypename: will always be present.Shows the filesystem type (usually "hfs"), from statfs.
-//- NSWorkspace_RBmntonname: will always be present. Shows the volume mount point.
-//- NSWorkspace_RBmntfromname: will always be present. Shows the BSD device path for local volumes; info for
-//      remote volumes depends on the filesystem type.
-//- NSWorkspace_RBconnectiontype: should always be present for local volumes. Shows the connection type ("SATA", "USB", etc.).
-//- NSWorkspace_RBpartitionscheme: should always be present for local volumes. Shows the partition scheme.
-//- NSWorkspace_RBdeviceinfo: should always be present for local volumes. Shows some information about the
-//      physical device; varies widely.
-//- NSWorkspace_RBimagefilepath: should be present for disk images only. Shows the path of the disk image file.
-//- NSWorkspace_RBserverURL: should be present for remote volumes only. Shows the server URL.
-
-- (NSDictionary*)propertiesForPath:
-(NSString*)path;
-
-@end
--- a/QTfrontend/NSWorkspace_RBAdditions.m	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,263 +0,0 @@
-//
-//  NSWorkspace_RBAdditions.m
-//  PathProps
-//
-//  Created by Rainer Brockerhoff on 10/04/2007.
-//  Copyright 2007 Rainer Brockerhoff. All rights reserved.
-//
-
-#import "NSWorkspace_RBAdditions.h"
-#include <IOKit/IOKitLib.h>
-#include <sys/mount.h>
-#include <mach/mach.h>
-
-NSString* NSWorkspace_RBfstypename = @"NSWorkspace_RBfstypename";
-NSString* NSWorkspace_RBmntonname = @"NSWorkspace_RBmntonname";
-NSString* NSWorkspace_RBmntfromname = @"NSWorkspace_RBmntfromname";
-NSString* NSWorkspace_RBdeviceinfo = @"NSWorkspace_RBdeviceinfo";
-NSString* NSWorkspace_RBimagefilepath = @"NSWorkspace_RBimagefilepath";
-NSString* NSWorkspace_RBconnectiontype = @"NSWorkspace_RBconnectiontype";
-NSString* NSWorkspace_RBpartitionscheme = @"NSWorkspace_RBpartitionscheme";
-NSString* NSWorkspace_RBserverURL = @"NSWorkspace_RBserverURL";
-
-// This static funtion concatenates two strings, but first checks several possibilities...
-// like one or the other nil, or one containing the other already.
-
-static NSString* AddPart(NSString* first,NSString* second) {
-	if (!second) {
-		return first;
-	}
-	second = [second stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
-	if (first) {
-		if ([first rangeOfString:second options:NSCaseInsensitiveSearch].location==NSNotFound) {
-			if ([second rangeOfString:first options:NSCaseInsensitiveSearch].location==NSNotFound) {
-				return [NSString stringWithFormat:@"%@; %@",first,second];
-			}
-			return second;
-		}
-		return first;
-	}
-	return second;
-}
-
-// This static functions recurses "upwards" over the IO registry. Returns strings that are concatenated
-// and ultimately end up under the NSWorkspace_RBdeviceinfo key.
-// This isn't too robust in that it assumes that objects returned by the objectForKey methods are
-// either strings or dictionaries. A "standard" implementations would use either only CoreFoundation and
-// IOKit calls for this, or do more robust type checking on the returned objects.
-//
-// Also notice that this works as determined experimentally in 10.4.9, there's no official docs I could find.
-// YMMV, and it may stop working in any new version of Mac OS X.
-
-static NSString* CheckParents(io_object_t thing,NSString* part,NSMutableDictionary* dict) {
-	NSString* result = part;
-    io_iterator_t parentsIterator = 0;
-    kern_return_t kernResult = IORegistryEntryGetParentIterator(thing,kIOServicePlane,&parentsIterator);
-    if ((kernResult==KERN_SUCCESS)&&parentsIterator) {
-		io_object_t nextParent = 0;
-		while ((nextParent = IOIteratorNext(parentsIterator))) {
-			NSDictionary* props = nil;
-			NSString* image = nil;
-			NSString* partition = nil;
-			NSString* connection = nil;
-			kernResult = IORegistryEntryCreateCFProperties(nextParent,(CFMutableDictionaryRef*)&props,kCFAllocatorDefault,0);
-			if (IOObjectConformsTo(nextParent,"IOApplePartitionScheme")) {
-				partition = [props objectForKey:@"Content Mask"];
-			} else if (IOObjectConformsTo(nextParent,"IOMedia")) {
-				partition = [props objectForKey:@"Content"];
-			} else if (IOObjectConformsTo(nextParent,"IODiskImageBlockStorageDeviceOutKernel")) {
-				NSData* data = nil;
-                                if ((data = [[props objectForKey:@"Protocol Characteristics"] objectForKey:@"Virtual Interface Location Path"])) {
-					image = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
-				}
-			} else if (IOObjectConformsTo(nextParent,"IOHDIXHDDriveInKernel")) {
-				image = [props objectForKey:@"KDIURLPath"];
-			}
-			NSDictionary* subdict;
-                        if ((subdict = [props objectForKey:@"Protocol Characteristics"])) {
-				connection = [subdict objectForKey:@"Physical Interconnect"];
-			} else {
-				connection = [props objectForKey:@"Physical Interconnect"];
-			}
-			if (connection) {
-				[dict setObject:AddPart([dict objectForKey:NSWorkspace_RBconnectiontype],connection) forKey:NSWorkspace_RBconnectiontype];
-			}
-			if (partition) {
-				[dict setObject:partition forKey:NSWorkspace_RBpartitionscheme];
-			}
-			if (image) {
-				[dict setObject:image forKey:NSWorkspace_RBimagefilepath];
-			}
-			NSString* value;
-                        if ((subdict = [props objectForKey:@"Device Characteristics"])) {
-                                if ((value = [subdict objectForKey:@"Product Name"])) {
-					result = AddPart(result,value);
-				}
-                                if ((value = [subdict objectForKey:@"Product Revision Level"])) {
-					result = AddPart(result,value);
-				}
-                                if ((value = [subdict objectForKey:@"Vendor Name"])) {
-					result = AddPart(result,value);
-				}
-			}
-                        if ((value = [props objectForKey:@"USB Serial Number"])) {
-				result = AddPart(result,value);
-			}
-                        if ((value = [props objectForKey:@"USB Vendor Name"])) {
-				result = AddPart(result,value);
-			}
-			NSString* cls = [(NSString*)IOObjectCopyClass(nextParent) autorelease];
-			if (![cls isEqualToString:@"IOPCIDevice"]) {
-
-// Uncomment the following line to have the device tree dumped to the console.
-//				NSLog(@"=================================> %@:%@\n",cls,props);
-
-				result = CheckParents(nextParent,result,dict);
-			}
-			IOObjectRelease(nextParent);
-		}
-    }
-    if (parentsIterator) {
-		IOObjectRelease(parentsIterator);
-    }
-	return result;
-}
-
-// This formats the (partially undocumented) AFPXMountInfo info into a string.
-
-/*
-static NSString* FormatAFPURL(AFPXVolMountInfoPtr mountInfo,NSString** devdesc) {
-	UInt8* work = ((UInt8*)mountInfo)+mountInfo->serverNameOffset;
-	if (devdesc) {
-		*devdesc = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
-	}
-	work = ((UInt8*)mountInfo)+mountInfo->volNameOffset;
-	NSString* volname = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
-	work = ((UInt8*)mountInfo)+mountInfo->alternateAddressOffset;
-	AFPAlternateAddress* afpa = (AFPAlternateAddress*)work;
-	AFPTagData* afpta = (AFPTagData*)(&afpa->fAddressList);
-	NSString* ip = nil;
-	NSString* dns = nil;
-	int i = afpa->fAddressCount;
-	while ((i-->0)) {
-		switch (afpta->fType) {
-			case kAFPTagTypeIP:
-				if (!ip) {
-					ip = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
-				}
-				break;
-			case kAFPTagTypeIPPort:
-				ip = [NSString stringWithFormat:@"%u.%u.%u.%u:%u",afpta->fData[0],afpta->fData[1],afpta->fData[2],afpta->fData[3],OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4])];
-				break;
-			case kAFPTagTypeDNS:
-				dns = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
-				break;
-			case 0x07:
-				ip = [NSString stringWithFormat:@"[%x:%x:%x:%x:%x:%x:%x:%x]",OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[0]),
-					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[2]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4]),
-					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[6]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[8]),
-					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[10]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[12]),
-					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[14])];
-				break;
-		}
-		afpta = (AFPTagData*)((char*)afpta+afpta->fLength);
-	}
-	return [NSString stringWithFormat:@"afp://%@/%@",dns?:(ip?:@""),volname];
-}
-*/
-
-@implementation NSWorkspace (NSWorkspace_RBAdditions)
-
-// Returns a NSDictionary with properties for the path. See details in the .h file.
-// This assumes that the length of path is less than PATH_MAX (currently 1024 characters).
-
-- (NSDictionary*)propertiesForPath:(NSString*)path {
-	const char* ccpath = (const char*)[path fileSystemRepresentation];
-	NSMutableDictionary* result = nil;
-	struct statfs fs;
-	if (!statfs(ccpath,&fs)) {
-		NSString* from = [NSString stringWithUTF8String:fs.f_mntfromname];
-		result = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-			[NSString stringWithUTF8String:fs.f_fstypename],NSWorkspace_RBfstypename,
-			[NSString stringWithUTF8String:fs.f_mntonname],NSWorkspace_RBmntonname,
-			nil];
-		if (strncmp(fs.f_mntfromname,"/dev/",5)==0) {
-// For a local volume,get the IO registry tree and search it for further info.
-			mach_port_t masterPort = 0;
-			io_iterator_t mediaIterator = 0;
-			kern_return_t kernResult = IOMasterPort(bootstrap_port,&masterPort);
-			if (kernResult==KERN_SUCCESS) {
-				CFMutableDictionaryRef classesToMatch = IOBSDNameMatching(masterPort,0,&fs.f_mntfromname[5]);
-				if (classesToMatch) {
-					kernResult = IOServiceGetMatchingServices(masterPort,classesToMatch,&mediaIterator);
-					if ((kernResult==KERN_SUCCESS)&&mediaIterator) {
-						io_object_t firstMedia = 0;
-						while ((firstMedia = IOIteratorNext(mediaIterator))) {
-							NSString* stuff = CheckParents(firstMedia,nil,result);
-							if (stuff) {
-								[result setObject:stuff forKey:NSWorkspace_RBdeviceinfo];
-							}
-							IOObjectRelease(firstMedia);
-						}
-					}
-				}
-			}
-			if (mediaIterator) {
-				IOObjectRelease(mediaIterator);
-			}
-			if (masterPort) {
-				mach_port_deallocate(mach_task_self(),masterPort);
-			}
-		}
-		//Don't need this for disk images, gets around warnings for some deprecated functions
-
-		/* else {
-// For a network volume, get the volume reference number and use to get the server URL.
-			FSRef ref;
-			if (FSPathMakeRef((const UInt8*)ccpath,&ref,NULL)==noErr) {
-				FSCatalogInfo info;
-				if (FSGetCatalogInfo(&ref,kFSCatInfoVolume,&info,NULL,NULL,NULL)==noErr) {
-					ParamBlockRec pb;
-					UInt16 vmisize = 0;
-					VolumeMountInfoHeaderPtr mountInfo = NULL;
-					pb.ioParam.ioCompletion = NULL;
-					pb.ioParam.ioNamePtr = NULL;
-					pb.ioParam.ioVRefNum = info.volume;
-					pb.ioParam.ioBuffer = (Ptr)&vmisize;
-					pb.ioParam.ioReqCount = sizeof(vmisize);
-					if ((PBGetVolMountInfoSize(&pb)==noErr)&&vmisize) {
-						mountInfo = (VolumeMountInfoHeaderPtr)malloc(vmisize);
-						if (mountInfo) {
-							pb.ioParam.ioBuffer = (Ptr)mountInfo;
-							pb.ioParam.ioReqCount = vmisize;
-							if (PBGetVolMountInfo(&pb)==noErr) {
-								NSString* url = nil;
-								switch (mountInfo->media) {
-								case AppleShareMediaType:
-									url = FormatAFPURL((AFPXVolMountInfoPtr)mountInfo,&from);
-									break;
-								case 'http':
-									url = from;
-									break;
-								case 'crbm':
-								case 'nfs_':
-								case 'cifs':
-									url = [NSString stringWithUTF8String:(char*)mountInfo+sizeof(VolumeMountInfoHeader)+sizeof(OSType)];
-									break;
-								}
-								if (url) {
-									[result setObject:url forKey:NSWorkspace_RBserverURL];
-								}
-							}
-						}
-						free(mountInfo);
-					}
-				}
-			}
-		}*/
-		[result setObject:from forKey:NSWorkspace_RBmntfromname];
-	}
-	return result;
-}
-
-@end
--- a/QTfrontend/SparkleAutoUpdater.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2008 Remko Troncon
- */
-
-#ifndef SPARKLEAUTOUPDATER_H
-#define SPARKLEAUTOUPDATER_H
-
-#include <QString>
-
-#include "AutoUpdater.h"
-
-class SparkleAutoUpdater : public AutoUpdater
-{
-    public:
-        SparkleAutoUpdater();
-        ~SparkleAutoUpdater();
-
-        void checkForUpdates();
-        void checkForUpdatesNow();
-
-    private:
-        class Private;
-        Private* d;
-};
-
-#endif
--- a/QTfrontend/SparkleAutoUpdater.mm	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-// see original example here http://el-tramo.be/blog/mixing-cocoa-and-qt
-
-#include "SparkleAutoUpdater.h"
-
-#include <Cocoa/Cocoa.h>
-#include <Sparkle/Sparkle.h>
-
-class SparkleAutoUpdater::Private
-{
-    public:
-        SUUpdater* updater;
-};
-
-SparkleAutoUpdater::SparkleAutoUpdater()
-{
-    d = new SparkleAutoUpdater::Private();
-
-    d->updater = [SUUpdater sharedUpdater];
-    [d->updater retain];
-}
-
-SparkleAutoUpdater::~SparkleAutoUpdater()
-{
-    [d->updater release];
-    delete d;
-}
-
-void SparkleAutoUpdater::checkForUpdates()
-{
-    [d->updater checkForUpdatesInBackground];
-}
-
-void SparkleAutoUpdater::checkForUpdatesNow()
-{
-    [d->updater checkForUpdates:NULL];
-}
--- a/QTfrontend/binds.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/binds.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -20,17 +20,15 @@
 
 const BindAction cbinds[BINDS_NUMBER] =
 {
-    {"+up",       "up",         QT_TRANSLATE_NOOP("binds", "up"),              QT_TRANSLATE_NOOP("binds (categories)", "Basic controls"), QT_TRANSLATE_NOOP("binds (descriptions)", "Move your hogs and aim:")},
+    {"+up",       "up",         QT_TRANSLATE_NOOP("binds", "up"),              QT_TRANSLATE_NOOP("binds (categories)", "Movement"), QT_TRANSLATE_NOOP("binds (descriptions)", "Hedgehog movement")},
     {"+left",     "left",       QT_TRANSLATE_NOOP("binds", "left"),            NULL, NULL},
     {"+right",    "right",      QT_TRANSLATE_NOOP("binds", "right"),           NULL, NULL},
     {"+down",     "down",       QT_TRANSLATE_NOOP("binds", "down"),            NULL, NULL},
     {"+precise",  "left_shift", QT_TRANSLATE_NOOP("binds", "precise aim"),     NULL, NULL},
     {"ljump",     "return",     QT_TRANSLATE_NOOP("binds", "long jump"),       NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Traverse gaps and obstacles by jumping:")},
     {"hjump",     "backspace",  QT_TRANSLATE_NOOP("binds", "high jump"),       NULL, NULL},
-    {"+attack",   "space",      QT_TRANSLATE_NOOP("binds", "attack"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Fire your selected weapon or trigger an utility item:")},
-    {"put",       "mousel",     QT_TRANSLATE_NOOP("binds", "put"),             NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or a target location under the cursor:")},
     {"switch",    "tab",        QT_TRANSLATE_NOOP("binds", "switch"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Switch your currently active hog (if possible):")},
-    {"ammomenu",  "mouser",     QT_TRANSLATE_NOOP("binds", "ammo menu"),       QT_TRANSLATE_NOOP("binds (categories)", "Weapon controls"), QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or utility item:")},
+    {"ammomenu",  "mouser",     QT_TRANSLATE_NOOP("binds", "ammo menu"),       QT_TRANSLATE_NOOP("binds (categories)", "Weapons"), QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or utility item:")},
     {"slot 1",    "f1",         QT_TRANSLATE_NOOP("binds", "slot 1"),          NULL, NULL},
     {"slot 2",    "f2",         QT_TRANSLATE_NOOP("binds", "slot 2"),          NULL, NULL},
     {"slot 3",    "f3",         QT_TRANSLATE_NOOP("binds", "slot 3"),          NULL, NULL},
@@ -46,7 +44,9 @@
     {"timer 3",   "3",          QT_TRANSLATE_NOOP("binds", "timer 3 sec"),     NULL, NULL},
     {"timer 4",   "4",          QT_TRANSLATE_NOOP("binds", "timer 4 sec"),     NULL, NULL},
     {"timer 5",   "5",          QT_TRANSLATE_NOOP("binds", "timer 5 sec"),     NULL, NULL},
-    {"findhh",    "h",          QT_TRANSLATE_NOOP("binds", "find hedgehog"),   QT_TRANSLATE_NOOP("binds (categories)", "Camera and cursor controls"), QT_TRANSLATE_NOOP("binds (descriptions)", "Move the camera to the active hog:")},
+    {"+attack",   "space",      QT_TRANSLATE_NOOP("binds", "attack"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Fire your selected weapon or trigger an utility item:")},
+    {"put",       "mousel",     QT_TRANSLATE_NOOP("binds", "put"),             NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or a target location under the cursor:")},
+    {"findhh",    "h",          QT_TRANSLATE_NOOP("binds", "find hedgehog"),   QT_TRANSLATE_NOOP("binds (categories)", "Camera"), QT_TRANSLATE_NOOP("binds (descriptions)", "Move the camera to the active hog:")},
     {"+cur_u",    "[8]",        QT_TRANSLATE_NOOP("binds", "up"),              NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Move the cursor or camera without using the mouse:")},
     {"+cur_l",    "[4]",        QT_TRANSLATE_NOOP("binds", "left"),            NULL, NULL},
     {"+cur_r",    "[6]",        QT_TRANSLATE_NOOP("binds", "right"),           NULL, NULL},
@@ -55,7 +55,7 @@
     {"zoomin",    "wheelup",    QT_TRANSLATE_NOOP("binds", "zoom in"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Modify the camera's zoom level:")},
     {"zoomout",   "wheeldown",  QT_TRANSLATE_NOOP("binds", "zoom out"),        NULL, NULL},
     {"zoomreset", "mousem",     QT_TRANSLATE_NOOP("binds", "reset zoom"),      NULL, NULL},
-    {"chat",      "t",          QT_TRANSLATE_NOOP("binds", "chat"),            QT_TRANSLATE_NOOP("binds (categories)", "Other"), QT_TRANSLATE_NOOP("binds (descriptions)", "Talk to your team or all participants:")},
+    {"chat",      "t",          QT_TRANSLATE_NOOP("binds", "chat"),            QT_TRANSLATE_NOOP("binds (categories)", "Miscellaneous"), QT_TRANSLATE_NOOP("binds (descriptions)", "Talk to your team or all participants:")},
     {"history",   "`",          QT_TRANSLATE_NOOP("binds", "chat history"),    NULL, NULL},
     {"pause",     "p",          QT_TRANSLATE_NOOP("binds", "pause"),           NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pause, continue or leave your game:")},
     {"quit",      "escape",     QT_TRANSLATE_NOOP("binds", "quit"),            NULL, NULL},
@@ -65,7 +65,7 @@
     {"mute",      "8",          QT_TRANSLATE_NOOP("binds", "mute audio"),      NULL, NULL},
     {"fullscr",   "f12",        QT_TRANSLATE_NOOP("binds", "change mode"),     NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle fullscreen mode:")},
     {"capture",   "c",          QT_TRANSLATE_NOOP("binds", "capture"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Take a screenshot:")},
-    {"rotmask",   "delete",     QT_TRANSLATE_NOOP("binds", "hedgehogs\ninfo"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")},
+    {"rotmask",   "delete",     QT_TRANSLATE_NOOP("binds", "hedgehog info"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")},
 #ifdef VIDEOREC
     {"record",    "r",          QT_TRANSLATE_NOOP("binds", "record"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
 #endif
--- a/QTfrontend/campaign.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/campaign.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -59,7 +59,7 @@
     QSettings campfile("physfs://Missions/Campaign/" + campaign + "/campaign.ini", QSettings::IniFormat, 0);
     campfile.setIniCodec("UTF-8");
     unsigned int mNum = campfile.value("MissionNum", 0).toInt();
-    
+
     QStringList missionList;
     for (unsigned int i = 0; i < mNum; i++)
     {
@@ -67,7 +67,7 @@
     }
     return missionList;
 }
-  
+
 unsigned int getCampProgress(QString & teamName, QString & campName)
 {
     QSettings teamfile(cfgdir->absolutePath() + "/Teams/" + teamName + ".hwt", QSettings::IniFormat, 0);
@@ -81,10 +81,3 @@
     campfile.setIniCodec("UTF-8");
     return campfile.value(QString("Mission %1/Script").arg(mNum)).toString();
 }
-
-
-    
-
-
-
-    
--- a/QTfrontend/game.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/game.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -22,13 +22,17 @@
 #include <QColor>
 #include <QStringListModel>
 #include <QTextStream>
+#include <utility>
 
+#include "hwform.h"
+#include "ui/page/pageoptions.h"
 #include "game.h"
 #include "hwconsts.h"
 #include "gameuiconfig.h"
 #include "gamecfgwidget.h"
 #include "teamselect.h"
 #include "proto.h"
+#include "binds.h"
 #include "campaign.h"
 
 #include <QTextStream>
@@ -73,6 +77,18 @@
     SetGameState(gsStopped);
 }
 
+void HWGame::addKeyBindings(QByteArray * buf)
+{
+    for(int i = 0; i < BINDS_NUMBER; i++)
+    {
+        QString value = config->value(QString("Binds/%1").arg(cbinds[i].action), cbinds[i].strbind).toString();
+        if (value.isEmpty() || value == "default") continue;
+
+        QString bind = QString("edbind " + value + " " + cbinds[i].action);
+        HWProto::addStringToBuffer(*buf, bind);
+    }
+}
+
 void HWGame::commonConfig()
 {
     QByteArray buf;
@@ -90,6 +106,8 @@
     }
     HWProto::addStringToBuffer(buf, gt);
 
+    addKeyBindings(&buf);
+
     buf += gamecfg->getFullConfig();
 
     if (m_pTeamSelWidget)
@@ -102,10 +120,11 @@
             HWProto::addStringToBuffer(buf, QString("eammreinf %1").arg(ammostr.mid(3 * cAmmoNumber, cAmmoNumber)));
             if(gamecfg->schemeData(15).toBool() || !gamecfg->schemeData(21).toBool()) HWProto::addStringToBuffer(buf, QString("eammstore"));
             HWProto::addStringListToBuffer(buf,
-                                           team.teamGameConfig(gamecfg->getInitHealth()));
+                                           team.teamGameConfig(gamecfg->getInitHealth(), config));
             ;
         }
     }
+
     RawSendIPC(buf);
 }
 
@@ -119,6 +138,8 @@
     QByteArray teamscfg;
     ThemeModel * themeModel = DataManager::instance().themeModel();
 
+    addKeyBindings(&teamscfg);
+
     HWProto::addStringToBuffer(teamscfg, "TL");
     HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
                                .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount()).data().toString() : "steel"));
@@ -132,7 +153,7 @@
     team1.setNumHedgehogs(4);
     HWNamegen::teamRandomNames(team1,true);
     HWProto::addStringListToBuffer(teamscfg,
-                                   team1.teamGameConfig(100));
+                                   team1.teamGameConfig(100, config));
 
     HWTeam team2;
     team2.setDifficulty(4);
@@ -142,7 +163,7 @@
         HWNamegen::teamRandomNames(team2,true);
     while(!team2.name().compare(team1.name()) || !team2.hedgehog(0).Hat.compare(team1.hedgehog(0).Hat));
     HWProto::addStringListToBuffer(teamscfg,
-                                   team2.teamGameConfig(100));
+                                   team2.teamGameConfig(100, config));
 
     HWProto::addStringToBuffer(teamscfg, QString("eammloadt %1").arg(cDefaultAmmoStore->mid(0, cAmmoNumber)));
     HWProto::addStringToBuffer(teamscfg, QString("eammprob %1").arg(cDefaultAmmoStore->mid(cAmmoNumber, cAmmoNumber)));
@@ -150,6 +171,7 @@
     HWProto::addStringToBuffer(teamscfg, QString("eammreinf %1").arg(cDefaultAmmoStore->mid(3 * cAmmoNumber, cAmmoNumber)));
     HWProto::addStringToBuffer(teamscfg, QString("eammstore"));
     HWProto::addStringToBuffer(teamscfg, QString("eammstore"));
+
     RawSendIPC(teamscfg);
 }
 
@@ -160,6 +182,8 @@
     HWProto::addStringToBuffer(traincfg, "eseed " + QUuid::createUuid().toString());
     HWProto::addStringToBuffer(traincfg, "escript " + training);
 
+    addKeyBindings(&traincfg);
+
     RawSendIPC(traincfg);
 }
 
@@ -171,6 +195,8 @@
 
     HWProto::addStringToBuffer(campaigncfg, "escript " + campaignScript);
 
+    addKeyBindings(&campaigncfg);
+
     RawSendIPC(campaigncfg);
 }
 
@@ -274,6 +300,16 @@
                 writeCampaignVar(msg.right(msg.size() - 3));
             break;
         }
+        case 'W':
+        {
+            // fetch new window resolution via IPC and save it in the settings
+            int size = msg.size();
+            QString newResolution = QString().append(msg.mid(2)).left(size - 4);
+            QStringList wh = newResolution.split('x');
+            config->Form->ui.pageOptions->windowWidthEdit->setText(wh[0]);
+            config->Form->ui.pageOptions->windowHeightEdit->setText(wh[1]);
+            break;
+        }
         default:
         {
             if (gameType == gtNet && !netSuspend)
@@ -313,7 +349,7 @@
 QStringList HWGame::getArguments()
 {
     QStringList arguments;
-    QRect resolution = config->vid_Resolution();
+    std::pair<QRect, QRect> resolutions = config->vid_ResolutionPair();
     QString nick = config->netNick().toUtf8().toBase64();
 
     arguments << "--internal"; //Must be passed as first argument
@@ -329,10 +365,14 @@
     arguments << QString::number(config->timerInterval());
     arguments << "--volume";
     arguments << QString::number(config->volume());
+    arguments << "--fullscreen-width";
+    arguments << QString::number(resolutions.first.width());
+    arguments << "--fullscreen-height";
+    arguments << QString::number(resolutions.first.height());
     arguments << "--width";
-    arguments << QString::number(resolution.width());
+    arguments << QString::number(resolutions.second.width());
     arguments << "--height";
-    arguments << QString::number(resolution.height());
+    arguments << QString::number(resolutions.second.height());
     arguments << "--raw-quality";
     arguments << QString::number(config->translateQuality());
     arguments << "--stereo";
--- a/QTfrontend/game.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/game.h	Sun Jan 27 00:28:57 2013 +0100
@@ -103,6 +103,7 @@
         TeamSelWidget* m_pTeamSelWidget;
         GameType gameType;
 
+        void addKeyBindings(QByteArray * buf);
         void commonConfig();
         void SendConfig();
         void SendQuickConfig();
--- a/QTfrontend/gameuiconfig.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/gameuiconfig.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -24,6 +24,7 @@
 #include <QStandardItemModel>
 #include <QNetworkProxy>
 #include <QNetworkProxyFactory>
+#include <utility>
 
 #include "gameuiconfig.h"
 #include "hwform.h"
@@ -34,6 +35,7 @@
 #include "fpsedit.h"
 #include "HWApplication.h"
 #include "DataManager.h"
+#include "SDL.h"
 
 
 const QNetworkProxy::ProxyType proxyTypesMap[] = {
@@ -52,6 +54,13 @@
 
     connect(Form->ui.pageOptions->CBFrontendMusic, SIGNAL(toggled(bool)), Form, SLOT(Music(bool)));
 
+    for(int i = 0; i < BINDS_NUMBER; i++)
+    {
+        m_binds.append(BindAction());
+        m_binds[i].action = cbinds[i].action;
+        m_binds[i].strbind = cbinds[i].strbind;
+    }
+
     //Form->resize(value("frontend/width", 640).toUInt(), value("frontend/height", 450).toUInt());
     resizeToConfigValues();
 
@@ -65,7 +74,7 @@
 {
     Form->ui.pageOptions->WeaponTooltip->setChecked(value("misc/weaponTooltips", true).toBool());
 
-    int t = Form->ui.pageOptions->CBResolution->findText(value("video/resolution").toString());
+    int t = Form->ui.pageOptions->CBResolution->findText(value("video/fullscreenResolution").toString());
     if (t < 0)
     {
         if (Form->ui.pageOptions->CBResolution->count() > 1)
@@ -74,6 +83,20 @@
             Form->ui.pageOptions->CBResolution->setCurrentIndex(0);
     }
     else Form->ui.pageOptions->CBResolution->setCurrentIndex(t);
+
+    // Default the windowed resolution to 5/6 of the screen size
+    int screenWidth = SDL_GetVideoInfo()->current_w * 5 / 6;
+    int screenHeight = SDL_GetVideoInfo()->current_h * 5 / 6;
+    QString widthStr; widthStr.setNum(screenWidth);
+    QString heightStr; heightStr.setNum(screenHeight);
+    QString wWidth = value("video/windowedWidth", widthStr).toString();
+    QString wHeight = value("video/windowedHeight", heightStr).toString();
+    // If left blank reset the resolution to the default
+    wWidth = (wWidth == "" ? widthStr : wWidth);
+    wHeight = (wHeight == "" ? heightStr : wHeight);
+    Form->ui.pageOptions->windowWidthEdit->setText(wWidth);
+    Form->ui.pageOptions->windowHeightEdit->setText(wHeight);
+
     Form->ui.pageOptions->CBResolution->setCurrentIndex((t < 0) ? 1 : t);
     Form->ui.pageOptions->CBFullscreen->setChecked(value("video/fullscreen", false).toBool());
     bool ffscr=value("frontend/fullscreen", false).toBool();
@@ -86,7 +109,7 @@
     Form->ui.pageOptions->CBFrontendSound->setChecked(value("frontend/sound", true).toBool());
     Form->ui.pageOptions->CBMusic->setChecked(value("audio/music", true).toBool());
     Form->ui.pageOptions->CBFrontendMusic->setChecked(value("frontend/music", true).toBool());
-    Form->ui.pageOptions->volumeBox->setValue(value("audio/volume", 100).toUInt());
+    Form->ui.pageOptions->SLVolume->setValue(value("audio/volume", 100).toUInt());
 
     QString netNick = value("net/nick", "").toString();
     Form->ui.pageOptions->editNetNick->setText(netNick);
@@ -133,7 +156,15 @@
     { // load colors
         QStandardItemModel * model = DataManager::instance().colorsModel();
         for(int i = model->rowCount() - 1; i >= 0; --i)
-            model->item(i)->setData(QColor(value(QString("colors/color%1").arg(i), model->item(i)->data().value<QColor>()).value<QColor>()));
+            model->item(i)->setData(value(QString("colors/color%1").arg(i), model->item(i)->data()));
+    }
+
+    { // load binds
+        for(int i = 0; i < BINDS_NUMBER; i++)
+        {
+            m_binds[i].strbind = value(QString("Binds/%1").arg(m_binds[i].action), cbinds[i].strbind).toString();
+            if (m_binds[i].strbind.isEmpty() || m_binds[i].strbind == "default") m_binds[i].strbind = cbinds[i].strbind;
+        }
     }
 }
 
@@ -199,7 +230,9 @@
 
 void GameUIConfig::SaveOptions()
 {
-    setValue("video/resolution", Form->ui.pageOptions->CBResolution->currentText());
+    setValue("video/fullscreenResolution", Form->ui.pageOptions->CBResolution->currentText());
+    setValue("video/windowedWidth", Form->ui.pageOptions->windowWidthEdit->text());
+    setValue("video/windowedHeight", Form->ui.pageOptions->windowHeightEdit->text());
     setValue("video/fullscreen", vid_Fullscreen());
 
     setValue("video/quality", Form->ui.pageOptions->SLQuality->value());
@@ -226,11 +259,11 @@
     setValue("frontend/sound", isFrontendSoundEnabled());
     setValue("audio/music", isMusicEnabled());
     setValue("frontend/music", isFrontendMusicEnabled());
-    setValue("audio/volume", Form->ui.pageOptions->volumeBox->value());
+    setValue("audio/volume", Form->ui.pageOptions->SLVolume->value());
 
     setValue("net/nick", netNick());
     if (netPasswordIsValid() && Form->ui.pageOptions->CBSavePassword->isChecked()) {
-    setPasswordHash(netPasswordHash());
+        setPasswordHash(netPasswordHash());
     }
     else if(!Form->ui.pageOptions->CBSavePassword->isChecked()) {
         clearPasswordHash();
@@ -319,16 +352,28 @@
     return Form->ui.pageOptions->CBLanguage->itemData(Form->ui.pageOptions->CBLanguage->currentIndex()).toString();
 }
 
-QRect GameUIConfig::vid_Resolution()
-{
-    QRect result(0, 0, 640, 480);
+std::pair<QRect, QRect> GameUIConfig::vid_ResolutionPair() {
+    // returns a pair of both the fullscreen and the windowed resolution
+    QRect full(0, 0, 640, 480);
+    QRect windowed(0, 0, 640, 480);
     QStringList wh = Form->ui.pageOptions->CBResolution->currentText().split('x');
     if (wh.size() == 2)
     {
-        result.setWidth(wh[0].toInt());
-        result.setHeight(wh[1].toInt());
+        full.setWidth(wh[0].toInt());
+        full.setHeight(wh[1].toInt());
     }
-    return result;
+    windowed.setWidth(Form->ui.pageOptions->windowWidthEdit->text().toInt());
+    windowed.setHeight(Form->ui.pageOptions->windowHeightEdit->text().toInt());
+    return std::make_pair(full, windowed);
+}
+
+QRect GameUIConfig::vid_Resolution()
+{
+    std::pair<QRect, QRect> result = vid_ResolutionPair();
+    if(Form->ui.pageOptions->CBFullscreen->isChecked())
+        return result.first;
+    else
+        return result.second;
 }
 
 bool GameUIConfig::vid_Fullscreen()
@@ -536,7 +581,7 @@
 
 quint8 GameUIConfig::volume()
 {
-    return Form->ui.pageOptions->volumeBox->value() * 128 / 100;
+    return Form->ui.pageOptions->SLVolume->value() * 128 / 100;
 }
 
 QString GameUIConfig::AVFormat()
@@ -581,3 +626,16 @@
 {
     return Form->ui.pageOptions->checkRecordAudio->isChecked();
 }
+
+// Gets a bind for a bindID
+QString GameUIConfig::bind(int bindID)
+{
+    return m_binds[bindID].strbind;
+}
+
+// Sets a bind for a bindID and saves it
+void GameUIConfig::setBind(int bindID, QString & strbind)
+{
+    m_binds[bindID].strbind = strbind;
+    setValue(QString("Binds/%1").arg(m_binds[bindID].action), strbind);
+}
--- a/QTfrontend/gameuiconfig.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/gameuiconfig.h	Sun Jan 27 00:28:57 2013 +0100
@@ -23,6 +23,9 @@
 #include <QStringList>
 #include <QRect>
 #include <QEvent>
+#include <QList>
+#include <utility>
+#include "binds.h"
 
 class HWForm;
 class QSettings;
@@ -36,6 +39,7 @@
         GameUIConfig(HWForm * FormWidgets, const QString & fileName);
         QStringList GetTeamsList();
         QRect vid_Resolution();
+        std::pair<QRect, QRect> vid_ResolutionPair();
         bool vid_Fullscreen();
         quint32 translateQuality();
         bool isSoundEnabled();
@@ -64,6 +68,8 @@
         void resizeToConfigValues();
         quint32 stereoMode() const;
         void setValue(const QString & key, const QVariant & value);
+        QString bind(int bindID);
+        void setBind(int bindID, QString & strbind);
 
         QString AVFormat();
         QString videoCodec();
@@ -91,7 +97,8 @@
     private:
         bool netPasswordIsValid();
         bool eventFilter(QObject *object, QEvent *event);
-    QString temphash;
+        QString temphash;
+        QList<BindAction> m_binds;
 };
 
 #endif
--- a/QTfrontend/hedgewars.qrc	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/hedgewars.qrc	Sun Jan 27 00:28:57 2013 +0100
@@ -45,6 +45,8 @@
         <file>res/LocalPlay.png</file>
         <file>res/NetworkPlay.png</file>
         <file>res/NetworkPlayDisabled.png</file>
+        <file>res/audio.png</file>
+        <file>res/camera.png</file>
         <file>res/Settings.png</file>
         <file>res/dropdown.png</file>
         <file>res/new.png</file>
@@ -103,6 +105,7 @@
         <file>res/iconRope.png</file>
         <file>res/dice.png</file>
         <file>res/Star.png</file>
+        <file>res/inverse-corner-bl.png</file>
         <file>res/Flake.png</file>
         <file>res/Egg.png</file>
         <file>res/Confetti.png</file>
@@ -130,6 +133,7 @@
         <file>res/StatsMostSelfDamage.png</file>
         <file>res/StatsSelfKilled.png</file>
         <file>res/StatsSkipped.png</file>
+        <file>res/Start.png</file>
         <file>res/mapRandom.png</file>
         <file>res/mapMaze.png</file>
         <file>res/mapMissing.png</file>
@@ -146,6 +150,8 @@
         <file>res/chat/serveradmin_gray.png</file>
         <file>res/chat/lamp_off.png</file>
         <file>res/chat/ingame.png</file>
-	<file>res/html/about.html</file>
+        <file>res/splash.png</file>
+        <file>res/html/about.html</file>
+        <file>res/xml/tips.xml</file>
     </qresource>
 </RCC>
--- a/QTfrontend/hwform.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/hwform.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -21,6 +21,7 @@
 #include <QTextStream>
 #include <QMessageBox>
 #include <QPushButton>
+#include <QSpinBox>
 #include <QListWidget>
 #include <QStackedLayout>
 #include <QLineEdit>
@@ -72,7 +73,6 @@
 #include "pagemultiplayer.h"
 #include "pagenet.h"
 #include "pagemain.h"
-#include "pagefeedback.h"
 #include "pagenetserver.h"
 #include "pagedrawmap.h"
 #include "pagegamestats.h"
@@ -95,7 +95,9 @@
 #include "roomslistmodel.h"
 #include "recorder.h"
 #include "playerslistmodel.h"
+#include "feedbackdialog.h"
 
+#include "MessageDialog.h"
 #include "DataManager.h"
 #include "AutoUpdater.h"
 
@@ -155,9 +157,9 @@
     ui.pageRoomsList->setSettings(config);
     ui.pageNetGame->chatWidget->setSettings(config);
     ui.pageRoomsList->chatWidget->setSettings(config);
+    ui.pageOptions->setConfig(config);
 #ifdef VIDEOREC
     ui.pageVideos->init(config);
-    ui.pageOptions->setConfig(config);
 #endif
 
 #ifdef __APPLE__
@@ -213,8 +215,7 @@
     connect(ui.pageMain->BtnSetup, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnSetup, ID_PAGE_SETUP);
 
-    connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
-    pageSwitchMapper->setMapping(ui.pageMain->BtnFeedback, ID_PAGE_FEEDBACK);
+    connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), this, SLOT(showFeedbackDialog()));
 
     connect(ui.pageMain->BtnInfo, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnInfo, ID_PAGE_INFO);
@@ -231,8 +232,6 @@
     //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
     //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
 
-    connect(ui.pageFeedback->BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
-
     connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
 
     connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
@@ -351,6 +350,7 @@
     }
 
     PagesStack.push(ID_PAGE_MAIN);
+    ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageEnter();
     GoBack();
 }
 
@@ -553,71 +553,51 @@
     GoToPage(ID_PAGE_VIDEOS);
 }
 
+//TODO: maybe find a better place for this?
+QString HWForm::stringifyPageId(quint32 id)
+{
+    QString pageName;
+    switch (id)
+    {
+      case ID_PAGE_SETUP_TEAM :   pageName = "PAGE_SETUP_TEAM"; break;
+      case ID_PAGE_SETUP :        pageName = "PAGE_SETUP"; break;
+      case ID_PAGE_MULTIPLAYER :  pageName = "PAGE_MULTIPLAYER"; break;
+      case ID_PAGE_DEMOS :        pageName = "PAGE_DEMOS"; break;
+      case ID_PAGE_NET :          pageName = "PAGE_NET"; break;
+      case ID_PAGE_NETGAME :      pageName = "PAGE_NETGAME"; break;
+      case ID_PAGE_INFO :         pageName = "PAGE_INFO"; break;
+      case ID_PAGE_MAIN :         pageName = "PAGE_MAIN"; break;
+      case ID_PAGE_GAMESTATS :    pageName = "PAGE_GAMESTATS"; break;
+      case ID_PAGE_SINGLEPLAYER : pageName = "PAGE_SINGLEPLAYER"; break;
+      case ID_PAGE_TRAINING :     pageName = "PAGE_TRAINING"; break;
+      case ID_PAGE_SELECTWEAPON : pageName = "PAGE_SELECTWEAPON"; break;
+      case ID_PAGE_NETSERVER :    pageName = "PAGE_NETSERVER"; break;
+      case ID_PAGE_INGAME :       pageName = "PAGE_INGAME"; break;
+      case ID_PAGE_ROOMSLIST :    pageName = "PAGE_ROOMSLIST"; break;
+      case ID_PAGE_CONNECTING :   pageName = "PAGE_CONNECTING"; break;
+      case ID_PAGE_SCHEME :       pageName = "PAGE_SCHEME"; break;
+      case ID_PAGE_ADMIN :        pageName = "PAGE_ADMIN"; break;
+      case ID_PAGE_CAMPAIGN :     pageName = "PAGE_CAMPAIGN"; break;
+      case ID_PAGE_DRAWMAP :      pageName = "PAGE_DRAWMAP"; break;
+      case ID_PAGE_DATADOWNLOAD : pageName = "PAGE_DATADOWNLOAD"; break;
+      case ID_PAGE_VIDEOS :       pageName = "PAGE_VIDEOS"; break;
+      case MAX_PAGE :             pageName = "MAX_PAGE"; break;
+      default :                   pageName = "UNKNOWN_PAGE"; break;
+    }
+    return pageName;
+}
+
 void HWForm::OnPageShown(quint8 id, quint8 lastid)
 {
 #ifdef USE_XFIRE
     updateXfire();
 #endif
 
-    QString openPrefix = "Debug:   (PAGE_OPENED: ";
-    QString openSuffix = ")";
-    QString closePrefix = "Debug:   (PAGE_LEFT: ";
-    QString closeSuffix = ")";
+    qDebug("Leaving %s, entering %s", qPrintable(stringifyPageId(lastid)), qPrintable(stringifyPageId(id)));
 
-    switch (lastid) { //Print the id of the page we're leaving
-      case ID_PAGE_SETUP_TEAM : qDebug("%sPAGE_SETUP_TEAM%s", qPrintable(closePrefix), qPrintable(closeSuffix));    break;
-      case ID_PAGE_SETUP :      qDebug("%sPAGE_SETUP%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_MULTIPLAYER :    qDebug("%sPAGE_MULTIPLAYER%s", qPrintable(closePrefix), qPrintable(closeSuffix));   break;
-      case ID_PAGE_DEMOS :      qDebug("%sPAGE_DEMOS%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_NET :        qDebug("%sPAGE_NET%s", qPrintable(closePrefix), qPrintable(closeSuffix));       break;
-      case ID_PAGE_NETGAME :        qDebug("%sPAGE_NETGAME%s", qPrintable(closePrefix), qPrintable(closeSuffix));       break;
-      case ID_PAGE_INFO :       qDebug("%sPAGE_INFO%s", qPrintable(closePrefix), qPrintable(closeSuffix));      break;
-      case ID_PAGE_MAIN :       qDebug("%sPAGE_MAIN%s", qPrintable(closePrefix), qPrintable(closeSuffix));      break;
-      case ID_PAGE_GAMESTATS :      qDebug("%sPAGE_GAMESTATS%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_SINGLEPLAYER :   qDebug("%sPAGE_SINGLEPLAYER%s", qPrintable(closePrefix), qPrintable(closeSuffix));  break;
-      case ID_PAGE_TRAINING :       qDebug("%sPAGE_TRAINING%s", qPrintable(closePrefix), qPrintable(closeSuffix));      break;
-      case ID_PAGE_SELECTWEAPON :   qDebug("%sPAGE_SELECTWEAPON%s", qPrintable(closePrefix), qPrintable(closeSuffix));  break;
-      case ID_PAGE_NETSERVER :      qDebug("%sPAGE_NETSERVER%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_INGAME :     qDebug("%sPAGE_INGAME%s", qPrintable(closePrefix), qPrintable(closeSuffix));        break;
-      case ID_PAGE_ROOMSLIST :      qDebug("%sPAGE_ROOMSLIST%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_CONNECTING : qDebug("%sPAGE_CONNECTING%s", qPrintable(closePrefix), qPrintable(closeSuffix));    break;
-      case ID_PAGE_SCHEME :     qDebug("%sPAGE_SCHEME%s", qPrintable(closePrefix), qPrintable(closeSuffix));        break;
-      case ID_PAGE_ADMIN :      qDebug("%sPAGE_ADMIN%s", qPrintable(closePrefix), qPrintable(closeSuffix));     break;
-      case ID_PAGE_CAMPAIGN :       qDebug("%sPAGE_CAMPAIGN%s", qPrintable(closePrefix), qPrintable(closeSuffix));      break;
-      case ID_PAGE_DRAWMAP :        qDebug("%sPAGE_DRAWMAP%s", qPrintable(closePrefix), qPrintable(closeSuffix));       break;
-      case ID_PAGE_DATADOWNLOAD :   qDebug("%sPAGE_DATADOWNLOAD%s", qPrintable(closePrefix), qPrintable(closeSuffix));  break;
-      case ID_PAGE_FEEDBACK :       qDebug("%sPAGE_FEEDBACK%s", qPrintable(closePrefix), qPrintable(closeSuffix));      break;
-      case ID_PAGE_VIDEOS :     qDebug("%sPAGE_VIDEOS%s", qPrintable(closePrefix), qPrintable(closeSuffix));        break;
-      case MAX_PAGE :           qDebug("%sMAX_PAGE%s", qPrintable(closePrefix), qPrintable(closeSuffix));       break;
-      default :         qDebug("%sUNKNOWN PAGE%s", qPrintable(closePrefix), qPrintable(closeSuffix));       break;
-    } //end switch(lastid)
-    switch (id) { //Print the id of the opened page
-      case ID_PAGE_SETUP_TEAM : qDebug("%sPAGE_SETUP_TEAM%s", qPrintable(openPrefix), qPrintable(openSuffix));      break;
-      case ID_PAGE_SETUP :      qDebug("%sPAGE_SETUP%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_MULTIPLAYER :    qDebug("%sPAGE_MULTIPLAYER%s", qPrintable(openPrefix), qPrintable(openSuffix));     break;
-      case ID_PAGE_DEMOS :      qDebug("%sPAGE_DEMOS%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_NET :        qDebug("%sPAGE_NET%s", qPrintable(openPrefix), qPrintable(openSuffix));         break;
-      case ID_PAGE_NETGAME :        qDebug("%sPAGE_NETGAME%s", qPrintable(openPrefix), qPrintable(openSuffix));     break;
-      case ID_PAGE_INFO :       qDebug("%sPAGE_INFO%s", qPrintable(openPrefix), qPrintable(openSuffix));        break;
-      case ID_PAGE_MAIN :       qDebug("%sPAGE_MAIN%s", qPrintable(openPrefix), qPrintable(openSuffix));        break;
-      case ID_PAGE_GAMESTATS :      qDebug("%sPAGE_GAMESTATS%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_SINGLEPLAYER :   qDebug("%sPAGE_SINGLEPLAYER%s", qPrintable(openPrefix), qPrintable(openSuffix));    break;
-      case ID_PAGE_TRAINING :       qDebug("%sPAGE_TRAINING%s", qPrintable(openPrefix), qPrintable(openSuffix));        break;
-      case ID_PAGE_SELECTWEAPON :   qDebug("%sPAGE_SELECTWEAPON%s", qPrintable(openPrefix), qPrintable(openSuffix));    break;
-      case ID_PAGE_NETSERVER :      qDebug("%sPAGE_NETSERVER%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_INGAME :     qDebug("%sPAGE_INGAME%s", qPrintable(openPrefix), qPrintable(openSuffix));      break;
-      case ID_PAGE_ROOMSLIST :      qDebug("%sPAGE_ROOMSLIST%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_CONNECTING : qDebug("%sPAGE_CONNECTING%s", qPrintable(openPrefix), qPrintable(openSuffix));      break;
-      case ID_PAGE_SCHEME :     qDebug("%sPAGE_SCHEME%s", qPrintable(openPrefix), qPrintable(openSuffix));      break;
-      case ID_PAGE_ADMIN :      qDebug("%sPAGE_ADMIN%s", qPrintable(openPrefix), qPrintable(openSuffix));       break;
-      case ID_PAGE_CAMPAIGN :       qDebug("%sPAGE_CAMPAIGN%s", qPrintable(openPrefix), qPrintable(openSuffix));        break;
-      case ID_PAGE_DRAWMAP :        qDebug("%sPAGE_DRAWMAP%s", qPrintable(openPrefix), qPrintable(openSuffix));     break;
-      case ID_PAGE_DATADOWNLOAD :   qDebug("%sPAGE_DATADOWNLOAD%s", qPrintable(openPrefix), qPrintable(openSuffix));    break;
-      case ID_PAGE_FEEDBACK :       qDebug("%sPAGE_FEEDBACK%s", qPrintable(openPrefix), qPrintable(openSuffix));        break;
-      case ID_PAGE_VIDEOS :     qDebug("%sPAGE_VIDEOS%s", qPrintable(openPrefix), qPrintable(openSuffix));      break;
-      case MAX_PAGE :           qDebug("%sMAX_PAGE%s", qPrintable(openPrefix), qPrintable(openSuffix));         break;
-      default :         qDebug("%sUNKNOWN PAGE%s", qPrintable(openPrefix), qPrintable(openSuffix));     break;
-    } //end switch(id)
+    // pageEnter and pageLeave events
+    ((AbstractPage*)ui.Pages->widget(lastid))->triggerPageLeave();
+    ((AbstractPage*)ui.Pages->widget(id))->triggerPageEnter();
 
     if (id == ID_PAGE_DATADOWNLOAD)
     {
@@ -690,20 +670,11 @@
             curTeamSelWidget->resetPlayingTeams(teamsList);
         }
     }
-    else if (id == ID_PAGE_GAMESTATS)
-    {
-        ui.pageGameStats->renderStats();
-    }
 
     if (id == ID_PAGE_MAIN)
     {
         ui.pageOptions->setTeamOptionsEnabled(true);
     }
-
-    if (id == ID_PAGE_FEEDBACK)
-    {
-        ui.pageFeedback->LoadCaptchaImage();
-    }
 }
 
 void HWForm::GoToPage(int id)
@@ -723,6 +694,7 @@
     unnecessary.
    */
 
+
 #if (QT_VERSION >= 0x040600)
     if (!stopAnim)
     {
@@ -790,6 +762,7 @@
     int curid = ui.Pages->currentIndex();
     if (curid == ID_PAGE_MAIN)
     {
+        ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageLeave();
         if (!ui.pageVideos->tryQuit(this))
             return;
         stopAnim = true;
@@ -975,18 +948,8 @@
 
 void HWForm::DeleteTeam(const QString & teamName)
 {
-    QMessageBox reallyDeleteMsg(this);
-    reallyDeleteMsg.setIcon(QMessageBox::Question);
-    reallyDeleteMsg.setWindowTitle(QMessageBox::tr("Teams - Are you sure?"));
-    reallyDeleteMsg.setText(QMessageBox::tr("Do you really want to delete the team '%1'?").arg(teamName));
-    reallyDeleteMsg.setWindowModality(Qt::WindowModal);
-    reallyDeleteMsg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
-
-    if (reallyDeleteMsg.exec() == QMessageBox::Ok)
-    {
-        ui.pageEditTeam->deleteTeam(teamName);
-        UpdateTeamsLists();
-    }
+    ui.pageEditTeam->deleteTeam(teamName);
+    UpdateTeamsLists();
 }
 
 void HWForm::DeleteScheme()
@@ -994,7 +957,7 @@
     ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
     if (ui.pageOptions->SchemesName->currentIndex() < ammoSchemeModel->numberOfDefaultSchemes)
     {
-        ShowErrorMessage(QMessageBox::tr("Cannot delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText()));
+        MessageDialog::ShowErrorMessage(QMessageBox::tr("Cannot delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText()), this);
     }
     else
     {
@@ -1020,7 +983,7 @@
     QListWidgetItem * curritem = ui.pagePlayDemo->DemosList->currentItem();
     if (!curritem)
     {
-        ShowErrorMessage(QMessageBox::tr("Please select a record from the list"));
+        MessageDialog::ShowErrorMessage(QMessageBox::tr("Please select a record from the list"), this);
         return;
     }
     CreateGame(0, 0, 0);
@@ -1048,6 +1011,7 @@
 
 void HWForm::NetPassword(const QString & nick)
 {
+    Q_UNUSED(nick);
     //Get hashes
     QString hash =  config->passwordHash();
     QString temphash =  config->tempHash();
@@ -1104,6 +1068,8 @@
 
 void HWForm::NetNickNotRegistered(const QString & nick)
 {
+    Q_UNUSED(nick);
+
     QMessageBox noRegMsg(this);
     noRegMsg.setIcon(QMessageBox::Information);
     noRegMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Nick not registered"));
@@ -1133,7 +1099,7 @@
     bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
     GoBack();
         if (retry) {
-           NetConnectOfficialServer();
+            NetConnectOfficialServer();
         }
         return;
     }
@@ -1194,7 +1160,7 @@
     switch (ui.Pages->currentIndex())
     {
         case ID_PAGE_INGAME:
-            ShowErrorMessage(errmsg);
+            MessageDialog::ShowErrorMessage(errmsg, this);
             // no break
         case ID_PAGE_NETGAME:
             ui.pageNetGame->displayError(errmsg);
@@ -1214,6 +1180,8 @@
 
 void HWForm::_NetConnect(const QString & hostName, quint16 port, QString nick)
 {
+    Q_UNUSED(nick);
+
     if(hwnet)
     {
         hwnet->Disconnect();
@@ -1291,6 +1259,8 @@
             hwnet, SLOT(infoPlayer(const QString&)));
     connect(ui.pageNetGame->chatWidget, SIGNAL(follow(const QString&)),
             hwnet, SLOT(followPlayer(const QString&)));
+    connect(ui.pageNetGame->chatWidget, SIGNAL(consoleCommand(const QString&)),
+            hwnet, SLOT(consoleCommand(const QString&)));
     connect(ui.pageRoomsList->chatWidget, SIGNAL(kick(const QString&)),
             hwnet, SLOT(kickPlayer(const QString&)));
     connect(ui.pageRoomsList->chatWidget, SIGNAL(ban(const QString&)),
@@ -1299,6 +1269,8 @@
             hwnet, SLOT(infoPlayer(const QString&)));
     connect(ui.pageRoomsList->chatWidget, SIGNAL(follow(const QString&)),
             hwnet, SLOT(followPlayer(const QString&)));
+    connect(ui.pageRoomsList->chatWidget, SIGNAL(consoleCommand(const QString&)),
+            hwnet, SLOT(consoleCommand(const QString&)));
 
 // chatting
     connect(ui.pageRoomsList->chatWidget, SIGNAL(chatLine(const QString&)),
@@ -1367,73 +1339,77 @@
     QString nickname = config->value("net/nick", "").toString();
     QString password;
 
-    if (nickname.isEmpty() || hash.isEmpty()) { //if something from login is missing, start dialog loop
-
+    //if something from login is missing, start dialog loop
+    if (nickname.isEmpty() || hash.isEmpty())
+    {
         while (nickname.isEmpty() || (hash.isEmpty() && temphash.isEmpty())) //while a nickname, or both hashes are missing
         {
-        //open dialog
+            //open dialog
             HWPasswordDialog * pwDialog = new HWPasswordDialog(this);
+            // make the "new account" button dialog open a browser with the registration page
+            connect(pwDialog->pbNewAccount, SIGNAL(clicked()), this, SLOT(openRegistrationPage()));
             pwDialog->cbSave->setChecked(config->value("net/savepassword", true).toBool());
 
-        //if nickname is present, put it into the field
-        if (!nickname.isEmpty()) {
-            pwDialog->leNickname->setText(nickname);
-            pwDialog->lePassword->setFocus();
-        }
+            //if nickname is present, put it into the field
+            if (!nickname.isEmpty()) {
+                pwDialog->leNickname->setText(nickname);
+                pwDialog->lePassword->setFocus();
+            }
+
+            //if dialog close, create an error message
+            if (pwDialog->exec() != QDialog::Accepted) {
+                delete pwDialog;
+                GoBack();
+                return;
+            }
+
+            //set nick and pass from the dialog
+            nickname = pwDialog->leNickname->text();
+            password = pwDialog->lePassword->text();
 
-        //if dialog close, create an error message
-        if (pwDialog->exec() != QDialog::Accepted) {
-            delete pwDialog;
-            GoBack();
-            return;
-        }
+            //check the nickname variable
+            if (nickname.isEmpty()) {
+                int retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
+                GoBack();
+                delete pwDialog;
+                if (retry) {
+                    NetConnectOfficialServer();
+                }
+                return;
+            }
+
+            if (!password.isEmpty()) {
+                //calculate temphash and set it into config
+                temphash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex();
+                config->setTempHash(temphash);
 
-        //set nick and pass from the dialog
-        nickname = pwDialog->leNickname->text();
-        password = pwDialog->lePassword->text();
+                //if user wants to save password
+                bool save = pwDialog->cbSave->isChecked();
+                config->setValue("net/savepassword", save);
+                if (save) // user wants to save password
+                {
+                    ui.pageOptions->CBSavePassword->setChecked(true);
+                    config->setPasswordHash(temphash);
+                }
+            }
+            else {
+                delete pwDialog;
+                config->setValue("net/nick", nickname);
+                config->updNetNick();
+                config->clearPasswordHash();
+                break;
+            }
 
-        //check the nickname variable
-        if (nickname.isEmpty()) {
-            int retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
-            GoBack();
             delete pwDialog;
-            if (retry) {
-                NetConnectOfficialServer();
-            }
-            return;
-        }
 
-        if (!password.isEmpty()) {
-            //calculate temphash and set it into config
-            temphash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex();
-            config->setTempHash(temphash);
-
-            //if user wants to save password
-            bool save = pwDialog->cbSave->isChecked();
-            config->setValue("net/savepassword", save);
-            if (save) // user wants to save password
-            {
-                config->setPasswordHash(temphash);
-            }
-        }
-        else {
-            delete pwDialog;
+            //update nickname
             config->setValue("net/nick", nickname);
             config->updNetNick();
-            config->clearPasswordHash();
-            break;
-        }
 
-        delete pwDialog;
-
-        //update nickname
-        config->setValue("net/nick", nickname);
-        config->updNetNick();
-
-        //and all the variables
-        hash = config->passwordHash();
-        temphash = config->tempHash();
-        nickname = config->value("net/nick", "").toString();
+            //and all the variables
+            hash = config->passwordHash();
+            temphash = config->tempHash();
+            nickname = config->value("net/nick", "").toString();
         }
     }
 
@@ -1469,7 +1445,7 @@
     pnetserver = new HWNetServer;
     if (!pnetserver->StartServer(ui.pageNetServer->sbPort->value()))
     {
-        ShowErrorMessage(QMessageBox::tr("Unable to start server"));
+        MessageDialog::ShowErrorMessage(QMessageBox::tr("Unable to start server"), this);
 
         delete pnetserver;
         pnetserver = 0;
@@ -1528,7 +1504,7 @@
     if (hwnet)
     {
         QString errorStr = QMessageBox::tr("Connection to server is lost") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Quit reason: ") + '"' + reason +'"'));
-        ShowErrorMessage(errorStr);
+        MessageDialog::ShowErrorMessage(errorStr, this);
     }
 
     while (ui.Pages->currentIndex() != ID_PAGE_NET
@@ -1634,16 +1610,6 @@
     m_lastDemo = QByteArray();
 }
 
-void HWForm::ShowErrorMessage(const QString & msg)
-{
-    QMessageBox msgMsg(this);
-    msgMsg.setIcon(QMessageBox::Warning);
-    msgMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Error"));
-    msgMsg.setText(msg);
-    msgMsg.setWindowModality(Qt::WindowModal);
-    msgMsg.exec();
-}
-
 void HWForm::GetRecord(RecordType type, const QByteArray & record)
 {
     if (type != rtNeither)
@@ -1676,7 +1642,7 @@
 
         QFile demofile(filename);
         if (!demofile.open(QIODevice::WriteOnly))
-            ShowErrorMessage(tr("Cannot save record to file %1").arg(filename));
+            MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filename), this);
         else
         {
             demofile.write(demo);
@@ -1747,7 +1713,7 @@
 
 void HWForm::NetGameChangeStatus(bool isMaster)
 {
-    ui.pageNetGame->pGameCFG->setEnabled(isMaster);
+    ui.pageNetGame->pGameCFG->setMaster(isMaster);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(isMaster);
 
     if (isMaster)
@@ -1762,21 +1728,22 @@
     ui.pageNetGame->restrictJoins->setChecked(false);
     ui.pageNetGame->restrictTeamAdds->setChecked(false);
     ui.pageNetGame->pGameCFG->GameSchemes->setModel(ammoSchemeModel);
-    ui.pageNetGame->pGameCFG->setEnabled(true);
+    ui.pageNetGame->pGameCFG->setMaster(true);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
 
     if (hwnet)
     {
         // disconnect connections first to ensure their inexistance and not to connect twice
-        ui.pageNetGame->BtnStart->disconnect(hwnet);
+        ui.pageNetGame->BtnStart->disconnect(this);
         ui.pageNetGame->BtnUpdate->disconnect(hwnet);
+        ui.pageNetGame->leRoomName->disconnect(hwnet);
         ui.pageNetGame->restrictJoins->disconnect(hwnet);
         ui.pageNetGame->restrictTeamAdds->disconnect(hwnet);
         ui.pageNetGame->disconnect(hwnet, SLOT(updateRoomName(const QString&)));
 
         ui.pageNetGame->setRoomName(hwnet->getRoom());
 
-        connect(ui.pageNetGame->BtnStart, SIGNAL(clicked()), hwnet, SLOT(startGame()));
+        connect(ui.pageNetGame->BtnStart, SIGNAL(clicked()), this, SLOT(startGame()));
         connect(ui.pageNetGame, SIGNAL(askForUpdateRoomName(const QString &)), hwnet, SLOT(updateRoomName(const QString &)));
         connect(ui.pageNetGame->restrictJoins, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictJoins()));
         connect(ui.pageNetGame->restrictTeamAdds, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictTeamAdds()));
@@ -1790,7 +1757,7 @@
 
 void HWForm::NetGameSlave()
 {
-    ui.pageNetGame->pGameCFG->setEnabled(false);
+    ui.pageNetGame->pGameCFG->setMaster(false);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(false);
 
     if (hwnet)
@@ -1799,6 +1766,8 @@
         connect(hwnet, SIGNAL(netSchemeConfig(QStringList &)), netAmmo, SLOT(setNetSchemeConfig(QStringList &)));
         ui.pageNetGame->pGameCFG->GameSchemes->setModel(netAmmo);
 
+        ui.pageNetGame->setRoomName(hwnet->getRoom());
+
         ui.pageNetGame->pGameCFG->GameSchemes->view()->disconnect(hwnet);
         connect(hwnet, SIGNAL(netSchemeConfig(QStringList &)),
                 this, SLOT(selectFirstNetScheme()));
@@ -1893,11 +1862,13 @@
     userPrefix = userPrefix.replace("/","\\");
 #endif
 
-    QRect resolution = config->vid_Resolution();
+    std::pair<QRect, QRect> resolutions = config->vid_ResolutionPair();
     return QString("--prefix " + prefix
                    + " --user-prefix " + userPrefix
-                   + " --width " + QString::number(resolution.width())
-                   + " --height " + QString::number(resolution.height())
+                   + " --fullscreen-width " + QString::number(resolutions.first.width())
+                   + " --fullscreen-height " + QString::number(resolutions.first.height())
+                   + " --width " + QString::number(resolutions.second.width())
+                   + " --height " + QString::number(resolutions.second.height())
                    + " --volume " + QString::number(config->volume())
                    + (config->isMusicEnabled() ? "" : " --nomusic")
                    + (config->isSoundEnabled() ? "" : " --nosound")
@@ -1955,7 +1926,12 @@
         infoMsg.exec();
     }
     else
-        ShowErrorMessage(QMessageBox::tr("File association failed."));
+        MessageDialog::ShowErrorMessage(QMessageBox::tr("File association failed."), this);
+}
+
+void HWForm::openRegistrationPage()
+{
+    QDesktopServices::openUrl(QUrl("http://www.hedgewars.org/user/register"));
 }
 
 void HWForm::saveDemoWithCustomName()
@@ -1974,7 +1950,7 @@
                 QFile demofile(filePath);
                 ok = demofile.open(QIODevice::WriteOnly);
                 if (!ok)
-                    ShowErrorMessage(tr("Cannot save record to file %1").arg(filePath));
+                    MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filePath), this);
                 else
                 {
                     ok = -1 != demofile.write(m_lastDemo);
@@ -1986,76 +1962,28 @@
     }
 }
 
-void HWForm::SendFeedback()
-{
-    // Get form data
-
-    QString summary = ui.pageFeedback->summary->text();
-    QString description = ui.pageFeedback->description->toPlainText();
-    QString email = ui.pageFeedback->email->text();
-    QString captchaCode = ui.pageFeedback->captcha_code->text();
-    QString captchaID = QString::number(ui.pageFeedback->captchaID);
-    QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
-
-    if (summary.isEmpty() || description.isEmpty())
-    {
-        ShowErrorMessage(QMessageBox::tr("Please fill out all fields. Email is optional."));
-        return;
-    }
-
-    // Submit issue to PHP script
 
-    QByteArray body;
-    body.append("captcha=");
-    body.append(captchaID);
-    body.append("&code=");
-    body.append(captchaCode);
-    body.append("&version=");
-    body.append(QUrl::toPercentEncoding(version));
-    body.append("&title=");
-    body.append(QUrl::toPercentEncoding(summary));
-    body.append("&body=");
-    body.append(QUrl::toPercentEncoding(description));
-    body.append("&email=");
-    body.append(QUrl::toPercentEncoding(email));
-    if (ui.pageFeedback->CheckSendSpecs->isChecked())
-    {
-        body.append("&specs=");
-        body.append(QUrl::toPercentEncoding(ui.pageFeedback->specs));
-    }
+void HWForm::ShowErrorMessage(const QString & msg)
+{
+    MessageDialog::ShowErrorMessage(msg, this);
+}
 
-    nam = new QNetworkAccessManager(this);
-    connect(nam, SIGNAL(finished(QNetworkReply*)),
-            this, SLOT(finishedSlot(QNetworkReply*)));
-
-    QNetworkRequest header(QUrl("http://hedgewars.org/feedback/?submit"));
-    header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
-
-    nam->post(header, body);
+void HWForm::showFeedbackDialog()
+{
+    FeedbackDialog dialog(this);
+    dialog.exec();
 }
 
-void HWForm::finishedSlot(QNetworkReply* reply)
+void HWForm::startGame()
 {
-    if (reply && reply->error() == QNetworkReply::NoError)
-    {
-            QMessageBox infoMsg(this);
-            infoMsg.setIcon(QMessageBox::Information);
-            infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
-            infoMsg.setText(reply->readAll());
-            infoMsg.setWindowModality(Qt::WindowModal);
-            infoMsg.exec();
+    QMessageBox questionMsg(this);
+    questionMsg.setIcon(QMessageBox::Question);
+    questionMsg.setWindowTitle(QMessageBox::tr("Not all players are ready"));
+    questionMsg.setText(QMessageBox::tr("Are you sure you want to start this game?\nNot all players are ready."));
+    questionMsg.setWindowModality(Qt::WindowModal);
+    questionMsg.addButton(QMessageBox::Yes);
+    questionMsg.addButton(QMessageBox::Cancel);
 
-            ui.pageFeedback->summary->clear();
-            ui.pageFeedback->email->clear();
-            ui.pageFeedback->description->clear();
-            ui.pageFeedback->LoadCaptchaImage();
-
-            return;
-    }
-    else
-    {
-        ShowErrorMessage(QString("Error: ") + reply->readAll());
-        ui.pageFeedback->LoadCaptchaImage();
-    }
+    if (hwnet->allPlayersReady() || questionMsg.exec() == QMessageBox::Yes)
+        hwnet->startGame();
 }
-
--- a/QTfrontend/hwform.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/hwform.h	Sun Jan 27 00:28:57 2013 +0100
@@ -109,7 +109,7 @@
         void NetNickNotRegistered(const QString & nick);
         void NetNickTaken(const QString & nick);
         void NetAuthFailed();
-    bool RetryDialog(const QString & title, const QString & label);
+        bool RetryDialog(const QString & title, const QString & label);
         void NetTeamAccepted(const QString& team);
         void AddNetTeam(const HWTeam& team);
         void RemoveNetTeam(const HWTeam& team);
@@ -126,10 +126,7 @@
         void UpdateCampaignPage(int index);
         void UpdateCampaignPageProgress(int index);
         void InitCampaignPage();
-        //Starts the transmission process for the feedback
-        void SendFeedback();
-        //Called after submitting new feedback
-        void finishedSlot(QNetworkReply* reply);
+        void showFeedbackDialog();
 
         void NetGameChangeStatus(bool isMaster);
         void NetGameMaster();
@@ -140,6 +137,9 @@
         void selectFirstNetScheme();
 
         void saveDemoWithCustomName();
+        void openRegistrationPage();
+
+        void startGame();
 
     private:
         void _NetConnect(const QString & hostName, quint16 port, QString nick);
@@ -148,6 +148,7 @@
         void closeEvent(QCloseEvent *event);
         void CustomizePalettes();
         void resizeEvent(QResizeEvent * event);
+        QString stringifyPageId(quint32 id);
         //void keyReleaseEvent(QKeyEvent *event);
 
         enum PageIDs
@@ -173,8 +174,7 @@
             ID_PAGE_CAMPAIGN       ,
             ID_PAGE_DRAWMAP        ,
             ID_PAGE_DATADOWNLOAD   ,
-            ID_PAGE_FEEDBACK       ,
-            ID_PAGE_VIDEOS     ,
+            ID_PAGE_VIDEOS         ,
             MAX_PAGE
         };
         QPointer<HWGame> game;
@@ -189,7 +189,6 @@
         BGWidget * wBackground;
         QSignalMapper * pageSwitchMapper;
         QByteArray m_lastDemo;
-        QNetworkAccessManager * nam;
 
         QPropertyAnimation *animationNewSlide;
         QPropertyAnimation *animationOldSlide;
--- a/QTfrontend/main.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/main.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -27,6 +27,8 @@
 #include <QSettings>
 #include <QStringListModel>
 #include <QDate>
+#include <QDesktopWidget>
+#include <QLabel>
 
 #include "hwform.h"
 #include "hwconsts.h"
@@ -37,11 +39,18 @@
 
 #ifdef _WIN32
 #include <Shlobj.h>
-#endif
-#ifdef __APPLE__
+#elif defined __APPLE__
 #include "CocoaInitializer.h"
 #endif
+#ifndef _WIN32
+#include <signal.h>
+#endif
 
+// Program resources
+#ifdef __APPLE__
+static CocoaInitializer * cocoaInit = NULL;
+#endif
+static FileEngineHandler * engine = NULL;
 
 //Determines the day of easter in year
 //from http://aa.usno.navy.mil/faq/docs/easter.php,adapted to C/C++
@@ -85,6 +94,14 @@
     else
         season = SEASON_NONE;
 }
+#ifndef _WIN32
+void terminateFrontend(int signal)
+{
+    Q_UNUSED(signal);
+
+    QCoreApplication::exit(0);
+}
+#endif
 
 bool checkForDir(const QString & dir)
 {
@@ -112,31 +129,53 @@
         return true;
 }
 
+// Guaranteed to be the last thing ran in the application's life time.
+// Closes resources that need to exist as long as possible.
+void closeResources(void)
+{
 #ifdef __APPLE__
-static CocoaInitializer *cocoaInit = NULL;
-// Function to be called at end of program's termination on OS X to release
-// the NSAutoReleasePool contained within the CocoaInitializer.
-void releaseCocoaPool(void)
-{
     if (cocoaInit != NULL)
     {
         delete cocoaInit;
         cocoaInit = NULL;
     }
+#endif
+    if (engine != NULL)
+    {
+        delete engine;
+        engine = NULL;
+    }
 }
-#endif
 
 int main(int argc, char *argv[])
 {
+    // Since we're calling this first, closeResources() will be the last thing called after main() returns.
+    atexit(closeResources);
+
 #ifdef __APPLE__
-    // This creates the autoreleasepool that prevents leaking, and destroys it only on exit
-    cocoaInit = new CocoaInitializer();
-    atexit(releaseCocoaPool);
+    cocoaInit = new CocoaInitializer(); // Creates the autoreleasepool preventing cocoa object leaks on OS X.
+#endif
+
+#ifndef _WIN32
+    signal(SIGINT, &terminateFrontend);
 #endif
 
     HWApplication app(argc, argv);
 
-    FileEngineHandler engine(argv[0]);
+    QLabel *splash = NULL;
+#if defined Q_WS_WIN
+    QPixmap pixmap(":res/splash.png");
+    splash = new QLabel(0, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
+    splash->setAttribute(Qt::WA_TranslucentBackground);
+    const QRect deskSize = QApplication::desktop()->screenGeometry(-1);
+    QPoint splashCenter = QPoint( (deskSize.width() - pixmap.width())/2,
+                                  (deskSize.height() - pixmap.height())/2 );
+    splash->move(splashCenter);
+    splash->setPixmap(pixmap);
+    splash->show();
+#endif
+
+    engine = new FileEngineHandler(argv[0]);
 
     app.setAttribute(Qt::AA_DontShowIconsInMenus,false);
 
@@ -194,8 +233,6 @@
 
     qRegisterMetaType<HWTeam>("HWTeam");
 
-    // workaround over NSIS installer which modifies the install path
-    //bindir->cd("./");
     bindir->cd(QCoreApplication::applicationDirPath());
 
     if(custom_config == false)
@@ -253,11 +290,11 @@
     }
 
     // setup PhysFS
-    engine.mount(datadir->absolutePath());
-    engine.mount(cfgdir->absolutePath() + "/Data");
-    engine.mount(cfgdir->absolutePath());
-    engine.setWriteDir(cfgdir->absolutePath());
-    engine.mountPacks();
+    engine->mount(datadir->absolutePath());
+    engine->mount(cfgdir->absolutePath() + "/Data");
+    engine->mount(cfgdir->absolutePath());
+    engine->setWriteDir(cfgdir->absolutePath());
+    engine->mountPacks();
 
     checkForFile("physfs://hedgewars.ini");
 
@@ -305,6 +342,7 @@
             break;
         default :
             fname = "qt.css";
+            break;
     }
 
     // load external stylesheet if there is any
@@ -319,5 +357,7 @@
 
     app.form = new HWForm(NULL, style);
     app.form->show();
+    if(splash)
+        splash->close();
     return app.exec();
 }
--- a/QTfrontend/model/GameStyleModel.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/GameStyleModel.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -23,14 +23,16 @@
 
 #include <QTextStream>
 
+#include "physfs.h"
 #include "GameStyleModel.h"
 
 
 void GameStyleModel::loadGameStyles()
 {
+    const QString appDir = QString(PHYSFS_getBaseDir());
+
     beginResetModel();
 
-
     // empty list, so that we can (re)fill it
     QStandardItemModel::clear();
 
@@ -77,11 +79,16 @@
                 weapons.replace("_", " ");
         }
 
-        QStandardItem * item = new QStandardItem(name);
+        // detect if script is dlc
+        QString scriptPath = PHYSFS_getRealDir(QString("Scripts/Multiplayer/%1.lua").arg(script).toLocal8Bit().data());
+        bool isDLC = !scriptPath.startsWith(appDir);
+
+        QStandardItem * item = new QStandardItem((isDLC ? "*" : "") + name);
 
         item->setData(script, ScriptRole);
         item->setData(scheme, SchemeRole);
         item->setData(weapons, WeaponsRole);
+        item->setData(isDLC, IsDlcRole);
 
         items.append(item);
     }
--- a/QTfrontend/model/GameStyleModel.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/GameStyleModel.h	Sun Jan 27 00:28:57 2013 +0100
@@ -38,7 +38,7 @@
         Q_OBJECT
 
     public:
-        enum DataRoles { ScriptRole = Qt::UserRole+1, SchemeRole, WeaponsRole };
+        enum DataRoles { ScriptRole = Qt::UserRole+1, SchemeRole, WeaponsRole, IsDlcRole };
 
     public slots:
         /// reloads the themes from the DataManager
--- a/QTfrontend/model/HatModel.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/HatModel.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -26,26 +26,28 @@
 #include <QDir>
 #include <QPixmap>
 #include <QPainter>
+#include <QList>
 #include "hwform.h" // player hash
 
 #include "DataManager.h"
 
 HatModel::HatModel(QObject* parent) :
-    QAbstractListModel(parent)
-{
-    hats = QVector<QPair<QString, QIcon> >();
-}
+    QStandardItemModel(parent)
+{}
 
 void HatModel::loadHats()
 {
     // this method resets the contents of this model (important to know for views).
-    beginResetModel();
+    QStandardItemModel::beginResetModel();
+    QStandardItemModel::clear();
 
-    // prepare hats Vector
-    hats.clear();
+    // New hats to add to model
+    QList<QStandardItem *> hats;
 
+    // we'll need the DataManager a few times, so let's get a reference to it
     DataManager & dataMgr = DataManager::instance();
 
+    // Default hat icon
     QPixmap hhpix = QPixmap("physfs://Graphics/Hedgehog/Idle.png").copy(0, 0, 32, 32);
 
     // my reserved hats
@@ -54,7 +56,6 @@
                                QDir::Files,
                                QStringList(playerHash+"*.png")
                            );
-
     int nReserved = hatsList.size();
 
     // regular hats
@@ -64,19 +65,20 @@
                         QStringList("*.png")
                     )
                    );
-
-
     int nHats = hatsList.size();
 
+    // Add each hat
     for (int i = 0; i < nHats; i++)
     {
         bool isReserved = (i < nReserved);
 
+        if (isReserved) continue; // For some reason, reserved hats were added in 9.19-dev, so this will hide them. Uncomment to show them.
+
         QString str = hatsList.at(i);
         str = str.remove(QRegExp("\\.png$"));
-        QPixmap pix(            
+        QPixmap pix(
                 "physfs://Graphics/Hats/" + QString(isReserved?"Reserved/":"") + str +
-                ".png"           
+                ".png"
         );
 
         // rename properly
@@ -94,51 +96,11 @@
         painter.end();
 
         if (str == "NoHat")
-            hats.prepend(qMakePair(str, QIcon(tmppix)));
+            hats.prepend(new QStandardItem(QIcon(tmppix), str));
         else
-            hats.append(qMakePair(str, QIcon(tmppix)));
+            hats.append(new QStandardItem(QIcon(tmppix), str));
     }
 
-
-    endResetModel();
-}
-
-QVariant HatModel::headerData(int section,
-                               Qt::Orientation orientation, int role) const
-{
-    Q_UNUSED(section);
-    Q_UNUSED(orientation);
-    Q_UNUSED(role);
-
-    return QVariant();
-}
-
-int HatModel::rowCount(const QModelIndex &parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return hats.size();
+    QStandardItemModel::appendColumn(hats);
+    QStandardItemModel::endResetModel();
 }
-
-/*int HatModel::columnCount(const QModelIndex & parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return 2;
-}
-*/
-QVariant HatModel::data(const QModelIndex &index,
-                         int role) const
-{
-    if (!index.isValid() || index.row() < 0
-            || index.row() >= hats.size()
-            || (role != Qt::DisplayRole && role != Qt::DecorationRole))
-        return QVariant();
-
-    if (role == Qt::DisplayRole)
-        return hats.at(index.row()).first;
-    else // role == Qt::DecorationRole
-        return hats.at(index.row()).second;
-}
--- a/QTfrontend/model/HatModel.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/HatModel.h	Sun Jan 27 00:28:57 2013 +0100
@@ -24,30 +24,22 @@
 #ifndef HEDGEWARS_HATMODEL_H
 #define HEDGEWARS_HATMODEL_H
 
-#include <QAbstractListModel>
+#include <QStandardItemModel>
 #include <QStringList>
 #include <QVector>
 #include <QPair>
 #include <QIcon>
 
-class HatModel : public QAbstractListModel
+class HatModel : public QStandardItemModel
 {
         Q_OBJECT
 
     public:
         HatModel(QObject *parent = 0);
 
-        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-        int rowCount(const QModelIndex & parent) const;
-        //int columnCount(const QModelIndex & parent) const;
-
     public slots:
         /// Reloads hats using the DataManager.
         void loadHats();
-
-        QVariant data(const QModelIndex &index, int role) const;
-    protected:
-        QVector<QPair<QString, QIcon> > hats;
 };
 
 #endif // HEDGEWARS_HATMODEL_H
--- a/QTfrontend/model/MapModel.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/MapModel.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -21,11 +21,20 @@
  * @brief MapModel class implementation
  */
 
+#include <QSettings>
+
+#include "physfs.h"
 #include "MapModel.h"
-
+#include "HWApplication.h"
 
-void MapModel::loadMaps()
+MapModel::MapInfo MapModel::MapInfoRandom = {MapModel::GeneratedMap, "+rnd+", "", 0, "", "", ""};
+MapModel::MapInfo MapModel::MapInfoMaze = {MapModel::GeneratedMaze, "+maze+", "", 0, "", "", ""};
+MapModel::MapInfo MapModel::MapInfoDrawn = {MapModel::HandDrawnMap, "+drawn+", "", 0, "", "", ""};
+
+void MapModel::loadMaps(MapType maptype)
 {
+    const QString appDir = QString(PHYSFS_getBaseDir());
+
     // this method resets the contents of this model (important to know for views).
     beginResetModel();
 
@@ -39,32 +48,18 @@
     // empty list, so that we can (re)fill it
     QStandardItemModel::clear();
 
-    QList<QStandardItem *> genMaps;
-    QList<QStandardItem *> missionMaps;
-    QList<QStandardItem *> staticMaps;
-
-    // add generated/handdrawn maps to list
-    // TODO: icons for these
-
-    genMaps.append(
-        infoToItem(QIcon(), QComboBox::tr("generated map..."), GeneratedMap, "+rnd+"));
-    genMaps.append(
-        infoToItem(QIcon(), QComboBox::tr("generated maze..."), GeneratedMaze, "+maze+"));
-    genMaps.append(
-        infoToItem(QIcon(), QComboBox::tr("hand drawn map..."), HandDrawnMap, "+drawn+"));
-
-    // only 2 map relate files are relevant:
-    // - the cfg file that contains the settings/info of the map
-    // - the lua file - if it exists it's a mission, otherwise it isn't
-    QFile mapLuaFile;
-    QFile mapCfgFile;
+    //QList<QStandardItem *> staticMaps;
+    //QList<QStandardItem *> missionMaps;
+    QList<QStandardItem *> mapList;
 
     // add mission/static maps to lists
     foreach (QString map, maps)
     {
-        mapCfgFile.setFileName(QString("physfs://Maps/%1/map.cfg").arg(map));
-        mapLuaFile.setFileName(QString("physfs://Maps/%1/map.lua").arg(map));
-
+        // only 2 map relate files are relevant:
+        // - the cfg file that contains the settings/info of the map
+        // - the lua file - if it exists it's a mission, otherwise it isn't
+        QFile mapLuaFile(QString("physfs://Maps/%1/map.lua").arg(map));
+        QFile mapCfgFile(QString("physfs://Maps/%1/map.cfg").arg(map));
 
         if (mapCfgFile.open(QFile::ReadOnly))
         {
@@ -73,25 +68,43 @@
             quint32 limit = 0;
             QString scheme;
             QString weapons;
+            QString desc;
+            bool dlc;
+
             // if there is a lua file for this map, then it's a mission
             bool isMission = mapLuaFile.exists();
-            MapType type = isMission?MissionMap:StaticMap;
+            MapType type = isMission ? MissionMap : StaticMap;
+
+            // if we're supposed to ignore this type, continue
+            if (type != maptype) continue;
 
             // load map info from file
             QTextStream input(&mapCfgFile);
-            input >> theme;
-            input >> limit;
+            theme = input.readLine();
+            limit = input.readLine().toInt();
             if (isMission) { // scheme and weapons are only relevant for missions
-                input >> scheme;
-                input >> weapons;
+                scheme = input.readLine();
+                weapons = input.readLine();
             }
             mapCfgFile.close();
 
+            // load description (if applicable)
+            if (isMission)
+            {
+                QString locale = HWApplication::keyboardInputLocale().name();
+
+                QSettings descSettings(QString("physfs://Maps/%1/desc.txt").arg(map), QSettings::IniFormat);
+                desc = descSettings.value(locale, QString()).toString().replace("|", "\n").replace("\\,", ",");
+            }
+
+            // detect if map is dlc
+            QString mapDir = PHYSFS_getRealDir(QString("Maps/%1/map.cfg").arg(map).toLocal8Bit().data());
+            dlc = !mapDir.startsWith(appDir);
+
             // let's use some semi-sane hedgehog limit, rather than none
             if (limit == 0)
                 limit = 18;
 
-
             // the default scheme/weaponset for missions.
             // if empty we assume the map sets these internally -> locked
             if (isMission)
@@ -107,97 +120,54 @@
                     weapons.replace("_", " ");
             }
 
-            // add a mission caption prefix to missions
-            if (isMission)
-            {
-                // TODO: icon
-                caption = QComboBox::tr("Mission") + ": " + map;
-            }
-            else
-                caption = map;
+            // caption
+            caption = map;
 
             // we know everything there is about the map, let's get am item for it
-            QStandardItem * item = infoToItem(
-                QIcon(), caption, type, map, theme, limit, scheme, weapons);
+            QStandardItem * item = MapModel::infoToItem(
+                QIcon(), caption, type, map, theme, limit, scheme, weapons, desc, dlc);
 
             // append item to the list
-            if (isMission)
-                missionMaps.append(item);
-            else
-                staticMaps.append(item);
-        
+            mapList.append(item);
         }
 
     }
 
-
-    // define a separator item
-    QStandardItem separator("---");
-    separator.setData(QLatin1String("separator"), Qt::AccessibleDescriptionRole);
-    separator.setFlags(separator.flags() & ~( Qt::ItemIsEnabled | Qt::ItemIsSelectable ) );
-
-    // create list:
-    // generated+handdrawn maps, 2 saperators, missions, 1 separator, static maps
-    QList<QStandardItem * > items;
-    items.append(genMaps);
-    items.append(separator.clone());
-    items.append(separator.clone());
-    items.append(missionMaps);
-    items.append(separator.clone());
-    items.append(staticMaps);
-
-
-    // create row-index lookup table
+    // Create column-index lookup table
 
     m_mapIndexes.clear();
 
-    int count = items.size();
 
+    int count = mapList.size();
     for (int i = 0; i < count; i++)
     {
-        QStandardItem * si = items.at(i);
+        QStandardItem * si = mapList.at(i);
         QVariant v = si->data(Qt::UserRole + 1);
         if (v.canConvert<MapInfo>())
             m_mapIndexes.insert(v.value<MapInfo>().name, i);
     }
 
-
-    // store start-index and count of relevant types
-
-    m_typeLoc.insert(GeneratedMap, QPair<int,int>(0, 1));
-    m_typeLoc.insert(GeneratedMaze, QPair<int,int>(1, 1));
-    m_typeLoc.insert(HandDrawnMap, QPair<int,int>(2, 1));
-    // mission maps
-    int startIdx = genMaps.size() + 2; // start after genMaps and 2 separators
-    count = missionMaps.size();
-    m_typeLoc.insert(MissionMap, QPair<int,int>(startIdx, count));
-    // static maps
-    startIdx += count + 1; // start after missions and 2 separators
-    count = staticMaps.size();
-    m_typeLoc.insert(StaticMap, QPair<int,int>(startIdx, count));
-
-    // store list contents in the item model
-    QStandardItemModel::appendColumn(items);
-
+    QStandardItemModel::appendColumn(mapList);
 
     endResetModel();
 }
 
-
-int MapModel::randomMap(MapType type) const
+bool MapModel::mapExists(const QString & map) const
 {
-    // return a random index for this type or -1 if none available
-    QPair<int,int> loc = m_typeLoc.value(type, QPair<int,int>(-1,0));
-
-    int startIdx = loc.first;
-    int count = loc.second;
-
-    if (count < 1)
-        return -1;
-    else
-        return startIdx + (rand() % count);
+    return findMap(map) >= 0;
 }
 
+int MapModel::findMap(const QString & map) const
+{
+    return m_mapIndexes.value(map, -1);
+}
+
+QStandardItem * MapModel::getMap(const QString & map)
+{
+    int loc = findMap(map);
+    if (loc < 0) return NULL;
+    return item(loc);
+}
 
 QStandardItem * MapModel::infoToItem(
     const QIcon & icon,
@@ -207,10 +177,11 @@
     QString theme,
     quint32 limit,
     QString scheme,
-    QString weapons)
-const
+    QString weapons,
+    QString desc,
+    bool dlc)
 {
-    QStandardItem * item = new QStandardItem(icon, caption);
+    QStandardItem * item = new QStandardItem(icon, (dlc ? "*" : "") + caption);
     MapInfo mapInfo;
     QVariant qvar(QVariant::UserType);
 
@@ -220,17 +191,11 @@
     mapInfo.limit = limit;
     mapInfo.scheme = scheme;
     mapInfo.weapons = weapons;
-
+    mapInfo.desc = desc.isEmpty() ? tr("No description available.") : desc;
+    mapInfo.dlc = dlc;
 
     qvar.setValue(mapInfo);
     item->setData(qvar, Qt::UserRole + 1);
 
     return item;
 }
-
-
-int MapModel::indexOf(const QString & map) const
-{
-    return m_mapIndexes.value(map, -1);
-}
-
--- a/QTfrontend/model/MapModel.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/MapModel.h	Sun Jan 27 00:28:57 2013 +0100
@@ -63,32 +63,50 @@
             quint32 limit; ///< The maximum allowed number of hedgehogs.
             QString scheme; ///< Default scheme name or "locked", for mission-maps.
             QString weapons; ///< Default weaponset name or "locked", for missions-maps.
+            QString desc; ///< The brief 1-2 sentence description of the mission, for mission-maps.
+            bool dlc; ///< True if this map was not packaged with the game
         };
 
         /**
-         * @brief Returns the row-index of the given map.
-         * @param map map of which to get the row-index of.
-         * @return row-index of map or -1 if not available.
+         * @brief Searches maps in model to find out if one exists
+         * @param map map of which to check existence
+         * @return true if it exists
          */
-        int indexOf(const QString & map) const;
+        bool mapExists(const QString & map) const;
+
+        /**
+         * @brief Finds a map index (column, row) for a map name
+         * @param map map of which to find index+column
+         * @return QPair<int, int> with column, index, or (-1, -1) if map not found
+         */
+        //QPair<int, int> findMap(const QString & map) const;
 
         /**
-         * @brief Returns the row-index of a random map with a specified type.
-         * @param type desired type of map.
-         * @return row-index of a map with the desired type, -1 if none found.
+         * @brief Finds a map index for a map name
+         * @param map map of which to find index
+         * @return int of index, or -1 if map not found
          */
-        int randomMap(MapType type) const;
+        int findMap(const QString & map) const;
+
+        /**
+         * @brief Finds and returns a map item for a map name
+         * @param map map
+         * @return QStandardItem of map, or NULL if map not found
+         */
+        QStandardItem * getMap(const QString & map);
+
+        // Static MapInfos for drawn and generated maps
+        static MapInfo MapInfoRandom, MapInfoMaze, MapInfoDrawn;
 
     public slots:
         /// Reloads the maps using the DataManager.
-        void loadMaps();
+        /// Accepts two map types: StaticMap or MissionMap.
+        void loadMaps(MapType maptype);
 
 
     private:
-        /// start-index and map count for each map-type.
-        QMap<MapType, QPair<int,int> > m_typeLoc;
-
-        /// map index lookup table
+        /// map index lookup table. QPair<int, int> contains: <column, index>
+        //QHash<QString, QPair<int, int> > m_mapIndexes;
         QHash<QString, int> m_mapIndexes;
 
         /**
@@ -102,9 +120,10 @@
          * @param limit the hedgehog limit of the map.
          * @param scheme mission map: default scheme name or "locked".
          * @param weapons mission map: default weaponset name or "locked".
+         * @param desc mission map: description of mission.
          * @return pointer to item representing the map info: at Qt::UserRole + 1.
          */
-        QStandardItem * infoToItem(
+        static QStandardItem * infoToItem(
             const QIcon & icon,
             const QString caption,
             MapType type = Invalid,
@@ -112,7 +131,9 @@
             QString theme = "",
             quint32 limit = 0,
             QString scheme = "",
-            QString weapons = "") const;
+            QString weapons = "",
+            QString desc = "",
+            bool dlc = false);
 };
 
 Q_DECLARE_METATYPE(MapModel::MapInfo)
--- a/QTfrontend/model/ThemeModel.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/ThemeModel.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -21,6 +21,7 @@
  * @brief ThemeModel class implementation
  */
 
+#include "physfs.h"
 #include "ThemeModel.h"
 
 ThemeModel::ThemeModel(QObject *parent) :
@@ -49,9 +50,10 @@
 
 void ThemeModel::loadThemes()
 {
+    const QString appDir = QString(PHYSFS_getBaseDir());
+
     beginResetModel();
 
-
     DataManager & datamgr = DataManager::instance();
 
     QStringList themes =
@@ -73,11 +75,20 @@
 
         QMap<int, QVariant> dataset;
 
+        // detect if theme is dlc
+        QString themeDir = PHYSFS_getRealDir(QString("Themes/%1/icon.png").arg(theme).toLocal8Bit().data());
+        dataset.insert(Qt::UserRole + 2, !themeDir.startsWith(appDir));
+
+        // set icon path
+        dataset.insert(Qt::UserRole + 1, iconpath);
+
         // set name
         dataset.insert(Qt::DisplayRole, theme);
 
         // load and set icon
-        QIcon icon(iconpath);
+        QIcon icon;
+        icon.addPixmap(QPixmap(iconpath), QIcon::Normal);
+        icon.addPixmap(QPixmap(iconpath), QIcon::Disabled);
 
         dataset.insert(Qt::DecorationRole, icon);
 
@@ -91,7 +102,3 @@
 
     endResetModel();
 }
-
-
-
-
--- a/QTfrontend/model/roomslistmodel.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/roomslistmodel.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -43,7 +43,8 @@
      << tr("Rules")
      << tr("Weapons");
 
-    m_mapModel = DataManager::instance().mapModel();
+    m_staticMapModel = DataManager::instance().staticMapModel();
+    m_missionMapModel = DataManager::instance().missionMapModel();
 }
 
 
@@ -134,7 +135,8 @@
             }
 
             // prefix ? if map not available
-            if ((m_mapModel->indexOf(content) < 0))
+            if (!m_staticMapModel->mapExists(content) &&
+                !m_missionMapModel->mapExists(content))
                 return QString ("? %1").arg(content);
         }
 
@@ -144,10 +146,14 @@
     // dye map names red if map not available
     if (role == Qt::ForegroundRole)
     {
-        if ((m_mapModel->indexOf(content) < 0))
+        if (content == "+rnd+" ||
+            content == "+maze+" ||
+            content == "+drawn+" ||
+            m_staticMapModel->mapExists(content) ||
+            m_missionMapModel->mapExists(content))
+            return QVariant();
+        else
             return QBrush(QColor("darkred"));
-        else
-            return QVariant();
     }
 
     if (role == Qt::TextAlignmentRole)
--- a/QTfrontend/model/roomslistmodel.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/model/roomslistmodel.h	Sun Jan 27 00:28:57 2013 +0100
@@ -64,7 +64,8 @@
     const int c_nColumns;
     QList<QStringList> m_data;
     QStringList m_headerData;
-    MapModel * m_mapModel;
+    MapModel * m_staticMapModel;
+    MapModel * m_missionMapModel;
 
     QStringList roomInfo2RoomRecord(const QStringList & info);
 };
--- a/QTfrontend/net/newnetclient.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/net/newnetclient.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -28,6 +28,8 @@
 #include "game.h"
 #include "roomslistmodel.h"
 #include "playerslistmodel.h"
+#include "servermessages.h"
+#include "HWApplication.h"
 
 char delimeter='\n';
 
@@ -251,7 +253,7 @@
     if (lst[0] == "ERROR")
     {
         if (lst.size() == 2)
-            emit Error(lst[1]);
+            emit Error(HWApplication::translate("server", lst[1].toAscii().constData()));
         else
             emit Error("Unknown error");
         return;
@@ -260,7 +262,7 @@
     if (lst[0] == "WARNING")
     {
         if (lst.size() == 2)
-            emit Warning(lst[1]);
+            emit Warning(HWApplication::translate("server", lst[1].toAscii().constData()));
         else
             emit Warning("Unknown warning");
         return;
@@ -579,14 +581,14 @@
         if (lst[1] == "Authentication failed")
         {
             emit AuthFailed();
-        m_game_connected = false;
-        Disconnect();
-        //omitted 'emit disconnected()', we don't want the error message
-        return;
+            m_game_connected = false;
+            Disconnect();
+            //omitted 'emit disconnected()', we don't want the error message
+            return;
         }
         m_game_connected = false;
         Disconnect();
-        emit disconnected(lst[1]);
+        emit disconnected(HWApplication::translate("server", lst[1].toAscii().constData()));
         return;
     }
 
@@ -926,6 +928,20 @@
     }
 }
 
+void HWNewNet::consoleCommand(const QString & cmd)
+{
+    RawSendNet(QString("CMD%1%2").arg(delimeter).arg(cmd));
+}
+
+bool HWNewNet::allPlayersReady()
+{
+    int ready = 0;
+    for (int i = 0; i < m_roomPlayersModel->rowCount(); i++)
+        if (m_roomPlayersModel->index(i, 0).data(PlayersListModel::Ready).toBool()) ready++;
+
+    return (ready == m_roomPlayersModel->rowCount());
+}
+
 void HWNewNet::startGame()
 {
     RawSendNet(QString("START_GAME"));
--- a/QTfrontend/net/newnetclient.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/net/newnetclient.h	Sun Jan 27 00:28:57 2013 +0100
@@ -60,6 +60,7 @@
         RoomsListModel * roomsListModel();
         QAbstractItemModel * lobbyPlayersModel();
         QAbstractItemModel * roomPlayersModel();
+        bool allPlayersReady();
 
     private:
         bool isChief;
@@ -155,6 +156,7 @@
         void kickPlayer(const QString &);
         void infoPlayer(const QString &);
         void followPlayer(const QString &);
+        void consoleCommand(const QString &);
         void startGame();
         void toggleRestrictJoins();
         void toggleRestrictTeamAdds();
Binary file QTfrontend/res/Start.png has changed
Binary file QTfrontend/res/audio.png has changed
Binary file QTfrontend/res/camera.png has changed
--- a/QTfrontend/res/css/qt.css	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/res/css/qt.css	Sun Jan 27 00:28:57 2013 +0100
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QListView, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -44,8 +44,8 @@
 QPushButton, QListWidget, QListView, QTableView, QLineEdit, QHeaderView,
 QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
-.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
-QTabWidget::pane, QTabBar::tab {
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
+QTabWidget::pane, QTabBar::tab, #mapPreview, #labelLikeLineEdit {
 border: solid;
 border-width: 3px;
 border-color: #ffcc00;
@@ -56,14 +56,30 @@
 border-color: yellow;
 }
 
+QToolButton {
+background-color: #11084A;
+}
+
+QToolButton:hover {
+background-color: #150A61;
+}
+
+QToolButton:pressed {
+background-color: #100744;
+}
+
 QLineEdit, QListWidget, QListView, QTableView, QTextBrowser,
-QSpinBox, QToolBox, QPlainTextEdit {
+QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
 }
 
+#mapPreview {
+background-color: #0d0544;
+}
+
 QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
-IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
+IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
 SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
@@ -72,7 +88,7 @@
 background-repeat: repeat-x;
 background-color: #000000;
 }
-.QGroupBox,GameCFGWidget,TeamSelWidget,SelWeaponWidget {
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget {
 background-position: bottom center;
 background-repeat: repeat-x;
 border-radius: 16px;
@@ -89,6 +105,18 @@
 background-color: #130f2c;
 }
 
+QTabWidget::pane {
+border-radius: 8px;
+border-top-left-radius: 0px;
+}
+
+QLineEdit:disabled, QSpinBox:disabled {
+border-color: gray;
+}
+
+GameCFGWidget {
+border: none;
+}
 
 QPushButton {
 border-radius: 8px;
@@ -97,7 +125,7 @@
 background-color: rgba(18, 42, 5, 70%);
 }
 
-QPushButton:pressed{
+QPushButton:pressed, QToolButton:pressed {
 border-color: white;
 }
 
@@ -240,9 +268,62 @@
 
 QSlider::handle::horizontal {
 border: 0px;
-margin: -2px 0px;
+margin: -8px 0px;
+background-color: #ffcc00;
+width: 12px;
+height: 6px;
 border-radius: 3px;
-background-color: #ffcc00;
-width: 8px;
+}
+
+HatButton {
+text-align: left;
+}
+
+#hatList, #hatList:hover {
+border-color: #F6CB1C;
+}
+
+#hatList QScrollBar {
+background-color: #130F2A;
+border-top-right-radius: 10px;
+border-bottom-right-radius: 10px;
+}
+
+#hatList {
+border-color: #F6CB1C;
+border-width: 3px;
+border-style: solid;
+border-radius: 10px;
+border-top-left-radius: 0px;
 }
 
+#hatList::item {
+background-color: #11084A;
+padding: 4px;
+border-radius: 10px;
+color: #ffcc00 !important;
+font: 8px;
+border-width: 2px;
+border-color: #11084A;
+}
+
+#hatList::item:hover {
+background-color: #150A61;
+}
+
+#hatList::item:selected {
+background-color: #150A61;
+}
+
+QDialogButtonBox QPushButton {
+padding: 3px 5px;
+}
+
+#gameCfgWidgetTabs {
+border-radius: 16px;
+border-top-left-radius: 0px;
+}
+
+TeamSelWidget, #gameStackContainer, #GBoxOptions {
+border-radius: 10px;
+}
\ No newline at end of file
--- a/QTfrontend/res/html/about.html	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/res/html/about.html	Sun Jan 27 00:28:57 2013 +0100
@@ -8,8 +8,8 @@
 </style>
 </head>
 <body>
-	<h2>Developers:</h2>
-	<p>
+    <h2>Developers:</h2>
+    <p>
         Engine, frontend, net server: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br>
         Many frontend improvements: Igor Ulyanov &lt;<a href="mailto:disinbox@gmail.com">disinbox@gmail.com</a>&gt;<br>
         Many engine and frontend improvements: Derek Pomery &lt;<a href="mailto:nemo@m8y.org">nemo@m8y.org</a>&gt;<br>
@@ -31,7 +31,7 @@
         Video recording: Stepan Podoskin &lt;<a href="mailto:stepik-777@mail.ru">stepik-777@mail.ru</a>&gt;<br>
         Campaign support, first campaign: Szabolcs Orb&agrave;n &lt;<a href="mailto:szabibibi@gmail.com">szabibibi@gmail.com</a>&gt;<br>
         </p>
-        
+
         <h2>Art:</h2>
             <p>John Dum &lt;<a href="mailto:fizzy@gmail.com">fizzy@gmail.com</a>&gt;
             <br>
@@ -49,7 +49,7 @@
             <br>
             Hats: Trey Perry &lt;<a href="mailto:tx.perry.j@gmail.com">tx.perry.j@gmail.com</a>&gt;
             </p>
-        
+
         <h2>Sounds:</h2>
         <p>
         Hedgehogs voice: Stephen Alexander &lt;<a href="mailto:ArmagonNo1@gmail.com">ArmagonNo1@gmail.com</a>&gt;
Binary file QTfrontend/res/inverse-corner-bl.png has changed
Binary file QTfrontend/res/splash.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/res/xml/tips.xml	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,61 @@
+# This is not xml actually, but it looks and behaves like it.
+# Including an xml library would need too much resources.
+# Tips between the platform specific tags are shown only on those platforms.
+# Do not escape characters or use the CDATA tag.
+<tips>
+    <tip>Simply pick the same color as a friend to play together as a team. Each of you will still control his or her own hedgehogs but they'll win or lose together.</tip>
+    <tip>Some weapons might do only low damage but they can be a lot more devastating in the right situation. Try to use the Desert Eagle to knock multiple hedgehogs into the water.</tip>
+    <tip>If you're unsure what to do and don't want to waste ammo, skip one round. But don't let too much time pass as there will be Sudden Death!</tip>
+    <tip>Want to save ropes? Release the rope in mid air and then shoot again. As long as you don't touch the ground or miss a shot you'll reuse your rope without wasting ammo!</tip>
+    <tip>If you'd like to keep others from using your preferred nickname on the official server, register an account at http://www.hedgewars.org/.</tip>
+    <tip>You're bored of default gameplay? Try one of the missions - they'll offer different gameplay depending on the one you picked.</tip>
+    <tip>By default the game will always record the last game played as a demo. Select 'Local Game' and pick the 'Demos' button on the lower right corner to play or manage them.</tip>
+    <tip>Hedgewars is free software (Open Source) we create in our spare time. If you've got problems, ask on our forums or visit our IRC room!</tip>
+    <tip>Hedgewars is free software (Open Source) we create in our spare time. If you like it, help us with a small donation or contribute your own work!</tip>
+    <tip>Hedgewars is free software (Open Source) we create in our spare time. Share it with your family and friends as you like!</tip>
+    <tip>Hedgewars is free software (Open Source) we create in our spare time, just for fun! Meet the devs in <a href="irc://irc.freenode.net/hedgewars">#hedgewars</a>!</tip>
+    <tip>From time to time there will be official tournaments. Upcoming events will be announced at http://www.hedgewars.org/ some days in advance.</tip>
+    <tip>Hedgewars is available in many languages. If the translation in your language seems to be missing or outdated, feel free to contact us!</tip>
+    <tip>Hedgewars can be run on lots of different operating systems including Microsoft Windows, Mac OS X and GNU/Linux.</tip>
+    <tip>Always remember you're able to set up your own games in local and network/online play. You're not restricted to the 'Simple Game' option.</tip>
+    <tip>Connect one or more gamepads before starting the game to be able to assign their controls to your teams.</tip>
+    <tip>Create an account on <a href="http://www.hedgewars.org/">http://www.hedgewars.org/</a> to keep others from using your most favourite nickname while playing on the official server.</tip>
+    <tip>While playing you should give yourself a short break at least once an hour.</tip>
+    <tip>If your graphics card isn't able to provide hardware accelerated OpenGL, try to enable the low quality mode to improve performance.</tip>
+    <tip>If your graphics card isn't able to provide hardware accelerated OpenGL, try to update the associated drivers.</tip>
+    <tip>We're open to suggestions and constructive feedback. If you don't like something or got a great idea, let us know!</tip>
+    <tip>Especially while playing online be polite and always remember there might be some minors playing with or against you as well!</tip>
+    <tip>Special game modes such as 'Vampirism' or 'Karma' allow you to develop completely new tactics. Try them in a custom game!</tip>
+    <tip>You should never install Hedgewars on computers you don't own (school, university, work, etc.). Please ask the responsible person instead!</tip>
+    <tip>Hedgewars can be perfect for short games during breaks. Just ensure you don't add too many hedgehogs or use an huge map. Reducing time and health might help as well.</tip>
+    <tip>No hedgehogs were harmed in making this game.</tip>
+    <tip>There are three different jumps available. Tap [high jump] twice to do a very high/backwards jump.</tip>
+    <tip>Afraid of falling off a cliff? Hold down [precise] to turn [left] or [right] without actually moving.</tip>
+    <tip>Some weapons require special strategies or just lots of training, so don't give up on a particular tool if you miss an enemy once.</tip>
+    <tip>Most weapons won't work once they touch the water. The Homing Bee as well as the Cake are exceptions to this.</tip>
+    <tip>The Old Limbuger only causes a small explosion. However the wind affected smelly cloud can poison lots of hogs at once.</tip>
+    <tip>The Piano Strike is the most damaging air strike. You'll lose the hedgehog performing it, so there's a huge downside as well.</tip>
+    <tip>The Homing Bee can be tricky to use. Its turn radius depends on its velocity, so try to not use full power.</tip>
+    <tip>Sticky Mines are a perfect tool to create small chain reactions knocking enemy hedgehogs into dire situations ... or water.</tip>
+    <tip>The Hammer is most effective when used on bridges or girders. Hit hogs will just break through the ground.</tip>
+    <tip>If you're stuck behind an enemy hedgehog, use the Hammer to free yourself without getting damaged by an explosion.</tip>
+    <tip>The Cake's maximum walking distance depends on the ground it has to pass. Use [attack] to detonate it early.</tip>
+    <tip>The Flame Thrower is a weapon but it can be used for tunnel digging as well.</tip>
+    <tip>Use the Molotov or Flame Thrower to temporary keep hedgehogs from passing terrain such as tunnels or platforms.</tip>
+    <tip>Want to know who's behind the game? Click on the Hedgewars logo in the main menu to see the credits.</tip>
+    <tip>Like Hedgewars? Become a fan on <a href="http://www.facebook.com/Hedgewars">Facebook</a> or follow us on <a href="http://twitter.com/hedgewars">Twitter</a></tip>
+    <tip>Feel free to draw your own graves, hats, flags or even maps and themes! But note that you'll have to share them somewhere to use them online.</tip>
+    <tip>Keep your video card drivers up to date to avoid issues playing the game.</tip>
+    <tip>You're able to associate Hedgewars related files (savegames and demo recordings) with the game to launch them right from your favorite file or internet browser.</tip>
+    <windows-only>
+	    <tip>The version of Hedgewars supports <a href="http://www.xfire.com">Xfire</a>. Make sure to add Hedgewars to its game list so your friends can see you playing.</tip>
+        <tip>You can find your Hedgewars configuration files under "My Documents\Hedgewars". Create backups or take the files with you, but don't edit them by hand.</tip>
+    </windows-only>
+    <mac-only>
+        <tip>You can find your Hedgewars configuration files under "Library/Application Support/Hedgewars" in your home directory. Create backups or take the files with you, but don't edit them by hand.</tip>
+    </mac-only>
+    <linux-only>
+        <tip>lintip</tip>
+        <tip>You can find your Hedgewars configuration files under ".hedgewars" in your home directory. Create backups or take the files with you, but don't edit them by hand.</tip>
+    </linux-only>
+</tips>
--- a/QTfrontend/team.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/team.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -23,10 +23,12 @@
 #include <QCryptographicHash>
 #include <QSettings>
 #include <QStandardItemModel>
+#include <QDebug>
 
 #include "team.h"
 #include "hwform.h"
 #include "DataManager.h"
+#include "gameuiconfig.h"
 
 HWTeam::HWTeam(const QString & teamname) :
     QObject(0)
@@ -50,7 +52,7 @@
     {
         m_binds.append(BindAction());
         m_binds[i].action = cbinds[i].action;
-        m_binds[i].strbind = cbinds[i].strbind;
+        m_binds[i].strbind = QString();
     }
     m_rounds = 0;
     m_wins = 0;
@@ -110,7 +112,7 @@
     {
         m_binds.append(BindAction());
         m_binds[i].action = cbinds[i].action;
-        m_binds[i].strbind = cbinds[i].strbind;
+        m_binds[i].strbind = QString();
     }
     m_rounds = 0;
     m_wins = 0;
@@ -191,7 +193,7 @@
         m_hedgehogs[i].Suicides = teamfile.value(hh + "Suicides", 0).toInt();
     }
     for(int i = 0; i < BINDS_NUMBER; i++)
-        m_binds[i].strbind = teamfile.value(QString("Binds/%1").arg(m_binds[i].action), cbinds[i].strbind).toString();
+        m_binds[i].strbind = teamfile.value(QString("Binds/%1").arg(m_binds[i].action), QString()).toString();
     for(int i = 0; i < MAX_ACHIEVEMENTS; i++)
         if(achievements[i][0][0])
             AchievementProgress[i] = teamfile.value(QString("Achievements/%1").arg(achievements[i][0]), 0).toUInt();
@@ -257,7 +259,7 @@
     return true;
 }
 
-QStringList HWTeam::teamGameConfig(quint32 InitHealth) const
+QStringList HWTeam::teamGameConfig(quint32 InitHealth, GameUIConfig * config) const
 {
     QStringList sl;
     if (m_isNetTeam)
@@ -273,9 +275,15 @@
     sl.push_back(QString("eflag " + m_flag));
 
     if (!m_isNetTeam)
+    {
         for(int i = 0; i < BINDS_NUMBER; i++)
-            if(!m_binds[i].strbind.isEmpty())
+        {
+            if(m_binds[i].strbind.isEmpty() || m_binds[i].strbind == "default")
+                sl.push_back(QString("ebind " + config->bind(i) + " " + m_binds[i].action));
+            else
                 sl.push_back(QString("ebind " + m_binds[i].strbind + " " + m_binds[i].action));
+        }
+    }
 
     for (int t = 0; t < m_numHedgehogs; t++)
     {
--- a/QTfrontend/team.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/team.h	Sun Jan 27 00:28:57 2013 +0100
@@ -93,7 +93,7 @@
         void incWins();
 
         // convert team info into strings for further computation
-        QStringList teamGameConfig(quint32 InitHealth) const;
+        QStringList teamGameConfig(quint32 InitHealth, GameUIConfig * config) const;
 
         // comparison operators
         bool operator == (const HWTeam& t1) const;
--- a/QTfrontend/ui/dialog/bandialog.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/dialog/bandialog.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -6,6 +6,7 @@
 #include <QPushButton>
 #include <QHBoxLayout>
 #include <QMessageBox>
+#include "HWApplication.h"
 
 #include "bandialog.h"
 
@@ -21,15 +22,16 @@
     leReason = new QLineEdit(this);
     cbTime = new QComboBox(this);
 
-    cbTime->addItem(tr("10 minutes"), 5 * 60);
-    cbTime->addItem(tr("30 minutes"), 10 * 60);
-    cbTime->addItem(tr("1 hour"), 60 * 60);
-    cbTime->addItem(tr("3 hours"), 3 * 60 * 60);
-    cbTime->addItem(tr("5 hours"), 5 * 60 * 60);
-    cbTime->addItem(tr("24 hours"), 24 * 60 * 60);
-    cbTime->addItem(tr("3 days"), 72 * 60 * 60);
-    cbTime->addItem(tr("7 days"), 168 * 60 * 60);
-    cbTime->addItem(tr("14 days"), 336 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 minutes", 0, 10).arg("10"), 5 * 60);
+    cbTime->addItem(HWApplication::tr("%1 minutes", 0, 30).arg("30"), 10 * 60);
+    cbTime->addItem(HWApplication::tr("%1 hour", 0, 10).arg("10"), 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 hours", 0, 3).arg("3"), 3 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 hours", 0, 5).arg("5"), 5 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 hours", 0, 12).arg("12"), 12 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 day", 0, 1).arg("1"), 24 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 days", 0, 3).arg("3"), 72 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 days", 0, 7).arg("7"), 168 * 60 * 60);
+    cbTime->addItem(HWApplication::tr("%1 days", 0, 14).arg("14"), 336 * 60 * 60);
     cbTime->addItem(tr("permanent"), 3650 * 24 * 60 * 60);
     cbTime->setCurrentIndex(0);
 
--- a/QTfrontend/ui/dialog/input_password.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/dialog/input_password.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -60,6 +60,7 @@
     layout->addWidget(cbSave, 5, 0);
 
     QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
+    pbNewAccount = dbbButtons->addButton(QString("New Account"), QDialogButtonBox::ActionRole);
     QPushButton * pbOK = dbbButtons->addButton(QDialogButtonBox::Ok);
     QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel);
     layout->addWidget(dbbButtons, 6, 0);
--- a/QTfrontend/ui/dialog/input_password.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/dialog/input_password.h	Sun Jan 27 00:28:57 2013 +0100
@@ -23,6 +23,7 @@
 
 class QLineEdit;
 class QCheckBox;
+class QPushButton;
 
 class HWPasswordDialog : public QDialog
 {
@@ -33,6 +34,7 @@
         QLineEdit* lePassword;
         QLineEdit* leNickname;
         QCheckBox* cbSave;
+        QPushButton * pbNewAccount;
 };
 
 
--- a/QTfrontend/ui/page/AbstractPage.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/AbstractPage.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -25,6 +25,7 @@
 #include <QLabel>
 #include <QSize>
 #include <QFontMetricsF>
+#include <QDebug>
 
 #include "qpushbuttonwithsound.h"
 
@@ -39,6 +40,8 @@
 void AbstractPage::initPage()
 {
     QGridLayout * pageLayout = new QGridLayout(this);
+    QHBoxLayout * bottomLeftLayout = new QHBoxLayout();
+    pageLayout->addLayout(bottomLeftLayout, 1, 0);
 
     // stretch grid space for body and footer
     pageLayout->setColumnStretch(0,1);
@@ -50,17 +53,23 @@
     // add back/exit button
     btnBack = formattedButton(":/res/Exit.png", true);
     btnBack->setWhatsThis(tr("Go back"));
-    pageLayout->addWidget(btnBack, 1, 0, 1, 1, Qt::AlignLeft | Qt::AlignBottom);
+    bottomLeftLayout->addWidget(btnBack, 0);
 
     // add body layout as defined by the subclass
     pageLayout->addLayout(bodyLayoutDefinition(), 0, 0, 1, 3);
 
+    // add left footer layout
+    QLayout * flld = footerLayoutLeftDefinition();
+    if (flld != NULL)
+        bottomLeftLayout->addLayout(flld, 0);
+
     descLabel = new QLabel();
     descLabel->setAlignment(Qt::AlignCenter);
     descLabel->setWordWrap(true);
     descLabel->setOpenExternalLinks(true);
     descLabel->setFixedHeight(50);
     descLabel->setStyleSheet("font-size: 16px");
+    bottomLeftLayout->addWidget(descLabel);
     pageLayout->addWidget(descLabel, 1, 1);
 
     // add footer layout
@@ -68,6 +77,8 @@
     if (fld != NULL)
         pageLayout->addLayout(fld, 1, 2);
 
+    bottomLeftLayout->addStretch(1);
+
     // connect signals
     connect(btnBack, SIGNAL(clicked()), this, SIGNAL(goBack()));
     connectSignals();
@@ -157,3 +168,13 @@
 {
     return defautDesc;
 }
+
+void AbstractPage::triggerPageEnter()
+{
+    emit pageEnter();
+}
+
+void AbstractPage::triggerPageLeave()
+{
+    emit pageLeave();
+}
--- a/QTfrontend/ui/page/AbstractPage.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/AbstractPage.h	Sun Jan 27 00:28:57 2013 +0100
@@ -78,12 +78,35 @@
         QString * getDefautDescription();
 
     signals:
+
         /**
          * @brief This signal is emitted when going back to the previous is
          * requested - e.g. when the back-button is clicked.
          */
         void goBack();
 
+        /**
+         * @brief This signal is emitted when the page is displayed
+         */
+        void pageEnter();
+
+        /**
+         * @brief This signal is emitted when this page is left
+         */
+        void pageLeave();
+
+    public slots:
+
+        /**
+         * @brief This slot is called to trigger this page's pageEnter signal
+         */
+        void triggerPageEnter();
+
+        /**
+         * @brief This slot is called to trigger this page's pageLeave signal
+         */
+        void triggerPageLeave();
+
     protected:
         /**
          * @brief Class constructor
@@ -121,6 +144,17 @@
          * @brief Used during page construction.
          * You can implement this method in your subclass.
          *
+         * Use it to define layout (not behavior) of the page's footer to the left of the help text.
+         */
+        virtual QLayout * footerLayoutLeftDefinition()
+        {
+            return NULL;
+        };
+
+        /**
+         * @brief Used during page construction.
+         * You can implement this method in your subclass.
+         *
          * This is a good place to connect signals within your page in order
          * to get the desired page behavior.<br />
          * Keep in mind not to expose twidgets as public!
--- a/QTfrontend/ui/page/pageeditteam.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pageeditteam.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -30,25 +30,26 @@
 #include <QDebug>
 #include "SquareLabel.h"
 #include "HWApplication.h"
+#include "keybinder.h"
 
 #include "DataManager.h"
-#include "HatModel.h"
+#include "hatbutton.h"
 
 #include "pageeditteam.h"
 
 QLayout * PageEditTeam::bodyLayoutDefinition()
 {
     QGridLayout * pageLayout = new QGridLayout();
-    QTabWidget * tbw = new QTabWidget();
+    tbw = new QTabWidget();
     QWidget * page1 = new QWidget(this);
-    QWidget * page2 = new QWidget(this);
+    binder = new KeyBinder(this, tr("Select an action to choose a custom key bind for this team"), tr("Use my default"), tr("Reset all binds"));
+    connect(binder, SIGNAL(resetAllBinds()), this, SLOT(resetAllBinds()));
     tbw->addTab(page1, tr("General"));
-    tbw->addTab(page2, tr("Advanced"));
+    tbw->addTab(binder, tr("Custom Controls"));
     pageLayout->addWidget(tbw, 0, 0, 1, 3);
 
     QHBoxLayout * page1Layout = new QHBoxLayout(page1);
     page1Layout->setAlignment(Qt::AlignTop);
-    QGridLayout * page2Layout = new QGridLayout(page2);
 
 // ====== Page 1 ======
     QVBoxLayout * vbox1 = new QVBoxLayout();
@@ -61,27 +62,33 @@
     GBoxHedgehogs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
     QGridLayout * GBHLayout = new QGridLayout(GBoxHedgehogs);
 
-    HatModel * hatModel = DataManager::instance().hatModel();
+
+    GBHLayout->addWidget(new QLabel(tr("Hat")), 0, 0);
+    GBHLayout->addWidget(new QLabel(tr("Name")), 0, 1);
 
     for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
     {
-        HHHats[i] = new QComboBox(GBoxHedgehogs);
-        HHHats[i]->setModel(hatModel);
-        HHHats[i]->setIconSize(QSize(32, 37));
-        //HHHats[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents);
-        //HHHats[i]->setModelColumn(1);
-        //HHHats[i]->setMinimumWidth(132);
-        GBHLayout->addWidget(HHHats[i], i, 0);
+        HHHats[i] = new HatButton(GBoxHedgehogs);
+        GBHLayout->addWidget(HHHats[i], i + 1, 0);
 
         HHNameEdit[i] = new QLineEdit(GBoxHedgehogs);
         HHNameEdit[i]->setMaxLength(64);
         HHNameEdit[i]->setMinimumWidth(120);
-        GBHLayout->addWidget(HHNameEdit[i], i, 1);
+        HHNameEdit[i]->setFixedHeight(36);
+        HHNameEdit[i]->setWhatsThis(tr("This hedgehog's name"));
+        HHNameEdit[i]->setStyleSheet("padding: 6px;");
+        GBHLayout->addWidget(HHNameEdit[i], i + 1, 1);
 
-        btnRandomHogName[i] = addButton(":/res/dice.png", GBHLayout, i, 3, 1, 1, true);
+        btnRandomHogName[i] = addButton(":/res/dice.png", GBHLayout, i + 1, 3, 1, 1, true);
+        btnRandomHogName[i]->setFixedHeight(HHNameEdit[i]->height());
+        btnRandomHogName[i]->setWhatsThis(tr("Randomize this hedgehog's name"));
     }
 
-    btnRandomTeam = addButton(QPushButton::tr("Random Team"), GBHLayout, 9, 0);
+    btnRandomTeam = new QPushButton();
+    btnRandomTeam->setText(tr("Random Team"));
+    btnRandomTeam->setStyleSheet("padding: 6px 10px;");
+    GBHLayout->addWidget(btnRandomTeam, 9, 0, 1, 4, Qt::AlignCenter);
+    btnRandomTeam->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 
     vbox1->addWidget(GBoxHedgehogs);
 
@@ -157,52 +164,6 @@
     vbox1->addStretch();
     vbox2->addStretch();
 
-// ====== Page 2 ======
-    GBoxBinds = new QGroupBox(this);
-    GBoxBinds->setTitle(QGroupBox::tr("Key binds"));
-    QGridLayout * GBBLayout = new QGridLayout(GBoxBinds);
-    BindsBox = new QToolBox(GBoxBinds);
-    BindsBox->setLineWidth(0);
-    GBBLayout->addWidget(BindsBox);
-    page2Layout->addWidget(GBoxBinds, 0, 0);
-
-    quint16 i = 0;
-    quint16 num = 0;
-    QWidget * curW = NULL;
-    QGridLayout * pagelayout = NULL;
-    QLabel* l = NULL;
-    while (i < BINDS_NUMBER)
-    {
-        if(cbinds[i].category != NULL)
-        {
-            if(curW != NULL)
-            {
-                l = new QLabel(curW);
-                l->setText("");
-                pagelayout->addWidget(l, num++, 0, 1, 2);
-            }
-            curW = new QWidget(this);
-            BindsBox->addItem(curW, HWApplication::translate("binds (categories)", cbinds[i].category));
-            pagelayout = new QGridLayout(curW);
-            num = 0;
-        }
-        if(cbinds[i].description != NULL)
-        {
-            l = new QLabel(curW);
-            l->setText((num > 0 ? QString("\n") : QString("")) + HWApplication::translate("binds (descriptions)", cbinds[i].description));
-            pagelayout->addWidget(l, num++, 0, 1, 2);
-        }
-
-        l = new QLabel(curW);
-        l->setText(HWApplication::translate("binds", cbinds[i].name));
-        l->setAlignment(Qt::AlignRight);
-        pagelayout->addWidget(l, num, 0);
-
-        CBBind[i] = new QComboBox(curW);
-        CBBind[i]->setModel(DataManager::instance().bindsModel());
-        pagelayout->addWidget(CBBind[i++], num++, 1);
-    }
-
     return pageLayout;
 }
 
@@ -213,7 +174,7 @@
 
 void PageEditTeam::connectSignals()
 {
-    connect(this, SIGNAL(goBack()), this, SLOT(saveTeam()));
+    connect(this, SIGNAL(pageLeave()), this, SLOT(saveTeam()));
 
     signalMapper1 = new QSignalMapper(this);
     signalMapper2 = new QSignalMapper(this);
@@ -407,6 +368,9 @@
 
 void PageEditTeam::loadTeam(const HWTeam & team)
 {
+    tbw->setCurrentIndex(0);
+    binder->resetInterface();
+
     TeamNameEdit->setText(team.name());
     CBTeamLvl->setCurrentIndex(team.difficulty());
 
@@ -419,7 +383,7 @@
         if (hh.Hat.startsWith("Reserved"))
             hh.Hat = "Reserved "+hh.Hat.remove(0,40);
 
-        HHHats[i]->setCurrentIndex(HHHats[i]->findData(hh.Hat, Qt::DisplayRole));
+        HHHats[i]->setCurrentHat(hh.Hat);
     }
 
     CBGrave->setCurrentIndex(CBGrave->findText(team.grave()));
@@ -431,10 +395,12 @@
     QStandardItemModel * binds = DataManager::instance().bindsModel();
     for(int i = 0; i < BINDS_NUMBER; i++)
     {
+        if (team.keyBind(i).isEmpty()) continue;
+
         QModelIndexList mdl = binds->match(binds->index(0, 0), Qt::UserRole + 1, team.keyBind(i), 1, Qt::MatchExactly);
 
         if(mdl.size() == 1)
-            CBBind[i]->setCurrentIndex(mdl[0].row());
+            binder->setBindIndex(i, mdl[0].row());
         else
             qDebug() << "Binds: cannot find" << team.keyBind(i);
     }
@@ -449,7 +415,7 @@
     {
         HWHog hh;
         hh.Name = HHNameEdit[i]->text();
-        hh.Hat = HHHats[i]->currentText();
+        hh.Hat = HHHats[i]->currentHat();
 
         if (hh.Hat.startsWith("Reserved"))
             hh.Hat = "Reserved"+m_playerHash+hh.Hat.remove(0,9);
@@ -465,7 +431,7 @@
     QStandardItemModel * binds = DataManager::instance().bindsModel();
     for(int i = 0; i < BINDS_NUMBER; i++)
     {
-        team.bindKey(i, binds->index(CBBind[i]->currentIndex(), 0).data(Qt::UserRole + 1).toString());
+        team.bindKey(i, binds->index(binder->bindIndex(i), 0).data(Qt::UserRole + 1).toString());
     }
 
     return team;
@@ -475,3 +441,10 @@
 {
     data().saveToFile();
 }
+
+// When the "Use default for all binds" is pressed...
+void PageEditTeam::resetAllBinds()
+{
+    for (int i = 0; i < BINDS_NUMBER; i++)
+        binder->setBindIndex(i, 0);
+}
--- a/QTfrontend/ui/page/pageeditteam.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pageeditteam.h	Sun Jan 27 00:28:57 2013 +0100
@@ -28,6 +28,8 @@
 #include "team.h"
 
 class SquareLabel;
+class KeyBinder;
+class HatButton;
 
 class PageEditTeam : public AbstractPage
 {
@@ -44,6 +46,7 @@
         void CBFort_activated(const QString & gravename);
 
     private:
+        QTabWidget * tbw;
         QSignalMapper* signalMapper1;
         QSignalMapper* signalMapper2;
         QGroupBox *GBoxHedgehogs;
@@ -59,10 +62,10 @@
         QToolBox *BindsBox;
         QLineEdit * TeamNameEdit;
         QLineEdit * HHNameEdit[HEDGEHOGS_PER_TEAM];
-        QComboBox * HHHats[HEDGEHOGS_PER_TEAM];
-        QComboBox * CBBind[BINDS_NUMBER];
+        HatButton * HHHats[HEDGEHOGS_PER_TEAM];
         HWTeam data();
         QString m_playerHash;
+        KeyBinder * binder;
 
         QLayout * bodyLayoutDefinition();
         QLayout * footerLayoutDefinition();
@@ -85,6 +88,7 @@
         void testSound();
 
         void fixHHname(int idx);
+        void resetAllBinds();
 };
 
 #endif
--- a/QTfrontend/ui/page/pagefeedback.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,400 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include <QHBoxLayout>
-#include <QLineEdit>
-#include <QTextBrowser>
-#include <QLabel>
-#include <QHttp>
-#include <QSysInfo>
-#include <QDebug>
-#include <QBuffer>
-#include <QApplication>
-#include <QDesktopWidget>
-#include <QNetworkReply>
-#include <QProcess>
-#include <QMessageBox>
-#include <QCheckBox>
-
-#include <string>
-
-#ifdef Q_WS_WIN
-#define WINVER 0x0500
-#include <windows.h>
-#else
-#include <unistd.h>
-#include <sys/types.h>
-#endif
-
-#ifdef Q_WS_MAC
-#include <sys/sysctl.h>
-#endif
-
-#include <stdint.h>
-
-#include "pagefeedback.h"
-#include "hwconsts.h"
-
-QLayout * PageFeedback::bodyLayoutDefinition()
-{
-    QVBoxLayout * pageLayout = new QVBoxLayout();
-    QHBoxLayout * summaryLayout = new QHBoxLayout();
-    QHBoxLayout * emailLayout = new QHBoxLayout();
-    QHBoxLayout * descriptionLayout = new QHBoxLayout();
-    QHBoxLayout * combinedTopLayout = new QHBoxLayout();
-    QHBoxLayout * systemLayout = new QHBoxLayout();
-
-    info = new QLabel();
-    info->setText(
-        "<style type=\"text/css\">"
-        "a { color: #fc0; }"
-        "b { color: #0df; }"
-        "</style>"
-        "<div align=\"center\"><h1>Please give us feedback!</h1>"
-        "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
-        "<h4>Your email address is optional, but we may want to contact you.<h4>"
-        "</div>"
-    );
-    pageLayout->addWidget(info);
-
-    QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
-
-    const int labelWidth = 90;
-
-    label_email = new QLabel();
-    label_email->setText(QLabel::tr("Your Email"));
-    label_email->setFixedWidth(labelWidth);
-    emailLayout->addWidget(label_email);
-    email = new QLineEdit();
-    emailLayout->addWidget(email);
-    summaryEmailLayout->addLayout(emailLayout);
-
-    label_summary = new QLabel();
-    label_summary->setText(QLabel::tr("Summary"));
-    label_summary->setFixedWidth(labelWidth);
-    summaryLayout->addWidget(label_summary);
-    summary = new QLineEdit();
-    summaryLayout->addWidget(summary);
-    summaryEmailLayout->addLayout(summaryLayout);
-
-    combinedTopLayout->addLayout(summaryEmailLayout);
-
-
-    CheckSendSpecs = new QCheckBox();
-    CheckSendSpecs->setText(QLabel::tr("Send system information"));
-    CheckSendSpecs->setChecked(true);
-    systemLayout->addWidget(CheckSendSpecs);
-    BtnViewInfo = addButton("View", systemLayout, 1, false);
-    BtnViewInfo->setFixedSize(60, 30);
-    connect(BtnViewInfo, SIGNAL(clicked()), this, SLOT(ShowSpecs()));
-    combinedTopLayout->addLayout(systemLayout);
-
-    combinedTopLayout->setStretch(0, 1);
-    combinedTopLayout->insertSpacing(1, 20);
-
-    pageLayout->addLayout(combinedTopLayout);
-
-    label_description = new QLabel();
-    label_description->setText(QLabel::tr("Description"));
-    label_description->setFixedWidth(labelWidth);
-    descriptionLayout->addWidget(label_description, 0, Qt::AlignTop);
-    description = new QTextBrowser();
-    description->setReadOnly(false);
-    descriptionLayout->addWidget(description);
-    pageLayout->addLayout(descriptionLayout);
-
-    return pageLayout;
-}
-
-QNetworkAccessManager * PageFeedback::GetNetManager()
-{
-    if (netManager) return netManager;
-    netManager = new QNetworkAccessManager(this);
-    connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(NetReply(QNetworkReply*)));
-    return netManager;
-}
-
-void PageFeedback::LoadCaptchaImage()
-{
-        QNetworkAccessManager *netManager = GetNetManager();
-        QUrl captchaURL("http://hedgewars.org/feedback/?gencaptcha");
-        QNetworkRequest req(captchaURL);
-        genCaptchaRequest = netManager->get(req);
-}
-
-void PageFeedback::NetReply(QNetworkReply *reply)
-{
-    if (reply == genCaptchaRequest)
-    {
-        if (reply->error() != QNetworkReply::NoError)
-        {
-            qDebug() << "Error generating captcha image: " << reply->errorString();
-            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
-            return;
-        }
-
-        bool okay;
-        QByteArray body = reply->readAll();
-        captchaID = QString(body).toInt(&okay);
-
-        if (!okay)
-        {
-            qDebug() << "Failed to get captcha ID: " << body;
-            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
-            return;
-        }
-
-        QString url = "http://hedgewars.org/feedback/?captcha&id=";
-        url += QString::number(captchaID);
-
-        QNetworkAccessManager *netManager = GetNetManager();
-        QUrl captchaURL(url);
-        QNetworkRequest req(captchaURL);
-        captchaImageRequest = netManager->get(req);
-    }
-    else if (reply == captchaImageRequest)
-    {
-        if (reply->error() != QNetworkReply::NoError)
-        {
-            qDebug() << "Error loading captcha image: " << reply->errorString();
-            ShowErrorMessage(QMessageBox::tr("Failed to download captcha"));
-            return;
-        }
-
-        QByteArray imageData = reply->readAll();
-        QPixmap pixmap;
-        pixmap.loadFromData(imageData);
-        label_captcha->setPixmap(pixmap);
-        captcha_code->setText("");
-    }
-}
-
-QLayout * PageFeedback::footerLayoutDefinition()
-{
-    QHBoxLayout * bottomLayout = new QHBoxLayout();
-    QHBoxLayout * captchaLayout = new QHBoxLayout();
-    QVBoxLayout * captchaInputLayout = new QVBoxLayout();
-
-    label_captcha = new QLabel();
-    label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
-    label_captcha->setText("<div style='width: 200px; height: 100px;'>loading<br>captcha</div>");
-    captchaLayout->addWidget(label_captcha);
-
-    label_captcha_input = new QLabel();
-    label_captcha_input->setText(QLabel::tr("Type the security code:"));
-    captchaInputLayout->addWidget(label_captcha_input);
-    captchaInputLayout->setAlignment(label_captcha, Qt::AlignBottom);
-    captcha_code = new QLineEdit();
-    captcha_code->setFixedSize(165, 30);
-    captchaInputLayout->addWidget(captcha_code);
-    captchaInputLayout->setAlignment(captcha_code, Qt::AlignTop);
-    captchaLayout->addLayout(captchaInputLayout);
-    captchaLayout->setAlignment(captchaInputLayout, Qt::AlignLeft);
-
-    captchaLayout->insertSpacing(-1, 40);
-    bottomLayout->addLayout(captchaLayout);
-
-    //TODO: create logo for send button
-    BtnSend = addButton("Send Feedback", bottomLayout, 0, false);
-    BtnSend->setFixedSize(120, 40);
-
-    bottomLayout->setStretchFactor(captchaLayout, 0);
-    bottomLayout->setStretchFactor(BtnSend, 1);
-
-    return bottomLayout;
-}
-
-void PageFeedback::GenerateSpecs()
-{
-    // Gather some information about the system and embed it into the report
-    QDesktopWidget* screen = QApplication::desktop();
-    QString os_version = "Operating system: ";
-    QString qt_version = QString("Qt version: ") + QT_VERSION_STR + QString("\n");
-    QString total_ram = "Total RAM: ";
-    QString number_of_cores = "Number of cores: ";
-    QString compiler_bits = "Compiler architecture: ";
-    QString compiler_version = "Compiler version: ";
-    QString kernel_line = "Kernel: ";
-    QString screen_size = "Size of the screen(s): " +
-        QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
-    QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
-    std::string processor_name = "Processor: ";
-
-    // platform specific code
-#ifdef Q_WS_MACX
-    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
-
-    uint64_t memsize;
-    size_t len = sizeof(memsize);
-    static int mib_s[2] = { CTL_HW, HW_MEMSIZE };
-    if (sysctl (mib_s, 2, &memsize, &len, NULL, 0) == 0)
-        total_ram += QString::number(memsize/1024/1024) + " MB\n";
-    else
-        total_ram += "Error getting total RAM information\n";
-
-    int mib[] = {CTL_KERN, KERN_OSRELEASE};
-    sysctl(mib, sizeof mib / sizeof(int), NULL, &len, NULL, 0);
-
-    char *kernelVersion = (char *)malloc(sizeof(char)*len);
-    sysctl(mib, sizeof mib / sizeof(int), kernelVersion, &len, NULL, 0);
-
-    QString kernelVersionStr = QString(kernelVersion);
-    free(kernelVersion);
-    int major_version = kernelVersionStr.split(".").first().toUInt() - 4;
-    int minor_version = kernelVersionStr.split(".").at(1).toUInt();
-    os_version += QString("Mac OS X 10.%1.%2").arg(major_version).arg(minor_version) + " ";
-
-    switch(major_version)
-    {
-        case 4:  os_version += "\"Tiger\"\n"; break;
-        case 5:  os_version += "\"Leopard\"\n"; break;
-        case 6:  os_version += "\"Snow Leopard\"\n"; break;
-        case 7:  os_version += "\"Lion\"\n"; break;
-        case 8:  os_version += "\"Mountain Lion\"\n"; break;
-        default: os_version += "\"Unknown version\"\n"; break;
-    }
-#endif
-#ifdef Q_WS_WIN
-    SYSTEM_INFO sysinfo;
-    GetSystemInfo(&sysinfo);
-    number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
-    MEMORYSTATUSEX status;
-    status.dwLength = sizeof(status);
-    GlobalMemoryStatusEx(&status);
-    total_ram += QString::number(status.ullTotalPhys);
-
-    switch(QSysInfo::WinVersion())
-    {
-        case QSysInfo::WV_2000: os_version += "Windows 2000\n"; break;
-        case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
-        case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
-        case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
-        default: os_version += "Windows (Unknown version)\n"; break;
-    }
-    kernel_line += "Windows kernel\n";
-#endif
-#ifdef Q_WS_X11
-    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
-    long pages = sysconf(_SC_PHYS_PAGES),
-#ifndef Q_OS_FREEBSD
-         available_pages = sysconf(_SC_AVPHYS_PAGES),
-#else
-         available_pages = 0,
-#endif
-         page_size = sysconf(_SC_PAGE_SIZE);
-    total_ram += QString::number(pages * page_size) + "\n";
-    os_version += "GNU/Linux or BSD\n";
-#endif
-
-    // uname -a
-#if defined(Q_WS_X11) || defined(Q_WS_MACX)
-    QProcess *process = new QProcess();
-    QStringList arguments = QStringList("-a");
-    process->start("uname", arguments);
-    if (process->waitForFinished())
-        kernel_line += QString(process->readAll());
-    delete process;
-#endif
-
-    // cpu info
-    quint32 registers[4];
-    quint32 i;
-
-    i = 0x80000002;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
-    i = 0x80000003;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
-    i = 0x80000004;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 3);
-
-    // compiler
-#ifdef __GNUC__
-    compiler_version += "GCC " + QString(__VERSION__) + "\n";
-#else
-    compiler_version += "Unknown\n";
-#endif
-
-    if(sizeof(void*) == 4)
-        compiler_bits += "i386\n";
-    else if(sizeof(void*) == 8)
-        compiler_bits += "x86_64\n";
-
-    // concat system info
-    specs = qt_version
-        + os_version
-        + total_ram
-        + screen_size
-        + number_of_screens
-        + QString::fromStdString(processor_name + "\n")
-        + number_of_cores
-        + compiler_version
-        + compiler_bits
-        + kernel_line;
-}
-
-void PageFeedback::connectSignals()
-{
-    //TODO
-}
-
-void PageFeedback::ShowErrorMessage(const QString & msg)
-{
-    QMessageBox msgMsg(this);
-    msgMsg.setIcon(QMessageBox::Warning);
-    msgMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Error"));
-    msgMsg.setText(msg);
-    msgMsg.setWindowModality(Qt::WindowModal);
-    msgMsg.exec();
-}
-
-void PageFeedback::ShowSpecs()
-{
-    QMessageBox msgMsg(this);
-    msgMsg.setIcon(QMessageBox::Information);
-    msgMsg.setWindowTitle(QMessageBox::tr("System Information Preview"));
-    msgMsg.setText(specs);
-    msgMsg.setTextFormat(Qt::PlainText);
-    msgMsg.setWindowModality(Qt::WindowModal);
-    msgMsg.setStyleSheet("background: #0A0533;");
-    msgMsg.exec();
-}
-
-PageFeedback::PageFeedback(QWidget* parent) : AbstractPage(parent)
-{
-    initPage();
-    netManager = NULL;
-    GenerateSpecs();
-}
--- a/QTfrontend/ui/page/pagefeedback.h	Wed Jan 02 11:11:49 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#ifndef PAGE_FEEDBACK_H
-#define PAGE_FEEDBACK_H
-
-#include "AbstractPage.h"
-
-class QNetworkReply;
-class QNetworkAccessManager;
-
-class PageFeedback : public AbstractPage
-{
-        Q_OBJECT
-
-    public:
-        PageFeedback(QWidget * parent = 0);
-        void EmbedSystemInfo();
-        void LoadCaptchaImage();
-
-        QPushButton * BtnSend;
-        QPushButton * BtnViewInfo;
-        QCheckBox * CheckSendSpecs;
-        QLineEdit * summary;
-        QTextBrowser * description;
-        QLabel * info;
-        QLabel * label_summary;
-        QLabel * label_description;
-        QLabel * label_captcha;
-        QLabel * label_email;
-        QLabel * label_captcha_input;
-        QLineEdit * captcha_code;
-        QLineEdit * email;
-        int captchaID;
-        QString specs;
-
-    private slots:
-
-        virtual void NetReply(QNetworkReply*);
-        virtual void ShowSpecs();
-
-    private:
-        void GenerateSpecs();
-        QLayout * bodyLayoutDefinition();
-        QLayout * footerLayoutDefinition();
-        QNetworkAccessManager * GetNetManager();
-        void ShowErrorMessage(const QString & msg);
-        void connectSignals();
-
-        QNetworkAccessManager * netManager;
-        QNetworkReply * captchaImageRequest;
-        QNetworkReply * genCaptchaRequest;
-};
-
-#endif
--- a/QTfrontend/ui/page/pagegamestats.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagegamestats.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -110,6 +110,7 @@
 
 void PageGameStats::connectSignals()
 {
+    connect(this, SIGNAL(pageEnter()), this, SLOT(renderStats()));
     connect(btnSave, SIGNAL(clicked()), this, SIGNAL(saveDemoRequested()));
 }
 
--- a/QTfrontend/ui/page/pagemain.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagemain.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -42,16 +42,13 @@
     pageLayout->setRowStretch(4, 1);
 
     BtnSinglePlayer = addButton(":/res/LocalPlay.png", pageLayout, 2, 0, 1, 2, true);
-    BtnSinglePlayer->setToolTip(tr("Local Game"));
     BtnSinglePlayer->setWhatsThis(tr("Play a game on a single computer"));
     pageLayout->setAlignment(BtnSinglePlayer, Qt::AlignHCenter);
 
     //BtnNet = addButton(":/res/NetworkPlay.png", (QBoxLayout*)netLayout, 1, true);
     BtnNet = addButton(":/res/NetworkPlay.png", pageLayout, 2, 2, 1, 2, true);
-    BtnNet->setToolTip(tr("Network Game"));
     BtnNet->setWhatsThis(tr("Play a game across a network"));
     pageLayout->setAlignment(BtnNet, Qt::AlignHCenter);
-    connect(BtnNet, SIGNAL(clicked()), this, SLOT(toggleNetworkChoice()));
 
     originalNetworkIcon = BtnNet->icon();
     disabledNetworkIcon = QIcon(":/res/NetworkPlayDisabled.png");
@@ -65,13 +62,11 @@
     netLayout->setAlignment(Qt::AlignHCenter);
 
     BtnNetLocal = addButton("Play local network game", (QBoxLayout*)netLayout, 0, false);
-    BtnNetLocal->setToolTip(tr("Play a local network game"));
     BtnNetLocal->setWhatsThis(tr("Play a game across a local area network"));
     BtnNetLocal->setFixedSize(BtnNet->width() - 50, 60);
     BtnNetLocal->setVisible(false);
 
     BtnNetOfficial = addButton("Play official network game", (QBoxLayout*)netLayout, 0, false);
-    BtnNetOfficial->setToolTip(tr("Play a network game"));
     BtnNetOfficial->setWhatsThis(tr("Play a game on an official server"));
     BtnNetOfficial->setFixedSize(BtnNet->width() - 50, 60);
     BtnNetOfficial->setVisible(false);
@@ -79,7 +74,6 @@
     // button order matters for overlapping (what's on top and what isn't)
     BtnInfo = addButton(":/res/HedgewarsTitle.png", pageLayout, 0, 0, 1, 4, true);
     BtnInfo->setStyleSheet("border: transparent;background: transparent;");
-    //BtnInfo->setToolTip(tr("Credits")); //tooltip looks horrible with transparent background buttons
     BtnInfo->setWhatsThis(tr("Read about who is behind the Hedgewars Project"));
     pageLayout->setAlignment(BtnInfo, Qt::AlignHCenter);
 
@@ -90,7 +84,6 @@
 
     BtnDataDownload = addButton(tr("Downloadable Content"), pageLayout, 5, 0, 1, 4, false);
     BtnDataDownload->setFixedSize(176, 27);
-    //BtnDataDownload->setToolTip(tr(Downloadable Content"));
     BtnDataDownload->setWhatsThis(tr("Access the user created content downloadable from our website"));
     pageLayout->setAlignment(BtnDataDownload, Qt::AlignHCenter);
 
@@ -126,7 +119,8 @@
 
 void PageMain::connectSignals()
 {
-    //TODO
+    connect(BtnNet, SIGNAL(clicked()), this, SLOT(toggleNetworkChoice()));
+    // TODO: add signal-forwarding required by (currently missing) encapsulation
 }
 
 PageMain::PageMain(QWidget* parent) : AbstractPage(parent)
@@ -149,64 +143,41 @@
 
 QString PageMain::randomTip() const
 {
+#ifdef _WIN32
+    int platform = 1;
+#elif defined __APPLE__
+    int platform = 2;
+#else
+    int platform = 3;
+#endif
     QStringList Tips;
-    Tips << tr("Simply pick the same color as a friend to play together as a team. Each of you will still control his or her own hedgehogs but they'll win or lose together.", "Tips");
-    Tips << tr("Some weapons might do only low damage but they can be a lot more devastating in the right situation. Try to use the Desert Eagle to knock multiple hedgehogs into the water.", "Tips");
-    Tips << tr("If you're unsure what to do and don't want to waste ammo, skip one round. But don't let too much time pass as there will be Sudden Death!", "Tips");
-    Tips << tr("Want to save ropes? Release the rope in mid air and then shoot again. As long as you don't touch the ground you'll reuse your rope without wasting ammo!", "Tips");
-    Tips << tr("If you'd like to keep others from using your preferred nickname on the official server, register an account at http://www.hedgewars.org/.", "Tips");
-    Tips << tr("You're bored of default gameplay? Try one of the missions - they'll offer different gameplay depending on the one you picked.", "Tips");
-    Tips << tr("By default the game will always record the last game played as a demo. Select 'Local Game' and pick the 'Demos' button on the lower right corner to play or manage them.", "Tips");
-    Tips << tr("Hedgewars is Open Source and Freeware we create in our spare time. If you've got problems, ask on our forums but please don't expect 24/7 support!", "Tips");
-    Tips << tr("Hedgewars is Open Source and Freeware we create in our spare time. If you like it, help us with a small donation or contribute your own work!", "Tips");
-    Tips << tr("Hedgewars is Open Source and Freeware we create in our spare time. Share it with your family and friends as you like!", "Tips");
-    Tips << tr("Hedgewars is Open Source and Freeware we create in our spare time. If someone sold you the game, you should try get a refund!", "Tips");
-    Tips << tr("From time to time there will be official tournaments. Upcoming events will be announced at http://www.hedgewars.org/ some days in advance.", "Tips");
-    Tips << tr("Hedgewars is available in many languages. If the translation in your language seems to be missing or outdated, feel free to contact us!", "Tips");
-    Tips << tr("Hedgewars can be run on lots of different operating systems including Microsoft Windows, Mac OS X and Linux.", "Tips");
-    Tips << tr("Always remember you're able to set up your own games in local and network/online play. You're not restricted to the 'Simple Game' option.", "Tips");
-    Tips << tr("Connect one or more gamepads before starting the game to be able to assign their controls to your teams.", "Tips");
-    Tips << tr("Create an account on %1 to keep others from using your most favourite nickname while playing on the official server.", "Tips").arg("<a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a>");
-    Tips << tr("While playing you should give yourself a short break at least once an hour.", "Tips");
-    Tips << tr("If your graphics card isn't able to provide hardware accelerated OpenGL, try to enable the low quality mode to improve performance.", "Tips");
-    Tips << tr("If your graphics card isn't able to provide hardware accelerated OpenGL, try to update the associated drivers.", "Tips");
-    Tips << tr("We're open to suggestions and constructive feedback. If you don't like something or got a great idea, let us know!", "Tips");
-    Tips << tr("Especially while playing online be polite and always remember there might be some minors playing with or against you as well!", "Tips");
-    Tips << tr("Special game modes such as 'Vampirism' or 'Karma' allow you to develop completely new tactics. Try them in a custom game!", "Tips");
-    Tips << tr("The Windows version of Hedgewars supports Xfire. Make sure to add Hedgewars to its game list so your friends can see you playing.", "Tips");
-    Tips << tr("You should never install Hedgewars on computers you don't own (school, university, work, etc.). Please ask the responsible person instead!", "Tips");
-    Tips << tr("Hedgewars can be perfect for short games during breaks. Just ensure you don't add too many hedgehogs or use an huge map. Reducing time and health might help as well.", "Tips");
-    Tips << tr("No hedgehogs were harmed in making this game.", "Tips");
-    Tips << tr("There are three different jumps available. Tap [high jump] twice to do a very high/backwards jump.", "Tips");
-    Tips << tr("Afraid of falling off a cliff? Hold down [precise] to turn [left] or [right] without actually moving.", "Tips");
-    Tips << tr("Some weapons require special strategies or just lots of training, so don't give up on a particular tool if you miss an enemy once.", "Tips");
-    Tips << tr("Most weapons won't work once they touch the water. The Homing Bee as well as the Cake are exceptions to this.", "Tips");
-    Tips << tr("The Old Limbuger only causes a small explosion. However the wind affected smelly cloud can poison lots of hogs at once.", "Tips");
-    Tips << tr("The Piano Strike is the most damaging air strike. You'll lose the hedgehog performing it, so there's a huge downside as well.", "Tips");
-    Tips << tr("The Homing Bee can be tricky to use. Its turn radius depends on its velocity, so try to not use full power.", "Tips");
-    Tips << tr("Sticky Mines are a perfect tool to create small chain reactions knocking enemy hedgehogs into dire situations ... or water.", "Tips");
-    Tips << tr("The Hammer is most effective when used on bridges or girders. Hit hogs will just break through the ground.", "Tips");
-    Tips << tr("If you're stuck behind an enemy hedgehog, use the Hammer to free yourself without getting damaged by an explosion.", "Tips");
-    Tips << tr("The Cake's maximum walking distance depends on the ground it has to pass. Use [attack] to detonate it early.", "Tips");
-    Tips << tr("The Flame Thrower is a weapon but it can be used for tunnel digging as well.", "Tips");
-    Tips << tr("Use the Molotov or Flame Thrower to temporary keep hedgehogs from passing terrain such as tunnels or platforms.", "Tips");
-    Tips << tr("Want to know who's behind the game? Click on the Hedgewars logo in the main menu to see the credits.", "Tips");
-    Tips << tr("Like Hedgewars? Become a fan on %1 or follow us on %2!", "Tips").arg("<a href=\"http://www.facebook.com/Hedgewars\">Facebook</a>").arg("<a href=\"http://twitter.com/hedgewars\">Twitter</a>");
-    Tips << tr("Feel free to draw your own graves, hats, flags or even maps and themes! But note that you'll have to share them somewhere to use them online.", "Tips");
-    Tips << tr("Really want to wear a specific hat? Donate to us and receive an exclusive hat of your choice!", "Tips");
+    QFile file(":/res/xml/tips.xml");
+    file.open(QIODevice::ReadOnly);
+    QTextStream in(&file);
+    QString line = in.readLine();
+    int tip_platform = 0;
+    while (!line.isNull()) {
+        if(line.contains("<windows-only>", Qt::CaseSensitive))
+            tip_platform = 1;
+        if(line.contains("<mac-only>", Qt::CaseSensitive))
+            tip_platform = 2;
+        if(line.contains("<linux-only>", Qt::CaseSensitive))
+            tip_platform = 3;
+        if(line.contains("</windows-only>", Qt::CaseSensitive) ||
+                line.contains("</mac-only>", Qt::CaseSensitive) ||
+                line.contains("</linux-only>", Qt::CaseSensitive)) {
+            tip_platform = 0;
+        }
+        QStringList split_string = line.split(QRegExp("</?tip>"));
+        if((tip_platform == platform || tip_platform == 0) && split_string.size() != 1)
+            Tips << tr(split_string[1].toLatin1().data(), "Tips");
+        line = in.readLine();
+    }
     // The following tip will require links to app store entries first.
     //Tips << tr("Want to play Hedgewars any time? Grab the Mobile version for %1 and %2.", "Tips").arg("").arg("");
     // the ios version is located here: http://itunes.apple.com/us/app/hedgewars/id391234866
-    Tips << tr("Keep your video card drivers up to date to avoid issues playing the game.", "Tips");
-    Tips << tr("You're able to associate Hedgewars related files (savegames and demo recordings) with the game to launch them right from your favorite file or internet browser.", "Tips");
-#ifdef _WIN32
-    Tips << tr("You can find your Hedgewars configuration files under \"My Documents\\Hedgewars\". Create backups or take the files with you, but don't edit them by hand.", "Tips");
-#elif defined __APPLE__
-    Tips << tr("You can find your Hedgewars configuration files under \"Library/Application Support/Hedgewars\" in your home directory. Create backups or take the files with you, but don't edit them by hand.", "Tips");
-#else
-    Tips << tr("You can find your Hedgewars configuration files under \".hedgewars\" in your home directory. Create backups or take the files with you, but don't edit them by hand.", "Tips");
-#endif
 
+    file.close();
     return Tips[QTime(0, 0, 0).secsTo(QTime::currentTime()) % Tips.length()];
 }
 
--- a/QTfrontend/ui/page/pagemultiplayer.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagemultiplayer.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -36,10 +36,6 @@
     gameCFG = new GameCFGWidget(this);
     pageLayout->addWidget(gameCFG, 0, 0, 1, 2);
 
-    btnSetup = new QPushButton(this);
-    btnSetup->setText(QPushButton::tr("Setup"));
-    pageLayout->addWidget(btnSetup, 1, 0, 1, 2);
-
     pageLayout->setRowStretch(2, 1);
 
     teamsSelect = new TeamSelWidget(this);
@@ -48,12 +44,30 @@
     return pageLayout;
 }
 
+QLayout * PageMultiplayer::footerLayoutLeftDefinition()
+{
+    QHBoxLayout * bottomLeftLayout = new QHBoxLayout();
+
+    btnSetup = addButton(":/res/Settings.png", bottomLeftLayout, 0, true);
+    btnSetup->setWhatsThis(tr("Edit game preferences"));
+
+    return bottomLeftLayout;
+}
+
 QLayout * PageMultiplayer::footerLayoutDefinition()
 {
     QHBoxLayout * footerLayout = new QHBoxLayout();
 
-    BtnStartMPGame = formattedButton(tr("Start"));
-    BtnStartMPGame->setMinimumWidth(180);
+    const QIcon& lp = QIcon(":/res/Start.png");
+    QSize sz = lp.actualSize(QSize(65535, 65535));
+    BtnStartMPGame = new QPushButton();
+    BtnStartMPGame->setText(tr("Start"));
+    BtnStartMPGame->setMinimumWidth(sz.width() + 60);
+    BtnStartMPGame->setIcon(lp);
+    BtnStartMPGame->setFixedHeight(50);
+    BtnStartMPGame->setIconSize(sz);
+    BtnStartMPGame->setFlat(true);
+    BtnStartMPGame->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 
     footerLayout->addStretch();
     footerLayout->addWidget(BtnStartMPGame);
--- a/QTfrontend/ui/page/pagemultiplayer.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagemultiplayer.h	Sun Jan 27 00:28:57 2013 +0100
@@ -41,6 +41,7 @@
     private:
         QLayout * bodyLayoutDefinition();
         QLayout * footerLayoutDefinition();
+        QLayout * footerLayoutLeftDefinition();
         void connectSignals();
 
         QPushButton * btnSetup;
--- a/QTfrontend/ui/page/pagenetgame.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagenetgame.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -28,63 +28,127 @@
 #include "teamselect.h"
 #include "chatwidget.h"
 
+const int cutoffHeight = 688; /* Don't make this number below 605, or else it'll
+                                 let the GameCFGWidget shrink too much before switching to tabbed mode. */
+
 QLayout * PageNetGame::bodyLayoutDefinition()
 {
     QGridLayout * pageLayout = new QGridLayout();
     pageLayout->setSizeConstraint(QLayout::SetMinimumSize);
-    //pageLayout->setSpacing(1);
-    pageLayout->setColumnStretch(0, 50);
-    pageLayout->setColumnStretch(1, 50);
+    pageLayout->setColumnStretch(0, 1);
+    pageLayout->setColumnStretch(1, 1);
+    pageLayout->setRowStretch(0, 0);
+    pageLayout->setRowStretch(1, 0);
+    pageLayout->setRowStretch(2, 1);
+
+    // Room config
+
+    QHBoxLayout * roomConfigLayout = new QHBoxLayout();
+    pageLayout->addLayout(roomConfigLayout, 0, 0, 1, 2);
+    roomConfigLayout->setSpacing(0);
+
+    leRoomName = new HistoryLineEdit(this, 10);
+    leRoomName->setMaxLength(60);
+    leRoomName->setMinimumWidth(400);
+    leRoomName->setMaximumWidth(600);
+    leRoomName->setFixedHeight(30);
+    leRoomName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    roomConfigLayout->addWidget(leRoomName, 100);
+
+    QLabel * lblRoomName = new QLabel(tr("Room name: "), leRoomName);
+    lblRoomName->setStyleSheet("font: 12px; font-weight: bold;");
+    lblRoomName->setStyleSheet(QString("font: 12px; font-weight: bold; background: none; margin-left: -%1px; margin-top: 8px;").arg(lblRoomName->width() - 20));
+    leRoomName->setStyleSheet(QString("font: 12px; border-right: 0; padding-left: %1px; padding-bottom: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px;").arg(lblRoomName->width() - 14));
+
+    BtnUpdate = new QPushButton();
+    BtnUpdate->setEnabled(false);
+    BtnUpdate->setText(tr("Update"));
+    BtnUpdate->setFixedHeight(leRoomName->height() - 0);
+    BtnUpdate->setStyleSheet("border-top-left-radius: 0px; border-bottom-left-radius: 0px; padding: auto 4px;");
+    roomConfigLayout->addWidget(BtnUpdate, 0);
 
-    // chatwidget
+    lblRoomNameReadOnly = new QLabel();
+    lblRoomNameReadOnly->setMinimumWidth(400);
+    lblRoomNameReadOnly->setMaximumWidth(600);
+    lblRoomNameReadOnly->setFixedHeight(30);
+    lblRoomNameReadOnly->setObjectName("labelLikeLineEdit");
+    lblRoomNameReadOnly->setStyleSheet("font: 12px;");
+    lblRoomNameReadOnly->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    lblRoomNameReadOnly->setVisible(false);
+    roomConfigLayout->addWidget(lblRoomNameReadOnly, 100);
+
+    roomConfigLayout->addSpacing(10);
+
+    BtnMaster = new QPushButton();
+    BtnMaster->setText(tr("Room controls"));
+    BtnMaster->setFixedHeight(leRoomName->height() - 0);
+    BtnMaster->setStyleSheet("QPushButton { padding: auto 4px; } QPushButton:pressed { background-color: #ffcc00; border-color: #ffcc00; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; color: #11084A; }");
+    roomConfigLayout->addWidget(BtnMaster, 0);
+
+    roomConfigLayout->addStretch(1);
+
+    // Game config
+
+    pGameCFG = new GameCFGWidget(this);
+    pageLayout->addWidget(pGameCFG, 1, 0);
+
+    // Teams
+
+    pNetTeamsWidget = new TeamSelWidget(this);
+    pNetTeamsWidget->setAcceptOuter(true);
+    pNetTeamsWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    pageLayout->addWidget(pNetTeamsWidget, 1, 1);
+
+    // Chat
+
     chatWidget = new HWChatWidget(this, true);
     chatWidget->setShowFollow(false); // don't show follow in nicks' context menus
     chatWidget->setIgnoreListKick(true); // kick ignored players automatically
+    chatWidget->setMinimumHeight(50);
+    chatWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
     pageLayout->addWidget(chatWidget, 2, 0, 1, 2);
-    pageLayout->setRowStretch(1, 100);
-    pageLayout->setRowStretch(2, 100);
-
-    pGameCFG = new GameCFGWidget(this);
-    pageLayout->addWidget(pGameCFG, 0, 0);
-
-    btnSetup = new QPushButton(this);
-    btnSetup->setText(QPushButton::tr("Setup"));
-    pageLayout->addWidget(btnSetup, 1, 0);
-
-    pNetTeamsWidget = new TeamSelWidget(this);
-    pNetTeamsWidget->setAcceptOuter(true);
-    pageLayout->addWidget(pNetTeamsWidget, 0, 1, 2, 1);
 
     return pageLayout;
 }
 
+QLayout * PageNetGame::footerLayoutLeftDefinition()
+{
+    QHBoxLayout * bottomLeftLayout = new QHBoxLayout();
+
+    btnSetup = addButton(":/res/Settings.png", bottomLeftLayout, 0, true);
+    btnSetup->setWhatsThis(tr("Edit game preferences"));
+
+    return bottomLeftLayout;
+}
+
 QLayout * PageNetGame::footerLayoutDefinition()
 {
     QHBoxLayout * bottomLayout = new QHBoxLayout;
 
-    leRoomName = new HistoryLineEdit(this,10);
-    leRoomName->setMaxLength(60);
-    leRoomName->setMinimumWidth(200);
-    leRoomName->setMaximumWidth(400);
+    // Ready button
 
-    //Button to signify whether the player is ready to start playing
     BtnGo = new QPushButton(this);
-    BtnGo->setToolTip(QPushButton::tr("Ready"));
     BtnGo->setIcon(QIcon(":/res/lightbulb_off.png"));
     BtnGo->setIconSize(QSize(25, 34));
     BtnGo->setMinimumWidth(50);
     BtnGo->setMinimumHeight(50);
 
-    bottomLayout->addWidget(leRoomName);
-    BtnUpdate = addButton(QAction::tr("Update"), bottomLayout, 1);
-
     bottomLayout->addStretch();
     bottomLayout->addWidget(BtnGo);
 
-    BtnMaster = addButton(tr("Control"), bottomLayout, 3);
-    bottomLayout->insertStretch(3, 100);
+    // Start button
 
-    BtnStart = addButton(QAction::tr("Start"), bottomLayout, 3);
+    const QIcon& lp = QIcon(":/res/Start.png");
+    QSize sz = lp.actualSize(QSize(65535, 65535));
+    BtnStart = new QPushButton();
+    BtnStart->setText(tr("Start"));
+    BtnStart->setMinimumWidth(sz.width() + 60);
+    BtnStart->setIcon(lp);
+    BtnStart->setFixedHeight(50);
+    BtnStart->setIconSize(sz);
+    BtnStart->setFlat(true);
+    BtnStart->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    bottomLayout->addWidget(BtnStart);
 
     return bottomLayout;
 }
@@ -94,6 +158,9 @@
     connect(btnSetup, SIGNAL(clicked()), this, SIGNAL(SetupClicked()));
 
     connect(BtnUpdate, SIGNAL(clicked()), this, SLOT(onUpdateClick()));
+    connect(leRoomName, SIGNAL(returnPressed()), this, SLOT(onUpdateClick()));
+
+    connect(leRoomName, SIGNAL(textChanged(const QString &)), this, SLOT(onRoomNameEdited()));
 }
 
 PageNetGame::PageNetGame(QWidget* parent) : AbstractPage(parent)
@@ -106,13 +173,25 @@
     restrictJoins->setCheckable(true);
     restrictTeamAdds = new QAction(QAction::tr("Restrict Team Additions"), menu);
     restrictTeamAdds->setCheckable(true);
-    //menu->addAction(startGame);
     menu->addAction(restrictJoins);
     menu->addAction(restrictTeamAdds);
 
     BtnMaster->setMenu(menu);
+
+    if (height() < cutoffHeight)
+        pGameCFG->setTabbed(true);
 }
 
+void PageNetGame::resizeEvent(QResizeEvent * event)
+{
+    int oldHeight = event->oldSize().height();
+    int newHeight = event->size().height();
+
+    if (newHeight < cutoffHeight && oldHeight >= cutoffHeight)
+        pGameCFG->setTabbed(true);
+    else if (newHeight >= cutoffHeight && oldHeight < cutoffHeight)
+        pGameCFG->setTabbed(false);
+}
 
 void PageNetGame::displayError(const QString & message)
 {
@@ -139,12 +218,18 @@
         BtnGo->setIcon(QIcon(":/res/lightbulb_off.png"));
 }
 
+void PageNetGame::onRoomNameEdited()
+{
+    BtnUpdate->setEnabled(true);
+}
+
 void PageNetGame::onUpdateClick()
 {
     if (!leRoomName->text().trimmed().isEmpty())
     {
         emit askForUpdateRoomName(leRoomName->text());
         leRoomName->rememberCurrentText();
+        BtnUpdate->setEnabled(false);
     }
     else
     {
@@ -163,6 +248,8 @@
 {
     leRoomName->setText(roomName);
     leRoomName->rememberCurrentText();
+    lblRoomNameReadOnly->setText(roomName);
+    BtnUpdate->setEnabled(false);
 }
 
 void PageNetGame::setMasterMode(bool isMaster)
@@ -171,6 +258,7 @@
     BtnStart->setVisible(isMaster);
     BtnUpdate->setVisible(isMaster);
     leRoomName->setVisible(isMaster);
+    lblRoomNameReadOnly->setVisible(!isMaster);
 }
 
 void PageNetGame::setUser(const QString & nickname)
--- a/QTfrontend/ui/page/pagenetgame.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagenetgame.h	Sun Jan 27 00:28:57 2013 +0100
@@ -48,6 +48,7 @@
         QPushButton *BtnMaster;
         QPushButton *BtnStart;
         QPushButton *BtnUpdate;
+        HistoryLineEdit *leRoomName;
 
         QAction * restrictJoins;
         QAction * restrictTeamAdds;
@@ -63,17 +64,24 @@
         void onUpdateClick();
         void setMasterMode(bool isMaster);
 
+    private slots:
+        void onRoomNameEdited();
+
     signals:
         void SetupClicked();
         void askForUpdateRoomName(const QString &);
 
+    protected:
+        void resizeEvent(QResizeEvent * event);
+
     private:
         QLayout * bodyLayoutDefinition();
         QLayout * footerLayoutDefinition();
+        QLayout * footerLayoutLeftDefinition();
         void connectSignals();
 
-        HistoryLineEdit * leRoomName;
         QPushButton * btnSetup;
+        QLabel * lblRoomNameReadOnly;
 };
 
 #endif
--- a/QTfrontend/ui/page/pageoptions.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pageoptions.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -23,23 +23,27 @@
 #include <QComboBox>
 #include <QCheckBox>
 #include <QLabel>
+#include <QTableWidget>
 #include <QLineEdit>
 #include <QSpinBox>
 #include <QTextBrowser>
-#include <QTableWidget>
+#include <QScrollArea>
+#include <QHeaderView>
 #include <QSlider>
 #include <QSignalMapper>
 #include <QColorDialog>
 #include <QStandardItemModel>
+#include <QDebug>
 
 #include "pageoptions.h"
 #include "gameuiconfig.h"
 #include "hwconsts.h"
 #include "fpsedit.h"
-#include "igbox.h"
 #include "DataManager.h"
 #include "LibavInteraction.h"
 #include "AutoUpdater.h"
+#include "HWApplication.h"
+#include "keybinder.h"
 
 #ifdef __APPLE__
 #ifdef SPARKLE_ENABLED
@@ -47,6 +51,35 @@
 #endif
 #endif
 
+const int OPTION_BOX_SPACING = 10;
+
+OptionGroupBox::OptionGroupBox(const QString & iconName,
+                               const QString & title,
+                               QWidget * parent) : IconedGroupBox(parent)
+{
+    setIcon(QIcon(iconName));
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    setTitle(title);
+    setMinimumWidth(300);
+    m_layout = new QGridLayout(this);
+    m_layout->setColumnStretch(0, 0);
+    m_layout->setColumnStretch(1, 1);
+}
+
+QGridLayout * OptionGroupBox::layout()
+{
+    return m_layout;
+}
+
+void OptionGroupBox::addDivider()
+{
+    QFrame * hr = new QFrame(this);
+    hr->setFrameStyle(QFrame::HLine);
+    hr->setLineWidth(3);
+    hr->setFixedHeight(10);
+    m_layout->addWidget(hr, m_layout->rowCount(), 0, 1, m_layout->columnCount());
+}
+
 // TODO cleanup
 QLayout * PageOptions::bodyLayoutDefinition()
 {
@@ -54,157 +87,503 @@
 
     QTabWidget * tabs = new QTabWidget(this);
     pageLayout->addWidget(tabs);
-    QWidget * page1 = new QWidget(this);
-    QWidget * page2 = new QWidget(this);
-    tabs->addTab(page1, tr("General"));
-    tabs->addTab(page2, tr("Advanced"));
+
+    binder = new KeyBinder(this, tr("Select an action to change what key controls it"), tr("Reset to default"), tr("Reset all binds"));
+    connect(binder, SIGNAL(bindUpdate(int)), this, SLOT(bindUpdated(int)));
+    connect(binder, SIGNAL(resetAllBinds()), this, SLOT(resetAllBinds()));
+
+    QWidget * pageGame = new QWidget(this);
+    tabs->addTab(pageGame, tr("Game"));
+
+    QWidget * pageGraphics = new QWidget(this);
+    tabs->addTab(pageGraphics, tr("Graphics"));
+
+    QWidget * pageAudio = new QWidget(this);
+    tabs->addTab(pageAudio, tr("Audio"));
+
+    binderTab = tabs->addTab(binder, tr("Controls"));
 
 #ifdef VIDEOREC
-    QWidget * page3 = new QWidget(this);
-    tabs->addTab(page3, tr("Video Recording"));
+    QWidget * pageVideoRec = new QWidget(this);
+    tabs->addTab(pageVideoRec, tr("Video Recording"));
 #endif
 
-    { // page 1
-        QGridLayout * page1Layout = new QGridLayout(page1);
-        //gbTBLayout->setMargin(0);
-        page1Layout->setSpacing(0);
-        page1Layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+    QWidget * pageNetwork = new QWidget(this);
+    tabs->addTab(pageNetwork, tr("Network"));
+
+    QWidget * pageAdvanced = new QWidget(this);
+    tabs->addTab(pageAdvanced, tr("Advanced"));
 
-        QPixmap pmNew(":/res/new.png");
-        QPixmap pmEdit(":/res/edit.png");
-        QPixmap pmDelete(":/res/delete.png");
+    connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tabIndexChanged(int)));
+
+    QPixmap pmNew(":/res/new.png");
+    QPixmap pmEdit(":/res/edit.png");
+    QPixmap pmDelete(":/res/delete.png");
 
-        {
-            teamsBox = new IconedGroupBox(this);
-            //teamsBox->setContentTopPadding(0);
-            //teamsBox->setAttribute(Qt::WA_PaintOnScreen, true);
-            teamsBox->setIcon(QIcon(":/res/teamicon.png"));
-            teamsBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-            teamsBox->setTitle(QGroupBox::tr("Teams"));
+    { // game page
+        QVBoxLayout * leftColumn, * rightColumn;
+        setupTabPage(pageGame, &leftColumn, &rightColumn);
 
-            QGridLayout * GBTlayout = new QGridLayout(teamsBox);
+        { // group: Teams
+            OptionGroupBox * groupTeams = new OptionGroupBox(":/res/teamicon.png", tr("Teams"), this);
+            groupTeams->setMinimumWidth(400);
+            rightColumn->addWidget(groupTeams);
 
-            CBTeamName = new QComboBox(teamsBox);
-            GBTlayout->addWidget(CBTeamName, 0, 0);
+            groupTeams->layout()->setColumnStretch(0, 1);
 
-            BtnNewTeam = new QPushButton(teamsBox);
-            BtnNewTeam->setToolTip(tr("New team"));
+            CBTeamName = new QComboBox(groupTeams);
+            groupTeams->layout()->addWidget(CBTeamName, 0, 0);
+
+            BtnNewTeam = new QPushButton(groupTeams);
+            BtnNewTeam->setWhatsThis(tr("New team"));
             BtnNewTeam->setIconSize(pmNew.size());
             BtnNewTeam->setIcon(pmNew);
             BtnNewTeam->setMaximumWidth(pmNew.width() + 6);
             connect(BtnNewTeam, SIGNAL(clicked()), this, SIGNAL(newTeamRequested()));
-            GBTlayout->addWidget(BtnNewTeam, 0, 1);
+            groupTeams->layout()->addWidget(BtnNewTeam, 0, 1);
 
-            BtnEditTeam = new QPushButton(teamsBox);
-            BtnEditTeam->setToolTip(tr("Edit team"));
+            BtnEditTeam = new QPushButton(groupTeams);
+            BtnEditTeam->setWhatsThis(tr("Edit team"));
             BtnEditTeam->setIconSize(pmEdit.size());
             BtnEditTeam->setIcon(pmEdit);
             BtnEditTeam->setMaximumWidth(pmEdit.width() + 6);
             connect(BtnEditTeam, SIGNAL(clicked()), this, SLOT(requestEditSelectedTeam()));
-            GBTlayout->addWidget(BtnEditTeam, 0, 2);
+            groupTeams->layout()->addWidget(BtnEditTeam, 0, 2);
 
-            BtnDeleteTeam = new QPushButton(teamsBox);
-            BtnDeleteTeam->setToolTip(tr("Delete team"));
+            BtnDeleteTeam = new QPushButton(groupTeams);
+            BtnDeleteTeam->setWhatsThis(tr("Delete team"));
             BtnDeleteTeam->setIconSize(pmDelete.size());
             BtnDeleteTeam->setIcon(pmDelete);
             BtnDeleteTeam->setMaximumWidth(pmDelete.width() + 6);
             connect(BtnDeleteTeam, SIGNAL(clicked()), this, SLOT(requestDeleteSelectedTeam()));
-            GBTlayout->addWidget(BtnDeleteTeam, 0, 3);
+            groupTeams->layout()->addWidget(BtnDeleteTeam, 0, 3);
 
-            LblNoEditTeam = new QLabel(teamsBox);
+            LblNoEditTeam = new QLabel(groupTeams);
             LblNoEditTeam->setText(tr("You can't edit teams from team selection. Go back to main menu to add, edit or delete teams."));
             LblNoEditTeam->setWordWrap(true);
             LblNoEditTeam->setVisible(false);
-            GBTlayout->addWidget(LblNoEditTeam, 0, 0);
-
-            page1Layout->addWidget(teamsBox, 0, 0);
+            groupTeams->layout()->addWidget(LblNoEditTeam, 1, 0, 1, 4);
         }
 
-        {
-            IconedGroupBox* groupWeapons = new IconedGroupBox(this);
+        { // group: schemes
+            OptionGroupBox * groupSchemes = new OptionGroupBox(":/res/weaponsicon.png", tr("Schemes"), this);
+            leftColumn->addWidget(groupSchemes);
 
-            //groupWeapons->setContentTopPadding(0);
-            //groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-            groupWeapons->setIcon(QIcon(":/res/weaponsicon.png"));
-            groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-            groupWeapons->setTitle(QGroupBox::tr("Schemes and Weapons"));
-            QGridLayout * WeaponsLayout = new QGridLayout(groupWeapons);
+            groupSchemes->layout()->setColumnStretch(0, 1);
 
-            QLabel* SchemeLabel = new QLabel(groupWeapons);
-            SchemeLabel->setText(QLabel::tr("Game scheme"));
-            WeaponsLayout->addWidget(SchemeLabel, 1, 0);
+            SchemesName = new QComboBox(groupSchemes);
+            groupSchemes->layout()->addWidget(SchemesName, 0, 0);
 
-            SchemesName = new QComboBox(groupWeapons);
-            WeaponsLayout->addWidget(SchemesName, 1, 1);
-
-            SchemeNew = new QPushButton(groupWeapons);
+            SchemeNew = new QPushButton(groupSchemes);
             SchemeNew->setWhatsThis(tr("New scheme"));
             SchemeNew->setIconSize(pmNew.size());
             SchemeNew->setIcon(pmNew);
             SchemeNew->setMaximumWidth(pmNew.width() + 6);
-            WeaponsLayout->addWidget(SchemeNew, 1, 2);
+            groupSchemes->layout()->addWidget(SchemeNew, 0, 1);
 
-            SchemeEdit = new QPushButton(groupWeapons);
+            SchemeEdit = new QPushButton(groupSchemes);
             SchemeEdit->setWhatsThis(tr("Edit scheme"));
             SchemeEdit->setIconSize(pmEdit.size());
             SchemeEdit->setIcon(pmEdit);
             SchemeEdit->setMaximumWidth(pmEdit.width() + 6);
-            WeaponsLayout->addWidget(SchemeEdit, 1, 3);
+            groupSchemes->layout()->addWidget(SchemeEdit, 0, 2);
 
-            SchemeDelete = new QPushButton(groupWeapons);
+            SchemeDelete = new QPushButton(groupSchemes);
             SchemeDelete->setWhatsThis(tr("Delete scheme"));
             SchemeDelete->setIconSize(pmDelete.size());
             SchemeDelete->setIcon(pmDelete);
             SchemeDelete->setMaximumWidth(pmDelete.width() + 6);
-            WeaponsLayout->addWidget(SchemeDelete, 1, 4);
+            groupSchemes->layout()->addWidget(SchemeDelete, 0, 3);
+        }
 
-            QLabel* WeaponLabel = new QLabel(groupWeapons);
-            WeaponLabel->setText(QLabel::tr("Weapons"));
-            WeaponsLayout->addWidget(WeaponLabel, 2, 0);
+        { // group: weapons
+            OptionGroupBox * groupWeapons = new OptionGroupBox(":/res/weaponsicon.png", tr("Weapons"), this);
+            leftColumn->addWidget(groupWeapons);
+
+            groupWeapons->layout()->setColumnStretch(0, 1);
 
             WeaponsName = new QComboBox(groupWeapons);
-            WeaponsLayout->addWidget(WeaponsName, 2, 1);
+            groupWeapons->layout()->addWidget(WeaponsName, 0, 0);
 
             WeaponNew = new QPushButton(groupWeapons);
             WeaponNew->setWhatsThis(tr("New weapon set"));
             WeaponNew->setIconSize(pmNew.size());
             WeaponNew->setIcon(pmNew);
             WeaponNew->setMaximumWidth(pmNew.width() + 6);
-            WeaponsLayout->addWidget(WeaponNew, 2, 2);
+            groupWeapons->layout()->addWidget(WeaponNew, 0, 1);
 
             WeaponEdit = new QPushButton(groupWeapons);
             WeaponEdit->setWhatsThis(tr("Edit weapon set"));
             WeaponEdit->setIconSize(pmEdit.size());
             WeaponEdit->setIcon(pmEdit);
             WeaponEdit->setMaximumWidth(pmEdit.width() + 6);
-            WeaponsLayout->addWidget(WeaponEdit, 2, 3);
+            groupWeapons->layout()->addWidget(WeaponEdit, 0, 2);
 
             WeaponDelete = new QPushButton(groupWeapons);
             WeaponDelete->setWhatsThis(tr("Delete weapon set"));
             WeaponDelete->setIconSize(pmDelete.size());
             WeaponDelete->setIcon(pmDelete);
             WeaponDelete->setMaximumWidth(pmDelete.width() + 6);
-            WeaponsLayout->addWidget(WeaponDelete, 2, 4);
+            groupWeapons->layout()->addWidget(WeaponDelete, 0, 3);
+        }
+
+        leftColumn->addStretch(1);
+        rightColumn->addStretch(1);
+    }
+
+    { // graphics page
+        QVBoxLayout * leftColumn, * rightColumn;
+        setupTabPage(pageGraphics, &leftColumn, &rightColumn);
+
+        { // group: game
+            OptionGroupBox * groupGame = new OptionGroupBox(":/res/graphicsicon.png", tr("Game"), this);
+            leftColumn->addWidget(groupGame);
+
+            groupGame->layout()->setColumnStretch(0, 0);
+            groupGame->layout()->setColumnStretch(1, 0);
+            groupGame->layout()->setColumnStretch(2, 1);
+
+            // Fullscreen
+
+            CBFullscreen = new QCheckBox(groupGame);
+            groupGame->layout()->addWidget(CBFullscreen, 0, 0, 1, 2);
+            CBFullscreen->setText(QLabel::tr("Fullscreen"));
+
+            // Fullscreen resolution
+
+            lblFullScreenRes = new QLabel(groupGame);
+            lblFullScreenRes->setText(QLabel::tr("Fullscreen Resolution"));
+            groupGame->layout()->addWidget(lblFullScreenRes, 1, 0);
+
+            CBResolution = new QComboBox(groupGame);
+            CBResolution->setFixedWidth(200);
+            groupGame->layout()->addWidget(CBResolution, 1, 1, Qt::AlignLeft);
+
+            // Windowed resolution
+
+            lblWinScreenRes = new QLabel(groupGame);
+            lblWinScreenRes->setText(QLabel::tr("Windowed Resolution"));
+            groupGame->layout()->addWidget(lblWinScreenRes, 2, 0);
+
+            winResContainer = new QWidget();
+            QHBoxLayout * winResLayout = new QHBoxLayout(winResContainer);
+            winResLayout->setSpacing(0);
+            groupGame->layout()->addWidget(winResContainer, 2, 1);
+
+            QLabel *winLabelX = new QLabel(groupGame);
+            winLabelX->setText("x"); // decorational x
+            winLabelX->setFixedWidth(40);
+            winLabelX->setAlignment(Qt::AlignCenter);
+
+            windowWidthEdit = new QLineEdit(groupGame);
+            windowWidthEdit->setValidator(new QIntValidator(this));
+            windowWidthEdit->setFixedSize(55, CBResolution->height());
+            windowHeightEdit = new QLineEdit(groupGame);
+            windowHeightEdit->setValidator(new QIntValidator(this));
+            windowHeightEdit->setFixedSize(55, CBResolution->height());
+
+            winResLayout->addWidget(windowWidthEdit, 0);
+            winResLayout->addWidget(winLabelX, 0);
+            winResLayout->addWidget(windowHeightEdit, 0);
+            winResLayout->addStretch(1);
+
+            // Quality
+
+            QLabel * lblQuality = new QLabel(groupGame);
+            lblQuality->setText(QLabel::tr("Quality"));
+            lblQuality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+            groupGame->layout()->addWidget(lblQuality, 3, 0);
+
+            SLQuality = new QSlider(Qt::Horizontal, groupGame);
+            SLQuality->setTickPosition(QSlider::TicksBelow);
+            SLQuality->setMaximum(5);
+            SLQuality->setMinimum(0);
+            SLQuality->setFixedWidth(150);
+            groupGame->layout()->addWidget(SLQuality, 3, 1, Qt::AlignLeft);
 
-            page1Layout->addWidget(groupWeapons, 1, 0);
+            // Stereo spacing
+
+            QLabel * lblStereo = new QLabel(groupGame);
+            lblStereo->setText(QLabel::tr("Stereo rendering"));
+            groupGame->layout()->addWidget(lblStereo, 4, 0);
+
+            CBStereoMode = new QComboBox(groupGame);
+            CBStereoMode->addItem(QComboBox::tr("Disabled"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
+            CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
+            CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Green"));
+            CBStereoMode->addItem(QComboBox::tr("Green/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
+            CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
+            CBStereoMode->setFixedWidth(CBResolution->width());
+            groupGame->layout()->addWidget(CBStereoMode, 4, 1);
+
+            // Divider
+
+            groupGame->addDivider(); // row 5
+
+            // FPS limit
+
+            QHBoxLayout * fpsLayout = new QHBoxLayout();
+            groupGame->layout()->addLayout(fpsLayout, 6, 0, 1, 2);
+            QLabel * maxfps = new QLabel(groupGame);
+            maxfps->setText(QLabel::tr("FPS limit"));
+            fpsLayout->addWidget(maxfps);
+            fpsLayout->addSpacing(30);
+            fpsedit = new FPSEdit(groupGame);
+            fpsLayout->addWidget(fpsedit);
+
+            // Show FPS
+
+            CBShowFPS = new QCheckBox(groupGame);
+            CBShowFPS->setText(QCheckBox::tr("Show FPS"));
+            fpsLayout->addWidget(CBShowFPS);
+            fpsLayout->addStretch(1);
+
+            // Divider
+
+            groupGame->addDivider(); // row 7
+
+            // Alternative damage show
+
+            CBAltDamage = new QCheckBox(groupGame);
+            CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
+            groupGame->layout()->addWidget(CBAltDamage, 8, 0, 1, 2);
+
+            // Show ammo menu tooltips
+
+            WeaponTooltip = new QCheckBox(groupGame);
+            WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
+            groupGame->layout()->addWidget(WeaponTooltip, 9, 0, 1, 2);
+        }
+
+        { // group: frontend
+            OptionGroupBox * groupFrontend = new OptionGroupBox(":/res/graphicsicon.png", tr("Frontend"), this);
+            rightColumn->addWidget(groupFrontend);
+
+            // Fullscreen
+
+            CBFrontendFullscreen = new QCheckBox(groupFrontend);
+            CBFrontendFullscreen->setText(QCheckBox::tr("Fullscreen"));
+            groupFrontend->layout()->addWidget(CBFrontendFullscreen, 0, 0);
+
+            // Visual effects
+
+            CBFrontendEffects = new QCheckBox(groupFrontend);
+            CBFrontendEffects->setText(QCheckBox::tr("Visual effects"));
+            groupFrontend->layout()->addWidget(CBFrontendEffects, 1, 0);
         }
 
-        {
-            IconedGroupBox* groupMisc = new IconedGroupBox(this);
-            //groupMisc->setContentTopPadding(0);
-            //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
-            groupMisc->setIcon(QIcon(":/res/miscicon.png"));
-            //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-            groupMisc->setTitle(QGroupBox::tr("Misc"));
-            QGridLayout * MiscLayout = new QGridLayout(groupMisc);
+        { // group: colors
+            OptionGroupBox * groupColors = new OptionGroupBox(":/res/lightbulb_on.png", tr("Custom colors"), this);
+            rightColumn->addWidget(groupColors);
+
+            groupColors->layout()->setColumnStretch(0, 1);
+            groupColors->layout()->setColumnStretch(1, 1);
+            groupColors->layout()->setColumnStretch(2, 1);
+
+            // Color buttons
+
+            QSignalMapper * mapper = new QSignalMapper(this);
+            QStandardItemModel * model = DataManager::instance().colorsModel();
+
+            connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(onColorModelDataChanged(QModelIndex,QModelIndex)));
+            for(int i = 0; i < model->rowCount(); ++i)
+            {
+                QPushButton * btn = new QPushButton(this);
+                btn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+                groupColors->layout()->addWidget(btn, i / 3, i % 3);
+                btn->setStyleSheet(QString("background: %1").arg(model->item(i)->data().value<QColor>().name()));
+                m_colorButtons.append(btn);
+                connect(btn, SIGNAL(clicked()), mapper, SLOT(map()));
+                mapper->setMapping(btn, i);
+            }
+
+            connect(mapper, SIGNAL(mapped(int)), this, SLOT(colorButtonClicked(int)));
+
+            // Reset default colors
+
+            QPushButton * btn = new QPushButton(this);
+            groupColors->layout()->addWidget(btn, (model->rowCount() - 1) / 3 + 1, 0, 1, 3);
+            btn->setText(tr("Reset to default colors"));
+            connect(btn, SIGNAL(clicked()), &DataManager::instance(), SLOT(resetColors()));
+        }
+
+        leftColumn->addStretch(1);
+        rightColumn->addStretch(1);
+    }
+
+    { // audio page
+        QVBoxLayout * leftColumn, * rightColumn;
+        setupTabPage(pageAudio, &leftColumn, &rightColumn);
+
+        { // group: game
+            OptionGroupBox * groupGame = new OptionGroupBox(":/res/audio.png", tr("Game audio"), this);
+            leftColumn->addWidget(groupGame);
+            groupGame->layout()->setColumnStretch(1, 0);
+            groupGame->layout()->setColumnStretch(2, 1);
+
+            // Initial sound volume
+
+            QLabel * vol = new QLabel(groupGame);
+            vol->setText(QLabel::tr("Initial sound volume"));
+            groupGame->layout()->addWidget(vol, 0, 0);
+
+            SLVolume = new QSlider(Qt::Horizontal, groupGame);
+            SLVolume->setTickPosition(QSlider::TicksBelow);
+            SLVolume->setMaximum(100);
+            SLVolume->setMinimum(0);
+            SLVolume->setFixedWidth(150);
+            groupGame->layout()->addWidget(SLVolume, 0, 1, 1, 2);
+
+            lblVolumeLevel = new QLabel(groupGame);
+            lblVolumeLevel->setFixedWidth(40);
+            groupGame->layout()->addWidget(lblVolumeLevel, 0, 3);
+
+            // Sound
+
+            CBSound = new QCheckBox(groupGame);
+            CBSound->setText(QCheckBox::tr("Sound"));
+            CBSound->setWhatsThis(QCheckBox::tr("In-game sound effects"));
+            groupGame->layout()->addWidget(CBSound, 1, 1);
+
+            // Music
+
+            CBMusic = new QCheckBox(groupGame);
+            CBMusic->setText(QCheckBox::tr("Music"));
+            CBMusic->setWhatsThis(QCheckBox::tr("In-game music"));
+            groupGame->layout()->addWidget(CBMusic, 1, 2, 1, 2, Qt::AlignLeft);
+        }
+
+        { // group: frontend
+            OptionGroupBox * groupFrontend = new OptionGroupBox(":/res/audio.png", tr("Frontend audio"), this);
+            rightColumn->addWidget(groupFrontend);
+
+            CBFrontendSound = new QCheckBox(groupFrontend);
+            CBFrontendSound->setText(QCheckBox::tr("Sound"));
+            CBFrontendSound->setWhatsThis(QCheckBox::tr("Frontend sound effects"));
+            groupFrontend->layout()->addWidget(CBFrontendSound, 0, 0);
+
+            CBFrontendMusic = new QCheckBox(groupFrontend);
+            CBFrontendMusic->setText(QCheckBox::tr("Music"));
+            CBFrontendMusic->setWhatsThis(QCheckBox::tr("Frontend music"));
+            groupFrontend->layout()->addWidget(CBFrontendMusic, 0, 1);
+        }
+
+        leftColumn->addStretch(1);
+        rightColumn->addStretch(1);
+    }
 
-            // Label for "Language"
+    { // network page
+        QVBoxLayout * leftColumn, * rightColumn;
+        setupTabPage(pageNetwork, &leftColumn, &rightColumn);
+
+        { // group: account
+            OptionGroupBox * groupAccount = new OptionGroupBox(":/res/teamicon.png", tr("Account"), this);
+            leftColumn->addWidget(groupAccount);
+
+            // Label and field for net nick
+
+            labelNN = new QLabel(groupAccount);
+            labelNN->setText(QLabel::tr("Nickname"));
+            groupAccount->layout()->addWidget(labelNN, 0, 0);
+
+            editNetNick = new QLineEdit(groupAccount);
+            editNetNick->setMaxLength(20);
+            editNetNick->setText(QLineEdit::tr("anonymous"));
+            groupAccount->layout()->addWidget(editNetNick, 0, 1);
+
+            // Checkbox and field for password
+
+            CBSavePassword = new QCheckBox(groupAccount);
+            CBSavePassword->setText(QCheckBox::tr("Save password"));
+            groupAccount->layout()->addWidget(CBSavePassword, 1, 0);
+
+            editNetPassword = new QLineEdit(groupAccount);
+            editNetPassword->setEchoMode(QLineEdit::Password);
+            groupAccount->layout()->addWidget(editNetPassword, 1, 1);
+        }
+
+        { // group: proxy
+            OptionGroupBox * groupProxy = new OptionGroupBox(":/res/net.png", tr("Proxy settings"), this);
+            rightColumn->addWidget(groupProxy);
+
+            // Labels
+
+            QStringList sl;
+            sl << tr("Proxy host")
+               << tr("Proxy port")
+               << tr("Proxy login")
+               << tr("Proxy password");
+
+            for(int i = 0; i < sl.size(); ++i)
+            {
+                QLabel * l = new QLabel(groupProxy);
+                l->setText(sl[i]);
+                groupProxy->layout()->addWidget(l, i + 1, 0);
+            }
+
+            // Proxy type
+
+            cbProxyType = new QComboBox(groupProxy);
+            cbProxyType->addItems(QStringList()
+                                  << tr("No proxy")
+                                  << tr("System proxy settings")
+                                  << tr("Socks5 proxy")
+                                  << tr("HTTP proxy"));
+            groupProxy->layout()->addWidget(cbProxyType, 0, 0, 1, 2);
+
+            // Proxy
+
+            leProxy = new QLineEdit(groupProxy);
+            groupProxy->layout()->addWidget(leProxy, 1, 1);
+
+            // Proxy
+
+            sbProxyPort = new QSpinBox(groupProxy);
+            sbProxyPort->setMaximum(65535);
+            groupProxy->layout()->addWidget(sbProxyPort, 2, 1);
+
+            leProxyLogin = new QLineEdit(groupProxy);
+            groupProxy->layout()->addWidget(leProxyLogin, 3, 1);
+
+            leProxyPassword = new QLineEdit(groupProxy);
+            leProxyPassword->setEchoMode(QLineEdit::Password);
+            groupProxy->layout()->addWidget(leProxyPassword, 4, 1);
+
+
+            connect(cbProxyType, SIGNAL(currentIndexChanged(int)), this, SLOT(onProxyTypeChanged()));
+            onProxyTypeChanged();
+        }
+
+        leftColumn->addStretch(1);
+        rightColumn->addStretch(1);
+    }
+
+    { // advanced page
+        QVBoxLayout * leftColumn, * rightColumn;
+        setupTabPage(pageAdvanced, &leftColumn, &rightColumn);
+
+        { // group: miscellaneous
+            OptionGroupBox * groupMisc = new OptionGroupBox(":/res/Settings.png", tr("Miscellaneous"), this);
+            leftColumn->addWidget(groupMisc);
+
+            // Language
+
             QLabel *labelLanguage = new QLabel(groupMisc);
-            labelLanguage->setText(QLabel::tr("Locale") + " *");
-            MiscLayout->addWidget(labelLanguage, 0, 0);
+            labelLanguage->setText(QLabel::tr("Locale"));
+            groupMisc->layout()->addWidget(labelLanguage, 0, 0);
 
-            // List of installed languages
             CBLanguage = new QComboBox(groupMisc);
+            groupMisc->layout()->addWidget(CBLanguage, 0, 1);
             QStringList locs = DataManager::instance().entryList("Locale", QDir::Files, QStringList("hedgewars_*.qm"));
             CBLanguage->addItem(QComboBox::tr("(System default)"), QString(""));
             for(int i = 0; i < locs.count(); i++)
@@ -213,414 +592,179 @@
                 CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
             }
 
-            MiscLayout->addWidget(CBLanguage, 0, 1);
-
-            // Label and field for net nick
-            labelNN = new QLabel(groupMisc);
-            labelNN->setText(QLabel::tr("Nickname"));
-            MiscLayout->addWidget(labelNN, 1, 0);
+            // Divider
 
-            editNetNick = new QLineEdit(groupMisc);
-            editNetNick->setMaxLength(20);
-            editNetNick->setText(QLineEdit::tr("anonymous"));
-            MiscLayout->addWidget(editNetNick, 1, 1);
+            groupMisc->addDivider(); // row 1
 
-            // checkbox and field for password
-            CBSavePassword = new QCheckBox(groupMisc);
-            CBSavePassword->setText(QCheckBox::tr("Save password"));
-            MiscLayout->addWidget(CBSavePassword, 2, 0);
+            // Append date and time to record file name
 
-            editNetPassword = new QLineEdit(groupMisc);
-            editNetPassword->setEchoMode(QLineEdit::Password);
-            MiscLayout->addWidget(editNetPassword, 2, 1);
-
-    #ifdef __APPLE__
-    #ifdef SPARKLE_ENABLED
-            CBAutoUpdate = new QCheckBox(groupMisc);
-            CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
-            MiscLayout->addWidget(CBAutoUpdate, 7, 0, 1, 1);
+            CBNameWithDate = new QCheckBox(groupMisc);
+            CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
+            groupMisc->layout()->addWidget(CBNameWithDate, 2, 0, 1, 2);
 
-            btnUpdateNow = new QPushButton(groupMisc);
-            connect(btnUpdateNow, SIGNAL(clicked()), this, SLOT(checkForUpdates()));
-            btnUpdateNow->setToolTip(tr("Check for updates"));
-            btnUpdateNow->setText("Check now");
-            btnUpdateNow->setFixedSize(130, 30);
-            MiscLayout->addWidget(btnUpdateNow, 7, 1, 1, 1);
-    #endif
-    #endif
-            page1Layout->addWidget(groupMisc, 2, 0);
+            // Associate file extensions
+
+            BtnAssociateFiles = new QPushButton(groupMisc);
+            BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
+            BtnAssociateFiles->setVisible(!custom_data && !custom_config);
+            groupMisc->layout()->addWidget(BtnAssociateFiles, 3, 0, 1, 2);
         }
 
-        {
-            AGGroupBox = new IconedGroupBox(this);
-            //AGGroupBox->setContentTopPadding(0);
-            AGGroupBox->setIcon(QIcon(":/res/graphicsicon.png"));
-            //AGGroupBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
-            AGGroupBox->setTitle(QGroupBox::tr("Audio/Graphic options"));
-
-            QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
-            QGridLayout * GBAfrontendlayout = new QGridLayout(0);
-            QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
-            QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
-            QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
-
-            QLabel * frontend = new QLabel(AGGroupBox);
-            frontend->setText(QLabel::tr("Frontend"));
-            frontend->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-            GBAfrontendlayout->addWidget(frontend, 0, 0, 1, 2);
-
-            CBFrontendFullscreen = new QCheckBox(AGGroupBox);
-            CBFrontendFullscreen->setText(QCheckBox::tr("Fullscreen"));
-            GBAfrontendlayout->addWidget(CBFrontendFullscreen, 1, 0);
-
-            CBFrontendEffects = new QCheckBox(AGGroupBox);
-            CBFrontendEffects->setText(QCheckBox::tr("Visual effects"));
-            GBAfrontendlayout->addWidget(CBFrontendEffects, 2, 0);
-
-            CBFrontendSound = new QCheckBox(AGGroupBox);
-            CBFrontendSound->setText(QCheckBox::tr("Sound"));
-            GBAfrontendlayout->addWidget(CBFrontendSound, 1, 1);
+#ifdef __APPLE__
+#ifdef SPARKLE_ENABLED
+        { // group: updates
+            OptionGroupBox * groupUpdates = new OptionGroupBox(":/res/net.png", tr("Updates"), this);
+            rightColumn->addWidget(groupUpdates);
 
-            CBFrontendMusic = new QCheckBox(AGGroupBox);
-            CBFrontendMusic->setText(QCheckBox::tr("Music"));
-            GBAfrontendlayout->addWidget(CBFrontendMusic, 2, 1);
-
-            GBAlayout->addLayout(GBAfrontendlayout);
-
-            QFrame * hr = new QFrame(AGGroupBox);
-            hr->setFrameStyle(QFrame::HLine);
-            hr->setLineWidth(3);
-            hr->setFixedHeight(10);
-            GBAlayout->addWidget(hr);
-
-            QLabel * resolution = new QLabel(AGGroupBox);
-            resolution->setText(QLabel::tr("Resolution"));
-            GBAreslayout->addWidget(resolution);
+            // Check for updates at startup
 
-            CBResolution = new QComboBox(AGGroupBox);
-            GBAreslayout->addWidget(CBResolution);
-            GBAlayout->addLayout(GBAreslayout);
-
-            CBFullscreen = new QCheckBox(AGGroupBox);
-            CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
-            GBAreslayout->addWidget(CBFullscreen);
-
-            QLabel * quality = new QLabel(AGGroupBox);
-            quality->setText(QLabel::tr("Quality"));
-            quality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-            GBAqualayout->addWidget(quality);
-
-            SLQuality = new QSlider(Qt::Horizontal, AGGroupBox);
-            SLQuality->setTickPosition(QSlider::TicksBelow);
-            SLQuality->setMaximum(5);
-            SLQuality->setMinimum(0);
-            SLQuality->setFixedWidth(150);
-            GBAqualayout->addWidget(SLQuality);
-            GBAlayout->addLayout(GBAqualayout);
+            CBAutoUpdate = new QCheckBox(groupUpdates);
+            CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
+            groupUpdates->layout()->addWidget(CBAutoUpdate, 0, 0);
 
-            QLabel * stereo = new QLabel(AGGroupBox);
-            stereo->setText(QLabel::tr("Stereo rendering"));
-            GBAstereolayout->addWidget(stereo);
-
-            CBStereoMode = new QComboBox(AGGroupBox);
-            CBStereoMode->addItem(QComboBox::tr("Disabled"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
-            CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
-            CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Green"));
-            CBStereoMode->addItem(QComboBox::tr("Green/Red"));
-            CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
-            CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
-            CBStereoMode->addItem(QComboBox::tr("Wiggle"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
-            CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
-            CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
-            CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
-            CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
-
-            GBAstereolayout->addWidget(CBStereoMode);
-            GBAlayout->addLayout(GBAstereolayout);
-
-            hr = new QFrame(AGGroupBox);
-            hr->setFrameStyle(QFrame::HLine);
-            hr->setLineWidth(3);
-            hr->setFixedHeight(10);
-            GBAlayout->addWidget(hr);
+            // Check for updates now
 
-            QGridLayout * GBAvollayout = new QGridLayout();
-            QLabel * vol = new QLabel(AGGroupBox);
-            vol->setText(QLabel::tr("Initial sound volume"));
-            GBAvollayout->addWidget(vol, 0, 0, 1, 2);
-            GBAlayout->addLayout(GBAvollayout);
-            volumeBox = new QSpinBox(AGGroupBox);
-            volumeBox->setRange(0, 100);
-            volumeBox->setSingleStep(5);
-            GBAvollayout->addWidget(volumeBox, 0, 2);
-
-            CBSound = new QCheckBox(AGGroupBox);
-            CBSound->setText(QCheckBox::tr("Sound"));
-            CBSound->setWhatsThis(QCheckBox::tr("In-game sound effects"));
-            GBAvollayout->addWidget(CBSound, 1, 0);
+            btnUpdateNow = new QPushButton(groupUpdates);
+            connect(btnUpdateNow, SIGNAL(clicked()), this, SLOT(checkForUpdates()));
+            btnUpdateNow->setWhatsThis(tr("Check for updates"));
+            btnUpdateNow->setText("Check now");
+            btnUpdateNow->setFixedSize(130, 30);
+            groupUpdates->layout()->addWidget(btnUpdateNow, 0, 1);
+        }
+#endif
+#endif
 
-            CBMusic = new QCheckBox(AGGroupBox);
-            CBMusic->setText(QCheckBox::tr("Music"));
-            CBMusic->setWhatsThis(QCheckBox::tr("In-game music"));
-            GBAvollayout->addWidget(CBMusic, 1, 1, 1, 2);
-
-            GBAvollayout->setSizeConstraint(QLayout::SetMinimumSize);
-
-            hr = new QFrame(AGGroupBox);
-            hr->setFrameStyle(QFrame::HLine);
-            hr->setLineWidth(3);
-            hr->setFixedHeight(10);
-            GBAlayout->addWidget(hr);
-
-            CBAltDamage = new QCheckBox(AGGroupBox);
-            CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
-            GBAlayout->addWidget(CBAltDamage);
-
-            page1Layout->addWidget(AGGroupBox, 0, 1, 3, 1);
-        }
-
-        page1Layout->addWidget(new QWidget(this), 3, 0);
-
+        leftColumn->addStretch(1);
+        rightColumn->addStretch(1);
     }
 
-    { // page 2
-        QGridLayout * page2Layout = new QGridLayout(page2);
-
-        {
-            IconedGroupBox * gbColors = new IconedGroupBox(this);
-            gbColors->setIcon(QIcon(":/res/lightbulb_on.png"));
-            gbColors->setTitle(QGroupBox::tr("Custom colors"));
-            page2Layout->addWidget(gbColors, 0, 0);
-            QGridLayout * gbCLayout = new QGridLayout(gbColors);
-
-            QSignalMapper * mapper = new QSignalMapper(this);
-
-            QStandardItemModel * model = DataManager::instance().colorsModel();
-
-            connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(onColorModelDataChanged(QModelIndex,QModelIndex)));
-            for(int i = 0; i < model->rowCount(); ++i)
-            {
-                QPushButton * btn = new QPushButton(this);
-                btn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
-                gbCLayout->addWidget(btn, i / 3, i % 3);
-                btn->setStyleSheet(QString("background: %1").arg(model->item(i)->data().value<QColor>().name()));
-                m_colorButtons.append(btn);
-                connect(btn, SIGNAL(clicked()), mapper, SLOT(map()));
-                mapper->setMapping(btn, i);
-            }
-
-            connect(mapper, SIGNAL(mapped(int)), this, SLOT(colorButtonClicked(int)));
-
-            QPushButton * btn = new QPushButton(this);
-            gbCLayout->addWidget(btn, (model->rowCount() - 1) / 3 + 1, 0, 1, 3);
-            btn->setText(tr("Reset to default colors"));
-            connect(btn, SIGNAL(clicked()), &DataManager::instance(), SLOT(resetColors()));
-        }
-
-        {
-            IconedGroupBox * gbMisc = new IconedGroupBox(this);
-            gbMisc->setIcon(QIcon(":/res/Settings.png"));
-            gbMisc->setTitle(QGroupBox::tr("Miscellaneous"));
-            page2Layout->addWidget(gbMisc, 0, 1);
-            QVBoxLayout * gbCLayout = new QVBoxLayout(gbMisc);
-
-            QHBoxLayout * GBAfpslayout = new QHBoxLayout(0);
-            QLabel * maxfps = new QLabel(AGGroupBox);
-            maxfps->setText(QLabel::tr("FPS limit"));
-            GBAfpslayout->addWidget(maxfps);
-            fpsedit = new FPSEdit(AGGroupBox);
-            GBAfpslayout->addWidget(fpsedit);
-
-            CBShowFPS = new QCheckBox(AGGroupBox);
-            CBShowFPS->setText(QCheckBox::tr("Show FPS"));
-            GBAfpslayout->addWidget(CBShowFPS);
-
-            gbCLayout->addLayout(GBAfpslayout);
-
-
-            WeaponTooltip = new QCheckBox(this);
-            WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
-            gbCLayout->addWidget(WeaponTooltip);
-
-
-            CBNameWithDate = new QCheckBox(this);
-            CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
-            gbCLayout->addWidget(CBNameWithDate);
-
-            BtnAssociateFiles = new QPushButton(this);
-            BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
-            BtnAssociateFiles->setVisible(!custom_data && !custom_config);
-            gbCLayout->addWidget(BtnAssociateFiles);
-        }
-
-        {
-            IconedGroupBox * gbProxy = new IconedGroupBox(this);
-            gbProxy->setIcon(QIcon(":/res/Settings.png"));
-            gbProxy->setTitle(QGroupBox::tr("Proxy settings"));
-            page2Layout->addWidget(gbProxy, 1, 0);
-            QGridLayout * gbLayout = new QGridLayout(gbProxy);
-
-            QStringList sl;
-            sl
-                    << tr("Proxy host")
-                    << tr("Proxy port")
-                    << tr("Proxy login")
-                    << tr("Proxy password")
-                       ;
-            for(int i = 0; i < sl.size(); ++i)
-            {
-                QLabel * l = new QLabel(gbProxy);
-                l->setText(sl[i]);
-                gbLayout->addWidget(l, i + 1, 0);
-            }
-
-            cbProxyType = new QComboBox(gbProxy);
-            cbProxyType->addItems(QStringList()
-                                  << tr("No proxy")
-                                  << tr("System proxy settings")
-                                  << tr("Socks5 proxy")
-                                  << tr("HTTP proxy"));
-            gbLayout->addWidget(cbProxyType, 0, 1);
-
-            leProxy = new QLineEdit(gbProxy);
-            gbLayout->addWidget(leProxy, 1, 1);
-
-            sbProxyPort = new QSpinBox(gbProxy);
-            sbProxyPort->setMaximum(65535);
-            gbLayout->addWidget(sbProxyPort, 2, 1);
-
-            leProxyLogin = new QLineEdit(gbProxy);
-            gbLayout->addWidget(leProxyLogin, 3, 1);
-
-            leProxyPassword = new QLineEdit(gbProxy);
-            leProxyPassword->setEchoMode(QLineEdit::Password);
-            gbLayout->addWidget(leProxyPassword, 4, 1);
-
-
-            connect(cbProxyType, SIGNAL(currentIndexChanged(int)), this, SLOT(onProxyTypeChanged()));
-            onProxyTypeChanged();
-        }
-
-        page2Layout->addWidget(new QWidget(this), 2, 0);
-    }
 #ifdef VIDEOREC
-    { // page 3
-        QGridLayout * page3Layout = new QGridLayout(page3);
-
-        IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
-        pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); // FIXME
-        pOptionsGroup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-        pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
-        QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
+    { // video recording page
+        OptionGroupBox * groupVideoRec = new OptionGroupBox(":/res/camera.png", tr("Video recording options"), this);
+        groupVideoRec->setMinimumWidth(500);
+        groupVideoRec->setMaximumWidth(650);
+        QHBoxLayout * layoutVideoRec = new QHBoxLayout(pageVideoRec);
+        layoutVideoRec->addWidget(groupVideoRec, 1, Qt::AlignTop | Qt::AlignHCenter);
 
         // label for format
-        QLabel *labelFormat = new QLabel(pOptionsGroup);
+
+        QLabel *labelFormat = new QLabel(groupVideoRec);
         labelFormat->setText(QLabel::tr("Format"));
-        pOptLayout->addWidget(labelFormat, 0, 0);
+        groupVideoRec->layout()->addWidget(labelFormat, 0, 0);
 
         // list of supported formats
-        comboAVFormats = new QComboBox(pOptionsGroup);
-        pOptLayout->addWidget(comboAVFormats, 0, 1, 1, 4);
+
+        comboAVFormats = new QComboBox(groupVideoRec);
+        groupVideoRec->layout()->addWidget(comboAVFormats, 0, 1, 1, 4);
         LibavInteraction::instance().fillFormats(comboAVFormats);
 
         // separator
-        QFrame * hr = new QFrame(pOptionsGroup);
+
+        QFrame * hr = new QFrame(groupVideoRec);
         hr->setFrameStyle(QFrame::HLine);
         hr->setLineWidth(3);
         hr->setFixedHeight(10);
-        pOptLayout->addWidget(hr, 1, 0, 1, 5);
+        groupVideoRec->layout()->addWidget(hr, 1, 0, 1, 5);
 
         // label for audio codec
-        QLabel *labelACodec = new QLabel(pOptionsGroup);
+
+        QLabel *labelACodec = new QLabel(groupVideoRec);
         labelACodec->setText(QLabel::tr("Audio codec"));
-        pOptLayout->addWidget(labelACodec, 2, 0);
+        groupVideoRec->layout()->addWidget(labelACodec, 2, 0);
 
         // list of supported audio codecs
-        comboAudioCodecs = new QComboBox(pOptionsGroup);
-        pOptLayout->addWidget(comboAudioCodecs, 2, 1, 1, 3);
+
+        comboAudioCodecs = new QComboBox(groupVideoRec);
+        groupVideoRec->layout()->addWidget(comboAudioCodecs, 2, 1, 1, 3);
 
         // checkbox 'record audio'
-        checkRecordAudio = new QCheckBox(pOptionsGroup);
+
+        checkRecordAudio = new QCheckBox(groupVideoRec);
         checkRecordAudio->setText(QCheckBox::tr("Record audio"));
-        pOptLayout->addWidget(checkRecordAudio, 2, 4);
+        groupVideoRec->layout()->addWidget(checkRecordAudio, 2, 4);
 
         // separator
-        hr = new QFrame(pOptionsGroup);
+
+        hr = new QFrame(groupVideoRec);
         hr->setFrameStyle(QFrame::HLine);
         hr->setLineWidth(3);
         hr->setFixedHeight(10);
-        pOptLayout->addWidget(hr, 3, 0, 1, 5);
+        groupVideoRec->layout()->addWidget(hr, 3, 0, 1, 5);
 
         // label for video codec
-        QLabel *labelVCodec = new QLabel(pOptionsGroup);
+
+        QLabel *labelVCodec = new QLabel(groupVideoRec);
         labelVCodec->setText(QLabel::tr("Video codec"));
-        pOptLayout->addWidget(labelVCodec, 4, 0);
+        groupVideoRec->layout()->addWidget(labelVCodec, 4, 0);
 
         // list of supported video codecs
-        comboVideoCodecs = new QComboBox(pOptionsGroup);
-        pOptLayout->addWidget(comboVideoCodecs, 4, 1, 1, 4);
+
+        comboVideoCodecs = new QComboBox(groupVideoRec);
+        groupVideoRec->layout()->addWidget(comboVideoCodecs, 4, 1, 1, 4);
 
         // label for resolution
-        QLabel *labelRes = new QLabel(pOptionsGroup);
+
+        QLabel *labelRes = new QLabel(groupVideoRec);
         labelRes->setText(QLabel::tr("Resolution"));
-        pOptLayout->addWidget(labelRes, 5, 0);
+        groupVideoRec->layout()->addWidget(labelRes, 5, 0);
 
         // width
-        widthEdit = new QLineEdit(pOptionsGroup);
+
+        widthEdit = new QLineEdit(groupVideoRec);
         widthEdit->setValidator(new QIntValidator(this));
-        pOptLayout->addWidget(widthEdit, 5, 1);
+        groupVideoRec->layout()->addWidget(widthEdit, 5, 1);
 
         // x
-        QLabel *labelX = new QLabel(pOptionsGroup);
+
+        QLabel *labelX = new QLabel(groupVideoRec);
         labelX->setText("X");
-        pOptLayout->addWidget(labelX, 5, 2);
+        groupVideoRec->layout()->addWidget(labelX, 5, 2);
 
         // height
-        heightEdit = new QLineEdit(pOptionsGroup);
-        heightEdit->setValidator(new QIntValidator(pOptionsGroup));
-        pOptLayout->addWidget(heightEdit, 5, 3);
+
+        heightEdit = new QLineEdit(groupVideoRec);
+        heightEdit->setValidator(new QIntValidator(groupVideoRec));
+        groupVideoRec->layout()->addWidget(heightEdit, 5, 3);
 
         // checkbox 'use game resolution'
-        checkUseGameRes = new QCheckBox(pOptionsGroup);
+
+        checkUseGameRes = new QCheckBox(groupVideoRec);
         checkUseGameRes->setText(QCheckBox::tr("Use game resolution"));
-        pOptLayout->addWidget(checkUseGameRes, 5, 4);
+        groupVideoRec->layout()->addWidget(checkUseGameRes, 5, 4);
 
         // label for framerate
-        QLabel *labelFramerate = new QLabel(pOptionsGroup);
+
+        QLabel *labelFramerate = new QLabel(groupVideoRec);
         labelFramerate->setText(QLabel::tr("Framerate"));
-        pOptLayout->addWidget(labelFramerate, 6, 0);
+        groupVideoRec->layout()->addWidget(labelFramerate, 6, 0);
 
-        framerateBox = new QComboBox(pOptionsGroup);
+        framerateBox = new QComboBox(groupVideoRec);
         framerateBox->addItem("24 fps", 24);
         framerateBox->addItem("25 fps", 25);
         framerateBox->addItem("30 fps", 30);
         framerateBox->addItem("50 fps", 50);
         framerateBox->addItem("60 fps", 60);
-        pOptLayout->addWidget(framerateBox, 6, 1);
+        groupVideoRec->layout()->addWidget(framerateBox, 6, 1);
 
         // label for Bitrate
-        QLabel *labelBitrate = new QLabel(pOptionsGroup);
+
+        QLabel *labelBitrate = new QLabel(groupVideoRec);
         labelBitrate->setText(QLabel::tr("Bitrate (Kbps)"));
-        pOptLayout->addWidget(labelBitrate, 6, 2);
+        groupVideoRec->layout()->addWidget(labelBitrate, 6, 2);
 
         // bitrate
-        bitrateBox = new QSpinBox(pOptionsGroup);
+
+        bitrateBox = new QSpinBox(groupVideoRec);
         bitrateBox->setRange(100, 5000);
         bitrateBox->setSingleStep(100);
-        pOptLayout->addWidget(bitrateBox, 6, 3);
+        groupVideoRec->layout()->addWidget(bitrateBox, 6, 3);
 
         // button 'set default options'
-        btnDefaults = new QPushButton(pOptionsGroup);
+
+        btnDefaults = new QPushButton(groupVideoRec);
         btnDefaults->setText(QPushButton::tr("Set default options"));
         btnDefaults->setWhatsThis(QPushButton::tr("Restore default coding parameters"));
-        pOptLayout->addWidget(btnDefaults, 7, 0, 1, 5);
-
-        page3Layout->addWidget(pOptionsGroup, 1, 0);
+        groupVideoRec->layout()->addWidget(btnDefaults, 7, 0, 1, 5);
     }
 #endif
 
@@ -628,6 +772,10 @@
     previousResolutionIndex = this->CBResolution->currentIndex();
     previousFullscreenValue = this->CBFullscreen->isChecked();
 
+    setFullscreen(CBFullscreen->isChecked());
+    setVolume(SLVolume->value());
+
+    // mutually exclude window and fullscreen resolution
     return pageLayout;
 }
 
@@ -644,7 +792,8 @@
     connect(comboAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
     connect(btnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
 #endif
-
+    //connect(this, SIGNAL(pageEnter()), this, SLOT(setTeamOptionsEnabled()));
+    connect(SLVolume, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int)));
     connect(SLQuality, SIGNAL(valueChanged(int)), this, SLOT(setQuality(int)));
     connect(CBResolution, SIGNAL(currentIndexChanged(int)), this, SLOT(setResolution(int)));
     connect(CBFullscreen, SIGNAL(stateChanged(int)), this, SLOT(setFullscreen(int)));
@@ -653,6 +802,26 @@
     connect(CBSavePassword, SIGNAL(stateChanged(int)), this, SLOT(savePwdChanged(int)));
 }
 
+void PageOptions::setVolume(int volume)
+{
+    lblVolumeLevel->setText(QString("%1\%").arg(volume));
+}
+
+void PageOptions::setupTabPage(QWidget * tabpage, QVBoxLayout ** leftColumn, QVBoxLayout ** rightColumn)
+{
+    QHBoxLayout * twoColumns = new QHBoxLayout(tabpage);
+    twoColumns->setSpacing(0);
+    *leftColumn = new QVBoxLayout();
+    *rightColumn = new QVBoxLayout();
+    (*leftColumn)->setSpacing(OPTION_BOX_SPACING);
+    (*rightColumn)->setSpacing(OPTION_BOX_SPACING);
+    twoColumns->addStretch(4);
+    twoColumns->addLayout(*leftColumn, 0);
+    twoColumns->addStretch(1);
+    twoColumns->addLayout(*rightColumn, 0);
+    twoColumns->addStretch(4);
+}
+
 PageOptions::PageOptions(QWidget* parent) : AbstractPage(parent), config(0)
 {
     initPage();
@@ -666,16 +835,16 @@
     {
         this->SLQuality->setValue(this->SLQuality->maximum());
         this->SLQuality->setEnabled(false);
-        this->CBFullscreen->setEnabled(!forced);
         this->CBFullscreen->setChecked(forced ? true : previousFullscreenValue);
+        setFullscreen(forced ? true : previousFullscreenValue);
         this->CBResolution->setCurrentIndex(forced ? 0 : previousResolutionIndex);
     }
     else
     {
         this->SLQuality->setEnabled(true);
-        this->CBFullscreen->setEnabled(true);
         this->SLQuality->setValue(previousQuality);
         this->CBFullscreen->setChecked(previousFullscreenValue);
+        setFullscreen(previousFullscreenValue);
         this->CBResolution->setCurrentIndex(previousResolutionIndex);
     }
 }
@@ -693,6 +862,11 @@
 {
     Q_UNUSED(state);
 
+    lblFullScreenRes->setVisible(state);
+    CBResolution->setVisible(state);
+    lblWinScreenRes->setVisible(!state);
+    winResContainer->setVisible(!state);
+
     int index = this->CBStereoMode->currentIndex();
     if (index != 7 && index != 8 && index != 9)
         previousFullscreenValue = this->CBFullscreen->isChecked();
@@ -914,3 +1088,57 @@
 
     return true;
 }
+
+// When the current tab is switched
+void PageOptions::tabIndexChanged(int index)
+{
+    if (index == binderTab) // Switched to bind tab
+    {
+        binder->resetInterface();
+
+        if (!config) return;
+
+        QStandardItemModel * binds = DataManager::instance().bindsModel();
+        for(int i = 0; i < BINDS_NUMBER; i++)
+        {
+            QString value = config->bind(i);
+            QModelIndexList mdl = binds->match(binds->index(0, 0), Qt::UserRole + 1, value, 1, Qt::MatchExactly);
+            if(mdl.size() == 1) binder->setBindIndex(i, mdl[0].row());
+        }
+    }
+
+    currentTab = index;
+}
+
+// When a key bind combobox is changed
+void PageOptions::bindUpdated(int bindID)
+{
+    int bindIndex = binder->bindIndex(bindID);
+
+    if (bindIndex == 0) bindIndex = resetBindToDefault(bindID);
+
+    // Save bind
+    QStandardItemModel * binds = DataManager::instance().bindsModel();
+    QString strbind = binds->index(binder->bindIndex(bindID), 0).data(Qt::UserRole + 1).toString();
+    config->setBind(bindID, strbind);
+}
+
+// Changes a key bind (bindID) to its default value. This updates the bind's combo-box in the UI.
+// Returns: The bind model index of the default.
+int PageOptions::resetBindToDefault(int bindID)
+{
+    QStandardItemModel * binds = DataManager::instance().bindsModel();
+    QModelIndexList mdl = binds->match(binds->index(0, 0), Qt::UserRole + 1, cbinds[bindID].strbind, 1, Qt::MatchExactly);
+    if(mdl.size() == 1) binder->setBindIndex(bindID, mdl[0].row());
+    return mdl[0].row();
+}
+
+// Called when "reset all binds" button is pressed
+void PageOptions::resetAllBinds()
+{
+    for (int i = 0; i < BINDS_NUMBER; i++)
+    {
+        resetBindToDefault(i);
+        bindUpdated(i);
+    }
+}
--- a/QTfrontend/ui/page/pageoptions.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pageoptions.h	Sun Jan 27 00:28:57 2013 +0100
@@ -19,12 +19,32 @@
 #ifndef PAGE_OPTIONS_H
 #define PAGE_OPTIONS_H
 
+#include "igbox.h"
 #include "AbstractPage.h"
 
+#include <QString>
+
 class GameUIConfig;
 class FPSEdit;
-class IconedGroupBox;
 class QSignalMapper;
+class KeyBinder;
+class QGridLayout;
+
+// Let's stay D-R-Y
+class OptionGroupBox : public IconedGroupBox
+{
+    Q_OBJECT
+
+    public:
+        OptionGroupBox(const QString & iconName,
+                       const QString & title,
+                       QWidget * parent = 0);
+        QGridLayout * layout();
+        void addDivider();
+
+    private:
+        QGridLayout * m_layout;
+};
 
 class PageOptions : public AbstractPage
 {
@@ -57,6 +77,8 @@
         QComboBox *CBTeamName;
         IconedGroupBox *AGGroupBox;
         QComboBox *CBResolution;
+        QLineEdit *windowWidthEdit;
+        QLineEdit *windowHeightEdit;
         QComboBox *CBStereoMode;
         QCheckBox *CBFrontendSound;
         QCheckBox *CBFrontendMusic;
@@ -75,7 +97,8 @@
 
         FPSEdit *fpsedit;
         QLabel *labelNN;
-        QSpinBox * volumeBox;
+        QSlider *SLVolume;
+        QLabel *lblVolumeLevel;
         QLineEdit *editNetNick;
         QLineEdit *editNetPassword;
         QSlider *SLQuality;
@@ -118,6 +141,8 @@
         QLayout * bodyLayoutDefinition();
         QLayout * footerLayoutDefinition();
         void connectSignals();
+        int resetBindToDefault(int bindID);
+        void setupTabPage(QWidget * tabpage, QVBoxLayout ** leftColumn, QVBoxLayout ** rightColumn);
 
         bool previousFullscreenValue;
         int previousResolutionIndex;
@@ -134,6 +159,13 @@
         QPushButton *btnDefaults;
         QPushButton *btnUpdateNow;
         GameUIConfig * config;
+        KeyBinder * binder;
+        int currentTab;
+        int binderTab;
+
+        QLabel * lblFullScreenRes;
+        QLabel * lblWinScreenRes;
+        QWidget * winResContainer;
 
     private slots:
         void forceFullscreen(int index);
@@ -151,6 +183,10 @@
         void changeUseGameRes(int state);
         void changeRecordAudio(int state);
         void checkForUpdates();
+        void tabIndexChanged(int);
+        void bindUpdated(int bindID);
+        void resetAllBinds();
+        void setVolume(int);
 
     public slots:
         void setDefaultOptions();
--- a/QTfrontend/ui/page/pagescheme.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagescheme.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -68,127 +68,102 @@
     // Left
 
     TBW_mode_Forts = new ToggleButtonWidget(gbGameModes, ":/res/btnForts@2x.png");
-    TBW_mode_Forts->setToolTip("<b>" + ToggleButtonWidget::tr("Fort Mode") + "</b>");
     TBW_mode_Forts->setWhatsThis(tr("Defend your fort and destroy the opponents, two team colours max!"));
     glGMLayout->addWidget(TBW_mode_Forts,0,0,1,1);
 
     TBW_teamsDivide = new ToggleButtonWidget(gbGameModes, ":/res/btnTeamsDivide@2x.png");
-    TBW_teamsDivide->setToolTip("<b>" + ToggleButtonWidget::tr("Divide Teams") + "</b>");
     TBW_teamsDivide->setWhatsThis(tr("Teams will start on opposite sides of the terrain, two team colours max!"));
     glGMLayout->addWidget(TBW_teamsDivide,0,1,1,1);
 
     TBW_solid = new ToggleButtonWidget(gbGameModes, ":/res/btnSolid@2x.png");
-    TBW_solid->setToolTip("<b>" + ToggleButtonWidget::tr("Solid Land") + "</b>");
     TBW_solid->setWhatsThis(tr("Land can not be destroyed!"));
     glGMLayout->addWidget(TBW_solid,0,2,1,1);
 
     TBW_border = new ToggleButtonWidget(gbGameModes, ":/res/btnBorder@2x.png");
-    TBW_border->setToolTip("<b>" + ToggleButtonWidget::tr("Add Border") + "</b>");
     TBW_border->setWhatsThis(tr("Add an indestructible border around the terrain"));
     glGMLayout->addWidget(TBW_border,0,3,1,1);
 
     TBW_lowGravity = new ToggleButtonWidget(gbGameModes, ":/res/btnLowGravity@2x.png");
-    TBW_lowGravity->setToolTip("<b>" + ToggleButtonWidget::tr("Low Gravity") + "</b>");
     TBW_lowGravity->setWhatsThis(tr("Lower gravity"));
     glGMLayout->addWidget(TBW_lowGravity,0,4,1,1);
 
     TBW_laserSight = new ToggleButtonWidget(gbGameModes, ":/res/btnLaserSight@2x.png");
-    TBW_laserSight->setToolTip("<b>" + ToggleButtonWidget::tr("Laser Sight") + "</b>");
     TBW_laserSight->setWhatsThis(tr("Assisted aiming with laser sight"));
     glGMLayout->addWidget(TBW_laserSight,1,0,1,1);
 
     TBW_invulnerable = new ToggleButtonWidget(gbGameModes, ":/res/btnInvulnerable@2x.png");
-    TBW_invulnerable->setToolTip("<b>" + ToggleButtonWidget::tr("Invulnerable") + "</b>");
     TBW_invulnerable->setWhatsThis(tr("All hogs have a personal forcefield"));
     glGMLayout->addWidget(TBW_invulnerable,1,1,1,1);
 
     TBW_resethealth = new ToggleButtonWidget(gbGameModes, ":/res/btnResetHealth@2x.png");
-    TBW_resethealth->setToolTip("<b>" + ToggleButtonWidget::tr("Reset Health") + "</b>");
     TBW_resethealth->setWhatsThis(tr("All (living) hedgehogs are fully restored at the end of turn"));
     glGMLayout->addWidget(TBW_resethealth,1,2,1,1);
 
     TBW_vampiric = new ToggleButtonWidget(gbGameModes, ":/res/btnVampiric@2x.png");
-    TBW_vampiric->setToolTip("<b>" + ToggleButtonWidget::tr("Vampirism") + "</b>");
     TBW_vampiric->setWhatsThis(tr("Gain 80% of the damage you do back in health"));
     glGMLayout->addWidget(TBW_vampiric,1,3,1,1);
 
     TBW_karma = new ToggleButtonWidget(gbGameModes, ":/res/btnKarma@2x.png");
-    TBW_karma->setToolTip("<b>" + ToggleButtonWidget::tr("Karma") + "</b>");
     TBW_karma->setWhatsThis(tr("Share your opponents pain, share their damage"));
     glGMLayout->addWidget(TBW_karma,1,4,1,1);
 
     TBW_artillery = new ToggleButtonWidget(gbGameModes, ":/res/btnArtillery@2x.png");
-    TBW_artillery->setToolTip("<b>" + ToggleButtonWidget::tr("Artillery") + "</b>");
     TBW_artillery->setWhatsThis(tr("Your hogs are unable to move, put your artillery skills to the test"));
     glGMLayout->addWidget(TBW_artillery,2,0,1,1);
 
     TBW_randomorder = new ToggleButtonWidget(gbGameModes, ":/res/btnRandomOrder@2x.png");
-    TBW_randomorder->setToolTip("<b>" + ToggleButtonWidget::tr("Random Order") + "</b>");
     TBW_randomorder->setWhatsThis(tr("Order of play is random instead of in room order."));
     glGMLayout->addWidget(TBW_randomorder,2,1,1,1);
 
     TBW_king = new ToggleButtonWidget(gbGameModes, ":/res/btnKing@2x.png");
-    TBW_king->setToolTip("<b>" + ToggleButtonWidget::tr("King") + "</b>");
     TBW_king->setWhatsThis(tr("Play with a King. If he dies, your side dies."));
     glGMLayout->addWidget(TBW_king,2,2,1,1);
 
     TBW_placehog = new ToggleButtonWidget(gbGameModes, ":/res/btnPlaceHog@2x.png");
-    TBW_placehog->setToolTip("<b>" + ToggleButtonWidget::tr("Place Hedgehogs") + "</b>");
     TBW_placehog->setWhatsThis(tr("Take turns placing your hedgehogs before the start of play."));
     glGMLayout->addWidget(TBW_placehog,2,3,1,1);
 
     TBW_sharedammo = new ToggleButtonWidget(gbGameModes, ":/res/btnSharedAmmo@2x.png");
-    TBW_sharedammo->setToolTip("<b>" + ToggleButtonWidget::tr("Clan Shares Ammo") + "</b>");
     TBW_sharedammo->setWhatsThis(tr("Ammo is shared between all teams that share a colour."));
     glGMLayout->addWidget(TBW_sharedammo,2,4,1,1);
 
     TBW_disablegirders = new ToggleButtonWidget(gbGameModes, ":/res/btnDisableGirders@2x.png");
-    TBW_disablegirders->setToolTip("<b>" + ToggleButtonWidget::tr("Disable Girders") + "</b>");
     TBW_disablegirders->setWhatsThis(tr("Disable girders when generating random maps."));
     glGMLayout->addWidget(TBW_disablegirders,3,0,1,1);
 
     TBW_disablelandobjects = new ToggleButtonWidget(gbGameModes, ":/res/btnDisableLandObjects@2x.png");
-    TBW_disablelandobjects->setToolTip("<b>" + ToggleButtonWidget::tr("Disable Land Objects") + "</b>");
     TBW_disablelandobjects->setWhatsThis(tr("Disable land objects when generating random maps."));
     glGMLayout->addWidget(TBW_disablelandobjects,3,1,1,1);
 
     TBW_aisurvival = new ToggleButtonWidget(gbGameModes, ":/res/btnAISurvival@2x.png");
-    TBW_aisurvival->setToolTip("<b>" + ToggleButtonWidget::tr("AI Survival Mode") + "</b>");
     TBW_aisurvival->setWhatsThis(tr("AI respawns on death."));
     glGMLayout->addWidget(TBW_aisurvival,3,2,1,1);
 
     TBW_infattack = new ToggleButtonWidget(gbGameModes, ":/res/btnInfAttack@2x.png");
-    TBW_infattack->setToolTip("<b>" + ToggleButtonWidget::tr("Unlimited Attacks") + "</b>");
     TBW_infattack->setWhatsThis(tr("Attacking does not end your turn."));
     glGMLayout->addWidget(TBW_infattack,3,3,1,1);
 
     TBW_resetweps = new ToggleButtonWidget(gbGameModes, ":/res/btnResetWeps@2x.png");
-    TBW_resetweps->setToolTip("<b>" + ToggleButtonWidget::tr("Reset Weapons") + "</b>");
     TBW_resetweps->setWhatsThis(tr("Weapons are reset to starting values each turn."));
     glGMLayout->addWidget(TBW_resetweps,3,4,1,1);
 
     TBW_perhogammo = new ToggleButtonWidget(gbGameModes, ":/res/btnPerHogAmmo@2x.png");
-    TBW_perhogammo->setToolTip("<b>" + ToggleButtonWidget::tr("Per Hedgehog Ammo") + "</b>");
     TBW_perhogammo->setWhatsThis(tr("Each hedgehog has its own ammo. It does not share with the team."));
     glGMLayout->addWidget(TBW_perhogammo,4,0,1,1);
 
     TBW_nowind = new ToggleButtonWidget(gbGameModes, ":/res/btnNoWind@2x.png");
-    TBW_nowind->setToolTip("<b>" + ToggleButtonWidget::tr("Disable Wind") + "</b>");
     TBW_nowind->setWhatsThis(tr("You will not have to worry about wind anymore."));
     glGMLayout->addWidget(TBW_nowind,4,1,1,1);
 
     TBW_morewind = new ToggleButtonWidget(gbGameModes, ":/res/btnMoreWind@2x.png");
-    TBW_morewind->setToolTip("<b>" + ToggleButtonWidget::tr("More Wind") + "</b>");
     TBW_morewind->setWhatsThis(tr("Wind will affect almost everything."));
     glGMLayout->addWidget(TBW_morewind,4,2,1,1);
 
     TBW_tagteam = new ToggleButtonWidget(gbGameModes, ":/res/btnTagTeam@2x.png");
-    TBW_tagteam->setToolTip("<b>" + ToggleButtonWidget::tr("Tag Team") + "</b>");
     TBW_tagteam->setWhatsThis(tr("Teams in each clan take successive turns sharing their turn time."));
     glGMLayout->addWidget(TBW_tagteam,4,3,1,1);
 
     TBW_bottomborder = new ToggleButtonWidget(gbGameModes, ":/res/btnBottomBorder@2x.png");
-    TBW_bottomborder->setToolTip("<b>" + ToggleButtonWidget::tr("Add Bottom Border") + "</b>");
     TBW_bottomborder->setWhatsThis(tr("Add an indestructible border along the bottom"));
     glGMLayout->addWidget(TBW_bottomborder,4,4,1,1);
 
--- a/QTfrontend/ui/page/pagesingleplayer.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagesingleplayer.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -36,22 +36,18 @@
 
     topLine->addStretch();
     BtnSimpleGamePage = addButton(":/res/SimpleGame.png", topLine, 0, true);
-    BtnSimpleGamePage->setToolTip(tr("Simple Game"));
     BtnSimpleGamePage->setWhatsThis(tr("Play a quick game against the computer with random settings"));
     topLine->addSpacing(60);
     BtnMultiplayer = addButton(":/res/Multiplayer.png", topLine, 1, true);
-    BtnMultiplayer->setToolTip(tr("Multiplayer"));
     BtnMultiplayer->setWhatsThis(tr("Play a hotseat game against your friends, or AI teams"));
     topLine->addStretch();
 
 
     BtnCampaignPage = addButton(":/res/Campaign.png", middleLine, 0, true);
-    BtnCampaignPage->setToolTip(tr("Campaign Mode"));
     BtnCampaignPage->setWhatsThis(tr("Campaign Mode"));
     BtnCampaignPage->setVisible(true);
 
     BtnTrainPage = addButton(":/res/Trainings.png", middleLine, 1, true);
-    BtnTrainPage->setToolTip(tr("Training Mode"));
     BtnTrainPage->setWhatsThis(tr("Practice your skills in a range of training missions"));
 
     return vLayout;
@@ -63,11 +59,9 @@
     bottomLine->addStretch();
 
     BtnDemos = addButton(":/res/Record.png", bottomLine, 1, true);
-    BtnDemos->setToolTip(tr("Demos"));
     BtnDemos->setWhatsThis(tr("Watch recorded demos"));
     BtnLoad = addButton(":/res/Load.png", bottomLine, 2, true);
     BtnLoad->setStyleSheet("QPushButton{margin: 24px 0 0 0;}");
-    BtnLoad->setToolTip(tr("Load"));
     BtnLoad->setWhatsThis(tr("Load a previously saved game"));
 
     bottomLine->setStretch(1,0);
--- a/QTfrontend/ui/page/pagevideos.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/page/pagevideos.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -177,8 +177,9 @@
                                            Qt::LinksAccessibleByMouse |
                                            Qt::LinksAccessibleByKeyboard);
         labelDesc->setTextFormat(Qt::RichText);
+        labelDesc->setWordWrap(true);
         labelDesc->setOpenExternalLinks(true);
-        labelDesc->setMinimumSize(ThumbnailSize);
+        //labelDesc->setMinimumSize(ThumbnailSize);
         //pTopDescLayout->addWidget(labelDesc, 1);
 
         // buttons: play and delete
@@ -195,12 +196,10 @@
         btnToYouTube->setWhatsThis(QPushButton::tr("Upload this video to your Youtube account"));
         pBottomDescLayout->addWidget(btnToYouTube);
 
-        pDescLayout->addStretch(1);
         pDescLayout->addWidget(labelThumbnail, 0);
+        pDescLayout->addWidget(labelDesc, 0);
+        pDescLayout->addLayout(pBottomDescLayout, 0);
         pDescLayout->addStretch(1);
-        pDescLayout->addWidget(labelDesc, 0);
-        pDescLayout->addStretch(1);
-        pDescLayout->addLayout(pBottomDescLayout, 0);
 
         pPageLayout->addWidget(pDescGroup, 0, 0);
     }
@@ -509,8 +508,8 @@
     else
     {
         QString path = item->path();
-        desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n';
-        desc += tr("Size: ") + FileSizeStr(path) + '\n';
+        desc += tr("Date: %1\n").arg(QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate));
+        desc += tr("Size: %1\n").arg(FileSizeStr(path));
         if (item->desc.isEmpty())
         {
             // Extract description from file;
--- a/QTfrontend/ui/widget/about.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/about.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -23,10 +23,21 @@
 #include <QRegExp>
 #include <QNetworkAccessManager>
 #include <QNetworkRequest>
+#include <QMessageBox>
 #include <QNetworkReply>
 #include <QDebug>
 #include "hwconsts.h"
 #include "SDLInteraction.h"
+#include "SDL.h"
+#include "SDL_version.h"
+#include "physfs.h"
+
+#ifdef VIDEOREC
+extern "C"
+{
+#include "libavutil/avutil.h"
+}
+#endif
 
 #include "about.h"
 
@@ -35,6 +46,9 @@
 {
     QGridLayout *mainLayout = new QGridLayout(this);
 
+    QVBoxLayout * leftLayout = new QVBoxLayout();
+    mainLayout->addLayout(leftLayout, 0, 0, 2, 1);
+
     QLabel *imageLabel = new QLabel;
     QImage image(":/res/Hedgehog.png");
     imageLabel->setPixmap(QPixmap::fromImage(image));
@@ -44,7 +58,7 @@
     imageLabel->setMinimumHeight(30);
     imageLabel->setMaximumHeight(300);
 
-    mainLayout->addWidget(imageLabel, 0, 0, 2, 1);
+    leftLayout->addWidget(imageLabel, 0, Qt::AlignHCenter);
 
     QLabel *lbl1 = new QLabel(this);
     lbl1->setOpenExternalLinks(true);
@@ -55,7 +69,7 @@
         "</style>"
         "<div align=\"center\"><h1>Hedgewars</h1>"
         "<h3>" + QLabel::tr("Version") + " " + *cVersionString + "</h3>"
-        "<p><a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a></p><br>" +
+        "<p><a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a></p>" +
         QLabel::tr("This program is distributed under the GNU General Public License v2") +
         "</div>"
     );
@@ -68,6 +82,42 @@
     lbl2->setSource(localpage); //sets the source of the label from the file above
     mainLayout->addWidget(lbl2, 1, 1);
 
+    /* Library information */
+
+    QString libinfo = "<style type=text/css>a:link { color: #FFFF6E; }</style>";
+
+#ifdef __GNUC__
+    libinfo.append(QString("Compiler: <a href=\"http://gcc.gnu.org\">GCC</a> %1<br>").arg(__VERSION__));
+#else
+    libinfo.append(QString("Compiler: Unknown<br>").arg(__VERSION__));
+#endif
+
+    libinfo.append(QString("<a href=\"http://www.libsdl.org/\">SDL</a> version: %1.%2.%3<br>")
+        .arg(SDL_MAJOR_VERSION)
+        .arg(SDL_MINOR_VERSION)
+        .arg(SDL_PATCHLEVEL));
+
+    libinfo.append(QString("<a href=\"http://qt-project.org/\">Qt</a> version: %1<br>").arg(QT_VERSION_STR));
+
+#ifdef VIDEOREC
+    libinfo.append(QString("<a href=\"http://libav.org\">Libav</a> version: %1.%2.%3<br>")
+        .arg(LIBAVUTIL_VERSION_MAJOR)
+        .arg(LIBAVUTIL_VERSION_MINOR)
+        .arg(LIBAVUTIL_VERSION_MICRO));
+#endif
+
+    libinfo.append(QString("<a href=\"http://icculus.org/physfs/\">PhysFS</a> version: %1.%2.%3<br>")
+        .arg(PHYSFS_VER_MAJOR)
+        .arg(PHYSFS_VER_MINOR)
+        .arg(PHYSFS_VER_PATCH));
+
+    QLabel * lblLibInfo = new QLabel();
+    lblLibInfo->setText(libinfo);
+    lblLibInfo->setWordWrap(true);
+    lblLibInfo->setMaximumWidth(280);
+    leftLayout->addWidget(lblLibInfo, 0, Qt::AlignTop | Qt::AlignHCenter);
+    leftLayout->addStretch(1);
+
     setAcceptDrops(true);
 }
 
--- a/QTfrontend/ui/widget/about.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/about.h	Sun Jan 27 00:28:57 2013 +0100
@@ -23,7 +23,6 @@
 #include <QDropEvent>
 #include <QTextBrowser>
 
-
 class About : public QWidget
 {
         Q_OBJECT
--- a/QTfrontend/ui/widget/bgwidget.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/bgwidget.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -176,6 +176,8 @@
         int ydiff = newPos.y() - oldPos.y();
         update(oldPos.x(), oldPos.y(), xdiff+sprite.width(), ydiff+sprite.height());
     }
+
+    //repaint(); // Repaint every frame. Prevents ghosting of widgets if widgets resize in runtime.
 }
 
 void BGWidget::startAnimation()
--- a/QTfrontend/ui/widget/chatwidget.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/chatwidget.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -187,42 +187,54 @@
     m_isAdmin = false;
     m_autoKickEnabled = false;
 
-    {
-        QStringList vpList =
-             QStringList() << "Classic" << "Default" << "Mobster" << "Russian";
+    QStringList vpList =
+         QStringList() << "Classic" << "Default" << "Mobster" << "Russian";
 
-        foreach (QString vp, vpList)
-        {
-            m_helloSounds.append(QString("physfs://Sounds/voices/%1/Hello.ogg").arg(vp));
-        }
-
-        m_hilightSound = "physfs://Sounds/beep.ogg";
-
+    foreach (const QString & vp, vpList)
+    {
+        m_helloSounds.append(QString("/Sounds/voices/%1/Hello.ogg").arg(vp));
     }
 
-    mainLayout.setSpacing(1);
-    mainLayout.setMargin(1);
-    mainLayout.setSizeConstraint(QLayout::SetMinimumSize);
-    mainLayout.setColumnStretch(0, 76);
-    mainLayout.setColumnStretch(1, 24);
+    m_hilightSound = "/Sounds/beep.ogg";
+
+    mainLayout.setMargin(0);
 
-    chatEditLine = new SmartLineEdit(this);
-    chatEditLine->setMaxLength(300);
-    connect(chatEditLine, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+    QWidget * leftSideContainer = new QWidget();
+    leftSideContainer->setObjectName("leftSideContainer");
+    leftSideContainer->setStyleSheet("#leftSideContainer { border-width: 0px; background-color: #ffcc00; border-radius: 10px;} QTextBrowser, SmartLineEdit { background-color: rgb(13, 5, 68); }");
+    QVBoxLayout * leftSide = new QVBoxLayout(leftSideContainer);
+    leftSide->setSpacing(3);
+    leftSide->setMargin(3);
+    mainLayout.addWidget(leftSideContainer, 76);
 
-    mainLayout.addWidget(chatEditLine, 2, 0);
+    // Chat view
 
     chatText = new QTextBrowser(this);
-
     chatText->document()->setDefaultStyleSheet(styleSheet());
-
     chatText->setMinimumHeight(20);
     chatText->setMinimumWidth(10);
     chatText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     chatText->setOpenLinks(false);
+    chatText->setStyleSheet("QTextBrowser { background-color: rgb(23, 11, 54); border-width: 0px; }");
     connect(chatText, SIGNAL(anchorClicked(const QUrl&)),
             this, SLOT(linkClicked(const QUrl&)));
-    mainLayout.addWidget(chatText, 0, 0, 2, 1);
+    leftSide->addWidget(chatText, 1);
+
+    // Input box
+
+    // Normal:  rgb(23, 11, 54)
+    // Hover:   rgb(13, 5, 68)
+
+    chatEditLine = new SmartLineEdit();
+    chatEditLine->setMaxLength(300);
+    chatEditLine->setStyleSheet("SmartLineEdit { background-color: rgb(23, 11, 54); padding: 2px 8px; border-width: 0px; border-radius: 7px; } SmartLineEdit:hover, SmartLineEdit:focus { background-color: rgb(13, 5, 68); }");
+    chatEditLine->setFixedHeight(24);
+    chatEditLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    connect(chatEditLine, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+
+    leftSide->addWidget(chatEditLine, 0);
+
+    // Nickname list
 
     chatNicks = new QListView(this);
     chatNicks->setIconSize(QSize(24, 16));
@@ -238,7 +250,8 @@
 
     connect(chatNicks, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(nicksContextMenuRequested(QPoint)));
 
-    mainLayout.addWidget(chatNicks, 0, 1, 3, 1);
+    mainLayout.addSpacing(0);
+    mainLayout.addWidget(chatNicks, 24);
 
     // the userData is used to flag things that are even available when user
     // is offline
@@ -500,7 +513,7 @@
 
     emit nickCountUpdate(chatNicks->model()->rowCount());
 
-    if(notifyNick && notify && gameSettings->value("frontend/sound", true).toBool())
+    if (notifyNick && notify && (m_helloSounds.size() > 0))
     {
         SDLInteraction::instance().playSoundFile(
                             m_helloSounds.at(rand() % m_helloSounds.size()));
@@ -801,11 +814,7 @@
         else if (tline == "/saveStyleSheet")
             saveStyleSheet();
         else
-        {
-            static QRegExp post("\\s.*$");
-            tline.remove(post);
-            displayWarning(tr("%1 is not a valid command!").arg(tline));
-        }
+            emit consoleCommand(tline.mid(1));
 
         return true;
     }
--- a/QTfrontend/ui/widget/chatwidget.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/chatwidget.h	Sun Jan 27 00:28:57 2013 +0100
@@ -100,10 +100,11 @@
         void info(const QString & str);
         void follow(const QString &);
         void nickCountUpdate(int cnt);
+        void consoleCommand(const QString & command);
 
     private:
         bool m_isAdmin;
-        QGridLayout mainLayout;
+        QHBoxLayout mainLayout;
         QTextBrowser* chatText;
         QStringList chatStrings;
         QListView* chatNicks;
--- a/QTfrontend/ui/widget/colorwidget.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/colorwidget.h	Sun Jan 27 00:28:57 2013 +0100
@@ -13,7 +13,7 @@
 class ColorWidget : public QFrame
 {
     Q_OBJECT
-    
+
 public:
     explicit ColorWidget(QStandardItemModel *colorsModel, QWidget *parent = 0);
     ~ColorWidget();
@@ -24,7 +24,7 @@
 
 signals:
     void colorChanged(int color);
-    
+
 private:
     int m_color;
     QStandardItemModel * m_colorsModel;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,484 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QTextBrowser>
+#include <QLabel>
+#include <QHttp>
+#include <QSysInfo>
+#include <QDebug>
+#include <QBuffer>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QNetworkReply>
+#include <QProcess>
+#include <QMessageBox>
+#include <QCheckBox>
+
+#include <string>
+
+#ifdef Q_WS_WIN
+#define WINVER 0x0500
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+
+#ifdef Q_WS_MAC
+#include <sys/sysctl.h>
+#endif
+
+#include <stdint.h>
+
+#include "AbstractPage.h"
+#include "hwconsts.h"
+#include "feedbackdialog.h"
+
+FeedbackDialog::FeedbackDialog(QWidget * parent) : QDialog(parent)
+{
+    setModal(true);
+    setWindowFlags(Qt::Sheet);
+    setWindowModality(Qt::WindowModal);
+    setMinimumSize(700, 460);
+    resize(700, 460);
+    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+    netManager = NULL;
+    GenerateSpecs();
+
+    /* Top layout */
+
+    QVBoxLayout * pageLayout = new QVBoxLayout();
+    QHBoxLayout * summaryLayout = new QHBoxLayout();
+    QHBoxLayout * emailLayout = new QHBoxLayout();
+    QHBoxLayout * descriptionLayout = new QHBoxLayout();
+    QHBoxLayout * combinedTopLayout = new QHBoxLayout();
+    QHBoxLayout * systemLayout = new QHBoxLayout();
+
+    info = new QLabel();
+    info->setText(
+        "<style type=\"text/css\">"
+        "a { color: #fc0; }"
+        "b { color: #0df; }"
+        "</style>"
+        "<div align=\"center\"><h1>Please give us feedback!</h1>"
+        "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
+        "<h4>Your email address is optional, but we may want to contact you.<h4>"
+        "</div>"
+    );
+    pageLayout->addWidget(info);
+
+    QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
+
+    const int labelWidth = 90;
+
+    label_email = new QLabel();
+    label_email->setText(QLabel::tr("Your Email"));
+    label_email->setFixedWidth(labelWidth);
+    emailLayout->addWidget(label_email);
+    email = new QLineEdit();
+    emailLayout->addWidget(email);
+    summaryEmailLayout->addLayout(emailLayout);
+
+    label_summary = new QLabel();
+    label_summary->setText(QLabel::tr("Summary"));
+    label_summary->setFixedWidth(labelWidth);
+    summaryLayout->addWidget(label_summary);
+    summary = new QLineEdit();
+    summaryLayout->addWidget(summary);
+    summaryEmailLayout->addLayout(summaryLayout);
+
+    combinedTopLayout->addLayout(summaryEmailLayout);
+
+    CheckSendSpecs = new QCheckBox();
+    CheckSendSpecs->setText(QLabel::tr("Send system information"));
+    CheckSendSpecs->setChecked(true);
+    systemLayout->addWidget(CheckSendSpecs);
+    BtnViewInfo = new QPushButton(tr("View"));
+    systemLayout->addWidget(BtnViewInfo, 1);
+    BtnViewInfo->setFixedSize(60, 30);
+    connect(BtnViewInfo, SIGNAL(clicked()), this, SLOT(ShowSpecs()));
+    combinedTopLayout->addLayout(systemLayout);
+
+    combinedTopLayout->setStretch(0, 1);
+    combinedTopLayout->insertSpacing(1, 20);
+
+    pageLayout->addLayout(combinedTopLayout);
+
+    label_description = new QLabel();
+    label_description->setText(QLabel::tr("Description"));
+    label_description->setFixedWidth(labelWidth);
+    descriptionLayout->addWidget(label_description, 0, Qt::AlignTop);
+    description = new QTextBrowser();
+    description->setReadOnly(false);
+    descriptionLayout->addWidget(description);
+    pageLayout->addLayout(descriptionLayout);
+
+    /* Bottom layout */
+
+    QHBoxLayout * bottomLayout = new QHBoxLayout();
+    QHBoxLayout * captchaLayout = new QHBoxLayout();
+    QVBoxLayout * captchaInputLayout = new QVBoxLayout();
+
+    QPushButton * BtnCancel = new QPushButton(tr("Cancel"));
+    bottomLayout->addWidget(BtnCancel, 0);
+    BtnCancel->setFixedSize(100, 40);
+    connect(BtnCancel, SIGNAL(clicked()), this, SLOT(reject()));
+
+    bottomLayout->insertStretch(1);
+
+    label_captcha = new QLabel();
+    label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
+    label_captcha->setText("loading<br>captcha");
+    label_captcha->setFixedSize(200, 50);
+    captchaLayout->addWidget(label_captcha);
+
+    label_captcha_input = new QLabel();
+    label_captcha_input->setText(QLabel::tr("Type the security code:"));
+    captchaInputLayout->addWidget(label_captcha_input);
+    captchaInputLayout->setAlignment(label_captcha, Qt::AlignBottom);
+    captcha_code = new QLineEdit();
+    captcha_code->setFixedSize(165, 30);
+    captchaInputLayout->addWidget(captcha_code);
+    captchaInputLayout->setAlignment(captcha_code, Qt::AlignTop);
+    captchaLayout->addLayout(captchaInputLayout);
+    captchaLayout->setAlignment(captchaInputLayout, Qt::AlignLeft);
+
+    bottomLayout->addLayout(captchaLayout);
+    bottomLayout->addSpacing(40);
+
+    // TODO: Set green arrow icon for send button (:/res/Start.png)
+    BtnSend = new QPushButton(tr("Send Feedback"));
+    bottomLayout->addWidget(BtnSend, 0);
+    BtnSend->setFixedSize(120, 40);
+    connect(BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
+
+    bottomLayout->setStretchFactor(captchaLayout, 0);
+    bottomLayout->setStretchFactor(BtnSend, 1);
+
+    QVBoxLayout * dialogLayout = new QVBoxLayout(this);
+    dialogLayout->addLayout(pageLayout, 1);
+    dialogLayout->addLayout(bottomLayout);
+
+    LoadCaptchaImage();
+}
+
+void FeedbackDialog::GenerateSpecs()
+{
+    // Gather some information about the system and embed it into the report
+    QDesktopWidget* screen = QApplication::desktop();
+    QString os_version = "Operating system: ";
+    QString qt_version = QString("Qt version: ") + QT_VERSION_STR + QString("\n");
+    QString total_ram = "Total RAM: ";
+    QString number_of_cores = "Number of cores: ";
+    QString compiler_bits = "Compiler architecture: ";
+    QString compiler_version = "Compiler version: ";
+    QString kernel_line = "Kernel: ";
+    QString screen_size = "Size of the screen(s): " +
+        QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
+    QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
+    std::string processor_name = "Processor: ";
+
+    // platform specific code
+#ifdef Q_WS_MACX
+    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
+
+    uint64_t memsize;
+    size_t len = sizeof(memsize);
+    static int mib_s[2] = { CTL_HW, HW_MEMSIZE };
+    if (sysctl (mib_s, 2, &memsize, &len, NULL, 0) == 0)
+        total_ram += QString::number(memsize/1024/1024) + " MB\n";
+    else
+        total_ram += "Error getting total RAM information\n";
+
+    int mib[] = {CTL_KERN, KERN_OSRELEASE};
+    sysctl(mib, sizeof mib / sizeof(int), NULL, &len, NULL, 0);
+
+    char *kernelVersion = (char *)malloc(sizeof(char)*len);
+    sysctl(mib, sizeof mib / sizeof(int), kernelVersion, &len, NULL, 0);
+
+    QString kernelVersionStr = QString(kernelVersion);
+    free(kernelVersion);
+    int major_version = kernelVersionStr.split(".").first().toUInt() - 4;
+    int minor_version = kernelVersionStr.split(".").at(1).toUInt();
+    os_version += QString("Mac OS X 10.%1.%2").arg(major_version).arg(minor_version) + " ";
+
+    switch(major_version)
+    {
+        case 4:  os_version += "\"Tiger\"\n"; break;
+        case 5:  os_version += "\"Leopard\"\n"; break;
+        case 6:  os_version += "\"Snow Leopard\"\n"; break;
+        case 7:  os_version += "\"Lion\"\n"; break;
+        case 8:  os_version += "\"Mountain Lion\"\n"; break;
+        default: os_version += "\"Unknown version\"\n"; break;
+    }
+#endif
+#ifdef Q_WS_WIN
+    SYSTEM_INFO sysinfo;
+    GetSystemInfo(&sysinfo);
+    number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
+    MEMORYSTATUSEX status;
+    status.dwLength = sizeof(status);
+    GlobalMemoryStatusEx(&status);
+    total_ram += QString::number(status.ullTotalPhys);
+
+    switch(QSysInfo::WinVersion())
+    {
+        case QSysInfo::WV_2000: os_version += "Windows 2000\n"; break;
+        case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
+        case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
+        case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
+        default: os_version += "Windows (Unknown version)\n"; break;
+    }
+    kernel_line += "Windows kernel\n";
+#endif
+#ifdef Q_WS_X11
+    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
+    long pages = sysconf(_SC_PHYS_PAGES),
+/*
+#ifndef Q_OS_FREEBSD
+         available_pages = sysconf(_SC_AVPHYS_PAGES),
+#else
+         available_pages = 0,
+#endif*/
+         page_size = sysconf(_SC_PAGE_SIZE);
+    total_ram += QString::number(pages * page_size) + "\n";
+    os_version += "GNU/Linux or BSD\n";
+#endif
+
+    // uname -a
+#if defined(Q_WS_X11) || defined(Q_WS_MACX)
+    QProcess *process = new QProcess();
+    QStringList arguments = QStringList("-a");
+    process->start("uname", arguments);
+    if (process->waitForFinished())
+        kernel_line += QString(process->readAll());
+    delete process;
+#endif
+
+    // cpu info
+    quint32 registers[4];
+    quint32 i;
+
+    i = 0x80000002;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 4);
+    i = 0x80000003;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 4);
+    i = 0x80000004;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 3);
+
+    // compiler
+#ifdef __GNUC__
+    compiler_version += "GCC " + QString(__VERSION__) + "\n";
+#else
+    compiler_version += "Unknown\n";
+#endif
+
+    if(sizeof(void*) == 4)
+        compiler_bits += "i386\n";
+    else if(sizeof(void*) == 8)
+        compiler_bits += "x86_64\n";
+
+    // concat system info
+    specs = qt_version
+        + os_version
+        + total_ram
+        + screen_size
+        + number_of_screens
+        + QString::fromStdString(processor_name + "\n")
+        + number_of_cores
+        + compiler_version
+        + compiler_bits
+        + kernel_line;
+}
+
+void FeedbackDialog::ShowErrorMessage(const QString & msg)
+{
+    QMessageBox msgMsg(this);
+    msgMsg.setIcon(QMessageBox::Warning);
+    msgMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Error"));
+    msgMsg.setText(msg);
+    msgMsg.setWindowModality(Qt::WindowModal);
+    msgMsg.exec();
+}
+
+void FeedbackDialog::ShowSpecs()
+{
+    QMessageBox msgMsg(this);
+    msgMsg.setIcon(QMessageBox::Information);
+    msgMsg.setWindowTitle(QMessageBox::tr("System Information Preview"));
+    msgMsg.setText(specs);
+    msgMsg.setTextFormat(Qt::PlainText);
+    msgMsg.setWindowModality(Qt::WindowModal);
+    msgMsg.setStyleSheet("background: #0A0533;");
+    msgMsg.exec();
+}
+
+void FeedbackDialog::NetReply(QNetworkReply *reply)
+{
+    if (reply == genCaptchaRequest)
+    {
+        if (reply->error() != QNetworkReply::NoError)
+        {
+            qDebug() << "Error generating captcha image: " << reply->errorString();
+            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
+            return;
+        }
+
+        bool okay;
+        QByteArray body = reply->readAll();
+        captchaID = QString(body).toInt(&okay);
+
+        if (!okay)
+        {
+            qDebug() << "Failed to get captcha ID: " << body;
+            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
+            return;
+        }
+
+        QString url = "http://hedgewars.org/feedback/?captcha&id=";
+        url += QString::number(captchaID);
+
+        QNetworkAccessManager *netManager = GetNetManager();
+        QUrl captchaURL(url);
+        QNetworkRequest req(captchaURL);
+        captchaImageRequest = netManager->get(req);
+    }
+    else if (reply == captchaImageRequest)
+    {
+        if (reply->error() != QNetworkReply::NoError)
+        {
+            qDebug() << "Error loading captcha image: " << reply->errorString();
+            ShowErrorMessage(QMessageBox::tr("Failed to download captcha"));
+            return;
+        }
+
+        QByteArray imageData = reply->readAll();
+        QPixmap pixmap;
+        pixmap.loadFromData(imageData);
+        label_captcha->setPixmap(pixmap);
+        captcha_code->setText("");
+    }
+}
+
+QNetworkAccessManager * FeedbackDialog::GetNetManager()
+{
+    if (netManager) return netManager;
+    netManager = new QNetworkAccessManager(this);
+    connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(NetReply(QNetworkReply*)));
+    return netManager;
+}
+
+void FeedbackDialog::LoadCaptchaImage()
+{
+        QNetworkAccessManager *netManager = GetNetManager();
+        QUrl captchaURL("http://hedgewars.org/feedback/?gencaptcha");
+        QNetworkRequest req(captchaURL);
+        genCaptchaRequest = netManager->get(req);
+}
+
+void FeedbackDialog::finishedSlot(QNetworkReply* reply)
+{
+    if (reply && reply->error() == QNetworkReply::NoError)
+    {
+            QMessageBox infoMsg(this);
+            infoMsg.setIcon(QMessageBox::Information);
+            infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
+            infoMsg.setText(reply->readAll());
+            infoMsg.setWindowModality(Qt::WindowModal);
+            infoMsg.exec();
+
+            accept();
+
+            return;
+    }
+    else
+    {
+        ShowErrorMessage(QString("Error: ") + reply->readAll());
+        LoadCaptchaImage();
+    }
+}
+
+void FeedbackDialog::SendFeedback()
+{
+    // Get form data
+
+    QString summary = this->summary->text();
+    QString description = this->description->toPlainText();
+    QString email = this->email->text();
+    QString captchaCode = this->captcha_code->text();
+    QString captchaID = QString::number(this->captchaID);
+    QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
+
+    if (summary.isEmpty() || description.isEmpty())
+    {
+        ShowErrorMessage(QMessageBox::tr("Please fill out all fields. Email is optional."));
+        return;
+    }
+
+    // Submit issue to PHP script
+
+    QByteArray body;
+    body.append("captcha=");
+    body.append(captchaID);
+    body.append("&code=");
+    body.append(captchaCode);
+    body.append("&version=");
+    body.append(QUrl::toPercentEncoding(version));
+    body.append("&title=");
+    body.append(QUrl::toPercentEncoding(summary));
+    body.append("&body=");
+    body.append(QUrl::toPercentEncoding(description));
+    body.append("&email=");
+    body.append(QUrl::toPercentEncoding(email));
+    if (CheckSendSpecs->isChecked())
+    {
+        body.append("&specs=");
+        body.append(QUrl::toPercentEncoding(specs));
+    }
+
+    nam = new QNetworkAccessManager(this);
+    connect(nam, SIGNAL(finished(QNetworkReply*)),
+            this, SLOT(finishedSlot(QNetworkReply*)));
+
+    QNetworkRequest header(QUrl("http://hedgewars.org/feedback/?submit"));
+    header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
+    header.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
+
+    nam->post(header, body);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/feedbackdialog.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,75 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef FEEDBACKDIALOG_H
+#define FEEDBACKDIALOG_H
+
+#include <QDialog>
+
+class QNetworkReply;
+class QNetworkAccessManager;
+class QCheckBox;
+class QLineEdit;
+class QTextBrowser;
+class QLabel;
+
+class FeedbackDialog : public QDialog
+{
+        Q_OBJECT
+
+    public:
+        FeedbackDialog(QWidget * parent = 0);
+        void EmbedSystemInfo();
+        void LoadCaptchaImage();
+
+        QPushButton * BtnSend;
+        QPushButton * BtnViewInfo;
+        QCheckBox * CheckSendSpecs;
+        QLineEdit * summary;
+        QTextBrowser * description;
+        QLabel * info;
+        QLabel * label_summary;
+        QLabel * label_description;
+        QLabel * label_captcha;
+        QLabel * label_email;
+        QLabel * label_captcha_input;
+        QLineEdit * captcha_code;
+        QLineEdit * email;
+        int captchaID;
+        QString specs;
+
+    private:
+        void GenerateSpecs();
+        QLayout * bodyLayoutDefinition();
+        QLayout * footerLayoutDefinition();
+        QNetworkAccessManager * GetNetManager();
+        void ShowErrorMessage(const QString & msg);
+
+        QNetworkAccessManager * netManager;
+        QNetworkReply * captchaImageRequest;
+        QNetworkReply * genCaptchaRequest;
+        QNetworkAccessManager * nam;
+
+    private slots:
+        virtual void NetReply(QNetworkReply*);
+        virtual void ShowSpecs();
+        void SendFeedback();
+        void finishedSlot(QNetworkReply* reply);
+};
+
+#endif // FEEDBACKDIALOG_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/flowlayout.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,190 @@
+ /****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the examples of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** You may use this file under the terms of the BSD license as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ **   * Redistributions of source code must retain the above copyright
+ **     notice, this list of conditions and the following disclaimer.
+ **   * Redistributions in binary form must reproduce the above copyright
+ **     notice, this list of conditions and the following disclaimer in
+ **     the documentation and/or other materials provided with the
+ **     distribution.
+ **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+ **     the names of its contributors may be used to endorse or promote
+ **     products derived from this software without specific prior written
+ **     permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+ #include <QtGui>
+
+ #include "flowlayout.h"
+
+FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
+    : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+    setContentsMargins(margin, margin, margin, margin);
+}
+
+FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
+    : m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+    setContentsMargins(margin, margin, margin, margin);
+}
+
+FlowLayout::~FlowLayout()
+{
+    QLayoutItem *item;
+    while ((item = takeAt(0)))
+        delete item;
+}
+
+void FlowLayout::addItem(QLayoutItem *item)
+{
+    itemList.append(item);
+}
+
+int FlowLayout::horizontalSpacing() const
+{
+    if (m_hSpace >= 0) {
+        return m_hSpace;
+    } else {
+        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+    }
+}
+
+int FlowLayout::verticalSpacing() const
+{
+    if (m_vSpace >= 0) {
+        return m_vSpace;
+    } else {
+        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+    }
+}
+
+int FlowLayout::count() const
+{
+    return itemList.size();
+}
+
+QLayoutItem *FlowLayout::itemAt(int index) const
+{
+    return itemList.value(index);
+}
+
+QLayoutItem *FlowLayout::takeAt(int index)
+{
+    if (index >= 0 && index < itemList.size())
+        return itemList.takeAt(index);
+    else
+        return 0;
+}
+
+Qt::Orientations FlowLayout::expandingDirections() const
+{
+    return 0;
+}
+
+bool FlowLayout::hasHeightForWidth() const
+{
+    return true;
+}
+
+int FlowLayout::heightForWidth(int width) const
+{
+    int height = doLayout(QRect(0, 0, width, 0), true);
+    return height;
+}
+
+void FlowLayout::setGeometry(const QRect &rect)
+{
+    QLayout::setGeometry(rect);
+    doLayout(rect, false);
+}
+
+QSize FlowLayout::sizeHint() const
+{
+    return minimumSize();
+}
+
+QSize FlowLayout::minimumSize() const
+{
+    QSize size;
+    QLayoutItem *item;
+    foreach (item, itemList)
+        size = size.expandedTo(item->minimumSize());
+
+    size += QSize(2*margin(), 2*margin());
+    return size;
+}
+
+int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
+{
+    int left, top, right, bottom;
+    getContentsMargins(&left, &top, &right, &bottom);
+    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+    int x = effectiveRect.x();
+    int y = effectiveRect.y();
+    int lineHeight = 0;
+
+    QLayoutItem *item;
+    foreach (item, itemList) {
+        QWidget *wid = item->widget();
+        int spaceX = horizontalSpacing();
+        if (spaceX == -1)
+            spaceX = wid->style()->layoutSpacing(
+                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+        int spaceY = verticalSpacing();
+        if (spaceY == -1)
+            spaceY = wid->style()->layoutSpacing(
+                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+        int nextX = x + item->sizeHint().width() + spaceX;
+        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
+            x = effectiveRect.x();
+            y = y + lineHeight + spaceY;
+            nextX = x + item->sizeHint().width() + spaceX;
+            lineHeight = 0;
+        }
+
+        if (!testOnly)
+            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+        x = nextX;
+        lineHeight = qMax(lineHeight, item->sizeHint().height());
+    }
+    return y + lineHeight - rect.y() + bottom;
+}
+int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
+{
+    QObject *parent = this->parent();
+    if (!parent) {
+        return -1;
+    } else if (parent->isWidgetType()) {
+        QWidget *pw = static_cast<QWidget *>(parent);
+        return pw->style()->pixelMetric(pm, 0, pw);
+    } else {
+        return static_cast<QLayout *>(parent)->spacing();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/flowlayout.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,77 @@
+ /****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the examples of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** You may use this file under the terms of the BSD license as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ **   * Redistributions of source code must retain the above copyright
+ **     notice, this list of conditions and the following disclaimer.
+ **   * Redistributions in binary form must reproduce the above copyright
+ **     notice, this list of conditions and the following disclaimer in
+ **     the documentation and/or other materials provided with the
+ **     distribution.
+ **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+ **     the names of its contributors may be used to endorse or promote
+ **     products derived from this software without specific prior written
+ **     permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+ #ifndef FLOWLAYOUT_H
+ #define FLOWLAYOUT_H
+
+ #include <QLayout>
+ #include <QRect>
+ #include <QStyle>
+ #include <QWidgetItem>
+ class FlowLayout : public QLayout
+ {
+ public:
+     FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
+     FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
+     ~FlowLayout();
+
+     void addItem(QLayoutItem *item);
+     int horizontalSpacing() const;
+     int verticalSpacing() const;
+     Qt::Orientations expandingDirections() const;
+     bool hasHeightForWidth() const;
+     int heightForWidth(int) const;
+     int count() const;
+     QLayoutItem *itemAt(int index) const;
+     QSize minimumSize() const;
+     void setGeometry(const QRect &rect);
+     QSize sizeHint() const;
+     QLayoutItem *takeAt(int index);
+
+ private:
+     int doLayout(const QRect &rect, bool testOnly) const;
+     int smartSpacing(QStyle::PixelMetric pm) const;
+
+     QList<QLayoutItem *> itemList;
+     int m_hSpace;
+     int m_vSpace;
+ };
+
+ #endif
--- a/QTfrontend/ui/widget/gamecfgwidget.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/gamecfgwidget.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -24,7 +24,11 @@
 #include <QLabel>
 #include <QMessageBox>
 #include <QTableView>
+#include <QScrollBar>
+#include <QTabWidget>
 #include <QPushButton>
+#include <QDebug>
+#include <QList>
 
 #include "gamecfgwidget.h"
 #include "igbox.h"
@@ -33,6 +37,7 @@
 #include "ammoSchemeModel.h"
 #include "proto.h"
 #include "GameStyleModel.h"
+#include "themeprompt.h"
 
 GameCFGWidget::GameCFGWidget(QWidget* parent) :
     QGroupBox(parent)
@@ -40,28 +45,75 @@
     , seedRegexp("\\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\}")
 {
     mainLayout.setMargin(0);
-//  mainLayout.setSizeConstraint(QLayout::SetMinimumSize);
+    setMinimumHeight(310);
+    setMaximumHeight(447);
+    setMinimumWidth(470);
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+    // Easy containers for the map/game options in either stacked or tabbed mode
+
+    mapContainerFree = new QWidget();
+    mapContainerTabbed = new QWidget();
+    optionsContainerFree = new QWidget();
+    optionsContainerTabbed = new QWidget();
+    tabbed = false;
+
+    // Container for when in tabbed mode
 
-    pMapContainer = new HWMapContainer(this);
-    mainLayout.addWidget(pMapContainer, 0, 0);
+    tabs = new QTabWidget(this);
+    tabs->setFixedWidth(470);
+    tabs->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+    tabs->addTab(mapContainerTabbed, tr("Map"));
+    tabs->addTab(optionsContainerTabbed, tr("Game options"));
+    tabs->setObjectName("gameCfgWidgetTabs");
+    mainLayout.addWidget(tabs, 1);
+    tabs->setVisible(false);
+
+    // Container for when in stacked mode
+
+    StackContainer = new QWidget();
+    StackContainer->setObjectName("gameStackContainer");
+    mainLayout.addWidget(StackContainer, 1);
+    QVBoxLayout * stackLayout = new QVBoxLayout(StackContainer);
 
-    IconedGroupBox *GBoxOptions = new IconedGroupBox(this);
-    GBoxOptions->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
-    mainLayout.addWidget(GBoxOptions, 1, 0);
+    // Map options
+
+    pMapContainer = new HWMapContainer(mapContainerFree);
+    stackLayout->addWidget(mapContainerFree, 0, Qt::AlignHCenter);
+    pMapContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    pMapContainer->setFixedSize(width() - 14, 278);
+    mapContainerFree->setFixedSize(pMapContainer->width(), pMapContainer->height());
+
+    // Horizontal divider
+
+    QFrame * divider = new QFrame();
+    divider->setFrameShape(QFrame::HLine);
+    divider->setFrameShadow(QFrame::Plain);
+    stackLayout->addWidget(divider, 0, Qt::AlignBottom);
+    //stackLayout->setRowMinimumHeight(1, 10);
 
-    QGridLayout *GBoxOptionsLayout = new QGridLayout(GBoxOptions);
+    // Game options
+
+    optionsContainerTabbed->setContentsMargins(0, 0, 0, 0);
+    optionsContainerFree->setFixedSize(width() - 14, 140);
+    stackLayout->addWidget(optionsContainerFree, 0, Qt::AlignHCenter);
 
-    GBoxOptions->setTitle(tr("Game Options"));
-    GBoxOptionsLayout->addWidget(new QLabel(QLabel::tr("Style"), GBoxOptions), 1, 0);
+    OptionsInnerContainer = new QWidget(optionsContainerFree);
+    m_childWidgets << OptionsInnerContainer;
+    OptionsInnerContainer->setFixedSize(optionsContainerFree->width(), optionsContainerFree->height());
+    OptionsInnerContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+    GBoxOptionsLayout = new QGridLayout(OptionsInnerContainer);
 
-    Scripts = new QComboBox(GBoxOptions);
+    GBoxOptionsLayout->addWidget(new QLabel(QLabel::tr("Style"), this), 1, 0);
+
+    Scripts = new QComboBox(this);
     GBoxOptionsLayout->addWidget(Scripts, 1, 1);
 
     Scripts->setModel(DataManager::instance().gameStyleModel());
     m_curScript = Scripts->currentText();
     connect(Scripts, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptChanged(int)));
 
-    QWidget *SchemeWidget = new QWidget(GBoxOptions);
+    QWidget *SchemeWidget = new QWidget(this);
     GBoxOptionsLayout->addWidget(SchemeWidget, 2, 0, 1, 2);
 
     QGridLayout *SchemeWidgetLayout = new QGridLayout(SchemeWidget);
@@ -76,7 +128,7 @@
     QPixmap pmEdit(":/res/edit.png");
 
     QPushButton * goToSchemePage = new QPushButton(SchemeWidget);
-    goToSchemePage->setToolTip(tr("Edit schemes"));
+    goToSchemePage->setWhatsThis(tr("Edit schemes"));
     goToSchemePage->setIconSize(pmEdit.size());
     goToSchemePage->setIcon(pmEdit);
     goToSchemePage->setMaximumWidth(pmEdit.width() + 6);
@@ -91,7 +143,7 @@
     connect(WeaponsName, SIGNAL(currentIndexChanged(int)), this, SLOT(ammoChanged(int)));
 
     QPushButton * goToWeaponPage = new QPushButton(SchemeWidget);
-    goToWeaponPage->setToolTip(tr("Edit weapons"));
+    goToWeaponPage->setWhatsThis(tr("Edit weapons"));
     goToWeaponPage->setIconSize(pmEdit.size());
     goToWeaponPage->setIcon(pmEdit);
     goToWeaponPage->setMaximumWidth(pmEdit.width() + 6);
@@ -99,7 +151,7 @@
     connect(goToWeaponPage, SIGNAL(clicked()), this, SLOT(jumpToWeapons()));
 
     bindEntries = new QCheckBox(SchemeWidget);
-    bindEntries->setToolTip(tr("When this option is enabled selecting a game scheme will auto-select a weapon"));
+    bindEntries->setWhatsThis(tr("Game scheme will auto-select a weapon"));
     bindEntries->setChecked(true);
     bindEntries->setMaximumWidth(42);
     bindEntries->setStyleSheet( "QCheckBox::indicator:checked   { image: url(\":/res/lock.png\"); }"
@@ -118,6 +170,42 @@
     connect(&DataManager::instance(), SIGNAL(updated()), this, SLOT(updateModelViews()));
 }
 
+void GameCFGWidget::setTabbed(bool tabbed)
+{
+    if (tabbed && !this->tabbed)
+    { // Make tabbed
+        tabs->setCurrentIndex(0);
+        StackContainer->setVisible(false);
+        tabs->setVisible(true);
+        pMapContainer->setParent(mapContainerTabbed);
+        OptionsInnerContainer->setParent(optionsContainerTabbed);
+        pMapContainer->setVisible(true);
+        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+        this->tabbed = true;
+    }
+    else if (!tabbed && this->tabbed)
+    { // Make stacked
+        pMapContainer->setParent(mapContainerFree);
+        OptionsInnerContainer->setParent(optionsContainerFree);
+        tabs->setVisible(false);
+        StackContainer->setVisible(true);
+        pMapContainer->setVisible(true);
+        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        this->tabbed = false;
+    }
+
+    // Restore scrollbar palettes, since Qt seems to forget them easily when switching parents
+    QList<QScrollBar *> allSBars = findChildren<QScrollBar *>();
+    QPalette pal = palette();
+    pal.setColor(QPalette::WindowText, QColor(0xff, 0xcc, 0x00));
+    pal.setColor(QPalette::Button, QColor(0x00, 0x35, 0x1d));
+    pal.setColor(QPalette::Base, QColor(0x00, 0x35, 0x1d));
+    pal.setColor(QPalette::Window, QColor(0x00, 0x00, 0x00));
+
+    for (int i = 0; i < allSBars.size(); ++i)
+        allSBars.at(i)->setPalette(pal);
+}
+
 void GameCFGWidget::jumpToSchemes()
 {
     emit goToSchemes(GameSchemes->currentIndex());
@@ -186,7 +274,7 @@
     if (schemeData(24).toBool())
         result |= 0x02000000;       // tag team
     if (schemeData(25).toBool())
-        result |= 0x04000000;       // bottom border
+        result |= 0x04000000;       // bottom
 
     return result;
 }
@@ -235,6 +323,8 @@
     bcfg << QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()).toUtf8();
     bcfg << QString("e$mapgen %1").arg(mapgen).toUtf8();
 
+
+
     switch (mapgen)
     {
         case MAPGEN_MAZE:
@@ -271,7 +361,7 @@
     bool illegal = ammo.size() != cDefaultAmmoStore->size();
     if (illegal)
     {
-        QMessageBox illegalMsg(this);
+        QMessageBox illegalMsg(parentWidget());
         illegalMsg.setIcon(QMessageBox::Warning);
         illegalMsg.setWindowTitle(QMessageBox::tr("Error"));
         illegalMsg.setText(QMessageBox::tr("Cannot use the ammo '%1'!").arg(name));
@@ -325,10 +415,6 @@
         if (param == "SEED")
         {
             pMapContainer->setSeed(value);
-            if (!seedRegexp.exactMatch(value))
-            {
-                pMapContainer->seedEdit->setVisible(true);
-            }
             return;
         }
         if (param == "THEME")
@@ -377,8 +463,6 @@
         if (param == "FULLMAPCONFIG")
         {
             QString seed = slValue[3];
-            if (!seedRegexp.exactMatch(seed))
-                pMapContainer->seedEdit->setVisible(true);
 
             pMapContainer->setAllMapParameters(
                 slValue[0],
@@ -586,3 +670,19 @@
             Scripts->setCurrentIndex(0);
     }
 }
+
+bool GameCFGWidget::isMaster()
+{
+    return m_master;
+}
+
+void GameCFGWidget::setMaster(bool master)
+{
+    if (master == m_master) return;
+    m_master = master;
+
+    pMapContainer->setMaster(master);
+
+    foreach (QWidget *widget, m_childWidgets)
+        widget->setEnabled(master);
+}
--- a/QTfrontend/ui/widget/gamecfgwidget.h	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/gamecfgwidget.h	Sun Jan 27 00:28:57 2013 +0100
@@ -31,11 +31,14 @@
 class QVBoxLayout;
 class QLabel;
 class QTableView;
+class QTabWidget;
 
 class GameCFGWidget : public QGroupBox
 {
         Q_OBJECT
 
+        Q_PROPERTY(bool master READ isMaster WRITE setMaster)
+
     public:
         GameCFGWidget(QWidget* parent);
         quint32 getGameFlags() const;
@@ -47,11 +50,14 @@
         HWMapContainer* pMapContainer;
         QTableView * tv;
         QVariant schemeData(int column) const;
+        bool isMaster();
 
     public slots:
         void setParam(const QString & param, const QStringList & value);
         void fullNetConfig();
         void resendSchemeData();
+        void setMaster(bool master);
+        void setTabbed(bool tabbed);
 
     signals:
         void paramChanged(const QString & param, const QStringList & value);
@@ -75,12 +81,24 @@
         void updateModelViews();
 
     private:
-        QGridLayout mainLayout;
+        QVBoxLayout mainLayout;
         QCheckBox * bindEntries;
         QString curNetAmmoName;
         QString curNetAmmo;
         QRegExp seedRegexp;
         QString m_curScript;
+        bool m_master;
+        QList<QWidget *> m_childWidgets;
+        QGridLayout * GBoxOptionsLayout;
+        QWidget * OptionsInnerContainer;
+        QWidget * StackContainer;
+
+        QWidget * mapContainerFree;
+        QWidget * mapContainerTabbed;
+        QWidget * optionsContainerFree;
+        QWidget * optionsContainerTabbed;
+        bool tabbed;
+        QTabWidget * tabs;
 
         void setNetAmmo(const QString& name, const QString& ammo);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/hatbutton.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,72 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QDebug>
+
+#include "hatprompt.h"
+#include "DataManager.h"
+#include "HatModel.h"
+#include "hatbutton.h"
+
+HatButton::HatButton(QWidget* parent) : QPushButton(parent)
+{
+    setIconSize(QSize(32, 37));
+    setFixedSize(44, 44);
+
+    m_hatModel = DataManager::instance().hatModel();
+    connect(this, SIGNAL(clicked()), this, SLOT(showPrompt()));
+
+    setCurrentIndex(0);
+}
+
+void HatButton::setCurrentIndex(int index)
+{
+    m_hat = m_hatModel->index(index, 0);
+    setWhatsThis(QString(tr("Change hat (%1)")).arg(m_hat.data(Qt::DisplayRole).toString()));
+    setToolTip(m_hat.data(Qt::DisplayRole).toString());
+    setIcon(m_hat.data(Qt::DecorationRole).value<QIcon>());
+}
+
+int HatButton::currentIndex()
+{
+    return m_hat.row();
+}
+
+void HatButton::setCurrentHat(const QString & name)
+{
+    QList<QStandardItem *> hats = m_hatModel->findItems(name);
+
+    if (hats.count() > 0)
+        setCurrentIndex(hats[0]->row());
+}
+
+QString HatButton::currentHat() const
+{
+    return m_hat.data(Qt::DisplayRole).toString();
+}
+
+void HatButton::showPrompt()
+{
+    HatPrompt prompt(currentIndex(), this);
+    int hatID = prompt.exec() - 1; // Since 0 means canceled, so all indexes are +1'd
+    if (hatID < 0) return;
+
+    setCurrentIndex(hatID);
+    emit currentIndexChanged(hatID);
+    emit currentHatChanged(currentHat());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/hatbutton.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,55 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef HATBUTTON_H
+#define HATBUTTON_H
+
+#include <QPushButton>
+#include <QString>
+#include <QModelIndex>
+
+class HatModel;
+
+class HatButton : public QPushButton
+{
+        Q_OBJECT
+        Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex)
+        Q_PROPERTY(QString currentHat READ currentHat WRITE setCurrentHat)
+
+    public:
+        HatButton(QWidget* parent);
+        int currentIndex();
+        QString currentHat() const;
+
+    private:
+        QModelIndex m_hat;
+        HatModel * m_hatModel;
+
+    signals:
+        void currentIndexChanged(int);
+        void currentHatChanged(const QString &);
+
+    public slots:
+        void setCurrentIndex(int index);
+        void setCurrentHat(const QString & name);
+
+    private slots:
+        void showPrompt();
+};
+
+#endif // HATBUTTON_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/hatprompt.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,169 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QDialog>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QScrollArea>
+#include <QPushButton>
+#include <QToolButton>
+#include <QWidgetItem>
+#include <QModelIndex>
+#include <QListView>
+#include <QLineEdit>
+#include <QLabel>
+#include <QSortFilterProxyModel>
+#include <QDebug>
+
+#include "DataManager.h"
+#include "lineeditcursor.h"
+#include "HatModel.h"
+#include "hatprompt.h"
+
+HatPrompt::HatPrompt(int currentIndex, QWidget* parent) : QDialog(parent)
+{
+    setModal(true);
+    setWindowFlags(Qt::Sheet);
+    setWindowModality(Qt::WindowModal);
+    setMinimumSize(550, 430);
+    resize(550, 430);
+    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+    setStyleSheet("QPushButton { padding: 5px; margin-top: 10px; }");
+
+    // Hat model, and a model for setting a filter
+    HatModel * hatModel = DataManager::instance().hatModel();
+    filterModel = new QSortFilterProxyModel();
+    filterModel->setSourceModel(hatModel);
+    filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+    // Grid
+    QGridLayout * dialogLayout = new QGridLayout(this);
+    dialogLayout->setSpacing(0);
+    dialogLayout->setColumnStretch(1, 1);
+
+    QHBoxLayout * topLayout = new QHBoxLayout();
+
+    // Help/prompt message at top
+    QLabel * lblDesc = new QLabel(tr("Select a hat"));
+    lblDesc->setObjectName("lblDesc");
+    lblDesc->setStyleSheet("#lblDesc { color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-top-left-radius: 10px; padding: 4px 10px;}");
+    lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    lblDesc->setFixedHeight(24);
+    lblDesc->setMinimumWidth(0);
+
+    // Filter text box
+    QWidget * filterContainer = new QWidget();
+    filterContainer->setFixedHeight(24);
+    filterContainer->setObjectName("filterContainer");
+    filterContainer->setStyleSheet("#filterContainer { background: #F6CB1C; border-top-right-radius: 10px; padding: 3px; }");
+    filterContainer->setFixedWidth(250);
+    txtFilter = new LineEditCursor(filterContainer);
+    txtFilter->setFixedWidth(250);
+    txtFilter->setFocus();
+    txtFilter->setFixedHeight(22);
+    connect(txtFilter, SIGNAL(textChanged(const QString &)), this, SLOT(filterChanged(const QString &)));
+    connect(txtFilter, SIGNAL(moveUp()), this, SLOT(moveUp()));
+    connect(txtFilter, SIGNAL(moveDown()), this, SLOT(moveDown()));
+    connect(txtFilter, SIGNAL(moveLeft()), this, SLOT(moveLeft()));
+    connect(txtFilter, SIGNAL(moveRight()), this, SLOT(moveRight()));
+
+    // Filter label
+    QLabel * lblFilter = new QLabel(tr("Filter: "), txtFilter);
+    lblFilter->setStyleSheet(QString("background: none; margin-left: -%1px; margin-top: 4px;").arg(lblFilter->width() / 2 - 3));
+    txtFilter->setStyleSheet(QString("border-width: 0px; background-color: rgb(13, 5, 68); border-radius: 6px; margin-top: 3px; margin-right: 3px; padding-left: %1px; padding-bottom: 2px;").arg(lblFilter->width() / 2));
+
+    // Corner widget
+    QLabel * corner = new QLabel();
+    corner->setPixmap(QPixmap(QString::fromUtf8(":/res/inverse-corner-bl.png")));
+    corner->setFixedSize(10, 10);
+
+    // Add widgets to top layout
+    topLayout->addWidget(lblDesc);
+    topLayout->addWidget(filterContainer);
+    topLayout->addWidget(corner, 0, Qt::AlignBottom);
+    topLayout->addStretch(1);
+
+    // Cancel button (closes dialog)
+    QPushButton * btnCancel = new QPushButton(tr("Cancel"));
+    connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
+
+    // Select button
+    QPushButton * btnSelect = new QPushButton(tr("Use selected hat"));
+    btnSelect->setDefault(true);
+    connect(btnSelect, SIGNAL(clicked()), this, SLOT(onAccepted()));
+
+    // Add hats
+    list = new HatListView();
+    list->setModel(filterModel);
+    list->setViewMode(QListView::IconMode);
+    list->setResizeMode(QListView::Adjust);
+    list->setMovement(QListView::Static);
+    list->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    list->setSpacing(8);
+    list->setWordWrap(true);
+    list->setSelectionMode(QAbstractItemView::SingleSelection);
+    list->setObjectName("hatList");
+    list->setCurrentIndex(filterModel->index(currentIndex, 0));
+    connect(list, SIGNAL(activated(const QModelIndex &)), this, SLOT(hatChosen(const QModelIndex &)));
+    connect(list, SIGNAL(clicked(const QModelIndex &)), this, SLOT(hatChosen(const QModelIndex &)));
+
+    // Add elements to layouts
+    dialogLayout->addLayout(topLayout, 0, 0, 1, 3);
+    dialogLayout->addWidget(list, 1, 0, 1, 3);
+    dialogLayout->addWidget(btnCancel, 2, 0, 1, 1, Qt::AlignLeft);
+    dialogLayout->addWidget(btnSelect, 2, 2, 1, 1, Qt::AlignRight);
+}
+
+void HatPrompt::moveUp()
+{
+    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier));
+}
+
+void HatPrompt::moveDown()
+{
+    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier));
+}
+
+void HatPrompt::moveLeft()
+{
+    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveLeft, Qt::NoModifier));
+}
+
+void HatPrompt::moveRight()
+{
+    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveRight, Qt::NoModifier));
+}
+
+void HatPrompt::onAccepted()
+{
+    hatChosen(list->currentIndex());
+}
+
+// When a hat is selected
+void HatPrompt::hatChosen(const QModelIndex & index)
+{
+    done(filterModel->mapToSource(index).row() + 1); // Since returning 0 means canceled
+}
+
+// When the text in the filter text box is changed
+void HatPrompt::filterChanged(const QString & text)
+{
+    filterModel->setFilterFixedString(text);
+    list->setCurrentIndex(filterModel->index(0, 0));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/hatprompt.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,61 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef HATPROMPT_H
+#define HATPROMPT_H
+
+#include <QWidget>
+#include <QDialog>
+#include <QListView>
+
+class QLineEdit;
+class QModelIndex;
+class QSortFilterProxyModel;
+class LineEditCursor;
+
+class HatListView : public QListView
+{
+    friend class HatPrompt;
+
+    public:
+        HatListView(QWidget* parent = 0) : QListView(parent){}
+};
+
+class HatPrompt : public QDialog
+{
+        Q_OBJECT
+
+    public:
+        HatPrompt(int currentIndex = 0, QWidget* parent = 0);
+
+    private:
+        LineEditCursor * txtFilter;
+        HatListView * list;
+        QSortFilterProxyModel * filterModel;
+
+    private slots:
+        void onAccepted();
+        void hatChosen(const QModelIndex & index);
+        void filterChanged(const QString & text);
+        void moveUp();
+        void moveDown();
+        void moveLeft();
+        void moveRight();
+};
+
+#endif // HATPROMPT_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/keybinder.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,301 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "keybinder.h"
+#include "HWApplication.h"
+#include "DataManager.h"
+#include <QHBoxLayout>
+#include <QScrollArea>
+#include <QTableWidget>
+#include <QTableWidgetItem>
+#include <QStandardItemModel>
+#include <QAbstractItemModel>
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QPushButton>
+#include <QHeaderView>
+#include <QComboBox>
+#include <QLabel>
+#include <QFrame>
+#include <QDebug>
+
+KeyBinder::KeyBinder(QWidget * parent, const QString & helpText, const QString & defaultText, const QString & resetButtonText) : QWidget(parent)
+{
+    this->defaultText = defaultText;
+    enableSignal = false;
+
+    // Two-column tab layout
+    QHBoxLayout * pageKeysLayout = new QHBoxLayout(this);
+    pageKeysLayout->setSpacing(0);
+    pageKeysLayout->setContentsMargins(0, 0, 0, 0);
+
+    // Table for category list
+    QVBoxLayout * catListContainer = new QVBoxLayout();
+    catListContainer->setContentsMargins(10, 10, 10, 10);
+    catList = new QListWidget();
+    catList->setFixedWidth(180);
+    catList->setStyleSheet("QListWidget::item { font-size: 14px; } QListWidget:hover { border-color: #F6CB1C; } QListWidget::item:selected { background: #150A61; color: yellow; }");
+    catList->setFocusPolicy(Qt::NoFocus);
+    connect(catList, SIGNAL(currentRowChanged(int)), this, SLOT(changeBindingsPage(int)));
+    catListContainer->addWidget(catList);
+    pageKeysLayout->addLayout(catListContainer);
+
+    // Reset all binds button
+    if (!resetButtonText.isEmpty())
+    {
+        QPushButton * btnResetAll = new QPushButton(resetButtonText);
+        catListContainer->addWidget(btnResetAll);
+        btnResetAll->setFixedHeight(40);
+        catListContainer->setStretch(1, 0);
+        catListContainer->setSpacing(10);
+        connect(btnResetAll, SIGNAL(clicked()), this, SIGNAL(resetAllBinds()));
+    }
+
+    // Container for pages of key bindings
+    QWidget * bindingsPagesContainer = new QWidget();
+    QVBoxLayout * rightLayout = new QVBoxLayout(bindingsPagesContainer);
+
+    // Scroll area for key bindings
+    QScrollArea * scrollArea = new QScrollArea();
+    scrollArea->setContentsMargins(0, 0, 0, 0);
+    scrollArea->setWidget(bindingsPagesContainer);
+    scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+    scrollArea->setWidgetResizable(true);
+    scrollArea->setFrameShape(QFrame::NoFrame);
+    scrollArea->setStyleSheet("background: #130F2A;");
+
+    // Add key binding pages to bindings tab
+    pageKeysLayout->addWidget(scrollArea);
+    pageKeysLayout->setStretch(1, 1);
+
+    // Custom help text
+    QLabel * helpLabel = new QLabel();
+    helpLabel->setText(helpText);
+    helpLabel->setStyleSheet("color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-radius: 10px; padding: auto 20px;");
+    helpLabel->setFixedHeight(24);
+    rightLayout->addWidget(helpLabel, 0, Qt::AlignCenter);
+
+    // Category list and bind table row heights
+    const int rowHeight = 20;
+    QSize catSize, headerSize;
+    catSize.setHeight(36);
+    headerSize.setHeight(24);
+
+    // Category list header
+    QListWidgetItem * catListHeader = new QListWidgetItem(tr("Category"));
+    catListHeader->setSizeHint(headerSize);
+    catListHeader->setFlags(Qt::NoItemFlags);
+    catListHeader->setForeground(QBrush(QColor("#130F2A")));
+    catListHeader->setBackground(QBrush(QColor("#F6CB1C")));
+    catListHeader->setTextAlignment(Qt::AlignCenter);
+    catList->addItem(catListHeader);
+
+    // Populate
+    bindingsPages = new QHBoxLayout();
+    bindingsPages->setContentsMargins(0, 0, 0, 0);
+    rightLayout->addLayout(bindingsPages);
+    QWidget * curPage = NULL;
+    QVBoxLayout * curLayout = NULL;
+    QTableWidget * curTable = NULL;
+    bool bFirstPage = true;
+    selectedBindTable = NULL;
+    bindComboBoxCellMappings = new QHash<QObject *, QTableWidgetItem *>();
+    bindCellComboBoxMappings = new QHash<QTableWidgetItem *, QComboBox *>();
+    for (int i = 0; i < BINDS_NUMBER; i++)
+    {
+        if (cbinds[i].category != NULL)
+        {
+            // Add stretch at end of previous layout
+            if (curLayout != NULL) curLayout->insertStretch(-1, 1);
+
+            // Category list item
+            QListWidgetItem * catItem = new QListWidgetItem(HWApplication::translate("binds (categories)", cbinds[i].category));
+            catItem->setSizeHint(catSize);
+            catList->addItem(catItem);
+
+            // Create new page
+            curPage = new QWidget();
+            curLayout = new QVBoxLayout(curPage);
+            curLayout->setSpacing(2);
+            bindingsPages->addWidget(curPage);
+            if (!bFirstPage) curPage->setVisible(false);
+        }
+
+        // Description
+        if (cbinds[i].description != NULL)
+        {
+            QLabel * desc = new QLabel(HWApplication::translate("binds (descriptions)", cbinds[i].description));
+            curLayout->addWidget(desc, 0);
+            QFrame * divider = new QFrame();
+            divider->setFrameShape(QFrame::HLine);
+            divider->setFrameShadow(QFrame::Plain);
+            curLayout->addWidget(divider, 0);
+        }
+
+        // New table
+        if (cbinds[i].category != NULL || cbinds[i].description != NULL)
+        {
+            curTable = new QTableWidget(0, 2);
+            curTable->verticalHeader()->setVisible(false);
+            curTable->horizontalHeader()->setVisible(false);
+            curTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
+            curTable->verticalHeader()->setDefaultSectionSize(rowHeight);
+            curTable->setShowGrid(false);
+            curTable->setStyleSheet("QTableWidget { border: none; } ");
+            curTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+            curTable->setSelectionMode(QAbstractItemView::SingleSelection);
+            curTable->setFocusPolicy(Qt::NoFocus);
+            connect(curTable, SIGNAL(itemSelectionChanged()), this, SLOT(bindSelectionChanged()));
+            connect(curTable, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(bindCellClicked(QTableWidgetItem *)));
+            curLayout->addWidget(curTable, 0);
+        }
+
+        // Hidden combo box
+        QComboBox * comboBox = CBBind[i] = new QComboBox(curTable);
+        comboBox->setModel((QAbstractItemModel*)DataManager::instance().bindsModel());
+        comboBox->setVisible(false);
+        comboBox->setFixedWidth(200);
+
+        // Table row
+        int row = curTable->rowCount();
+        QTableWidgetItem * nameCell = new QTableWidgetItem(HWApplication::translate("binds", cbinds[i].name));
+        nameCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+        curTable->insertRow(row);
+        curTable->setItem(row, 0, nameCell);
+        QTableWidgetItem * bindCell = new QTableWidgetItem(comboBox->currentText());
+        bindCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+        curTable->setItem(row, 1, bindCell);
+        curTable->resizeColumnsToContents();
+        curTable->setFixedHeight(curTable->verticalHeader()->length() + 10);
+
+        // Updates the text in the table cell
+        connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(bindChanged(const QString &)));
+
+        // Map combo box and that row's cells to each other
+        bindComboBoxCellMappings->insert(comboBox, bindCell);
+        bindCellComboBoxMappings->insert(nameCell, comboBox);
+        bindCellComboBoxMappings->insert(bindCell, comboBox);
+    }
+
+    // Add stretch at end of last layout
+    if (curLayout != NULL) curLayout->insertStretch(-1, 1);
+
+    // Go to first page
+    catList->setCurrentItem(catList->item(1));
+
+    enableSignal = true;
+}
+
+KeyBinder::~KeyBinder()
+{
+    delete bindComboBoxCellMappings;
+    delete bindCellComboBoxMappings;
+}
+
+// Switches between different pages of key binds
+void KeyBinder::changeBindingsPage(int page)
+{
+    page--; // Disregard first item (the list header)
+    int pages = bindingsPages->count();
+    for (int i = 0; i < pages; i++)
+        bindingsPages->itemAt(i)->widget()->setVisible(false);
+    bindingsPages->itemAt(page)->widget()->setVisible(true);
+}
+
+// When a key bind combobox value is changed, updates the table cell text
+void KeyBinder::bindChanged(const QString & text)
+{
+    bindComboBoxCellMappings->value(sender())->setText(text);
+
+    if (enableSignal)
+    {
+        for (int i = 0; i < BINDS_NUMBER; i++)
+        {
+            if (CBBind[i] == sender())
+            {
+                emit bindUpdate(i);
+                break;
+            }
+        }
+    }
+}
+
+// When a row in a key bind table is clicked, this shows the popup
+void KeyBinder::bindCellClicked(QTableWidgetItem * item)
+{
+    QComboBox * box = bindCellComboBoxMappings->value(item);
+    QTableWidget * table = item->tableWidget();
+    QFrame * frame = box->findChild<QFrame*>();
+
+    box->showPopup();
+    frame->move(
+        frame->x() + table->horizontalHeader()->sectionSize(0),
+        frame->y() + (table->verticalHeader()->defaultSectionSize() * item->row())
+    );
+}
+
+// When a new row in a bind table is *selected*, this clears selection in any other table
+void KeyBinder::bindSelectionChanged()
+{
+    QTableWidget * theSender = (QTableWidget*)sender();
+    if (theSender != selectedBindTable)
+    {
+        if (selectedBindTable != NULL)
+            selectedBindTable->clearSelection();
+        selectedBindTable = theSender;
+    }
+}
+
+// Set a combobox's index
+void KeyBinder::setBindIndex(int keyIndex, int bindIndex)
+{
+    enableSignal = false;
+    CBBind[keyIndex]->setCurrentIndex(bindIndex);
+    enableSignal = true;
+}
+
+// Return a combobox's selected index
+int KeyBinder::bindIndex(int keyIndex)
+{
+    return CBBind[keyIndex]->currentIndex();
+}
+
+// Clears selection and goes to first category
+void KeyBinder::resetInterface()
+{
+    enableSignal = false;
+
+    catList->setCurrentItem(catList->item(1));
+    changeBindingsPage(1);
+    if (selectedBindTable != NULL)
+    {
+        selectedBindTable->clearSelection();
+        selectedBindTable = NULL;
+    }
+
+    // Default bind text
+    DataManager::instance().bindsModel()->item(0)->setData(defaultText, Qt::DisplayRole);
+    for (int i = 0; i < BINDS_NUMBER; i++)
+    {
+        CBBind[i]->setModel(DataManager::instance().bindsModel());
+        CBBind[i]->setCurrentIndex(0);
+        bindComboBoxCellMappings->value(CBBind[i])->setText(defaultText);
+    }
+
+    enableSignal = true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/keybinder.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,68 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef _KEY_BINDER_H
+#define _KEY_BINDER_H
+
+#include <QWidget>
+#include <QHash>
+
+#include "binds.h"
+
+class QListWidget;
+class QTableWidgetItem;
+class QTableWidget;
+class QBoxLayout;
+class QComboBox;
+
+// USAGE NOTE: Every time the widget comes into view, you must call resetInterface()
+
+class KeyBinder : public QWidget
+{
+    Q_OBJECT
+
+    public:
+        KeyBinder(QWidget * parent = NULL, const QString & helpText = QString(), const QString & defaultText = QString(), const QString & resetButtonText = QString());
+        ~KeyBinder();
+
+        void setBindIndex(int keyIndex, int bindIndex);
+        int bindIndex(int keyIndex);
+        void resetInterface();
+
+    private:
+        QHash<QObject *, QTableWidgetItem *> * bindComboBoxCellMappings;
+        QHash<QTableWidgetItem *, QComboBox *> * bindCellComboBoxMappings;
+        QTableWidget * selectedBindTable;
+        QListWidget * catList;
+        QBoxLayout *bindingsPages;
+        QComboBox * CBBind[BINDS_NUMBER];
+        QString defaultText;
+        bool enableSignal;
+
+    signals:
+        void bindUpdate(int bindID);
+        void resetAllBinds();
+
+    private slots:
+        void changeBindingsPage(int page);
+        void bindChanged(const QString &);
+        void bindCellClicked(QTableWidgetItem * item);
+        void bindSelectionChanged();
+};
+
+#endif // _KEY_BINDER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/lineeditcursor.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,35 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QKeyEvent>
+
+#include "lineeditcursor.h"
+
+void LineEditCursor::keyPressEvent(QKeyEvent * event)
+{
+    if (event->key() == Qt::Key_Up)
+        emit moveUp();
+    else if (event->key() == Qt::Key_Down)
+        emit moveDown();
+    else if (event->key() == Qt::Key_Left)
+        emit moveLeft();
+    else if (event->key() == Qt::Key_Right)
+        emit moveRight();
+    else
+        QLineEdit::keyPressEvent(event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/lineeditcursor.h	Sun Jan 27 00:28:57 2013 +0100
@@ -0,0 +1,41 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LINEEDITCURSOR_H
+#define LINEEDITCURSOR_H
+
+#include <QLineEdit>
+
+class LineEditCursor : public QLineEdit
+{
+        Q_OBJECT
+
+    public:
+        LineEditCursor(QWidget* parent = 0) : QLineEdit(parent) {}
+
+    signals:
+        void moveUp();
+        void moveDown();
+        void moveLeft();
+        void moveRight();
+
+    private:
+        void keyPressEvent(QKeyEvent * event);
+};
+
+#endif // LINEEDITCURSOR_H
--- a/QTfrontend/ui/widget/mapContainer.cpp	Wed Jan 02 11:11:49 2013 +0100
+++ b/QTfrontend/ui/widget/mapContainer.cpp	Sun Jan 27 00:28:57 2013 +0100
@@ -30,13 +30,24 @@
 #include <QIcon>
 #include <QLineEdit>
 #include <QStringListModel>
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QDebug>
+#include <QFile>
+#include <QFileDialog>
+#include <QInputDialog>
+#include <QMessageBox>
 
 #include "hwconsts.h"
 #include "mapContainer.h"
+#include "themeprompt.h"
+#include "seedprompt.h"
 #include "igbox.h"
 #include "HWApplication.h"
 #include "ThemeModel.h"
 
+
+
 HWMapContainer::HWMapContainer(QWidget * parent) :
     QWidget(parent),
     mainLayout(this),
@@ -47,145 +58,205 @@
     hhSmall.load(":/res/hh_small.png");
     hhLimit = 18;
     templateFilter = 0;
+    m_master = true;
 
     linearGrad = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
     linearGrad.setColorAt(1, QColor(0, 0, 192));
     linearGrad.setColorAt(0, QColor(66, 115, 225));
 
     mainLayout.setContentsMargins(HWApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
-                                  1,
+                                  10,
                                   HWApplication::style()->pixelMetric(QStyle::PM_LayoutRightMargin),
                                   HWApplication::style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
 
-    QWidget* mapWidget = new QWidget(this);
-    mainLayout.addWidget(mapWidget, 0, 0, Qt::AlignHCenter);
-
-    QGridLayout* mapLayout = new QGridLayout(mapWidget);
-    mapLayout->setMargin(0);
+    m_staticMapModel = DataManager::instance().staticMapModel();
+    m_missionMapModel = DataManager::instance().missionMapModel();
+    m_themeModel = DataManager::instance().themeModel();
 
-    imageButt = new QPushButton(mapWidget);
-    imageButt->setObjectName("imageButt");
-    imageButt->setFixedSize(256 + 6, 128 + 6);
-    imageButt->setFlat(true);
-    imageButt->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);//QSizePolicy::Minimum, QSizePolicy::Minimum);
-    mapLayout->addWidget(imageButt, 0, 0, 1, 2);
-    connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomMap()));
+    /* Layouts */
 
-    chooseMap = new QComboBox(mapWidget);
-    chooseMap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-    m_mapModel = DataManager::instance().mapModel();
-    chooseMap->setEditable(false);
-    chooseMap->setModel(m_mapModel);
+    QHBoxLayout * typeLayout = new QHBoxLayout();
+    QHBoxLayout * seedLayout = new QHBoxLayout();
+    QHBoxLayout * twoColumnLayout = new QHBoxLayout();
+    QVBoxLayout * leftLayout = new QVBoxLayout();
+    QVBoxLayout * rightLayout = new QVBoxLayout();
+    twoColumnLayout->addLayout(leftLayout, 0);
+    twoColumnLayout->addStretch(1);
+    twoColumnLayout->addLayout(rightLayout, 0);
+    QVBoxLayout * drawnControls = new QVBoxLayout();
+    leftLayout->addLayout(typeLayout, 0);
+    rightLayout->addLayout(seedLayout, 0);
+
+    /* Map type combobox */
 
-    mapLayout->addWidget(chooseMap, 1, 1);
+    typeLayout->setSpacing(10);
+    typeLayout->addWidget(new QLabel(tr("Map type:")), 0);
+    cType = new QComboBox(this);
+    typeLayout->addWidget(cType, 1);
+    cType->insertItem(0, tr("Image map"), MapModel::StaticMap);
+    cType->insertItem(1, tr("Mission map"), MapModel::MissionMap);
+    cType->insertItem(2, tr("Hand-drawn"), MapModel::HandDrawnMap);
+    cType->insertItem(3, tr("Randomly generated"), MapModel::GeneratedMap);
+    cType->insertItem(4, tr("Random maze"), MapModel::GeneratedMaze);
+    connect(cType, SIGNAL(currentIndexChanged(int)), this, SLOT(mapTypeChanged(int)));
+    m_childWidgets << cType;
 
-    QLabel * lblMap = new QLabel(tr("Map"), mapWidget);
-    mapLayout->addWidget(lblMap, 1, 0);
-
-    lblFilter = new QLabel(tr("Filter"), mapWidget);
-    mapLayout->addWidget(lblFilter, 2, 0);
+    /* Randomize button */
 
-    cbTemplateFilter = new QComboBox(mapWidget);
-    cbTemplateFilter->addItem(tr("All"), 0);
-    cbTemplateFilter->addItem(tr("Small"), 1);
-    cbTemplateFilter->addItem(tr("Medium"), 2);
-    cbTemplateFilter->addItem(tr("Large"), 3);
-    cbTemplateFilter->addItem(tr("Cavern"), 4);
-    cbTemplateFilter->addItem(tr("Wacky"), 5);
-    mapLayout->addWidget(cbTemplateFilter, 2, 1);
+    seedLayout->addStretch(1);
+    const QIcon& lp = QIcon(":/res/dice.png");
+    QSize sz = lp.actualSize(QSize(65535, 65535));
+    btnRandomize = new QPushButton();
+    btnRandomize->setText(tr("Random"));
+    btnRandomize->setIcon(lp);
+    btnRandomize->setFixedHeight(30);
+    btnRandomize->setIconSize(sz);
+    btnRandomize->setFlat(true);
+    btnRandomize->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    connect(btnRandomize, SIGNAL(clicked()), this, SLOT(setRandomMap()));
+    m_childWidgets << btnRandomize;
+    btnRandomize->setStyleSheet("padding: 5px;");
+    btnRandomize->setFixedHeight(cType->height());
+    seedLayout->addWidget(btnRandomize, 1);
 
-    connect(cbTemplateFilter, SIGNAL(activated(int)), this, SLOT(setTemplateFilter(int)));
+    /* Seed button */
+    btnSeed = new QPushButton(parentWidget()->parentWidget());
+    btnSeed->setText(tr("Seed"));
+    btnSeed->setStyleSheet("padding: 5px;");
+    btnSeed->setFixedHeight(cType->height());
+    connect(btnSeed, SIGNAL(clicked()), this, SLOT(showSeedPrompt()));
+    seedLayout->addWidget(btnSeed, 0);
 
-    maze_size_label = new QLabel(tr("Type"), mapWidget);
-    mapLayout->addWidget(maze_size_label, 2, 0);
-    maze_size_label->hide();
-    cbMazeSize = new QComboBox(mapWidget);
-    cbMazeSize->addItem(tr("Small tunnels"), 0);
-    cbMazeSize->addItem(tr("Medium tunnels"), 1);
-    cbMazeSize->addItem(tr("Large tunnels"), 2);
-    cbMazeSize->addItem(tr("Small floating islands"), 3);
-    cbMazeSize->addItem(tr("Medium floating islands"), 4);
-    cbMazeSize->addItem(tr("Large floating islands"), 5);
-    cbMazeSize->setCurrentIndex(1);
+    /* Map preview label */
+
+    QLabel * lblMapPreviewText = new QLabel(this);
+    lblMapPreviewText->setText(tr("Map preview:"));
+    leftLayout->addWidget(lblMapPreviewText, 0);
+    leftLayout->addSpacing(2);
+
+    /* Map Preview */
 
-    mapLayout->addWidget(cbMazeSize, 2, 1);
-    cbMazeSize->hide();
-    connect(cbMazeSize, SIGNAL(activated(int)), this, SLOT(setMazeSize(int)));
+    mapPreview = new QLabel(this);
+    mapPreview->setObjectName("mapPreview");
+    mapPreview->setFixedSize(256, 128);
+    leftLayout->addWidget(mapPreview, 0);
+
+    /* Bottom-Left layout */
 
-    gbThemes = new IconedGroupBox(mapWidget);
-    gbThemes->setTitleTextPadding(80);
-    gbThemes->setContentTopPadding(15);
-    gbThemes->setTitle(tr("Themes"));
+    QVBoxLayout * bottomLeftLayout = new QVBoxLayout();
+    leftLayout->addLayout(bottomLeftLayout, 1);
+
+    /* Map list label */
+
+    lblMapList = new QLabel();
+    rightLayout->addWidget(lblMapList, 0);
+
+    /* Static maps list */
 
-    //gbThemes->setStyleSheet("padding: 0px"); // doesn't work - stylesheet is set with icon
-    mapLayout->addWidget(gbThemes, 0, 2, 3, 1);
-    // disallow row to be collapsed (so it can't get ignored when Qt applies rowSpan of gbThemes)
-    mapLayout->setRowMinimumHeight(2, 13);
-    QVBoxLayout * gbTLayout = new QVBoxLayout(gbThemes);
-    gbTLayout->setContentsMargins(0, 0, 0 ,0);
-    gbTLayout->setSpacing(0);
-    lvThemes = new QListView(mapWidget);
-    lvThemes->setMinimumHeight(30);
-    lvThemes->setFixedWidth(140);
-    m_themeModel = DataManager::instance().themeModel();
-    lvThemes->setModel(m_themeModel);
-    lvThemes->setIconSize(QSize(16, 16));
-    lvThemes->setEditTriggers(QListView::NoEditTriggers);
+    staticMapList = new QListView;
+    staticMapList->setModel(m_staticMapModel);
+    rightLayout->addWidget(staticMapList, 1);
+    staticMapList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    m_childWidgets << staticMapList;
+    QItemSelectionModel * staticSelectionModel = staticMapList->selectionModel();
+    connect(staticSelectionModel,
+            SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)),
+            this,
+            SLOT(staticMapChanged(const QModelIndex &, const QModelIndex &)));
 
-    connect(lvThemes->selectionModel(), SIGNAL(currentRowChanged( const QModelIndex &, const QModelIndex &)), this, SLOT(themeSelected( const QModelIndex &, const QModelIndex &)));
+    /* Mission maps list */
 
-    // override default style to tighten up theme scroller
-    lvThemes->setStyleSheet(QString(
-                                "QListView{"
-                                "border: solid;"
-                                "border-width: 0px;"
-                                "border-radius: 0px;"
-                                "border-color: transparent;"
-                                "background-color: #0d0544;"
-                                "color: #ffcc00;"
-                                "font: bold 13px;"
-                                "}"
-                            )
-                           );
+    missionMapList = new QListView;
+    missionMapList->setModel(m_missionMapModel);
+    missionMapList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    rightLayout->addWidget(missionMapList, 1);
+    m_childWidgets << missionMapList;
+    QItemSelectionModel * missionSelectionModel = missionMapList->selectionModel();
+    connect(missionSelectionModel,
+            SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)),
+            this,
+            SLOT(missionMapChanged(const QModelIndex &, const QModelIndex &)));
+
+    /* Map load and edit buttons */
+
+    drawnControls->addStretch(1);
 
-    gbTLayout->addWidget(lvThemes);
-    lvThemes->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
+    btnLoadMap = new QPushButton(tr("Load map drawing"));
+    btnLoadMap->setStyleSheet("padding: 20px;");
+    drawnControls->addWidget(btnLoadMap, 0);
+    m_childWidgets << btnLoadMap;
+    connect(btnLoadMap, SIGNAL(clicked()), this, SLOT(loadDrawing()));
+
+    btnEditMap = new QPushButton(tr("Edit map drawing"));
+    btnEditMap->setStyleSheet("padding: 20px;");
+    drawnControls->addWidget(btnEditMap, 0);
+    m_childWidgets << btnEditMap;
+    connect(btnEditMap, SIGNAL(clicked()), this, SIGNAL(drawMapRequested()));
+
+    drawnControls->addStretch(1);
+
+    rightLayout->addLayout(drawnControls);
+
+    /* Generator style list */
 
-    mapLayout->setSizeConstraint(QLayout::SetFixedSize);
+    generationStyles = new QListWidget();
+    new QListWidgetItem(tr("All"), generationStyles);
+    new QListWidgetItem(tr("Small"), generationStyles);
+    new QListWidgetItem(tr("Medium"), generationStyles);
+    new QListWidgetItem(tr("Large"), generationStyles);
+    new QListWidgetItem(tr("Cavern"), generationStyles);
+    new QListWidgetItem(tr("Wacky"), generationStyles);
+    connect(generationStyles, SIGNAL(currentRowChanged(int)), this, SLOT(setTemplateFilter(int)));
+    m_childWidgets << generationStyles;
+    rightLayout->addWidget(generationStyles, 1);
+
+    /* Maze style list */
 
-    QWidget* seedWidget = new QWidget(this);
-    mainLayout.addWidget(seedWidget, 1, 0);
+    mazeStyles = new QListWidget();
+    new QListWidgetItem(tr("Small tunnels"), mazeStyles);
+    new QListWidgetItem(tr("Medium tunnels"), mazeStyles);
+    new QListWidgetItem(tr("Largetunnels"), mazeStyles);
+    new QListWidgetItem(tr("Small islands"), mazeStyles);
+    new QListWidgetItem(tr("Medium islands"), mazeStyles);
+    new QListWidgetItem(tr("Large islands"), mazeStyles);
+    connect(mazeStyles, SIGNAL(currentRowChanged(int)), this, SLOT(setMazeSize(int)));
+    m_childWidgets << mazeStyles;
+    rightLayout->addWidget(mazeStyles, 1);
 
-    QGridLayout* seedLayout = new QGridLayout(seedWidget);
-    seedLayout->setMargin(0);
+    /* Mission description */
 
-    seedLabel = new QLabel(tr("Seed"), seedWidget);
-    seedLayout->addWidget(seedLabel, 3, 0);
-    seedEdit = new QLineEdit(seedWidget);
-    seedEdit->setMaxLength(54);
-    connect(seedEdit, SIGNAL(returnPressed()), this, SLOT(seedEdited()));
-    seedLayout->addWidget(seedEdit, 3, 1);
-    seedLayout->setColumnStretch(1, 5);
-    seedSet = new QPushButton(seedWidget);
-    seedSet->setText(QPushButton::tr("more"));
-    connect(seedSet, SIGNAL(clicked()), this, SLOT(seedEdited()));
-    seedLayout->setColumnStretch(2, 1);
-    seedLayout->addWidget(seedSet, 3, 2);
+    lblDesc = new QLabel();
+    lblDesc->setWordWrap(true);
+    lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    lblDesc->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+    lblDesc->setStyleSheet("font: 10px;");
+    bottomLeftLayout->addWidget(lblDesc, 1);
+
+    /* Add stretch above theme button */
+
+    bottomLeftLayout->addStretch(1);
+
+    /* Theme chooser */
 
-    seedLabel->setVisible(false);
-    seedEdit->setVisible(false);
+    btnTheme = new QPushButton();
+    connect(btnTheme, SIGNAL(clicked()), this, SLOT(showThemePrompt()));
+    m_childWidgets << btnTheme;
+    bottomLeftLayout->addWidget(btnTheme, 0);
 
-    setRandomSeed();
-    setRandomTheme();
+    /* Add everything to main layout */
+
+    mainLayout.addLayout(twoColumnLayout, 0);
 
-    chooseMap->setCurrentIndex(0);
-    mapChanged(0);
-    // use signal "activated" rather than currentIndexChanged
-    // because index is somtimes changed a few times in a row programmatically
-    connect(chooseMap, SIGNAL(activated(int)), this, SLOT(mapChanged(int)));
+    /* Set defaults */
 
-    // update model views after model changes (to e.g. re-adjust separators)
-    connect(&DataManager::instance(), SIGNAL(updated()), this, SLOT(updateModelViews()));
+    setRandomTheme();
+    setRandomSeed();
+    setMazeSize(0);
+    setTemplateFilter(0);
+    staticMapChanged(m_staticMapModel->index(0, 0));
+    missionMapChanged(m_missionMapModel->index(0, 0));
+    updateTheme(m_themeModel->index(0, 0));
+    changeMapType(MapModel::GeneratedMap);
 }
 
 void HWMapContainer::setImage(const QImage newImage)
@@ -202,8 +273,9 @@
     p.drawPixmap(QPoint(0, 0), px);
 
     addInfoToPreview(pxres);
-    //chooseMap->setCurrentIndex(mapgen);
     pMap = 0;
+
+    cType->setEnabled(isMaster());
 }
 
 void HWMapContainer::setHHLimit(int newHHLimit)
@@ -211,70 +283,6 @@
     hhLimit = newHHLimit;
 }
 
-void HWMapContainer::mapChanged(int index)
-{
-    if (chooseMap->currentIndex() != index)
-        chooseMap->setCurrentIndex(index);
-
-    if (index < 0)
-    {
-        m_mapInfo.type = MapModel::Invalid;
-        updatePreview();
-        return;
-    }
-
-    Q_ASSERT(chooseMap->itemData(index, Qt::UserRole + 1).canConvert<MapModel::MapInfo>());
-    m_mapInfo = chooseMap->itemData(index, Qt::UserRole + 1).value<MapModel::MapInfo>();
-    m_curMap = m_mapInfo.name;
-
-    switch(m_mapInfo.type)
-    {
-        case MapModel::GeneratedMap:
-            mapgen = MAPGEN_REGULAR;
-            gbThemes->show();
-            lblFilter->show();
-            cbTemplateFilter->show();
-            maze_size_label->hide();
-            cbMazeSize->hide();
-            break;
-        case MapModel::GeneratedMaze:
-            mapgen = MAPGEN_MAZE;
-            gbThemes->show();
-            lblFilter->hide();
-            cbTemplateFilter->hide();
-            maze_size_label->show();
-            cbMazeSize->show();
-            break;
-        case MapModel::HandDrawnMap:
-            mapgen = MAPGEN_DRAWN;
-            gbThemes->show();
-            lblFilter->hide();
-            cbTemplateFilter->hide();
-            maze_size_label->hide();
-            cbMazeSize->hide();
-            break;
-        default:
-            mapgen = MAPGEN_MAP;
-            gbThemes->hide();
-            lblFilter->hide();
-            cbTemplateFilter->hide();
-            maze_size_label->hide();
-            cbMazeSize->hide();
-            m_theme = m_mapInfo.theme;
-    }
-
-    // the map has no pre-defined theme, so let's use the selected one
-    if (m_mapInfo.theme.isEmpty())
-    {
-        m_theme = lvThemes->currentIndex().data().toString();
-        emit themeChanged(m_theme);
-    }
-
-    updatePreview();
-    emit mapChanged(m_curMap);
-    emit mapgenChanged(mapgen);
-}
-
 // Should this add text to identify map size?
 void HWMapContainer::addInfoToPreview(QPixmap image)
 {
@@ -292,8 +300,7 @@
     p.drawText(image.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text);
     p.drawPixmap(image.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall);
 
-    imageButt->setIcon(finalImage);
-    imageButt->setIconSize(image.size());
+    mapPreview->setPixmap(finalImage);
 }
 
 void HWMapContainer::askForGeneratedPreview()
@@ -322,14 +329,8 @@
     p.drawPixmap(QPoint(x, y), waitIcon);
 
     addInfoToPreview(waitImage);
-}
 
-void HWMapContainer::themeSelected(const QModelIndex & current, const QModelIndex &)
-{
-    m_theme = current.data().toString();
-
-    gbThemes->setIcon(qVariantValue<QIcon>(current.data(Qt::UserRole)));
-    emit themeChanged(m_theme);
+    cType->setEnabled(false);
 }
 
 QString HWMapContainer::getCurrentSeed() const
@@ -339,8 +340,14 @@
 
 QString HWMapContainer::getCurrentMap() const
 {
-    if(chooseMap->currentIndex() < MAPGEN_MAP) return QString();
-    return(m_curMap);
+    switch (m_mapInfo.type)
+    {
+        case MapModel::StaticMap:
+        case MapModel::MissionMap:
+            return m_curMap;
+        default:
+            return QString();
+    }
 }
 
 QString HWMapContainer::getCurrentTheme() const
@@ -370,20 +377,17 @@
 
 quint32 HWMapContainer::getTemplateFilter() const
 {
-    return cbTemplateFilter->itemData(cbTemplateFilter->currentIndex()).toInt();
+    return generationStyles->currentRow();
 }
 
 void HWMapContainer::resizeEvent ( QResizeEvent * event )
 {
     Q_UNUSED(event);
-    //imageButt->setIconSize(imageButt->size());
 }
 
 void HWMapContainer::intSetSeed(const QString & seed)
 {
     m_seed = seed;
-    if (seed != seedEdit->text())
-        seedEdit->setText(seed);
 }
 
 void HWMapContainer::setSeed(const QString & seed)
@@ -395,11 +399,29 @@
 
 void HWMapContainer::intSetMap(const QString & map)
 {
-    m_curMap = map;
-
-    int id = m_mapModel->indexOf(map);
-
-    mapChanged(id);
+    if (map == "+rnd+")
+    {
+        changeMapType(MapModel::GeneratedMap);
+    }
+    else if (map == "+maze+")
+    {
+        changeMapType(MapModel::GeneratedMaze);
+    }
+    else if (map == "+drawn+")
+    {
+        changeMapType(MapModel::HandDrawnMap);
+    }
+    else if (m_staticMapModel->mapExists(map))
+    {
+        changeMapType(MapModel::StaticMap, m_staticMapModel->index(m_staticMapModel->findMap(map), 0));
+    }
+    else if (m_missionMapModel->mapExists(map))
+    {
+        changeMapType(MapModel::MissionMap, m_missionMapModel->index(m_missionMapModel->findMap(map), 0));
+    } else
+    {
+        qDebug() << "HWMapContainer::intSetMap: Map doesn't exist: " << map;
+    }
 }
 
 void HWMapContainer::setMap(const QString & map)
@@ -413,13 +435,13 @@
     QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), Qt::DisplayRole, theme);
 
     if(mdl.size())
-        lvThemes->setCurrentIndex(mdl.at(0));
+        updateTheme(mdl.at(0));
+    else
+        intSetIconlessTheme(theme);
 }
 
 void HWMapContainer::setRandomMap()
 {
-    int idx;
-
     setRandomSeed();
     switch(m_mapInfo.type)
     {
@@ -427,21 +449,17 @@
         case MapModel::GeneratedMaze:
             setRandomTheme();
             break;
-        case MapModel::HandDrawnMap:
-            emit drawMapRequested();
+        case MapModel::MissionMap:
+            missionMapChanged(m_missionMapModel->index(rand() % m_missionMapModel->rowCount(), 0));
             break;
-        case MapModel::MissionMap:
         case MapModel::StaticMap:
-            // get random map of same type
-            idx = m_mapModel->randomMap(m_mapInfo.type);
-            mapChanged(idx);
+            staticMapChanged(m_staticMapModel->index(rand() % m_staticMapModel->rowCount(), 0));
             break;
-        case MapModel::Invalid:
-            mapChanged(0);
+        default:
+            break;
     }
 }
 
-
 void HWMapContainer::setRandomSeed()
 {
     setSeed(QUuid::createUuid().toString());
@@ -452,12 +470,12 @@
 {
     if(!m_themeModel->rowCount()) return;
     quint32 themeNum = rand() % m_themeModel->rowCount();
-    lvThemes->setCurrentIndex(m_themeModel->index(themeNum));
+    updateTheme(m_themeModel->index(themeNum));
 }
 
 void HWMapContainer::intSetTemplateFilter(int filter)
 {
-    cbTemplateFilter->setCurrentIndex(filter);
+    generationStyles->setCurrentRow(filter);
     emit newTemplateFilter(filter);
 }
 
@@ -475,12 +493,12 @@
 
 int HWMapContainer::getMazeSize(void) const
 {
-    return cbMazeSize->currentIndex();
+    return mazeStyles->currentRow();
 }
 
 void HWMapContainer::intSetMazeSize(int size)
 {
-    cbMazeSize->setCurrentIndex(size);
+    mazeStyles->setCurrentRow(size);
     emit mazeSizeChanged(size);
 }
 
@@ -521,9 +539,6 @@
                 break;
         }
 
-        if(m != MAPGEN_MAP)
-            chooseMap->setCurrentIndex(m);
-
         emit mapgenChanged(m);
     }
 }
@@ -546,23 +561,10 @@
     return drawMapScene.encode();
 }
 
-void HWMapContainer::seedEdited()
+void HWMapContainer::setNewSeed(const QString & newSeed)
 {
-    if (seedLabel->isVisible() == false )
-    {
-        seedLabel->setVisible(true);
-        seedEdit->setVisible(true);
-        seedSet->setText(tr("Set"));
-        return;
-    }
-
-    if (seedEdit->text().isEmpty())
-        seedEdit->setText(m_seed);
-    else
-    {
-        setSeed(seedEdit->text());
-        emit seedChanged(seedEdit->text());
-    }
+    setSeed(newSeed);
+    emit seedChanged(newSeed);
 }
 
 DrawMapScene * HWMapContainer::getDrawMapScene()
@@ -592,8 +594,7 @@
     {
         case MapModel::Invalid:
             failIcon = QPixmap(":/res/btnDisabled.png");
-            imageButt->setIcon(failIcon);
-            imageButt->setIconSize(failIcon.size());
+            mapPreview->setPixmap(failIcon);
             break;
         case MapModel::GeneratedMap:
             askForGeneratedPreview();
@@ -610,7 +611,7 @@
 
             if(!success)
             {
-                imageButt->setIcon(QIcon());
+                mapPreview->setPixmap(QPixmap());
                 return;
             }
 
@@ -629,7 +630,6 @@
     intSetMap(map);
 }
 
-
 void HWMapContainer::updateModelViews()
 {
     // restore theme selection
@@ -638,13 +638,13 @@
     {
         QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), Qt::DisplayRole, m_theme);
         if (mdl.size() > 0)
-            lvThemes->setCurrentIndex(mdl.at(0));
+            updateTheme(mdl.at(0));
         else
             setRandomTheme();
     }
 
     // restore map selection
-    if ((!m_curMap.isEmpty()) && (chooseMap->currentIndex() < 0))
+    if (!m_curMap.isEmpty())
         intSetMap(m_curMap);
     else
         updatePreview();
@@ -656,3 +656,249 @@
     if (map == pMap)
         pMap = 0;
 }
+
+void HWMapContainer::mapTypeChanged(int index)
+{
+    changeMapType((MapModel::MapType)cType->itemData(index).toInt());
+}
+
+void HWMapContainer::changeMapType(MapModel::MapType type, const QModelIndex & newMap)
+{
+    staticMapList->hide();
+    missionMapList->hide();
+    lblMapList->hide();
+    generationStyles->hide();
+    mazeStyles->hide();
+    lblDesc->hide();
+    btnLoadMap->hide();
+    btnEditMap->hide();
+
+    switch (type)
+    {
+        case MapModel::GeneratedMap:
+            mapgen = MAPGEN_REGULAR;
+            setMapInfo(MapModel::MapInfoRandom);
+            lblMapList->setText(tr("Map size:"));
+            lblMapList->show();
+            generationStyles->show();
+            break;
+        case MapModel::GeneratedMaze:
+            mapgen = MAPGEN_MAZE;
+            setMapInfo(MapModel::MapInfoMaze);
+            lblMapList->setText(tr("Maze style:"));
+            lblMapList->show();
+            mazeStyles->show();
+            break;