+bool endswith( const char *haystack, const char *needle ){
+ size_t lh = strlen( haystack );
+ size_t ln = strlen( needle );
+ if ( lh < ln ) {
+ return false;
+ }
+ return !memcmp( haystack + ( lh - ln ), needle, ln );
+}
+
+bool texture_name_ignore( const char* name ){
+ StringOutputStream strTemp( string_length( name ) );
+ strTemp << LowerCase( name );
+
+ return
+ endswith( strTemp.c_str(), ".specular" ) ||
+ endswith( strTemp.c_str(), ".glow" ) ||
+ endswith( strTemp.c_str(), ".bump" ) ||
+ endswith( strTemp.c_str(), ".diffuse" ) ||
+ endswith( strTemp.c_str(), ".blend" ) ||
+ endswith( strTemp.c_str(), ".alpha" ) ||
+ endswith( strTemp.c_str(), "_norm" ) ||
+ endswith( strTemp.c_str(), "_bump" ) ||
+ endswith( strTemp.c_str(), "_glow" ) ||
+ endswith( strTemp.c_str(), "_gloss" ) ||
+ endswith( strTemp.c_str(), "_pants" ) ||
+ endswith( strTemp.c_str(), "_shirt" ) ||
+ endswith( strTemp.c_str(), "_reflect" ) ||
+ endswith( strTemp.c_str(), "_alpha" ) ||
+ 0;
+}
+
+class LoadShaderVisitor : public Archive::Visitor
+{
+public:
+void visit( const char* name ){
+ IShader* shader = QERApp_Shader_ForName( CopiedString( StringRange( name, path_get_filename_base_end( name ) ) ).c_str() );
+ shader->DecRef();
+}
+};
+
+void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused );
+
+ui::Widget g_page_textures;
+
+void TextureBrowser_toggleShow(){
+ GroupDialog_showPage( g_page_textures );
+}
+
+
+void TextureBrowser_updateTitle(){
+ GroupDialog_updatePageTitle( g_page_textures );
+}
+
+
+
+class TextureCategoryLoadShader
+{
+const char* m_directory;
+std::size_t& m_count;
+public:
+typedef const char* first_argument_type;
+
+TextureCategoryLoadShader( const char* directory, std::size_t& count )
+ : m_directory( directory ), m_count( count ){
+ m_count = 0;
+}
+void operator()( const char* name ) const {
+ if ( shader_equal_prefix( name, "textures/" )
+ && shader_equal_prefix( name + string_length( "textures/" ), m_directory ) ) {
+ ++m_count;
+ // request the shader, this will load the texture if needed
+ // this Shader_ForName call is a kind of hack
+ IShader *pFoo = QERApp_Shader_ForName( name );
+ pFoo->DecRef();
+ }
+}
+};
+
+void TextureDirectory_loadTexture( const char* directory, const char* texture ){
+ StringOutputStream name( 256 );
+ name << directory << StringRange( texture, path_get_filename_base_end( texture ) );
+
+ if ( texture_name_ignore( name.c_str() ) ) {
+ return;
+ }
+
+ if ( !shader_valid( name.c_str() ) ) {
+ globalOutputStream() << "Skipping invalid texture name: [" << name.c_str() << "]\n";
+ return;
+ }
+
+ // if a texture is already in use to represent a shader, ignore it
+ IShader* shader = QERApp_Shader_ForName( name.c_str() );
+ shader->DecRef();
+}
+typedef ConstPointerCaller1<char, const char*, TextureDirectory_loadTexture> TextureDirectoryLoadTextureCaller;
+
+class LoadTexturesByTypeVisitor : public ImageModules::Visitor
+{
+const char* m_dirstring;
+public:
+LoadTexturesByTypeVisitor( const char* dirstring )
+ : m_dirstring( dirstring ){
+}
+void visit( const char* minor, const _QERPlugImageTable& table ) const {
+ GlobalFileSystem().forEachFile( m_dirstring, minor, TextureDirectoryLoadTextureCaller( m_dirstring ) );
+}
+};
+
+void TextureBrowser_ShowDirectory( TextureBrowser& textureBrowser, const char* directory ){
+ if ( TextureBrowser_showWads() ) {
+ Archive* archive = GlobalFileSystem().getArchive( directory );
+ ASSERT_NOTNULL( archive );
+ LoadShaderVisitor visitor;
+ archive->forEachFile( Archive::VisitorFunc( visitor, Archive::eFiles, 0 ), "textures/" );
+ }
+ else
+ {
+ g_TextureBrowser_currentDirectory = directory;
+ TextureBrowser_heightChanged( textureBrowser );
+
+ std::size_t shaders_count;
+ GlobalShaderSystem().foreachShaderName( makeCallback1( TextureCategoryLoadShader( directory, shaders_count ) ) );
+ globalOutputStream() << "Showing " << Unsigned( shaders_count ) << " shaders.\n";
+
+ if ( g_pGameDescription->mGameType != "doom3" ) {
+ // load remaining texture files
+
+ StringOutputStream dirstring( 64 );
+ dirstring << "textures/" << directory;
+
+ Radiant_getImageModules().foreachModule( LoadTexturesByTypeVisitor( dirstring.c_str() ) );
+ }
+ }
+
+ // we'll display the newly loaded textures + all the ones already in use
+ TextureBrowser_SetHideUnused( textureBrowser, false );
+
+ TextureBrowser_updateTitle();
+}
+
+void TextureBrowser_ShowTagSearchResult( TextureBrowser& textureBrowser, const char* directory ){
+ g_TextureBrowser_currentDirectory = directory;
+ TextureBrowser_heightChanged( textureBrowser );
+
+ std::size_t shaders_count;
+ GlobalShaderSystem().foreachShaderName( makeCallback1( TextureCategoryLoadShader( directory, shaders_count ) ) );
+ globalOutputStream() << "Showing " << Unsigned( shaders_count ) << " shaders.\n";
+
+ if ( g_pGameDescription->mGameType != "doom3" ) {
+ // load remaining texture files
+ StringOutputStream dirstring( 64 );
+ dirstring << "textures/" << directory;
+
+ {
+ LoadTexturesByTypeVisitor visitor( dirstring.c_str() );
+ Radiant_getImageModules().foreachModule( visitor );
+ }
+ }
+
+ // we'll display the newly loaded textures + all the ones already in use
+ TextureBrowser_SetHideUnused( textureBrowser, false );
+}
+
+
+bool TextureBrowser_hideUnused();
+
+void TextureBrowser_hideUnusedExport( const BoolImportCallback& importer ){
+ importer( TextureBrowser_hideUnused() );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
+
+void TextureBrowser_showShadersExport( const BoolImportCallback& importer ){
+ importer( GlobalTextureBrowser().m_showShaders );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
+
+void TextureBrowser_showShaderlistOnly( const BoolImportCallback& importer ){
+ importer( g_TextureBrowser_shaderlistOnly );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
+
+void TextureBrowser_fixedSize( const BoolImportCallback& importer ){
+ importer( g_TextureBrowser_fixedSize );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport;
+
+void TextureBrowser_filterMissing( const BoolImportCallback& importer ){
+ importer( g_TextureBrowser_filterMissing );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_filterMissing> TextureBrowser_filterMissingExport;
+
+void TextureBrowser_filterFallback( const BoolImportCallback& importer ){
+ importer( g_TextureBrowser_filterFallback );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_filterFallback> TextureBrowser_filterFallbackExport;
+
+void TextureBrowser_enableAlpha( const BoolImportCallback& importer ){
+ importer( g_TextureBrowser_enableAlpha );
+}
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_enableAlpha> TextureBrowser_enableAlphaExport;
+
+void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ){
+ if ( hideUnused ) {
+ textureBrowser.m_hideUnused = true;
+ }
+ else
+ {
+ textureBrowser.m_hideUnused = false;
+ }
+
+ textureBrowser.m_hideunused_item.update();
+
+ TextureBrowser_heightChanged( textureBrowser );
+ textureBrowser.m_originInvalid = true;
+}
+
+void TextureBrowser_ShowStartupShaders( TextureBrowser& textureBrowser ){
+ if ( textureBrowser.m_startupShaders == STARTUPSHADERS_COMMON ) {
+ TextureBrowser_ShowDirectory( textureBrowser, TextureBrowser_getComonShadersDir() );
+ }
+}
+
+
+//++timo NOTE: this is a mix of Shader module stuff and texture explorer
+// it might need to be split in parts or moved out .. dunno
+// scroll origin so the specified texture is completely on screen
+// if current texture is not displayed, nothing is changed
+void TextureBrowser_Focus( TextureBrowser& textureBrowser, const char* name ){
+ TextureLayout layout;
+ // scroll origin so the texture is completely on screen
+ Texture_StartPos( layout );
+
+ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
+ {
+ IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
+
+ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused ) ) {
+ continue;
+ }
+
+ int x, y;
+ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y );
+ qtexture_t* q = shader->getTexture();
+ if ( !q ) {
+ break;
+ }
+
+ // we have found when texdef->name and the shader name match
+ // NOTE: as everywhere else for our comparisons, we are not case sensitive
+ if ( shader_equal( name, shader->getName() ) ) {
+ int textureHeight = (int)( q->height * ( (float)textureBrowser.m_textureScale / 100 ) )
+ + 2 * TextureBrowser_fontHeight( textureBrowser );
+
+ int originy = TextureBrowser_getOriginY( textureBrowser );
+ if ( y > originy ) {
+ originy = y;
+ }
+
+ if ( y - textureHeight < originy - textureBrowser.height ) {
+ originy = ( y - textureHeight ) + textureBrowser.height;
+ }
+
+ TextureBrowser_setOriginY( textureBrowser, originy );
+ return;
+ }
+ }
+}
+
+IShader* Texture_At( TextureBrowser& textureBrowser, int mx, int my ){
+ my += TextureBrowser_getOriginY( textureBrowser ) - textureBrowser.height;
+
+ TextureLayout layout;
+ Texture_StartPos( layout );
+ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
+ {
+ IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
+
+ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused ) ) {
+ continue;
+ }
+
+ int x, y;
+ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y );
+ qtexture_t *q = shader->getTexture();
+ if ( !q ) {
+ break;
+ }
+
+ int nWidth = textureBrowser.getTextureWidth( q );
+ int nHeight = textureBrowser.getTextureHeight( q );
+ if ( mx > x && mx - x < nWidth
+ && my < y && y - my < nHeight + TextureBrowser_fontHeight( textureBrowser ) ) {
+ return shader;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ ==============
+ SelectTexture
+
+ By mouse click
+ ==============
+ */
+void SelectTexture( TextureBrowser& textureBrowser, int mx, int my, bool bShift ){
+ IShader* shader = Texture_At( textureBrowser, mx, my );
+ if ( shader != 0 ) {
+ if ( bShift ) {
+ if ( shader->IsDefault() ) {
+ globalOutputStream() << "ERROR: " << shader->getName() << " is not a shader, it's a texture.\n";
+ }
+ else{
+ ViewShader( shader->getShaderFileName(), shader->getName() );
+ }
+ }
+ else
+ {
+ TextureBrowser_SetSelectedShader( textureBrowser, shader->getName() );
+ TextureBrowser_textureSelected( shader->getName() );
+
+ if ( !FindTextureDialog_isOpen() && !textureBrowser.m_rmbSelected ) {
+ UndoableCommand undo( "textureNameSetSelected" );
+ Select_SetShader( shader->getName() );
+ }
+ }
+ }
+}
+
+/*
+ ============================================================================
+
+ MOUSE ACTIONS
+
+ ============================================================================
+ */
+
+void TextureBrowser_trackingDelta( int x, int y, unsigned int state, void* data ){
+ TextureBrowser& textureBrowser = *reinterpret_cast<TextureBrowser*>( data );
+ if ( y != 0 ) {
+ int scale = 1;
+
+ if ( state & GDK_SHIFT_MASK ) {
+ scale = 4;
+ }
+
+ int originy = TextureBrowser_getOriginY( textureBrowser );
+ originy += y * scale;
+ TextureBrowser_setOriginY( textureBrowser, originy );
+ }
+}
+
+void TextureBrowser_Tracking_MouseDown( TextureBrowser& textureBrowser ){
+ textureBrowser.m_freezePointer.freeze_pointer( textureBrowser.m_parent, TextureBrowser_trackingDelta, &textureBrowser );
+}
+
+void TextureBrowser_Tracking_MouseUp( TextureBrowser& textureBrowser ){
+ textureBrowser.m_freezePointer.unfreeze_pointer( textureBrowser.m_parent );
+}
+
+void TextureBrowser_Selection_MouseDown( TextureBrowser& textureBrowser, guint32 flags, int pointx, int pointy ){
+ SelectTexture( textureBrowser, pointx, textureBrowser.height - 1 - pointy, ( flags & GDK_SHIFT_MASK ) != 0 );
+}
+
+/*
+ ============================================================================
+
+ DRAWING
+
+ ============================================================================
+ */
+
+/*
+ ============
+ Texture_Draw
+ TTimo: relying on the shaders list to display the textures
+ we must query all qtexture_t* to manage and display through the IShaders interface
+ this allows a plugin to completely override the texture system
+ ============
+ */
+void Texture_Draw( TextureBrowser& textureBrowser ){
+ int originy = TextureBrowser_getOriginY( textureBrowser );
+
+ glClearColor( textureBrowser.color_textureback[0],
+ textureBrowser.color_textureback[1],
+ textureBrowser.color_textureback[2],
+ 0 );
+ glViewport( 0, 0, textureBrowser.width, textureBrowser.height );
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ glDisable( GL_DEPTH_TEST );
+ if ( g_TextureBrowser_enableAlpha ) {
+ glEnable( GL_BLEND );
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ else {
+ glDisable( GL_BLEND );
+ }
+ glOrtho( 0, textureBrowser.width, originy - textureBrowser.height, originy, -100, 100 );
+ glEnable( GL_TEXTURE_2D );
+
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+
+ int last_y = 0, last_height = 0;
+
+ TextureLayout layout;
+ Texture_StartPos( layout );
+ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
+ {
+ IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
+
+ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused ) ) {
+ continue;
+ }
+
+ int x, y;
+ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y );
+ qtexture_t *q = shader->getTexture();
+ if ( !q ) {
+ break;
+ }
+
+ int nWidth = textureBrowser.getTextureWidth( q );
+ int nHeight = textureBrowser.getTextureHeight( q );
+
+ if ( y != last_y ) {
+ last_y = y;
+ last_height = 0;
+ }
+ last_height = std::max( nHeight, last_height );
+
+ // Is this texture visible?
+ if ( ( y - nHeight - TextureBrowser_fontHeight( textureBrowser ) < originy )
+ && ( y > originy - textureBrowser.height ) ) {
+ // borders rules:
+ // if it's the current texture, draw a thick red line, else:
+ // shaders have a white border, simple textures don't
+ // if !texture_showinuse: (some textures displayed may not be in use)
+ // draw an additional square around with 0.5 1 0.5 color
+ if ( shader_equal( TextureBrowser_GetSelectedShader( textureBrowser ), shader->getName() ) ) {
+ glLineWidth( 3 );
+ if ( textureBrowser.m_rmbSelected ) {
+ glColor3f( 0,0,1 );
+ }
+ else {
+ glColor3f( 1,0,0 );
+ }
+ glDisable( GL_TEXTURE_2D );
+
+ glBegin( GL_LINE_LOOP );
+ glVertex2i( x - 4,y - TextureBrowser_fontHeight( textureBrowser ) + 4 );
+ glVertex2i( x - 4,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight - 4 );
+ glVertex2i( x + 4 + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight - 4 );
+ glVertex2i( x + 4 + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) + 4 );
+ glEnd();
+
+ glEnable( GL_TEXTURE_2D );
+ glLineWidth( 1 );
+ }
+ else
+ {
+ glLineWidth( 1 );
+ // shader border:
+ if ( !shader->IsDefault() ) {
+ glColor3f( 1,1,1 );
+ glDisable( GL_TEXTURE_2D );
+
+ glBegin( GL_LINE_LOOP );
+ glVertex2i( x - 1,y + 1 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x - 1,y - nHeight - 1 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x + 1 + nWidth,y - nHeight - 1 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x + 1 + nWidth,y + 1 - TextureBrowser_fontHeight( textureBrowser ) );
+ glEnd();
+ glEnable( GL_TEXTURE_2D );
+ }
+
+ // highlight in-use textures
+ if ( !textureBrowser.m_hideUnused && shader->IsInUse() ) {
+ glColor3f( 0.5,1,0.5 );
+ glDisable( GL_TEXTURE_2D );
+ glBegin( GL_LINE_LOOP );
+ glVertex2i( x - 3,y + 3 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x - 3,y - nHeight - 3 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x + 3 + nWidth,y - nHeight - 3 - TextureBrowser_fontHeight( textureBrowser ) );
+ glVertex2i( x + 3 + nWidth,y + 3 - TextureBrowser_fontHeight( textureBrowser ) );
+ glEnd();
+ glEnable( GL_TEXTURE_2D );
+ }
+ }
+
+ // draw checkerboard for transparent textures
+ if ( g_TextureBrowser_enableAlpha )
+ {
+ glDisable( GL_TEXTURE_2D );
+ glBegin( GL_QUADS );
+ int font_height = TextureBrowser_fontHeight( textureBrowser );
+ for ( int i = 0; i < nHeight; i += 8 )
+ for ( int j = 0; j < nWidth; j += 8 )
+ {
+ unsigned char color = (i + j) / 8 % 2 ? 0x66 : 0x99;
+ glColor3ub( color, color, color );
+ int left = j;
+ int right = std::min(j+8, nWidth);
+ int top = i;
+ int bottom = std::min(i+8, nHeight);
+ glVertex2i(x + right, y - nHeight - font_height + top);
+ glVertex2i(x + left, y - nHeight - font_height + top);
+ glVertex2i(x + left, y - nHeight - font_height + bottom);
+ glVertex2i(x + right, y - nHeight - font_height + bottom);
+ }
+ glEnd();
+ glEnable( GL_TEXTURE_2D );
+ }
+
+ // Draw the texture
+ glBindTexture( GL_TEXTURE_2D, q->texture_number );
+ GlobalOpenGL_debugAssertNoErrors();
+ glColor3f( 1,1,1 );
+ glBegin( GL_QUADS );
+ glTexCoord2i( 0,0 );
+ glVertex2i( x,y - TextureBrowser_fontHeight( textureBrowser ) );
+ glTexCoord2i( 1,0 );
+ glVertex2i( x + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) );
+ glTexCoord2i( 1,1 );
+ glVertex2i( x + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight );
+ glTexCoord2i( 0,1 );
+ glVertex2i( x,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight );
+ glEnd();
+
+ // draw the texture name
+ glDisable( GL_TEXTURE_2D );
+ glColor3f( 1,1,1 );
+
+ glRasterPos2i( x, y - TextureBrowser_fontHeight( textureBrowser ) + 5 );
+
+ // don't draw the directory name
+ const char* name = shader->getName();
+ name += strlen( name );
+ while ( name != shader->getName() && *( name - 1 ) != '/' && *( name - 1 ) != '\\' )
+ name--;
+
+ GlobalOpenGL().drawString( name );
+ glEnable( GL_TEXTURE_2D );
+ }
+
+ //int totalHeight = abs(y) + last_height + TextureBrowser_fontHeight(textureBrowser) + 4;
+ }
+
+
+ // reset the current texture
+ glBindTexture( GL_TEXTURE_2D, 0 );
+ //qglFinish();
+}
+
+void TextureBrowser_queueDraw( TextureBrowser& textureBrowser ){
+ if ( textureBrowser.m_gl_widget ) {
+ gtk_widget_queue_draw( textureBrowser.m_gl_widget );
+ }
+}
+
+
+void TextureBrowser_setScale( TextureBrowser& textureBrowser, std::size_t scale ){
+ textureBrowser.m_textureScale = scale;
+
+ TextureBrowser_queueDraw( textureBrowser );
+}
+
+void TextureBrowser_setUniformSize( TextureBrowser& textureBrowser, std::size_t scale ){
+ textureBrowser.m_uniformTextureSize = scale;
+
+ TextureBrowser_queueDraw( textureBrowser );
+}
+
+
+void TextureBrowser_MouseWheel( TextureBrowser& textureBrowser, bool bUp ){
+ int originy = TextureBrowser_getOriginY( textureBrowser );
+
+ if ( bUp ) {
+ originy += int(textureBrowser.m_mouseWheelScrollIncrement);
+ }
+ else
+ {
+ originy -= int(textureBrowser.m_mouseWheelScrollIncrement);
+ }
+
+ TextureBrowser_setOriginY( textureBrowser, originy );
+}
+
+XmlTagBuilder TagBuilder;
+
+enum
+{
+ TAG_COLUMN,
+ N_COLUMNS
+};
+
+void BuildStoreAssignedTags( ui::ListStore store, const char* shader, TextureBrowser* textureBrowser ){
+ GtkTreeIter iter;
+
+ gtk_list_store_clear( store );
+
+ std::vector<CopiedString> assigned_tags;
+ TagBuilder.GetShaderTags( shader, assigned_tags );
+
+ for ( size_t i = 0; i < assigned_tags.size(); i++ )
+ {
+ gtk_list_store_append( store, &iter );
+ gtk_list_store_set( store, &iter, TAG_COLUMN, assigned_tags[i].c_str(), -1 );
+ }
+}
+
+void BuildStoreAvailableTags( ui::ListStore storeAvailable,
+ ui::ListStore storeAssigned,
+ const std::set<CopiedString>& allTags,
+ TextureBrowser* textureBrowser ){
+ GtkTreeIter iterAssigned;
+ GtkTreeIter iterAvailable;
+ std::set<CopiedString>::const_iterator iterAll;
+ gchar* tag_assigned;
+
+ gtk_list_store_clear( storeAvailable );
+
+ bool row = gtk_tree_model_get_iter_first( GTK_TREE_MODEL( storeAssigned ), &iterAssigned ) != 0;
+
+ if ( !row ) { // does the shader have tags assigned?
+ for ( iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll )
+ {
+ gtk_list_store_append( storeAvailable, &iterAvailable );
+ gtk_list_store_set( storeAvailable, &iterAvailable, TAG_COLUMN, ( *iterAll ).c_str(), -1 );
+ }
+ }
+ else
+ {
+ while ( row ) // available tags = all tags - assigned tags
+ {
+ gtk_tree_model_get( GTK_TREE_MODEL( storeAssigned ), &iterAssigned, TAG_COLUMN, &tag_assigned, -1 );
+
+ for ( iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll )
+ {
+ if ( strcmp( (char*)tag_assigned, ( *iterAll ).c_str() ) != 0 ) {
+ gtk_list_store_append( storeAvailable, &iterAvailable );
+ gtk_list_store_set( storeAvailable, &iterAvailable, TAG_COLUMN, ( *iterAll ).c_str(), -1 );
+ }
+ else
+ {
+ row = gtk_tree_model_iter_next( GTK_TREE_MODEL( storeAssigned ), &iterAssigned ) != 0;
+
+ if ( row ) {
+ gtk_tree_model_get( GTK_TREE_MODEL( storeAssigned ), &iterAssigned, TAG_COLUMN, &tag_assigned, -1 );
+ }
+ }
+ }
+ }
+ }
+}
+
+gboolean TextureBrowser_button_press( ui::Widget widget, GdkEventButton* event, TextureBrowser* textureBrowser ){
+ if ( event->type == GDK_BUTTON_PRESS ) {
+ if ( event->button == 3 ) {
+ if ( GlobalTextureBrowser().m_tags ) {
+ textureBrowser->m_rmbSelected = true;
+ TextureBrowser_Selection_MouseDown( *textureBrowser, event->state, static_cast<int>( event->x ), static_cast<int>( event->y ) );
+
+ BuildStoreAssignedTags( textureBrowser->m_assigned_store, textureBrowser->shader.c_str(), textureBrowser );
+ BuildStoreAvailableTags( textureBrowser->m_available_store, textureBrowser->m_assigned_store, textureBrowser->m_all_tags, textureBrowser );
+ textureBrowser->m_heightChanged = true;
+ textureBrowser->m_tag_frame.show();
+
+ ui::process();
+
+ TextureBrowser_Focus( *textureBrowser, textureBrowser->shader.c_str() );
+ }
+ else
+ {
+ TextureBrowser_Tracking_MouseDown( *textureBrowser );
+ }
+ }
+ else if ( event->button == 1 ) {
+ TextureBrowser_Selection_MouseDown( *textureBrowser, event->state, static_cast<int>( event->x ), static_cast<int>( event->y ) );
+
+ if ( GlobalTextureBrowser().m_tags ) {
+ textureBrowser->m_rmbSelected = false;
+ gtk_widget_hide( textureBrowser->m_tag_frame );
+ }
+ }
+ }
+ return FALSE;
+}
+
+gboolean TextureBrowser_button_release( ui::Widget widget, GdkEventButton* event, TextureBrowser* textureBrowser ){
+ if ( event->type == GDK_BUTTON_RELEASE ) {
+ if ( event->button == 3 ) {
+ if ( !GlobalTextureBrowser().m_tags ) {
+ TextureBrowser_Tracking_MouseUp( *textureBrowser );
+ }
+ }
+ }
+ return FALSE;
+}
+
+gboolean TextureBrowser_motion( ui::Widget widget, GdkEventMotion *event, TextureBrowser* textureBrowser ){
+ return FALSE;
+}
+
+gboolean TextureBrowser_scroll( ui::Widget widget, GdkEventScroll* event, TextureBrowser* textureBrowser ){
+ if ( event->direction == GDK_SCROLL_UP ) {
+ TextureBrowser_MouseWheel( *textureBrowser, true );
+ }
+ else if ( event->direction == GDK_SCROLL_DOWN ) {
+ TextureBrowser_MouseWheel( *textureBrowser, false );
+ }
+ return FALSE;
+}
+
+void TextureBrowser_scrollChanged( void* data, gdouble value ){
+ //globalOutputStream() << "vertical scroll\n";
+ TextureBrowser_setOriginY( *reinterpret_cast<TextureBrowser*>( data ), -(int)value );
+}
+
+static void TextureBrowser_verticalScroll( GtkAdjustment *adjustment, TextureBrowser* textureBrowser ){
+ textureBrowser->m_scrollAdjustment.value_changed( gtk_adjustment_get_value(adjustment) );
+}
+
+void TextureBrowser_updateScroll( TextureBrowser& textureBrowser ){
+ if ( textureBrowser.m_showTextureScrollbar ) {
+ int totalHeight = TextureBrowser_TotalHeight( textureBrowser );
+
+ totalHeight = std::max( totalHeight, textureBrowser.height );
+
+ GtkAdjustment *vadjustment = gtk_range_get_adjustment( GTK_RANGE( textureBrowser.m_texture_scroll ) );
+
+ gtk_adjustment_set_value(vadjustment, -TextureBrowser_getOriginY( textureBrowser ));
+ gtk_adjustment_set_page_size(vadjustment, textureBrowser.height);
+ gtk_adjustment_set_page_increment(vadjustment, textureBrowser.height / 2);
+ gtk_adjustment_set_step_increment(vadjustment, 20);
+ gtk_adjustment_set_lower(vadjustment, 0);
+ gtk_adjustment_set_upper(vadjustment, totalHeight);
+
+ g_signal_emit_by_name( G_OBJECT( vadjustment ), "changed" );
+ }
+}
+
+gboolean TextureBrowser_size_allocate( ui::Widget widget, GtkAllocation* allocation, TextureBrowser* textureBrowser ){
+ textureBrowser->width = allocation->width;
+ textureBrowser->height = allocation->height;
+ TextureBrowser_heightChanged( *textureBrowser );
+ textureBrowser->m_originInvalid = true;
+ TextureBrowser_queueDraw( *textureBrowser );
+ return FALSE;
+}
+
+gboolean TextureBrowser_expose( ui::Widget widget, GdkEventExpose* event, TextureBrowser* textureBrowser ){
+ if ( glwidget_make_current( textureBrowser->m_gl_widget ) != FALSE ) {
+ GlobalOpenGL_debugAssertNoErrors();
+ TextureBrowser_evaluateHeight( *textureBrowser );
+ Texture_Draw( *textureBrowser );
+ GlobalOpenGL_debugAssertNoErrors();
+ glwidget_swap_buffers( textureBrowser->m_gl_widget );
+ }
+ return FALSE;
+}
+
+
+TextureBrowser g_TextureBrowser;
+
+TextureBrowser& GlobalTextureBrowser(){
+ return g_TextureBrowser;
+}
+
+bool TextureBrowser_hideUnused(){
+ return g_TextureBrowser.m_hideUnused;
+}
+
+void TextureBrowser_ToggleHideUnused(){
+ if ( g_TextureBrowser.m_hideUnused ) {
+ TextureBrowser_SetHideUnused( g_TextureBrowser, false );
+ }
+ else
+ {
+ TextureBrowser_SetHideUnused( g_TextureBrowser, true );
+ }
+}
+
+void TextureGroups_constructTreeModel( TextureGroups groups, GtkTreeStore* store ){
+ // put the information from the old textures menu into a treeview
+ GtkTreeIter iter, child;
+
+ TextureGroups::const_iterator i = groups.begin();
+ while ( i != groups.end() )
+ {
+ const char* dirName = ( *i ).c_str();
+ const char* firstUnderscore = strchr( dirName, '_' );
+ StringRange dirRoot( dirName, ( firstUnderscore == 0 ) ? dirName : firstUnderscore + 1 );
+
+ TextureGroups::const_iterator next = i;
+ ++next;
+ if ( firstUnderscore != 0
+ && next != groups.end()
+ && string_equal_start( ( *next ).c_str(), dirRoot ) ) {
+ gtk_tree_store_append( store, &iter, NULL );
+ gtk_tree_store_set( store, &iter, 0, CopiedString( StringRange( dirName, firstUnderscore ) ).c_str(), -1 );
+
+ // keep going...
+ while ( i != groups.end() && string_equal_start( ( *i ).c_str(), dirRoot ) )
+ {
+ gtk_tree_store_append( store, &child, &iter );
+ gtk_tree_store_set( store, &child, 0, ( *i ).c_str(), -1 );
+ ++i;
+ }
+ }
+ else
+ {
+ gtk_tree_store_append( store, &iter, NULL );
+ gtk_tree_store_set( store, &iter, 0, dirName, -1 );
+ ++i;
+ }
+ }
+}
+
+TextureGroups TextureGroups_constructTreeView(){
+ TextureGroups groups;
+
+ if ( TextureBrowser_showWads() ) {
+ GlobalFileSystem().forEachArchive( TextureGroupsAddWadCaller( groups ) );
+ }
+ else
+ {
+ // scan texture dirs and pak files only if not restricting to shaderlist
+ if ( g_pGameDescription->mGameType != "doom3" && !g_TextureBrowser_shaderlistOnly ) {
+ GlobalFileSystem().forEachDirectory( "textures/", TextureGroupsAddDirectoryCaller( groups ) );
+ }
+
+ GlobalShaderSystem().foreachShaderName( TextureGroupsAddShaderCaller( groups ) );
+ }
+
+ return groups;
+}
+
+void TextureBrowser_constructTreeStore(){
+ TextureGroups groups = TextureGroups_constructTreeView();
+ GtkTreeStore* store = gtk_tree_store_new( 1, G_TYPE_STRING );
+ TextureGroups_constructTreeModel( groups, store );
+ std::set<CopiedString>::iterator iter;
+
+ GtkTreeModel* model = GTK_TREE_MODEL( store );
+
+ gtk_tree_view_set_model( GTK_TREE_VIEW( g_TextureBrowser.m_treeViewTree ), model );
+
+ g_object_unref( G_OBJECT( store ) );
+}