ItemEditor: Make the dialog names the same
[alacarte.git] / Alacarte / ItemEditor.py
bloba41ab59ae2cd78231ace782d6400d500735d32f1
1 # -*- coding: utf-8 -*-
2 # Alacarte Menu Editor - Simple fd.o Compliant Menu Editor
3 # Copyright (C) 2013 Red Hat, Inc.
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Library General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Library General Public License for more details.
15 # You should have received a copy of the GNU Library General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 import gettext
20 import os
21 from gi.repository import GLib, Gtk
22 from Alacarte import config, util
24 from gi._glib import GError
26 _ = gettext.gettext
28 EXTENSIONS = (".png", ".xpm", ".svg")
30 def try_icon_name(filename):
31 # Detect if the user picked an icon, and make
32 # it into an icon name.
33 if not filename.endswith(EXTENSIONS):
34 return filename
36 filename = filename[:-4]
38 theme = Gtk.IconTheme.get_default()
39 resolved_path = None
40 for path in theme.get_search_path():
41 if filename.startswith(path):
42 resolved_path = filename[len(path):].lstrip(os.sep)
43 break
45 if resolved_path is None:
46 return filename
48 parts = resolved_path.split(os.sep)
49 # icon-theme/size/category/icon
50 if len(parts) != 4:
51 return filename
53 return parts[3]
55 def get_icon_string(image):
56 filename = image.props.file
57 if filename is not None:
58 return try_icon_name(filename)
60 return image.props.icon_name
62 def strip_extensions(icon):
63 if icon.endswith(EXTENSIONS):
64 return icon[:-4]
65 else:
66 return icon
68 def set_icon_string(image, icon):
69 if GLib.path_is_absolute(icon):
70 image.props.file = icon
71 else:
72 image.props.icon_name = strip_extensions(icon)
74 DESKTOP_GROUP = GLib.KEY_FILE_DESKTOP_GROUP
76 # XXX - replace with a better UI eventually
77 class IconPicker(object):
78 def __init__(self, dialog, button, image):
79 self.dialog = dialog
80 self.button = button
81 self.button.connect('clicked', self.pick_icon)
82 self.image = image
84 def pick_icon(self, button):
85 chooser = Gtk.FileChooserDialog(title=_("Choose an icon"),
86 parent=self.dialog,
87 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
88 Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
89 response = chooser.run()
90 if response == Gtk.ResponseType.ACCEPT:
91 self.image.props.file = chooser.get_filename()
92 chooser.destroy()
94 class ItemEditor(object):
95 def get_keyfile_edits(self):
96 raise NotImplementedError()
98 def set_text(self, ctl, name):
99 try:
100 val = self.keyfile.get_string(DESKTOP_GROUP, name)
101 except GError:
102 pass
103 else:
104 self.builder.get_object(ctl).set_text(val)
106 def set_check(self, ctl, name):
107 try:
108 val = self.keyfile.get_boolean(DESKTOP_GROUP, name)
109 except GError:
110 pass
111 else:
112 self.builder.get_object(ctl).set_active(val)
114 def set_icon(self, ctl, name):
115 try:
116 val = self.keyfile.get_string(DESKTOP_GROUP, name)
117 except GError:
118 pass
119 else:
120 set_icon_string(self.builder.get_object(ctl), val)
122 def load(self):
123 self.keyfile = GLib.KeyFile()
124 try:
125 self.keyfile.load_from_file(self.item_path, util.KEY_FILE_FLAGS)
126 except GError:
127 pass
129 def save(self):
130 util.fillKeyFile(self.keyfile, self.get_keyfile_edits())
131 contents, length = self.keyfile.to_data()
132 with open(self.item_path, 'w') as f:
133 f.write(contents)
135 def run(self):
136 self.dialog.present()
138 def on_response(self, dialog, response):
139 if response == Gtk.ResponseType.OK:
140 self.save()
141 self.dialog.destroy()
143 class LauncherEditor(ItemEditor):
144 def __init__(self, parent, item_path):
145 self.builder = Gtk.Builder()
146 self.builder.add_from_file(os.path.join(config.pkgdatadir, 'launcher-editor.ui'))
148 self.dialog = self.builder.get_object('editor')
149 self.dialog.set_transient_for(parent)
150 self.dialog.connect('response', self.on_response)
152 self.icon_picker = IconPicker(self.dialog,
153 self.builder.get_object('icon-button'),
154 self.builder.get_object('icon-image'))
156 self.builder.get_object('exec-browse').connect('clicked', self.pick_exec)
158 self.builder.get_object('name-entry').connect('changed', self.resync_validity)
159 self.builder.get_object('exec-entry').connect('changed', self.resync_validity)
161 self.item_path = item_path
162 self.load()
163 self.resync_validity()
165 def resync_validity(self, *args):
166 name_text = self.builder.get_object('name-entry').get_text()
167 exec_text = self.builder.get_object('exec-entry').get_text()
168 valid = (name_text is not None and exec_text is not None)
169 self.builder.get_object('ok').set_sensitive(valid)
171 def load(self):
172 super(LauncherEditor, self).load()
173 self.set_text('name-entry', "Name")
174 self.set_text('exec-entry', "Exec")
175 self.set_text('comment-entry', "Comment")
176 self.set_check('terminal-check', "Terminal")
177 self.set_icon('icon-image', "Icon")
179 def get_keyfile_edits(self):
180 return dict(Name=self.builder.get_object('name-entry').get_text(),
181 Exec=self.builder.get_object('exec-entry').get_text(),
182 Comment=self.builder.get_object('comment-entry').get_text(),
183 Terminal=self.builder.get_object('terminal-check').get_active(),
184 Icon=get_icon_string(self.builder.get_object('icon-image')),
185 Type="Application")
187 def pick_exec(self, button):
188 chooser = Gtk.FileChooserDialog(title=_("Choose a command"),
189 parent=self.dialog,
190 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
191 Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
192 response = chooser.run()
193 if response == Gtk.ResponseType.ACCEPT:
194 self.builder.get_object('exec-entry').set_text(chooser.get_filename())
195 chooser.destroy()
197 class DirectoryEditor(ItemEditor):
198 def __init__(self, parent, item_path):
199 self.builder = Gtk.Builder()
200 self.builder.add_from_file(os.path.join(config.pkgdatadir, 'directory-editor.ui'))
202 self.dialog = self.builder.get_object('editor')
203 self.dialog.set_transient_for(parent)
204 self.dialog.connect('response', self.on_response)
206 self.icon_picker = IconPicker(self.dialog,
207 self.builder.get_object('icon-button'),
208 self.builder.get_object('icon-image'))
210 self.builder.get_object('name-entry').connect('changed', self.resync_validity)
212 self.item_path = item_path
213 self.load()
214 self.resync_validity()
216 def resync_validity(self, *args):
217 name_text = self.builder.get_object('name-entry').get_text()
218 valid = (name_text is not None)
219 self.builder.get_object('ok').set_sensitive(valid)
221 def load(self):
222 super(DirectoryEditor, self).load()
223 self.set_text('name-entry', "Name")
224 self.set_text('comment-entry', "Comment")
225 self.set_icon('icon-image', "Icon")
227 def get_keyfile_edits(self):
228 return dict(Name=self.builder.get_object('name-entry').get_text(),
229 Comment=self.builder.get_object('comment-entry').get_text(),
230 Icon=get_icon_string(self.builder.get_object('icon-image')),
231 Type="Directory")
233 def test_editor(path):
234 if path.endswith('.directory'):
235 return DirectoryEditor(path)
236 elif path.endswith('.desktop'):
237 return LauncherEditor(path)
238 else:
239 raise ValueError("Invalid filename, %r" % (path,))
241 def test():
242 import sys
244 Gtk.Window.set_default_icon_name('alacarte')
245 editor = test_editor(sys.argv[1])
246 editor.dialog.connect('destroy', Gtk.main_quit)
247 editor.run()
248 Gtk.main()
250 if __name__ == "__main__":
251 test()