* shot down spammy warning about samplesize for lmsize<=128; -debugsamplesize to show
* numBspModels ('brusmodels') stat emitting
Radiant:
misc...
* filters toolbar (disableable)
* fix: shift + m1 click in tex browser to open shader in internal/external editor;
defaulted internal; focuses on wanted shader; correct opening/saving
* fix: angles "0 x 0" autoconvert to angle "x" on transform (was getting deleted w/o a trace)
988 lines
31 KiB
C++
988 lines
31 KiB
C++
/*
|
|
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
|
|
For a list of contributors, see the accompanying CONTRIBUTORS file.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant 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.
|
|
|
|
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
//
|
|
// User preferences
|
|
//
|
|
// Leonardo Zide (leo@lokigames.com)
|
|
//
|
|
|
|
#include "preferences.h"
|
|
#include "environment.h"
|
|
|
|
#include "debugging/debugging.h"
|
|
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtkvbox.h>
|
|
#include <gtk/gtkhbox.h>
|
|
#include <gtk/gtkframe.h>
|
|
#include <gtk/gtklabel.h>
|
|
#include <gtk/gtktogglebutton.h>
|
|
#include <gtk/gtkspinbutton.h>
|
|
#include <gtk/gtkscrolledwindow.h>
|
|
#include <gtk/gtktreemodel.h>
|
|
#include <gtk/gtktreeview.h>
|
|
#include <gtk/gtktreestore.h>
|
|
#include <gtk/gtktreeselection.h>
|
|
#include <gtk/gtkcellrenderertext.h>
|
|
#include <gtk/gtknotebook.h>
|
|
|
|
#include "generic/callback.h"
|
|
#include "math/vector.h"
|
|
#include "string/string.h"
|
|
#include "stream/stringstream.h"
|
|
#include "os/file.h"
|
|
#include "os/path.h"
|
|
#include "os/dir.h"
|
|
#include "gtkutil/filechooser.h"
|
|
#include "gtkutil/messagebox.h"
|
|
#include "cmdlib.h"
|
|
|
|
#include "error.h"
|
|
#include "console.h"
|
|
#include "xywindow.h"
|
|
#include "mainframe.h"
|
|
#include "qe3.h"
|
|
#include "gtkdlgs.h"
|
|
|
|
|
|
|
|
void Global_constructPreferences( PreferencesPage& page ){
|
|
page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
|
|
}
|
|
|
|
void Interface_constructPreferences( PreferencesPage& page ){
|
|
#ifdef WIN32
|
|
page.appendCheckBox( "", "External Shader Editor", g_TextEditor_useWin32Editor );
|
|
#else
|
|
{
|
|
GtkWidget* use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
|
|
GtkWidget* custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
|
|
Widget_connectToggleDependency( custom_editor, use_custom );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Mouse_constructPreferences( PreferencesPage& page ){
|
|
{
|
|
const char* buttons[] = { "2 button", "3 button", };
|
|
page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
|
|
}
|
|
page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
|
|
}
|
|
void Mouse_constructPage( PreferenceGroup& group ){
|
|
PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
|
|
Mouse_constructPreferences( page );
|
|
}
|
|
void Mouse_registerPreferencesPage(){
|
|
PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
|
|
}
|
|
|
|
|
|
/*!
|
|
=========================================================
|
|
Games selection dialog
|
|
=========================================================
|
|
*/
|
|
|
|
#include <map>
|
|
|
|
inline const char* xmlAttr_getName( xmlAttrPtr attr ){
|
|
return reinterpret_cast<const char*>( attr->name );
|
|
}
|
|
|
|
inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
|
|
return reinterpret_cast<const char*>( attr->children->content );
|
|
}
|
|
|
|
CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
|
|
// read the user-friendly game name
|
|
xmlNodePtr pNode = pDoc->children;
|
|
|
|
while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
|
|
{
|
|
pNode = pNode->next;
|
|
}
|
|
if ( !pNode ) {
|
|
Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
|
|
}
|
|
|
|
for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
|
|
{
|
|
m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
|
|
}
|
|
|
|
{
|
|
StringOutputStream path( 256 );
|
|
path << AppPath_get() << gameFile.c_str() << "/";
|
|
mGameToolsPath = path.c_str();
|
|
}
|
|
|
|
ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
|
|
|
|
mGameFile = gameFile;
|
|
|
|
{
|
|
GameDescription::iterator i = m_gameDescription.find( "type" );
|
|
if ( i == m_gameDescription.end() ) {
|
|
globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
|
|
// default
|
|
mGameType = "q3";
|
|
}
|
|
else
|
|
{
|
|
mGameType = ( *i ).second.c_str();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameDescription::Dump(){
|
|
globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
|
|
for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
|
|
{
|
|
globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
|
|
}
|
|
}
|
|
|
|
CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
|
|
|
|
|
|
#include "warnings.h"
|
|
#include "stream/textfilestream.h"
|
|
#include "container/array.h"
|
|
#include "xml/ixml.h"
|
|
#include "xml/xmlparser.h"
|
|
#include "xml/xmlwriter.h"
|
|
|
|
#include "preferencedictionary.h"
|
|
#include "stringio.h"
|
|
|
|
const char* const PREFERENCES_VERSION = "1.0";
|
|
|
|
bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
|
|
bool ret = false;
|
|
TextFileInputStream file( filename );
|
|
if ( !file.failed() ) {
|
|
XMLStreamParser parser( file );
|
|
XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
|
|
parser.exportXML( importer );
|
|
ret = true;
|
|
}
|
|
|
|
int l = strlen( cmdline_prefix );
|
|
for ( int i = 1; i < g_argc - 1; ++i )
|
|
{
|
|
if ( g_argv[i][0] == '-' ) {
|
|
if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
|
|
if ( g_argv[i][l + 1] == '-' ) {
|
|
preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
|
|
TextFileOutputStream file( filename );
|
|
if ( !file.failed() ) {
|
|
XMLStreamWriter writer( file );
|
|
XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
|
|
exporter.exportXML( writer );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
|
|
Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
|
|
*( tmpName.end() - 4 ) = 'T';
|
|
*( tmpName.end() - 3 ) = 'M';
|
|
*( tmpName.end() - 2 ) = 'P';
|
|
*( tmpName.end() - 1 ) = '\0';
|
|
|
|
return Preferences_Save( preferences, tmpName.data() )
|
|
&& ( !file_exists( filename ) || file_remove( filename ) )
|
|
&& file_move( tmpName.data(), filename );
|
|
}
|
|
|
|
|
|
|
|
void LogConsole_importString( const char* string ){
|
|
g_Console_enableLogging = string_equal( string, "true" );
|
|
Sys_LogFile( g_Console_enableLogging );
|
|
}
|
|
typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
|
|
|
|
|
|
void RegisterGlobalPreferences( PreferenceSystem& preferences ){
|
|
preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
|
|
preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
|
|
preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
|
|
}
|
|
|
|
|
|
PreferenceDictionary g_global_preferences;
|
|
|
|
void GlobalPreferences_Init(){
|
|
RegisterGlobalPreferences( g_global_preferences );
|
|
}
|
|
|
|
void CGameDialog::LoadPrefs(){
|
|
// load global .pref file
|
|
StringOutputStream strGlobalPref( 256 );
|
|
strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
|
|
|
|
globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
|
|
|
|
if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
|
|
globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
|
|
}
|
|
}
|
|
|
|
void CGameDialog::SavePrefs(){
|
|
StringOutputStream strGlobalPref( 256 );
|
|
strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
|
|
|
|
globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
|
|
|
|
if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
|
|
globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
|
|
}
|
|
}
|
|
|
|
void CGameDialog::DoGameDialog(){
|
|
// show the UI
|
|
DoModal();
|
|
|
|
// we save the prefs file
|
|
SavePrefs();
|
|
}
|
|
|
|
void CGameDialog::GameFileImport( int value ){
|
|
m_nComboSelect = value;
|
|
// use value to set m_sGameFile
|
|
std::list<CGameDescription *>::iterator iGame = mGames.begin();
|
|
int i;
|
|
for ( i = 0; i < value; i++ )
|
|
{
|
|
++iGame;
|
|
}
|
|
m_sGameFile = ( *iGame )->mGameFile;
|
|
}
|
|
|
|
void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
|
|
// use m_sGameFile to set value
|
|
std::list<CGameDescription *>::const_iterator iGame;
|
|
int i = 0;
|
|
for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
|
|
{
|
|
if ( ( *iGame )->mGameFile == m_sGameFile ) {
|
|
m_nComboSelect = i;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
importCallback( m_nComboSelect );
|
|
}
|
|
|
|
void CGameDialog_GameFileImport( CGameDialog& self, int value ){
|
|
self.GameFileImport( value );
|
|
}
|
|
|
|
void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
|
|
self.GameFileExport( importCallback );
|
|
}
|
|
|
|
void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
|
|
std::vector<const char*> games;
|
|
games.reserve( mGames.size() );
|
|
for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
|
|
{
|
|
games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
|
|
}
|
|
page.appendCombo(
|
|
"Select the game",
|
|
StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
|
|
ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
|
|
ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
|
|
);
|
|
page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
|
|
}
|
|
|
|
GtkWindow* CGameDialog::BuildDialog(){
|
|
GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
|
|
|
|
GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
|
|
gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
|
|
|
|
{
|
|
PreferencesPage preferencesPage( *this, GTK_WIDGET( vbox2 ) );
|
|
Global_constructPreferences( preferencesPage );
|
|
CreateGlobalFrame( preferencesPage );
|
|
}
|
|
|
|
return create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) );
|
|
}
|
|
|
|
class LoadGameFile
|
|
{
|
|
std::list<CGameDescription*>& mGames;
|
|
const char* mPath;
|
|
public:
|
|
LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
|
|
}
|
|
void operator()( const char* name ) const {
|
|
if ( !extension_equal( path_get_extension( name ), "game" ) ) {
|
|
return;
|
|
}
|
|
StringOutputStream strPath( 256 );
|
|
strPath << mPath << name;
|
|
globalOutputStream() << strPath.c_str() << '\n';
|
|
|
|
xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
|
|
if ( pDoc ) {
|
|
mGames.push_front( new CGameDescription( pDoc, name ) );
|
|
xmlFreeDoc( pDoc );
|
|
}
|
|
else
|
|
{
|
|
globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
void CGameDialog::ScanForGames(){
|
|
StringOutputStream strGamesPath( 256 );
|
|
strGamesPath << AppPath_get() << "games/";
|
|
const char *path = strGamesPath.c_str();
|
|
|
|
globalOutputStream() << "Scanning for game description files: " << path << '\n';
|
|
|
|
/*!
|
|
\todo FIXME LINUX:
|
|
do we put game description files below AppPath, or in ~/.radiant
|
|
i.e. read only or read/write?
|
|
my guess .. readonly cause it's an install
|
|
we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
|
|
(if that's really needed)
|
|
*/
|
|
|
|
Directory_forEach( path, LoadGameFile( mGames, path ) );
|
|
}
|
|
|
|
CGameDescription* CGameDialog::GameDescriptionForComboItem(){
|
|
std::list<CGameDescription *>::iterator iGame;
|
|
int i = 0;
|
|
for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
|
|
{
|
|
if ( i == m_nComboSelect ) {
|
|
return ( *iGame );
|
|
}
|
|
}
|
|
return 0; // not found
|
|
}
|
|
|
|
void CGameDialog::InitGlobalPrefPath(){
|
|
g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
|
|
}
|
|
|
|
void CGameDialog::Reset(){
|
|
if ( !g_Preferences.m_global_rc_path ) {
|
|
InitGlobalPrefPath();
|
|
}
|
|
StringOutputStream strGlobalPref( 256 );
|
|
strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
|
|
file_remove( strGlobalPref.c_str() );
|
|
}
|
|
|
|
void CGameDialog::Init(){
|
|
InitGlobalPrefPath();
|
|
LoadPrefs();
|
|
ScanForGames();
|
|
if ( mGames.empty() ) {
|
|
Error( "Didn't find any valid game file descriptions, aborting\n" );
|
|
}
|
|
else
|
|
{
|
|
std::list<CGameDescription *>::iterator iGame, iPrevGame;
|
|
for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
|
|
{
|
|
if ( iPrevGame != mGames.end() ) {
|
|
if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
|
|
CGameDescription *h = *iGame;
|
|
*iGame = *iPrevGame;
|
|
*iPrevGame = h;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CGameDescription* currentGameDescription = 0;
|
|
|
|
if ( !m_bGamePrompt ) {
|
|
// search by .game name
|
|
std::list<CGameDescription *>::iterator iGame;
|
|
for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
|
|
{
|
|
if ( ( *iGame )->mGameFile == m_sGameFile ) {
|
|
currentGameDescription = ( *iGame );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( m_bGamePrompt || !currentGameDescription ) {
|
|
Create();
|
|
DoGameDialog();
|
|
// use m_nComboSelect to identify the game to run as and set the globals
|
|
currentGameDescription = GameDescriptionForComboItem();
|
|
ASSERT_NOTNULL( currentGameDescription );
|
|
}
|
|
g_pGameDescription = currentGameDescription;
|
|
|
|
g_pGameDescription->Dump();
|
|
}
|
|
|
|
CGameDialog::~CGameDialog(){
|
|
// free all the game descriptions
|
|
std::list<CGameDescription *>::iterator iGame;
|
|
for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
|
|
{
|
|
delete ( *iGame );
|
|
*iGame = 0;
|
|
}
|
|
if ( GetWidget() != 0 ) {
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
|
|
const char* identifier = gameDescription.getKeyValue( "index" );
|
|
if ( string_empty( identifier ) ) {
|
|
identifier = "1";
|
|
}
|
|
return identifier;
|
|
}
|
|
|
|
void CGameDialog::AddPacksURL( StringOutputStream &URL ){
|
|
// add the URLs for the list of game packs installed
|
|
// FIXME: this is kinda hardcoded for now..
|
|
std::list<CGameDescription *>::iterator iGame;
|
|
for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
|
|
{
|
|
URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
|
|
}
|
|
}
|
|
|
|
CGameDialog g_GamesDialog;
|
|
|
|
|
|
// =============================================================================
|
|
// Widget callbacks for PrefsDlg
|
|
|
|
static void OnButtonClean( GtkWidget *widget, gpointer data ){
|
|
// make sure this is what the user wants
|
|
if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
|
|
"Next time you start Radiant it will be good as new. Do you wish to continue?",
|
|
"Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
|
|
PrefsDlg *dlg = (PrefsDlg*)data;
|
|
dlg->EndModal( eIDCANCEL );
|
|
|
|
g_preferences_globals.disable_ini = true;
|
|
Preferences_Reset();
|
|
gtk_main_quit();
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// PrefsDlg class
|
|
|
|
/*
|
|
========
|
|
|
|
very first prefs init deals with selecting the game and the game tools path
|
|
then we can load .ini stuff
|
|
|
|
using prefs / ini settings:
|
|
those are per-game
|
|
|
|
look in ~/.radiant/<version>/gamename
|
|
========
|
|
*/
|
|
|
|
#define PREFS_LOCAL_FILENAME "local.pref"
|
|
|
|
void PrefsDlg::Init(){
|
|
// m_global_rc_path has been set above
|
|
// m_rc_path is for game specific preferences
|
|
// takes the form: global-pref-path/gamename/prefs-file
|
|
|
|
// this is common to win32 and Linux init now
|
|
m_rc_path = g_string_new( m_global_rc_path->str );
|
|
|
|
// game sub-dir
|
|
g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
|
|
g_string_append( m_rc_path, "/" );
|
|
Q_mkdir( m_rc_path->str );
|
|
|
|
// then the ini file
|
|
m_inipath = g_string_new( m_rc_path->str );
|
|
g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
|
|
}
|
|
|
|
void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
|
|
int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
|
|
if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
|
|
gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
|
|
}
|
|
}
|
|
|
|
void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
|
|
notebook_set_page( m_notebook, prefpage );
|
|
return;
|
|
}
|
|
|
|
static void treeSelection( GtkTreeSelection* selection, gpointer data ){
|
|
PrefsDlg *dlg = (PrefsDlg*)data;
|
|
|
|
GtkTreeModel* model;
|
|
GtkTreeIter selected;
|
|
if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
|
|
GtkWidget* prefpage;
|
|
gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
|
|
dlg->showPrefPage( prefpage );
|
|
}
|
|
}
|
|
|
|
typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
|
|
|
|
inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
|
|
for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
|
|
{
|
|
( *i )( group );
|
|
}
|
|
}
|
|
|
|
|
|
inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
|
|
callbacks.push_back( callback );
|
|
}
|
|
|
|
typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
|
|
|
|
inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
|
|
for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
|
|
{
|
|
( *i )( page );
|
|
}
|
|
}
|
|
|
|
inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
|
|
callbacks.push_back( callback );
|
|
}
|
|
|
|
PreferencesPageCallbacks g_interfacePreferences;
|
|
void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
|
|
PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
|
|
}
|
|
PreferenceGroupCallbacks g_interfaceCallbacks;
|
|
void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
|
|
PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
|
|
}
|
|
|
|
PreferencesPageCallbacks g_displayPreferences;
|
|
void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
|
|
PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
|
|
}
|
|
PreferenceGroupCallbacks g_displayCallbacks;
|
|
void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
|
|
PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
|
|
}
|
|
|
|
PreferencesPageCallbacks g_settingsPreferences;
|
|
void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
|
|
PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
|
|
}
|
|
PreferenceGroupCallbacks g_settingsCallbacks;
|
|
void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
|
|
PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
|
|
}
|
|
|
|
void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
|
|
gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
|
|
}
|
|
|
|
void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
|
|
Widget_updateDependency( self, toggleButton );
|
|
}
|
|
|
|
void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
|
|
if ( state == GTK_STATE_INSENSITIVE ) {
|
|
Widget_updateDependency( self, toggleButton );
|
|
}
|
|
}
|
|
|
|
void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
|
|
g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
|
|
g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
|
|
Widget_updateDependency( self, toggleButton );
|
|
}
|
|
|
|
|
|
inline GtkWidget* getVBox( GtkWidget* page ){
|
|
return gtk_bin_get_child( GTK_BIN( page ) );
|
|
}
|
|
|
|
GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
|
|
GtkTreeIter group;
|
|
gtk_tree_store_append( store, &group, parent );
|
|
gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
|
|
return group;
|
|
}
|
|
|
|
GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
|
|
GtkWidget* preflabel = gtk_label_new( name );
|
|
gtk_widget_show( preflabel );
|
|
|
|
GtkWidget* pageframe = gtk_frame_new( name );
|
|
gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
|
|
gtk_widget_show( pageframe );
|
|
|
|
GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
|
|
gtk_widget_show( vbox );
|
|
gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
|
|
gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
|
|
|
|
// Add the page to the notebook
|
|
gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
|
|
|
|
return pageframe;
|
|
}
|
|
|
|
class PreferenceTreeGroup : public PreferenceGroup
|
|
{
|
|
Dialog& m_dialog;
|
|
GtkWidget* m_notebook;
|
|
GtkTreeStore* m_store;
|
|
GtkTreeIter m_group;
|
|
public:
|
|
PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
|
|
m_dialog( dialog ),
|
|
m_notebook( notebook ),
|
|
m_store( store ),
|
|
m_group( group ){
|
|
}
|
|
PreferencesPage createPage( const char* treeName, const char* frameName ){
|
|
GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
|
|
PreferenceTree_appendPage( m_store, &m_group, treeName, page );
|
|
return PreferencesPage( m_dialog, getVBox( page ) );
|
|
}
|
|
};
|
|
|
|
GtkWindow* PrefsDlg::BuildDialog(){
|
|
PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
|
|
Mouse_registerPreferencesPage();
|
|
|
|
GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
|
|
|
|
{
|
|
GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
|
|
gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
|
|
gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
|
|
gtk_widget_show( mainvbox );
|
|
|
|
{
|
|
GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
|
|
gtk_widget_show( hbox );
|
|
gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
|
|
|
|
{
|
|
GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
|
|
gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
|
|
}
|
|
{
|
|
GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
|
|
gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
|
|
}
|
|
{
|
|
GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
|
|
gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
|
|
}
|
|
}
|
|
|
|
{
|
|
GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
|
|
gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
|
|
gtk_widget_show( hbox );
|
|
|
|
{
|
|
GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
|
|
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
|
|
gtk_widget_show( sc_win );
|
|
gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
|
|
|
|
// prefs pages notebook
|
|
m_notebook = gtk_notebook_new();
|
|
// hide the notebook tabs since its not supposed to look like a notebook
|
|
gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
|
|
gtk_widget_show( m_notebook );
|
|
|
|
|
|
{
|
|
GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
|
|
|
|
GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
|
|
gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
|
|
|
|
{
|
|
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
|
|
GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
|
|
gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
|
|
}
|
|
|
|
{
|
|
GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
|
|
g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
|
|
}
|
|
|
|
gtk_widget_show( view );
|
|
|
|
gtk_container_add( GTK_CONTAINER( sc_win ), view );
|
|
|
|
{
|
|
/********************************************************************/
|
|
/* Add preference tree options */
|
|
/********************************************************************/
|
|
// Front page...
|
|
//GtkWidget* front =
|
|
PreferencePages_addPage( m_notebook, "Front Page" );
|
|
|
|
{
|
|
GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
|
|
{
|
|
PreferencesPage preferencesPage( *this, getVBox( global ) );
|
|
Global_constructPreferences( preferencesPage );
|
|
}
|
|
GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
|
|
{
|
|
GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
|
|
PreferencesPage preferencesPage( *this, getVBox( game ) );
|
|
g_GamesDialog.CreateGlobalFrame( preferencesPage );
|
|
|
|
PreferenceTree_appendPage( store, &group, "Game", game );
|
|
}
|
|
}
|
|
|
|
{
|
|
GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
|
|
{
|
|
PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
|
|
PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
|
|
}
|
|
|
|
GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
|
|
PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
|
|
|
|
PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
|
|
}
|
|
|
|
{
|
|
GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
|
|
{
|
|
PreferencesPage preferencesPage( *this, getVBox( display ) );
|
|
PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
|
|
}
|
|
GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
|
|
PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
|
|
|
|
PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
|
|
}
|
|
|
|
{
|
|
GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
|
|
{
|
|
PreferencesPage preferencesPage( *this, getVBox( settings ) );
|
|
PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
|
|
}
|
|
|
|
GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
|
|
PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
|
|
|
|
PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
|
|
}
|
|
}
|
|
|
|
gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
|
|
|
|
g_object_unref( G_OBJECT( store ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
|
|
|
|
return dialog;
|
|
}
|
|
|
|
preferences_globals_t g_preferences_globals;
|
|
|
|
PrefsDlg g_Preferences; // global prefs instance
|
|
|
|
|
|
void PreferencesDialog_constructWindow( GtkWindow* main_window ){
|
|
g_Preferences.m_parent = main_window;
|
|
g_Preferences.Create();
|
|
}
|
|
void PreferencesDialog_destroyWindow(){
|
|
g_Preferences.Destroy();
|
|
}
|
|
|
|
|
|
PreferenceDictionary g_preferences;
|
|
|
|
PreferenceSystem& GetPreferenceSystem(){
|
|
return g_preferences;
|
|
}
|
|
|
|
class PreferenceSystemAPI
|
|
{
|
|
PreferenceSystem* m_preferencesystem;
|
|
public:
|
|
typedef PreferenceSystem Type;
|
|
STRING_CONSTANT( Name, "*" );
|
|
|
|
PreferenceSystemAPI(){
|
|
m_preferencesystem = &GetPreferenceSystem();
|
|
}
|
|
PreferenceSystem* getTable(){
|
|
return m_preferencesystem;
|
|
}
|
|
};
|
|
|
|
#include "modulesystem/singletonmodule.h"
|
|
#include "modulesystem/moduleregistry.h"
|
|
|
|
typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
|
|
typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
|
|
StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
|
|
|
|
void Preferences_Load(){
|
|
g_GamesDialog.LoadPrefs();
|
|
|
|
globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
|
|
|
|
if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
|
|
globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
|
|
}
|
|
}
|
|
|
|
void Preferences_Save(){
|
|
if ( g_preferences_globals.disable_ini ) {
|
|
return;
|
|
}
|
|
|
|
g_GamesDialog.SavePrefs();
|
|
|
|
globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
|
|
|
|
if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
|
|
globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
|
|
}
|
|
}
|
|
|
|
void Preferences_Reset(){
|
|
file_remove( g_Preferences.m_inipath->str );
|
|
}
|
|
|
|
|
|
void PrefsDlg::PostModal( EMessageBoxReturn code ){
|
|
if ( code == eIDOK ) {
|
|
Preferences_Save();
|
|
UpdateAllWindows();
|
|
}
|
|
}
|
|
|
|
std::vector<const char*> g_restart_required;
|
|
|
|
void PreferencesDialog_restartRequired( const char* staticName ){
|
|
g_restart_required.push_back( staticName );
|
|
}
|
|
|
|
void PreferencesDialog_showDialog(){
|
|
//if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
|
|
if ( g_Preferences.DoModal() == eIDOK ) {
|
|
if ( !g_restart_required.empty() ) {
|
|
StringOutputStream message( 256 );
|
|
message << "Preference changes require a restart:\n";
|
|
for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
|
|
{
|
|
message << ( *i ) << '\n';
|
|
}
|
|
gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
|
|
g_restart_required.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GameName_importString( const char* value ){
|
|
gamename_set( value );
|
|
}
|
|
typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
|
|
void GameName_exportString( const StringImportCallback& importer ){
|
|
importer( gamename_get() );
|
|
}
|
|
typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
|
|
|
|
void GameMode_importString( const char* value ){
|
|
gamemode_set( value );
|
|
}
|
|
typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
|
|
void GameMode_exportString( const StringImportCallback& importer ){
|
|
importer( gamemode_get() );
|
|
}
|
|
typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
|
|
|
|
|
|
void RegisterPreferences( PreferenceSystem& preferences ){
|
|
#ifdef WIN32
|
|
preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
|
|
#else
|
|
preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
|
|
preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
|
|
#endif
|
|
|
|
preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
|
|
preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
|
|
}
|
|
|
|
void Preferences_Init(){
|
|
RegisterPreferences( GetPreferenceSystem() );
|
|
}
|