示例#1
0
    def __init__(self, controller, extend=False):
        BaseGui.__init__(self, controller)
        gtk.Window.__init__(self)

        self.extend = extend and extend_mode

        self.set_title('Turpial')
        self.set_size_request(280, 350)
        self.set_default_size(320, 480)
        self.current_width = 320
        self.current_height = 480
        self.set_icon(self.load_image('turpial_icon.png', True))
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('delete-event', self.__close)
        self.connect('size-request', self.size_request)
        self.connect('configure-event', self.move_event)
        self.connect('mykeypress', self.__on_key_press)

        self.mode = 0
        self.vbox = None
        self.contentbox = gtk.VBox(False)

        # Valores de config. por defecto
        self.showed = True
        self.minimize = 'on'
        self.workspace = 'single'
        self.link_color = 'ff6633'
        self.home_interval = -1
        self.replies_interval = -1
        self.directs_interval = -1
        self.me = None
        self.version = None

        self.home_timer = None
        self.replies_timer = None
        self.directs_timer = None

        self.notify = Notification()

        self.home = Home(self, self.workspace)
        self.profile = Profile(self)
        self.contenido = self.home
        self.updatebox = UpdateBox(self)
        self.replybox = ReplyBox(self)

        if self.extend:
            log.debug('Cargado modo GTK Extendido')
            self.browser = OAuthWindow(self)
        else:
            log.debug('Cargado modo GTK Simple')

        self.dock = Dock(self, self.workspace)
        self.__create_trayicon()
示例#2
0
文件: main.py 项目: Art3mis4/Turpial
 def __init__(self, controller, extend=False):
     BaseGui.__init__(self, controller)
     gtk.Window.__init__(self)
     
     self.extend = extend and extend_mode
     
     self.set_title('Turpial')
     self.set_size_request(280, 350)
     self.set_default_size(320, 480)
     self.current_width = 320
     self.current_height = 480
     self.set_icon(self.load_image('turpial_icon.png', True))
     self.set_position(gtk.WIN_POS_CENTER)
     self.connect('delete-event', self.__close)
     self.connect('size-request', self.size_request)
     self.connect('configure-event', self.move_event)
     self.connect('mykeypress', self.__on_key_press)
     
     self.mode = 0
     self.vbox = None
     self.contentbox = gtk.VBox(False)
     
     # Valores de config. por defecto
     self.showed = True
     self.minimize = 'on'
     self.workspace = 'single'
     self.link_color = 'ff6633'
     self.home_interval = -1
     self.replies_interval = -1
     self.directs_interval = -1
     self.me = None
     self.version = None
     
     self.home_timer = None
     self.replies_timer = None
     self.directs_timer = None
     
     self.notify = Notification()
     
     self.home = Home(self, self.workspace)
     self.profile = Profile(self)
     self.contenido = self.home
     self.updatebox = UpdateBox(self)
     self.replybox = ReplyBox(self)
     
     if self.extend:
         log.debug('Cargado modo GTK Extendido')
         self.browser = OAuthWindow(self)
     else:
         log.debug('Cargado modo GTK Simple')
     
     self.dock = Dock(self, self.workspace)
     self.__create_trayicon()
