Merge lp:~erduende/gwibber/bitly-support into lp:gwibber

Proposed by Jesús Carmona
Status: Rejected
Rejected by: Ken VanDine
Proposed branch: lp:~erduende/gwibber/bitly-support
Merge into: lp:gwibber
Diff against target: 443 lines (+209/-26)
6 files modified
gwibber/microblog/dispatcher.py (+10/-0)
gwibber/microblog/urlshorter/__init__.py (+2/-1)
gwibber/microblog/urlshorter/bitly.py (+81/-0)
gwibber/microblog/util/const.py (+1/-0)
gwibber/preferences.py (+52/-1)
ui/gwibber-preferences-dialog.ui (+63/-24)
To merge this branch: bzr merge lp:~erduende/gwibber/bitly-support
Reviewer Review Type Date Requested Status
Ken VanDine Needs Resubmitting
Omer Akram (community) Needs Resubmitting
Review via email: mp+26412@code.launchpad.net

Description of the change

This patch adds bit.ly urlshorter support. In order to implement urlshorten service account authentication i have done some changes to preference window layout. Preferences windows now has a new tab named 'services' where you can find urlshorten configuration and perhaps add photo upload services in the future.

I have tried to follow your coding style but if you think something has to be changed, i'll change it to accomplish your requirements.

Waiting your considerations, i still have to finish text translations.

Sorry about my bad english ;)

To post a comment you must log in.
lp:~erduende/gwibber/bitly-support updated
749. By Jesús Carmona

Removed merge conflict tags from code

Revision history for this message
Omer Akram (om26er) wrote :

Can you please only make the branch for bit.ly currently there is no scope for an extra tab.

review: Needs Resubmitting
Revision history for this message
Ken VanDine (ken-vandine) wrote :

It would be great to support bit.ly, could you update this to work with current trunk and make it use oauth instead of username/password? Perhaps if you select bit.ly from the drop down you can show a link authorize it in a browser.

review: Needs Resubmitting

Unmerged revisions

749. By Jesús Carmona

Removed merge conflict tags from code

748. By Jesus Carmona <pixie@pixie-desktop>

Bazaar conflicts corrections??

747. By Jesús Carmona

Added auth error window before saving username and api key if validation data is incorrect

746. By Jesús Carmona

Removed gwibber's bitly account API KEY because it is not needed for validation

745. By Jesús Carmona

Added bit.ly account validation befrore preferences saving

- Created bit.ly gwibber account (gwibberservice)
- Added API KEY to bitly.py

744. By Jesus Carmona <pixie@pixie-desktop>

- Added support for Bit.ly url shorter service

