wxlauncher-0.9.4/.hg_archival.txt0000644000000000000000000000023412155177255016741 0ustar rootroot00000000000000repo: 8e1ccc294a6d654462b92d6aefbdf050366d871b node: 73cd35fb8961918dcb68a4f2a0172c55c2694a80 branch: default latesttag: release-0.9.4 latesttagdistance: 1 wxlauncher-0.9.4/.hgignore0000644000000000000000000000015212155177255015455 0ustar rootroot00000000000000build personal glob:code/version.cpp glob:generated glob:output glob:*.pyc glob:*~ glob:*.orig glob:*.rej wxlauncher-0.9.4/.hgtags0000644000000000000000000000105212155177255015130 0ustar rootroot000000000000007184d2e62810c6c0675086c51d6f279ac4a0ad62 release-0.7.0 ba39a8c9b4dc0d9567591b4d0e7b74c219c114d9 release-0.7.1 0790073edd8804999c6ed76a6db4a4bf5324bd2d release-0.7.2 7bbf6b02d7756a4c81cb99561fdc86069a35d4dc release-0.7.3 4b2c81797599abe172df1f2722e3f1c23084a494 release-0.7.4 c40b0cdca13d3499529294bf2739e96f6fc55a6a release-0.8.0 b1abc59900ecabe795769a7c1502f5616127bdc4 release-0.8.0-OSX 327b236623d3ae1abda2dc51485c366e5954fee7 release-0.9.0 b8d4ff741d9db5e694a3ef1bd67d055a6dac0a7e release-0.9.1 67777df3c3e6832507a1c3d5b16191d7129973bf release-0.9.4 wxlauncher-0.9.4/Authors.txt0000644000000000000000000000074312155177255016046 0ustar rootroot00000000000000Iss Mneur kkmic Councilor We would also like to thank: wxWidgets Team - wxWidgets Source Code Project (SCP) - For the wonderful fs2_open Hard-Light Community - For ideas and support Volition, Inc. - Developer of Freespace 2 Interplay - Publisher of Freespace 2 wxlauncher-0.9.4/CMakeLists.txt0000644000000000000000000003113512155177255016417 0ustar rootroot00000000000000# let's go with lower case for commands (or at least be consistent) # arbitrarily following http://techbase.kde.org/Policies/CMake_Coding_Style#Upper.2Flower_casing cmake_minimum_required(VERSION 2.8) # We don't use C but CMake on linux complains if we don't have it enabled project(wxlauncher CXX C) set(VERSION_MAJOR 0) set(VERSION_MINOR 9) set(VERSION_PATCH 4) list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/include") set(CMAKE_OSX_ARCHITECTURES i386) if(NOT(DEFINED IS_WIN32 OR DEFINED IS_LINUX OR DEFINED IS_APPLE)) if(WIN32) set(IS_WIN32 TRUE) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(IS_APPLE TRUE) else() set(IS_LINUX TRUE) endif() endif() find_package(wxWidgets 2.8.10 REQUIRED base core net xml html adv qa richtext) if(NOT PYTHON_EXECUTABLE) # Only try to find python iff not already given include(FindPythonInterp) #PYTHON_EXECUTABLE endif() find_program(HG_EXECUTABLE hg) option(onlinehelpmaker_debug "Have the onlinehelpmaker.py script output debug information" OFF) if(onlinehelpmaker_debug) set(HELPMAKER_DEBUG "-d") set(HELPMAKER_QUIET "") else(onlinehelpmaker_debug) set(HELPMAKER_DEBUG "") option(onlinehelpmaker_quiet "Have onlinehelpmaker.py produce even less output than normal" ON) if(onlinehelpmaker_quiet) set(HELPMAKER_QUIET "-q") else() set(HELPMAKER_QUIET "") endif() endif(onlinehelpmaker_debug) # This needs to be set so that VS2008 will link against the most recent # version of the CRT, the one that is being distrubuted in the installer if(IS_WIN32) add_definitions("/D_BIND_TO_CURRENT_CRT_VERSION=1") endif(IS_WIN32) set(helphtblocation ${CMAKE_CURRENT_BINARY_DIR}/generated/onlinehelp.htb) option(DEVELOPMENT_MODE "Doing development (cannot build installer in this mode)" OFF) if(DEVELOPMENT_MODE) set(HELP_HTB_LOCATION ${helphtblocation}) set(RESOURCES_PATH ${PROJECT_SOURCE_DIR}/resources) else(DEVELOPMENT_MODE) if (NOT DEFINED RESOURCES_PATH) if(IS_WIN32) set(RESOURCES_PATH resources) elseif(IS_APPLE) set(RESOURCES_PATH wxlauncher.app/Contents/Resources) else() set(RESOURCES_PATH /usr/local/share/wxlauncher/) endif() endif() set(HELP_HTB_LOCATION ${RESOURCES_PATH}/onlinehelp.htb) endif(DEVELOPMENT_MODE) option(PROFILE_DEBUGGING "Extra verbose debug logs that include snapshots of profile contents at important steps while auto-save is off" OFF) option(USE_SPEECH "Build launcher with speech support?" OFF) option(USE_JOYSTICK "Build launcher with joystick support?" ON) option(USE_OPENAL "Build launcher with OpenAL support?" ON) if(USE_OPENAL) find_package(OpenAL REQUIRED) include_directories(${OPENAL_INCLUDE_DIR}) endif(USE_OPENAL) if(UNIX) find_package(SDL REQUIRED) include_directories(${SDL_INCLUDE_DIR}) set(HAS_SDL true) endif() if(UNIX AND NOT DEFINED PLATFORM_HAS_BROKEN_OPENAL) set(PLATFORM_HAS_BROKEN_OPENAL TRUE) endif() if(IS_APPLE) option(USING_SDL_FRAMEWORK "Check this if building with an SDL framework." ON) endif() # Make PLATFORM_USES_REGISTRY true only on windows, so that wxLauncher # will not try to compile the registry code on non Win32 systems. # Allow the user to define this as false even on windows if they want if((IS_WIN32) AND (NOT DEFINED PLATFORM_USES_REGISTRY)) set(PLATFORM_USES_REGISTRY TRUE) endif() include(${wxWidgets_USE_FILE}) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/code) if(IS_WIN32) # helper is only needed on windows add_executable(registry_helper WIN32 code/global/ids.h code/global/BasicDefaults.h code/global/BasicDefaults.cpp code/global/ModDefaults.h code/global/ModDefaults.cpp code/global/ModIniKeys.h code/global/ModIniKeys.cpp code/global/ProfileKeys.h code/global/ProfileKeys.cpp code/global/RegistryKeys.h code/global/RegistryKeys.cpp code/global/SkinDefaults.h code/global/SkinDefaults.cpp code/global/Utils.h code/global/Utils.cpp code/apis/RegistryProfileManager.cpp platform/win32/registry_helper.cpp code/apis/PlatformProfileManagerShared.cpp code/apis/CmdLineManager.h code/apis/CmdLineManager.cpp code/apis/FlagListManager.h code/apis/FlagListManager.cpp code/apis/ProfileManager.h code/apis/ProfileManager.cpp code/apis/ProfileProxy.h code/apis/ProfileProxy.cpp code/apis/SkinManager.h code/apis/SkinManager.cpp code/apis/TCManager.h code/apis/TCManager.cpp code/controls/LightingPresets.h code/controls/LightingPresets.cpp code/controls/ModList.h code/controls/ModList.cpp code/datastructures/FlagFileData.h code/datastructures/FlagFileData.cpp code/datastructures/FSOExecutable.h code/datastructures/FSOExecutable.cpp code/datastructures/NewsSource.h code/datastructures/NewsSource.cpp ) set_target_properties(registry_helper PROPERTIES LINKER_LANGUAGE CXX LINK_FLAGS /MANIFESTUAC:NO COMPILE_DEFINITIONS REGISTRY_HELPER ) target_link_libraries(registry_helper ${wxWidgets_LIBRARIES}) endif() # Check to see if we have a HG executable so that we will be able to # generate the version.cpp. If we don't have a hg binary, notify and # create the target to copy a template version. if(HG_EXECUTABLE) add_custom_target(version.cpp.maker ALL COMMAND ${PYTHON_EXECUTABLE} scripts/version.cpp.maker.py build ${CMAKE_CURRENT_BINARY_DIR}/generated/version_strings.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.cpp.maker.temp --hgpath=${HG_EXECUTABLE} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) else() add_custom_target(version.cpp.maker ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/code/global/version_strings.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version_strings.cpp ) message( "Cannot find a commandline Mercurial client (hg). version_strings.cpp will be generated by copying a dummy file.") endif() set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/generated/version_strings.cpp) add_custom_target(helpmaker ALL COMMAND ${PYTHON_EXECUTABLE} scripts/onlinehelpmaker.py build ${helphtblocation} ${CMAKE_SOURCE_DIR}/onlinehelp ${HELPMAKER_QUIET} ${HELPMAKER_DEBUG} -t ${CMAKE_CURRENT_BINARY_DIR}/onlinehelpmaker -c ${CMAKE_CURRENT_BINARY_DIR}/generated/helplinks.cpp WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/generated/helplinks.cpp) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${helphtblocation}) file(GLOB_RECURSE helpmaker_temp_files ${CMAKE_CURRENT_BINARY_DIR}/onlinehelpmaker/*.*) set_source_files_properties(${helphtblocation} ${CMAKE_CURRENT_BINARY_DIR}/generated/helplinks.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/version_strings.cpp PROPERTIES GENERATED true EXTERNAL_OBJECT true) set(TAB_CODE_FILES code/tabs/AdvSettingsPage.h code/tabs/AdvSettingsPage.cpp code/tabs/BasicSettingsPage.h code/tabs/BasicSettingsPage.cpp code/tabs/InstallPage.h code/tabs/InstallPage.cpp code/tabs/ModsPage.h code/tabs/ModsPage.cpp code/tabs/WelcomePage.h code/tabs/WelcomePage.cpp ) source_group(Tabs FILES ${TAB_CODE_FILES}) set(GUI_CONTROL_CODE_FILES code/controls/BottomButtons.h code/controls/BottomButtons.cpp code/controls/FlagListBox.h code/controls/FlagListBox.cpp code/controls/LightingPresets.h code/controls/LightingPresets.cpp code/controls/Logger.h code/controls/Logger.cpp code/controls/ModList.h code/controls/ModList.cpp code/controls/StatusBar.h code/controls/StatusBar.cpp code/controls/TruncatableChoice.h code/controls/TruncatableChoice.cpp ) source_group("GUI Controls" FILES ${GUI_CONTROL_CODE_FILES}) set(GLOBAL_CODE_FILES code/global/configure_launcher.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/configure_launcher.h code/global/BasicDefaults.h code/global/BasicDefaults.cpp code/global/ids.h code/global/MemoryDebugging.h code/global/ModDefaults.h code/global/ModDefaults.cpp code/global/ModIniKeys.h code/global/ModIniKeys.cpp code/global/ProfileKeys.h code/global/ProfileKeys.cpp code/global/RegistryKeys.h code/global/RegistryKeys.cpp code/global/SkinDefaults.h code/global/SkinDefaults.cpp code/global/targetver.h code/global/Utils.h code/global/Utils.cpp code/global/version.h code/global/version.cpp ) source_group(Global FILES ${GLOBAL_CODE_FILES}) set(DATASTRUCTURE_CODE_FILES code/datastructures/FlagInfo.cpp code/datastructures/FlagFileData.h code/datastructures/FlagFileData.cpp code/datastructures/FSOExecutable.h code/datastructures/FSOExecutable.cpp code/datastructures/NewsSource.h code/datastructures/NewsSource.cpp code/datastructures/ResolutionMap.h code/datastructures/ResolutionMap.cpp ) source_group("Data Structures" FILES ${DATASTRUCTURE_CODE_FILES}) set(API_CODE_FILES code/apis/CmdLineManager.h code/apis/CmdLineManager.cpp code/apis/FlagListManager.h code/apis/FlagListManager.cpp code/apis/FREDManager.h code/apis/FREDManager.cpp code/apis/HelpManager.h code/apis/HelpManager.cpp code/apis/JoystickManager.h code/apis/JoystickManager.cpp code/apis/OpenALManager.h code/apis/OpenALManager.cpp code/apis/ProfileManager.h code/apis/ProfileManagerOperator.h code/apis/ProfileManager.cpp code/apis/ProfileManagerOperator.cpp code/apis/ProfileProxy.h code/apis/ProfileProxy.cpp code/apis/SkinManager.h code/apis/SkinManager.cpp code/apis/SpeechManager.h code/apis/SpeechManager.cpp code/apis/TCManager.h code/apis/TCManager.cpp code/apis/PlatformProfileManager.h code/apis/RegistryProfileManager.cpp code/apis/FileProfileManager.cpp code/apis/PlatformProfileManagerShared.cpp ) source_group(Apis FILES ${API_CODE_FILES}) set(RESOURCE_FILES platform/win32/wxlauncher.rc ${CMAKE_CURRENT_BINARY_DIR}/generated/version_strings.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/helplinks.cpp ) source_group(Resources FILES ${RESOURCE_FILES}) set(CODE_FILES code/MainWindow.h code/MainWindow.cpp code/wxLauncherApp.h code/wxLauncherApp.cpp ) source_group("Main Code Files" FILES ${CODE_FILES}) add_executable(wxlauncher WIN32 MACOSX_BUNDLE ${TAB_CODE_FILES} ${GUI_CONTROL_CODE_FILES} ${GLOBAL_CODE_FILES} ${DATASTRUCTURE_CODE_FILES} ${API_CODE_FILES} ${RESOURCE_FILES} ${CODE_FILES} ) set_target_properties(wxlauncher PROPERTIES LINKER_LANGUAGE CXX) # Files that are not to be compiled directly set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/generated/helplinks.cpp ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/onlinehelp.htb ${CMAKE_CURRENT_BINARY_DIR}/generated/configure_launcher.h code/datastructures/FlagInfo.cpp code/global/configure_launcher.h.in PROPERTIES HEADER_FILE_ONLY true) add_dependencies(wxlauncher helpmaker version.cpp.maker) if(IS_WIN32) add_dependencies(wxlauncher registry_helper) endif(IS_WIN32) configure_file(${CMAKE_SOURCE_DIR}/code/global/configure_launcher.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/configure_launcher.h) foreach(temp_file ${helpmaker_temp_files}) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${temp_file}) endforeach(temp_file) target_link_libraries(wxlauncher ${wxWidgets_LIBRARIES} ${SDL_LIBRARY}) # adapted from http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_apply_resources_on_Mac_OS_X_automatically.3F # copies necessary resources (and frameworks, if needed) to .app bundle if(IS_APPLE) set(APP_RESOURCES_PATH ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${RESOURCES_PATH}) set(APP_FRAMEWORKS_PATH ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/wxlauncher.app/Contents/Frameworks) add_custom_command(TARGET wxlauncher POST_BUILD COMMAND rm -rf ${APP_RESOURCES_PATH} COMMAND rm -rf ${APP_FRAMEWORKS_PATH} COMMAND mkdir ${APP_RESOURCES_PATH} COMMAND mkdir ${APP_FRAMEWORKS_PATH} COMMAND cp ${PROJECT_SOURCE_DIR}/resources/* ${APP_RESOURCES_PATH} COMMAND cp ${helphtblocation} ${APP_RESOURCES_PATH} COMMAND cp ${PROJECT_SOURCE_DIR}/platform/macosx/wxlauncher.icns ${APP_RESOURCES_PATH}) if(USING_SDL_FRAMEWORK) # then copy the framework into the app add_custom_command(TARGET wxlauncher POST_BUILD COMMAND cp -R ${SDL_LIBRARY} ${APP_FRAMEWORKS_PATH}) endif(USING_SDL_FRAMEWORK) endif(IS_APPLE) # packaging set(LAUNCHER_CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}/cmake) if(DEVELOPMENT_MODE) message( "Development mode is set. Building of installers is not allowed. Set DEVELOPMENT_MODE=OFF to build installers.") configure_file(${CMAKE_SOURCE_DIR}/cmake/FailCPack.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CPackConfig.cmake COPYONLY) configure_file(${CMAKE_SOURCE_DIR}/cmake/FailCPack.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CPackSourceConfig.cmake COPYONLY) else() include(${LAUNCHER_CMAKE_SOURCE_DIR}/wxLauncherInstaller.cmake) endif() wxlauncher-0.9.4/CodingStyle.txt0000644000000000000000000000411312155177255016640 0ustar rootroot00000000000000The coding style for wxLauncher is split into three parts, one for the C/C++ portion of the code, one for the python code, and one for the online help manual(Markdown) portion of code. All code ======== All code for wxLauncher should have a line feed (LF) (ASCII 0x0A) as the line endings in all files. This line ending style is also known as the Unix style line ending. This means all linux and OSX coders should have: [hooks] pretxncommit.crlf = python:hgext.win32text.forbidcrlf [patch] eol = lf as in their .hgrc files. The setting can be set in either the ~/.hgrc or in the /.hg/.hgrc. Either location will work and will cause mercurial to automaticilly convert line endings. All windows users should set: [hooks] # make sure that crlf's do not get committed. pretxncommit.crlf = python:hgext.win32text.forbidcrlf [patch] eol = lf in their Mercurial.ini file (located in the user profile) or in the /.hg/.hgrc file. This setting allows mercurial to make sure that any incoming files with the wrong line endings will be corrected quickly. Tabs are to be set two spaces and to be auto converted to spaces. This is espcially important when editing the Python code. Help Manual =========== For the coding style and language style for the help manual see the ReadMe.txt in the onlinehelp folder. Python Code =========== Python coding style is to follow PEP 8 found on the python website at: . C/C++ Code ========== The C/C++ coding style similar to the Python and wxWidgets coding style. -Class names should be CamelCased with the first letter also capitalized. -Public functions of classes should also be CamelCased and start with a capital letter. -Event handling functions should be named On. -Other functions should also be where possible. -Private class variables should be camelCased starting with a lowercase letter and always referenced via this. this->memberVariable = 9; -Macros must be named with all capital letters with words separated by underscores. wxlauncher-0.9.4/GPLv2.txt0000644000000000000000000004310212155177255015307 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.wxlauncher-0.9.4/License.txt0000644000000000000000000000211612155177255015777 0ustar rootroot00000000000000wxLauncher is copyright the wxLauncher Team. wxLauncher is Free Software, released under the GNU General Public License, version 2. You should have one copy of the license downloaded with the source (GPLv2.txt found alongside this summary). If it is missing, you can find a copy of the license at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Please add it back to the source package ;) The source code for this software is available for download at http://code.google.com/p/wxlauncher/downloads/list ** The interfaces that wxLauncher uses to interact with the FreeSpace 2 Open binaries are licensed ( including but not limited to the Volition Pack files, and flag file) under FreeSpace 2 Open's license and are copyright Volition, Inc. (developer of Freespace 2), Interplay (publisher of FreeSpace 2), THQ (Volition, Inc's current owner) and/or the Freespace 2 Source Code Project (SCP). ** All source in the libraries folder is licensed under its repective license which is indicated by a License.txt in its source folder and/or a header in the code file. The wxLauncher Team. wxlauncher-0.9.4/ReadMe.ContentAuthors.txt0000644000000000000000000000402312155177255020530 0ustar rootroot00000000000000** This document is indented for content authors and contains instructions about how wxLauncher can help them. If you just want to play FS2 Open see the main ReadMe.txt. ** Introduction ============ wxLauncher aims to give one unified answer to members of the FreeSpace 2 Open modding community looking for an easy way to give your users an easy to use launcher (and installer, once that happens), as well as providing you with some useful tools for making your life easier by reducing your cross platform install issues. wxLauncher can also make your life easier for working with FSO and jumping back and forth between different content that you have being developed or completed. Features ======== - Add a profile to wxLauncher from a file. - Select the profile that wxLauncher will use on next startup. A note about wxLaunchers commandline ==================================== There are three types of commandline options to wxLauncher. A switch: Enables specific functionality just from its presence. A operator: Causes wxLauncher to act (normally without showing the GUI) upon its internal data structures. Used with /OPERANDS/ to provide the targets or sources for operations. An operand: Used with operators to specify what to act upon. Adding profiles programmatically ============================ This is done with the --add-profile commandline operator. Requires two Operands: --profile - Contains name of the profile to add --file - Contains the fully qualified path to a profile to import. This function will not overwrite an existing profile of the same name. wxLauncher developer note: this function behaves as if the user asked wxLauncher to clone the profile that is contained in the file specified. Changing the default profile programmatically ========================================= This is done with the --select-profile commandline operator. It requires one Operand --profile - The name of the profile to switch to This function will cause wxLauncher to change the default profile to the specified profile. wxlauncher-0.9.4/ReadMe.txt0000644000000000000000000002615312155177255015561 0ustar rootroot00000000000000wxLauncher - A multi-platform dual-purpose application http://code.google.com/p/wxlauncher/ Introduction ============ wxLauncher aims to give one unified answer to members of the Freespace 2 Open community looking for an easy way to control fs2_open on various platforms and to those looking for an easy way to find and get updates to their Freespace 2 MODs and TCs. Compatibility ============= wxLauncher is built for the 3.6.9+ versions of fs2_open. while older versions might function, we don't officially support them. Use them at your own risk. Download binaries ============= Precompiled binaries can be found on the project downloads page: http://code.google.com/p/wxlauncher/downloads/list Just find the build for your system and you're set. Building from source ============ The wxLauncher requires the CMake build system for building. CMake is a cross-platform meta-build system; it generates the files that allow a platform's native build system to build the launcher. CMake supports VS2005 and newer, as well as Xcode, KDevelop, and of course autotools. Requirements shortlist: All platforms: - CMake 2.8 - wxWidgets 2.8.10+ (but not 2.9.x) - Python 2.6+ (but not 3.x) - Markdown in Python Windows - Platform SDK - Nullsoft Scriptable Install System (NSIS) Linux - SDL 1.2 OS X - SDL 1.2 Optional components All platforms: - OpenAL wxLauncher is built using CMake. Only version 2.8 has been tested (and the CMake file enforces this). CMake can be downloaded in binary form, from the [CMake Home Page] or if you run Linux, from your distro's package repository. [Cmake Home Page]: http://cmake.org wxLauncher is a wxWidgets-based application. It can only be built with wxWidgets version 2.8.10 or higher installed on your system. wxWidgets homepage: http://www.wxwidgets.org/ Python 2.6 or higher is required to build this project. This project also assumes that the python executable is in your PATH. Check your operating system's documentation for information on how to add Python to your PATH. Python homepage: http://www.python.org/ Markdown in Python is required in order to build the integrated help system. Markdown in Python homepage: http://freewisdom.org/projects/python-markdown/ On debian-based systems: apt-get install python-markdown The OpenAL Software Development Kit is an optional component needed to build this program. OpenAL support requires the preprocessor symbol USE_OPENAL=1. This symbol is set to 1 by default by CMake. You can pass -DUSE_OPENAL=0 to CMake to disable building with OpenAL support. See the compiler specific instructions for getting your compiler ready to build with OpenAL. Note that Mac OS X ships with OpenAL pre-installed. OpenAL homepage: http://connect.creativelabs.com/openal/default.aspx The Microsoft Windows SDK (formerly the Platform SDK) is required to build both this application and wxWidgets when on Windows. Note that only the Windows SDK for Windows Vista and Windows 7 have been tested with this application. The Nullsoft Scriptable Install System (NSIS) is required on Windows to build the installer. The latest version at the time of writing is 2.46. Be sure to either install NSIS before running CMake or re-run CMake after installing NSIS. NSIS homepage: http://nsis.sourceforge.net/ wxLauncher has only been built with Microsoft's Visual Studio 2008 Express edition. Other VS2008 versions should work, as well as any version of VS2005 should work, but have not been tested. Visual Studio 2008 C++ Express can be downloaded from [Microsoft] for free. [Microsoft]: http://www.microsoft.com/express/download/ wxLauncher's source can be explored from the project's source page: http://code.google.com/p/wxlauncher/source/checkout To get the source, you'll need Mercurial: http://mercurial.selenic.com/ or the TortoiseHG frontend (which includes Mercurial): http://tortoisehg.bitbucket.org/ Once Mercurial is installed, you can get a copy of the source by running the following command in a folder of your choice: hg clone https://code.google.com/p/wxlauncher/ Building - Windows ================== Run CMake in your favourite way (GUI, or on the commandline ccmake (uses curses) or cmake). CMake QT Gui ------------ Assuming the GUI, select the CMakeLists.txt in the main wxLauncher source directory and set your output directory to where you want the native build tool to be placed, somewhere without spaces. Click configure until the Generate button Enables. The lines that are highligted red are new variables that CMake has found. - Set wxWdigets_ROOT_DIR to the root directory of your wxWidgets source directory if it remains NOTFOUND. - Set PYTHON_EXECUTABLE to the python that you want to use. It may not show up. If it doesn't show up, it means that cmake found python automatically, but if cmake selects the wrong version of python, please see "CMake selects the wrong python". - Check DEVELOPMENT_MODE if you are going to be debugging wxLauncher. Make sure that it is unchecked if you plan on distributing the code as with this checked, the launcher will only run on the dev machine. This option changes where the launcher looks for files to display as the default interface. - Check USE_JOYSTICK, USE_OPENAL, and/or USE_SPEECH if you want those options compiled in. - If USE_OPENAL is checked, OPENAL_INCLUDE_DIR will appear. Set it to the include folder in the OpenAL SDK folder. Building - Linux (has been tested on Ubuntu) ========================= Command line ----------- - Download the wxLauncher source - sudo apt-get install build-essential libopenal-dev libwxgtk2.8-dev libwxgtk2.8-dbg python-markdown - Download and install the cmake 2.8 .debs for your platform from: . You will need cmake-data, and cmake-2.8.0* for your platform, plus cmake-curses-gui or cmake-qt-gui - cd - mkdir build - cd build To prevent error messages like "The launcher is expecting (/home/foo/bar) to contain the resource images." from occurring, either type: - cmake -DUSE_OPENAL=1 -DDEVELOPMENT_MODE=1 -DCMAKE_INSTALL_PREFIX=/usr/local ../ - make Or instead type: - cmake -DUSE_OPENAL=1 -DCMAKE_INSTALL_PREFIX=/usr/local ../ - make - make install (with root privileges) CMake selects the wrong python ------------------------------ If CMake selects the wrong version of python (should be version 2), you can set the version yourself by setting PYTHON_EXECUTABLE when you run CMake by adding "-DPYTHON_EXECUTABLE=" to your command line, for example "-DPYTHON_EXECUTABLE=/usr/bin/python2". If you run into this problem, please post in this ticket with your distro, OS version, and whether you're running 32- or 64-bit. Building - OS X 10.6 (Snow Leopard), although should also work on 10.5 (Leopard) ============================== - Download and install the most recent version of Xcode 3. You will need a free Apple Developer Center account to download Xcode 3. If you have a different version of Xcode 3, such as a copy provided on your OS X install disc, that might also work, but it can't be guaranteed. - Download and install the most recent version of Python 2 if you don't already have at least Python 2.6; Python 3 will not work - Download and install Markdown in Python (link is provided above) - Download and install Mercurial, making sure that you select the version of Mercurial for your version of OS X - Download the wxLauncher source - Download and install CMake 2.8 - Download and build wxWidgets 2.8.12 (use the wxMac version). Once you've downloaded and extracted the wxMac-2.8.12 tarball, do these things: * cd wxMac-2.8.12/ * type either "mkdir build-debug" or "mkdir build-release" (your choice) * cd * Type the following to configure, adjusting according to the notes that follow: * arch_flags="-arch i386" * ../configure CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags" --enable-unicode --enable-debug --disable-shared --with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk --with-macosx-version-min=10.5 > If you're building on Leopard, leave out the 'arch_flags="-arch i386"' and the 'CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags"' parts > If you're not interested in compatibility with Leopard, leave out the '--with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk --with-macosx-version-min=10.5' part > If you want a release build rather than a debug build, leave out the '--enable-debug' * make - Install SDL: you can either (1) download, build, and install it yourself (such as with MacPorts) or (2) simply download the Frameworks.tgz tarball from the FreeSpace Open source tree and extract the SDL.framework, then copy the SDL.framework folder to your /Library/Frameworks folder - Run CMake *twice* either by using cmake at the command line or by using the CMake GUI (which is most likely in /Applications, although you can find it using Spotlight), selecting Xcode as your generator. For currently unknown reasons, CMake must be run twice for CPack to correctly generate drag-and-drop .apps. - A few notes on configuring the CMake variables: * Set wxWidgets_CONFIG_EXECUTABLE and wxWidgets_wxrc_EXECUTABLE to point to the version of wxWidgets you built, not the pre-installed version in /usr/local. wxWidgets_CONFIG_EXECUTABLE is located at /yourWxWidgetsBuildDir/wx-config and wxWidgets_wxrc_EXECUTABLE is located at /yourWxWidgetsBuildDir/utils/wxrc/wxrc * If you are not using SDL.framework, uncheck USING_SDL_FRAMEWORK (or on command line, add -DUSING_SDL_FRAMEWORK=0) * If you are using SDL.framework in /Library/Frameworks, make sure that SDL_LIBRARY is set to /Library/Frameworks/SDL.framework, since CMake might automatically add extra stuff to SDL_LIBRARY, such as ";-framework Cocoa" * If you're building on Snow Leopard or later with Leopard compatibility, make sure that CMAKE_OSX_SYSROOT is set to /Developer/SDKs/MacOSX10.5.sdk - Example command-line usage of CMake if you're not building for 10.5 and are using the SDL.framework: * cd * mkdir build * cd build * cmake -G Xcode -DwxWidgets_CONFIG_EXECUTABLE=//wx-config -DwxWidgets_wxrc_EXECUTABLE=/utils/wxrc/wxrc -DSDL_LIBRARY=/Library/Frameworks/SDL.framework -DUSE_OPENAL=1 ../ - Once you have your Xcode project set up, build the "ALL_BUILD" target to build wxlauncher.app in /yourWxLauncherBuildDir/YourSelectedBuildConfig/ , or type "xcodebuild -configuration " at the shell prompt in your wxLauncher build folder. Make sure that the build configuration you choose (Debug or Release) matches the build configuration you used when you built wxWidgets. Important known issues on OS X: - After startup or after a FS2 Open binary is (re-)selected, checkboxes on the advanced settings page may not appear until after a moment or after the user interacts with the advanced settings page, such as by clicking on the flag list. License ======= See License.txt. wxlauncher-0.9.4/Thanks.txt0000644000000000000000000000137112155177255015647 0ustar rootroot00000000000000This is a list of additional resources that are used in the launcher but are not our creation: * Statusbar icons by FamFamFam . License: the Creative Commons 2.5 Attribution license * Warning icon (warning_big.png) from by David Vignoni, modified by Bastique . Recolored by flamurai . License: LGPL * Help icon by Thoosje . License: Freeware, author credit required. * Info icon (info_big.png) by Amanda44 . License: Public domainwxlauncher-0.9.4/cmake/FailCPack.cmake.in0000644000000000000000000000025512155177255020122 0ustar rootroot00000000000000MESSAGE(FATAL_ERROR "This build environment was configured using DEVELOPMENT_MODE=ON. To build a package for wxLauncher regenerated environment using DEVELOPMENT_MODE=OFF")wxlauncher-0.9.4/cmake/include/NSIS.template.in0000644000000000000000000007326712155177255021314 0ustar rootroot00000000000000; CPack install script designed for a nmake build ;-------------------------------- ; You must define these values !define VERSION "@CPACK_PACKAGE_VERSION@" !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" !define UKP "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ;-------------------------------- ;Variables Var MUI_TEMP Var STARTMENU_FOLDER Var SV_ALLUSERS Var START_MENU Var DO_NOT_ADD_TO_PATH Var ADD_TO_PATH_ALL_USERS Var ADD_TO_PATH_CURRENT_USER Var INSTALL_DESKTOP Var IS_DEFAULT_INSTALLDIR ; The already INstalled Version bits. Var INVMAJOR Var INVMINOR Var INVPATCH Var UNINSTALLER_DIR ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;Default installation folder InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ;-------------------------------- ;General ;Name and file Name "@CPACK_NSIS_PACKAGE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" ;Set compression SetCompressor @CPACK_NSIS_COMPRESSOR@ @CPACK_NSIS_DEFINES@ !include Sections.nsh ;--- Component support macros: --- ; The code for the add/remove functionality is from: ; http://nsis.sourceforge.net/Add/Remove_Functionality ; It has been modified slightly and extended to provide ; inter-component dependencies. Var AR_SecFlags Var AR_RegFlags @CPACK_NSIS_SECTION_SELECTED_VARS@ ; Loads the "selected" flag for the section named SecName into the ; variable VarName. !macro LoadSectionSelectedIntoVar SecName VarName SectionGetFlags ${${SecName}} $${VarName} IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits !macroend ; Loads the value of a variable... can we get around this? !macro LoadVar VarName IntOp $R0 0 + $${VarName} !macroend ; Sets the value of a variable !macro StoreVar VarName IntValue IntOp $${VarName} 0 + ${IntValue} !macroend !macro InitSection SecName ; This macro reads component installed flag from the registry and ;changes checked state of the section on the components page. ;Input: section index constant name specified in Section command. ClearErrors ;Reading component status from registry ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" "Installed" IfErrors "default_${SecName}" ;Status will stay default if registry value not found ;(component was never installed) IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit ; Note whether this component was installed before !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags IntOp $R0 $AR_RegFlags & $AR_RegFlags ;Writing modified flags SectionSetFlags ${${SecName}} $AR_SecFlags "default_${SecName}:" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected !macroend !macro FinishSection SecName ; This macro reads section flag set by user and removes the section ;if it is not selected. ;Then it writes component installed flag to registry ;Input: section index constant name specified in Section command. SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags ;Checking lowest bit: IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} IntCmp $AR_SecFlags 1 "leave_${SecName}" ;Section is not selected: ;Calling Section uninstall macro and writing zero installed flag !insertmacro "Remove_${${SecName}}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ "Installed" 0 Goto "exit_${SecName}" "leave_${SecName}:" ;Section is selected: WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ "Installed" 1 "exit_${SecName}:" !macroend !macro RemoveSection SecName ; This macro is used to call section's Remove_... macro ;from the uninstaller. ;Input: section index constant name specified in Section command. !insertmacro "Remove_${${SecName}}" !macroend ; Determine whether the selection of SecName changed !macro MaybeSelectionChanged SecName !insertmacro LoadVar ${SecName}_selected SectionGetFlags ${${SecName}} $R1 IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits ; See if the status has changed: IntCmp $R0 $R1 "${SecName}_unchanged" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" !insertmacro "Deselect_required_by_${SecName}" goto "${SecName}_unchanged" "${SecName}_was_selected:" !insertmacro "Select_${SecName}_depends" "${SecName}_unchanged:" !macroend ;--- End of Add/Remove macros --- ;-------------------------------- ;Interface Settings !define MUI_HEADERIMAGE !define MUI_ABORTWARNING ;-------------------------------- ; path functions !verbose 3 !include "WinMessages.NSH" !verbose 4 ;---------------------------------------- ; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" ;---------------------------------------- !verbose 3 !include "WinMessages.NSH" !verbose 4 ;==================================================== ; get_NT_environment ; Returns: the selected environment ; Output : head of the stack ;==================================================== !macro select_NT_profile UN Function ${UN}select_NT_profile StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single DetailPrint "Selected environment for all users" Push "all" Return environment_single: DetailPrint "Selected environment for current user only." Push "current" Return FunctionEnd !macroend !insertmacro select_NT_profile "" !insertmacro select_NT_profile "un." ;---------------------------------------------------- !define NT_current_env 'HKCU "Environment"' !define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif ; AddToPath - Adds the given dir to the search path. ; Input - head of the stack ; Note - Win9x systems requires reboot Function AddToPath Exch $0 Push $1 Push $2 Push $3 # don't add if the path doesn't exist IfFileExists "$0\*.*" "" AddToPath_done ReadEnvStr $1 PATH ; if the path is too long for a NSIS variable NSIS will return a 0 ; length string. If we find that, then warn and skip any path ; modification as it will trash the existing path. StrLen $2 $1 IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done CheckPathLength_ShowPathWarning: Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" Goto AddToPath_done CheckPathLength_Done: Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done GetFullPathName /SHORT $3 $0 Push "$1;" Push "$3;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$3\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Call IsNT Pop $1 StrCmp $1 1 AddToPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" a FileSeek $1 -1 END FileReadByte $1 $2 IntCmp $2 26 0 +2 +2 # DOS EOF FileSeek $1 -1 END # write over EOF FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" FileClose $1 SetRebootFlag true Goto AddToPath_done AddToPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto DoTrim ReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" DoTrim: StrCmp $1 "" AddToPath_NTdoIt Push $1 Call Trim Pop $1 StrCpy $0 "$1;$0" AddToPath_NTdoIt: StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $0 Goto DoSend WriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $0 DoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToPath_done: Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Remove a given dir from the path ; Input: head of the stack Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 IntFmt $6 "%c" 26 # DOS EOF Call un.IsNT Pop $1 StrCmp $1 1 unRemoveFromPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w GetFullPathName /SHORT $0 $0 StrCpy $0 "SET PATH=%PATH%;$0" Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoop: FileRead $1 $3 StrCpy $5 $3 1 -1 # read last char StrCmp $5 $6 0 +2 # if DOS EOF StrCpy $3 $3 -1 # remove DOS EOF so we can compare StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "" unRemoveFromPath_dosLoopEnd FileWrite $2 $3 Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto unRemoveFromPath_done unRemoveFromPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto unDoTrim unReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" unDoTrim: StrCpy $5 $1 1 -1 # copy last char StrCmp $5 ";" +2 # if last char != ; StrCpy $1 "$1;" # append ; Push $1 Push "$0;" Call un.StrStr ; Find `$0;` in $1 Pop $2 ; pos of our dir StrCmp $2 "" unRemoveFromPath_done ; else, it is in path # $0 - path to add # $1 - path var StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 # $5 is now the part before the path to remove StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove StrCpy $3 $5$6 StrCpy $5 $3 1 -1 # copy last char StrCmp $5 ";" 0 +2 # if last char == ; StrCpy $3 $3 -1 # remove last char StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $3 Goto unDoSend unWriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $3 unDoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromPath_done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Uninstall sutff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ########################################### # Utility Functions # ########################################### ;==================================================== ; IsNT - Returns 1 if the current system is NT, 0 ; otherwise. ; Output: head of the stack ;==================================================== ; IsNT ; no input ; output, top of the stack = 1 if NT or 0 if not ; ; Usage: ; Call IsNT ; Pop $R0 ; ($R0 at this point is 1 or 0) !macro IsNT un Function ${un}IsNT Push $0 ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in ; output, top of stack (replaces with the portion of the string remaining) ; modifies no other variables. ; ; Usage: ; Push "this is a long ass string" ; Push "ass" ; Call StrStr ; Pop $R0 ; ($R0 at this point is "ass string") !macro StrStr un Function ${un}StrStr Exch $R1 ; st=haystack,old$R1, $R1=needle Exch ; st=old$R1,haystack Exch $R2 ; st=old$R1,old$R2, $R2=haystack Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=needle ; $R2=haystack ; $R3=len(needle) ; $R4=cnt ; $R5=tmp loop: StrCpy $R5 $R2 $R3 $R4 StrCmp $R5 $R1 done StrCmp $R5 "" done IntOp $R4 $R4 + 1 Goto loop done: StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." Function Trim ; Added by Pelaca Exch $R1 Push $R2 Loop: StrCpy $R2 "$R1" 1 -1 StrCmp "$R2" " " RTrim StrCmp "$R2" "$\n" RTrim StrCmp "$R2" "$\r" RTrim StrCmp "$R2" ";" RTrim GoTo Done RTrim: StrCpy $R1 "$R1" -1 Goto Loop Done: Pop $R2 Exch $R1 FunctionEnd Function ConditionalAddToRegisty Pop $0 Pop $1 StrCmp "$0" "" ConditionalAddToRegisty_EmptyString WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \ "$1" "$0" ;MessageBox MB_OK "Set Registry: '$1' to '$0'" DetailPrint "Set install registry entry: '$1' to '$0'" ConditionalAddToRegisty_EmptyString: FunctionEnd ;-------------------------------- !ifdef CPACK_USES_DOWNLOAD Function DownloadFile IfFileExists $INSTDIR\* +2 CreateDirectory $INSTDIR Pop $0 ; Skip if already downloaded IfFileExists $INSTDIR\$0 0 +2 Return StrCpy $1 "@CPACK_DOWNLOAD_SITE@" try_again: NSISdl::download "$1/$0" "$INSTDIR\$0" Pop $1 StrCmp $1 "success" success StrCmp $1 "Cancelled" cancel MessageBox MB_OK "Download failed: $1" cancel: Return success: FunctionEnd !endif ;-------------------------------- ; Installation types @CPACK_NSIS_INSTALLATION_TYPES@ ;-------------------------------- ; Component sections @CPACK_NSIS_COMPONENT_SECTIONS@ ;-------------------------------- ; Define some macro setting for the gui @CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ @CPACK_NSIS_INSTALLER_ICON_CODE@ @CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ @CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@ ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" Page custom InstallOptionsPage !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @CPACK_NSIS_PAGE_COMPONENTS@ !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "Albanian" !insertmacro MUI_LANGUAGE "Arabic" !insertmacro MUI_LANGUAGE "Basque" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Bosnian" !insertmacro MUI_LANGUAGE "Breton" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Farsi" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Hebrew" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Icelandic" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Irish" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Korean" !insertmacro MUI_LANGUAGE "Kurdish" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Luxembourgish" !insertmacro MUI_LANGUAGE "Macedonian" !insertmacro MUI_LANGUAGE "Malay" !insertmacro MUI_LANGUAGE "Mongolian" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Serbian" !insertmacro MUI_LANGUAGE "SerbianLatin" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Slovenian" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Thai" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Ukrainian" !insertmacro MUI_LANGUAGE "Welsh" ;-------------------------------- ;Reserve Files ;These files should be inserted before other files in the data block ;Keep these lines before any File command ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) ReserveFile "NSIS.InstallOptions.ini" !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ;-------------------------------- ;Installer Sections Section "-Core installation" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. SetOutPath "$INSTDIR" @CPACK_NSIS_FULL_INSTALL@ ;Store installation folder WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "cmdinterpreter" "$INSTDIR\bin\wxlauncher.exe" ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" Push "DisplayName" Push "@CPACK_NSIS_DISPLAY_NAME@" Call ConditionalAddToRegisty Push "DisplayVersion" Push "@CPACK_PACKAGE_VERSION@" Call ConditionalAddToRegisty Push "Publisher" Push "@CPACK_PACKAGE_VENDOR@" Call ConditionalAddToRegisty Push "UninstallString" Push "$INSTDIR\Uninstall.exe" Call ConditionalAddToRegisty Push "NoRepair" Push "1" Call ConditionalAddToRegisty !ifdef CPACK_NSIS_ADD_REMOVE ;Create add/remove functionality Push "ModifyPath" Push "$INSTDIR\AddRemove.exe" Call ConditionalAddToRegisty !else Push "NoModify" Push "1" Call ConditionalAddToRegisty !endif ; Optional registration Push "DisplayIcon" Push "$INSTDIR\bin\wxlauncher.exe" Call ConditionalAddToRegisty Push "HelpLink" Push "@CPACK_NSIS_HELP_LINK@" Call ConditionalAddToRegisty Push "URLInfoAbout" Push "@CPACK_NSIS_URL_INFO_ABOUT@" Call ConditionalAddToRegisty Push "Contact" Push "@CPACK_NSIS_CONTACT@" Call ConditionalAddToRegisty Push "VersionMajor" Push "@CPACK_PACKAGE_VERSION_MAJOR@" Call ConditionalAddToRegisty Push "VersionMinor" Push "@CPACK_PACKAGE_VERSION_MINOR@" Call ConditionalAddToRegisty Push "VersionPatch" Push "@CPACK_PACKAGE_VERSION_PATCH@" Call ConditionalAddToRegisty Push "" Push "$INSTDIR" Call ConditionalAddToRegisty !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" ;Read a value from an InstallOptions INI file !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" ; Write special uninstall registry entries Push "StartMenu" Push "$STARTMENU_FOLDER" Call ConditionalAddToRegisty Push "DoNotAddToPath" Push "$DO_NOT_ADD_TO_PATH" Call ConditionalAddToRegisty Push "AddToPathAllUsers" Push "$ADD_TO_PATH_ALL_USERS" Call ConditionalAddToRegisty Push "AddToPathCurrentUser" Push "$ADD_TO_PATH_CURRENT_USER" Call ConditionalAddToRegisty Push "InstallToDesktop" Push "$INSTALL_DESKTOP" Call ConditionalAddToRegisty !insertmacro MUI_STARTMENU_WRITE_END @CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ SectionEnd Section "-Add to path" Push $INSTDIR\bin StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 Call AddToPath doNotAddToPath: SectionEnd ;-------------------------------- ; Create custom pages Function InstallOptionsPage !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" FunctionEnd ;-------------------------------- ; determine admin versus local install Function un.onInit ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' Goto done StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' Goto done noLM: ;Get installation folder from registry if available done: FunctionEnd ;--- Add/Remove callback functions: --- !macro SectionList MacroName ;This macro used to perform operation on multiple sections. ;List all of your components in following manner here. @CPACK_NSIS_COMPONENT_SECTION_LIST@ !macroend Section -FinishComponents ;Removes unselected components and writes component status to registry !insertmacro SectionList "FinishSection" !ifdef CPACK_NSIS_ADD_REMOVE ; Get the name of the installer executable System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' StrCpy $R3 $R0 ; Strip off the last 13 characters, to see if we have AddRemove.exe StrLen $R1 $R0 IntOp $R1 $R0 - 13 StrCpy $R2 $R0 13 $R1 StrCmp $R2 "AddRemove.exe" addremove_installed ; We're not running AddRemove.exe, so install it CopyFiles $R3 $INSTDIR\AddRemove.exe addremove_installed: !endif SectionEnd ;--- End of Add/Remove callback functions --- ;-------------------------------- ; Component dependencies Function .onSelChange !insertmacro SectionList MaybeSelectionChanged FunctionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath" ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers" ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser" ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" ReadRegStr $INSTALL_DESKTOP SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop" ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " @CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ ;Remove files we installed. ;Keep the list of directories here in sync with the File commands above. @CPACK_NSIS_DELETE_FILES@ @CPACK_NSIS_DELETE_DIRECTORIES@ !ifdef CPACK_NSIS_ADD_REMOVE ;Remove the add/remove program Delete "$INSTDIR\AddRemove.exe" !endif ;Remove the uninstaller itself. Delete "$INSTDIR\Uninstall.exe" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ;Remove the installation directory if it is empty. RMDir "$INSTDIR" ; Remove the registry entries. DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ; Removes all optional components !insertmacro SectionList "RemoveSection" !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS@ @CPACK_NSIS_DELETE_ICONS_EXTRA@ ;Delete empty start menu parent diretories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" startMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors startMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop startMenuDeleteLoopDone: ; If the user changed the shortcut, then untinstall may not work. This should ; try to fix it. StrCpy $MUI_TEMP "$START_MENU" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS_EXTRA@ ;Delete empty start menu parent diretories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" secondStartMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors secondStartMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop secondStartMenuDeleteLoopDone: DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" Push $INSTDIR\bin StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 Call un.RemoveFromPath doNotRemoveFromPath: SectionEnd ;-------------------------------- ; determine admin versus local install ; Is install for "AllUsers" or "JustMe"? ; Default to "JustMe" - set to "AllUsers" if admin or on Win9x ; This function is used for the very first "custom page" of the installer. ; This custom page does not show up visibly, but it executes prior to the ; first visible page and sets up $INSTDIR properly... ; Choose different default installation folder based on SV_ALLUSERS... ; "Program Files" for AllUsers, "My Documents" for JustMe... Function .onInit ; Reads components status for registry !insertmacro SectionList "InitSection" ; check to see if /D has been used to change ; the install directory by comparing it to the ; install directory that is expected to be the ; default StrCpy $IS_DEFAULT_INSTALLDIR 0 StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 StrCpy $IS_DEFAULT_INSTALLDIR 1 StrCpy $SV_ALLUSERS "JustMe" ; if default install dir then change the default ; if it is installed for JustMe StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' StrCpy $SV_ALLUSERS "AllUsers" Goto done StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' StrCpy $SV_ALLUSERS "AllUsers" Goto done noLM: StrCpy $SV_ALLUSERS "AllUsers" ;Get installation folder from registry if available done: StrCmp $SV_ALLUSERS "AllUsers" 0 +3 StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" noOptionsPage: ; check for an older version ClearErrors ReadRegDWORD $INVMAJOR SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "VersionMajor" ReadRegDWORD $INVMINOR SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "VersionMinor" ReadRegDWORD $INVPATCH SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "VersionPatch" ; now to check with current versions ; if version not equal then offer uninstall ; if version is older then do uninstall ; if version is newer then prompt an offer to uninstall IntCmp $INVMAJOR "@CPACK_PACKAGE_VERSION_MAJOR@" 0 douninstall new_ver_inst IntCmp $INVMINOR "@CPACK_PACKAGE_VERSION_MINOR@" 0 douninstall new_ver_inst IntCmp $INVPATCH "@CPACK_PACKAGE_VERSION_PATCH@" 0 douninstall new_ver_inst MessageBox MB_YESNO|MB_ICONQUESTION \ "You already have $INVMAJOR.$INVMINOR.$INVPATCH of @CPACK_NSIS_DISPLAY_NAME@ installed. Reinstall?" \ /SD IDYES IDYES douninstall Abort "@CPACK_NSIS_DISPLAY_NAME@ is already installed." new_ver_inst: MessageBox MB_YESNO|MB_ICONQUESTION \ "Newer version $0.$1.$2 is already installed. \ Downgrade to \ @CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@?" \ /SD IDNO IDYES douninstall Abort "Newer @CPACK_NSIS_DISPLAY_NAME@ already installed." douninstall: ReadRegStr $UNINSTALLER_DIR SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" IfFileExists "$UNINSTALLER_DIR\Uninstall.exe" actuallyuninstall DetailPrint "$UNINSTALLER_DIR\Uninstall.exe does not exist" goto notinstalled ; just give up actuallyuninstall: DetailPrint "Uninstalling old @CPACK_NSIS_DISPLAY_NAME@" Exec '"$UNINSTALLER_DIR\Uninstall.exe" /S' unistallerexists: Sleep 100 IfFileExists "$UNINSTALLER_DIR\Uninstall.exe" unistallerexists Sleep 1000 notinstalled: FunctionEnd wxlauncher-0.9.4/cmake/wxLauncherInstaller.cmake0000644000000000000000000001621312155177255021737 0ustar rootroot00000000000000# Note: assuming that this file is included in CMakeLists.txt, so # using IS_WIN32, IS_APPLE, IS_LINUX # Setup the packer if(IS_WIN32) # and WIN64 # find the runtime files if(CMAKE_BUILD_TYPE MATCHES Debug) set(VC9_RUNTIME_LOCATIONS "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/Debug_NonRedist/x86/Microsoft.VC90.DebugCRT" "C:/Program Files/Microsoft Visual Studio 9.0/VC/redist/Debug_NonRedist/x86/Microsoft.VC90.DebugCRT") foreach(dll_name msvcm90d.dll msvcp90d.dll msvcr90d.dll Microsoft.VC90.DebugCRT.manifest) find_file(DLLPATH_${dll_name} ${dll_name} PATHS ${VC9_RUNTIME_LOCATIONS} NO_DEFAULT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) install(FILES ${DLLPATH_${dll_name}} DESTINATION bin) endforeach() find_file(wxLauncher_pdb wxlauncher.pdb PATHS ${CMAKE_CURRENT_BINARY_DIR}) install(FILES ${wxLauncher_pdb} DESTINATION bin) find_file(registry_helper_pdb registry_helper.pdb PATHS ${CMAKE_CURRENT_BINARY_DIR} NO_DEFAULT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) install(FILES ${registry_helper_pdb} DESTINATION bin) else() set(VC9_RUNTIME_LOCATIONS "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86/Microsoft.VC90.CRT" "C:/Program Files/Microsoft Visual Studio 9.0/VC/redist/x86/Microsoft.VC90.CRT") foreach(dll_name msvcp90.dll msvcm90.dll msvcr90.dll Microsoft.VC90.CRT.manifest) find_file(DLLPATH_${dll_name} ${dll_name} PATHS ${VC9_RUNTIME_LOCATIONS} NO_DEFAULT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) install(FILES ${DLLPATH_${dll_name}} DESTINATION bin) endforeach() endif() endif() # IS_WIN32 set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) set(CPACK_PACKAGE_NAME "wxLauncher") set(CPACK_PACKAGE_VENDOR "wxLauncher Team") set(CPACK_PACKAGE_INSTALL_DIRECTORY "wxLauncher") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Launcher and installer for the FreeSpace SCP") set(CPACK_PACKAGE_DESCRIPTION "Launcher and installer for the FreeSpace 2 Open Source Code Project") # TODO we need separate welcome and readme files, # and a readme better geared towards end users if(NOT IS_APPLE) # a license agreement popping up whenever people open the DMG will drive them crazy set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/License.txt) endif() set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/ReadMe.txt) set(CPACK_RESOURCE_FILE_WELCOME ${PROJECT_SOURCE_DIR}/ReadMe.txt) if(IS_WIN32) set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY wxLauncher) # the \\\\ is because NSIS does not handle unix paths correctly - http://www.batchmake.org/Wiki/CMake:Packaging_With_CPack set(CPACK_NSIS_MUI_ICON ${PROJECT_SOURCE_DIR}/platform/win32\\\\wxlauncher.ico) set(CPACK_NSIS_MUI_UNIICON ${PROJECT_SOURCE_DIR}/platform/win32\\\\wxlauncher.ico) # the header image *must* be a bitmap set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/platform/win32\\\\installer_header.bmp) set(CPACK_NSIS_HELP_LINK http::\\\\\\\\wxlauncher.googlecode.com) set(CPACK_NSIS_URL_INFO_ABOUT http::\\\\\\\\www.hard-light.net) endif() if(DEVELOPMENT_MODE) set(CPACK_PACKAGE_FILE_NAME wxlaucher-development-mode-no-distribution) else() if(CMAKE_BUILD_TYPE MATCHES Debug) set(CPACK_PACKAGE_FILE_NAME wxlauncher-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-debug-withpdbs) elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) set(CPACK_PACKAGE_FILE_NAME wxlauncher-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-withpdbs) else() set(CPACK_PACKAGE_FILE_NAME wxlauncher-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) endif() endif() if( IS_WIN32 AND NOT UNIX ) set(CPACK_PACKAGE_EXECUTABLES "wxLauncher" "wxLauncher for the SCP") endif() if(IS_APPLE) # currently can't get bundle name and long version string to display, grr # set(MACOSX_BUNDLE_BUNDLE_NAME "wxLauncher") # set(MACOSX_BUNDLE_LONG_VERSION_STRING "wxLauncher for the SCP, version ${MACOSX_BUNDLE_SHORT_VERSION_STRING}") set(MACOSX_BUNDLE_ICON_FILE wxlauncher.icns) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2009-2012 ${CPACK_PACKAGE_VENDOR}") endif(IS_APPLE) if(IS_LINUX) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Iss Mneur ") set(CPACK_PACKAGE_DEPENDS "libwxgtk2.8 libopenal1") set(CPACK_DEBIAN_PACKAGE_SECTION "Games") endif() if(IS_WIN32) set(CPACK_BINARY_NSIS "ON") option(BUILD_BINARY_ZIP "Generate a binary zip" "OFF") if(BUILD_BINARY_ZIP) set(CPACK_BINARY_ZIP "ON") else() set(CPACK_BINARY_ZIP "OFF") endif() elseif(IS_APPLE) set(CPACK_BINARY_DRAGNDROP "ON") set(CPACK_BINARY_PACKAGEMAKER "OFF") set(CPACK_BINARY_STGZ "OFF") set(CPACK_BINARY_TZ "OFF") set(CPACK_BINARY_TGZ "OFF") else() set(CPACK_BINARY_DEB "ON") find_program(rpmbuild_exists rpmbuild) if(rpmbuild_exists) set(CPACK_BINARY_RPM "ON") else() set(CPACK_BINARY_RPM "OFF") message(STATUS "rpmbuild does not exist, will not build RPM") endif() set(CPACK_BINARY_STGZ "OFF") set(CPACK_BINARY_TZ "OFF") set(CPACK_BINARY_TGZ "OFF") endif() include(CPack) if(IS_WIN32) install(TARGETS registry_helper RUNTIME DESTINATION bin) install(TARGETS wxlauncher RUNTIME DESTINATION bin) elseif(IS_APPLE) install(TARGETS wxlauncher RUNTIME DESTINATION . BUNDLE DESTINATION .) else() install(TARGETS wxlauncher RUNTIME DESTINATION bin) endif() # prototype for post-processing code that will be needed later # to automatically generate .dmg that doesn't have /Applications symlink #if(IS_APPLE) # set(DRAGNDROP_PATH ${CMAKE_CURRENT_BINARY_DIR}/_CPack_Packages/Darwin/DragNDrop) # set(DMG_FOLDER_PATH ${DRAGNDROP_PATH}/${PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) # message(${DRAGNDROP_PATH}) # message(${DMG_FOLDER_PATH}) # TODO the current major problem is getting the command to run at the right time # add_custom_command(TARGET package POST_BUILD # COMMAND echo post-build test # # TODO delete auto-generated .dmg, delete /Applications symlink, re-generate .dmg # ) #endif(IS_APPLE) if(IS_WIN32) install(DIRECTORY resources/ DESTINATION resources) install(FILES ${helphtblocation} DESTINATION resources) elseif(IS_APPLE) install(FILES ${PROJECT_SOURCE_DIR}/License.txt DESTINATION .) install(FILES ${PROJECT_SOURCE_DIR}/GPLv2.txt DESTINATION .) # add_custom_target(RemoveAppsLink ALL ${PROJECT_SOURCE_DIR}/platform/macosx/removeAppsLink.sh "${CMAKE_CURRENT_BINARY_DIR}/_CPack_Packages/Darwin/DragNDrop/wxlauncher-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" VERBATIM) # attempt to remove link to Applications folder, will keep this as placeholder until I come up with a better idea -- maybe a postinstall script to delete the generated DMG, remove the Applications link, then create a new DMG with hdiutil? else() install(DIRECTORY resources/ DESTINATION ${RESOURCES_PATH}) install(FILES ${helphtblocation} DESTINATION ${RESOURCES_PATH}) endif() wxlauncher-0.9.4/code/MainWindow.cpp0000644000000000000000000002324412155177255017353 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "global/ids.h" #include "global/ProfileKeys.h" #include "generated/configure_launcher.h" #include "MainWindow.h" #include "tabs/WelcomePage.h" #include "tabs/ModsPage.h" #include "tabs/BasicSettingsPage.h" #include "tabs/AdvSettingsPage.h" #include "tabs/InstallPage.h" #include "controls/BottomButtons.h" #include "apis/SkinManager.h" #include "controls/Logger.h" #include "controls/StatusBar.h" #include "apis/HelpManager.h" #include "apis/FREDManager.h" #include "global/MemoryDebugging.h" // Last include for memory debugging #define MAINWINDOW_STYLE (wxBORDER_SUNKEN | wxSYSTEM_MENU\ | wxCAPTION | wxCLOSE_BOX | wxMINIMIZE_BOX | wxCLIP_CHILDREN) const int WINDOW_WIDTH = TAB_AREA_WIDTH; MainWindow::MainWindow() { this->Create((wxFrame*)NULL, wxID_ANY, SkinSystem::GetSkinSystem()->GetWindowTitle(), wxDefaultPosition, wxSize(WINDOW_WIDTH, 550), MAINWINDOW_STYLE); SkinSystem::RegisterTCSkinChanged(this); this->FS2_pid = 0; this->FRED2_pid = 0; this->SetFont(SkinSystem::GetSkinSystem()->GetFont()); this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); this->SetStatusBar(new StatusBar(this)); #if IS_WIN32 || IS_LINUX this->SetIcon(SkinSystem::GetSkinSystem()->GetWindowIcon()); #endif // setup keyboard shortcuts wxAcceleratorEntry entries[1]; entries[0].Set(wxACCEL_NORMAL, WXK_F3, ID_F3_PRESSED); wxAcceleratorTable accel(1, entries); SetAcceleratorTable(accel); // setup tabs this->mainTab = new wxNotebook(); this->mainTab->Create(this, ID_MAINTAB, wxPoint(0,0), wxSize(WINDOW_WIDTH,-1), wxNB_TOP); this->mainTab->AddPage(new WelcomePage(this->mainTab), _("Welcome"), true); this->mainTab->AddPage(new ModsPage(this->mainTab), _("Mods"), false); this->mainTab->AddPage(new BasicSettingsPage(this->mainTab), _("Basic Settings"), false); this->mainTab->AddPage(new AdvSettingsPage(this->mainTab), _("Advanced Settings"), false); #if 0 this->mainTab->AddPage(new InstallPage(this->mainTab), _("Install/Update"), false); #endif wxPoint bbpoint(0, -1); wxSize bbsize(WINDOW_WIDTH, -1); BottomButtons* bb = new BottomButtons(this, bbpoint, bbsize); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(this->mainTab); sizer->Add(bb, wxSizerFlags().Expand()); sizer->SetSizeHints(this); this->SetSizerAndFit(sizer); this->Layout(); this->Center(); } MainWindow::~MainWindow() { // } BEGIN_EVENT_TABLE(MainWindow, wxFrame) EVT_BUTTON(ID_CLOSE_BUTTON, MainWindow::OnQuit) EVT_BUTTON(ID_HELP_BUTTON, MainWindow::OnHelp) EVT_BUTTON(ID_FRED_BUTTON, MainWindow::OnStartFred) EVT_BUTTON(ID_UPDATE_BUTTON, MainWindow::OnUpdate) EVT_BUTTON(ID_PLAY_BUTTON, MainWindow::OnFSButton) EVT_BUTTON(ID_ABOUT_BUTTON, MainWindow::OnAbout) EVT_HELP(wxID_ANY, MainWindow::OnContextHelp) EVT_END_PROCESS(ID_FS2_PROCESS, MainWindow::OnFS2Exited) EVT_END_PROCESS(ID_FRED2_PROCESS, MainWindow::OnFRED2Exited) EVT_COMMAND(wxID_NONE, EVT_TC_SKIN_CHANGED, MainWindow::OnTCSkinChanged) EVT_MENU(ID_F3_PRESSED, MainWindow::OnF3Pressed) END_EVENT_TABLE() void MainWindow::OnQuit(wxCommandEvent& WXUNUSED(event)) { this->Destroy(); } void MainWindow::OnHelp(wxCommandEvent& WXUNUSED(event)) { if (HelpManager::IsInitialized()) { HelpManager::OpenMainHelpPage(); } else { wxLogWarning(_("Help Manager is not initialized")); } } void MainWindow::OnStartFred(wxCommandEvent& WXUNUSED(event)) { bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); if ( fredEnabled ) { wxButton* fred = dynamic_cast( wxWindow::FindWindowById(ID_FRED_BUTTON, this)); wxCHECK_RET(fred != NULL, _T("Unable to find FRED button")); if (this->FRED2_pid == 0) { this->OnStart(fred, true); } else { this->OnKill(fred, true); } } else { wxLogError(_T("OnStartFred called while fredSupport is disabled!")); } } void MainWindow::OnFSButton(wxCommandEvent& WXUNUSED(event)) { wxButton* play = dynamic_cast( wxWindow::FindWindowById(ID_PLAY_BUTTON, this)); wxCHECK_RET(play != NULL, _T("Unable to find play button")); if (this->FS2_pid == 0) { this->OnStart(play); } else { this->OnKill(play); } } void MainWindow::OnStart(wxButton* button, bool startFred) { button->SetLabel(_("Starting")); button->Disable(); const wxString defaultButtonValue((startFred)?_("FRED"):_("Play")); const wxString cfgBinaryPath((startFred)? PRO_CFG_TC_CURRENT_FRED : PRO_CFG_TC_CURRENT_BINARY); ProMan* p = ProMan::GetProfileManager(); wxString folder, binary; if ( !p->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &folder) ) { wxLogError(_T("Game root folder for current profile is not set (%s)"), PRO_CFG_TC_ROOT_FOLDER.c_str()); button->SetLabel(defaultButtonValue); button->Enable(); return; } if ( !p->ProfileRead(cfgBinaryPath, &binary) ) { wxLogError(_T("No FS2 Open executable has been selected (%s)"), cfgBinaryPath.c_str()); button->SetLabel(defaultButtonValue); button->Enable(); return; } #if IS_APPLE wxFileName path(folder + wxFileName::GetPathSeparator() + binary, wxPATH_NATIVE); #else wxFileName path(folder, binary, wxPATH_NATIVE); #endif if ( !path.FileExists() ) { wxLogError(_T("Executable %s does not exist"), path.GetFullName().c_str()); button->SetLabel(defaultButtonValue); button->Enable(); return; } if ( ProMan::NoError != ProMan::GetProfileManager()->PushCurrentProfile() ) { button->SetLabel(defaultButtonValue); button->Enable(); return; } wxString previousWorkingDir(::wxGetCwd()); // hopefully this doesn't goof anything up if ( !::wxSetWorkingDirectory(folder) ) { wxLogError(_T("Unable to change working directory to %s"), folder.c_str()); button->SetLabel(defaultButtonValue); button->Enable(); return; } if ( startFred ) { this->process = new wxProcess(this, ID_FRED2_PROCESS); } else { this->process = new wxProcess(this, ID_FS2_PROCESS); } wxString command; // the "" correct for spaces in the path if (path.GetFullPath().Find(_T(" ")) != wxNOT_FOUND) { command = _T("\"") + path.GetFullPath() + _T("\""); } else { command = path.GetFullPath(); } wxLogDebug(_T("Starting a process using '%s'"), command.c_str()); long pid = ::wxExecute(command, wxEXEC_ASYNC, this->process); if ( pid == 0 ) { button->SetLabel(defaultButtonValue); button->Enable(); return; } if ( startFred ) { this->FRED2_pid = pid; wxLogInfo(_T("FRED2 Open is now running...")); } else { this->FS2_pid = pid; wxLogInfo(_T("FS2 Open is now running...")); } if ( !::wxSetWorkingDirectory(previousWorkingDir) ) { wxLogError(_T("Unable to change back to working directory %s"), previousWorkingDir.c_str()); } button->SetLabel(_T("Kill")); button->Enable(); } void MainWindow::OnKill(wxButton* button, bool killFred) { button->SetLabel(_T("Stopping")); button->Disable(); int ret = ::wxKill((killFred)?this->FRED2_pid:this->FS2_pid, wxSIGKILL); if ( ret != wxKILL_OK ) { wxLogError(_T("Got KillError %d"), ret); wxLogError(_T("Failed to kill %s process!"), killFred?_T("FRED2 Open"):_T("FS2 Open")); } } void MainWindow::OnUpdate(wxCommandEvent& WXUNUSED(event)) { wxMessageBox(_T("Update")); } void MainWindow::OnAbout(wxCommandEvent& WXUNUSED(event)) { wxMessageBox(_T("About")); } void MainWindow::OnContextHelp(wxHelpEvent& event) { HelpManager::OpenHelpById((WindowIDS)event.GetId()); } void MainWindow::OnFS2Exited(wxProcessEvent &event) { if ( this->FS2_pid == 0 ) { wxLogError(_T("OnFS2Exited called before there is a process running")); return; } int exitCode = event.GetExitCode(); wxLogInfo(_T("FS2 Open exited with a status of %d"), exitCode); delete this->process; this->process = NULL; this->FS2_pid = 0; wxButton* play = dynamic_cast( wxWindow::FindWindowById(ID_PLAY_BUTTON, this)); wxCHECK_RET(play != NULL, _T("Unable to find play button")); play->SetLabel(_T("Play")); play->Enable(); } void MainWindow::OnFRED2Exited(wxProcessEvent &event) { if ( this->FRED2_pid == 0 ) { wxLogError(_T("OnFRED2Exited called before there is a process running")); return; } int exitCode = event.GetExitCode(); wxLogInfo(_T("FRED2 Open exited with a status of %d"), exitCode); delete this->process; this->process = NULL; this->FRED2_pid = 0; wxButton* fred = dynamic_cast( wxWindow::FindWindowById(ID_FRED_BUTTON, this)); wxCHECK_RET(fred != NULL, _T("Unable to find FRED button")); fred->SetLabel(_T("FRED")); fred->Enable(); } void MainWindow::OnTCSkinChanged(wxCommandEvent& event) { this->SetTitle(SkinSystem::GetSkinSystem()->GetWindowTitle()); this->SetIcon(SkinSystem::GetSkinSystem()->GetWindowIcon()); } void MainWindow::OnF3Pressed(wxCommandEvent& WXUNUSED(event)) { ProMan* proman = ProMan::GetProfileManager(); wxCHECK_RET(proman != NULL, _T("OnF3Pressed(): proman is NULL!")); bool fredEnabled; proman->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); proman->GlobalWrite(GBL_CFG_OPT_CONFIG_FRED, !fredEnabled); FREDManager::GenerateFREDEnabledChanged(); } wxlauncher-0.9.4/code/MainWindow.h0000644000000000000000000000313212155177255017012 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include class MainWindow: public wxFrame { public: MainWindow(); ~MainWindow(); void OnQuit(wxCommandEvent& event); void OnHelp(wxCommandEvent& event); void OnFSButton(wxCommandEvent& event); void OnStart(wxButton* button, bool startFred=false); void OnKill(wxButton* button, bool killFred=false); void OnStartFred(wxCommandEvent& event); void OnUpdate(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); void OnContextHelp(wxHelpEvent& event); void OnFS2Exited(wxProcessEvent& event); void OnFRED2Exited(wxProcessEvent& event); void OnTCSkinChanged(wxCommandEvent& event); /** F3 toggles FRED launching. */ void OnF3Pressed(wxCommandEvent& event); private: wxProcess* process; wxNotebook* mainTab; long FS2_pid, FRED2_pid; DECLARE_EVENT_TABLE(); }; #endif wxlauncher-0.9.4/code/apis/CmdLineManager.cpp0000644000000000000000000000641712155177255021044 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/CmdLineManager.h" /** \class CmdLineManager CmdLineManager is used to notify controls that have registered with it that the command line or custom flags have changed. */ CmdLineEventHandlers CmdLineManager::CmdLineChangedHandlers; CmdLineEventHandlers CmdLineManager::CustomFlagsChangedHandlers; DEFINE_EVENT_TYPE(EVT_CMD_LINE_CHANGED); DEFINE_EVENT_TYPE(EVT_CUSTOM_FLAGS_CHANGED); #include // required magic incantation WX_DEFINE_LIST(CmdLineEventHandlers); void CmdLineManager::RegisterCmdLineChanged(wxEvtHandler *handler) { wxASSERT_MSG(CmdLineChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterCmdLineChanged(): Handler at %p already registered."), handler)); CmdLineChangedHandlers.Append(handler); } void CmdLineManager::RegisterCustomFlagsChanged(wxEvtHandler *handler) { wxASSERT_MSG(CustomFlagsChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterCustomFlagsChanged(): Handler at %p already registered."), handler)); CustomFlagsChangedHandlers.Append(handler); } void CmdLineManager::UnRegisterCmdLineChanged(wxEvtHandler *handler) { wxASSERT_MSG(CmdLineChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterCmdLineChanged(): Handler at %p not registered."), handler)); CmdLineChangedHandlers.DeleteObject(handler); } void CmdLineManager::UnRegisterCustomFlagsChanged(wxEvtHandler *handler) { wxASSERT_MSG(CustomFlagsChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterCustomFlagsChanged(): Handler at %p not registered."), handler)); CustomFlagsChangedHandlers.DeleteObject(handler); } void CmdLineManager::GenerateCmdLineChanged() { wxCommandEvent event(EVT_CMD_LINE_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_CMD_LINE_CHANGED event")); for (CmdLineEventHandlers::iterator iter = CmdLineChangedHandlers.begin(), end = CmdLineChangedHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_CMD_LINE_CHANGED event to %p"), current); } } void CmdLineManager::GenerateCustomFlagsChanged() { wxCommandEvent event(EVT_CUSTOM_FLAGS_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_CUSTOM_FLAGS_CHANGED event")); for (CmdLineEventHandlers::iterator iter = CustomFlagsChangedHandlers.begin(), end = CustomFlagsChangedHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_CUSTOM_FLAGS_CHANGED event to %p"), current); } } wxlauncher-0.9.4/code/apis/CmdLineManager.h0000644000000000000000000000306612155177255020506 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CMDLINEMANAGER_H #define CMDLINEMANAGER_H #include /** Command line has changed. */ DECLARE_EVENT_TYPE(EVT_CMD_LINE_CHANGED, wxID_ANY); /** Custom flags have changed. */ DECLARE_EVENT_TYPE(EVT_CUSTOM_FLAGS_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, CmdLineEventHandlers); class CmdLineManager { private: CmdLineManager(); ~CmdLineManager(); public: static void RegisterCmdLineChanged(wxEvtHandler *handler); static void UnRegisterCmdLineChanged(wxEvtHandler *handler); static void RegisterCustomFlagsChanged(wxEvtHandler *handler); static void UnRegisterCustomFlagsChanged(wxEvtHandler *handler); static void GenerateCmdLineChanged(); static void GenerateCustomFlagsChanged(); private: static CmdLineEventHandlers CmdLineChangedHandlers; static CmdLineEventHandlers CustomFlagsChangedHandlers; }; #endif wxlauncher-0.9.4/code/apis/FREDManager.cpp0000644000000000000000000000410512155177255020241 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/FREDManager.h" #include "apis/ProfileManager.h" #include "global/ProfileKeys.h" DEFINE_EVENT_TYPE(EVT_FRED_ENABLED_CHANGED); #include // required magic incantation WX_DEFINE_LIST(FREDEnabledEventHandlers); FREDEnabledEventHandlers FREDManager::FREDEnabledChangedHandlers; void FREDManager::RegisterFREDEnabledChanged(wxEvtHandler *handler) { wxASSERT_MSG(FREDEnabledChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterFREDEnabledChanged(): Handler at %p already registered."), handler)); FREDEnabledChangedHandlers.Append(handler); } void FREDManager::UnRegisterFREDEnabledChanged(wxEvtHandler *handler) { wxASSERT_MSG(FREDEnabledChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterFREDEnabledChanged(): Handler at %p not registered."), handler)); FREDEnabledChangedHandlers.DeleteObject(handler); } void FREDManager::GenerateFREDEnabledChanged() { wxCommandEvent event(EVT_FRED_ENABLED_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_FRED_ENABLED_CHANGED event")); FREDEnabledEventHandlers::iterator iter = FREDEnabledChangedHandlers.begin(); while (iter != FREDEnabledChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_FRED_ENABLED_CHANGED event to %p"), current); iter++; } } wxlauncher-0.9.4/code/apis/FREDManager.h0000644000000000000000000000252212155177255017707 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** Manages the status of whether FRED launching is enabled. */ #ifndef FRED_MANAGER_H #define FRED_MANAGER_H #include /** FRED enabled status has changed. */ DECLARE_EVENT_TYPE(EVT_FRED_ENABLED_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, FREDEnabledEventHandlers); class FREDManager { public: static void RegisterFREDEnabledChanged(wxEvtHandler *handler); static void UnRegisterFREDEnabledChanged(wxEvtHandler *handler); static void GenerateFREDEnabledChanged(); private: FREDManager(); // prevents instantiation static FREDEnabledEventHandlers FREDEnabledChangedHandlers; }; #endif wxlauncher-0.9.4/code/apis/FileProfileManager.cpp0000644000000000000000000003372612155177255021734 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "generated/configure_launcher.h" #include "apis/ProfileManager.h" #include "apis/PlatformProfileManager.h" #include "global/BasicDefaults.h" #include "global/ProfileKeys.h" #include "global/RegistryKeys.h" // NOTE: this function is also used by PushCmdlineFSO() in PlatformProfileManagerShared.cpp inline wxFileName GetPlatformDefaultConfigFilePath() { wxFileName path; #if IS_WIN32 path.AssignDir(wxStandardPaths::Get().GetUserConfigDir()); path.AppendDir(_T("FS2 Open")); #elif IS_APPLE path.AssignHomeDir(); path.AppendDir(_T("Library")); path.AppendDir(_T("FS2_Open")); #elif IS_LINUX path.AssignHomeDir(); path.AppendDir(_T(".fs2_open")); #else # error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif return path; } #define FSO_CONFIG_FILENAME _T("fs2_open.ini") #define ReturnChecker(retvalue, location) \ if ( retvalue != true ) {\ wxLogError(_T("Unhandled error in writing to the configuration file above line %d"), location);\ return ProMan::UnknownError;\ } ProMan::RegistryCodes FilePushProfile(wxFileConfig *cfg) { wxFileName configFileName; if ( cfg->Exists(INT_CONFIG_FILE_LOCATION) ) { wxString configFileNameString; if (cfg->Read(INT_CONFIG_FILE_LOCATION, &configFileNameString)) { configFileName.Assign(configFileNameString); } else { wxLogError(_T("Unable to retrieve Config File location even though config says key exists")); return ProMan::UnknownError; } } else { configFileName = GetPlatformDefaultConfigFilePath(); } wxASSERT_MSG( configFileName.Normalize(), wxString::Format(_T("Unable to normalize PlatformDefaultConfigFilePath (%s)"), configFileName.GetFullPath().c_str())); if ( !configFileName.FileExists() && configFileName.DirExists() ) { // was given a directory name configFileName.SetFullName(FSO_CONFIG_FILENAME); } wxFFileInputStream configFileInputStream(configFileName.GetFullPath()); wxStringInputStream configBlankInputStream(_T("")); // in case ini file doesn't exist wxInputStream* configInputStreamPtr = &configFileInputStream; if (!configFileInputStream.IsOk()) { wxLogDebug(_T("Could not read from ini file %s, writing new file"), configFileName.GetFullPath().c_str()); configInputStreamPtr = &configBlankInputStream; } wxFileConfig outConfig(*configInputStreamPtr, wxMBConvUTF8()); bool ret; // most settings are written to "Default" folder outConfig.SetPath(REG_KEY_DEFAULT_FOLDER_CFG); // Video int width, height, bitdepth; cfg->Read(PRO_CFG_VIDEO_RESOLUTION_WIDTH, &width, DEFAULT_VIDEO_RESOLUTION_WIDTH); cfg->Read(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, &height, DEFAULT_VIDEO_RESOLUTION_HEIGHT); cfg->Read(PRO_CFG_VIDEO_BIT_DEPTH, &bitdepth, DEFAULT_VIDEO_BIT_DEPTH); wxString videocardValue = wxString::Format(_T("OGL -(%dx%d)x%d bit"), width, height, bitdepth); ret = outConfig.Write(REG_KEY_VIDEO_RESOLUTION_DEPTH, videocardValue); ReturnChecker(ret, __LINE__); wxString filterMethod; cfg->Read(PRO_CFG_VIDEO_TEXTURE_FILTER, &filterMethod, DEFAULT_VIDEO_TEXTURE_FILTER); int filterMethodValue = ( filterMethod.StartsWith(_T("Bilinear"))) ? 0 : 1; ret = outConfig.Write(REG_KEY_VIDEO_TEXTURE_FILTER, filterMethodValue); ReturnChecker(ret, __LINE__); int oglAnisotropicFilter; cfg->Read(PRO_CFG_VIDEO_ANISOTROPIC, &oglAnisotropicFilter, DEFAULT_VIDEO_ANISOTROPIC); // Caution: FSO expects anisotropic values to be a string, // but since we're writing to an .ini file, we can write it out as an int ret = outConfig.Write(REG_KEY_VIDEO_ANISOTROPIC, oglAnisotropicFilter); ReturnChecker(ret, __LINE__); int oglAntiAliasSample; cfg->Read(PRO_CFG_VIDEO_ANTI_ALIAS, &oglAntiAliasSample, DEFAULT_VIDEO_ANTI_ALIAS); ret = outConfig.Write(REG_KEY_VIDEO_ANTI_ALIAS, oglAntiAliasSample); ReturnChecker(ret, __LINE__); // Audio wxString soundDevice; cfg->Read(PRO_CFG_OPENAL_DEVICE, &soundDevice, DEFAULT_AUDIO_OPENAL_DEVICE); ret = outConfig.Write(REG_KEY_AUDIO_OPENAL_DEVICE, soundDevice); ReturnChecker(ret, __LINE__); // new sound code settings are written to "Sound" folder outConfig.SetPath(REG_KEY_AUDIO_FOLDER_CFG); wxString playbackDevice; cfg->Read( PRO_CFG_OPENAL_DEVICE, &playbackDevice, DEFAULT_AUDIO_OPENAL_PLAYBACK_DEVICE); ret = outConfig.Write(REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE, playbackDevice); ReturnChecker(ret, __LINE__); wxString captureDevice; bool hasEntry = cfg->Read( PRO_CFG_OPENAL_CAPTURE_DEVICE, &captureDevice, DEFAULT_AUDIO_OPENAL_CAPTURE_DEVICE); if (hasEntry) { ret = outConfig.Write(REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE, captureDevice); ReturnChecker(ret, __LINE__); } int enableEFX; hasEntry = cfg->Read(PRO_CFG_OPENAL_EFX, &enableEFX, DEFAULT_AUDIO_OPENAL_EFX); if (hasEntry) { ret = outConfig.Write(REG_KEY_AUDIO_OPENAL_EFX, enableEFX); ReturnChecker(ret, __LINE__); } int sampleRate; cfg->Read( PRO_CFG_OPENAL_SAMPLE_RATE, &sampleRate, DEFAULT_AUDIO_OPENAL_SAMPLE_RATE); if (sampleRate != DEFAULT_AUDIO_OPENAL_SAMPLE_RATE) { ret = outConfig.Write(REG_KEY_AUDIO_OPENAL_SAMPLE_RATE, sampleRate); ReturnChecker(ret, __LINE__); } outConfig.SetPath(REG_KEY_DEFAULT_FOLDER_CFG); // Speech #if IS_WIN32 // speech is currently not supported in OS X or Linux (although Windows doesn't use this code) int speechVoice; cfg->Read(PRO_CFG_SPEECH_VOICE, &speechVoice, DEFAULT_SPEECH_VOICE); ret = outConfig.Write(REG_KEY_SPEECH_VOICE, speechVoice); ReturnChecker(ret, __LINE__); int speechVolume; cfg->Read(PRO_CFG_SPEECH_VOLUME, &speechVolume, DEFAULT_SPEECH_VOLUME); ret = outConfig.Write(REG_KEY_SPEECH_VOLUME, speechVolume); ReturnChecker(ret, __LINE__); int inTechroom, inBriefings, inGame, inMulti; cfg->Read(PRO_CFG_SPEECH_IN_TECHROOM, &inTechroom, DEFAULT_SPEECH_IN_TECHROOM); cfg->Read(PRO_CFG_SPEECH_IN_BRIEFINGS, &inBriefings, DEFAULT_SPEECH_IN_BRIEFINGS); cfg->Read(PRO_CFG_SPEECH_IN_GAME, &inGame, DEFAULT_SPEECH_IN_GAME); cfg->Read(PRO_CFG_SPEECH_IN_MULTI, &inMulti, DEFAULT_SPEECH_IN_MULTI); ret = outConfig.Write(REG_KEY_SPEECH_IN_TECHROOM, inTechroom); ReturnChecker(ret, __LINE__); ret = outConfig.Write(REG_KEY_SPEECH_IN_BRIEFINGS, inBriefings); ReturnChecker(ret, __LINE__); ret = outConfig.Write(REG_KEY_SPEECH_IN_GAME, inGame); ReturnChecker(ret, __LINE__); ret = outConfig.Write(REG_KEY_SPEECH_IN_MULTI, inMulti); ReturnChecker(ret, __LINE__); #endif // Joystick int currentJoystick; cfg->Read(PRO_CFG_JOYSTICK_ID, ¤tJoystick, DEFAULT_JOYSTICK_ID); ret = outConfig.Write(REG_KEY_JOYSTICK_ID, currentJoystick); ReturnChecker(ret, __LINE__); int joystickForceFeedback; cfg->Read( PRO_CFG_JOYSTICK_FORCE_FEEDBACK, &joystickForceFeedback, DEFAULT_JOYSTICK_FORCE_FEEDBACK); ret = outConfig.Write(REG_KEY_JOYSTICK_FORCE_FEEDBACK, joystickForceFeedback); ReturnChecker(ret, __LINE__); int joystickHit; cfg->Read(PRO_CFG_JOYSTICK_DIRECTIONAL, &joystickHit, DEFAULT_JOYSTICK_DIRECTIONAL); ret = outConfig.Write(REG_KEY_JOYSTICK_DIRECTIONAL, joystickHit); ReturnChecker(ret, __LINE__); // Network wxString networkConnectionValue; cfg->Read(PRO_CFG_NETWORK_TYPE, &networkConnectionValue, DEFAULT_NETWORK_TYPE); ret = outConfig.Write(REG_KEY_NETWORK_TYPE, networkConnectionValue); ReturnChecker(ret, __LINE__); wxString connectionSpeedValue; cfg->Read(PRO_CFG_NETWORK_SPEED, &connectionSpeedValue, DEFAULT_NETWORK_SPEED); ret = outConfig.Write(REG_KEY_NETWORK_SPEED, connectionSpeedValue); ReturnChecker(ret, __LINE__); int forcedport; cfg->Read(PRO_CFG_NETWORK_PORT, &forcedport, DEFAULT_NETWORK_PORT); if (forcedport != DEFAULT_NETWORK_PORT) { ret = outConfig.Write(REG_KEY_NETWORK_PORT, forcedport); ReturnChecker(ret, __LINE__); } else if (outConfig.Exists(REG_KEY_NETWORK_PORT)) { ret = outConfig.DeleteEntry(REG_KEY_NETWORK_PORT, false); ReturnChecker(ret, __LINE__); } // custom IP is written to "Network" folder outConfig.SetPath(REG_KEY_NETWORK_FOLDER_CFG); wxString networkIP; cfg->Read(PRO_CFG_NETWORK_IP, &networkIP, DEFAULT_NETWORK_IP); if (networkIP != DEFAULT_NETWORK_IP) { ret = outConfig.Write(REG_KEY_NETWORK_IP, networkIP); ReturnChecker(ret, __LINE__); } else if (outConfig.Exists(REG_KEY_NETWORK_IP)) { ret = outConfig.DeleteEntry(REG_KEY_NETWORK_IP, false); ReturnChecker(ret, __LINE__); } outConfig.SetPath(REG_KEY_DEFAULT_FOLDER_CFG); wxLogDebug(_T("Writing fs2_open.ini to %s"), configFileName.GetFullPath().c_str()); wxFFileOutputStream outFileStream(configFileName.GetFullPath()); outConfig.Save(outFileStream); return PushCmdlineFSO(cfg); } ProMan::RegistryCodes FilePullProfile(wxFileConfig *cfg) { wxFileName inFileName; if ( cfg->Exists(INT_CONFIG_FILE_LOCATION) ) { wxString inFileNameString; if (cfg->Read(INT_CONFIG_FILE_LOCATION, &inFileNameString)) { inFileName.Assign(inFileNameString); } else { wxLogError(_T("Unable to retrieve Config File location even though config says key exists")); return ProMan::UnknownError; } } else { inFileName = GetPlatformDefaultConfigFilePath(); } wxASSERT( inFileName.Normalize() ); if ( !inFileName.FileExists() && inFileName.DirExists() ) { // was given a directory name inFileName.SetFullName(FSO_CONFIG_FILENAME); } wxFFileInputStream inConfigStream(inFileName.GetFullPath(), _T("rb")); wxFileConfig inConfig(inConfigStream, wxMBConvUTF8()); wxString readString; int readNumber; // Video if ( inConfig.Read(REG_KEY_VIDEO_RESOLUTION_DEPTH, &readString) ) { // parses VideocardFS2open into its parts wxString videoCard(readString); wxString rest, rest1, rest2, rest3; long width = 0, height = 0, bitdepth = 0; if ( videoCard.StartsWith(_T("OGL -("), &rest) ) { int xLocation = rest.Find(_T('x')); if ( xLocation != wxNOT_FOUND ) { wxString widthStr(rest.Mid(0, xLocation)); rest1 = rest.Mid(xLocation); if ( !widthStr.ToLong(&width, 10) ) { width = 0; } int bLocation = rest1.Find(_T(')')); if ( bLocation != wxNOT_FOUND ) { wxString heightStr(rest1.Mid(0, bLocation)); rest2 = rest1.Mid(bLocation+1); if ( !heightStr.ToLong(&height, 10) ) { height = 0; } int spaceLoc = rest2.Find(_T(' ')); if ( spaceLoc != wxNOT_FOUND ) { wxString bitStr(rest2.Mid(0, spaceLoc)); if ( !bitStr.ToLong(&bitdepth, 10) ) { bitdepth = 0; } } } } } if ( width > 0 ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_WIDTH, width); } if ( height > 0 ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, height); } if ( bitdepth > 0 ) { cfg->Write(PRO_CFG_VIDEO_BIT_DEPTH, bitdepth); } } if ( inConfig.Read(REG_KEY_VIDEO_TEXTURE_FILTER, &readNumber) ) { cfg->Write(PRO_CFG_VIDEO_TEXTURE_FILTER, readNumber); } if ( inConfig.Read(REG_KEY_VIDEO_ANISOTROPIC, &readString) ) { long anisotropic; // necessary because FSO expects registry value to be a string if ( readString.ToLong(&anisotropic) ) { cfg->Write(PRO_CFG_VIDEO_ANISOTROPIC, anisotropic); } } if ( inConfig.Read(REG_KEY_VIDEO_ANTI_ALIAS, &readNumber) ) { cfg->Write(PRO_CFG_VIDEO_ANTI_ALIAS, readNumber); } // Audio if ( inConfig.Read(REG_KEY_AUDIO_OPENAL_DEVICE, &readString) ) { cfg->Write(PRO_CFG_OPENAL_DEVICE, readString); } if ( inConfig.Read(REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE, &readString) && !inConfig.Exists(PRO_CFG_OPENAL_DEVICE)) { cfg->Write(PRO_CFG_OPENAL_DEVICE, readString); } if ( inConfig.Read(REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE, &readString) ) { cfg->Write(PRO_CFG_OPENAL_CAPTURE_DEVICE, readString); } if ( inConfig.Read(REG_KEY_AUDIO_OPENAL_EFX, &readNumber) ) { cfg->Write(PRO_CFG_OPENAL_EFX, readNumber); } if ( inConfig.Read(REG_KEY_AUDIO_OPENAL_SAMPLE_RATE, &readNumber) ) { cfg->Write(PRO_CFG_OPENAL_SAMPLE_RATE, readNumber); } // Speech #if IS_WIN32 // Linux/OS X don't yet support speech if ( inConfig.Read(REG_KEY_SPEECH_VOICE, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_VOICE, readNumber); } if ( inConfig.Read(REG_KEY_SPEECH_VOLUME, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_VOLUME, readNumber); } if ( inConfig.Read(REG_KEY_SPEECH_IN_TECHROOM, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_IN_TECHROOM, readNumber); } if ( inConfig.Read(REG_KEY_SPEECH_IN_BRIEFINGS, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_IN_BRIEFINGS, readNumber); } if ( inConfig.Read(REG_KEY_SPEECH_IN_GAME, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_IN_GAME, readNumber); } if ( inConfig.Read(REG_KEY_SPEECH_IN_MULTI, &readNumber) ) { cfg->Write(PRO_CFG_SPEECH_IN_MULTI, readNumber); } #endif // Joystick if ( inConfig.Read(REG_KEY_JOYSTICK_ID, &readNumber) ) { cfg->Write(PRO_CFG_JOYSTICK_ID, readNumber); } if ( inConfig.Read(REG_KEY_JOYSTICK_FORCE_FEEDBACK, &readNumber) ) { cfg->Write(PRO_CFG_JOYSTICK_FORCE_FEEDBACK, readNumber); } if ( inConfig.Read(REG_KEY_JOYSTICK_DIRECTIONAL, &readNumber) ) { cfg->Write(PRO_CFG_JOYSTICK_DIRECTIONAL, readNumber); } // Network if ( inConfig.Read(REG_KEY_NETWORK_TYPE, &readString) ) { cfg->Write(PRO_CFG_NETWORK_TYPE, readString); } if ( inConfig.Read(REG_KEY_NETWORK_SPEED, &readString) ) { cfg->Write(PRO_CFG_NETWORK_SPEED, readString); } if ( inConfig.Read(REG_KEY_NETWORK_PORT, &readNumber) ) { cfg->Write(PRO_CFG_NETWORK_PORT, readNumber); } if ( inConfig.Read(REG_KEY_NETWORK_IP, &readString) ) { cfg->Write(PRO_CFG_NETWORK_IP, readString); } return ProMan::NoError; } wxlauncher-0.9.4/code/apis/FlagListManager.cpp0000644000000000000000000004667712155177255021252 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "apis/FlagListManager.h" #include "apis/ProfileManager.h" #include "apis/TCManager.h" #include "datastructures/FSOExecutable.h" #include "global/ProfileKeys.h" #include "global/MemoryDebugging.h" /** \class FlagListManager FlagListManager is used to notify controls that have registered with it that the flag file processing's status has changed. It also extracts the data in flag files generated by FS2 Open executables. */ DEFINE_EVENT_TYPE(EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED); #include // required magic incantation WX_DEFINE_LIST(FlagFileProcessingEventHandlers); #include // Magic Incantation WX_DEFINE_OBJARRAY(FlagFileArray); FlagFileProcessingEventHandlers FlagListManager::ffProcessingStatusChangedHandlers; void FlagListManager::RegisterFlagFileProcessingStatusChanged(wxEvtHandler *handler) { wxASSERT(FlagListManager::IsInitialized()); wxASSERT_MSG(ffProcessingStatusChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterFlagFileProcessingStatusChanged(): Handler at %p already registered."), handler)); FlagListManager::ffProcessingStatusChangedHandlers.Append(handler); } void FlagListManager::UnRegisterFlagFileProcessingStatusChanged(wxEvtHandler *handler) { wxASSERT(FlagListManager::IsInitialized()); wxASSERT_MSG(ffProcessingStatusChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterFlagFileProcessingStatusChanged(): Handler at %p not registered."), handler)); FlagListManager::ffProcessingStatusChangedHandlers.DeleteObject(handler); } void FlagListManager::GenerateFlagFileProcessingStatusChanged(const FlagFileProcessingStatus& status) { wxASSERT(FlagListManager::IsInitialized()); wxCommandEvent event(EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED, wxID_NONE); event.SetInt(status); wxLogDebug(_T("Generating EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED event")); for (FlagFileProcessingEventHandlers::iterator iter = FlagListManager::ffProcessingStatusChangedHandlers.begin(), end = FlagListManager::ffProcessingStatusChangedHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED event to %p"), current); } } FlagListManager* FlagListManager::flagListManager = NULL; bool FlagListManager::Initialize() { wxASSERT(!FlagListManager::IsInitialized()); FlagListManager::flagListManager = new FlagListManager(); return true; } void FlagListManager::DeInitialize() { wxASSERT(FlagListManager::IsInitialized()); if (!FlagListManager::ffProcessingStatusChangedHandlers.IsEmpty()) { wxLogDebug(_T("FlagListManager::DeInitialize(): contents of handler list:")); for (FlagFileProcessingEventHandlers::const_iterator iter = FlagListManager::ffProcessingStatusChangedHandlers.begin(), end = FlagListManager::ffProcessingStatusChangedHandlers.end(); iter != end; ++iter) { wxLogDebug(_T(" handler at %p"), *iter); } FlagListManager::ffProcessingStatusChangedHandlers.Clear(); } FlagListManager* temp = FlagListManager::flagListManager; FlagListManager::flagListManager = NULL; delete temp; } bool FlagListManager::IsInitialized() { return (FlagListManager::flagListManager != NULL); } FlagListManager* FlagListManager::GetFlagListManager() { wxCHECK_MSG(FlagListManager::IsInitialized(), NULL, _T("Attempt to get flag list manager when it has not been initialized.")); return FlagListManager::flagListManager; } FlagListManager::FlagListManager() : data(NULL), proxyData(NULL), buildCaps(0) { TCManager::RegisterTCBinaryChanged(this); } FlagListManager::~FlagListManager() { TCManager::UnRegisterTCBinaryChanged(this); this->DeleteExistingData(); } BEGIN_EVENT_TABLE(FlagListManager, wxEvtHandler) EVT_COMMAND(wxID_NONE, EVT_TC_BINARY_CHANGED, FlagListManager::OnBinaryChanged) END_EVENT_TABLE() void FlagListManager::OnBinaryChanged(wxCommandEvent& event) { wxCHECK_RET(this->GetProcessingStatus() != WAITING_FOR_FLAG_FILE, _T("Received binary changed event in middle of flag file processing.")); this->DeleteExistingData(); this->SetProcessingStatus(INITIAL_STATUS); } void FlagListManager::DeleteExistingData() { if (this->data != NULL) { FlagFileData* temp = this->data; this->data = NULL; delete temp; } if (this->proxyData != NULL) { ProxyFlagData* temp = this->proxyData; this->proxyData = NULL; delete temp; } this->buildCaps = 0; } void FlagListManager::BeginFlagFileProcessing() { wxCHECK_RET(this->GetProcessingStatus() != WAITING_FOR_FLAG_FILE, _T("Began flag file processing while processing was underway.")); this->DeleteExistingData(); // don't leak any existing data this->data = new FlagFileData(); this->proxyData = new ProxyFlagData(); wxString tcPath, exeName; wxFileName exeFilename; wxLogDebug(_T("Initializing flag file processing.")); if ( !ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath) ) { this->SetProcessingStatus(MISSING_TC); return; } if (!wxFileName::DirExists(tcPath)) { this->SetProcessingStatus(NONEXISTENT_TC); return; } if (!FSOExecutable::HasFSOExecutables(wxFileName(tcPath, wxEmptyString))) { this->SetProcessingStatus(INVALID_TC); return; } if ( !ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_BINARY, &exeName)) { this->SetProcessingStatus(MISSING_EXE); return; } #if IS_APPLE // needed because on OSX exeName is a relative path from TC root dir exeFilename.Assign(tcPath + wxFileName::GetPathSeparator() + exeName); #else exeFilename.Assign(tcPath, exeName); #endif wxLogDebug(_T("exeName: ") + exeName); wxLogDebug(_T("exeFilename: ") + exeFilename.GetFullPath()); if (!exeFilename.FileExists()) { this->SetProcessingStatus(INVALID_BINARY); return; } // Make sure that the directory that I am going to change to exists wxFileName tempExecutionLocation; tempExecutionLocation.AssignDir(GetProfileStorageFolder()); tempExecutionLocation.AppendDir(_T("temp_flag_folder")); if ( !tempExecutionLocation.DirExists() && !tempExecutionLocation.Mkdir() ) { wxLogError(_T("Unable to create flag folder at %s"), tempExecutionLocation.GetFullPath().c_str()); this->SetProcessingStatus(CANNOT_CREATE_FLAGFILE_FOLDER); return; } FlagFileArray flagFileLocations; flagFileLocations.Add(wxFileName(tcPath, _T("flags.lch"))); flagFileLocations.Add(wxFileName(tempExecutionLocation.GetFullPath(), _T("flags.lch"))); // remove potential flag files to eliminate any confusion. for( size_t i = 0; i < flagFileLocations.Count(); i++ ) { bool exists = flagFileLocations[i].FileExists(); if (exists) { ::wxRemoveFile(flagFileLocations[i].GetFullPath()); wxLogDebug(_T(" Cleaned up %s ... %s"), flagFileLocations[i].GetFullPath().c_str(), (flagFileLocations[i].FileExists())? _T("Failed") : _T("Removed")); } } wxString previousWorkingDir(::wxGetCwd()); // hopefully this doesn't goof anything up if ( !::wxSetWorkingDirectory(tempExecutionLocation.GetFullPath()) ) { wxLogError(_T("Unable to change working directory to %s"), tempExecutionLocation.GetFullPath().c_str()); this->SetProcessingStatus(CANNOT_CHANGE_WORKING_FOLDER); return; } wxString commandline; // use "" to correct for spaces in path to exeFilename if (exeFilename.GetFullPath().Find(_T(" ")) != wxNOT_FOUND) { commandline = _T("\"") + exeFilename.GetFullPath() + _T("\"") + _T(" -get_flags"); } else { commandline = exeFilename.GetFullPath() + _T(" -get_flags"); } wxLogDebug(_T(" Called FS2 Open with command line '%s'."), commandline.c_str()); FlagProcess *process = new FlagProcess(flagFileLocations); ::wxExecute(commandline, wxEXEC_ASYNC, process); if ( !::wxSetWorkingDirectory(previousWorkingDir) ) { wxLogError(_T("Unable to change back to working directory %s"), previousWorkingDir.c_str()); this->SetProcessingStatus(CANNOT_CHANGE_WORKING_FOLDER); return; } this->SetProcessingStatus(WAITING_FOR_FLAG_FILE); } wxString FlagListManager::GetStatusMessage() const { wxCHECK_MSG(!this->IsProcessingOK(), wxEmptyString, _T("status message requested, even though processing succeeded")); wxString msg; switch(this->GetProcessingStatus()) { case INITIAL_STATUS: msg = _("Waiting for flag file processing to be initialized."); break; case MISSING_TC: msg = _("No FS2 Open game has been selected.\n\nSelect the root folder of an FS2 Open game on the Basic Settings page."); break; case NONEXISTENT_TC: msg = _("The selected root folder does not exist.\n\nSelect a different FS2 Open game on the Basic Settings page."); break; case INVALID_TC: msg = wxString(_("The selected root folder does not contain any FS2 Open executables.\n\n")) + _("On the Basic Settings page, either select a different root folder, or add FS2 Open executables to the selected root folder and press the Refresh button."); break; case MISSING_EXE: msg = _("No FS2 Open executable has been selected.\n\nSelect an executable on the Basic Settings page."); break; case INVALID_BINARY: msg = _("The selected FS2 Open executable does not exist.\n\nSelect another on the Basic Settings page."); break; case WAITING_FOR_FLAG_FILE: msg = _("Waiting for flag file to be generated and parsed."); break; case FLAG_FILE_NOT_GENERATED: msg = _("The executable did not generate a flag file.\n\nMake sure that the executable is an FS2 Open executable."); break; case FLAG_FILE_NOT_VALID: msg = _("Generated flag file was not complete.\n\nPlease talk to a maintainer of this launcher, since you probably found a bug."); break; case FLAG_FILE_NOT_SUPPORTED: msg = _("Generated flag file is not supported.\n\nUpdate the launcher or talk to a maintainer of this launcher if you have the most recent version."); break; default: msg = wxString::Format( _("Unknown error (%d) occurred while obtaining the flag file from the FS2 Open executable."), this->GetProcessingStatus()); break; } return msg; } FlagFileData* FlagListManager::GetFlagFileData() { wxCHECK_MSG(this->IsProcessingOK(), NULL, _T("attempt to get flag file data even though processing hasn't succeeded")); wxCHECK_MSG(this->data != NULL, NULL, _T("attempt to get flag file data after it has already been retrieved")); FlagFileData* temp = this->data; this->data = NULL; return temp; } ProxyFlagData* FlagListManager::GetProxyFlagData() { wxCHECK_MSG(this->IsProcessingOK(), NULL, _T("attempt to get proxy flag data list even though processing hasn't succeeded")); wxCHECK_MSG(this->proxyData != NULL, NULL, _T("attempt to get proxy flag data list after it has already been retrieved")); ProxyFlagData* temp = this->proxyData; this->proxyData = NULL; return temp; } wxByte FlagListManager::GetBuildCaps() const { wxCHECK_MSG(this->IsProcessingOK(), 0, _T("attempt to get build caps even though processing hasn't succeeded")); return this->buildCaps; } FlagListManager::ProcessingStatus FlagListManager::ParseFlagFile(const wxFileName& flagfilename) { if (!flagfilename.FileExists()) { wxLogError(_T("The FS2 Open executable did not generate a flag file.")); return FLAG_FILE_NOT_GENERATED; } wxFile flagfile(flagfilename.GetFullPath()); wxLogDebug(_T("Reading flag file %s."), flagfilename.GetFullPath().c_str()); // Flagfile requires that we use 32 bit little-endian numbers wxInt32 easy_flag_size, flag_size, num_easy_flags, num_flags; size_t bytesRead; bytesRead = flagfile.Read(&easy_flag_size, sizeof(easy_flag_size)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(easy_flag_size) ) { wxLogError(_T(" Flag file is too short (failed to read easy_flag_size)")); return FLAG_FILE_NOT_VALID; } if ( easy_flag_size != 32 ) { wxLogError(_T(" Easy flag size (%d) is not supported"), easy_flag_size); return FLAG_FILE_NOT_SUPPORTED; } bytesRead = flagfile.Read(&flag_size, sizeof(flag_size)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(flag_size) ) { wxLogError(_T(" Flag file is too short (failed to read flag_size)")); return FLAG_FILE_NOT_VALID; } if ( flag_size != 344 ) { wxLogError(_T(" Exe flag structure (%d) size is not supported"), flag_size); return FLAG_FILE_NOT_SUPPORTED; } bytesRead = flagfile.Read(&num_easy_flags, sizeof(num_easy_flags)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(num_easy_flags) ) { wxLogError(_T(" Flag file is too short (failed to read num_easy_flags)")); return FLAG_FILE_NOT_VALID; } for ( int i = 0; i < num_easy_flags; i++ ) { char easy_flag[32]; bytesRead = flagfile.Read(&easy_flag, sizeof(easy_flag)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(easy_flag) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (easy_flag)"), sizeof(easy_flag), bytesRead); return FLAG_FILE_NOT_VALID; } wxString easyFlagStr(easy_flag, wxConvUTF8, strlen(easy_flag)); this->data->AddEasyFlag(easyFlagStr); } bytesRead = flagfile.Read(&num_flags, sizeof(num_flags)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(num_flags) ) { wxLogError(_T(" Flag file is too short (failed to read num_flags)")); return FLAG_FILE_NOT_VALID; } for ( int i = 0; i < num_flags; i++ ) { char flag_string[20]; char description[40]; wxInt32 fso_only, easy_on_flags, easy_off_flags; char easy_catagory[16], web_url[256]; bytesRead = flagfile.Read(&flag_string, sizeof(flag_string)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(flag_string) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (flag_string)"), sizeof(flag_string), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&description, sizeof(description)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(description) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (description)"), sizeof(description), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&fso_only, sizeof(fso_only)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(fso_only) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (fso_only)"), sizeof(fso_only), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&easy_on_flags, sizeof(easy_on_flags)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(easy_on_flags) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (easy_on_flags)"), sizeof(easy_on_flags), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&easy_off_flags, sizeof(easy_off_flags)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(easy_off_flags) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (easy_off_flags)"), sizeof(easy_off_flags), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&easy_catagory, sizeof(easy_catagory)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(easy_catagory) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (easy_category)"), sizeof(easy_catagory), bytesRead); return FLAG_FILE_NOT_VALID; } bytesRead = flagfile.Read(&web_url, sizeof(web_url)); if ( (size_t)wxInvalidOffset == bytesRead || bytesRead != sizeof(web_url) ) { wxLogError(_T(" Flag file is too short, expected %d, got %d bytes (web_url)"), sizeof(web_url), bytesRead); return FLAG_FILE_NOT_VALID; } flag_string[sizeof(flag_string)-1] = _T('\0'); description[sizeof(description)-1] = _T('\0'); easy_catagory[sizeof(easy_catagory)-1] = _T('\0'); web_url[sizeof(web_url)-1] = _T('\0'); Flag* flag = new Flag(); flag->flagString = wxString(flag_string, wxConvUTF8, strlen(flag_string)); flag->shortDescription = wxString(description, wxConvUTF8, strlen(description)); flag->webURL = wxString(web_url, wxConvUTF8, strlen(web_url)); flag->fsoCatagory = wxString(easy_catagory, wxConvUTF8, strlen(easy_catagory)); flag->isRecomendedFlag = false; // much better from a UI point of view than "true" flag->easyEnable = easy_on_flags; flag->easyDisable = easy_off_flags; this->data->AddFlag(flag); } wxLogDebug(_T(" easy_flag_size: %d, %d; flag_size: %d, %d; num_easy_flags: %d, %d; num_flags: %d, %d"), easy_flag_size, sizeof(easy_flag_size), flag_size, sizeof(flag_size), num_easy_flags, sizeof(num_easy_flags), num_flags, sizeof(num_flags)); // build capabilities, which are needed for supporting the new sound code wxByte buildCaps; bytesRead = flagfile.Read(&buildCaps, sizeof(buildCaps)); if ( (size_t)wxInvalidOffset == bytesRead ) { wxLogInfo(_T(" Old build that does not output its capabilities, must not support OpenAL")); buildCaps = 0; } this->buildCaps = buildCaps; this->data->GenerateFlagSets(); this->proxyData = this->data->GenerateProxyFlagData(); return PROCESSING_OK; } void FlagListManager::SetProcessingStatus(const ProcessingStatus& processingStatus) { this->processingStatus = processingStatus; wxLogDebug(_T("current flag file processing status: %d"), processingStatus); this->GenerateFlagFileProcessingStatusChanged(this->GetFlagFileProcessingStatus()); } FlagListManager::FlagFileProcessingStatus FlagListManager::GetFlagFileProcessingStatus() const { const ProcessingStatus& processingStatus = this->GetProcessingStatus(); if (processingStatus == PROCESSING_OK) { return FlagListManager::FLAG_FILE_PROCESSING_OK; } else if (processingStatus == WAITING_FOR_FLAG_FILE) { return FlagListManager::FLAG_FILE_PROCESSING_WAITING; } else if (processingStatus == INITIAL_STATUS) { return FlagListManager::FLAG_FILE_PROCESSING_RESET; } else { return FlagListManager::FLAG_FILE_PROCESSING_ERROR; } } FlagListManager::FlagProcess::FlagProcess(FlagFileArray flagFileLocations) : flagFileLocations(flagFileLocations) { } void FlagListManager::FlagProcess::OnTerminate(int pid, int status) { wxLogDebug(_T(" FS2 Open returned %d when polled for the flags"), status); // Find the flag file wxFileName flagfile; for( size_t i = 0; i < flagFileLocations.Count(); i++ ) { bool exists = flagFileLocations[i].FileExists(); if (exists) { flagfile = flagFileLocations[i]; wxLogDebug(_T(" Searching for flag file at %s ... %s"), flagFileLocations[i].GetFullPath().c_str(), (flagFileLocations[i].FileExists())? _T("Located") : _T("Not Here")); } } if ( !flagfile.FileExists() ) { FlagListManager::GetFlagListManager()->SetProcessingStatus(FLAG_FILE_NOT_GENERATED); wxLogError(_T(" FS2 Open did not generate a flag file.")); return; } FlagListManager::GetFlagListManager()->SetProcessingStatus( FlagListManager::GetFlagListManager()->ParseFlagFile(flagfile)); if ( FlagListManager::GetFlagListManager()->IsProcessingOK() ) { ::wxRemoveFile(flagfile.GetFullPath()); } delete this; } wxlauncher-0.9.4/code/apis/FlagListManager.h0000644000000000000000000001013012155177255020666 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FLAGLISTMANAGER_H #define FLAGLISTMANAGER_H #include #include #include #include "datastructures/FlagFileData.h" /** Flag file processing status has changed. The event's int value indicates the FlagFileProcessingStatus. */ DECLARE_EVENT_TYPE(EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, FlagFileProcessingEventHandlers); WX_DECLARE_OBJARRAY(wxFileName, FlagFileArray); class FlagListManager: public wxEvtHandler { public: static bool Initialize(); static void DeInitialize(); static bool IsInitialized(); static FlagListManager* GetFlagListManager(); ~FlagListManager(); /** FlagFileProcessingStatus is a high-level description of the status of flag file processing. */ enum FlagFileProcessingStatus { FLAG_FILE_PROCESSING_OK = 0, FLAG_FILE_PROCESSING_WAITING, FLAG_FILE_PROCESSING_RESET, FLAG_FILE_PROCESSING_ERROR }; void OnBinaryChanged(wxCommandEvent &event); static void RegisterFlagFileProcessingStatusChanged(wxEvtHandler *handler); static void UnRegisterFlagFileProcessingStatusChanged(wxEvtHandler *handler); void BeginFlagFileProcessing(); /** Returns true when the flag file processing has succeeded, false otherwise. */ inline bool IsProcessingOK() const { return (this->processingStatus == PROCESSING_OK); } /** Returns the message to display when processing has not (yet) succeded. This function should not called when processing has succeeded. */ wxString GetStatusMessage() const; /** Returns the extracted data from the flag file. Should only be called when processing succeeds and only once per flag file processed. */ FlagFileData* GetFlagFileData(); /** Returns the extracted data in a form suitable for use by the profile proxy. Should only be called when processing succeeds and only once per flag file processed. */ ProxyFlagData* GetProxyFlagData(); /** Gets the build capabilities of the currently selected FSO executable. Should only be called when processing succeeds. */ wxByte GetBuildCaps() const; private: FlagListManager(); void DeleteExistingData(); static FlagListManager* flagListManager; static FlagFileProcessingEventHandlers ffProcessingStatusChangedHandlers; static void GenerateFlagFileProcessingStatusChanged(const FlagFileProcessingStatus& status); /** ProcessingStatus is a low-level description of the status of flag file processing. */ enum ProcessingStatus { PROCESSING_OK = 0, INITIAL_STATUS, MISSING_TC, NONEXISTENT_TC, INVALID_TC, MISSING_EXE, INVALID_BINARY, WAITING_FOR_FLAG_FILE, FLAG_FILE_NOT_GENERATED, FLAG_FILE_NOT_VALID, FLAG_FILE_NOT_SUPPORTED, CANNOT_CREATE_FLAGFILE_FOLDER, CANNOT_CHANGE_WORKING_FOLDER, MAX_PROCESSINGSTATUS }; ProcessingStatus processingStatus; //!< has processing succeeded ProcessingStatus ParseFlagFile(const wxFileName& flagfile); void SetProcessingStatus(const ProcessingStatus& processingStatus); inline const ProcessingStatus& GetProcessingStatus() const { return this->processingStatus; } FlagFileProcessingStatus GetFlagFileProcessingStatus() const; FlagFileData* data; ProxyFlagData* proxyData; wxByte buildCaps; class FlagProcess: public wxProcess { public: FlagProcess(FlagFileArray flagFileLocations); virtual void OnTerminate(int pid, int status); private: FlagFileArray flagFileLocations; }; DECLARE_EVENT_TABLE() }; #endif wxlauncher-0.9.4/code/apis/HelpManager.cpp0000644000000000000000000001051112155177255020407 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/HelpManager.h" #include "generated/configure_launcher.h" #include #include #include "global/ids.h" #include "global/MemoryDebugging.h" using namespace HelpManager; namespace HelpManager { struct helpLink { WindowIDS id; wxString location; }; helpLink helpLinks[] = { #include "generated/helplinks.cpp" }; bool initialized = false; wxHtmlHelpController *controller = NULL; size_t numberOfHelpLinks = sizeof(HelpManager::helpLinks)/sizeof(HelpManager::helpLink); class ExternLinkHandler: public wxEvtHandler { public: void LinkClicked(wxHtmlLinkEvent &event); }; ExternLinkHandler* externLinkHandler; class HtmlHelpController: public wxHtmlHelpController { public: virtual wxHtmlHelpFrame* CreateHelpFrame(wxHtmlHelpData *data); }; }; bool HelpManager::Initialize() { if (HelpManager::IsInitialized()) { return false; } externLinkHandler = new ExternLinkHandler(); controller = new HtmlHelpController(); wxFileName file(_T(HELP_HTB_LOCATION)); if ( file.FileExists() ) { controller->AddBook(file, false); } else { ::wxLogWarning(_T("Unable to find help file %s"), file.GetFullName().c_str()); delete controller; controller = NULL; return false; } HelpManager::initialized = true; return true; } bool HelpManager::DeInitialize() { if ( HelpManager::IsInitialized()) { HelpManager::initialized = false; delete controller; delete externLinkHandler; } return true; } bool HelpManager::IsInitialized() { return HelpManager::initialized; } void HelpManager::OpenHelpById(WindowIDS id) { wxCHECK_RET( HelpManager::IsInitialized(), _("Help manager is not initialized")); // find id if it exists for (size_t i = 0; i < HelpManager::numberOfHelpLinks; i++) { if (HelpManager::helpLinks[i].id == id) { HelpManager::controller->Display( HelpManager::helpLinks[i].location); return; } } ::wxLogInfo(_T(" ID %d does not have more specific help"), id); } void HelpManager::OpenMainHelpPage() { wxCHECK_RET( HelpManager::IsInitialized(), _T("Help is not initialized")); // Hacky way to get the help tree to expand wxString faq(_T("jfaq.htm")); wxString index(_T("index.htm")); HelpManager::OpenHelpByString(faq); HelpManager::OpenHelpByString(index); } /** Opens the help file passing str to the help controller. The help controller tries to find a page in the manual in 4 ways: \li as a direct filename of the document in the manual \li as a chapter name (based on the page title displayed in the contents) \li a word from the index \li any word (will open search pane and do a search) \note Capitalization matters. */ void HelpManager::OpenHelpByString(wxString& str) { wxCHECK_RET( HelpManager::IsInitialized(), _("Help manager is not initialized")); HelpManager::controller->Display(str); } void HelpManager::ExternLinkHandler::LinkClicked(wxHtmlLinkEvent &event) { wxHtmlLinkInfo info(event.GetLinkInfo()); wxString href(info.GetHref()); if ( href.StartsWith(_T("http://")) ) { ::wxLaunchDefaultBrowser(href); } else { event.Skip(); // not external, so I don't want it. } } wxHtmlHelpFrame* HelpManager::HtmlHelpController::CreateHelpFrame(wxHtmlHelpData *data) { wxHtmlHelpFrame* frame = new wxHtmlHelpFrame(data); frame->SetController(this); frame->Create(m_parentWindow, -1, wxEmptyString, m_FrameStyle, m_Config, m_ConfigRoot); frame->SetTitleFormat(m_titleFormat); frame->GetEventHandler() // hook in to allow veto on the links so we can open external browsers ->Connect(wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler(ExternLinkHandler::LinkClicked), NULL, HelpManager::externLinkHandler); m_helpFrame = frame; return frame; } wxlauncher-0.9.4/code/apis/HelpManager.h0000644000000000000000000000177312155177255020066 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef HELPMANAGER_H #define HELPMANAGER_H #include #include "global/ids.h" namespace HelpManager { bool Initialize(); bool DeInitialize(); bool IsInitialized(); void OpenHelpById(WindowIDS id); void OpenMainHelpPage(); void OpenHelpByString(wxString& str); }; #endif wxlauncher-0.9.4/code/apis/JoystickManager.cpp0000644000000000000000000001544712155177255021333 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "apis/JoystickManager.h" #include "generated/configure_launcher.h" #if USE_JOYSTICK && HAS_SDL #include "SDL.h" #endif #include "global/BasicDefaults.h" #include "global/MemoryDebugging.h" namespace JoyMan { #if USE_JOYSTICK bool isInitialized = false; wxArrayString joysticks; unsigned int numOfJoysticks = 0; //!< number of plugged in joysticks #endif }; /** \namespace JoyMan The JoyMan namespace contains helper functions for working with joysticks on the compiled platform. The interface of this namespace is always available but will only work if preprocessor symbol USE_JOYSTICK is set to 1, and of course if the platform supports it. \sa JoyMan::WasCompiledIn(); */ using namespace JoyMan; /** \return true when JoyMan is ready to accept queries about joysticks. \note Always returns false when JoyMan is not compiled in. \sa JoyMan::WasCompiledIn() */ bool JoyMan::IsInitialized() { #if USE_JOYSTICK return isInitialized; #else return false; #endif } /** Gets JoyMan ready to manage joysticks. \return true if successful, false otherwise. \note Will also return false if JoyMan is not compiled in. \sa JoyMan::WasCompiledIn() */ bool JoyMan::Initialize() { if ( JoyMan::IsInitialized() ) { wxLogDebug(_T("JoyMan already initialized with %d joysticks"), joysticks.Count()); return true; } #if USE_JOYSTICK && IS_WIN32 UINT num = joyGetNumDevs(); // get the number of joys supported by windows. if ( num > 16 ) { /* greater than 16 is cause for a warning because according to MSDN, Windows 2000 and later support -1 thru 16 in the MM api (and windows NT only supports 1 or 2). MSDN also notes if you want more than 16 use DirectInput. */ wxLogWarning(_T("Windows reports that the joystick driver ") _T("supports more than 16 joysticks (reports %d)!"), num); } MMRESULT result = JOYERR_NOERROR; JOYINFO joyinfo; JOYCAPS joycaps; JoyMan::numOfJoysticks = 0; JoyMan::joysticks.clear(); int totalNumberOfJoysticks = 0; for (UINT counter = 0; counter < num; counter++) { memset(reinterpret_cast(&joyinfo), 0, sizeof(JOYINFO)); result = joyGetPos(counter, &joyinfo); wxString joystickName; if ( result == JOYERR_NOERROR ) { // joystick plugged in totalNumberOfJoysticks++; memset(reinterpret_cast(&joycaps), 0, sizeof(JOYCAPS)); result = joyGetDevCaps(counter, &joycaps, sizeof(JOYCAPS)); if ( result == JOYERR_NOERROR ) { numOfJoysticks++; joystickName = wxString(joycaps.szPname, wxMBConvUTF16()); joysticks.Add(joystickName); } else { wxLogError(_T("Error in retrieving joystick caps")); continue; } } else if ( result == JOYERR_UNPLUGGED ) { // unplugged totalNumberOfJoysticks++; } else { // Joystick doesn't exist, do nothing } } wxLogInfo(_T("Windows reports %d joysticks, %d seem to be plugged in."), totalNumberOfJoysticks, numOfJoysticks ); return true; #elif USE_JOYSTICK && HAS_SDL SDL_InitSubSystem(SDL_INIT_JOYSTICK); JoyMan::numOfJoysticks = 0; JoyMan::joysticks.clear(); SDL_Joystick *joy = NULL; for (int i = 0; i < SDL_NumJoysticks(); i++) { joy = SDL_JoystickOpen(i); if ( joy != NULL ) { wxString joystickName(SDL_JoystickName(i), wxConvLocal); joysticks.Add(joystickName); } SDL_JoystickClose(joy); } return true; #else return false; #endif } /** Dismantles JoyMan and frees any memory used. \note Will always return true when JoyMan is not compiled in. \return Return true if DeInitializtion was successful. */ bool JoyMan::DeInitialize() { #if USE_JOYSTICK if ( JoyMan::IsInitialized() ) { JoyMan::isInitialized = false; JoyMan::numOfJoysticks = 0; JoyMan::joysticks.clear(); } #endif return true; } /** \return true if the internals of JoyMan were compiled into the launcher. */ bool JoyMan::WasCompiledIn() { #if USE_JOYSTICK return true; #else return false; #endif } /** \return number of joysticks that the system reports as existing. \sa JoyMan::IsPluggedIn() */ unsigned int JoyMan::NumberOfJoysticks() { #if USE_JOYSTICK return JoyMan::joysticks.size(); #else return 0; #endif } /** \return number of joystsicks that are plugged in. \sa JoyMan::NumberOfJoysticks() \sa JoyMan::IsPluggedIn() */ unsigned int JoyMan::NumberOfPluggedInJoysticks() { unsigned int total = JoyMan::NumberOfJoysticks(); unsigned int pluggedIn = 0; for ( unsigned int i = 0; i < total; i++) { if ( JoyMan::IsJoystickPluggedIn(i) ) { pluggedIn++; } } return pluggedIn; } /** \bug Assumes all joysticks support ForceFeedback */ #if USE_JOYSTICK bool JoyMan::SupportsForceFeedback(unsigned int i) { if ( i == static_cast(DEFAULT_JOYSTICK_ID) ) { return false; } else { return true; } #else bool JoyMan::SupportsForceFeedback(unsigned int) { return false; #endif } /** \bug Assumes all joysticks have a calibrate tool. */ #if USE_JOYSTICK bool JoyMan::HasCalibrateTool(unsigned int i) { if ( i == static_cast(DEFAULT_JOYSTICK_ID) ) { return false; } else { return true; } #else bool JoyMan::HasCalibrateTool(unsigned int) { return false; #endif } /** Launch the windows joystick calibration tool. */ void JoyMan::LaunchCalibrateTool(unsigned int WXUNUSED(i)) { #if IS_WIN32 // the same call that the current window launcher uses to open the // calibration tool. Obviously it only works on windows :) WinExec("rundll32.exe shell32.dll,Control_RunDLL joy.cpl", SW_SHOWNORMAL); #endif } /** Returns the name of the joystick for display. */ #if USE_JOYSTICK wxString JoyMan::JoystickName(unsigned int i) { return JoyMan::joysticks[i]; #else wxString JoyMan::JoystickName(unsigned int) { return wxEmptyString; #endif } /** Returns true when the joystick is plugged in. */ #if USE_JOYSTICK bool JoyMan::IsJoystickPluggedIn(unsigned int i) { if ( i == static_cast(DEFAULT_JOYSTICK_ID) ) { return false; } else { // FIXME because we're using indexes to represent joysticks, there's no guarantee // that an index value always refers to the same joystick return i >= JoyMan::joysticks.GetCount() ? false : !JoyMan::joysticks[i].IsEmpty(); } #else bool JoyMan::IsJoystickPluggedIn(unsigned int) { return false; #endif } wxlauncher-0.9.4/code/apis/JoystickManager.h0000644000000000000000000000227412155177255020772 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef JOYSTICKMANAGER_H #define JOYSTICKMANAGER_H #include namespace JoyMan { bool Initialize(); bool DeInitialize(); bool IsInitialized(); bool WasCompiledIn(); unsigned int NumberOfJoysticks(); unsigned int NumberOfPluggedInJoysticks(); bool SupportsForceFeedback(unsigned int i); bool HasCalibrateTool(unsigned int i); void LaunchCalibrateTool(unsigned int i); wxString JoystickName(unsigned int i); bool IsJoystickPluggedIn(unsigned int i); }; #endif wxlauncher-0.9.4/code/apis/OpenALManager.cpp0000644000000000000000000003344512155177255020650 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "generated/configure_launcher.h" #include "apis/FlagListManager.h" #include "apis/OpenALManager.h" #include "apis/ProfileManager.h" #include "global/ProfileKeys.h" #if USE_OPENAL #include #include #endif #include "global/MemoryDebugging.h" // from FSO, code/sound/openal.cpp, SVN r8840 // enumeration extension #ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER #define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 #endif #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif const wxByte BUILD_CAP_NEW_SND = 1<<2; #if USE_OPENAL namespace OpenALMan { wxDynamicLibrary OpenALLib; bool isInitialized = false; }; using namespace OpenALMan; #endif bool OpenALMan::Initialize() { #if USE_OPENAL if ( isInitialized ) { return true; #if IS_APPLE } else if ( OpenALLib.Load(_T("/System/Library/Frameworks/OpenAL.framework/OpenAL"), wxDL_VERBATIM) ) { isInitialized = true; return true; } else if ( OpenALLib.Load(_T("/Library/Frameworks/OpenAL.framework/OpenAL"), wxDL_VERBATIM) ) { isInitialized = true; return true; #else } else if ( OpenALLib.Load(_T("OpenAL32")) ) { isInitialized = true; return true; } else if ( OpenALLib.Load(_T("libopenal")) ) { isInitialized = true; return true; } else if ( OpenALLib.Load(_T("OpenAL")) ) { isInitialized = true; return true; #endif } else { return false; } #else return false; #endif } bool OpenALMan::DeInitialize() { #if USE_OPENAL OpenALLib.Unload(); return true; #else return false; #endif } bool OpenALMan::IsInitialized() { #if USE_OPENAL return isInitialized; #else return false; #endif } bool OpenALMan::WasCompiledIn() { #if USE_OPENAL return true; #else return false; #endif } #if USE_OPENAL typedef const ALCchar* (ALC_APIENTRY *alcGetStringType)(ALCdevice*, ALenum); typedef ALCboolean (ALC_APIENTRY *alcIsExtensionPresentType)(ALCdevice*, const ALchar*); typedef const ALchar* (AL_APIENTRY *alGetStringType)(ALenum); typedef ALenum (AL_APIENTRY *alGetErrorType)(void); typedef ALCdevice* (ALC_APIENTRY *alcOpenDeviceType)(const ALCchar *); typedef ALCboolean (ALC_APIENTRY *alcCloseDeviceType)(ALCdevice *); typedef ALCcontext* (ALC_APIENTRY *alcCreateContextType)(const ALCdevice*, const ALCint*); typedef ALCboolean (ALC_APIENTRY *alcMakeContextCurrentType)(ALCcontext*); typedef void (ALC_APIENTRY *alcDestroyContextType)(ALCcontext*); namespace OpenALMan { template< typename funcPtrType> funcPtrType GetOpenALFunctionPointer(const wxString& name, size_t line); bool checkForALError_(size_t line); }; #define ___GetOALFuncPtr(type, name, line) GetOpenALFunctionPointer(_T(name), line) #define GetOALFuncPtr(type, name) ___GetOALFuncPtr(type, #name, __LINE__) #define checkForALError() OpenALMan::checkForALError_(__LINE__) template< typename funcPtrType> funcPtrType OpenALMan::GetOpenALFunctionPointer(const wxString& name, size_t line) { if ( !OpenALLib.HasSymbol(name) ) { wxLogError( _T("OpenAL does not have %s() for function containing line %d"), name.c_str(), line); return NULL; } funcPtrType pointer = NULL; pointer = reinterpret_cast( OpenALLib.GetSymbol(name)); if ( pointer == NULL ) { wxLogError(_T("Unable to get %s() function from OpenAL, even though it apparently exists for function containing line %d"), name.c_str(), line); return NULL; } return pointer; } bool OpenALMan::checkForALError_(size_t line) { alGetErrorType getError = GetOALFuncPtr(alGetErrorType, alGetError); if ( getError == NULL ) { return false; } ALenum errorcode = (*getError)(); if ( errorcode == AL_NO_ERROR ) { return true; } else if ( errorcode == AL_INVALID_NAME ) { wxLogError(_T("OpenAL:%d: a bad name (ID) was passed to an OpenAL function"), line); } else if ( errorcode == AL_INVALID_ENUM ) { wxLogError(_T("OpenAL:%d: an invalid enum value was passed to an OpenAL function"), line); } else { wxLogError(_T("OpenAL:%d: Unknown error number 0x%08x"), line, errorcode); } #if PLATFORM_HAS_BROKEN_OPENAL == 1 /** \todo a hack to fix certain OpenAL implementations that are not clearing the errors correctly. */ return true; #else return false; #endif } #endif wxArrayString GetAvailableDevices(const ALenum deviceType) { wxArrayString arr; #if USE_OPENAL wxCHECK_MSG(OpenALMan::IsInitialized(), arr, _T("GetAvailableDevices called but OpenALMan not initialized")); wxCHECK_MSG(deviceType == ALC_DEVICE_SPECIFIER || deviceType == ALC_CAPTURE_DEVICE_SPECIFIER, arr, wxString::Format(_T("GetAvailableDevices given invalid specifier %d"), deviceType)); ALenum adjustedDeviceType = deviceType; alcIsExtensionPresentType isExtensionPresent = GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent); if ( isExtensionPresent != NULL ) { if ( (*isExtensionPresent)(NULL, "ALC_ENUMERATION_EXT") != AL_TRUE ) { wxLogFatalError(_T("OpenAL does not seem to support device enumeration.")); return arr; } } else { return arr; } if ((deviceType == ALC_DEVICE_SPECIFIER) && (*isExtensionPresent)(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE) { adjustedDeviceType = ALC_ALL_DEVICES_SPECIFIER; } alcGetStringType GetString = GetOALFuncPtr(alcGetStringType, alcGetString); if ( GetString != NULL ) { const ALCchar* devices = (*GetString)(NULL, adjustedDeviceType); if ( devices != NULL ) { size_t len; size_t offset = 0; do { len = strlen(devices+offset); if ( len > 0 ) { wxString device(devices+offset, wxConvUTF8); arr.Add(device); } offset += len+1; } while ( len != 0 ); } else { wxLogError(_T("OpenAL gave NULL for list of devices.")); return arr; } } else { return arr; } #endif return arr; } wxArrayString OpenALMan::GetAvailablePlaybackDevices() { #if USE_OPENAL wxCHECK_MSG(OpenALMan::IsInitialized(), wxArrayString(), _T("GetAvailablePlaybackDevices called but OpenALMan not initialized")); return GetAvailableDevices(ALC_DEVICE_SPECIFIER); #else return wxArrayString(); #endif } wxArrayString OpenALMan::GetAvailableCaptureDevices() { #if USE_OPENAL wxCHECK_MSG(OpenALMan::IsInitialized(), wxArrayString(), _T("GetAvailableCaptureDevices called but OpenALMan not initialized")); return GetAvailableDevices(ALC_CAPTURE_DEVICE_SPECIFIER); #else return wxArrayString(); #endif } wxString GetSystemDefaultDevice(const ALenum deviceType) { #if USE_OPENAL wxCHECK_MSG( OpenALMan::IsInitialized(), wxEmptyString, _T("GetSystemDefaultDevice called but OpenALMan not initialized")); wxCHECK_MSG(deviceType == ALC_DEFAULT_DEVICE_SPECIFIER || deviceType == ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER, wxEmptyString, wxString::Format(_T("GetSystemDefaultDevice given invalid specifier %d"), deviceType)); ALenum adjustedDeviceType = deviceType; alcIsExtensionPresentType isExtensionPresent = GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent); if ((isExtensionPresent != NULL) && (deviceType == ALC_DEFAULT_DEVICE_SPECIFIER) && ((*isExtensionPresent)(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE)) { adjustedDeviceType = ALC_DEFAULT_ALL_DEVICES_SPECIFIER; } alcGetStringType GetString = GetOALFuncPtr(alcGetStringType, alcGetString); if ( GetString == NULL ) { return wxEmptyString; } else { const ALCchar* defaultDevice = (*GetString)(NULL, adjustedDeviceType); if ( defaultDevice == NULL ) { wxLogError(_("Unable to get system default OpenAL %sdevice"), (deviceType == ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER ? _T("capture ") : wxEmptyString)); return wxEmptyString; } else { return wxString(defaultDevice, wxConvUTF8); } } #else return wxEmptyString; #endif } wxString OpenALMan::GetSystemDefaultPlaybackDevice() { #if USE_OPENAL wxCHECK_MSG(OpenALMan::IsInitialized(), wxEmptyString, _T("GetSystemDefaultPlaybackDevice called but OpenALMan not initialized")); return GetSystemDefaultDevice(ALC_DEFAULT_DEVICE_SPECIFIER); #else return wxEmptyString; #endif } wxString OpenALMan::GetSystemDefaultCaptureDevice() { #if USE_OPENAL wxCHECK_MSG(OpenALMan::IsInitialized(), wxEmptyString, _T("GetSystemDefaultCaptureDevice called but OpenALMan not initialized")); return GetSystemDefaultDevice(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); #else return wxEmptyString; #endif } wxString OpenALMan::GetCurrentVersion() { #if USE_OPENAL alGetStringType GetString = GetOALFuncPtr(alGetStringType,alGetString); if ( GetString == NULL ) { return _("Unknown version"); } wxString selectedDevice; ProMan::GetProfileManager()->ProfileRead(PRO_CFG_OPENAL_DEVICE, &selectedDevice); // clear errors, I have not done any openAL stuff, so make sure that any // errors that are active are because of me. checkForALError(); alcOpenDeviceType OpenDevice = GetOALFuncPtr(alcOpenDeviceType,alcOpenDevice); if ( OpenDevice == NULL || checkForALError() == false) { return _("Unable to open device"); } ALCdevice* device = (*OpenDevice)(selectedDevice.char_str()); if ( device == NULL || checkForALError() == false ) { wxLogError(_T("alcOpenDevice returned NULL for selected device '%s'"), selectedDevice.c_str()); return _("Error opening device"); } alcCreateContextType CreateContext = GetOALFuncPtr(alcCreateContextType,alcCreateContext); if ( CreateContext == NULL || checkForALError() == false ) { return _("Unable to open context on device"); } ALCint attributes = 0; ALCcontext* context = (*CreateContext)(device,NULL); if ( context == NULL || checkForALError() == false) { return _("Error in opening context"); } alcMakeContextCurrentType MakeContextCurrent = GetOALFuncPtr(alcMakeContextCurrentType,alcMakeContextCurrent); if ( MakeContextCurrent == NULL || checkForALError() == false) { return _("Unable to set context as current"); } if ( (*MakeContextCurrent)(context) != ALC_TRUE || checkForALError() == false ) { return _("Error in setting context as current"); } const ALCchar* version = (*GetString)(AL_VERSION); if ( !checkForALError() || version == NULL ) { wxLogError(_T("OpenAL: Unable to retrieve Version String")); return _("Unknown version"); } wxString Version(version, wxConvUTF8); // unset the current context (*MakeContextCurrent)(NULL); alcDestroyContextType DestroyContext = GetOALFuncPtr(alcDestroyContextType,alcDestroyContext); if ( DestroyContext == NULL ) { return _("Unable to destroy context"); } (*DestroyContext)(context); context = NULL; if ( checkForALError() == false ) { return _("Error in destroying context"); } alcCloseDeviceType CloseDevice = GetOALFuncPtr(alcCloseDeviceType,alcCloseDevice); if ( CloseDevice == NULL ) { return _("Unable to close device"); } (*CloseDevice)(device); if ( checkForALError() == false ) { return _("Error in closing device"); } return wxString::Format(_("Detected OpenAL version: %s"), Version.c_str()); #else return wxEmptyString; #endif } // bits are adapted from GetCurrentVersion() and FSO, sound/ds.cpp, ds_init() bool OpenALMan::IsEFXSupported(const wxString& playbackDeviceName) { #if USE_OPENAL wxCHECK_MSG( OpenALMan::IsInitialized(), false, _T("IsEFXSupported called but OpenALMan not initialized")); if (playbackDeviceName.IsEmpty()) { wxLogError(_T("IsEFXSupported: playback device name is empty")); return false; } // clear errors, I have not done any openAL stuff, so make sure that any // errors that are active are because of me. checkForALError(); alcOpenDeviceType OpenDevice = GetOALFuncPtr(alcOpenDeviceType, alcOpenDevice); if (OpenDevice == NULL || checkForALError() == false) { wxLogError(_T("IsEFXSupported: Unable to open device.")); return false; } ALCdevice* playbackDevice = (*OpenDevice)(playbackDeviceName.char_str()); if (playbackDevice == NULL || checkForALError() == false) { wxLogError( _T("IsEFXSupported: alcOpenDevice returned NULL when opening device '%s'"), playbackDeviceName.c_str()); return false; } alcIsExtensionPresentType isExtensionPresent = GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent); if (isExtensionPresent == NULL || checkForALError() == false) { wxLogError( _T("IsEFXSupported: Could not get alcIsExtensionPresent function.")); return false; } bool hasEFX = (*isExtensionPresent)(playbackDevice, "ALC_EXT_EFX") == AL_TRUE; if (checkForALError() == false) { wxLogError(_T("IsEFXSupported: Error in checking for EFX extension")); return false; } alcCloseDeviceType CloseDevice = GetOALFuncPtr(alcCloseDeviceType, alcCloseDevice); if (CloseDevice == NULL) { wxLogError(_T("IsEFXSupported: Unable to close device.")); return false; } (*CloseDevice)(playbackDevice); if (checkForALError() == false) { wxLogError(_T("IsEFXSupported: Error in closing device")); return false; } return hasEFX; #else return false; #endif } bool OpenALMan::BuildHasNewSoundCode() { wxCHECK_MSG(OpenALMan::IsInitialized(), false, _T("OpenALMan has not been initialized.")); wxCHECK_MSG(FlagListManager::IsInitialized(), false, _T("FlagListManager has not been initialized.")); wxCHECK_MSG(FlagListManager::GetFlagListManager()->IsProcessingOK(), false, _T("Flag file processing has not (yet) succeeded.")); return FlagListManager::GetFlagListManager()->GetBuildCaps() & BUILD_CAP_NEW_SND; } wxlauncher-0.9.4/code/apis/OpenALManager.h0000644000000000000000000000252412155177255020307 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef OPENALMANAGER #define OPENALMANAGER #include #if wxUSE_DYNLIB_CLASS == 0 && USE_OPENAL == 1 #error Cannot compile OPENAL into launcher if wxWidgets has wxUSE_DYNLIB_CLASS set to 0 #endif namespace OpenALMan { bool Initialize(); bool DeInitialize(); bool WasCompiledIn(); bool IsInitialized(); wxArrayString GetAvailablePlaybackDevices(); wxArrayString GetAvailableCaptureDevices(); wxString GetCurrentVersion(); wxString GetSystemDefaultPlaybackDevice(); wxString GetSystemDefaultCaptureDevice(); bool IsEFXSupported(const wxString& playbackDeviceName); bool BuildHasNewSoundCode(); }; #endif wxlauncher-0.9.4/code/apis/PlatformProfileManager.h0000644000000000000000000000221112155177255022267 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLATFORMPROFILEMANAGER_H #define PLATFORMPROFILEMANAGER_H #include #include "apis/ProfileManager.h" ProMan::RegistryCodes RegistryPushProfile(wxFileConfig *cfg); ProMan::RegistryCodes RegistryPullProfile(wxFileConfig *cfg); ProMan::RegistryCodes FilePushProfile(wxFileConfig *cfg); ProMan::RegistryCodes FilePullProfile(wxFileConfig *cfg); ProMan::RegistryCodes PushCmdlineFSO(wxFileConfig *cfg); #endifwxlauncher-0.9.4/code/apis/PlatformProfileManagerShared.cpp0000644000000000000000000000777712155177255023777 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "generated/configure_launcher.h" #include "apis/PlatformProfileManager.h" #include "controls/LightingPresets.h" #include "global/ProfileKeys.h" ProMan::RegistryCodes PushCmdlineFSO(wxFileConfig *cfg) { wxString modLine, flagLine, tcPath; cfg->Read(PRO_CFG_TC_CURRENT_MODLINE, &modLine); cfg->Read(PRO_CFG_TC_CURRENT_FLAG_LINE, &flagLine); cfg->Read(PRO_CFG_TC_ROOT_FOLDER, &tcPath); wxString presetName; wxString lightingPresetFlagSet; if (cfg->Read(PRO_CFG_LIGHTING_PRESET, &presetName)) { lightingPresetFlagSet = LightingPresets::PresetNameToPresetFlagSet(presetName); } wxString cmdLineString; #if IS_LINUX // write to folder in home dir extern wxFileName GetPlatformDefaultConfigFilePath(); cmdLineString += GetPlatformDefaultConfigFilePath().GetFullPath().c_str(); #else cmdLineString += tcPath.c_str(); cmdLineString += wxFileName::GetPathSeparator(); #endif cmdLineString += _T("data"); #if IS_LINUX // try to rename file in root folder if exists wxFileName tcCfgFile(tcPath + wxFileName::GetPathSeparator()); tcCfgFile.AppendDir(_T("data")); tcCfgFile.SetFullName(_T("cmdline_fso.cfg")); if (tcCfgFile.IsOk() && ::wxFileExists(tcCfgFile.GetFullPath())) { wxFileName tcCfgRenameFile(tcCfgFile); tcCfgRenameFile.SetFullName(_T("cmdline_fso.old.cfg")); // rename target exists; attempt to delete it if (tcCfgRenameFile.IsOk() && ::wxFileExists(tcCfgRenameFile.GetFullPath())) { wxLogWarning(_T("Backup cmdline_fso.old.cfg file %s exists, deleting it"), tcCfgRenameFile.GetFullPath().c_str()); if (!::wxRemoveFile(tcCfgRenameFile.GetFullPath())) { wxLogError(_T("Could not remove backup cmdline_fso.old.cfg file %s"), tcCfgRenameFile.GetFullPath().c_str()); } } // now try the rename if (!::wxRenameFile(tcCfgFile.GetFullPath(), tcCfgRenameFile.GetFullPath(), false)) { wxLogError(_T("Could not rename root folder copy %s to %s"), tcCfgFile.GetFullPath().c_str(), tcCfgRenameFile.GetFullPath().c_str()); } else { wxLogInfo(_T("Renamed root folder copy of cmdline_fso.cfg")); } } #endif // if data folder does not exist in cmdline folder, attempt to create it first if (!wxDir::Exists(cmdLineString)) { if (!::wxMkdir(cmdLineString)) { wxLogError(_T("Couldn't create 'data' folder %s"), cmdLineString.c_str()); return ProMan::UnknownError; } wxLogDebug(_T("'data' folder %s created"), cmdLineString.c_str()); } else { wxLogDebug(_T("'data' folder %s found"), cmdLineString.c_str()); } cmdLineString += wxFileName::GetPathSeparator(); cmdLineString += _T("cmdline_fso.cfg"); wxFileName cmdLineFileName(cmdLineString); wxFFileOutputStream outStream(cmdLineFileName.GetFullPath(), _T("w+b")); if ( !outStream.IsOk() ) { return ProMan::UnknownError; } if ( !modLine.IsEmpty()) { outStream.Write("-mod ", 5); outStream.Write(modLine.char_str(), modLine.size()); } if ( !flagLine.IsEmpty() ) { outStream.Write(" ", 1); outStream.Write(flagLine.char_str(), flagLine.size()); } if ( !lightingPresetFlagSet.IsEmpty()) { outStream.Write(" ", 1); outStream.Write(lightingPresetFlagSet.char_str(), lightingPresetFlagSet.size()); } if ( !outStream.Close() ) { return ProMan::UnknownError; } return ProMan::NoError; }wxlauncher-0.9.4/code/apis/ProfileManager.cpp0000644000000000000000000014300412155177255021123 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "generated/configure_launcher.h" #include "apis/ProfileManager.h" #include "apis/PlatformProfileManager.h" #include "wxLauncherApp.h" #include "global/ProfileKeys.h" #include "global/MemoryDebugging.h" ProMan* ProMan::proman = NULL; bool ProMan::isInitialized = false; ProMan::Flags ProMan::flags; const wxString& ProMan::DEFAULT_PROFILE_NAME = _T("Default"); #define GLOBAL_INI_FILE_NAME _T("global.ini") ///////////// Events /** EVT_PROFILE_EVENT */ DEFINE_EVENT_TYPE(EVT_PROFILE_CHANGE); DEFINE_EVENT_TYPE(EVT_CURRENT_PROFILE_CHANGED); #include // required magic incatation WX_DEFINE_LIST(EventHandlers); void ProMan::GenerateChangeEvent() { wxCommandEvent event(EVT_PROFILE_CHANGE, wxID_NONE); wxLogDebug(_T("Generating profile change event")); EventHandlers::iterator iter = this->eventHandlers.begin(); while (iter != this->eventHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent Profile Change event to %p"), current); iter++; } } void ProMan::GenerateCurrentProfileChangedEvent() { wxCommandEvent event(EVT_CURRENT_PROFILE_CHANGED, wxID_NONE); wxLogDebug(_T("Generating current profile changed event")); EventHandlers::iterator iter = this->eventHandlers.begin(); while (iter != this->eventHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent current profile changed event to %p"), current); iter++; } } void ProMan::AddEventHandler(wxEvtHandler *handler) { wxASSERT_MSG(eventHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("ProMan::AddEventHandler(): Handler at %p already registered."), handler)); this->eventHandlers.Append(handler); } void ProMan::RemoveEventHandler(wxEvtHandler *handler) { wxASSERT_MSG(eventHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("ProMan::RemoveEventHandler(): Handler at %p not registered."), handler)); this->eventHandlers.DeleteObject(handler); } NewsData::NewsData(const wxString& theNews, const wxDateTime& lastDownloadNews) : theNews(theNews), lastDownloadNews(lastDownloadNews) { wxASSERT(!theNews.IsEmpty()); wxASSERT(lastDownloadNews.IsValid()); } /** Load a profile from a fully quaified path. Returns NULL on failure or a pointer to a wxFileConfig that you must delete when done. */ wxFileConfig* LoadProfileFromFile(const wxFileName &file) { wxFFileInputStream globalProfileInput(file.GetFullPath(), (file.FileExists())?_T("rb"):_T("w+b")); return new wxFileConfig(globalProfileInput); } /** Sets up the profile manager. Must be called on program startup so that it can intercept global wxWidgets configuation functions. \return true when setup was successful, false if proman is not ready and the program should not continue. */ bool ProMan::Initialize(Flags flags) { wxConfigBase::DontCreateOnDemand(); ProMan::proman = new ProMan(); ProMan::flags = flags; wxFileName file; file.Assign(GetProfileStorageFolder(), GLOBAL_INI_FILE_NAME); if ( !file.IsOk() ) { wxLogError(_T(" '%s' is not valid!"), file.GetFullPath().c_str()); return false; } wxLogInfo(_T(" My profiles file is: %s"), file.GetFullPath().c_str()); if ( !wxFileName::DirExists(file.GetPath()) && !wxFileName::Mkdir(file.GetPath(), 0700, wxPATH_MKDIR_FULL ) ) { wxLogError(_T(" Unable to make profile folder.")); return false; } ProMan::proman->globalProfile = LoadProfileFromFile(file); ProMan::proman->LoadNewsMapFromGlobalProfile(); // fetch all profiles. wxArrayString foundProfiles; wxDir::GetAllFiles(GetProfileStorageFolder(), &foundProfiles, _T("pro?????.ini")); wxLogInfo(_T(" Found %d profile(s)."), foundProfiles.Count()); for( size_t i = 0; i < foundProfiles.Count(); i++) { wxLogDebug(_T(" Opening %s"), foundProfiles[i].c_str()); wxFFileInputStream instream(foundProfiles[i]); wxFileConfig *config = new wxFileConfig(instream); wxString name; config->Read(PRO_CFG_MAIN_NAME, &name, wxString::Format(_T("Profile %05d"), i)); ProMan::proman->profiles[name] = config; wxLogDebug(_T(" Opened profile named: %s"), name.c_str()); } wxString currentProfile; ProMan::proman->globalProfile->Read( GBL_CFG_MAIN_LASTPROFILE, ¤tProfile, ProMan::DEFAULT_PROFILE_NAME); wxLogDebug(_T(" Searching for profile: %s"), currentProfile.c_str()); if ( ProMan::proman->profiles.find(currentProfile) == ProMan::proman->profiles.end() ) { // lastprofile does not exist wxLogDebug(_T(" lastprofile '%s' does not exist!"), currentProfile.c_str()); if ( ProMan::proman->profiles.find(ProMan::DEFAULT_PROFILE_NAME) == ProMan::proman->profiles.end() ) { // default profile also does not exist. // Means this is likely the first run this system // Create a default profile wxLogInfo(_T(" Default profile does not exist! Creating...")); ProMan::proman->CreateNewProfile(ProMan::DEFAULT_PROFILE_NAME); // FIXME figure out how PullProfile is corrupting default profile (esp. on Windows) // wxLogInfo(_T(" Priming profile...")); // PullProfile(ProMan::proman->profiles[ProMan::DEFAULT_PROFILE_NAME]); } wxLogInfo(_T(" Resetting lastprofile to Default.")); // Do not ignore updating last profile here because this is fixing bad data ProMan::proman->globalProfile->Write(GBL_CFG_MAIN_LASTPROFILE, ProMan::DEFAULT_PROFILE_NAME); wxFFileOutputStream globalProfileOutput(file.GetFullPath()); ProMan::proman->globalProfile->Save(globalProfileOutput); currentProfile = ProMan::DEFAULT_PROFILE_NAME; } wxLogDebug(_T(" Making '%s' the application profile"), currentProfile.c_str()); if ( !ProMan::proman->SwitchTo(currentProfile) ) { wxLogError(_T("Unable to set current profile to '%s'"), currentProfile.c_str()); return false; } ProMan::isInitialized = true; wxLogDebug(_T(" Profile Manager is set up")); return true; } /** clean up the memory that the manager is using. */ bool ProMan::DeInitialize() { if ( ProMan::isInitialized ) { ProMan::isInitialized = false; ProMan::proman->SaveProfilesBeforeExiting(); delete ProMan::proman; ProMan::proman = NULL; // Set the wxWidgets default FileConfig to null // so that it doesn't try to delete it again when // we shutdown wxFileConfig::Set((wxConfigBase*)NULL); return true; } else { return false; } } ProMan* ProMan::GetProfileManager() { if ( ProMan::isInitialized ) { return ProMan::proman; } else { return NULL; } } /** Private constructor. Just makes instance variables safe. Call Initialize() to setup class, then call GetProfileManager() to get a pointer to the instance. */ ProMan::ProMan() { this->globalProfile = NULL; this->isAutoSaving = true; this->currentProfile = NULL; this->privateCopyFilename = wxFileName::CreateTempFileName(_T("wxLtest")); wxFFileInputStream instream(this->privateCopyFilename); this->privateCopy = new wxFileConfig(instream); } /** Destructor. */ ProMan::~ProMan() { // don't leak the wxFileConfigs ProfileMap::iterator iter = this->profiles.begin(); while ( iter != this->profiles.end() ) { delete iter->second; iter++; } delete this->privateCopy; privateCopy = NULL; ::wxRemoveFile(this->privateCopyFilename); } /** Saves changes to profiles according to autosave profiles checkbox. */ void ProMan::SaveProfilesBeforeExiting() { if ( this->globalProfile != NULL ) { wxLogInfo(_T("saving global profile before exiting.")); SaveNewsMapToGlobalProfile(); wxFileName file; file.Assign(GetProfileStorageFolder(), GLOBAL_INI_FILE_NAME); wxFFileOutputStream globalProfileOutput(file.GetFullPath()); this->globalProfile->Save(globalProfileOutput); delete this->globalProfile; } else { wxLogWarning(_T("global profile is null, cannot save it")); } if (this->HasUnsavedChanges()) { if (this->isAutoSaving) { wxLogInfo(_T("autosaving profile %s before exiting"), this->GetCurrentName().c_str()), this->SaveCurrentProfile(); } else { int response = wxMessageBox( GetSaveDialogMessageText(ProMan::ON_EXIT, this->GetCurrentName()), GetSaveDialogCaptionText(ProMan::ON_EXIT, this->GetCurrentName()), wxYES_NO); if ( response == wxYES ) { wxLogInfo(_T("saving profile %s before exiting"), this->GetCurrentName().c_str()); this->SaveCurrentProfile(); } else { wxLogWarning(_T("exiting without saving changes to profile %s"), this->GetCurrentName().c_str()); } } } else { wxLogInfo(_T("Current profile %s has no unsaved changes. Exiting."), this->GetCurrentName().c_str()); } } void ProMan::LoadNewsMapFromGlobalProfile() { wxASSERT(newsMap.empty()); globalProfile->SetPath(GBL_CFG_NET_FOLDER); // inspired by CopyConfig() wxString groupName; long groupIndex; bool groupKeepGoing; wxString theNews; wxString lastDownloadNewsStr; wxDateTime lastDownloadNews; groupKeepGoing = globalProfile->GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { globalProfile->SetPath(groupName); if (globalProfile->Read(GBL_CFG_NET_THE_NEWS, theNews) && (globalProfile->Read(GBL_CFG_NET_NEWS_LAST_TIME, lastDownloadNewsStr))) { if ((!theNews.IsEmpty()) && (NULL != lastDownloadNews.ParseFormat( lastDownloadNewsStr, NEWS_LAST_TIME_FORMAT))) { newsMap[groupName] = NewsData(theNews, lastDownloadNews); wxLogDebug(_T("Created news map entry for source %s"), groupName.c_str()); } } globalProfile->SetPath(_T("..")); groupKeepGoing = globalProfile->GetNextGroup(groupName, groupIndex); } globalProfile->SetPath(_T("/")); } void ProMan::SaveNewsMapToGlobalProfile() { globalProfile->SetPath(GBL_CFG_NET_FOLDER); for (NewsMap::const_iterator it = newsMap.begin(), end = newsMap.end(); it != end; ++it) { const wxString& newsSource = it->first; const NewsData& newsData = it->second; globalProfile->SetPath(newsSource); globalProfile->Write(GBL_CFG_NET_THE_NEWS, newsData.theNews); globalProfile->Write(GBL_CFG_NET_NEWS_LAST_TIME, newsData.lastDownloadNews.Format(NEWS_LAST_TIME_FORMAT)); globalProfile->SetPath(_T("..")); } globalProfile->SetPath(_T("/")); } /** Resets the private copy so that it contains a copy of the current profile's contents. */ void ProMan::ResetPrivateCopy() { wxCHECK_RET(this->currentProfile != NULL, _T("ResetPrivateCopy called with null current profile!")); ClearConfig(*(this->privateCopy)); CopyConfig(*(this->currentProfile), *(this->privateCopy)); } /** Creates a new profile including the directory for it to go in, the entry in the profiles map. Returns true if creation was successful. */ bool ProMan::CreateNewProfile(wxString newName) { wxFileName profile; profile.Assign( GetProfileStorageFolder(), this->GenerateNewProfileFileName()); wxLogInfo(_T("New profile will be written to %s"), profile.GetFullPath().c_str()); wxASSERT_MSG( profile.IsOk(), _T("Profile filename is invalid")); if ( !wxFileName::DirExists(profile.GetPath()) && !wxFileName::Mkdir( profile.GetPath(), wxPATH_MKDIR_FULL) ) { wxLogWarning(_T(" Unable to create profile folder: %s"), profile.GetPath().c_str()); return false; } wxFFileInputStream configInput(profile.GetFullPath(), _T("w+b")); wxFileConfig* config = new wxFileConfig(configInput); config->Write(PRO_CFG_MAIN_NAME, newName); config->Write(PRO_CFG_MAIN_FILENAME, profile.GetFullName()); wxFFileOutputStream configOutput(profile.GetFullPath()); config->Save(configOutput); this->profiles[newName] = config; return true; } /** Generates a filename for a new profile, where the name is of the form pro#####.ini with ##### being the least 5-digit number not yet taken. */ wxString ProMan::GenerateNewProfileFileName() { wxArrayString profileFiles; wxDir::GetAllFiles(GetProfileStorageFolder(), &profileFiles, _T("pro*.ini"), wxDIR_FILES); long l; std::vector proNums; for (int i = 0, n = profileFiles.GetCount(); i < n; ++i) { profileFiles[i] = profileFiles[i].AfterLast(_T('o')).BeforeLast(_T('.')); profileFiles[i].ToLong(&l); proNums.push_back(l); } // just in case they're not already in sorted order sort(proNums.begin(), proNums.end()); proNums.insert(proNums.begin(), -1); // dummy value that allows for easily checking first element long proIndex = 0; int i; const int n = proNums.size(); for (i = 1; i < n; ++i) { if (proNums[i] - proNums[i-1] > 1) { // if there's a gap in the numbering, try to fill it proIndex = proNums[i-1] + 1; break; } } if (i == n) { // there are no gaps, so increment from the last element proIndex = proNums[n-1] + 1; } wxLogDebug(_T("new profile number: %ld"), proIndex); wxASSERT(proIndex <= 99999); // the maximum possible index given a 5-digit number return wxString::Format(_T("pro%05d.ini"), static_cast(proIndex)); } // global profile access functions /** Tests whether the key strName is in the global profile. */ bool ProMan::GlobalExists(wxString& strName) const { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to check existence of key %s in null global profile"), strName.c_str()); return false; } else { return this->globalProfile->Exists(strName); } } /** Tests whether the key strName is in the global profile */ bool ProMan::GlobalExists(const wxChar* strName) const { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to check existence of key %s in null global profile"), strName); return false; } else { return this->globalProfile->Exists(strName); } } /** Reads a bool from the global profile. Returns true on success. */ bool ProMan::GlobalRead(const wxString& key, bool* b) const { if (this->globalProfile == NULL) { wxLogWarning( _T("attempt to read bool for key %s from null global profile"), key.c_str()); return false; } else { return this->globalProfile->Read(key, b); } } /** Reads a bool from the global profile, using the default value if the key is not present. If the entry is not present in the global profile and writeBackIfAbsent is set to true, then the default value is written back to the global profile. Returns true on success. */ bool ProMan::GlobalRead(const wxString& key, bool* b, bool defaultVal, bool writeBackIfAbsent) { if (this->globalProfile == NULL) { wxLogWarning( _T("attempt to read bool for key %s with default value %s from null global profile"), key.c_str(), defaultVal ? _T("true") : _T("false")); return false; } else { bool readSuccess = this->globalProfile->Read(key, b, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in global profile is absent. writing default value %s to it."), key.c_str(), defaultVal ? _T("true") : _T("false")); this->globalProfile->Write(key, defaultVal); } return readSuccess; } } /** Reads a string from the global profile. Returns true on success. */ bool ProMan::GlobalRead(const wxString& key, wxString* str) const { if (this->globalProfile == NULL) { wxLogWarning( _T("attempt to read string for key %s with from null global profile"), key.c_str()); return false; } else { return this->globalProfile->Read(key, str); } } /** Reads a string from the global profile, using the default value if the key is not present. If the entry is not present in the global profile and writeBackIfAbsent is set to true, then the default value is written back to the global profile. Returns true on success. */ bool ProMan::GlobalRead(const wxString& key, wxString* str, const wxString& defaultVal, bool writeBackIfAbsent) { if (this->globalProfile == NULL) { wxLogWarning( _T("attempt to read string for key %s with default value %s from null global profile"), key.c_str(), defaultVal.c_str()); return false; } else { bool readSuccess = this->globalProfile->Read(key, str, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in global profile is absent. writing default value %s to it."), key.c_str(), defaultVal.c_str()); this->globalProfile->Write(key, defaultVal); } return readSuccess; } } /** Reads a long from the global profile, using the default value if the key is not present. If the entry is not present in the global profile and writeBackIfAbsent is set to true, then the default value is written back to the global profile. Returns true on success. */ bool ProMan::GlobalRead(const wxString& key, long* l, long defaultVal, bool writeBackIfAbsent) { if (this->globalProfile == NULL) { wxLogWarning( _T("attempt to read long for key %s with default value %ld from null global profile"), key.c_str(), defaultVal); return false; } else { bool readSuccess = this->globalProfile->Read(key, l, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in global profile is absent. writing default value %ld to it."), key.c_str(), defaultVal); this->globalProfile->Write(key, defaultVal); } return readSuccess; } } /** Writes a string for the given key to the global profile. Returns true on success. */ bool ProMan::GlobalWrite(const wxString& key, const wxString& value) { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null global profile"), value.c_str(), key.c_str()); return false; } else { return this->globalProfile->Write(key, value); } } /** Writes a string for the given key to the global profile. Returns true on success. */ bool ProMan::GlobalWrite(const wxString& key, const wxChar* value) { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null global profile"), value, key.c_str()); return false; } else { return this->globalProfile->Write(key, value); } } /** Writes a long for the given key to the global profile. Returns true on success. */ bool ProMan::GlobalWrite(const wxString& key, long value) { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to write %ld to %s in null global profile"), value, key.c_str()); return false; } else { return this->globalProfile->Write(key, value); } } /** Writes a bool for the given key to the global profile. Returns true on success. */ bool ProMan::GlobalWrite(const wxString& key, bool value) { if (this->globalProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null global profile"), value ? _T("true") : _T("false"), key.c_str()); return false; } else { return this->globalProfile->Write(key, value); } } // current profile access functions /** Tests whether the key strName is in the current profile. */ bool ProMan::ProfileExists(wxString& strName) const { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to check existence of key %s in null current profile"), strName.c_str()); return false; } else { return this->currentProfile->Exists(strName); } } /** Tests whether the key strName is in the current profile. */ bool ProMan::ProfileExists(const wxChar* strName) const { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to check existence of key %s in null current profile"), strName); return false; } else { return this->currentProfile->Exists(strName); } } /** Reads a bool from the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, bool* b) const { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read bool for key %s from null current profile"), key.c_str()); return false; } else { return this->currentProfile->Read(key, b); } } /** Reads a bool from the current profile, using the default value if the key is not present. If the entry is not present in the current profile and writeBackIfAbsent is set to true, then the default value is written back to the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, bool* b, bool defaultVal, bool writeBackIfAbsent) { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read bool for key %s with default value %s from null current profile"), key.c_str(), defaultVal ? _T("true") : _T("false")); return false; } else { bool readSuccess = this->currentProfile->Read(key, b, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in current profile is absent. writing default value %s to it."), key.c_str(), defaultVal ? _T("true") : _T("false")); this->currentProfile->Write(key, defaultVal); } return readSuccess; } } /** Reads a string from the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, wxString* str) const { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read string for key %s with from null current profile"), key.c_str()); return false; } else { return this->currentProfile->Read(key, str); } } /** Reads a string from the current profile, using the default value if the key is not present. If the entry is not present in the current profile and writeBackIfAbsent is set to true, then the default value is written back to the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, wxString* str, const wxString& defaultVal, bool writeBackIfAbsent) { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read string for key %s with default value %s from null current profile"), key.c_str(), defaultVal.c_str()); return false; } else { bool readSuccess = this->currentProfile->Read(key, str, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in current profile is absent. writing default value %s to it."), key.c_str(), defaultVal.c_str()); this->currentProfile->Write(key, defaultVal); } return readSuccess; } } /** Reads a long from the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, long* l) const { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read long for key %s from null current profile"), key.c_str()); return false; } else { return this->currentProfile->Read(key, l); } } /** Reads a long from the current profile, using the default value if the key is not present. If the entry is not present in the current profile and writeBackIfAbsent is set to true, then the default value is written back to the current profile. Returns true on success. */ bool ProMan::ProfileRead(const wxString& key, long* l, long defaultVal, bool writeBackIfAbsent) { if (this->currentProfile == NULL) { wxLogWarning( _T("attempt to read long for key %s with default value %ld from null current profile"), key.c_str(), defaultVal); return false; } else { bool readSuccess = this->currentProfile->Read(key, l, defaultVal); if (!readSuccess && writeBackIfAbsent) { wxLogDebug(_T("entry %s in current profile is absent. writing default value %ld to it."), key.c_str(), defaultVal); this->currentProfile->Write(key, defaultVal); } return readSuccess; } } /** Writes a string for the given key to the current profile. Returns true on success. */ bool ProMan::ProfileWrite(const wxString& key, const wxString& value) { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null current profile"), value.c_str(), key.c_str()); return false; } else { if (!this->currentProfile->Exists(key)) { wxLogDebug(_T("adding entry %s with value %s to current profile"), key.c_str(), value.c_str()); } else { wxString oldValue; if (this->currentProfile->Read(key, &oldValue) && (value != oldValue)) { wxLogDebug(_T("replacing old value %s with value %s for current profile entry %s"), oldValue.c_str(), value.c_str(), key.c_str()); } } return this->currentProfile->Write(key, value); } } /** Writes a string for the given key to the current profile. Returns true on success. */ bool ProMan::ProfileWrite(const wxString& key, const wxChar* value) { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null current profile"), value, key.c_str()); return false; } else { if (!this->currentProfile->Exists(key)) { wxLogDebug(_T("adding entry %s with value %s to current profile"), key.c_str(), value); } else { wxString oldValue; if (this->currentProfile->Read(key, &oldValue) && (value != oldValue)) { wxLogDebug(_T("replacing old value %s with value %s for current profile entry %s"), oldValue.c_str(), value, key.c_str()); } } return this->currentProfile->Write(key, value); } } /** Writes a long for the given key to the current profile. Returns true on success. */ bool ProMan::ProfileWrite(const wxString& key, long value) { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to write %ld to %s in null current profile"), value, key.c_str()); return false; } else { if (!this->currentProfile->Exists(key)) { wxLogDebug(_T("adding entry %s with value %ld to current profile"), key.c_str(), value); } else { long oldValue; if (this->currentProfile->Read(key, &oldValue) && (value != oldValue)) { wxLogDebug(_T("replacing old value %ld with value %ld for current profile entry %s"), oldValue, value, key.c_str()); } } return this->currentProfile->Write(key, value); } } /** Writes a bool for the given key to the current profile. Returns true on success. */ bool ProMan::ProfileWrite(const wxString& key, bool value) { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to write %s to %s in null current profile"), value ? _T("true") : _T("false"), key.c_str()); return false; } else { if (!this->currentProfile->Exists(key)) { wxLogDebug(_T("adding entry %s with value %s to current profile"), key.c_str(), value ? _T("true") : _T("false")); } else { bool oldValue; if (this->currentProfile->Read(key, &oldValue) && (value != oldValue)) { wxLogDebug(_T("replacing old value %s with value %s for current profile entry %s"), oldValue ? _T("true") : _T("false"), value ? _T("true") : _T("false"), key.c_str()); } } return this->currentProfile->Write(key, value); } } /** Deletes an entry from the current profile, deleting the group if the entry was the only one in the group and the second parameter is true. */ bool ProMan::ProfileDeleteEntry(const wxString& key, bool bDeleteGroupIfEmpty) { if (this->currentProfile == NULL) { wxLogWarning(_T("attempt to delete entry %s in null current profile"), key.c_str()); return false; } else { if (this-currentProfile->Exists(key)) { wxLogDebug(_T("deleting key %s in profile"), key.c_str()); } return this->currentProfile->DeleteEntry(key, bDeleteGroupIfEmpty); } } const NewsData* ProMan::NewsRead(const wxString& newsSource) const { wxCHECK_MSG(!newsSource.IsEmpty(), NULL, _T("NewsRead: newsSource is empty!")); NewsMap::const_iterator it = newsMap.find(newsSource); if (it == newsMap.end()) { return NULL; } else { return &(it->second); } } void ProMan::NewsWrite(const wxString& newsSource, const NewsData& data) { wxCHECK_RET(!newsSource.IsEmpty(), _T("NewsWrite: newsSource is empty!")); wxCHECK_RET(data.IsValid(), _T("NewsWrite: data is not valid!")); newsMap[newsSource] = data; } /** Returns the text to use in the "save changes?" dialog's caption (window title) */ const wxString ProMan::GetSaveDialogCaptionText(ProMan::SaveDialogContext context, const wxString& profileName) { // currently, the same text is used on all platforms, although it doesn't have to be that way // NOTE: don't attempt to collapse the cases by removing break statements! it's asking for trouble switch (context) { case ON_PROFILE_SWITCH: #if PROFILE_DEBUGGING wxLogDebug(_T("contents of private copy at save prompt on profile switch:")); LogConfigContents(*ProMan::proman->privateCopy); wxLogDebug(_T("contents of current profile at save prompt on profile switch:")); LogConfigContents(*ProMan::proman->currentProfile); #endif return _T("Save changes to current profile?"); break; case ON_PROFILE_CREATE: #if PROFILE_DEBUGGING wxLogDebug(_T("contents of private copy at save prompt on profile create:")); LogConfigContents(*ProMan::proman->privateCopy); wxLogDebug(_T("contents of current profile at save prompt on profile create:")); LogConfigContents(*ProMan::proman->currentProfile); #endif return _T("Save changes to current profile?"); break; case ON_EXIT: #if PROFILE_DEBUGGING wxLogDebug(_T("contents of private copy at save prompt on exit:")); LogConfigContents(*ProMan::proman->privateCopy); wxLogDebug(_T("contents of current profile at save prompt on exit:")); LogConfigContents(*ProMan::proman->currentProfile); #endif return _T("Save changes to current profile?"); break; default: wxCHECK_MSG(false, wxEmptyString, _T("ProMan::GetSaveDialogCaptionText: provided context is invalid")); break; } } /** Returns the text to use in the "save changes?" dialog's message (text area) */ const wxString ProMan::GetSaveDialogMessageText(ProMan::SaveDialogContext context, const wxString& profileName) { switch (context) { case ON_PROFILE_SWITCH: return wxString::Format(_T("Save changes to profile '%s' before switching profiles?"), profileName.c_str()); break; case ON_PROFILE_CREATE: return wxString::Format(_T("Save changes to profile '%s' before creating a new profile?"), profileName.c_str()); break; case ON_EXIT: #if IS_WIN32 return wxString::Format(_T("Save changes to profile '%s' before exiting?"), profileName.c_str()); #else return wxString::Format(_T("Save changes to profile '%s' before quitting?"), profileName.c_str()); #endif break; default: wxCHECK_MSG(false, wxEmptyString, _T("ProMan::GetSaveDialogMessageText: provided context is invalid")); break; } } /** Returns true if the named profile exists, false otherwise. */ bool ProMan::DoesProfileExist(wxString name) { /* Item exists if the returned value from find() does not equal the value of .end(). As per the HashMap docs. */ return (this->profiles.find(name) != this->profiles.end()); } /** Returns an wxArrayString of all of the profile names. */ wxArrayString ProMan::GetAllProfileNames() { wxArrayString out(this->profiles.size()); ProfileMap::iterator iter = this->profiles.begin(); do { out.Add(iter->first); iter++; } while (iter != this->profiles.end()); return out; } void SaveProfileToDisk(wxFileConfig* toSave, const wxString& name) { wxString profileFilename; if ( !toSave->Read(PRO_CFG_MAIN_FILENAME, &profileFilename) ) { wxLogError(_T("Profile '%s' does not have a file name. Cannot save it."), name.c_str()); // FIXME maybe make a new file and save the current profile there } else { wxFileName file; file.Assign(GetProfileStorageFolder(), profileFilename); wxASSERT( file.IsOk() ); wxFFileOutputStream configOutput(file.GetFullPath()); toSave->Save(configOutput); wxLogDebug(_T("Profile '%s' saved to '%s'"), name.c_str(), file.GetFullPath().c_str()); } } /** Saves the current profile to disk, regardless of whether it has unsaved changes. Does not affect the global profile or any other profile. */ void ProMan::SaveCurrentProfile(bool quiet) { wxConfigBase* configbase = wxFileConfig::Get(false); if ( configbase == NULL ) { wxLogError(_T("There is no global file config.")); return; } wxFileConfig* config = dynamic_cast(configbase); if ( config != NULL ) { SaveProfileToDisk(config, this->currentProfileName.c_str()); this->ResetPrivateCopy(); if (!quiet) { wxLogStatus(_T("Profile '%s' saved"), this->currentProfileName.c_str()); } wxLogDebug(_T("Current config%s saved."), quiet ? _T(" quietly") : wxEmptyString); } else { wxLogError(_T("Configbase is not a wxFileConfig.")); } } /** Reverts any unsaved changes to the current profile. */ void ProMan::RevertCurrentProfile() { ClearConfig(*(this->currentProfile)); CopyConfig(*(this->privateCopy), *(this->currentProfile)); } bool ProMan::HasUnsavedChanges() { return !AreConfigsEqual(*(this->currentProfile), *(this->privateCopy)); } wxString ProMan::GetCurrentName() { return this->currentProfileName; } /** Implements the literal switch to another profile. Does not cause prompts and may destroy data if autosave is not on. */ bool ProMan::SwitchTo(wxString name) { if ( this->profiles.find(name) == this->profiles.end() ) { return false; } else { if (this->currentProfile != NULL && this->HasUnsavedChanges()) { if (this->isAutoSaving) { wxLogDebug(_T("Auto saving current profile %s before switching profiles"), this->currentProfileName.c_str()); this->SaveCurrentProfile(); } else { wxLogDebug(_T("Reverting unsaved changes to current profile %s before switching"), this->currentProfileName.c_str()); this->RevertCurrentProfile(); } } this->currentProfileName = name; this->currentProfile = this->profiles.find(name)->second; wxFileConfig::Set(this->currentProfile); if ( !(ProMan::flags & NoUpdateLastProfile) ) this->globalProfile->Write(GBL_CFG_MAIN_LASTPROFILE, name); this->ResetPrivateCopy(); // TestConfigFunctions(*this->currentProfile); // remove after testing on all platforms this->GenerateCurrentProfileChangedEvent(); return true; } } /** Creates a profile from a fullyqualified path. */ bool ProMan::CreateProfile(const wxString& newProfileName, const wxFileName& sourceFile) { wxLogDebug(_T("Importing profile in '%s' as '%s'"), sourceFile.GetFullPath().c_str(), newProfileName.c_str()); wxFileConfig *sourceConfig = LoadProfileFromFile(sourceFile); return this->CreateProfile(newProfileName, sourceConfig); } /** Creates a profile named newProfileName from the contents of sourceConfig. If sourceConfig is NULL then the newProfile is created blank. */ bool ProMan::CreateProfile(const wxString& newProfileName, const wxFileConfig *sourceConfig) { if (this->DoesProfileExist(newProfileName)) { wxLogWarning(_("New profile '%s' already exists!"), newProfileName.c_str()); return false; } if (!this->CreateNewProfile(newProfileName)) { wxLogWarning(_T("New profile creation failed.")); return false; } if (sourceConfig != NULL) { /* We just created this profile it had better exist */ wxFileConfig* newProfileConfig = this->profiles[newProfileName]; wxCHECK_MSG(newProfileConfig != NULL, false, _T("Create returned true but did not create profile")); #if PROFILE_DEBUGGING wxLogDebug(_T("contents of new profile '%s' before clone:"), newProfileName.c_str()); LogConfigContents(*newProfileConfig); #endif CopyConfig(*sourceConfig, *newProfileConfig, false); SaveProfileToDisk(newProfileConfig, newProfileName); #if PROFILE_DEBUGGING wxLogDebug(_T("contents of new profile '%s' after clone:"), newProfileName.c_str()); LogConfigContents(*newProfileConfig); wxLogDebug(_T("contents of cloned from profile '%s' after clone:"), cloneFromProfileName.c_str()); LogConfigContents(*cloneFromProfileConfig); #endif } this->GenerateChangeEvent(); return true; } /** Creates a profile by name. If the second argument is non-empty and is the name of an existing profile, its settings/flags will be copied over to the new profile. Returns true on success. */ bool ProMan::CreateProfile(const wxString& newProfileName, const wxString& cloneFromProfileName) { wxFileConfig *cloneSource = NULL; if (cloneFromProfileName.IsEmpty()) { wxLogDebug(_T("Creating blank profile '%s'"), newProfileName.c_str()); } else { wxLogDebug(_T("Cloning original profile (%s) to '%s'"), cloneFromProfileName.c_str(), newProfileName.c_str()); if (!this->DoesProfileExist(cloneFromProfileName)) { wxLogWarning(_("Profile to clone from '%s' does not exist!"), cloneFromProfileName.c_str()); return false; } cloneSource = this->profiles[cloneFromProfileName]; wxCHECK_MSG( cloneSource != NULL, false, wxString::Format(_T("Cannot find profile '%s' from which to clone"), cloneFromProfileName.c_str()) ); } return this->CreateProfile(newProfileName, cloneSource); } bool ProMan::DeleteProfile(wxString name) { wxLogDebug(_T("Deleting profile: %s"), name.c_str()); if ( name == ProMan::DEFAULT_PROFILE_NAME ) { wxLogWarning(_("Cannot delete 'Default' profile.")); return false; } if ( name == this->currentProfileName ) { wxLogInfo( _T("Deleting current profile. Switching to '%s' profile"), ProMan::DEFAULT_PROFILE_NAME.c_str()); this->SwitchTo(ProMan::DEFAULT_PROFILE_NAME); } if ( this->DoesProfileExist(name) ) { wxLogDebug(_T(" Profile exists")); wxFileConfig* config = this->profiles[name]; wxString filename; if ( !config->Read(PRO_CFG_MAIN_FILENAME, &filename) ) { wxLogWarning(_T("Unable to get filename to delete %s"), name.c_str()); return false; } wxFileName file; file.Assign(GetProfileStorageFolder(), filename); if ( file.FileExists() ) { wxLogDebug(_T(" Backing file exists")); if ( wxRemoveFile(file.GetFullPath()) ) { this->profiles.erase(this->profiles.find(name)); delete config; wxLogMessage(_("Profile '%s' deleted."), name.c_str()); this->GenerateChangeEvent(); return true; } else { wxLogWarning(_("Unable to delete file for profile '%s'"), name.c_str()); } } else { wxLogWarning(_("Backing file (%s) for profile '%s' does not exist"), file.GetFullPath().c_str(), name.c_str()); } } else { wxLogWarning(_("Profile %s does not exist. Cannot delete."), name.c_str()); } return false; } // the config manipulation functions are adapted from CopyEntriesRecursive and CopyEntry // from http://audacity.googlecode.com/svn/audacity-src/trunk/src/Prefs.cpp SVN r11245 /** copies the contents of one wxConfigBase to another wxConfigBase. The wxWindows group is not copied, if it exists. If includeMainGroup is true, then the contents of the "main" group (profile-specific information, such as name and filename) are copied as well, otherwise they are not copied. */ void ProMan::CopyConfig(const wxConfigBase& in_src, wxConfigBase& dest, const bool includeMainGroup, const wxString path) { wxString entryName; long entryIndex; bool entryKeepGoing; /* BUGNOTE - This is possibly not safe, but because the following code uses non const functions and restores what it does mutate this should be safe. */ wxConfigBase& src = const_cast(in_src); entryKeepGoing = src.GetFirstEntry(entryName, entryIndex); while (entryKeepGoing) { CopyConfigEntry(src, dest, path, entryName); entryKeepGoing = src.GetNextEntry(entryName, entryIndex); } wxString groupName; long groupIndex; bool groupKeepGoing; groupKeepGoing = src.GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { if (groupName != _T("wxWindows") && (includeMainGroup || groupName != _T("main"))) { wxString subPath = path + groupName + _T("/"); src.SetPath(subPath); CopyConfig(src, dest, includeMainGroup, subPath); src.SetPath(path); } groupKeepGoing = src.GetNextGroup(groupName, groupIndex); } } // assumed that cfg1's path is already at path // and that cfg1 has an entry at /path/entry /** Helper function for CopyConfig(). Copies a single config entry. */ void ProMan::CopyConfigEntry(const wxConfigBase& src, wxConfigBase& dest, const wxString path, const wxString entry) { switch(src.GetEntryType(entry)) { case wxConfigBase::Type_Unknown: case wxConfigBase::Type_String: { wxString value = src.Read(entry, wxEmptyString); dest.Write(path + entry, value); break; } case wxConfigBase::Type_Boolean: { bool value = false; src.Read(entry, &value, value); dest.Write(path + entry, value); break; } case wxConfigBase::Type_Integer: { long value = false; src.Read(entry, &value, value); dest.Write(path + entry, value); break; } case wxConfigBase::Type_Float: { double value = false; src.Read(entry, &value, value); dest.Write(path + entry, value); break; } } } /** Clears the contents of the provided config. */ void ProMan::ClearConfig(wxConfigBase& cfg) { wxString entryName; long entryIndex; bool entryKeepGoing; wxArrayString entries, groups; entryKeepGoing = cfg.GetFirstEntry(entryName, entryIndex); while (entryKeepGoing) { entries.Add(entryName); entryKeepGoing = cfg.GetNextEntry(entryName, entryIndex); } wxString groupName; long groupIndex; bool groupKeepGoing; groupKeepGoing = cfg.GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { groups.Add(groupName); groupKeepGoing = cfg.GetNextGroup(groupName, groupIndex); } for (int i = 0, n = entries.GetCount(); i < n; ++i) { cfg.DeleteEntry(entries.Item(i)); } for (int i = 0, n = groups.GetCount(); i < n; ++i) { cfg.DeleteGroup(groups.Item(i)); } } /** Tests whether two configs are equal, where two configs are defined as equal if they have the same entries and groups with the same values, except that the wxWindows group is ignored, if it exists. */ bool ProMan::AreConfigsEqual(wxConfigBase& cfg1, wxConfigBase& cfg2) { return IsConfigSubset(cfg1, cfg2) && IsConfigSubset(cfg2, cfg1); } /** Tests whether the first config is a subset of the second, where subset means that the first config's entries and groups appear in the second config in the same structure and with the same values, with the exception that the wxWindows group is ignored if it exists.*/ bool ProMan::IsConfigSubset(wxConfigBase& cfg1, wxConfigBase& cfg2, const wxString path) { wxString entryName; long entryIndex; bool entryKeepGoing; entryKeepGoing = cfg1.GetFirstEntry(entryName, entryIndex); while (entryKeepGoing) { if (!AreEntriesEqual(cfg1, cfg2, path, entryName)) { return false; } entryKeepGoing = cfg1.GetNextEntry(entryName, entryIndex); } wxString groupName; long groupIndex; bool groupKeepGoing; groupKeepGoing = cfg1.GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { if (groupName != _T("wxWindows")) { wxString subPath = path + groupName + _T("/"); cfg1.SetPath(subPath); if (!IsConfigSubset(cfg1, cfg2, subPath)) { cfg1.SetPath(path); // fix the path before returning return false; } cfg1.SetPath(path); } groupKeepGoing = cfg1.GetNextGroup(groupName, groupIndex); } return true; } // assumed that cfg1's path is already at path /** Helper function for AreConfigsEqual(). Tests whether an entry exists in two configs and if so, whether the value stored at the entry in each is equal.*/ bool ProMan::AreEntriesEqual(const wxConfigBase& cfg1, const wxConfigBase& cfg2, const wxString path, const wxString entry) { switch(cfg1.GetEntryType(entry)) { case wxConfigBase::Type_Unknown: case wxConfigBase::Type_String: { wxString value1, value2; if ((!cfg1.Read(entry, &value1)) || (!cfg2.Read(path + entry, &value2))) { return false; } else { return value1 == value2; } break; } case wxConfigBase::Type_Boolean: { bool value1, value2; if ((!cfg1.Read(entry, &value1)) || (!cfg2.Read(path + entry, &value2))) { return false; } else { return value1 == value2; } break; } case wxConfigBase::Type_Integer: { long value1, value2; if ((!cfg1.Read(entry, &value1)) || (!cfg2.Read(path + entry, &value2))) { return false; } else { return value1 == value2; } break; } case wxConfigBase::Type_Float: { double value1, value2; if ((!cfg1.Read(entry, &value1)) || (!cfg2.Read(path + entry, &value2))) { return false; } else { return value1 == value2; } break; } default: wxLogWarning(_T("unknown entry type %d"), cfg1.GetEntryType(entry)); return false; break; } wxCHECK_MSG(false, false, _T("escaped switch statement in ProMan::AreEntriesEqual")); } /** Debugging function that prints contents of provided config to log. */ void ProMan::LogConfigContents(wxConfigBase& cfg, const wxString path, const bool includeWxWindows) { wxString entryName; long entryIndex; bool entryKeepGoing; entryKeepGoing = cfg.GetFirstEntry(entryName, entryIndex); while (entryKeepGoing) { wxLogDebug(_T(" %s%s = %s"), path.c_str(), entryName.c_str(), cfg.Read(entryName, _T("ENTRY_NOT_FOUND")).c_str()); entryKeepGoing = cfg.GetNextEntry(entryName, entryIndex); } wxString groupName; long groupIndex; bool groupKeepGoing; groupKeepGoing = cfg.GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { if (includeWxWindows) { wxString subPath = path + groupName + _T("/"); cfg.SetPath(subPath); LogConfigContents(cfg, subPath); cfg.SetPath(path); } else if (groupName != _T("wxWindows")) { wxString subPath = path + groupName + _T("/"); cfg.SetPath(subPath); LogConfigContents(cfg, subPath); cfg.SetPath(path); } groupKeepGoing = cfg.GetNextGroup(groupName, groupIndex); } } /** Start of a test case for config manipulation functions */ void ProMan::TestConfigFunctions(wxConfigBase& src) { wxLogDebug(_T("contents of source config:")); LogConfigContents(src); wxString tempFileName = wxFileName::CreateTempFileName(_T("wxLtest")); wxLogDebug(_T("created temp file '%s'"), tempFileName.c_str()); wxFFileInputStream instream(tempFileName); wxFileConfig* dest = new wxFileConfig(instream); wxLogDebug(_T("initial contents of dest config:")); LogConfigContents(*dest); wxLogDebug(_T("is src a subset of dest? %s"), IsConfigSubset(src, *dest) ? _T("true") : _T("false")); wxLogDebug(_T("is dest a subset of src? %s"), IsConfigSubset(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("are configs src and dest equal? %s"), AreConfigsEqual(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("copying source config to dest config")); CopyConfig(src, *dest); wxLogDebug(_T("contents of dest config after copying:")); LogConfigContents(*dest); wxLogDebug(_T("is src a subset of dest? %s"), IsConfigSubset(src, *dest) ? _T("true") : _T("false")); wxLogDebug(_T("is dest a subset of src? %s"), IsConfigSubset(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("are configs src and dest equal? %s"), AreConfigsEqual(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("deleting entry %s from dest"), PRO_CFG_LIGHTING_PRESET.c_str()); dest->DeleteEntry(PRO_CFG_LIGHTING_PRESET, true); wxLogDebug(_T("contents of dest config after entry deletion:")); LogConfigContents(*dest); wxLogDebug(_T("is src a subset of dest? %s"), IsConfigSubset(src, *dest) ? _T("true") : _T("false")); wxLogDebug(_T("is dest a subset of src? %s"), IsConfigSubset(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("are configs src and dest equal? %s"), AreConfigsEqual(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("clearing dest config")); wxLogDebug(_T("before clearing, dest has %d entries and %d groups"), dest->GetNumberOfEntries(true), dest->GetNumberOfGroups(true)); ClearConfig(*dest); wxLogDebug(_T("contents of dest config after clearing:")); LogConfigContents(*dest); wxLogDebug(_T("after clearing, dest has %d entries and %d groups"), dest->GetNumberOfEntries(true), dest->GetNumberOfGroups(true)); wxLogDebug(_T("recopying src to dest")); CopyConfig(src, *dest); wxLogDebug(_T("contents of dest config after second copying:")); LogConfigContents(*dest); wxLogDebug(_T("is src a subset of dest? %s"), IsConfigSubset(src, *dest) ? _T("true") : _T("false")); wxLogDebug(_T("is dest a subset of src? %s"), IsConfigSubset(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("are configs src and dest equal? %s"), AreConfigsEqual(*dest, src) ? _T("true") : _T("false")); const wxString FAKE_ENTRY = _T("/foo/bar"); const wxString FAKE_VALUE = _T("blah"); wxLogDebug(_T("adding entry %s to dest"), FAKE_ENTRY.c_str()); dest->Write(FAKE_ENTRY, FAKE_VALUE); wxLogDebug(_T("contents of dest config after entry addition:")); LogConfigContents(*dest); wxLogDebug(_T("is src a subset of dest? %s"), IsConfigSubset(src, *dest) ? _T("true") : _T("false")); wxLogDebug(_T("is dest a subset of src? %s"), IsConfigSubset(*dest, src) ? _T("true") : _T("false")); wxLogDebug(_T("are configs src and dest equal? %s"), AreConfigsEqual(*dest, src) ? _T("true") : _T("false")); delete dest; wxLogDebug(_T("config test complete.")); } /** Applies the current profile to the registry where Freespace 2 can read it. */ ProMan::RegistryCodes ProMan::PushCurrentProfile() { if (this->currentProfile == NULL) { wxLogError(_T("PushCurrentProfile: attempt to push null current profile")); return ProMan::UnknownError; } else { return ProMan::PushProfile(this->currentProfile); } } /** Applies the passed wxFileConfig profile to the registry where Freespace 2 can read it. */ ProMan::RegistryCodes ProMan::PushProfile(wxFileConfig *cfg) { wxCHECK_MSG(cfg != NULL, ProMan::UnknownError, _T("ProMan::PushProfile given null wxFileConfig!")); #if IS_WIN32 // check if binary supports configfile return RegistryPushProfile(cfg); #elif IS_LINUX || IS_APPLE return FilePushProfile(cfg); #else #error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif } /** Takes the settings in the registry and puts them into the wxFileConfig */ ProMan::RegistryCodes ProMan::PullProfile(wxFileConfig *cfg) { wxCHECK_MSG(cfg != NULL, ProMan::UnknownError, _T("ProMan::PullProfile given null wxFileConfig!")); #if IS_WIN32 // check if binary supports configfile return RegistryPullProfile(cfg); #elif IS_LINUX || IS_APPLE return FilePullProfile(cfg); #else #error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif } wxlauncher-0.9.4/code/apis/ProfileManager.h0000644000000000000000000001634512155177255020577 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROFILEMANAGER_H #define PROFILEMANAGER_H #include #include #include WX_DECLARE_STRING_HASH_MAP( wxFileConfig*, ProfileMap ); WX_DECLARE_LIST(wxEvtHandler, EventHandlers); /** event is generated anytime the number of profiles in the manager change. */ DECLARE_EVENT_TYPE(EVT_PROFILE_CHANGE, -1); /** Event is generated anytime the currently selected profile is changed. */ DECLARE_EVENT_TYPE(EVT_CURRENT_PROFILE_CHANGED, -1); /** Stores data about downloaded news. */ struct NewsData { NewsData() { } // required for wxHashMap, unfortunately NewsData(const wxString& theNews, const wxDateTime& lastDownloadNews); bool IsValid() const { return (!theNews.IsEmpty()) && lastDownloadNews.IsValid(); } wxString theNews; wxDateTime lastDownloadNews; }; /** Maps a news source by name to the locally stored data on it. */ WX_DECLARE_STRING_HASH_MAP(NewsData, NewsMap); class ProMan { public: enum Flags { None = 0, NoUpdateLastProfile = 1 << 0, ProManFlagsMax }; static bool Initialize(Flags flags = None); static bool DeInitialize(); static ProMan* GetProfileManager(); virtual ~ProMan(); wxArrayString GetAllProfileNames(); wxString GetCurrentName(); bool GlobalExists(const wxChar* strName) const; bool GlobalExists(wxString& strName) const; bool GlobalRead(const wxString& key, bool* b) const; bool GlobalRead(const wxString& key, bool* b, bool defaultVal, bool writeBackIfAbsent = false); bool GlobalRead(const wxString& key, wxString* str) const; bool GlobalRead(const wxString& key, wxString* str, const wxString& defaultVal, bool writeBackIfAbsent = false); bool GlobalRead(const wxString& key, long* l, long defaultVal, bool writeBackIfAbsent = false); bool GlobalWrite(const wxString& key, const wxString& value); bool GlobalWrite(const wxString& key, const wxChar* value); bool GlobalWrite(const wxString& key, long value); bool GlobalWrite(const wxString& key, bool value); bool ProfileExists(const wxChar* strName) const; bool ProfileExists(wxString& strName) const; bool ProfileRead(const wxString& key, bool* b) const; bool ProfileRead(const wxString& key, bool* b, bool defaultVal, bool writeBackIfAbsent = false); bool ProfileRead(const wxString& key, wxString* str) const; bool ProfileRead(const wxString& key, wxString* str, const wxString& defaultVal, bool writeBackIfAbsent = false); bool ProfileRead(const wxString& key, long* l) const; bool ProfileRead(const wxString& key, long* l, long defaultVal, bool writeBackIfAbsent = false); bool ProfileWrite(const wxString& key, const wxString& value); bool ProfileWrite(const wxString& key, const wxChar* value); bool ProfileWrite(const wxString& key, long value); bool ProfileWrite(const wxString& key, bool value); bool ProfileDeleteEntry(const wxString& key, bool bDeleteGroupIfEmpty = true); /** Returns NULL if not found in global profile (or found but invalid). */ const NewsData* NewsRead(const wxString& newsSource) const; void NewsWrite(const wxString& newsSource, const NewsData& data); enum SaveDialogContext { ON_PROFILE_SWITCH, ON_PROFILE_CREATE, ON_EXIT }; static const wxString GetSaveDialogCaptionText(SaveDialogContext context, const wxString& profileName); static const wxString GetSaveDialogMessageText(SaveDialogContext context, const wxString& profileName); bool CreateProfile(const wxString& newProfileName, const wxString& cloneFromProfileName = wxEmptyString); bool CreateProfile(const wxString& newProfileName, const wxFileName& sourceFile); bool CreateProfile(const wxString& newProfileName, const wxFileConfig *sourceConfig); bool DeleteProfile(wxString name); bool DoesProfileExist(wxString name); bool SwitchTo(wxString name); void SaveCurrentProfile(bool quiet = false); void RevertCurrentProfile(); bool HasUnsavedChanges(); inline bool NeedToPromptToSave() { return (!this->isAutoSaving) && this->HasUnsavedChanges(); } void SetAutoSave(bool value) { this->isAutoSaving = value; } void AddEventHandler(wxEvtHandler *handler); void RemoveEventHandler(wxEvtHandler *handler); enum RegistryCodes { InvalidParameters = -1, NoError = 0, AccessDenied, FileIsNotOK, InputFileDoesNotExist, SupportNotCompiledIn, UnknownError, }; RegistryCodes PushCurrentProfile(); //!< push current profile into registry static const wxString& DEFAULT_PROFILE_NAME; private: static ProMan* proman; static bool isInitialized; static Flags flags; wxFileConfig* currentProfile; wxString currentProfileName; bool CreateNewProfile(wxString newName); static wxString GenerateNewProfileFileName(); static RegistryCodes PushProfile(wxFileConfig *cfg); //!< push profile into registry static RegistryCodes PullProfile(wxFileConfig *cfg); //!< pull profile from registry static void CopyConfig(const wxConfigBase& src, wxConfigBase &dest, const bool includeMainGroup = true, const wxString path = _T("/")); static void CopyConfigEntry(const wxConfigBase& src, wxConfigBase& dest, const wxString path, const wxString entry); static void ClearConfig(wxConfigBase& cfg); static bool AreConfigsEqual(wxConfigBase& cfg1, wxConfigBase& cfg2); static bool IsConfigSubset(wxConfigBase& cfg1, wxConfigBase& cfg2, const wxString path = _T("/")); static bool AreEntriesEqual(const wxConfigBase& cfg1, const wxConfigBase& cfg2, const wxString path, const wxString entry); static void LogConfigContents(wxConfigBase& cfg, const wxString path = _T("/"), const bool includeWxWindows = false); static void TestConfigFunctions(wxConfigBase& src); ProMan(); void SaveProfilesBeforeExiting(); NewsMap newsMap; void LoadNewsMapFromGlobalProfile(); void SaveNewsMapToGlobalProfile(); ProfileMap profiles; //!< The profiles. Indexed by Name; wxFileConfig* globalProfile; //!< Global profile settings, like language, or proxy wxString privateCopyFilename; //!< Name of file used for private copy wxFileConfig* privateCopy; //!< Private copy, used in determining whether current profile has unsaved changes void ResetPrivateCopy(); bool isAutoSaving; //!< Are we auto saving the profiles? void GenerateChangeEvent(); void GenerateCurrentProfileChangedEvent(); EventHandlers eventHandlers; }; // These operators should be refactored into a common // macro rather than copied for the next enum flag inline ProMan::Flags operator|(ProMan::Flags a, ProMan::Flags b) { return static_cast( static_cast(a) | static_cast(b) ); } inline ProMan::Flags operator&(ProMan::Flags a, ProMan::Flags b) { return static_cast( static_cast(a) & static_cast(b) ); } #endifwxlauncher-0.9.4/code/apis/ProfileManagerOperator.cpp0000644000000000000000000000244312155177255022640 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "apis/ProfileManager.h" #include "apis/ProfileManagerOperator.h" #include "wxLauncherApp.h" #include "global/MemoryDebugging.h" int ProManOperator::RunProfileOperator(ProManOperator::profileOperator op) { wxLauncher &app = wxGetApp(); if (op == add) { wxFileName sourceFile(app.mFileOperand); if (ProMan::GetProfileManager()-> CreateProfile(app.mProfileOperand, sourceFile)) { return 0; } } else if (op == select) { ProMan::GetProfileManager()-> SwitchTo(app.mProfileOperand); return 0; } return 1; } wxlauncher-0.9.4/code/apis/ProfileManagerOperator.h0000644000000000000000000000214212155177255022301 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROFILEMANAGEROPERATOR_H #define PROFILEMANAGEROPERATOR_H /** ProManOperator: Mechanism to manipulate profiles through cmd line options. none indicates that this feature is not in use and that normal operation should occur instead. */ namespace ProManOperator { enum profileOperator { none = 0, add, select, invalid }; int RunProfileOperator(profileOperator op); }; #endifwxlauncher-0.9.4/code/apis/ProfileProxy.cpp0000644000000000000000000003041012155177255020666 0ustar rootroot00000000000000/* Copyright (C) 2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/ProfileProxy.h" #include "apis/CmdLineManager.h" #include "apis/FlagListManager.h" #include "apis/ProfileManager.h" #include "controls/LightingPresets.h" #include "global/ProfileKeys.h" #include DEFINE_EVENT_TYPE(EVT_PROXY_RESET); DEFINE_EVENT_TYPE(EVT_PROXY_FLAG_DATA_READY); #include // required magic incantation WX_DEFINE_LIST(ProxyEventHandlers); void ProfileProxy::RegisterProxyReset(wxEvtHandler *handler) { wxASSERT(ProfileProxy::IsInitialized()); wxASSERT_MSG(this->resetEventHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterProxyReset(): Handler at %p already registered."), handler)); this->resetEventHandlers.Append(handler); } void ProfileProxy::UnRegisterProxyReset(wxEvtHandler *handler) { wxASSERT(ProfileProxy::IsInitialized()); wxASSERT_MSG(this->resetEventHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterProxyReset(): Handler at %p not registered."), handler)); this->resetEventHandlers.DeleteObject(handler); } void ProfileProxy::RegisterProxyFlagDataReady(wxEvtHandler *handler) { wxASSERT(ProfileProxy::IsInitialized()); wxASSERT_MSG(this->readyEventHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterProxyFlagDataReady(): Handler at %p already registered."), handler)); this->readyEventHandlers.Append(handler); } void ProfileProxy::UnRegisterProxyFlagDataReady(wxEvtHandler *handler) { wxASSERT(ProfileProxy::IsInitialized()); wxASSERT_MSG(this->readyEventHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterProxyFlagDataReady(): Handler at %p not registered."), handler)); this->readyEventHandlers.DeleteObject(handler); } void ProfileProxy::GenerateProxyReset() { wxASSERT(ProfileProxy::IsInitialized()); wxCommandEvent event(EVT_PROXY_RESET, wxID_NONE); wxLogDebug(_T("Generating EVT_PROXY_RESET event")); for (ProxyEventHandlers::iterator iter = this->resetEventHandlers.begin(), end = this->resetEventHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_PROXY_RESET event to %p"), current); } } void ProfileProxy::GenerateProxyFlagDataReady() { wxASSERT(ProfileProxy::IsInitialized()); wxCommandEvent event(EVT_PROXY_FLAG_DATA_READY, wxID_NONE); wxLogDebug(_T("Generating EVT_PROXY_FLAG_DATA_READY event")); for (ProxyEventHandlers::iterator iter = this->readyEventHandlers.begin(), end = this->readyEventHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_PROXY_FLAG_DATA_READY event to %p"), current); } } ProfileProxy* ProfileProxy::proxy = NULL; ProfileProxy* ProfileProxy::GetProxy() { wxCHECK_MSG(IsInitialized(), NULL, _T("GetProxy() called when proxy wasn't initialized.")); return proxy; } bool ProfileProxy::Initialize() { wxASSERT(!IsInitialized()); proxy = new ProfileProxy(); return true; } void ProfileProxy::DeInitialize() { wxASSERT(IsInitialized()); ProfileProxy* temp = proxy; proxy = NULL; delete temp; } bool ProfileProxy::IsInitialized() { return proxy != NULL; } ProfileProxy::ProfileProxy() : isFlagDataReady(false) { FlagListManager::RegisterFlagFileProcessingStatusChanged(this); } ProfileProxy::~ProfileProxy() { FlagListManager::UnRegisterFlagFileProcessingStatusChanged(this); } BEGIN_EVENT_TABLE(ProfileProxy, wxEvtHandler) EVT_COMMAND(wxID_NONE, EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED, ProfileProxy::OnFlagFileProcessingStatusChanged) END_EVENT_TABLE() void ProfileProxy::OnFlagFileProcessingStatusChanged(wxCommandEvent &event) { wxASSERT(event.GetEventType() == EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED); const FlagListManager::FlagFileProcessingStatus status = static_cast(event.GetInt()); if (status == FlagListManager::FLAG_FILE_PROCESSING_OK) { wxCHECK_RET(!this->IsFlagDataReady(), _T("ProfileProxy received FLAG_FILE_PROCESSING_OK when flag data is ready.")); const ProxyFlagData* proxyData = FlagListManager::GetFlagListManager()->GetProxyFlagData(); wxCHECK_RET(proxyData != NULL, _T("ProfileProxy: flag file processing succeeded but proxy data was NULL.")); this->ProcessFlagData(*proxyData); this->ProcessFlagLine(); this->isFlagDataReady = true; GenerateProxyFlagDataReady(); delete proxyData; } else if (status == FlagListManager::FLAG_FILE_PROCESSING_RESET) { this->Reset(); this->GenerateProxyReset(); } } void ProfileProxy::SetFlag(const wxString& flag, const bool isChecked) { wxCHECK_RET(this->IsFlagDataReady(), _T("SetFlag() called when proxy flag data isn't ready.")); wxCHECK_RET(this->flagMap.count(flag) > 0, wxString::Format(_T("SetFlag(): given unknown flag %s."), flag.c_str())); const int flagIndex = this->flagMap.find(flag)->second; wxASSERT_MSG(VerifyEnabledFlagsForFlag(flag, flagIndex).IsEmpty(), VerifyEnabledFlagsForFlag(flag, flagIndex)); if (isChecked) { this->enabledFlags[flagIndex] = flag; } else { this->enabledFlags.erase(flagIndex); } this->WriteFlagLineToProfile(); CmdLineManager::GenerateCmdLineChanged(); } std::vector ProfileProxy::GetEnabledFlags() const { wxCHECK_MSG(this->IsFlagDataReady(), std::vector(), _T("GetEnabledFlags() called when proxy flag data isn't ready.")); std::vector flags; for (std::map::const_iterator it = enabledFlags.begin(), end = enabledFlags.end(); it != end; ++it) { flags.push_back(it->second); } return flags; } wxString ProfileProxy::GetEnabledFlagsString() const { wxCHECK_MSG(this->IsFlagDataReady(), wxEmptyString, _T("GetEnabledFlagsString() called when proxy flag data isn't ready.")); std::vector flags(this->GetEnabledFlags()); wxString flagStr; for (std::vector::const_iterator it = flags.begin(), end = flags.end(); it != end; ++it) { if (!flagStr.IsEmpty()) { flagStr += _T(" "); } flagStr += *it; } return flagStr; } // GenerateCustomFlagsChanged() is used to update the custom flags box // GenerateCmdLineChanged() is used to update the current command line void ProfileProxy::SetCustomFlags(const wxString& customFlags, const bool updateTextCtrl) { this->customFlags = customFlags; this->WriteFlagLineToProfile(); if (updateTextCtrl) { CmdLineManager::GenerateCustomFlagsChanged(); } CmdLineManager::GenerateCmdLineChanged(); } bool ProfileProxy::HasLightingPreset() const { return ProMan::GetProfileManager()->ProfileExists(PRO_CFG_LIGHTING_PRESET); } wxString ProfileProxy::GetLightingPresetName() const { wxString presetName; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_LIGHTING_PRESET, &presetName, wxEmptyString); return presetName; } void ProfileProxy::SetLightingPreset(const wxString& presetName) { ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_LIGHTING_PRESET, presetName); CmdLineManager::GenerateCmdLineChanged(); } void ProfileProxy::CopyPresetToCustomFlags() { wxCHECK_RET(this->IsFlagDataReady(), _T("CopyPresetToCustomFlags() called when proxy flag data isn't ready.")); wxCHECK_RET(this->HasLightingPreset(), _T("CopyPresetToCustomFlags(): profile has no preset entry.")); wxString presetName(this->GetLightingPresetName()); wxCHECK_RET(!presetName.IsEmpty(), _T("CopyPresetToCustomFlags(): preset name is empty.")); const wxString& flagSet = LightingPresets::PresetNameToPresetFlagSet(presetName); if (!flagSet.IsEmpty()) { this->SetCustomFlags(flagSet + _T(" ") + this->GetCustomFlags()); } else { wxLogError( _T("CopyPresetToCustomFlags(): retrieved flag set was empty for name %s"), presetName.c_str()); } } bool ProfileProxy::IsProfileInitialized() const { bool isProfileInitialized; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_MAIN_INITIALIZED, &isProfileInitialized, false); return isProfileInitialized; } void ProfileProxy::FinishProfileInitialization() const { wxCHECK_RET(!this->IsProfileInitialized(), _T("FinishProfileInitialization(): profile already initialized.")); if (!ProMan::GetProfileManager()->ProfileExists(PRO_CFG_TC_CURRENT_FLAG_LINE)) { ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_TC_CURRENT_FLAG_LINE, wxEmptyString); } wxLogDebug(_T("Autosaving newly initialized profile '%s'."), ProMan::GetProfileManager()->GetCurrentName().c_str()); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_MAIN_INITIALIZED, true); ProMan::GetProfileManager()->SaveCurrentProfile(true); } void ProfileProxy::WriteFlagLineToProfile() const { wxCHECK_RET(this->IsFlagDataReady(), _T("WriteFlagLineToProfile() called when proxy flag data isn't ready.")); wxString newFlagLine( this->GetEnabledFlagsString() + _T(" ") + this->GetCustomFlags()); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_TC_CURRENT_FLAG_LINE, newFlagLine); } void ProfileProxy::ProcessFlagData(const ProxyFlagData& data) { wxASSERT(!data.IsEmpty()); for (ProxyFlagData::const_iterator it = data.begin(), end = data.end(); it != end; ++it) { const ProxyFlagDataItem& item = **it; wxASSERT_MSG(this->flagMap.count(item.GetFlagString()) == 0, wxString::Format(_T("ProcessFlagData(): attempted to add flag %s twice."), item.GetFlagString().c_str())); this->flagMap[item.GetFlagString()] = item.GetFlagIndex(); } } void ProfileProxy::ProcessFlagLine() { wxASSERT(!this->flagMap.empty()); wxASSERT(this->enabledFlags.empty()); wxASSERT(this->customFlags.IsEmpty()); wxASSERT(!this->IsFlagDataReady()); wxString flagLine; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_TC_CURRENT_FLAG_LINE, &flagLine, wxEmptyString); wxStringTokenizer tokenizer(flagLine, _T(" ")); while(tokenizer.HasMoreTokens()) { wxString flag(tokenizer.GetNextToken()); if (this->flagMap.count(flag) > 0) { int flagIndex = this->flagMap.find(flag)->second; this->enabledFlags[flagIndex] = flag; } else { if (!this->customFlags.IsEmpty()) { this->customFlags += _T(" "); } this->customFlags += flag; } } } std::vector ProfileProxy::FindInEnabledFlags(const wxString& flag) const { std::vector indices; for (std::map::const_iterator it = this->enabledFlags.begin(); it != this->enabledFlags.end(); ++it) { const std::pair& entry = *it; if (entry.second == flag) { indices.push_back(entry.first); } } return indices; } wxString ProfileProxy::VerifyEnabledFlagsForFlag( const wxString& flag, const int flagIndex) const { const std::vector indicesFound(this->FindInEnabledFlags(flag)); if (indicesFound.size() > 1) { wxString indicesStr; for (std::vector::const_iterator it = indicesFound.begin() + 1; it != indicesFound.end(); ++it) { if (!indicesStr.IsEmpty()) { indicesStr += _T(", "); } indicesStr += wxString::Format(_T("%d"), *it); } return wxString::Format( _T("Found flag %s at indices %s."), flag.c_str(), indicesStr.c_str()); } if (indicesFound.size() == 1 && indicesFound[0] != flagIndex) { return wxString::Format(_T("Found flag %s at index %d instead of index %d."), flag.c_str(), indicesFound[0], flagIndex); } if ((this->enabledFlags.count(flagIndex) > 1) && (this->enabledFlags.find(flagIndex)->second != flag)) { return wxString::Format(_T("Found flag %s at index %d instead of flag %s."), flag.c_str(), flagIndex, this->enabledFlags.find(flagIndex)->second.c_str()); } return wxEmptyString; // no errors found } void ProfileProxy::Reset() { this->flagMap.clear(); this->enabledFlags.clear(); this->customFlags.Empty(); this->isFlagDataReady = false; } wxlauncher-0.9.4/code/apis/ProfileProxy.h0000644000000000000000000001072412155177255020341 0ustar rootroot00000000000000/* Copyright (C) 2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROFILEPROXY_H #define PROFILEPROXY_H #include #include "datastructures/FlagFileData.h" #include #include /* ProfileProxy - a high-level API for the data in the current profile. Other classes should use the ProfileProxy instead of the ProfileManager whenever possible. */ /** Indicates the proxy has reset itself. */ DECLARE_EVENT_TYPE(EVT_PROXY_RESET, wxID_ANY); /** Indicates the proxy has successfully processed flag data from the flag file and profile. */ DECLARE_EVENT_TYPE(EVT_PROXY_FLAG_DATA_READY, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, ProxyEventHandlers); WX_DECLARE_STRING_HASH_MAP(int, FlagStringToIndexMap); class ProfileProxy: public wxEvtHandler { public: static ProfileProxy* GetProxy(); static bool Initialize(); static void DeInitialize(); static bool IsInitialized(); void RegisterProxyReset(wxEvtHandler *handler); void UnRegisterProxyReset(wxEvtHandler *handler); void RegisterProxyFlagDataReady(wxEvtHandler *handler); void UnRegisterProxyFlagDataReady(wxEvtHandler *handler); virtual ~ProfileProxy(); void OnFlagFileProcessingStatusChanged(wxCommandEvent &event); /** Indicates whether the proxy has processed flag data for the current profile's FSO binary and flag line. */ bool IsFlagDataReady() const { return this->isFlagDataReady; } /** Sets a flag from the flag list. */ void SetFlag(const wxString& flag, bool isChecked); /** Gets the enabled flag list flags as individual flag strings. */ std::vector GetEnabledFlags() const; /** Gets the enabled flag list flags as a single string. */ wxString GetEnabledFlagsString() const; /** Gets the custom flags. */ const wxString& GetCustomFlags() const { return this->customFlags; } /** Sets the custom flags. */ void SetCustomFlags(const wxString& customFlags, bool updateTextCtrl = true); /** Indicates whether the current profile has a lighting preset entry. */ bool HasLightingPreset() const; /** Gets the name of the selected lighting preset. Returns an empty string if no lighting preset is in the profile or if the preset name stored in the profile is empty. */ wxString GetLightingPresetName() const; /** Sets the current lighting preset. */ void SetLightingPreset(const wxString& presetName); /** Prepends the current lighting preset to custom flags The profile must have a non-empty lighting preset name.*/ void CopyPresetToCustomFlags(); /** Returns whether the current profile has been initialized. */ bool IsProfileInitialized() const; /** Finishes initialization of the current profile. The profile must not already be initialized. */ void FinishProfileInitialization() const; private: static ProfileProxy* proxy; ProfileProxy(); ProxyEventHandlers resetEventHandlers; ProxyEventHandlers readyEventHandlers; void GenerateProxyReset(); void GenerateProxyFlagDataReady(); /** Writes both flag list flags and custom flags to profile. */ void WriteFlagLineToProfile() const; /** Processes data extracted from the flag file. */ void ProcessFlagData(const ProxyFlagData& data); /** Process the current profile's flag line, using the data extracted from the flag file. */ void ProcessFlagLine(); /** Returns all indices (if any) where flag is found in enabledFlags. */ std::vector FindInEnabledFlags(const wxString& flag) const; /** Verifies that the flag is either not in enabledFlags or at its flagIndex. Returns an empty string on success, an error message otherwise. */ wxString VerifyEnabledFlagsForFlag(const wxString& flag, int flagIndex) const; void Reset(); std::map enabledFlags; // ordered, flagIndex to flagString FlagStringToIndexMap flagMap; wxString customFlags; bool isFlagDataReady; DECLARE_EVENT_TABLE() }; #endif wxlauncher-0.9.4/code/apis/RegistryProfileManager.cpp0000644000000000000000000007765612155177255022677 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/ProfileManager.h" #include "apis/PlatformProfileManager.h" #include "global/BasicDefaults.h" #include "global/ProfileKeys.h" #include "global/RegistryKeys.h" #include "generated/configure_launcher.h" #include #include #include #include #if PLATFORM_USES_REGISTRY == 1 #include #endif #include "global/MemoryDebugging.h" #if PLATFORM_USES_REGISTRY == 1 #define ReturnChecker(retvalue, location) \ if ( retvalue != ERROR_SUCCESS ) {\ LPWSTR message = NULL;\ DWORD nchars = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\ NULL,\ retvalue,\ 0,\ (LPWSTR)&message,\ 0,\ NULL);\ if ( nchars == 0 ) {\ wxLogFatalError(_T("An error occurred, but unable to generate error message"));\ } else {\ wxMBConvUTF16 conv;\ wxString msg(message, conv, nchars);\ wxLogError(_T("Unhandled error in setting HKLM key in registry! %s"), msg);\ }\ return ProMan::UnknownError;\ } #define UNKOWN_ERROR_MSG _T("Unhandled error number %d from query above line %d") #define REG_DATA_NOT_DWORD _T("Registry key lookup above line %d is not a DWORD") #define REG_DATA_NOT_STRING _T("Registry key lookup above line %d is not a DWORD") #endif /* File contains the Win32 incatations for Pushing and Pulling the passed profile to/from the registry. */ ProMan::RegistryCodes RegistryPushProfile(wxFileConfig *cfg) { #if PLATFORM_USES_REGISTRY == 1 LONG ret = ERROR_SUCCESS; HKEY regHandle = 0; ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, REG_KEY_FOLDER_LOCATION, 0, // Reserved NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, // default security. ®Handle, NULL // I don't care if the key was created before opening ); #ifdef REGISTRY_HELPER // Do not attempt to call registry_helper.exe if already in registry_helper.exe if ( ret != ERROR_SUCCESS ) { #else if ( ret != ERROR_SUCCESS && ret != ERROR_ACCESS_DENIED) { #endif // Call failed LPWSTR message = NULL; DWORD nchars = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, ret, 0, (LPWSTR)&message, 0, NULL); if ( nchars == 0 ) { wxLogFatalError(_T("An error occurred, but unable to generate error message")); } else { wxMBConvUTF16 conv; wxString msg(message, conv, nchars); wxLogError(_T("Unhandled error in creating HKLM key in registry! %s"), msg); LocalFree(message); } return ProMan::AccessDenied; #ifndef REGISTRY_HELPER } else if ( ret == ERROR_ACCESS_DENIED ) { // Only try calling registry_helper.exe if not already in registry_helper.exe wxString tempdir(L"profilespit"); wxFile outfile; wxString tempfile = wxFileName::CreateTempFileName(tempdir, &outfile); wxLogDebug(_T("Launching helper on %s"), tempfile.c_str()); wxFileOutputStream out(outfile); cfg->Save(out); out.Close(); wxArrayString processOutput, processError; long ret = wxExecute(wxString::Format(_T("registry_helper.exe push \"%s\""), tempfile), processOutput, processError); wxLogDebug(_T(" Registry helper returned 0x%08X"), ret); for(size_t i = 0; i < processError.size(); i++) { wxLogInfo(_T("EREG:%s"), processError[i]); } for(size_t i = 0; i < processOutput.size(); i++) { wxLogInfo(_T(" REG:%s"), processOutput[i]); } ::wxRemoveFile(tempfile); if ( ret == ProMan::NoError ) { // no error so just return, because the other process did what I needed. return ProMan::NoError; } else { wxLogError(_T("Unable to write FS2 Open settings to the registry (0x%08X)"), ret); return static_cast(ret); } #endif } // Video int width, height, bitdepth; cfg->Read(PRO_CFG_VIDEO_RESOLUTION_WIDTH, &width, DEFAULT_VIDEO_RESOLUTION_WIDTH); cfg->Read(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, &height, DEFAULT_VIDEO_RESOLUTION_HEIGHT); cfg->Read(PRO_CFG_VIDEO_BIT_DEPTH, &bitdepth, DEFAULT_VIDEO_BIT_DEPTH); wxString videocardValue = wxString::Format(_T("OGL -(%dx%d)x%d bit"), width, height, bitdepth); ret = RegSetValueExW( regHandle, REG_KEY_VIDEO_RESOLUTION_DEPTH, 0, REG_SZ, (BYTE*)videocardValue.c_str(), (videocardValue.size() + 1)*2); ReturnChecker(ret, __LINE__); wxString filterMethod; cfg->Read(PRO_CFG_VIDEO_TEXTURE_FILTER, &filterMethod, DEFAULT_VIDEO_TEXTURE_FILTER); int filterMethodValue = ( filterMethod.StartsWith(_T("Bilinear"))) ? 0 : 1; ret = RegSetValueExW( regHandle, REG_KEY_VIDEO_TEXTURE_FILTER, 0, REG_DWORD, (BYTE*)&filterMethodValue, sizeof(filterMethodValue)); ReturnChecker(ret, __LINE__); int oglAnisotropicFilterInt; cfg->Read(PRO_CFG_VIDEO_ANISOTROPIC, &oglAnisotropicFilterInt, DEFAULT_VIDEO_ANISOTROPIC); // Since FSO expects anisotropic to be a string, we must convert it wxString oglAnisotropicFilter( wxString::Format(_T("%d"), oglAnisotropicFilterInt)); ret = RegSetValueExW( regHandle, REG_KEY_VIDEO_ANISOTROPIC, 0, REG_SZ, (BYTE*)oglAnisotropicFilter.c_str(), (oglAnisotropicFilter.size() + 1)*2); ReturnChecker(ret, __LINE__); int oglAntiAliasSample; cfg->Read(PRO_CFG_VIDEO_ANTI_ALIAS, &oglAntiAliasSample, DEFAULT_VIDEO_ANTI_ALIAS); ret = RegSetValueExW( regHandle, REG_KEY_VIDEO_ANTI_ALIAS, 0, REG_DWORD, (BYTE*)&oglAntiAliasSample, sizeof(oglAntiAliasSample)); ReturnChecker(ret, __LINE__); // Audio wxString soundDevice; cfg->Read(PRO_CFG_OPENAL_DEVICE, &soundDevice, DEFAULT_AUDIO_OPENAL_DEVICE); ret = RegSetValueExW( regHandle, REG_KEY_AUDIO_OPENAL_DEVICE, 0, REG_SZ, (BYTE*)soundDevice.c_str(), (soundDevice.size() + 1)*2); ReturnChecker(ret, __LINE__); // Audio folder (for new sound code settings) HKEY audioRegHandle = 0; ret = RegCreateKeyExW( regHandle, REG_KEY_AUDIO_FOLDER_REGISTRY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &audioRegHandle, NULL); // just want handle, don't care if it was created or opened ReturnChecker(ret, __LINE__); wxString playbackDevice; cfg->Read( PRO_CFG_OPENAL_DEVICE, &playbackDevice, DEFAULT_AUDIO_OPENAL_PLAYBACK_DEVICE); ret = RegSetValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE, 0, REG_SZ, (BYTE*)playbackDevice.c_str(), (playbackDevice.size() + 1)*2); ReturnChecker(ret, __LINE__); wxString captureDevice; bool hasEntry = cfg->Read( PRO_CFG_OPENAL_CAPTURE_DEVICE, &captureDevice, DEFAULT_AUDIO_OPENAL_CAPTURE_DEVICE); if (hasEntry) { ret = RegSetValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE, 0, REG_SZ, (BYTE*)captureDevice.c_str(), (captureDevice.size() + 1)*2); ReturnChecker(ret, __LINE__); } int enableEFX; hasEntry = cfg->Read(PRO_CFG_OPENAL_EFX, &enableEFX, DEFAULT_AUDIO_OPENAL_EFX); if (hasEntry) { ret = RegSetValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_EFX, 0, REG_DWORD, (BYTE*)&enableEFX, sizeof(enableEFX)); ReturnChecker(ret, __LINE__); } int sampleRate; cfg->Read( PRO_CFG_OPENAL_SAMPLE_RATE, &sampleRate, DEFAULT_AUDIO_OPENAL_SAMPLE_RATE); if (sampleRate != DEFAULT_AUDIO_OPENAL_SAMPLE_RATE) { ret = RegSetValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_SAMPLE_RATE, 0, REG_DWORD, (BYTE*)&sampleRate, sizeof(sampleRate)); ReturnChecker(ret, __LINE__); } RegCloseKey(audioRegHandle); // Speech int speechVoice; cfg->Read(PRO_CFG_SPEECH_VOICE, &speechVoice, DEFAULT_SPEECH_VOICE); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_VOICE, 0, REG_DWORD, (BYTE*)&speechVoice, sizeof(speechVoice)); ReturnChecker(ret, __LINE__); int speechVolume; cfg->Read(PRO_CFG_SPEECH_VOLUME, &speechVolume, DEFAULT_SPEECH_VOLUME); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_VOLUME, 0, REG_DWORD, (BYTE*)&speechVolume, sizeof(speechVolume)); ReturnChecker(ret, __LINE__); int inTechroom, inBriefings, inGame, inMulti; cfg->Read(PRO_CFG_SPEECH_IN_TECHROOM, &inTechroom, DEFAULT_SPEECH_IN_TECHROOM); cfg->Read(PRO_CFG_SPEECH_IN_BRIEFINGS, &inBriefings, DEFAULT_SPEECH_IN_BRIEFINGS); cfg->Read(PRO_CFG_SPEECH_IN_GAME, &inGame, DEFAULT_SPEECH_IN_GAME); cfg->Read(PRO_CFG_SPEECH_IN_MULTI, &inMulti, DEFAULT_SPEECH_IN_MULTI); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_IN_TECHROOM, 0, REG_DWORD, (BYTE*)&inTechroom, sizeof(inTechroom)); ReturnChecker(ret, __LINE__); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_IN_BRIEFINGS, 0, REG_DWORD, (BYTE*)&inBriefings, sizeof(inBriefings)); ReturnChecker(ret, __LINE__); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_IN_GAME, 0, REG_DWORD, (BYTE*)&inGame, sizeof(inGame)); ReturnChecker(ret, __LINE__); ret = RegSetValueExW( regHandle, REG_KEY_SPEECH_IN_MULTI, 0, REG_DWORD, (BYTE*)&inMulti, sizeof(inMulti)); ReturnChecker(ret, __LINE__); // Joystick int currentJoystick; cfg->Read(PRO_CFG_JOYSTICK_ID, ¤tJoystick, DEFAULT_JOYSTICK_ID); ret = RegSetValueExW( regHandle, REG_KEY_JOYSTICK_ID, 0, REG_DWORD, (BYTE*)¤tJoystick, sizeof(currentJoystick)); ReturnChecker(ret, __LINE__); int joystickForceFeedback; cfg->Read( PRO_CFG_JOYSTICK_FORCE_FEEDBACK, &joystickForceFeedback, DEFAULT_JOYSTICK_FORCE_FEEDBACK); ret = RegSetValueExW( regHandle, REG_KEY_JOYSTICK_FORCE_FEEDBACK, 0, REG_DWORD, (BYTE*)&joystickForceFeedback, sizeof(joystickForceFeedback)); ReturnChecker(ret, __LINE__); int joystickHit; cfg->Read(PRO_CFG_JOYSTICK_DIRECTIONAL, &joystickHit, DEFAULT_JOYSTICK_DIRECTIONAL); ret = RegSetValueExW( regHandle, REG_KEY_JOYSTICK_DIRECTIONAL, 0, REG_DWORD, (BYTE*)&joystickHit, sizeof(joystickHit)); ReturnChecker(ret, __LINE__); // Network wxString networkConnectionValue; cfg->Read(PRO_CFG_NETWORK_TYPE, &networkConnectionValue, DEFAULT_NETWORK_TYPE); ret = RegSetValueExW( regHandle, REG_KEY_NETWORK_TYPE, 0, REG_SZ, (BYTE*)networkConnectionValue.c_str(), (networkConnectionValue.size() + 1)*2); ReturnChecker(ret, __LINE__); wxString connectionSpeedValue; cfg->Read(PRO_CFG_NETWORK_SPEED, &connectionSpeedValue, DEFAULT_NETWORK_SPEED); ret = RegSetValueExW( regHandle, REG_KEY_NETWORK_SPEED, 0, REG_SZ, (BYTE*)connectionSpeedValue.c_str(), (connectionSpeedValue.size() + 1)*2); ReturnChecker(ret, __LINE__); int forcedport; cfg->Read(PRO_CFG_NETWORK_PORT, &forcedport, DEFAULT_NETWORK_PORT); if (forcedport != DEFAULT_NETWORK_PORT) { ret = RegSetValueExW( regHandle, REG_KEY_NETWORK_PORT, 0, REG_DWORD, (BYTE*)&forcedport, sizeof(forcedport)); ReturnChecker(ret, __LINE__); } else { wxLogDebug(_T("Forced Port is default. Clearing entry if it exists.")); ret = RegDeleteValueW( regHandle, REG_KEY_NETWORK_PORT); if (ret != ERROR_FILE_NOT_FOUND) { // ignore if entry doesn't exist ReturnChecker(ret, __LINE__); } } wxString networkIP; cfg->Read(PRO_CFG_NETWORK_IP, &networkIP, DEFAULT_NETWORK_IP); // Network folder (for custom IP address) HKEY networkRegHandle = 0; ret = RegCreateKeyExW( regHandle, REG_KEY_NETWORK_FOLDER_REGISTRY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &networkRegHandle, NULL); // just want handle, don't care if it was created or opened ReturnChecker(ret, __LINE__); if (networkIP != DEFAULT_NETWORK_IP) { ret = RegSetValueExW( networkRegHandle, REG_KEY_NETWORK_IP, 0, REG_SZ, (BYTE*)networkIP.c_str(), (networkIP.size()+1)*2); ReturnChecker(ret, __LINE__); } else { wxLogDebug(_T("Custom IP is default. Clearing entry if it exists.")); ret = RegDeleteValueW( networkRegHandle, REG_KEY_NETWORK_IP); if (ret != ERROR_FILE_NOT_FOUND) { // ignore if entry doesn't exist ReturnChecker(ret, __LINE__); } } RegCloseKey(networkRegHandle); RegCloseKey(regHandle); return PushCmdlineFSO(cfg); #else // PLATFORM_USES_REGISTRY return ProMan::SupportNotCompiledIn; #endif // PLATFORM_USES_REGISTRY } ProMan::RegistryCodes RegistryPullProfile(wxFileConfig *cfg) { #if PLATFORM_USES_REGISTRY == 1 LONG ret = ERROR_SUCCESS; HKEY regHandle = 0; // try opening registry with write privleges even though I do not need // them because I want to trigger registry_helper here just like when // tring to write the settings. ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, REG_KEY_FOLDER_LOCATION, 0, // Reserved NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, // default security. ®Handle, NULL // I don't care if the key was created before opening ); #ifdef REGISTRY_HELPER // Do not attempt to call registry_helper.exe if already in registry_helper.exe if ( ret != ERROR_SUCCESS ) { #else if ( ret != ERROR_SUCCESS && ret != ERROR_ACCESS_DENIED) { #endif // Call failed LPWSTR message = NULL; DWORD nchars = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, ret, 0, (LPWSTR)&message, 0, NULL); if ( nchars == 0 ) { wxLogFatalError(_T("An error occurred, but unable to generate error message")); } else { wxMBConvUTF16 conv; wxString msg(message, conv, nchars); wxLogError(_T("Unhandled error in creating HKLM key in registry! %s"), msg); LocalFree(message); } return ProMan::AccessDenied; #ifndef REGISTRY_HELPER } else if ( ret == ERROR_ACCESS_DENIED ) { // Only try calling registry_helper.exe if not already in registry_helper.exe wxString tempfile = wxFileName::CreateTempFileName(wxStandardPaths::Get().GetTempDir()); wxLogDebug(_T("Launching helper on %s"), tempfile); wxArrayString processOutput; long ret = wxExecute(wxString::Format(_T("registry_helper.exe pull \"%s\""), tempfile), processOutput); wxLogDebug(_T(" Registry helper returned %d"), ret); for(size_t i = 0; i < processOutput.size(); i++) { wxLogInfo(_T(" REG:%s"), processOutput[i]); } wxFileInputStream in(tempfile); wxFileConfig inConfig(in); // a hack to make the copying fo the items from the groups work, // check if the possible entries set exist in the response from registry_helper. wxString configData; if ( inConfig.Read(PRO_CFG_VIDEO_RESOLUTION_WIDTH, &configData) ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_WIDTH, configData); } if ( inConfig.Read(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, &configData) ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, configData); } if ( inConfig.Read(PRO_CFG_VIDEO_BIT_DEPTH, &configData) ) { cfg->Write(PRO_CFG_VIDEO_BIT_DEPTH, configData); } if ( inConfig.Read(PRO_CFG_VIDEO_TEXTURE_FILTER, &configData) ) { cfg->Write(PRO_CFG_VIDEO_TEXTURE_FILTER, configData); } if ( inConfig.Read(PRO_CFG_VIDEO_ANISOTROPIC, &configData) ) { cfg->Write(PRO_CFG_VIDEO_ANISOTROPIC, configData); } if ( inConfig.Read(PRO_CFG_VIDEO_ANTI_ALIAS, &configData) ) { cfg->Write(PRO_CFG_VIDEO_ANTI_ALIAS, configData); } if ( inConfig.Read(PRO_CFG_OPENAL_DEVICE, &configData) ) { cfg->Write(PRO_CFG_OPENAL_DEVICE, configData); } if ( inConfig.Read(PRO_CFG_OPENAL_CAPTURE_DEVICE, &configData) ) { cfg->Write(PRO_CFG_OPENAL_CAPTURE_DEVICE, configData); } if ( inConfig.Read(PRO_CFG_OPENAL_EFX, &configData) ) { cfg->Write(PRO_CFG_OPENAL_EFX, configData); } if ( inConfig.Read(PRO_CFG_OPENAL_SAMPLE_RATE, &configData) ) { cfg->Write(PRO_CFG_OPENAL_SAMPLE_RATE, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_VOICE, &configData) ) { cfg->Write(PRO_CFG_SPEECH_VOICE, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_VOLUME, &configData) ) { cfg->Write(PRO_CFG_SPEECH_VOLUME, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_IN_TECHROOM, &configData) ) { cfg->Write(PRO_CFG_SPEECH_IN_TECHROOM, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_IN_BRIEFINGS, &configData) ) { cfg->Write(PRO_CFG_SPEECH_IN_BRIEFINGS, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_IN_GAME, &configData) ) { cfg->Write(PRO_CFG_SPEECH_IN_GAME, configData); } if ( inConfig.Read(PRO_CFG_SPEECH_IN_MULTI, &configData) ) { cfg->Write(PRO_CFG_SPEECH_IN_MULTI, configData); } if ( inConfig.Read(PRO_CFG_JOYSTICK_ID, &configData) ) { cfg->Write(PRO_CFG_JOYSTICK_ID, configData); } if ( inConfig.Read(PRO_CFG_JOYSTICK_FORCE_FEEDBACK, &configData) ) { cfg->Write(PRO_CFG_JOYSTICK_FORCE_FEEDBACK, configData); } if ( inConfig.Read(PRO_CFG_JOYSTICK_DIRECTIONAL, &configData) ) { cfg->Write(PRO_CFG_JOYSTICK_DIRECTIONAL, configData); } if ( inConfig.Read(PRO_CFG_NETWORK_TYPE, &configData) ) { cfg->Write(PRO_CFG_NETWORK_TYPE, configData); } if ( inConfig.Read(PRO_CFG_NETWORK_SPEED, &configData) ) { cfg->Write(PRO_CFG_NETWORK_SPEED, configData); } if ( inConfig.Read(PRO_CFG_NETWORK_PORT, &configData) ) { cfg->Write(PRO_CFG_NETWORK_PORT, configData); } if ( inConfig.Read(PRO_CFG_NETWORK_IP, &configData) ) { cfg->Write(PRO_CFG_NETWORK_IP, configData); } if ( ret == ProMan::NoError ) { // no error so just return, because the other process did what I needed. return ProMan::NoError; } else { wxLogError(_T("Unable to read FS2 Open settings from the registry (%d)"), ret); return static_cast(ret); } #endif } wxMBConvUTF16 textConv; DWORD type = 0; BYTE data[MAX_PATH*2]; memset(static_cast(data), 0, MAX_PATH*2); DWORD dataSize = sizeof(data); // Video ret = RegQueryValueEx( regHandle, REG_KEY_VIDEO_RESOLUTION_DEPTH, NULL, // Reserved &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS ) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { // parses VideocardFS2open into its parts const char* data1 = reinterpret_cast(data); wxString videoCard(data1, textConv, dataSize); wxString rest, rest1, rest2, rest3; long width = 0, height = 0, bitdepth = 0; if ( videoCard.StartsWith(_T("OGL -("), &rest) ) { int xLocation = rest.Find(_T('x')); if ( xLocation != wxNOT_FOUND ) { wxString widthStr(rest.Mid(0, xLocation)); rest1 = rest.Mid(xLocation); if ( !widthStr.ToLong(&width, 10) ) { width = 0; } int bLocation = rest1.Find(_T(')')); if ( bLocation != wxNOT_FOUND ) { wxString heightStr(rest1.Mid(0, bLocation)); rest2 = rest1.Mid(bLocation+1); if ( !heightStr.ToLong(&height, 10) ) { height = 0; } int spaceLoc = rest2.Find(_T(' ')); if ( spaceLoc != wxNOT_FOUND ) { wxString bitStr(rest2.Mid(0, spaceLoc)); if ( !bitStr.ToLong(&bitdepth, 10) ) { bitdepth = 0; } } } } } if ( width > 0 ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_WIDTH, width); } if ( height > 0 ) { cfg->Write(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, height); } if ( bitdepth > 0 ) { cfg->Write(PRO_CFG_VIDEO_BIT_DEPTH, bitdepth); } } DWORD numberdata; type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_VIDEO_TEXTURE_FILTER, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_VIDEO_TEXTURE_FILTER, static_cast(numberdata)); } type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( regHandle, REG_KEY_VIDEO_ANISOTROPIC, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString ani(data1, textConv, dataSize); long anisotropic; if ( !ani.IsEmpty() && ani.ToLong(&anisotropic)) { cfg->Write(PRO_CFG_VIDEO_ANISOTROPIC, anisotropic); } } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_VIDEO_ANTI_ALIAS, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_VIDEO_ANTI_ALIAS, static_cast(numberdata)); } // Audio type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( regHandle, REG_KEY_AUDIO_OPENAL_DEVICE, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString soundDevice(data1, textConv, dataSize); if ( !soundDevice.IsEmpty() ) { cfg->Write(PRO_CFG_OPENAL_DEVICE, soundDevice); } } // Audio folder HKEY audioRegHandle = 0; ret = RegCreateKeyExW( regHandle, REG_KEY_AUDIO_FOLDER_REGISTRY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, // need write to make sure we get the virtualized registry when reading. NULL, &audioRegHandle, NULL); // just want handle, don't care if it was created or opened ReturnChecker(ret, __LINE__); type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString playbackDevice(data1, textConv, dataSize); if ( !playbackDevice.IsEmpty() && !cfg->Exists(PRO_CFG_OPENAL_DEVICE)) { cfg->Write(PRO_CFG_OPENAL_DEVICE, playbackDevice); } } type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString captureDevice(data1, textConv, dataSize); if ( !captureDevice.IsEmpty() ) { cfg->Write(PRO_CFG_OPENAL_CAPTURE_DEVICE, captureDevice); } } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_EFX, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_OPENAL_EFX, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( audioRegHandle, REG_KEY_AUDIO_OPENAL_SAMPLE_RATE, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_OPENAL_SAMPLE_RATE, static_cast(numberdata)); } // Speech type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_VOICE, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_VOICE, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_VOLUME, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_VOLUME, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_IN_TECHROOM, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_IN_TECHROOM, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_IN_BRIEFINGS, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_IN_BRIEFINGS, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_IN_GAME, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_IN_GAME, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_SPEECH_IN_MULTI, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_SPEECH_IN_MULTI, static_cast(numberdata)); } // Joystick type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_JOYSTICK_ID, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_JOYSTICK_ID, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_JOYSTICK_FORCE_FEEDBACK, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_JOYSTICK_FORCE_FEEDBACK, static_cast(numberdata)); } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_JOYSTICK_DIRECTIONAL, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_JOYSTICK_DIRECTIONAL, static_cast(numberdata)); } // Network type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( regHandle, REG_KEY_NETWORK_TYPE, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString connection(data1, textConv, dataSize); if ( !connection.IsEmpty() ) { cfg->Write(PRO_CFG_NETWORK_TYPE, connection); } } type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( regHandle, REG_KEY_NETWORK_SPEED, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString speed(data1, textConv, dataSize); if ( !speed.IsEmpty() ) { cfg->Write(PRO_CFG_NETWORK_SPEED, speed); } } type = 0; numberdata = 0; dataSize = sizeof(numberdata); ret = RegQueryValueExW( regHandle, REG_KEY_NETWORK_PORT, NULL, &type, reinterpret_cast(&numberdata), &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_DWORD && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_DWORD, __LINE__); } else { cfg->Write(PRO_CFG_NETWORK_PORT, static_cast(numberdata)); } // Network folder HKEY networkRegHandle = 0; ret = RegCreateKeyExW( regHandle, REG_KEY_NETWORK_FOLDER_REGISTRY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, // need write to make sure we get the virtualized registry when reading. NULL, &networkRegHandle, NULL); // just want handle, don't care if it was created or opened ReturnChecker(ret, __LINE__); type = 0; memset(static_cast(data), 0, MAX_PATH*2); dataSize = sizeof(data); ret = RegQueryValueExW( networkRegHandle, REG_KEY_NETWORK_IP, NULL, &type, data, &dataSize); if ( ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND ) { wxLogError(UNKOWN_ERROR_MSG, ret, __LINE__); return ProMan::UnknownError; } else if ( type != REG_SZ && ret == ERROR_SUCCESS) { wxLogWarning(REG_DATA_NOT_STRING, __LINE__); } else { const char* data1 = reinterpret_cast(data); wxString ip(data1, textConv, dataSize); if ( !ip.IsEmpty() ) { cfg->Write(PRO_CFG_NETWORK_IP, ip); } } RegCloseKey(audioRegHandle); RegCloseKey(networkRegHandle); RegCloseKey(regHandle); return ProMan::NoError; #else // PLATFORM_USES_REGISTRY return ProMan::SupportNotCompiledIn; #endif // PLATFORM_USES_REGISTRY } wxlauncher-0.9.4/code/apis/SkinManager.cpp0000644000000000000000000005341012155177255020430 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/SkinManager.h" #include "global/SkinDefaults.h" #include #include "generated/configure_launcher.h" #include "global/MemoryDebugging.h" // Last include for memory debugging DEFINE_EVENT_TYPE(EVT_TC_SKIN_CHANGED); #include // required magic incantation WX_DEFINE_LIST(TCSkinEventHandlers); TCSkinEventHandlers SkinSystem::TCSkinChangedHandlers; void SkinSystem::RegisterTCSkinChanged(wxEvtHandler *handler) { wxASSERT(SkinSystem::IsInitialized()); wxASSERT_MSG(TCSkinChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterTCSkinChanged(): Handler at %p already registered."), handler)); SkinSystem::TCSkinChangedHandlers.Append(handler); } void SkinSystem::UnRegisterTCSkinChanged(wxEvtHandler *handler) { wxASSERT(SkinSystem::IsInitialized()); wxASSERT_MSG(TCSkinChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterTCSkinChanged(): Handler at %p not registered."), handler)); SkinSystem::TCSkinChangedHandlers.DeleteObject(handler); } void SkinSystem::GenerateTCSkinChanged() { wxASSERT(SkinSystem::IsInitialized()); wxCommandEvent event(EVT_TC_SKIN_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_TC_SKIN_CHANGED event")); for (TCSkinEventHandlers::iterator iter = SkinSystem::TCSkinChangedHandlers.begin(), end = SkinSystem::TCSkinChangedHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_TC_SKIN_CHANGED event to %p"), current); } } Skin::Skin() : newsSource(NULL) { } bool Skin::SetWindowTitle(const wxString& windowTitle) { if (windowTitle.IsEmpty()) { wxLogWarning(_T("Provided window title is empty.")); return false; } else { this->windowTitle = windowTitle; return true; } } bool Skin::SetWindowIcon(const wxIcon& windowIcon) { if (!windowIcon.IsOk()) { wxLogWarning(_T("Provided window icon is not valid.")); return false; } else { this->windowIcon = windowIcon; return true; } } bool Skin::SetBanner(const wxBitmap& banner) { if (!banner.IsOk()) { wxLogWarning(_T("Provided banner is not valid.")); return false; } else if (banner.GetWidth() > SkinSystem::BannerMaxWidth) { wxLogWarning(_T("Provided banner is too wide (%d vs. %d pixels)."), banner.GetWidth(), SkinSystem::BannerMaxWidth); return false; } else if (banner.GetHeight() != SkinSystem::BannerHeight) { wxLogWarning(_T("Provided banner is incorrect height (%d vs. %d pixels)."), banner.GetHeight(), SkinSystem::BannerHeight); return false; } else { this->banner = banner; return true; } } bool Skin::SetWelcomeText(const wxString& welcomeText) { if (welcomeText.IsEmpty()) { wxLogWarning(_T("Provided welcome text is empty.")); return false; } else { this->welcomeText = welcomeText; return true; } } bool Skin::SetModImage(const wxBitmap& modImage) { if (!modImage.IsOk()) { wxLogWarning(_T("Provided mod image is not valid.")); return false; } else if ((modImage.GetWidth() > SkinSystem::ModInfoDialogImageWidth) || (modImage.GetHeight() > SkinSystem::ModInfoDialogImageHeight)) { wxLogDebug(_T("Provided mod image size %dx%d is larger than expected size %dx%d. Resizing."), modImage.GetWidth(), modImage.GetHeight(), SkinSystem::ModInfoDialogImageWidth, SkinSystem::ModInfoDialogImageHeight); wxImage tempModImage(modImage.ConvertToImage()); wxImage scaledTempModImage( tempModImage.Scale( SkinSystem::ModListImageWidth, SkinSystem::ModListImageHeight, wxIMAGE_QUALITY_HIGH)); wxBitmap newModImage(scaledTempModImage); wxASSERT(newModImage.GetWidth() == SkinSystem::ModListImageWidth); wxASSERT(newModImage.GetHeight() == SkinSystem::ModListImageHeight); this->modImage = wxBitmap(newModImage); return true; } else if ((modImage.GetWidth() < SkinSystem::ModInfoDialogImageWidth) || (modImage.GetHeight() < SkinSystem::ModInfoDialogImageHeight)) { wxLogDebug(_T("Provided mod image size %dx%d is smaller than expected size %dx%d. Using as is."), modImage.GetWidth(), modImage.GetHeight(), SkinSystem::ModInfoDialogImageWidth, SkinSystem::ModInfoDialogImageHeight); return true; } else { this->modImage = modImage; return true; } } bool Skin::SetSmallModImage(const wxBitmap& smallModImage) { if (!smallModImage.IsOk()) { wxLogWarning(_T("Provided small mod image is not valid.")); return false; } else if ((smallModImage.GetWidth() != SkinSystem::ModListImageWidth) || (smallModImage.GetHeight() != SkinSystem::ModListImageHeight)) { wxLogWarning(_T("Provided small mod image size %dx%d is not expected size %dx%d."), smallModImage.GetWidth(), smallModImage.GetHeight(), SkinSystem::ModListImageWidth, SkinSystem::ModListImageHeight); return false; } else { this->smallModImage = smallModImage; return true; } } bool Skin::SetOkIcon(const wxBitmap& okIcon) { if (!okIcon.IsOk()) { wxLogWarning(_T("Provided ok icon is not valid.")); return false; } else if (!CheckStatusBarIconDimensions(okIcon)) { wxLogWarning(_T("Provided ok icon size is wrong.")); return false; } else { this->okIcon = okIcon; return true; } } bool Skin::SetWarningIcon(const wxBitmap& warningIcon) { if (!warningIcon.IsOk()) { wxLogWarning(_T("Provided warning icon is not valid.")); return false; } else if (!CheckStatusBarIconDimensions(warningIcon)) { wxLogWarning(_T("Provided warning icon size is wrong.")); return false; } else { this->warningIcon = warningIcon; return true; } } bool Skin::SetBigWarningIcon(const wxBitmap& bigWarningIcon) { if (!bigWarningIcon.IsOk()) { wxLogWarning(_T("Provided big warning icon is not valid.")); return false; } else if (!CheckBigIconDimensions(bigWarningIcon)) { wxLogWarning(_T("Provided big warning icon size is wrong.")); return false; } else { this->bigWarningIcon = bigWarningIcon; return true; } } bool Skin::SetErrorIcon(const wxBitmap& errorIcon) { if (!errorIcon.IsOk()) { wxLogWarning(_T("Provided error icon is not valid.")); return false; } else if (!CheckStatusBarIconDimensions(errorIcon)) { wxLogWarning(_T("Provided error icon size is wrong.")); return false; } else { this->errorIcon = errorIcon; return true; } } bool Skin::SetInfoIcon(const wxBitmap& infoIcon) { if (!infoIcon.IsOk()) { wxLogWarning(_T("Provided info icon is not valid.")); return false; } else if (!CheckStatusBarIconDimensions(infoIcon)) { wxLogWarning(_T("Provided info icon size is wrong.")); return false; } else { this->infoIcon = infoIcon; return true; } } bool Skin::SetBigInfoIcon(const wxBitmap& bigInfoIcon) { if (!bigInfoIcon.IsOk()) { wxLogWarning(_T("Provided big info icon is not valid.")); return false; } else if (!CheckBigIconDimensions(bigInfoIcon)) { wxLogWarning(_T("Provided big info icon size is wrong.")); return false; } else { this->bigInfoIcon = bigInfoIcon; return true; } } bool Skin::SetHelpIcon(const wxBitmap& helpIcon) { if (!helpIcon.IsOk()) { wxLogWarning(_T("Provided help icon is not valid.")); return false; } else if ((helpIcon.GetWidth() != SkinSystem::HelpIconWidth) || (helpIcon.GetHeight() != SkinSystem::HelpIconHeight)) { wxLogWarning(_T("Provided help icon size %dx%d is not expected size %dx%d."), helpIcon.GetWidth(), helpIcon.GetHeight(), SkinSystem::HelpIconWidth, SkinSystem::HelpIconHeight); return false; } else { this->helpIcon = helpIcon; return true; } } bool Skin::SetBigHelpIcon(const wxBitmap& bigHelpIcon) { if (!bigHelpIcon.IsOk()) { wxLogWarning(_T("Provided help icon is not valid.")); return false; } else if (!CheckBigIconDimensions(bigHelpIcon)) { wxLogWarning(_T("Provided help icon size is wrong.")); return false; } else { this->bigHelpIcon = bigHelpIcon; return true; } } bool Skin::SetIdealIcon(const wxBitmap& idealIcon) { if (!idealIcon.IsOk()) { wxLogWarning(_T("Provided ideal icon is not valid.")); return false; } else if ((idealIcon.GetWidth() != SkinSystem::IdealIconWidth) || (idealIcon.GetHeight() != SkinSystem::IdealIconHeight)) { wxLogWarning(_T("Provided ideal icon size %dx%d is not expected size %dx%d."), idealIcon.GetWidth(), idealIcon.GetHeight(), SkinSystem::IdealIconWidth, SkinSystem::IdealIconHeight); return false; } else { this->idealIcon = idealIcon; return true; } } bool Skin::SetNewsSource(const NewsSource* newsSource) { if (newsSource == NULL) { wxLogWarning(_T("Provided news source is NULL.")); return false; } else { this->newsSource = newsSource; return true; } } bool Skin::CheckStatusBarIconDimensions(const wxBitmap& icon) { const bool result = (icon.GetWidth() == SkinSystem::StatusBarIconWidth) && (icon.GetHeight() == SkinSystem::StatusBarIconHeight); if (!result) { wxLogWarning(_T("Expected status bar icon size is %dx%d but icon size is %dx%d"), SkinSystem::StatusBarIconWidth, SkinSystem::StatusBarIconHeight, icon.GetWidth(), icon.GetHeight()); } return result; } bool Skin::CheckBigIconDimensions(const wxBitmap& icon) { const bool result = (icon.GetWidth() == SkinSystem::BigIconWidth) && (icon.GetHeight() == SkinSystem::BigIconHeight); if (!result) { wxLogWarning(_T("Expected big icon size is %dx%d but icon size is %dx%d"), SkinSystem::BigIconWidth, SkinSystem::BigIconHeight, icon.GetWidth(), icon.GetHeight()); } return result; } SkinSystem* SkinSystem::skinSystem = NULL; bool SkinSystem::Initialize() { wxASSERT(!SkinSystem::IsInitialized()); SkinSystem::skinSystem = new SkinSystem(); return true; } void SkinSystem::DeInitialize() { wxASSERT(SkinSystem::IsInitialized()); if (!SkinSystem::TCSkinChangedHandlers.IsEmpty()) { wxLogDebug(_T("SkinSystem::DeInitialize(): contents of handler list:")); for (TCSkinEventHandlers::const_iterator iter = SkinSystem::TCSkinChangedHandlers.begin(), end = SkinSystem::TCSkinChangedHandlers.end(); iter != end; ++iter) { wxLogDebug(_T(" handler at %p"), *iter); } SkinSystem::TCSkinChangedHandlers.Clear(); } SkinSystem* temp = SkinSystem::skinSystem; SkinSystem::skinSystem = NULL; delete temp; } bool SkinSystem::IsInitialized() { return (SkinSystem::skinSystem != NULL); } SkinSystem* SkinSystem::GetSkinSystem() { wxCHECK_MSG(SkinSystem::IsInitialized(), NULL, _T("Attempt to get skin system when it has not been initialized.")); return SkinSystem::skinSystem; } SkinSystem::SkinSystem() : TCSkin(NULL), font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)), messageFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL) { InitializeDefaultSkin(); } SkinSystem::~SkinSystem() { if (this->TCSkin != NULL) delete this->TCSkin; } void SkinSystem::InitializeDefaultSkin() { // launcher can't function if any of the default skin is missing/invalid bool success = false; success = this->defaultSkin.SetWindowTitle(DEFAULT_SKIN_WINDOW_TITLE); if (!success) { wxLogFatalError(_T("Setting default window title '%s' failed"), DEFAULT_SKIN_WINDOW_TITLE.c_str()); } wxFileName filename(_T(RESOURCES_PATH), DEFAULT_SKIN_WINDOW_ICON); success = this->defaultSkin.SetWindowIcon( wxIcon(filename.GetFullPath(), wxBITMAP_TYPE_ICO)); if (!success) { wxLogFatalError(_T("Setting default window icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_BANNER); success = this->defaultSkin.SetBanner( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default banner '%s' failed"), filename.GetFullPath().c_str()); } success = this->defaultSkin.SetWelcomeText(DEFAULT_SKIN_WELCOME_TEXT); if (!success) { wxLogFatalError(_T("Setting default welcome text '%s' failed"), DEFAULT_SKIN_WELCOME_TEXT.c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_MOD_IMAGE_255X112); success = this->defaultSkin.SetModImage( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default mod image '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_MOD_IMAGE_182X80); success = this->defaultSkin.SetSmallModImage( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default small mod image '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_OK); success = this->defaultSkin.SetOkIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default ok icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_WARNING); success = this->defaultSkin.SetWarningIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default warning icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_WARNING_BIG); success = this->defaultSkin.SetBigWarningIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default big warning icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_ERROR); success = this->defaultSkin.SetErrorIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default error icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_INFO); success = this->defaultSkin.SetInfoIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default info icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_INFO_BIG); success = this->defaultSkin.SetBigInfoIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default big info icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_HELP); success = this->defaultSkin.SetHelpIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default help icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_HELP_BIG); success = this->defaultSkin.SetBigHelpIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default big help icon '%s' failed"), filename.GetFullPath().c_str()); } filename = wxFileName(_T(RESOURCES_PATH), DEFAULT_SKIN_ICON_IDEAL); this->defaultSkin.SetIdealIcon( wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY)); if (!success) { wxLogFatalError(_T("Setting default ideal icon '%s' failed"), filename.GetFullPath().c_str()); } success = this->defaultSkin.SetNewsSource( NewsSource::FindSource(DEFAULT_SKIN_NEWS_SOURCE)); if (!success) { wxLogFatalError(_T("Setting default news source '%d' failed"), DEFAULT_SKIN_NEWS_SOURCE); } } const wxString& SkinSystem::GetWindowTitle() const { if ( this->TCSkin != NULL && !this->TCSkin->GetWindowTitle().IsEmpty() ) { return this->TCSkin->GetWindowTitle(); } else { return this->defaultSkin.GetWindowTitle(); } } const wxIcon& SkinSystem::GetWindowIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetWindowIcon().IsOk() ) { return this->TCSkin->GetWindowIcon(); } else { return this->defaultSkin.GetWindowIcon(); } } const wxBitmap& SkinSystem::GetBanner() const { if ( this->TCSkin != NULL && this->TCSkin->GetBanner().IsOk() ) { return this->TCSkin->GetBanner(); } else { return this->defaultSkin.GetBanner(); } } const wxString& SkinSystem::GetWelcomeText() const { if ( this->TCSkin != NULL && !this->TCSkin->GetWelcomeText().IsEmpty() ) { return this->TCSkin->GetWelcomeText(); } else { return this->defaultSkin.GetWelcomeText(); } } const wxBitmap& SkinSystem::GetModImage() const { if ( this->TCSkin != NULL && this->TCSkin->GetModImage().IsOk() ) { return this->TCSkin->GetModImage(); } else { return this->defaultSkin.GetModImage(); } } const wxBitmap& SkinSystem::GetSmallModImage() const { if ( this->TCSkin != NULL && this->TCSkin->GetSmallModImage().IsOk() ) { return this->TCSkin->GetSmallModImage(); } else { return this->defaultSkin.GetSmallModImage(); } } const wxBitmap& SkinSystem::GetOkIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetOkIcon().IsOk() ) { return this->TCSkin->GetOkIcon(); } else { return this->defaultSkin.GetOkIcon(); } } const wxBitmap& SkinSystem::GetWarningIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetWarningIcon().IsOk() ) { return this->TCSkin->GetWarningIcon(); } else { return this->defaultSkin.GetWarningIcon(); } } const wxBitmap& SkinSystem::GetBigWarningIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetBigWarningIcon().IsOk() ) { return this->TCSkin->GetBigWarningIcon(); } else { return this->defaultSkin.GetBigWarningIcon(); } } const wxBitmap& SkinSystem::GetErrorIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetErrorIcon().IsOk() ) { return this->TCSkin->GetErrorIcon(); } else { return this->defaultSkin.GetErrorIcon(); } } const wxBitmap& SkinSystem::GetInfoIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetInfoIcon().IsOk() ) { return this->TCSkin->GetInfoIcon(); } else { return this->defaultSkin.GetInfoIcon(); } } const wxBitmap& SkinSystem::GetBigInfoIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetBigInfoIcon().IsOk() ) { return this->TCSkin->GetBigInfoIcon(); } else { return this->defaultSkin.GetBigInfoIcon(); } } const wxBitmap& SkinSystem::GetHelpIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetHelpIcon().IsOk() ) { return this->TCSkin->GetHelpIcon(); } else { return this->defaultSkin.GetHelpIcon(); } } const wxBitmap& SkinSystem::GetBigHelpIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetBigHelpIcon().IsOk() ) { return this->TCSkin->GetBigHelpIcon(); } else { return this->defaultSkin.GetBigHelpIcon(); } } const wxBitmap& SkinSystem::GetIdealIcon() const { if ( this->TCSkin != NULL && this->TCSkin->GetIdealIcon().IsOk() ) { return this->TCSkin->GetIdealIcon(); } else { return this->defaultSkin.GetIdealIcon(); } } const NewsSource& SkinSystem::GetNewsSource() const { if ( this->TCSkin != NULL && this->TCSkin->GetNewsSource() != NULL ) { return *this->TCSkin->GetNewsSource(); } else { return *this->defaultSkin.GetNewsSource(); } } void SkinSystem::SetTCSkin(const Skin* skin) { wxCHECK_RET(skin != NULL, _T("SetTCSkin() given null Skin")); // FIXME: Must confirm that generating the event twice isn't problematic if (this->TCSkin != NULL) { ResetTCSkin(); } this->TCSkin = skin; GenerateTCSkinChanged(); } // Does nothing if TCSkin is NULL. void SkinSystem::ResetTCSkin() { if (this->TCSkin != NULL) { const Skin* temp = this->TCSkin; this->TCSkin = NULL; delete temp; GenerateTCSkinChanged(); } } /** Returns true if function is able to get a valid filename object for the passed paths. Filename is returned via the param filename. */ bool SkinSystem::SearchFile(wxFileName& filename, wxString currentTC, wxString shortmodname, wxString filepath) { if (shortmodname.IsEmpty()) { // indicates that the mod is (No mod) filename.Assign( wxString::Format(_T("%s"), filepath.c_str())); } else { filename.Assign( wxString::Format(_T("%s/%s"), shortmodname.c_str(), filepath.c_str())); } if ( filename.Normalize(wxPATH_NORM_ALL, currentTC, wxPATH_UNIX) ) { if ( filename.IsOk() ) { if ( filename.FileExists() ) { return true; } else { wxLogDebug(_T(" File '%s' does not exist"), filename.GetFullPath().c_str()); } } else { wxLogDebug(_T(" File '%s' is not valid"), filename.GetFullPath().c_str()); } } else { wxLogDebug(_T(" Unable to normalize '%s' '%s' '%s'"), currentTC.c_str(), shortmodname.c_str(), filepath.c_str()); } return false; } wxBitmap SkinSystem::MakeModListImage(const wxBitmap &orig) { wxASSERT(orig.GetWidth() == SkinSystem::ModInfoDialogImageWidth); wxASSERT(orig.GetHeight() == SkinSystem::ModInfoDialogImageHeight); wxImage temp(orig.ConvertToImage()); wxImage scaledTemp(temp.Scale(SkinSystem::ModListImageWidth, SkinSystem::ModListImageHeight, wxIMAGE_QUALITY_HIGH)); wxBitmap outimg(scaledTemp); wxASSERT(outimg.GetWidth() == SkinSystem::ModListImageWidth); wxASSERT(outimg.GetHeight() == SkinSystem::ModListImageHeight); return outimg; } wxBitmap SkinSystem::MakeModInfoDialogImage(const wxBitmap &orig) { wxASSERT(orig.GetWidth() == SkinSystem::ModListImageWidth); wxASSERT(orig.GetHeight() == SkinSystem::ModListImageHeight); wxImage temp(orig.ConvertToImage()); wxImage scaledTemp(temp.Scale(SkinSystem::ModInfoDialogImageWidth, SkinSystem::ModInfoDialogImageHeight, wxIMAGE_QUALITY_HIGH)); wxBitmap outimg(scaledTemp); wxASSERT(outimg.GetWidth() == SkinSystem::ModInfoDialogImageWidth); wxASSERT(outimg.GetHeight() == SkinSystem::ModInfoDialogImageHeight); return outimg; } wxlauncher-0.9.4/code/apis/SkinManager.h0000644000000000000000000001370412155177255020077 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SKIN_H #define SKIN_H #include #include #include "datastructures/NewsSource.h" /** TC skin has changed. */ DECLARE_EVENT_TYPE(EVT_TC_SKIN_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, TCSkinEventHandlers); /** Holds a skin's information. */ /** The Set() functions return true on success, false otherwise. */ class Skin { public: Skin(); const wxString& GetWindowTitle() const { return this->windowTitle; } bool SetWindowTitle(const wxString& windowTitle); const wxIcon& GetWindowIcon() const { return this->windowIcon; } bool SetWindowIcon(const wxIcon& windowIcon); const wxBitmap& GetBanner() const { return this->banner; } bool SetBanner(const wxBitmap& banner); const wxString& GetWelcomeText() const { return this->welcomeText; } bool SetWelcomeText(const wxString& welcomeText); const wxBitmap& GetModImage() const { return this->modImage; } bool SetModImage(const wxBitmap& modImage); const wxBitmap& GetSmallModImage() const { return this->smallModImage; } bool SetSmallModImage(const wxBitmap& smallModImage); const wxBitmap& GetOkIcon() const { return this->okIcon; } bool SetOkIcon(const wxBitmap& okIcon); const wxBitmap& GetWarningIcon() const { return this->warningIcon; } bool SetWarningIcon(const wxBitmap& warningIcon); const wxBitmap& GetBigWarningIcon() const { return this->bigWarningIcon; } bool SetBigWarningIcon(const wxBitmap& bigWarningIcon); const wxBitmap& GetErrorIcon() const { return this->errorIcon; } bool SetErrorIcon(const wxBitmap& errorIcon); const wxBitmap& GetInfoIcon() const { return this->infoIcon; } bool SetInfoIcon(const wxBitmap& infoIcon); const wxBitmap& GetBigInfoIcon() const { return this->bigInfoIcon; } bool SetBigInfoIcon(const wxBitmap& bigInfoIcon); const wxBitmap& GetHelpIcon() const { return this->helpIcon; } bool SetHelpIcon(const wxBitmap& helpIcon); const wxBitmap& GetBigHelpIcon() const { return this->bigHelpIcon; } bool SetBigHelpIcon(const wxBitmap& bigHelpIcon); const wxBitmap& GetIdealIcon() const { return this->idealIcon; } bool SetIdealIcon(const wxBitmap& idealIcon); const NewsSource* GetNewsSource() const { return this->newsSource; } bool SetNewsSource(const NewsSource* newsSource); private: static bool CheckStatusBarIconDimensions(const wxBitmap& icon); static bool CheckBigIconDimensions(const wxBitmap& icon); wxString windowTitle; wxIcon windowIcon; wxBitmap banner; wxString welcomeText; /** The 255x112 and 182x80 versions of the default mod image. */ wxBitmap modImage; wxBitmap smallModImage; wxBitmap okIcon; wxBitmap warningIcon; wxBitmap bigWarningIcon; wxBitmap errorIcon; wxBitmap infoIcon; wxBitmap bigInfoIcon; wxBitmap helpIcon; wxBitmap bigHelpIcon; wxBitmap idealIcon; const NewsSource* newsSource; }; /** Class used to manage the skinning of the launcher. The skinnable parts of the app register with the skin system, so that when the skin changes, they can be updated if needed. */ class SkinSystem { public: static bool Initialize(); static void DeInitialize(); static bool IsInitialized(); static SkinSystem* GetSkinSystem(); ~SkinSystem(); static void RegisterTCSkinChanged(wxEvtHandler *handler); static void UnRegisterTCSkinChanged(wxEvtHandler *handler); const wxString& GetWindowTitle() const; const wxIcon& GetWindowIcon() const; const wxBitmap& GetBanner() const; const wxString& GetWelcomeText() const; const wxBitmap& GetModImage() const; const wxBitmap& GetSmallModImage() const; const wxBitmap& GetOkIcon() const; const wxBitmap& GetWarningIcon() const; const wxBitmap& GetBigWarningIcon() const; const wxBitmap& GetErrorIcon() const; const wxBitmap& GetInfoIcon() const; const wxBitmap& GetBigInfoIcon() const; const wxBitmap& GetHelpIcon() const; const wxBitmap& GetBigHelpIcon() const; const wxBitmap& GetIdealIcon() const; const NewsSource& GetNewsSource() const; const wxFont& GetFont() const { return this->font; } const wxFont& GetMessageFont() const { return this->messageFont; } void SetTCSkin(const Skin* skin); void ResetTCSkin(); static wxBitmap MakeModListImage(const wxBitmap &orig); static wxBitmap MakeModInfoDialogImage(const wxBitmap &orig); static bool SearchFile(wxFileName& filename, wxString currentTC, wxString shortmodname, wxString filepath); /** Dimensions for banner image on welcome page. */ static const int BannerMaxWidth = 630; static const int BannerHeight = 150; /** Dimensions for mod images, depending on where the image appears. */ static const int ModInfoDialogImageWidth = 255; static const int ModInfoDialogImageHeight = 112; static const int ModListImageWidth = 182; static const int ModListImageHeight = 80; static const int StatusBarIconWidth = 24; static const int StatusBarIconHeight = 24; static const int BigIconWidth = 64; static const int BigIconHeight = 64; static const int HelpIconWidth = 32; static const int HelpIconHeight = 32; static const int IdealIconWidth = 24; static const int IdealIconHeight = 24; private: SkinSystem(); static SkinSystem* skinSystem; static TCSkinEventHandlers TCSkinChangedHandlers; static void GenerateTCSkinChanged(); void InitializeDefaultSkin(); Skin defaultSkin; const Skin* TCSkin; wxFont font; wxFont messageFont; }; #endifwxlauncher-0.9.4/code/apis/SpeechManager.cpp0000644000000000000000000002100512155177255020726 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "generated/configure_launcher.h" #if USE_SPEECH #include #endif #include #include "apis/SpeechManager.h" #include "global/MemoryDebugging.h" using namespace SpeechMan; #if USE_SPEECH // wxLauncher objects bool isInitialized = false; class VoiceData { public: VoiceData(const wxString& voiceName, ISpObjectToken* voice); VoiceData::VoiceData(const VoiceData& vd); VoiceData& VoiceData::operator=(const VoiceData& vd); const wxString& GetVoiceName() const { return this->voiceName; } ISpObjectToken* GetVoice() const { return this->voice; } private: VoiceData(); wxString voiceName; ISpObjectToken* voice; }; VoiceData::VoiceData(const wxString& voiceName, ISpObjectToken* voice) : voiceName(voiceName), voice(voice) { } VoiceData::VoiceData(const VoiceData& vd) : voiceName(vd.voiceName), voice(vd.voice) { } VoiceData& VoiceData::operator=(const VoiceData& vd) { if (this == &vd) { return *this; } this->voiceName = vd.voiceName; this->voice = vd.voice; return *this; } std::vector voices; // COM objects ISpVoice * comVoice = NULL; void enumerateObjectToken(ISpObjectToken * token) { HRESULT valuesres = S_OK; HRESULT datares = S_OK; wxLogDebug(_T("Enumerating token:")); ULONG valueIndex = 0; LPWSTR value = NULL; LPWSTR valuedata = NULL; do { valuesres = token->EnumValues(valueIndex, &value); if ( valuesres == S_OK ) { datares = token->GetStringValue(value, &valuedata); if ( datares == S_OK ) { wxLogDebug(_T(" %s=%s"), wxString(value, wxMBConvUTF16()), wxString(valuedata, wxMBConvUTF16())); CoTaskMemFree(valuedata); valuedata = NULL; } else { wxLogDebug(_T(" %s= (NONE)"), wxString(value, wxMBConvUTF16())); } CoTaskMemFree(value); value = NULL; } valueIndex++; } while( SUCCEEDED(valuesres) ); } bool EnumerateVoices() { HRESULT comResult = S_OK; ISpObjectTokenCategory * comTokenCategory = NULL; IEnumSpObjectTokens * comVoices = NULL; ULONG comVoicesCount = 0; // Init speech api comResult = ::CoCreateInstance( CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (LPVOID*)&comVoice); wxCHECK_MSG( SUCCEEDED(comResult), false, _T("Unable to instantiate speech API")); // Generate enumeration of voices comResult = ::CoCreateInstance(CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, IID_ISpObjectTokenCategory, (LPVOID*)&comTokenCategory); wxCHECK_MSG( SUCCEEDED(comResult), false, _T("Unable to instantiate a TokenCategory")); comResult = comTokenCategory->SetId(SPCAT_VOICES, false); wxCHECK_MSG( SUCCEEDED(comResult), false, _T("Unable to to set location to find the installed voices.") _T("Likely the key that I am looking for does not exist and thus ") _T("there are no installed voices on this system.")); comResult = comTokenCategory->EnumTokens(NULL, NULL, &comVoices); wxCHECK_MSG( SUCCEEDED(comResult), false, _T("Unable to enumerate the installed voices. Check that this system") _T(" has voices installed.")); comResult = comVoices->GetCount(&comVoicesCount); wxCHECK_MSG( SUCCEEDED(comResult), false, _T("Unable get a count of the installed voices.")); while( comVoicesCount > 0 ) { ISpObjectToken * comAVoice = NULL; comVoices->Next(1, &comAVoice, NULL); // retrieve just one LPWSTR id = NULL; comAVoice->GetStringValue(NULL, &id); size_t idlength = wcslen(id); wxLogDebug(_T(" Got string of length %d:"), idlength); for(size_t i = 0; i < idlength; i++) { wxLogDebug(_T(" %04X"), id[i]); } voices.push_back(VoiceData(wxString(id, wxMBConvUTF16(), wcslen(id)), comAVoice)); #ifdef __WXDEBUG__ enumerateObjectToken(comAVoice); #endif comAVoice->Release(); comVoicesCount--; } comTokenCategory->Release(); return true; } #endif // USE_SPEECH bool SpeechMan::Initialize() { #if USE_SPEECH ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); wxCHECK_MSG(EnumerateVoices(), false, _T("Cannot enumerate voices")); isInitialized = true; return true; #else return false; #endif } bool SpeechMan::DeInitialize() { #if USE_SPEECH isInitialized = false; ::CoUninitialize(); return true; #else return false; #endif } /** Returns true if Speech Support was compiled in. Returns false if speech support was not compiled in. */ bool SpeechMan::WasBuiltIn() { #if USE_SPEECH return true; #else return false; #endif } bool SpeechMan::IsInitialized() { #if USE_SPEECH return isInitialized; #else return false; #endif } wxArrayString SpeechMan::EnumVoices() { wxArrayString arr; #if USE_SPEECH for (std::vector::const_iterator it = voices.begin(); it != voices.end(); ++it) { arr.Add(it->GetVoiceName()); } #endif return arr; // reference counted copy } void SpeechMan::Speak(wxString what) { #if USE_SPEECH wxCHECK_RET( isInitialized, _T("Speak called but SpeechMan not initialized.")); wxCHECK_RET( comVoice != NULL, _T("Speak called but comVoice is null")); wxWritableWCharBuffer buffer(what.wchar_str()); LPCWSTR str = buffer.operator const wchar_t *(); HRESULT result = comVoice->Speak(str, SPF_IS_NOT_XML, NULL); wxCHECK_RET( SUCCEEDED(result), _T("Failed to speak")); #else wxFAIL_MSG(_T("Speak called but USE_SPEECH not defined")); #endif } #if USE_SPEECH void SpeechMan::SetVoice(size_t i) { wxCHECK_RET( isInitialized, _T("SetVoice called but SpeechMan not initialized.")); wxCHECK_RET( comVoice != NULL, _T("SetVoice called but comVoice is null")); wxCHECK_RET( i < voices.size(), _T("i is larger than the number of voices")); wxLogDebug(_T("Selected voice: %s"), voices[i].GetVoiceName().c_str()); HRESULT result = comVoice->SetVoice(voices[i].GetVoice()); wxCHECK_RET( SUCCEEDED(result), _T("Error in setting voice")); #else void SpeechMan::SetVoice(size_t) { wxFAIL_MSG(_T("SetVoice called but USE_SPEECH not defined")); #endif } /** Returns the currently set voice. Useful mostly get get the system default voice if there is no value for the voice set by the profile, or the FS2_open registry entries. */ int SpeechMan::GetVoice() { #if USE_SPEECH wxCHECK_MSG( isInitialized, -1, _T("GetVoice called but SpeechMan not initialized.")); wxCHECK_MSG( comVoice != NULL, -1, _T("GetVoice called but comVoice is null")); ISpObjectToken * voice = NULL; HRESULT result = comVoice->GetVoice(&voice); wxCHECK_MSG( SUCCEEDED(result), -1, _T("Error in getting voice")); LPWSTR name = NULL; result = voice->GetStringValue(NULL, &name); wxCHECK_MSG( SUCCEEDED(result), -1, _T("Error in getting voice name")); wxString voiceName(name, wxMBConvUTF16()); int index = EnumVoices().Index(voiceName.c_str()); wxCHECK_MSG( index != wxNOT_FOUND, -1, wxString::Format(_T("Could not find voice (%s) in database"), voiceName)); return index; #else wxFAIL_MSG(_T("GetVoice called but USE_SPEECH not defined")); return -1; #endif } int SpeechMan::GetVolume() { #if USE_SPEECH wxCHECK_MSG( isInitialized, -1, _T("GetVolume called but SpeechMan not initialized.")); wxCHECK_MSG( comVoice != NULL, -1, _T("GetVolume called but comVoice is null")); USHORT volume = 100; HRESULT result = comVoice->GetVolume(&volume); wxCHECK_MSG( SUCCEEDED(result), -1, _T("Unable to retrieve volume")); return volume; #else wxFAIL_MSG(_T("GetVolume called but USE_SPEECH not defined")); return 0; #endif } #if USE_SPEECH void SpeechMan::SetVolume(int volume) { wxCHECK_RET( isInitialized, _T("SetVolume called but SpeechMan not initialized.")); wxCHECK_RET( comVoice != NULL, _T("SetVolume called but comVoice is null")); wxCHECK_RET( volume >= 0, _T("Volume is less than zero")); wxCHECK_RET( volume <= 100, _T("Volume is greater than 100")); /* volume must be within 0-100 which is well within USHORT's range.*/ USHORT v = static_cast(volume); HRESULT result = comVoice->SetVolume(v); wxCHECK_RET( SUCCEEDED(result), _T("Unable to set volume")); #else void SpeechMan::SetVolume(int) { wxFAIL_MSG(_T("SetVolume called but USE_SPEECH not defined")); #endif } wxlauncher-0.9.4/code/apis/SpeechManager.h0000644000000000000000000000205112155177255020373 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SPEECHMANAGER_H #define SPEECHMANAGER_H #include namespace SpeechMan { bool Initialize(); bool DeInitialize(); bool WasBuiltIn(); bool IsInitialized(); void Speak(wxString what); void SetVoice(size_t i); int GetVoice(); wxArrayString EnumVoices(); void SetVolume(int volume); int GetVolume(); }; #endifwxlauncher-0.9.4/code/apis/TCManager.cpp0000644000000000000000000001561312155177255020035 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "apis/TCManager.h" #include "apis/ProfileManager.h" #include "global/MemoryDebugging.h" /** \class TCManager TCManager is used to pickup CurrentProfileChanged events from ProMan and send the approriate messages to the controls that care. It also sends out events when the binary or root folder get changed by user controls so that other controls that are affected (like ModList) get notified in all cases and will be able to update the information. */ /** Contructor. Static class does nothing. */ TCManager::TCManager() { wxLogDebug(_T("TCManager is at %p."), this); ProMan::GetProfileManager()->AddEventHandler(this); } /** Destructor. Static class does nothing. */ TCManager::~TCManager() { ProMan::GetProfileManager()->RemoveEventHandler(this); } TCManager* TCManager::manager = NULL; TCEventHandlers TCManager::TCChangedHandlers; TCEventHandlers TCManager::TCBinaryChangedHandlers; TCEventHandlers TCManager::TCActiveModChangedHandlers; TCEventHandlers TCManager::TCFredBinaryChangedHandlers; void TCManager::Initialize() { if ( !IsInitialized() ) { manager = new TCManager(); } } void TCManager::DeInitialize() { if ( IsInitialized()) { delete manager; manager = NULL; } } bool TCManager::IsInitialized() { return manager != NULL; } TCManager* TCManager::Get() { return manager; } /////////////////////////////////////////////////////////////////////////////// ///// Events BEGIN_EVENT_TABLE(TCManager, wxEvtHandler) EVT_COMMAND(wxID_ANY, EVT_CURRENT_PROFILE_CHANGED, TCManager::CurrentProfileChanged) END_EVENT_TABLE() DEFINE_EVENT_TYPE(EVT_TC_CHANGED); DEFINE_EVENT_TYPE(EVT_TC_BINARY_CHANGED); DEFINE_EVENT_TYPE(EVT_TC_ACTIVE_MOD_CHANGED); DEFINE_EVENT_TYPE(EVT_TC_FRED_BINARY_CHANGED); #include // required magic incatation WX_DEFINE_LIST(TCEventHandlers); void TCManager::RegisterTCBinaryChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCBinaryChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterTCBinaryChanged(): Handler at %p already registered."), handler)); TCBinaryChangedHandlers.Append(handler); } void TCManager::UnRegisterTCBinaryChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCBinaryChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterTCBinaryChanged(): Handler at %p not registered."), handler)); TCBinaryChangedHandlers.DeleteObject(handler); } void TCManager::RegisterTCChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterTCChanged(): Handler at %p already registered."), handler)); TCChangedHandlers.Append(handler); } void TCManager::UnRegisterTCChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterTCChanged(): Handler at %p not registered."), handler)); TCChangedHandlers.DeleteObject(handler); } void TCManager::RegisterTCActiveModChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCActiveModChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterTCActiveModChanged(): Handler at %p already registered."), handler)); TCActiveModChangedHandlers.Append(handler); } void TCManager::UnRegisterTCActiveModChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCActiveModChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterTCActiveModChanged(): Handler at %p not registered."), handler)); TCActiveModChangedHandlers.DeleteObject(handler); } void TCManager::RegisterTCFredBinaryChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCFredBinaryChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterTCFredBinaryChanged(): Handler at %p already registered."), handler)); TCFredBinaryChangedHandlers.Append(handler); } void TCManager::UnRegisterTCFredBinaryChanged(wxEvtHandler *handler) { wxASSERT_MSG(TCFredBinaryChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterTCFredBinaryChanged(): Handler at %p not registered."), handler)); TCFredBinaryChangedHandlers.DeleteObject(handler); } void TCManager::GenerateTCChanged() { wxCommandEvent event(EVT_TC_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_TC_CHANGED event")); TCEventHandlers::iterator iter = TCChangedHandlers.begin(); while (iter != TCChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_TC_CHANGED event to %p"), current); iter++; } } void TCManager::GenerateTCBinaryChanged() { wxCommandEvent event(EVT_TC_BINARY_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_TC_BINARY_CHANGED event")); TCEventHandlers::iterator iter = TCBinaryChangedHandlers.begin(); while (iter != TCBinaryChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_TC_BINARY_CHANGED event to %p"), current); iter++; } } void TCManager::GenerateTCActiveModChanged() { wxCommandEvent event(EVT_TC_ACTIVE_MOD_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_TC_ACTIVE_MOD_CHANGED event")); TCEventHandlers::iterator iter = TCActiveModChangedHandlers.begin(); while (iter != TCActiveModChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_TC_ACTIVE_MOD_CHANGED event to %p"), current); iter++; } } void TCManager::GenerateTCFredBinaryChanged() { wxCommandEvent event(EVT_TC_FRED_BINARY_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_TC_FRED_BINARY_CHANGED event")); TCEventHandlers::iterator iter = TCFredBinaryChangedHandlers.begin(); while (iter != TCBinaryChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_TC_FRED_BINARY_CHANGED event to %p"), current); iter++; } } void TCManager::CurrentProfileChanged(wxCommandEvent &WXUNUSED(event)) { TCManager::GenerateTCChanged(); // it's assumed that BasicSettingsPage::OnTCChanged() (which is called on an EVT_TC_CHANGED event) // calls TCManager::GenerateTCBinaryChanged() unconditionally, so no need to explicitly call it here // it's also assumed that BasicSettingsPage::OnTCChanged() calls TCManager::GenerateTCFredBinaryChanged() // unconditionally if FRED launching is enabled } wxlauncher-0.9.4/code/apis/TCManager.h0000644000000000000000000000451712155177255017503 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TCMANAGER_H #define TCMANAGER_H #include #include #include /** Selected TC has changed. */ DECLARE_EVENT_TYPE(EVT_TC_CHANGED, wxID_ANY); /** Selected binary has changed. */ DECLARE_EVENT_TYPE(EVT_TC_BINARY_CHANGED, wxID_ANY); /** Active mod has changed. */ DECLARE_EVENT_TYPE(EVT_TC_ACTIVE_MOD_CHANGED, wxID_ANY); /** Selected FRED binary has changed. */ DECLARE_EVENT_TYPE(EVT_TC_FRED_BINARY_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, TCEventHandlers); class TCManager: public wxEvtHandler { public: ~TCManager(); static void Initialize(); static void DeInitialize(); static bool IsInitialized(); static TCManager* Get(); private: TCManager(); static TCManager *manager; public: void CurrentProfileChanged(wxCommandEvent &event); // Events public: static void RegisterTCChanged(wxEvtHandler *handler); static void UnRegisterTCChanged(wxEvtHandler *handler); static void RegisterTCBinaryChanged(wxEvtHandler *handler); static void UnRegisterTCBinaryChanged(wxEvtHandler *handler); static void RegisterTCActiveModChanged(wxEvtHandler *handler); static void UnRegisterTCActiveModChanged(wxEvtHandler *handler); static void RegisterTCFredBinaryChanged(wxEvtHandler *handler); static void UnRegisterTCFredBinaryChanged(wxEvtHandler *handler); static void GenerateTCChanged(); static void GenerateTCBinaryChanged(); static void GenerateTCActiveModChanged(); static void GenerateTCFredBinaryChanged(); private: static TCEventHandlers TCChangedHandlers, TCBinaryChangedHandlers, TCActiveModChangedHandlers, TCFredBinaryChangedHandlers; DECLARE_EVENT_TABLE(); }; #endifwxlauncher-0.9.4/code/controls/BottomButtons.cpp0000644000000000000000000001241012155177255021756 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "global/ids.h" #include "global/ProfileKeys.h" #include "controls/BottomButtons.h" #include "controls/ModList.h" #include "datastructures/FSOExecutable.h" #include "datastructures/ResolutionMap.h" #include "apis/FREDManager.h" #include "apis/TCManager.h" #include "apis/ProfileManager.h" #include "generated/configure_launcher.h" // needed to check whether OSX is used #include "global/MemoryDebugging.h" // Last include for memory debugging BottomButtons::BottomButtons(wxWindow* parent, wxPoint &pos, wxSize &size) : wxPanel( parent, wxID_ANY, pos, size) { this->SetMinSize(size); bool showFred; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &showFred, false); #if 0 this->close = new wxButton(this, ID_CLOSE_BUTTON, _("Close")); #endif this->help = new wxButton(this, ID_HELP_BUTTON, _("Help")); this->fred = new wxButton(this, ID_FRED_BUTTON, _("FRED")); this->fred->Show(showFred); #if 0 this->update = new wxButton(this, ID_UPDATE_BUTTON, _("Update Available")); this->update->Hide(); #endif this->play = new wxButton(this, ID_PLAY_BUTTON, _("Play")); wxFont playButtonFont = this->play->GetFont(); playButtonFont.SetWeight(wxFONTWEIGHT_BOLD); this->play->SetFont(playButtonFont); wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); #if 0 sizer->Add(this->close); #endif sizer->Add(this->help); sizer->AddStretchSpacer(1); #if 0 sizer->Add(this->update, wxSizerFlags().ReserveSpaceEvenIfHidden()); sizer->AddStretchSpacer(1); #endif sizer->Add(this->fred, wxSizerFlags().ReserveSpaceEvenIfHidden()); sizer->Add(this->play); this->SetSizerAndFit(sizer); this->Layout(); wxLogDebug(_T("BottomButtons is at %p."), this); TCManager::RegisterTCBinaryChanged(this); TCManager::RegisterTCChanged(this); TCManager::RegisterTCActiveModChanged(this); TCManager::RegisterTCFredBinaryChanged(this); FREDManager::RegisterFREDEnabledChanged(this); ResolutionMap::RegisterResolutionMapChanged(this); wxCommandEvent nullEvent; this->OnTCChanges(nullEvent); } BEGIN_EVENT_TABLE(BottomButtons, wxPanel) EVT_COMMAND(wxID_NONE, EVT_TC_CHANGED, BottomButtons::OnTCChanges) EVT_COMMAND(wxID_NONE, EVT_TC_ACTIVE_MOD_CHANGED, BottomButtons::OnTCChanges) EVT_COMMAND(wxID_NONE, EVT_TC_BINARY_CHANGED, BottomButtons::OnTCChanges) EVT_COMMAND(wxID_NONE, EVT_TC_FRED_BINARY_CHANGED, BottomButtons::OnTCChanges) EVT_COMMAND(wxID_NONE, EVT_FRED_ENABLED_CHANGED, BottomButtons::OnFREDEnabledChanged) EVT_COMMAND(wxID_NONE, EVT_RESOLUTION_MAP_CHANGED, BottomButtons::OnTCChanges) END_EVENT_TABLE() /** remove the text after ".app" in the executable name. A no op on other platforms*/ #if IS_APPLE wxString FixBinaryName(const wxString& binaryName) { wxString fixedBinaryName(binaryName); // the trailing / ensures that the .app indicates an extension int DotAppIndex = binaryName.Find(_T(".app/")); if (DotAppIndex != wxNOT_FOUND) { fixedBinaryName = binaryName.Left(DotAppIndex + 4); // 4 so that ".app" is retained } return fixedBinaryName; } #else #define FixBinaryName(binaryName) binaryName #endif void BottomButtons::OnTCChanges(wxCommandEvent &WXUNUSED(event)) { wxString tc, binary, fredBinary; ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tc, wxEmptyString); ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_BINARY, &binary, wxEmptyString); if ( tc.IsEmpty() || binary.IsEmpty() || ModList::GetActiveMod() == NULL || !ResolutionMap::HasEntryForActiveMod()) { this->play->Disable(); } else if ( wxFileName(tc + wxFileName::GetPathSeparator() + binary).FileExists() ) { this->play->Enable(); } else { wxLogWarning(_("Executable %s does not exist"), FixBinaryName(binary).c_str()); this->play->Disable(); } ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_FRED, &fredBinary, wxEmptyString); if ( this->fred == NULL ) { // do nothing, no button to manipulate } else if ( tc.IsEmpty() || fredBinary.IsEmpty() || (!wxFileName::DirExists(tc)) || (!FSOExecutable::HasFSOExecutables(wxFileName(tc, wxEmptyString))) ) { this->fred->Disable(); #if IS_APPLE } else if ( wxFileName(tc + wxFileName::GetPathSeparator() + fredBinary).FileExists() ) { #else } else if ( wxFileName(tc, fredBinary).FileExists() ) { #endif this->fred->Enable(); } else { wxLogWarning(_("FRED executable %s does not exist"), FixBinaryName(fredBinary).c_str()); this->fred->Disable(); } } void BottomButtons::OnFREDEnabledChanged(wxCommandEvent &WXUNUSED(event)) { bool showFred; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &showFred, false); this->fred->Show(showFred); } wxlauncher-0.9.4/code/controls/BottomButtons.h0000644000000000000000000000211512155177255021424 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BOTTOMBUTTONS_H #define BOTTOMBUTTONS_H #include class BottomButtons: public wxPanel { public: BottomButtons(wxWindow* parent, wxPoint &pos, wxSize &size); private: wxButton *close, *help, *fred, *update, *play; public: void OnTCChanges(wxCommandEvent &event); void OnFREDEnabledChanged(wxCommandEvent &event); DECLARE_EVENT_TABLE() }; #endifwxlauncher-0.9.4/code/controls/FlagListBox.cpp0000644000000000000000000003013312155177255021313 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "controls/FlagListBox.h" #include "apis/ProfileProxy.h" #include "apis/SkinManager.h" #include "global/ids.h" #include "global/MemoryDebugging.h" FlagListCheckBox::FlagListCheckBox( wxWindow* parent, const wxString& label, const wxString& flagString) : wxCheckBox(parent, wxID_ANY, label), flagString(flagString) { wxASSERT(parent != NULL); wxASSERT(!flagString.IsEmpty()); } void FlagListCheckBox::OnClicked(wxCommandEvent &WXUNUSED(event)) { ProfileProxy::GetProxy()->SetFlag(this->flagString, this->IsChecked()); wxLogDebug(_T("flag %s is now %s"), flagString.c_str(), this->IsChecked() ? _T("on") : _T("off")); } FlagListCheckBoxItem::FlagListCheckBoxItem(const wxString& fsoCategory) : fsoCategory(fsoCategory), checkBox(NULL), checkBoxSizer(NULL), shortDescription(wxEmptyString), flagString(wxEmptyString), isRecommendedFlag(false) { wxASSERT(!fsoCategory.IsEmpty()); } FlagListCheckBoxItem::FlagListCheckBoxItem( FlagListCheckBox& checkBox, wxSizer& checkBoxSizer, const wxString& shortDescription, const wxString& flagString, const bool isRecommendedFlag) : fsoCategory(wxEmptyString), checkBox(&checkBox), checkBoxSizer(&checkBoxSizer), shortDescription(shortDescription), flagString(flagString), isRecommendedFlag(isRecommendedFlag) { // shortDescription can be empty wxASSERT(!flagString.IsEmpty()); } FlagListCheckBoxItem::~FlagListCheckBoxItem() { delete this->checkBoxSizer; } #include // Magic Incantation WX_DEFINE_LIST(FlagListCheckBoxItems); DEFINE_EVENT_TYPE(EVT_FLAG_LIST_BOX_READY); #include // required magic incantation WX_DEFINE_LIST(FlagListBoxReadyEventHandlers); void FlagListBox::RegisterFlagListBoxReady(wxEvtHandler *handler) { wxASSERT_MSG(flagListBoxReadyHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterFlagListBoxReady(): Handler at %p already registered."), handler)); this->flagListBoxReadyHandlers.Append(handler); } void FlagListBox::UnRegisterFlagListBoxReady(wxEvtHandler *handler) { wxASSERT_MSG(flagListBoxReadyHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterFlagListBoxReady(): Handler at %p not registered."), handler)); this->flagListBoxReadyHandlers.DeleteObject(handler); } void FlagListBox::GenerateFlagListBoxReady() { wxASSERT(this->IsReady()); wxASSERT_MSG(!this->isReadyEventGenerated, _T("GenerateFlagListBoxReady() was called a second time.")); wxCommandEvent event(EVT_FLAG_LIST_BOX_READY, wxID_NONE); wxLogDebug(_T("Generating EVT_FLAG_LIST_BOX_READY event")); for (FlagListBoxReadyEventHandlers::iterator iter = this->flagListBoxReadyHandlers.begin(), end = this->flagListBoxReadyHandlers.end(); iter != end; ++iter) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_FLAG_LIST_BOX_READY event to %p"), current); } this->isReadyEventGenerated = true; } struct FlagInfo { wxString flag; wxString category; bool takesArg; }; #include "datastructures/FlagInfo.cpp" #define WIDTH_OF_CHECKBOX 16 // allows flag checkbox and text to be lined up, while avoiding // visual collisions with flag category lines const int ITEM_VERTICAL_OFFSET = 2; // in pixels #if IS_LINUX const int VERTICAL_OFFSET_MULTIPLIER = 2; // in pixels #else const int VERTICAL_OFFSET_MULTIPLIER = 1; // in pixels #endif FlagListBox::FlagListBox(wxWindow* parent) : wxVListBox(parent,ID_FLAGLISTBOX), isReadyEventGenerated(false), isReady(false), flagsLoaded(false), flagData(NULL), areCheckBoxesGenerated(false) { } void FlagListBox::AcceptFlagData(FlagFileData* flagData) { wxCHECK_RET(flagData != NULL, _T("AcceptFlagData(): flagData is null.")); wxCHECK_RET(this->flagData == NULL, _T("AcceptFlagData(): flag list box given flag data twice.")); this->isReady = true; this->flagData = flagData; FlagListBoxData* data = this->flagData->GenerateFlagListBoxData(); wxCHECK_RET(data != NULL, _T("AcceptFlagData(): FlagFileData::GenerateFlagListBoxData() returned null.")); this->GenerateCheckBoxes(*data); this->SetItemCount(flagData->GetItemCount()); this->GenerateFlagListBoxReady(); } void FlagListBox::GenerateCheckBoxes(const FlagListBoxData& data) { wxASSERT(!data.IsEmpty()); wxASSERT_MSG(!this->areCheckBoxesGenerated, _T("Attempted to generate checkboxes a second time.")); FlagListCheckBox* checkBox; wxSizer* checkBoxSizer; for (FlagListBoxData::const_iterator dataIter = data.begin(); dataIter != data.end(); ++dataIter) { FlagListBoxDataItem* item = *dataIter; if (!item->fsoCategory.IsEmpty()) { this->checkBoxes.Append( new FlagListCheckBoxItem(item->fsoCategory)); continue; } checkBox = new FlagListCheckBox( this, wxEmptyString, item->flagString); checkBox->Hide(); // we don't yet know where it should appear, so hide checkBox->Connect( checkBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(FlagListCheckBox::OnClicked)); checkBoxSizer = new wxBoxSizer(wxVERTICAL); checkBoxSizer->AddSpacer(ITEM_VERTICAL_OFFSET); checkBoxSizer->Add(checkBox); this->checkBoxes.Append( new FlagListCheckBoxItem(*checkBox, *checkBoxSizer, item->shortDescription, item->flagString, item->isRecommendedFlag)); } this->areCheckBoxesGenerated = true; } FlagListBox::~FlagListBox() { FlagFileData* temp = this->flagData; this->flagData = NULL; delete temp; this->checkBoxes.Clear(); } FlagListCheckBoxItem* FlagListBox::FindFlagAt(size_t n) const { wxCHECK_MSG(this->IsReady(), NULL, _T("FindFlagAt() called when flag list box is not ready")); wxCHECK_MSG(n >= 0 && n < this->checkBoxes.GetCount(), NULL, wxString::Format(_T("FindFlagAt() called with out-of-range value %lu"), n)); return this->checkBoxes[n]; } void FlagListBox::OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const { #if IS_WIN32 // replace the ugly default font with one based on the system default wxFont font(SkinSystem::GetSkinSystem()->GetFont()); dc.SetFont(font); #endif if (this->IsReady()) { FlagListCheckBoxItem* item = this->FindFlagAt(n); wxCHECK_RET(item != NULL, _T("Flag pointer is null")); if (item->GetCheckBox() != NULL) { if (item->IsRecommendedFlag()) { dc.DrawBitmap( SkinSystem::GetSkinSystem()->GetIdealIcon(), rect.x, rect.y); } item->GetCheckBox()->Show(); item->GetCheckBoxSizer()->SetDimension( rect.x + SkinSystem::IdealIconWidth, rect.y, SkinSystem::IdealIconWidth, rect.height); if (item->GetShortDescription().IsEmpty()) { dc.DrawText(wxString(_T(" ")) + item->GetFlagString(), rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX, rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET)); } else { dc.DrawText(wxString(_T(" ")) + item->GetShortDescription(), rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX, rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET)); } } else { // draw a category wxASSERT(!item->GetFsoCategory().IsEmpty()); #if IS_WIN32 font.SetWeight(wxFONTWEIGHT_BOLD); dc.SetTextForeground(*wxWHITE); dc.SetFont(font); #endif dc.DrawText(wxString(_T(" ")) + item->GetFsoCategory(), rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX, rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET)); #if IS_WIN32 dc.SetTextForeground(*wxBLACK); #endif } } else { wxASSERT_MSG( n == 0, _T("FLAGLISTBOX: Trying to draw background n != 0") ); } } wxCoord FlagListBox::OnMeasureItem(size_t n) const { if ( this->IsReady()) { return SkinSystem::IdealIconHeight; } else { return this->GetSize().y; } } void FlagListBox::OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const { wxColour background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); if (this->IsReady()) { FlagListCheckBoxItem* item = FindFlagAt(n); if (item != NULL && item->GetFlagString().IsEmpty()) { // category header background = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); } } dc.DestroyClippingRegion(); wxBrush b(background); dc.SetPen(wxPen(background)); dc.SetBrush(b); dc.SetBackground(b); dc.DrawRectangle(rect); } void FlagListBox::OnDoubleClickFlag(wxCommandEvent &WXUNUSED(event)) { wxCHECK_RET(this->IsReady(), _T("OnDoubleClickFlag() called when flag list box is not ready.")); const wxString* webURL = this->flagData->GetWebURL(this->GetSelection()); wxCHECK_RET(webURL != NULL, _T("GetWebURL() returned NULL, which shouldn't happen.")); if (!webURL->IsEmpty()) { wxLaunchDefaultBrowser(*webURL); } } void FlagListBox::LoadEnabledFlags() { wxCHECK_RET(this->IsReady(), _T("LoadEnabledFlags() called when flag list box is not ready.")); wxCHECK_RET(ProfileProxy::GetProxy()->IsFlagDataReady(), _T("LoadEnabledFlags() called when proxy flag data is not ready.")); wxCHECK_RET(!this->flagsLoaded, _T("LoadEnabledFlags() called when flags have already been loaded.")); std::vector enabledFlags( ProfileProxy::GetProxy()->GetEnabledFlags()); for (std::vector::const_iterator it = enabledFlags.begin(), end = enabledFlags.end(); it != end; ++it) { const wxString& flag(*it); wxCHECK_RET(this->SetFlag(flag, true), wxString::Format( _T("LoadEnabledFlags(): Couldn't find flag %s"), flag.c_str())); } this->flagsLoaded = true; } bool FlagListBox::SetFlag( const wxString& flagString, const bool state, const bool updateProxy) { wxCHECK_MSG(this->IsReady(), false, _T("SetFlag() called when flag list box is not ready.")); wxCHECK_MSG(ProfileProxy::GetProxy()->IsFlagDataReady(), false, _T("SetFlag() called when proxy flag data is not ready.")); wxCHECK_MSG(!flagString.IsEmpty(), false, _T("SetFlag() called with empty flagString.")); for (FlagListCheckBoxItems::iterator it = this->checkBoxes.begin(), end = this->checkBoxes.end(); it != end; ++it) { FlagListCheckBoxItem* item = *it; if (!item->GetFlagString().IsEmpty() && item->GetFlagString() == flagString) { item->GetCheckBox()->SetValue(state); if (updateProxy) { ProfileProxy::GetProxy()->SetFlag(flagString, state); } return true; } } return false; } BEGIN_EVENT_TABLE(FlagListBox, wxVListBox) EVT_LISTBOX_DCLICK(ID_FLAGLISTBOX, FlagListBox::OnDoubleClickFlag) END_EVENT_TABLE() bool FlagListBox::SetFlagSet(const wxString& setToFind) { wxASSERT(!setToFind.IsEmpty()); wxCHECK_MSG(this->IsReady(), false, _T("SetFlagSet() called when flag list box is not ready.")); const FlagSet* flagSet = this->flagData->GetFlagSet(setToFind); if ( flagSet == NULL ) { return false; } wxArrayString::const_iterator disableIter = flagSet->flagsToDisable.begin(); while ( disableIter != flagSet->flagsToDisable.end() ) { if (!this->SetFlag(*disableIter, false, true)) { wxLogWarning(_T("Could not find flag %s to disable for flag set %s."), disableIter->c_str(), setToFind.c_str()); } disableIter++; } wxArrayString::const_iterator enableIter = flagSet->flagsToEnable.begin(); while ( enableIter != flagSet->flagsToEnable.end() ) { if (!this->SetFlag(*enableIter, true, true)) { wxLogWarning(_T("Could not find flag %s to enable for flag set %s."), enableIter->c_str(), setToFind.c_str()); } enableIter++; } return true; } void FlagListBox::GetFlagSets(wxArrayString& arr) const { wxASSERT(arr.IsEmpty()); wxCHECK_RET(this->IsReady(), _T("GetFlagSets() called when flag list box is not ready.")); this->flagData->GetFlagSetNames(arr); } wxlauncher-0.9.4/code/controls/FlagListBox.h0000644000000000000000000000754412155177255020772 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FLAGLISTBOX_H #define FLAGLISTBOX_H #include #include #include "apis/FlagListManager.h" class FlagListCheckBox: public wxCheckBox { public: FlagListCheckBox( wxWindow* parent, const wxString& label, const wxString& flagString); void OnClicked(wxCommandEvent &event); private: FlagListCheckBox(); wxString flagString; }; class FlagListCheckBoxItem { public: FlagListCheckBoxItem(const wxString& fsoCategory); FlagListCheckBoxItem(FlagListCheckBox& checkBox, wxSizer& checkBoxSizer, const wxString& shortDescription, const wxString& flagString, bool isRecommendedFlag); ~FlagListCheckBoxItem(); const wxString& GetFsoCategory() const { return this->fsoCategory; } FlagListCheckBox* GetCheckBox() { return this->checkBox; } wxSizer* GetCheckBoxSizer() { return this->checkBoxSizer; } const wxString& GetShortDescription() const { return this->shortDescription; } const wxString& GetFlagString() const { return this->flagString; } bool IsRecommendedFlag() const { return this->isRecommendedFlag; } private: FlagListCheckBoxItem(); wxString fsoCategory; FlagListCheckBox* checkBox; wxSizer* checkBoxSizer; wxString shortDescription; wxString flagString; bool isRecommendedFlag; }; WX_DECLARE_LIST(FlagListCheckBoxItem, FlagListCheckBoxItems); /** Flag list box is ready for use. */ DECLARE_EVENT_TYPE(EVT_FLAG_LIST_BOX_READY, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, FlagListBoxReadyEventHandlers); class FlagListBox: public wxVListBox { public: FlagListBox(wxWindow* parent); ~FlagListBox(); void RegisterFlagListBoxReady(wxEvtHandler *handler); void UnRegisterFlagListBoxReady(wxEvtHandler *handler); virtual void OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const; virtual void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const; virtual wxCoord OnMeasureItem(size_t n) const; void OnDoubleClickFlag(wxCommandEvent &event); /** Loads enabled flags from the proxy and checks the corresponding boxes. */ void LoadEnabledFlags(); /** Tries to find the flagSet specified and then set or unset all flags contained in the flag set, returns true on success, returns false iff it cannot find the flagset. That is, will return true if none of the flags in the flag set are real flags. */ bool SetFlagSet(const wxString& setToFind); void GetFlagSets(wxArrayString& arr) const; void AcceptFlagData(FlagFileData* flagData); bool IsReady() const { return this->isReady; } bool FlagsLoaded() const { return this->flagsLoaded; } private: FlagListBoxReadyEventHandlers flagListBoxReadyHandlers; void GenerateFlagListBoxReady(); bool isReadyEventGenerated; bool isReady; bool flagsLoaded; /** Tries to find flagString in the list of flags and set it to state. returns true on successful set, returns false if cannot find flag. Will update the proxy if updateProxy is true. */ bool SetFlag(const wxString& flagString, bool state, bool updateProxy = false); FlagFileData* flagData; FlagListCheckBoxItems checkBoxes; void GenerateCheckBoxes(const FlagListBoxData& data); bool areCheckBoxesGenerated; FlagListCheckBoxItem* FindFlagAt(size_t n) const; DECLARE_EVENT_TABLE(); }; #endif wxlauncher-0.9.4/code/controls/LightingPresets.cpp0000644000000000000000000002756712155177255022271 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "controls/LightingPresets.h" #include "apis/ProfileProxy.h" #include "apis/TCManager.h" #include "controls/ModList.h" #include "global/ids.h" #include "global/MemoryDebugging.h" #include "global/ModDefaults.h" #include "generated/configure_launcher.h" #include #include Preset::Preset(const wxString& name, const int buttonId, const wxString& flagSet): name(name), buttonId(buttonId), flagSet(flagSet) { } void Preset::SetFlagSet(const wxString& flagSet) { wxCHECK_RET(!flagSet.IsEmpty(), _T("SetFlagSet() given empty flag set!")); this->flagSet = flagSet; } // vertical spacing between radio buttons is platform-specific #if IS_WIN32 const int RADIOBUTTON_SPACING = 20; #elif IS_LINUX const int RADIOBUTTON_SPACING = 5; #else const int RADIOBUTTON_SPACING = 10; #endif const int DEFAULT_PRESET_ID = ID_PRESETS_OFF; PresetHashMap LightingPresets::presets; LightingPresets::LightingPresets(wxWindow* parent) : wxPanel(parent, wxID_ANY) { if (presets.size() == 0) { InitializePresets(); } TCManager::RegisterTCActiveModChanged(this); ProfileProxy::GetProxy()->RegisterProxyFlagDataReady(this); wxStaticBox* lightingPresetsBox = new wxStaticBox(this, wxID_ANY, _("Lighting presets")); wxHyperlinkCtrl* presetDescsUrl = new wxHyperlinkCtrl(this, wxID_ANY, _T("Preset descriptions"), _T("http://www.hard-light.net/wiki/index.php/Sample_Lighting_Settings")); wxButton* customFlagsCopyButton = new wxButton(this, ID_COPY_PRESET_BUTTON, _T("Copy selected preset to custom flags")); wxRadioButton* radioButton1 = new wxRadioButton (this, ID_PRESETS_OFF, _T("Presets off"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); wxRadioButton* radioButton2 = new wxRadioButton (this, ID_PRESET_BASELINE, DEFAULT_MOD_RECOMMENDED_LIGHTING_NAME); wxRadioButton* radioButton3 = new wxRadioButton (this, ID_PRESET_DABRAIN, _T("DaBrain's")); wxRadioButton* radioButton4 = new wxRadioButton (this, ID_PRESET_HERRA_TOHTORI, _T("Herra Tohtori's")); wxRadioButton* radioButton5 = new wxRadioButton (this, ID_PRESET_CKID, _T("CKid's")); wxRadioButton* radioButton6 = new wxRadioButton (this, ID_PRESET_COLECAMPBELL666, _T("ColeCampbell666's")); wxRadioButton* radioButton7 = new wxRadioButton (this, ID_PRESET_CASTOR, _T("Castor's")); wxRadioButton* radioButton8 = new wxRadioButton (this, ID_PRESET_SPIDEY, _T("Spidey's")); wxRadioButton* radioButton9 = new wxRadioButton (this, ID_PRESET_WOOLIE_WOOL, _T("Woolie Wool's")); this->Initialize(); wxGridBagSizer* lightingInsideSizer = new wxGridBagSizer(); lightingInsideSizer->Add(presetDescsUrl, wxGBPosition(0,0), wxGBSpan(1,1), wxALIGN_CENTER_HORIZONTAL|wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(customFlagsCopyButton, wxGBPosition(1,0), wxGBSpan(1,1), wxALIGN_CENTER_HORIZONTAL|wxBOTTOM, RADIOBUTTON_SPACING + 5); lightingInsideSizer->Add(radioButton1, wxGBPosition(2,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton2, wxGBPosition(3,0), wxGBSpan(1,1), wxEXPAND|wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton3, wxGBPosition(4,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton4, wxGBPosition(5,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton5, wxGBPosition(6,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton6, wxGBPosition(7,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton7, wxGBPosition(8,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton8, wxGBPosition(9,0), wxGBSpan(1,1), wxBOTTOM, RADIOBUTTON_SPACING); lightingInsideSizer->Add(radioButton9, wxGBPosition(10,0), wxGBSpan(1,1)); wxStaticBoxSizer* lightingPresetsSizer = new wxStaticBoxSizer(lightingPresetsBox, wxHORIZONTAL); lightingPresetsSizer->Add(lightingInsideSizer, wxSizerFlags().Expand().Border(wxALL, 5)); this->SetSizer(lightingPresetsSizer); } void LightingPresets::InitializePresets() { wxASSERT_MSG(presets.size() == 0, _T("presets have already been initialized")); presets[ID_PRESETS_OFF] = Preset(_T("Off"), ID_PRESETS_OFF, wxEmptyString); presets[ID_PRESET_BASELINE] = Preset(_T("BaselineRecommended"), ID_PRESET_BASELINE, DEFAULT_MOD_RECOMMENDED_LIGHTING_FLAGSET); presets[ID_PRESET_DABRAIN] = Preset(_T("DaBrain"), ID_PRESET_DABRAIN, _T("-ambient_factor 10 -no_emissive_light -spec_exp 7.0 -spec_point 8.6 -spec_static 12.8 -spec_tube 5.0")); presets[ID_PRESET_HERRA_TOHTORI] = Preset(_T("HerraTohtori"), ID_PRESET_HERRA_TOHTORI, _T("-ambient_factor 35 -no_emissive_light -spec_exp 15 -spec_point 1.2 -spec_static 1.5 -spec_tube 1.5 -ogl_spec 20")); presets[ID_PRESET_CKID] = Preset(_T("CKid"), ID_PRESET_CKID, _T("-ambient_factor 35 -no_emissive_light -spec_exp 16.7 -spec_point 0.6 -spec_static 0.9 -spec_tube 1")); presets[ID_PRESET_COLECAMPBELL666] = Preset(_T("ColeCampbell666"), ID_PRESET_COLECAMPBELL666, _T("-ambient_factor 0 -no_emissive_light -spec_exp 11 -spec_point .6 -spec_static .8 -spec_tube .4 -ogl_spec 80")); presets[ID_PRESET_CASTOR] = Preset(_T("Castor"), ID_PRESET_CASTOR, _T("-ambient_factor 75 -spec_exp 7.0 -spec_point 8.6 -spec_static 3.0 -spec_tube 5.0")); presets[ID_PRESET_SPIDEY] = Preset(_T("Spidey"), ID_PRESET_SPIDEY, _T("-ambient_factor 5 -spec_exp 15 -spec_point 1.2 -spec_static 1.7 -spec_tube 1.5 -ogl_spec 50")); presets[ID_PRESET_WOOLIE_WOOL] = Preset(_T("WoolieWool"), ID_PRESET_WOOLIE_WOOL, _T("-ambient_factor 105 -no_emissive_light -spec_exp 9 -spec_point 0.3 -spec_static 0.8 -spec_tube 0.7 -ogl_spec 120")); } BEGIN_EVENT_TABLE(LightingPresets, wxPanel) EVT_BUTTON(ID_COPY_PRESET_BUTTON, LightingPresets::OnCopyLightingPreset) EVT_RADIOBUTTON(ID_PRESETS_OFF, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_BASELINE, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_DABRAIN, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_HERRA_TOHTORI, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_CKID, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_COLECAMPBELL666, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_CASTOR, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_SPIDEY, LightingPresets::OnSelectLightingPreset) EVT_RADIOBUTTON(ID_PRESET_WOOLIE_WOOL, LightingPresets::OnSelectLightingPreset) EVT_COMMAND(wxID_NONE, EVT_TC_ACTIVE_MOD_CHANGED, LightingPresets::OnActiveModChanged) EVT_COMMAND(wxID_NONE, EVT_PROXY_FLAG_DATA_READY, LightingPresets::OnProxyFlagDataReady) END_EVENT_TABLE() void LightingPresets::OnSelectLightingPreset(wxCommandEvent &event) { int id = event.GetId(); const wxString& presetName = PresetButtonIdToPresetName(id); wxLogDebug(_T("lighting preset %s selected"), presetName.c_str()); wxButton* copyPresetButton = dynamic_cast(wxWindow::FindWindowById(ID_COPY_PRESET_BUTTON, this)); wxCHECK_RET( copyPresetButton != NULL, _T("Unable to find copy lighting preset button")); if (id == ID_PRESETS_OFF) { copyPresetButton->Disable(); } else { copyPresetButton->Enable(); } ProfileProxy::GetProxy()->SetLightingPreset(presetName); } void LightingPresets::OnCopyLightingPreset(wxCommandEvent &WXUNUSED(event)) { wxASSERT_MSG(presets.size() != 0, _T("presets have not been initialized")); wxCHECK_RET(ProfileProxy::GetProxy()->HasLightingPreset(), _T("copy lighting preset button pressed with no preset stored in profile")); wxString presetName(ProfileProxy::GetProxy()->GetLightingPresetName()); wxCHECK_RET(presetName != presets[ID_PRESETS_OFF].GetName(), _T("copy lighting preset button pressed when 'presets off' is selected")); wxLogDebug(_T("attempting to copy preset named %s to custom flags"), presetName.c_str()); ProfileProxy::GetProxy()->CopyPresetToCustomFlags(); this->Reset(); } void LightingPresets::OnActiveModChanged(wxCommandEvent &WXUNUSED(event)) { const ModItem* activeMod = ModList::GetActiveMod(); wxCHECK_RET(activeMod != NULL, _T("LightingPresets::OnActiveModChanged(): activeMod is NULL!")); presets[ID_PRESET_BASELINE].SetFlagSet(activeMod->recommendedlightingflagset); wxRadioButton* radioButtonRecommended = dynamic_cast( wxWindow::FindWindowById(ID_PRESET_BASELINE, this)); wxCHECK_RET(radioButtonRecommended != NULL, _T("Could not find recommended preset radio button")); radioButtonRecommended->SetLabel(activeMod->recommendedlightingname); } void LightingPresets::OnProxyFlagDataReady(wxCommandEvent &WXUNUSED(event)) { this->Initialize(); } const wxString& LightingPresets::PresetNameToPresetFlagSet(const wxString& presetName) { if (presets.size() == 0) { // for registry_helper, so that it can write cmdline_fso.cfg InitializePresets(); } for (PresetHashMap::iterator it = presets.begin(), end = presets.end(); it != end; ++it) { if (it->second.GetName() == presetName) { return it->second.GetFlagSet(); } } wxLogWarning(_T("PresetNameToPresetFlagSet: unknown preset name %s, returning default (%s)"), presetName.c_str(), presets[DEFAULT_PRESET_ID].GetName().c_str()); return presets[DEFAULT_PRESET_ID].GetFlagSet(); } void LightingPresets::Initialize() { wxString presetName; if (ProfileProxy::GetProxy()->HasLightingPreset()) { presetName = ProfileProxy::GetProxy()->GetLightingPresetName(); } else { presetName = PresetButtonIdToPresetName(DEFAULT_PRESET_ID); } int presetButtonId = PresetNameToPresetButtonId(presetName); wxRadioButton *selectedPresetButton = dynamic_cast(wxWindow::FindWindowById(presetButtonId, this)); wxCHECK_RET( selectedPresetButton != NULL, _T("Cannot find selected preset button")); selectedPresetButton->SetValue(true); wxCommandEvent presetSelectionEvent(wxEVT_COMMAND_RADIOBUTTON_SELECTED, presetButtonId); this->OnSelectLightingPreset(presetSelectionEvent); } void LightingPresets::Reset() { wxRadioButton* presetsOffButton = dynamic_cast(wxWindow::FindWindowById(ID_PRESETS_OFF, this)); wxCHECK_RET( presetsOffButton != NULL, _T("Unable to find lighting presets off radio button")); presetsOffButton->SetValue(true); wxCommandEvent resetEvent(wxEVT_COMMAND_RADIOBUTTON_SELECTED, ID_PRESETS_OFF); this->OnSelectLightingPreset(resetEvent); } int LightingPresets::PresetNameToPresetButtonId(const wxString& presetName) { wxASSERT_MSG(presets.size() != 0, _T("presets have not been initialized")); for (PresetHashMap::iterator it = presets.begin(), end = presets.end(); it != end; ++it) { if (it->second.GetName() == presetName) { return it->second.GetButtonId(); } } wxLogWarning(_T("PresetNameToPresetButtonId: unknown preset name %s, returning default (%s)"), presetName.c_str(), presets[DEFAULT_PRESET_ID].GetName().c_str()); return DEFAULT_PRESET_ID; } const wxString& LightingPresets::PresetButtonIdToPresetName(int buttonId) { wxASSERT_MSG(presets.size() != 0, _T("presets have not been initialized")); PresetHashMap::iterator it = presets.find(buttonId); wxCHECK_MSG(it != presets.end(), presets[DEFAULT_PRESET_ID].GetName(), wxString::Format(_T("PresetButtonIdToPresetName given invalid button ID %d"), buttonId)); return it->second.GetName(); } wxlauncher-0.9.4/code/controls/LightingPresets.h0000644000000000000000000000375112155177255021723 0ustar rootroot00000000000000/* Copyright (C) 2009-2011 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIGHTINGPRESETS_H #define LIGHTINGPRESETS_H #include class Preset { public: Preset() { } // needed for wxHashMap, unfortunately Preset(const wxString& name, const int buttonId, const wxString& preset); const wxString& GetName() const { return this->name; } const int GetButtonId() const { return this->buttonId; } const wxString& GetFlagSet() const { return this->flagSet; } void SetFlagSet(const wxString& flagSet); private: wxString name; int buttonId; wxString flagSet; }; // from wxWidgets window IDs to Presets WX_DECLARE_HASH_MAP(int, Preset, wxIntegerHash, wxIntegerEqual, PresetHashMap); class LightingPresets: public wxPanel { public: LightingPresets(wxWindow* parent); void OnSelectLightingPreset(wxCommandEvent &event); void OnCopyLightingPreset(wxCommandEvent &event); void OnActiveModChanged(wxCommandEvent &event); void OnProxyFlagDataReady(wxCommandEvent &event); static const wxString& PresetNameToPresetFlagSet(const wxString& presetName); private: void Initialize(); void Reset(); static void InitializePresets(); static int PresetNameToPresetButtonId(const wxString& presetName); static const wxString& PresetButtonIdToPresetName(int buttonId); static PresetHashMap presets; DECLARE_EVENT_TABLE() }; #endif wxlauncher-0.9.4/code/controls/Logger.cpp0000644000000000000000000000634712155177255020366 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "controls/Logger.h" #include "controls/StatusBar.h" #include "global/MemoryDebugging.h" ////// Logger const wxString levels[] = { _T("FATAL"), _T("ERROR"), _T("WARN "), _T("MSG "), _T("STSBR"), _T("INFO "), _T("DEBUG"), }; /** Constructor. */ Logger::Logger() { wxFileName outFileName(wxStandardPaths::Get().GetUserDataDir(), _T("wxLauncher.log")); if (!outFileName.DirExists() && !wxFileName::Mkdir(outFileName.GetPath(), 0700, wxPATH_MKDIR_FULL) ) { wxLogFatalError(_T("Unable to create folder to place log in. (%s)"), outFileName.GetPath().c_str()); } this->outFile = new wxFFile(outFileName.GetFullPath(), _T("wb")); if (!outFile->IsOpened()) { wxLogFatalError(_T("Unable to open log output file. (%s)"), outFileName.GetFullPath().c_str()); } this->out = new wxFFileOutputStream(*outFile); wxASSERT_MSG(out->IsOk(), _T("Log output file is not valid!")); this->out->Write("\357\273\277", 3); this->statusBar = NULL; } /** Destructor. */ Logger::~Logger() { char exitmsg[] = "\nLog closed.\n"; this->out->Write(exitmsg, strlen(exitmsg)); this->out->Close(); delete this->out; delete this->outFile; } /** Overridden as per wxWidgets docs to implement a wxLog. */ void Logger::DoLog(wxLogLevel level, const wxChar *msg, time_t time) { wxString timestr = wxDateTime(time).Format(_T("%y%j%H%M%S"), wxDateTime::GMT0); wxString str = wxString::Format( _T("%s:%s:"), timestr.c_str(), levels[level].c_str()); wxString buf(msg); out->Write(str.mb_str(wxConvUTF8), str.size()); out->Write(buf.mb_str(wxConvUTF8), buf.size()); out->Write("\n", 1); if ( this->statusBar != NULL ) { if ( level == 1 ) { // error this->statusBar->SetMainStatusText(buf, ID_SB_ERROR); } else if ( level == 2 ) { // warning this->statusBar->SetMainStatusText(buf, ID_SB_WARNING); } else if ( level == 3 || level == 4 ) { // message, statubar this->statusBar->SetMainStatusText(buf, ID_SB_OK); } else if ( level == 5 ) { // info this->statusBar->SetMainStatusText(buf, ID_SB_INFO); } } } void Logger::Flush() { outFile->Flush(); // Warning: ignoring return value from Flush(). } /** Stores the pointer the status bar that I am to send status messages to. If a status bar is already set, function will do nothing to the old statusbar. Logger does not take over managment of the statusbar passed in. */ void Logger::SetStatusBarTarget(StatusBar *bar) { this->statusBar = bar; } wxlauncher-0.9.4/code/controls/Logger.h0000644000000000000000000000220312155177255020016 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LAUNCHERLOGGER_H #define LAUNCHERLOGGER_H #include #include #include "controls/StatusBar.h" class Logger: public wxLog { public: Logger(); virtual ~Logger(); void SetStatusBarTarget(StatusBar *bar); virtual void DoLog(wxLogLevel level, const wxChar *msg, time_t time); virtual void Flush(); private: wxFFileOutputStream *out; StatusBar *statusBar; wxFFile *outFile; }; #endif wxlauncher-0.9.4/code/controls/ModList.cpp0000644000000000000000000013354712155177255020525 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apis/SkinManager.h" #include "global/ids.h" #include "global/ProfileKeys.h" #include "global/ModDefaults.h" #include "global/ModIniKeys.h" #include "global/Utils.h" #include "controls/ModList.h" #include "apis/ProfileManager.h" #include "apis/TCManager.h" #include "global/MemoryDebugging.h" using TextUtils::Words; using TextUtils::ArrayOfWords; const wxString NO_MOD(_("(No mod)")); // to keep the presets box from overlapping with flag list const size_t MAX_PRESET_NAME_LENGTH = 32; class ModInfoDialog: wxDialog { public: ModInfoDialog(ModItem* item, wxWindow* parent); void OnLinkClicked(wxHtmlLinkEvent &event); private: class ImageDrawer: public wxPanel { public: ImageDrawer(ModInfoDialog* parent); void OnDraw(wxPaintEvent &event); private: ModInfoDialog* parent; DECLARE_EVENT_TABLE(); }; friend class ImageDrawer; ModItem* item; }; ConfigPair::ConfigPair(const wxString &shortname, wxFileConfig *config) { this->shortname = shortname; this->config = config; } ConfigPair::~ConfigPair() { if ( this->config != NULL ) { delete this->config; } } #include WX_DEFINE_OBJARRAY(ConfigArray); bool CompareModItems(ModItem* item1, ModItem* item2) { wxASSERT(item1 != NULL); wxASSERT(item2 != NULL); wxString item1Name( (!item1->name.IsEmpty()) ? item1->name : item1->shortname); wxString item2Name( (!item2->name.IsEmpty()) ? item2->name : item2->shortname); // ignore a leading "the" for comparison purposes wxString temp; if (item1Name.Lower().StartsWith(_T("the"), &temp)) { item1Name = temp.Trim(false); } if (item2Name.Lower().StartsWith(_T("the"), &temp)) { item2Name = temp.Trim(false); } // (No mod) must come before all other mods if (!item1->shortname.Cmp(NO_MOD)) { return true; } else if (!item2->shortname.Cmp(NO_MOD)) { return false; } else { return item1Name.CmpNoCase(item2Name) < 0; } } const ModItem* ModList::activeMod = NULL; class ModIniFinder: public wxDirTraverser { public: const wxArrayString& GetFiles() const { return files; } virtual wxDirTraverseResult OnDir(const wxString& dirname) { if (ShouldIgnore(dirname)) { return wxDIR_IGNORE; } else { return wxDIR_CONTINUE; } } virtual wxDirTraverseResult OnFile(const wxString& filename) { if (filename.EndsWith(_T("mod.ini"))) { files.Add(filename); } return wxDIR_CONTINUE; } private: bool ShouldIgnore(const wxString& dirname) { const wxString realDirName(dirname.AfterLast(wxFileName::GetPathSeparator())); return realDirName.EndsWith(_T(".app")) || realDirName.StartsWith(_T(".")); } wxArrayString files; }; void ModList::SetSkinBitmap( const wxFileConfig& config, const wxString& modIniKey, const wxString& tcPath, const wxString& bitmapName, bool (Skin::* setFnPtr)(const wxBitmap&)) { wxCHECK_RET(!modIniKey.IsEmpty(), _T("SetSkinBitmap(): modIniKey is empty!")); wxCHECK_RET(!tcPath.IsEmpty(), _T("SetSkinBitmap(): tcPath is empty!")); wxCHECK_RET(!bitmapName.IsEmpty(), _T("SetSkinBitmap(): bitmapName is empty!")); wxCHECK_RET(setFnPtr != NULL, _T("SetSkinBitmap(): setFnPtr is NULL!")); wxCHECK_RET(this->TCSkin != NULL, _T("SetSkinBitmap(): TCSkin is NULL!")); wxString bitmapPath; readIniFileString(&config, modIniKey, bitmapPath); if ( !bitmapPath.IsEmpty() ) { wxFileName filename; if (SkinSystem::SearchFile(filename, tcPath, wxEmptyString, bitmapPath)) { if ((this->TCSkin->*setFnPtr)(wxBitmap(filename.GetFullPath(), wxBITMAP_TYPE_ANY))) { wxLogDebug(_T("Set skin %s to '%s'"), bitmapName.c_str(), filename.GetFullPath().c_str()); } else { wxLogWarning(_T("Could not set skin %s to '%s'"), bitmapName.c_str(), filename.GetFullPath().c_str()); } } else { wxLogWarning(_T("Could not find skin %s file '%s%c%s'."), bitmapName.c_str(), tcPath.c_str(), wxFileName::GetPathSeparator(), bitmapPath.c_str()); } } } ModList::ModList(wxWindow *parent, wxSize& size, wxString tcPath) : configFiles(new ConfigArray()), tableData(new ModItemArray()), TCSkin(NULL) { this->Create(parent, ID_MODLISTBOX, wxDefaultPosition, size, wxLB_SINGLE | wxLB_ALWAYS_SB | wxBORDER); this->SetMargins(10, 10); SkinSystem::RegisterTCSkinChanged(this); std::vector modsTemp; // for use in presorting // scan for mods in the current TCs directory ModIniFinder iniFinder; wxASSERT(wxDir::Exists(tcPath)); wxDir dir(tcPath); dir.Traverse(iniFinder, _T("mod.ini")); wxArrayString foundInis(iniFinder.GetFiles()); if ( foundInis.Count() > 0 ) { wxLogDebug(_T("I found %d .ini files:"), foundInis.Count()); } else { wxLogDebug(_T("I did not find any .ini files.")); } // parse mod.ini's in all of the directories that contain one wxLogDebug(_T("Inserting '(No mod)'")); wxFileName tcmodini(tcPath, _T("mod.ini")); if ( tcmodini.IsOk() && tcmodini.FileExists() ) { wxLogDebug(_T(" Found a mod.ini in the root TC folder. (%s)"), tcmodini.GetFullPath().c_str()); if (!ParseModIni(tcmodini.GetFullPath(), tcPath, true)) { wxLogError(_T(" Error parsing mod.ini in the root TC folder. (%s)"), tcmodini.GetFullPath().c_str()); } // make sure that a mod.ini in the root TC folder is not apart of this set // because it will be addressed shortly and specificly int pos = foundInis.Index(tcmodini.GetFullPath()); if ( pos != wxNOT_FOUND ) { foundInis.RemoveAt(pos); } } else { this->configFiles->Add(new ConfigPair(NO_MOD, new wxFileConfig())); wxLogDebug(_T(" Using defaults for TC.")); } wxLogDebug(_T("Starting to parse mod.ini's...")); for (size_t i = 0; i < foundInis.Count(); i++) { wxLogDebug(_T(" Parsing %s"), foundInis.Item(i).c_str()); if (!ParseModIni(foundInis.Item(i), tcPath)) { wxLogError(_T(" Parsing %s failed."), foundInis.Item(i).c_str()); } } // create internal repesentation of the mod.ini's wxLogDebug(_T("Transforming mod.ini's")); for(size_t i = 0; i < this->configFiles->size(); i++) { wxString shortname = this->configFiles->Item(i).shortname; wxFileConfig* config = this->configFiles->Item(i).config; ModItem* item = new ModItem(); wxLogDebug(_T(" %s"), shortname.c_str()); item->shortname = shortname; readIniFileString(config, MOD_INI_KEY_LAUNCHER_MOD_NAME, item->name); wxString image255x112path; readIniFileString(config, MOD_INI_KEY_LAUNCHER_IMAGE_255X112, image255x112path); if (!image255x112path.IsEmpty()) { wxFileName filename; wxString searchShortname((i == 0) ? wxString(wxEmptyString) : shortname); if (SkinSystem::SearchFile(filename, tcPath, searchShortname, image255x112path)) { wxImage image(filename.GetFullPath()); if (image.IsOk()) { if ((image.GetWidth() == SkinSystem::ModInfoDialogImageWidth) && (image.GetHeight() == SkinSystem::ModInfoDialogImageHeight)) { item->image255x112 = wxBitmap(image); } else { wxLogWarning(_T("image255x112 has invalid dimensions %dx%d"), image.GetWidth(), image.GetHeight()); } } else { wxLogWarning(_T("Could not set image255x112 file to '%s'"), filename.GetFullPath().c_str()); } } else { wxLogWarning(_T("Could not find image255x112 file %s%s"), (searchShortname.IsEmpty() ? wxEmptyString : wxString(searchShortname + wxFileName::GetPathSeparator()).c_str()), image255x112path.c_str()); } } wxString image182x80path; readIniFileString(config, MOD_INI_KEY_LAUNCHER_IMAGE_182X80, image182x80path); if (!image182x80path.IsEmpty()) { wxFileName filename; wxString searchShortname((i == 0) ? wxString(wxEmptyString) : shortname); if (SkinSystem::SearchFile(filename, tcPath, searchShortname, image182x80path)) { wxImage image(filename.GetFullPath()); if (image.IsOk()) { if ((image.GetWidth() == SkinSystem::ModListImageWidth) && (image.GetHeight() == SkinSystem::ModListImageHeight)) { item->image182x80 = wxBitmap(image); } else { wxLogWarning(_T("image182x80 has invalid dimensions %dx%d"), image.GetWidth(), image.GetHeight()); } } else { wxLogWarning(_T("Could not set image182x80 file to '%s'"), filename.GetFullPath().c_str()); } } else { wxLogWarning(_T("Could not find image182x80 file %s%s"), (searchShortname.IsEmpty() ? wxEmptyString : wxString(searchShortname + wxFileName::GetPathSeparator()).c_str()), image182x80path.c_str()); } } // other cases, which don't require handling here: // if both images are Ok, then we just use them // if both images are not Ok, then we use SkinSystem::modImage/smallModImage if (item->image255x112.IsOk() && !item->image182x80.IsOk()) { item->image182x80 = SkinSystem::MakeModListImage(item->image255x112); } else if (!item->image255x112.IsOk() && item->image182x80.IsOk()) { item->image255x112 = SkinSystem::MakeModInfoDialogImage(item->image182x80); } wxASSERT(item->image255x112.IsOk() == item->image182x80.IsOk()); readIniFileString(config, MOD_INI_KEY_LAUNCHER_INFO_TEXT, item->infotext); readIniFileString(config, MOD_INI_KEY_LAUNCHER_AUTHOR, item->author); readIniFileString(config, MOD_INI_KEY_LAUNCHER_NOTES, item->notes); config->Read(MOD_INI_KEY_LAUNCHER_WARN, &(item->warn), false); readIniFileString(config, MOD_INI_KEY_LAUNCHER_WEBSITE, item->website); readIniFileString(config, MOD_INI_KEY_LAUNCHER_FORUM, item->forum); readIniFileString(config, MOD_INI_KEY_LAUNCHER_BUGS, item->bugs); readIniFileString(config, MOD_INI_KEY_LAUNCHER_SUPPORT, item->support); config->Read( MOD_INI_KEY_RESOLUTION_MIN_HORIZONTAL_RES, &item->minhorizontalres, DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES); config->Read( MOD_INI_KEY_RESOLUTION_MIN_VERTICAL_RES, &item->minverticalres, DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES); if ((item->minhorizontalres < DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES) || (item->minverticalres < DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES)) { wxLogWarning(_T("Invalid minimum resolution %dx%d, using default"), item->minhorizontalres, item->minverticalres); item->minhorizontalres = DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES; item->minverticalres = DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES; } readIniFileString( config, MOD_INI_KEY_RECOMMENDED_LIGHTING_NAME, item->recommendedlightingname); readIniFileString( config, MOD_INI_KEY_RECOMMENDED_LIGHTING_FLAGSET, item->recommendedlightingflagset); if (!item->recommendedlightingflagset.IsEmpty()) { if (item->recommendedlightingname.IsEmpty()) { item->recommendedlightingname = (i == 0) ? _("TC recommended") : _("Mod recommended"); // required because & is interpreted as setting keyboard shortcut // see http://docs.wxwidgets.org/stable/wx_wxcontrol.html#wxcontrolsetlabel item->recommendedlightingname.Replace(_T("&"), _T("&&")); } else { item->recommendedlightingname.Trim(true).Trim(false); item->recommendedlightingname.Truncate(MAX_PRESET_NAME_LENGTH); } } else { wxLogDebug(_T("Recommended lighting flagset is missing or empty; using defaults.")); item->recommendedlightingname = DEFAULT_MOD_RECOMMENDED_LIGHTING_NAME; item->recommendedlightingflagset = DEFAULT_MOD_RECOMMENDED_LIGHTING_FLAGSET; } readIniFileString(config, MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_ON, item->forcedon); readIniFileString(config, MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_OFF, item->forcedoff); readIniFileString(config, MOD_INI_KEY_MULTIMOD_PRIMARY_LIST, item->primarylist); // Log the warning for any mod authors, specifically for those who indicate // that they are mod authors by their having FRED launching enabled bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); if ( config->Exists(MOD_INI_KEY_MULTIMOD_SECONDRY_LIST) && fredEnabled) { wxLogInfo(_T(" DEPRECATION WARNING: Mod '%s' uses deprecated mod.ini parameter 'secondrylist'"), shortname.c_str()); } readIniFileString(config, MOD_INI_KEY_MULTIMOD_SECONDRY_LIST, item->secondarylist); readIniFileString(config, MOD_INI_KEY_MULTIMOD_SECONDARY_LIST, item->secondarylist); // flag sets if ( config->Exists(_T("/flagsetideal")) ) { item->flagsets = new FlagSets(); FlagSetItem* flagset = new FlagSetItem(); readFlagSet(config, _T("/flagsetideal"), *flagset); item->flagsets->Add(flagset); unsigned int counter = 1; bool done = false; do { wxString sectionname = wxString::Format(_T("/flagset%d"), counter); if ( config->Exists( sectionname )) { FlagSetItem* numberedflagset = new FlagSetItem(); readFlagSet(config, sectionname, *numberedflagset); item->flagsets->Add(numberedflagset); } else { done = true; } counter++; } while ( !done ); } else { #if 0 // preprocessing out until this functionality is complete wxLogDebug(_T(" Does Not Contain An idealflagset Section.")); #endif } // skin (only available to TCs) if ( i == 0 ) { if ( config->Exists(_T("/skin")) ) { // deleting any existing TCSkin will be handled by SkinSystem::ResetTCSkin() // so it shouldn't be deleted here this->TCSkin = new Skin(); wxString windowTitle; readIniFileString(config, MOD_INI_KEY_SKIN_WINDOW_TITLE, windowTitle); if (!windowTitle.IsEmpty()) { this->TCSkin->SetWindowTitle(windowTitle); } SetSkinBitmap(*config, MOD_INI_KEY_SKIN_BANNER, tcPath, _T("banner"), &Skin::SetBanner); wxString windowIconPath; readIniFileString(config, MOD_INI_KEY_SKIN_WINDOW_ICON, windowIconPath); if (!windowIconPath.IsEmpty()) { wxFileName filename; if (SkinSystem::SearchFile(filename, tcPath, wxEmptyString, windowIconPath)) { if (this->TCSkin->SetWindowIcon(wxIcon(filename.GetFullPath(), wxBITMAP_TYPE_ICO))) { wxLogDebug(_T("Set skin window icon to '%s'"), filename.GetFullPath().c_str()); } else { wxLogWarning(_T("Could not set skin window icon to '%s'"), filename.GetFullPath().c_str()); } } else { wxLogWarning(_T("Could not find skin window icon file.")); } } wxString welcomeText; readIniFileString(config, MOD_INI_KEY_SKIN_WELCOME_TEXT, welcomeText); if (!welcomeText.IsEmpty()) { this->TCSkin->SetWelcomeText(welcomeText); } SetSkinBitmap(*config, MOD_INI_KEY_SKIN_MOD_IMAGE_255X112, tcPath, _T("mod image"), &Skin::SetModImage); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_MOD_IMAGE_182X80, tcPath, _T("small mod image"), &Skin::SetSmallModImage); // if one mod image is missing, create it by scaling the other one if (this->TCSkin->GetModImage().IsOk() && !this->TCSkin->GetSmallModImage().IsOk()) { this->TCSkin->SetSmallModImage( SkinSystem::MakeModListImage(this->TCSkin->GetModImage())); } else if (!this->TCSkin->GetModImage().IsOk() && this->TCSkin->GetSmallModImage().IsOk()) { this->TCSkin->SetModImage( SkinSystem::MakeModInfoDialogImage(this->TCSkin->GetSmallModImage())); } SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_OK, tcPath, _T("ok icon"), &Skin::SetOkIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_WARNING, tcPath, _T("warning icon"), &Skin::SetWarningIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_WARNING_BIG, tcPath, _T("big warning icon"), &Skin::SetBigWarningIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_ERROR, tcPath, _T("error icon"), &Skin::SetErrorIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_INFO, tcPath, _T("info icon"), &Skin::SetInfoIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_INFO_BIG, tcPath, _T("big info icon"), &Skin::SetBigInfoIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_HELP, tcPath, _T("help icon"), &Skin::SetHelpIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_HELP_BIG, tcPath, _T("big help icon"), &Skin::SetBigHelpIcon); SetSkinBitmap(*config, MOD_INI_KEY_SKIN_ICON_IDEAL, tcPath, _T("ideal icon"), &Skin::SetIdealIcon); wxString newsSourceName; readIniFileString(config, MOD_INI_KEY_SKIN_NEWS_SOURCE, newsSourceName); if (!newsSourceName.IsEmpty()) { const NewsSource* source = NewsSource::FindSource(newsSourceName); if (source != NULL) { this->TCSkin->SetNewsSource(source); } } SkinSystem::GetSkinSystem()->SetTCSkin(this->TCSkin); this->TCSkin = NULL; } else { wxLogDebug(_T(" Does Not Contain A skin Section.")); SkinSystem::GetSkinSystem()->ResetTCSkin(); } } #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now // langauges for ( size_t i = 0; i < SupportedLanguages.Count(); i++ ) { wxString section = wxString::Format(_T("/%s"), SupportedLanguages[i].c_str()); if ( config->Exists(section) ) { if ( item->i18n == NULL ) { item->i18n = new I18nData(); } } I18nItem *temp = NULL; readTranslation(config, SupportedLanguages[i], &temp); if ( temp != NULL ) { (*(item->i18n))[SupportedLanguages[i]] = temp; } } #endif modsTemp.push_back(item); } std::sort(modsTemp.begin(), modsTemp.end(), CompareModItems); for (std::vector::const_iterator it = modsTemp.begin(); it != modsTemp.end(); ++it) { this->tableData->Add(*it); } this->SetItemCount(this->tableData->Count()); SetSelectedMod(); this->infoButton = new wxButton(this, ID_MODLISTBOX_INFO_BUTTON, _("Info")); this->activateButton = new wxButton(this, ID_MODLISTBOX_ACTIVATE_BUTTON, _("Activate")); this->warnBitmap = new wxStaticBitmap(this, wxID_ANY, SkinSystem::GetSkinSystem()->GetWarningIcon()); this->warnBitmap->SetToolTip(_("This mod requires your attention before playing it. Click Info for more details.")); this->buttonSizer = new wxBoxSizer(wxVERTICAL); this->buttonSizer->AddStretchSpacer(2); this->buttonSizer->Add(this->activateButton, wxSizerFlags().Expand().ReserveSpaceEvenIfHidden()); this->buttonSizer->AddStretchSpacer(1); this->buttonSizer->Add(this->infoButton, wxSizerFlags().Expand().ReserveSpaceEvenIfHidden()); this->buttonSizer->AddStretchSpacer(2); wxSizer* warningSizer = new wxBoxSizer(wxVERTICAL); warningSizer->AddStretchSpacer(1); warningSizer->Add(this->warnBitmap, wxSizerFlags().ReserveSpaceEvenIfHidden()); warningSizer->AddStretchSpacer(1); this->sizer = new wxBoxSizer(wxHORIZONTAL); this->sizer->AddStretchSpacer(1); this->sizer->Add(warningSizer, wxSizerFlags().Expand().ReserveSpaceEvenIfHidden()); this->sizer->AddStretchSpacer(1); this->sizer->Add(this->buttonSizer, wxSizerFlags().Expand().ReserveSpaceEvenIfHidden()); this->sizer->AddStretchSpacer(2); this->buttonSizer->Show(false); this->warnBitmap->Show(false); } /** the dtor. Cleans up stuff. */ ModList::~ModList() { SkinSystem::UnRegisterTCSkinChanged(this); if ( this->configFiles != NULL ) { delete this->configFiles; } ModList::activeMod = NULL; if ( this->tableData != NULL ) { delete this->tableData; } // deleting any existing TCSkin will be handled by SkinSystem::ResetTCSkin() // so it shouldn't be deleted here if ( this->sizer != NULL ) { delete this->sizer; } } /** Takes the key to search for and sets location to key's value. If the key is not found, location is unchanged. */ void ModList::readIniFileString(const wxFileConfig* config, const wxString& key, wxString& location) { wxASSERT(config != NULL); wxASSERT(location.IsEmpty()); if ( config->HasEntry(key) ) { config->Read(key, &location); if ( location.EndsWith(_T(";")) ) { location.RemoveLast(); } } wxLogDebug(_T(" %s:'%s'"), key.c_str(), location.IsEmpty() ? _T("Not Specified") : escapeSpecials(location).c_str()); } /** re-escape the newlines in the mod.ini values. */ wxString ModList::escapeSpecials(const wxString& toEscape) { wxString toEscapeTemp(toEscape); wxString::iterator iter = toEscapeTemp.begin(); while (iter != toEscapeTemp.end() ) { if ( *iter == wxChar('\n') ) { wxString::iterator end = iter; end++; toEscapeTemp.replace(iter, end, _T("\\n")); // have to start over because we wrote to the string, // which invalidated the iterator. iter = toEscapeTemp.begin(); } else { ++iter; } } return toEscapeTemp; } /** */ void ModList::readFlagSet(const wxFileConfig* config, const wxString& keyprefix, FlagSetItem& set) { wxCHECK_RET(config != NULL, _T("readFlagSet(): config is NULL!")); readIniFileString(config, wxString::Format(_T("%s/name"), keyprefix.c_str()), set.name); readIniFileString(config, wxString::Format(_T("%s/flagset"), keyprefix.c_str()), set.flagset); readIniFileString(config, wxString::Format(_T("%s/notes"), keyprefix.c_str()), set.notes); } #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now void ModList::readTranslation(wxFileConfig* config, wxString langaugename, I18nItem **trans) { wxString section = wxString::Format(_T("/%s"), langaugename.c_str()); if ( config->Exists(section) ) { *trans = new I18nItem(); readIniFileString(config, wxString::Format(_T("%s/modname"), section.c_str()), &((*trans)->modname)); readIniFileString(config, wxString::Format(_T("%s/infotext"), section.c_str()), &((*trans)->infotext)); } else { wxLogDebug( wxString::Format(_T(" Section '%s' does not exist."), langaugename.c_str())); } } #endif /** Parses the specified mod.ini file and adds it to configFiles. Returns true on success, false otherwise. */ bool ModList::ParseModIni(const wxString& modIniPath, const wxString& tcPath, const bool isNoMod) { wxFFileInputStream stream(modIniPath); if ( stream.IsOk() ) { wxLogDebug(_T(" Opened ok")); } else { wxLogError(_T(" Open failed!")); return false; } // check if the stream is a UTF-8 File (has a BOM) char header[3]; stream.Read(reinterpret_cast(&header), sizeof(header)); stream.SeekI(0); bool isUTF8 = false; if ( header[0] == '\357' && header[1] == '\273' && header[2] == '\277' ) { // is a UTF-8 file isUTF8 = true; } wxMemoryOutputStream tempStream; tempStream.Write(stream); wxStreamBuffer* buf = tempStream.GetOutputStreamBuffer(); const size_t size = buf->GetBufferSize(); char* characterBuffer = new char[size+1]; characterBuffer[size] = '\0'; buf->Seek(0, wxFromStart); // don't try to read in buffer when there is nothing to read. size_t read = (size == 0) ? 0 : buf->Read(reinterpret_cast(characterBuffer), size); if ( read != size ) { wxLogError(_T("read (%d) not equal to size (%d)"), read, size); delete[] characterBuffer; return false; } const wxMBConv* conv = NULL; if ( isUTF8 ) { conv = &wxConvUTF8; } else { conv = &wxConvISO8859_1; } wxString stringBuffer(characterBuffer, *conv); // A hack to insert a backslash into the stream so that when // wxFileConfig escapes the backslashes, the one that is in // the file is returned stringBuffer.Replace(_T("\\"), _T("\\\\")); wxStringInputStream finalBuffer(stringBuffer); wxFileConfig* config = new wxFileConfig(finalBuffer); delete[] characterBuffer; wxString shortname(isNoMod ? NO_MOD : GetShortName(modIniPath, tcPath)); if (!isNoMod) { wxLogDebug(_T(" Mod fancy name is: %s"), config->Read(MOD_INI_KEY_LAUNCHER_MOD_NAME, _T("Not specified")).c_str()); wxLogDebug(_T(" Mod short name is: %s"), shortname.c_str()); } this->configFiles->Add(new ConfigPair(shortname, config)); return true; } /** Set currently select mod as selected or set (No mod) if none or previous does not exist. */ void ModList::SetSelectedMod() { wxString currentMod; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_TC_CURRENT_MOD, ¤tMod, NO_MOD); size_t i; for ( i = 0; i < this->tableData->size(); ++i ) { if ( this->tableData->Item(i).shortname == currentMod ) { break; } } if ( i < this->tableData->size() ) { // found it this->SetSelection(i); } else { this->SetSelection(0); } wxCommandEvent activateModEvent; this->OnActivateMod(activateModEvent); } /** get the mod.ini's short name (base directory) */ /** /modfolder/mod.ini \modfolder\mod.ini */ wxString ModList::GetShortName(const wxString& modIniPath, const wxString& tcPath) { wxArrayString tokens = wxStringTokenize(modIniPath, _T("\\/"), wxTOKEN_STRTOK); /* breakup on folder markers and never return an empty string. */ wxArrayString tcTokens = wxStringTokenize(tcPath, _T("\\/"), wxTOKEN_STRTOK); wxString shortname; // "tokens.GetCount() - 1" is to skip "mod.ini" at end for ( size_t j = tcTokens.GetCount(); j < (tokens.GetCount() - 1); ++j ) { if ( !shortname.IsEmpty() ) { shortname += _T("/"); } shortname += tokens[j]; } return shortname; } void ModList::OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const { wxLogDebug(_T(" Draw %04d,%04d = %04d,%04d"), rect.x, rect.y, rect.width, rect.height); this->tableData->Item(n).Draw(dc, rect, this->IsSelected(n), this->sizer, this->buttonSizer, this->warnBitmap); } void ModList::OnDrawSeparator(wxDC &WXUNUSED(dc), wxRect& WXUNUSED(rect), size_t WXUNUSED(n)) const { //dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); } void ModList::OnDrawBackground(wxDC &dc, const wxRect& rect, size_t n) const { wxLogDebug(_T(" Background %04d,%04d = %04d,%04d"), rect.x, rect.y, rect.width, rect.height); dc.DestroyClippingRegion(); wxColour highlighted = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); wxColour background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); wxString activeMod; ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_MOD, &activeMod, NO_MOD, true); wxBrush b; wxRect selectedRect(rect.x+2, rect.y+2, rect.width-4, rect.height-4); wxRect activeRect(selectedRect.x+3, selectedRect.y+3, selectedRect.width-7, selectedRect.height-7); if ( this->IsSelected(n) ) { b = wxBrush(highlighted, wxTRANSPARENT); dc.SetPen(wxPen(highlighted, 4)); } else if ( this->isCurrentSelectionAnAppendMod(this->tableData->Item(n).shortname) ) { b = wxBrush(highlighted, wxBDIAGONAL_HATCH); dc.SetPen(wxPen(highlighted, 1)); } else if ( this->isCurrentSelectionAPrependMod(this->tableData->Item(n).shortname) ) { b = wxBrush(highlighted, wxFDIAGONAL_HATCH); dc.SetPen(wxPen(highlighted, 1)); } else { b = wxBrush(background); dc.SetPen(wxPen(background, 4)); } dc.SetBackground(b); dc.SetBrush(b); dc.DrawRoundedRectangle(selectedRect, 10.0); if ( activeMod == this->tableData->Item(n).shortname ) { b = wxBrush(highlighted, wxSOLID); dc.SetPen(wxPen(highlighted, 1)); } else if ( this->isAnAppendMod(this->tableData->Item(n).shortname) ) { b = wxBrush(highlighted, wxBDIAGONAL_HATCH); dc.SetPen(wxPen(highlighted, 1)); } else if ( this->isAPrependMod(this->tableData->Item(n).shortname) ) { b = wxBrush(highlighted, wxFDIAGONAL_HATCH); dc.SetPen(wxPen(highlighted, 1)); } else { b = wxBrush(background, wxSOLID); dc.SetPen(wxPen(background, 1)); } dc.SetBackground(b); dc.SetBrush(b); dc.DrawRoundedRectangle(activeRect, 10.0); } wxCoord ModList::OnMeasureItem(size_t WXUNUSED(n)) const { return 80; } void ModList::OnSelectionChange(wxCommandEvent &event) { wxLogDebug(_T("Selection changed to %d (%s)."), event.GetInt(), this->tableData->Item(event.GetInt()).shortname.c_str()); this->Refresh(); } void ModList::OnActivateMod(wxCommandEvent &WXUNUSED(event)) { int selected = this->GetSelection(); wxCHECK_RET(selected != wxNOT_FOUND, _T("Do not have a valid selection.")); ModList::activeMod = &this->tableData->Item(selected); wxString modline; const wxString& shortname(this->tableData->Item(selected).shortname); this->prependmods = this->tableData->Item(selected).primarylist; this->appendmods = this->tableData->Item(selected).secondarylist; if ( !this->prependmods.IsEmpty() ) { wxStringTokenizer prependtokens(this->prependmods, _T(","), wxTOKEN_STRTOK); // no empty tokens while ( prependtokens.HasMoreTokens() ) { if ( !modline.IsEmpty() ) { modline += _T(","); } modline += prependtokens.GetNextToken().Trim(true).Trim(false); } } wxCHECK_RET( !shortname.IsEmpty(), _T("Mod shortname is empty!")); if ( !modline.IsEmpty() ) { modline += _T(","); } if ( selected != 0 ) { // put current mods name into the list unless it is (No mod) modline += shortname; } if ( !this->appendmods.IsEmpty() ) { wxStringTokenizer appendtokens(this->appendmods, _T(","), wxTOKEN_STRTOK); while ( appendtokens.HasMoreTokens() ) { if ( !modline.IsEmpty() ) { modline += _T(","); } modline += appendtokens.GetNextToken().Trim(true).Trim(false); } } wxLogDebug(_T("New modline is %s"), modline.c_str()); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_TC_CURRENT_MODLINE, modline); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_TC_CURRENT_MOD, shortname); TCManager::GenerateTCActiveModChanged(); this->Refresh(); } void ModList::OnInfoMod(wxCommandEvent &WXUNUSED(event)) { int selected = this->GetSelection(); wxCHECK_RET(selected != wxNOT_FOUND, _T("Do not have a valid selection.")); new ModInfoDialog(new ModItem(this->tableData->Item(selected)), this); } void ModList::OnTCSkinChanged(wxCommandEvent &WXUNUSED(event)) { Refresh(); } // comparison is case-insensitive, and mod names containing spaces are preserved bool ModList::isADependency(const wxString &mod, const wxString& modlist) { wxCHECK_MSG(!mod.IsEmpty(), false, _T("isADependency() called with empty mod!")); wxCHECK_MSG(!modlist.IsEmpty(), false, _T("isADependency() called with empty modlist!")); wxString normalizedModName(mod); normalizedModName.Trim(true).Trim(false).MakeLower(); wxStringTokenizer tokens(modlist, _T(","), wxTOKEN_STRTOK); while ( tokens.HasMoreTokens() ) { if ( tokens.GetNextToken().Trim(true).Trim(false).Lower() == normalizedModName ) { return true; } } return false; } bool ModList::isAnAppendMod(const wxString &mod) const { if ( this->appendmods.IsEmpty() ) return false; // no append mods, mod cannot be one return ModList::isADependency(mod, this->appendmods); } bool ModList::isAPrependMod(const wxString &mod) const { if ( this->prependmods.IsEmpty() ) return false; // no prepend mods, mod cannot be one return ModList::isADependency(mod, this->prependmods); } bool ModList::isCurrentSelectionAnAppendMod(const wxString &mod) const { int selection = this->GetSelection(); if ( selection == wxNOT_FOUND || this->tableData->Item(selection).secondarylist.IsEmpty()) { return false; } return ModList::isADependency(mod, this->tableData->Item(selection).secondarylist); } bool ModList::isCurrentSelectionAPrependMod(const wxString &mod) const { int selection = this->GetSelection(); if ( selection == wxNOT_FOUND || this->tableData->Item(selection).primarylist.IsEmpty()) { return false; } return ModList::isADependency(mod, this->tableData->Item(selection).primarylist); } BEGIN_EVENT_TABLE(ModList, wxVListBox) EVT_LISTBOX(ID_MODLISTBOX, ModList::OnSelectionChange) EVT_BUTTON(ID_MODLISTBOX_ACTIVATE_BUTTON, ModList::OnActivateMod) EVT_BUTTON(ID_MODLISTBOX_INFO_BUTTON, ModList::OnInfoMod) END_EVENT_TABLE() /////////////////////////////////////////////////////////////////////////////// // Flagsets /** \class FlagSetItem Structure used to store the name, notes, and the flagset itself. Pointer's that are not NULL when structure is destroyed will be deleted by the structure. */ /** Constructor. Only makes sure that the members are nulled. Does nothing else.*/ FlagSetItem::FlagSetItem() { } /** Destructor. Deletes any non NULL pointers that are contained in the structure. */ FlagSetItem::~FlagSetItem() { } #include WX_DEFINE_OBJARRAY(FlagSets); #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now /////////////////////////////////////////////////////////////////////////////// // I18nData /** \class I18nItem Structure to store the translation of the supported translatable strings (modname and infotext). Structure will delete the non-null pointers it contains at the structures time of destruction. */ /** Constructor. Only NULL's the member variables. */ I18nItem::I18nItem() { this->modname = NULL; this->infotext = NULL; } /** Destructor. Deletes any non-null member variables. */ I18nItem::~I18nItem() { if ( this->modname ) { delete this->modname; } if ( this->infotext ) { delete this->infotext; } } /** The supported langauges. */ const wxString __SupportedLanguages[] = { _T("French"), _T("Romanian"), _T("German"), _T("Polish"), }; /** Languages that are supported by the launcher and thus will search for translations for in mod.ini's. */ wxSortedArrayString SupportedLanguages = wxArrayString(sizeof(__SupportedLanguages)/sizeof(wxString), __SupportedLanguages); #endif //#include //WX_DEFINE_OBJARRAY(I18nData); /////////////////////////////////////////////////////////////////////////////// // ModGridTable::ModItem /** \struct ModGridTable::ModItem Structure that holds all of the information for a single line in the mod table. */ /** Constructor.*/ ModItem::ModItem() { warn = false; this->flagsets = NULL; #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now this->i18n = NULL; #endif this->infoTextPanel = new InfoText(this); this->modImagePanel = new ModImage(this); this->modNamePanel = new ModName(this); } /** Destructor. Deletes all memory pointed to by non NULL internal pointers. */ ModItem::~ModItem() { if (this->flagsets != NULL) delete this->flagsets; #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now if (this->i18n != NULL) { I18nData::iterator i18niter = this->i18n->begin(); while (i18niter != this->i18n->end()) { delete i18niter->second; i18niter++; } delete this->i18n; } #endif if (this->infoTextPanel != NULL) delete this->infoTextPanel; if (this->modImagePanel != NULL) delete this->modImagePanel; if (this->modNamePanel != NULL) delete this->modNamePanel; } void ModItem::Draw(wxDC &dc, const wxRect &rect, bool selected, wxSizer* mainSizer, wxSizer* buttons, wxStaticBitmap* warn) { wxRect titlerect = rect; titlerect.width = 150; wxRect imgrect = rect; imgrect.width = SkinSystem::ModListImageWidth; imgrect.x = titlerect.width; wxRect infotextrect = rect; // The 5 is to keep the text away from the image. infotextrect.x = titlerect.width + imgrect.width + 5; infotextrect.width = rect.width - infotextrect.x; wxFont titlefont = SkinSystem::GetSkinSystem()->GetFont(); titlefont.SetPointSize(titlefont.GetPointSize() + 2); titlefont.SetWeight(wxFONTWEIGHT_BOLD); dc.SetFont(titlefont); this->modNamePanel->Draw(dc, titlerect); dc.SetFont(SkinSystem::GetSkinSystem()->GetFont()); this->modImagePanel->Draw(dc, imgrect); if ( selected ) { /* If I am selected do not have info panel draw because I am going to put the buttons over the info text. */ buttons->Show(true); if ( this->warn) { warn->Show(true); } else if ( !this->warn) { warn->Show(false); } mainSizer->SetDimension(infotextrect.x, infotextrect.y, infotextrect.width, infotextrect.height); } else { this->infoTextPanel->Draw(dc, infotextrect); } } #include WX_DEFINE_OBJARRAY(ModItemArray); /////////////////////////////////////////// /** \class ModItem::InfoText Extends wxPanel so that it can draw the info text to the correct size in the list */ /** Constructor. Sets up stuff. */ ModItem::InfoText::InfoText(ModItem *myData) { this->myData = myData; } void ModItem::InfoText::Draw(wxDC &dc, const wxRect &rect) { if ( !this->myData->infotext.IsEmpty() ) { // to keep "\n" from appearing in the mod list info text wxString escapedInfoText(this->myData->infotext); escapedInfoText.Replace(_T("\\n"), _T(" ")); wxStringTokenizer tokens(escapedInfoText); ArrayOfWords words; words.Alloc(tokens.CountTokens()); FillArrayOfWordsFromTokens(tokens, dc, NULL, words); const int maxwidth = rect.width; int currentx = rect.x, currenty = rect.y; wxSize spaceSize = dc.GetTextExtent(_T(" ")); int currentwidth = 0; wxString string; for( size_t i = 0; i < words.Count(); i++) { if ( currentwidth + words[i].size.x + spaceSize.x > maxwidth ) { dc.DrawText(string, currentx, currenty); string.Empty(); currentwidth = 0; currenty += words[i].size.y; if (currenty + words[i].size.y> rect.y + rect.height) { break; } } else { if (!(string.IsEmpty())) { // prevent leading space in info text string.append(_T(" ")); } } string.append(words[i].word); currentwidth += words[i].size.x + spaceSize.x; } if ( !string.IsEmpty()) { dc.DrawText(string, currentx, currenty); } } } /////////////////////////////////////////// /** \class ModItem::ModName Extends wxPanel so that it can draw the mod's name to the correct size in the list or the mod's short name. */ /** Constructor. Sets up stuff. */ ModItem::ModName::ModName(ModItem *myData) { this->myData = myData; } void ModItem::ModName::Draw(wxDC &dc, const wxRect &rect) { wxString name; if ( !this->myData->name.IsEmpty() ) { name = this->myData->name; } else { name = this->myData->shortname; } wxCoord width, height; wxFont testFont(dc.GetFont()); /* font to use to compensate for GetTextExtent's inabliity to handle bold font.*/ testFont.SetPointSize(testFont.GetPointSize() + 2); dc.GetMultiLineTextExtent(name, &width, &height, NULL, &testFont); if ( width > rect.width ) { // too wide need to wrap if possible. ArrayOfWords titleWords; wxStringTokenizer tokens(name); ArrayOfWords words; words.Alloc(tokens.CountTokens()); FillArrayOfWordsFromTokens(tokens, dc, &testFont, words); const int maxwidth = rect.width; int currentx = rect.x, currenty = rect.y; wxCoord spaceX, spaceY; dc.GetTextExtent(_T(" "), &spaceX, &spaceY, NULL, NULL, &testFont); int currentwidth = 0; wxString string; for( size_t i = 0; i < words.Count(); i++) { if ( currentwidth + words[i].size.x + spaceX > maxwidth ) { wxCoord tempX, tempY; dc.GetTextExtent(string, &tempX, &tempY, NULL, NULL, &testFont); Words* temp = new Words(); temp->size.SetWidth(tempX); temp->size.SetHeight(tempY); temp->word = string; titleWords.Add(temp); string.Empty(); currentwidth = 0; currenty += words[i].size.y; if (currenty + words[i].size.y > rect.y + rect.height) { break; } } else if ( string.size() > 0 ) { string.append(_T(" ")); } string.append(words[i].word); currentwidth += words[i].size.x + spaceX; } wxCoord tempX, tempY; dc.GetTextExtent(string, &tempX, &tempY, NULL, NULL, &testFont); Words* temp = new Words(); temp->size.SetWidth(tempX); temp->size.SetHeight(tempY); temp->word = string; titleWords.Add(temp); // draw the words properly centered // Find the hight of all of the lines of text int totalHeight = 0; for( size_t i = 0; i < titleWords.Count(); i++ ) { totalHeight += titleWords[i].size.y; } int currentHeightOffset = 0; for( size_t i = 0; i < titleWords.Count(); i++ ) { dc.DrawText(titleWords[i].word, rect.x + rect.width/2 - titleWords[i].size.x/2, rect.y + rect.height/2 - titleWords[i].size.y/2 + currentHeightOffset - totalHeight/2); currentHeightOffset += titleWords[i].size.y; } } else { dc.DrawText(name, rect.x + rect.width/2 - width/2, rect.y + rect.height/2 - height/2); } } /////////////////////////////////////////// /** \class ModItem::ModImage Draws the Mod's image on the list or degrades smoothly to drawing the text "NO IMAGE". */ /** Constructor. Sets up stuff. */ ModItem::ModImage::ModImage(ModItem *myData) { this->myData = myData; } void ModItem::ModImage::Draw(wxDC &dc, const wxRect &rect) { if ( this->myData->image182x80.IsOk() ) { dc.DrawBitmap(this->myData->image182x80, rect.x, rect.y); } else if ( this->myData->shortname != NO_MOD ) { dc.DrawBitmap(SkinSystem::GetSkinSystem()->GetSmallModImage(), rect.x, rect.y); } else { dc.DrawRectangle(rect); wxPen pen(dc.GetPen()); pen.SetWidth(1); dc.SetPen(pen); wxString noimg = _("NO IMAGE"); wxSize size = dc.GetTextExtent(noimg); dc.DrawText(noimg, rect.x + rect.width/2 - size.x/2, rect.y + rect.height/2 - size.y/2); } } ModInfoDialog::ModInfoDialog(ModItem* item, wxWindow* parent) { wxASSERT(item != NULL); this->item = item; wxASSERT(!item->name.IsEmpty() || !item->shortname.IsEmpty()); wxString modName = wxString::Format(_T("%s"), item->name.IsEmpty() ? item->shortname.c_str(): item->name.c_str()); wxDialog::Create(parent, wxID_ANY, modName, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED | wxBORDER_DOUBLE ); this->SetBackgroundColour(wxColour(_T("WHITE"))); wxStaticText* titleBox = new wxStaticText(this, wxID_ANY, modName, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); wxFont titleFont = titleBox->GetFont(); titleFont.SetWeight(wxFONTWEIGHT_BOLD); titleFont.SetPointSize(14); titleBox->SetFont(titleFont); wxString tcPath; ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath, wxEmptyString); wxString modFolderString = wxString::Format(_T("%s%s"), tcPath.c_str(), (item->shortname == NO_MOD) ? wxEmptyString : (wxString(wxFileName::GetPathSeparator()) + item->shortname).c_str()); wxStaticText* modFolderBox = new wxStaticText(this, wxID_ANY, modFolderString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); wxPanel* modImage = new ModInfoDialog::ImageDrawer(this); modImage->SetMaxSize(wxSize(SkinSystem::ModInfoDialogImageWidth, SkinSystem::ModInfoDialogImageHeight)); wxHtmlWindow* info = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN); info->SetMinSize(wxSize(SkinSystem::ModInfoDialogImageWidth, 250)); if ( item->infotext.IsEmpty() ) { info->SetPage(DEFAULT_MOD_LAUNCHER_INFO_TEXT); } else { wxString infoText(item->infotext); infoText.Replace(_T("\\n"), _T("
")); info->SetPage(infoText); } wxHtmlWindow* links = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN | wxHW_SCROLLBAR_NEVER ); links->SetSize(SkinSystem::ModInfoDialogImageWidth, 40); links->SetPage(wxString::Format(_T("
%s%s%s%s
"), (!item->website.IsEmpty()) ? wxString::Format(_T("%s :: "), item->website.c_str(), _("Website")).c_str():wxEmptyString, wxString::Format(_T("%s"), (!item->forum.IsEmpty()) ? item->forum.c_str():_("http://www.hard-light.net/forums/index.php?board=124.0"), _("Forum")).c_str(), (!item->bugs.IsEmpty()) ? wxString::Format(_T(" :: %s"), item->bugs.c_str(), _("Bugs")).c_str() : wxEmptyString, (!item->support.IsEmpty()) ? wxString::Format(_T(" :: %s"), item->support.c_str(), _("Support")).c_str() : wxEmptyString )); links->Connect(wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler(ModInfoDialog::OnLinkClicked)); wxStaticBitmap* warning = NULL; wxHtmlWindow* notesText = NULL; if ( !item->notes.IsEmpty() ) { if ( item->warn ) { warning = new wxStaticBitmap(this, wxID_ANY, SkinSystem::GetSkinSystem()->GetBigWarningIcon()); } notesText = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN); notesText->SetPage(item->notes); notesText->SetMinSize(wxSize(200, 64)); } wxButton* close = new wxButton(this, wxID_ANY, _("Close")); this->SetEscapeId(close->GetId()); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(titleBox, wxSizerFlags().Centre()); sizer->Add(modFolderBox, wxSizerFlags().Centre()); sizer->Add(modImage, wxSizerFlags().Centre()); sizer->Add(info, wxSizerFlags().Centre()); sizer->Add(links, wxSizerFlags().Centre()); if ( notesText != NULL ) { if ( warning != NULL ) { wxSizer* linkSizer = new wxBoxSizer(wxHORIZONTAL); linkSizer->Add(warning); linkSizer->Add(notesText); sizer->Add(linkSizer, wxSizerFlags().Expand().Centre()); } else { sizer->Add(notesText, wxSizerFlags().Expand().Centre()); } } sizer->Add(close, wxSizerFlags().Centre()); this->SetSizerAndFit(sizer); this->Layout(); this->CentreOnParent(); this->ShowModal(); } void ModInfoDialog::OnLinkClicked(wxHtmlLinkEvent &event) { wxHtmlLinkInfo info = event.GetLinkInfo(); wxString rest; wxLaunchDefaultBrowser(info.GetHref()); } ModInfoDialog::ImageDrawer::ImageDrawer(ModInfoDialog* parent): wxPanel(parent) { this->parent = parent; if (!parent->item->image255x112.IsOk()) { this->SetSize(SkinSystem::ModInfoDialogImageWidth, SkinSystem::ModInfoDialogImageHeight); } else { this->SetSize( parent->item->image255x112.GetWidth(), parent->item->image255x112.GetHeight()); } this->GetEventHandler()->Connect(wxEVT_PAINT, wxPaintEventHandler(ModInfoDialog::ImageDrawer::OnDraw)); } void ModInfoDialog::ImageDrawer::OnDraw(wxPaintEvent &WXUNUSED(event)) { wxPaintDC dc(this); if ( parent->item->image255x112.IsOk() ) { dc.DrawBitmap(parent->item->image255x112, 0, 0); } else if ( parent->item->shortname != NO_MOD ) { dc.DrawBitmap(SkinSystem::GetSkinSystem()->GetModImage(), 0, 0); } else { wxCoord textWidth, textHeight; dc.GetTextExtent(_("NO IMAGE"), &textWidth, &textHeight); wxCoord drawLocationX = SkinSystem::ModInfoDialogImageWidth/2 - textWidth/2; wxCoord drawLocationY = SkinSystem::ModInfoDialogImageHeight/2 - textHeight/2; dc.DrawText(_("NO IMAGE"), drawLocationX, drawLocationY); } } BEGIN_EVENT_TABLE(ModInfoDialog::ImageDrawer, wxPanel) END_EVENT_TABLE() wxlauncher-0.9.4/code/controls/ModList.h0000644000000000000000000001243612155177255020163 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MODLIST_H #define MODLIST_H #include #include #include #include #include "apis/SkinManager.h" #include "controls/LightingPresets.h" class ConfigPair { public: ConfigPair(const wxString &shortname, wxFileConfig* config); ~ConfigPair(); wxString shortname; wxFileConfig* config; }; WX_DECLARE_OBJARRAY(ConfigPair, ConfigArray); class FlagSetItem { public: FlagSetItem(); ~FlagSetItem(); wxString name; wxString flagset; wxString notes; }; WX_DECLARE_OBJARRAY(FlagSetItem, FlagSets); #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now extern wxSortedArrayString SupportedLanguages; class I18nItem { public: I18nItem(); ~I18nItem(); wxString* modname; wxString* infotext; }; WX_DECLARE_STRING_HASH_MAP(I18nItem*, I18nData); #endif class ModItem{ public: ModItem(); ~ModItem(); wxString name; wxString shortname; wxBitmap image255x112; wxBitmap image182x80; wxString infotext; wxString author; wxString notes; bool warn; wxString website; wxString forum; wxString bugs; wxString support; long minhorizontalres; long minverticalres; wxString forcedon; wxString forcedoff; wxString primarylist; wxString secondarylist; wxString recommendedlightingname; wxString recommendedlightingflagset; FlagSets* flagsets; // set 0 is the ideal set. #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now I18nData* i18n; #endif void Draw(wxDC &dc, const wxRect &rect, bool selected, wxSizer *mainSizer, wxSizer *buttons, wxStaticBitmap* warn); private: class InfoText{ public: InfoText(ModItem *myData); void Draw(wxDC &dc, const wxRect &rect); private: ModItem *myData; }; InfoText *infoTextPanel; class ModImage{ public: ModImage(ModItem *myData); void Draw(wxDC &dc, const wxRect &rect); private: ModItem *myData; }; ModImage *modImagePanel; class ModName{ public: ModName(ModItem *myData); void Draw(wxDC &dc, const wxRect &rect); private: ModItem *myData; }; ModName* modNamePanel; }; WX_DECLARE_OBJARRAY(ModItem, ModItemArray); class ModList: public wxVListBox { public: ModList(wxWindow *parent, wxSize& size, wxString tcPath); ~ModList(); // overrides for wxVListBox virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const; virtual void OnDrawSeparator(wxDC &dc, wxRect& rect, size_t n) const; virtual void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const; virtual wxCoord OnMeasureItem(size_t n) const; void OnSelectionChange(wxCommandEvent &event); void OnActivateMod(wxCommandEvent &event); void OnInfoMod(wxCommandEvent &event); void OnTCSkinChanged(wxCommandEvent &event); static const ModItem* GetActiveMod() { return ModList::activeMod; } private: /** A hash map of the wxFileConfigs that represent the mod.ini files for each mod. The key is the the mod's folder name which is used as the mod's internal name. */ ConfigArray* configFiles; ModItemArray* tableData; Skin* TCSkin; wxButton *infoButton, *activateButton; wxStaticBitmap *warnBitmap; wxBoxSizer *buttonSizer, *sizer; static const ModItem* activeMod; /** Sets a bitmap of the ModList's TCSkin. */ void SetSkinBitmap( const wxFileConfig& config, const wxString& modIniKey, const wxString& tcPath, const wxString& bitmapName, bool (Skin::* setFnPtr)(const wxBitmap&)); void readIniFileString(const wxFileConfig* config, const wxString& key, wxString& location); void readFlagSet(const wxFileConfig* config, const wxString& keyprefix, FlagSetItem& set); #ifdef MOD_TEXT_LOCALIZATION // mod text localization is not supported for now void readTranslation(wxFileConfig* config, wxString langaugename, I18nItem ** trans); #endif wxString escapeSpecials(const wxString& toEscape); bool ParseModIni(const wxString& modIniPath, const wxString& tcPath, bool isNoMod = false); void SetSelectedMod(); static wxString GetShortName(const wxString& modIniPath, const wxString& tcPath); /** The active mod's prepend mods and append mods. */ wxString prependmods, appendmods; /** Tests whether a mod is a prepend/append mod of the active mod. */ bool isAPrependMod(const wxString& mod) const; bool isAnAppendMod(const wxString& mod) const; /** Tests whether a mod appears in the specified mod list. */ static bool isADependency(const wxString& mod, const wxString& modlist); /** Tests whether a mod is a prepend/append mod of the selected mod. */ bool isCurrentSelectionAPrependMod(const wxString &mod) const; bool isCurrentSelectionAnAppendMod(const wxString &mod) const; DECLARE_EVENT_TABLE(); }; #endifwxlauncher-0.9.4/code/controls/StatusBar.cpp0000644000000000000000000001174512155177255021055 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "apis/SkinManager.h" #include "controls/StatusBar.h" #include "global/ids.h" #include "controls/Logger.h" #include "generated/configure_launcher.h" #include "global/MemoryDebugging.h" // Last include for memory debugging enum FieldIDs { SB_FIELD_ICON = 0, SB_FIELD_MAINTEXT, SB_FIELD_PROGRESS_BAR, SB_FIELD_PROGRESS_TEXT, SB_FIELD_MAX, }; #if IS_APPLE const int ICON_FIELD_WIDTH = 18; #else const int ICON_FIELD_WIDTH = 25; #endif BEGIN_EVENT_TABLE(StatusBar, wxStatusBar) EVT_SIZE(StatusBar::OnSize) EVT_COMMAND(wxID_NONE, EVT_TC_SKIN_CHANGED, StatusBar::OnTCSkinChanged) END_EVENT_TABLE() StatusBar::StatusBar(wxWindow *parent) :wxStatusBar(parent) { this->parent = parent; this->showingToolTip = false; SkinSystem::RegisterTCSkinChanged(this); wxCommandEvent nullEvent; OnTCSkinChanged(nullEvent); // Just creating these now, will place them in the OnSize event handler new wxStaticBitmap(this, ID_STATUSBAR_STATUS_ICON, this->icons[ID_SB_OK]); #if 0 // this progress bar doesn't need to be here new wxGauge(this, ID_STATUSBAR_PROGRESS_BAR, 100); #endif // eliminate (by zeroing the width) the fields we're currently not using // FIXME and when we start using those fields, add the width back! int widths[] = { ICON_FIELD_WIDTH, -1, 0, 0 }; wxASSERT_MSG( sizeof(widths)/sizeof(int) == SB_FIELD_MAX, wxString::Format( _T("Number of fields (%d) and number of widths (%d) do not match"), SB_FIELD_MAX, sizeof(widths)/sizeof(int))); this->SetFieldsCount(SB_FIELD_MAX, widths); this->SetStatusText(_T("Status bar created"), SB_FIELD_MAINTEXT); dynamic_cast(wxLog::GetActiveTarget())->SetStatusBarTarget(this); } StatusBar::~StatusBar() { dynamic_cast(wxLog::GetActiveTarget())->SetStatusBarTarget(NULL); } void StatusBar::OnSize(wxSizeEvent& WXUNUSED(event)) { wxWindow* icon = dynamic_cast(wxWindow::FindWindowById(ID_STATUSBAR_STATUS_ICON, this)); wxCHECK_RET( icon != NULL, _T("Cannot find status bar icon control")); wxRect iconrect; this->GetFieldRect(SB_FIELD_ICON, iconrect); // OS X-only hack to keep the icon from being clipped off #if IS_APPLE iconrect.SetX(-3); iconrect.SetY(-2); #endif icon->SetSize(iconrect); #if 0 // this progress bar doesn't need to be here wxWindow* bar = dynamic_cast(wxWindow::FindWindowById(ID_STATUSBAR_PROGRESS_BAR, this)); wxCHECK_RET( bar != NULL, _T("Cannot find status bar progress bar")); wxRect barrect; this->GetFieldRect(SB_FIELD_PROGRESS_BAR, barrect); bar->SetSize(barrect); #endif } void StatusBar::OnTCSkinChanged(wxCommandEvent &WXUNUSED(event)) { this->icons[ID_SB_OK] = SkinSystem::GetSkinSystem()->GetOkIcon(); this->icons[ID_SB_WARNING] = SkinSystem::GetSkinSystem()->GetWarningIcon(); this->icons[ID_SB_ERROR] = SkinSystem::GetSkinSystem()->GetErrorIcon(); this->icons[ID_SB_INFO] = SkinSystem::GetSkinSystem()->GetInfoIcon(); for( int i = 0; i < ID_SB_MAX_ID; i++) { // Check that all icons are okay. if ( !this->icons[i].IsOk() ) { this->icons[i] = wxNullBitmap; wxLogWarning(_T("Icon ID: %d is not okay"), i); } } } void StatusBar::SetMainStatusText(wxString msg, int icon) { this->SetStatusText(msg, SB_FIELD_MAINTEXT); if ( icon > ID_SB_NO_CHANGE && icon < ID_SB_MAX_ID ) { wxStaticBitmap* iconImage = dynamic_cast(wxWindow::FindWindowById(ID_STATUSBAR_STATUS_ICON, this)); wxCHECK_RET( iconImage != NULL, _T("Cannot find status bar icon image")); iconImage->SetBitmap(this->icons[icon]); iconImage->Refresh(); } else { if ( icon != ID_SB_NO_CHANGE ) { wxLogWarning(_T("SetMainStatusText was passed (%d) as an icon, which is out of range"), icon); } } } /** Causes the status bar to show the msg until EndToolTipStatusText() is called. When EndToolTipStatusText() is called the status text will be returned to the original text. */ void StatusBar::StartToolTipStatusText(wxString msg) { if (!this->showingToolTip) { this->PushStatusText(msg, SB_FIELD_MAINTEXT); this->showingToolTip = true; } } /** Returns the statusbar text to the orginal message after a mouse over. See StartToolTipStatusText() for more information. */ void StatusBar::EndToolTipStatusText() { if (this->showingToolTip) { this->PopStatusText(SB_FIELD_MAINTEXT); this->showingToolTip = false; } } wxlauncher-0.9.4/code/controls/StatusBar.h0000644000000000000000000000300312155177255020506 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STATUSLOG_H #define STATUSLOG_H #include #include "global/ids.h" /** Extends wxStatusBar. Provides the a 4 section status bar: icon, main text, progress bar, and small text for the progress bar. This class will interface this Logger to enable code to eaisly send messages to the statusbar. */ class StatusBar: public wxStatusBar { public: StatusBar(wxWindow* parent); virtual ~StatusBar(); void OnSize(wxSizeEvent& event); void OnTCSkinChanged(wxCommandEvent& event); void SetMainStatusText(wxString msg, int icon=ID_SB_NO_CHANGE); void SetJobStatusText(int value, wxString msg=_T("")); void StartToolTipStatusText(wxString msg); void EndToolTipStatusText(); private: wxWindow* parent; wxBitmap icons[ID_SB_MAX_ID]; bool showingToolTip; DECLARE_EVENT_TABLE(); }; #endifwxlauncher-0.9.4/code/controls/TruncatableChoice.cpp0000644000000000000000000000307712155177255022523 0ustar rootroot00000000000000/* Copyright (C) 2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "controls/TruncatableChoice.h" TruncatableChoice::TruncatableChoice(wxWindow *parent, wxWindowID id) : wxChoice(parent, id), maxLength(0) { } void TruncatableChoice::SetMaxLength(const int maxLength) { wxCHECK_RET(maxLength >= 0, wxString::Format(_T("Invalid value %d for maxLength."), maxLength)); this->maxLength = maxLength; if ((maxLength == 0) || (this->maxLength > GetEffectiveMinSize().GetWidth())) { this->SetMinSize(this->GetEffectiveMinSize()); } else { this->SetMinSize(wxSize(maxLength, -1)); } } wxSize TruncatableChoice::DoGetBestSize() const { wxASSERT(this->maxLength >= 0); wxSize bestChoiceSize(wxChoice::DoGetBestSize()); if ((this->maxLength == 0) || (this->maxLength > bestChoiceSize.GetWidth())) { return bestChoiceSize; } else { return wxSize(wxSize(maxLength, bestChoiceSize.GetHeight())); } } wxlauncher-0.9.4/code/controls/TruncatableChoice.h0000644000000000000000000000222312155177255022160 0ustar rootroot00000000000000/* Copyright (C) 2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TRUNCATABLECHOICE_H #define TRUNCATABLECHOICE_H #include /** A form of wxChoice that allows for limiting its length. */ class TruncatableChoice: public wxChoice { public: TruncatableChoice(wxWindow *parent, wxWindowID id); virtual wxSize DoGetBestSize() const; int GetMaxLength() const { return this->maxLength; } void SetMaxLength(int maxLength); private: TruncatableChoice(); int maxLength; }; #endif wxlauncher-0.9.4/code/datastructures/FSOExecutable.cpp0000644000000000000000000003026512155177255023006 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "datastructures/FSOExecutable.h" #include #include #include "global/MemoryDebugging.h" enum BuildCapabilities_enum { SUPPORT_OPENAL = 1<<0, NOT_SUPPORT_DIRECT3D = 1<<1, } BuildCapabilities; FSOExecutable::FSOExecutable() { major = 0; minor = 0; revision = 0; inferno = false; sse = 0; debug = false; build = 0; antipodes = false; antNumber = 0; buildCaps = 0; } FSOExecutable::~FSOExecutable() { } bool FSOExecutable::SupportsDirect3D() { return (buildCaps & NOT_SUPPORT_DIRECT3D) == 0; } bool FSOExecutable::SupportsOpenAL() { return (buildCaps & SUPPORT_OPENAL) > 0; } bool FSOExecutable::IsRootFolderValid(const wxFileName& path, bool quiet) { if ( !path.IsOk() ) { if (!quiet) { wxLogError(_T(" New root folder %s is not OK"), path.GetFullPath().c_str()); } return false; } if ( path.GetPath().IsEmpty() ) { if (!quiet) { wxLogError(_T(" Root folder %s is empty"), path.GetFullPath().c_str()); } return false; } return HasFSOExecutables(path); } bool FSOExecutable::HasFSOExecutables(const wxFileName& path) { wxCHECK_MSG(path.IsOk(), false, wxString::Format(_T("provided path %s to HasFSOExecutables is invalid"), path.GetFullPath().c_str())); return !FSOExecutable::GetBinariesFromRootFolder(path, true).IsEmpty(); } #if IS_LINUX bool IsFileToIgnore(const wxString& filename) { return filename.EndsWith(_T(".exe")) || filename.EndsWith(_T(".map")) || filename.EndsWith(_T(".pdb")) || filename.EndsWith(_T(".app")) || filename.EndsWith(_T(".ini")) || filename.EndsWith(_T(".tar")) || filename.EndsWith(_T(".gz")) || filename.EndsWith(_T(".bz2")) || filename.EndsWith(_T(".tgz")) || filename.EndsWith(_T(".tbz")) || filename.EndsWith(_T(".tbz2")); } #endif #if IS_WIN32 #define EXECUTABLE_GLOB_PATTERN _T("fs2_*.exe") #define FRED_EXECUTABLE_GLOB_PATTERN _T("fred2_*.exe") #elif IS_LINUX #define EXECUTABLE_GLOB_PATTERN _T("fs2_*") #define FRED_EXECUTABLE_GLOB_PATTERN _T("fred2_*") #elif IS_APPLE #define EXECUTABLE_GLOB_PATTERN _T("fs2_*.app") #define FRED_EXECUTABLE_GLOB_PATTERN _T("fred2_*.app") #else #error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif // quiet is for when you just want to check whether there are FSO/FRED binaries wxArrayString FSOExecutable::GetBinariesFromRootFolder(const wxFileName& path, bool quiet) { return FSOExecutable::GetBinariesFromRootFolder(path, EXECUTABLE_GLOB_PATTERN, quiet); } wxArrayString FSOExecutable::GetFredBinariesFromRootFolder(const wxFileName& path, bool quiet) { return FSOExecutable::GetBinariesFromRootFolder(path, FRED_EXECUTABLE_GLOB_PATTERN, quiet); } wxArrayString FSOExecutable::GetBinariesFromRootFolder(const wxFileName& path, const wxString& globPattern, bool quiet) { wxArrayString files; wxDir folder(path.GetPath()); wxString filename; #if IS_APPLE // Binaries are directories on OSX. bool cont = folder.GetFirst(&filename, globPattern, wxDIR_DIRS); #else bool cont = folder.GetFirst(&filename, globPattern, wxDIR_FILES); #endif while (cont) { #if IS_LINUX if ( !IsFileToIgnore(filename) ) { #endif files.Add(filename); #if IS_LINUX } #endif cont = folder.GetNext(&filename); } // filter out launcher binaries (at least on OSX) for (int i = files.GetCount() - 1; i >= 0; --i) { if (files[i].Lower().Find(_T("launcher")) != wxNOT_FOUND) { files.RemoveAt(i); } } #if IS_APPLE // find actual (Unix) executable inside .app bundle and call the path to it the "executable" for (wxArrayString::iterator it = files.begin(), end = files.end(); it != end; ++it) { wxString pathToBin = wxDir::FindFirst(path.GetPath(wxPATH_GET_SEPARATOR) + *it + _T("/Contents/MacOS"), _T("*"), wxDIR_FILES); pathToBin.Replace(path.GetPath(wxPATH_GET_SEPARATOR), _T("")); *it = pathToBin; } #endif if (!quiet) { wxString execType = globPattern.Lower().Find(_T("fred")) == wxNOT_FOUND ? _T("FS2") : _T("FRED2"); wxLogInfo(_T(" Found %d %s Open executables in '%s'"), files.GetCount(), execType.c_str(), path.GetPath().c_str()); for (size_t i = 0, n = files.GetCount(); i < n; ++i) { wxLogDebug(_T("Found executable: %s"), files.Item(i).c_str()); } } return files; } FSOExecutable FSOExecutable::GetBinaryVersion(wxString binaryname) { wxLogDebug(_T("Making version struct for the executable '%s'"), binaryname.c_str()); FSOExecutable ver; wxStringTokenizer tok(binaryname, _T("_.- ()[]/")); ver.executablename = binaryname; if ( !tok.HasMoreTokens() ) { wxLogError( _T("Did not find initial 'fs2' or 'fred2' token in executable '%s'"), binaryname.c_str()); return ver; } wxString first(tok.GetNextToken()); if ( tok.HasMoreTokens() && (!first.CmpNoCase(_T("fred2")) || !first.CmpNoCase(_T("fs2"))) ) { wxString second(tok.GetNextToken()); if ( !second.CmpNoCase(_T("open")) ) { if ( !first.CmpNoCase(_T("fs2")) ) { ver.binaryname = _T("FS2 Open"); } else { ver.binaryname = _T("FRED2 Open"); } } else { wxLogWarning(_T("was expecting 'open'; got %s in executable %s"), second.c_str(), binaryname.c_str()); return ver; } } else { wxLogWarning(_T("executable name '%s' too short"), binaryname.c_str()); return ver; } while ( tok.HasMoreTokens() ) { wxString token = tok.GetNextToken(); wxString temp; long tempVersion; if (token.IsEmpty()) { // can happen in OS X nightly debug builds // do nothing } else if ( !token.CmpNoCase(_T("exe")) ) { ; // do nothing #if IS_APPLE } else if ( !token.CmpNoCase(_T("app")) ) { break; // we've reached the end of the app name #endif } else if ( token.ToLong(&tempVersion) && token.size() == 8 ) { // must be a date from a nightly build; just ignore it } else if ( token.ToLong(&tempVersion) && ver.antipodes && ver.antNumber == 0) { // must be antipodes number if ( tempVersion > 0 ) { ver.antNumber = (int)tempVersion; } else { wxLogWarning( _T("antipodes number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else if ( token.ToLong(&tempVersion) && ver.major == 0 ) { // must be major version number if ( tempVersion < 1000 && tempVersion > 0 ) { ver.major = (int)tempVersion; } else { wxLogWarning( _T("major version number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else if ( token.ToLong(&tempVersion) && ver.minor == 0 ) { // must be minor version number if ( tempVersion < 1000 && tempVersion >= 0 ) { ver.minor = (int)tempVersion; } else { wxLogWarning( _T("minor version number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else if ( token.ToLong(&tempVersion) && ver.revision == 0) { // must be revision version number if ( tempVersion < 1000 && tempVersion >= 0 ) { ver.revision = (int)tempVersion; } else { wxLogWarning( _T("Revision version number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else if ( !token.CmpNoCase(_T("d")) || !token.CmpNoCase(_T("debug")) ) { ver.debug = true; } else if ( token.Lower().EndsWith(_T("d"), &temp) ) { if ( temp.ToLong(&tempVersion) ) { // is the revision version number if ( tempVersion < 1000 && tempVersion >= 0 ) { ver.revision = (int)tempVersion; ver.debug = true; } else { wxLogWarning( _T("Revision version number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else { wxLogWarning( _T("Token ending in 'd' is not a number (%s) in executable %s"), token.c_str(), binaryname.c_str()); } } else if ( token.Lower().EndsWith(_T("r"), &temp) ) { if (temp.IsEmpty()) { // do nothing, the 'r' wasn't preceded by a number } else if ( temp.ToLong(&tempVersion) ) { // is the revision version number if ( tempVersion < 1000 && tempVersion >= 0 ) { ver.revision = (int)tempVersion; ver.debug = false; } else { wxLogWarning( _T("Revision version number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else { wxLogWarning( _T("Token ending in 'r' is not a number (%s) in executable %s"), token.c_str(), binaryname.c_str()); } } else if ( token.StartsWith(_T("r"), &temp) && temp.ToLong(&tempVersion) ) { // must be a revision number from SirKnightly's builds if ( tempVersion > 0 ) { ver.build = (int)tempVersion; } else { wxLogWarning( _T("SirKnightly build number out of range (%ld) in executable %s"), tempVersion, binaryname.c_str()); } } else if ( !token.CmpNoCase(_T("ant")) ) { ver.antipodes = true; } else if ( token.Lower().StartsWith(_T("ant"), &temp) && !ver.antipodes ) { ver.antipodes = true; // in case the token is of the format, e.g., "Ant8" long antNumber; if (temp.ToLong(&antNumber)) { if (antNumber > 0) { ver.antNumber = antNumber; } else { wxLogWarning(_T("Invalid antipodes number %ld in executable %s"), antNumber, binaryname.c_str()); } } } else if ( !token.CmpNoCase(_T("sse2")) ) { ver.sse = 2; } else if ( !token.CmpNoCase(_T("sse")) ) { if (ver.string.Lower().EndsWith(_T("no"))) { // probably NO SSE ver.sse = -1; const int lastSpaceIndex = ver.string.Find(_T(' '), true); if (lastSpaceIndex == wxNOT_FOUND) { wxASSERT(ver.string.Lower() == _T("no")); ver.string = wxEmptyString; } else { ver.string = ver.string.Mid(0, lastSpaceIndex); } } else { ver.sse = 1; } } else if ( !token.CmpNoCase(_T("inf")) || !token.CmpNoCase(_T("inferno"))) { ver.inferno = true; } else { if (!ver.string.IsEmpty()) { ver.string += _T(" "); } ver.string += token; } } return ver; } /** Returns the version string to display to the user from a previously parsed FSOVersion object. The intention is to display all information that is normally encoded into the executable's file name into a long string that more user friendly. The resulting string looks something like this: \verbatim FS2 Open Antipodes 4 Debug SSE2 FS2 Open 3.6.10 Inferno SSE FRED2 Open 3.6.11 Debug \endverbatim */ wxString FSOExecutable::GetVersionString() const { const bool hasVersion = (this->major != 0) || this->antipodes; const bool useFullVersion = hasVersion && !this->antipodes; // just to improve code readability wxString antipodesStr; if (this->antipodes) { antipodesStr += _T(" Antipodes"); if (this->antNumber != 0) { antipodesStr += wxString::Format(_T(" %d"), antNumber); } } // again, to improve code readability wxString sseStr; switch (this->sse) { case -1: sseStr = _T(" NO SSE"); break; case 1: sseStr = _T(" SSE"); break; case 2: sseStr = _T(" SSE2"); break; default: // nothing break; } return wxString::Format(_T("%s%s%s%s%s%s%s%s"), (this->binaryname.IsEmpty()) ? _T("Unknown") : this->binaryname.c_str(), // FS2 Open (useFullVersion) ? wxString::Format(_T(" %d.%d.%d"), this->major, this->minor, this->revision).c_str() : wxEmptyString, (this->antipodes) ? antipodesStr.c_str() : wxEmptyString, (this->build == 0) ? wxEmptyString : wxString::Format((hasVersion) ? _T(" (Build %d)") : _T(" Build %d"), this->build).c_str(), (this->string.IsEmpty()) ? wxEmptyString : wxString::Format((hasVersion) ? _T(" (%s)") : _T(" %s"), this->string.c_str()).c_str(), (this->debug) ? _T(" Debug") : wxEmptyString, (this->inferno && !this->antipodes) ? _T(" Inferno") : wxEmptyString, (this->sse == 0) ? wxEmptyString : sseStr.c_str() ); } wxlauncher-0.9.4/code/datastructures/FSOExecutable.h0000644000000000000000000000421612155177255022450 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FSOEXECUTABLE_H #define FSOEXECUTABLE_H #include #include #include class FSOExecutable: public wxClientData { public: virtual ~FSOExecutable(); bool SupportsDirect3D(); bool SupportsOpenAL(); inline bool ExecutableNameEqualTo(const wxString& str) const; inline const wxString& GetExecutableName() const; static bool IsRootFolderValid(const wxFileName& path, bool quiet = false); static bool HasFSOExecutables(const wxFileName& path); static wxArrayString GetBinariesFromRootFolder(const wxFileName &path, bool quiet = false); static wxArrayString GetFredBinariesFromRootFolder(const wxFileName &path, bool quiet = false); static FSOExecutable GetBinaryVersion(wxString binaryname); wxString GetVersionString() const; protected: int major; int minor; int revision; bool inferno; int sse; bool debug; int build; bool antipodes; int antNumber; //!< antipodes number (such as 8) wxString string; wxString binaryname; //!< FS2 Open or FRED wxString executablename; //!< the actual name of the binary wxByte buildCaps; private: FSOExecutable(); static wxArrayString GetBinariesFromRootFolder(const wxFileName &path, const wxString &globPattern, bool quiet); }; inline bool FSOExecutable::ExecutableNameEqualTo(const wxString& str) const { return this->executablename == str; } inline const wxString& FSOExecutable::GetExecutableName() const { return this->executablename; } #endifwxlauncher-0.9.4/code/datastructures/FlagFileData.cpp0000644000000000000000000002257212155177255022622 0ustar rootroot00000000000000/* Copyright (C) 2009-2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "datastructures/FlagFileData.h" #include "global/MemoryDebugging.h" int Flag::flagIndexCounter = 0; Flag::Flag() : flagIndex(flagIndexCounter++) { } #include // Magic Incantation WX_DEFINE_LIST(FlagList); #include // Magic Incantation WX_DEFINE_LIST(FlagCategoryList); FlagSet::FlagSet(wxString name) : name(name) { } #include // Magic Incantation WX_DEFINE_LIST(FlagSetsList); ProxyFlagDataItem::ProxyFlagDataItem(const wxString& flagString, int flagIndex) : flagString(flagString), flagIndex(flagIndex) { } #include // Magic Incantation WX_DEFINE_LIST(ProxyFlagData); FlagListBoxDataItem::FlagListBoxDataItem(const wxString& fsoCategory) : fsoCategory(fsoCategory), shortDescription(wxEmptyString), flagString(wxEmptyString), isRecommendedFlag(false) { wxASSERT(!fsoCategory.IsEmpty()); } FlagListBoxDataItem::FlagListBoxDataItem(const wxString& shortDescription, const wxString& flagString, bool isRecommendedFlag) : fsoCategory(wxEmptyString), shortDescription(shortDescription), flagString(flagString), isRecommendedFlag(isRecommendedFlag) { // shortDescription can be empty wxASSERT(!flagString.IsEmpty()); } #include // Magic Incantation WX_DEFINE_LIST(FlagListBoxData); FlagFileData::FlagFileData() : isProxyDataGenerated(false), isFlagListBoxDataGenerated(false) { } FlagFileData::~FlagFileData() { for ( FlagCategoryList::iterator catIter = this->begin(); catIter != this->end(); catIter++ ) { FlagCategory* category = *catIter; for ( FlagList::iterator iter = category->flags.begin(); iter != category->flags.end(); iter++ ) { Flag *flag = *iter; delete flag; } category->flags.Clear(); delete category; } this->allSupportedFlagsByCategory.Clear(); FlagSetsList::iterator flagSetIter = this->flagSets.begin(); while ( flagSetIter != this->flagSets.end() ) { delete *flagSetIter; flagSetIter++; } this->flagSets.clear(); } void FlagFileData::AddEasyFlag(const wxString& easyFlag) { wxASSERT(!easyFlag.IsEmpty()); wxASSERT_MSG(this->easyFlags.Index(easyFlag.c_str()) == wxNOT_FOUND, wxString::Format(_T("attempted to add easy flag '%s' a second time"), easyFlag.c_str())); this->easyFlags.Add(easyFlag); } void FlagFileData::AddFlag(Flag* flag) { wxASSERT(flag != NULL); wxASSERT(!flag->fsoCatagory.IsEmpty()); FlagCategoryList::iterator iter; for (iter = this->begin(); iter != this->end(); iter++ ) { if ( flag->fsoCatagory == (*iter)->categoryName ) { break; } } if ( iter == this->end() ) { // did not find the category, so add it FlagCategory* flagCat = new FlagCategory(); flagCat->categoryName = flag->fsoCatagory; Flag* headFlag = new Flag(); headFlag->fsoCatagory = flag->fsoCatagory; headFlag->isRecomendedFlag = false; flagCat->flags.Append(headFlag); flagCat->flags.Append(flag); this->allSupportedFlagsByCategory.Append(flagCat); } else { (*iter)->flags.Append(flag); } } void FlagFileData::GenerateFlagSets() { wxASSERT_MSG(!this->easyFlags.IsEmpty(), _T("GenerateFlagSets() called when there are no easy flag categories.")); // GenerateFlagSets should be run exactly once, at least until the new mod.ini support is working wxASSERT_MSG(this->flagSets.IsEmpty(), _T("GenerateFlagSets() called when there already are flag sets.")); // \todo include the flag sets of the mod.inis as well // custom this->flagSets.Append(new FlagSet(_("Custom"))); // the easy flags. wxUint32 counter = 0; wxArrayString::const_iterator easyIter = this->easyFlags.begin(); while ( easyIter != this->easyFlags.end() ) { wxString easyFlag = *easyIter; if ( easyFlag.StartsWith(_T("Custom")) ) { // do nothing, we already have a custom } else { FlagSet* flagSet = new FlagSet(easyFlag); for (FlagCategoryList::const_iterator catIter = this->begin(); catIter != this->end(); catIter++) { for (FlagList::const_iterator flagIter = (*catIter)->flags.begin(); flagIter != (*catIter)->flags.end(); flagIter++) { Flag* flag = *flagIter; if ( !flag->flagString.IsEmpty() && (flag->easyEnable & counter) > 0 ) { flagSet->flagsToEnable.Add(flag->flagString); } if ( !flag->flagString.IsEmpty() && (flag->easyDisable & counter) > 0 ) { flagSet->flagsToDisable.Add(flag->flagString); } } } this->flagSets.Append(flagSet); } if (counter < 1) { counter = 2; // prime the counter so we can bitshift for the rest } else { counter = counter << 1; } if ( counter > (wxUint32)(1 << 31) ) { // we have checked 31 bits of counter, this is too many easy flag sets easyIter = this->easyFlags.end(); wxLogError(_T("FS2 Open executable has more than 31 easy flag categories")); } else { easyIter++; } } } ProxyFlagData* FlagFileData::GenerateProxyFlagData() const { wxASSERT(!this->allSupportedFlagsByCategory.IsEmpty()); wxASSERT_MSG(!this->isProxyDataGenerated, _T("Attempted to generate proxy data twice.")); // should never need to generate proxy data twice ProxyFlagData* proxyData = new ProxyFlagData(); for (FlagCategoryList::const_iterator catIter = this->begin(); catIter != this->end(); catIter++) { for (FlagList::const_iterator flagIter = (*catIter)->flags.begin(); flagIter != (*catIter)->flags.end(); flagIter++) { Flag* flag = *flagIter; if (flag->flagString.IsEmpty()) { // must be a category header continue; } proxyData->Append(new ProxyFlagDataItem(flag->flagString, flag->GetFlagIndex())); } } // keep const in the function prototype to avoid corrupting data, but allow for making this one change const_cast(this)->isProxyDataGenerated = true; return proxyData; } FlagListBoxData* FlagFileData::GenerateFlagListBoxData() const { wxASSERT(!this->allSupportedFlagsByCategory.IsEmpty()); wxASSERT_MSG(!this->isFlagListBoxDataGenerated, _T("Attempted to generate flag list box data twice.")); FlagListBoxData* flagListBoxData = new FlagListBoxData(); for (FlagCategoryList::const_iterator catIter = this->begin(); catIter != this->end(); catIter++) { for (FlagList::const_iterator flagIter = (*catIter)->flags.begin(); flagIter != (*catIter)->flags.end(); flagIter++) { Flag* flag = *flagIter; if (flag->flagString.IsEmpty()) { flagListBoxData->Append( new FlagListBoxDataItem(flag->fsoCatagory)); } else { flagListBoxData->Append( new FlagListBoxDataItem( flag->shortDescription, flag->flagString, flag->isRecomendedFlag)); } } } // keep const in the function prototype to avoid corrupting data, // but allow for making this one change const_cast(this)->isFlagListBoxDataGenerated = true; return flagListBoxData; } size_t FlagFileData::GetItemCount() const { size_t itemCount = 0; for (FlagCategoryList::const_iterator iter = this->begin(); iter != this->end(); iter++) { itemCount += (*iter)->flags.GetCount(); } return itemCount; } const FlagSet* FlagFileData::GetFlagSet(const wxString& flagSetName) const { wxCHECK_MSG(!this->flagSets.IsEmpty(), NULL, wxString::Format( _T("Attempted to set flag set '%s' when there are no flag sets."), flagSetName.c_str())); // TODO once new mod.ini supported, may need to rethink this assert, // and possibly also regenerate flag sets FlagSetsList::const_iterator flagSetsIter = this->flagSets.begin(); while(flagSetsIter != this->flagSets.end()) { if ( (*flagSetsIter)->name.StartsWith(flagSetName) ) { return *flagSetsIter; } flagSetsIter++; } wxLogWarning(_T("GetFlagSet(): could not find set %s"), flagSetName.c_str()); return NULL; } void FlagFileData::GetFlagSetNames(wxArrayString& arr) const { wxCHECK_RET(!this->flagSets.IsEmpty(), _T("Attempted to get flag sets when there are none.")); // TODO once new mod.ini supported, may need to rethink this assert, // and possibly also regenerate flag sets FlagSetsList::const_iterator flagSetsIter = this->flagSets.begin(); while ( flagSetsIter != this->flagSets.end() ) { arr.Add((*flagSetsIter)->name); flagSetsIter++; } } const wxString* FlagFileData::GetWebURL(const int n) const { wxCHECK_MSG(n >= 0 && n < static_cast(this->GetItemCount()), NULL, wxString::Format(_T("GetWebURL(): given invalid index %d"), n)); int flagIndex = 0; FlagCategoryList::const_iterator category = this->begin(); while (category != this->end()) { FlagList::const_iterator flag = (*category)->flags.begin(); while( flag != (*category)->flags.end() ) { if (flagIndex == n) { return &((*flag)->webURL); } flag++; flagIndex++; } category++; } wxFAIL_MSG(_T("GetWebURL(): should never reach here!")); return NULL; } wxlauncher-0.9.4/code/datastructures/FlagFileData.h0000644000000000000000000001000012155177255022246 0ustar rootroot00000000000000/* Copyright (C) 2009-2012 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FLAGFILEDATA_H #define FLAGFILEDATA_H class Flag { public: Flag(); wxString flagString; wxString shortDescription; wxString fsoCatagory; wxString webURL; bool isRecomendedFlag; wxUint32 easyEnable; wxUint32 easyDisable; int GetFlagIndex() const { return this->flagIndex; } private: int flagIndex; // private because the proxy depends on it being correct, so nothing should mess it up static int flagIndexCounter; }; WX_DECLARE_LIST(Flag, FlagList); /** Contains all of the flags in a category. */ class FlagCategory { public: wxString categoryName; FlagList flags; }; WX_DECLARE_LIST(FlagCategory, FlagCategoryList); class FlagSet { public: FlagSet(wxString name); wxString name; wxArrayString flagsToEnable; wxArrayString flagsToDisable; }; WX_DECLARE_LIST(FlagSet, FlagSetsList); /** Flag data needed by the profile proxy. */ class ProxyFlagDataItem { public: ProxyFlagDataItem(const wxString& flagString, int flagIndex); const wxString& GetFlagString() const { return flagString; } int GetFlagIndex() const { return flagIndex; } private: wxString flagString; int flagIndex; }; WX_DECLARE_LIST(ProxyFlagDataItem, ProxyFlagData); /** Flag data needed by the flag list box. */ class FlagListBoxDataItem { public: FlagListBoxDataItem(const wxString& fsoCategory); FlagListBoxDataItem(const wxString& shortDescription, const wxString& flagString, bool isRecommendedFlag); wxString fsoCategory; wxString shortDescription; wxString flagString; bool isRecommendedFlag; private: FlagListBoxDataItem(); }; WX_DECLARE_LIST(FlagListBoxDataItem, FlagListBoxData); /** The data extracted from the flag file. */ class FlagFileData { public: FlagFileData(); ~FlagFileData(); /** Adds the name of an "easy setup" flag set. */ void AddEasyFlag(const wxString& easyFlag); void AddFlag(Flag* flag); /** Generates the "easy setup" flag sets. This function requires that at least one "easy setup" name has been added. Until support for the new mod.ini is added, this function should be called exactly once. */ void GenerateFlagSets(); /** Creates a version of the data suitable for use by the profile proxy. */ ProxyFlagData* GenerateProxyFlagData() const; /** Creates a version of the data suitable for use by the flag list box. */ FlagListBoxData* GenerateFlagListBoxData() const; /** Returns the total number of flags and flag category headers. */ size_t GetItemCount() const; /** Returns a FlagSet, given its name. Returns NULL if not found. */ const FlagSet* GetFlagSet(const wxString& flagSetName) const; /** Stores the names of the flag sets in the passed-in array. */ void GetFlagSetNames(wxArrayString& arr) const; /** Gets the nth flag's webURL (if it has one). */ const wxString* GetWebURL(int n) const; private: FlagCategoryList::iterator begin() { return this->allSupportedFlagsByCategory.begin(); } FlagCategoryList::const_iterator begin() const { return this->allSupportedFlagsByCategory.begin(); } FlagCategoryList::iterator end() { return this->allSupportedFlagsByCategory.end(); } FlagCategoryList::const_iterator end() const { return this->allSupportedFlagsByCategory.end(); } wxArrayString easyFlags; FlagSetsList flagSets; FlagCategoryList allSupportedFlagsByCategory; bool isProxyDataGenerated; bool isFlagListBoxDataGenerated; }; #endif wxlauncher-0.9.4/code/datastructures/FlagInfo.cpp0000644000000000000000000000400312155177255022031 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* This file holds the internal list of flags that are not returned by the FS2_Open when queryed. Let's hope this list will never change and not be even need to change after the next FS2_Open release. It will be kept for backward compatibility though */ FlagInfo flagInfo[] = { //Lighting {_T("-ambient_factor"), _("Lighting"), true}, {_T("-no_emissive_light"), _("Lighting"), false}, {_T("-spec_exp"), _("Lighting"), true}, {_T("-spec_point"), _("Lighting"), true}, {_T("-spec_static"), _("Lighting"), true}, {_T("-spec_tube"), _("Lighting"), true}, {_T("-ogl_spec"), _("Lighting"), true}, //Graphics {_T("-height"), _("Graphics"), false}, {_T("-clipdist"), _("Graphics"), true}, {_T("-res"), _("Graphics"), true}, //Gameplay //{_T("-mod"), _("Gameplay"), true}, // This flag is handled elsewhere, but kept on list for completeness {_T("-fov"), _("Gameplay"), true}, //Multiplayer {_T("-gamename"), _("Multiplayer"), true}, {_T("-password"), _("Multiplayer"), true}, {_T("-allowabove"), _("Multiplayer"), true}, {_T("-allowbelow"), _("Multiplayer"), true}, {_T("-port"), _("Multiplayer"), true}, {_T("-connect"), _("Multiplayer"), true}, //string, ip to connnect to {_T("-timeout"), _("Multiplayer"), true}, //int, time before timeout {_T("-cap_object_update"), _("Multiplayer"), true}, };wxlauncher-0.9.4/code/datastructures/NewsSource.cpp0000644000000000000000000000542212155177255022447 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include // for _() #include #include "NewsSource.h" using std::vector; vector NewsSource::newsSources; NewsSource::NewsSource(const NewsSourceId id, const wxString& name, const wxString& newsUrl, const wxString& label) : id(id), name(name), newsUrl(newsUrl), label(label) { wxASSERT(!name.IsEmpty()); wxASSERT(!newsUrl.IsEmpty()); wxASSERT(!label.IsEmpty()); } const NewsSource* NewsSource::FindSource(const NewsSourceId id) { wxCHECK_MSG(id != NEWS_SOURCE_ID_INVALID, NULL, _T("FindSource given NEWS_SOURCE_ID_INVALID!")); if (newsSources.empty()) { InitializeSources(); } for (vector::const_iterator it = newsSources.begin(), end = newsSources.end(); it != end; ++it) { if (it->GetId() == id) { return &(*it); } } wxLogWarning(_T("FindSource(): Unknown news source ID %d, returning NULL"), id); return NULL; } const NewsSource* NewsSource::FindSource(const wxString& name) { wxCHECK_MSG(!name.IsEmpty(), NULL, _T("FindSource() given empty name!")); if (newsSources.empty()) { InitializeSources(); } for (vector::const_iterator it = newsSources.begin(), end = newsSources.end(); it != end; ++it) { if (!it->GetName().CmpNoCase(name)) { return &(*it); } } wxLogWarning(_T("FindSource(): Unknown name %s, returning NULL"), name.c_str()); return NULL; } void NewsSource::InitializeSources() { wxASSERT_MSG(newsSources.empty(), _T("news sources have already been initialized")); // NOTE: According to http://docs.wxwidgets.org/stable/wx_wxcontrol.html#wxcontrolsetlabel , // individual '&'s will be interpreted to indicate keyboard shortcuts // so if anyone wants a & to appear in the news box label, write it as && newsSources.push_back( NewsSource( NEWS_SOURCE_ID_HLP, _T("hlp"), _T("http://www.audiozone.ro/hl/"), _("Latest highlights from Hard Light Productions"))); newsSources.push_back( NewsSource( NEWS_SOURCE_ID_DIASPORA, _T("diaspora"), _T("http://diaspora.hard-light.net/hl.htm"), _("Latest Diaspora news"))); } wxlauncher-0.9.4/code/datastructures/NewsSource.h0000644000000000000000000000305212155177255022111 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef NEWS_SOURCE_H #define NEWS_SOURCE_H #include #include enum NewsSourceId { NEWS_SOURCE_ID_HLP, NEWS_SOURCE_ID_DIASPORA, NEWS_SOURCE_ID_INVALID }; class NewsSource { public: NewsSource(NewsSourceId id, const wxString& name, const wxString& newsUrl, const wxString& label); const NewsSourceId GetId() const { return this->id; } const wxString& GetName() const { return this->name; } const wxString& GetNewsUrl() const { return this->newsUrl; } const wxString& GetLabel() const { return this->label; } static const NewsSource* FindSource(NewsSourceId id); static const NewsSource* FindSource(const wxString& name); private: NewsSourceId id; wxString name; wxString newsUrl; wxString label; static void InitializeSources(); static std::vector newsSources; }; #endif wxlauncher-0.9.4/code/datastructures/ResolutionMap.cpp0000644000000000000000000000603312155177255023152 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "datastructures/ResolutionMap.h" #include #include "controls/ModList.h" DEFINE_EVENT_TYPE(EVT_RESOLUTION_MAP_CHANGED); #include // required magic incatation WX_DEFINE_LIST(ResolutionMapEventHandlers); ResolutionMapEventHandlers ResolutionMap::resolutionMapChangedHandlers; WX_DECLARE_STRING_HASH_MAP( ResolutionData, PreferredResolutionMap); PreferredResolutionMap ResolutionMap::prefResMap; const ResolutionData* ResolutionMap::ResolutionRead(const wxString& shortname) { PreferredResolutionMap::const_iterator it = prefResMap.find(shortname); return (it == prefResMap.end()) ? NULL : &it->second; } void ResolutionMap::ResolutionWrite(const wxString& shortname, const ResolutionData& resData) { wxASSERT(!shortname.IsEmpty()); wxASSERT(resData.IsValid()); prefResMap[shortname] = resData; wxLogDebug(_T("Wrote resolution %dx%d for mod %s"), resData.width, resData.height, shortname.c_str()); GenerateResolutionMapChanged(); } bool ResolutionMap::HasEntryForActiveMod() { const ModItem* activeMod = ModList::GetActiveMod(); wxCHECK_MSG(activeMod != NULL, false, _T("HasEntryForActiveMod: activeMod is NULL!")); return prefResMap.find(activeMod->shortname) != prefResMap.end(); } void ResolutionMap::RegisterResolutionMapChanged(wxEvtHandler *handler) { wxASSERT_MSG(resolutionMapChangedHandlers.IndexOf(handler) == wxNOT_FOUND, wxString::Format( _T("RegisterResolutionMapChanged(): Handler at %p already registered."), handler)); resolutionMapChangedHandlers.Append(handler); } void ResolutionMap::UnRegisterResolutionMapChanged(wxEvtHandler *handler) { wxASSERT_MSG(resolutionMapChangedHandlers.IndexOf(handler) != wxNOT_FOUND, wxString::Format( _T("UnRegisterResolutionMapChanged(): Handler at %p not registered."), handler)); resolutionMapChangedHandlers.DeleteObject(handler); } void ResolutionMap::GenerateResolutionMapChanged() { wxCommandEvent event(EVT_RESOLUTION_MAP_CHANGED, wxID_NONE); wxLogDebug(_T("Generating EVT_RESOLUTION_MAP_CHANGED event")); ResolutionMapEventHandlers::iterator iter = resolutionMapChangedHandlers.begin(); while (iter != resolutionMapChangedHandlers.end()) { wxEvtHandler* current = *iter; current->AddPendingEvent(event); wxLogDebug(_T(" Sent EVT_RESOLUTION_MAP_CHANGED event to %p"), current); iter++; } } wxlauncher-0.9.4/code/datastructures/ResolutionMap.h0000644000000000000000000000416112155177255022617 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RESOLUTION_MAP_H #define RESOLUTION_MAP_H #include #include #include "global/ModDefaults.h" /** Maps a mod shortname to its user-preferred resolution. */ // TODO FIXME: Once the refresh button for the mod list is added, // then this map will need to be cleared whenever the user presses the button. /** ResolutionMap has changed. */ DECLARE_EVENT_TYPE(EVT_RESOLUTION_MAP_CHANGED, wxID_ANY); WX_DECLARE_LIST(wxEvtHandler, ResolutionMapEventHandlers); class PreferredResolutionMap; // in .cpp file struct ResolutionData { ResolutionData() { } // required for wxHashMap, unfortunately ResolutionData(long width, long height) : width(width), height(height) { } bool IsValid() const { return (width >= DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES) && (height >= DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES); } long width; long height; }; class ResolutionMap { public: static const ResolutionData* ResolutionRead(const wxString& shortname); static void ResolutionWrite(const wxString& shortname, const ResolutionData& resData); static bool HasEntryForActiveMod(); static void RegisterResolutionMapChanged(wxEvtHandler *handler); static void UnRegisterResolutionMapChanged(wxEvtHandler *handler); private: static void GenerateResolutionMapChanged(); static ResolutionMapEventHandlers resolutionMapChangedHandlers; static PreferredResolutionMap prefResMap; }; #endif wxlauncher-0.9.4/code/global/BasicDefaults.cpp0000644000000000000000000000407312155177255021247 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "BasicDefaults.h" // internal constants const unsigned int JOYMAN_INVALID_JOYSTICK = 99999; // defaults for basic settings const long DEFAULT_VIDEO_RESOLUTION_WIDTH = 1024; const long DEFAULT_VIDEO_RESOLUTION_HEIGHT = 768; const long DEFAULT_VIDEO_BIT_DEPTH = 32; const wxString DEFAULT_VIDEO_TEXTURE_FILTER = _T("Trilinear"); const long DEFAULT_VIDEO_ANISOTROPIC = 0; const long DEFAULT_VIDEO_ANTI_ALIAS = 0; const wxString DEFAULT_AUDIO_OPENAL_DEVICE = _T("no sound"); const wxString DEFAULT_AUDIO_OPENAL_PLAYBACK_DEVICE = wxEmptyString; const wxString DEFAULT_AUDIO_OPENAL_CAPTURE_DEVICE = wxEmptyString; const bool DEFAULT_AUDIO_OPENAL_EFX = false; const long DEFAULT_AUDIO_OPENAL_SAMPLE_RATE = 0; const long DEFAULT_SPEECH_VOICE = 0; const long DEFAULT_SPEECH_VOLUME = 100; const bool DEFAULT_SPEECH_IN_TECHROOM = false; const bool DEFAULT_SPEECH_IN_BRIEFINGS = false; const bool DEFAULT_SPEECH_IN_GAME = false; const bool DEFAULT_SPEECH_IN_MULTI = false; const long DEFAULT_JOYSTICK_ID = JOYMAN_INVALID_JOYSTICK; const bool DEFAULT_JOYSTICK_FORCE_FEEDBACK = false; const bool DEFAULT_JOYSTICK_DIRECTIONAL = false; const wxString DEFAULT_NETWORK_TYPE = _T("None"); const wxString DEFAULT_NETWORK_SPEED = _T("None"); const long DEFAULT_NETWORK_PORT = 0; const wxString DEFAULT_NETWORK_IP = wxEmptyString; wxlauncher-0.9.4/code/global/BasicDefaults.h0000644000000000000000000000376312155177255020721 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASIC_DEFAULTS_H #define BASIC_DEFAULTS_H #include /** \defgroup basicdefaults default values for basic settings */ /** @{*/ extern const long DEFAULT_VIDEO_RESOLUTION_WIDTH; extern const long DEFAULT_VIDEO_RESOLUTION_HEIGHT; extern const long DEFAULT_VIDEO_BIT_DEPTH; extern const wxString DEFAULT_VIDEO_TEXTURE_FILTER; extern const long DEFAULT_VIDEO_ANISOTROPIC; extern const long DEFAULT_VIDEO_ANTI_ALIAS; extern const wxString DEFAULT_AUDIO_OPENAL_DEVICE; extern const wxString DEFAULT_AUDIO_OPENAL_PLAYBACK_DEVICE; extern const wxString DEFAULT_AUDIO_OPENAL_CAPTURE_DEVICE; extern const bool DEFAULT_AUDIO_OPENAL_EFX; extern const long DEFAULT_AUDIO_OPENAL_SAMPLE_RATE; extern const long DEFAULT_SPEECH_VOICE; extern const long DEFAULT_SPEECH_VOLUME; extern const bool DEFAULT_SPEECH_IN_TECHROOM; extern const bool DEFAULT_SPEECH_IN_BRIEFINGS; extern const bool DEFAULT_SPEECH_IN_GAME; extern const bool DEFAULT_SPEECH_IN_MULTI; extern const long DEFAULT_JOYSTICK_ID; extern const bool DEFAULT_JOYSTICK_FORCE_FEEDBACK; extern const bool DEFAULT_JOYSTICK_DIRECTIONAL; extern const wxString DEFAULT_NETWORK_TYPE; extern const wxString DEFAULT_NETWORK_SPEED; extern const long DEFAULT_NETWORK_PORT; extern const wxString DEFAULT_NETWORK_IP; /** @}*/ #endif wxlauncher-0.9.4/code/global/MemoryDebugging.h0000644000000000000000000000200312155177255021256 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Used to setup memory leak detection on Windows with VisualStudio. */ #ifdef _DEBUG #define MSCRTMEMORY 1 #define _CRTDBG_MAP_ALLOC #include #include // catch uses of new and delete #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW #endif wxlauncher-0.9.4/code/global/ModDefaults.cpp0000644000000000000000000000236312155177255020745 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ModDefaults.h" #include #include "global/ids.h" const wxString DEFAULT_MOD_LAUNCHER_INFO_TEXT (_("No information available.")); const long DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES = 640; const long DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES = 480; const wxString DEFAULT_MOD_RECOMMENDED_LIGHTING_NAME (_T("Baseline recommended")); const wxString DEFAULT_MOD_RECOMMENDED_LIGHTING_FLAGSET (_T("-ambient_factor 75 -spec_exp 11 -spec_point 0.6 -spec_static 0.8 -spec_tube 0.4 -ogl_spec 60")); wxlauncher-0.9.4/code/global/ModDefaults.h0000644000000000000000000000227612155177255020415 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOD_DEFAULTS_H #define MOD_DEFAULTS_H #include /** \defgroup moddefaults default values for mod-specific settings */ /** @{*/ extern const wxString DEFAULT_MOD_LAUNCHER_INFO_TEXT; extern const long DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES; extern const long DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES; extern const wxString DEFAULT_MOD_RECOMMENDED_LIGHTING_NAME; extern const wxString DEFAULT_MOD_RECOMMENDED_LIGHTING_FLAGSET; /** @}*/ #endif wxlauncher-0.9.4/code/global/ModIniKeys.cpp0000644000000000000000000000702112155177255020545 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "global/ModIniKeys.h" const wxString MOD_INI_KEY_LAUNCHER_MOD_NAME (_T("/launcher/modname")); const wxString MOD_INI_KEY_LAUNCHER_IMAGE_255X112 (_T("/launcher/image255x112")); const wxString MOD_INI_KEY_LAUNCHER_IMAGE_182X80 (_T("/launcher/image182x80")); const wxString MOD_INI_KEY_LAUNCHER_INFO_TEXT (_T("/launcher/infotext")); const wxString MOD_INI_KEY_LAUNCHER_AUTHOR (_T("/launcher/author")); const wxString MOD_INI_KEY_LAUNCHER_NOTES (_T("/launcher/notes")); const wxString MOD_INI_KEY_LAUNCHER_WARN (_T("/launcher/warn")); const wxString MOD_INI_KEY_LAUNCHER_WEBSITE (_T("/launcher/website")); const wxString MOD_INI_KEY_LAUNCHER_FORUM (_T("/launcher/forum")); const wxString MOD_INI_KEY_LAUNCHER_BUGS (_T("/launcher/bugs")); const wxString MOD_INI_KEY_LAUNCHER_SUPPORT (_T("/launcher/support")); const wxString MOD_INI_KEY_RESOLUTION_MIN_HORIZONTAL_RES (_T("/resolution/minhorizontalres")); const wxString MOD_INI_KEY_RESOLUTION_MIN_VERTICAL_RES (_T("/resolution/minverticalres")); const wxString MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_ON (_T("/extremeforce/forcedflagson")); const wxString MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_OFF (_T("/extremeforce/forcedflagsoff")); const wxString MOD_INI_KEY_MULTIMOD_PRIMARY_LIST (_T("/multimod/primarylist")); const wxString MOD_INI_KEY_MULTIMOD_SECONDRY_LIST (_T("/multimod/secondrylist")); const wxString MOD_INI_KEY_MULTIMOD_SECONDARY_LIST (_T("/multimod/secondarylist")); const wxString MOD_INI_KEY_SKIN_WINDOW_TITLE (_T("/skin/windowtitle")); const wxString MOD_INI_KEY_SKIN_WINDOW_ICON (_T("/skin/windowicon")); const wxString MOD_INI_KEY_SKIN_BANNER (_T("/skin/banner")); const wxString MOD_INI_KEY_SKIN_WELCOME_TEXT (_T("/skin/welcometext")); const wxString MOD_INI_KEY_SKIN_MOD_IMAGE_255X112 (_T("/skin/modimage255x112")); const wxString MOD_INI_KEY_SKIN_MOD_IMAGE_182X80 (_T("/skin/modimage182x80")); const wxString MOD_INI_KEY_SKIN_ICON_OK (_T("/skin/iconok")); const wxString MOD_INI_KEY_SKIN_ICON_WARNING (_T("/skin/iconwarn")); const wxString MOD_INI_KEY_SKIN_ICON_WARNING_BIG (_T("/skin/iconwarnbig")); const wxString MOD_INI_KEY_SKIN_ICON_ERROR (_T("/skin/iconerror")); const wxString MOD_INI_KEY_SKIN_ICON_INFO (_T("/skin/iconinfo")); const wxString MOD_INI_KEY_SKIN_ICON_INFO_BIG (_T("/skin/iconinfobig")); const wxString MOD_INI_KEY_SKIN_ICON_HELP (_T("/skin/iconhelp")); const wxString MOD_INI_KEY_SKIN_ICON_HELP_BIG (_T("/skin/iconhelpbig")); const wxString MOD_INI_KEY_SKIN_ICON_IDEAL (_T("/skin/iconideal")); const wxString MOD_INI_KEY_SKIN_NEWS_SOURCE (_T("/skin/newssource")); const wxString MOD_INI_KEY_RECOMMENDED_LIGHTING_NAME (_T("/recommendedlighting/name")); const wxString MOD_INI_KEY_RECOMMENDED_LIGHTING_FLAGSET (_T("/recommendedlighting/flagset")); wxlauncher-0.9.4/code/global/ModIniKeys.h0000644000000000000000000000560212155177255020215 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOD_INI_KEYS_H #define MOD_INI_KEYS_H #include /** \defgroup modkeys Keys used in mod.ini file */ /** @{*/ extern const wxString MOD_INI_KEY_LAUNCHER_MOD_NAME; extern const wxString MOD_INI_KEY_LAUNCHER_IMAGE_255X112; extern const wxString MOD_INI_KEY_LAUNCHER_IMAGE_182X80; extern const wxString MOD_INI_KEY_LAUNCHER_INFO_TEXT; extern const wxString MOD_INI_KEY_LAUNCHER_AUTHOR; extern const wxString MOD_INI_KEY_LAUNCHER_NOTES; extern const wxString MOD_INI_KEY_LAUNCHER_WARN; extern const wxString MOD_INI_KEY_LAUNCHER_WEBSITE; extern const wxString MOD_INI_KEY_LAUNCHER_FORUM; extern const wxString MOD_INI_KEY_LAUNCHER_BUGS; extern const wxString MOD_INI_KEY_LAUNCHER_SUPPORT; extern const wxString MOD_INI_KEY_RESOLUTION_MIN_VERTICAL_RES; extern const wxString MOD_INI_KEY_RESOLUTION_MIN_HORIZONTAL_RES; extern const wxString MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_ON; extern const wxString MOD_INI_KEY_EXTREMEFORCE_FORCED_FLAGS_OFF; extern const wxString MOD_INI_KEY_MULTIMOD_PRIMARY_LIST; extern const wxString MOD_INI_KEY_MULTIMOD_SECONDRY_LIST; extern const wxString MOD_INI_KEY_MULTIMOD_SECONDARY_LIST; // TODO: /flagsetideal and /flagset# extern const wxString MOD_INI_KEY_SKIN_WINDOW_TITLE; extern const wxString MOD_INI_KEY_SKIN_WINDOW_ICON; extern const wxString MOD_INI_KEY_SKIN_BANNER; extern const wxString MOD_INI_KEY_SKIN_WELCOME_TEXT; extern const wxString MOD_INI_KEY_SKIN_MOD_IMAGE_255X112; extern const wxString MOD_INI_KEY_SKIN_MOD_IMAGE_182X80; extern const wxString MOD_INI_KEY_SKIN_ICON_OK; extern const wxString MOD_INI_KEY_SKIN_ICON_WARNING; extern const wxString MOD_INI_KEY_SKIN_ICON_WARNING_BIG; extern const wxString MOD_INI_KEY_SKIN_ICON_ERROR; extern const wxString MOD_INI_KEY_SKIN_ICON_INFO; extern const wxString MOD_INI_KEY_SKIN_ICON_INFO_BIG; extern const wxString MOD_INI_KEY_SKIN_ICON_HELP; extern const wxString MOD_INI_KEY_SKIN_ICON_HELP_BIG; extern const wxString MOD_INI_KEY_SKIN_ICON_IDEAL; extern const wxString MOD_INI_KEY_SKIN_NEWS_SOURCE; // TODO: mod text localization? extern const wxString MOD_INI_KEY_RECOMMENDED_LIGHTING_NAME; extern const wxString MOD_INI_KEY_RECOMMENDED_LIGHTING_FLAGSET; /** @}*/ #endif wxlauncher-0.9.4/code/global/ProfileKeys.cpp0000644000000000000000000000733412155177255020775 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ProfileKeys.h" // Global profile keys and constants const wxString GBL_CFG_MAIN_AUTOSAVEPROFILES (_T("/main/autosaveprofiles")); const wxString GBL_CFG_MAIN_LASTPROFILE (_T("/main/lastprofile")); const wxString GBL_CFG_PROXY_TYPE (_T("/proxy/type")); const wxString GBL_CFG_PROXY_SERVER (_T("/proxy/server")); const wxString GBL_CFG_PROXY_PORT (_T("/proxy/port")); const wxString GBL_CFG_NET_FOLDER (_T("/net")); const wxString GBL_CFG_NET_DOWNLOAD_NEWS (_T("/net/downloadnews")); const wxString NEWS_LAST_TIME_FORMAT (_T("%Y %j %H %M %S")); const wxString GBL_CFG_NET_NEWS_LAST_TIME (_T("lastdownloadnews")); const wxString GBL_CFG_NET_THE_NEWS (_T("thenews")); const wxString GBL_CFG_OPT_CONFIG_FRED (_T("/opt/configfred")); // Profile keys and constants const wxString PRO_CFG_MAIN_NAME (_T("/main/name")); const wxString PRO_CFG_MAIN_FILENAME (_T("/main/filename")); const wxString PRO_CFG_MAIN_INITIALIZED (_T("/main/initialized")); const wxString PRO_CFG_TC_ROOT_FOLDER (_T("/tc/folder")); const wxString PRO_CFG_TC_CURRENT_BINARY (_T("/tc/currentbinary")); const wxString PRO_CFG_TC_CURRENT_MODLINE (_T("/tc/currentmodline")); const wxString PRO_CFG_TC_CURRENT_MOD (_T("/tc/currentmod")); const wxString PRO_CFG_TC_CURRENT_FLAG_LINE (_T("/tc/flags")); const wxString PRO_CFG_TC_CURRENT_FRED (_T("/tc/currentfred")); const wxString PRO_CFG_VIDEO_RESOLUTION_WIDTH (_T("/video/width")); const wxString PRO_CFG_VIDEO_RESOLUTION_HEIGHT (_T("/video/height")); const wxString CFG_RES_FORMAT_STRING (_T("%d x %d")); const wxString PRO_CFG_VIDEO_BIT_DEPTH (_T("/video/depth")); const wxString PRO_CFG_VIDEO_ANISOTROPIC (_T("/video/anisotropic")); const wxString PRO_CFG_VIDEO_ANTI_ALIAS (_T("/video/antialias")); const wxString PRO_CFG_VIDEO_TEXTURE_FILTER (_T("/video/texturefilter")); const wxString PRO_CFG_LIGHTING_PRESET (_T("/lighting/preset")); const wxString PRO_CFG_SPEECH_VOICE (_T("/speech/voice")); const wxString PRO_CFG_SPEECH_VOLUME (_T("/speech/volume")); const wxString PRO_CFG_SPEECH_IN_TECHROOM (_T("/speech/intechroom")); const wxString PRO_CFG_SPEECH_IN_BRIEFINGS (_T("/speech/inbriefings")); const wxString PRO_CFG_SPEECH_IN_GAME (_T("/speech/ingame")); const wxString PRO_CFG_SPEECH_IN_MULTI (_T("/speech/inmulti")); const wxString PRO_CFG_NETWORK_TYPE (_T("/network/type")); const wxString PRO_CFG_NETWORK_SPEED (_T("/network/speed")); const wxString PRO_CFG_NETWORK_PORT (_T("/network/port")); const wxString PRO_CFG_NETWORK_IP (_T("/network/ip")); const wxString PRO_CFG_OPENAL_DEVICE (_T("/openal/device")); const wxString PRO_CFG_OPENAL_CAPTURE_DEVICE (_T("/openal/capturedevice")); const wxString PRO_CFG_OPENAL_EFX (_T("/openal/efx")); const wxString PRO_CFG_OPENAL_SAMPLE_RATE (_T("/openal/samplerate")); const wxString PRO_CFG_JOYSTICK_ID (_T("/joystick/id")); const wxString PRO_CFG_JOYSTICK_FORCE_FEEDBACK (_T("/joystick/forcefeedback")); const wxString PRO_CFG_JOYSTICK_DIRECTIONAL (_T("/joystick/directional")); /** @}*/ wxlauncher-0.9.4/code/global/ProfileKeys.h0000644000000000000000000001077312155177255020443 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROFILE_KEYS_H #define PROFILE_KEYS_H #include #include /** \defgroup Storage Storage locations */ /** @{*/ inline const wxString GetProfileStorageFolder() { return wxStandardPaths::Get().GetUserDataDir(); } /** @}*/ /** \defgroup Internal Store location */ /** @{ */ #define INT_CONFIG_FILE_LOCATION _T("/wxlauncher/configlocation") //!< string /** @} */ /** \defgroup globalkeys Keys used in global config file */ /** @{*/ extern const wxString GBL_CFG_MAIN_AUTOSAVEPROFILES; //!< bool extern const wxString GBL_CFG_MAIN_LASTPROFILE; //!< string, internal profile name extern const wxString GBL_CFG_PROXY_TYPE; //!< string extern const wxString GBL_CFG_PROXY_SERVER; //!< string extern const wxString GBL_CFG_PROXY_PORT; //!< int extern const wxString GBL_CFG_NET_FOLDER; //!< string (folder name) extern const wxString GBL_CFG_NET_DOWNLOAD_NEWS; //!< bool, true means autodownload extern const wxString NEWS_LAST_TIME_FORMAT; // these two are entries relative to news source folders, not absolute paths extern const wxString GBL_CFG_NET_NEWS_LAST_TIME; //!< string, formated time as NEWS_LAST_TIME_FORMAT extern const wxString GBL_CFG_NET_THE_NEWS; //!< string, the formatted text (workin' for a livin'!) extern const wxString GBL_CFG_OPT_CONFIG_FRED; //!< bool, true means show the user the FRED button and allow user to select FRED executable /** @}*/ /** \defgroup profilekeys Keys used in profiles */ /** @{*/ extern const wxString PRO_CFG_MAIN_NAME; //!< string, name of profile extern const wxString PRO_CFG_MAIN_FILENAME; //!< string, full path to profile extern const wxString PRO_CFG_MAIN_INITIALIZED; //!< bool, indicates whether profile has been saved with initial GUI values extern const wxString PRO_CFG_TC_ROOT_FOLDER; //!< string, absolute path extern const wxString PRO_CFG_TC_CURRENT_BINARY; //!< string, binary name extern const wxString PRO_CFG_TC_CURRENT_MODLINE; //!< string, the entire line that should follow -mod extern const wxString PRO_CFG_TC_CURRENT_MOD; //!< string, the mod shortname (for modlist) extern const wxString PRO_CFG_TC_CURRENT_FLAG_LINE; //!< string, the flags that we as the modline to to make the cmdline extern const wxString PRO_CFG_TC_CURRENT_FRED; //!< string, FRED binary's name extern const wxString PRO_CFG_VIDEO_RESOLUTION_WIDTH; //!< int extern const wxString PRO_CFG_VIDEO_RESOLUTION_HEIGHT; //!< int extern const wxString CFG_RES_FORMAT_STRING; extern const wxString PRO_CFG_VIDEO_BIT_DEPTH; //!< int extern const wxString PRO_CFG_VIDEO_ANISOTROPIC; //!< int extern const wxString PRO_CFG_VIDEO_ANTI_ALIAS; //!< int extern const wxString PRO_CFG_VIDEO_TEXTURE_FILTER; //!< string extern const wxString PRO_CFG_LIGHTING_PRESET; //!< string extern const wxString PRO_CFG_SPEECH_VOICE; //!< int, same as what the current engine uses extern const wxString PRO_CFG_SPEECH_VOLUME; //!< int extern const wxString PRO_CFG_SPEECH_IN_TECHROOM; //!< bool extern const wxString PRO_CFG_SPEECH_IN_BRIEFINGS; //!< bool extern const wxString PRO_CFG_SPEECH_IN_GAME; //!< bool extern const wxString PRO_CFG_SPEECH_IN_MULTI; //!< bool extern const wxString PRO_CFG_NETWORK_TYPE; //!< string extern const wxString PRO_CFG_NETWORK_SPEED; //!< string extern const wxString PRO_CFG_NETWORK_PORT; //!< int extern const wxString PRO_CFG_NETWORK_IP; //!< string extern const wxString PRO_CFG_OPENAL_DEVICE; //!< string extern const wxString PRO_CFG_OPENAL_CAPTURE_DEVICE; //!< string extern const wxString PRO_CFG_OPENAL_EFX; //!< bool extern const wxString PRO_CFG_OPENAL_SAMPLE_RATE; //!< int extern const wxString PRO_CFG_JOYSTICK_ID; //!< int extern const wxString PRO_CFG_JOYSTICK_FORCE_FEEDBACK; //!< bool extern const wxString PRO_CFG_JOYSTICK_DIRECTIONAL; //!< bool /** @}*/ #endif wxlauncher-0.9.4/code/global/RegistryKeys.cpp0000644000000000000000000000517512155177255021206 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RegistryKeys.h" const wxString REG_KEY_FOLDER_LOCATION (L"Software\\Volition\\Freespace2"); const wxString REG_KEY_VIDEO_RESOLUTION_DEPTH (L"VideocardFs2open"); const wxString REG_KEY_VIDEO_TEXTURE_FILTER (L"TextureFilter"); const wxString REG_KEY_VIDEO_ANISOTROPIC (L"OGL_AnisotropicFilter"); const wxString REG_KEY_VIDEO_ANTI_ALIAS (L"OGL_AntiAliasSamples"); const wxString REG_KEY_AUDIO_OPENAL_DEVICE (L"SoundDeviceOAL"); const wxString REG_KEY_AUDIO_FOLDER_CFG (L"/Sound"); // FileProfileManager const wxString REG_KEY_AUDIO_FOLDER_REGISTRY (L"Sound"); // RegistryProfileManager const wxString REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE (L"PlaybackDevice"); const wxString REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE (L"CaptureDevice"); const wxString REG_KEY_AUDIO_OPENAL_EFX (L"EnableEFX"); const wxString REG_KEY_AUDIO_OPENAL_SAMPLE_RATE (L"SampleRate"); const wxString REG_KEY_SPEECH_VOICE (L"SpeechVoice"); const wxString REG_KEY_SPEECH_VOLUME (L"SpeechVolume"); const wxString REG_KEY_SPEECH_IN_TECHROOM (L"SpeechTechroom"); const wxString REG_KEY_SPEECH_IN_BRIEFINGS (L"SpeechBriefings"); const wxString REG_KEY_SPEECH_IN_GAME (L"SpeechIngame"); const wxString REG_KEY_SPEECH_IN_MULTI (L"SpeechMulti"); const wxString REG_KEY_JOYSTICK_ID (L"CurrentJoystick"); const wxString REG_KEY_JOYSTICK_FORCE_FEEDBACK (L"EnableJoystickFF"); const wxString REG_KEY_JOYSTICK_DIRECTIONAL (L"EnableHitEffect"); const wxString REG_KEY_NETWORK_TYPE (L"NetworkConnection"); const wxString REG_KEY_NETWORK_SPEED (L"ConnectionSpeed"); const wxString REG_KEY_NETWORK_PORT (L"ForcePort"); const wxString REG_KEY_NETWORK_FOLDER_CFG (L"/Network"); // FileProfileManager const wxString REG_KEY_DEFAULT_FOLDER_CFG (L"/Default"); // FileProfileManager const wxString REG_KEY_NETWORK_FOLDER_REGISTRY (L"Network"); // RegistryProfileManager const wxString REG_KEY_NETWORK_IP (L"CustomIP"); wxlauncher-0.9.4/code/global/RegistryKeys.h0000644000000000000000000000440612155177255020647 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REGISTRY_KEYS_H #define REGISTRY_KEYS_H #include /** \defgroup registrykeys Keys used in the registry or fs2_open.ini */ /** @{*/ extern const wxString REG_KEY_FOLDER_LOCATION; extern const wxString REG_KEY_VIDEO_RESOLUTION_DEPTH; extern const wxString REG_KEY_VIDEO_TEXTURE_FILTER; extern const wxString REG_KEY_VIDEO_ANISOTROPIC; extern const wxString REG_KEY_VIDEO_ANTI_ALIAS; extern const wxString REG_KEY_AUDIO_OPENAL_DEVICE; extern const wxString REG_KEY_AUDIO_FOLDER_CFG; extern const wxString REG_KEY_AUDIO_FOLDER_REGISTRY; extern const wxString REG_KEY_AUDIO_OPENAL_PLAYBACK_DEVICE; extern const wxString REG_KEY_AUDIO_OPENAL_CAPTURE_DEVICE; extern const wxString REG_KEY_AUDIO_OPENAL_EFX; extern const wxString REG_KEY_AUDIO_OPENAL_SAMPLE_RATE; extern const wxString REG_KEY_SPEECH_VOICE; extern const wxString REG_KEY_SPEECH_VOLUME; extern const wxString REG_KEY_SPEECH_IN_TECHROOM; extern const wxString REG_KEY_SPEECH_IN_BRIEFINGS; extern const wxString REG_KEY_SPEECH_IN_GAME; extern const wxString REG_KEY_SPEECH_IN_MULTI; extern const wxString REG_KEY_JOYSTICK_ID; extern const wxString REG_KEY_JOYSTICK_FORCE_FEEDBACK; extern const wxString REG_KEY_JOYSTICK_DIRECTIONAL; extern const wxString REG_KEY_NETWORK_TYPE; extern const wxString REG_KEY_NETWORK_SPEED; extern const wxString REG_KEY_NETWORK_PORT; extern const wxString REG_KEY_NETWORK_FOLDER_CFG; extern const wxString REG_KEY_DEFAULT_FOLDER_CFG; extern const wxString REG_KEY_NETWORK_FOLDER_REGISTRY; extern const wxString REG_KEY_NETWORK_IP; /** @}*/ #endif wxlauncher-0.9.4/code/global/SkinDefaults.cpp0000644000000000000000000000473212155177255021134 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "SkinDefaults.h" #include // for _() macro const wxString DEFAULT_SKIN_WINDOW_TITLE (_("wxLauncher for the FreeSpace 2 Source Code Project")); const wxString DEFAULT_SKIN_WINDOW_ICON (_T("wxlauncher.ico")); const wxString DEFAULT_SKIN_BANNER (_T("SCP_Header.png")); const wxString DEFAULT_SKIN_WELCOME_TEXT (_("

Welcome to wxLauncher, the next-generation launcher for FS2 Open games

New to FreeSpace 2? Check out these links:

= FreeSpace Wiki = Hard Light Productions Forums =
= Multiplayer Setup = Support Forum =
= Troubleshooting FAQ =

")); const wxString DEFAULT_SKIN_MOD_IMAGE_255X112 (_T("modimg.png")); const wxString DEFAULT_SKIN_MOD_IMAGE_182X80 (_T("modimg_small.png")); const wxString DEFAULT_SKIN_ICON_OK (_T("icon_ok.png")); const wxString DEFAULT_SKIN_ICON_WARNING (_T("icon_warning.png")); const wxString DEFAULT_SKIN_ICON_WARNING_BIG (_T("warning_big.png")); const wxString DEFAULT_SKIN_ICON_ERROR (_T("icon_error.png")); const wxString DEFAULT_SKIN_ICON_INFO (_T("icon_information.png")); const wxString DEFAULT_SKIN_ICON_INFO_BIG (_T("info_big.png")); const wxString DEFAULT_SKIN_ICON_HELP (_T("helpicon.png")); const wxString DEFAULT_SKIN_ICON_HELP_BIG (_T("questionmark.png")); const wxString DEFAULT_SKIN_ICON_IDEAL (_T("recommended.png")); const NewsSourceId DEFAULT_SKIN_NEWS_SOURCE (NEWS_SOURCE_ID_HLP); wxlauncher-0.9.4/code/global/SkinDefaults.h0000644000000000000000000000327112155177255020576 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SKIN_DEFAULTS_H #define SKIN_DEFAULTS_H #include #include "datastructures/NewsSource.h" /** \defgroup skindefaults values for default skin */ /** @{*/ extern const wxString DEFAULT_SKIN_WINDOW_TITLE; extern const wxString DEFAULT_SKIN_WINDOW_ICON; extern const wxString DEFAULT_SKIN_BANNER; extern const wxString DEFAULT_SKIN_WELCOME_TEXT; extern const wxString DEFAULT_SKIN_MOD_IMAGE_255X112; extern const wxString DEFAULT_SKIN_MOD_IMAGE_182X80; extern const wxString DEFAULT_SKIN_ICON_OK; extern const wxString DEFAULT_SKIN_ICON_WARNING; extern const wxString DEFAULT_SKIN_ICON_WARNING_BIG; extern const wxString DEFAULT_SKIN_ICON_ERROR; extern const wxString DEFAULT_SKIN_ICON_INFO; extern const wxString DEFAULT_SKIN_ICON_INFO_BIG; extern const wxString DEFAULT_SKIN_ICON_HELP; extern const wxString DEFAULT_SKIN_ICON_HELP_BIG; extern const wxString DEFAULT_SKIN_ICON_IDEAL; extern const NewsSourceId DEFAULT_SKIN_NEWS_SOURCE; /** @}*/ #endif wxlauncher-0.9.4/code/global/Utils.cpp0000644000000000000000000000343112155177255017633 0ustar rootroot00000000000000/* Copyright (C) 2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "Utils.h" namespace TextUtils { #include WX_DEFINE_OBJARRAY(ArrayOfWords); void FillArrayOfWordsFromTokens(wxStringTokenizer &tokens, wxDC &dc, wxFont* testFont, ArrayOfWords& words, const bool useAppleDebugFilter) { while ( tokens.HasMoreTokens() ) { wxString tok = tokens.GetNextToken(); #if IS_APPLE if (useAppleDebugFilter) { // left over from tokenizing executable in debug .app if (tok.Lower() == _T("(debug)")) { tok = _T(""); } // remove the text after ".app" in the FSO executable name // the trailing / ensures that the .app indicates an extension int DotAppIndex = tok.Find(_T(".app/")); if (DotAppIndex != wxNOT_FOUND) { tok = tok.Mid(0, DotAppIndex + 4); // 4 to retain ".app" } } #endif int x, y; dc.GetTextExtent(tok, &x, &y, NULL, NULL, testFont); Words* temp = new Words(); temp->size.SetWidth(x); temp->size.SetHeight(y); temp->word = tok; words.Add(temp); } } } wxlauncher-0.9.4/code/global/Utils.h0000644000000000000000000000223612155177255017302 0ustar rootroot00000000000000/* Copyright (C) 2013 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UTILS_H #define UTILS_H #include #include namespace TextUtils { struct Words { wxString word; wxSize size; }; WX_DECLARE_OBJARRAY(Words, ArrayOfWords); /* Fills an array of words from a token stream. */ void FillArrayOfWordsFromTokens(wxStringTokenizer &tokens, wxDC &dc, wxFont* testFont, ArrayOfWords& words, bool useAppleDebugFilter = false); } #endif wxlauncher-0.9.4/code/global/configure_launcher.h.in0000644000000000000000000000220512155177255022445 0ustar rootroot00000000000000#ifndef CONFIGURE_LAUNCHER_H_ #define CONFIGURE_LAUNCHER_H_ // File is modified by the build system to tell the launcher where stuff // is located #cmakedefine RESOURCES_PATH "${RESOURCES_PATH}" #cmakedefine HELP_HTB_LOCATION "${HELP_HTB_LOCATION}" #cmakedefine01 USE_SPEECH #cmakedefine01 USE_JOYSTICK #cmakedefine01 USE_OPENAL #cmakedefine01 PLATFORM_USES_REGISTRY #cmakedefine01 PLATFORM_HAS_BROKEN_OPENAL #cmakedefine01 PROFILE_DEBUGGING #cmakedefine01 HAS_SDL #cmakedefine VERSION_MAJOR ${VERSION_MAJOR} #cmakedefine VERSION_MINOR ${VERSION_MINOR} #cmakedefine VERSION_PATCH ${VERSION_PATCH} // If cmake did not define these, it means they are zero #ifndef VERSION_MAJOR #define VERSION_MAJOR 0 #endif #ifndef VERSION_MINOR #define VERSION_MINOR 0 #endif #ifndef VERSION_PATCH #define VERSION_PATCH 0 #endif // What platform am I on #cmakedefine01 IS_WIN32 #cmakedefine01 IS_LINUX #cmakedefine01 IS_APPLE #if (IS_WIN32 + IS_LINUX + IS_APPLE) > 1 #error "Only one of IS_WIN32, IS_LINUX, and IS_APPLE may evaluate to true" #elif (IS_WIN32 + IS_LINUX + IS_APPLE) < 1 #error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif #endif wxlauncher-0.9.4/code/global/ids.h0000644000000000000000000000732012155177255016760 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IDS_HPP #define IDS_HPP /* File contains all IDs and other application constants that are to be used in the application. */ #define TAB_AREA_WIDTH (639) //!< the number of pixels wide for the useable area of the tab #define TAB_AREA_HEIGHT (595) //!< the number of pixels high for the useable area of the tab /** The IDs that wxLauncher uses to identify its controls to wxWidgets. These are in no particular order, but they are grouped into the tab or section of the the control that it represents is located on. These IDs are also used by the help manager to open the correct help page for the context help. */ enum WindowIDS { ID_MAINTAB = wxID_HIGHEST + 1, /* wxID_HIGHEST is the maximum event id that wx will use. */ ID_CLOSE_BUTTON, ID_HELP_BUTTON, ID_FRED_BUTTON, ID_UPDATE_BUTTON, ID_PLAY_BUTTON, ID_ABOUT_BUTTON, ID_FS2_PROCESS, ID_FRED2_PROCESS, ID_F3_PRESSED, ID_PROFILE_COMBO, ID_NEW_PROFILE, ID_DELETE_PROFILE, ID_SAVE_PROFILE, ID_SAVE_DEFAULT_CHECK, ID_SUMMARY_HTML_PANEL, ID_NEWS_BOX, ID_NEWS_HTML_PANEL, ID_VIDEO_STATIC_BOX, ID_RESOLUTION_COMBO, ID_DEPTH_COMBO, ID_TEXTURE_FILTER_COMBO, ID_ANISOTROPIC_COMBO, ID_AA_COMBO, // Lighting presets ID_PRESETS_OFF, ID_PRESET_BASELINE, ID_PRESET_DABRAIN, ID_PRESET_HERRA_TOHTORI, ID_PRESET_CKID, ID_PRESET_COLECAMPBELL666, ID_PRESET_CASTOR, ID_PRESET_SPIDEY, ID_PRESET_WOOLIE_WOOL, ID_COPY_PRESET_BUTTON, ID_SPEECH_TEST_TEXT, ID_SPEECH_VOICE_COMBO, ID_SPEECH_VOICE_VOLUME, ID_SPEECH_PLAY_BUTTON, ID_SPEECH_IN_TECHROOM, ID_SPEECH_IN_BRIEFING, ID_SPEECH_IN_GAME, ID_SPEECH_IN_MULTI, ID_SPEECH_MORE_VOICES_BUTTON, ID_NETWORK_TYPE, ID_NETWORK_SPEED, ID_NETWORK_PORT, ID_NETWORK_IP, ID_SELECT_SOUND_DEVICE, ID_SELECT_CAPTURE_DEVICE, ID_ENABLE_EFX, ID_AUDIO_SAMPLE_RATE, ID_DOWNLOAD_OPENAL, ID_DETECT_OPENAL, ID_JOY_SELECTED, ID_JOY_FORCE_FEEDBACK, ID_JOY_DIRECTIONAL_HIT, ID_JOY_CALIBRATE_BUTTON, ID_JOY_DETECT_BUTTON, ID_PROXY_TYPE, ID_PROXY_HTTP_SERVER, ID_PROXY_HTTP_PORT, ID_PROXY_AUTO_URL, ID_MODS_PAGE_INFO_IMAGE, ID_MODS_PAGE_WARNING_IMAGE, ID_MODLISTBOX, ID_MODLISTBOX_ACTIVATE_BUTTON, ID_MODLISTBOX_INFO_BUTTON, ID_STATUSBAR_STATUS_ICON, ID_STATUSBAR_PROGRESS_BAR, ID_EXE_ROOT_FOLDER_BOX_TEXT, ID_EXE_ROOT_FOLDER_BOX, ID_EXE_SELECT_ROOT_BUTTON, ID_EXE_CHOICE_BOX, ID_EXE_CHOICE_REFRESH_BUTTON, ID_EXE_FRED_CHOICE_TEXT, ID_EXE_FRED_CHOICE_BOX, ID_EXE_FRED_CHOICE_REFRESH_BUTTON, ID_CLONE_PROFILE_DIALOG, ID_CLONE_PROFILE_NEWNAME, ID_CLONE_PROFILE_CHECKBOX, ID_DELETE_PROFILE_DIALOG, // Advanced settings page ID_FLAGLISTBOX, ID_SELECT_FLAG_SET, ID_CUSTOM_FLAGS_TEXT, ID_COMMAND_LINE_TEXT, ID_FLAG_SET_NOTES_TEXT, ID_NET_DOWNLOAD_NEWS, ID_EVENT_NET_DOWNLOAD_NEWS, ID_MORE_INFO_PRIVACY, }; enum MainTabImageIDs { ID_TAB_WELCOME_IMAGE, ID_TAB_MOD_IMAGE, ID_TAB_BASIC_SETTINGS_IMAGE, ID_TAB_ADV_SETTINGS_IMAGE, ID_TAB_INSTALL_IMAGE, }; enum StatusBarIconIDs { ID_SB_NO_CHANGE = -1, ID_SB_INFO, ID_SB_OK, ID_SB_WARNING, ID_SB_ERROR, ID_SB_MAX_ID, }; #endif wxlauncher-0.9.4/code/global/targetver.h0000644000000000000000000000412612155177255020205 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once // The following macros define the minimum required platform. The minimum required platform // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run // your application. The macros work by enabling all features available on platform versions up to and // including the version specified. // Modify the following defines if you have to target a platform prior to the ones specified below. // Refer to MSDN for the latest info on corresponding values for different platforms. #ifndef WINVER // Specifies that the minimum required platform is Windows Vista. #define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. #define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. #endif #ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0. #define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE. #endif wxlauncher-0.9.4/code/global/version.cpp0000644000000000000000000000177212155177255020226 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "version.h" #include "generated/configure_launcher.h" const wxUint8 MAJOR_VERSION = VERSION_MAJOR; const wxUint8 MINOR_VERSION = VERSION_MINOR; const wxUint8 PATCH_VERSION = VERSION_PATCH; const wxUint64 FULL_VERSION = VERSION_MAJOR*1000000 + VERSION_MINOR*1000 + VERSION_PATCH;wxlauncher-0.9.4/code/global/version.h0000644000000000000000000000175112155177255017670 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VERSION_H #define VERSION_H #include extern const wxUint8 MAJOR_VERSION; extern const wxUint8 MINOR_VERSION; extern const wxUint8 PATCH_VERSION; extern const wxUint64 FULL_VERSION; extern const wchar_t *HGVersion; extern const wchar_t *HGDate; #endifwxlauncher-0.9.4/code/global/version_strings.cpp.in0000644000000000000000000000017312155177255022376 0ustar rootroot00000000000000const wchar_t *HGVersion = L"123456789abc+ unknown custom tip"; const wchar_t *HGDate = L"Thu Jan 01 00:00:00 1970 -0000"; wxlauncher-0.9.4/code/tabs/AdvSettingsPage.cpp0000644000000000000000000004200512155177255021254 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include "tabs/AdvSettingsPage.h" #include "apis/CmdLineManager.h" #include "apis/FlagListManager.h" #include "apis/ProfileProxy.h" #include "apis/TCManager.h" #include "apis/ProfileManager.h" #include "apis/SkinManager.h" #include "controls/LightingPresets.h" #include "global/ids.h" #include "global/ProfileKeys.h" #include "global/Utils.h" #include #include #include "global/MemoryDebugging.h" // Last include for memory debugging const size_t TOP_SIZER_INDEX = 0; const size_t TOP_LEFT_SIZER_INDEX = 0; const size_t WIKI_LINK_SIZER_INDEX = 1; const size_t TOP_RIGHT_SIZER_INDEX = 1; const size_t BOTTOM_SIZER_INDEX = 1; AdvSettingsPage::AdvSettingsPage(wxWindow* parent): wxPanel(parent, wxID_ANY), flagListBox(NULL) { this->lightingPresets = new LightingPresets(this); this->errorText = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); wxLogDebug(_T("AdvSettingsPage is at %p."), this); CmdLineManager::RegisterCmdLineChanged(this); CmdLineManager::RegisterCustomFlagsChanged(this); FlagListManager::GetFlagListManager()->RegisterFlagFileProcessingStatusChanged(this); TCManager::RegisterTCActiveModChanged(this); ProfileProxy::GetProxy()->RegisterProxyReset(this); ProfileProxy::GetProxy()->RegisterProxyFlagDataReady(this); } BEGIN_EVENT_TABLE(AdvSettingsPage, wxPanel) EVT_COMMAND(wxID_NONE, EVT_PROXY_RESET, AdvSettingsPage::OnExeChanged) EVT_COMMAND(wxID_NONE, EVT_PROXY_FLAG_DATA_READY, AdvSettingsPage::OnProxyFlagDataReady) EVT_COMMAND(wxID_NONE, EVT_TC_ACTIVE_MOD_CHANGED, AdvSettingsPage::OnNeedUpdateCommandLine) EVT_COMMAND(wxID_NONE, EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED, AdvSettingsPage::OnFlagFileProcessingStatusChanged) EVT_COMMAND(wxID_NONE, EVT_CMD_LINE_CHANGED, AdvSettingsPage::OnNeedUpdateCommandLine) EVT_COMMAND(wxID_NONE, EVT_CUSTOM_FLAGS_CHANGED, AdvSettingsPage::OnNeedUpdateCustomFlags) EVT_COMMAND(wxID_NONE, EVT_FLAG_LIST_BOX_READY, AdvSettingsPage::OnFlagListBoxReady) EVT_TEXT(ID_CUSTOM_FLAGS_TEXT, AdvSettingsPage::OnCustomFlagsBoxChanged) EVT_CHOICE(ID_SELECT_FLAG_SET, AdvSettingsPage::OnSelectFlagSet) END_EVENT_TABLE() // FIXME HACK for now, hard-code flag list box height (sigh) #if IS_WIN32 const int FLAG_LIST_BOX_HEIGHT = 410; #elif IS_LINUX const int FLAG_LIST_BOX_HEIGHT = 363; #elif IS_APPLE const int FLAG_LIST_BOX_HEIGHT = 363; #else #error "One of IS_WIN32, IS_LINUX, IS_APPLE must evaluate to true" #endif void AdvSettingsPage::OnExeChanged(wxCommandEvent& event) { if (this->GetSizer() != NULL) { // detach it so it won't be deleted when we clear the sizer this->lightingPresets->GetContainingSizer()->Detach(this->lightingPresets); this->GetSizer()->Clear(true); } // top left components this->flagListBox = new FlagListBox(this); this->flagListBox->RegisterFlagListBoxReady(this); FlagListManager::GetFlagListManager()->BeginFlagFileProcessing(); #if 0 // doesn't do anything wxHtmlWindow* description = new wxHtmlWindow(this); description->SetPage(_T("

")); #endif #if IS_WIN32 wxStaticText* wikiLinkText1 = new wxStaticText(this, wxID_ANY, _T("Double-click on a flag for its online documentation, if available.")); #else wxStaticText* wikiLinkText1 = new wxStaticText(this, wxID_ANY, _T("Double-click on a flag")); wxStaticText* wikiLinkText2 = new wxStaticText(this, wxID_ANY, _T("for its online documentation, if available.")); #endif wxBoxSizer* wikiLinkSizer = new wxBoxSizer(wxVERTICAL); wikiLinkSizer->Add(wikiLinkText1, 0, wxALIGN_CENTER_HORIZONTAL); #if !IS_WIN32 wikiLinkSizer->Add(wikiLinkText2, 0, wxALIGN_CENTER_HORIZONTAL); #endif wxBoxSizer* topLeftSizer = new wxBoxSizer(wxVERTICAL); topLeftSizer->Add(this->flagListBox, wxSizerFlags().Proportion(1).Expand()); topLeftSizer->Add(wikiLinkSizer, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 5); // top right components wxBoxSizer* lightingPresetsSizer = new wxBoxSizer(wxVERTICAL); lightingPresetsSizer->Add(this->lightingPresets, wxSizerFlags().Proportion(1).Expand()); wxStaticText* flagSetChoiceLabel = new wxStaticText(this, wxID_ANY, _T("Flag sets:")); wxChoice* flagSetChoice = new wxChoice(this, ID_SELECT_FLAG_SET); wxBoxSizer* topRightSizer = new wxBoxSizer(wxVERTICAL); topRightSizer->Add(lightingPresetsSizer, wxSizerFlags().Proportion(1).Border(wxBOTTOM, 5)); topRightSizer->Add(flagSetChoiceLabel, wxSizerFlags().Left().Border(wxBOTTOM, 5)); topRightSizer->Add(flagSetChoice, wxSizerFlags().Expand()); // putting the top sizer together wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL); topSizer->Add(topLeftSizer, wxSizerFlags().Proportion(1).Expand()); topSizer->Add(topRightSizer, wxSizerFlags().Expand().Border(wxLEFT, 5)); #if 0 topSizer->Add(description, wxSizerFlags().Proportion(1).Expand()); #endif #if 0 // related to functionality that isn't working yet wxStaticBitmap* idealIcon = new wxStaticBitmap(this, wxID_ANY, this->skin->GetIdealIcon()); wxStaticText* idealLabel = new wxStaticText(this, wxID_ANY, _("= Recommended flag")); wxBoxSizer* idealFlagsRowSizer = new wxBoxSizer(wxHORIZONTAL); idealFlagsRowSizer->Add(idealIcon); wxBoxSizer* idealLabelSizer = new wxBoxSizer(wxVERTICAL); idealLabelSizer->AddStretchSpacer(1); idealLabelSizer->Add(idealLabel); idealLabelSizer->AddStretchSpacer(1); idealFlagsRowSizer->Add(idealLabelSizer); #endif #if 0 // doesn't do anything wxStaticBox* flagsetNotesBox = new wxStaticBox(this, wxID_ANY, _("Flag set notes")); wxTextCtrl* flagsetNotes = new wxTextCtrl(this, ID_FLAG_SET_NOTES_TEXT, wxEmptyString, wxDefaultPosition, wxSize(-1, TAB_AREA_HEIGHT/8), wxTE_MULTILINE|wxTE_READONLY); wxStaticBoxSizer* flagsetNotesSizer = new wxStaticBoxSizer(flagsetNotesBox, wxVERTICAL); flagsetNotesSizer->Add(flagsetNotes, wxSizerFlags().Expand()); #endif wxStaticText* customFlagsTextLabel = new wxStaticText(this, wxID_ANY, _("Custom flags:")); wxTextCtrl* customFlagsText = new wxTextCtrl(this, ID_CUSTOM_FLAGS_TEXT); wxStaticText* commandLineTextLabel = new wxStaticText(this, wxID_ANY, _("Current command line:")); wxTextCtrl* commandLineText = new wxTextCtrl(this, ID_COMMAND_LINE_TEXT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY); wxBoxSizer* bottomSizer = new wxBoxSizer(wxVERTICAL); bottomSizer->Add(customFlagsTextLabel, wxSizerFlags().Left().Border(wxTOP|wxBOTTOM, 5)); bottomSizer->Add(customFlagsText, wxSizerFlags().Expand().Border(wxBOTTOM, 10)); bottomSizer->Add(commandLineTextLabel, wxSizerFlags().Left().Border(wxBOTTOM, 5)); bottomSizer->Add(commandLineText, wxSizerFlags().Proportion(1).Expand()); // final layout wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(topSizer, wxSizerFlags().Expand().Border(wxALL, 5)); #if 0 sizer->Add(idealFlagsRowSizer, wxSizerFlags().Expand().Border(wxALL, 5)); sizer->Add(flagsetNotesSizer, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); #endif sizer->Add(bottomSizer, wxSizerFlags().Expand().Proportion(1).Border(wxALL, 5)); this->SetSizer(sizer); this->Layout(); if (!ProfileProxy::GetProxy()->IsProfileInitialized()) { ProfileProxy::GetProxy()->FinishProfileInitialization(); } } void AdvSettingsPage::OnFlagFileProcessingStatusChanged(wxCommandEvent &event) { wxASSERT(event.GetEventType() == EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED); const FlagListManager::FlagFileProcessingStatus status = static_cast(event.GetInt()); // Ignore an event with reset status, since it's meant for the proxy if (status == FlagListManager::FLAG_FILE_PROCESSING_RESET) { return; } this->UpdateComponents(); if (status == FlagListManager::FLAG_FILE_PROCESSING_OK) { FlagFileData* flagData = FlagListManager::GetFlagListManager()->GetFlagFileData(); wxCHECK_RET(flagData != NULL, _T("Flag file processing succeeded but could not retrieve extracted data.")); this->flagListBox->AcceptFlagData(flagData); } else { this->UpdateErrorText(); } } void AdvSettingsPage::OnNeedUpdateCustomFlags(wxCommandEvent &event) { wxASSERT((this->flagListBox != NULL) && this->flagListBox->IsReady()); wxASSERT(ProfileProxy::GetProxy()->IsFlagDataReady()); wxTextCtrl* customFlagsText = dynamic_cast( wxWindow::FindWindowById(ID_CUSTOM_FLAGS_TEXT, this)); wxCHECK_RET(customFlagsText != NULL, _T("Unable to find the custom flags text ctrl")); customFlagsText->ChangeValue(ProfileProxy::GetProxy()->GetCustomFlags()); } void AdvSettingsPage::OnFlagListBoxReady(wxCommandEvent &WXUNUSED(event)) { wxASSERT((this->flagListBox != NULL) && this->flagListBox->IsReady()); if (ProfileProxy::GetProxy()->IsFlagDataReady() && !this->flagListBox->FlagsLoaded()) { this->flagListBox->LoadEnabledFlags(); CmdLineManager::GenerateCustomFlagsChanged(); CmdLineManager::GenerateCmdLineChanged(); } this->UpdateComponents(); this->UpdateFlagSetsBox(); } void AdvSettingsPage::OnProxyFlagDataReady(wxCommandEvent& event) { wxASSERT((this->flagListBox != NULL) && ProfileProxy::GetProxy()->IsFlagDataReady()); if (this->flagListBox->IsReady() && !this->flagListBox->FlagsLoaded()) { this->flagListBox->LoadEnabledFlags(); CmdLineManager::GenerateCustomFlagsChanged(); CmdLineManager::GenerateCmdLineChanged(); } } void AdvSettingsPage::UpdateComponents() { wxSizer* topSizer = this->GetSizer()->GetItem(TOP_SIZER_INDEX)->GetSizer(); wxCHECK_RET(topSizer != NULL, _T("cannot find the top sizer")); wxSizer* topLeftSizer = topSizer->GetItem(TOP_LEFT_SIZER_INDEX)->GetSizer(); wxCHECK_RET(topLeftSizer != NULL, _T("cannot find the top left sizer")); wxCHECK_RET(this->flagListBox != NULL, _T("UpdateComponents() called when flagListBox was null.")); if (this->flagListBox->IsReady()) { topSizer->Show(TOP_RIGHT_SIZER_INDEX); topLeftSizer->Show(WIKI_LINK_SIZER_INDEX); this->GetSizer()->Show(BOTTOM_SIZER_INDEX); this->flagListBox->Show(); this->errorText->Hide(); this->Layout(); } else { topSizer->Hide(TOP_RIGHT_SIZER_INDEX); topLeftSizer->Hide(WIKI_LINK_SIZER_INDEX); this->GetSizer()->Hide(BOTTOM_SIZER_INDEX); this->flagListBox->Hide(); this->errorText->Show(); this->Layout(); } } void AdvSettingsPage::UpdateErrorText() { wxCHECK_RET(!FlagListManager::GetFlagListManager()->IsProcessingOK(), _T("UpdateErrorText() called when processing succeeded.")); wxRect rect(0, 0, this->GetSize().x, this->GetSize().y); wxString msg = FlagListManager::GetFlagListManager()->GetStatusMessage(); wxASSERT(!msg.IsEmpty()); this->errorText->SetLabel(msg); this->errorText->SetFont(SkinSystem::GetSkinSystem()->GetMessageFont()); this->errorText->SetSize(rect, wxSIZE_FORCE); this->errorText->Wrap(rect.width - 225); // to match mods page this->errorText->Center(); } void AdvSettingsPage::OnCustomFlagsBoxChanged(wxCommandEvent &WXUNUSED(event)) { wxASSERT(this->flagListBox != NULL && this->flagListBox->IsReady()); wxASSERT(ProfileProxy::GetProxy()->IsFlagDataReady()); wxTextCtrl* customFlagsText = dynamic_cast( wxWindow::FindWindowById(ID_CUSTOM_FLAGS_TEXT, this)); wxCHECK_RET(customFlagsText != NULL, _T("Unable to find the custom flags text ctrl")); ProfileProxy::GetProxy()->SetCustomFlags(customFlagsText->GetValue(), false); } void AdvSettingsPage::UpdateFlagSetsBox() { wxASSERT(this->flagListBox != NULL); wxASSERT(this->flagListBox->IsReady()); wxChoice *flagSetChoice = dynamic_cast( wxWindow::FindWindowById(ID_SELECT_FLAG_SET, this)); wxCHECK_RET(flagSetChoice != NULL, _T("Unable to find the flagset choice control")); // TODO rethink the following assertion when new mod.ini is supported // the assertion also assumes that the flag sets box is never reused wxASSERT(flagSetChoice->IsEmpty()); // shouldn't add sets more than once wxArrayString flagSetsArray; this->flagListBox->GetFlagSets(flagSetsArray); // before populating the flag set choice box, let's remove the flag sets // that don't make sense and can thus potentially confuse users flagSetsArray.Remove(_T("Custom")); flagSetChoice->Append(flagSetsArray); wxClientDC dc(this); wxArrayString flagSets = flagSetChoice->GetStrings(); wxFont font(this->GetFont()); int maxStringWidth = 0; int x, y; for (int i = 0, n = flagSets.GetCount(); i < n; ++i) { dc.GetTextExtent(flagSets[i], &x, &y, NULL, NULL, &font); if (x > maxStringWidth) { maxStringWidth = x; } } flagSetChoice->SetMinSize( wxSize(maxStringWidth + 40, // 40 to include drop down box control flagSetChoice->GetSize().GetHeight())); this->Layout(); } void AdvSettingsPage::OnNeedUpdateCommandLine(wxCommandEvent &WXUNUSED(event)) { if ( (this->flagListBox == NULL) || !this->flagListBox->IsReady() ) { // The control I need to update does not exist, do nothing return; } wxTextCtrl* commandLine = dynamic_cast( wxWindow::FindWindowById(ID_COMMAND_LINE_TEXT, this)); wxCHECK_RET( commandLine != NULL, _T("Unable to find the command line view text control") ); wxString tcPath, exeName, modline; wxCHECK_RET( ProMan::GetProfileManager()->ProfileRead( PRO_CFG_TC_ROOT_FOLDER, &tcPath), _T("Could not find profile entry for root folder.")); wxCHECK_RET( ProMan::GetProfileManager()->ProfileRead( PRO_CFG_TC_CURRENT_BINARY, &exeName), _T("Could not find profile entry for FSO binary.")); wxCHECK_RET( ProMan::GetProfileManager()->ProfileRead( PRO_CFG_TC_CURRENT_MODLINE, &modline), _T("Could not find profile entry for mod line.")); wxString flagFileFlags(ProfileProxy::GetProxy()->GetEnabledFlagsString()); wxString lightingPresetString; if (ProfileProxy::GetProxy()->HasLightingPreset()) { lightingPresetString = LightingPresets::PresetNameToPresetFlagSet( ProfileProxy::GetProxy()->GetLightingPresetName()); } wxString customFlags(ProfileProxy::GetProxy()->GetCustomFlags()); wxString cmdLine = wxString::Format(_T("%s%c%s%s%s%s%s"), tcPath.c_str(), wxFileName::GetPathSeparator(), exeName.c_str(), (modline.IsEmpty() ? wxEmptyString : wxString::Format(_T(" -mod %s"), modline.c_str()).c_str()), (flagFileFlags.IsEmpty() ? wxEmptyString : wxString::Format(_T(" %s"), flagFileFlags.c_str()).c_str()), (lightingPresetString.IsEmpty() ? wxEmptyString : wxString::Format(_T(" %s"), lightingPresetString.c_str()).c_str()), (customFlags.IsEmpty() ? wxEmptyString : wxString::Format(_T(" %s"), customFlags.c_str()).c_str())); commandLine->ChangeValue(FormatCommandLineString(cmdLine, commandLine->GetSize().GetWidth() - 30)); // 30 for scrollbar } wxString AdvSettingsPage::FormatCommandLineString(const wxString& origCmdLine, const int textAreaWidth) { // inspired by ModItem::InfoText::Draw() wxStringTokenizer tokens(origCmdLine); TextUtils::ArrayOfWords words; words.Alloc(tokens.CountTokens()); wxClientDC dc(this); wxFont font(this->GetFont()); FillArrayOfWordsFromTokens(tokens, dc, &font, words, true); const int spaceWidth = dc.GetTextExtent(_T(" ")).GetWidth(); int currentWidth = 0; wxString formattedCmdLine; wxString currentLine; bool spaceAdded = false; for (size_t i = 0, n = words.Count(); i < n; i++) { if (words[i].word.IsEmpty()) { // skip over remnants of words eliminated in tokens-to-words conversion continue; } else if (currentWidth + words[i].size.x + spaceWidth > textAreaWidth) { // prevents trailing newline in cmdLineString (nitpicky, I know) formattedCmdLine += wxString::Format(formattedCmdLine.IsEmpty() ? _T("%s") : _T("\n%s"), currentLine.c_str()); currentLine.Empty(); currentWidth = 0; } else { if (!currentLine.IsEmpty()) { currentLine.append(_T(" ")); spaceAdded = true; } } currentLine.append(words[i].word); currentWidth += words[i].size.x + (spaceAdded ? spaceWidth : 0); spaceAdded = false; } if (!currentLine.IsEmpty()) { formattedCmdLine += wxString::Format(formattedCmdLine.IsEmpty() ? _T("%s") : _T("\n%s"), currentLine.c_str()); } return formattedCmdLine; } void AdvSettingsPage::OnSelectFlagSet(wxCommandEvent &WXUNUSED(event)) { wxChoice* choice = dynamic_cast( wxWindow::FindWindowById(ID_SELECT_FLAG_SET, this)); wxCHECK_RET( choice != NULL, _T("Unable to get flagSetChoice box")); wxString selectedSet = choice->GetString(choice->GetSelection()); bool ret = this->flagListBox->SetFlagSet(selectedSet); if ( ret == false ) { wxLogError(_T("Unable to set flag set (%s). Set does not exist"), selectedSet.c_str()); } } wxlauncher-0.9.4/code/tabs/AdvSettingsPage.h0000644000000000000000000000323612155177255020724 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ADVSETTINGSPAGE_H #define ADVSETTINGSPAGE_H #include #include "controls/FlagListBox.h" #include "controls/LightingPresets.h" class AdvSettingsPage: public wxPanel { public: AdvSettingsPage(wxWindow* parent); void OnNeedUpdateCommandLine(wxCommandEvent &event); private: void UpdateComponents(); void UpdateErrorText(); void UpdateFlagSetsBox(); wxString FormatCommandLineString(const wxString& origCmdLine, const int textAreaWidth); FlagListBox* flagListBox; LightingPresets* lightingPresets; wxStaticText* errorText; public: void OnExeChanged(wxCommandEvent& event); void OnSelectFlagSet(wxCommandEvent& event); void OnFlagFileProcessingStatusChanged(wxCommandEvent& event); void OnNeedUpdateCustomFlags(wxCommandEvent& event); void OnCustomFlagsBoxChanged(wxCommandEvent& event); void OnFlagListBoxReady(wxCommandEvent& event); void OnProxyFlagDataReady(wxCommandEvent& event); DECLARE_EVENT_TABLE() }; #endifwxlauncher-0.9.4/code/tabs/BasicSettingsPage.cpp0000644000000000000000000026515212155177255021575 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "generated/configure_launcher.h" #if HAS_SDL == 1 #include "SDL.h" #endif #include "tabs/BasicSettingsPage.h" #include "global/BasicDefaults.h" #include "global/ids.h" #include "global/ProfileKeys.h" #include "apis/FlagListManager.h" #include "apis/FREDManager.h" #include "apis/ProfileManager.h" #include "apis/TCManager.h" #include "apis/SpeechManager.h" #include "apis/OpenALManager.h" #include "apis/JoystickManager.h" #include "apis/HelpManager.h" #include "controls/ModList.h" #include "datastructures/FSOExecutable.h" #include "datastructures/ResolutionMap.h" #include "global/MemoryDebugging.h" // Last include for memory debugging /** A mechanism for allowing a network settings option's description (GUI label) to differ from its corresponding registry value. */ class NetworkSettingsOption { public: NetworkSettingsOption(const wxString& regValue, const wxString& guiDesc); const wxString& GetRegistryValue() const { return this->regValue; } const wxString& GetDescription() const { return this->guiDesc; } private: NetworkSettingsOption(); wxString regValue; wxString guiDesc; }; typedef std::vector NetworkSettingsOptions; NetworkSettingsOptions networkTypeOptions; NetworkSettingsOptions networkSpeedOptions; NetworkSettingsOption::NetworkSettingsOption( const wxString& regValue, const wxString& guiDesc) : regValue(regValue), guiDesc(guiDesc) { wxASSERT(!regValue.IsEmpty()); wxASSERT(!guiDesc.IsEmpty()); } void InitializeNetworkOptions() { wxASSERT(networkTypeOptions.empty()); wxASSERT(networkSpeedOptions.empty()); networkTypeOptions.push_back(NetworkSettingsOption(_T("None"), _T("None"))); networkTypeOptions.push_back(NetworkSettingsOption(_T("Dialup"), _T("Dialup"))); networkTypeOptions.push_back(NetworkSettingsOption(_T("LAN"), _T("Broadband/LAN"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("None"), _T("None"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("Slow"), _T("28k modem"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("56K"), _T("56k modem"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("ISDN"), _T("ISDN"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("Cable"), _T("DSL"))); networkSpeedOptions.push_back(NetworkSettingsOption(_T("Fast"), _T("Cable/LAN"))); } int FindOptionIndexWithRegistryValue( const NetworkSettingsOptions &options, const wxString& regValue) { wxCHECK_MSG(!options.empty(), -1, _T("FindOptionIndexGivenRegistryValue(): passed in options is empty.")); wxCHECK_MSG(!regValue.IsEmpty(), -1, _T("FindOptionIndexGivenRegistryValue(): passed in registry value is empty.")); for (int i = 0, n = options.size(); i < n; ++i) { if (options[i].GetRegistryValue() == regValue) { return i; } } return -1; // not found } /** The index in the basic settings page's sizer where the settings sizer is located. */ const size_t SETTINGS_SIZER_INDEX = 1; class ProxyChoice: public wxChoicebook { public: ProxyChoice(wxWindow *parent, wxWindowID id); virtual ~ProxyChoice(); void OnChangeServer(wxCommandEvent &event); void OnChangePort(wxCommandEvent &event); void OnProxyTypeChange(wxChoicebookEvent &event); private: DECLARE_EVENT_TABLE(); }; class ExeChoice: public wxChoice { public: ExeChoice(wxWindow * parent, wxWindowID id) : wxChoice(parent, id) {} bool FindAndSetSelectionWithClientData(wxString item) { size_t number = this->GetStrings().size(); for( size_t i = 0; i < number; i++ ) { FSOExecutable* data = dynamic_cast(this->GetClientObject(i)); wxCHECK2_MSG( data != NULL, continue, _T("Client data is not a FSOVersion pointer")); if ( data->GetExecutableName() == item ) { this->SetSelection(i); return true; } } return false; } }; BasicSettingsPage::BasicSettingsPage(wxWindow* parent): wxPanel(parent, wxID_ANY) { wxLogDebug(_T("BasicSettingsPage is at %p."), this); this->InitializeMemberVariables(); if (networkTypeOptions.empty() || networkSpeedOptions.empty()) { InitializeNetworkOptions(); } TCManager::Initialize(); TCManager::RegisterTCChanged(this); TCManager::RegisterTCActiveModChanged(this); TCManager::RegisterTCBinaryChanged(this); TCManager::RegisterTCFredBinaryChanged(this); ProMan::GetProfileManager()->AddEventHandler(this); FlagListManager::GetFlagListManager()->RegisterFlagFileProcessingStatusChanged(this); FREDManager::RegisterFREDEnabledChanged(this); wxCommandEvent event(this->GetId()); this->ProfileChanged(event); } void BasicSettingsPage::ProfileChanged(wxCommandEvent &WXUNUSED(event)) { if (this->GetSizer() != NULL) { this->GetSizer()->Clear(true); } this->InitializeMemberVariables(); ProMan* proman = ProMan::GetProfileManager(); // exe Selection wxStaticBox* exeBox = new wxStaticBox(this, wxID_ANY, _("FS2 Open game root folder and executable")); wxStaticText* rootFolderText = new wxStaticText(this, ID_EXE_ROOT_FOLDER_BOX_TEXT, _("Game root folder:")); wxTextCtrl* rootFolderBox = new wxTextCtrl(this, ID_EXE_ROOT_FOLDER_BOX); wxButton* selectButton = new wxButton(this, ID_EXE_SELECT_ROOT_BUTTON, _T("Browse...")); rootFolderBox->SetEditable(false); wxStaticText* useExeText = new wxStaticText(this, wxID_ANY, _("FS2 Open executable:")); ExeChoice* useExeChoice = new ExeChoice(this, ID_EXE_CHOICE_BOX); wxButton* exeChoiceRefreshButton = new wxButton(this, ID_EXE_CHOICE_REFRESH_BUTTON, _("Refresh")); wxStaticText* useFredText = new wxStaticText(this, ID_EXE_FRED_CHOICE_TEXT, _("FRED2 Open executable:")); ExeChoice* useFredChoice = new ExeChoice(this, ID_EXE_FRED_CHOICE_BOX); wxButton* exeFredChoiceRefreshButton = new wxButton(this, ID_EXE_FRED_CHOICE_REFRESH_BUTTON, _("Refresh")); wxCommandEvent nullEvent; OnFREDEnabledChanged(nullEvent); // new sizer layout that should line things up nicely // inspired by the thread http://markmail.org/message/rlgv6y6xbw5dkvyy#query:+page:1+mid:5cqagz2jbygwqt2x+state:results // or "RE: [wxPython-users] wx.FlexGridSizer..." Mar 31, 2005 in com.googlegroups.wxpython-users // this idea could also work on, say, the video box, if you needed, for Windows wxFlexGridSizer* exeInsideSizer = new wxFlexGridSizer(3,3,0,0); exeInsideSizer->AddGrowableCol(1); exeInsideSizer->Add(rootFolderText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); exeInsideSizer->Add(rootFolderBox, wxSizerFlags().Proportion(1).Expand()); exeInsideSizer->Add(selectButton, wxSizerFlags().Expand().Border(wxLEFT, 5)); exeInsideSizer->Add(useExeText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); exeInsideSizer->Add(useExeChoice, wxSizerFlags().Proportion(1).Expand()); exeInsideSizer->Add(exeChoiceRefreshButton, wxSizerFlags().Expand().Border(wxLEFT, 5)); exeInsideSizer->Add(useFredText, 0, wxALIGN_CENTER_VERTICAL|wxRESERVE_SPACE_EVEN_IF_HIDDEN|wxRIGHT, 5); exeInsideSizer->Add(useFredChoice, wxSizerFlags().Proportion(1).Expand().ReserveSpaceEvenIfHidden()); exeInsideSizer->Add(exeFredChoiceRefreshButton, wxSizerFlags().Expand().ReserveSpaceEvenIfHidden().Border(wxLEFT, 5)); wxStaticBoxSizer* exeSizer = new wxStaticBoxSizer(exeBox, wxHORIZONTAL); exeSizer->Add(exeInsideSizer, wxSizerFlags().Proportion(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); // Video Section wxStaticBox* videoBox = new wxStaticBox(this, ID_VIDEO_STATIC_BOX, _("Video")); wxStaticText* resolutionText = new wxStaticText(this, wxID_ANY, _("Resolution:")); wxChoice* resolutionCombo = new wxChoice(this, ID_RESOLUTION_COMBO); SetUpResolution(); wxStaticText* depthText = new wxStaticText(this, wxID_ANY, _("Depth:")); wxChoice* depthCombo = new wxChoice(this, ID_DEPTH_COMBO); long bitDepth; depthCombo->Append(_("16-bit")); depthCombo->Append(_("32-bit")); proman->ProfileRead(PRO_CFG_VIDEO_BIT_DEPTH, &bitDepth, DEFAULT_VIDEO_BIT_DEPTH, true); depthCombo->SetSelection((bitDepth == 16) ? 0 : 1); wxStaticText* textureFilterText = new wxStaticText(this, wxID_ANY, _("Texture filter:")); wxChoice* textureFilterCombo = new wxChoice(this, ID_TEXTURE_FILTER_COMBO); wxString filter; textureFilterCombo->Append(_("Bilinear")); textureFilterCombo->Append(_("Trilinear")); proman->ProfileRead(PRO_CFG_VIDEO_TEXTURE_FILTER, &filter, DEFAULT_VIDEO_TEXTURE_FILTER, true); // FIXME shouldn't need case folding. comparison should be case-sensitive: // either Bilinear or Trilinear. // although now we've created legacy texture filter values. hmm. filter.MakeLower(); textureFilterCombo->SetSelection( (filter == _T("bilinear")) ? 0 : 1); #if !IS_WIN32 // AF/AA don't yet work on Windows wxStaticText* anisotropicText = new wxStaticText(this, wxID_ANY, _("Anisotropic:")); wxChoice* anisotropicCombo = new wxChoice(this, ID_ANISOTROPIC_COMBO); long anisotropic; anisotropicCombo->Append(_("Off")); anisotropicCombo->Append(_T(" 1x")); anisotropicCombo->Append(_T(" 2x")); anisotropicCombo->Append(_T(" 4x")); anisotropicCombo->Append(_T(" 8x")); anisotropicCombo->Append(_T("16x")); proman->ProfileRead(PRO_CFG_VIDEO_ANISOTROPIC, &anisotropic, DEFAULT_VIDEO_ANISOTROPIC, true); switch(anisotropic) { case 0: anisotropic = 0; break; case 1: anisotropic = 1; break; case 2: anisotropic = 2; break; case 4: anisotropic = 3; break; case 8: anisotropic = 4; break; case 16: anisotropic = 5; break; default: wxLogWarning(_T("invalid anisotropic factor %ld, setting to 0"), anisotropic); proman->ProfileWrite(PRO_CFG_VIDEO_ANISOTROPIC, static_cast(0)); anisotropic = 0; } anisotropicCombo->SetSelection(anisotropic); wxStaticText* aaText = new wxStaticText(this, wxID_ANY, _("Anti-aliasing:")); wxChoice* aaCombo = new wxChoice(this, ID_AA_COMBO); long antialias; aaCombo->Append(_("Off")); aaCombo->Append(_T(" 2x")); aaCombo->Append(_T(" 4x")); aaCombo->Append(_T(" 8x")); aaCombo->Append(_T("16x")); proman->ProfileRead(PRO_CFG_VIDEO_ANTI_ALIAS, &antialias, DEFAULT_VIDEO_ANTI_ALIAS, true); switch(antialias) { case 0: antialias = 0; break; case 2: antialias = 1; break; case 4: antialias = 2; break; case 8: antialias = 3; break; case 16: antialias = 4; break; default: wxLogWarning(_T("invalid anti-aliasing factor %ld, setting to 0"), antialias); proman->ProfileWrite(PRO_CFG_VIDEO_ANTI_ALIAS, static_cast(0)); antialias = 0; } aaCombo->SetSelection(antialias); #endif // Sizer for graphics, resolution, depth, etc wxGridSizer* videoSizerL = new wxFlexGridSizer(2); videoSizerL->Add(resolutionText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); videoSizerL->Add(resolutionCombo, wxSizerFlags().Expand()); videoSizerL->Add(depthText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); videoSizerL->Add(depthCombo, wxSizerFlags().Expand()); wxGridSizer* videoSizerR = new wxFlexGridSizer(2); videoSizerR->Add(textureFilterText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); videoSizerR->Add(textureFilterCombo, wxSizerFlags().Expand()); #if !IS_WIN32 // AF/AA don't yet work on Windows videoSizerR->Add(anisotropicText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); videoSizerR->Add(anisotropicCombo, wxSizerFlags().Expand()); videoSizerR->Add(aaText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); videoSizerR->Add(aaCombo, wxSizerFlags().Expand()); #endif wxStaticBoxSizer* videoSizer = new wxStaticBoxSizer(videoBox, wxHORIZONTAL); #if IS_WIN32 videoSizer->Add(videoSizerL, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxBOTTOM, 5); videoSizer->Add(videoSizerR, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5); #else videoSizer->Add(videoSizerL, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxBOTTOM, 5); videoSizer->AddStretchSpacer(5); videoSizer->Add(videoSizerR, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); videoSizer->AddStretchSpacer(5); #endif #if IS_WIN32 // Speech wxStaticBox* speechBox = new wxStaticBox(this, wxID_ANY, _("Speech")); wxTextCtrl* speechTestText = new wxTextCtrl(this, ID_SPEECH_TEST_TEXT, _("Press play to test this string."), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); wxChoice* speechVoiceCombo = new wxChoice(this, ID_SPEECH_VOICE_COMBO); wxStaticText* speechVolumeLabel = new wxStaticText(this, wxID_ANY, _T("Volume")); wxSlider* speechVoiceVolume = new wxSlider(this, ID_SPEECH_VOICE_VOLUME, 50, 0, 100); wxButton* speechPlayButton = new wxButton(this, ID_SPEECH_PLAY_BUTTON, _("Play")); wxStaticText* speechUseInText = new wxStaticText(this, wxID_ANY, _("Use simulated speech in:")); wxCheckBox* speechInTechroomCheck = new wxCheckBox(this, ID_SPEECH_IN_TECHROOM, _("Tech room")); wxCheckBox* speechInBriefingCheck = new wxCheckBox(this, ID_SPEECH_IN_BRIEFING, _("Briefings")); wxCheckBox* speechInGameCheck = new wxCheckBox(this, ID_SPEECH_IN_GAME, _("In-game")); wxCheckBox* speechInMultiCheck= new wxCheckBox(this, ID_SPEECH_IN_MULTI, _("Multiplayer")); wxButton* speechMoreVoicesButton = new wxButton(this, ID_SPEECH_MORE_VOICES_BUTTON, _("Get more voices")); wxGridBagSizer* speechLeftSizer = new wxGridBagSizer(); speechLeftSizer->Add(speechVoiceCombo, wxGBPosition(0,0), wxGBSpan(1,1), wxEXPAND|wxRIGHT, 10); speechLeftSizer->Add(speechMoreVoicesButton, wxGBPosition(0,2), wxGBSpan(1,1), wxEXPAND); speechLeftSizer->Add(speechTestText, wxGBPosition(1,0), wxGBSpan(2,3), wxEXPAND|wxTOP|wxBOTTOM, 5); speechLeftSizer->Add(speechPlayButton, wxGBPosition(3,0), wxGBSpan(1,1), wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 10); speechLeftSizer->Add(speechVolumeLabel, wxGBPosition(3,1), wxGBSpan(1,1), wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP, 5); speechLeftSizer->Add(speechVoiceVolume, wxGBPosition(3,2), wxGBSpan(1,1), wxEXPAND); wxBoxSizer* speechRightSizer = new wxBoxSizer(wxVERTICAL); speechRightSizer->Add(speechUseInText, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); speechRightSizer->Add(speechInTechroomCheck, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); speechRightSizer->Add(speechInBriefingCheck, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); speechRightSizer->Add(speechInGameCheck, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); speechRightSizer->Add(speechInMultiCheck, wxSizerFlags().Expand()); wxStaticBoxSizer* speechSizer = new wxStaticBoxSizer(speechBox, wxHORIZONTAL); speechSizer->Add(speechLeftSizer, wxSizerFlags().Expand().Border(wxLEFT|wxBOTTOM, 5)); speechSizer->AddStretchSpacer(3); speechSizer->Add(speechRightSizer, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); speechSizer->AddStretchSpacer(2); if ( SpeechMan::WasBuiltIn() && SpeechMan::Initialize() ) { speechVoiceCombo->Append(SpeechMan::EnumVoices()); // FIXME consolidate this code and similar code in AdvSettingsPage.cpp wxClientDC dc(this); wxArrayString voices = speechVoiceCombo->GetStrings(); wxFont font(this->GetFont()); int maxStringWidth = 0; int x, y; for (int i = 0, n = voices.GetCount(); i < n; ++i) { dc.GetTextExtent(voices[i], &x, &y, NULL, NULL, &font); if (x > maxStringWidth) { maxStringWidth = x; } } speechVoiceCombo->SetMinSize(wxSize(maxStringWidth + 40, // 40 to include drop down box control speechVoiceCombo->GetSize().GetHeight())); this->Layout(); long speechVoice; int speechSystemVoice = SpeechMan::GetVoice(); if ( speechSystemVoice < 0 ) { wxLogWarning(_T("Had problem retrieving the system voice, using voice %d"), DEFAULT_SPEECH_VOICE); speechSystemVoice = DEFAULT_SPEECH_VOICE; } // set the voice to what is in the profile, if not set in profile use // system settings proman->ProfileRead(PRO_CFG_SPEECH_VOICE, &speechVoice, speechSystemVoice, true); // there should not be more than MAX_INT voices installed on a system so // the cast of an unsigned int to a signed int should not result in a // loss of data. if ( speechVoice >= static_cast(speechVoiceCombo->GetCount()) ) { wxLogWarning(_T("Profile speech voice index out of range,") _T(" setting to system default")); speechVoice = speechSystemVoice; } speechVoiceCombo->SetSelection(speechVoice); long speechVolume; int speechSystemVolume = SpeechMan::GetVolume(); if (speechSystemVolume < 0) { wxLogWarning(_T("Had problem in retrieving the system speech volume,") _T(" setting to %d"), DEFAULT_SPEECH_VOLUME); speechSystemVolume = DEFAULT_SPEECH_VOLUME; } proman->ProfileRead(PRO_CFG_SPEECH_VOLUME, &speechVolume, speechSystemVolume, true); if ( speechVolume < 0 || speechVolume > 100 ) { wxLogWarning(_T("Speech Volume recorded in profile is out of range,") _T(" resetting to %d"), DEFAULT_SPEECH_VOLUME); speechVolume = DEFAULT_SPEECH_VOLUME; } speechVoiceVolume->SetValue(speechVolume); bool speechInTechroom; proman->ProfileRead( PRO_CFG_SPEECH_IN_TECHROOM, &speechInTechroom, DEFAULT_SPEECH_IN_TECHROOM, true); speechInTechroomCheck->SetValue(speechInTechroom); bool speechInBriefings; proman->ProfileRead( PRO_CFG_SPEECH_IN_BRIEFINGS, &speechInBriefings, DEFAULT_SPEECH_IN_BRIEFINGS, true); speechInBriefingCheck->SetValue(speechInBriefings); bool speechInGame; proman->ProfileRead( PRO_CFG_SPEECH_IN_GAME, &speechInGame, DEFAULT_SPEECH_IN_GAME, true); speechInGameCheck->SetValue(speechInGame); bool speechInMulti; proman->ProfileRead( PRO_CFG_SPEECH_IN_MULTI, &speechInMulti, DEFAULT_SPEECH_IN_MULTI, true); speechInMultiCheck->SetValue(speechInMulti); } else { speechBox->Disable(); speechTestText->Disable(); speechVoiceCombo->Disable(); speechVoiceVolume->Disable(); speechPlayButton->Disable(); speechUseInText->Disable(); speechInTechroomCheck->Disable(); speechInBriefingCheck->Disable(); speechInGameCheck->Disable(); speechInMultiCheck->Disable(); speechMoreVoicesButton->Disable(); } #endif // Network wxStaticBox* networkBox = new wxStaticBox(this, wxID_ANY, _("Network")); wxChoice* networkType = new wxChoice(this, ID_NETWORK_TYPE); for (NetworkSettingsOptions::const_iterator it = networkTypeOptions.begin(), end = networkTypeOptions.end(); it != end; ++it) { networkType->Append(it->GetDescription()); } wxString type; proman->ProfileRead(PRO_CFG_NETWORK_TYPE, &type, DEFAULT_NETWORK_TYPE, true); int networkTypeSelection = FindOptionIndexWithRegistryValue(networkTypeOptions, type); if (networkTypeSelection < 0) { wxLogError( _T("Profile value '%s' was not found in list of type options. ") _T("Using default '%s'."), type.c_str(), DEFAULT_NETWORK_TYPE.c_str()); networkTypeSelection = FindOptionIndexWithRegistryValue(networkTypeOptions, DEFAULT_NETWORK_TYPE); if (networkTypeSelection < 0) { wxLogError( _T("Default value '%s' was not found in list of type options. ") _T("Using first entry '%s'."), DEFAULT_NETWORK_TYPE.c_str(), networkTypeOptions[0].GetRegistryValue().c_str()); networkTypeSelection = 0; } } networkType->SetSelection(networkTypeSelection); wxChoice* networkSpeed = new wxChoice(this, ID_NETWORK_SPEED); for (NetworkSettingsOptions::const_iterator it = networkSpeedOptions.begin(), end = networkSpeedOptions.end(); it != end; ++it) { networkSpeed->Append(it->GetDescription()); } wxString speed; proman->ProfileRead(PRO_CFG_NETWORK_SPEED, &speed, DEFAULT_NETWORK_SPEED, true); int networkSpeedSelection = FindOptionIndexWithRegistryValue(networkSpeedOptions, speed); if (networkSpeedSelection < 0) { wxLogError( _T("Profile value '%s' was not found in list of speed options. ") _T("Using default '%s'."), speed.c_str(), DEFAULT_NETWORK_SPEED.c_str()); networkSpeedSelection = FindOptionIndexWithRegistryValue(networkSpeedOptions, DEFAULT_NETWORK_SPEED); if (networkSpeedSelection < 0) { wxLogError( _T("Default value '%s' was not found in list of speed options. ") _T("Using first entry '%s'."), DEFAULT_NETWORK_SPEED.c_str(), networkSpeedOptions[0].GetRegistryValue().c_str()); networkSpeedSelection = 0; } } networkSpeed->SetSelection(networkSpeedSelection); wxTextCtrl* networkPort = new wxTextCtrl(this, ID_NETWORK_PORT, wxEmptyString); long port; proman->ProfileRead(PRO_CFG_NETWORK_PORT, &port, DEFAULT_NETWORK_PORT, true); if (port != DEFAULT_NETWORK_PORT) { networkPort->ChangeValue(wxString::Format(_T("%ld"), port)); } networkPort->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); networkPort->SetMaxLength(5); wxTextCtrl* networkIP = new wxTextCtrl(this, ID_NETWORK_IP, wxEmptyString); wxString ip; proman->ProfileRead(PRO_CFG_NETWORK_IP, &ip, DEFAULT_NETWORK_IP, true); networkIP->ChangeValue(ip); networkIP->SetMaxLength(15); // for ###.###.###.### wxGridSizer* networkInsideSizerL = new wxFlexGridSizer(2); networkInsideSizerL->Add(new wxStaticText(this, wxID_ANY, _("Connection type:")), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); networkInsideSizerL->Add(networkType, wxSizerFlags().Expand()); networkInsideSizerL->Add(new wxStaticText(this, wxID_ANY, _("Connection speed:")), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); networkInsideSizerL->Add(networkSpeed, wxSizerFlags().Expand()); wxGridSizer* networkInsideSizerR = new wxFlexGridSizer(2); networkInsideSizerR->Add(new wxStaticText(this, wxID_ANY, _("Force local port:")), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); networkInsideSizerR->Add(networkPort, wxSizerFlags().Expand()); networkInsideSizerR->Add(new wxStaticText(this, wxID_ANY, _("Force IP address:")), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); networkInsideSizerR->Add(networkIP, wxSizerFlags().Expand()); wxStaticBoxSizer* networkSizer = new wxStaticBoxSizer(networkBox, wxHORIZONTAL); networkSizer->Add(networkInsideSizerL, wxSizerFlags().Expand().Border(wxLEFT|wxBOTTOM, 5)); networkSizer->AddStretchSpacer(5); networkSizer->Add(networkInsideSizerR, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); networkSizer->AddStretchSpacer(5); // Audio wxStaticBox* audioBox = new wxStaticBox(this, wxID_ANY, _("Audio")); this->soundDeviceText = new wxStaticText(this, wxID_ANY, _("Sound device:")); this->soundDeviceCombo = new TruncatableChoice(this, ID_SELECT_SOUND_DEVICE); this->captureDeviceText = new wxStaticText(this, wxID_ANY, _("Capture device:")); this->captureDeviceCombo = new TruncatableChoice(this, ID_SELECT_CAPTURE_DEVICE); wxCheckBox* efxCheckBox = new wxCheckBox(this, ID_ENABLE_EFX, _("Enable EFX")); wxStaticText* sampleRateText = new wxStaticText(this, wxID_ANY, _("Sample rate:")); wxTextCtrl* sampleRateBox = new wxTextCtrl(this, ID_AUDIO_SAMPLE_RATE); sampleRateBox->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); sampleRateBox->SetMaxLength(5); this->openALVersion = new wxStaticText(this, wxID_ANY, wxEmptyString); this->downloadOpenALButton = new wxButton(this, ID_DOWNLOAD_OPENAL, _("Download OpenAL")); this->detectOpenALButton = new wxButton(this, ID_DETECT_OPENAL, _("Detect OpenAL")); this->audioOldSoundSizer = new wxBoxSizer(wxVERTICAL); this->audioOldSoundSizer->Add(soundDeviceText, wxSizerFlags().Border(wxBOTTOM, 5)); this->audioOldSoundSizer->Add(soundDeviceCombo, wxSizerFlags().Expand()); this->audioOldSoundSizer->Add(openALVersion, wxSizerFlags().Center().Border(wxTOP, 5)); this->audioNewSoundDeviceSizer = new wxFlexGridSizer(2); this->audioNewSoundDeviceSizer->Add(captureDeviceText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); this->audioNewSoundDeviceSizer->Add(captureDeviceCombo, wxSizerFlags().Expand()); this->audioNewSoundSizer = new wxBoxSizer(wxHORIZONTAL); this->audioNewSoundSizer->Add(this->audioNewSoundDeviceSizer); this->audioNewSoundSizer->AddStretchSpacer(5); this->audioNewSoundSizer->Add(efxCheckBox, 0, wxALIGN_CENTER_VERTICAL|wxRESERVE_SPACE_EVEN_IF_HIDDEN); this->audioNewSoundSizer->AddStretchSpacer(5); this->audioNewSoundSizer->Add(sampleRateText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); this->audioNewSoundSizer->Add(sampleRateBox, 0, wxALIGN_CENTER_VERTICAL); this->audioNewSoundSizer->AddStretchSpacer(5); this->audioButtonsSizer = new wxBoxSizer(wxVERTICAL); this->audioButtonsSizer->Add(downloadOpenALButton, wxSizerFlags().Expand()); this->audioButtonsSizer->Add(detectOpenALButton, wxSizerFlags().Expand()); this->audioSizer = new wxStaticBoxSizer(audioBox, wxHORIZONTAL); this->audioSizer->Add(audioOldSoundSizer, wxSizerFlags().Proportion(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); this->audioSizer->Add(audioNewSoundSizer); this->audioSizer->Hide(audioNewSoundSizer, true); this->audioSizer->Add(audioButtonsSizer, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxBOTTOM, 5); // Joystick wxStaticBox* joystickBox = new wxStaticBox(this, wxID_ANY, _("Joystick")); this->joystickSelected = new wxChoice(this, ID_JOY_SELECTED); #if IS_WIN32 this->joystickForceFeedback = new wxCheckBox(this, ID_JOY_FORCE_FEEDBACK, _("Force feedback")); this->joystickDirectionalHit = new wxCheckBox(this, ID_JOY_DIRECTIONAL_HIT, _("Directional hit")); this->joystickCalibrateButton = new wxButton(this, ID_JOY_CALIBRATE_BUTTON, _("Calibrate")); this->joystickDetectButton = new wxButton(this, ID_JOY_DETECT_BUTTON, _("Detect")); #else // FIXME get Detect button working on Linux/OS X wxStaticText* detectJoystickText = new wxStaticText(this, wxID_ANY, _("Restart launcher to re-detect joysticks."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); #endif this->SetupJoystickSection(); wxBoxSizer* joystickDetectionSizer = new wxBoxSizer(wxVERTICAL); joystickDetectionSizer->Add(joystickSelected, wxSizerFlags().Proportion(1).Expand().Border(wxBOTTOM, 5)); #if IS_WIN32 joystickDetectionSizer->Add(joystickDetectButton, wxSizerFlags().Right()); #else joystickDetectionSizer->Add(detectJoystickText, wxSizerFlags().Center()); #endif #if IS_WIN32 wxBoxSizer* joystickExtrasSizer = new wxBoxSizer(wxVERTICAL); joystickExtrasSizer->Add(joystickForceFeedback, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); joystickExtrasSizer->Add(joystickDirectionalHit, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); joystickExtrasSizer->Add(joystickCalibrateButton, wxSizerFlags().Expand()); #endif wxStaticBoxSizer* joystickSizer = new wxStaticBoxSizer(joystickBox, wxHORIZONTAL); #if IS_WIN32 joystickSizer->Add(joystickDetectionSizer, 1, wxALIGN_BOTTOM|wxLEFT|wxRIGHT|wxBOTTOM, 5); joystickSizer->Add(joystickExtrasSizer, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); #else joystickSizer->Add(joystickDetectionSizer, wxSizerFlags().Expand().Proportion(1).Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); #endif // Proxy // sorry, but there won't be space for the proxy on any platform #if 0 wxStaticBox* proxyBox = new wxStaticBox(this, wxID_ANY, _("Proxy")); wxChoicebook* proxyChoice = new ProxyChoice(this, ID_PROXY_TYPE); wxStaticBoxSizer* proxySizer = new wxStaticBoxSizer(proxyBox, wxVERTICAL); proxySizer->Add(proxyChoice, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT, 5)); #endif // Final Layout wxBoxSizer* settingsSizer = new wxBoxSizer(wxVERTICAL); settingsSizer->Add(videoSizer, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); settingsSizer->Add(audioSizer, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); #if IS_WIN32 settingsSizer->Add(speechSizer, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); #endif settingsSizer->Add(joystickSizer, wxSizerFlags().Expand().Border(wxBOTTOM, 5)); settingsSizer->Add(networkSizer, wxSizerFlags().Expand()); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->SetMinSize(wxSize(TAB_AREA_WIDTH-5, -1)); // 5 being for the border sizer->Add(exeSizer, wxSizerFlags().Expand().Border(wxALL, 5)); sizer->Add(settingsSizer, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); this->SetSizer(sizer); this->Layout(); } BasicSettingsPage::~BasicSettingsPage() { TCManager::DeInitialize(); if ( SpeechMan::IsInitialized() ) { SpeechMan::DeInitialize(); } JoyMan::DeInitialize(); OpenALMan::DeInitialize(); } /// Event Handling BEGIN_EVENT_TABLE(BasicSettingsPage, wxPanel) EVT_BUTTON(ID_EXE_SELECT_ROOT_BUTTON, BasicSettingsPage::OnSelectTC) EVT_CHOICE(ID_EXE_CHOICE_BOX, BasicSettingsPage::OnSelectExecutable) EVT_BUTTON(ID_EXE_CHOICE_REFRESH_BUTTON, BasicSettingsPage::OnPressExecutableChoiceRefreshButton) EVT_CHOICE(ID_EXE_FRED_CHOICE_BOX, BasicSettingsPage::OnSelectFredExecutable) EVT_BUTTON(ID_EXE_FRED_CHOICE_REFRESH_BUTTON, BasicSettingsPage::OnPressFredExecutableChoiceRefreshButton) EVT_COMMAND(wxID_NONE, EVT_TC_CHANGED, BasicSettingsPage::OnTCChanged) EVT_COMMAND(wxID_NONE, EVT_TC_ACTIVE_MOD_CHANGED, BasicSettingsPage::OnActiveModChanged) EVT_COMMAND(wxID_NONE, EVT_TC_BINARY_CHANGED, BasicSettingsPage::OnCurrentBinaryChanged) EVT_COMMAND(wxID_NONE, EVT_TC_FRED_BINARY_CHANGED, BasicSettingsPage::OnCurrentFredBinaryChanged) EVT_COMMAND(wxID_NONE, EVT_FLAG_FILE_PROCESSING_STATUS_CHANGED, BasicSettingsPage::OnFlagFileProcessingStatusChanged) EVT_COMMAND(wxID_NONE, EVT_FRED_ENABLED_CHANGED, BasicSettingsPage::OnFREDEnabledChanged) // Video controls EVT_CHOICE(ID_RESOLUTION_COMBO, BasicSettingsPage::OnSelectVideoResolution) EVT_CHOICE(ID_DEPTH_COMBO, BasicSettingsPage::OnSelectVideoDepth) EVT_CHOICE(ID_TEXTURE_FILTER_COMBO, BasicSettingsPage::OnSelectVideoTextureFilter) EVT_CHOICE(ID_ANISOTROPIC_COMBO, BasicSettingsPage::OnSelectVideoAnisotropic) EVT_CHOICE(ID_AA_COMBO, BasicSettingsPage::OnSelectVideoAntiAlias) // Speech Controls EVT_CHOICE(ID_SPEECH_VOICE_COMBO, BasicSettingsPage::OnSelectSpeechVoice) EVT_SLIDER(ID_SPEECH_VOICE_VOLUME, BasicSettingsPage::OnChangeSpeechVolume) EVT_BUTTON(ID_SPEECH_PLAY_BUTTON, BasicSettingsPage::OnPlaySpeechText) EVT_CHECKBOX(ID_SPEECH_IN_TECHROOM, BasicSettingsPage::OnToggleSpeechInTechroom) EVT_CHECKBOX(ID_SPEECH_IN_BRIEFING, BasicSettingsPage::OnToggleSpeechInBriefing) EVT_CHECKBOX(ID_SPEECH_IN_GAME, BasicSettingsPage::OnToggleSpeechInGame) EVT_CHECKBOX(ID_SPEECH_IN_MULTI, BasicSettingsPage::OnToggleSpeechInMulti) EVT_BUTTON(ID_SPEECH_MORE_VOICES_BUTTON, BasicSettingsPage::OnGetMoreVoices) // Network EVT_CHOICE(ID_NETWORK_TYPE, BasicSettingsPage::OnSelectNetworkType) EVT_CHOICE(ID_NETWORK_SPEED, BasicSettingsPage::OnSelectNetworkSpeed) EVT_TEXT(ID_NETWORK_PORT, BasicSettingsPage::OnChangePort) EVT_TEXT(ID_NETWORK_IP, BasicSettingsPage::OnChangeIP) // OpenAL EVT_CHOICE(ID_SELECT_SOUND_DEVICE, BasicSettingsPage::OnSelectSoundDevice) EVT_CHOICE(ID_SELECT_CAPTURE_DEVICE, BasicSettingsPage::OnSelectCaptureDevice) EVT_CHECKBOX(ID_ENABLE_EFX, BasicSettingsPage::OnToggleEnableEFX) EVT_TEXT(ID_AUDIO_SAMPLE_RATE, BasicSettingsPage::OnChangeSampleRate) EVT_BUTTON(ID_DOWNLOAD_OPENAL, BasicSettingsPage::OnDownloadOpenAL) EVT_BUTTON(ID_DETECT_OPENAL, BasicSettingsPage::OnDetectOpenAL) // Joystick EVT_CHOICE(ID_JOY_SELECTED, BasicSettingsPage::OnSelectJoystick) EVT_CHECKBOX(ID_JOY_FORCE_FEEDBACK, BasicSettingsPage::OnCheckForceFeedback) EVT_CHECKBOX(ID_JOY_DIRECTIONAL_HIT, BasicSettingsPage::OnCheckDirectionalHit) EVT_BUTTON(ID_JOY_CALIBRATE_BUTTON, BasicSettingsPage::OnCalibrateJoystick) EVT_BUTTON(ID_JOY_DETECT_BUTTON, BasicSettingsPage::OnDetectJoystick) // Profile EVT_COMMAND(wxID_NONE, EVT_CURRENT_PROFILE_CHANGED, BasicSettingsPage::ProfileChanged) END_EVENT_TABLE() void BasicSettingsPage::OnFlagFileProcessingStatusChanged(wxCommandEvent& event) { const FlagListManager::FlagFileProcessingStatus status = static_cast(event.GetInt()); if (status == FlagListManager::FLAG_FILE_PROCESSING_OK) { this->SetupOpenALSection(); } } void BasicSettingsPage::OnFREDEnabledChanged(wxCommandEvent& WXUNUSED(event)) { wxStaticText* useFredText = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_TEXT, this)); wxCHECK_RET(useFredText != NULL, _T("Cannot find use FRED text")); ExeChoice* useFredChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_BOX, this)); wxCHECK_RET(useFredChoice != NULL, _T("Cannot find use FRED choice")); wxButton* exeFredChoiceRefreshButton = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_REFRESH_BUTTON, this)); wxCHECK_RET(exeFredChoiceRefreshButton != NULL, _T("Cannot find FRED exe choice refresh button")); bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); useFredText->Show(fredEnabled); useFredChoice->Show(fredEnabled); exeFredChoiceRefreshButton->Show(fredEnabled); } void BasicSettingsPage::OnSelectTC(wxCommandEvent &WXUNUSED(event)) { wxString directory; ProMan* proman = ProMan::GetProfileManager(); proman->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &directory, wxEmptyString); wxDirDialog filechooser(this, _T("Choose the root folder of an FS2 Open game"), directory, wxDD_DEFAULT_STYLE|wxDD_DIR_MUST_EXIST); wxString chosenDirectory; wxFileName path; while (true) { if ( wxID_CANCEL == filechooser.ShowModal() ) { return; } chosenDirectory = filechooser.GetPath(); if ( chosenDirectory == directory ) { wxLogInfo(_T("The game root folder selection was not changed.")); return; // User canceled, bail out. } path.SetPath(chosenDirectory); if ( !path.IsOk() ) { wxLogWarning(_T("Folder is not valid")); continue; } else if ( FSOExecutable::IsRootFolderValid(path) ) { break; } else { wxString folderName; if (path.GetDirCount() != 0) { folderName = path.GetDirs().Last(); } else { folderName = path.GetVolume(); if (folderName.IsEmpty()) { // occurs on Unix, according to wx docs folderName = _T("/"); } } wxLogWarning(_T("Folder \"%s\" does not have any supported executables"), folderName.c_str()); wxLogDebug(_T("Folder \"%s\" does not have any supported executables"), path.GetFullPath().c_str()); } } wxLogDebug(_T("User chose '%s' as the TC root folder"), path.GetPath().c_str()); proman->ProfileWrite(PRO_CFG_TC_ROOT_FOLDER, path.GetPath()); TCManager::GenerateTCChanged(); } /** Handles TCChanged events from TCManager. //FIXME rewrite OnTCChanged() documentation once revisions are complete Currently function only changes the executable dropbox control (clearing, and filling in the executables that are in the new TC folder) and removes the currently select executable from the active profile only if the executable specified in the profile does not exist in the TC. \note clearing the selected executable disables the play button. \note Emits a EVT_TC_BINARY_CHANGED in any case.*/ void BasicSettingsPage::OnTCChanged(wxCommandEvent &WXUNUSED(event)) { ExeChoice* exeChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_BOX, this)); wxCHECK_RET( exeChoice != NULL, _T("Cannot find executable choice control")); wxButton* exeChoiceRefreshButton = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_REFRESH_BUTTON, this)); wxCHECK_RET( exeChoiceRefreshButton != NULL, _T("Cannot find executable choice refresh button")); bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); ExeChoice* fredChoice = NULL; wxButton* fredChoiceRefreshButton = NULL; if (fredEnabled) { fredChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_BOX, this)); wxCHECK_RET( fredChoice != NULL, _T("Cannot find FRED executable choice control")); fredChoiceRefreshButton = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_REFRESH_BUTTON, this)); wxCHECK_RET( fredChoiceRefreshButton != NULL, _T("Cannot find FRED executable choice refresh button")); } wxTextCtrl* tcFolder = dynamic_cast( wxWindow::FindWindowById(ID_EXE_ROOT_FOLDER_BOX, this)); wxCHECK_RET( tcFolder != NULL, _T("Cannot find Text Control to show folder in.")); wxString tcPath, binaryName, fredBinaryName; exeChoice->Clear(); if (fredEnabled) { fredChoice->Clear(); } if ( ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath) ) { wxLogInfo(_T("The current game root folder is %s"), tcPath.c_str()); tcFolder->ChangeValue(tcPath); // note that disabling the controls is necessary if we reached this code from the // "refresh list of FSO execs" button being pressed if (!wxFileName::DirExists(tcPath)) { this->isTcRootFolderValid = false; this->DisableExecutableChoiceControls(NONEXISTENT_TC_ROOT_FOLDER); } else if (!FSOExecutable::HasFSOExecutables(wxFileName(tcPath, wxEmptyString))) { this->isTcRootFolderValid = false; this->DisableExecutableChoiceControls(INVALID_TC_ROOT_FOLDER); } else { // the root folder is valid this->isTcRootFolderValid = true; this->FillFSOExecutableDropBox(exeChoice, wxFileName(tcPath, wxEmptyString)); exeChoice->Enable(); exeChoiceRefreshButton->Enable(); if (fredEnabled) { this->FillFredExecutableDropBox(fredChoice, wxFileName(tcPath, wxEmptyString)); fredChoice->Enable(); fredChoiceRefreshButton->Enable(); } // set selection to profile entry for current binary if there is one, noting if selected binary can't be found bool hasBinary = ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_BINARY, &binaryName); if ( hasBinary && !exeChoice->FindAndSetSelectionWithClientData(binaryName) ) { // no need for a warning, since classes handling the EVT_TC_BINARY_CHANGED will issue warnings wxLogDebug(_T("BasicSettingsPage::OnTCChanged(): couldn't find selected FSO executable %s in list of executables"), binaryName.c_str()); } if (fredEnabled) { // set selection to profile entry for current FRED binary if there is one, // noting if the FRED binary can't be found bool hasFredBinary = ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_FRED, &fredBinaryName); if ( hasFredBinary && !fredChoice->FindAndSetSelectionWithClientData(fredBinaryName) ) { // no need for a warning, since classes handling the EVT_TC_FRED_BINARY_CHANGED will issue warnings wxLogDebug(_T("BasicSettingsPage::OnTCChanged(): couldn't find selected FRED executable %s in list of executables"), fredBinaryName.c_str()); } } } wxLogDebug(_T("The current root folder is %s."), this->isTcRootFolderValid ? _T("valid") : _T("invalid")); } else { wxLogDebug(_T("The current profile has no entry for root folder.")); this->isTcRootFolderValid = false; this->DisableExecutableChoiceControls(MISSING_TC_ROOT_FOLDER); } this->GetSizer()->Layout(); // TCManager::CurrentProfileChanged() (which calls TCManager::GenerateTCChanged()) // assumes that TCManager::GenerateTCBinaryChanged() is called here unconditionally TCManager::GenerateTCBinaryChanged(); // TCManager::CurrentProfileChanged() also assumes that TCManager::GenerateTCFredBinaryChanged() // is called here unconditionally if FRED launching is enabled if (fredEnabled) { TCManager::GenerateTCFredBinaryChanged(); } } /** Puts the pretty description of all of the executables in the TCs folder into the Executable DropBox. This function does nothing else to the choice control, not even clearing the drop box (call the Clear function if you don't want the old items to stay. */ void BasicSettingsPage::FillFSOExecutableDropBox(wxChoice* exeChoice, wxFileName path) { BasicSettingsPage::FillExecutableDropBox(exeChoice, FSOExecutable::GetBinariesFromRootFolder(path)); } void BasicSettingsPage::FillFredExecutableDropBox(wxChoice* exeChoice, wxFileName path) { BasicSettingsPage::FillExecutableDropBox(exeChoice, FSOExecutable::GetFredBinariesFromRootFolder(path)); } bool compareExecutables(FSOExecutable exe1, FSOExecutable exe2) { return exe1.GetVersionString().CmpNoCase(exe2.GetVersionString()) < 0; } void BasicSettingsPage::FillExecutableDropBox(wxChoice* exeChoice, wxArrayString exes) { std::vector fsoExes; wxArrayString::iterator iter = exes.begin(); while ( iter != exes.end() ) { wxFileName path(*iter); #if IS_APPLE // need complete path through app bundle to executable FSOExecutable ver = FSOExecutable::GetBinaryVersion(path.GetFullPath()); #else FSOExecutable ver = FSOExecutable::GetBinaryVersion(path.GetFullName()); #endif fsoExes.push_back(ver); iter++; } sort(fsoExes.begin(), fsoExes.end(), compareExecutables); for (std::vector::const_iterator it = fsoExes.begin(), end = fsoExes.end(); it != end; ++it) { exeChoice->Append(it->GetVersionString(), new FSOExecutable(*it)); } } void BasicSettingsPage::OnSelectExecutable(wxCommandEvent &WXUNUSED(event)) { ExeChoice* choice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_BOX, this)); wxCHECK_RET( choice != NULL, _T("OnSelectExecutable: cannot find FS2 Open choice drop down box")); FSOExecutable* ver = dynamic_cast( choice->GetClientObject(choice->GetSelection())); wxCHECK_RET( ver != NULL, _T("OnSelectExecutable: choice does not have FSOVersion data")); wxLogDebug(_T("Have selected ver for %s"), ver->GetExecutableName().c_str()); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_TC_CURRENT_BINARY, ver->GetExecutableName()); TCManager::GenerateTCBinaryChanged(); } void BasicSettingsPage::OnPressExecutableChoiceRefreshButton(wxCommandEvent &WXUNUSED(event)) { ExeChoice* exeChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_BOX, this)); wxCHECK_RET( exeChoice != NULL, _T("Cannot find executable choice control")); wxString tcPath, binaryName; wxCHECK_RET(ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath), _T("OnPressExecutableChoiceRefreshButton: root folder entry not found")); if (!wxFileName::DirExists(tcPath)) { // warning not needed, since it will be issued by DisableExecutableChoiceControls wxLogDebug(_T("OnPressExecutableChoiceRefreshButton: root folder '%s' no longer exists"), tcPath.c_str()); TCManager::GenerateTCChanged(); return; } exeChoice->Clear(); this->FillFSOExecutableDropBox(exeChoice, wxFileName(tcPath, wxEmptyString)); if (exeChoice->IsEmpty()) { // current root folder has no FSO executables. update GUI if there were FSO executables before if (this->isTcRootFolderValid) { wxLogWarning(_T("after refreshing list of FSO executables, none were found")); TCManager::GenerateTCChanged(); } } else if (!this->isTcRootFolderValid) { wxLogInfo(_T("after refreshing list of FSO executables, some have now been found")); TCManager::GenerateTCChanged(); } else { // set selection to profile entry for current binary if there is one, // noting if selected binary can't be found and could be found before or vice versa if (ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_BINARY, &binaryName)) { bool exeFound = exeChoice->FindAndSetSelectionWithClientData(binaryName); if (!exeFound && this->isCurrentBinaryValid) { wxLogDebug(_T("OnPressExecutableChoiceRefresh: couldn't find selected FSO executable %s in list of executables"), binaryName.c_str()); TCManager::GenerateTCBinaryChanged(); } else if (exeFound && !this->isCurrentBinaryValid) { wxLogDebug(_T("OnPressExecutableChoiceRefresh: found selected FSO executable %s in list after previously unable to do so"), binaryName.c_str()); TCManager::GenerateTCBinaryChanged(); } } } } void BasicSettingsPage::OnSelectFredExecutable(wxCommandEvent &WXUNUSED(event)) { ExeChoice* choice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_BOX, this)); wxCHECK_RET( choice != NULL, _T("OnSelectExecutable: cannot find FRED choice drop down box")); FSOExecutable* ver = dynamic_cast( choice->GetClientObject(choice->GetSelection())); wxCHECK_RET( ver != NULL, _T("OnSelectExecutable: choice does not have FSOVersion data")); wxLogDebug(_T("Have selected ver for %s"), ver->GetExecutableName().c_str()); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_TC_CURRENT_FRED, ver->GetExecutableName()); TCManager::GenerateTCFredBinaryChanged(); } void BasicSettingsPage::OnPressFredExecutableChoiceRefreshButton(wxCommandEvent &WXUNUSED(event)) { bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); wxCHECK_RET(fredEnabled, _T("OnPressFredExecutableChoiceRefreshButton called when fredEnabled is false")); ExeChoice* fredChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_BOX, this)); wxCHECK_RET( fredChoice != NULL, _T("Cannot find FRED executable choice control")); wxString tcPath, fredBinaryName; wxCHECK_RET(ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath), _T("OnPressFredExecutableChoiceRefreshButton: root folder entry not found")); if (!wxFileName::DirExists(tcPath)) { // warning not needed, since it will be issued by DisableExecutableChoiceControls wxLogDebug(_T("OnPressExecutableChoiceRefreshButton: root folder '%s' no longer exists"), tcPath.c_str()); TCManager::GenerateTCChanged(); return; } fredChoice->Clear(); this->FillFredExecutableDropBox(fredChoice, wxFileName(tcPath, wxEmptyString)); // set selection to profile entry for current FRED binary if there is one, // noting if selected FRED binary can't be found and could be found before or vice versa if (ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_FRED, &fredBinaryName)) { bool fredExeFound = fredChoice->FindAndSetSelectionWithClientData(fredBinaryName); if (!fredExeFound && this-isCurrentFredBinaryValid) { wxLogDebug(_T("OnPressFredExecutableChoiceRefresh: couldn't find selected FRED exec %s in list of executables"), fredBinaryName.c_str()); TCManager::GenerateTCFredBinaryChanged(); } else if (fredExeFound && !this->isCurrentFredBinaryValid) { wxLogDebug( _T("OnPressFredExecutableChoiceRefresh: found selected FRED exec %s in list after previously unable to do so"), fredBinaryName.c_str()); TCManager::GenerateTCFredBinaryChanged(); } } } /** Initializes member variables, such as setting pointers to NULL. Made into a function, since it needs to be done at multiple places in the code. */ void BasicSettingsPage::InitializeMemberVariables() { // following the order in BasicSettingsPage.h this->soundDeviceText = NULL; this->soundDeviceCombo = NULL; this->captureDeviceText = NULL; this->captureDeviceCombo = NULL; this->openALVersion = NULL; this->downloadOpenALButton = NULL; this->detectOpenALButton = NULL; this->audioButtonsSizer = NULL; this->audioOldSoundSizer = NULL; this->audioNewSoundSizer = NULL; this->audioNewSoundDeviceSizer = NULL; this->audioSizer = NULL; this->joystickSelected = NULL; #if IS_WIN32 this->joystickForceFeedback = NULL; this->joystickDirectionalHit = NULL; this->joystickCalibrateButton = NULL; this->joystickDetectButton = NULL; #endif this->isTcRootFolderValid = false; this->isCurrentBinaryValid = false; this->isCurrentFredBinaryValid = false; } /** Disables the executable choice and refresh button controls, such as would occur if the currently loaded TC root folder doesn't exist or has no FSO executables. */ void BasicSettingsPage::DisableExecutableChoiceControls(const ReasonForExecutableDisabling reason) { wxString tcFolderPath; bool hasTcPath = ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcFolderPath); wxCHECK_RET(hasTcPath || (reason == MISSING_TC_ROOT_FOLDER), _T("DisableChoiceExecutableControls: profile has no root folder entry, but reason is not a missing root folder")); wxCHECK_RET((!hasTcPath) || (reason != MISSING_TC_ROOT_FOLDER), _T("DisableChoiceExecutableControls: reason is a missing root folder, but profile has a root folder entry")); switch (reason) { case MISSING_TC_ROOT_FOLDER: // no user-facing message needed, since this is not an error state wxLogDebug(_T("disabling executable controls, since root folder entry is missing")); break; case NONEXISTENT_TC_ROOT_FOLDER: wxLogWarning(_("Selected root folder does not exist.")); break; case INVALID_TC_ROOT_FOLDER: wxLogWarning(_("Selected root folder has no FS2 Open executables")); break; default: wxCHECK_RET(false, _T("DisableExecutableChoiceControls called with invalid reason")); break; } ExeChoice *exeChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_BOX, this)); wxCHECK_RET( exeChoice != NULL, _T("Cannot find executable choice control")); wxButton* exeChoiceRefreshButton = dynamic_cast( wxWindow::FindWindowById(ID_EXE_CHOICE_REFRESH_BUTTON, this)); wxCHECK_RET( exeChoiceRefreshButton != NULL, _T("Cannot find executable choice refresh button")); exeChoice->Clear(); exeChoice->Disable(); if (reason == NONEXISTENT_TC_ROOT_FOLDER || reason == MISSING_TC_ROOT_FOLDER) { exeChoiceRefreshButton->Disable(); } bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); if (fredEnabled) { ExeChoice* fredChoice = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_BOX, this)); wxCHECK_RET( fredChoice != NULL, _T("Cannot find FRED executable choice control")); wxButton* fredChoiceRefreshButton = dynamic_cast( wxWindow::FindWindowById(ID_EXE_FRED_CHOICE_REFRESH_BUTTON, this)); wxCHECK_RET( fredChoiceRefreshButton != NULL, _T("Cannot find FRED executable choice refresh button")); fredChoice->Clear(); fredChoice->Disable(); fredChoiceRefreshButton->Disable(); } } void BasicSettingsPage::SetUpResolution(const long minHorizRes, const long minVertRes) { ProMan* proman = ProMan::GetProfileManager(); wxChoice* resolutionCombo = dynamic_cast( wxWindow::FindWindowById(ID_RESOLUTION_COMBO, this)); wxCHECK_RET(resolutionCombo != NULL, _T("Unable to find resolution combo")); FillResolutionDropBox(resolutionCombo, minHorizRes, minVertRes); long width = 0, height = 0; const ModItem* activeMod = ModList::GetActiveMod(); wxCHECK_RET(activeMod != NULL, _T("SetUpResolution: activeMod is NULL!")); const wxString& shortname(activeMod->shortname); bool hasValidRes = false; wxString resString; // first try reading from ResolutionMap const ResolutionData* resDataPtr = ResolutionMap::ResolutionRead(shortname); if (resDataPtr != NULL) { width = resDataPtr->width; height = resDataPtr->height; resString = wxString::Format( CFG_RES_FORMAT_STRING, static_cast(width), static_cast(height)); hasValidRes = resolutionCombo->SetStringSelection(resString); } // then try reading resolution from profile if (!hasValidRes) { if (!resString.IsEmpty()) { wxLogDebug(_T("map resolution %s not found, attempting to use profile value"), resString.c_str()); resString.Clear(); } else { wxLogDebug(_T("no resolution found in map, attempting to use profile value")); } bool hasResWidth = proman->ProfileRead(PRO_CFG_VIDEO_RESOLUTION_WIDTH, &width); bool hasResHeight = proman->ProfileRead(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, &height); if (hasResWidth && hasResHeight) { resString = wxString::Format( CFG_RES_FORMAT_STRING, static_cast(width), static_cast(height)); hasValidRes = resolutionCombo->SetStringSelection(resString); } } // last resort, use default (max supported resolution) if (!hasValidRes) { if (!resString.IsEmpty()) { wxLogDebug(_T("resolution map value %s not found, using default max supported resolution"), resString.c_str()); } else { wxLogDebug(_T("no resolution found in map, using default max supported resolution")); } int maxResIndex = GetMaxSupportedResolution(*resolutionCombo, width, height); if (maxResIndex != wxNOT_FOUND) { resolutionCombo->SetSelection(maxResIndex); } else { const wxString InsufficientResolutionMsg( wxString::Format( _T("Your system does not support the minimum resolution needed to play this mod (%dx%d)."), minHorizRes, minVertRes)); wxLogError(InsufficientResolutionMsg); wxMessageBox(InsufficientResolutionMsg, _T("Mod not supported"), wxOK | wxICON_ERROR); return; } } // update profile and ResolutionMap proman->ProfileWrite(PRO_CFG_VIDEO_RESOLUTION_WIDTH, width); proman->ProfileWrite(PRO_CFG_VIDEO_RESOLUTION_HEIGHT, height); ResolutionMap::ResolutionWrite(shortname, ResolutionData(width, height)); } /** Adjust the resolution drop down box according to the active mod's restrictions. */ void BasicSettingsPage::OnActiveModChanged(wxCommandEvent& event) { wxChoice* resChoice = dynamic_cast( wxWindow::FindWindowById(ID_RESOLUTION_COMBO, this)); wxCHECK_RET(resChoice != NULL, _T("Unable to find resolution combo")); resChoice->Clear(); const ModItem* activeMod = ModList::GetActiveMod(); wxCHECK_RET(activeMod != NULL, _T("BSP::OnActiveModChanged(): activeMod is NULL!")); SetUpResolution(activeMod->minhorizontalres, activeMod->minverticalres); } /** Updates the status of whether the currently selected FSO binary is valid. */ void BasicSettingsPage::OnCurrentBinaryChanged(wxCommandEvent& event) { wxString tcPath, binaryName; if (!ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath)) { this->isCurrentBinaryValid = false; this->ShowSettings(this->isCurrentBinaryValid); return; } if ((!FSOExecutable::IsRootFolderValid(wxFileName(tcPath, wxEmptyString), true)) && this->isTcRootFolderValid) { this->isCurrentBinaryValid = false; this->ShowSettings(this->isCurrentBinaryValid); TCManager::GenerateTCChanged(); return; } bool hasBinary = ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_BINARY, &binaryName); this->isCurrentBinaryValid = this->isTcRootFolderValid && hasBinary && wxFileName::FileExists(tcPath + wxFileName::GetPathSeparator() + binaryName); if (hasBinary) { wxLogDebug(_T("The current profile's listed FSO executable '%s' is %s."), binaryName.c_str(), this->isCurrentBinaryValid ? _T("valid") : _T("invalid")); } else { wxLogDebug(_T("The current profile has no FSO executable listed.")); } this->ShowSettings(this->isCurrentBinaryValid); } /** Updates the status of whether the currently selected FRED binary is valid. */ void BasicSettingsPage::OnCurrentFredBinaryChanged(wxCommandEvent& event) { bool fredEnabled; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_OPT_CONFIG_FRED, &fredEnabled, false); wxCHECK_RET(fredEnabled, _T("OnCurrentFredBinaryChanged called while fredEnabled is false")); wxString tcPath, fredBinaryName; if (!ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath)) { this->isCurrentFredBinaryValid = false; return; } if ((!FSOExecutable::IsRootFolderValid(wxFileName(tcPath, wxEmptyString), true)) && this->isTcRootFolderValid) { TCManager::GenerateTCChanged(); return; } bool hasFredBinary = ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_CURRENT_FRED, &fredBinaryName); this->isCurrentFredBinaryValid = this->isTcRootFolderValid && hasFredBinary && wxFileName::FileExists(tcPath + wxFileName::GetPathSeparator() + fredBinaryName); if (hasFredBinary) { wxLogDebug(_T("The current profile's listed FRED executable '%s' is %s."), fredBinaryName.c_str(), this->isCurrentFredBinaryValid ? _T("valid") : _T("invalid")); } else { wxLogDebug(_T("The current profile has no FRED executable listed.")); } } /** Hides or shows the settings. */ void BasicSettingsPage::ShowSettings(const bool showSettings) { if (showSettings) { if (!this->GetSizer()->GetItem(SETTINGS_SIZER_INDEX)->IsShown()) { this->GetSizer()->Show(SETTINGS_SIZER_INDEX); this->Layout(); } } else { // using else rather than else-if to make the code easier to read (indentation-wise) if (this->GetSizer()->GetItem(SETTINGS_SIZER_INDEX)->IsShown()) { this->GetSizer()->Hide(SETTINGS_SIZER_INDEX); this->Layout(); } } } class Resolution: public wxClientData { public: Resolution(const int width, const int height, const bool isHeader) { wxASSERT_MSG(width > 0, wxString::Format(_T("Resolution: provided width %d is invalid"), width)); wxASSERT_MSG(height > 0, wxString::Format(_T("Resolution: provided height %d is invalid"), height)); this->width = width; this->height = height; this->isHeader = isHeader; resString = isHeader ? wxString::Format(_T("--- %d:%d ---"), width, height) : wxString::Format(CFG_RES_FORMAT_STRING, width, height); } virtual ~Resolution() {} const int GetWidth() const { return this->width; } const int GetHeight() const { return this->height; } const bool IsHeader() const { return this->isHeader; } const wxString &GetResString() const { return this->resString; } bool IsSameResolution(const int otherWidth, const int otherHeight) const { return ((this->width == otherWidth) && (this->height == otherHeight)); } private: int width; int height; bool isHeader; wxString resString; }; WX_DEFINE_ARRAY_PTR(Resolution *, ResolutionArray); // sort by aspect ratio, then by size, in descending order int CompareResolutions(Resolution **resp1, Resolution **resp2) { wxASSERT_MSG(resp1 != NULL, _T("CompareResolutions: provided resp1 is NULL")); wxASSERT_MSG((*resp1) != NULL, _T("CompareResolutions: resp1 points to NULL")); wxASSERT_MSG(resp2 != NULL, _T("CompareResolutions: provided resp2 is NULL")); wxASSERT_MSG((*resp2) != NULL, _T("CompareResolutions: resp2 points to NULL")); // compare the two width/height ratios by cross-multiplying and comparing the results const int width1 = (*resp1)->GetWidth(); const int height1 = (*resp1)->GetHeight(); const int width2 = (*resp2)->GetWidth(); const int height2 = (*resp2)->GetHeight(); wxASSERT_MSG(width1 > 0, wxString::Format(_T("CompareResolutions: width1 %d is invalid"), width1)); wxASSERT_MSG(height1 > 0, wxString::Format(_T("CompareResolutions: height1 %d is invalid"), height1)); wxASSERT_MSG(width2 > 0, wxString::Format(_T("CompareResolutions: width2 %d is invalid"), width2)); wxASSERT_MSG(height2 > 0, wxString::Format(_T("CompareResolutions: height2 %d is invalid"), height2)); const int value1 = width1 * height2; const int value2 = width2 * height1; int result; // first compare aspect ratio if (value1 < value2) { result = -1; } else if (value1 > value2) { result = 1; } else { // then compare size if aspect ratios are equal if (width1 < width2) { result = -1; } else if (width1 > width2) { result = 1; } else { result = 0; } } return (-1) * result; // for reverse comparison (descending order) } // brought to you by http://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations int ComputeGCD(int a, int b) { wxASSERT_MSG(a > 0, wxString::Format(_T("ComputeGCD(a=%d, b=%d): a must be positive"), a, b)); wxASSERT_MSG(b > 0, wxString::Format(_T("ComputeGCD(a=%d, b=%d): b must be positive"), a, b)); // part of the original algorithm, but irrelevant, since a and b are positive // if (a == 0) { // return b; // } while (b != 0) { if (a > b) { a = a - b; } else { b = b - a; } } return a; } // Assumed resolutions has been sorted using CompareResolutions void AddHeaders(ResolutionArray &resolutions) { if (resolutions.IsEmpty()) { wxLogWarning(_T("AddHeaders: provided ResolutionArray is empty")); return; } ResolutionArray headers; wxArrayInt headerIndexes; int lastAspectWidth = -1; int lastAspectHeight = -1; int width; int height; int gcd; // find aspect ratios and determine where the aspect ratio headers should be inserted for (int i = 0, n = resolutions.GetCount(); i < n; ++i) { width = resolutions.Item(i)->GetWidth(); height = resolutions.Item(i)->GetHeight(); gcd = ComputeGCD(width, height); width /= gcd; height /= gcd; // special exception: 8:5 should be 16:10 if ((width == 8) && (height == 5)) { width *= 2; height *= 2; } if ((width != lastAspectWidth) || (height != lastAspectHeight)) { wxLogDebug(_T(" found aspect ratio %d:%d"), width, height); headers.Add(new Resolution(width, height, true)); headerIndexes.Add(i); lastAspectWidth = width; lastAspectHeight = height; } } // now insert the headers into resolutions // must insert in reverse to avoid messing up insertion indexes for (int i = headerIndexes.GetCount() - 1; i >= 0; --i) { resolutions.Insert(headers.Item(i), headerIndexes.Item(i)); } } void BasicSettingsPage::FillResolutionDropBox(wxChoice *resChoice, const long minHorizontalRes, const long minVerticalRes) { ResolutionArray resolutions; // first, detect supported resolutions #ifdef WIN32 DEVMODE deviceMode; DWORD modeCounter = 0; BOOL result; wxLogDebug(_T("Enumerating graphics modes")); do { memset(&deviceMode, 0, sizeof(DEVMODE)); deviceMode.dmSize = sizeof(DEVMODE); result = EnumDisplaySettings(NULL, modeCounter, &deviceMode); if ( result == TRUE ) { wxLogDebug(_T(" %dx%d %d bit %d hertz (%d)"), deviceMode.dmPelsWidth, deviceMode.dmPelsHeight, deviceMode.dmBitsPerPel, deviceMode.dmDisplayFrequency, deviceMode.dmDisplayFlags); // check to see if the resolution has already been added // FIXME is there a reasonable way to not make populating resolutions O(n^2)? bool resExists = false; for (ResolutionArray::iterator it = resolutions.begin(), end = resolutions.end(); it != end; ++it) { if ((*it)->IsSameResolution(deviceMode.dmPelsWidth, deviceMode.dmPelsHeight)) { resExists = true; break; // no need to keep looking } } if ( !resExists && (deviceMode.dmPelsWidth >= static_cast(minHorizontalRes)) && (deviceMode.dmPelsHeight >= static_cast(minVerticalRes))) { resolutions.Add(new Resolution(deviceMode.dmPelsWidth, deviceMode.dmPelsHeight, false)); } } modeCounter++; } while ( result == TRUE ); #elif HAS_SDL == 1 wxLogDebug(_T("Enumerating graphics modes with SDL")); SDL_Rect** modes; modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); if ( modes == (SDL_Rect**)NULL ) { wxLogWarning(_T("Unable to retrieve any video modes")); } else if ( modes == (SDL_Rect**)(-1) ) { wxLogWarning(_T("All resolutions are available. If you get this message please report it to the developers as they do not think this response is actually possible")); } else { wxLogDebug(_T("Found the following video modes:")); for(int i = 0; modes[i]; i++) { wxLogDebug(_T(" %d x %d"), modes[i]->w, modes[i]->h); // FIXME why is "check to see if resolution has already been added" not used here? if ((modes[i]->w >= minHorizontalRes) && (modes[i]->h >= minVerticalRes)) { resolutions.Add(new Resolution(modes[i]->w, modes[i]->h, false)); } } } #else #error "BasicSettingsPage::FillResolutionDropBox not implemented because not on windows and SDL is not implemented" #endif // then arrange the resolutions for drop down box and insert them resolutions.Sort(CompareResolutions); AddHeaders(resolutions); for (ResolutionArray::iterator it = resolutions.begin(), end = resolutions.end(); it != end; ++it) { resChoice->Append((*it)->GetResString(), *it); } } /** Takes a resolution drop down box, finds the maximum resolution, updates the provided width and height variables, and returns the index at which the maximum resolution was found, returning wxNOT_FOUND on error. */ int BasicSettingsPage::GetMaxSupportedResolution(const wxChoice& resChoice, long& width, long& height) { if (resChoice.IsEmpty()) { wxLogError(_T("GetMaxSupportedResolution() given empty resChoice.")); return wxNOT_FOUND; } int maxResIndex = -1; int maxResProduct = 0; Resolution* res; for (unsigned int i = 0; i < resChoice.GetCount(); ++i) { res = dynamic_cast(resChoice.GetClientObject(i)); wxCHECK_MSG(res != NULL, wxNOT_FOUND, wxString::Format(_T("choice does not have Resolution object at index %d"), i)); // FIXME could be clever and only read the highest resolution in each aspect ratio group if (!res->IsHeader()) { int resProduct = res->GetWidth() * res->GetHeight(); if (resProduct > maxResProduct) { maxResIndex = i; maxResProduct = resProduct; } } } wxCHECK_MSG(maxResIndex > -1, wxNOT_FOUND, _T("maximum Resolution was not found")); res = dynamic_cast(resChoice.GetClientObject(maxResIndex)); wxCHECK_MSG(res != NULL, wxNOT_FOUND, _T("Choice is missing max Resolution object")); width = res->GetWidth(); height = res->GetHeight(); wxLogDebug(_T("Found max resolution of %s at index %d"), res->GetResString().c_str(), maxResIndex); return maxResIndex; } void BasicSettingsPage::OnSelectVideoResolution(wxCommandEvent &WXUNUSED(event)) { wxChoice* choice = dynamic_cast( wxWindow::FindWindowById(ID_RESOLUTION_COMBO, this)); wxCHECK_RET( choice != NULL, _T("Unable to find resolution combo")); Resolution* res = dynamic_cast( choice->GetClientObject(choice->GetSelection())); wxCHECK_RET( res != NULL, _T("Choice does not have Resolution objects")); if (res->IsHeader()) { // then advance to first resolution in the header's list choice->SetSelection(choice->GetSelection() + 1); } res = dynamic_cast(choice->GetClientObject(choice->GetSelection())); wxCHECK_RET( res != NULL, _T("after adjusting selection from header, Choice does not have Resolution objects")); const long width = static_cast(res->GetWidth()); const long height = static_cast(res->GetHeight()); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_RESOLUTION_WIDTH, width); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_RESOLUTION_HEIGHT, height); // update ResolutionMap const ModItem* activeMod = ModList::GetActiveMod(); wxCHECK_RET(activeMod != NULL, _T("OnSelectVideoResolution: activeMod is NULL!")); ResolutionMap::ResolutionWrite(activeMod->shortname, ResolutionData(width, height)); } void BasicSettingsPage::OnSelectVideoDepth(wxCommandEvent &WXUNUSED(event)) { wxChoice* depth = dynamic_cast( wxWindow::FindWindowById(ID_DEPTH_COMBO, this)); wxCHECK_RET( depth != NULL, _T("Unable to find depth choice box")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_BIT_DEPTH, (depth->GetSelection() == 0) ? static_cast(16) : static_cast(32)); } void BasicSettingsPage::OnSelectVideoTextureFilter(wxCommandEvent &WXUNUSED(event)) { wxChoice* tex = dynamic_cast( wxWindow::FindWindowById(ID_TEXTURE_FILTER_COMBO, this)); wxCHECK_RET( tex != NULL, _T("Unable to find texture filter choice")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_TEXTURE_FILTER, (tex->GetSelection() == 0) ? _T("Bilinear") : _T("Trilinear")); } void BasicSettingsPage::OnSelectVideoAnisotropic(wxCommandEvent &WXUNUSED(event)) { wxChoice* as = dynamic_cast( wxWindow::FindWindowById(ID_ANISOTROPIC_COMBO, this)); wxCHECK_RET( as != NULL, _T("Unable to find anisotropic choice")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_ANISOTROPIC, (as->GetSelection() == 0) ? static_cast(0) : static_cast(1 << (as->GetSelection()-1))); } void BasicSettingsPage::OnSelectVideoAntiAlias(wxCommandEvent &WXUNUSED(event)) { wxChoice* aa = dynamic_cast( wxWindow::FindWindowById(ID_AA_COMBO, this)); wxCHECK_RET( aa != NULL, _T("Unable to find anti-alias choice")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_VIDEO_ANTI_ALIAS, (aa->GetSelection() == 0) ? static_cast(0) : static_cast(1 << (aa->GetSelection()))); } void BasicSettingsPage::OnSelectSpeechVoice(wxCommandEvent &WXUNUSED(event)) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); wxChoice* voice = dynamic_cast( wxWindow::FindWindowById(ID_SPEECH_VOICE_COMBO, this)); wxCHECK_RET( voice != NULL, _T("Unable to find voice choice box")); int v = voice->GetSelection(); SpeechMan::SetVoice(v); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_VOICE, static_cast(v)); } void BasicSettingsPage::OnChangeSpeechVolume(wxCommandEvent &WXUNUSED(event)) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); wxSlider* volume = dynamic_cast( wxWindow::FindWindowById(ID_SPEECH_VOICE_VOLUME, this)); wxCHECK_RET( volume != NULL, _T("Unable to find speech volume slider")); int v = volume->GetValue(); SpeechMan::SetVolume(v); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_VOLUME, static_cast(v)); } void BasicSettingsPage::OnPlaySpeechText(wxCommandEvent &WXUNUSED(event)) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); wxTextCtrl *text = dynamic_cast( wxWindow::FindWindowById(ID_SPEECH_TEST_TEXT, this)); wxCHECK_RET( text != NULL, _T("Unable to find text control to get play text")); wxString str = text->GetValue(); SpeechMan::Speak(str); } void BasicSettingsPage::OnToggleSpeechInTechroom(wxCommandEvent &event) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); bool checked = event.IsChecked(); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_IN_TECHROOM, checked); } void BasicSettingsPage::OnToggleSpeechInBriefing(wxCommandEvent &event) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); bool checked = event.IsChecked(); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_IN_BRIEFINGS, checked); } void BasicSettingsPage::OnToggleSpeechInGame(wxCommandEvent &event) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); bool checked = event.IsChecked(); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_IN_GAME, checked); } void BasicSettingsPage::OnToggleSpeechInMulti(wxCommandEvent &event) { wxCHECK_RET( SpeechMan::WasBuiltIn(), _T("Speech was not compiled in.")); bool checked = event.IsChecked(); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_SPEECH_IN_MULTI, checked); } void BasicSettingsPage::OnGetMoreVoices(wxCommandEvent &WXUNUSED(event)) { HelpManager::OpenHelpById(ID_SPEECH_MORE_VOICES_BUTTON); } void BasicSettingsPage::OnChangeIP(wxCommandEvent &event) { wxTextCtrl* ip = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(ip != NULL, _T("Unable to find IP Text Control")); wxString string(ip->GetValue()); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_NETWORK_IP, string); } void BasicSettingsPage::OnChangePort(wxCommandEvent &event) { wxTextCtrl* port = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(port != NULL, _T("Unable to find Port Text Control")); if (port->IsEmpty()) { wxLogDebug(_T("Port field is blank, writing 0 to profile")); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_NETWORK_PORT, static_cast(0)); return; } long portNumber; if ( port->GetValue().ToLong(&portNumber) ) { if ( portNumber < 0 ) { wxLogInfo(_T("Port number must be greater than or equal to 0")); } else if ( portNumber > 65535 ) { wxLogInfo(_T("Port number must be less than 65536")); } else { ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_NETWORK_PORT, portNumber); } } else { wxLogWarning(_T("Port number is not a number")); } } void BasicSettingsPage::OnSelectNetworkSpeed(wxCommandEvent &event) { wxChoice* networkSpeed = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(networkSpeed != NULL, _T("Unable to find Network speed choice")); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_NETWORK_SPEED, networkSpeedOptions[networkSpeed->GetSelection()].GetRegistryValue()); } void BasicSettingsPage::OnSelectNetworkType(wxCommandEvent &event) { wxChoice* networkType = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(networkType != NULL, _T("Unable to find Network type choice")); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_NETWORK_TYPE, networkTypeOptions[networkType->GetSelection()].GetRegistryValue()); } void BasicSettingsPage::OnSelectSoundDevice(wxCommandEvent &event) { wxChoice* openaldevice = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(openaldevice != NULL, _T("Unable to find OpenAL Device choice")); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_OPENAL_DEVICE, openaldevice->GetStringSelection()); if (OpenALMan::BuildHasNewSoundCode()) { wxCheckBox* enableEFX = dynamic_cast( wxWindow::FindWindowById(ID_ENABLE_EFX)); wxCHECK_RET(enableEFX != NULL, _T("Unable to find enable EFX checkbox")); enableEFX->Show(OpenALMan::IsEFXSupported(openaldevice->GetStringSelection())); } } void BasicSettingsPage::OnSelectCaptureDevice(wxCommandEvent &event) { wxCHECK_RET(OpenALMan::BuildHasNewSoundCode(), _T("Selected FSO build doesn't have new sound code.")); wxChoice* captureDevice = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(captureDevice != NULL, _T("Unable to find capture device choice")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_OPENAL_CAPTURE_DEVICE, captureDevice->GetStringSelection()); } void BasicSettingsPage::OnToggleEnableEFX(wxCommandEvent &event) { wxCHECK_RET(OpenALMan::BuildHasNewSoundCode(), _T("Selected FSO build doesn't have new sound code.")); ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_OPENAL_EFX, event.IsChecked()); } void BasicSettingsPage::OnChangeSampleRate(wxCommandEvent &event) { wxCHECK_RET(OpenALMan::BuildHasNewSoundCode(), _T("Selected FSO build doesn't have new sound code.")); wxTextCtrl* sampleRateBox = dynamic_cast( wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET(sampleRateBox != NULL, _T("Unable to find sample rate text control")); if (sampleRateBox->IsEmpty()) { wxLogDebug(_T("Sample rate field is blank, writing 0 to profile")); ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_OPENAL_SAMPLE_RATE, static_cast(0)); return; } long sampleRate; if ( sampleRateBox->GetValue().ToLong(&sampleRate) ) { if ( sampleRate <= 0 ) { wxLogInfo(_T("Sample rate must be greater than 0")); } else if ( sampleRate > 48000 ) { wxLogInfo(_T("Sample rate must be at most 48000")); } else { ProMan::GetProfileManager()->ProfileWrite( PRO_CFG_OPENAL_SAMPLE_RATE, sampleRate); } } else { wxLogWarning(_T("Sample rate is not a number")); } } void BasicSettingsPage::OnDownloadOpenAL(wxCommandEvent &WXUNUSED(event)) { this->OpenNonSCPWebSite(_T("http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx")); } void BasicSettingsPage::OnDetectOpenAL(wxCommandEvent& WXUNUSED(event)) { if ( !OpenALMan::IsInitialized() ) { this->SetupOpenALSection(); } } void BasicSettingsPage::InitializeSoundDeviceDropDownBox( const SoundDeviceType deviceType) { wxCHECK_RET((deviceType == PLAYBACK) || (deviceType == CAPTURE), wxString::Format(_T("Invalid device type %d given."), deviceType)); WindowIDS deviceDropDownBoxID; wxString deviceTypeNameAdjustment; wxString deviceProfileEntryName; wxArrayString availableDevices; wxString defaultDevice; if (deviceType == PLAYBACK) { deviceDropDownBoxID = ID_SELECT_SOUND_DEVICE; // (deviceTypeNameAdjustment remains empty in this case) deviceProfileEntryName = PRO_CFG_OPENAL_DEVICE; availableDevices = OpenALMan::GetAvailablePlaybackDevices(); defaultDevice = OpenALMan::GetSystemDefaultPlaybackDevice(); } else { deviceDropDownBoxID = ID_SELECT_CAPTURE_DEVICE; deviceTypeNameAdjustment = _T(" capture"); deviceProfileEntryName = PRO_CFG_OPENAL_CAPTURE_DEVICE; availableDevices = OpenALMan::GetAvailableCaptureDevices(); defaultDevice = OpenALMan::GetSystemDefaultCaptureDevice(); } wxChoice* deviceDropDownBox = dynamic_cast( wxWindow::FindWindowById(deviceDropDownBoxID, this)); // validity checking wxCHECK_RET(deviceDropDownBox != NULL, wxString::Format(_T("Unable to find the sound%s device drop down box"), deviceTypeNameAdjustment.c_str())); wxASSERT_MSG(deviceDropDownBox->IsEmpty(), wxString::Format( _T("Sound%s device drop down box has a count of %d") _T(" when it should be empty; first entry is %s"), deviceDropDownBox->GetCount(), deviceDropDownBox->GetString(0).c_str())); wxASSERT(!deviceProfileEntryName.IsEmpty()); if (availableDevices.IsEmpty()) { wxASSERT_MSG(deviceType == CAPTURE, _T("No sound devices were found on the system.")); wxLogDebug(_T("No capture devices were found on the system.")); return; } wxASSERT_MSG(!defaultDevice.IsEmpty(), wxString::Format(_T("No default sound%s device was found."), deviceTypeNameAdjustment.c_str())); // update device drop down box and select a device deviceDropDownBox->Append(availableDevices); wxString device; if (ProMan::GetProfileManager()->ProfileRead(deviceProfileEntryName, &device)) { deviceDropDownBox->SetStringSelection(device); } else { wxLogDebug(_T("Reported default sound%s device: %s"), deviceTypeNameAdjustment.c_str(), defaultDevice.c_str()); if (!defaultDevice.IsEmpty() && (deviceDropDownBox->FindString(defaultDevice) != wxNOT_FOUND)) { deviceDropDownBox->SetStringSelection(defaultDevice); } else { wxLogWarning( _T("Default sound%s device %s not found. Using first entry %s."), deviceTypeNameAdjustment.c_str(), defaultDevice.c_str(), deviceDropDownBox->GetString(0).c_str()); deviceDropDownBox->SetSelection(0); } } // update current profile if necessary if (!ProMan::GetProfileManager()->ProfileRead(deviceProfileEntryName, &device) || (device != deviceDropDownBox->GetStringSelection())) { wxLogDebug(_T("updating OpenAL sound%s device profile entry to \"%s\""), deviceTypeNameAdjustment.c_str(), deviceDropDownBox->GetStringSelection().c_str()); ProMan::GetProfileManager()->ProfileWrite( deviceProfileEntryName, deviceDropDownBox->GetStringSelection()); } } void BasicSettingsPage::SetupOpenALSection() { if ( !OpenALMan::WasCompiledIn() ) { wxLogWarning(_T("Launcher was not compiled to support OpenAL")); if (this->openALVersion != NULL) { this->openALVersion->SetLabel(_("Launcher was not compiled to support OpenAL")); } this->soundDeviceText->Disable(); this->soundDeviceCombo->Disable(); this->downloadOpenALButton->Disable(); this->detectOpenALButton->Disable(); } else if ( !OpenALMan::Initialize() ) { wxLogError(_T("Unable to initialize OpenAL")); if (this->openALVersion != NULL) { this->openALVersion->SetLabel(_("Unable to initialize OpenAL")); } this->soundDeviceText->Disable(); this->soundDeviceCombo->Disable(); this->detectOpenALButton->SetLabel(_("Redetect OpenAL")); this->downloadOpenALButton->Enable(); } else { // have working openal if (this->soundDeviceCombo->IsEmpty()) { this->InitializeSoundDeviceDropDownBox(PLAYBACK); } wxASSERT_MSG(!soundDeviceCombo->IsEmpty(), _T("sound device combo box is empty!")); if (OpenALMan::BuildHasNewSoundCode()) { if (this->captureDeviceCombo->IsEmpty()) { this->InitializeSoundDeviceDropDownBox(CAPTURE); } wxCheckBox* efxCheckBox = dynamic_cast( wxWindow::FindWindowById(ID_ENABLE_EFX, this)); wxCHECK_RET(efxCheckBox != NULL, _T("Cannot find enable EFX checkbox.")); bool enableEFX; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_OPENAL_EFX, &enableEFX, false); efxCheckBox->SetValue(enableEFX); long sampleRate; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_OPENAL_SAMPLE_RATE, &sampleRate, DEFAULT_AUDIO_OPENAL_SAMPLE_RATE); wxTextCtrl* sampleRateBox = dynamic_cast( wxWindow::FindWindowById(ID_AUDIO_SAMPLE_RATE, this)); wxCHECK_RET(sampleRateBox != NULL, _T("Cannot find sample rate text ctrl.")); if (sampleRate != DEFAULT_AUDIO_OPENAL_SAMPLE_RATE) { sampleRateBox->ChangeValue(wxString::Format(_T("%ld"), sampleRate)); } this->soundDeviceText->SetLabel(_("Playback device:")); if (this->audioOldSoundSizer->GetItem(this->soundDeviceText) != NULL) { wxASSERT(this->audioOldSoundSizer->GetItem( this->soundDeviceCombo) != NULL); this->audioOldSoundSizer->Detach(this->soundDeviceText); this->audioOldSoundSizer->Detach(this->soundDeviceCombo); this->audioNewSoundDeviceSizer->Prepend(this->soundDeviceCombo, wxSizerFlags().Expand()); this->audioNewSoundDeviceSizer->Prepend(this->soundDeviceText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); } this->audioSizer->Hide(this->audioOldSoundSizer, true); this->audioSizer->Show(this->audioNewSoundSizer, true); const wxString playbackDevice(this->soundDeviceCombo->GetStringSelection()); if (!OpenALMan::IsEFXSupported(playbackDevice)) { wxLogDebug( _T("Playback device '%s' does not support EFX.") _T(" Hiding Enable EFX checkbox."), playbackDevice.c_str()); this->audioNewSoundSizer->Hide(efxCheckBox); } if (this->captureDeviceCombo->IsEmpty()) { this->audioNewSoundDeviceSizer->Hide(this->captureDeviceCombo); this->audioNewSoundDeviceSizer->Hide(this->captureDeviceText); } // compute and set max device drop down box length this->audioNewSoundSizer->Detach(this->audioNewSoundDeviceSizer); const int minAudioNewSoundSizerWidth = ClientToWindowSize(audioNewSoundSizer->GetMinSize()).GetWidth(); wxCHECK_RET(minAudioNewSoundSizerWidth > 0, wxString::Format( _T("minimum audio new sound sizer width is invalid value %d"), minAudioNewSoundSizerWidth)); // TODO: Remove the debug statement after further testing. wxLogDebug(_T("min audio new sound sizer width: %d"), minAudioNewSoundSizerWidth); // the 75 is to cover spacing and static box borders (yes, hackish) const int maxDeviceComboLength = TAB_AREA_WIDTH - minAudioNewSoundSizerWidth - this->soundDeviceText->GetSize().GetWidth() - 75; // TODO: Remove the debug statement after further testing. wxLogDebug(_T("max device combo length: %d"), maxDeviceComboLength); this->soundDeviceCombo->SetMaxLength(maxDeviceComboLength); this->captureDeviceCombo->SetMaxLength(maxDeviceComboLength); this->audioNewSoundSizer->Prepend(this->audioNewSoundDeviceSizer); this->audioSizer->Detach(this->audioNewSoundSizer); this->audioSizer->Add(this->audioNewSoundSizer, wxSizerFlags().Proportion(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); } else { this->soundDeviceText->SetLabel(_("Sound device:")); this->soundDeviceCombo->SetMaxLength(0); // reset if (this->audioNewSoundDeviceSizer->GetItem( this->soundDeviceText) != NULL) { wxASSERT(this->audioNewSoundDeviceSizer->GetItem( this->soundDeviceCombo) != NULL); this->audioNewSoundDeviceSizer->Detach(this->soundDeviceText); this->audioNewSoundDeviceSizer->Detach(this->soundDeviceCombo); this->audioOldSoundSizer->Add(soundDeviceText, wxSizerFlags().Border(wxBOTTOM, 5)); this->audioOldSoundSizer->Add(soundDeviceCombo, wxSizerFlags().Proportion(1).Expand()); } this->audioSizer->Hide(this->audioNewSoundSizer, true); this->audioSizer->Show(this->audioOldSoundSizer, true); this->audioSizer->Detach(this->audioOldSoundSizer); this->audioSizer->Add(this->audioOldSoundSizer, wxSizerFlags().Proportion(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); } this->audioSizer->Hide(this->audioButtonsSizer, true); this->soundDeviceText->Enable(); this->soundDeviceCombo->Enable(); wxLogInfo(OpenALMan::GetCurrentVersion()); this->audioOldSoundSizer->Hide(this->openALVersion); this->Layout(); this->Refresh(); } } void BasicSettingsPage::OpenNonSCPWebSite(wxString url) { ::wxLaunchDefaultBrowser(url); } /** Client data for the Joystick Choice box. Stores the joysticks Windows ID so that we can pass it back correctly to the engine. */ class JoyNumber: public wxClientData { public: JoyNumber(unsigned int i) { this->number = i; } int GetNumber() { return static_cast(this->number); } private: unsigned int number; }; void BasicSettingsPage::SetupJoystickSection() { this->joystickSelected->Clear(); if ( !JoyMan::WasCompiledIn() ) { this->joystickSelected->Disable(); this->joystickSelected->Append(_("No Launcher Support")); #if IS_WIN32 this->joystickForceFeedback->Disable(); this->joystickDirectionalHit->Disable(); this->joystickCalibrateButton->Disable(); this->joystickDetectButton->Disable(); #endif } else if ( !JoyMan::Initialize() ) { this->joystickSelected->Disable(); this->joystickSelected->Append(_("Initialize Failed")); #if IS_WIN32 this->joystickForceFeedback->Disable(); this->joystickDirectionalHit->Disable(); this->joystickCalibrateButton->Disable(); this->joystickDetectButton->Enable(); #endif } else { this->joystickSelected ->Append(_("No Joystick"), new JoyNumber(DEFAULT_JOYSTICK_ID)); for ( unsigned int i = 0; i < JoyMan::NumberOfJoysticks(); i++ ) { if ( JoyMan::IsJoystickPluggedIn(i) ) { this->joystickSelected ->Append(JoyMan::JoystickName(i), new JoyNumber(i)); } } if ( JoyMan::NumberOfPluggedInJoysticks() == 0 ) { this->joystickSelected->SetSelection(0); this->joystickSelected->Disable(); #if IS_WIN32 this->joystickForceFeedback->Disable(); this->joystickDirectionalHit->Disable(); this->joystickCalibrateButton->Disable(); this->joystickDetectButton->Enable(); #endif } else { long profileJoystick; unsigned int i; this->joystickSelected->Enable(); #if IS_WIN32 this->joystickDetectButton->Enable(); #endif ProMan::GetProfileManager()-> ProfileRead(PRO_CFG_JOYSTICK_ID, &profileJoystick, DEFAULT_JOYSTICK_ID, true); // set current joystick for ( i = 0; i < this->joystickSelected->GetCount(); i++ ) { JoyNumber* data = dynamic_cast( this->joystickSelected->GetClientObject(i)); wxCHECK2_MSG( data != NULL, continue, _T("JoyNumber is not the clientObject in joystickSelected")); if ( profileJoystick == data->GetNumber() ) { this->joystickSelected->SetSelection(i); this->SetupControlsForJoystick(i); return; // All joystick controls are now setup } } // Getting here means that the joystick is no longer installed // or is not plugged in if ( JoyMan::IsJoystickPluggedIn(profileJoystick) ) { wxLogWarning(_T("Last selected joystick is not plugged in")); } else { wxLogWarning(_T("Last selected joystick does not seem to be installed")); } // set to no joystick (the first selection) this->joystickSelected->SetSelection(0); this->SetupControlsForJoystick(0); } } } void BasicSettingsPage::SetupControlsForJoystick(unsigned int i) { JoyNumber* joynumber = dynamic_cast( this->joystickSelected->GetClientObject(i)); wxCHECK_RET( joynumber != NULL, _T("JoyNumber is not joystickSelected's client data")); #if IS_WIN32 // calibration and force feedback don't work on OS X or Linux at the moment if ( JoyMan::HasCalibrateTool(joynumber->GetNumber()) ) { this->joystickCalibrateButton->Enable(); } else { this->joystickCalibrateButton->Disable(); } if ( JoyMan::SupportsForceFeedback(joynumber->GetNumber()) ) { bool ff, direct; ProMan::GetProfileManager()->ProfileRead( PRO_CFG_JOYSTICK_DIRECTIONAL, &direct, DEFAULT_JOYSTICK_DIRECTIONAL, true); ProMan::GetProfileManager()->ProfileRead( PRO_CFG_JOYSTICK_FORCE_FEEDBACK, &ff, DEFAULT_JOYSTICK_FORCE_FEEDBACK, true); this->joystickDirectionalHit->SetValue(direct); this->joystickForceFeedback->SetValue(ff); this->joystickDirectionalHit->Enable(); this->joystickForceFeedback->Enable(); } else { this->joystickDirectionalHit->Disable(); this->joystickForceFeedback->Disable(); } #endif ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_JOYSTICK_ID, static_cast(joynumber->GetNumber())); } void BasicSettingsPage::OnSelectJoystick( wxCommandEvent &WXUNUSED(event)) { this->SetupControlsForJoystick( this->joystickSelected->GetSelection()); } void BasicSettingsPage::OnCheckForceFeedback( wxCommandEvent &event) { ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_JOYSTICK_FORCE_FEEDBACK, event.IsChecked()); } void BasicSettingsPage::OnCheckDirectionalHit( wxCommandEvent &event) { ProMan::GetProfileManager()->ProfileWrite(PRO_CFG_JOYSTICK_DIRECTIONAL, event.IsChecked()); } void BasicSettingsPage::OnCalibrateJoystick( wxCommandEvent &WXUNUSED(event)) { JoyNumber *data = dynamic_cast( this->joystickSelected->GetClientObject( this->joystickSelected->GetSelection())); wxCHECK_RET( data != NULL, _T("joystickSelected does not have JoyNumber as clientdata")); JoyMan::LaunchCalibrateTool(data->GetNumber()); } void BasicSettingsPage::OnDetectJoystick(wxCommandEvent &WXUNUSED(event)) { if ( JoyMan::DeInitialize() ) { this->SetupJoystickSection(); } } //////////// ProxyChoice ProxyChoice::ProxyChoice(wxWindow *parent, wxWindowID id) :wxChoicebook(parent, id) { wxString type; // NOTE: not adding writeBackIfAbsent to GlobalRead calls, since proxy box not in GUI ProMan::GetProfileManager()->GlobalRead(GBL_CFG_PROXY_TYPE, &type, _T("none")); wxPanel* noneProxyPanel = new wxPanel(this); this->AddPage(noneProxyPanel, _("None")); /// Manual Proxy wxPanel* manualProxyPanel = new wxPanel(this); wxStaticText* proxyHttpText = new wxStaticText(manualProxyPanel, wxID_ANY, _("HTTP Proxy:")); wxString server; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_PROXY_SERVER, &server, _T("")); wxTextCtrl* proxyHttpServer = new wxTextCtrl(manualProxyPanel, ID_PROXY_HTTP_SERVER, server); wxStaticText* proxyHttpPortText = new wxStaticText(manualProxyPanel, wxID_ANY, _("Port:")); long port; ProMan::GetProfileManager()->GlobalRead(GBL_CFG_PROXY_PORT, &port, 0); wxTextCtrl* proxyHttpPort = new wxTextCtrl(manualProxyPanel, ID_PROXY_HTTP_PORT, wxString::Format(_T("%ld"), port)); wxBoxSizer* manualProxyPortSizer = new wxBoxSizer(wxHORIZONTAL); manualProxyPortSizer->Add(proxyHttpPortText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); manualProxyPortSizer->Add(proxyHttpPort, wxSizerFlags().Proportion(1)); wxBoxSizer* manualProxySizer = new wxBoxSizer(wxVERTICAL); manualProxySizer->Add(proxyHttpText); manualProxySizer->Add(proxyHttpServer, wxSizerFlags().Expand()); manualProxySizer->Add(manualProxyPortSizer, wxSizerFlags().Expand()); manualProxySizer->SetContainingWindow(this); manualProxyPanel->SetSizer(manualProxySizer); this->AddPage(manualProxyPanel, _T("Manual")); if ( type == _T("manual") ) { this->SetSelection(1); } else { this->SetSelection(0); } } ProxyChoice::~ProxyChoice() { } BEGIN_EVENT_TABLE(ProxyChoice, wxChoicebook) EVT_TEXT(ID_PROXY_HTTP_SERVER, ProxyChoice::OnChangeServer) EVT_TEXT(ID_PROXY_HTTP_PORT, ProxyChoice::OnChangePort) EVT_CHOICEBOOK_PAGE_CHANGED(ID_PROXY_TYPE, ProxyChoice::OnProxyTypeChange) END_EVENT_TABLE() void ProxyChoice::OnChangeServer(wxCommandEvent &event) { wxString str = event.GetString(); if ( str == wxEmptyString ) { // do nothing } else { ProMan::GetProfileManager()->GlobalWrite(GBL_CFG_PROXY_SERVER, str); } } void ProxyChoice::OnChangePort(wxCommandEvent &event) { int port = event.GetInt(); ProMan::GetProfileManager()->GlobalWrite(GBL_CFG_PROXY_PORT, static_cast(port)); } void ProxyChoice::OnProxyTypeChange(wxChoicebookEvent &event) { int page = event.GetSelection(); wxString str; switch (page) { case 0: str = _T("none"); break; case 1: str = _T("manual"); break; default: wxFAIL_MSG(wxString::Format(_T("Proxy type changed to invalid id %d"), page)); return; } ProMan::GetProfileManager()->GlobalWrite(GBL_CFG_PROXY_TYPE, str); } wxlauncher-0.9.4/code/tabs/BasicSettingsPage.h0000644000000000000000000001146012155177255021231 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASICSETTINGSPAGE_H #define BASICSETTINGSPAGE_H #include #include #include "controls/TruncatableChoice.h" #include "global/ModDefaults.h" class BasicSettingsPage : public wxPanel { public: BasicSettingsPage(wxWindow* parent); ~BasicSettingsPage(); void OnSelectTC(wxCommandEvent &event); void OnTCChanged(wxCommandEvent &event); void OnSelectExecutable(wxCommandEvent &event); void OnPressExecutableChoiceRefreshButton(wxCommandEvent &event); void OnSelectFredExecutable(wxCommandEvent &event); void OnPressFredExecutableChoiceRefreshButton(wxCommandEvent &event); void OnSelectVideoResolution(wxCommandEvent &event); void OnSelectVideoDepth(wxCommandEvent &event); void OnSelectVideoAnisotropic(wxCommandEvent &event); void OnSelectVideoAntiAlias(wxCommandEvent &event); void OnSelectVideoTextureFilter(wxCommandEvent &event); void OnSelectSpeechVoice(wxCommandEvent &event); void OnChangeSpeechVolume(wxCommandEvent &event); void OnPlaySpeechText(wxCommandEvent &event); void OnToggleSpeechInTechroom(wxCommandEvent &event); void OnToggleSpeechInBriefing(wxCommandEvent &event); void OnToggleSpeechInGame(wxCommandEvent &event); void OnToggleSpeechInMulti(wxCommandEvent &event); void OnGetMoreVoices(wxCommandEvent &event); void OnSelectNetworkType(wxCommandEvent &event); void OnSelectNetworkSpeed(wxCommandEvent &event); void OnChangePort(wxCommandEvent &event); void OnChangeIP(wxCommandEvent &event); void OnSelectSoundDevice(wxCommandEvent &event); void OnSelectCaptureDevice(wxCommandEvent &event); void OnToggleEnableEFX(wxCommandEvent &event); void OnChangeSampleRate(wxCommandEvent &event); void OnDownloadOpenAL(wxCommandEvent& event); void OnDetectOpenAL(wxCommandEvent& event); void OnSelectJoystick(wxCommandEvent& event); void OnCheckForceFeedback(wxCommandEvent& event); void OnCheckDirectionalHit(wxCommandEvent& event); void OnCalibrateJoystick(wxCommandEvent& event); void OnDetectJoystick(wxCommandEvent& event); void ProfileChanged(wxCommandEvent& event); void OnFlagFileProcessingStatusChanged(wxCommandEvent& event); void OnFREDEnabledChanged(wxCommandEvent& event); void OnActiveModChanged(wxCommandEvent& event); private: static void FillFSOExecutableDropBox(wxChoice* exeChoice, wxFileName path); static void FillFredExecutableDropBox(wxChoice* exeChoice, wxFileName path); static void FillExecutableDropBox(wxChoice* exeChoice, wxArrayString exes); void SetUpResolution( long minHorizRes = DEFAULT_MOD_RESOLUTION_MIN_HORIZONTAL_RES, long minVertRes = DEFAULT_MOD_RESOLUTION_MIN_VERTICAL_RES); static void FillResolutionDropBox(wxChoice* resChoice, long minHorizRes, long minVertRes); static int GetMaxSupportedResolution(const wxChoice& resChoice, long& width, long& height); enum ReasonForExecutableDisabling { MISSING_TC_ROOT_FOLDER, NONEXISTENT_TC_ROOT_FOLDER, INVALID_TC_ROOT_FOLDER }; void InitializeMemberVariables(); void DisableExecutableChoiceControls(const ReasonForExecutableDisabling reason); void OnCurrentBinaryChanged(wxCommandEvent& event); void OnCurrentFredBinaryChanged(wxCommandEvent& event); void ShowSettings(const bool showSettings); void OpenNonSCPWebSite(wxString url); enum SoundDeviceType { PLAYBACK, CAPTURE }; void InitializeSoundDeviceDropDownBox(const SoundDeviceType deviceType); void SetupOpenALSection(); void SetupJoystickSection(); void SetupControlsForJoystick(unsigned int i); DECLARE_EVENT_TABLE(); private: wxStaticText* soundDeviceText; TruncatableChoice* soundDeviceCombo; wxStaticText* captureDeviceText; TruncatableChoice* captureDeviceCombo; wxStaticText* openALVersion; wxButton* downloadOpenALButton; wxButton* detectOpenALButton; wxSizer* audioButtonsSizer; wxSizer* audioOldSoundSizer; wxSizer* audioNewSoundSizer; wxSizer* audioNewSoundDeviceSizer; wxSizer* audioSizer; wxChoice* joystickSelected; #if IS_WIN32 wxCheckBox* joystickForceFeedback; wxCheckBox* joystickDirectionalHit; wxButton* joystickCalibrateButton; wxButton* joystickDetectButton; #endif bool isTcRootFolderValid; bool isCurrentBinaryValid; bool isCurrentFredBinaryValid; }; #endif wxlauncher-0.9.4/code/tabs/InstallPage.cpp0000644000000000000000000000216412155177255020431 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "global/ids.h" #include "apis/ProfileManager.h" #include "tabs/InstallPage.h" #include "apis/HelpManager.h" #include "generated/configure_launcher.h" #include "global/MemoryDebugging.h" // Last include for memory debugging InstallPage::InstallPage(wxWindow* parent): wxPanel(parent, wxID_ANY) { } BEGIN_EVENT_TABLE(InstallPage, wxPanel) END_EVENT_TABLE()wxlauncher-0.9.4/code/tabs/InstallPage.h0000644000000000000000000000161112155177255020072 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INSTALLPAGE_H #define INSTALLPAGE_H #include class InstallPage: public wxPanel { public: InstallPage(wxWindow* parent); DECLARE_EVENT_TABLE() }; #endifwxlauncher-0.9.4/code/tabs/ModsPage.cpp0000644000000000000000000001456412155177255017734 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "tabs/ModsPage.h" #include "global/ids.h" #include "global/ProfileKeys.h" #include "controls/ModList.h" #include "apis/SkinManager.h" #include "apis/ProfileManager.h" #include "apis/TCManager.h" #include "datastructures/FSOExecutable.h" #include "generated/configure_launcher.h" #include "global/MemoryDebugging.h" // Last include for memory debugging const int TEXT_WRAP_WIDTH = TAB_AREA_WIDTH - 225; ModsPage::ModsPage(wxWindow* parent): wxPanel(parent, wxID_ANY) { wxLogDebug(_T("ModsPage is at %p."), this); SkinSystem::RegisterTCSkinChanged(this); TCManager::RegisterTCChanged(this); wxCommandEvent nullEvent; this->OnTCChanged(nullEvent); } BEGIN_EVENT_TABLE(ModsPage, wxPanel) EVT_COMMAND(wxID_NONE, EVT_TC_CHANGED, ModsPage::OnTCChanged) EVT_COMMAND(wxID_NONE, EVT_TC_SKIN_CHANGED, ModsPage::OnTCSkinChanged) END_EVENT_TABLE() void ModsPage::OnTCChanged(wxCommandEvent &WXUNUSED(event)) { wxString tcPath; ProMan::GetProfileManager()->ProfileRead(PRO_CFG_TC_ROOT_FOLDER, &tcPath, wxEmptyString); wxSizer* currentSizer = this->GetSizer(); if ( currentSizer != NULL ) { currentSizer->Clear(true); } if ( tcPath.IsEmpty()) { SkinSystem::GetSkinSystem()->ResetTCSkin(); wxStaticText* noTC = new wxStaticText(this, wxID_ANY, _("To view a list of available mods, you must first select the root folder of an FS2 Open game on the Basic Settings page."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); noTC->SetFont(SkinSystem::GetSkinSystem()->GetMessageFont()); noTC->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); noTC->Wrap(TEXT_WRAP_WIDTH); wxStaticBitmap* infoImage = new wxStaticBitmap(this, ID_MODS_PAGE_INFO_IMAGE, SkinSystem::GetSkinSystem()->GetBigInfoIcon()); wxBoxSizer* noTCSizer = new wxBoxSizer(wxVERTICAL); noTCSizer->AddStretchSpacer(1); noTCSizer->Add(infoImage,0, wxALL | wxCENTER); noTCSizer->AddSpacer(10); noTCSizer->Add(noTC, 0, wxALL | wxCENTER); noTCSizer->AddStretchSpacer(1); #if IS_LINUX this->SetSizerAndFit(noTCSizer); #else this->SetSizer(noTCSizer); #endif } else if ( !wxFileName::DirExists(tcPath) ) { SkinSystem::GetSkinSystem()->ResetTCSkin(); wxStaticText* nonexistentTC = new wxStaticText(this, wxID_ANY, _("The selected root folder does not exist.\n\nSelect a valid root folder for an FS2 Open game on the Basic Settings page."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); nonexistentTC->SetFont(SkinSystem::GetSkinSystem()->GetMessageFont()); nonexistentTC->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); nonexistentTC->Wrap(TEXT_WRAP_WIDTH); wxStaticBitmap* warningImage = new wxStaticBitmap(this, ID_MODS_PAGE_WARNING_IMAGE, SkinSystem::GetSkinSystem()->GetBigWarningIcon()); wxBoxSizer* nonexistentTCSizer = new wxBoxSizer(wxVERTICAL); nonexistentTCSizer->AddStretchSpacer(1); nonexistentTCSizer->Add(warningImage,0, wxALL | wxCENTER); nonexistentTCSizer->AddSpacer(10); nonexistentTCSizer->Add(nonexistentTC, 0, wxALL | wxCENTER); nonexistentTCSizer->AddStretchSpacer(1); #if IS_LINUX this->SetSizerAndFit(nonexistentTCSizer); #else this->SetSizer(nonexistentTCSizer); #endif } else if ( !FSOExecutable::HasFSOExecutables(wxFileName(tcPath, wxEmptyString)) ) { SkinSystem::GetSkinSystem()->ResetTCSkin(); wxStaticText* invalidTC = new wxStaticText(this, wxID_ANY, _("The selected root folder does not contain any FS2 Open executables.\n\nOn the Basic Settings page, either select a different root folder, or add FS2 Open executables to the selected root folder and press the Refresh button."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); invalidTC->SetFont(SkinSystem::GetSkinSystem()->GetMessageFont()); invalidTC->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); invalidTC->Wrap(TEXT_WRAP_WIDTH); wxStaticBitmap* warningImage = new wxStaticBitmap(this, ID_MODS_PAGE_WARNING_IMAGE, SkinSystem::GetSkinSystem()->GetBigWarningIcon()); wxBoxSizer* invalidTCSizer = new wxBoxSizer(wxVERTICAL); invalidTCSizer->AddStretchSpacer(1); invalidTCSizer->Add(warningImage,0, wxALL | wxCENTER); invalidTCSizer->AddSpacer(10); invalidTCSizer->Add(invalidTC, 0, wxALL | wxCENTER); invalidTCSizer->AddStretchSpacer(1); #if IS_LINUX this->SetSizerAndFit(invalidTCSizer); #else this->SetSizer(invalidTCSizer); #endif } else { #if 0 wxStaticText* header = new wxStaticText(this, wxID_ANY, _("Installed mods. Click on Install/Update in the left to search, download, and install additional mods and updates."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); header->Wrap(TAB_AREA_WIDTH); #endif wxSize modGridSize(TAB_AREA_WIDTH - 20, TAB_AREA_HEIGHT); // FIXME for left and right borders of 5 pixels each -- but why does it have to be 20? ModList* modGrid = new ModList(this, modGridSize, tcPath); modGrid->SetMinSize(modGridSize); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); #if 0 sizer->Add(header); #endif sizer->Add(modGrid, wxSizerFlags().Center().Border(wxALL,5)); this->SetMaxSize(wxSize(TAB_AREA_WIDTH, TAB_AREA_HEIGHT)); this->SetSizer(sizer); } this->Layout(); } void ModsPage::OnTCSkinChanged(wxCommandEvent &WXUNUSED(event)) { wxStaticBitmap* infoImage = dynamic_cast( wxWindow::FindWindowById(ID_MODS_PAGE_INFO_IMAGE, this)); if (infoImage != NULL) { infoImage->SetBitmap(SkinSystem::GetSkinSystem()->GetBigInfoIcon()); } wxStaticBitmap* warningImage = dynamic_cast( wxWindow::FindWindowById(ID_MODS_PAGE_WARNING_IMAGE, this)); if (warningImage != NULL) { warningImage->SetBitmap(SkinSystem::GetSkinSystem()->GetBigWarningIcon()); } } wxlauncher-0.9.4/code/tabs/ModsPage.h0000644000000000000000000000174012155177255017371 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MODSPAGE_H #define MODSPAGE_H #include class ModsPage: public wxPanel { public: ModsPage(wxWindow* parent); void OnTCChanged(wxCommandEvent &event); void OnTCSkinChanged(wxCommandEvent &event); private: DECLARE_EVENT_TABLE(); }; #endifwxlauncher-0.9.4/code/tabs/WelcomePage.cpp0000644000000000000000000006614712155177255020431 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include "generated/configure_launcher.h" #include "tabs/WelcomePage.h" #include "controls/StatusBar.h" #include "global/ProfileKeys.h" #include "apis/HelpManager.h" #include "apis/SkinManager.h" #include "global/MemoryDebugging.h" // Last include for memory debugging #define TIME_BETWEEN_NEWS_UPDATES wxTimeSpan::Day() class CloneProfileDialog: public wxDialog { public: CloneProfileDialog(wxWindow* parent); const wxString GetSourceProfileName(); const wxString& GetNewProfileName(); bool UseProfileCloning() const { return useProfileCloning; } void OnUpdateText(wxCommandEvent& event); void OnPressEnterKey(wxCommandEvent& event); void OnClickCloneCheckbox(wxCommandEvent& event); private: bool useProfileCloning; wxString newProfileName; wxButton *createButton; wxChoice *cloneFrom; bool NewNameIsValid(); DECLARE_EVENT_TABLE(); }; class DeleteProfileDialog: public wxDialog { public: DeleteProfileDialog(wxWindow* parent, wxString name); }; /** Class that manages the header image for the welcome tab. */ class HeaderBitmap: public wxPanel { public: HeaderBitmap(wxWindow* parent): wxPanel(parent, wxID_ANY) { SkinSystem::GetSkinSystem()->RegisterTCSkinChanged(this); UpdateBanner(); } virtual void OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); dc.DrawBitmap(this->bitmap, (this->GetSize().GetWidth()/2) - (this->bitmap.GetWidth()/2), 0); } private: void UpdateBanner() { this->bitmap = SkinSystem::GetSkinSystem()->GetBanner(); wxASSERT_MSG(this->bitmap.IsOk(), _("Loaded bitmap is invalid.")); this->SetMinSize(wxSize(bitmap.GetWidth(), bitmap.GetHeight())); } void OnTCSkinChanged(wxCommandEvent &WXUNUSED(event)) { UpdateBanner(); } wxBitmap bitmap; DECLARE_EVENT_TABLE(); }; BEGIN_EVENT_TABLE(HeaderBitmap, wxPanel) EVT_PAINT(HeaderBitmap::OnPaint) EVT_COMMAND(wxID_NONE, EVT_TC_SKIN_CHANGED, HeaderBitmap::OnTCSkinChanged) END_EVENT_TABLE() BEGIN_EVENT_TABLE(WelcomePage, wxPanel) EVT_HTML_LINK_CLICKED(ID_SUMMARY_HTML_PANEL, WelcomePage::LinkClicked) EVT_HTML_CELL_HOVER(ID_SUMMARY_HTML_PANEL, WelcomePage::LinkHover) EVT_HTML_LINK_CLICKED(ID_NEWS_HTML_PANEL, WelcomePage::LinkClicked) EVT_HTML_CELL_HOVER(ID_NEWS_HTML_PANEL, WelcomePage::LinkHover) EVT_COMMAND( wxID_NONE, EVT_PROFILE_CHANGE, WelcomePage::ProfileCountChanged) EVT_COMMAND( wxID_NONE, EVT_CURRENT_PROFILE_CHANGED, WelcomePage::ProfileCountChanged) EVT_COMMAND( wxID_NONE, EVT_TC_SKIN_CHANGED, WelcomePage::OnTCSkinChanged) // Profile controls EVT_BUTTON(ID_NEW_PROFILE, WelcomePage::ProfileButtonClicked) EVT_BUTTON(ID_DELETE_PROFILE, WelcomePage::ProfileButtonClicked) EVT_BUTTON(ID_SAVE_PROFILE, WelcomePage::ProfileButtonClicked) EVT_CHECKBOX(ID_SAVE_DEFAULT_CHECK, WelcomePage::AutoSaveProfilesChecked) EVT_CHOICE(ID_PROFILE_COMBO, WelcomePage::ProfileChanged) EVT_CHECKBOX(ID_NET_DOWNLOAD_NEWS, WelcomePage::OnDownloadNewsCheck) EVT_IDLE(WelcomePage::UpdateNews) END_EVENT_TABLE() BEGIN_EVENT_TABLE(CloneProfileDialog, wxDialog) EVT_TEXT(ID_CLONE_PROFILE_NEWNAME, CloneProfileDialog::OnUpdateText) EVT_TEXT_ENTER(ID_CLONE_PROFILE_NEWNAME, CloneProfileDialog::OnPressEnterKey) EVT_CHECKBOX(ID_CLONE_PROFILE_CHECKBOX, CloneProfileDialog::OnClickCloneCheckbox) END_EVENT_TABLE() WelcomePage::WelcomePage(wxWindow* parent): wxPanel(parent, wxID_ANY) { // member varirable init this->lastLinkInfo = NULL; ProMan* proman = ProMan::GetProfileManager(); wxLogDebug(_T("WelcomePage is at %p."), this); SkinSystem::RegisterTCSkinChanged(this); #if 0 // language wxStaticText* launcherLanguageText = new wxStaticText(this, wxID_ANY, _("Launcher language:")); wxChoice* launcherLanguageCombo = new wxChoice(this, wxID_ANY); launcherLanguageCombo->Insert(_("English (US)"), 0); launcherLanguageCombo->SetSelection(0); launcherLanguageCombo->Disable(); wxBoxSizer* languageSizer = new wxBoxSizer(wxHORIZONTAL); languageSizer->AddStretchSpacer(2); languageSizer->Add(launcherLanguageText); languageSizer->Add(launcherLanguageCombo); #endif // header image HeaderBitmap* header = new HeaderBitmap(this); // Info wxStaticBox* generalBox = new wxStaticBox(this, wxID_ANY, _("")); wxHtmlWindow* general = new wxHtmlWindow(this, ID_SUMMARY_HTML_PANEL, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER ); general->SetPage(SkinSystem::GetSkinSystem()->GetWelcomeText()); general->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(WelcomePage::OnMouseOut)); wxStaticBoxSizer* generalSizer = new wxStaticBoxSizer(generalBox, wxVERTICAL); generalSizer->SetMinSize(wxSize(-1, 175)); generalSizer->Add(general, wxSizerFlags().Expand().Proportion(1)); // Profiles wxStaticBox* profileBox = new wxStaticBox(this, wxID_ANY, _("Profile management")); wxChoice* profileCombo = new wxChoice(this, ID_PROFILE_COMBO, wxDefaultPosition, wxDefaultSize, 0, // number of choices 0, // choices wxCB_SORT); profileCombo->Append(proman->GetAllProfileNames()); proman->AddEventHandler(this); wxString lastselected; proman->GlobalRead(GBL_CFG_MAIN_LASTPROFILE, &lastselected, ProMan::DEFAULT_PROFILE_NAME); profileCombo->SetStringSelection(lastselected); wxButton* newButton = new wxButton(this, ID_NEW_PROFILE, _("New...")); wxButton* deleteButton = new wxButton(this, ID_DELETE_PROFILE, _("Delete...")); wxButton* saveButton = new wxButton(this, ID_SAVE_PROFILE, _("Save")); wxCheckBox* autoSaveProfilesCheck = new wxCheckBox(this, ID_SAVE_DEFAULT_CHECK, _("Automatically save profiles")); bool autosave; proman->GlobalRead(GBL_CFG_MAIN_AUTOSAVEPROFILES, &autosave, true, true); autoSaveProfilesCheck->SetValue(autosave); wxCommandEvent autoSaveEvent(wxEVT_COMMAND_CHECKBOX_CLICKED, ID_SAVE_DEFAULT_CHECK); autoSaveEvent.SetInt(autosave); this->AutoSaveProfilesChecked(autoSaveEvent); wxBoxSizer* profileButtonsSizer = new wxBoxSizer(wxHORIZONTAL); profileButtonsSizer->Add(newButton); profileButtonsSizer->Add(deleteButton); profileButtonsSizer->Add(saveButton); profileButtonsSizer->AddStretchSpacer(1); profileButtonsSizer->Add(autoSaveProfilesCheck, 0, wxALIGN_CENTER_VERTICAL); wxStaticBoxSizer* profileVerticalSizer = new wxStaticBoxSizer(profileBox, wxVERTICAL); profileVerticalSizer->Add(profileCombo, 0, wxALL | wxEXPAND, 5); profileVerticalSizer->Add(profileButtonsSizer, 0, wxALL | wxEXPAND, 5); // Latest news wxStaticBox* newsBox = new wxStaticBox(this, ID_NEWS_BOX, SkinSystem::GetSkinSystem()->GetNewsSource().GetLabel()); wxHtmlWindow* newsView = new wxHtmlWindow(this, ID_NEWS_HTML_PANEL); newsView->SetPage(_T("")); newsView->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(WelcomePage::OnMouseOut)); wxCheckBox* updateNewsCheck = new wxCheckBox(this, ID_NET_DOWNLOAD_NEWS, _("Automatically retrieve news at startup")); updateNewsCheck->SetToolTip(_("Check this to have the launcher retrieve the news the next time it runs")); updateNewsCheck->SetValue(this->getOrPromptUpdateNews()); this->needToUpdateNews = true; wxStaticBoxSizer* newsSizer = new wxStaticBoxSizer(newsBox, wxVERTICAL); newsSizer->Add(newsView, wxSizerFlags().Expand().Proportion(1).Border(wxLEFT|wxRIGHT, 5)); newsSizer->Add(updateNewsCheck, #if IS_WIN32 wxSizerFlags().Right().Border(wxTOP|wxRIGHT|wxBOTTOM,5)); #else // helps ensure that all four news items appear without having to scroll wxSizerFlags().Right().Border(wxTOP|wxRIGHT,5)); #endif // Final layout wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); #if 0 sizer->Add(languageSizer); #endif sizer->Add(header, wxSizerFlags().Proportion(0).Expand().Center().Border(wxTOP, 5)); sizer->Add(generalSizer, wxSizerFlags().Proportion(0).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); sizer->Add(profileVerticalSizer, wxSizerFlags().Proportion(0).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); sizer->Add(newsSizer, wxSizerFlags().Expand().Proportion(1).Border(wxLEFT|wxRIGHT|wxBOTTOM, 5)); // mildly hackish way of ensuring a minimum launcher window size on all platforms #if IS_WIN32 sizer->SetMinSize(wxSize(TAB_AREA_WIDTH-10, TAB_AREA_HEIGHT-5)); #else // to accommodate larger fonts/widgets on OS X/Linux sizer->SetMinSize(wxSize(TAB_AREA_WIDTH-10, TAB_AREA_HEIGHT+10)); #endif this->SetSizerAndFit(sizer); this->Layout(); } void WelcomePage::LinkClicked(wxHtmlLinkEvent &event) { wxHtmlLinkInfo info = event.GetLinkInfo(); wxString rest; if (info.GetHref().StartsWith(_T("help://"), &rest)) { HelpManager::OpenHelpByString(rest); } else { wxLaunchDefaultBrowser(info.GetHref()); } } void WelcomePage::LinkHover(wxHtmlCellEvent &event) { wxHtmlLinkInfo* info = event.GetCell()->GetLink(); // will be NULL if not a link. wxFrame *frame = dynamic_cast(this->GetParent()->GetParent()); wxASSERT( frame != NULL ); StatusBar *bar = dynamic_cast(frame->GetStatusBar()); wxASSERT( bar != NULL ); if (info == NULL) { if ( this->lastLinkInfo == NULL ) { // do nothing } else { bar->EndToolTipStatusText(); this->lastLinkInfo = NULL; } } else { if ( info == this->lastLinkInfo ) { // do nothing } else if ( this->lastLinkInfo == NULL ) { bar->StartToolTipStatusText(info->GetHref()); this->lastLinkInfo = reinterpret_cast(info); } else { // still on a URL but we potentially changed the URL bar->EndToolTipStatusText(); bar->StartToolTipStatusText(info->GetHref()); this->lastLinkInfo = reinterpret_cast(info); } } } void WelcomePage::OnMouseOut(wxMouseEvent &WXUNUSED(event)) { WXUNUSED(event); // clear url from status bar. wxFrame *frame = dynamic_cast(this->GetParent()->GetParent()->GetParent()); wxASSERT( frame != NULL ); StatusBar *bar = dynamic_cast(frame->GetStatusBar()); wxASSERT( bar != NULL ); bar->EndToolTipStatusText(); } /** Calls the dialogs for cloning, saving or deleting profiles. */ void WelcomePage::ProfileButtonClicked(wxCommandEvent& event) { wxChoice* profileCombo = dynamic_cast(wxWindow::FindWindowById(ID_PROFILE_COMBO, this)); ProMan *proman = ProMan::GetProfileManager(); switch(event.GetId()) { case ID_NEW_PROFILE: cloneNewProfile(profileCombo, proman); break; case ID_DELETE_PROFILE: deleteProfile(profileCombo, proman); break; case ID_SAVE_PROFILE: proman->SaveCurrentProfile(); break; default: wxCHECK_RET( false, _("Reached impossible location.\nHandler has been attached to a button that it cannot handle.")); } } void WelcomePage::AutoSaveProfilesChecked(wxCommandEvent& event) { ProMan* proman = ProMan::GetProfileManager(); if ( event.IsChecked() ) { proman->SetAutoSave(true); proman->GlobalWrite(GBL_CFG_MAIN_AUTOSAVEPROFILES, true); proman->SaveCurrentProfile(); wxLogStatus(_("Now autosaving profiles.")); } else { proman->SetAutoSave(false); proman->GlobalWrite(GBL_CFG_MAIN_AUTOSAVEPROFILES, false); wxLogStatus(_("No longer autosaving profiles.")); } } void WelcomePage::ProfileChanged(wxCommandEvent& WXUNUSED(event)) { wxChoice* profileCombo = dynamic_cast(wxWindow::FindWindowById(ID_PROFILE_COMBO, this)); wxString newProfile = profileCombo->GetStringSelection(); ProMan* proman = ProMan::GetProfileManager(); if (newProfile == proman->GetCurrentName()) { // user selected the current profile, so no need to switch, just return return; } if ( proman->DoesProfileExist(newProfile) ) { if ( proman->NeedToPromptToSave() ) { int response = wxMessageBox( ProMan::GetSaveDialogMessageText(ProMan::ON_PROFILE_SWITCH, proman->GetCurrentName()), ProMan::GetSaveDialogCaptionText(ProMan::ON_PROFILE_SWITCH, proman->GetCurrentName()), wxYES_NO|wxCANCEL, this); if ( response == wxYES ) { wxLogDebug(_T("saving profile %s before switching to profile %s"), proman->GetCurrentName().c_str(), newProfile.c_str()); proman->SaveCurrentProfile(); } else if ( response == wxCANCEL ) { wxLogInfo(_T("Cancelled switch to profile %s."), newProfile.c_str()); profileCombo->SetSelection(profileCombo->FindString(proman->GetCurrentName())); return; } else { wxLogWarning(_T("switching from profile %s to profile %s without saving changes"), proman->GetCurrentName().c_str(), newProfile.c_str()); } } if ( proman->SwitchTo(newProfile) ) { wxLogMessage(_T("Profile %s is now the active profile."), proman->GetCurrentName().c_str()); } else { wxLogError(_T("Unable to switch to %s, staying on %s."), newProfile.c_str(), proman->GetCurrentName().c_str()); } } else { wxLogError(_T("Profile does not exist. Use Clone to create profile first")); } } void WelcomePage::cloneNewProfile(wxChoice* profileCombo, ProMan* proman) { if ( proman->NeedToPromptToSave() ) { int response = wxMessageBox( ProMan::GetSaveDialogMessageText(ProMan::ON_PROFILE_CREATE, proman->GetCurrentName()), ProMan::GetSaveDialogCaptionText(ProMan::ON_PROFILE_CREATE, proman->GetCurrentName()), wxYES_NO|wxCANCEL, this); if ( response == wxYES ) { wxLogDebug(_T("Saving current profile %s before opening create profile dialog."), proman->GetCurrentName().c_str()); proman->SaveCurrentProfile(); } else if ( response == wxCANCEL ) { wxLogInfo(_T("Cancelled opening create profile dialog.")); return; } else { wxLogWarning(_T("Reverting unsaved changes to current profile %s before opening create profile dialog"), proman->GetCurrentName().c_str()); proman->RevertCurrentProfile(); } } CloneProfileDialog cloneDialog(this); if ( cloneDialog.ShowModal() == cloneDialog.GetAffirmativeId() ) { wxLogDebug(_T("User clicked %s"), cloneDialog.UseProfileCloning() ? _T("clone") : _T("create")); if ( proman->CreateProfile( cloneDialog.GetNewProfileName(), cloneDialog.GetSourceProfileName()) ) { if (cloneDialog.UseProfileCloning()) { wxLogStatus(_("Cloned profile '%s' from '%s'"), cloneDialog.GetNewProfileName().c_str(), cloneDialog.GetSourceProfileName().c_str()); } else { wxLogStatus(_("Created profile '%s'"), cloneDialog.GetNewProfileName().c_str()); } proman->SwitchTo(cloneDialog.GetNewProfileName()); } else { // profile creation was unsuccessful if (cloneDialog.UseProfileCloning()) { wxLogError(_("Unable to clone profile '%s' from '%s'. See log for details."), cloneDialog.GetNewProfileName().c_str(), cloneDialog.GetSourceProfileName().c_str()); } else { wxLogError(_("Unable to create profile '%s'. See log for details."), cloneDialog.GetNewProfileName().c_str()); } } } else { wxLogStatus(_("Profile creation aborted")); } } void WelcomePage::deleteProfile(wxChoice* profileCombo, ProMan* proman) { wxString nametodelete = profileCombo->GetStringSelection(); if ( proman->DoesProfileExist(nametodelete) ) { if (nametodelete == ProMan::DEFAULT_PROFILE_NAME) { wxLogWarning(_T("The default profile cannot be deleted.")); return; } DeleteProfileDialog deleteDialog(this, nametodelete); if ( deleteDialog.ShowModal() == deleteDialog.GetAffirmativeId()) { if ( proman->DeleteProfile(nametodelete) ) { wxLogStatus(_("Deleted profile named '%s'."), nametodelete.c_str()); } else { wxLogWarning(_("Unable to delete profile '%s', see log for more details."), nametodelete.c_str()); } } else { wxLogStatus(_("Deletion of profile '%s' cancelled"), nametodelete.c_str()); } } else { wxLogWarning(_T("Unable to delete non-existent profile '%s'"), nametodelete.c_str()); } } void WelcomePage::ProfileCountChanged(wxCommandEvent &WXUNUSED(event)) { wxChoice* profileCombo = dynamic_cast(wxWindow::FindWindowById(ID_PROFILE_COMBO, this)); wxCHECK_RET(profileCombo != NULL, _T("can't find the profile combobox")); ProMan *proman = ProMan::GetProfileManager(); profileCombo->Clear(); profileCombo->Append(proman->GetAllProfileNames()); profileCombo->SetStringSelection(proman->GetCurrentName()); } void WelcomePage::UpdateNews(wxIdleEvent& WXUNUSED(event)) { if ( !this->needToUpdateNews ) { return; } this->needToUpdateNews = false; wxHtmlWindow* newsWindow = dynamic_cast(wxWindow::FindWindowById(ID_NEWS_HTML_PANEL, this)); wxCHECK_RET(newsWindow != NULL, _T("Update news called, but can't find the news window")); ProMan* proman = ProMan::GetProfileManager(); bool allowedToUpdateNews; if ( !proman->GlobalRead(GBL_CFG_NET_DOWNLOAD_NEWS, &allowedToUpdateNews)) { return; } if (allowedToUpdateNews) { const NewsSource& newsSource(SkinSystem::GetSkinSystem()->GetNewsSource()); const NewsData* newsData = proman->NewsRead(newsSource.GetName()); wxASSERT((newsData == NULL) || newsData->IsValid()); if ((newsData != NULL) && (wxDateTime::Now() - newsData->lastDownloadNews < TIME_BETWEEN_NEWS_UPDATES) ) { // post the news that we have on file for now newsWindow->SetPage(newsData->theNews); } else { wxFileSystem filesystem; wxFSFile* news = filesystem.OpenFile(newsSource.GetNewsUrl(), wxFS_READ); if ( news == NULL ) { wxLogError(_("Error in retrieving news")); return; } wxInputStream* rawNewsStream = news->GetStream(); wxLogDebug(_T("news loaded from %s with type %s"), news->GetLocation().c_str(), news->GetMimeType().c_str()); wxString newsStr; wxStringOutputStream newsStrStream(&newsStr); rawNewsStream->Read(newsStrStream); wxString formattedData(_T("
    ")); wxStringTokenizer tok(newsStr, _T("\t"), wxTOKEN_STRTOK); while(tok.HasMoreTokens()) { wxString title(tok.GetNextToken()); wxCHECK_RET(tok.HasMoreTokens(), _T("news formatter has run out of tokens at wrong time")); wxString link(tok.GetNextToken()); wxCHECK_RET(tok.HasMoreTokens(), _T("news formatter has run out of tokens at wrong time")); wxString imglink(tok.GetNextToken()); formattedData += wxString::Format(_T("\n
  • %s
  • "), link.c_str(), title.c_str(), imglink.c_str()); } formattedData += _T("\n
"); newsWindow->SetPage(formattedData); proman->NewsWrite( newsSource.GetName(), NewsData(formattedData, wxDateTime::Now())); } } else { newsWindow->SetPage(_("Automatic news retrieval disabled.")); } } bool WelcomePage::getOrPromptUpdateNews() { bool updateNews; if (!ProMan::GetProfileManager()->GlobalRead(GBL_CFG_NET_DOWNLOAD_NEWS, &updateNews)) { wxDialog* updateNewsQuestion = new wxDialog(NULL, wxID_ANY, _("wxLauncher - network access request"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP | wxDIALOG_NO_PARENT); updateNewsQuestion->SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); wxIcon helpIcon; helpIcon.CopyFromBitmap(SkinSystem::GetSkinSystem()->GetHelpIcon()); updateNewsQuestion->SetIcon(helpIcon); wxStaticText* updateNewsText1 = new wxStaticText(updateNewsQuestion, wxID_ANY, _("Should wxLauncher automatically retrieve the latest news?"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); wxStaticText* updateNewsText2 = new wxStaticText(updateNewsQuestion, wxID_ANY, _("You can change this setting on the Welcome page."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); wxButton* allowNewsUpdate = new wxButton(updateNewsQuestion, wxID_ANY, _("Yes")); wxButton* denyNewsUpdate = new wxButton(updateNewsQuestion, wxID_ANY, _("No")); updateNewsQuestion->SetAffirmativeId(allowNewsUpdate->GetId()); updateNewsQuestion->SetEscapeId(denyNewsUpdate->GetId()); #if 0 wxButton* helpButton = new wxButton(updateNewsQuestion, wxID_ANY, _T("?"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); helpButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(WelcomePage::OnUpdateNewsHelp)); #endif wxBoxSizer* updateNewsSizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* bodySizer= new wxBoxSizer(wxVERTICAL); wxBoxSizer* choiceSizer = new wxBoxSizer(wxHORIZONTAL); wxStaticBitmap* questionImage = new wxStaticBitmap(updateNewsQuestion, wxID_ANY, SkinSystem::GetSkinSystem()->GetBigHelpIcon()); choiceSizer->Add(allowNewsUpdate, wxSizerFlags().Border(wxRIGHT, 5)); choiceSizer->Add(denyNewsUpdate); #if 0 choiceSizer->Add(helpButton, wxSizerFlags().Border(wxLEFT, 15)); #endif bodySizer->Add(updateNewsText1, wxSizerFlags().Center()); bodySizer->Add(updateNewsText2, wxSizerFlags().Center().Border(wxBOTTOM, 10)); bodySizer->Add(choiceSizer, wxSizerFlags().Center()); updateNewsSizer->Add(questionImage, wxSizerFlags().Center().Border(wxTOP|wxLEFT|wxBOTTOM, 5)); updateNewsSizer->Add(bodySizer, wxSizerFlags().Center().Border(wxALL, 5)); updateNewsQuestion->SetSizerAndFit(updateNewsSizer); updateNewsQuestion->Centre(wxBOTH | wxCENTRE_ON_SCREEN); if ( updateNewsQuestion->GetAffirmativeId() == updateNewsQuestion->ShowModal() ) { updateNews = true; } else { updateNews = false; } ProMan::GetProfileManager()->GlobalWrite(GBL_CFG_NET_DOWNLOAD_NEWS, updateNews); updateNewsQuestion->Destroy(); } return updateNews; } void WelcomePage::OnDownloadNewsCheck(wxCommandEvent& event) { wxCheckBox* checkbox = dynamic_cast(wxWindow::FindWindowById(event.GetId(), this)); wxCHECK_RET( checkbox != NULL, _T("OnDownloadNewsCheck called by non checkbox")); ProMan::GetProfileManager()->GlobalWrite(GBL_CFG_NET_DOWNLOAD_NEWS, checkbox->IsChecked()); } void WelcomePage::OnUpdateNewsHelp(wxCommandEvent &WXUNUSED(event)) { HelpManager::OpenHelpById(ID_MORE_INFO_PRIVACY); } void WelcomePage::OnTCSkinChanged(wxCommandEvent &WXUNUSED(event)) { wxHtmlWindow* general = dynamic_cast( wxWindow::FindWindowById(ID_SUMMARY_HTML_PANEL, this)); wxCHECK_RET(general != NULL, _T("Unable to find welcome text area")); general->SetPage(SkinSystem::GetSkinSystem()->GetWelcomeText()); wxStaticBox* newsBox = dynamic_cast( wxWindow::FindWindowById(ID_NEWS_BOX, this)); wxCHECK_RET(newsBox != NULL, _T("Unable to find news box")); newsBox->SetLabel(SkinSystem::GetSkinSystem()->GetNewsSource().GetLabel()); this->needToUpdateNews = true; } /////////////////////////////////////////////////////////////////////////////// ///// DIALOGS /// CloneProfileDialog::CloneProfileDialog(wxWindow* parent): wxDialog(parent, ID_CLONE_PROFILE_DIALOG, _("Create new profile"), wxDefaultPosition, wxDefaultSize) { wxStaticText *newNameText = new wxStaticText(this, wxID_ANY, _("New profile name:")); wxTextCtrl *newName = new wxTextCtrl(this, ID_CLONE_PROFILE_NEWNAME, wxEmptyString, wxDefaultPosition, wxSize(200,-1), wxTE_PROCESS_ENTER); wxSizer* nameSizer = new wxFlexGridSizer(3); nameSizer->AddStretchSpacer(1); nameSizer->Add(newNameText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); nameSizer->Add(newName, wxSizerFlags().Expand()); wxCheckBox* cloneFromCheckbox = new wxCheckBox(this, ID_CLONE_PROFILE_CHECKBOX, wxEmptyString); wxStaticText* cloneFromText = new wxStaticText(this, wxID_ANY, _("Clone settings from:")); cloneFrom = new wxChoice(this, wxID_ANY); nameSizer->Add(cloneFromCheckbox, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); nameSizer->Add(cloneFromText, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); nameSizer->Add(cloneFrom, wxSizerFlags().Expand()); this->createButton = new wxButton(this, wxID_OK, _("Create")); wxCommandEvent initDialogEvent; CloneProfileDialog::OnUpdateText(initDialogEvent); wxButton *closeButton = new wxButton(this, wxID_CANCEL, _("Cancel")); wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL); buttonSizer->Add(createButton, wxSizerFlags().Border(wxRIGHT, 5)); buttonSizer->Add(closeButton, wxSizerFlags()); wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(nameSizer, wxSizerFlags().Expand().Border(wxALL, 5)); sizer->Add(buttonSizer, wxSizerFlags().Right().Border(wxALL, 5)); newName->SetValidator(wxTextValidator(wxFILTER_NONE, &(this->newProfileName))); cloneFrom->Append(ProMan::GetProfileManager()->GetAllProfileNames()); cloneFrom->SetStringSelection(ProMan::GetProfileManager()->GetCurrentName()); wxCommandEvent initEvent(wxEVT_COMMAND_CHECKBOX_CLICKED, ID_CLONE_PROFILE_CHECKBOX); initEvent.SetInt(0); // will initialize checkbox to unchecked this->OnClickCloneCheckbox(initEvent); this->SetSizerAndFit(sizer); this->Layout(); this->Center(); wxLogDebug(_T("Clone Profile Dialog Created")); } const wxString& CloneProfileDialog::GetNewProfileName() { return this->newProfileName.Trim(true).Trim(false); // strip trailing/leading whitespace } const wxString CloneProfileDialog::GetSourceProfileName() { // can't return wxString& since cloneFrom returns a temporary var return this->UseProfileCloning() ? cloneFrom->GetStringSelection() : wxString(wxEmptyString); } void CloneProfileDialog::OnUpdateText(wxCommandEvent& event) { this->createButton->Enable(this->NewNameIsValid()); } void CloneProfileDialog::OnPressEnterKey(wxCommandEvent& event) { if (this->NewNameIsValid()) { wxCommandEvent simulatedClickEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK); this->createButton->Command(simulatedClickEvent); } } void CloneProfileDialog::OnClickCloneCheckbox(wxCommandEvent& event) { if (event.IsChecked()) { this->useProfileCloning = true; this->createButton->SetLabel(_("Clone")); this->cloneFrom->Enable(); this->Refresh(); this->Update(); } else { this->useProfileCloning = false; this->createButton->SetLabel(_("Create")); this->cloneFrom->Disable(); this->Refresh(); this->Update(); } } // a valid name is non-empty and does not consist purely of whitespace bool CloneProfileDialog::NewNameIsValid() { wxTextCtrl* newName = dynamic_cast(wxWindow::FindWindowById(ID_CLONE_PROFILE_NEWNAME, this)); wxCHECK_MSG(newName != NULL, false, _T("can't find the clone profile new name text ctrl")); return !(newName->GetValue().Trim().IsEmpty()); } DeleteProfileDialog::DeleteProfileDialog(wxWindow* parent, wxString name): wxDialog(parent, ID_DELETE_PROFILE_DIALOG, _("Delete profile?"), wxDefaultPosition, wxDefaultSize) { wxStaticText* text = new wxStaticText(this, wxID_ANY, wxString::Format(_("Are you sure you want to delete profile '%s'?"), name.c_str())); wxButton *deleteButton = new wxButton(this, wxID_ANY, _("Delete")); wxButton *cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL); buttonSizer->Add(deleteButton, wxSizerFlags().Border(wxRIGHT, 5)); buttonSizer->Add(cancelButton, wxSizerFlags()); wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(text, wxSizerFlags().Expand().Border(wxALL, 5)); sizer->Add(buttonSizer, wxSizerFlags().Right().Border(wxALL, 5)); this->SetSizerAndFit(sizer); this->Layout(); this->Center(); this->SetAffirmativeId(deleteButton->GetId()); this->SetEscapeId(cancelButton->GetId()); wxLogDebug(_T("Delete Profile Dialog created.")); } wxlauncher-0.9.4/code/tabs/WelcomePage.h0000644000000000000000000000335512155177255020066 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WELCOMEPAGE_H #define WELCOMEPAGE_H #include #include "global/ids.h" #include "apis/ProfileManager.h" class WelcomePage: public wxPanel { public: WelcomePage(wxWindow* parent); void LinkClicked(wxHtmlLinkEvent& event); void LinkHover(wxHtmlCellEvent& event); void OnMouseOut(wxMouseEvent& event); void ProfileButtonClicked(wxCommandEvent& event); void AutoSaveProfilesChecked(wxCommandEvent& event); void ProfileChanged(wxCommandEvent& event); void ProfileCountChanged(wxCommandEvent& event); void UpdateNews(wxIdleEvent& event); void OnDownloadNewsCheck(wxCommandEvent& event); void OnUpdateNewsHelp(wxCommandEvent& event); void OnTCSkinChanged(wxCommandEvent& event); private: /** The width of the items on the welcome tab. */ const static int stuffWidth = TAB_AREA_WIDTH; void *lastLinkInfo; void cloneNewProfile(wxChoice* combobox, ProMan* profile); void deleteProfile(wxChoice* combobox, ProMan* profile); bool needToUpdateNews; bool getOrPromptUpdateNews(); DECLARE_EVENT_TABLE(); }; #endifwxlauncher-0.9.4/code/wxLauncherApp.cpp0000644000000000000000000001760612155177255020065 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "generated/configure_launcher.h" #include #include #include #include #include #include #include #if HAS_SDL == 1 #include "SDL.h" #endif // prevents SDL_main.h (included by SDL.h) from redefining main #ifdef main #undef main #endif #include "wxLauncherApp.h" #include "MainWindow.h" #include "apis/SkinManager.h" #include "controls/Logger.h" #include "global/version.h" #include "apis/TCManager.h" #include "apis/ProfileManager.h" #include "apis/HelpManager.h" #include "apis/FlagListManager.h" #include "apis/ProfileProxy.h" #include "global/MemoryDebugging.h" // Last include for memory debugging IMPLEMENT_APP(wxLauncher); const static wxCmdLineEntryDesc CmdLineOptions[] = { {wxCMD_LINE_SWITCH, NULL, _T("session-only"), _T("Do not remember the profile that is selected at exit")}, {wxCMD_LINE_SWITCH, NULL, _T("add-profile"), _T("Add profile PROFILE from FILE. If PROFILE already exists ") _T("it will not be overwritten. *Operator*")}, {wxCMD_LINE_SWITCH, NULL, _T("select-profile"), _T("Make PROFILE the that wxLauncher will use on next run. *Operator*")}, {wxCMD_LINE_OPTION, NULL, _T("profile"), _T("The name of a profile to operate on. Operand PROFILE."), wxCMD_LINE_VAL_STRING, 0}, {wxCMD_LINE_OPTION, NULL, _T("file"), _T("The path to a file to operate on. Operand FILE."), wxCMD_LINE_VAL_STRING, 0}, {wxCMD_LINE_NONE}, }; void wxLauncher::OnInitCmdLine(wxCmdLineParser& parser) { parser.SetDesc(CmdLineOptions); parser.SetSwitchChars(_T("-")); // always use -, even on windows wxApp::OnInitCmdLine(parser); } bool wxLauncher::OnCmdLineParsed(wxCmdLineParser& parser) { if (!wxApp::OnCmdLineParsed(parser)) return false; if (parser.Found(_T("session-only"))) { mKeepForSessionOnly = true; } if (parser.Found(_T("add-profile"))) { mProfileOperator = ProManOperator::add; if (!parser.Found(_T("profile"), &mProfileOperand)) { wxLogError(_T("No profile specified to add")); return false; } if (!parser.Found(_T("file"), &mFileOperand)) { wxLogError(_T("No file specified to add as profile")); return false; } } else if(parser.Found(_T("select-profile"))) { mProfileOperator = ProManOperator::select; if (!parser.Found(_T("profile"), &mProfileOperand)) { wxLogError(_T("No profile specified to select")); return false; } } return true; } wxLauncher::wxLauncher() :mProfileOperator(ProManOperator::none), mKeepForSessionOnly(false), mShowGUI(false) // The strings init themselves sanely { } /** Display the splash screen. \param splashWindow Out. Point to splash window if created. NULL otherwise. \param returns false if an error occurented while loading spalsh; a false return value indicates that this function has already informed the user of the failue. Returns true otherwise. */ bool displaySplash(wxSplashScreen **splashWindow) { wxBitmap splash; // splash image location is fixed so that it's known at compile time wxFileName splashFile(_T(RESOURCES_PATH), _T("wxL_Splash.png")); if (splash.LoadFile(splashFile.GetFullPath(), wxBITMAP_TYPE_ANY)) { #if NDEBUG (*splashWindow) = new wxSplashScreen(splash, wxSPLASH_CENTRE_ON_SCREEN, 0, NULL, wxID_ANY); #else (*splashWindow) = NULL;//new wxSplashScreen(splash, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 1000, NULL, wxID_ANY); #endif wxYield(); } else { wxFileName expectedDir; if (wxFileName(_T(RESOURCES_PATH)).IsAbsolute()) { expectedDir = wxFileName(_T(RESOURCES_PATH)); } else { expectedDir = wxFileName(::wxGetCwd(), _T(RESOURCES_PATH)); } wxLogFatalError(wxString::Format( _T("Unable to load splash image. ") _T("This normally means that you are running the Launcher from a folder") _T(" that the launcher does not know how to find the resource folder from.") _T("\n\nThe launcher is expecting (%s) to contain the resource images."), expectedDir.GetFullPath().c_str()).c_str()); return false; } return true; } int wxLauncher::OnRun() { if (mProfileOperator == ProManOperator::none) { return wxApp::OnRun(); } else { return ProManOperator::RunProfileOperator(mProfileOperator); } } bool wxLauncher::OnInit() { wxInitAllImageHandlers(); // call base class OnInit so that cmdline stuff works. if (!wxApp::OnInit()) return false; // base said abort so abort wxLog::SetActiveTarget(new Logger()); wxLogInfo(_T("wxLauncher Version %d.%d.%d"), MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION); wxLogInfo(_T("Build \"%s\" committed on (%s)"), HGVersion, HGDate); wxLogInfo(wxDateTime(time(NULL)).Format(_T("%c"))); #if MSCRTMEMORY _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif #if HAS_SDL == 1 if ( 0 != SDL_InitSubSystem(SDL_INIT_VIDEO) ) { wxLogFatalError(_T("SDL_InitSubSystem failed")); } #endif // Little hack to deal with people starting the launcher from the bin folder if ( !wxFileName::DirExists(_T(RESOURCES_PATH)) ) { wxFileName resourceDir; resourceDir.AssignDir(::wxGetCwd()); resourceDir.AppendDir(_T("..")); resourceDir.AppendDir(_T(RESOURCES_PATH)); if ( resourceDir.DirExists() ) { wxFileName newWorkingDir(::wxGetCwd(), _T("..")); ::wxSetWorkingDirectory(newWorkingDir.GetFullPath()); } } wxSplashScreen* splashWindow = NULL; if (!displaySplash(&splashWindow)) return false; wxLogInfo(_T("Initializing profiles...")); ProMan::Flags promanFlags = ProMan::None; if (mKeepForSessionOnly) promanFlags = promanFlags | ProMan::NoUpdateLastProfile; if ( !ProMan::Initialize(promanFlags) ) { wxLogFatalError(_T("ProfileManager failed to initialize. Aborting! See log file for more details.")); return false; } if (mProfileOperator != ProManOperator::none) { // We are not to create a GUI so we are done init now return true; } wxFileSystem::AddHandler(new wxArchiveFSHandler); wxFileSystem::AddHandler(new wxInternetFSHandler); wxLogInfo(_T("Initializing SkinSystem...")); SkinSystem::Initialize(); wxLogInfo(_T("Initializing HelpManager...")); HelpManager::Initialize(); wxLogInfo(_T("Initializing FlagListManager...")); FlagListManager::Initialize(); wxLogInfo(_T("Initializing ProfileProxy...")); ProfileProxy::Initialize(); wxLogInfo(_T("wxLauncher starting up.")); MainWindow* window = new MainWindow(); wxLogStatus(_T("MainWindow is complete")); window->Show(true); #if NDEBUG // will autodelete when timout runs out in debug splashWindow->Show(false); splashWindow->Destroy(); #endif // must call TCManager::CurrentProfileChanged() manually on startup, // since initial profile switch takes place before TCManager has been initialized // calling it here to ensure that AdvSettingsPage is set up by the time events are triggered wxCommandEvent tcMgrInitEvent; TCManager::Get()->CurrentProfileChanged(tcMgrInitEvent); wxLogStatus(_T("Ready.")); return true; } int wxLauncher::OnExit() { ProMan::DeInitialize(); if (mProfileOperator == ProManOperator::none) { // deinitialize subsystems in the opposite order of initialization ProfileProxy::DeInitialize(); FlagListManager::DeInitialize(); HelpManager::DeInitialize(); SkinSystem::DeInitialize(); #if HAS_SDL == 1 SDL_Quit(); #endif } wxLogInfo(_T("wxLogger shutdown complete.")); return wxApp::OnExit(); } wxlauncher-0.9.4/code/wxLauncherApp.h0000644000000000000000000000235212155177255017522 0ustar rootroot00000000000000/* Copyright (C) 2009-2010 wxLauncher Team 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; either version 2 of the License, or (at your option) any later version. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WXLAUNCHERAPP_H #define WXLAUNCHERAPP_H #include #include "apis/ProfileManagerOperator.h" class wxLauncher: public wxApp { public: wxLauncher(); virtual bool OnInit(); virtual int OnExit(); virtual int OnRun(); virtual void OnInitCmdLine(wxCmdLineParser& parser); virtual bool OnCmdLineParsed(wxCmdLineParser& parser); wxString mFileOperand; wxString mProfileOperand; ProManOperator::profileOperator mProfileOperator; bool mKeepForSessionOnly; bool mShowGUI; }; DECLARE_APP(wxLauncher); #endif wxlauncher-0.9.4/documentation/Eventflow.dia0000644000000000000000000037437512155177255021200 0ustar rootroot00000000000000 #A4# #EVT_PROFILE_CHANGE# #EVT_CURRENT_PROFILE_CHANGED# #CreateProfile ProfileManager.cpp:900# #DeleteProfile ProfileManager.cpp:936# #SwitchTo ProfileManager.cpp:848# #January 18th, 2012# #ProfileCountChanged WelcomePage.cpp:425# ## #CurrentProfileChanged TCManager.cpp:38# #GenerateTCChanged# #GenerateTCSelectModChanged# #ProfileChanged BasicSettingsPage.cpp:89# #OnCurrentProfileChanged AdvSettingsPage.cpp:223# #RefreshFlags AdvSettingsPage.cpp:227# #EVT_TC_CHANGED# #EVT_TC_SELECTED_MOD_CHANGED# #ModsPage::OnTCChanged# #BasicSettingsPage::OnTCChanged# #TCManager::GenerateTCBinaryChanged# #TCManager::GenerateTCFredBinaryChanged# #AdvSettingsPage::OnNeedUpdateCommandLine# #BottomButtons::OnTCChanges# #EVT_TC_BINARY_CHANGED# #EVT_TC_FRED_BINARY_CHANGED# #BasicSettingsPage::OnCurrentFredBinaryChanged# #AdvSettingsPage::OnExeChanged# #new ModList# #ModList::OnActivateMod# #EVT_CMD_LINE_CHANGED# #LightingPresets::OnSelectLightingPreset# #AdvSettingsPage::OnSelectFlagSet# #FlagListBoxStatus == FLAGLISTBOX_OK# #AdvSettingsPage::OnDrawStatusChanged# ## ## #EVT_FLAG_LIST_BOX_DRAW_STATUS_CHANGED# #FlagListManager::GenerateFlagListBoxDrawStatusChanged# #FlagListBox::SetDrawStatus# #FlagListBox::FlagListBox# #FlagListBox::Initialize# #FlagListBox::FlagProcess::OnTerminate# #BasicSettingsPage::OnCurrentBinaryChanged# wxlauncher-0.9.4/onlinehelp/10Introduction/02Profiles.help0000644000000000000000000000464712155177255023445 0ustar rootroot00000000000000Profiles # Profiles Profiles are one of the biggest features introduced by wxLauncher, allowing you to save "snapshots" of the launcher settings. Think of them like the "save" feature commonly found in games: you can save your current settings and restore them later. ## Why use profiles Since most of the available [mods][mod]![][li]/[TCs][tc]![][li] use different settings to achieve the best experience for the player, the profiles feature was created in order to allow the player to quickly switch between settings for his/her mods instead of manually having to set the various options that [FSO][]![][li] supports when switching to another mod. ## Copying settings from one profile to another While you can't directly copy settings from one profile to another, you can choose to base a new profile on an existing profile by cloning the existing profile's contents. **Tip**: You can use this feature to rename a profile. Simply click the new profile button, choose the profile you want to rename and type in the new name. Click "Clone" to create the new profile, and then delete the old profile. ## What data is saved A profile contains the following data: * The selected mod. * Status of all the options from the [Basic Settings][basic]![][li] page. * Status of all the flags from the [Advanced Settings][advanced]![][li] page, including the [lighting presets][lighting]![][li] and [custom flags][custom]![][li]. **What is not saved**: The option of whether to automatically retrieve the highlights from Hard Light Productions at launcher startup. ## Using wxLauncher without using profiles Although you can't disable the profiles feature, you can avoid using them by checking the "Automatically save profiles" box on the Welcome page and then only using the default profile, which has the name "Default". This way, every change you make is automatically saved to the default profile when the launcher exits, and wxLauncher will behave just as the standard Windows launcher does. [li]: /images/li.png [mod]: /50TechnicalStuff/02Terminology.help [tc]: /50TechnicalStuff/02Terminology.help [fso]: /50TechnicalStuff/02Terminology.help [basic]: /30Reference/30BasicSettings/index.help [advanced]: /30Reference/40AdvancedSettings/index.help [lighting]: /30Reference/40AdvancedSettings/021lighting.help [custom]: /30Reference/40AdvancedSettings/10customflags.help [welcome]: /30Reference/10Welcome/index.help wxlauncher-0.9.4/onlinehelp/10Introduction/index.help0000644000000000000000000000013212155177255022610 0ustar rootroot00000000000000Overview # Overview This section covers the key features of wxLauncher. wxlauncher-0.9.4/onlinehelp/30Reference/10Welcome/02profiles.help0000644000000000000000000000313212155177255024444 0ustar rootroot00000000000000Profile management # Profile management This set of controls allows you to to create and delete profiles. Check the [Profiles][profiles]![][li] section for information about what profiles are and how they work. ### New Creates a new profile. You can either create a blank profile or clone an existing profile. Cloning allows you to quickly and easily make a new profile by making slight changes to a copy of one of your existing profiles. ### Delete Deletes the selected profile. You will be asked to confirm the deletion. **Note**: You can't delete the "Default" profile. ### Save Saves the current profile. ### Automatically save profiles If checked, any changes made to the current profile will be saved automatically on profile switch, profile creation, and launcher exit. If unchecked and the current profile contains unsaved changes, the launcher will ask you if you want to save your changes when you switch profiles, create a new profile, or exit wxLauncher. **Tip**: If you don't want to use profiles, check this box and select the Default profile, at which point wxLauncher will act like the standard Windows launcher, remembering your changes automatically. [li]: /images/li.png [le]: ../../images/le.png [profiles]: ../../10Introduction/02Profiles.help [derelict]: http://freespace.volitionwatch.com/derelict/index.shtml [bp]: http://blueplanet.hard-light.net/ wxlauncher-0.9.4/onlinehelp/30Reference/10Welcome/03news.help0000644000000000000000000000151212155177255023576 0ustar rootroot00000000000000Latest highlights # Latest highlights from Hard Light Productions The highlights give you a quick preview of new releases on [Hard Light Productions][hl]![][le] (HLP). If you have checked the "Automatically retrieve highlights at startup" box, wxLauncher will retrieve and display the highlights from the HLP website when it starts up. [le]: ../../images/le.png [hl]: http://www.hard-light.net/ wxlauncher-0.9.4/onlinehelp/30Reference/10Welcome/index.help0000644000000000000000000000072512155177255023573 0ustar rootroot00000000000000Welcome page #Welcome page The welcome page is your first contact with the launcher and the first page you'll always see on a fresh launcher start. It's like your browser homepage. Because of that, the welcome page contains two items that are essential to any FreeSpace 2 fan: * [Profile management controls][profiles]![][li] * [Hard Light Productions highlights][hlnews]![][li] [li]: /images/li.png [profiles]: 01profiles.help [hlnews]: 03news.help wxlauncher-0.9.4/onlinehelp/30Reference/20MODs/01modlist.help0000644000000000000000000000217712155177255023513 0ustar rootroot00000000000000Mod list # Mod list ## Changing the active mod To change the active mod, scroll the list and click the [mod][mod]![][li] you wish to activate. Two buttons will appear, one labeled **Activate** and the other **Info**. Click **Activate** to make the selected mod active. **Note**: The first item on the mod list is always the "(No mod)" entry. Activate this in order to play the current [TC][mod]![][li] in its original form without any mods. ## Learning more about a mod To learn more about a mod, such as a description and links to its website or forum if available, select the mod and click the **Info** button that appears when the mod is selected. [li]: /images/li.png [mod]: /50TechnicalStuff/02Terminology.help wxlauncher-0.9.4/onlinehelp/30Reference/20MODs/index.help0000644000000000000000000000046412155177255023003 0ustar rootroot00000000000000Mods page # Mods page This page allows you to [select a mod to activate][am]![][li] among the [mods][mod]![][li] installed for the current selected [TC][]![][li]. [li]: /images/li.png [mod]: /50TechnicalStuff/02Terminology.help [tc]: /50TechnicalStuff/02Terminology.help [am]: activate.helpwxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/01executable.help0000644000000000000000000000403612155177255026116 0ustar rootroot00000000000000 FS2 Open game root folder and executable # FS2 Open game root folder and executable In order to be able to launch [FSO][]![][li], the launcher needs to know two things: * In which folder are the FS2 Open (and optionally, FRED2 Open) executables located * Which executable it should use ### Game root folder You must select the root folder of an FS2 Open game before you can select an FSO executable (or [mod][mod]![][li] on the [mods page][modpage]![][li]) to use. To select the root folder, click **Browse...** and browse to the folder containing the FSO executables, then click **Choose** to confirm. The "Browse for folder" dialog box won't close if the selected folder does not contain valid FSO executables. ### FS2 Open executable selection Once you have selected a valid root folder, the FS2 Open executable selection box will be populated with each FSO executable found in the TC root folder, branded with the version number, build number (if it's a nightly build), build type and SSE/SSE2 build indication. ### FRED2 Open executable selection **Compatibility note**: Although FRED executable selection is supported on all platforms, FRED is currently available on Windows only. **Note**: FRED launching is not enabled by default. Enable it by pressing F3. **TODO** [li]: /images/li.png [fso]: ../../50TechnicalStuff/02Terminology.help [tc]: /50TechnicalStuff/02Terminology.help [mod]: /50TechnicalStuff/02Terminology.help [modpage]: ../20MODs/index.help wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/02video.help0000644000000000000000000000607212155177255025106 0ustar rootroot00000000000000Video # Video This is where you configure the basic settings of the graphics engine to best suit your needs (and to best fit your computer's graphic and processing power). -- screenshot with the "video" section highlighted -- ## Options supported on all platforms ### Resolution The number of pixels you see on the screen when playing the game. Chosing a bigger resolution increases the quality of the graphics output of the engine but requires more processing power. If the game runs sluggish, try lowering the resolution. **TODO is this actually true about sluggishness? sounds iffy.** ### Depth Bit depth (also called *color depth* or *screen depth*) describes the number of bits used to represent the color of a single pixel. In game terms, this means that a 32-bit depth creates a more vivid image than a 16-bit depth mode. **Note**: Although a 32-bit mode requires a bit more processing power than 16-bit, this option should be set to the same value as your Windows bit depth (usually 32-bit nowdays), as using a different bit depth can cause *major* slowdowns. If your graphics card is having difficulty running FSO, the SCP suggests that you change other graphics settings and leave this one where it is now. ## Options currently supported on Linux and Mac OS X only ### Texture filter The texture filter determines the method used to blend the pixels you see on a textured object/model in game with that of neighbouring pixels to create the illusion of a continuous surface. The more advanced the method, the better the result. The cost is a slight performance loss. ### Anisotropic Anisotropic is a second filter applied to the textures you see in-game to correct blurriness when the textured surface is at an oblique angle. Since most of the textures in-game are viewed at oblique angles, it is a good idea to turn this on. Like most other options, choosing a larger correction factor results in a slight performance loss. ### Anti-aliasing Anti-aliasing is a technique that minimizes the "jagged edges" of any object you see in-game. Check this [Wikipedia article on anti-aliasing][aa]![][le] for a more detailed explanation. **TODO need to say something about FXAA** **Tip**: In a furious dogfight, you won't notice any difference if this is turned off or set to maximum, but if you want to admire the beautiful world of FreeSpace, it could be worth using it. **Warning**: Using a large anti-aliasing factor can cause a considerable drop in performance, depending on your system configuration. [li]: /images/li.png [le]: /images/le.png [fso]: /50TechnicalStuff/02Terminology.help [scp]: /50TechnicalStuff/02Terminology.help [aa]: http://en.wikipedia.org/wiki/Anti-alias wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/03audio.help0000644000000000000000000000251312155177255025076 0ustar rootroot00000000000000Audio # Audio Here you set the device that [FSO][]![][li] will use to output the sound effects. -- screenshot with the "audio" section highlighted -- ###Sound device Lists the compatible sound devices that are installed on your system. "Generic software" works on any machine, choose that if you don't have a high-end dedicated sound card. **Note**: If you don't have any device listed in the droplist, click the button below to download the latest version of OpenAL. You might want to check this [Wikipedia page][openalw]![][le] to learn more about OpenAL. ###Download OpenAL ** this button should always be enabled -- how do we know the user has the latest version? or the latest version is not needed, what is needed is a version that works? ** Click this button to open the [OpenAL download section][openald]![][le] in your browser. Then, locate, download and install the appropiate package for your system. ###Detect Click this to redetect the audio devices installed on your system. Useful if you made changes to your system - like installing a new version of OpenAL for example, without restarting wxLauncher. [li]: /images/li.png [le]: /images/le.png [openalw]: http://en.wikipedia.org/wiki/OpenAL [openald]: http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx [fso]: /50TechnicalStuff/02Terminology.help wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/04speech.help0000644000000000000000000000455212155177255025252 0ustar rootroot00000000000000 Speech # Speech (Windows only) Here you can set various aspects related to the speech engine. This is intended as an option while playing a user-made campaigns that lacks voice acting. While a simulated speech is of far inferior quality compared to a human voice, sometimes it is better than nothing. **Compatibility note:** Speech is currently supported on Windows only. ### Test string Type a test string for testing the selected voice. "Press play to test this string." is provided by default. **Note**: When testing the voice, the launcher will be unresponsive until the simulated speech stops, so try to limit your string to a few words only. ### Voice selection Lists the available voices installed on your system, from which you can select one. ### Volume slider This slider sets the volume of simulated speech in game. Drag it all the way to the right to set the volume to maximum. **Tip**: Remember that when you play the game, there will be countless other sounds beside the simulated voices, so if you plan to use the simulated speech in-game, you might want to set the volume a bit higher than what you find acceptable when testing your settings from the launcher, especially if you're in a quiet room. That way, the simulated speech will be more than some indistinguishable noise in the background when playing. ### Play button Starts the simulated-voice playback of the test string. ### Where to use speech Defines the areas of the game where the simulated speech should be used. Check as needed. * Tech room * Briefings * In-game * Multiplayer ### Get more voices * **Windows XP or older**: You can download Mike and Mary from [Microsoft's Download Center][mdc]![][le]. * **Vista or 7**: There are no other free voices available from Microsoft (the voices available for Windows XP or older will install but Windows will not allow them to be used) **TODO verify the above statements** [le]:/images/le.png [mdc]: http://www.microsoft.com/downloads/details.aspx?FamilyId=5E86EC97-40A7-453F-B0EE-6583171B4530&displaylang=en wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/05joystick.help0000644000000000000000000000314312155177255025636 0ustar rootroot00000000000000Joystick # Joystick This is where you select and configure your joystick. -- screenshot of with the joystick area highlighted -- ## Options supported on all platforms ### Select joystick This is a list if joysticks connected to your computer and recognized by the operating system. Choose the one you want to use. **Linux and Mac OS X note**: If you connect or disconnect a joystick while wxLauncher is running, you will need to restart wxLauncher for it to detect the changes. ## Options currently supported on Windows only **Note**: Your joystick must support force feedback for the "Force feedback" and "Directional hit" options to have any effect. ### Force feedback Force feedback, or *haptics* (from *Haptic Technology*) is a tactile feedback technology which takes advantage of a user's sense of touch by applying forces, vibrations, and/or motions upon the user. An example of this feature is the simulated automobile steering wheels that are programmed to provide a "feel" of the road. As the user makes a turn or accelerates, the steering wheel responds by resisting turns or slipping out of control. So, if you want to "feel" your fighter more closely, enable this option. ### Directional hit When *Directional hit* is enabled, the joystick will bump in the opposite direction of where you were hit (by lasers, missiles, etc), but only if your joystick supports force feedback. ### Calibrate Clicking this button opens the joystick calibration page in the Control Panel. ### Detect Makes the launcher look for joysticks again. Useful if you plugged your joystick in after the launcher was started. wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/06network.help0000644000000000000000000000231012155177255025464 0ustar rootroot00000000000000Network # Network Here you can set several options regarding your network connection. ## Required information for playing multiplayer ### Connection type In order to play online, [FSO][]![][li] needs to know what kind of network configuration you have. Choose the appropriate value. ### Connection speed For a smooth experience, select the appropriate connection speed here. ## Advanced options ### Force local port If you want FSO to use a specific [port][]![][le] when playing over LAN/Internet, type it here. Most users will not need to use this setting, but some may need it to handle problems with older Network Address Translation (NAT) firewalls. ### Force IP address Same as above, but for the [IP address][]![][le]. Only version 4 addresses (the "classic" IP address - IPv4) are supported for the moment. As with **Force local port**, most users will not need to use this setting, but some may need it to get around older NAT firewalls. [li]: /images/li.png [le]: /images/le.png [fso]: /50TechnicalStuff/02Terminology.help [port]: http://en.wikipedia.org/wiki/Computer_port_%28software%29 [ip]: http://en.wikipedia.org/wiki/IP_address wxlauncher-0.9.4/onlinehelp/30Reference/30BasicSettings/index.help0000644000000000000000000000154512155177255024745 0ustar rootroot00000000000000Basic Settings page # Basic Settings page The basic settings are (basicaly) the settings that were configurable by the original FreeSpace 2 Launcher - the one provided by [Volition][=V=]![][le] and installed with the game - plus the new simulated speech feature and the [TC][tc]![][li] root folder and executable selection. What you can set on this page: * [TC root folder and Executable][exe]![][li] * [Video options][video]![][li] * [Audio device][audio]![][li] * [Speech settings][speech]![][li] * [Joystick selection][joystick]![][li] * [Network configuration][network]![][li] [li]: /images/li.png [le]: /images/le.png [=V=]: http://www.volition-inc.com/ [tc]: /50TechnicalStuff/02Terminology.help [exe]: 01executable.help [video]: 02video.help [audio]: 03audio.help [speech]: 04speech.help [joystick]: 05joystick.help [network]: 06network.help wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/01flaglist.help0000644000000000000000000000005612155177255026265 0ustar rootroot00000000000000Flag list # Flag list **TODO**wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/021lighting.help0000644000000000000000000000123512155177255026347 0ustar rootroot00000000000000Lighting presets # Lighting presets **TODO scribble: presets of lighting settings. provide link to fs wiki ., select radio button to have preset automatically included in your command line. if you'd like to edit one of the presets, select it, then click on the copy button to copy it to your custom flags, where you can edit it to your taste .** [li]: /images/li.png [le]: /images/le.png [fso]: /50TechnicalStuff/02Terminology.help [scp]: /50TechnicalStuff/02Terminology.help [presetdescs]: http://www.hard-light.net/wiki/index.php/Sample_Lighting_Settings wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/05flagsets.help0000644000000000000000000000013212155177255026267 0ustar rootroot00000000000000Flag sets # Flag sets **TODO should also rename these to something else**wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/10customflags.help0000644000000000000000000000006512155177255027007 0ustar rootroot00000000000000Custom flags # Custom flags **TODO**wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/15commandline.help0000644000000000000000000000007612155177255026755 0ustar rootroot00000000000000Command line # Current command line **TODO** wxlauncher-0.9.4/onlinehelp/30Reference/40AdvancedSettings/index.help0000644000000000000000000000011212155177255025417 0ustar rootroot00000000000000Advanced Settings page # Advanced Settings page **TODO** wxlauncher-0.9.4/onlinehelp/30Reference/index.help0000644000000000000000000000063512155177255022037 0ustar rootroot00000000000000Reference # Reference This section explains the various controls and functions of the launcher: * [Welcome page][welcome]![][li] * [Mods page][mods]![][li] * [Basic Settings page][basic]![][li] * [Advanced Settings page][advanced]![][li] [li]: /images/li.png [welcome]: 10Welcome/index.help [mods]: 20MODs/index.help [basic]: 30BasicSettings/index.help [advanced]: 40AdvancedSettings/index.help wxlauncher-0.9.4/onlinehelp/50TechnicalStuff/02Terminology.help0000644000000000000000000000560712155177255024414 0ustar rootroot00000000000000Terminology # Terminology ## FSO The **FreeSpace 2 Open** (also called FreeSpace Open) engine is an [open-source][os]![][le] modification project of the retail FreeSpace 2 engine started after the public release of the original source code in 2002. The community, under the direction of the Source Code Project (or SCP), made major upgrades to the engine and made it portable to multiple operating systems. ## SCP The **FreeSpace 2 Source Code Project** (or FreeSpace Source Code Project or simply Source Code Project) is a central source to coordinate development of the FreeSpace 2 Open engine. It was created after the source code of the FreeSpace 2 game engine become public in order to prevent multiple diverging versions of the game from appearing. * [SCP Homepage][scp]![][le] * [SCP Forums][forums]![][le] on Hard Light Productions ## Mod A Mod (short from **mod**ification) is, as its title suggests, a collection of one or more additions to the original game. While some mods make relatively small changes to the game (like a highly detailed skin for a ship, a new beam graphic or an explosion sound) others can almost be considered TCs, adding a new and original story, new ship models, new characters and settings, providing a new game experience for the player. Check the [Mod Database][moddb]![][le] on the [FreeSpace Wiki][fswiki]![][le] for various mods (campaigns, effects, etc.). ## TC **TC** stands for **T**otal **C**onversion. This implies that all of the content shipped by the TC is original and does not contain any content from the original FreeSpace 2 game. A TC can stand alone and should stand alone from the normal FreeSpace 2 install location and does not require the player to purchase FreeSpace 2 to play. Examples of TCs include * [The Babylon Project][tbp]![][le] - based on the Babylon 5 universe * [Wing Commander Saga][wcs]![][le] - based on the Wing Commander universe Check the **Hosted** section on the [Hard Light Productions][hl]![][le] website for a more detailed list. **TODO I think there's a list of TCs on the Campaigns List on the FS Wiki. Maybe use that instead of Hosted section.** **Technical note:** The launcher regards FreeSpace 2 as a TC itself. ***TODO for now.*** [le]: ../images/le.png [os]: http://en.wikipedia.org/wiki/Open_source [scp]: http://scp.indiegames.us/ [forums]: http://www.hard-light.net/forums/index.php?board=50.0 [moddb]: http://www.hard-light.net/wiki/index.php/Mod_Database [fswiki]: http://www.hard-light.net/wiki/index.php/Main_Page [tbp]: http://babylon.hard-light.net/ [wcs]: http://wcsaga.hard-light.net/ [hl]: http://www.hard-light.net/ wxlauncher-0.9.4/onlinehelp/50TechnicalStuff/03privacy.help0000644000000000000000000000355112155177255023556 0ustar rootroot00000000000000 Privacy: the technical details #Privacy: the technical details As noted in the [FAQ][faq]![][li], no information is ever sent out that could endanger your privacy. Actually, what information wxLauncher sends is nothing more than a simple page request. To get the highlights, wxLauncher asks for a [specially crafted page][page]![][le] that returns a list of the current highlights in a custom format. It's like a news feed, but with no HTML or XML, just plain text. The result will look similar to this: New Blue Planet beam piercing effects by Komet and Wanderer http://www.hard-light.net/forums/index.php?topic= 67663.0 http://www.hard-light.net/images/highlights/generic.png Ker.Soth has released a new mini-campaign for The Babylon Project. http://www.hard-light.net/forums/index.php?topic=67320.0 http://www.hard-light.net/images/highlights/tbp-highlight.png ReeNoiP has released his 9 mission minicampaign Uncharted Territory. http://www.hard-light.net/forums/index.php?topic=67327.0 http://www.hexellent.com/files/42/Uncharted.jpg Slasher puts us into contact with... THE RELIC! http://www.hard-light.net/forums/index.php?topic=67113.0 http://www.hard-light.net/images/highlights/tbp-highlight.png From that information, wxLauncher will extract the necessary information to nicely display the HLP highlights for you. That's it. A page request, nothing more. Your privacy is safe with us. Remember that you can always examine the source code to check for yourself how wxLauncher works. [li]: /images/li.png [le]: /images/le.png [faq]: /jfaq.help [page]: http://www.audiozone.ro/hl wxlauncher-0.9.4/onlinehelp/50TechnicalStuff/index.help0000644000000000000000000000013612155177255023041 0ustar rootroot00000000000000Other information **TODO Under the hood, translating and stuff, terminology** wxlauncher-0.9.4/onlinehelp/ReadMe.txt0000644000000000000000000000452212155177255017712 0ustar rootroot00000000000000What is this? ============= This folder contains the Online help for wxLauncher. This online help is for the users and FSO modders. The documenation of the launcher's technical details can be found in the code itself, the /docs folder, the wxLauncher wiki or the doxygen generated help. Technical details ================= The contents of this folder are "source" files that are used to generate the online help for the launcher. The help generated from this "source" is also transformed into a format suitable for the wxLauncher wiki. Each folder is the name of a subsection in the table of contents, or optionally the contents of the 'title' tags from the index.help files will be used as the name of the subsection. Every .help file will be a page in the help document. index.help will be the default page for each book and subsection of the book. The contents of html tag "title" will be the name of the page in the table of contents. The .help files are assumed to be [1][markdown markup]. [1]: http://daringfireball.net/projects/markdown/syntax Note that any links in the html files should be relative such that if you were to open the .help file as an .html document in a browser the link would work. Though because they can be in markdown, the browser may not do anything sensible with them in this case. Both html and markdown image links are allowed in the .help source. If the image will be outside of the online help archive, the file is copied to a random name within the archive and the image tag changed so that it will work correctly. For the wiki output the all tags are changed so that they will load correctly on the wiki. The online help compiler also recongizes meta tags that have a key "name" with a value of "control". These tags are recongized as anchors from the file for to the control for the context help system. The tag is also required to have key called "content" with a value that is the name of the ID of a control in the launcher proper. When a tag is encountered the help system will generate a link between the control that has an ID by that name. NOTE: The correctness of the ID name given is not checked by the complier of the online help. The names given are only checked by the c++ compiler when the interface file is compiled into the launcher itself. NOTE: The list of valid names is found in ids.h, in the enum WindowIDS. wxlauncher-0.9.4/onlinehelp/images/header.png0000644000000000000000000031025712155177255021224 0ustar rootroot00000000000000PNG  IHDRX1tEXtSoftwareAdobe ImageReadyqe<QIDATxk%u%v""ުnA(4KX^W?ȳ4FeIH  ij͌887ouKiYU72>UL  ԟC"}KC}|(_~~ԯ!ZbaG~o,ÏBA~DK)3~^3?Qyt"?G. ? x}?L1&|A;nmî}zP՟?/_{/yj;ӿW?_kcb:46)ѿ++-IyY!e㣼{z˂bv]~\Ql}DDg]}M6nc^f~y>&7_ yvge[[o"cDu:Ot3Eee={=M?{=/m=َq9p<י`]W(/ؔ)]<a>e s/L߄מ}!}9$e77}u94]oÉX~ hRK |MmsٌݷGrtGR2Fzl@Ʋ~9rŇ&KZuS= hhWPf6Y9I5|AI X$[T+pZF` @3% I0S}=~W-8LooϗE4$NZh>>8AAgA9 " ݀3?v`+{ECHVY׹730-4FqԱ5Ixg@4W&dnyӌ@M1y^ǀR q32't}g$h3@~?{}\Z!;ug!W?'(0#ൣt̶f#0o$0Iu`EfʯMÁ6@b"=|8" (!4(k 㴗30O>e|9{~Tݹ-ߗ3!q˓ 5|_Ʒ/>(V'dɢC`?(EFy>yDø}x{D L^Dv:Spo-ck`D>w蘘vH@#BHE#4;h$.8,~DD)FI72\/kEZ~yۀ"M~Zpu:'h#$L'cHPțz|Q+m̎^nAA Eu NTQ.Dm^nD0zif@Qo@idd ;cB[p~됍Y6li`5F _u'țpGb:2 tڼ9I V*HW_JRÃ|^/ԃxsO^{~=X:xZ~8<6gij}>\]ᦾV!f~SN ?{5DȽΩ -1$(8d3;S uSi@`QEcϑx>|@Ffp)I93W~7 ~ zfqA(Y29x> Aa {B`eͼeI[!A OO ^ \qf&Q ~s}o<㣮?K;K ("{N;n-+Cpbgr\y} d h:iX"(I15$_$@!htlg!ܨlƬXyEU?tjCz"Zۂe$R H36f+G9Y#o\Cu ]2tGɸ7)p'{`\JfBvn\c)5MQ%3=M-܈U ԅ_P(JIG3tH.t'\鰥v%0Le_7a84㍉P:H}f:8)^VxB9 -` 6OQ@HY}<ױ!颫%O>_~^%|3x^fMi"?6 sHjF3"Ij@N5fev"! @ͪl6g@EVmw3g2 ĜZOYg{\K;$bl?8`ňM7.4jRcr1E\-hiCPĬ>JKH@؁&r'~b?ʀVwhd!(if2:R[BEJM4vέ]W^z"Y7,آvo> : K}MLLheI O#/a3Q?̪W`R(rUH1HTYC{%EjNpa@[4BMGmփ~8a7 F iG8~ǝAVDįGZJUǣQ)E msj XQ#e֭ #R_AOs?>}ggË;N<fzwڗD蓘'ưsMSE ~iO@ mMUwH,1uaGѴ3#SWU^Yte4FY^&Ť {e"i5+SڪD@@3[D I7tѨʜ=vӊVR^g:izd +`ZgYKhAeQ1h~@ΰQq@dڙpY[&kXuB m-c@zsY؇:2@+1$Ȳ3>5ч{"U"dT1{Gqqt [.R9HÍ/Ӌd8Eq#IBq4#=r ZGm#lŞl!:q XUEW!E0^dBGfĩWgL07яzM5A;ϏZ2ۼ#3;Yz=s=0@ϚC,t6*0([ERs*8-(E/ЙGg_'|og@[öj {x8Vg/_]=M +'̚X|77^W=ϯY*O ) &Ƭ,6] bdF% p&Tf^PHgĄ T +@#Oȩ_סΝ0qJ#И?=kHa'#%=\d9ދ>Zd `+ijǘ,XRT@’VT&Ҿ&!(x:`NSaŃEpGI31Zr]YZP@7nl| 08ɸeUU> {4dr?DJȼ3P%km:_t3PYhpmmHOqa&,V-4Ȉ%A)( 1`bD\2= W?젬h`^V7* MOH[A%-(,RǤ#耔/^tA+=e*/WeӅvFG & ˍ 2(}P".΂AmpBvs k_2b7K0h5^lQC Zg)4u),$-eK[QV 9,j M ЀX.V9iF xl"5m2n!R [E=H,)NF4-q1Ђ fUꡡsP4-XH͏*HKBU$/W>!88Bkj(Qn[=>gO1k<ʈK;֤ZuNXHt])<|!֛7;ko[omxoT@=WE {{RMIT|M  4!S,U-{P%!9E#CBp`ԪKT ҨC".a-R ։,iRʬD3F1 X@=W@#+I:_ù+' #XimkaAR{k{QLl) JcyҺ$\6.@Id.=Q(| !!-Ep:6lϬB+OfRt:` mGM1Lg Ue\&*E,,O-fHi| 0f7=~4-W PKEAEL~᝿ xG?욀@?'8|9θz/Ȗbq`~"^|?+o?w~y>43R'8G;&H҇M 8BKUYIrTMP]t)}*C7.,HD_3ޱfobQ nfX\kyqClrTH/Y iQ+ cQ E]䙖eV, LJmM>~NHlVҼffG;,N pmtZ\,'է@.͂)yaޅ16}tQY5sRәEt˒>%h5M_K\_gT=M4RZlmsMٰCjDp t Jv2EA~gJ?}h/k57r2k~6dSx(_UTjyRY>dnYz9SoR҂=e&Eb'< kڍ,^OJ/ u ]e``Lt{S)ݶ&_L*ZE-C2VEL$VTjP…D_aXdO:"A0a#B*['Q*($GұT)|J"% OV l(B?VdUpbҷp$Qw]/*eW N_np๰ᣟ ??|+Ez Pl Kp+lIR~47 z}_?K ǟ|o~l aNǶ^c 6iмF![5F)RItu.ൂJ*5pBEpfAa,_$U{w=L%HY`[>4I/3\J`LiR҂dJ꥖+DĄMu영%]]\=(YnvWg:5ʝs!Kd{oD@lђ"C lT4p/V K`#v)K'ңiXN$Niɔ52;2eT(د(PCibQ6vW I4F;߫WK~C2c؁gXHSzDsNU/[cE]2(Ơw . 0ɖ6Y+Ff6* w(iCqXϻV?ZG\d̈́$̚AWVԯˀ{aK3<^"do'agPUiVC.c6acfJC6 PٗUZV,KOqu4ܼOTyk6oM=ɜK3qXP*Z[&jyj}nSpHH&նRoR,n=(VBbj5 Lvvc?&?2Q+5#u,!:Pj}'DfuGc׆F+kJ3%p^GIg'.^eRuNVE&)G}X&bS\J#HfIqVrh +NcPԃ鮂>~; p3ƃ?Y#jlĸY!W<;|65x77$ĺ<~VIX+ݚXGoLVtcRe&;|g >yʭ}^jbzpQh_]s-;C*K6G4+#cf=ǼV|}5rЪ>&zYҁ~?yV:V2[ y_iZ)t|lqˊ6+7cP4s~9B !n^acڦ /*f$zТrcglѯZ_Bx%Ze>0]{Rl-bn [Do*j.VQRlƬ)lt((L+b ^mSNy+tzZ+4q|αnTNYIA8k1Ey,ie5WڌU._ /&; jHbli!Tpd?!oiKu$\3_oZo>scO7rvNY5H TIJ 11TTBӧg^>*=w淿̀@HJ6f/16Ƴ^4hx^/ ":\˞[2yidk"Xs++*6?QD2i PǙY$XS-Rt %pyw?,V_%R,KI o jGj[4E VVlчݘb.лf q]n@qM ꘐ:8ϢCZcJ|s*Z޾%'枞3=0)霸>k˛\~@52M l3aPۿ '2a vL88پWx $G MCy>_}ScˇcWW4̠UAƛ%Bش2s=:U"lKoyEuXg=t\5֐c#n ˎ!`_W1Mo1dh`;.˃DHI4c M)ן*ކM4~q7e YӆH-45S 2;'씭)zA4鼢xKZfՔrBoll P}p8ΒjQH(eH8]sUҲw#W-}qokǯC<߈٠c/(L!ݞZ1 cݱpp =9x͚Ě.eItfx`6*f  RVG%JڝX9vĿ+EJ[|uӸ_ų;#P\E|c6[Z"Izn;$W!i:HQBY+M$ing-i:O륥|yc; r2'6@ya(>b z T50(ӿjfxҊ& `-C@@UzbiVh=[6"n݄dG|߳ﵗ٦ !mT`mpiQWoW&c5 hezL*b>+MĚkͦ00rr}"i@M$+"L * o=?RARo7b+vȉ uB=,. <;;1M>g0ĨZ-`LY?@2jERI<8 at}d7 #2MmAT(XԈ\# U#G{*S\iܳSX3:',B9hyG=UHY5T0*2ɆF")p{Qtx-U{u퀾x5K;+ Eoˊ% 19{fc(%qɶ´V7>{ XK+.ɪ5:U4ˆp:t3-mJ7-vX 0Y/k WcLW3H<(qaXg\s7 M|CaS.rr{m|m$:Ľ~qt nI[BC+1")Шu͜.LG]ShQy@'r =ܔ *{mAgjCi)cit<,UVi7\ȥJvK0<6 U~ЙxnӁX52l]5l9Պ*'So̺/ _.Nd;P񬕁4K!Ԗ'UbT66T$9j[$NPi>} {Kz,aֲe*> 1n@EZ%mψ\)P]ljΨL~!+r^?k*8T+$a`:Y*h@RRٷ@h-*8-;lPߋyyPl*ZՉ)8v;gSb͘zv jm_qb洞9S4`̧#\ΖND$ξ^%+fV|:q{:]36m {N~6eTJ"6K? ܶԙi|Z:Xڧ/]/GFM-5mLEءU9Aw)0fO^s{_n2^Ւ}k614KA{\=xAm.ނvFEr" -f*:b}"1D5ImOOO5oVMf:Vuɉ g ZRT qJY"`x[q!>ӚO>??-^/ǟ:j_@Vl3.ȃ-fewtk=(KvNx|zt zbhr븬7Xkrv$f,+`b08 7tbsyC7V=)RGYԲ L+?FIFܯ!Jͭ|N67.{aN@ư6ͫkl)Dʓc>QRZNd1v2KҰiP}',2,'؍鿬~y`I,̜YW)wZ>_ 9tLQKnޮ%{UyA# zR `T{aN so8u7x-LjR"AΛzTky,BZ>WmDF}B+)~6M`L|j1ñW}6\g6XNcѲXk7yT@AWh,5c6ኡ理lucmr[_- 3 BEtF 34}r/3N ͎۱z ef !:TR!7IRC f+ɭ"s>3ANHZw?z'w.sn~6r>\Xyb~%si6r*8[AB >[f8b[`> 6*qMG9MHog0W4Vw/ЍOjL8Psf1gailĂ3)9:g >h]d9&G|xI۪9f"WɊ!C|…td`6Ua+6Խp3@f)fY;r8RZhxeϭtA Q,#sME{2K峏z&SJ F]u<)2m_9#"$aG:H5y|k}۟f e8~KVp?I佮KP+YtP\D,ihj5Zk/ÄZN?1|><V͞>UR[ZPg(ʻڨtd.KD-KW<3Z{x~Np< pV f}Ia=XZP{YI pS$7`իZU hTq:UUAtZ ӧՀW6l5**~p}Dvm (9"jnzmsf;M}n:l2U#lEM/3lVPU"g4.':zR+,eqcT:Pm/'qPݷfp L\m=4o$Z(D5ӈҹt|iRxReKA5ȾZil|<.uVaW;}kl jwPrik٨2]%@I1F'}]̋R\Cf1Wd=ē+r,sWX?.G+*Ъ-wۖH_2]T&m~D[_ށ?tOAuh,TlU4ؼǢj'FTѫPƫs6^?h5r:yh ‚%;OhV-!3NܐH[ Nvu7}V4 vvTXEt$plBbG1 ~P=i_5UVlbSeǑQQ` WpN*lm߿+n#q{CM9\a|~O2Á9*JѸx  ۞}Cd=4V0X^ẆkoLnj*6bRxIoԞ+,цm A-ѓQKr(j**z0Fm rL+ Zh$h_-0:̨A_ ~4ӂ~+Q(c/ɚ%ӁзH $ Gz RAV &:0·] iFM}#I!AVO~2>`MȖJ"F>~dD¤NA8]iJ x=i$dy@rpEn악XzO4IsQϤbkQ M o j5MkG\}Rr) J#zAjDl+}qʪ,ی?CAPP}뢂E[!eyH4 u+K9hEWk*Wzzu6–XDm^ME6/1sgdٞ`뻮]R.l< jKAŶ%p s跺WA[eZ)ᣖ"BzOtcTښa^(q2I{_8H 6_c1K Dk??Cܢ/FGp`cD Q4"GpH|.*X nȥB,k~u#!56snJsp#߳!$bEz`sZ5c}cWNJ00%(}?6S计#D YKŬk_-. )dͲ-LBojM)3J @ޖ3Ijs@"/ڳztRڛ0hm]C-}n<ȻӚ̹|d%,zҨe9vb[DS(Fhytq!ޫ#{e\e'5/m=q8>U`va A4XԗzfzωB CeMJ[aGz|]a{eTWHIƝJ 6]hq,-zHq~"t:0?x篾1W fq56۔%MTtf_íD͔o8S6E}IaϏpsNfA+Њ"/.up õDOQ9 >40Y5nn+HM,FOdd>aiשb>.SR"#$*ZB @ǵ1aKUezXNnbP;vVӻ?̴^(Q[I+ DoV +Wfu/0 ANWeҌwrx$is.4k1MC7e=/Q췁`Xu*-En9^+oYwi -cɱ|? @l]924-UpmeKQEBgapBtM H{kBk сk~3t==EYRIsiDʶ٨cy_/ܻ ,]/)g-Ӷ&ևN 1W u`4N Bj-#:AJQk--6 UBØmQ@4=hKŎ@5 ?rWs?HzkTg7MqV ^)QuWZl,É_+xB{LJ|nL?*C|G6(Ɓ˪S@Ut  ^0)hQuXd,fWG~N X[yMFD֫k8e1d"jZ0%H13D2pUA!T"*+`"ϴ*Jqgx$JŞӄTͻJ뜢rb4N: JhlΙ0] +\ܹ*oHF7 Hp碃UEk152\!li ],mME!5yQ }i%E;!Tp^m}=V\ qx+R_2h$~uXIVal@m|qsvzVq]G/-B2[}Lt!(¥ R=]q̞'-F6D0xCCE@c9?TpO v^stT24VZJG|*,hfbkd}uwR}XR|x "<$C~*c^u]ػdF5pМ^R<ygfsy)b@T`QI۹ӀW%rcwѵ)pL!^êqlԙ#cPE}tA5̪1ZkҬ;EYZ!fAOAeUn[nih.Sv`VR!v۔ ZKf%a #0bH 5vڨ-` P[BuRQmS86 uvqCMoՄ8NWeSisPm& Uzi^fns| MiV" WI^%7*U^R5/[o+QFIyɠu7)Jtqbkr[ٌO7Aӥ޿ `Wƽt~wsغ>~gx<#h |҃*]0"ܹǪ[\Rr3HM#?y㗙fl(tfZ-M;ICWUɛuf`3լK p|"wyGGdIAx#pE}IKLN2zض'/%߃S]I٭v?~֓XE4.nT\뤦 VUjOf;XO&Ya}&j"lAtb\/Y=p:JS|N_ko GZ׶TdJv(WUVh{ЗMA3_KoBg\PKƆk[jn 8 b|#j;lՑ9`J:vzJR[Vk/lRh2BZp%v :-S2k%/t}`(ȱ7w.$xwk}pV pmZ7p*hwN>j5OEvڥ&7j~Os~Bo4_S b+\f}߅WGAfB_yo{'l7G12V  ~ 4ciS&w'Ħ#ݗ~HF__qplȺ4d)OCMʾXx / U"jĜTLl Y1-T p:՟'8 Kz2<Կ WN%M#h98XENn^z9dҴe#cc4HSഩuM@==K6fCrQ>/ yNс$'Nq ¦w%za@jJV1MpmN Tgժ 1$uQvR gRb..J%?kΗbrpEfxelm7={HMY1٧&=kv(K_ۀj lOlپtF,XǨ 2\qcoT\X 1c-\ U A_7Dj 3,\LR;-AHāD  M#Ep0qNSꨤJtU^kHlk={d=`ISiz. rjQ(1+S4CX+t 38A Sy͐>5v3aH³"$Vwv7 1$*b#>Z+*OQ=Dd/ Z/RH\)>Ӎ: 騼UzPl+`2.j8S, ";lm쳘KS_ڴeNap+@u3GWh``@si,TL ,)ӈַ.n0hה1yqci0 yd,jdoм& ٮP߯A*'jJXS~ q^ +er%1cWb|oSQ)hWkQ[+kǴX{ z}2aSb}k BZ7up=M\d{ۘ$upE. ֈ+хh A5(촹"[aB|#tьآrVE퐈ڤ.R^_㷂y]*z&^LHTnJ^MtF{04С0=<;!feRe5&UO`ѪRؒTAz&[^__p^m51U1=`TJU!z0Lab\?HV$prb1Z׬{٬rZr2\B tx-gތ4R- BԺ`˺e+u o:{LgEFiˢ]1*8T7=^YIŹ"PLOhS).6+2ݯ"ZmI/:̅YB=מr@I@lAf83cyzHMIw/TAWm *:<9CCn(xCa%`>nJ 3RZmni*ecA[$@"0Cp=9\ꕪׇP!9Bju;R!hQ Axe|mlqzrNtL5Q瞖e }=v'凞s~c"Jd;=h2 5ct5̖Z1smQ^q@xģƷj.bkͭ^~j`.:4h( 8OKJBUd혪"assj.{̡53%‚"PSQEo᳏?ܫU|qLK2d{ć-UQڗD̢pD7Z,_?frQ>dn+( i|RS& oOG+??UJ')KO)K@0=OQn`܆~v$|?Wz)s@T{:L-4yfA\l) WV-$M=Hcq(̑%I$zg:%qsޣP;* ŀ-oJVy ;Q_wF{=kΖ[?ǭr/*IRL2zvO?/qr3PX߆aRJ;'e\wn+7b;M#|z@akâ:>%v,i+ڬ"z>8J+ NiDMKa׋ZWI 0oנn_.nȶ@I@0RK'|9{aO^Jq].mpC5~5&5'v/w85L೦um9OZԄv_$tCVR>KlDb|hLU@a[rخZW? @Iukjc~yiYƥޏ4sC-nڢ^UT6Qi(,S{;W}Y"LQyTLE҃~R'V%e 诂Aev1fW{ufsdi/ @Yr-Vj홴DT$l {6fXسT=Kh)=W 8 ǂEs*ثcjB]Jz+'zP0N7KILҼ^ӷ r@LQ|(ΛK2IдMGׅB<qTVxp9"-ʼXO 3 &- {o{3 IEήzi<Kyj/ILb-!d@03$r,7 e^ce> cyh$1ff6t[h>Y<^lݵ6\5=Hm+5p}CEٺl?{5 PGݠ/^@M%}eJ6-_>@k5nY2MK邏m@5 ?6lg/5_}Nm-(A*Pa8S_p`+ZH"R]"cD_N} z,yt{)~\' F|\{k;8т+Jvm:Qr@QHrj|%x{Xy(Dg[kQmYJ?MFɃ,4:OphR"~Wۡ-MP!r/ [נ}d-5cP|!h\S B).|vol̗EFݸ^c'>/^7&.7d}@BߨOG7 x=Æ:Aiq6(ek-1ڐډN+B7]4!o-,7?FgWfi>,o+f%hmuqEMvFʡcWh{)Re:Xcq9Jc//aWtŭCJD!  3vb*NQ/u"Ѥc&^]OEڍi '?#u ɳ [J6w3ccבp(N5|^Ȗ{, pK\ғ'o5FfhíH^R_UqWpi Tɷ`l|< 1.& "W)NJ ~ĉ)^k <5 xynVRb/#Us4P Ak>f#Qۯe_U6ץy ą0.nxwҊh[c涬 Z0^';P%Up- bVySiSTK#"(U}5e1Zt8yhԘޜE@G7 %gcg'MtZmj`"bib/ =|Tqo/_N иqk-k`LsM-:o d}q=ooeԵ/| _gOB^n:wy _s #y_[lTlUd4 qGDl"GE ,CR*}5嘼 ZJQ'ʼ-m&!:3 ,j9p=WƒU62vI =Q , v@xmQb4K"+Un&IZ&r 7OW[nB:B|Rm7n>"|ěn5i!]V+| &} b8%H $%`Īʪr{hVg,'ai,) UahU5"߫n@e 6Kۭ2gl.v$ ]e#p\y@%i+ sY/k \7s99=ZwmD`ٱG]F @{[$W ܡ;Ԫmp5GnA|;oOXV3ؒ;e`ф5@!.dFبƞކЋ6`t{Z^1[;MD8Y^ =[5]ͰîZtԙDZC"evk/B'tUSUTT xMSS=}g3q+".6IQpT$tLzK N&+4`/<t=4=62*4?[3WRe%X0xx0ieOQdA2;TpXÈ:Ls2nEJj27d WកQLj{{;XG%yNQ0]*`8ÉS'vRyQUn@a vp|4s*v=^bA MbNGe .̨6Jj%7\/t ꨞi[|2$g"@rc. V hFxOu8e4Hs{mTws%B"Yƺ)0A&Jl}yJ*PO][гΌ=鲧UA{=&L#NG?T)=ZA-'i^=>ث0Suya w'YTPƉ)dX?! R3*˝ h֛>5&(9̬ ړd>- bӒm% kfq9>A[6t&KmcэMէF!xz=A}[3 6Y]Xk%i&i8h9]8VGz]4)2>OY4&2VWQҌPCᲈI=\ٽ&i:0+#- jmO^zs>~no >wl '.Uz|&6Po^/. SDإ%$}Tu}=R> z"6ʹr5![z s&rGQ)L6Z$Vl JeNfZmd<0ũ&:)? z_G^YPx[$PaF [)a2i"HZ1ezM6?ϪudOiVyQ{*8i#}"4|w{6mӿv\FA30򑠦g4nv_;W47)m,-K)X yf^cO`etvzG#j-X V S}okǀ8d<^)3}Fȹc@pO1#eK=~BTض<_%t)&﹅;RPrd` Ye,,ۡ(`esͼMv v]L\و")i(dN$be#cD'4Hsz鼦(럀 GؕBo;ؼI[y7i_۹gbx8%m1t g\Q"8p:+ʛQU9^ B˅7TrXk jd,:]Il`W(N.Ykk>ZWKV;Y1RyPe,x{gWM sbUCf~[>6 ֑q#$oO47+Mp^C̹x5#ZnF} 續9 ZZ5(n@׭ae/P=;;tw\G4XՈ#oiؾx~^]Ե $)y–~n r}(\Ok]%Qȉ3T\3gf0HBsvy$cIf k˵?c픛X,+n_0:bLܞKanj({LSGLâ1xJ5 Q7:2&ط}hc~0ökcEZHhݤ>ۘX)T gX̪e.yZNe]:DZ}tUɂ=X]؇>ާT+ᛙD6Eo'c I6m 1cJܼh0MGit>`}3 s`6I :"RT;u}ǼEsa@2%dVxGt:jGl:v= k :{j ѱ*[I`3==pN!x/V~ JX{9 'wRysQ2WFQ[zȆNQ3b6GDų,ԓVzAXDo9LiwaA|س[>[z-ʮ*Q'n"@}N@43n\CK RQi(UJGUH1]2ƿ RbDƚ + f׍Eu|'w1JApIQ !fN*@%=7跙4vp!S U 70CUq }#4>a;([0=&vx9N4,ȎXWhlzm@+2?'c$snnI8ԅhw&R7uX%>LiP@[ߦ- D4ccdts@8SճFM֠+17l9\ WB̤Lsj^__u {騯͋]+?v3k&S\*wypP8isV'Y5|О'#uNo&ęsr(-徵k?羴8I;W#'bCrp>_m*Lua UTMNj6li:oxY׋vIS0EVNZ]X]=3{;_w*=Զ>{i_sXL  쁫Q؟BA6!?hf ԅ'k BP[8l _aEK\z<n~Bps%#U+^i֮ ͘ -oCN mII~!ct[&f<yҽ߃|?{|I!a{<ܚcz MWJ%*:+zᔊJޜsC${!fX;{ҨwlS#'fkG`0,)<~MLyZDTq0a2攋\l\Rblq< [W5;i }P2ξkۨ~hpuРrC$-@]D!ereXd`]h'2Mj^ؤH~߀[,|`|iH}cpi"~plr<=,Ik/|p``d;hW,f4`zk, dQ(ڑK~6.XIOu=qJ2޻HE.}E0% Zjl%:}] Ba%ٻS^=993#Vֵ ګ5 xSa?tG}{<G),Kp'+?}ă*,=SyCPc5ϫ6L\J^ZW%rE5$Lfl20vr=EX gx=SA֑h1-JVhZTX"Y= #hQ(gJCn@܀!QUOs9 [hmS.-tdJVz99=Vz>b}`a>{i`Ozˤ"*OpIlf\oL4}SZP)Ȉ=ڼU{Z5R%j glWk6\n&H_Lu,UU-'0UHniG/P8{=FOJ"h9G8MJL!V Σu ݁YOPm'KS Ǡ|#{ǣqt͌ǡq˥u{`3tYTGZ2ȠCV9^&QͤhN:>6? dj}mKTI2'42ǪкTq-⺛_^PXB}'˪%FQTl-rU"v TNcUڗYĔ!Գke$\*/ cx-3e< ^;~?zS_PC`_-ً ܬ(mnR4 eI{aYJ pXbm.UbE_v=>|"!%?g2Vtc&~nlՁ,f1iu e O ՜("hanWBf.~_d7/MzM1d񠾮)ʡwT@J{[DGgqZ"alHw fl5guV)DQWb@X)r ~* MS"d}PҘ5z{›eei 9XᠣFfuCeќ4$`/f]Am򇓂 #)W*THϟs@׾I?ffڂa"~PXl죻+ ټkGӘ"WIݝX{B dKJ~=u6>b= {A v~n)uG?ڋgcZCo#4z$~ S۩ʿ 9h[˜p`>yۭ -3ڬ\V> 0?Ry&5*ޒPc yD{akFRkƦg/ |4^~}J57_Ă3i7S-zKڤӿ!p3+BS q$r$W*5oqLl%d1zj/IG,y1MȞ#ay8~l5R=09 O;rN$yd|rx)Q|  $ kg0k>cOxjN _rA@-INMYmXa Pu k=^acYxn ?^بP)N-I`+JP}'Oz@YuThmb^1Ṟ&pWU$RUhkFLS'sCh 9P}®Pհ5;Q{1:A@> QFN`d[m<"hwc2qiƵC!5audh2׈1VʄtiߚtjK=e۝4 +159O5hZ,qAa.C'!!vExIMq~+&n&=&+^d,b IAY#4./eDj*oȖ X ;6$Ym)+xQ 4bH:~0ꔫ>Vzx~CӠ% :!Xt%IJbB/K39; /2=c`J`־ (',P(jU"X9nbGĭtM{Z9⊛*YCfB!7U(na=WP}/u:If)읷 Ȣ. yKkS)iHpMZ+E+"lSua@끁7gU+"<+?x#抾F,oR!Ǧ*Y]ۮ a V4 Skh"jY7 ]Xq1R@MPmhO;U '~埓8ˋ(?j;­rPd|t_hZ!ibxc,Eux}q-HN5dSe!6eKJ/yJz`^蔙O].ek.t50`T!XR| ]6pi{vg1wzb?`]omczc~8y$mcG$V\SStن_*[iuǁ=9E76d=##)ӅAzT?^yb;|c-ԏQŒhb{I2ÌEG%#RڌY6Q讋V$y)p*@2 sr(M٩9O 6 >ۇi#a ,yҙbx8@K\QI%cϔnOJ W3!*q再ewӫt[xEL+WJiL~{[eYD9hQO7QDE$,tPx{9~C@4i@)Atڴ͢ϟh!\ѹW'ϛTb;뾷,]_I FVbO \. . D]8^"W"n7cF`7[ثZz[eM%SA41^VU$ ԭFk^vonh\eO DBzZz ZZu">ʨ j7:+`FUS̅]* DNER5zH}pU-Xr^BnHQ({F% ۂ>Rjm\Q8[b\<#Y,v8"! Y8D%@ @͊хȖ-C:bek̘DGG}EqւjIy.59#E^HW^:{[ ,FFs#^eЋV'\k?GpEN7f+sBfvO ".EJ,A@1tBF@UIk<*:Vs8E!B!*0>V/ ݅qΛVPq/ Ws?/u6f\ I%e .EGQ`q^@ʢycwavـnxnߪ\$JsGg D^=&jcr [{1. ԐBYFt v;.%s&b*D,GYaP &B:J EuӬ}d*m谊7i0iWZuV9[6G`s*-jdaUI $; BM?ѱn^dcY!ۈ!XQ/~=˸݃ݷ{&wd'[pͫc"+jGyU9Kꆑii=3LLIIV{{̖Q_f!axF`IbUe0hN?߄~C?5FsOrψ/4fyz>(B7%NY EH(Htۗ܆l4:wj.hT4ا G kƑ]_~>n-A1hoM!PtZQ(-&+ abCf=hlx|o5 IEaH©%*טہJFzƊ_z@ȃ S}y@#;8r4QkZ ]E kbsUTcDjuaP!9 3w{QX,$/E{5WxӘq jI8?BG;$~HR#q`ΆUeh: 侶c65uaӠM= ˦yu¢ybUՐc8?4| J{+JQаNH2+qyd bgV! 3F@:h}%&}RkraTy̷̎6/ 8ߚ>z2\]wi26@F[ʈENƅ-Rk'z^Av nR9 #>,><_S+C3ٜDv'/.U]z `14J/{&:\{|iDi7NA,,>Z汮 S8ҳx\M=ZȀڕLJD7=][0\`1ynP.䗸Lͽ|Cދ0yf;<"扟scW|oH~ĀmXc8fnև9á}|h`kߞ?}^_zwxXЮa 9̉#(=\)?DN@} p'!i1RMD d@֫1CD,R `"68, lj*)JU9?kzcqBRFIssU M(K¢)oP۔b'ͪk kC1e.;P*p!'{&ߧNlƁi@^ YǝNהa!!@Vj) Y)/ޡW5͵-&X(&B 墳8*|ﺏʚi)SGr^bKVhXȨ\6EYm $>\{6얃p+0-դ69yKRa*;21B2Z"Uk,/*I$Z޼+ONno:)crgtv@sUlkLwNL d?rkZeh4z:-8ka30e,*D1r}Psb>qtd>Hwd8Pl $;S܈BF7v7-kW: )Į/ʦ6J+r1WNz ͜0K-iPQflטs({j\:Ttq;9;BUy7JR]kߡ \6٭tZ$k(LGR#=;Xckv[cK1̤ISݷqw \f ֩> 7C3l:Sn$5<:$!cEaCu] xB9ܔlH~쥘vsPuL<>éC9T[@"M!h!axQe@M0Cg=Ψ228eDTk3  T}Qo6,,46m8 Tޔ&}W1 TW4b=b3p]h 빲 5r&b*̧,%k$ Ӥ||s{F8IG5S$,yI^To)VG#’HBrՑ:a(ž n*?^[9|:k]nj;Oj@iyU }nokPbyϢޱ{x+u8I#@N6Ϋ%e݅~J#*ɫw,ͶҪ/4 [5GU\,=,g) #x/?EWy7ezggNmbIֻIcXNJk~7W5>%V0nϰ&VvV˦U|%SZc FYE]ebYB]9bgde:TfX%uI\`yN YR0M~x14RpH2$2͍ 9{Τ:Z .HO]!]]|5glqdepdל<}Du8f`d|ܮٷ 9=^ǘk : ܕW^DS 7dwʟE/b,lu"CX} Yς[{UXnNl ƜIO$lv;־KK-F"R!WQ?x% ;tSƂgy.Jy⾃vULjjqZWV(H3Q->0cuBe8eTٝX$T%t59k' 8Ɣ ֖{I8ZDunVkഉb:sCF9ʕuӃk0iF>1l* kyу0s"1,`^wEXb0h!nq#KR*Ӆ%[dYHF%$eLd@G׬R+E\HkC^+E<+e j>vLOa!hœ3 FZ^P.1,%<]jnTH) [{kUA\5_k .2M^U ΌI#^ZЙN'UU#h [ci@,VQ^g`ȴs'wXR3`ً6@.Ki4B$w-9FEnAk{]^=3eϷ* Sξ~%19U Aݫb'd$ZKgs^|dJ+>ٙ]⡞6mBa'KAv_dd@m*=Ƌ,_#q(cЄ)!ga)p P$??ǎXTkjܧE"2ʍt_X/W*ES@[nҜYviRӤ@Yi+>Th][ZGڼ"*ފ?Bg^yn7G֘j=| t~uWZ})>!@)I 4PX`e߱KA,BN^YobQ̳O~)PhQp>R_Yʄd$ Ɯ{URХZl5e.ζ*9?u5aF#t OZf ZZkbwXQQcTVg8KSQfQFh*KWhǣ 6ĎݔI"nf˜o/M19?3&՗j6׾ N4rDEf>o#Ƹ&g~wCǠUxxp~~- z]|,ʻ3U1 +*uU61 ' kΐ̳/c};%Qw2@IA42T R\ '.w~-<}1ٳ|1N*:8ffr YIyo ;8jm5:CO@S2b |kŌUЛMhi#D)c!/4Vޯ>0[3;z_hV hiE eUX: mČaiUSɓ]v|c<(eyqL!r ? hٵO,\Ut ~H= smi RECAUZ- mxQ}5JEYRĀx{bF' keHA#9cv]efAʾe& w1Ւ)7kY>SwYm;HUWrFW&ɫB땭Gɕ5u-aY댗lk,W)jTĠ3k%,Зr0e TI>TI"Gh^ȳ\TY:k9ehZ4 }fzEQ"ˇlꮺNxtaui9c]vt_2"T= 8 T3=LCF2Hkpgr.tMP,X+aȳĴf4$Hogjh"JV hh%:+a駀 sKJY <Q <"+emeL)t>eD`I.LadNKɏw xQ+^!=OJ>r>9rJ &k7 "Q7O7Aj؈t@kD%el M0: r}݉"b茽:}-զ+VO9 Hw,>0\b$"CړxW$YsYݸvFhjlK4]ˢ"z-NBp.a oRO;^ ٹ6XK!%zJNT,6PS7dM9)K<1C ~y+4FxKۣ3{ݺImY]c_ԕRcGEߟR1|qKA*g1S DW]yQr!m۶B~ qbmgn n,ې c7W3MYe"qCsQKXg҉fqGt_ 7j~:2[H]V$$J)|R@`ϹOE6D?o7=UK2MRb#lѤ`H4#u텥kG|ܓ5+35 ;5)$s['Ow8q$ N<5z]xUS{eK XOɄ݁*F}ќVBDq]xYi2KJU iiŽiYsx8~:[Va$1;u::]}L.\0תvya 3y ǾeMTg0W h )_7JBеcrz8a߮ lBnάA2ץ- \?:̪YFS{g[R_.ri!25RMR)k (+Fǿ]:kΞMt&>LpU 'G, 򓗳ZRr|298O?&6)".+vA[{|&@tz&Wt+Qxb]'eg6U!ORuWI9?nnbUzw LE@p~2F*%qMέ MTsuG`TK99桏p33PĘz(ښx5)|`Ѡ5Il(EWUփe։ê-ĉI1 [qtԅeHyKpk?*0+*@W@L~ rYV cE/"lrHE]W\-~ (j_uH#%&E@79n0>h>g_1Ju=/= L?S7܃5x֩qu1TC )C⩳6>ݗ9a$@UY4# E=`3#<ۉT;l1ke 5ZC;0ac1.,7*eY@VHFagha(?g)juQdVE2Qn E5Z]? uD!rJr b[̞'ˈO ΞK!$)tAyMOE@5yյPr}@k*ǜ"PNSc~,JU݆u.aQIZEvϻ.U/ep>n•sЯYކp9{0|0\%u"y&ި<1R'7Sth*:y8GK(Ykk_3Ҿ"ωG^mwt&#{+ >YknzT -!րu?UWJgE뮦HC*ֵ3QuȽc 6 kIJ~]idX '$TwRF1qZ8v=]D̤)0]at@%֢֤ -Kf@+昬X@ZQ-UT18&Z zVan kUeax{E\L:X p-%4(5 @>2 њ6(H.]1k5@tY)NØ 3gMY}hZt]4cD9*Ѝ26^ aVY_i_ |Tǖ+qaҀPk5 ^ ݁g55XjCz֕<9IEN8;\6Vӥ0? %oꝁu.W/Lח%י"7̀`K'32U :Euŗѡ&?.~hCURE#[7S*ܷ~τ]ioړNjkYtnp`'I@mVVΏsFX%xWvnB-0IHkM6&k1-ng4uM*qViEZ}S1;y/ZKk38e cKR~ 0#-ю)'sUY(0Y]+7z⺁/ g,̡K3z,h,\?R/3 Uexfn +V7" 녁Um/7-`6We3CYqR>ELBufC&K<οr7H舐kh 2ڙn?MZ+vWN ;UXK5菘&Sga>`?}h ÑվvEo#ХUBh5&sVZjaLBL۵<ܚZY@ q֞xIqé\k` XGڔ(an% f,V*ؘwe$vYV$:xT&G:+ ZP!F~GzL!OvTe MRAYe:-a ) i,ٚ:sEwRX $]W#\ooðtU7Rߐ0KV2i𢜧WH'ä,j%F=E)E>y%{m B$@4-Σ|f~C3Xxǖ,He` Γf'*)E\d !>ZteUNՄUY3@u%iEf3rޘd}!%c|XƩs1lujy^/yY'u:0h?7U tC,ƺ漍bΦU+k,_x8ry낤S \bMi홱j DWE0r ̎-KE쫋;;5q -8/B!qMѪ JD?U5MZϷB-@*lEC (u.Zz͋kta!z L/$'JЮY9lj̘,3+AW7\ okD[mQ79ZD\Zr)ЀOw}fqi=veoMVѶwػ1s0;.#gfR&دHu$.sKensùT>; }CQ=s>(yk}r^ܩH'mF&fTT7W^hhwi^<Z M#ga@2lW**\i2Y3W+(X h]2˞?'ŤVȰGF<.L<2rhbp#fߋ޳lN}L5HZ:iJsfVs5|)\w1FvK4yI>WҘ/R t:YZ-b1 :ۅY.RWz;1Z^s/ *c4ЕM##Dlf0(pRz/.ŠC{k,)IE[%Epf+,_ȄOm &]ZN^$lTR3)~{n`?5`E@y$xlo{ط(v}!v1%DʮxF|蜷='o"3}YB3l}IAx'rZAY)i0_H ~!+kR䥜 U$)fewaz`©:KmUkZ8l/iT/oY s:96_Wԙ6E1Yغ/y0i_^ ]rYǏv=_801K-;feG !&g~BEZOmMvC^YZm|v.iduM)l>D2Z-w<ԔWVZiDDԖ052I44 e|OW8Y)1b/P MRCQLpy3&u^Oe߾|;2Kum-[Uֹ: d_ un~U~k@Ǎl]x'"E"l%X"caMoe 9x'C@߬1:iҪ|B84f/N05`?~sU:=,o'fhLDBbصGݬ`(a|NWF,r; 2&Z=WBEW"ό8TZ uDHNN)bd ֤:䎻km5Tc~* lӊ' :s"Ė8}[c$Լ%[E$YuGn%!>n0r~ǟ)s[>pjoO49>=5pՀ3ڵ߷@u˓N^v" ȗЃ~iO+zX\w rQâLDs7i1p$L\ JˢյUPzQ^brJ`{]|uP,策O%Qd]!丵zH9QSspU,1 FcfRbt[x.̒kn ^D uxrZ4C M(U!~UFy:\QRĜrvw xj woӕOfӮT'{=O:6uegyˬ8omzm{nAAfG 0@ǿST'\ZNN \}e88)xΝ.!:ҞsRohj"4 2ý]8J}Lfr5T{LKt/ ϥ ̙*2GTuV롯5|b x~мA0.3yRxCg&[Kp!y6Є]`q^ c4}#RdaY掐qR34Rc} 3Frt*qu}Րz#A7h%pjÂ+$C_WH?uJ-qaBz63rkkZI(tPĩ~:R So P-Pkpypf6pG;eԞSQI!!ba5y)vC3(GzYTuoYz؏,4EÒWX nj 3UvQSyܿBHZ8jyLuCUWw E MY5之Qi`5 B"Ysv˜FKxTȬǞ iv x=3:<:|{%F^j@wxwt8BjDw0)cPpv3U+Z/cC bO!R-^ ed վ:ur \}b@p> <no \3<}t`MϜfEEװKv+Wxg:7T}a4+=^=BdGYJPQ@tbBwJ/t&g\?g?w0.8'=Ca,wZ-!>e/qy9(hۃd@Ѐd6 gixU&|N]Z)EU0^H wȗY-9{2Zkv̰}f㇉qŞxBP` ՎoUI 8MgrܨHʿkO2KuGB.Yf5Lirng_xG@3/m8F ; c=#🄌&Ͱ{]}43JĹ ȸ&9l15Fp j jr tVEЪ_ʘSբu=&Y,Nh w5>$zIc%& 8\G{ePUpoa(Dވdz6.@Ozt'ahO5@]"ri{u;~I,^s1*^/ >uL-1iA2ɺ*E=2^J {6]{-?ۧ7xy W84o_? o90sL610YӸ[&#s S<}dPSTSTO j ?1:sCSGo'-G=P5,lƔ遾L.ܢ,Wߛ">p8{s+܋SM; UcvqXNzCU=MIF$ i>tK ecG`-SJJ VZ&-(4x6UB"%(a /C|.LfR栙IqsFWfh{Z1C}?5VV*:c,a,gЪ4AݚdʮmH/kG"]ES2 ryxE$5jrU>":VZT<}TDߛuTABnI3&P$`2rB>7^X,>>$6M2Gd/!d=L,/رf.1P&u=Ej_>KDF!ЕECˬVj.N蚶 n:8-\4w",Ti*}lHܰ7x N/_?8J||?e/lb+K7>PT!O,lW9nfմ/Et H}l%l^m-s:O4 _N? >~>>O|Ɓ KF66qr߄n ־˗/_ o؆Z6u,I,J?+hBjrՓk9sK3Xu|%:6΅*\w%݉C$Qa܍635R\$^p@|?jS;ڳV>4yJ>D \%> j 'i#!$#6zj2fl_tp;Uaم_$):n2 {ٙ锻L#'O7LSUFm 6!OjvڻLց{xs}8pvN#N=ȍE&FOPf]虄lM;n-iUˠ!ɸm?AƉW<; <$ )N  Ѿ3O }{|2=~$T2( qtcx> #L7GnY*h91miP]`O(LO"?(o@_>o_ T1R`uTͅZn>μM+KەDnXgXW8',o_\[qh -IPzFM]^A;0)*c@NQ.2{$Dkht~ymL8$lfPQX嵼,u$J H/YZIY+xn&Hknԥ ,Pzr͖ͪ`6>&iԣl=S]zZ!&eLV5:I.2CjFv=+;ٙOA݇8N džpKE.Bg Ljլ,.s4vY[^![ԉ 8|h2vY׽C{^Bs/Yf͘pO܅ۧrw[GUک$;>~iǿ'_~>=ç _5AV,fv{Mb+b z])xbGyFsX*n^߮;, | hCUbT:Bֱ^) BϤ(Bjoi'/((\hFl^(,Y)s˜w Cxr\'QoƑ`O_>N׭M'Gm2X57싴MԲk/i vR%Տ6jiӑu-􈀜-&@F$&f*P>Vuh?ZRp)m(S:pw㌭?SW2Qh;$!aGkN͒$וuRUMc#JF%d/LG$H@/Uo%"~#2UuAFEz?s65lyնJqncQ^f _بVDNLbTxUd6h,Pbszۤv2?R7YQlc*21%>Zܗ`IJ]e]q* {<laZwH( ܊EM(t3֫9 E&WDNҍqDٟ߈9LQMrvl 1{+_sVEZF2?DE@'^˾5m^)H+s4`yơܽ> \}sxS3xu=ݖ0XJڽDQ͋F54M7ոL}ٍ$yEYg :?OGj`3f3{f(Xtl]ț#yp}Jt/T_o DL.eBmw}T~eiݠ4; 0$>mZi*zaoĵY0H f"BVSZ-0VnRK,# r(i]@d(*u7e$Z.]~BҖ}(EW%ĬU1#i/큘2PS0dB}~{nfex֧~Û(^tA8P.) }rY]pkW`("=8Z#`#!EKd账8fs|y9ͪKM)kIRp y0+RX=LWՉ?\5iO> |[w 2 !JIŸ3~m0sv* ^b\b#9Vcc8%3>h !ɼBP<-h򙼱d0rM8 ]{E$ț$тQϟȌ%U;}:03a^!(te KVԝ IE]Ɲ7 4uD b Td( ȝ@@A,0hT:6d8zSx&<78h{'mxΞKأFQ%E!rwҊOM5,3&䁇'LEM'ՎuO Ⱥ" ih>x+4S <Z_ڠ@UE(d1Ƌ2'Vt쩹$݁R%OQZCQ5Q3A,EUռ {Y`kH.]ϑ_&oqCtLe\q4oI TO/l^{Z"M$X"%aePc.$6By&ۿ:!X(q/KA}rޕ0Hi0pI*SV&v3YP3LV:2"Qk)7- o[gN|;$š!_/nl-!Kι(?@u|k{Nw=M-'`v0 UQ9u.̪dh=8LwPaeC>U2ST W!>4s^^ pJc-N.md 峼z _36w3[$nv he[{hc^iҙJ/lDr, Ϊ$ckܖm=f"58ܯQ\\a^E1Wx{aŌg2^Α$l.}?GFe cUnHTK[CE<サBݴ; Sn{gX&gsͥ3홀J6ˍMR.gϾP=FHPdG( ,uSDC0T0n9k3\|)|A℠̰Q#345Nw=|wwzs X^8XZuZd8$M$O݄]E. ى mZEUhL(y}"p}s_k djedP3H%Ik ՁfQNdot{Μ-麶++O+k6ѠǧJ9sY=RFzdw\N%*{"$Dy>O?-NZ@,߻@ze D.DG+R-ܔ9T mSϮ0*{2HOhB.cQdSh6 [Hy2J0ݼP[Y2[.]0|\ Ɣ"a#yG´RURsKg[F;^O+*|bG"=ˁ;ܼ#9? Т$I5ZTo%7wID8Dә)mv,(m~%]>J5Ol/8/_NKoَOX^. ƈDZ19{P dV07dmmܜg!ͣ&bj @QQ'3emQ'^dO3 _;FU{}62֩$ݧ_V!VޣYLvXeeQ0 QED ټBnLL7wÕiM Uѻ#椗LXqzW03\r 9T:ܑS8!} ,??$)86Շ@p)+HzBaP ):`4w$kF8HuAT& H[?#ϻ\y0u@^/ #,aѨQmdG|'M }u'5q ʊ|.Lpؘ!$N-Қ>@`u-`nnaSՖ o_M+maGa'v4.':|j{lĕ!QBW5z$5}K݄rlvF fsk(Q{߲;sxV,;3hb{{n0*c .T;3ۂ+x/i 8K@5 l\F6̣h7_c@-,\Ke&#XJugkLC;EQ3DI^R:XtѡZRj]̦ܽ~ú8CbpQE Mq9X>hPvg>oIϹ[ښ Y2gp_{l5 } qYH~]&.}"po4Bt*& )p0[SV씟Xr,`@ $ʱ{R(l*-u!R"w!xY0-R3rXn|[5E=<и(-bP'xeVYl>KS.3MMk:AJV`@TaTJSuꅥ:0V;iڬڔ7T) kCU(n7Ue;tՋ:ᇏլ'!tk"="-wKpvDI}xjx%B rbԂ<›b=9rHe%=Lξ3Q\E%|`FřމvleqforPVdm# ;XXwMˆQX(جTNsb0Q 蔽Gr͟Lxp>tRǹdp% eDLy4df:;iK2VM\m q^@/1ʾF6f;\<ud \[)7:sR@rs{PWݒtB֛2&ʽO׿{@a1IK]-MZ ݬ`W6eͧ'|.iXud#w 3ݱ ΅Na!1i7e{EIiL?8\{sVgdX6o^~ '9Vϡ|oNx#vG$&jajBv~Xtjc9-bQ4;@iLK|:sn t&Ig^YB,;hm0f^X-f")Yd,:Yf@#I9.siW<5i1JWU[I#Svg|\ZTmm+vǧPXs$A& {bu& nWp.ob^[=@e 2{U}e@ %Ƕi7<$گ3 S7z~(?8Jb3ҁXs 93I5QҶuȦҒE$oD_Fu⟇_fi}`3k9/-n/iƬn .PEQjW08jBʡ7 {g:sǙV۟7d.oĨ?(t4νs]?Kz¯V[_9<}S6 Og6(tLٺ ]#9jP#S% 34r?j 7˂=Wo.b \~zmy$ 6<o:جܗ2Y}VN`\cE/>) lL{rȁp@mjeq:BFRhFc92w,qrйP~˦64c9CRc^UbVT6G#ܼdM# BдʈftfU#%7-ndp3b6wٛ*.C)ef {Yyq *l+Bl.e@Yu"`jJU\lUjC&KxUr\<= ;"4-;Ta+pS\^LkTf4]C0)}xrgE3>1ڈTz\%; aA3gEkQ_kY B_v-aW.f@WZeT.S鐂a0[F.N' fT3Cqe[I:9h{UAu,Fb3T>_*W3) @-i2,sr13]my@ITJV0k5NWp-@8fe_]۲{lY[bn#C$HZ4_V_Oq{ %fx-"+Bˋ3ZrFv-?%AżkA< yԉ%)k3\v 8XHPvYAmXtwQI` KAD)O] B/f cZwH=~OAվ-b(VxxJT:t|b%*ǀcwFS9 c+{Zx@c'I:eT3 DaO#oޭVYn6<[2gGvn7oK'kh̺5$Y, Wdah,J\>1Ò W*S'&+*;j^-+Aǹ{`m7HNy_UyrÖ^S*sJ:bݠdAu'A]fL2tً_ ZdjՄp~\QZP3TmiҘ(Li:z]^MC:HƓƎi V32;.rc5nIoˀVt;1P$܎XFElu4>1j]dH@&l"t[$rjd߫ɪYZ̵㪃R5 VeXc,-.3콦+X;{`e.2^@Cvv˔2sUiC压yd[i 0\1Bhj]yP`\EE-Yuv<Ohll䳜_0Nښ vO_ /%ZܞL|\k:.uSY6ZCuVee 'wTCݫ7He=i,E;&>XO;ߨxyn[]y690WylL Ui!rq>'U/ ]B31R:Mk #o16`'/Ӳp`?OL}Lvdݤ Xb[\`BOى9Dꡢ)g.:)X$-ijƔ)6[UAzn?`(tw"Z+S%fDtMqxs֩vS;$nv:5Iй^d@N:Ss{:C~,큼˒LXbtNe̝"p bHڀ(6UH?7հ,,͖EU| MOG#T^{fg*ˍrL=vs.ݣ145v`f5z>,?wSWI*rݶ+wϧ[8J 7񑅘N׏R2$n.%^[8n`;qR\?r 2xs8;9dsEfji.mHYœkfnr} ikB>Η ,p=-2G5w3ʡ'٨iP@2(EY4 FT aSv+)#6-ؘŒ֫,  ;:˫;H+tAi&I]bTX&/jg@ ?b)lGΝlTs)qW[df?ES]pqQs~"Gs" $-}'sC_iB[Pֵ%s2V80cD S' p?r"'w&XT4A.jM{סQjhmrW6ni Z4 Jlq0=?+x4кv%D[E˼m#+@~~YNfIw҃|萻}ttbzv=idD{$fH *Oe¥DLJtHC%DN-? > k`Įt,j05뻈WUy`\bN - ^-jJ^}la&+"Y5*]q0uFW??ifXԦIuj׎,NI#?xW30Is45X96Pf!jJBҦ"i_0gSc?3AD3T=OHWVQJ)9 \'`DӢځu݆a*`iYolLzsLI=@y[=,i(@韔&43-. :SzYߔC;|5"M@ 77t(>pQ覸MNY(buvwT e5I {=@Ea8O2 5ѼԆ?Ic-9YF|˛biM1jj+9"8pGp/p$)KwUWźLSAuVB' Ms)"e66{ a`GAKm}m%m,ƂcVpTU \Y{B˺D[ؗ - O,>6j^OtZ ~C4z 3.2Tk$v" o6-WrM"b0~BW} |XMpZƋvE;]g6K>f m:H }~=Ll1آv* P6P#{}5<} ?pk: I%eī3.뗅!ZQc+lB қNNZD)(h,RM.r"{I 5V\ШyxaT+-Hd@ e{*0= e΀k/<b욦]-3Ea@E-h5XE5f:5Zp%YI뿓0!YbݎSYe9Հh|uaW>i>^.'iܜ@兺3ٕ4<ח CmtQ)6.sЦ>~FK03IRʈf^ Ϻ2kBP,0pan'h]}=jy1IU(-*K'JL7UF(+ڛ /al~ Yp\!K:CCEaCYdXМ?Ijbxhýj]+a2}YtS]C٬ ڧeon&h9 l{:ך1so|]}P))y'DZDJz܃'2%PaHa j(5$Dܨ haJsŲmC62nYӅ!\<*ٺ&Q7f節/Y2d̓&YAûGVu3%3kT1SQf8ITyq ϗ܎՚vz;2b>>.~rF$2sn 7g:j9tW#pC SW ޾{|9|Odz~Mnf%_\ =qƣ!.03;A>l$[a#P(JNJky2ko!󨕢%Ctؘ22J_Q,Zy$Ae*85ŏ)8ʮk;ʬpbPub E,ILKE?DHkVB{o(/P</ &/ο3F(-2S|rmyFlnQB"+*Ь™u?ha>ٌOG*mpbl̎]襀ixZE^2*E㜋AJwu[i1jٛvtTo8i`k{ET-,C -'_|-P?PU8fF2[~48s1ƚh=AYtɞwp(esر;>qiBXeRy:.<`OVg\mn\uߓJ2Gm?3΅އDn|etHN]T-Q԰:pb\ !ʨ19-Y+)ٓuݦgϩDs^ǯ'nZ'†olOEDäM,އ YԖvG_]'1kA5A7R~aɊDzs<RL6@e,LQ8WYi|W@HkG;rw %;‹(OCv7V3@l>b):G- tu}Ш`;&b c'aX 7 ߹̨D]ېcʊy\9V )б#gbU"X O2h ;ymMk20Dt'`kdqd,.̕cƋ'DT煓sʊB;e*/ʴAxTFdp蹅Z?&$/:Blm2Mf0d.VP-U]QXkˤbYkl?Uoltz U栬ӀfȉZƭ䕒 ${y!*|O/ o,Cf[T~{8f!lhw"V Wȷ:=6P[iB2Z:lW@6+9 ̾+68pfM,ayw >"n_q)  L@S>vM;P3P!x_ j&]Q#](?TL&V [jF1U?',է0Dg=(NzSL\n{<p~`3<C_[G Bĺ/DDZ@vuaʔ4dm9=xk4dww0mttn!ڐOuOF4:u;*3d_ -:Bp9Ρ>jW}iઃnS1\ʬU.)OrKќg/ߖi"+kN ]d1X="FjK%TLLw.Y ,y+mFdc\ˬ0UH;BӬD3S$1\XVNX|4eՇtgmR+ A 5𘚩hԜΠeMzĥY5F ¨/yeO6I4 ΅Id`,tRHڢM_NI6߇NpٽrB{-(q~ͣ@J~ReX f@qfJ-v۲hSd}Wi0c:R rȦM]MyY+*t d!(~V$ԣv R%&!s9?ʹgo~ P׿W`}k8>~;< Qe3.|*d cۄ֮ɡSOe>:tp? xtE@zI&Y!Q;ZEK "ŒPfUoJ+zW|&5ENF[V0fk4YSb4RG ŭY Oꯥ`ZFݤY>5P]K=bSA We&T\7UaI ABAXƩFנJmw`f~.QW'I1j@Uo-IKZ>rʳ*$EO1V]ONetb^nVvV+ VbEP=G&#?iws~b իpv 8اh8Fů,_ϻU@Zn5݅.mI|BIZjES>;pzy}MpWTNXv}e@y~}Nl^\A l4xx+0VŤ4CXDah=$`2*ou֑)!snkݐg!rS ^[Rف6nDJyFAh'7f:"8P٢eB|Ђ:ԋFet4kIٰqa^`l•\+Ye屰SmX k`Xp2Fk4/.YLO54ɺ\|V~$aZK(, =&E,@NKfֿU}$0Vh^Q_`}Ył^(k$w2)Ae#IDڱA2+`׀ưE݈*߹ h3)NTɗ?'OI۫_&(e+1G{a6B HuM):E &}.V66>VT(Bdz[^kq4]f3yŚn{[Ml^o6zuz?ͬQ^B4U+aF=ݴCzc3cWUp<^lHVԬ&p 0PXWC֮hXg25 CXtT|դSlE P*݇VY١ 8D) _ˆRy0-K^tT)0E™`k֗\iФ?5RCm͖uUd-9x}d ृMR~ q taU5o>u +|P̪ XU@\áZdzKSйNC\!q{G6P$Tg/Ğ8#fme3W:M.t9<3g1? Zؖ);i]qXV3c"̻*k`ՂHت +Dh)!Ҟ9W䩱P(|)֨d ^>g~>.AB~USY&5Tg5_lNAz:c}+ܿXn#Q@ Ol*,6Jjap¹kl+ !N1.1喣bkij"J2[ؓ{AT;bhm; ma,1ZH VyTNelPΎC ;Buw8"j9(,rY jxC6ŀ++u@0K@ӈrߨ.pF}Lvk-2^I^GYr Y,iIRڨ-F1Q;(a(Qd2ZYal9!LwJZ#X) t +n*%o2d]MnJ՞*_t=[xPEICY[ Ъl=UT gfuPwb|bK;]r/%Y}֎Nk[)U˱S \+ۭ(@w՗T6șo}y7leCiSyzwx{^ÿ ?uIg%?>WcsӤ+s4=Dڵ IT6:U[Jj%X z@ƥL9 XA1*qfɘvjghm2)1v,cK>7Y )|g|t MoZ&nЗ1V͘YUO &o'V\4CKĎa_iG8<>/O2[U.:>\R3߱ix&J6X#=V C`vJ%Z˽IIS]'9xYGȉtmy?YHz3&UAdn)tvrҳ.ӽ Њ: Wm%)!rIE+Ua2T upuVm6u #X2+*@:^"o*9&dBHԨ%=gb0eʬi@`\έ̦0djcL0;2f]3k^в#h%'58 A[!{{}6 VMwզ"U3k0;WE.J(ku;sw+E 8U<4Q6VШvye+IcPbRy/~yZC9g%Ccq .?sAVcCTZĖ9J~bce 9F(iٽ`{CJ;JD5 -ܼ¿y IeLX(&VahUGaf䃆/Ȃ1%1 X&$:*I$d S8z@,5uFFѨF; ΃s!6F*s\`vRcVi™Au&3!xU@Kb{[G_@<3zG~Y?*\Wi`Z 7{ E@9FcE^V:kE@#{x~z'2@|W@;?~?h :S{yx|?3{u#X@5ܿmYۛn lalq`l6kib/ZdxН.ajNх'WLxO'7RXYirfW-E2^V6'qegFˈvjf9V76T F5n˕Ք[Oέ3],Xvm qmzEi2evyvZ[8٘45,PUfEoLކZj(,n+`'?/~>_g!$[xv FyYhޗT^6j5K_vfsL.HBEk%q -5EYT6[S^܆7?^/U4/~ӖPLJjōmq^>wИs\Gb>+*FP_bp)U ѡ9iZWw Lg1c)`ZЕ |DNf9&n ߼|{xپ1g- #R#쵪N}v Ng>Vֹ?p>%HH`f1=r'8M`=eE ):\ DYb?ǙqEV R"6Cgr]hRb lHݚ/a)d6;rywomh}[o`-\Z W8lTE@kgj"xH)%ܯZB`;'x-!PiUW~@0 1p`}uasR)n@;n24A~}l/;&ΞϠ#ډTOۇgj%A71,`]j=H<6ar]P״^t93PuL|u҂>3Z^GB@h ;1bmDOe\+e &g,cS>Aԅm0q0D W^>t+Pl \^(hr566uɬP' +UV4Gi㱀POf zA<5>?*>aHAL#Zhv MqŮ!.\E[t(YcKWޤcSW@IejK{XV67`[ֶz{åCYvݗ+`7I@ՊV-1#lu4FqGhՍnZ%}hYicE.2̴1 /lmXR0YI3ۤO 9B-#ϣ ^ʤxQ5Qƀ'dFOae 5o=^Ɗ+=fK&\5 fgʠ.3ҕnpK y E -zkJA=j_~|/(asIcU'Ÿrj-\nIRΙ[pdu ڼ,(5Ch&^Wp&T3L[W+rц`J F)).ɧAFr0)T~U!@97Xh&]EF4n\0amZfx6}!;;@/='fguIh#\:ңKw^&oW(@ ߫OT .ķ Ί Un⽪^G) @Z,yC2{w$ܐi/sBɑ@XIzt(Bpӣ {s.0ۊb(k+ߌ^!&OB-g J:GŎ̈́\y]vPk9E礞f" ~w#]8[ ZoouZHH)ˊ#v+֪cm0YeD4=<@Lva!Foچ\=>6& ] xghx_3lAZvԓf;J͊Ga_eh4$=2@nӘ [Ą> VNY%-#bNPD_XtȰ~iUuKgeB+ǧ f"lp^~P@15Q=,oGoo* Rp4U*cP8gۅeσQe*V-YRZL]ݐ M-zE4'ʍ۾ (XN:@<]acԕ| uH/1g;Om"κ6+ζaY IFfZaF799 9߷xo c#/6ͻVQF?0f:}ͯ*$,BQMTDOO }: <1oyT'TQV3lgCnL`1I4caI 5P5v2*>YJ>\:)f ԳqN=3[ԝV;XQ3w H?\6 nkb Zo6mkXڔA._YT|gNqSw|LDk+w=`+*"/t/d  Oѳ|&dی8H+y94=3Q2<ϫtr*5^UaXBmHsNe΃~p6$ gCӹDu0s& ,QC~lė\`ܖ ^DТw"p7ߙn>8TcvN[2%VC;Yө|N ga`EN }r|iq{8jnFg N VLXYD߄EI /J*%wktg‹ XRsႤ]UáZ$ T5͹v jfmQ6K uQ%PeӁ *k E]?2"E6Ñ@b@ w݁vʁLD,Kl + gEEVLi n)B,3"VV\: ø^C dڷƪ'`+ *k}{+@kKr?ݶz)%l4[ѢKsmuXK5yPPC;-<%`cep׬ZsɕqK`X>֘>Mw-Ŏ(hgS^tص$K%=U-w̧rca:6\p ތ=\pY{q'f/" D6?\1XO/}v>bo*}~ tiXZcVJDXs625ںI|Lϧ-5,Tc!\bėuk~Lk%?IYU`5iY>dXcrsiwk]t1g<+e| Ӛ[.2wJ;2gїX3+kf- ִ ;]%)AѢSh/\uŮpqAn\2O>Ec=u[ Ix*3u㪉&z`.fZ0[ƿ;ď, $%й/"KiY3RIT>5ZpGF[ txu>nK`c=׆-a+ak]jŬmuaU¦{'cBJ:=W0Zx~vqfNA "ꧩ߶H+w^ 5ͭgZ&X-O0\NF̊-|1u+RdLYelcl,|Vn p[ l.^.0`΀hY* sۦFl]zԓ|C]M‹H&Z'oDfɕ.' fb]2h ЛEOD\\ݧ%>Rl$FQKCR7(SE>S*g. " u?::i:]:#0sQ>W k̛WoR-ݯ=K+ \RT(lXrlpvz[Lq 輅R-e#`k}*go9)\l^Kr?7v8V }ƬڐӨ:`VA4_FayzG5z4O ` s68W@@t:1DʀdIIA,f"/с)-q\'FAL-O73s:(3)nH/\FHk_2;4 9Z2 2p$`-#[|jMeIհA!P= 㮗1J}by*'stm ڈSeӞ/]+yJ-_Ӟ+"L \Zde4:USQ>i>˥>0cDc -$i&=xpy7faj@X,E`ل(B{6?m _(ѵf-W<\{@-K!`dpeęwD39k΢b\d3^p7X(tLT]D 34T>MBg2-zZǔ4FKALqv/bbX>SE%:Уn?TxP=Ieaӏ̡)k vrdՍڋ JMhf23ݕ1_v^TK]y}ˆNt|9>gӤ߹MDf̍q(NŤ]ypnzYE,ԈH)iSboʇ\' Zj,Z rㆁHCb޾eϠWy`A\t=KLh=]X6.Ee)vY$ ɺb dF#)ѹ+e.12t|]Y=R7~\YʝsFW> p2~<1-} 0uߏq %,?{,]R~. VXڏc47goޏf0+,;hLeDg;R&kSuDg5u [i&6"hicRHLZ󦚔ry(ILQK}L H]|q`Ϗ^z4}㯼93c6Qٴ2'ඹtVSi cQ_<:īr'լ8suĩih4yjhz)!֨LP ŨU+$ZV ^ܡ10"pE.e,N1] }`1;,J.ʨ#]{-Q"֭hcJ`>6eG=xx]-cvVʈLȼ1xMq$5?c*^8Xx4&^ i,WAwZ;ӖGu ٣ጆ+ lzXhC:q涚 :Ƀ)hQ;2sԾ}aˆ:E b?5-v+D8IDa`1W^h,G&~__9sӹk;:ʲU9yi;,Y-_5橁$|/XN y:8&8&2I,ԄzI XU]Q ioʣ0Lz''a(&T6k`M*2g4yNvOS (kRSOfm$ͦ3QYsxw~2:73%BfU00*\z^>"bj ݽtJ; \/RD >YЎ,)ҁ.J- Z-΀y0YТf@.ϋr`k'}IAu-&IZj$V H:aV\ "?/ۢc& L S&3idxc ]J"':}8A*o1S7S"a8AvpOt}C)> +Fje]do݊GDԋYDIldj0 DN5U4`=kBVsyK-SWY$sUFٴ/x75ch8(FC~O]wqYkzkQfhzQ!(UǬ1+^.D%uzkFn taT\އ*9s}:)q)hZܡR0tG#MG7dmÁ ˔D06vW6~ўET=~0w)զ@aFT5)^c P}MLDž ƾsW m{%_j#ZQRUY+s(3ĚkD0{:i3^2bLYb$PNw3ZfW= 3IGg2Sg1Ӈ-L>3C ,_x뀚M'ʕIwO@UT~sts~>.憐, /T2^e퇅=Sd~9Uf}ȡfFۤ@ (@Axa{hIxMVJT0~n)!]uQRUG7&J)Գ>j?[52H8;x/H١5W!l~b J 2",p)ityܮ)X5uU6#0ysd]DY0?7gTW|{YTrQ}5GA)3dC㒶nstӊ9[?*/5Q?!*C͘1et5轊/JCʀ XdR' Sc3 %4K.S졀Jn3kۼL~πئ*UOZr]-lvs.PuaX%YzF*h(rKA %>۷U*h Q+npGrpP" η )Ol -,&^T˟.TEfKs PN#}~]}~Eh?RcFT>v]eD5F`_J۵kZ^ ?}?ġg_G}>J+p_t3K:bMS>#Ei--)DryXErHwuRiZnS2ccLa+;?)9F,k),u.  Z.tfȣUe# 2>+9>yԿ#kJ__ǹky4Z]8ӫS ɚAp#@g yw{s]l͠2){YM+`y<3ag$q3 H0ρp2wa:/^X]xa `@@H2_/1<&\CX*NkXj3PfL@zm(!K@R0hIe6SD57qX3NFY=5%NKc Rvk5KYL挘Tg8jKą*嚱c8l\YuF V3C@S#59yp* [˒ T@ NV#@xux݉Q*tgJ|. I.h녕l8e;%a<.GouS+ǣ ?A1>$#/VG:F |hqstΟ Og^^XGNG!hpuUbm;ґ*ab7 tniciѠhdiԡ K\|Z^ܧ ϴR6&wi e/#WnW-}`~/[TPo.7@s w@EVHQOfQ ꕁR__SŹl HuCed 65ϊeckxKQh`I`uFh:Y1a@o:VH4ly4# X18.pؑK\%ojZz]3pey e-GV*@;oAͿwWKZ}&c18G Jr֩{aJnF%)a/`,Fj |k7u7Nj*gwJ{[Ğt8o|ty-Fu Ȳg9/9q&@w#H7o<\p){EBR0Rg$X.lآcɟz5hsꩀ21Ka1Mvp7aH(b͚ʙaiǚTŽ# S{ 5hz}&p'Qrmn(};1~4x{{99x"܌38;6ڑvo|.2@S ]MhBu Vegcͬ[4z>X(oD ʫ U}_#mnr` &ڑ٥@U$ FQLtՈ,`Ȳ]?S5kτ mm`m`5Ƌ ,^^7K1a#J_ T% G ̂( nk҂GOGDs=QS3QaJr01,14c{O5a8\pM7}v??]`2O >Ock^5S`#3&}1t#u3ysg+ d *X!s6(-8x<cɴ<)4Ge 'r(\x@!ERۺU¸ޛL'~I;ײַor[FhhF8p 8H1U*M&c'xEcVF p4\^[>Ttw}=^sT{e|4󸼀e`ԊSLAcp,K$x1dۅzvg2%.᝭ĊJuc"CB?;p vjGyZdjPڢ0pQ)#)~Wqfb!eue`5:ms>?RwSlE9ˬq_.l yB"lda Z גl8%Ɠ g*Ig5)XKz,H'M֭h.E믠 "O8ɅASK7NdF9wmE~F#n~;țF{x*95ݍ \y2|U.TC?} *?ujIRcBCLjuF0z5 ,Ok`Xa&X>e[8lqa@ .ehIGmEp͸Qa3L}ԉzګ;l|SBo-C{tmN9Z'i_ [s>uDUS$MK*g$1Nj趈gDI-@gg" !Y#cd< _)(3CĈ\O)XޅGH)Z{'x,i(f50u0dLuHƒ`ea!S]E:"Q u q8:|x|>OQ0Y2r ۨAY%2HQu~ǒUbxfk 70fs̗ilzse XMp=ثV+Gj:tY27 aBoJ=hLr5;hu7 ν 2#up81u+'Λ{6',o;P/D[RP:('o`fʻ(+xAue8GJuyߝO%G7rq2G1,GMŴBr1YI]q9X0TzL7?ށE,Wc8 2>j uXh!,FH+*HНᦏs/i cGtMVlMDž|@_T DJ71l/NJx B勵,f]uoP;&JPUlai-X(54a%(eBZ+KK6݋ij!`uy- ]C_Ls~RH.Ji̕ZLZ(#ч(ojXkmc Gp^κ)Z ϼ1Fd:HS5zݙp0s}h2Z}ut^-qs>vI,T5r; BT"h:1E|NAL^9(Xܦv X{ǨpW@{~(=qaoYs;n5}k:6*#q-! S 0tFL]O;Ft8ѬI (3[ AS$ZbuxԸA|4t;_Yg2E1Kc:8ϻڸFpYSNf^-1"#tڪd\`DsCͧ㝦{[xI Jp%=xz)&g, Oxs$(]rmTu(Kc 2zF;1bM7OL~6IE e eɋLp1nɋv ieyV 0 qbV<ٳ"|1eP?a[ Τh0r)϶ |sJ;PIW>Sd7<+-7cʔ🡙dvΟOoHtf"-L_˗mJAMGZ5/uڽh?l7N`*'3gMAu~{S[ӹ ZЄxڤdݽN΀d\kp ui`d R#>Mu!Ә,c#̎/60`g̋DS߻gyPֆ Cb}opQ=뻁<Ti^ MZWĉ-/G6Tg ѽ2=5^ãN$xM<တ`ymkfޕfcVMa=h 39Em1N,Q̱W/,v|I_GAYHGPH;p}iݒK+%t8z<txrFq9ēE8 2w![@v*JRx_#5 v9P6ALLj8B3-I` EF15_+LXBZbCR %xK l,85ju /qP/Iӯ+p?^H5&yj[/7ӕT\ڪvKQԘal„!Qx)nzUIG7Przk8THxÂ`嬏2;@ 5N]s}L!i /f23bX uLk kC· +:8-7t1ރ,kPtT P#Uji6:(0etS L5, Xs˜:w4ʕ9F$ PS3e;gsbù]DB'񕗢tU g s9kbhB,+ZkcFt&9d|,sL7J`Vҽw5&|S9tot 跬\YG*n9L96QɤC/ҙR{uVF?HZ?.̀F=KDw7xb!ePm/ +a?1UBO~@tU|d@&ЇHP܀6J6UB9nQ4qq0AL2jn [y֊3KƖQ|ƓVXUגe`Ɠ ۳F@lӸj0&/'UѧT.!tv~^E9R{j]&)rIOi(GFv5^{|yL0R(x 5 Ņ *5sѱ1ki8v:AAW ^@=nR~H}գjMXp>/j[ ꛝr}xm_dDV X #'K` @}>MX R,ׯ q@&r)͉K^[0z2ΏD x:#e[" `A#Yz=L:&~p`.cgZ`d:Uӌ@8AɆ3ϑƼֹDB@o_ZFe|qu_1kd#yDHJ#A3D:MƶF(Y;Sl m 4m,W'F/sx7]Ϣp̔X.͛gA>t}]wM N` 4 @eQVhF؄u.g"ㆵ RG >^]gAX0(_NπAm5s%&0^Eϑ&$(#hLqQf̌\ X6O4ꅊ{2sҡrU\J~h c,_j 0GHεWrO6'ڗ/rv'0XBd`y=cCRSdH&y箛-)6:! /dmb-b}bdי"rGNfNCt07{,&)>=a B-Ѐ應l3)(L2L#`ƃc uPGM pĐr@WS& *u}^9$eܪ DT *U&jӳ!@#Ƣ&s.u5U)(ǚ?¶ts#sǗ2 'JO_zޞV"&͗?m:Uz U+e>Op[AᅰEQM{߿x ?kǜc yY4';FJ1;X'3eehwQGB8igd@9p+su~޾mUPsuM=yC):AΗE^ ٚg ϑ9Z攡& nssc_STȣI[}\Jxߏ5gɨX/ lA5#6R9ca"e9رcXi)ܶ 2+xJl7(%. 5JS9呺^m &[.  "02z[3V#{*|({#2y@uzmv]:*{`2QgLC=XUj]`M[pw⾅&+c~q#g4r:-" 䗵EZTJR˂Τ5iy4nmDK9E^+dg::!`Tt$Zesf؅ŰV\56mEV/jT/U},p/0ήaT6Ϋm^Bt2H^S,B5aB߉)!xsńG&n/cC=@E2x~#/<5,\:ăvQ8JK&ő)ePrF5Ue|v`@1*K8YvX&gDe2D7ce ?vyـ 2d{,N>s> 0e'_'fN5'PkOI2P_WMƌ36n=ήlַeS]hb Y,OeXo:MΑX{c$8J"㳱 Ƒ;eFW Bȟ T̜U3dU.>β h?#9@F2p[E,p ( ^AOcޓ3چ[`{61,dcjv:hv ˬa4̠ăǪMҬ/:O"7Z'ݣ.9VQh}ĽTGU0BLm-?cL{.܌0`ʖ]ೃ< *VhRLKpJ9`U 쫅6e.#k9Ux9Ԛ]]oء{]n~QXe$w%7X› n/q# ;hp胤' ʶ <)?܌Pw6,j 7i 6r$ȧݲ-=o.ՙEΝœϱV0jrB<:D.Te# OjF=*m1HV4j"JnM.Z>u|l+hV"FVyԄSC|!x 2$>7w.yt/?LoD,pe =WD߲D3қl.Kǰ'\;:õ|n`Mbd( =K0+SCE3̡.Xݺv+j28_{?Aخc2@O1nquYvѮC:[j<)Z&)87}j)P^X\SU?}auN-:+)`Ĩ%x2})!6숬1Ci˼i>Z)!z?Sc>F,p'-cB mq#F/f h6Vilo-OT l@cIʙTOej0jgT3/Ѹ슦0s"&Zbs7ex0XgWiPB! 為o=wWL{U>woR J" ;:ZS=a E\XT-锜.2enQSKIPeZ-cL~=!lh^;6e"P{,$E4oIؽRFNv9sXoܠ/Y 6҇o:?ZL}x7ЖAĄ0GS,Xl]תGv&%sAQM=9j=5exz<VA{]˚s_BO;Œ4-9n]h݌I.={b `S}Q%{c8@嘏jCHF۠uL1ٻVK!ÙC >)BwBXo=r^炃J(sӡ;b"lٌz/#+旪_&h14F@Y,wLd_Osi~Vu&KA1naXr;L1+P#OQ1ޣ}ĩ:{o$K׵QGTRțaeȣ[%7e4: "AS[}Ѻ)Ta&B wu [t2Sk ǟdG]XI#@zqLE%#ήFOT%,S|)Md kA]HSV ;B-"FpV ;g`(+'S;#CK}pKuرk'kpu_WJu]NCZEdDAmX> .:T *tVjJ>-'穀#h.aaб\.y\|a:ȓ|l7 5|eI<]ziwoεw "_݃uom.ڪri#uɅ!Oj  G.ÆʠjF{FhxLؼṋ̌*vk+_Hf4g9}36?̀0>]cJ2QG scʠy%8S1@aꉽr4*cpI5l}<|/Xv`bqLJ&|ϯo`SMկ@U o($ЏbY33*zS/^>&_Gs)0UܿO;WDo)>4fUZ9/6D-p5vТZ;"ImWa{yBj11Bvo ϳ8E@Sg p9G֣$ǡE~i-_?hs:\֏U^Gb[Dc1`w1o7d~ e5: S;cr ~j)N$_5W8kxfEo0'&S~֕ 2zM|| :9cDٍ֋.p"/I]H:V1+o^yq z"<֨ )n% .X qhR$cpK{ꂪaMWFyi zp_kV\0ܟaFRbd̔>P}TPӢk]Lܒ)wTL&-S_t~0ɥ잏jPa}6nQhZe0qʙv(YΊ TϷQ*z:]1epfWj Vxq!9MZ9Xϗ3qmT<397b6ޔ`Q)/ԄM c:.ҊT3YKŵț+y"gn,WSkZ1BTX) fM2>kh2{00X6 tRMιʺ A/C6zGZokS6F XDC_kyi1/=i{DQ>٨YSםjZM^ ȱ?Q oޗVIENDB`wxlauncher-0.9.4/onlinehelp/images/le.png0000644000000000000000000000167512155177255020375 0ustar rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<OIDATO[e9m99&`2"S &d F Wdy3/af0c2mW:R8=y)% c}y䗟}+"`|aL^Oh`erf+!ܷ@R20n?{9O԰*+TIC Yk}ሗ{֡ LvםM5PpmUA o; z]/O9sB! V #u-~b}k80⧯5_T|ASn圏>ƫ.$]:~|2!_ ξiVOLĄfbnK<j-p)Jvb#`7Ȼ/PVGO(j8"H.Sw`@*(Lpm!wW(D/F;ieg6tcwܧGVhi#fq%~ሿvk̤8;`d=t]mo"^*@6eyyh6nFo=|:ޞ%W賯u:֒:,7Iwn)~3ôm9LN H4 vqô%J144 UnT?c9m޻SO|O(cbbZt]Gz2t:m*RJx%l͟#S(D#˱iԄ#ޣ=+RJ}Ǒ*MQPx\)*(Xf2W`:IENDB`wxlauncher-0.9.4/onlinehelp/images/li.png0000644000000000000000000000157312155177255020376 0ustar rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڤS[hY&i2bbRmLڊxk(^Kdm;( * TQ]X)[E-[/xinMm4I3?yUas}ƶmSze{->HƲ,ſ7Gf3;ssՉ{{) ׋SIdlw $ $'.$3|ӚծM[t###`YN6bɯ xMnnaXPܥiKQ|`|(ϯIENDB`wxlauncher-0.9.4/onlinehelp/images/readme.txt0000644000000000000000000000010112155177255021244 0ustar rootroot00000000000000This folder should contain all the images used by the help systemwxlauncher-0.9.4/onlinehelp/index.help0000644000000000000000000000132012155177255017766 0ustar rootroot00000000000000Welcome #Welcome to wxLauncher ![wxLauncher logo](images/header.png "wxLauncher") The [FAQ][]![][li] has basic information about wxLauncher, **TODO something about how to get started, overview of the guide's contents, etc could direct people based on what they're looking for (the possible reasons why they would've looked at the manual in the first place).** **TODO scale down header image to fit default width of viewing area** [FAQ]: jfaq.help [li]: images/li.png wxlauncher-0.9.4/onlinehelp/jfaq.help0000644000000000000000000000740412155177255017611 0ustar rootroot00000000000000FAQ # FAQ ## What is wxLauncher? wxLauncher aims to be a cross-platform launcher/updater for FreeSpace 2 and total conversions using the [FSO][]![][li] engine. ## What can wxLauncher do? Currently, wxLauncher allows you to launch [FSO][]![][li] with your favorite mods, settings, and flags. Starting with version 2.0, wxLauncher will also help you install new mods and keep your FreeSpace files and mods up-to-date. ## Is wxLauncher free software? The launcher is published under the [GNU General Public License v2][gpl]![][le], making it free software, for you to use and modify. The source files are available on the project's [source page][source]![][le]. ## Why is it called wxLauncher? The name was chosen because the launcher is built using the [wxWidgets][]![][le] GUI toolkit, which allows it to run on multiple platforms. [wxWidgets]: http://www.wxwidgets.org/ ## On what platforms does wxLauncher run? wxLauncher runs on the same platforms as FSO, namely Windows, Linux and Mac OS X. ## Who made wxLauncher? Check the [authors][]![][li] section for details. ## What about my privacy? wxLauncher does not send any data over the Internet, so your privacy is safe. You don't have to take our word for it, though: if you have the necessary technical knowledge, you can examine the [source code][source]![][le] and see for yourself. If you don't, but would like to know what happens when wxLauncher goes online, you can check the [privacy][privacy]![][li] page in the [Other information][technical]![][li] section. ## Are there any translations available? Not yet, although there will be for the official release of version 1.0. ## I've found a bug. What should I do? If you've found a bug, please do the following: * First, check the project's [issues page][issues]![][le] to see if someone else has already reported a problem similar to what you are experiencing. If so, please read the issue in question to determine if you have information of your own to add. Contact the owner of the issue with the details. * If the issue you are experiencing is not reported in the [issues page][issues]![][le], please contact one of the [project owners][people]![][le] or any [developer][people]![][le] with all the relevant details (what were you doing when the issue happened, logs, screenshots, launcher version, FSO version, etc.). We'll analyze the details and may ask you for some more information in order to track down the problem and fix it. ## I have a suggestion. What should I do? Suggestions are always welcome. Contact one of the [project owners or developers][people]![][le]. If it's worth implementing it, we'll do so. [li]: images/li.png [le]: images/le.png [fso]: 50TechnicalStuff/02Terminology.help [gpl]: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html [authors]: kauthors.help [source]: http://code.google.com/p/wxlauncher/source/browse/ [privacy]: 50TechnicalStuff/03privacy.help [technical]: 50TechnicalStuff/index.help [downloads]: http://code.google.com/p/wxlauncher/downloads/list [issues]: http://code.google.com/p/wxlauncher/issues/list [people]: http://code.google.com/p/wxlauncher/people/list wxlauncher-0.9.4/onlinehelp/kauthors.help0000644000000000000000000000246012155177255020525 0ustar rootroot00000000000000Authors # Authors People who directly contributed to the project: * **Nigel "Iss Mneur" W** - Coding and bug fixing * **Silviu "kkmic" G** - Design, testing, help system content, and miscellaneous graphics * **Joshua "jg18" G** - Coding, bug fixing, design, editing, help system content, and OS X support * **Johanes B "Councilor" Permadi** - Banner image and logo ([http://councilor.deviantart.com/][councilor]![][le]) * **Mike "MjnMixael" Nelson** - Splash image The wxLauncher development team would also like to thank: * **wxWidgets Team** - for the wxWidgets GUI toolkit ([http://www.wxwidgets.org/][wx]![][le]) * **The FreeSpace 2 Source Code Project (SCP)** - for the wonderful [FSO][]![][li] ([http://scp.indiegames.us/][scp]![][le]) * **Hard Light Productions** - for ideas and support ([http://www.hard-light.net/][hl]![][le]) * **Volition, Inc.** - developer of FreeSpace 2 ([http://www.volition-inc.com/][vl]![][le]) * **Interplay** - publisher of FreeSpace 2 ([http://www.interplay.com/][ip]![][le]) [councilor]: http://councilor.deviantart.com/ [le]: images/le.png [li]: images/li.png [wx]: http://www.wxwidgets.org/ [scp]: http://scp.indiegames.us/ [fso]: 50TechnicalStuff/02Terminology.help [hl]: http://www.hard-light.net/ [vl]: http://www.volition-inc.com/ [ip]: http://www.interplay.com/ wxlauncher-0.9.4/onlinehelp/lchanges.help0000644000000000000000000000167012155177255020453 0ustar rootroot00000000000000Latest changes # Latest changes ## Version 0.9.0 * A brand new cross-platform launcher, the first ever to run on Windows, Linux, and OS X. * [Profiles][]![][li]. * The [highlights][]![][li] from [Hard Light Productions][hl]![][le] at your fingertips. * Mods displayed as a [single list][modmng]![][li]. * Redesigned configuration pages, split into [basic][]![][li] and [advanced][]![][li] settings. * [Lighting presets][lighting]![][li] for you to try out and adjust to your taste. [le]: images/le.png [li]: images/li.png [profiles]: 10Introduction/02Profiles.help [hl]: http://www.hard-light.net/ [highlights]: 30Reference/10Welcome/03news.help [modmng]: 30Reference/20MODs/index.help [basic]: 30Reference/30BasicSettings/index.help [advanced]: 30Reference/40AdvancedSettings/index.help [lighting]: 30Reference/40AdvancedSettings/021lighting.help [mod]: 50TechnicalStuff/02Terminology.help [TC]: 50TechnicalStuff/02Terminology.help wxlauncher-0.9.4/platform/macosx/wxlauncher.icns0000644000000000000000000011502212155177255022031 0ustar rootroot00000000000000icnsic08 jP ftypjp2 jp2 Ojp2hihdrcolr"cdef3jp2cOQ2R \@@HHPHHPHHPHHPHHP S ]@@HHPHHPHHPHHPHHPS ]@@HHPHHPHHPHHPHHPS ]@@HHPHHPHHPHHPHHPz YggN(] pEyIv$$MXU}Reu$W {z,E@L0<hM3Nzs3"c~s3JNs'9΍)z|T (a>})8aRQlfǦa'h'U<}65e`+B08nC^vaǗ(O%\:H,֗ =Ԏ5 S3(Tн !Gu ذo"T W m^FPC~ԙC7լcsV/y>h[r) 83t3Y iokʱ<LpgZ0qE%\ nK,aKRo)Gb]8b% EX3H^A ш d,OK"j=fd' ss. Z.p'ڭR~Z*B%NRO_>}ltJb 9vGqOO{4Du' %\Th-'Vcc>u7!_Rof2-h| fxA* Z-iv:7aC !,~je:7ُR" wlZF:@90f% mFּyso.fκmc3-Xyގ1N]glA۶iϷs?Yґ%I2^[Aޥ`mD@0CWѰ`X/mͥnRYLcMot\0th~@p}CS8NʻN]kx3{,ez-ܧRX79ݼ[ntR:t5ڙTee瑹[K Y)bgWF{M5LIY`?Ш ,C݂mcʒ_%߉7,|Dz&ԙ>Vcj9; &+(|F/B?{؞ -P/kJ"jϔ:r#$ 0&}*(j >NG-jo?$^v͖mx0僣ޘm]HAumoJ@U*CfM؂ ˉW^eMn()OL#aA_ K#` %aݍ2a[nt`K7 y9v)?  flmM=<~qxbaI %,bfou:VCd&9붾=Q~ ͺ=qߙi~ +3Qa#2,bK|ϼ|K?V(4*R=KFa2/m`%Z?Pʗa"_|tۧQm }7 =VǓ`WZN' KI{Efi+/W[ QtPt;_GIѼwwƓg-ʽ}+l, kω ߔ&e+1 $gAi#`{#RYd~Ca d;I0)y0 +*1 0v#@6+}4,UM/Evp5Pee^/=U$nH,8jHhނW$'e97߾f>!H&"[c'Ut %Ӏv 0["!ւup\b(HbFcKЬFIDid"ymx'7[Pa$onq_˅ƕf_e!IG]HOCEٷ9J: }@qsЉ4Kv݀g`qB>$)fJrAZI3z㓭OT4й^蚹i Hꞙ]_iOscAS;`Is,o.Ryԟ&"<]Ԗ!o2䃘yZ8xNeic]a{AmHni"HA.2ӽ9%DOic=wvPMPTQw: #Z)N9P?,@`D2@ %?'VWC}H HVhk^pKe,-{r-,pľ%%qp7^R>Yy-EWJD˯jٝػ9gq"y&myҸ6^nϟA)a؈IJ.gGvVwT ~4%dNKUCqB <@u&aw9pMnpߚcx; e᎛] ٯlX U 繁g#\jߴMp4Pm2'G,=O0,qSNOwXaMT +AaC\aYj%8AV(?Ddv8.v_>/c5F’+T0e HiZ`cV^X㸯?R뿊40s S]MXhDc+8\Ԙ@Ћ2>YOM $?: V!db>Jp|h3Sf`%PwR=ٕkb(lTTbWYm󨒭A|:eVJe 6rel_y7d9&i.Y>%F1 q,zgkdCu8bPДw26RI)[Y ()MY.[j$VARu\ p\ԬMvxԅ+m3y)J)t\S8YgܒIC8!Nvmj6r +!cJ|Tw.w_kь&B'Y샓ey,܌&qcKh`JPA| p|ǺYO_ad Y`|6?%=Bbh&!9_P%>kw:s i  u7| S30y\q>ph*Gzg8% l>5ΓsJ5O% ׅs# ue%vf]}`GܛSqEws``GdFϒ ?O5l%aJEMrZWК׍Qz̚&I@gkD) N w1z 쒀ڂ\X { gE&B4zzR24ffWxJq ߔXxȷ9MjdI:;3_&}~K4>%rCk4fP ;:yZt8 ѰO6JM*aCVq`&%8q\/~Cz<Kگeӫ͏r%юh7Y*`!寸?4-,BŘ^q 9&>'B3auT9_Aɻg"iK, P!-- ·!Jҝ=lq-S^5#53Z3 "Oؗ.OԑHh k^yA>?UU$ƝJfoSm[hQ>qK0fr!=_yL?H((M)ږZۓ˝Y ˃uToE]DE2CN+FMnG${ 2y6b:ga*6qTӿo{wM)K^^R0bW+wkMZ!I5PI/!44CQ!{@fKeJmu2 "L\^OIhB0q+;D}.]('uQq.4#>w`j&:|}@(BPyyɕUڄ$УHHuQt7^9: &Kלk!@(63='jRy/vy-!,6X<(՟Pyw\a\ljjwUcQb ֛w\z/>ʄjtTt ƿ{JX BaQE*Ob8MHwk^A،7ީeMMt*eR_r2H\}rV hH?rqrUX ovv- 9.Q*rl=umffK29|ZO i/N8.) }|cKybJȊt](!Z5<tPR@(@Us 202sCml6ҏ;H┰/[R c+m6'+rĻ?1 @Ts2@$ʑY{pf3_ۗa/^L+)!Hk{ؤq?pQ'ZHe<-q4psq:g@SIΚ=ǴݠYۚ@~L9O<3/ ŬvǪ;΋UBtrrB[cIAAFi{ߵʏ6Ŗ 9=$ea'%e4z(vEHYG QpC"Ƿt΂B ]R*bx-39p$"ōq4+d?մ&O0d̴2A_(YN+6 ڴ 9!WO֘jbߺ 6)Y>=fW+sz͡rV-+n=a6LxC!]k6NQ#>to|K>I`,Un+}pb䨳e g)ipdX75*;+*.Î1vkR 혞n6tu\° fxtu1; ^n߿ȸ>83Ikp1SY6Y"T[?7 KkKM;M^ŀWJHŖnqRe8FȪ50Idܣm%.$;jCQ k?C\%\ʷS ,~-hFJ&gݓN6cqjPsmF<ޛG/\gebқ ?66/rF,iV0@Ε^ JGT@_\Y "R%WS5Wcz1G8*97TA|Dyi" <8Cs[Dڸ֍zyÂGR˴?_ȩ;]3ݏ_0|N34A4t7naJERT4ȼe[w{U]IAD|1pmMpM{ ^/ݍiLm17IהYNaVUdw TK> nN0LndTtEr3c\Hq q g}OuCzzthCř6lm0wfh #z{Ay#$ \)(8m&%c S#fd32?2@9i"Oh[ ՞IDVԝ  Ztf 4p\];5;`_ k'vj2d'Af=ؽg tۣU0Y&sMɍ!|4oXZdFft,y5 '(ë/0JIP*9AGI0Qיt1xws35B.%r\cŅ7wAZ)"[Bm;弱wGIǘ2]Hz"A4{[朎3O{TȇpQ PoȔfsgѾedE9E`E I~ b󠩶dU@feVP3Ut*D9Np0]k1'ڱ#rs(U0_ptYH;@1˕qBǫEB`EOn60I$Cљo$IC&h_v>tƋ@ϤMfl[DD( t촄11#≖4iPwźʃJ 넠:j!^KVP>J"ֻFEBWA.#pY^Ÿ&'L O ?07L `֭/nY`(1V1@6~;0 X" 'Nնy,ghN2Y xoFGtK,?OSQS{r,coҜ-;B= K3$px*dK(ՊI@mwø'ֶ%K MyLq1,ޡqtk/@ЂDS Ve1 *7=ݗrC)1%9q]Ax'0SDI^:raܢZS.A6“~{)@i[^n7ao"ߢ N sd ұ<#] LDLF;5YNh 8)ذK(U΀2(!~]kݵh)|g 'n?[4ѨhPX.--ܹZ\sKDBM L`B u+:z}QȝxsNîր@=<)˞IBpz4E':C+k?n39lDӓߐI_onGζܺqph7f +qU#{-\,5ҒѰqUkZBwϕt}AnYk/%1ɜ65gTg5f5"@c=(BFP=mhPR*nJӿK~J{v'I4j; 9A$PѠc!TR!yͨ^}㛽hwz+mj6cI> lJh33H#W%nZB{+`~jut+$&BDVWs(mq_^w>x2`D6cO9PHR-\rock]/‰7V7LЎ]wsMpoE](US+*h"A1_A*R^8Z8#J9O\ (QO2U, !"jRFk%Wͣifb'z7~͌ *.m`;dB̴36_Zѫ #ٻ3<c|xC;7s__+x&7?_LUo?9yaCG}_!H2@5n>9#(`ms_-䛓~A Dm~n $0ʴP$=mSV &B|N)/RzC\yG|4gA/Hk"P@d;i~n]{6 1dХM[@ŬfQCfػ +f\[;hiK& ϶lBf_3e*A?dXWEAw2EÓfrsJsp'.cYk1qW$KfQ9?Ua(dH>W szH,ǻ4AH_^MMFn,"&7GfMLkpXS -dդAvw ;Oh>$&>f_@2-ɘ9pF4(ԬsGɂH!o^NA5tJxϭK_r;1 kA~ h7+(CjS?Iq.A^tYv! a=9cI{ns/Wf! "~ ٹ% !*wm/_k{fNSF]n~ʚt]^|ܯP>1T0,WމLF<>ϩأ}Wz~{~)@i[^n9,ǖ#H2fхLUԢQε2x@zYԥ|ȠΗ*o} #OAY]x732Y_L ̙gI@AрYeꃛ焮AC~s;>uȯmeIǩ.2zȱWՍNy 'FGH0OM %Jk.r$TYgꗟ9+GʩZnOs*l(9&eu |>6\q4ԑA4f%JRN[*K;tniIowu!ȑ LalFaC&)Z鈴tvP@LlI 7[5Yͯݰt-o$4aI $x̌|)qC?L<Ö*$ztgSB)z&;ry:9lF-շȔ??V5o/.Jgm {nA-}'F ٌ)%*ӈ~jD`ؙKs?"Ck[,LUxIlp l@5C{AOHQIh>SVqi kMqTovcefA4Jo5ЯhHxpW(qSwj#*?9x DRR{-pi&T^"Mm+71\jGsoS*jL{jr8dG,mA#Lu)3SrTO$PDfh[JGmgV0m( Jtf@^eV% >gC.`.Z*R:w-رep9ݤTů1毞,C Dqӱ&E0> 9N=׿;~/w^I}~zΥ*âw~a/k<69Mn1 m=tz% _J'ҟo.@ $03/ 'g Dw$( Ch<! V+:6[\~tyX$v:i_ݭ;H$BEU,TaD#0xfjbUW _!v B՟<+{(g޷_?iz]'OPE7b J ջgi{v&^MO述I['b>ߡ5b4-(!YAgLϿ h&)WS"my6'.Nru%dQ{O:>x].űx?piZ#vk%3 _3D 9CAwCAq][z17ҊoVU tj\؂D]۟ rP'奙cPH ۯj(b .}9{#1c5敪RdHp@hʰC{5WTq77vOS-Pl܌VM u=ίbȾb0K]gZ%llGɗH-1ZE!kn qj?q+md޵OcqJZVyeC?wzO'T 5 h?`Na'@m-C[1T/q@t8Ocgt5se%$#ֺ@o&VX.O҄!"pZӊ+wppuW>H=fqjd sq&]H-I0=pxæ pΆ"}?݉ s#([`>lbRńQbJE$ֻCW|'OU_|rlYe(T1&q7sffkx/&+hfOEZICvytE=oꝩ3Mf,JʼnDF 1 1. vѩ,uQTolC__#V5,y,Y>nme Ŷ9t𞂑 d=W;j m8_Πq ]I̵mw4z,詇`ɤ-a\?}#"̘c`LUs/z9.Srwl1 G-"'UeƄzÆ^@djLGC4!ל[xԭ㒏*w9IZZBiuYO$ɇVEԷ xԌѽ5:Cxvo9Z~&TIxvu*mǭ~g;}*F=o1yq+8$uiT u9}gxփZ$jozJchQ+Mt8'+RWTDn&>ؕFpp%oKic'eC+;¯ >$k\HXRrqP9/Yjy}Z筎вz%[TCOXUQsl/-/mSPl\ϿIXV, aɫ0vFIڭpI@mER F,P3[sмǥ4?ϟ *{>ra#NCf'ouJaU N+[x~"ⶒV/3ۭn})]^ꮎu&p^5vq2NhI:9x6h]}k6M|0x 77r65GZViP.s*eQ@DȽ඿r)u CC$2HIs&3ڭ}F)W+y%; Zn`Nv*R%@> ^H$y^D4i_7^.ՂJ>ZJ?3꨿&5to\Ci\8})}u XAo,ЎpTZB%9}1bŝYF_ӟ ^xנ^ >+执,TNЙ~F6vYQeR I_j`S[=A7k5=6@smql3}5/؍C6MCK:F6|ICW8l]`7˒<ƂP >D;Q{`PNv8[O6g0ΑpO[`b ڙj<3ã} mts:jmزQ{2 \Mrz}` Y`jBϣSb{'J n`$G-W~\W Zhu!%t{\|~9qSWMM\i2~"ޞp-EB`4tcv:\ n"(N@S$U\@sR{r߅٩hq0 yPRj83 w?6S}1c_(Xh3pf(Z5GQ;垔Yz bŒZ[dgEr(p0r^74qyq|t0t\ހs~zOʃaڪ /ٮH9fCsWLc3t%` eCWD?+Ui򈻁 9+3xeHfqzjfuaX¯S${?X=yUw9} {ci2֜EPN9#_EtRD++XQ'{S9om11'$@Ysތ<&fLDʈػpr {pqKkIh"'{ KBW 2gY)W /" -p3_+~dizM-D} GƦD1_]x(IO޾ 0["XvqnZ/0MńRueωv^ْwi$k"MWY%W]XY4Fđ1J3jofbvȬ>(pn! }dҚ57pOhꎔS0.A  ~F?igy?g&`e i(=x`izC\`3U93U($K'h%0 H8(O=*(#]4z2)lUR:Lφ# .|#{Q˟UM_R4Ъ%:ԓiAI)1pxCY%RvK?jes7X9IevYK>+z|l:(C >,/}gDu#`bk` )vqT e\1yRҠoT vi tgJJrxvfU%p5ٶ| Qbgψf lQ`<aK3hA *iק3:Ӎ[hu+Jܥ`rzxcxWʌ?' 3BF79:٘I 0:-ScD'*u򇵪u\n8e8b"@RIȃ]h{V L"3(`F8X3hj^md.79ĶQ ]pHMl6XC߄&y"GhJwZsSZoňݛBw$}Cݣz:q„ռq5; hf.K`uz<`vtQSH\,s]D{qy*8Z񟩍^1Rs%2"P+E My߆9PDăJ-H&q pцbwgd aF Jz VKrLm4vkLD^IO@n aVLcPV2=˷O{c\\|c~yoHE !{`IUK/AT3CgDDWms|.pk;-E QQg$hBhńInj *aK(foG^Y\e*iˈemҵnG@_:@~",YPRFfV"IMТ|&4g1!cXVdl4L3UP=b{u($i9cQa & ^ߚ;DbvXx.O]"3)iu5%'3*įC^ hĻTU.OuK'-X2%%e_uteF=u|8M2763H(Uv;~fv9unbfun|܏9QG޽?-j޵ɷ(ے}5s2141()+yOv8HZ4T\JEUm$K lY<^;P`@Ǝ P :<8LIl5FI4$ɴ҄KJ3^ h5V5߯NFru 3VpfFl\oJ%L:]42q^inP]oRIcu'f+A&;Ʈ{nhSRq=&,}lrP|,5  @=%STL|ˏ{Ƭĺ"::ߖ|Ul{!, P=\I!aUqx3(CN҄#^}\\0Q(LFQ)T;r@#mg])ۻyF6YU/q/$>ŜdTWY0zNԓ_y{U:e e1 ]8p˾¢!,.!Z˄ż1E[#,c㷷 0]dZӼk8I*aPgF7@zb\nXsLZ#3DY@RX$t49=zN#iZ4bP4NI85ec|xq*  ?ڡP䒠9, ā7/(N8u\'UU:D25q 򀫴QXVCǨgB4?KO8PqaQJO5JNK%([T'ZB:}YgPDcl:8n}r/[(@EX\L-\HhI|pU7ٷ 9&pH!y7W*oH%\톑V9U}fO_E'őBc)BHiT'\(;Y `C7 $uPltC$ʞxO)n 52#<- x;,Wūj˩((0hm׵_/$וs\&Э&|վy~9O*(##spf;̘ +#y6X?AUZEעRg{hO{z-0iF)k^3~&:ەq y0(xCx0I}z{ҏB[]|0ٽ(qYC0 g9dj<]ˢfœ-88,?CMF$ y@%q΢Kts kx pq/Y)tY=9d39>s[UWܬ[ $N3(^go/kzoC n>{)BEsƕGdbWGFOxkc8! d8@t5>LrJ V2~K7B9".2XAsIC\hK׌S$S:V.4O$엞il;bMb;`%P}$?qnb%LMhV:s =}C!w.InP9p8? զ&syhCfhj'_سĩ́<ͮViD5tKH-_?4;iVN]+X}Ҫn< g-J4@DLz%!/SG_ڕc|+,(]w-{\pQوчE=\E5X ȋ](3j`Mkf_mH_K.:]JSSSS_a1Xܰ:UZkzh*@@j҈EYafpkѢ?Z^r\~@/q)|Gl 2uZ_Hƣg6_A*!Zz˄IiBZi HsOnf%&fw !_gLbBEK6w` ,APQ(S֊nΆ;ȜQt:?.<: =_L.6+*MxYˎ+i2P7OrS-Дܳ-4)yȹQZhá 1CsX\FSfW]R &jk mw  wsb=oSo$fP{}_T ]Krj.}8zD5h>rh6@Hc|'#dUkхc(@^&sкusҒ{h7-ǤCFTAdU; |PiXxT5;mx+iߍ< WF:u¼<)!rѠ݇ XL8~S9fAYstlACہ /950RF) 4 Y9=%pXybs=N%kJG*YXϣy0𿸠Q*Ya(JqL}¢B޲yBy37oSO,GقWbXӹSQk0y?BJF4jh#B&.{G R1FV^;mLYV4 tikҶS\;ڏΚ۞]/>[(;@ޡ})EHG|E#MsnXY {pjȝt\iuoΧ.–d[Q A!|m6yV61 Ȭ8& 㵽>#$"Ȼ1T6sf-5j pޥ 3gF2mh+ӯv>JJRҙu?]bxXP;츖m#)Ù2LWR_ȏs>@FeŸړLT̚|Ӵ sBlC2C:vnp-tu p]{o#Vqߥ'; cX%*d>ei\JyzKPV灲P\[Y7bCwrv|BYl[W Sl_Xv|/ojb)w'N 3~2A#4QxJv-+LդPh rܤBO3l m֧UNSw%amzQܵaZ G0OK8EmҾW"wpԘWGρB~'l3^׬h]C(Ⱥ V;yӝtyj}^YJXÓ^l~״*NָM&;cpD.5\cjFw75gm-J\iBР8rh݆ in͡Q4G@1s/?@'}nf -Dt?*0}ZdoRwp0}{.bFW1~N"lxgzt66 *ǞOH>afb1i:I5f௩n l4ˌ%cq+E} a}!8Z 7X#H۩4Ȇ'0}% #y6 %g];F9Jbli(A_("8 #=>d&-17 y,}R&|ZQ oXd#ۦԖI"0B*se2jU%TWw3Lą!$s`Kڷry=V6Q4zi|Hwku wO#W\%-`EV&ˆ!<9ٶFdPal SBLHjk*aQ8"sHL2?}AȔO?F8N$GW}7Hhmjt8CLe~'{5Kɔe0Dt^bP㥪9zeզ!kju2fHE̗ܲD0QmM%zpk-Qks,*;goɕ9॔$¦2ThMf6~gC3!~0ϝ nA;OWQH1TN r.Y,erC ' "s/ܮx↠js'^(pbaEj68 p*8@ة_CidhՅ$j&OI->B6;R?0"-L Z0GYixIr -o*apb ;9rʻIT]9zQsÛ iL%RS_%JuݾThEyހs)12ŒI\: HiHy)&hT|ۏ Ld3A@NR=p B"X40xrUh7RHxבZZkKWVu ߯oq52:812KA!G3LOiKK 1g섑LkI!J].WtvwFO?_ WͮWK&Lv`@zc2ͻ[!u7=~4bc5"t""gP>XN{)q-\>_%kB+AgL8-)(A97oSO* ||?I&Lk"Y{ [^c/HvErhP$n?f6 d->jq:3:#@{I촅%:ĎaoKc4Ļx#s~GkrBi #/|1z~޳oX~/ѥz8G!y_.uV45RhZڴMfOe`9in%J= / ȝދ4E$2ќk os$M+ OSx{6ff#bjɅ j)Y8O|i9k#b̷jyATUI6swG([v5_(o*;#֕(5'E2I>9tKo F/+W8_zR+$~H0Vq@1QwWH;Q"TNI;Vdo#M.AW\?2\5^dO4#0OϏW7)A5Z ^ꨕSE6xF* x6y@Y'|9cʫ9 ?JM ʦCP//.P=2<ڥk5P@C)o;,HogAg@;b\mWe ӫ YAk<QKrM`Fv Eoȧ9^%i2;h>/IYCe"=4ӳ'١q[mr]z[F$H)ܢng-zwlL&*v|AmjUH#ڞ!Û5,#x?x_P%o p Vˑrf.bڕ|Im|~%m;BGS/#Np,N3&X_ z )Śk!߸xEd= h#Z awzcIÄI5e+U|58ٝy}-=+ʔf!R@xrceRD$/iK9hr+O L'a88Q ?hp늗 ɺTP=2(X83c;' ,42^ &cQ`g'wc0RӋqU0(jsJv#k>ջ!:1l_@jA%g$YI@DZð ={X؉c#E2R{L #;%bX8&ՉsD t!AAܪ^金[d1Z:wCqZSHs)0,4"VY2`?5+H;ϒqꊺ,dSg/e1M RKe &|TcDždUNJ0@nPfZdx9I*JaГu/#$1ngJ!'_)ި7 pI^`=ѩn8#/Į <9iSYnhޜ$wb߲pڡ:{p]9x&NnҀVhI}Qc飩]KN]CZɨa1"eG^K#ԸX#ڈϏ1@\lQƅ{CP^APodX)' S=լhǫr^#x6ڧ=&ƥU߻eGt`Lu1^<|zm,y8[1P~O/$3pǿV+j`wO=u1cII'=optX::s 0=_C"_ȶdPDG[w)*;- x1 Є_ >D$F 4bH؁Gz=ΠJea *NK / :g, yY6t } __OHBɶs< l-C6kUc$=UB7@3 sA=/a@*:!ܓ^&IQg1^1T6ߑ~ΨRw(mBi|5`(v-1%)Fϒ*G+qR 4a%T/q2vk Q۰ ~S^ۂJ\[רN66bCCu&oGDø0gR9TOk_(G39?ÿ9~W ܟ'"ܢ]y&zay(#u}K5M 89ٹi?(\ձJ$(r}ރ;Cd@MāYl +EpHڟLUqJ߉6Noۄ:mM^NvAoσw+W<Qh@T۪TBVcbd\arWC~A`[=dQ(1|w2I=Gh>FKH,rjҧXG hj۵,)ZyQtԐ%Mx~> G;룺a@3!Hd_QXvYU3\v+VGrg2RH.v^rUY!nʍ.w+@yH?j[zJ -]ru~+\wX\9ڻ]3iO "0NUQCd=G_PD9?Bk5ȏ«@*5qu-|ۂ;lqm5@`0=)sq^+6TzA«G?dŮ4.$J0._EFpa U7r/ߨJBH %[y?J@,l'fGw:7*Y|˟ .d+^>YCE+(, -HQZu :]Nu V0T >@?я[/;z"m$'uq dho (OKQlƕ4/m=si ꭋc0Hٰ:@{W|P}2,w{P%<7 D\2 ?EIĐ7esTISN*Ldf<Mmgy J!?2JE9RI2X^ogpR4PCJp-ofCg&aJBV >D Oƫ+e`C;\0-`<;Kj J/_0`xSQm'Dz#`XDݬc Mȷ.YQT Bf M;g)hH[ވg_ʃ;;ʪW7ܖw:SC7W%EF_y%u$4B*MkT_8AMCv{J:@?a|$>Mq8-V#%c WO]P=ᴙ4j˪V(CO%y= afsyy tG3Bni8dw+{[JHB~̄tfyː^7t -z|ѥ[z>I${ *ZޤDlO#u2o0U_ (ԜQBʒHۗ P5&7[wC[Pޅ9ݩ?+gm1|YE&>.e깠1nHH[Q'Z@"[+FG{ͧ+.Pt ][5@`~dqRՍ}{@ x9;*{]bj .tBQrsvZ\&'ծ;ѩ_^g.$9MH]~XPI  2д\lgF\C2NT$7 9d)"8fDLrwW_Ԕ"uRN{_gw,ảۅ\[ېYɦ}s ;;lFh.dNN$]5<ҝ'<|٬#Kt p+[7hr- )o%6 HOTQgo': rK0}Ǎկ t47=<Ñ ,s,-*H:IMNP9sK(S$2/Hb Z-Zևƴ8 f>F];LQ'ct;):${{ei{f&)QPZfTg}&G|Q0zOf*[nUmRqPנ?䲄s/jM}R2TJ /Aɨ_ -S w.{)6>~q:! g&'}\y f #ү],bk*;Qˋ4V`}>?㋑ *0N%咔&ܯǗvq#&.տ\ nvٜ&݈2("|C e0KmlL^<pW譵"w@72ؘ-]T= n)vܩ#x+_N~}erKOd)'b%+P$Js Sn;%M_j *^vWK{m?6؇PH7j0*8y1X>PLK;҆* ?0[?n|U]\7?Sz9<pUbÝQRqs|?{tiG~']K>p_$tO'X֪y$ OQ}RC@cTNp3 ,8eƆ"EA9 NO#XvO:!!Wf.%:Y)>t)^ __#6Lfݕ8 RQi$@RzjXiAsuBoۯw0tZ/=m{JFQ2k%6aZU功cà ri#]6 +$԰oQKt cYxNupZrvU`紽1G%g[0ԃ!kaazo~RRFj߭n@Mž'ȣ!Ȋc$t^19=-I!yZT.1rL-A=Hdo5)k֋7G{j տ܀)(݌`9썮P@ruR)LuXz w7O;j݁۞/̪`ۅtDaO1%LY|x^s {%=>y),K5ԐKZ,@ ;7Z*C:QNL_آ=O@/i^Aٸ1NjAQ̈gkA5䙚/9S,f:x+MhԴwF>0??4 V BO-⫗8 d24Hb6jr^x+BIWϠʤͱs nk7 e;זOvvjWÕoPUo0`{'VjJ4p9ϿJ#7fnى2eb.Wa[ͿU.=9uuͧ?7[6 h7WϯlՐPx6Q9y"E#M~_-N>oh\VH6_')G8ؐz1FЍkk1Y:wqӚ(9#?5ZPdqS2zKm c(2H@YЀ=.` d0CN(JL/J؊7V␋!C#8㾓.MnҰd:p.b`K0 wtNJ΢d XM8sm]ʘvtFLvŧpr>pJL&ïrr'ڶ$\AzE_o! <>~0'ehz2|u(W猭Vz~NPX<3}tZGC߼BG:7 =]Qw7y{%N`K1r!xظw(d&K9k ԫPrSnP$OtʙZC\r۸c|QU|X $J[L7->:-@:/C:1D<0F>1F>1IB2NF4PH9SJ=TK=UL>XP?WP=WO>XP?YQ@ZQ@XP?VN=VN=WO>XP?YQ@YQ@YRA[SB\SB_TC^TC]UD^TC_VD_UD_UD`VD`VE_UD^TB_UCbXFcYGbXF^TDZRBWN>UMVM?UL>TK=SJQH:QH:QH:RH;QH:RI;QH:RI;SJTK=TK=TLWO>XP?XP?XQ?ZPAZQC[SAbWFf\Je[Ig]Kf\JaWFaWFcZHk`NmbPg]Kf\Kg\Jh^Mi_Ni_Mj`Nj`NkaOmcQmcQpeSqfTpfTqgTtjXuc|hyq^tkYvjXym[xn\}taxd{q_zp^{q_zp^|r`|r`~sa}saygnqrn}i}ilnpy~šӲ̹ɹŶ²ǸƸzuuxuoprk{hyfxdvduc~tb|r`{q_{q_zp^yo]wm[xn\wm[vlZvlZtjXsiWsiWrhVqgUqgUpfTpfSndRmdRncQlaOjaOjbOj_Mh^Lh^LlbPqfUrfTmaOj_MkaOf\JcYGg]Lf\JaWF^TC\R@ZRAZRAZRAXP?XP?YR@^UCcYHcXG\TBVN=UL>UL=WP>ZRAVN>RI;PG9PG9PI:OH9OH9OH9SJSJVN=WO>YO>\TB`WEbWG`UD[SBZQ@[SB[TC]TC^TC^TC]SB]TC]TC[RA\SA^UB_UCcXFbWE^UDZQCYPBZRA[TAZRAWN>UL>SJ6,=7,?:.>9/;6-;4+71(2.%/-#/.$1,#.*!/' /(!-(!-&,&)$(%(%(&(&)&+' +)'%&#%"##""!!!                                                          ! !"# "$ & $!%"%"&#&#'%'$)&-) 0+"2-$3.%1,#0+"0+"0+"3,#4.#2-$4/&50'31'72(:3*:3*=6,=7,<8+=:,@:0B;/E=/G?2G@2IB3JB6OF9SJ;UL>XO@XQ>XQ>WO>XP?\R@^TB\RAYQ?WP>XP?YQ@YQ@YRA[SB]SB_UD_UD_UD`VD`VDaWEaWFaWE`VE`VD`VD`VE`VEaWFdZHdZHcYF^TBZQ@XP?XP?VN=UM=UM>UL>TK>TK>SJ=SJYPBXOASJTK=UL=UMWO>WO>WO>XP?YQ@YQ@YRA[SB\UB^UCe[Ih^Lh^Kj`Nh]LcYHdZIh^LoeSmcQi_Mh^Li_Nj`OkaNlbPkaOmcQndSndRpfTpfTrhVrhVsiW{q_~kxdvl[xl[zn\xn\}s`{gvd|r`}sa{ta{ta~saucucygnsuslklpqx²ƷȤֵμ˻ǸŴʻȹ{xx|tnssl|h{gzfxewfvd~tb}sa}sa{q_{q_zp^zp^yo]xn\wm[vlZvlZukYsiWsiWrhVqgUqgUpfToeSndRmcQlcQjdQjaOj`NlbPrhVuiWqeSlaOlbPj`NdZHg]Kj`NdZH`UG_UE^TB\RA]SB[RAYQ@ZQA_VEe[IcYH`UDXP>VN=TL;XP?[SBVN=TK;SIQH:NG8NG8NG8NG8NG8MF7MF7MF7NG8OH9PG9SJVN>SJSL=QK;QJ;QJ;OG9MF7LE6JB6JC4JC4G?2G?2E=0D1H@3H@3JC3MC6PH:SL=VN>WP=YQ@YQ@ZQ?[R@]TC]UD\TAZR@ZQ@[RA[RA\SB^TC^TB_VC`VEaWEaWEbXFaWFbXFcYGbXFbXFcYGaXFaWFbXFeZHi\Jh\IcXF]SB[RAZP@YP?XP?WN?WO@VN?VM?VM>UM>TL=TKZQAZQBVM>RI;TJUM=UMVN=VNXN=WN=WO>WP>YR@ZQ@[Q@ZRA\SA]SB[TB_UDaWEg]Jk`Lm`NlcPg^LeZKf\KkaOqfTmbPj_MlaOkaOmaPncRncQpeSqfTqfTrgUshVtiWujXtkYwn\zej}sayo^{p^zp^|s`{g{huctb~vd~vdvdwexfygnuvvompsrxĵǸʧٹѿξɻǸͿʻ}{|~uswtn~k}i|hzgziygvc}ta~tb}sa|r`{q_{q_zp^zp^xn\xn\wm[vlZvlZukYtjXsiWrhVrhVqgUpfTndRmeRldQkcPlbQriVumYukWpdRoeRmcRh^Lg]KkaOh^KbXF`VG_UE_UC^TC^SC\TA\SAbWEg\Je[I`WDYQ>VO=VO=[RA\TBXO@SKWO>TMWP=SK;TKWO>XP>[RA_WFdZHcYHbXF`VD`VD^VD`WE`WEaWE_VE`WE`VE_VE`VEaVDcXFeZHg\JeYG`UD^TE`VFaXF`WE^UCXQ?VM>UL>UL>SK2E=0D<.B:.A9-A:.?:0@;0A;/@9.=5+:3)60&60'3.%1-#1-$1+#0*"0*#.(!-' -& +'+&-&.(!-)".)!-) )''$%#$!$!#!"#"!!! !                                                           ! !"""$& % $!%"%"&#'$'$(%)$*$)%+'.) 1,#4/&50'50'60'6.%6-$4.%3.%6/'80(81):3*;4+=7,=6+?8->:/@;0C:.F1G@1HA2IA3KE5NH8RJ;ULYO>WO=UNTL=SK;SJ\R@[RAWN?VL>WMYO?WO?WP=YQ@YP?ZO?XP>XR>ZR@\R@]RB[SA_UC`UC]VD_VDaWEe[HmaNobPlcPldQj_Mi^Kk`MtgUthVocQodQncQncRpeTqfTrhTsiVshVujXvjXvjXwlZxm[wo]x`n}i~r`}r`}sa~u`{gk{hwewfygxfzh{j{jpxzwrorutyŶʺΪܻâ̽ǻ¡˼~zvwzvpmk~j}i{j{iyewevducuc|r`}sa|r`{q_{q_zp^yo]yo]wm[wm[vlZukYtjXsiWsiWrhVpfTodRncRndRrhUxo[wnZpgUpfTpfTj`Ni_MmcQj`Ne[IcYHbXFaWF^TC^UD^TC^TBbWEg]Kj^LcYG[R@WO>WQ>\UBaWF\R@VM>UK?SK>TJ=SJYR@UNZRAYQ@VN=UMWN>XP?XP?YQ@[SA`VCcYGe[IdZHbXFaWE`VDbXFbXFbXFbXFaWEbXFcYGbXEbXFdXFg[Ih]Kd[IcYGaWEaWEcYHcYG_VEZRBXP?VO=VN>UL>TK=SL=PI:PI:OH9NF7LE4LE4HA3H@3G?2E=1C9.@;0C:/C:/D=/F?0G?2HA3JA6KB8LF7PI9UM=WO>YQ@\SB\TC^TC`UDaWFbXHbXF`VD_UC_UC_UD`VDbXGcYHeXGeZHhZHeZHeZHf[If[Ih]Jj]Kg\Jh\JgZHg[Ig[Ik_Lm`Nk`Mh[JcXF_UC`UC^TC[RA[R@^SA\R@YP=YQ=YQ>ZP>ZP>YP>YO>XN=YPYP?WNYP>WNZO>ZQ@ZQ?[Q?[R@[S?[R@]SB]SA]TA\SA]SA`UC^UC_VDaWEaXFbZFd[Gc[Gg]In`MsfSseTuhUtfSobOobOseRylYwlXrgSqgSshUtiVujWvlWvlXwlYxmZymZzo\{p]{q^{r_wamnyftbwcxd|fmlzg{h}i}j}j|j}kqx|{vruxx}ǹνѮ޿ƤĤĤ̽|zy|wponkk}j}kzhxfxfwewevduc}sa~sa|r`|r`{q_zp^xn\yo]xn\wm[vlZukYtjXsiWrhVqgUpfTqhUxn[zq]ulXrgUujXneSj`NndRndRi_MdZIe[IcYHbXGaWF`VEaWEcXFg]Ki_Me[I_UD\RA[RA^UCaXE^SBWO>VN=VM?UL?UL>SJRI;QH9SJVN=WO>VN=XP?XP?XP?[RA\RA\SA^UDcXGh]Ki]KeZHcYGcYGcYGeZHeZHdZHcYGdZIcYGcYGdZHd[Ig]Kk_Mi^Le[IbXFcYGdZHdZIbXG_UCZQAYQ@WO>VN=TL;TK=RI;QH:PI:NH8NF8MD6KD5IB3H@3G?2E=0D9.:5+72)72)72(50'3.%3.%2-$1,#0+"/)"/)"/)"-(/*!0+"1,#1+".)!+&*%'%&#&#$!$!$!###"""!!                                                           !!    !!!"# # "$ $"%"$!%"&#&#'$'$)&)&*%+&-' -' 0*#2-$50'72*83*62)70'81(61(71(:3*93+95*<5+=6-?9.A8/B:/C<.E=0F=3G@2I@5JA7KD6ME5PG7SK:WN=\QA^TCaVDbVDaWEaXEcZFd\Hd[GdYGcXFbYEcYGdYGfZGg[Hi[Hg\Hg\Hj^Ki^Jl_Ll`Mk`Ll`Lm_Lm`Lj_Kk_Jo`MrdPsdPnaLj^GhZFdYEcXDcXDcXCaXDaXD^UA]UA^UA_S@]T@]T@]RA_S@_S@`SA_T@_S?_SA_T@_S?`U@_T?_T>`U@aVAe[Gg\HcXB`V?`T?bUAaV@_V?aVAbWAcXAcXCcWAeXCgZDdYBdYCdZDfZFiZGh\Gh]Hl^Jl_IiaJmaKobLrgRxjWzlW{nWxkUvhRvhR{mX~s^{oXymVylW{mY|oY|oZ}p[~p]r^r^t`t`uaxcyblto{gze{g}gorljjlmlmpy{ux{{~Ķ˼Գɨǧââɨοz|wsrpqomm|j{i{h{iygxfweucuctctb}sa{q_{q_zp^yo]xn\wm\vlZvlZtjXtjXsiWsiWxn\}t`yp\tkWulXtiVlbPndRqgUlbPg]Ke[Je[IcYGcYIbXGaWEeYGk_MnbPg]K`VE[Q@\RA_UDcYG`VCYP?WO>VNXO@]TDZRAWO>UM]SB_UD\SAWP?WO>WO>XP?YQ@YQ@ZRA\RA^SC`VDcXFi]Kk_Mi]Kh\JfYGdZHdZHdZHf[Ig[Ie[Ie[IdZIe[Ie[Ig]Ki_Ml`Ni]KfZHfZHe[Ie\IdZIaWF^UD\SBYQ@XP?VN=WO>TM0G?1G@2E>2D8,A9.A9-@;,DWO>WN>WN@UM=VO=[UA]VCYQ@UL>UL>UK>TK?TK>SK:TLUL>TL>UM=ZSA\TC[RAVO>WO>[SB`VE_UD[Q@ZQ?XP?YQ@ZRA[Q@^SB]TB_UD`VEbXFh\Jl`Nl`Nj^Lh\Jg[Ii\Ji]Kg\Jf\Jg\Jj^Lg\Je\Jf[Ij]Kl`MlbNl`Mj]Kg[Ii]Kk^Li]KcYG_VE\TC[SBZRAYQ@VN=UM6+?7*=6*=6*?6*?7*@8+B9,B<-D=/G0J?2LB3ND4OF5QH6RK7RK9UK:VN<[Q=_S@eWCg\Fi^Im`Kl`KmaLraMtdNtdOrdOpcLpcJoaLrdNrdNteOwfPwhQxiSyjSyjT{lS}mS{mS{mT}lT|mT|nT}mU~nUsWrYpV|kRyiOzhOxiPwgOvfMwhMwgMvfLueKvgLueJueJufKufKufKufKvgKueJteIteJueJteIteJueKugKwgK}kO|mQ{kOziMxiMxhLyhNziNziM{jN}lP|kO}lP|lOzlN|mO}lP|mSnSoToUpWpWsWtXtXx]|`cc~b|`|`~agf~c}dddfggiikkklnvwpqrt|{wvwvwwxȺʽŷ´ǹťʧܺҰЯ̬ͫүƤƺ¶|ywwutsrpoomlljjk}i{g{gyfxdxevbta~r_s`|q]|q]zp\{q[xbzeu`}r]{r]ulXtjVwlYtiWncQmbOl`Mm`Ni^Lh^Le\If\JkaNpeRncPg[I`WE`UCbWEg[IcXF]SAYQ@WP?XQ?WP>XO?YP>]TB`WEYR?UM=VM>VM?UM=VM>VM?UMZP@^TC^UE[SBYO?YP?_UDcYGaWF^TC\RA\RA]TC]TC^TC]SB_UCbXEbXFe\IkaMnaOmaOj^Lh\Ji]Kj^Lj_Mk_Mk^Lj^Lj^Lk_Mj^Lj`MmbNqcQodPk`Li]Jk]Kk_Mk`Nh]KdYGaVE]TC\TC[SBYQ@WO>VO>VN=TK=SJ5+B:-D<.D=.C;,B:+D;-E<.E=,E>.G>0J?1MB2PC4RF4TH6UI7WK8WN:ZO;\Q=]R>aS?cU@fXBh]DkaIrcLtgNuhPzhQ{jP|kQ~nT~nT|nR{lR|mR|mRoSpVqVrWrXsXvYwYxXwZv[xZy[x[x[z[|]~_}^z[vWvWuVuVuVuVtUsTtTsRtTtStSsSqRsRtRsQtRtSsRtSuTtSvSvUyX|[yYxWvVvWwWwWwWxXxXyVyVzWzX{Y{Y{Y|\}]}_~_`^`bfjlmjihmqnlllmopqqsstuty{yz{|~}|}ƥ˾ȻƧ̫Э׶ճЮѳ׶ɧȼ~||zyvuttrqqponlkk~h}g|f{ezdxbxbwatau_zch~fw`wa}r]xmY{o\{p\ujVqfRpeQoePncOmbNl`Mk`LnfQsjVrgSj_LfYHcWEeYGj^Lg[I`UC]UBZS@XQ@YR@ZQ?]SB`VEaWF]SAYP>VNWN?WP=WO>VM@WO>VO\SAaVD`WDZSAYQ@]SBbXFcYGaWE^TC]SB^TC^UD_UDaWFaWFbXEbXFeZHj`NlcOndPmaMk_Mk_Ml`Ml`Nk_Mj`Nk`NmaOlaMl`Nl`NnbPrfSreRoaOl`Mk`MmaNmaOj^LdZHaWE`VD`VD]TD]SBZRAYQ@VO>UN>TM8-<6+;5*:4):4)83)81(60'4/'3.&3.%3-$6/&81(81(8/&5,#1*".(!-' *'*&,%($'%'$&#%"%#%#%"$!"#!"#"""  !    !                                                           !#"! !%%$!%$%#$"&!$$& & '!(#'#(#*%+&,(,'-).)-)0*2*3+!5."6.!8.#9/$90&:1&<3'<4(>6(@7)D:+G<-K@0ND2OE4OD2MC0NC0PD1QE2RG4UH5WJ6ZL7\N7_Q:bSZQ?[Q?ZQ@YP@XO@ZP@\RAaVDdYH`XG]TB\R@aUDh[Ig[HaUE]UE`SEbSE`VE_WEcXFfYHfZGh\Hl_MpcPqfRpeQmbNmbNmbNmbNncOncOmaOmbNmbNnbPnbPocPpfQugTreQlbOkbNoaOpbPl`Og]Jb[IcXFaWE`VD^UD[TBZS@XP@WP>VN=UL>SJ=RI;QH:OF8ME7LE6LE6MF7NF8LD6JA4E=3A;0?9.>8-=7,<6+;4*;4+82)50'71(60'5/'60'81(;3*:2)70'3.%1*#0(!,( *',&*&)%)#)#($(%&#$"%#$!$!$ $ #""#"!!!!!!!                                                        !$% %!%!% $$%"&#($+&,',&+&+%,%+%,&,(/(0)0*2+3+4-5.6/ 6/ :1";2#<4%>3%@4%B7'C8(D9)F:)H<*I=+K?-MA-OC/RF2UI4YK6]N8^P9^O7`Q9_P8aR:cS;eU;iX=kY>n\Ap^Ar_CtbEwdHxeHzgJ|iJkNmOoNrQvTyW}\_accbddedfeghhijlmmmopqrqpstvtrpnnonnmllmmmmlmllmmnnommnprrooooooqrqssttuuvvwxzzz}ĪƭŮŭê¨ĬǰƯĬīĬŭŭŮƮŮůǰưưDZ˶ҽκȴƳdzɴκλɶȵɶɶɶɶʷϼĢƣμͼϽѽãЮֵЯ̩ЭԳٸ޺޼׳Юʩ¡ϾпνǷȷƸ~}{|{ywvutsqonmmmruojke~dg{cv^s[rZqY}pY|nU{mU~pXu]u\zmVseOqdMreNtiRqhQl`Ji^Ig\Gf[FdYEdYEh[Hk^Jk^IfYDcXBbVB`UC_TB_UB_TA_TA]SA]SBaVDf[Hh[IdYGbWDaVCeZGf\If[IcWEcVEcWFcXFcYGeXGf[Ig[JhZKl^LqcPugUsgTodPncOmbNncOpeQncOpdPpeQpePodPodPocQsfSthTsgSrePndPoePpfQrdPl_Mh\JdZHcYGbWFaVD`UD_TC[RAYQ@XOAWO>TM;TL:RJ9OH8NF8NE7NG8PI:PI:MF9JB6G>2D<0B9/?:/>8-=7,<6+<5+:3*92)92)60'81(:2):3*92)93*71(4.%2,#/*!-)!,( *','-%+%+$*$*$'%&$&#%"%"$!%!##"##""!!! !!""!                                                       !"#& )#*$,&,&-'-$-%-%0(2*3-5/5-6. 5, 5,!7."8.:1 ;2!=3"?3"@5#B8$C9&C9%H;(I=(J?*L>*N@+PD.RC.TD/UE0YH2ZI2\K3_M3aO5cR8fR9iU;lX=p]@s`BubDwbDxdEvbExdF|gFiHiGmInKoLsPuRwSzU{X}WY]\^beimnnoprrtrsttwxxy¨{é{ũ}ƪ~ǫȫȫȭ~ɭʮ˯ͱβ̰ʰ~ˮ}̮~̮~ͮ}̮|˭|̬|ͭ}̭|̭|ͮ}ͭ|ή|Ϯ}ϯ}ϰ}ϯ}ΰ}ϰ}ί}ϰ~б~ϱ}ѰҴӵӴҲҲҲӳҲӳҴҳӴԵԵԵԵԵյշշշָָֹֺ׻ؾٿٿֽؼ•ؾپٿپپؿ——ØÚɡ̦ƟÛĜĝǡʥƠşşşƠƠƠɤΫӯϪʥʥʧ˨Ϭٸ߾ۺ״ٷݽ޺ٶԱ̨ʧʫǧàťà̼ʹɸǶƵŵòñ°~|{zywutx{yttokomfcb}a}`z]x[y]bby]rX|nS~nTqX~pWvjQtfMtdMrcLrcLpaJscMufPrdNl_Hj]Ej]Fj\Fh[Fg[EgZEfZDfZDdYFf[Hk_JmaKj\GgZEh[Hm_LmaMi_Jf[GeZFg\Hh[Hg\Ig^Kl^Kl^Kj`LpcPugTwhVvhUrfSpeQpeQpeQpeRreRreRqfRqfRrgSqfQtgSxjYwhVugTseRqeQrgSqhSncPk_Mh\JeZHdZHbXFaWE_WE]UB]SBZQ@WO>VM?UL?SK:QI8OI7NH7NF9PG9PI:PI:LE7F@3D<1C:0B;0?8.?9.=7,;5*;5*92*:4):3)93):3*;4+;3*;4+61(4/&1,#/,".* .( ,( +( )&+%+%+%)%($'$&$%#%"%"%"$!# $ ##""""! !! !"##"!                                                                !!""$&( (")",$-%.%1(3* 5-"8/!80:2 ;0 ;1!<0#=2$B5%E8&F;(F;&H;'I<(J<(J='M>(N@)QB)SD)UF,WH.XI/ZJ/\L1`O3cQ5eR6gR6jU8kV9nX:q[;t]yc@~dBfDiFkHnJrMvQwQzQ{Q|Q|R}SUXX\^^addfiiklnråvũyȬ}ʮ˯̰βддҴҴӵӵշָ׹غٻڼ۽ݾ߿߿߿‘ēœēÒЏŽŽÐÐÎÎďďÏÏďŐŐŏƐƑɔȓǒƑƑƑǒƑǒȓȓȔɔɔȖȖʘʘ˘ʙ˚˚˚͜ΟϢУѤУΡϡҤҥУϡТТϤФФѦѦѦѦҧѧլٲױԫӪӪծײְիԫիիլԫ֯ٶݹݷسհײسڶִܺճ״ұϬѯͩȢƢŠĠŸҾѾнϻͺ̹˷ɶǴdzƲıį~|uxyqnmlihggklg`|\}^~`{^wZsVpToSmRnQpSqUnTyjNvhLufLteLteLscLrdMpbKoaJpaJreMthPtfOqcLpaJrdMtfPtfPpcMm`Jn`Kn`JlbKnaLoaKobLpcNsfPviS{mU{mVxjTwhSvhSugTugTtgStgSugSuhStgRthRwjTylVzmWyjWvfTtfSugTtiUqfRmbOk_Mf\Je[JdZHcYGaWEaVG]TD[SB[Q@YQ@VN=UL>RJ=RI;RI;PI;RJ8-;6+:5*;3)93(;5*<6+;5*=7,<6+71(4/&1,#0+"0+"/)"/*!.) -& +'*'+( )&)&'%(%'$&#&#&#$!$!$"# $ ##"#"!! !"##$ $ !                                                                    !"$$%'!' )")"+$-%.&0'2)2)5+7-9.:.<1>3 @5#D6%H;'K<(O?+P@+Q?+Q@+SB+WF*XH+[K/^K0aM1aO2aN1bO2eQ3hT5kU4mW7oY9r[9t^;v`XN=UMRJ4*;6+95*=5+=7,?9.>8-;5*83(60&3.%1,#0+"0+"/*!/)"/*!-' .&,%)&(''%'$'$'$&#&#%"%#%"%!%"%"# ##""""# $ $ %!#""                                                           "$&'( (!+$+$/%/&1(3)5*6+8,;.<0>2@3 A4!E7"G8$I9$L<%O>'RA(SB)TC+YF+YG+^J.cL2fP2kS4kV5nW7oY6pY7u\9v^;{a<}e?gAhBhAkCkBnDpFsGvJxKzM{P}QQTUVY[[]_acdfœhĞjơlȣmɤpͧsϫuѬvԯyկxկyزyڳz۵|ݸ߹Ẅễ侇濊Œčŏǐɒʒ˔ϘЙҜԠ֢إإ٧ڨ۩۩۩ܪܪܪݬޭޮ߯޹޺ݹܷ۵ڴٳٳױױ֯լӫӫѩЦͥΤ̣ˡɠȟƝƜȞʠȞěÙؿսֿջжʹ̱ȯƮŭīƭȯĬ{wwyvrnljihimhcb`a`]^]~[\_`^{[zZ~]~^{\wYvXvXvZuZuZtZtXxYz\}^{^y]v[u\v\v]v[u[t[t[sZrZu[w^u[rZ~pWpVpY|nWzjTufQrdQpbOoaNn^Li^Jh]Id[GdYFaUD_SB\RAXQ>VMVK?WM@VL=SJ1E=0D;.@:/@:0?8.<8->6+?5+>8.=9.?:/>8-<6+:3)60'3/&4-$3-$1,#1,#0+"0+!.* 0)!,&,',&(&)%*$)$($%"&#'$&#%"$ % $ # ###$ $ $ $ $"%"#!"                                                         "##$')!,#.#/&1(2'5)7+;-0@2 B4!E5 H7#J8#K:#N=%Q>&TA'VC)YE*\G,^J.aL0dN0gQ1mS4oU4rY6uZ8w\9z_<}`=e?g@jCmEpGrGuIwIxJ{M~NQTUVWWY\\^acd›eĞgƠiʣl̥nΧoѪoӫqխsׯuڲxܴzݵ{߷}⹀㻁滁罃ˆŋƌȎɏʏˑ͔͒ϕїәӜ՝֠ء٣ڤۤܦܧݩޭ߮߷ߵ޴ݳ۱ڰٯث׫حخخ֬ԨҦϣϣ͢ʝǙŘוݿ•ٿ׺ҶѴҵдͰ˭ɫ}Ʃ{çy¦w¦yħ{xroonlkjjjjmlhegjjfcccbcbbbeggeccacaa~`~`}`}a~abb|ax]x\z^w\qY{mVyjSwiRvhRtePqcNnaKl`Jj]Jf[GdYFbXD`UB^SAZQ?ZP>[Q?[Q@WO>VN=RI:NE7KB4JA3H?2G=1C<.D;0A:/@:/?9/>7->9-A:/B9/B:0@:/<6+93)60&4/&4/&2-$2-$2,#1,#/)!.)!.(!.)!/' +(+' ,%(&*&*#($(%&#&#%"%"%#%!%!$!#"$!$!&"'$'$&#%!#!                                                   ! #&&& ) +!*!,".$/%0'3)6+9,<.>/@1B3D5H8!L:%M=$P>#SA&WB(XE)\G+`J-cL/eO/hP1kR3oU4rX4vZ6y^8|a;~bh@jBmCpErGuHyJzK}N~PRSUWZ^_`acÜdƠfȢi̤mЦnѨnҪpԪq׫rٰtڲv޳yz|~~꽀쿃…ćƉȋʎˏ̑ΓЖЖҘәԛ֜؞٢ڤڥۦۦۦۨݩުݬ߮޳޲޲ܯ٫ة֧զӤңҥӦҢΞʙɘșǗŔ‘޾ܽٻڻڻ׸ԵѲϰί̭~ˬ|ɫ{ȩyŨzŨzƩ{Ũzwuvvtqpppomlnprtsommlkkiihhhjkheced~`x]uYrWqWpV~mUzjRwhPsfNpcLn`Kk\Jg[GeYEbWBcWCaXC_UB]TAZQ@UN;QJ7NF5ME4KB4JA3G>1E>/C=.A:.A:/@9-A;0C.B1E4H7 L:"P>$S@&WC(\H+`J-bL.gO0iQ/kR0oU2rW3vZ6{_8}`8c:h>i?m@pBrDvHyJ|K~LPQTVWY\^Ù`Ɯcɞe͢hФjӧlթnجqۯr޲twy|}ƒąƆȉʋ͍ϐϑДҖӗԙ՛֜ןأ٢٣ڥۥۧݩܫݬݮޮް޲߶ޭݪݨۧ٦ؤפբӠӡҠҟНΛ̚Λ͚ʗȕŔǐھֺԹԹӷѵδ˲ʰȯƮƭŬª~|{{wrqmmkgeda_}[zXvVtUpRoQ~nP|mQ{kOvfKrbHl]DgY@cV@aT>^Q<\O:YM8XK7UI5QF3QF4QG4QF5QD5LD2H?0E<,B:*?8*>5)<4(:2):2(92'5/%4/$4.%3-$2-$0,#.)!.(!.(!-' +'+' *'+&,$)%'%'$'$'$(#("*%)&)')&)''%%"# #""!!!  !                                 "%(,".$2&6)9+;.=/A1D2G5G6L8!O<#S@%WA&YD']H)bJ*fM,kQ.nS0qV2v\6}_9deV6)>5(=5(92%90$8/&70&5/$3,#0,#0+".) -' -(-' *'+&-%(%)%*%(&*',&-' -(!,&'$%"$"$"$!# #""!! !                                            !"$&) +".$2'4(8+=-?0C4G6K9 P=!T@%YC'\G)aK-eM-gO.lS1qU1uZ3z]6~_7c9g;i/G;,G:+B8*>6(>5(;3&:2%90#70%4.#4,#3+"2*!/) -'.',')(,&.& /(!.'!/(!+( *''$&#$"$!$!$!$"#"!"""!!                              "#&( ,!-"1%5(8,<-?/E3I7K9P< U?#YC&\F'bJ*hN,lQ-pV0uY2{]6b8e;i=n@sEuFyI|KMPQUWZ\ę`ȝb̡dФhԦj٬nݯpswy}ÃŅLJʊ̍͏ϒєӕԙԚ֛םؠأڤڦ۩۫ݬݮް޴޵ޮܫ٨եԡҞН͛ʘȔƑÏῌݽܺ׷ԴѲбͭ|ɩyãtpmkgb`^\ZW~V}U{TwQqNkJ}gExdBta@p]?mZ>jV>eQ;aP9]M7ZK5XH4SE0QC/M@-I>,G<,E:+C8)@6(?5'<2&:0$8/$5."2,"3+ 3+ 2,!0,!2,"3,"1*"/( +'(%&#&#$"$ $ $!# $#!""!!!                              "&',".#2&4(7+;-?/B2F6K9P;!T@"YC$_G(eK)hO,mS/sW2w[4}_5c9hmZ=iV4&<3&<4':2%7."4,"0*-(*')%($($&"&"%"#$ "#!!!"!!                              "#'(+!/#4(9+=/B2G6K9Q="WB$\E%`H(eL*iP+oS.tW0x[3~a6e8h:m=q@uE|HKNQSWZƚ^ɝ`͠cѤgשj٬mްptx|}‚Ņɉ̌͐ϐђҔӖԙלמء٣ڦ۩۫ݮݱ޳߯߭ެܩ٥֡ԞКΗ˔Ȑō‹忇⼅ݺ۸ٵֲ|үyͨsɤpğljhea^ZXV|SwQtMqKmHiD}dBxa?s]>oZ4%:/!7.!4-!0* .),)+'*%*$'#%$$!%"# $ """                       "%),"0$4'8*>.C1G5K9!P=!U@#[C&aI(gO+mS.sX1y[2`7f:j;pAtDwG}KLNSWZØ]ɜ`Πcҥfשjڬo߰qtw{~ÁƄȇˋΏБѕҗԚ֞סؤ٦ڨܫݮܯ޲ߵޮޫݩۦآס֞ԜћЖΔʏƋ彅Ẃ޷ڵ|հyѬuͧqɤmğjiec`]YUS{PwNrJnGjEgCzcAv`?p]=n[;mZqZ;kV8hS5bN3]K1WG.TD,QA+J<'F9%D7#?5"<3"8/"6-!3,/(.'+'*$)#'!%#!"" !!                   "'+ 0#5'9+>-C2I7O;!U?$\D(cK)iN,qT0wX3|^5e9i=o@tDxGJMSW•Zƙ]ʞaУe֧jۭmswx~‚Ƅʈ΋АѓҔԘ֛מء٤ڨܪܭޯ߲ޮެܩۧڤء֝՛їΔːȌň罂~ߵ{ڱwխtЩsͦpȣmğjec_ZVQzNtJqGlCgA|a>u];oZ8iU5eQ4^L1ZH.UD+OA)J>(G:'B6$?4";1!8.!4+2)0(-'+$)"&!% %#"!                  #', 0#4&9*?.E3L8Q<"XA$_G)gM-mR.tV1|\4c7h"[D%aH'hO+oS/xZ2a7g;m>rByFKPRXĖ]̝aңfקiޭnrw|Ńɇ̌ЏГӗ՛֝ءإ٩۬ܰ޲߶ߴݱݮܫ۩ۦ٢ןԛҗГ̑ȌÇ꿂~zܱv׭sШpˣkŞfb_ZUR}NxJqFlBf?zb+I:(D7%@5$<2#8/ 4* 1'.',$+#(!%%!              !&+0#7(>-E3K7S<"[C%bI)jP-rU1z\4a7j;o@vD}INRW×]˝`ѣf٪jptx}ÀdžʊΎБӖ՛םנئڪܭݰ޳߱ܭܩۦڣנ՜әѕ͑ʌň{ݳw׮tѨn̢jĞfb]XRO{LsHmDg?{b=v\9nV4fQ3_L0ZF-SC+N>(I:&C6$>1"9/ 5+2).%+$)"& $!!              %'- 3'9*?.I3P: WA#`F'iM*qT.{\2b7isC}INT•[ʜaӤf۫kqv}łɇ̌ВӘԛ֠إڨۭݱ߶ްܬۨڤנ՚ҖΑ̍Ƈ|v٭rҧmʢiÚc_YS~OvJoEiA}b&G6">17-1).%)!%!           %* 4&?.J5V?"bH(lP+xY1e9o?xEMT”ZϠa٩ipx~ƅ̍ѓԙ՞أڨܮ޴߳߱ް߯ޯޮޮݭޮݭݭܬݬܫݬܫܫ۪ݨݨݨۨۧڦۧۦۦۥۦۦۦܧܧܧܧܧܧܨܨۧۧۦۧڥۦۦۦۧۨۧۧۧڦۧۨܨݩީުުެ߮ۦВˋˉʊˊˋˋ͎͍̌̋̋̋̌̌̌͌͋͌ΎΎΎΏΏΏΏϏΑΑϑϑϒВВϒϒϒГГѓГѓғҔҔҔӕӕҖӘԙ՚՛֛՛՝՝՜֞ؠ٤ۦۧܨܨܧܨݧߩߩ߱ܫ٦أלҗΒʌĆ~xݯrҧkȞe_ZS|LsGi@|`:pX6fQ1]H+SA(H:$@3 :.4)/&("#"         %, 7'A.L8[B$gL*uW/`6jr@tAu@vAwAyAzB|C}C~DGPXVPUYY[ġdʤcȟZ̦^ӫb֮a۲`߹acmӊ޲ܫڤ֞Җ̎Ɔ~t֩mȞcZR{KoDdU>S>T> U>U> V>!W?!W?!W?!W?!X?!X?!X@!X@!X@!X@!YA"ZB"ZB"ZB#ZB"ZB#\C$]D#]D#]D#]C"^D#^D#^E$^E$_E$_F#`G$aH%bF&cG&cG&cH%bH%bH%cH&dI&dI&dI&fK(fK'fK'iL'iL(kN)mO*nP+oR+pS,sU.vX/wW.xY.z[/|]/~^0_1`1b2c2g6sC}LzI|ILLINWOGMLLȠMӦN߮P[~߻ޱ۪ڣ՜Гʊy٬nˠe^T{KnA~a:mU2^I*R?%E8!:-1')#$!    *6)C3UA"iL)~[2l?|HTɛ_ڨit~ʉѓ՝ئܮߵ޲ݮܬ۪ڧ٤٢ן֝ԚԖӕГϐΏˌɉLJń‚~zyxtޯrۭpثnթlӧjѥiΣgˡfȞdƛcÛa`^^\ZYWVVUUSRRPO~P}N{MyLwLvJuHsGpEoFmFlCkCkBkAi@h@h@fAe@fAfAfCgD~fCgD~fC~eD|cBza>wa>v`>u^>t^=u_>t_@t_@waBwbBxcDvaBu`AuaBs^?s]>r]>p[>p[>p[?p[?p[?q[?p\?p\?p]?o]@o]@o]@p]@q^Ap^Ar]@r^>s`@t`At_DtaCucDucDweGygJ{iL}jL~kNnOnQoRqSoRoPmNmNmNnOoPpRqTsVnR]L2=-8&8'8(8(9(9'8(9):):):):):):):):):):):);*;*<+<+>,>+?,@-@-@-?,@-@-@/@/@.A/B0D/D0D0D0D0E1E1E1E1F1E1F1F2G3G4I6J6L6M7O9P;R<U?WAXBZC ^E!`F"aG"cH"eJ#hL#iM$nQ){^7jBnEtHzKwDp>uBwBv;v8=<9@ʚBئDS|ݯڧןӘ̏Äxڭpˠf\QxHj?w\7fP0XE)K9#=0 4*+"$      ,!8*H6YD$nS,c5tANZդfsˊђ֝٩ݱ߳ݰݭ۪٥آנ֞֝әіϒΏ͌ˊȇƃ~{ywt߰rܭp٫nժkѥh΢fˠdȝbĚa˜`][YXWURROO~N}MzKwKvJtHsGqFnDmDkBjBjBhCe?d=~e=|c<|b<|b={a>za=y`=w_cV>fY@iZAj\Cl_EpbIrcJseKufLvgNtfMvfMufMsdKsdKsdKtfLuhNOC0)! ! " " " " " " " # # # # # # # # # # # # $ $ $ % % & & ' ( ( ()( ( ( )) *******+ ,,,,- .!.!0"0"0"3$6&6&9)<,>.?.B0E2G3G3I5K7N8P;P;YC!jT3s^;zd@~iAyi?yh?}h=j9j6f.i,o.t/}37ē=ԡCQ}޲۪آԘ΍ńz٫oʞcZPrEdd<c<|a;x^:v\9u\8rY7pY7oW6nV6kT4iR4iR4fQ4eO2dN1aM0`K0_K.^J/\I0\I/[I.ZI/[J1ZJ/[K1[K1ZJ0XG1WG1UF-UE.SC-QC*PA+O?+O@+OA-N@,L?+NA,N?*O@,M?+O@,PC/PE1PE1QE2RE3SE4QD2RD2PC0NA.NA/MA-MA.NB/PE0RF2TI4TI5UI6SH6RG3QG2PF2OE2PD2PC2NC2PD3PE4PF4RG5QG4SG4TG4UH5VI6VI6WJ7WJ9XK9WK9[M;[N:[O;YO:\Q<`T?cWBfZDi\Gj^Il_Jn_Ko`LobKo`JnaInaJLB2        " $ % ( * ,.!2#2#4%6':):)=+@.@.M:cO2jV8jU4hW3laCj`=j`1la0o_,u]'~_&i*u1|16Ó=ӡAGݬأԙ͑Åwԧk™aUyJi?uZ5cL/Q?'D5 6,+"$    *9+K9`H&wZ1n?LZզfuńБԛ٧ݲ߶޲ܮکڦףן՚ӕҔϏ̊ɈƄ~{vrްp٬lԧjѤgˠcǜcĘ_ZYWTRPN~L{JxHuFsDoAm?j?h?e=b:}a:z^8w\7t[5sY5pW4lS2kS2gR1fP0eO1bL/aK.^I-\H,YG,YG,XF*VE*UC*TB*RA*Q@*N?(N>)M=)K<(K<)J;'I:&F;'G9(F8&E8%D8%C7$D8$E9$D9&D:'D9(F;*G;)I;+H;+D<)D;)C8)B6)A6'A6'@6'>5&@4%@4%>4&A5'A6'?6'@7(B8)C9*D:,F=-G>-F=/G=0G>,E=,C:,C:-C:-D;-C;,D=,G>.I@1KB1LD2KC1JB1JA0IA2G@0H@1H@1G?0H@1JA2KA0LA0LB1MC2ND3OD4OD4MD3ME4PG6SH7RI8QH7RJ9TJ9VL;WN=ZP>]SAaVEdYFg[Hi^Ik_ImbLRH8                                                        ! $ & ' * - /! 0" 2$ 4$ ?/TC)UC)XF(UI+\V>_Y;]T+`Y-c[.i_4p]1x`+p2w3{1>ǙAڡAL٪߶߶ޱݯޯݮޮ߶ܬؤӗˌޱs͢g[NnB{^:eN0TA(D6 6*,#$  &7'J6bG&yZ0o>NŘ\۫kyʊҘפۮ޳ݮ۪٦أ֟ԚҔёΎʋdžÀ|yvr۬mէhѤf̟cǛ_×\ZWUQNL|JyHuFsEoBl>h0H=1E<0E=.H?1G?0H?1JA2KB3JA3H@1KB4KB4ND7LD5MF5NG7PH7PH7QI8RI9TJ;WN>[Q@_UCbYFi^ITK<      !%') +. 8*B3B2G7MC)RM6TO1UL(XO*\U0c[5shIwnPnC|Dz9;ŖDϜDDbܷڵٳرװկԭլիիҩѥУΠ͟ϡϡѣӣӤӤӢԡԡԡ֠أؤ٧ۨܩܪݪޭ߭۩ןВƇyӧj^QrC|_:fN/RA&B4 5)+!!   1$E2\C$vX0m>MǙ]߭l~͎՛ڨ߷ް۬اע՝ԙҔϏ͋Ȇł~zvsܮnשkУf˟cƛb]ZUTPM{JwFsEoAl?j=f:a9|^8xZ5tX5oV2lS/hP.eM-cL-`J,\G*YE(WD(TA'R@&O>%N;%L<$J:#G9"F6"D4!B4!@3!?1>2 >1 :/9.9.9-7,7-6,5+5+4)3*1)0(1)1)1(1(0(/&/&/&-(-(-&-(-(-(-(.)-(-(-'.(.)-(.'/(/*1,!3,"3-"6/$80%6/%82':1(92(71&70%5/$5/%4.$4.#3.$3.$1.$1-$6.%8/&81';2)=5)=6+>8,A:.A;/B<1B9/A9-?7,?7,?8*?9*A:-C=/E?0GA2IB3JB5IA4H@3H@2G?0E>0E>/G?0G>1H?2JA4I@3JA4JA5LC6LD6MF7OF8OG8QH:QH:QH;SJ=UK%@21''    +"@/U>"nS-j;LŖ\߮m}А֠ܭݲ۫ئע՛җђ̍ȇł|wsݮnتjӤf̟aƙ][WSOM|JxGtEpClAi?e=b:z^7v[6qW4lR2iQ0eM-bK-]H*YE(UC&SA&O>#N<$K9$I7$F5"E7!C3A1>1<0;/9.8-7+5+3)2(1'1(0'0'/%-%,%,%,$+$+$*#*"+#*"("("(#)#)$("(")#(")#)#'#(#(#'$)%($*&)$)%(&)$*%)&)%+%+%*%+',(*(,(.)!1*!3-#3-#6/&71'73)93)91)71(60'6/&6.&6.&3.&6,%7.'3/&6/'70'91)92);4+=7->8-@:/D<1E=0D=2A<0A;.B;0@;.@:+B;-D1IA4KC6LE5KD5KC5JC4IA3H?3H@2HA3J@3IA3JC4LC6KC7LE6NG8NH9PI:PH:QI;SJVN>4/&    #' (. 4%;-K=%J;D9JA(TQ?VT>XX9]Y7c\8e]4rN~uOrCYOM͡[֤VMUՍ޻ٷձҮΫ˧ȥŤġĠнʸǴűűDZȱȲɱǰƯƬūūƬɬɮ̮̯αѲѲӴֵٹܽ޿ÍďŐŏǐɑ˓ΖϘқԝ՟סآڥۧܩݫޭ۪߰םΐ￀ڭpĚ`RoCv\7`I-L:#9,,#$   $5(N8fJ(b7zFXةj}Αנܯ߳ܮ٨֣՝ӖВ͌DŽ€{vq٪jѤeʞaŘ]YTPL}IwErCn?i;e:~a7z\5tX3pV1kR/hP.bK,_I+[F)YE)UB'R?%O>&L<$J9$F7"D6"B4 ?1;.:.6,5+2)2(0&/%/%,#-$+#)")")"( ' %%& %$#####$$$$#$$%%$%%$ #$ ##$!%"%#%#&#'$&$&#&#&"'#'$'%(%*$)&*%+%*'+'-& /' .' -(!/)!2+#2-%4/%60'81(;4+;4+;3+;3*;3*;2*91)71'82)70'70'81(92):3*:3*;4*=7,@:/B;1C=1D?0E?2F>1F?0E>/D=.C;/D<.F?0H@3JB4LE6ME8NF8MF8MF7LE6LB6JC4JC4KC6KE5MF7MF7MF7OH9PG9QI9QJ@8,       #& ) /!B4!N?+L<#F5D7NC+VTD\\JZV9^X6jd?hd>qiBxoGpETRQˣf׬gYPcܸ׵ұͫɨƥŢĠ̽Ŷ±|xvuustrrrsqrtv¥xŧyƩ|ɬ}ɫ{ˬz̭{Ϯ|ѯ}ղ}ֳ~صܹ߻㾈ŒŎȏʓ͔ϕҘԛ՟ء٣ۦܨݪެ߰ݭסΓ٬o^Nj>pU3XE)D5 2')!!  + @/YA#tW1pASңey͍֠ܮܱۭ٥֞ԙВ̍Ɉ|tޯo֨k͡cȚ_\UPK|HwDp@j=g:b6z\3tX2nS0jO-cM*]I*ZE'WA%S>#P="L: H6 E4A3=0=/:-:-7+6*4(2)2(1'/&/&,%+#+$("(!& $$$"!!!   !!   !!!!!"##$ "### # $!%#%"$"%#%"&#'$'$'$(%(%)&(%(&+&+( )'+'-& -' -(!,' .' .*".+!1,#4-$5/&71(82(92)=5,=6-<5,=7+=6,:4+94+:3*:3)92)92)93*:5*;5*;4+=7+@:-A;/E=1G?3IB3JC4H@4I@3G@2H@2G?2H@3H@3KC6MG7OH9PI:QG:PH:OH9NG9ME7MF7ME7NG8OG9QH:QH;QH;UK#M:!H7F6A2=/:.8+5(3(1&.$,#*")"' ' '$###"!$##"!"!!!!!    !"""#"##$ $# #!$!$!%"%#&$%"'$'$'$(%(%(%(%(&(%)'+'*'+( .( -) ,+!/*"/*!/) /*!0+"1,#1,#1,#2.$3.%6/&71(:2)<7+=6-?9.@:/?:.?9.>8-=7,<8-<6*;5+;5+<5+=7,>8-=8-?9.D=.F>2G>5JA5LD7LE6LD8KB9KD5JC5JB4KD5ME7OF9RI;RKRI;RH:QH;PG:PG9PG;RI:TJ;0*"      ! $ & * . 5':+;+O>%O$N:"H7E4A1=.9,7)2'/&.$,"*!)'%$#$     !!  !!!!"!!"#"##$!$ # %"%"%!&"%"%"&#'$(%'$'$(%(%)')&*'*'*( *',','.*".)!.)!/*!/*!0+"0+"1,#1,#1,#2-$3.%5.&5/'53(62(;4*=5+>7-?8.A;0B;0C;1D;/B:.@9-@:-@9.@8-?8.>8-@7,B9/A;0E<0G?2HA2KC6ME9MF7NG8OF8NG8NF8MD7NF8NG8PG9SJVN>VN>TL=VL=E>2        " $ (- 0"0"6&9(?.G4S?%TA$_O5ylRfY:kY:sa>whEuiHrT|WtN]`X¢^ֲfieh{ݽٸԴѰ̭ɬĦȺ~wtrmkkh~e{cybx`x`yaza|az`y^x]uYsVpUqUoSpSpSqSrSqSrRuSuSvSwUxWyX|W}XY[Z\__abeghlŸmƣoʦrΪtҬv֯y۴{޸~㼁濅ÈnjʏΓіԛמ٣ڧܩޮ߲۪ӘÅڬp]{Ic:cL.K:#8,)#   )>.WB#yZ3uEYޭoƆԙܮ޴ݫעӚϒʊ€xq٨j͟bė\UP}IwDoAh;a7v[3nT0hO,bJ(\F'T@#O<I7E4?/<,9*4'/%.$,!((&""!   !""""#""!!    !!   !"####$"$!$ $!$!%!&#%"&#&$%#'$(#)$(%)&(%)&*')')'+( -' .' -(!/(!0)"/)"0+"0+"0+"1,#2-$3-$3-$3.%5-&4.'11'30&90(:1(:1(:3*;5+>7,A8-C:.D1F>1F=2E=4C>1E:1C:1A<0B<.C;.DXP?\QAXP?+&   %+"-#5(6(3%4&:*>,A-A,L7YC'^L-k^DcW9fT1}hBsNmM{_xZvUkm_[ү_giq۹ٸԴѰɫ÷zvtrnli}g{eycwayawbxcxbw`t\~qY{lUxjSvhQtfOteOteMufMteLteKufLveLwfLweKwgKxhKxhK{hM|iL|jMlOmNnMoOqPsPvRwRyS|V~XWYZ]^abdgi kȣo̧rѫuְy۴{}异ŊʏΓҖ՛؟ڤܨޭ޴٠͏yɟdOlAkT3R@&>0-##   + @0^F%~a5|IŚ_sˌנ޵۪ןӖΎDžztتkΠc×]TM{IsBk>b8y[5qU2iO.^H(VC%Q>!K:F5A2=/9+5'2&+#+!(!!"          ###"#$ $!$"$"# # $$$ # """"$# $"$!$!&#%!%!%#%#&#&$'$'$(%(%)&(&)'*')&+&.' -' -' -' /)"/*!0+"0+"1,#1,#2-#1,#2-$3.%3.%3/&50'50'50'61(42(82):1(93(94):4*:6*?9,B7.A;.B=.F=0G?2IA3JB4JB5IA4G?2G?2G?1E=1E=2E>0G>1F?2HB5KF6MF5PI:RKUN>UM;TK=SJUL>XN@;8+      !'*"2(6*5'5'4&9)>-C/D/G2U?!iQ0eP.bM*n[7udC}oNyeDz`h}_r|l]Ϊ\citݻٶЮǨɽ¶|xtrqnk|h|f|e}f|e{dybv`}p[ylWxkUtgRqeOpcNobMmaKnbMl_Jk_Il^Ik^Hj^Hj]Fk]Fj]Fj\Ej\Em_Fm]Fn_Gm^En_Fo`Gq`Fr`FtbHudFwfFyhG|gKhKmKoLpMqNtPwQ{R|T~TWY\^adghàjʥoЪsԯuڳz~彃‡nj͐і՚؟ڥܫޯ۩ѕԧkWsGsZ6XD*B4!2('    . D2aI&c7Jʝaw͏ؤߵ۪מӕ̌Âw߯mҢdĘ\UMyHp@g=~^7tW2kP-bI)ZD&S>#J8 C4>09,4)0%-#* '%"               !!"$!$!$ %!%"'#&$&$&#%#'"&!&!%!%!$"$!%"$!%"%"&#&$%#'$'$'%'%*$)%*'*'*'+( ,&,&.(!-' /)"/*!/)!1+#0*"0+"1,#2-$3.%3.%2-$60'5/&50'60'70(71(82)92*94)84)93*95*<6,=5+>7+?9,@9-A:.C<0E>2G@3HA4KD6LE7ME7KD4KD3KD4JB4IB3GB1IB3JA5KC5MF5PH:RK;TL=VM?WO@WOAYQAQK:      # (2'4'2%8)6%;)D1I5F1K6\G'rX5pT.cG y`;~kH}mM~nQ~adqzlȧ]ܶahrٹίŧʿǺŹ·|{vsqomllji~hydu`zp[xmXtiVrgTreSocPncPl`Mi_Kg]Ih]Ig\HeZGfYFeYGcXDcWBdXCaVBaVBaWAcXCcWCcWDcVBcVBdVBeVBeVAgWAfY@fY>h[Ak\Bl[Bn]Bq_Cr`DtaDvcEyeE|fF~hGjHmJpLsNwPzQ}SUYZ]`cgŠk̥pҫtرyݵ|仂njΒїלڢکݯݯ՜Ň٭q^yLz_9^K.G9#6+(!   /!G4dK(e8K͟bxΐ٦ܮ֣ӗ͌€u۪k͞cXQ|IrBg:|^6qU/hN-]F(V@#L; F5@1:+5'1$+ )%!                       !!!" $!%"$!&#&$)$*$*$,&+$*$)$)&'$'$'$'$'$'#&$'%'$'%)$*$)&*'*'*'+'-' -' .(!.(!/)"/)"/("/)"0*#0+"2-$2-$2-$4.%3/&7/&80'50'71(91)72)71)91)73(84)83+:4+<6+<6+>8->8-=7.?8/@:0@:/A;/B=0E?2H@3KC6MF6NG5OH9PI:OH9NG6NG7OF9ME5LE4NE7ME7OG:PI:SK:UM,J6O: E0L6iR5{aC~b?qS,xX,f9j:~i=wUvzuĦeѰ^cl{նͯȫçʾķ~|xusrqppnk|gxd}q_xn[tkXrhVqfToeSncQlaPk`Ni^Lg]Kf]Id[GdZHbXFbVD`VD^TB_TA^SA]SA]SAZQ>ZP>[P>\Q@ZP?ZP?[Q?[O=\P>\O=\P=]Q>\Q=]R<^Q=_P=`R=aSeU=hW>jXXO=XNn[?q]?u`AwbC}gEiFnIqMvO{RTW]`dÞi̦o֭v|꿁ȊД՝٦ޯ߲֟ȋܯr^yK|`9^I,F6 4(&   + B2]G%~`4{GǙ^tˌס޴޳إҘˊ{nϞbXNtEj<|]5nR0bI)WA%M9 E2=-5(/#, &#                                !! "#""##$!%#%!&"&!(#)$*%+&,( .(!.)"0*#2*#/)"2*$/*".*"/)!1)"/)!0(!/)!/*!.) 0*!/*!/*!/+!0+"0+"1,#1,#2-$3.%4.%3.%6/&5.%50'70'92)72)82):3*;5*;5*;6*<5,:4+<6+=7,=7,>8-?9.@:/A;0@;0C<0C;.D<.DUL=VO@VO@WN@VM>UO>82(       " % ) + /4#7%:'@,G1R; [C&aG'[@cI%tV.|[-{Y'|X"["_"c$q=szgƣ^ө[۳[__dqݿٺԸҴͰħȽ~zuql|hxfvd|sa{q_yo]wm[tjXrhVpfTogTkdQlaOkaOj_Mh]Ke[IcYGbXGaWF`VE]TC\UD[RAZS@XQ@WO>VO>VN=WN=VN=TK;SJ.XB"xZ2uDZޮnƄӚܭ߱أДdžwצhĖZOvEg.5(0$+!%                                  ! !"!!""# "## $!$ $ $!%!%"&#'%'&'&)',&.(!.(!1+#2,"2-$3.%5.%3.%3.%4.&4/&2-$4,#3-$2-$2-$1,#1,#1,#2-$2-$3.%3.%6/&60'4/&60'71(:2)93*93*92):4):5*<6+85+96,>7,<6+=9->8,@:/?9.@:/A:/B:.D/E?0E>0G?3H@2HA1IB3IB5LE6LE6OF9RH;SK;VM?YQ@UP>'"       # )+ .1"7&:(>(A-E/H1Z? kN.aB_BnL uR!uQ|V W"b$g&o/QqsţbѧRݮSPWYaiuټҶ˯èƻ}zuqm}jzixf~vc{tazq_xn\vlZtjXsiWqgUnfSlfRkdQj`Ni_Mg^Lf\KdZIcYHaWF`VE\UCZSBZRA[Q@YQ@XP?WO=VN=UMwaA~fElIrJyMSZ`Ɵhӫq{ŇҔءݯڪҗթmXsGuY5XD)B43'&  " 9*U= pS/l>Qҥg}В٦٥єdžuԢdU~Hl=}^5kP-[D&O;!C2:+2$,!&!                                  !  !!""### #$ $!$"%!%"%"%"%"%"&$&#'$*$*$)%*&+'+( .)"/)!0*#2,$3.%40&4/&71(61(71)71(:2)82)81(70'82'61'70'81(81(60'50'82)81)92)92):2*:3*:4+<4+<4*=6,>8->8,>8-?9.?9.@:/@:/@:/C:.D;.D0E>1F?2G?2IA4HA3JB5JB6JB5KD4LE7NE8OF9OH9TL>E=1      " '+-/!9)<*<*A+E/D.K3gK,uY8gG#hGoKsNwO{R ~X a%j&r-}<[dѬZޱQOQTX_fpپѵɮ§ž}zvspm}k|hwe~uc|sa{rayo]vlZukYsiWqgUneSmdRlcQkaOj`Nh^Le[Je[JbWJaWG`VE`TD^SB[SBZR@YQ?WO@VN@UM1G>0H?1H>3F?1F?0F>0F?0G=/F>/I?/JA0KA0MA/NC0SE2TF2WJ2[J4aO8cR9jV:qZ?u`AgDnHwMTZeӪpzȉ՗ڥߵ٣ϐ|̠hRnBmU3TB'@20&%   2%J7fK(d7Hř\rȆ՚۬ڬԚʈvԤeUzFg9tV/bH'Q5+>5+>6,=8+=8-?9.?9.@:/@:0A;/C;.C<.D1F>1G?2G?2H@3IB3IA4JB5KD5KD5MF7MF8NF9PG9PH:+&    ! & *- 0"2#9):(A,N9Q;I1P8mS2pR/eFiHrO wR {S V!Y"`&k)s,~6LʦYڲUPPSW^dlwٻеɯĩý¸¶}zxuspm~k{gvd~tb}r`{q`yo]wm[tjXrhVpfToeSmcQkaOj`Ni_Mg]Lf\KdZIcYHaXG`VE`SC^RAZRAYQ@VO=WN?WN@UMTK>SK:RJ:QH:QH:PG9PG9NG8MF7LE6KD5KC5JB5JB5H@4HA2HA2G?2F?0F>1G>0E=/D-K>/L?0MB/OC1TE3VH3\L4cP6iW:q[<{cAkEuJR[şfگsБןݯܬ՛Ƈްs_~Kd8-=9.?:.?9/?9.>9.=9.=7,>8,?8.>8.=7->7.=8-=9.?9.@:/@:.?9-@:-A;.C;.D1G?2H@3H@3IB3JC4KC6LD6MF7LE6NG8OH9RI7E>1  ! & */!2$4$8(B0G2S=#YD([C%]C%dG%eE fEmIqMzQ!V#X#]%a%k)v,2ÐAңMSUY`gnt}ؼѵ̲Ưĩ¦ɾȼ¸}zxtqpn}k|hxfuc~tb|r`yo]wm[tjXsiWrhVpfTmcQlbPkaOj`Ng]Lg]LcZIbZH_WE^UD]TCZRAYQ@XP?XP>WN@WN@TL;UM=TK>SI0E=0E<0D1D>0D=0D<0C<0C1F>1F>1F=1F=/E>.DiQ0Q?&@12'$  , @.V@"qU.k8+A:-C;.D1G?2H@3J@4JB4JA4JB5IA3IA3H@3H@3G?2G?2G?2H@3H@3IA4HA2JC4KB5KC5MF7MF7NG8PI:ME8#   " " % ) + 3$9)>.G4N9#T?'V@']E)fL/rW6xY5oO'pM!vQ"|T#W$]&`'b'g*k+v.0Ç2Д7ڤAN\jyپؼ׼Ӻеɰĩż}{wurom}k|hygve~tb|r`zp^wm[vlZtjXrhVpfTndRlaOj`Ni_Mg]Ke[IdZHcYHaWF^VE]UD[SBZRA[Q?XP?WO>WO>VN=UM;TK1F?0F>1E=/G?.E?0E=/D>/D=/E>0F>0G>/H>0I?/KA/NC0OC1RF1VG3[K7^N7eS9lX8-?9.@:/A9/D;1F>2H@3IA4JC4MF7NG7MF7NG8NG8NG8MF7LE6LE7NG8MF7LE6ME6LE4ME7OG8QJ9E=1   # ' ( + 0 4%A/I6 O;#R<"S< V<`E$qS1wW2nL#oL!xP#~U"Z%^&c'f)i)l+t+|/Ç3ΐ4ڛ;CO^p׻жǯŻ~{wuson~k}hyfvd~tb|r`zp^xn\vlZsiWqgUoeSncQnbPkaOh^Lf]Ke[IdZIcYHaWF]VE]UD[SB\RA[Q?XP?WP?WO>VN=VN=TM;TL;SJ:RI;QH:PG9PF:PG9OG7MF5LE7KD6LE5KC2JA4JA4JA4JA4KA4I@2J@1I@0JA1KA1IA0K@/KB0JB0KC1MC3QE3SF4VG5XK5[M6_P7dT8jV10&'    )5(K8bI'|[3o@Mř]ݪkzˊҘآݯۭӛʈtϞ`OrA}]3fK(S="D37)/"("                                      !! "!""""###$ $!%#&"%"%"&#%"&#&#&#'%'$($)%)&(%*&*'*' -' /(!.( .(!/("/)!0+"0+"1-"0+"2-$1,#2,%3.%4/&4/&4/&81(81(50'71'92)92):2)93*:5):7*;5*;5*<6+>8->8-?7-@8.A:0C:.C;.D=/F?0IB2JB4KD6MF7OH9OH9PI:QI;TJ=TJ=RI;SJ;SK;SIWP=VM>VN>VL=TK=SJ;SJn\@r_CvcE{fFkInLsMxQ}TX\agɢm׭s|Ĉϒ֝ܩް٥ԙ̍ۮrśbUvHbKY֦fuŃϐԝ٧޲ݰԟ͍z٨iY~Kk=xY2eK+U?#G6>.6'-"&$                                       !!!""""####$$!$"%!%"%"&"%"&#&#'$'$'$)%*$)%)&*&+&-' -' -' -' -'.) .("/*!0+"0+"1,#1,#2-$3.%3.%3.%3/&20&90'90'51(72(92):2);3*:3*<4+<5+<6+<6+<6+=7,>8-?9.@:/@;0@:.B9-C;.C<.D0F@0HA2IB3JC4LE7PH:QI:QJ;SL=UN?VN?XO@WM>&$ ! & * - 1!4#9&?*E0I2M4U9X<_@dClHsO!yS!~V#[%a'f*i*o.t1v0:8Lj4͗B֜@ۘ7=DO_tչжʳǰʼŹ~|yvsqn~lzhwevd~tb{q_yo]xn\vlZtjXqgUpfToeSlbPkaOj`Nh^Mf\Ke[JcZIbXG`UD]UD[SB[TAZS@YQ@XQ?WP=WNAUM>VM=UL=TK;TLhX?jY@k[Aq^CuaEwcE{gF~iGkHnKqNvM{PUY]afşkͧoذv}ˍӘؠܪݱڧ֞Вƈzըm`SuFc;jR1VC(G7!8,-#%  ! -!=,N9aH'wY1j<}IUΟbn|ʉѓ֞٨޲ݲףѓărϠbT{Hk=|]4kP,]E'Q<"H4?/8*3&-#'%!                                    ! !!!"#"""### #$$!$!%!%#$!&#'$&#&#'$'$)#*#*&)&*'+%-&.&-' ,' -) .(/*!/)"/*!0+"1,#2,#2.$1,#0-#1/%4/&3.%4/&80'90'61(82)92)92)92):3*:3*;5*=7,=7,=7,=7,=7,?9.@:/?9/@:.B<0D1F>0G@1HA2H@3IA4IB3JC4LE6NG8OH9PI:TL>JB6  ! & + - 1! 5$:(@*E.I0P6[? ^A ^CdGmN$sS%xT&|^-n֤IݥG?=DOczؽӻϷ˳ǰ¬ɽź~|xvsqm}kzhxfvd~tb{q_yo]yo]vlZtjXrhVqgUoeSmcQlbPi_Mg]Lg]Le[JcYH`YF`YF`UE^TB]TA\S@[Q?[RAZR?ZR@ZQ>YP=YO>XN=YO>XN=WM\O>^P=_Q>`R>aT>cU?eWAfXBiZCl]Cm^Do`Dq`EsaFwdGzeF{gH~iIjImKqLsMwN{QVXZ^chȡlϨqدw|龂Ƌ͐Әؠڨްޱڨ֟ҕˋޱr͢g\QqD~a:iR0WD)G7":--%&!    ! -!:*K6^E&qT.e7vDPĖ[եgs~ˋЖ֞٧ܰ߸ߵ٨Ӛˍ}ޮnʝbU~LqAe9wY3jP._H(U?#L9 D4>.8)2&.") &$                                ! !  "!"""### ##$!$!%"%#$"%"&#&#&#'$'$*$+%)&(%)&)&-( +&-' /( /) /*!0*#/)#/)"0+"0*#2,$0.$1,#1.$2.%4.%6/&70'80'80'60'82)92)92)92):3*;4+;5*<6+<6+=7,=7,>8-?9.?9.@:.?9,A:0D1F>1G?2H@3H@3JB5IB3JC4KC5LE6MF7NG9PG9=7+   " ( + /3#9&='@*G1M6S7\?eG#cH#hN'nS,xW,~Z*f4{Hs7r0x1y-|12ą5̐=ԗ?ڙ=ۛ@MF=AHYk׿ӻζ˳ǯìȾû|xurpn}k|jygvd}sa{q_zp^wm[vlZtjYsiWqgUodRmcQk`OlbPi^Mi]Lg[Jf[HbZFbZGeXFcWCcWCaUB`SAaTB`SAaTB`T@`U@`U@`UAbVBdWCeWDfYDfZDh[EiZFk]Gm_Gm`Go_GpbGqbGtcFvdHwdGzgG{gG~hHjJlKoKrLuNxQzTUW[\_eiơļqԭwڲ{⹀Ɗ̎Е֞٥ܪޱܭ٦מҕ̋vԨl™aU{KlBy^8gO/VC(G7!9-/%(       )6(G4W@!jN*|]4l>{GQș]بhs}ɇВԛأܫ޴۫ՠѕȈ|ݮo˟eZPyHoCe8-?9.?9.@:.@:,B9/D;0D`D!hN)pN%sP&Z*^)b(f*k,q.x0{13Å5ą3͌6ۖ;ݖ8ߗ:>@@CGQe~ԼϷ̴ȯīļ~{yvrqom{iygxftc~razp_zn]yo\xmZukWthUsgTqeRodQncPlaNm`Mk^Ki^Kg^Ii^Ii\HjZHi[Gi[Gk]Hl^Il^Jn`Km_JnaHocIpcJrdKsdKreKueIvfHygIyfJzfJ{gJ|hL|iK~kLkLlLoMqOtOvPyQ|TWWZ]`cfhĠmʤqЪtװxܵ|㺀鿄ŊɎϔә֞ۥܩޮ߶ޯ۪ע՜ВɈvөlƜaYPqEd=qV3_K-Q?&D5"9,/%' !     '5%A/P:aF'qT/b6p?JUə^ئgq|ȆΏҖ֟٦ܬ޳߲۫עӚΐŅ{ޮqТgř`VPzIpCg<~`8tX3lQ/dJ+\E(U?%O;#H7 C2=.:+6(2&.#*) &##                        !!!"""#"## $ #$!%!&!%"%#&#&$&$&#'$'$(%(%)&(&)&*'+'-' -' .' /' -' 0+#/+$0+#0+"0+"0,#2-$3-$4.$3-$5/&5/&6/&6/&70'81(81(92)93*82);4+;4+;5*=7,=7,=7,>8-?9.?9.@:/@:/A;0B9/C:0D/G@1F>1G?2H@3IA4IA4JB5JB5KC7ME7MF7OH9NF7!   % -/ 2#7%=(B,D.J3Q7W<^F&cL,nX3rN!xR"W#])a)f*l+s0w0|24ɇ7̋9Ғ:ܕ:ܖ:;;=?EJTe|ԼиʹɰŬǿû~|zvtrnl~k{hzfwdub}r_~r_|q]{o[zmZylWxkVuiSvjTthRrfPtgPugPsfOugPwhQwgQvfQxhRyjQzlP{jP{kQ|lP|lQ}lP}lPmPlQlOmNmNnNoOoPqNsNuQwSyT|U}VY[[]`aeižlƢmʦqΩtԮxڳ|޸~佃‡Ƌʎϔә֞٣ڨݬޱݯۨ٣֜җ͎Ƈ|߲tӧlěcYQvHi@x\7hP1YD)L<$@36,.$' !      $ 0":*I3V?"eK(uW0d8rAJTƘ\եenyĂˋЏӘ֠פک۬ܮܯۮ۫٧֢ԛѓʋÃ{߰sԧjʞd]UPzKsFkAc;{]6sX3mQ0eL.]G*XC'S>&M:$H6!C3?0:-7*3'0%-%+!) & %#"!          !!"""###$ $ $ %"$!% %"&#&#&$&$'$&$(#'%(%*&(&)&+&-%.&-&,' -(!.) .+!.*"/,"1,#1,#0+"2-$4-$4.%3.%3.%4/&6/'70'70'81(81(92):4+92);4+;5+<6+=7,=7,=7,?9.@:/@:/@:0@:0B;0C:0D;0D/E>/G?2H@3H@3H@3IA4JB5JB5KC7ME7LE6OI9JC5   ' 0"1!2"9%>*C,F0K4O5W<[B"iP1v_;vS&~V'Z'^'d*j,o.v1x2~3Æ6ʈ7̍>ٚFۖ;ݜADECACKYlԽζʹɰŬȿż~|zyvsomk}j}h{eycwcubuau`u^t^t]s\s\s\s\u\u[uYuZtYsXsWrVrWtUrSrRsQrRsRuRuRvSwTyUzW~YXYY[^_acehhkġoɥrΩsӭvױzڴ}ก佅ƌɎ͒ї՚ן٤ۧܫޱ߳ݮ۩ء՝ӗΐƈxڭp͢g`WNwIj@z^9lT2_I,Q?&E7#=02(,"' "     $ (2%@.J9XC"hL+wY2f8p@|HP”YОcکkszłˊϑҖӛ֢֝֠֠֟ԛӗє̏lj{uثoϣhǛb]VQ}LvGpDi?c;|^9uX5mS1hO/cJ-^G,XB'R?"N<#J7"E6C4>0:.7,3(1&/%,#+#* (&%#$#!    !! !"#""# ##!# $$"%"$!%"&#%"&#&#&#'#(#(%(%(&*%,$-%-%,&+( ,' -'!.)!/*!0+"0*"0+"1+$1+$1,#1-$2-$3/&50'4/&4.'50'62)81(92):2):3*;4+:3*:5*;7*;5*<6+<6+>8-?9.@:/?9.A9.B;-D;1D;1C1F>1G?2G?2H@3H@3JB5JB5KC6KD7LE6MF7PH9B>1    ( /0 4#;'>)A,J1K3S@#ZC$[@fN,{_:|\/Z*_-d-f-k/q3z7;>ƈ:ʉ6я@ݞMޞELFFJLOQ]uԼи̳ɰūļ¹~|zuqonoonomkiijggfdc~b}a}`|^|^|^zZyYzXzY{Y{Y~XYZ[\^^`abceggiloĢpŤpʧsΪvүxײ{ڵ~޸⻃ÉƋʑϓЗԛמ١ۤܩܭްߵ޴ݰܫڥؠ֜є̍Ƈyۯrҧmǝe]V~OuHkB{a;oX5dM1VC+K<&B5"9/3*,%("%!      #+4&>.K6WA"eL(sV.a7l>wELTŗ\Сcڪjty€DžʊΏВГєѓϑ͏ʌƈ~{u٫pҦk˞eĘ`\XSPzJvEpDjAf=`;z\:sX6nT1hP0bL-^H*ZD*VB&R@$M;#I8!E6B3>1;/9-7+5)1&0%/#-",!*!)!'%$$"""!       !!!"#""###!# $!%#$!%"%"%"%"&#&#'$'$'%)&)&(%)&*&*&,' ,&-' .' .(!-(!.) .)"0+"0+"0*#0,$2-$2-$3.%4.%5.%5/&4/&61(61(81(80';1(92(:4):3*;5*=7,<6+=7,=7,=7,>8-?9.@:/@:.A:-C;/C;0D1G?2G?2IA4H@3JB4KD6KD5LE7LE6NG8PI9:6*   *1!2"6$<&@+H3J1RA&YI,W>^C"kO,tV0]4_0c2j8p:p:t;CIđNʍ@ˋ;ӑCߞNSODMOTX]g}սҹε˱Ǭêļ}{|zzxywvsoonkjigfdbcbabaabcccdfghiknop¢rţsȧt˪wΫxѯz԰|״ܷߺ㽆ċǎʑ͓ϖљ՝נ٣ܥܨެ߰޳ݭܩڦؠ՚җϑʌą뾀{ڮsҧmȟe_YT{NrHhAz`1G?2G?2H@3H@3IB3JD4KD5LE6LE6NG8PI:5/&   ,4$3$7%;'?,J2L6UF*ZC'[B"gJ)oN(wU,[1`2d2l8q=u@HJHÕRϓGҏ>ؖEߠOXNFPVY\`n־ҺϷ̳ȭë§ƿ¼º{yvuroopmmmlmkjlllnonnoprrĥsŧtȩvʫyέ|Ұ~ӱ~شڵݸ߻⾈‹čǑʒ͔Иқԝנڣ٤ۨݪޭ߰ݱܭۨڥן՚ӖБˍŇ|޳w֬qϥjŝe`[U|OrGkB~c=rY7hQ1^J.UB)J;'B5";/4+.&*#%!        %, 5&@-I5T>!^E'hM,tW0b8j=tC|IPU•[˝bУfةkޭptuxyz{zyw߱uݯr٭qթnѥl͢iɟfĚc`]XVSQL{KxJuFpCmAi?e<a:z_6wZ6rV2nS0jQ0eM-bK,]H*[E)YD(UA&Q>$O=%L:#H8!F6 D5A3?1?0=0:.9,7*5)4'1'0'0%.%,#,#+")"' ' ' &&$"""!!!   !    !"""""!"!"" " "$"$!#%"$!%!%"&#%"&#&#'$'%(%(%(%(%*'+%*(,' +%,&-' -' -' .(!0)#1+#1,#0,#1,$2-$3.%2-$3.%4/&4/&50'50'61(81(92):3*:5*:4*;4+:5+96+=7,=7,>8-<8->9.?9.@:/A:.B9,C:0C:0D1F>1F>1H@3H@3H@3JB4JC4KD5LE6MF7NG8MF7*%   *2"5%7%<(A,E.N; VB%_C%dI*kM*pO({V,]3a3e4n;s?xAILŠIǔQԚNؓCۗGUXPKPWVY`nٿջӺжγ˳ʯǭŪè¨ʽȻƹö~{z{yxwxwxxwxwvx¦yŧyǩ{Ȫ|˭}̭|ϯ}ұմ׵ٷڹݻᾉŒÍŏȒʔ̖ЙњӜ՟סأۦۧݩݬ߯߳ޱݭ۪٤؟՛ӗђ͎ȈÃ쿂|߲x׭sЦkɟgb\VPyLrGjA}a=sY7jS4aK/VD)O>&F7$?2 8,3)-%("$"       $ *0%8*B0L6X?"`F&jO-wX2`6h1;/:,7-6,5*2)2)1'/&.&-%.&,%+#*#)")"(!)!'!&& &&%& & %&$$$% &!&!%%%$ %!$% &"&!$ $!#!$!%#$"%#%"%#%"&$&$'$&#'$'$(%)&*$*$*(,' ,&-' -' .(!/)"/)"0("0*"1,#2-$1,#2-$4/&3.%4/&4/&3.%4/&50'71(92)92):3*84)94*:3*:5*;5+;5*<6+=7,>8-?9.@:/@:/A;0B:/C:/D<0D1G?2F>1F>1G?2H@3IA4JB5JD4KD5LE6MF7NG8KC5    +2"8&:'?+E.I3RyE~GJKŌI̒OڟVߛKHPQQQPQUX_hxۿؽԺӸеͱǭƪħ̽ʻȹŶ´²«êëĭŬƫǭȮ˯̰αвѴԶ׸ٸۺݽ߽ΉÍďƑɓʔ̖ΘКқԞססأڦۧܨݪެ߲ޯܫ۩ڣآמԚѕϒ̎ȈÄ}wۯsԩnͤkƝfb]WR|MuInDg@|`sBzH~LOTXZ\Ø_Ś`ƛaȝcȝdȝdǛbƚbębřbØa``]\[[XVSRQQ~N{KyJxHsFrEnCkAi@g>d=~a;{^9y]9v[7sX5pV4nU4jR1gP1gP2eN1aM/_I.]H-ZF+[G,XD+UC*SA(RA'M>&M=&I:$I:%G9#D7#C5#A4"@3"=1 :/9/9/9/8-6.5,4*5-4,3)3+2*1)0(1(1'/(.(.'-&-&,&,&+'+'+'+$*$*%*&*%*$*%+',&)$)$($($)&*'*'(&)'('(&)'(')&*'*()'+'+&+%+( *( .(!.(!-' .)".) 0*!1(#1+"0+"0+"1,#1,#2-$3.%2-%4/&4/&50'60'70'91(92)92)73(82(;4+<6+;5*;6+=7,>8-=7,>8->8,?:.?:/B1H@3H@3H@3KC6IA4KC5KD5LE6MF7PH9LD6  .!3#6$9&A-I3M7S=!Z@$`E&gI'sQ-yW/~[0^3d6i8m;xCHJH‹F̒LٝVQHIINQMOWX\etؼպҶϲ̮ȫƩĦĢοϿͼͻλ͹͹̸̷̹ͷ̸ͷϷѸзҸӹҺֺٻؼٽ۾’đƓǕȕʘ˙̚ΘМҟӞՠעأڥۧݩܪ߲߫߮ݮܬ۩ۦ٤ءםԙӖВ̎ɋLJƒzvڰrӪoϦkȟgÛc^[WR}NwIpDiAc=w]:nV5gP2`K/WF*QB(I;%C6#>2!7-2(/&+$'!$!       #)0#3';,D2L8S>!]E&gL*mR.vX2`6e:k>qBvEzJLOPSUWYZZZ[ZZ[YZYXWWUSQRPO~N{JzLxKvGsEqDpCmBk@i@g>e=b;~b;z^9x]8w\7uZ6rX5qW4mT2kS3iR2hP1dP0cM0`J/]I,\H,YF+WE*UD*SA(Q?(M=&L;&K:$G8"E7$D6#E7$B5#@4#A4#?3!>2"?2"<1 <1";1!90":/!9. 7. 7.!7.!6- 5- 4,4+!5,"3* 4+!2,!1+ 1+2,1+1+0+/+ .*/,2-!0- /,0+!0+ 2,!2,"3,#2,#1+!2,"2+"1*!2,"3+"2-#1-"4.#4.$2,#1-"4-$2,#3-$6-$3-$2-$0-$3-$6.%4.%3.%6/&5/&4/&50'81'82'82';3)93(93(93(;5*;3)=4*;4*;4*=6+<5+=6,>5+=8-=8-@7-?7-?:-B8.A9/C;-D;.E=0De=c<~b:~a<{_:x^9w\8v\7sY6qV5mU4lT3jR3hQ1gQ1dN1aK/_I-[H+YE+VD)TB(SB*Q@'N?'L=&K<&K;'H9%F7%F7%D6#D6#@4 ?4"@3"?2!>3"<0#<1!:/ :0!9/!8. 6-7- 7- 5- 4,4,4,4,3+2+3,3,!2+!2,2+3,"3,"3,3,!4-"3,!4-"3,"5-#4,#4,#5-$4-$6.%5/$5/$5.%5.%5.%70'81(7/&91(71(93*72)83)=3*83*72);3*:4+84+:3*;5+<6+=6,>7.>8-?9.?9.?:/A9.B9/@:/A;0B<1A;0C<1E;0D<1E>2F=1D=0D>1F=2G?1H?2I@3H@3IB3JC4KC5KC5LD6LC6KB5LD7NE8NF7NG8OH9QI9RK;JC4   -4$8';(@,E0L7U;]?"aG'iQ.sT.zW.[/_3c3f2n6v:}@BDċF̒K֞SRHJNLNNKPTS\mٽٺ׹նԴӲѱѱЯѭήϫϫΫϩϪϪϩΨΨϦШЧЦѦҦҧҦӨԨשש֪ث٫ګܭݮޯޯްޭݬݩڦ٤٢נ֝ԛҙҕϒ̏ɌƈĆ齀|y޲uٮrөoΥkȡhĝd`_ZWRP{LvIpFlCg@|b=u]8pX6jT4cO2]H.WD,RA+L<(E8&C5#=1!8-5+1(-%*#("&$!            ! #&-!2%9*>.B2I6Q<"XB%^F'bI+iO.pT1uX3y\5`9c:f=j?lAnBpDsEuHwHyIzL|K|M}L~M~M~M~O}O}O}N~L|K{L{MzJzKxJwIwIuGtGsFsGrEoCoCnBlBkBjAiAh@g@e>c=c=~`<{_;y_:w\8u[8sY6qW6oV5mT5hR2fP2dN0cM/`L/`K0\I-[G-ZF+XE,UC*S@)Q@)P@(O?'M>'K<'I;&I9%H:&G9$E8%C6$C6$B5$@4"@4">2"=0!=2#<1"<1!:/!:0"90!9/!8.!7.!7-"6-!7.!6-!5,!6- 6/ 5-3+4,4, 5,!4+4+3+!3,!4+!3+!1,!2-"3,"4,#4-$5.$5,#4-$4,$4.$4/$50%70%60'70':1(91(71(81(82(:2*:3*;4+;7*<7+<6+=7,>8-?8.?9,?9-A9/A;.C1H>1E>1FA4JB5IC4KE5KD6LD7MG7NG8OF8OH9PI:PG9QH:RI;SJVM>YQ>MF7   ! / 5$7&:'C4H7L3Q5W=aE#hL(qS-{X/Y-]0e4h4k2s7z<~>EČH˓LآWOGHJHJKLOSU^oݾܻڼٹڸظ׵ش״ײײײױױװٰذدٯخٯٮ٭ۭۮۮܯܯݯްް߱߱߯ݭޫݪۨڧڦ٤ءן֞ԛӘҔϑ̏ɎȊƅ꿂|zܱy׭uԪrЧọkȠhœeb_[XUR}NyLtHoDlCh@}b)G9%B6#>3";/ 6-3*/',&+#)!%#!            "&).#4&9*>-C1I6N8 S>#XB%]F(cJ*gN,mQ/pU0tW3x[5{]7`:b:ec=}b>|a)N=)M<(L<)K<)I;(G9&F8&E7&E7%D6&C6'B5%A5$?4$>4$>4$=3#=3$<2";1#90$:2!92"90#90"90"80#7/"6.!5."5/"7."6."3.#2-"3-"5,"3-#5-"5,"3,#3,#4,"3-"5/$60%4.$4-$5.%6/%5.%5.%4.%3.%5.%70'51'60'81(72)82(92):4(:4)<4+<6)>7*?7-?9->8-B;/A;.@:-C=2E=1E>0E>0G?2HA2IA3IA4IA4KC6LD7MD7ME8OG9OH9PI:QJ;QJ;VN?HB4     .6%9(=*D4H5N5Q7X> ^C$jN+sT.|V.Y-]/b1g2j2q6z:=CŌF͓J٠TLFGIHKKMPTW_u޿޽޻ۻ۸ڷڶٵٵڴڴڲٱڰ۱ڰ۱ڰ۱گܮܯܯܯޯޯ߲߲߰߰߰߮߮ޭݬݪܩܩۧۦڥ٤آסן֞ԜӘҗϔ͓̐ˎȌŊÈ较}޵z۲xٱvԬsѩqͦoɢlƞhÜeca]YWSR~PzNuIrGnDjBe?zai?j@lAmCmBnAoCoCoCpDpDpCpCpCpCpCnCoDoDnCmBnCnBnBmCmClAjBj@jAh@f?e>d?}c=|a<{_*M>*J<)J<)J<*H:'H;(F:(F:'F9'D7'D9%C6&A6(@5%A6&@5'@5&=4%=4%<4%;3$;3$:3$92#92$91$61$70"7/"60$6.#7.$7.%7.$7.$7.$5/$5/$5/$6/&6/%5/$5.$6/&40&40'5.&5.%50'60'81(61(71(92):4)<5*=4+;5(;5(;5+:6+;7,=8-@:/?9.@:/A:.B:-B:.D1G?2H@3H@3IA4JC5JC4LE6MF7LE6PH:@9-    ,4#:)=)A,G2L4T:\D&_J(pY4pO%xR%~V'[)^+e-j0p4x8:=ƊAΖKڡUSIGIJJKNPT[dx߿޾ܼ޺ݺ۹۶۶۶ٵڴڴ۳ڲٱڰ۱ڰ۱ۯڮۯۯۮܯܯܯޮޯ߰߰߮߮߮߮߮߭߭߭߭߮ެ߭߭߭ެޫޫݬݫݪݪܪܩܧܦڦۦۦ٣٣آءן֞՝ԛԙәїϔΓ͒ʏɍnjƊĈ辄罃廁~߶|۳yٱw֮uӫsϨqͦoɢlşiÝhcb_^[XVT~Q{OxLtHqGlDiC~e@yb>v^$UA%WD&\F)_H*bK*fM-iO/lS0oV1pV2sX5tZ7v[8z]8x\6|_:~a:c;d;ef=g=h=h?i@i@i@kAj@k@kAkBlBlBlAlBlAjAjBjBh@gAf@f>d?~c>}b=|a<|`=z_;y_;w^:u\8t[9s[8pY8pX7oW8mU7kU5jT5iT4gS3eQ2eP3dP2bO2aM2`L1_M1_K1\I0ZH.ZG/YG/WF-VE.TC-RB,PB+PA+P@-O?+N?+L=+K=+J<*K=+J<*H;(H:*E:'F:&F9(D9(C8'A8'@8&@8&>6%=6%=5'=4&<3%<5&;4&;2%:2%:0%=1':0&90&9/%80$71%71&60%60%71&61%60%61%50'6.&60'4/&70'81(50(82)92):3*;3)=3);5*<6+;5*>7,?8-?9.>8-@:/>9.A:-C;-C:0D;1D1G?2H@3H@3IA4JB6IA5KD5MF7NG8OH9?9-    -5$9&>(B,F/I0Q:\G'gQ-jM&lJ vN"|S$Y'_)b*j/p3u6~8;Ɉ>ˑFڣXZMIHLLMPQW_m}߿߾ݼݺܹ۹۷۶ڶٵ۴ڳڱڱٯٯٯٯ٭٭ج٭٬ث٬٫٪٩۪ڪڪک٨۩ڨ٧٧ڦ٦إڤ٣أأأآס֠֟՟֟՟ӞӞҝӜқљҘИϘ͖Ε͓ˑɑȐǍƌĊȇ濄佃ề߹޷~ܵ|ڳzذwկvѬtϩşrʥpǢmğkžifeca_\YVUS}PzNxMtKqHnGiEeA|cAza>v]3#;1"9/ 7.4,1+0(/',%*$(#& &!$%"!!                           #&),!0#2&7(7):,?0C3E5H6K:!N<"R>$U@$YD&[E(^G*`I+cJ,eL,eO-jQ0jR0mU0qV2sW3rX4tZ4v[6w\7w[7x\7z_9{_9|`:}a;}a<c=b=ce?f@e?f@f@e?e@d?e?~c>}a>|`=|`={`=z`6(?7(@4'>4'<3&=3&;2%<3&;2&;2&92%92%90&8/&70%71&71&72'50%80'80'50'71(91(51(92):2):3*:3);4*;5*;5*<6+=6+>7,>8-?9.?9.@:/@:.B;.D;/D;/E=0F>0F>1E=0G?2H@3H@3H@3IA5KC6LE6KD6LE7PI9?9-    .4$9&?*D-G/I3Q=\A`DfFnIvO#}T$Z']*c*i.o3v6|9:ȇ=͍BޡSPJJJJNQUU\eq߿ݼۻܺڷٹٷٵص׳ְֲ׮֭լԬիիӪԧҦӦӥҥҥӤңҢӢҠѠППџОϝΜΛΙ̗͘͘˖˕ʕʔȒǑǑƏŏďÍ͌俈㾆㽆Ễ޺޺ݷ۵ٴ}ٴ}ױ{԰xӮvҭuЫtΨrʧpɤqƢoàmjihfdaa_][YXUT|QyOvNtLqJoHkFiD~fCzc@v_>s\=pZ5";2":0"6. 3- 2+ 0)/(.(+&*%*$)"%"$ $ "##!!   !                        "#&(*"-"2%3'6(8+;-?0@2C4F5 G7J: L;#P<"R@$U@%XD(ZF)]G+`H+_J+bL,eN-fO/hP0jQ/iS0mT1oV3mU3oV4rX5tX5tZ5u\7u\7x^9y]9z]:z]:z_:z_:{_<{_;z^:{^:z`*I>*H<*F;)E;)C:)A9(B8)C8*B7)?5'>5(>6)=6)=4'<3&<3'<3(;3%:2%:0&81&82'82'71&71%81(82'50(71(70'70':2):3*:4):4):4);5*;5*<6+<6+>8->8-@:/@:/@:/@:.?9,C1G?2H@3G?2JB5JB5KD5LE6LD7ME7PI:?9-     /!5$8%>)B,F/QD(YG'Z;`@hDnKtO"|S#Y&_*c+j0p3v6|9;ɉ?ЎBۙHHHHHLQUV[bkxܼܼٹٹض׳մղԯӯҭҭЫѩϧΦ̢̠ͦͤͣ˟˝˝ɜțșșșƗƕƕŔŔÒÑÐ࿍߿߽߼ݺܺ۹۹ٷضص״ճԱӯ}Ѯ|Ѯ|ϭxάwͫv̩vʧuɦsǤrƣqĢpnljhhfdc``_]\YXWUR|QxPvPtOrMoKmIkGiEfC}dAya@u_?q\=o[;lW9jU8fT7cP5^M2]J3[I3YG/TD.QB,N?,L>,J=+F;)C9&C6$>5$<4#;1#90"70!5-3,1+0*.)-(+')&)$($%!&"$ $ #!" """   !"!                            !"$'( +!/$0&1&4(8*8,;-=/?1B1D4 F7I8!M:"L;"O>#R>$T@&VB'XC'XD([F*]H*]I+^J,`J-cK-dM.fO0hP1iQ2jR3kS1lT2mT3nV4oV5oV4oV5oV4pW6pW5oV6pW7oU6oV4nV5mV6mV6mW4mW7nW7mV6lU6lV6kU5lU6kT5kT5iT4iT4iS5hS5gR5gQ5fQ5fQ5eP4cN3dO4bN3`L1_K2]I0^K1\K2[I0[H1ZG0YG0VG0WG0VE0TE.SE.SD0SE/QC/QB0PB,OB+N@,O@.M?,K?+J?*J>+F;)E;)D;*D;*D:)B8(@6)A6(@6)>6)=5(>5(=4(=4(;4&:2%:2%90%81'82'93(82'81'82'70'70'70'91(92):3*93(:4*:4*;5*;5)<6+;5*=7,>8-?9.@:/@:/@:.@:-B;.C;.E=0E=0G?2F>1F>1H@3H@3IA4JB5KD5LE6LE6MF7PH9A;.     -4#8%?)A+E2TH+YC$X;`@iFoK!sP$~W*X$^)b,j.n1u5|8<ˋ@ӐBږEHIGJRRTX^eq{޿ܾټع׷մմ԰ҰЯάΩ̨̧˦ɥǣǠŠŝĜ›˜ؿؾ׽ټػ׺ֺ׹ոոԶӴҴѲбаϯͮ~ˬ{ˬzʪzǨxǧwƦvĤt£s£rqponmkkihgeebca`^][ZXVU}T|SzRvPtOrNqJoKmJjGhE}gEzdDybDvaBs^@q\>nZ>lX=kV;gU9dR8aO6^M5ZK3YJ4WH2TF0RD/PB-M@,L>-J;,H;)D8(C7'@7'>4'<3&:1$:0#:."5-!1, 1, 1+0*-),'*&)%)&(#(%'%&"$!#!#!$ #"""##!! !                                #$&'*!,"-#0&3&4&6*9+<-;.>0A2A2C3F6G7H8!L9"K;#M<#O>%R?&S@%UA&YC)YD)[F+\H*[G*]I,`J-aJ-bK.cL/bK/cL/dM0dM0dM0cL0dN0cN1cN0dN1dO1eP2eP2dO1dP2dP2eP1eP2eQ3eP2eO2bO1bN3dO4dO4bN2bN2bN3`L1`L2_L2^K1]K1]K1ZJ/[J1ZH1YH/YH1XG0WF/WG0VF0VE0UE-TF,SE/SE/QC.QC/QC-OB,OA-N?-M?,M?,K=*J>*H<*E;)F<)E:)D:)E9(C8)B7)?7(>6)>5(?6)>4(=3(<3%;4%;3&:2&93(72&82'93(81(82'80(81(81(:2)82)92);4*93);3+;5*<6);5+=7,=7,=7,?9.@:/?9.@:.?9,D1G?2G?2IA4IA4KB6LE6MF7LE6MF7PI9C=/    , 4#7&<(@+H9UB&V;Y=aAfEmJ!rN&}]3[+]'a+h.m0u3{9;ɉ?ҏAږFJHHIQTSW^fr~ܿٻ׹ԷӴҲЯͬ˪˪ȨƥƤġßҿмѻϹз϶ε̴ʲɱɱȰȭȮǬéī¨}|{wwusrqpmlkihheecbb`_^][[XW~W|V|UzTxSwRvPsOqOoMmJmJkIiF|gFzfExcEucEs`Bq^@p]@n[>lY=iX;gV;eS:cQ9aQ8`N7^L5[L5XH4XG2UE0QD0PC.M@/L?.K?,H<*F:*F;+B8(A7(>4(=3'=2$:0$8/$9/$7/#5,#4,"4+"4* /),','+')&*&+%)$&$&#%"%"&#&"$!$ #!$!## #"!! !   !                                       ""%''+ -"-#/$3&1&3'7)6+8+<-;.>/A1A2C4D4 G7!I7!J9"K:#M;$N=%O=%O>&Q?'Q?'R@(R?'TB)UC)UC)VC*WD*XF+XE-XE,YF,YG,[F-[G-ZF-ZF-[F,]H0]H/\G-]H/[H.[H.[I/[J0ZH.[J0ZH.YG.ZG0YG.XH-XG/XG/WF.WF/VE/VE/UD/TE.RD-SD.SD/RC/RC,RD,QC-QC,OA,PC-OB,NA+NB+M?+K>*J>)I=)H<*F<*F<*E;*D:)C:)F8)D8*A8*>6)?5(?5)>5(=4'=4(;4&;4%;3&:1&82'82'82'82'70'82'81(70'81(81(92)92);4*:3*;4+;5*<6*<6+=7,=7,>8-?9.>8-@:/@:0@:.C;.D1F>1F>1H@3IA4IA4JB5JC4LE6LE6MF7QJ:D>1     ,3"7%<(@*I7WJ,Q7X<`AeE jG!tT/z^:_2`.c+i/n2r4z7<lj@ЎCږHLKJJRTTW_hq~ؼֹҴαͯʬǨŤãοϽ̺ʷȷɶƳıï¯~|{ywutsppnkjhgecb`_^^\Z~Y|X{WzVwUvUuTrQrQrQqOoMnMlLjJjI|gGzeGxeGvcEtaCr_Cq]An\?m[?lZ>iX=hV=fV=cT6(=5'<3&;3&92$80$6.#5-$3-!3,"3+"2+!1* 2).),'-&*&*&*&)%'$'$($&$$"&"%#$$$"$!$!#!##"#""!!!   !                                        ! $$%'( *#+#/$.$/&1&2(5(6)7*8,;.;/=/?1?1A3 A3 C4!D4!E6#D5"F7$G8#H:%H:%I;%J:(J<'J<&K<&M<'M=&M<'O>(O>(P>*O>)O>)O?(P@)P@*QA)QA*QA*RB+RA+RA,QA,PA+Q@+Q@+Q@+Q@+Q@+P?*N?,O@-N?,N?,N?,M?,N@-M@*M@+M?,M?,L?,M?-L>+K=*I>*I>*I>*H=)E<*E;)E;)E;*D:)D9*C7)C7)B7)A5)?6)>6)=4)=3'=4'=5(<5&:2$;3'92'83(71&82'60%70'91'50(71(81(61(93*92):4):4*;4+<6+;5)<6+=7,>8-=7,?9.@:/@:/@:/A;/C;.C;.D1F>1G?2G?2H@3IA5JC4KD4KD5KD6ME7PI9FA2     -4#6%;(@*F1OD&R:X;\?bE jJ$sV2{^9b8tDm7i1o3t5y8<ƉAЏDٕHLLKMTUTW^fp{ڼָҵгͰȫť¢̿ɻŸĵ³~}zxutrpnkjifdbba_~\|YzZxXwWuUtTrSqRoPnOmN}kL}kM{iMzgMyfKwdHucIsbGpbFpaEo`En^Dl\Bj[AiZ@hXAfV?dT=bT=`R/E<.E<-D:-E9-C9-A8+?6)=6)<4'<4':3(82'71&71&5/$5/$50$3,"3,#0+".* .(!/' 0( /'/(+( +'+%(%($)$(%'$&$'#&#%"&#%#%!%!#!$ $ " # ###""!!!!                                            !$#%''(*")",#.$/&/&0&2'5)4(5(6)8,6,9-9.;/=/=0>2 >1@2@3 @4!A4!C4#B3#C5"D5$D5%E7%E6$E7$E7%F9$H:%G:%H;&I;&I;&H;&I;'H:(I:(G9'I;(H9(F8'H:(G9(G:(H:)H:)H:)H;)H<(F<)F;)F<(E;(F:)G;)E:(E:'F:(E:(D:(C:(C:(B9'@8(@7)@6(@6'A6(>5'?5)>5(=5'=3(<2'=4'<4&:3$:1%90'70&72&71&71&82'81'91(4/&70'81'72(91(:2);3(;3):4*;5*;5)<6+<6+>8->8-?9.?9.@:/A;0A;/C;/D<0D1G?2G?2G?2H@3IA4IB4KC4KD5KD6ME7PI:ID5    -3#5$<(A+C/M<T9W;\AcF"gJ%kO)xY2`8zM{Fi0m2t5x7;ć@ώBؔEHKMOSSTSYbgpzܿڽػֹӷѴϲ̱ȮƩĦ˿ɽƻŶ}{wuromjgec~a{^z]xZvZuYtWrUoSnS}lO{iNzhMxhLtfJsdIqcGpbFoaFnaFm^Gk]Ej[DiYChYBeWBdW@cW@bU?aT>`S=_R<_R<]P;[O:ZL:YL:WI8VI7UG6SG5RG4QE4PD4OD3KA3J@2JB0H@1E5*<4*=3)<3(92(71'71'70&4/%4/$3.#3.#3-#1+!2,"1+!/*!0+".)!-( -( -&+&*(*&*%)%(%)%($'$'$&#&#%"%"%#$"#!$!$$ $!"##""!!! !                                                   !"$%%(( )!* *!+",#,#-#/%0&/%0&1(3(3(4)3*5*6+7,6,8.9.:. ;/ ;0 ;0<1>1!=1"=1">2">3#?3$?2">3"?4#?4#?4#>3#?3#>2$>2#?3$?3$?4%?4%?5$?6#?5"@6%A7$A7$?5$@6%@6$A7%@6$?5$?5%?5&>6&>5&<4%=6'=5&;4%=5(<3&<3&=4&<2';1&:0&:1&:2%91$8/&7/'6/'82'82'71&70&70'70'4/&71(71'82';2):2):4*94(:4)<6+<6+;5*=7,>8->8-?9.?9.?9.@:.@:-C:0E:1E=0D1F>1G?2H@3H@3IA4JB5JB5JC4MF7MF7OH8LF7 ! . 4$6$<(@*E3N="N7Y@![C"aE#hI&kM%tV0}_6|Q~Gg.k0p3x7~:„=͋AՐBܖDHNLNQPQW]dlszؿԺж͵ɯƬè¦ɿȺĹĸô~|xwuqnjgg}dyax_t\rZpX}nVzmTxjQwfQteOqcKqaIo`Jk_Gi^HhZFh[CeZCeXCdWDcUD_S@`S?^R>]P?\Q>[O.E=0C=/C<.B:-B:,A:,@8+@9*>6(>5*=5,>4+=2)92(93(;1':0'70'4/&4.%5.%3-$2-%2-$1,#1+$/*"0+"/*"0*#0+!-)!,)!,)!*'*')&)')&)&)&'&'%'$'$&#&#&#&#%"%"$"%"$ ## # """#!""!!                                               !!##$%%& &&((")"(!*#*$,%.&-%.%/'0(1)1)1)2*1)3+4+5+5,4,6,6-6-7. 6-7. 8/!7- 7.!8.!8.!8.!9/"90":1 90 :0#;2#;2":0":1";1#;2#;1#;1#;2$:1#:3$;2%:2$:2$:2$91$91$9/&90&90%8.&80$90&8/&8.%70%60%70%60%60%60%60&70'70'6/&40'5/&81(82(91(92):3*:4):4);5*<6+;5*<6+=7,>8-?9.?9.A;0A;/A;.C:/D;.D1H@3G?2H@3IA4JB5KD5KD5MF7MF7OH8NG7&"  .!2#7&:&=*E4M; O9S> XA!aE%fK(lN)rV1z[4{OyDf-k0p3v8|9;ȇ>Ҏ?ٓBߘBKNOOMRVX`fjqzۿպѶͯȬèɿù~}yywstrqolkj}h{dxbv`s]~pZ}oXzmVwkTwiStgQqeMoaLnaKk_Ji\Ih[FfYCcWC`TB]TA\R@ZP>YN=YN=XM=VK:UK:SJ9RI8QH7OF5OF7OG6NF4LC5LC4JA2I@1I@1H?1G>0F=/D<-E;0C;/B<.?;0?9-A9-@6,>6,=7,=6+;4);5):4*92):2)92)81(81(80'6/&71(4/&3.%3.%1,#2-$2-$1,#0*#/*"0+"0*#/)"/* .)!-' +( *'*'*'(&*%*&(%'$'$(%'$&#&#&#%"&#$!$!$ # ## # #""!"!!! !                                                         #$$#$$% &!' '!'"(")$*$)$*$*$,$-$,$,%/&/'/'0(0(0(1(1)2* 1)1)2* 2* 2* 2+2+3+!3,4-4-5- 5.!5- 5- 6.!6.!5.!6/"6.!5- 4.!3.!4."4-#6-"7.$6-#5-#6-#7.$5.%5.%5/$5/%5/$5/$71&6/&5/&4/&50'3/&20%60'81(83*71(92):3*:4*:3*;5*<6+<6+=7,=7,>8->8-?9.?9.@:0A;/C:/D;.D=0E=0E=0F>1G?2G?2H@3H@3JB5JC4KD5KD5MF7MF7PI82.&   /"5%8'9'<*B1J9L8O9WA#aH)hP.mQ,sS/z_6xKoύ?ՐAڕCKQUPLRVZ`_ft{ؽֹӸѴг˰ȭéɾŹ}{wuqoljgh}h|fzdybybwav`s^s]~r\|oZznXzmXylXvkUvhUtgSsgRrcQpcOlbNj`Li^Jf]If\Id[HcYG`VD^TB\R@[RAZP?VM=UM>TK0D9.@8->5+=7,=7,;4,;4+;5*;4+:3*72)73*92):1(71(50'4/&4/&4/&2-$3.%1,#2-$1,#0*#/*"/+!.(!/)".) .( -' .(!,&+%*'*&+%)&(&($'$'$&#&#&#&#%"%"%"%"$ $ $ # ###"""!!  ! !                                                             !!!!!$%$%#&( ' (!)")")#)"*#*$*#*$+$+%+%+$+%.'.(/)/(/)/(/*/*/*/)0+0+0+ 0,!1-"4+"3+!0,"0,!1-"3.#3-#3-#3-#3-"3,#4-$4."50$4.#3,#5.%4.$4.%4.%3.%3.%4/&40'32(61(81(61(82)92):3*:3);4*;5*<6+=7,=7,=7,>8->8-?:.@:/@:0B<1C:0D:0D=/E=0F>1G?2G?2H@3H@3IA4IA4JC4KD5LE6MF7OH9RK;:3)   1$5&6'7';(B0G8L7Q7YA&aJ-hO.mS0rU0}i=o?b.d.i0n2t6z8=ƈ@όAҎ@וFLQUPKSWY_bku|ֽҹϸ˳ʯ٬̿ȽżźƸ{tomk|fxdtb|s`{q^zo[xmZvlXtiVrgUrhUqgTodRoeRodQocPodOmaNl`Mk`Lm`Mk_Li_Jh^Jg]If]If]JdZGdZHcYGaWEaWD`VD]UC[SBZR@YP@XP>VO=VM@UM?SK:QH:OG:OI9NG8LF7KD5KD6IA4H@2G?2F=1E>0D8.=7,<6+:4*;5+:4*;4+:3*83)73*82):1'61'50'5/&5/&5/&2.%2-$2-$2-$2-$1+#0+"0+"0*#/)"0+!/)!-' -' +%,&*'*&,%)&(&(%(%'$'$'$%"&#&$$"$!$ $ $ ## ### # """                                                         !""""#$& $$& %& & '!'!'!'"+$+&,&+%,'+',(+(,(,(-),),)-).)0) 0) .* .)/* .* 1*"2*!1*!2+"1*!2+"3,#3,"4,$3-$2-#2,#1,#1,#2-$3.%3.%5/&40'61'92)50(82):2):3*:3*;4+;4+;5*<6+<6+>8->8-?9.?9.@:/@:/A;0C:0E;0D1E=0F>1G?2H@3H@3IA4JB5JC4LE6LE6MF7NG8RJ;?9-    , 0#7)8*<,@.E5K5O5V?#_H,dK+lR/t^5{c6_-^,d.h1m3t7z9<Ĉ@̍BяBؘJNQTNKOVZaiqzԹζɱŬɿƼ¸~{vtqn~kzgyf~ub}r^xo[ukXrgTneRndQkaPi`Nh^Lf\Jd[IcZHcYGbXF`WFbWEaVF_VD`VE`VD^VD^VE^TE_TE^TC^TB^SC\SD[RC[SBZS@YQAYP@WP>WO?VN>WN=TL>TK2E>1F?2D<0A:-@9-A:-?9,<5,<5,;5)93)92):3(82(71'70&70'5/&2-$3.%2.%1,#0+"0+"0+"/*!/)".(!/* .)!-' -' -' -' *'*&-$)&)%)$'$'$'$'$%"'$&$%#$!$ $$ ## ##""#"!!!                                                               !!" !""$$%%'!& '!'"(#)#)$)%*%*&)$*%+&+',(+(+'-&.'-(-(,'-(.( 0) 1*!1)"1*!3,#1*"0(!0*#0+"1-"1,#2-$2-$1,#3.%4/&6.%4/'70'70(70)92*92)92):3*;4+;4+;4+;5*<6+<6+>8-@:/?9.@:/@:/A;0B9/D:0D=.D1F>1G?2G?2H@3H@3IA4JC4LE6LE6LE6NG8OH8IC5   *5(1$8*;,>,E1H4M5T>#[E(bJ)iR/rX0xU)W'\+`,f0o5s7y9~<‡AȊCϏF֗LQROJKOV\clvڿռѸʲįʿ»~}{yyyusrpolk|hzfvb}s`yo]vmZtkXrgUoeSlbOi_Mg]Ke[JeYGbXF^UC^TB\RB[R@ZP>YQ?WP>VN>VM?WO>XNUM=UL>VN=UNSJ=SJ;QI8RI:QH:QH:PG9PG9NG8MF7LE6KD5LE6KC6JC4IA2I@3G?2G@1F>1E=1D8.>8.=7,=7,=6+=4+71(50'50'5/&3.%3.%2-$1,#0+".(!/)!/* -( .(!-' .&.&*'*&,$)%)%)$'%(%&#&#%"&#%"%"%!% #$$ # ### "!"" !                                                         !   !"""# #!#$ $!$!$!%"&"%!%"&#&#'$&$'$($+#)&)'+&*(*(*),'/&.) /( .'/)"0*#/(!0*"0+"0+"1,#2-$2-$3.%3.%3.%5/(4.'5/'61(52(81)73(84)84)94);5*;4+=7-=7,=7,?9.>8->8-?9.A;0@:0C;.D;-DDƊȄFӖKޠQPKKKQW_jtֽԺҹи̴ɱƮëɽ¸|xwttrqomllj~i|h{gzfxdwcta|q^{p\{mZwkXtiUshTpeQndOk_Mi^Ke[IaXG_UC_UD^SB[RAWP>UN=WM=UM;UL3F?0E<2E;0E>.D<1D:0D;0A;/@;0?8/?9.?9.>8->7-<7+=5,=4+<4+:4*92)92)81(70'6/&6.%1,$0-"/-".+"0)!/) /(-(+&*&(%'$'%'%'$%#%#&#$"$#$!$!#!# $ # $ #"""""!!                                                         ! !!#""# "####$ %!$!$!%"%"&#&$'$'#(#(%(%)&(%*'+'+%,%/' /&-'!/)".)!.) /*!0+"1,#2-$2-$2-$3.%3.%2-%4.'3.%40&50'51(71(92)83):4*:4);5*<6*;5*<6+=7,=7,>8-?9.?9.A;0A;0B:-D;-D1F>0F>0G@1IA3H@3IA4JB5KC5KD5MF7MF7OH8QJ;,)!   &-#0$5(9)9)B/G3J6S?#YC'dM.jM)jK%oL%|_4pAb0d/j3m5t:~AFƌGˏGՙOTPLMOS[eqԽѹϵ˲ǯì«ǼĹ|yvspnk}izgxfwdubub}ta{q^}q^{q^yo\xo\wo\wn\xlZvjXuiXtgUrgRqfSocQmaOk`Ni^Lg]KdZIcYHbXG`WE]UD\RAYQ@XP?XM>TK:RJ;RH2F?1F>1E=1F>1E=0D<3E>2E>1E<3F=2E>0D<1D<1D=/C<0D<2B;0A<0A;0?9-=7,>7,=7,<6+;5*<6+;5+93):4*92)93*82)81(81(71(50'50%50%3.&4.&2-$2-$2-$0+#1+#/(!/(!,)+'*&)%)&($&$&$%"%"$"$!## ##"""!! "!                                                             !!"##!#$ $$$ $%"%#%#%#%"&#'$(%&$'$)$(%(&*%+&+$+%,&.' -' .(!-' .(!0+!0+"0+"0+"1,#1,#2-$2-$3.%4/&3.%4/&50'50'61(82)92)92)92):3*;5*;5*;5*<6+<6+>8->8-@:/@:/A;0@:/C;.D;-C;/D1H@2H@4IA4IA4IB4KD5LE6LE6LE6NG8RI:<8,  &0'1'3&6'9(?-C1I6Q=#U@%_N2jS0iI!lI"vY3sMd4c0i4n6t9~?FƌI̐JҖNڛNPQRTW`isվҺεɱĬȿļ}xsqn~j{hyfwe}sa{q_yp^wm[vlZtjXrhWrhVqgUohTngTpfTodRncRocQodPnbPnbPmaOl`Nl_Mj^Lg\Ke[JdZHdZGbXFaWF_WF]UC\SA[S@XQ@WN@UM=SL;RK:PH7OG8LE7LE6LC6JA4H@3H@2G?2F=1F>0D8->8-=7,>9.=7*>9->8-=8-?9.?9.>8->8-<8-<6+=7,>6,>5+;6+94*;4+92)93*62)81(81(50'4/&4/%5.%2-%3-$2-$2-$2-$1,#3-$3+"3+"2*#1)$0("/( /) -' +( *(+&)&)&(%&#&#$!$ %"$ #"""!                                                              !!""""### # $# %"%!%#%#&#&#'$'$&$'%)#)$(&+%-%)&+&-' +( ,(!.' .(!/)"/*!0+"0+"0+"1,#2-$2-$2-$2-$3.%3.%4/&4/&70'61(82)92)92)92);4+;5*;5*<6*<6+=7,>8->8-?9.?9.@:/A;0B:-D<.E=0D1G?2G?2H@3H@3IA4IB3KC5JB6KD5MF7NG8OG8F?2    %1+ 3'1%5&9(=,A/G6O;"S<"]G/kX?kM*lJ"vV/h@`0c0h2o7u:{=FÊIːIΓIӖJߝOTW[^bgqۿ׿ҺεɱƮü~{wro~lzhvf~sbzp^zp^wn\ulZtkYsiWpfToeSndQjaOj`Nh^Lg]Ki\Jf[If\Je[Ie[Ig\Jh\Jg[IdZHdZIdZHcYGbXFaWE^WE_TC_SB]UB[TA[SBYQ@WP=WO>VN=UM1D8.>8-=7,=7,=7,<6+<6+:4);4*:3*92)92):3*72)61(72)72)92):2);2)93(93):2*92)92)73*82)81(71(50'50'4/&4/&4-$3-$1,#1,#/*!0+!0*!/* -' ,'".'",(*(*'+( *'+%,&*()'*')&)&(%(%'$&#%"%"% $!$!#"!!                                                               !  !!"!""### # $ # %"$!&#&#&#&#&#&#'$'$(#)%'%+%,$)'+&-& *( +' .(!.(!/)"/)!0+"0+"0+"1,#2-$2-$2-$3.%3.%3.%3/&4/&81(81(81(92):3*:3*;4+;4*<5*<6+<6+=7,>8->8-?9.?9.?9.@:/C;.D<.E=0D1F>1G?2G?2H@3IA4KD5KD6KC7LE6MF5MF7OH9LD7  #.*-"/#4'8*<+@/E4N;!P;"XD,cS8jS0iJ$u[6oJg9d2f0m7u={AFIȏJʏHϓJܜPU[`bddlwܿ׾Һ͵ʲƭªĽ}}|zzywtqn|jyf~ta}r_xn\wm[tjXrhVpfTpdRmbPj`Ni_Mh^Le[IdZHbYGaWF`VE_VD]TB^UC]TB\SB[SB\TC^TC^TC]TB\TB]SB_SC]UB[TA[QAWO?WPWO>UM