示例#3
0
文件: main.py 项目: Art3mis4/Turpial
class Main(BaseGui, gtk.Window):
    __gsignals__ = dict(mykeypress=(gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION, None, (str,)))

    def __init__(self, controller, extend=False):
        BaseGui.__init__(self, controller)
        gtk.Window.__init__(self)
        
        self.extend = extend and extend_mode
        
        self.set_title('Turpial')
        self.set_size_request(280, 350)
        self.set_default_size(320, 480)
        self.current_width = 320
        self.current_height = 480
        self.set_icon(self.load_image('turpial_icon.png', True))
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('delete-event', self.__close)
        self.connect('size-request', self.size_request)
        self.connect('configure-event', self.move_event)
        self.connect('mykeypress', self.__on_key_press)
        
        self.mode = 0
        self.vbox = None
        self.contentbox = gtk.VBox(False)
        
        # Valores de config. por defecto
        self.showed = True
        self.minimize = 'on'
        self.workspace = 'single'
        self.link_color = 'ff6633'
        self.home_interval = -1
        self.replies_interval = -1
        self.directs_interval = -1
        self.me = None
        self.version = None
        
        self.home_timer = None
        self.replies_timer = None
        self.directs_timer = None
        
        self.notify = Notification()
        
        self.home = Home(self, self.workspace)
        self.profile = Profile(self)
        self.contenido = self.home
        self.updatebox = UpdateBox(self)
        self.replybox = ReplyBox(self)
        
        if self.extend:
            log.debug('Cargado modo GTK Extendido')
            self.browser = OAuthWindow(self)
        else:
            log.debug('Cargado modo GTK Simple')
        
        self.dock = Dock(self, self.workspace)
        self.__create_trayicon()
        
    def __create_trayicon(self):
        if gtk.check_version(2, 10, 0) is not None:
            self.log.debug("Disabled Tray Icon. It needs PyGTK >= 2.10.0")
            return
        self.tray = gtk.StatusIcon()
        self.tray.set_from_pixbuf(self.load_image('turpial_icon.png', True))
        self.tray.set_tooltip('Turpial')
        self.tray.connect("activate", self.__on_trayicon_click)
        self.tray.connect("popup-menu", self.__show_tray_menu)
        
        
    def __on_trayicon_click(self, widget):
        if(self.showed is True):
            self.showed = False
            self.hide()
        else:
            self.showed = True
            self.show()
            #self.present()
            
    def __show_tray_menu(self, widget, button, activate_time):
        menu = gtk.Menu()
        tweet = gtk.MenuItem(_('Tweet'))
        exit = gtk.MenuItem(_('Exit'))
        if self.mode == 2:
            menu.append(tweet)
        menu.append(exit)
        
        exit.connect('activate', self.quit)
        tweet.connect('activate', self.__show_update_box_from_menu)
            
        menu.show_all()
        menu.popup(None, None, None, button, activate_time)
        
    def __show_update_box_from_menu(self, widget):
        self.show_update_box()
        
    def __close(self, widget, event=None):
        if self.minimize == 'on':
            self.showed = False
            self.hide()
        else:
            self.quit(widget)
        return True
        
    def __save_size(self):
        if self.mode < 2: return
        wide_value = "%i, %i" % (self.wide_win_size[0], self.wide_win_size[1])
        single_value = "%i, %i" % (self.single_win_size[0], self.single_win_size[1])
        pos_value = "%i, %i" % (self.win_pos[0], self.win_pos[1])
        log.debug('Guardando tamaño de la ventana')
        log.debug('--Single: %s' % single_value)
        log.debug('--Wide: %s' % wide_value)
        log.debug('--Position: %s' % pos_value)
        self.save_config({'General': {'single-win-size': single_value,
            'wide-win-size': wide_value, 'window-position': pos_value}},
            update=False)
            
    def __toogle_remember(self, widget):
        if self.remember.get_active():
            self.username.set_sensitive(False)
            self.password.set_sensitive(False)
        else:
            self.username.set_sensitive(True)
            self.password.set_sensitive(True)
            
    def load_image(self, path, pixbuf=False):
        img_path = os.path.realpath(os.path.join(os.path.dirname(__file__),
            '..', '..', 'data', 'pixmaps', path))
        pix = gtk.gdk.pixbuf_new_from_file(img_path)
        if pixbuf: return pix
        avatar = gtk.Image()
        avatar.set_from_pixbuf(pix)
        del pix
        return avatar
        
    def load_avatar(self, dir, path, image=False):
        img_path = os.path.join(dir, path)
        pix = gtk.gdk.pixbuf_new_from_file(img_path)
        if not image: return pix
        avatar = gtk.Image()
        avatar.set_from_pixbuf(pix)
        del pix
        return avatar
    
    def resize_avatar(self, pic):
        ext = pic[-3:].lower()
        fullname = os.path.join(self.imgdir, pic)
        
        orig = gtk.gdk.pixbuf_new_from_file(fullname)
        pw, ph = orig.get_width(), orig.get_height()
        
        if pw >= ph:
            ratio = float(ph) / pw
            fw = util.AVATAR_SIZE
            fh = int(fw * ratio)
        else:
            ratio = float(pw) / ph
            fh = util.AVATAR_SIZE
            fw = int(fh * ratio)
            
        dest = orig.scale_simple(fw, fh, gtk.gdk.INTERP_BILINEAR)
        dest.save(fullname, 'png')
        
        del orig
        del dest
        
    def request_conversation(self, twt_id, user):
        self.replybox.show(twt_id, user)
        BaseGui.request_conversation(self, twt_id, user)
        
    def get_user_avatar(self, user, pic_url):
        pix = self.request_user_avatar(user, pic_url)
        if pix:
            # Try to load user avatar from file. If fail (by corrupt data, etc)
            # then load default image
            try:
                return self.load_avatar(self.imgdir, pix)
            except:
                return self.load_image('unknown.png', pixbuf=True)
        else:
            return self.load_image('unknown.png', pixbuf=True)
    
    def quit(self, widget):
        self.__save_size()
        gtk.main_quit()
        self.destroy()
        self.tray = None
        self.request_signout()
        
    def main_loop(self):
        #gtk.gdk.threads_enter()
        gtk.main()
        #gtk.gdk.threads_leave()
        
    def show_login(self, global_config):

        self.mode = 1
        if self.vbox is not None: self.remove(self.vbox)
        
        avatar = self.load_image('logo2.png')
        self.message = LoginLabel(self)
        
        lbl_user = gtk.Label()
        #lbl_user.set_justify(gtk.JUSTIFY_LEFT)
        lbl_user.set_use_markup(True)
        lbl_user.set_markup(u'<span size="small">%s</span>' % _('User and Password'))
        lbl_user.set_alignment(0, 0.5)
        
        self.username = gtk.Entry()
        self.password = gtk.Entry()
        self.password.set_visibility(False)
        
        self.remember = gtk.CheckButton(_('Remember my credentials'))
        
        self.btn_oauth = gtk.Button(_('Connect'))
        
        self.btn_settings = gtk.Button()
        self.btn_settings.set_relief(gtk.RELIEF_NONE)
        self.btn_settings.set_tooltip_text(_('Preferences'))
        self.btn_settings.set_image(self.load_image('settings-single.png'))
        settings_box = gtk.Alignment(xalign=1.0, yalign=0.5)
        settings_box.set_padding(70, 10, 40, 40)
        settings_box.add(self.btn_settings)
        
        self.waiting = CairoWaiting(self)
        align = gtk.Alignment(xalign=1, yalign=0.5)
        align.add(self.waiting)
        
        hbox = gtk.HBox(False)
        hbox.pack_start(lbl_user, False, False, 2)
        hbox.pack_start(align, True, True, 2)
        
        table = gtk.Table(10, 1, False)
        table.attach(avatar, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 10, 50)
        table.attach(self.message, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL, 20, 3)
        table.attach(hbox, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL, 50, 0)
        table.attach(self.username, 0, 1, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL, 50, 0)
        table.attach(self.password, 0, 1, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL, 50, 0)
        table.attach(self.btn_oauth, 0, 1, 7, 8, gtk.EXPAND, gtk.FILL, 0, 10)
        table.attach(self.remember, 0, 1, 8, 9, gtk.EXPAND, gtk.FILL, 0, 10)
        table.attach(settings_box, 0, 1, 9, 10, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 10)
        
        self.vbox = gtk.VBox(False, 5)
        self.vbox.pack_start(table, False, False, 2)
        
        self.add(self.vbox)
        self.show_all()
        
        self.btn_oauth.connect('clicked', self.oauth)
        self.password.connect('activate', self.oauth)
        self.remember.connect("toggled", self.__toogle_remember)
        self.btn_settings.connect('clicked', self.show_preferences, 'global')
        
        username = global_config.read('Login', 'username')
        password = global_config.read('Login', 'password')
        if username != '' and password != '':
            self.username.set_text(username)
            self.password.set_text(base64.b64decode(password))
            self.remember.set_active(True)
        
    def signin(self, widget, username, password):
        self.message.deactivate()
        self.waiting.start()
        self.btn_oauth.set_sensitive(False)
        self.username.set_sensitive(False)
        self.password.set_sensitive(False)
        self.request_signin(username.get_text(), password.get_text())
        
    def oauth(self, widget):
        self.message.deactivate()
        self.waiting.start()
        self.btn_oauth.set_sensitive(False)
        self.username.set_sensitive(False)
        self.password.set_sensitive(False)
        self.remember.set_sensitive(False)
        self.btn_settings.set_sensitive(False)
        self.request_oauth(self.username.get_text(), self.password.get_text(), self.remember.get_active())
        
    def cancel_login(self, error):
        self.message.set_error(error)
        self.waiting.stop(error=True)
        self.btn_oauth.set_sensitive(True)
        self.remember.set_sensitive(True)
        self.btn_settings.set_sensitive(True)
        if not self.remember.get_active():
            self.username.set_sensitive(True)
            self.password.set_sensitive(True)
        
    def show_main(self, config, global_cfg, p):
        log.debug('Cargando ventana principal')
        self.mode = 2
        
        self.update_config(config, global_cfg, True)
        
        gtk.gdk.threads_enter()
        self.contentbox.add(self.contenido)
        
        self.statusbar = gtk.Statusbar()
        self.statusbar.push(0, _('Wait a few seconds while I load everything...'))
        if (self.vbox is not None): self.remove(self.vbox)
        
        self.vbox = gtk.VBox(False, 0)
        self.vbox.pack_start(self.contentbox, True, True, 0)
        self.vbox.pack_start(self.dock, False, False, 0)
        self.vbox.pack_start(self.statusbar, False, False, 0)
        
        self.profile.set_user_profile(p)
        self.me = p['screen_name']
        title = 'Turpial - %s' % self.me
        self.set_title(title)
        self.tray.set_tooltip(title)
        
        if config.read('General', 'profile-color') == 'on':
            self.link_color = p['profile_link_color']
        
        self.add(self.vbox)
        self.show_all()
        if (self.win_pos[0] > 0 and self.win_pos[1] > 0):
            self.move(self.win_pos[0], self.win_pos[1])
        gtk.gdk.threads_leave()
        
        self.notify.login(p)
        
        gobject.timeout_add(6 * 60 * 1000, self.download_rates)
        
    def show_home(self, widget):
        self.contentbox.remove(self.contenido)
        self.contenido = self.home
        self.contentbox.add(self.contenido)
        
    def show_profile(self, widget):
        self.contentbox.remove(self.contenido)
        self.contenido = self.profile
        self.contentbox.add(self.contenido)
        
    def show_update_box(self, text='', id='', user=''):
        if self.updatebox.get_property('visible'): 
            self.updatebox.present()
            return
        self.updatebox.show(text, id, user)
        
    def show_preferences(self, widget, mode='user'):
        prefs = Preferences(self, mode)
        
    def show_oauth_pin_request(self, url):
        if self.extend:
            gtk.gdk.threads_enter()
            self.browser.open(url)
            gtk.gdk.threads_leave()
        else:
            webbrowser.open(url)
            gtk.gdk.threads_enter()
            p = InputPin(self)
            response = p.run()
            if response == gtk.RESPONSE_ACCEPT:
                verifier = p.pin.get_text()
                if verifier == '': 
                    self.cancel_login(_('Must write a valid PIN'))
                else:
                    self.request_auth_token(verifier)
            else:
                self.cancel_login(_('Login cancelled by user'))
                
            p.destroy()
            gtk.gdk.threads_leave()
        
    def start_updating_timeline(self):
        self.home.timeline.start_update()
        
    def start_updating_replies(self):
        self.home.replies.start_update()
        
    def start_updating_directs(self):
        self.home.direct.start_update()
        
    def start_search(self):
        self.profile.topics.topics.waiting.start()
        
    def update_timeline(self, tweets):
        log.debug(u'Actualizando el timeline')
        gtk.gdk.threads_enter()
        
        last = self.home.timeline.last
        count = self.home.timeline.update_tweets(tweets)
        
        if count > 0 and self.updating['home']:
            tweet = None
            i = 0
            while 1:
                if tweets[i]['user']['screen_name'] == self.me:
                    if not util.has_tweet(last, tweets[i]):
                        tweet = tweets[i]
                        break
                else:
                    tweet = tweets[i]
                    break
                i += 1
            
            p = self.parse_tweet(tweet)
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_tweets(count, text, icon)
            
        gtk.gdk.threads_leave()
        self.updating['home'] = False
        
    def update_replies(self, tweets):
        log.debug(u'Actualizando las replies')
        gtk.gdk.threads_enter()
        count = self.home.replies.update_tweets(tweets)
        
        if count > 0 and self.updating['replies']:
            p = self.parse_tweet(tweets[0])
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_replies(count, text, icon)
        
        gtk.gdk.threads_leave()
        self.updating['replies'] = False
        
    def update_directs(self, recv):
        log.debug(u'Actualizando mensajes directos')
        gtk.gdk.threads_enter()
        count = self.home.direct.update_tweets(recv)
        
        if count > 0 and self.updating['directs']:
            p = self.parse_tweet(recv[0])
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_directs(count, text, icon)
            
        gtk.gdk.threads_leave()
        self.updating['directs'] = False
        
    def update_favorites(self, tweets, replies, favs):
        log.debug(u'Actualizando favoritos')
        gtk.gdk.threads_enter()
        self.home.timeline.update_tweets(tweets)
        self.home.replies.update_tweets(replies)
        self.profile.favorites.update_tweets(favs)
        gtk.gdk.threads_leave()
        
    def update_user_profile(self, profile):
        log.debug(u'Actualizando perfil del usuario')
        gtk.gdk.threads_enter()
        self.profile.set_user_profile(profile)
        gtk.gdk.threads_leave()
        
    def update_follow(self, user, follow):
        self.notify.following(user, follow)
        
    def update_rate_limits(self, val):
        if val is None or val == []: return
        gtk.gdk.threads_enter()
        self.statusbar.push(0, util.get_rates(val))
        gtk.gdk.threads_leave()
        
    def update_search_topics(self, val):
        log.debug(u'Mostrando resultados de la búsqueda')
        gtk.gdk.threads_enter()
        if val is None:
            self.profile.topics.update_tweets(val)
        else:
            self.profile.topics.update_tweets(val['results'])
        gtk.gdk.threads_leave()
        
    def update_search_people(self, val):
        self.search.people.update_profiles(val)
        #self.show_search(self)
        #if self.workspace != 'wide':
        #    self.contenido.wrapper.set_current_page(1)
        
    def update_trends(self, current, day, week):
        self.search.trending.update_trends(current, day, week)
        
    def update_friends(self, friends):
        pass
        
    def update_user_avatar(self, user, pic):
        self.home.timeline.update_user_pic(user, pic)
        self.home.replies.update_user_pic(user, pic)
        self.home.direct.update_user_pic(user, pic)
        self.profile.favorites.update_user_pic(user, pic)
        self.profile.user_form.update_user_pic(user, pic)
        self.profile.topics.update_user_pic(user, pic)
        
    def update_in_reply_to(self, tweet):
        gtk.gdk.threads_enter()
        self.replybox.update([tweet])
        gtk.gdk.threads_leave()
        
    def update_conversation(self, tweets):
        gtk.gdk.threads_enter()
        self.replybox.update(tweets)
        gtk.gdk.threads_leave()
        
    def tweet_changed(self, timeline, replies, favs):
        log.debug(u'Tweet modificado')
        gtk.gdk.threads_enter()
        log.debug(u'--Actualizando el timeline')
        self.home.timeline.update_tweets(timeline)
        log.debug(u'--Actualizando las replies')
        self.home.replies.update_tweets(replies)
        log.debug(u'--Actualizando favoritos')
        self.profile.favorites.update_tweets(favs)
        gtk.gdk.threads_leave()
        
    def tweet_done(self, tweets):
        log.debug(u'Actualizando nuevo tweet')
        gtk.gdk.threads_enter()
        self.updatebox.release()
        if tweets: 
            self.updatebox.done()
        gtk.gdk.threads_leave()
        self.update_timeline(tweets)
        
    def set_mode(self):
        cur_x, cur_y = self.get_position()
        cur_w, cur_h = self.get_size()
        
        if self.workspace == 'wide':
            size = self.wide_win_size
            self.resize(size[0], size[1])
            self.set_default_size(size[0], size[1])
            x = (size[0] - cur_w) / 2
            self.move(cur_x - x, cur_y)
        else:
            size = self.single_win_size
            self.resize(size[0], size[1])
            self.set_default_size(size[0], size[1])
            x = (cur_w - size[0]) / 2
            self.move(cur_x + x, cur_y)
        
        log.debug('Cambiando a modo %s (%s)' % (self.workspace, size))
        self.dock.change_mode(self.workspace)
        self.home.change_mode(self.workspace)
        self.profile.change_mode(self.workspace)
        self.show_all()
        
    def update_config(self, config, global_cfg=None, thread=False):
        log.debug('Actualizando configuracion')
        self.workspace = config.read('General', 'workspace')
        self.minimize = config.read('General', 'minimize-on-close')
        home_interval = int(config.read('General', 'home-update-interval'))
        replies_interval = int(config.read('General', 'replies-update-interval'))
        directs_interval = int(config.read('General', 'directs-update-interval'))
        self.notify.update_config(config.read_section('Notifications'))
        
        if thread: 
            self.version = global_cfg.read('App', 'version')
            self.imgdir = config.imgdir
            single_size = config.read('General', 'single-win-size').split(',')
            wide_size = config.read('General', 'wide-win-size').split(',')
            win_pos = config.read('General', 'window-position').split(',')
            self.single_win_size = (int(single_size[0]), int(single_size[1]))
            self.wide_win_size = (int(wide_size[0]), int(wide_size[1]))
            self.win_pos = (int(win_pos[0]), int(win_pos[1]))
            gtk.gdk.threads_enter()
            
        self.set_mode()
        
        if (self.home_interval != home_interval):
            if self.home_timer: gobject.source_remove(self.home_timer)
            self.home_interval = home_interval
            self.home_timer = gobject.timeout_add(self.home_interval * 60 * 1000, self.download_timeline)
            log.debug('--Creado timer de Timeline cada %i min' % self.home_interval)
            
        if (self.replies_interval != replies_interval):
            if self.replies_timer: gobject.source_remove(self.replies_timer)
            self.replies_interval = replies_interval
            self.replies_timer = gobject.timeout_add(self.replies_interval * 60 * 1000, self.download_replies)
            log.debug('--Creado timer de Replies cada %i min' % self.replies_interval)
            
        if (self.directs_interval != directs_interval):
            if self.directs_timer: gobject.source_remove(self.directs_timer)
            self.directs_interval = directs_interval
            self.directs_timer = gobject.timeout_add(self.directs_interval * 60 * 1000, self.download_directs)
            log.debug('--Creado timer de Directs cada %i min' % self.directs_interval)
            
        if thread: 
            gtk.gdk.threads_leave()
        
    def size_request(self, widget, event, data=None):
        """Callback when the window changes its sizes. We use it to set the
        proper word-wrapping for the message column."""
        if self.mode < 2: return
        
        w, h = self.get_size()
        
        if self.workspace == 'single':
            if (w, h) == self.single_win_size: return
            self.single_win_size = (w, h)
        else:
            if (w, h) == self.wide_win_size: return
            self.wide_win_size = (w, h)
        
        self.win_pos = self.get_position()
        
        self.contenido.update_wrap(w, self.workspace)
        
        return
    
    def move_event(self, widget, event):
        self.win_pos = self.get_position()
        
    def __on_key_press(self, widget, keycomb):
        if self.mode < 2: return
        if keycomb == 'ctrl+n':
            self.show_update_box()