- Added "services" tab to preferences ui
- Moved url shorter preferences to "services" tab
- Show/hide login and api key fields if protocol needs them
- Added "Get api key" link to bit.ly service
- Stores bit.ly username in couchdb settings db and api key in gnomekeyring

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'gwibber/microblog/dispatcher.py'
--- gwibber/microblog/dispatcher.py 2010-04-14 15:30:37 +0000
+++ gwibber/microblog/dispatcher.py 2010-05-31 11:52:27 +0000
@@ -20,6 +20,11 @@
20from util.const import *20from util.const import *
2121
22try:22try:
23 import gnomekeyring
24except:
25 gnomekeyring = None
26
27try:
23 import indicate28 import indicate
24except:29except:
25 indicate = None30 indicate = None
@@ -613,6 +618,11 @@
613 if self.IsShort(url): return url618 if self.IsShort(url): return url
614 try:619 try:
615 s = urlshorter.PROTOCOLS[service].URLShorter()620 s = urlshorter.PROTOCOLS[service].URLShorter()
621
622 if urlshorter.PROTOCOLS[service].PROTOCOL_INFO.get('authtype') == 'login':
623 urlshorter_key = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,{"id": SETTINGS['urlshorter_user']})[0].secret
624 s.auth(SETTINGS['urlshorter_user'], urlshorter_key)
625
616 return s.short(url)626 return s.short(url)
617 except: return url627 except: return url
618628
619629
=== modified file 'gwibber/microblog/urlshorter/__init__.py'
--- gwibber/microblog/urlshorter/__init__.py 2009-04-15 22:56:03 +0000
+++ gwibber/microblog/urlshorter/__init__.py 2010-05-31 11:52:27 +0000
@@ -1,5 +1,5 @@
11
2import cligs, isgd, tinyurlcom, trim, ur1ca2import cligs, isgd, tinyurlcom, trim, ur1ca, bitly
3#import snipurlcom, zima3#import snipurlcom, zima
44
5PROTOCOLS = {5PROTOCOLS = {
@@ -10,4 +10,5 @@
10 "tr.im": trim,10 "tr.im": trim,
11 "ur1.ca": ur1ca,11 "ur1.ca": ur1ca,
12 #"zi.ma": zima,12 #"zi.ma": zima,
13 "bit.ly": bitly,
13}14}
1415
=== added file 'gwibber/microblog/urlshorter/bitly.py'
--- gwibber/microblog/urlshorter/bitly.py 1970-01-01 00:00:00 +0000
+++ gwibber/microblog/urlshorter/bitly.py 2010-05-31 11:52:27 +0000
@@ -0,0 +1,81 @@
1
2"""
3
4Bit.ly interface for Gwibber
5erduende (Jesus Carmona) - 05/14/2010
6
7"""
8
9import gtk
10import urllib2
11import json
12
13
14PROTOCOL_INFO = {
15
16 "name": "bit.ly",
17 "version": 0.1,
18 "fqdn" : "http://bit.ly",
19
20 "config": [
21 "private:api_key",
22 "username",
23 ],
24
25 "authtype": "login",
26
27}
28
29API_SERVER = 'http://api.bit.ly/v3'
30API_KEY = "R_58b2b15d49b0f84f90fb03fcf8f82d23"
31
32class URLShorter:
33
34 def short(self, text):
35 shorten = json.loads(urllib2.urlopen(API_SERVER+"/shorten?login="+self.user+"&apiKey="+self.key+"&uri=%s&format=json" % urllib2.quote(text)).read())
36 if shorten.get('status_code') == 200:
37 return shorten['data']['url']
38 else:
39 return text
40
41 def validate(self):
42 validate = json.loads(urllib2.urlopen(API_SERVER+"/validate?x_login=%s&x_apiKey=%s&login=%s&apiKey=%s&format=json" % (self.urlshorter_login_entry.get_text(), self.urlshorter_apikey_entry.get_text(), self.urlshorter_login_entry.get_text(), self.urlshorter_apikey_entry.get_text())).read())
43 if validate.get('status_code') == 200:
44 return validate['data']['valid']
45 else:
46 return False
47
48 def auth(self, user=None, key=None):
49 self.user = user
50 self.key = key
51
52
53 def auth_ui(self, login=None, api_key=None):
54 self.urlshorter_auth_table = gtk.Table(3, 2, True)
55 urlshorter_login_label = gtk.Label("bit.ly login:")
56 self.urlshorter_login_entry = gtk.Entry()
57 if login:
58 self.urlshorter_login_entry.set_text(login)
59 self.urlshorter_auth_table.attach(urlshorter_login_label, 0, 1, 0, 1)
60 self.urlshorter_auth_table.attach(self.urlshorter_login_entry, 1, 2, 0, 1)
61
62 urlshorter_apikey_label = gtk.Label("bit.ly API key:")
63 self.urlshorter_apikey_entry = gtk.Entry()
64 if api_key:
65 self.urlshorter_apikey_entry.set_text(api_key)
66 self.urlshorter_auth_table.attach(urlshorter_apikey_label, 0, 1, 1, 2)
67 self.urlshorter_auth_table.attach(self.urlshorter_apikey_entry, 1, 2, 1, 2)
68
69 urlshorter_getkey_link = gtk.LinkButton("http://bit.ly/a/your_api_key", "Get your API key")
70 self.urlshorter_auth_table.attach(urlshorter_getkey_link, 1, 2, 2, 3, gtk.EXPAND)
71
72 return self.urlshorter_auth_table
73
74 def clear_ui(self):
75 self.urlshorter_auth_table.destroy()
76
77 def get_username(self):
78 return self.urlshorter_login_entry.get_text()
79
80 def get_password(self):
81 return self.urlshorter_apikey_entry.get_text()
082
=== modified file 'gwibber/microblog/util/const.py'
--- gwibber/microblog/util/const.py 2010-04-15 05:47:35 +0000
+++ gwibber/microblog/util/const.py 2010-05-31 11:52:27 +0000
@@ -13,6 +13,7 @@
13 "show_fullname": True,13 "show_fullname": True,
14 "shorten_urls": True,14 "shorten_urls": True,
15 "urlshorter": "is.gd",15 "urlshorter": "is.gd",
16 "urlshorter_user": None,
16 "reply_append_colon": True,17 "reply_append_colon": True,
17 "retweet_style": "recycle",18 "retweet_style": "recycle",
18 "global_retweet": False,19 "global_retweet": False,
1920
=== modified file 'gwibber/preferences.py'
--- gwibber/preferences.py 2010-05-03 04:25:30 +0000
+++ gwibber/preferences.py 2010-05-31 11:52:27 +0000
@@ -29,6 +29,11 @@
29from gwibber import util29from gwibber import util
30import gtk, gconf30import gtk, gconf
3131
32try:
33 import gnomekeyring
34except:
35 gnomekeyring = None
36
32import gettext37import gettext
33from gettext import lgettext as _38from gettext import lgettext as _
34if hasattr(gettext, 'bind_textdomain_codeset'):39if hasattr(gettext, 'bind_textdomain_codeset'):
@@ -37,6 +42,8 @@
3742
38from microblog.util.const import *43from microblog.util.const import *
39from microblog.urlshorter import PROTOCOLS as urlshorters44from microblog.urlshorter import PROTOCOLS as urlshorters
45from microblog.util.exceptions import GwibberProtocolError
46import microblog.urlshorter
4047
41from dbus.mainloop.glib import DBusGMainLoop48from dbus.mainloop.glib import DBusGMainLoop
4249
@@ -84,8 +91,24 @@
84 for urlshorter in urlshorters.keys(): self.urlshorter_selector.append_text(urlshorter)91 for urlshorter in urlshorters.keys(): self.urlshorter_selector.append_text(urlshorter)
85 self.ui.get_object("urlshorter_container").pack_start(self.urlshorter_selector, True, True)92 self.ui.get_object("urlshorter_container").pack_start(self.urlshorter_selector, True, True)
86 self.urlshorter_selector.set_active_iter(dict([(x[0].strip(), x.iter) for x in self.urlshorter_selector.get_model()]).get(self.settings["urlshorter"], self.urlshorter_selector.get_model().get_iter_root()))93 self.urlshorter_selector.set_active_iter(dict([(x[0].strip(), x.iter) for x in self.urlshorter_selector.get_model()]).get(self.settings["urlshorter"], self.urlshorter_selector.get_model().get_iter_root()))
94 self.urlshorter_selector.connect("changed", self.on_urlshorter_selector_changed, None)
87 self.urlshorter_selector.show_all()95 self.urlshorter_selector.show_all()
8896
97 active_urlshorter = urlshorters.get(self.urlshorter_selector.get_active_text())
98 if active_urlshorter.PROTOCOL_INFO.get('authtype') == 'login':
99 try:
100 urlshorter_user = str(self.settings.get("urlshorter_user"))
101 urlshorter_key = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET, {"id": urlshorter_user})[0].secret
102 except gnomekeyring.NoMatchError:
103 urlshorter_user = None
104 urlshorter_key = None
105
106 self.urlshorter_authenticated = active_urlshorter.URLShorter()
107 #self.urlshorter_auth_ui = self.urlshorter_authenticated.auth_ui(urlshorter_user, urlshorter_key)
108 self.ui.get_object("urlshorter_table").attach(self.urlshorter_authenticated.auth_ui(urlshorter_user, urlshorter_key), 0, 1, 2, 3)
109 else:
110 self.urlshorter_authenticated = None
111
89 self.retweet_style_selector = gtk.combo_box_new_text()112 self.retweet_style_selector = gtk.combo_box_new_text()
90 for format in RETWEET_FORMATS: self.retweet_style_selector.append_text(format)113 for format in RETWEET_FORMATS: self.retweet_style_selector.append_text(format)
91 self.ui.get_object("retweet_style_container").pack_start(self.retweet_style_selector, True, True)114 self.ui.get_object("retweet_style_container").pack_start(self.retweet_style_selector, True, True)
@@ -94,7 +117,26 @@
94117
95 def on_save_button_clicked(self, widget, data=None):118 def on_save_button_clicked(self, widget, data=None):
96 self.settings["interval"] = int(self.ui.get_object("interval").get_value())119 self.settings["interval"] = int(self.ui.get_object("interval").get_value())
97 120
121 # Only process authenticated url shorten service if url shorten is enabled
122 if self.ui.get_object("shorten_urls").get_property("active"):
123 if self.urlshorter_authenticated:
124 if self.urlshorter_authenticated.validate():
125 self.settings["urlshorter_user"] = self.urlshorter_authenticated.get_username()
126 gnomekeyring.item_create_sync(
127 gnomekeyring.get_default_keyring_sync(),
128 gnomekeyring.ITEM_GENERIC_SECRET,
129 "Gwibber pref: %s" % ('urlshorter'),
130 {"id": self.urlshorter_authenticated.get_username()},
131 self.urlshorter_authenticated.get_password(), True)
132 else:
133 GwibberProtocolError(type='auth', protocol='bit.ly', username=self.urlshorter_authenticated.get_username())
134 return
135 else:
136 # TODO: Erease urlshorter key from keyring
137 self.settings["urlshorter_user"] = None
138
139
98 # Only change autostart if it was already set before or if the user enabled it140 # Only change autostart if it was already set before or if the user enabled it
99 if self.gc.get("/apps/gwibber/autostart") is None:141 if self.gc.get("/apps/gwibber/autostart") is None:
100 if self.ui.get_object("autostart").get_property("active"):142 if self.ui.get_object("autostart").get_property("active"):
@@ -120,3 +162,12 @@
120 def on_prefs_dialog_destroy_event(self, widget, data=None):162 def on_prefs_dialog_destroy_event(self, widget, data=None):
121 gtk.main_quit()163 gtk.main_quit()
122164
165 def on_urlshorter_selector_changed(self, widget, data=None):
166 if urlshorters.get(widget.get_active_text()).PROTOCOL_INFO.get('authtype') == 'login':
167 self.urlshorter_authenticated = urlshorters.get(widget.get_active_text()).URLShorter()
168 self.ui.get_object("urlshorter_table").attach(self.urlshorter_authenticated.auth_ui(), 0, 1, 2, 3)
169 self.ui.get_object("urlshorter_table").show_all()
170 else:
171 if self.urlshorter_authenticated:
172 self.urlshorter_authenticated.clear_ui()
173 self.urlshorter_authenticated = None
123174
=== modified file 'ui/gwibber-preferences-dialog.ui'
--- ui/gwibber-preferences-dialog.ui 2010-03-05 17:54:43 +0000
+++ ui/gwibber-preferences-dialog.ui 2010-05-31 11:52:27 +0000
@@ -19,7 +19,6 @@
19 <child internal-child="vbox">19 <child internal-child="vbox">
20 <object class="GtkVBox" id="dialog-vbox1">20 <object class="GtkVBox" id="dialog-vbox1">
21 <property name="visible">True</property>21 <property name="visible">True</property>
22 <property name="orientation">vertical</property>
23 <property name="spacing">2</property>22 <property name="spacing">2</property>
24 <child>23 <child>
25 <object class="GtkNotebook" id="notebook1">24 <object class="GtkNotebook" id="notebook1">
@@ -30,7 +29,6 @@
30 <property name="visible">True</property>29 <property name="visible">True</property>
31 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>30 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
32 <property name="border_width">5</property>31 <property name="border_width">5</property>
33 <property name="orientation">vertical</property>
34 <property name="spacing">10</property>32 <property name="spacing">10</property>
35 <child>33 <child>
36 <object class="GtkFrame" id="frame1">34 <object class="GtkFrame" id="frame1">
@@ -217,13 +215,11 @@
217 <object class="GtkVBox" id="vbox1">215 <object class="GtkVBox" id="vbox1">
218 <property name="visible">True</property>216 <property name="visible">True</property>
219 <property name="border_width">5</property>217 <property name="border_width">5</property>
220 <property name="orientation">vertical</property>
221 <property name="spacing">5</property>218 <property name="spacing">5</property>
222 <child>219 <child>
223 <object class="GtkVBox" id="vbox7">220 <object class="GtkVBox" id="vbox7">
224 <property name="visible">True</property>221 <property name="visible">True</property>
225 <property name="border_width">5</property>222 <property name="border_width">5</property>
226 <property name="orientation">vertical</property>
227 <property name="spacing">5</property>223 <property name="spacing">5</property>
228 <child>224 <child>
229 <object class="GtkCheckButton" id="show_fullname">225 <object class="GtkCheckButton" id="show_fullname">
@@ -257,7 +253,6 @@
257 <child>253 <child>
258 <object class="GtkVBox" id="vbox3">254 <object class="GtkVBox" id="vbox3">
259 <property name="visible">True</property>255 <property name="visible">True</property>
260 <property name="orientation">vertical</property>
261 <child>256 <child>
262 <object class="GtkCheckButton" id="global_retweet">257 <object class="GtkCheckButton" id="global_retweet">
263 <property name="label" translatable="yes">Send retweets to all services</property>258 <property name="label" translatable="yes">Send retweets to all services</property>
@@ -273,7 +268,6 @@
273 <child>268 <child>
274 <object class="GtkVBox" id="retweet_style_container">269 <object class="GtkVBox" id="retweet_style_container">
275 <property name="visible">True</property>270 <property name="visible">True</property>
276 <property name="orientation">vertical</property>
277 <child>271 <child>
278 <placeholder/>272 <placeholder/>
279 </child>273 </child>
@@ -315,7 +309,6 @@
315 <object class="GtkTable" id="table16">309 <object class="GtkTable" id="table16">
316 <property name="visible">True</property>310 <property name="visible">True</property>
317 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>311 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
318 <property name="n_rows">3</property>
319 <property name="homogeneous">True</property>312 <property name="homogeneous">True</property>
320 <child>313 <child>
321 <object class="GtkCheckButton" id="reply_append_colon">314 <object class="GtkCheckButton" id="reply_append_colon">
@@ -326,11 +319,57 @@
326 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>319 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
327 <property name="draw_indicator">True</property>320 <property name="draw_indicator">True</property>
328 </object>321 </object>
329 <packing>
330 <property name="top_attach">2</property>
331 <property name="bottom_attach">3</property>
332 </packing>
333 </child>322 </child>
323 </object>
324 </child>
325 </object>
326 </child>
327 <child type="label">
328 <object class="GtkLabel" id="label79">
329 <property name="visible">True</property>
330 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
331 <property name="label" translatable="yes">&lt;b&gt;Advanced&lt;/b&gt;</property>
332 <property name="use_markup">True</property>
333 </object>
334 </child>
335 </object>
336 <packing>
337 <property name="expand">False</property>
338 <property name="position">2</property>
339 </packing>
340 </child>
341 </object>
342 <packing>
343 <property name="position">1</property>
344 </packing>
345 </child>
346 <child type="tab">
347 <object class="GtkLabel" id="messages_label">
348 <property name="visible">True</property>
349 <property name="label" translatable="yes">Messages</property>
350 </object>
351 <packing>
352 <property name="position">1</property>
353 <property name="tab_fill">False</property>
354 </packing>
355 </child>
356 <child>
357 <object class="GtkVBox" id="vbox6">
358 <property name="visible">True</property>
359 <child>
360 <object class="GtkFrame" id="frame6">
361 <property name="visible">True</property>
362 <property name="label_xalign">0</property>
363 <property name="shadow_type">none</property>
364 <child>
365 <object class="GtkAlignment" id="alignment5">
366 <property name="visible">True</property>
367 <property name="left_padding">12</property>
368 <child>
369 <object class="GtkTable" id="urlshorter_table">
370 <property name="visible">True</property>
371 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
372 <property name="n_rows">3</property>
334 <child>373 <child>
335 <object class="GtkCheckButton" id="shorten_urls">374 <object class="GtkCheckButton" id="shorten_urls">
336 <property name="label" translatable="yes">Automatically shorten pasted URLs using:</property>375 <property name="label" translatable="yes">Automatically shorten pasted URLs using:</property>
@@ -344,7 +383,6 @@
344 <child>383 <child>
345 <object class="GtkVBox" id="urlshorter_container">384 <object class="GtkVBox" id="urlshorter_container">
346 <property name="visible">True</property>385 <property name="visible">True</property>
347 <property name="orientation">vertical</property>
348 <property name="spacing">5</property>386 <property name="spacing">5</property>
349 <child>387 <child>
350 <placeholder/>388 <placeholder/>
@@ -355,36 +393,39 @@
355 <property name="bottom_attach">2</property>393 <property name="bottom_attach">2</property>
356 </packing>394 </packing>
357 </child>395 </child>
396 <child>
397 <placeholder/>
398 </child>
358 </object>399 </object>
359 </child>400 </child>
360 </object>401 </object>
361 </child>402 </child>
362 <child type="label">403 <child type="label">
363 <object class="GtkLabel" id="label79">404 <object class="GtkLabel" id="label2">
364 <property name="visible">True</property>405 <property name="visible">True</property>
365 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>406 <property name="label" translatable="yes">&lt;b&gt;URL Shortener&lt;/b&gt;</property>
366 <property name="label" translatable="yes">&lt;b&gt;Advanced&lt;/b&gt;</property>
367 <property name="use_markup">True</property>407 <property name="use_markup">True</property>
368 </object>408 </object>
369 </child>409 </child>
370 </object>410 </object>
371 <packing>411 <packing>
372 <property name="expand">False</property>412 <property name="expand">False</property>
373 <property name="position">2</property>413 <property name="fill">False</property>
414 <property name="position">0</property>
374 </packing>415 </packing>
375 </child>416 </child>
376 </object>417 </object>
377 <packing>418 <packing>
378 <property name="position">1</property>419 <property name="position">2</property>
379 </packing>420 </packing>
380 </child>421 </child>
381 <child type="tab">422 <child type="tab">
382 <object class="GtkLabel" id="messages_label">423 <object class="GtkLabel" id="services_label">
383 <property name="visible">True</property>424 <property name="visible">True</property>
384 <property name="label" translatable="yes">Messages</property>425 <property name="label" translatable="yes">Services</property>
385 </object>426 </object>
386 <packing>427 <packing>
387 <property name="position">1</property>428 <property name="position">2</property>
388 <property name="tab_fill">False</property>429 <property name="tab_fill">False</property>
389 </packing>430 </packing>
390 </child>431 </child>
@@ -392,7 +433,6 @@
392 <object class="GtkVBox" id="vbox5">433 <object class="GtkVBox" id="vbox5">
393 <property name="visible">True</property>434 <property name="visible">True</property>
394 <property name="border_width">5</property>435 <property name="border_width">5</property>
395 <property name="orientation">vertical</property>
396 <property name="spacing">10</property>436 <property name="spacing">10</property>
397 <child>437 <child>
398 <object class="GtkFrame" id="frame3">438 <object class="GtkFrame" id="frame3">
@@ -408,7 +448,6 @@
408 <object class="GtkVBox" id="theme_container">448 <object class="GtkVBox" id="theme_container">
409 <property name="visible">True</property>449 <property name="visible">True</property>
410 <property name="tooltip_text" translatable="yes">Select a message theme</property>450 <property name="tooltip_text" translatable="yes">Select a message theme</property>
411 <property name="orientation">vertical</property>
412 <property name="spacing">5</property>451 <property name="spacing">5</property>
413 <child>452 <child>
414 <placeholder/>453 <placeholder/>
@@ -432,7 +471,7 @@
432 </child>471 </child>
433 </object>472 </object>
434 <packing>473 <packing>
435 <property name="position">2</property>474 <property name="position">3</property>
436 </packing>475 </packing>
437 </child>476 </child>
438 <child type="tab">477 <child type="tab">
@@ -441,7 +480,7 @@
441 <property name="label" translatable="yes">Style</property>480 <property name="label" translatable="yes">Style</property>
442 </object>481 </object>
443 <packing>482 <packing>
444 <property name="position">2</property>483 <property name="position">3</property>
445 <property name="tab_fill">False</property>484 <property name="tab_fill">False</property>
446 </packing>485 </packing>
447 </child>486 </child>