1 /*
2 * this file is part of the oxygen gtk engine
3 * Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
4 *
5 * inspired notably from kdelibs/kdeui/color/kcolorutils.h
6 * Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
7 * Copyright (C) 2007 Thomas Zander <zander@kde.org>
8 * Copyright (C) 2007 Zack Rusin <zack@kde.org>
9 *
10 * This  library is free  software; you can  redistribute it and/or
11 * modify it  under  the terms  of the  GNU Lesser  General  Public
12 * License  as published  by the Free  Software  Foundation; either
13 * version 2 of the License, or( at your option ) any later version.
14 *
15 * This library is distributed  in the hope that it will be useful,
16 * but  WITHOUT ANY WARRANTY; without even  the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License  along  with  this library;  if not,  write to  the Free
22 * Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
23 * MA 02110-1301, USA.
24 */
25 
26 #include "oxygengtkicons.h"
27 #include "config.h"
28 
29 #include <algorithm>
30 #include <fstream>
31 #include <iostream>
32 #include <sstream>
33 
34 #include <gtk/gtk.h>
35 
36 namespace Oxygen
37 {
38 
39     //_________________________________________
GtkIcons(void)40     GtkIcons::GtkIcons( void ):
41         _factory( 0L ),
42         _dirty( true )
43     {
44 
45         // initialize sizes
46         _sizes.push_back( std::make_pair( "panel-menu", 16 ) );
47         _sizes.push_back( std::make_pair( "panel", 32 ) );
48         _sizes.push_back( std::make_pair( "gtk-small-toolbar", 22 ) );
49         _sizes.push_back( std::make_pair( "gtk-large-toolbar", 22 ) );
50         _sizes.push_back( std::make_pair( "gtk-dnd", 48 ) );
51         _sizes.push_back( std::make_pair( "gtk-button", 16 ) );
52         _sizes.push_back( std::make_pair( "gtk-menu", 16 ) );
53         _sizes.push_back( std::make_pair( "gtk-dialog", 32 ) );
54         _sizes.push_back( std::make_pair( "", 16 ) );
55 
56     }
57 
58     //_________________________________________
~GtkIcons(void)59     GtkIcons::~GtkIcons( void )
60     {
61         if( _factory )
62         { gtk_icon_factory_remove_default( _factory ); }
63     }
64 
65     //_________________________________________
setIconSize(const std::string & tag,unsigned int value)66     void GtkIcons::setIconSize( const std::string& tag, unsigned int value )
67     {
68         SizeMap::iterator iter( std::find_if( _sizes.begin(), _sizes.end(), SameTagFTor( tag ) ) );
69         if( iter == _sizes.end() ) {
70 
71             std::cerr << "GtkIcons::setIconSize - no match for" << tag << "," << value << std::endl;
72 
73         } else if( iter->second != value ) {
74 
75             iter->second = value;
76             _dirty = true;
77 
78         }
79 
80     }
81 
82     //_________________________________________
loadTranslations(const std::string & filename)83     void GtkIcons::loadTranslations( const std::string& filename )
84     {
85 
86         if( filename == _filename )
87         { return; }
88 
89         _filename = filename;
90         _dirty = true;
91         _icons.clear();
92 
93         std::ifstream in( filename.c_str() );
94         if( !in )
95         {
96             std::cerr << "Oxygen::GtkIcons::loadTranslations - could not open " << filename << std::endl;
97             return;
98         }
99 
100         std::string line;
101         while( std::getline( in, line, '\n' ) )
102         {
103 
104             if( line.empty() ) continue;
105 
106             IconPair iconPair;
107             std::istringstream stream( line.c_str() );
108             stream >> iconPair.first >> iconPair.second;
109             if( ( stream.rdstate() & std::ios::failbit ) != 0 )
110             { continue; }
111 
112             _icons.insert( iconPair );
113 
114         }
115 
116     }
117 
118     //_________________________________________
generate(const PathList & pathList)119     Gtk::RC GtkIcons::generate( const PathList& pathList )
120     {
121 
122         if( (!_dirty) && _pathList == pathList ) return _rc;
123 
124         #if OXYGEN_DEBUG
125         std::cerr << "Oxygen::GtkIcons::generate - regenerating translation tables" << std::endl;
126         #endif
127 
128         _pathList = pathList;
129         _rc.clear();
130 
131         // reset factory
132         if( _factory )
133         {
134             gtk_icon_factory_remove_default( _factory );
135             g_object_unref( G_OBJECT( _factory ) );
136         }
137 
138         // create new
139         _factory = gtk_icon_factory_new();
140 
141         // generate icon size string
142         std::ostringstream iconSizesStr;
143         for( SizeMap::const_iterator iter = _sizes.begin(); iter != _sizes.end(); ++iter )
144         {
145             if( iter->first.empty() ) continue;
146             if( iter != _sizes.begin() ) iconSizesStr << ": ";
147             iconSizesStr << iter->first << " = " << iter->second << "," << iter->second;
148         }
149 
150         // pass to settings
151         GtkSettings* settings( gtk_settings_get_default() );
152         gtk_settings_set_string_property( settings, "gtk-icon-sizes", iconSizesStr.str().c_str(), "oxygen-gtk" );
153 
154         // generate pixmap path
155         // this must be passed to gtk before any icon settings, otherwise
156         // other icons are not recognized
157         std::ostringstream pixmapPathStr;
158         pixmapPathStr << "pixmap_path \"";
159         for( PathList::const_iterator iter = pathList.begin(); iter != pathList.end(); ++iter )
160         {
161             #if OXYGEN_DEBUG
162             std::cerr << "Oxygen::GtkIcons::generate - adding path: " << *iter << std::endl;
163             #endif
164 
165             if( iter != pathList.begin() ) pixmapPathStr << ":";
166             pixmapPathStr << *iter;
167         }
168         pixmapPathStr << "\"";
169         _rc.addToHeaderSection( pixmapPathStr.str() );
170 
171         // loop over icons
172         bool empty( true );
173         for( IconMap::const_iterator iconIter = _icons.begin(); iconIter != _icons.end(); ++iconIter )
174         {
175 
176             GtkIconSet* iconSet( generate( iconIter->first, iconIter->second, pathList ) );
177             if( iconSet )
178             {
179                 gtk_icon_factory_add( _factory, iconIter->first.c_str(), iconSet );
180                 gtk_icon_set_unref( iconSet );
181                 empty = false;
182             }
183 
184         }
185 
186         if( empty )
187         {
188 
189             g_object_unref( G_OBJECT( _factory ) );
190             _factory = 0L;
191 
192         } else gtk_icon_factory_add_default( _factory );
193 
194         // extra settings for entries
195         std::string stock( generateString( "gtk-clear", "actions/edit-clear-locationbar-rtl.png", pathList ) );
196         if( !stock.empty() )
197         {
198             _rc.addSection( "oxygen-icons-editor", Gtk::RC::defaultSection() );
199             _rc.addToCurrentSection( stock );
200             _rc.addToRootSection( "class \"*Entry*\" style \"oxygen-icons-editor\"" );
201         }
202 
203         _dirty = false;
204         return _rc;
205 
206     }
207 
208     //__________________________________________________________________
generate(const std::string & gtkIconName,const std::string & kdeIconName,const PathList & pathList) const209     GtkIconSet* GtkIcons::generate(
210         const std::string& gtkIconName,
211         const std::string& kdeIconName,
212         const PathList& pathList ) const
213     {
214 
215 
216         if( kdeIconName == "NONE" ) return 0L;
217 
218         bool empty( true );
219 
220         // create iconSet
221         GtkIconSet* iconSet = gtk_icon_set_new();
222 
223         // loop over iconSizes
224         for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
225         {
226 
227             // generate full icon name
228             std::ostringstream iconFileStream;
229             iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;
230 
231             // loop over provided path to see if at least one icon is found
232             for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
233             {
234                 std::string filename( *pathIter + '/' + iconFileStream.str() );
235                 if( !std::ifstream( filename.c_str() ) ) continue;
236 
237                 empty = false;
238                 GtkIconSource* iconSource( gtk_icon_source_new() );
239 
240                 // set name
241                 gtk_icon_source_set_filename( iconSource, filename.c_str() );
242 
243                 // set direction and state wildcarded
244                 gtk_icon_source_set_direction_wildcarded( iconSource, TRUE );
245                 gtk_icon_source_set_state_wildcarded( iconSource, TRUE );
246 
247                 // set size
248                 if( sizeIter->first.empty() ) gtk_icon_source_set_size_wildcarded( iconSource, TRUE );
249                 else {
250 
251                     GtkIconSize size = gtk_icon_size_from_name( sizeIter->first.c_str() );
252                     if (size != GTK_ICON_SIZE_INVALID)
253                     {
254                         gtk_icon_source_set_size_wildcarded( iconSource, FALSE );
255                         gtk_icon_source_set_size( iconSource, size );
256                     }
257                 }
258 
259                 // add source to iconSet
260                 gtk_icon_set_add_source( iconSet, iconSource );
261                 gtk_icon_source_free( iconSource );
262                 break;
263 
264             }
265 
266         }
267 
268         // if nothing found, return;
269         if( empty )
270         {
271 
272             gtk_icon_set_unref( iconSet );
273             return 0L;
274 
275         } else return iconSet;
276 
277     }
278 
279     //__________________________________________________________________
generateString(const std::string & gtkIconName,const std::string & kdeIconName,const PathList & pathList) const280     std::string GtkIcons::generateString(
281         const std::string& gtkIconName,
282         const std::string& kdeIconName,
283         const PathList& pathList ) const
284     {
285 
286 
287         if( kdeIconName == "NONE" ) return std::string();
288 
289         bool empty( true );
290         std::ostringstream stockOut;
291 
292         // new stock
293         stockOut << "  stock[\"" << gtkIconName << "\"]={" << std::endl;
294 
295         // loop over iconSizes
296         for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
297         {
298 
299             // generate full icon name
300             std::ostringstream iconFileStream;
301             iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;
302 
303             // loop over provided path to see if at least one icon is found
304             bool found( false );
305             for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
306             {
307                 std::string filename( *pathIter + '/' + iconFileStream.str() );
308                 if( !std::ifstream( filename.c_str() ) ) continue;
309                 found = true;
310                 break;
311             }
312 
313             if( !found ) continue;
314             empty = false;
315             if( sizeIter->first.empty() ) stockOut << "    { \"" << iconFileStream.str() << "\" }" << std::endl;
316             else stockOut << "    { \"" << iconFileStream.str() << "\", *, *, \"" << sizeIter->first << "\" }," << std::endl;
317 
318 
319         }
320 
321         stockOut << "  }" << std::endl;
322 
323         return empty ? std::string() : stockOut.str();
324 
325     }
326 
327 
328 
329 }
330