示例#4
0
class Main(BaseGui, gtk.Window):
    __gsignals__ = dict(mykeypress=(gobject.SIGNAL_RUN_LAST
                                    | gobject.SIGNAL_ACTION, None, (str, )))

    def __init__(self, controller, extend=False):
        BaseGui.__init__(self, controller)
        gtk.Window.__init__(self)

        self.extend = extend and extend_mode

        self.set_title('Turpial')
        self.set_size_request(280, 350)
        self.set_default_size(320, 480)
        self.current_width = 320
        self.current_height = 480
        self.set_icon(self.load_image('turpial_icon.png', True))
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('delete-event', self.__close)
        self.connect('size-request', self.size_request)
        self.connect('configure-event', self.move_event)
        self.connect('mykeypress', self.__on_key_press)

        self.mode = 0
        self.vbox = None
        self.contentbox = gtk.VBox(False)

        # Valores de config. por defecto
        self.showed = True
        self.minimize = 'on'
        self.workspace = 'single'
        self.link_color = 'ff6633'
        self.home_interval = -1
        self.replies_interval = -1
        self.directs_interval = -1
        self.me = None
        self.version = None

        self.home_timer = None
        self.replies_timer = None
        self.directs_timer = None

        self.notify = Notification()

        self.home = Home(self, self.workspace)
        self.profile = Profile(self)
        self.contenido = self.home
        self.updatebox = UpdateBox(self)
        self.replybox = ReplyBox(self)

        if self.extend:
            log.debug('Cargado modo GTK Extendido')
            self.browser = OAuthWindow(self)
        else:
            log.debug('Cargado modo GTK Simple')

        self.dock = Dock(self, self.workspace)
        self.__create_trayicon()

    def __create_trayicon(self):
        if gtk.check_version(2, 10, 0) is not None:
            self.log.debug("Disabled Tray Icon. It needs PyGTK >= 2.10.0")
            return
        self.tray = gtk.StatusIcon()
        self.tray.set_from_pixbuf(self.load_image('turpial_icon.png', True))
        self.tray.set_tooltip('Turpial')
        self.tray.connect("activate", self.__on_trayicon_click)
        self.tray.connect("popup-menu", self.__show_tray_menu)

    def __on_trayicon_click(self, widget):
        if (self.showed is True):
            self.showed = False
            self.hide()
        else:
            self.showed = True
            self.show()
            #self.present()

    def __show_tray_menu(self, widget, button, activate_time):
        menu = gtk.Menu()
        tweet = gtk.MenuItem(_('Tweet'))
        exit = gtk.MenuItem(_('Exit'))
        if self.mode == 2:
            menu.append(tweet)
        menu.append(exit)

        exit.connect('activate', self.quit)
        tweet.connect('activate', self.__show_update_box_from_menu)

        menu.show_all()
        menu.popup(None, None, None, button, activate_time)

    def __show_update_box_from_menu(self, widget):
        self.show_update_box()

    def __close(self, widget, event=None):
        if self.minimize == 'on':
            self.showed = False
            self.hide()
        else:
            self.quit(widget)
        return True

    def __save_size(self):
        if self.mode < 2: return
        wide_value = "%i, %i" % (self.wide_win_size[0], self.wide_win_size[1])
        single_value = "%i, %i" % (self.single_win_size[0],
                                   self.single_win_size[1])
        pos_value = "%i, %i" % (self.win_pos[0], self.win_pos[1])
        log.debug('Guardando tamaño de la ventana')
        log.debug('--Single: %s' % single_value)
        log.debug('--Wide: %s' % wide_value)
        log.debug('--Position: %s' % pos_value)
        self.save_config(
            {
                'General': {
                    'single-win-size': single_value,
                    'wide-win-size': wide_value,
                    'window-position': pos_value
                }
            },
            update=False)

    def __toogle_remember(self, widget):
        if self.remember.get_active():
            self.username.set_sensitive(False)
            self.password.set_sensitive(False)
        else:
            self.username.set_sensitive(True)
            self.password.set_sensitive(True)

    def load_image(self, path, pixbuf=False):
        img_path = os.path.realpath(
            os.path.join(os.path.dirname(__file__), '..', '..', 'data',
                         'pixmaps', path))
        pix = gtk.gdk.pixbuf_new_from_file(img_path)
        if pixbuf: return pix
        avatar = gtk.Image()
        avatar.set_from_pixbuf(pix)
        del pix
        return avatar

    def load_avatar(self, dir, path, image=False):
        img_path = os.path.join(dir, path)
        pix = gtk.gdk.pixbuf_new_from_file(img_path)
        if not image: return pix
        avatar = gtk.Image()
        avatar.set_from_pixbuf(pix)
        del pix
        return avatar

    def resize_avatar(self, pic):
        ext = pic[-3:].lower()
        fullname = os.path.join(self.imgdir, pic)

        orig = gtk.gdk.pixbuf_new_from_file(fullname)
        pw, ph = orig.get_width(), orig.get_height()

        if pw >= ph:
            ratio = float(ph) / pw
            fw = util.AVATAR_SIZE
            fh = int(fw * ratio)
        else:
            ratio = float(pw) / ph
            fh = util.AVATAR_SIZE
            fw = int(fh * ratio)

        dest = orig.scale_simple(fw, fh, gtk.gdk.INTERP_BILINEAR)
        dest.save(fullname, 'png')

        del orig
        del dest

    def request_conversation(self, twt_id, user):
        self.replybox.show(twt_id, user)
        BaseGui.request_conversation(self, twt_id, user)

    def get_user_avatar(self, user, pic_url):
        pix = self.request_user_avatar(user, pic_url)
        if pix:
            # Try to load user avatar from file. If fail (by corrupt data, etc)
            # then load default image
            try:
                return self.load_avatar(self.imgdir, pix)
            except:
                return self.load_image('unknown.png', pixbuf=True)
        else:
            return self.load_image('unknown.png', pixbuf=True)

    def quit(self, widget):
        self.__save_size()
        gtk.main_quit()
        self.destroy()
        self.tray = None
        self.request_signout()

    def main_loop(self):
        #gtk.gdk.threads_enter()
        gtk.main()
        #gtk.gdk.threads_leave()

    def show_login(self, global_config):

        self.mode = 1
        if self.vbox is not None: self.remove(self.vbox)

        avatar = self.load_image('logo2.png')
        self.message = LoginLabel(self)

        lbl_user = gtk.Label()
        #lbl_user.set_justify(gtk.JUSTIFY_LEFT)
        lbl_user.set_use_markup(True)
        lbl_user.set_markup(u'<span size="small">%s</span>' %
                            _('User and Password'))
        lbl_user.set_alignment(0, 0.5)

        self.username = gtk.Entry()
        self.password = gtk.Entry()
        self.password.set_visibility(False)

        self.remember = gtk.CheckButton(_('Remember my credentials'))

        self.btn_oauth = gtk.Button(_('Connect'))

        self.btn_settings = gtk.Button()
        self.btn_settings.set_relief(gtk.RELIEF_NONE)
        self.btn_settings.set_tooltip_text(_('Preferences'))
        self.btn_settings.set_image(self.load_image('settings-single.png'))
        settings_box = gtk.Alignment(xalign=1.0, yalign=0.5)
        settings_box.set_padding(70, 10, 40, 40)
        settings_box.add(self.btn_settings)

        self.waiting = CairoWaiting(self)
        align = gtk.Alignment(xalign=1, yalign=0.5)
        align.add(self.waiting)

        hbox = gtk.HBox(False)
        hbox.pack_start(lbl_user, False, False, 2)
        hbox.pack_start(align, True, True, 2)

        table = gtk.Table(10, 1, False)
        table.attach(avatar, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 10, 50)
        table.attach(self.message, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL,
                     20, 3)
        table.attach(hbox, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL, 50, 0)
        table.attach(self.username, 0, 1, 3, 4, gtk.EXPAND | gtk.FILL,
                     gtk.FILL, 50, 0)
        table.attach(self.password, 0, 1, 5, 6, gtk.EXPAND | gtk.FILL,
                     gtk.FILL, 50, 0)
        table.attach(self.btn_oauth, 0, 1, 7, 8, gtk.EXPAND, gtk.FILL, 0, 10)
        table.attach(self.remember, 0, 1, 8, 9, gtk.EXPAND, gtk.FILL, 0, 10)
        table.attach(settings_box, 0, 1, 9, 10, gtk.EXPAND | gtk.FILL,
                     gtk.EXPAND | gtk.FILL, 0, 10)

        self.vbox = gtk.VBox(False, 5)
        self.vbox.pack_start(table, False, False, 2)

        self.add(self.vbox)
        self.show_all()

        self.btn_oauth.connect('clicked', self.oauth)
        self.password.connect('activate', self.oauth)
        self.remember.connect("toggled", self.__toogle_remember)
        self.btn_settings.connect('clicked', self.show_preferences, 'global')

        username = global_config.read('Login', 'username')
        password = global_config.read('Login', 'password')
        if username != '' and password != '':
            self.username.set_text(username)
            self.password.set_text(base64.b64decode(password))
            self.remember.set_active(True)

    def signin(self, widget, username, password):
        self.message.deactivate()
        self.waiting.start()
        self.btn_oauth.set_sensitive(False)
        self.username.set_sensitive(False)
        self.password.set_sensitive(False)
        self.request_signin(username.get_text(), password.get_text())

    def oauth(self, widget):
        self.message.deactivate()
        self.waiting.start()
        self.btn_oauth.set_sensitive(False)
        self.username.set_sensitive(False)
        self.password.set_sensitive(False)
        self.remember.set_sensitive(False)
        self.btn_settings.set_sensitive(False)
        self.request_oauth(self.username.get_text(), self.password.get_text(),
                           self.remember.get_active())

    def cancel_login(self, error):
        self.message.set_error(error)
        self.waiting.stop(error=True)
        self.btn_oauth.set_sensitive(True)
        self.remember.set_sensitive(True)
        self.btn_settings.set_sensitive(True)
        if not self.remember.get_active():
            self.username.set_sensitive(True)
            self.password.set_sensitive(True)

    def show_main(self, config, global_cfg, p):
        log.debug('Cargando ventana principal')
        self.mode = 2

        self.update_config(config, global_cfg, True)

        gtk.gdk.threads_enter()
        self.contentbox.add(self.contenido)

        self.statusbar = gtk.Statusbar()
        self.statusbar.push(0,
                            _('Wait a few seconds while I load everything...'))
        if (self.vbox is not None): self.remove(self.vbox)

        self.vbox = gtk.VBox(False, 0)
        self.vbox.pack_start(self.contentbox, True, True, 0)
        self.vbox.pack_start(self.dock, False, False, 0)
        self.vbox.pack_start(self.statusbar, False, False, 0)

        self.profile.set_user_profile(p)
        self.me = p['screen_name']
        title = 'Turpial - %s' % self.me
        self.set_title(title)
        self.tray.set_tooltip(title)

        if config.read('General', 'profile-color') == 'on':
            self.link_color = p['profile_link_color']

        self.add(self.vbox)
        self.show_all()
        if (self.win_pos[0] > 0 and self.win_pos[1] > 0):
            self.move(self.win_pos[0], self.win_pos[1])
        gtk.gdk.threads_leave()

        self.notify.login(p)

        gobject.timeout_add(6 * 60 * 1000, self.download_rates)

    def show_home(self, widget):
        self.contentbox.remove(self.contenido)
        self.contenido = self.home
        self.contentbox.add(self.contenido)

    def show_profile(self, widget):
        self.contentbox.remove(self.contenido)
        self.contenido = self.profile
        self.contentbox.add(self.contenido)

    def show_update_box(self, text='', id='', user=''):
        if self.updatebox.get_property('visible'):
            self.updatebox.present()
            return
        self.updatebox.show(text, id, user)

    def show_preferences(self, widget, mode='user'):
        prefs = Preferences(self, mode)

    def show_oauth_pin_request(self, url):
        if self.extend:
            gtk.gdk.threads_enter()
            self.browser.open(url)
            gtk.gdk.threads_leave()
        else:
            webbrowser.open(url)
            gtk.gdk.threads_enter()
            p = InputPin(self)
            response = p.run()
            if response == gtk.RESPONSE_ACCEPT:
                verifier = p.pin.get_text()
                if verifier == '':
                    self.cancel_login(_('Must write a valid PIN'))
                else:
                    self.request_auth_token(verifier)
            else:
                self.cancel_login(_('Login cancelled by user'))

            p.destroy()
            gtk.gdk.threads_leave()

    def start_updating_timeline(self):
        self.home.timeline.start_update()

    def start_updating_replies(self):
        self.home.replies.start_update()

    def start_updating_directs(self):
        self.home.direct.start_update()

    def start_search(self):
        self.profile.topics.topics.waiting.start()

    def update_timeline(self, tweets):
        log.debug(u'Actualizando el timeline')
        gtk.gdk.threads_enter()

        last = self.home.timeline.last
        count = self.home.timeline.update_tweets(tweets)

        if count > 0 and self.updating['home']:
            tweet = None
            i = 0
            while 1:
                if tweets[i]['user']['screen_name'] == self.me:
                    if not util.has_tweet(last, tweets[i]):
                        tweet = tweets[i]
                        break
                else:
                    tweet = tweets[i]
                    break
                i += 1

            p = self.parse_tweet(tweet)
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_tweets(count, text, icon)

        gtk.gdk.threads_leave()
        self.updating['home'] = False

    def update_replies(self, tweets):
        log.debug(u'Actualizando las replies')
        gtk.gdk.threads_enter()
        count = self.home.replies.update_tweets(tweets)

        if count > 0 and self.updating['replies']:
            p = self.parse_tweet(tweets[0])
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_replies(count, text, icon)

        gtk.gdk.threads_leave()
        self.updating['replies'] = False

    def update_directs(self, recv):
        log.debug(u'Actualizando mensajes directos')
        gtk.gdk.threads_enter()
        count = self.home.direct.update_tweets(recv)

        if count > 0 and self.updating['directs']:
            p = self.parse_tweet(recv[0])
            icon = self.current_avatar_path(p['avatar'])
            text = util.escape_text(p['text'])
            text = "<b>@%s</b> %s" % (p['username'], text)
            self.notify.new_directs(count, text, icon)

        gtk.gdk.threads_leave()
        self.updating['directs'] = False

    def update_favorites(self, tweets, replies, favs):
        log.debug(u'Actualizando favoritos')
        gtk.gdk.threads_enter()
        self.home.timeline.update_tweets(tweets)
        self.home.replies.update_tweets(replies)
        self.profile.favorites.update_tweets(favs)
        gtk.gdk.threads_leave()

    def update_user_profile(self, profile):
        log.debug(u'Actualizando perfil del usuario')
        gtk.gdk.threads_enter()
        self.profile.set_user_profile(profile)
        gtk.gdk.threads_leave()

    def update_follow(self, user, follow):
        self.notify.following(user, follow)

    def update_rate_limits(self, val):
        if val is None or val == []: return
        gtk.gdk.threads_enter()
        self.statusbar.push(0, util.get_rates(val))
        gtk.gdk.threads_leave()

    def update_search_topics(self, val):
        log.debug(u'Mostrando resultados de la búsqueda')
        gtk.gdk.threads_enter()
        if val is None:
            self.profile.topics.update_tweets(val)
        else:
            self.profile.topics.update_tweets(val['results'])
        gtk.gdk.threads_leave()

    def update_search_people(self, val):
        self.search.people.update_profiles(val)
        #self.show_search(self)
        #if self.workspace != 'wide':
        #    self.contenido.wrapper.set_current_page(1)

    def update_trends(self, current, day, week):
        self.search.trending.update_trends(current, day, week)

    def update_friends(self, friends):
        pass

    def update_user_avatar(self, user, pic):
        self.home.timeline.update_user_pic(user, pic)
        self.home.replies.update_user_pic(user, pic)
        self.home.direct.update_user_pic(user, pic)
        self.profile.favorites.update_user_pic(user, pic)
        self.profile.user_form.update_user_pic(user, pic)
        self.profile.topics.update_user_pic(user, pic)

    def update_in_reply_to(self, tweet):
        gtk.gdk.threads_enter()
        self.replybox.update([tweet])
        gtk.gdk.threads_leave()

    def update_conversation(self, tweets):
        gtk.gdk.threads_enter()
        self.replybox.update(tweets)
        gtk.gdk.threads_leave()

    def tweet_changed(self, timeline, replies, favs):
        log.debug(u'Tweet modificado')
        gtk.gdk.threads_enter()
        log.debug(u'--Actualizando el timeline')
        self.home.timeline.update_tweets(timeline)
        log.debug(u'--Actualizando las replies')
        self.home.replies.update_tweets(replies)
        log.debug(u'--Actualizando favoritos')
        self.profile.favorites.update_tweets(favs)
        gtk.gdk.threads_leave()

    def tweet_done(self, tweets):
        log.debug(u'Actualizando nuevo tweet')
        gtk.gdk.threads_enter()
        self.updatebox.release()
        if tweets:
            self.updatebox.done()
        gtk.gdk.threads_leave()
        self.update_timeline(tweets)

    def set_mode(self):
        cur_x, cur_y = self.get_position()
        cur_w, cur_h = self.get_size()

        if self.workspace == 'wide':
            size = self.wide_win_size
            self.resize(size[0], size[1])
            self.set_default_size(size[0], size[1])
            x = (size[0] - cur_w) / 2
            self.move(cur_x - x, cur_y)
        else:
            size = self.single_win_size
            self.resize(size[0], size[1])
            self.set_default_size(size[0], size[1])
            x = (cur_w - size[0]) / 2
            self.move(cur_x + x, cur_y)

        log.debug('Cambiando a modo %s (%s)' % (self.workspace, size))
        self.dock.change_mode(self.workspace)
        self.home.change_mode(self.workspace)
        self.profile.change_mode(self.workspace)
        self.show_all()

    def update_config(self, config, global_cfg=None, thread=False):
        log.debug('Actualizando configuracion')
        self.workspace = config.read('General', 'workspace')
        self.minimize = config.read('General', 'minimize-on-close')
        home_interval = int(config.read('General', 'home-update-interval'))
        replies_interval = int(
            config.read('General', 'replies-update-interval'))
        directs_interval = int(
            config.read('General', 'directs-update-interval'))
        self.notify.update_config(config.read_section('Notifications'))

        if thread:
            self.version = global_cfg.read('App', 'version')
            self.imgdir = config.imgdir
            single_size = config.read('General', 'single-win-size').split(',')
            wide_size = config.read('General', 'wide-win-size').split(',')
            win_pos = config.read('General', 'window-position').split(',')
            self.single_win_size = (int(single_size[0]), int(single_size[1]))
            self.wide_win_size = (int(wide_size[0]), int(wide_size[1]))
            self.win_pos = (int(win_pos[0]), int(win_pos[1]))
            gtk.gdk.threads_enter()

        self.set_mode()

        if (self.home_interval != home_interval):
            if self.home_timer: gobject.source_remove(self.home_timer)
            self.home_interval = home_interval
            self.home_timer = gobject.timeout_add(
                self.home_interval * 60 * 1000, self.download_timeline)
            log.debug('--Creado timer de Timeline cada %i min' %
                      self.home_interval)

        if (self.replies_interval != replies_interval):
            if self.replies_timer: gobject.source_remove(self.replies_timer)
            self.replies_interval = replies_interval
            self.replies_timer = gobject.timeout_add(
                self.replies_interval * 60 * 1000, self.download_replies)
            log.debug('--Creado timer de Replies cada %i min' %
                      self.replies_interval)

        if (self.directs_interval != directs_interval):
            if self.directs_timer: gobject.source_remove(self.directs_timer)
            self.directs_interval = directs_interval
            self.directs_timer = gobject.timeout_add(
                self.directs_interval * 60 * 1000, self.download_directs)
            log.debug('--Creado timer de Directs cada %i min' %
                      self.directs_interval)

        if thread:
            gtk.gdk.threads_leave()

    def size_request(self, widget, event, data=None):
        """Callback when the window changes its sizes. We use it to set the
        proper word-wrapping for the message column."""
        if self.mode < 2: return

        w, h = self.get_size()

        if self.workspace == 'single':
            if (w, h) == self.single_win_size: return
            self.single_win_size = (w, h)
        else:
            if (w, h) == self.wide_win_size: return
            self.wide_win_size = (w, h)

        self.win_pos = self.get_position()

        self.contenido.update_wrap(w, self.workspace)

        return

    def move_event(self, widget, event):
        self.win_pos = self.get_position()

    def __on_key_press(self, widget, keycomb):
        if self.mode < 2: return
        if keycomb == 'ctrl+n':
            self.show_update_box()