File indexing completed on 2024-04-28 05:32:11

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004 
0005     inspired notably from kdelibs/kdeui/color/kcolorutils.h
0006     SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0008     SPDX-FileCopyrightText: 2007 Zack Rusin <zack@kde.org>
0009 
0010     SPDX-License-Identifier: LGPL-2.0-or-later
0011 */
0012 
0013 #include "oxygengtkicons.h"
0014 #include "config.h"
0015 
0016 #include <algorithm>
0017 #include <fstream>
0018 #include <iostream>
0019 #include <sstream>
0020 
0021 #include <gtk/gtk.h>
0022 
0023 namespace Oxygen
0024 {
0025 
0026     //_________________________________________
0027     GtkIcons::GtkIcons( void ):
0028         _factory( 0L ),
0029         _dirty( true )
0030     {
0031 
0032         // initialize sizes
0033         _sizes.push_back( std::make_pair( "panel-menu", 16 ) );
0034         _sizes.push_back( std::make_pair( "panel", 32 ) );
0035         _sizes.push_back( std::make_pair( "gtk-small-toolbar", 22 ) );
0036         _sizes.push_back( std::make_pair( "gtk-large-toolbar", 22 ) );
0037         _sizes.push_back( std::make_pair( "gtk-dnd", 48 ) );
0038         _sizes.push_back( std::make_pair( "gtk-button", 16 ) );
0039         _sizes.push_back( std::make_pair( "gtk-menu", 16 ) );
0040         _sizes.push_back( std::make_pair( "gtk-dialog", 32 ) );
0041         _sizes.push_back( std::make_pair( "", 16 ) );
0042 
0043     }
0044 
0045     //_________________________________________
0046     GtkIcons::~GtkIcons( void )
0047     {
0048         if( _factory )
0049         { gtk_icon_factory_remove_default( _factory ); }
0050     }
0051 
0052     //_________________________________________
0053     void GtkIcons::setIconSize( const std::string& tag, unsigned int value )
0054     {
0055         SizeMap::iterator iter( std::find_if( _sizes.begin(), _sizes.end(), SameTagFTor( tag ) ) );
0056         if( iter == _sizes.end() ) {
0057 
0058             std::cerr << "GtkIcons::setIconSize - no match for" << tag << "," << value << std::endl;
0059 
0060         } else if( iter->second != value ) {
0061 
0062             iter->second = value;
0063             _dirty = true;
0064 
0065         }
0066 
0067     }
0068 
0069     //_________________________________________
0070     void GtkIcons::loadTranslations( const std::string& filename )
0071     {
0072 
0073         if( filename == _filename )
0074         { return; }
0075 
0076         _filename = filename;
0077         _dirty = true;
0078         _icons.clear();
0079 
0080         std::ifstream in( filename.c_str() );
0081         if( !in )
0082         {
0083             std::cerr << "Oxygen::GtkIcons::loadTranslations - could not open " << filename << std::endl;
0084             return;
0085         }
0086 
0087         std::string line;
0088         while( std::getline( in, line, '\n' ) )
0089         {
0090 
0091             if( line.empty() ) continue;
0092 
0093             IconPair iconPair;
0094             std::istringstream stream( line.c_str() );
0095             stream >> iconPair.first >> iconPair.second;
0096             if( ( stream.rdstate() & std::ios::failbit ) != 0 )
0097             { continue; }
0098 
0099             _icons.insert( iconPair );
0100 
0101         }
0102 
0103     }
0104 
0105     //_________________________________________
0106     Gtk::RC GtkIcons::generate( const PathList& pathList )
0107     {
0108 
0109         if( (!_dirty) && _pathList == pathList ) return _rc;
0110 
0111         #if OXYGEN_DEBUG
0112         std::cerr << "Oxygen::GtkIcons::generate - regenerating translation tables" << std::endl;
0113         #endif
0114 
0115         _pathList = pathList;
0116         _rc.clear();
0117 
0118         // reset factory
0119         if( _factory )
0120         {
0121             gtk_icon_factory_remove_default( _factory );
0122             g_object_unref( G_OBJECT( _factory ) );
0123         }
0124 
0125         // create new
0126         _factory = gtk_icon_factory_new();
0127 
0128         // generate icon size string
0129         std::ostringstream iconSizesStr;
0130         for( SizeMap::const_iterator iter = _sizes.begin(); iter != _sizes.end(); ++iter )
0131         {
0132             if( iter->first.empty() ) continue;
0133             if( iter != _sizes.begin() ) iconSizesStr << ": ";
0134             iconSizesStr << iter->first << " = " << iter->second << "," << iter->second;
0135         }
0136 
0137         // pass to settings
0138         GtkSettings* settings( gtk_settings_get_default() );
0139         gtk_settings_set_string_property( settings, "gtk-icon-sizes", iconSizesStr.str().c_str(), "oxygen-gtk" );
0140 
0141         // generate pixmap path
0142         // this must be passed to gtk before any icon settings, otherwise
0143         // other icons are not recognized
0144         std::ostringstream pixmapPathStr;
0145         pixmapPathStr << "pixmap_path \"";
0146         for( PathList::const_iterator iter = pathList.begin(); iter != pathList.end(); ++iter )
0147         {
0148             #if OXYGEN_DEBUG
0149             std::cerr << "Oxygen::GtkIcons::generate - adding path: " << *iter << std::endl;
0150             #endif
0151 
0152             if( iter != pathList.begin() ) pixmapPathStr << ":";
0153             pixmapPathStr << *iter;
0154         }
0155         pixmapPathStr << "\"";
0156         _rc.addToHeaderSection( pixmapPathStr.str() );
0157 
0158         // loop over icons
0159         bool empty( true );
0160         for( IconMap::const_iterator iconIter = _icons.begin(); iconIter != _icons.end(); ++iconIter )
0161         {
0162 
0163             GtkIconSet* iconSet( generate( iconIter->first, iconIter->second, pathList ) );
0164             if( iconSet )
0165             {
0166                 gtk_icon_factory_add( _factory, iconIter->first.c_str(), iconSet );
0167                 gtk_icon_set_unref( iconSet );
0168                 empty = false;
0169             }
0170 
0171         }
0172 
0173         if( empty )
0174         {
0175 
0176             g_object_unref( G_OBJECT( _factory ) );
0177             _factory = 0L;
0178 
0179         } else gtk_icon_factory_add_default( _factory );
0180 
0181         // extra settings for entries
0182         std::string stock( generateString( "gtk-clear", "actions/edit-clear-locationbar-rtl.png", pathList ) );
0183         if( !stock.empty() )
0184         {
0185             _rc.addSection( "oxygen-icons-editor", Gtk::RC::defaultSection() );
0186             _rc.addToCurrentSection( stock );
0187             _rc.addToRootSection( "class \"*Entry*\" style \"oxygen-icons-editor\"" );
0188         }
0189 
0190         _dirty = false;
0191         return _rc;
0192 
0193     }
0194 
0195     //__________________________________________________________________
0196     GtkIconSet* GtkIcons::generate(
0197         const std::string& gtkIconName,
0198         const std::string& kdeIconName,
0199         const PathList& pathList ) const
0200     {
0201 
0202 
0203         if( kdeIconName == "NONE" ) return 0L;
0204 
0205         bool empty( true );
0206 
0207         // create iconSet
0208         GtkIconSet* iconSet = gtk_icon_set_new();
0209 
0210         // loop over iconSizes
0211         for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
0212         {
0213 
0214             // generate full icon name
0215             std::ostringstream iconFileStream;
0216             iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;
0217 
0218             // loop over provided path to see if at least one icon is found
0219             for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
0220             {
0221                 std::string filename( *pathIter + '/' + iconFileStream.str() );
0222                 if( !std::ifstream( filename.c_str() ) ) continue;
0223 
0224                 empty = false;
0225                 GtkIconSource* iconSource( gtk_icon_source_new() );
0226 
0227                 // set name
0228                 gtk_icon_source_set_filename( iconSource, filename.c_str() );
0229 
0230                 // set direction and state wildcarded
0231                 gtk_icon_source_set_direction_wildcarded( iconSource, TRUE );
0232                 gtk_icon_source_set_state_wildcarded( iconSource, TRUE );
0233 
0234                 // set size
0235                 if( sizeIter->first.empty() ) gtk_icon_source_set_size_wildcarded( iconSource, TRUE );
0236                 else {
0237 
0238                     GtkIconSize size = gtk_icon_size_from_name( sizeIter->first.c_str() );
0239                     if (size != GTK_ICON_SIZE_INVALID)
0240                     {
0241                         gtk_icon_source_set_size_wildcarded( iconSource, FALSE );
0242                         gtk_icon_source_set_size( iconSource, size );
0243                     }
0244                 }
0245 
0246                 // add source to iconSet
0247                 gtk_icon_set_add_source( iconSet, iconSource );
0248                 gtk_icon_source_free( iconSource );
0249                 break;
0250 
0251             }
0252 
0253         }
0254 
0255         // if nothing found, return;
0256         if( empty )
0257         {
0258 
0259             gtk_icon_set_unref( iconSet );
0260             return 0L;
0261 
0262         } else return iconSet;
0263 
0264     }
0265 
0266     //__________________________________________________________________
0267     std::string GtkIcons::generateString(
0268         const std::string& gtkIconName,
0269         const std::string& kdeIconName,
0270         const PathList& pathList ) const
0271     {
0272 
0273 
0274         if( kdeIconName == "NONE" ) return std::string();
0275 
0276         bool empty( true );
0277         std::ostringstream stockOut;
0278 
0279         // new stock
0280         stockOut << "  stock[\"" << gtkIconName << "\"]={" << std::endl;
0281 
0282         // loop over iconSizes
0283         for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
0284         {
0285 
0286             // generate full icon name
0287             std::ostringstream iconFileStream;
0288             iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;
0289 
0290             // loop over provided path to see if at least one icon is found
0291             bool found( false );
0292             for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
0293             {
0294                 std::string filename( *pathIter + '/' + iconFileStream.str() );
0295                 if( !std::ifstream( filename.c_str() ) ) continue;
0296                 found = true;
0297                 break;
0298             }
0299 
0300             if( !found ) continue;
0301             empty = false;
0302             if( sizeIter->first.empty() ) stockOut << "    { \"" << iconFileStream.str() << "\" }" << std::endl;
0303             else stockOut << "    { \"" << iconFileStream.str() << "\", *, *, \"" << sizeIter->first << "\" }," << std::endl;
0304 
0305 
0306         }
0307 
0308         stockOut << "  }" << std::endl;
0309 
0310         return empty ? std::string() : stockOut.str();
0311 
0312     }
0313 
0314 
0315 
0316 }