Add maintainace tool for LXDE icon theme.
[lxde/lxde-icon-theme.git] / icon-migrate2.py
CommitLineData
1ae79b63
HJYP
1#!/usr/bin/env python
2#
3# icon-migrate.py
4#
5# Copyright 2009 PCMan <pcman.tw@gmail.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20# MA 02110-1301, USA.
21
22import xml.dom.minidom, os, sys, os.path, shutil
23from ConfigParser import *
24import filecmp
25import pygtk
26import gtk
27
28icon_theme_dir = ''
29size_dirs = []
30contexts = []
31
32all_mappings=[]
33
34subdirs = None
35
36exts = ['.png', '.svg', '.xpm']
37
38class Mapping:
39 def __init__(self, xml_icon_node):
40 self.new_name = xml_icon_node.getAttribute('name')
41 self.old_names = []
42 links = xml_icon_node.getElementsByTagName('link')
43 for link in links:
44 self.old_names.append(link.childNodes[0].data)
45
46class Context:
47 def __init__(self, xml_context_node):
48 self.name = xml_context_node.getAttribute('dir')
49 self.mappings = []
50 icon_nodes = xml_context_node.getElementsByTagName('icon')
51 for icon_node in icon_nodes:
52 mapping = Mapping(icon_node)
53 self.mappings.append(mapping)
54
55
56def find_icon_file_in_context_dir(context_dir, icon_name):
57 prefix = os.path.join(context_dir, icon_name)
58 for ext in exts:
59 file = prefix + ext
60 if os.path.exists(file):
61 return file
62 return None
63
64def find_icon_file_in_all_contexts(icon_name):
65 for size_dir in size_dirs:
66 sub_dirs = os.listdir(size_dir)
67 for sub_dir in sub_dirs:
68 if sub_dir[0] == '.':
69 continue
70 full_dir_name = os.path.join(size_dir, sub_dir)
71 ret = find_icon_file_in_context_dir(full_dir_name, icon_name)
72 if ret:
73 return ret
74 return None
75
76def replace_link_with_real_file(file):
77 real_path = os.path.realpath(file)
78 os.unlink(file)
79 shutil.copy2(real_path, file)
80 print 'copy %s to %s' % (real_path, file)
81
82
83def copy_old_name_to_new(context_dir, mapping, full_old_name):
84 full_new_name = os.path.join(context_dir, mapping.new_name);
85 if not os.path.exists(full_old_name):
86 return
87
88 (icon_name, ext) = os.path.splitext(full_old_name)
89 try:
90 shutil.copy2(full_old_name, full_new_name + ext)
91 finally:
92 print 'error copying %s to %s' % (full_old_name, full_new_name + ext)
93
94 for old_name in mapping.old_names:
95 full_path = os.path.join(context_dir, old_name + ext)
96 if full_path != full_old_name:
97 if not os.path.exists(full_path):
98 try:
99 shutil.copy2(full_old_name, full_path)
100 finally:
101 print 'error copying %s to %s' % (full_old_name, full_path)
102
103def convert_links_to_copies():
104 for size in sizes:
105 dirs = sizes[size]
106 for dir in dirs: # dirs of different contexts
107 dir = os.path.join(icon_theme_dir, dir)
108 files = os.listdir(dir)
109 for file in files:
110 for ext in exts:
111 if file.endswith(ext): # it's an image file
112 if os.path.islink(file):
113 replace_link_with_real_file(file)
114 break
115
116def is_icon_new_name(icon_name):
117 for context in contexts:
118 for mapping in context.mappings:
119 if mapping.new_name == icon_name:
120 return True
121 return False
122
123def convert_duplicated_files_to_symlinks():
124 # find duplicated files with fdupes
125 os.system( 'fdupes --nohidden -r nuoveXT2 > dups.txt' )
126 files = []
127 links = []
128
129 print '\n\n--- svn commands ---\n'
130
131 f = open( 'dups.txt', 'r' )
132 for line in f:
133 file = line.rstrip()
134 if file != '':
135 files.append(file)
136 else:
137 if len(files) == 0:
138 continue
139
140 files.sort( lambda x, y:len(x)-len(y) )
141
142 primary = ''
143 for file in files:
144 # if the file has a valid new name,
145 # symlink other duplicates to it
146 basename = os.path.basename(file)
147 (icon_name, ext) = os.path.splitext(basename)
148 # print 'is_icon_new_name %s' % icon_name
149 if is_icon_new_name(icon_name):
150 primary = file
151 break
152 if primary == '':
153 primary = files[0]
154 print 'svn --force add %s' % primary
155
156 for file in files:
157 if file != primary:
158 # os.path.unlink(file)
159
160 print 'svn --force rm %s' % file
161 dir = os.path.dirname(file)
162 rel_path = os.path.relpath(primary, dir)
163 # os.path.symlink(rel_path, file)
164
165 links.append( (rel_path, file) )
166 # print 'need to symlink(%s %s)' % (rel_path, file)
167
168 files = []
169
170 print '---------- symlinks ------------'
171 for link in links:
172 print '$(LN_S) -f %s $(DESTDIR)$(datadir)/icons/%s' % (link[0], link[1])
173
174
175def find_icon_file_in_dir(dir, icon_name):
176 for ext in exts: # try .png, .svg, and .xpm
177 base_name = icon_name + ext
178 fpath=os.path.join(dir, base_name)
179 if os.path.exists(fpath):
180 return fpath, ext
181 return None, None
182
183symlinks=[]
184files_to_del=[]
185
186sizes = {}
187
188def choose_icon(new_name, icons):
189 dlg=gtk.Dialog()
190 dlg.vbox.add(gtk.Label('Choose an icon for %s' % new_name))
191 idx = 0
192 for icon in icons:
193 btn = gtk.Button(icon[0])
194 btn.set_image(gtk.image_new_from_file(icon[0]))
195 btn.connect('clicked', lambda btn, dlg, idx: dlg.response(idx), dlg, idx)
196 idx = idx + 1
197 dlg.vbox.add(btn)
198 dlg.show_all()
199 ret = dlg.run()
200 dlg.destroy()
201 return ret if ret >= 0 else -1
202
203def find_icon_file_of_size(size, icon_name):
204 subdirs=sizes[size]
205 for sizedir in subdirs:
206 dir = os.path.join(icon_theme_dir, sizedir)
207 filename, ext = find_icon_file_in_dir(dir, icon_name)
208 if filename:
209 return filename, ext
210 return None, None
211
212def fix_icons_of_specified_size(size, subdir, mappings):
213 print 'fix icons in %s of size: %d' % (subdir, size)
214 for mapping in mappings:
215 dir = os.path.join(icon_theme_dir, subdir)
216 (fpath, ext) = find_icon_file_in_dir(dir, mapping.new_name)
217 if not fpath: # icon with new_name is not found, choose one icon from old_names
218 choices=[]
219 for old_name in mapping.old_names:
220 for subdir in sizes[size]:
221 dir = os.path.join(icon_theme_dir, subdir)
222 (fpath2, ext2) = find_icon_file_in_dir(dir, old_name)
223 if fpath2:
224 choices.append((fpath2, ext2))
225 if not choices:
226 print '%s is missing, need a new icon for it' % mapping.new_name
227 elif len(choices) == 1:
228 (fpath2, ext2) = choices[0]
229 fpath = os.path.join(icon_theme_dir, subdir, (mapping.new_name + ext2))
230 # copy old file name to new one
231 shutil.copy2(fpath2, fpath)
232 # print 'ls -s %s to %s' % (fpath, fpath2) # auto-choice
233 # rel = os.path.relpath(fpath, os.path.dirname(fpath2))
234 # symlinks.append((rel, fpath2))
235 else:
236 same = True
237 for i in range(len(choices) - 1):
238 # different file extensions
239 if choices[i][1] != choices[i+1][1]:
240 same = False
241 break
242 # different file content
243 if not filecmp.cmp(choices[i][0], choices[i+1][0], False):
244 same = False
245 break
246 if same: # they are actually the same image, can auto-choose
247 (fpath2, ext2) = choices[0]
248 fpath = os.path.join(icon_theme_dir, subdir, (mapping.new_name + ext2))
249 # copy old file name to new one
250 shutil.copy2(fpath2, fpath)
251 # print 'same img, symlink %s to %s' % (fpath, fpath2) # auto-choice
252 # rel = os.path.relpath(fpath, os.path.dirname(fpath2))
253 # symlinks.append((rel, fpath2))
254 else:
255 print 'choices:', choices
256 ret = choose_icon(mapping.new_name, choices)
257 if ret >=0:
258 fpath = os.path.join(icon_theme_dir, subdir, (mapping.new_name + choices[ret][1]))
259 shutil.copy2(choices[ret][0], fpath)
260 else:
261 print '%s is missing, need a new icon for it' % mapping.new_name
262 else:
263 print '%s is found: %s' % (mapping.new_name, fpath)
264 # create symlinks for old_names if they don't exist
265 for old_name in mapping.old_names:
266 # print 'find oldname:', old_name
267 (fpath2, ext2) = find_icon_file_of_size(size, old_name)
268 if not fpath2:
269 fpath2 = os.path.join(os.path.dirname(fpath), old_name + ext)
270 print 'copy newname to oldname: %s => %s' % (fpath, fpath2)
271 shutil.copy2(fpath, fpath2)
272
273# ----------------------------------------------------------------
274# start
275
276if len(sys.argv) < 2:
277 sys.exit()
278
279icon_theme_dir = sys.argv[1]
280cfg = SafeConfigParser()
281cfg.read(os.path.join(icon_theme_dir, 'index.theme'))
282subdirs = cfg.get('Icon Theme', 'Directories').split(',')
283
284for subdir in subdirs:
285 size=cfg.getint(subdir, 'Size')
286 if size in sizes:
287 sizes[size].append(subdir)
288 else:
289 l=[]
290 l.append(subdir)
291 sizes[size] = l
292
293sizes.keys().sort()
294
295# load the mappings
296doc = xml.dom.minidom.parse('legacy-icon-mapping.xml')
297context_nodes = doc.getElementsByTagName('context')
298for context_node in context_nodes:
299 context = Context(context_node)
300 contexts.append(context)
301
302# create a flat list of all available mappings
303for ctx in contexts:
304 for mapping in ctx.mappings:
305 all_mappings.append(mapping)
306
307# convert symlinks to real files
308convert_links_to_copies()
309# now, there is no symlink under the icon theme dir
310
311for size in sizes:
312 print 'for size: %d' % size
313 # all sub dirs containing icons of specified size
314 subdirs = sizes[size]
315
316 for ctx in contexts:
317 sub = None
318 # find subdir corresponding speficied context
319 for subdir in subdirs:
320 ctx_dirname = os.path.basename(subdir)
321 if ctx_dirname == ctx.name:
322 sub = subdir
323 break
324 if not sub:
325 sub = ctx.name
326 print 'missing context: %s' % sub
327
328 fix_icons_of_specified_size(size, sub, ctx.mappings)
329
330
331print '\n\n--------------------symlinks----------------------\n\n'
332
333convert_duplicated_files_to_symlinks()