def __make_playback_box(self, stream, index):
     process_id = int(stream['proplist']['application.process.id'])
     if process_id == os.getpid():
         return
     # if it has show mpris, then don't show sink_input
     if process_id in self.mpris_list:
         self.mpris_stream[process_id] = index
         self.stream_mpris[index] = process_id
         return
     icon_name, is_filtered = self.__white_list_check(stream, index)
     if is_filtered:
         return
     self.stream_list[index] = {}
     volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE
     if icon_name:
         if icon_name[0] == '/':
             try:
                 img = gtk.image_new_from_pixbuf(gtk.gdk.pixbuf_new_from_file(
                     icon_name).scale_simple(16, 16, gtk.gdk.INTERP_TILES))
             except:
                 img = gtk.image_new_from_pixbuf(self.stream_icon)
         else:
             image_pixbuf = self.__get_pixbuf_from_icon_name(icon_name)
             if image_pixbuf:
                 img = gtk.image_new_from_pixbuf(image_pixbuf)
             else:
                 img = gtk.image_new_from_pixbuf(self.stream_icon)
                 #img = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
     else:
         img = gtk.image_new_from_pixbuf(self.stream_icon)
     img.set_size_request(16, 16)
     scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
     scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
     scale.set_size_request(90, 10)
     mute_button = SwitchButton(
         inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
         active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
     hbox = gtk.HBox()
     hbox.pack_start(self.__make_align(img), False, False)
     hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), False, False)
     hbox.pack_start(self.__make_align(mute_button), False, False)
     self.stream_list[index]['scale'] = scale
     self.stream_list[index]['button'] = mute_button
     self.stream_list[index]['container'] = hbox
     self.stream_list[index]['process_id'] = process_id
     self.stream_list[index]['stream_id'] = index
     self.stream_process[process_id] = self.stream_list[index]
     self.__set_playback_status(stream, scale, mute_button)
     if stream['volume_writable']:
         scale.connect("value-changed", self.playback_stream_scale_changed_cb, index, mute_button)
         mute_button.connect("toggled", self.playback_stream_toggled_cb, index, scale)
     hbox.show_all()
     self.__app_vbox.pack_start(hbox, False, False)
 def __make_playback_box(self, stream, index):
     process_id = int(stream['proplist']['application.process.id'])
     # if it has show mpris, then don't show sink_input
     if process_id in self.mpris_list:
         self.mpris_stream[process_id] = index
         self.stream_mpris[index] = process_id
         return
     self.stream_list[index] = {}
     volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE
     icon_name = self.__white_list_check(stream)
     if icon_name:
         if icon_name[0] == '/' and os.path.exists(icon_name):
             try:
                 img = gtk.image_new_from_pixbuf(gtk.gdk.pixbuf_new_from_file(
                     icon_name).scale_simple(16, 16, gtk.gdk.INTERP_TILES))
             except:
                 img = gtk.image_new_from_pixbuf(self.stream_icon)
         else:
             img = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
     else:
         img = gtk.image_new_from_pixbuf(self.stream_icon)
     scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
     scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
     scale.set_size_request(90, 10)
     mute_button = SwitchButton(
         inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
         active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
     hbox = gtk.HBox()
     hbox.pack_start(self.__make_align(img), False, False)
     hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), False, False)
     hbox.pack_start(self.__make_align(mute_button), False, False)
     self.stream_list[index]['scale'] = scale
     self.stream_list[index]['button'] = mute_button
     self.stream_list[index]['container'] = hbox
     self.stream_list[index]['process_id'] = process_id
     self.stream_list[index]['stream_id'] = index
     self.stream_process[process_id] = self.stream_list[index]
     self.__set_playback_status(stream, scale, mute_button)
     if stream['volume_writable']:
         scale.connect("value-changed", self.playback_stream_scale_changed_cb, index, mute_button)
         mute_button.connect("toggled", self.playback_stream_toggled_cb, index, scale)
     hbox.show_all()
     self.__app_vbox.pack_start(hbox, False, False)
    def mpris2_new_cb(self, obj, pid):
        vbox = gtk.VBox()
        image_pixbuf = self.__get_pixbuf_from_icon_name(
            obj.mpris_process[pid]['property']['DesktopEntry'])
        logo_pixbuf = self.__get_pixbuf_from_icon_name(
            obj.mpris_process[pid]['property']['DesktopEntry'], 32)
        if image_pixbuf:
            img = gtk.image_new_from_pixbuf(image_pixbuf)
        else:
            img = gtk.image_new_from_pixbuf(self.stream_icon)
        img.set_size_request(16, 16)
        # application title
        app_title = obj.mpris_process[pid]['property']['Identity']
        #if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Stopped':
            #app_title = obj.mpris_process[pid]['property']['Identity']
        #else:
            #app_title = "%s - %s" % (obj.mpris_process[pid]['property']['Identity'], _(obj.mpris_process[pid]['property']['PlaybackStatus']))
        label = Label(app_title, label_width=115)
        hbox = gtk.HBox(False, 5)
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(label), False, False)
        vbox.pack_start(hbox, False, False)

        # metadata info
        meta_box = gtk.HBox(False, 10)
        xesam_vbox = gtk.VBox(False)
        #art_img = gtk.Image()
        art_img = gtk.EventBox()
        art_img.set_size_request(40, 40)
        art_img.connect("expose-event", self.__draw_mpris_art_img)

        xesam_title = Label("", label_width=75)
        xesam_artist = Label("", label_width=75)
        xesam_album = Label("", label_width=75)
        xesam_vbox.pack_start(xesam_title)
        xesam_vbox.pack_start(xesam_artist)
        #xesam_vbox.pack_start(xesam_album)
        meta_box.pack_start(self.__make_align(art_img, padding_left=21, height=40), False, False)
        meta_box.pack_start(xesam_vbox)

        self.mpris_list[pid] = {}
        # mpris control
        scale = HScalebar(app_theme.get_pixbuf("sound/point.png"), show_value=False, format_value="%", value_min=0, value_max=1, line_height=3)
        scale.set_magnetic_values([(0, 0.1), (1, 0.1)])
        scale.set_size_request(70, 10)
        prev_bt = gtk.Button()
        pause_bt = gtk.Button()
        stop_bt = gtk.Button()
        next_bt = gtk.Button()

        prev_bt.set_size_request(20, 22)
        pause_bt.set_size_request(29, 30)
        #stop_bt.set_size_request(16, 16)
        next_bt.set_size_request(20, 22)

        prev_bt.pixbuf = "previous"
        if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Playing':
            pause_bt.pixbuf = "pause"
        else:
            pause_bt.pixbuf = "play"
        #stop_bt.pixbuf = self.stop_img
        next_bt.pixbuf = "next"

        scale.set_value(obj.mpris_process[pid]['property']['Volume'])

        prev_bt.connect("clicked", self.__mpris_prev_cb, obj, pid)
        pause_bt.connect("clicked", self.__mpris_pause_cb, obj, pid)
        #stop_bt.connect("clicked", self.__mpris_stop_cb, obj, pid)
        next_bt.connect("clicked", self.__mpris_next_cb, obj, pid)
        prev_bt.connect("expose-event", self.__draw_mpris_button_cb)
        pause_bt.connect("expose-event", self.__draw_mpris_button_cb)
        stop_bt.connect("expose-event", self.__draw_mpris_button_cb)
        next_bt.connect("expose-event", self.__draw_mpris_button_cb)
        scale.connect("value-changed", self.__mpris_volume_cb, obj, pid)

        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(prev_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(pause_bt, height=-1), False, False)
        #hbox.pack_start(self.__make_align(stop_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(next_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, height=-1), False, False)
        vbox.pack_start(self.__make_align(hbox, xalign=0.5, padding_top=5, padding_bottom=10, height=-1), False, False)

        self.mpris_list[pid]['app_title'] = label
        self.mpris_list[pid]['prev'] = prev_bt
        self.mpris_list[pid]['pause'] = pause_bt
        self.mpris_list[pid]['stop'] = stop_bt
        self.mpris_list[pid]['next'] = next_bt
        self.mpris_list[pid]['scale'] = scale
        self.mpris_list[pid]['meta'] = meta_box
        self.mpris_list[pid]['meta_img'] = art_img
        self.mpris_list[pid]['meta_title'] = xesam_title
        self.mpris_list[pid]['meta_artist'] = xesam_artist
        self.mpris_list[pid]['meta_album'] = xesam_album
        self.mpris_list[pid]['container'] = vbox
        self.mpris_list[pid]['logo'] = logo_pixbuf
        if not obj.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['height'] = self.mpris_base_height
        else:
            vbox.pack_start(meta_box, False, False)
            vbox.reorder_child(meta_box, 1)
            self.__set_mpris_meta_info(pid)
            self.mpris_list[pid]['height'] = self.mpris_base_height + 40
        self.__mpris_total_height += self.mpris_list[pid]['height']
        # delete playback_stream widget
        if pid in self.stream_process:
            self.stream_process[pid]['container'].destroy()
            del self.stream_list[self.stream_process[pid]['stream_id']]
            del self.stream_process[pid]
        vbox.show_all()
        self.__mpris_vbox.pack_start(vbox, False, False)

        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num > 0 or mpris_num > 0:
            self.__app_vbox.set_no_show_all(False)
            self.__app_vbox.show_all()
        self.adjust_size()
        self.emit("stream-changed")
class TrayGui(gtk.VBox):
    '''sound tray gui'''
    #BASE_HEIGHT = 128  # has microphone
    BASE_HEIGHT = 103   # no microphone

    __gsignals__ = {
        "stream-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())}

    def __init__(self, tray_obj=None):
        super(TrayGui, self).__init__(False)
        self.tray_obj = tray_obj

        self.stream_icon = app_theme.get_pixbuf("sound/device.png").get_pixbuf().scale_simple(16, 16, gtk.gdk.INTERP_TILES)
        self.stream_num = 0
        self.stream_list = {}    # stream widgets
        self.stream_process = {} # process id to stream widgets
        self.stream_mpris = {}   # stream id to mpris process id

        self.__mpris_total_height = 0
        self.mpris_base_height = 70
        self.mpris_list = {}     # mpris widgets
        self.mpris_stream = {}   # mpris process id to stream id
        self.mpris2 = Mpris2()
        self.mpris2.connect("new", self.mpris2_new_cb)
        self.mpris2.connect("removed", self.mpris2_removed_cb)
        self.mpris2.connect("changed", self.mpris2_changed_cb)

        self.__play_dingdong = Play(os.path.join(get_parent_dir(__file__, 1), 'dingdong.wav'))

        hbox = gtk.HBox(False)
        hbox.set_spacing(WIDGET_SPACING)
        #separator_color = [(0, ("#000000", 0.3)), (0.5, ("#000000", 0.2)), (1, ("#777777", 0.0))]
        #hseparator = HSeparator(app_theme.get_shadow_color("hSeparator").get_color_info(), 0, 0)
        #hseparator.set_size_request(150, 3)
        separator_color = [(0, ("#777777", 0.0)), (0.5, ("#000000", 0.3)), (1, ("#777777", 0.0))]
        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 5)
        #hbox.pack_start(self.__make_align(Label(_("Device"), enable_select=False, enable_double_click=False)), False, False)
        #hbox.pack_start(self.__make_align(hseparator), True, True)
        self.pack_start(self.__make_align(Label(_("Device"), enable_select=False, enable_double_click=False), height=-1), False, False)
        self.pack_start(self.__make_align(hseparator, xalign=0.5, height=5), False, False)

        volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE

        table = gtk.Table(2, 3)
        speaker_img = ImageBox(app_theme.get_pixbuf("sound/speaker-3.png"))
        self.speaker_scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        self.speaker_scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
        self.speaker_scale.set_size_request(90, 10)
        self.speaker_mute_button = SwitchButton(
            inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        table.attach(self.__make_align(speaker_img), 0, 1, 0, 1, 4)
        table.attach(self.__make_align(self.speaker_scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), 1, 2, 0, 1, 4)
        table.attach(self.__make_align(self.speaker_mute_button), 2, 3, 0, 1, 4)

        #microphone_img = ImageBox(app_theme.get_pixbuf("sound/microphone.png"))
        #self.microphone_scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        #self.microphone_scale.set_size_request(90, 10)
        #self.microphone_mute_button = SwitchButton(
            #inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            #active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        #table.attach(self.__make_align(microphone_img), 0, 1, 1, 2, 4)
        #table.attach(self.__make_align(self.microphone_scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), 1, 2, 1, 2, 4)
        #table.attach(self.__make_align(self.microphone_mute_button), 2, 3, 1, 2, 4)

        self.pack_start(table, False, False)

        self.__app_vbox = gtk.VBox(False)
        separator_color = [(0, ("#777777", 0.0)), (0.5, ("#000000", 0.3)), (1, ("#777777", 0.0))]
        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 5)
        self.__app_vbox.pack_start(self.__make_align(Label(_("Applications"), enable_select=False, enable_double_click=False)), False, False)
        self.__app_vbox.pack_start(self.__make_align(hseparator, xalign=0.5, height=5), False, False)

        self.__mpris_vbox = gtk.VBox(False)
        self.__app_vbox.pack_start(self.__mpris_vbox)
        self.pack_start(self.__app_vbox)

        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 7)
        self.pack_start(self.__make_align(hseparator, xalign=0.5, height=7), False, False)

        self.button_more = SelectButton(_("Advanced..."), font_size=10, ali_padding=5)
        self.button_more.set_size_request(-1, 25)
        self.pack_start(self.button_more, False, False)
        #self.pack_start(self.__make_align(height=10))
        ##########################################
        self.__fallback_sink_index = None
        self.__fallback_source_index = None
        state_cb_fun = {}
        state_cb_fun["server"] = self.__server_state_cb
        state_cb_fun["sink"] = self.__sink_state_cb
        #state_cb_fun["source"] = self.__source_state_cb
        state_cb_fun["sinkinput"] = self.__sinkinput_state_cb
        pypulse.PULSE.connect_to_pulse(state_cb_fun)
        self.__set_output_status()
        #self.__set_input_status()

        # widget signals
        self.speaker_mute_button.connect("toggled", self.speaker_toggled)
        #self.microphone_mute_button.connect("toggled", self.microphone_toggled)
        self.speaker_scale.connect("value-changed", self.speaker_scale_value_changed)
        self.speaker_scale.connect("button-release-event", lambda w, e: self.__play_dingdong.play())
        #self.microphone_scale.connect("value-changed", self.microphone_scale_value_changed)
        # pulseaudio signals
        pypulse.PULSE.connect("sink-removed", self.sink_removed_cb)
        #pypulse.PULSE.connect("source-removed", self.source_removed_cb)
        pypulse.PULSE.connect("sinkinput-removed", self.sinkinput_removed_cb)

        self.mpris2.get_mpris_list()
        self.mpris_num = len(self.mpris2.mpris_process.keys())
        if self.mpris_num == 0:
            self.__app_vbox.set_no_show_all(True)

    def __make_align(self, widget=None, xalign=0.0, yalign=0.5, xscale=0.0,
                     yscale=0.0, padding_top=0, padding_bottom=0, padding_left=0,
                     padding_right=0, width=-1, height=25):
        align = gtk.Alignment()
        align.set_size_request(width, height)
        align.set(xalign, yalign, xscale, yscale)
        align.set_padding(padding_top, padding_bottom, padding_left, padding_right)
        if widget:
            align.add(widget)
        return align

    def __make_playback_box(self, stream, index):
        process_id = int(stream['proplist']['application.process.id'])
        if process_id == os.getpid():
            return
        # if it has show mpris, then don't show sink_input
        if process_id in self.mpris_list:
            self.mpris_stream[process_id] = index
            self.stream_mpris[index] = process_id
            return
        icon_name, is_filtered = self.__white_list_check(stream, index)
        if is_filtered:
            return
        self.stream_list[index] = {}
        volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE
        if icon_name:
            if icon_name[0] == '/':
                try:
                    img = gtk.image_new_from_pixbuf(gtk.gdk.pixbuf_new_from_file(
                        icon_name).scale_simple(16, 16, gtk.gdk.INTERP_TILES))
                except:
                    img = gtk.image_new_from_pixbuf(self.stream_icon)
            else:
                image_pixbuf = self.__get_pixbuf_from_icon_name(icon_name)
                if image_pixbuf:
                    img = gtk.image_new_from_pixbuf(image_pixbuf)
                else:
                    img = gtk.image_new_from_pixbuf(self.stream_icon)
                    #img = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
        else:
            img = gtk.image_new_from_pixbuf(self.stream_icon)
        img.set_size_request(16, 16)
        scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
        scale.set_size_request(90, 10)
        mute_button = SwitchButton(
            inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), False, False)
        hbox.pack_start(self.__make_align(mute_button), False, False)
        self.stream_list[index]['scale'] = scale
        self.stream_list[index]['button'] = mute_button
        self.stream_list[index]['container'] = hbox
        self.stream_list[index]['process_id'] = process_id
        self.stream_list[index]['stream_id'] = index
        self.stream_process[process_id] = self.stream_list[index]
        self.__set_playback_status(stream, scale, mute_button)
        if stream['volume_writable']:
            scale.connect("value-changed", self.playback_stream_scale_changed_cb, index, mute_button)
            mute_button.connect("toggled", self.playback_stream_toggled_cb, index, scale)
        hbox.show_all()
        self.__app_vbox.pack_start(hbox, False, False)

    def __white_list_check(self, stream, index):
        icon_name = None
        # check deepin-game-center
        if 'application.process.binary' in stream['proplist'] and \
                stream['proplist']['application.process.binary'].startswith('python'):
            process_id = int(stream['proplist']['application.process.id'])
            stream_process_obj = psutil.Process(process_id)
            if len(stream_process_obj.cmdline) > 1 and \
                    'deepin-game-center' in stream_process_obj.cmdline[1]:
                return "deepin-game-center", False
        # check deepin-media-player
        if 'application.process.binary' in stream['proplist'] and \
                stream['proplist']['application.process.binary'] == 'mplayer':
            process_id = int(stream['proplist']['application.process.id'])
            stream_process_obj = psutil.Process(process_id)
            stream_process_parent_obj = stream_process_obj.parent
            if stream_process_parent_obj.pid != 0 and \
                    stream_process_parent_obj.name.startswith("deepin-media"):
                children_process = stream_process_parent_obj.get_children()
                # check this process is the preview window of deepin-media-player
                if process_id != children_process[0].pid:
                    return None, True
                # check deepin-media-player whether enable mpris
                if stream_process_parent_obj.pid in self.mpris_list:
                    self.mpris_stream[stream_process_parent_obj.pid] = index
                    self.stream_mpris[index] = stream_process_parent_obj.pid
                    return None, True
                return "deepin-media-player", False
        if 'application.icon_name' in stream['proplist']:
            icon_name = stream['proplist']['application.icon_name']
        # check deepin-music-player
        if 'application.name' in stream['proplist']:
            if stream['proplist']['application.name'] == 'deepin-music-player':
                icon_name = "deepin-music-player"
        return icon_name, False

    def __get_pixbuf_from_icon_name(self, name, size=16):
        screen = self.get_screen()
        icon_theme = gtk.icon_theme_get_for_screen(screen)
        icon_info = icon_theme.lookup_icon(name, size, 0)
        if not icon_info:
            return None
        filename = icon_info.get_filename()
        if not filename or not os.path.exists(filename):
            return None
        pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
        if icon_info.get_base_size() != size:
            pixbuf = pixbuf.scale_simple(size, size, gtk.gdk.INTERP_TILES)
        return pixbuf

    ####################################################
    # widget signals
    def speaker_value_changed_thread(self):
        ''' speaker hscale value changed callback thread'''
        if not self.speaker_mute_button.get_active():
            self.speaker_mute_button.set_active(True)
        current_sink = self.__fallback_sink_index
        if current_sink is None:
            return
        volume_list = pypulse.output_volumes[current_sink]
        channel_list = pypulse.output_channels[current_sink]
        if not volume_list or not channel_list:
            return
        balance = pypulse.get_volume_balance(channel_list['channels'],
                                             volume_list, channel_list['map'])
        volume = int((self.speaker_scale.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_output_volume_with_balance(current_sink, volume, balance, channel_list['channels'], channel_list['map'])

    def speaker_scale_value_changed(self, widget, value):
        '''set output volume'''
        self.speaker_value_changed_thread()

    def microphone_value_changed_thread(self):
        ''' microphone value changed callback thread'''
        if not self.microphone_mute_button.get_active():
            self.microphone_mute_button.set_active(True)
        current_source = self.__fallback_source_index
        if current_source is None:
            return
        channel_list = pypulse.input_channels[current_source]
        if not channel_list:
            return

        volume = int((self.microphone_scale.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_input_volume(current_source, [volume] * channel_list['channels'], channel_list['channels'])

    def microphone_scale_value_changed(self, widget, value):
        ''' set input volume'''
        self.microphone_value_changed_thread()

    def speaker_toggled(self, button):
        active = button.get_active()
        current_sink = self.__fallback_sink_index
        self.speaker_scale.set_enable(active)
        if current_sink is not None:
            pypulse.PULSE.set_output_mute(current_sink, not active)

    def microphone_toggled(self, button):
        active = button.get_active()
        current_source = self.__fallback_source_index
        self.microphone_scale.set_enable(active)
        if current_source is not None:
            pypulse.PULSE.set_input_mute(current_source, not active)

    def playback_stream_scale_changed_cb(self, widget, value, index, button):
        if not button.get_active():
            button.set_active(True)
        volume = int((widget.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_sink_input_volume(index, [volume] * widget.volume_channels, widget.volume_channels)

    def playback_stream_toggled_cb(self, button, index, scale):
        active = button.get_active()
        scale.set_enable(active)
        pypulse.PULSE.set_sink_input_mute(index, not active)

    # pulseaudio signals callback
    def __sink_state_cb(self, obj, channel, port, volume, sink, idx):
        pypulse.output_channels[idx] = channel
        pypulse.output_active_ports[idx] = port
        pypulse.output_volumes[idx] = volume
        pypulse.output_devices[idx] = sink
        if self.__fallback_sink_index is None and pypulse.get_fallback_sink_name() == sink['name']:
            self.__fallback_sink_index = idx
        if self.__fallback_sink_index == idx:
            self.__set_output_status()

    # update tray_icon
    def update_tray_icon(self):
        if self.tray_obj:
            current_sink = self.__fallback_sink_index
            sinks = pypulse.output_devices
            sink_volume = pypulse.output_volumes
            tip_text = "%s %d%%" % (_("Volume"), round(self.speaker_scale.get_value()))
            if current_sink in sinks and current_sink in sink_volume:
                is_mute = sinks[current_sink]['mute']
                if is_mute:
                    tip_text = _("Mute")
                volume = max(sink_volume[current_sink]) * 100.0 / pypulse.NORMAL_VOLUME_VALUE
                if volume == 0:
                    volume_level = 0
                elif 0 < volume <= 40:
                    volume_level = 1
                elif 40 < volume <= 80:
                    volume_level = 2
                else:
                    volume_level = 3
                self.tray_obj.set_tray_icon(volume_level, is_mute)
            if self.tray_obj.tray_obj:
                self.tray_obj.tray_obj.set_tooltip_text(tip_text)

    def __source_state_cb(self, obj, channel, port, volume, source, idx):
        pypulse.input_channels[idx] = channel
        pypulse.input_active_ports[idx] = port
        pypulse.input_volumes[idx] = volume
        pypulse.input_devices[idx] = source
        if self.__fallback_source_index is None and pypulse.get_fallback_source_name() == source['name']:
            self.__fallback_source_index = idx
        if self.__fallback_source_index == idx:
            self.__set_input_status()

    def __server_state_cb(self, obj, dt):
        pypulse.server_info = dt
        self.__fallback_sink_index = pypulse.get_fallback_sink_index()
        if self.__fallback_sink_index in pypulse.output_volumes:
            self.__set_output_status()
        self.__fallback_source_index = pypulse.get_fallback_source_index()
        if self.__fallback_source_index in pypulse.input_volumes:
            self.__set_input_status()

    def __sinkinput_state_cb(self, obj, dt, index):
        if index in self.stream_mpris:
            return
        process_id = int(dt['proplist']['application.process.id'])
        if process_id == os.getpid():
            return
        if index not in self.stream_list:
            self.__make_playback_box(dt, index)
            stream_num = len(self.stream_list.keys())
            mpris_num = len(self.mpris_list.keys())
            if stream_num > 0 or mpris_num > 0:
                self.__app_vbox.set_no_show_all(False)
                self.__app_vbox.show_all()
            self.adjust_size()
            self.emit("stream-changed")
        else:
            self.__set_playback_status(dt,
                                       self.stream_list[index]['scale'],
                                       self.stream_list[index]['button'])
        pypulse.playback_info[index] = dt

    def sinkinput_removed_cb(self, obj, index):
        if index in self.stream_mpris:
            if self.stream_mpris[index] in self.mpris_stream:
                del self.mpris_stream[self.stream_mpris[index]]
            del self.stream_mpris[index]
        if index in pypulse.playback_info:
            del pypulse.playback_info[index]
        if index in self.stream_list:
            process_id = self.stream_list[index]['process_id']
            if process_id in self.stream_process:
                del self.stream_process[process_id]
            self.stream_list[index]['container'].destroy()
            del self.stream_list[index]
            self.stream_num -= 1
            stream_num = len(self.stream_list.keys())
            mpris_num = len(self.mpris_list.keys())
            if stream_num == 0 and mpris_num == 0:
                self.__app_vbox.hide_all()
                self.__app_vbox.set_no_show_all(True)
            self.adjust_size()
            self.emit("stream-changed")

    def sink_removed_cb(self, obj, index):
        if index in pypulse.output_devices:
            del pypulse.output_devices[index]
        if index in pypulse.output_channels:
            del pypulse.output_channels[index]
        if index in pypulse.output_active_ports:
            del pypulse.output_active_ports[index]
        if index in pypulse.output_volumes:
            del pypulse.output_volumes[index]

    def source_removed_cb(self, obj, index):
        if index in pypulse.input_devices:
            del pypulse.input_devices[index]
        if index in pypulse.input_channels:
            del pypulse.input_channels[index]
        if index in pypulse.input_active_ports:
            del pypulse.input_active_ports[index]
        if index in pypulse.input_volumes:
            del pypulse.input_volumes[index]

    def __set_output_status(self):
        # if sinks list is empty, then can't set output volume
        current_sink = self.__fallback_sink_index
        sinks = pypulse.output_devices
        if current_sink is None:
            self.speaker_scale.set_sensitive(False)
            self.speaker_mute_button.set_sensitive(False)
            self.speaker_scale.set_enable(False)
            self.speaker_mute_button.set_active(False)
        # set output volume
        else:
            self.speaker_scale.set_sensitive(True)
            self.speaker_mute_button.set_sensitive(True)
            is_mute = sinks[current_sink]['mute']
            self.speaker_mute_button.set_active(not is_mute)
            self.speaker_scale.set_enable(not is_mute)
            volume = max(pypulse.output_volumes[current_sink])
            self.speaker_scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
        self.update_tray_icon()

    def __set_input_status(self):
        # if sources list is empty, then can't set input volume
        current_source = self.__fallback_source_index
        sources = pypulse.input_devices
        if current_source is None:
            self.microphone_scale.set_sensitive(False)
            self.microphone_mute_button.set_sensitive(False)
            self.microphone_scale.set_enable(False)
            self.microphone_mute_button.set_active(False)
        # set input volume
        else:
            self.microphone_scale.set_sensitive(True)
            self.microphone_mute_button.set_sensitive(True)
            is_mute = sources[current_source]['mute']
            self.microphone_mute_button.set_active(not is_mute)
            self.microphone_scale.set_enable(not is_mute)
            volume = max(pypulse.input_volumes[current_source])
            self.microphone_scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
    
    def __set_playback_status(self, stream, scale, button):
        if not stream['has_volume']:
            scale.set_sensitive(False)
            button.set_sensitive(False)
            scale.set_enable(False)
            button.set_active(False)
        else:
            scale.set_sensitive(True)
            button.set_sensitive(True)
            is_mute = stream['mute']
            button.set_active(not is_mute)
            scale.set_enable(not is_mute)
            volume = max(stream['volume'])
            scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
            scale.volume_channels = len(stream['volume'])

    # mpris dbus signal
    def mpris2_new_cb(self, obj, pid):
        vbox = gtk.VBox()
        image_pixbuf = self.__get_pixbuf_from_icon_name(
            obj.mpris_process[pid]['property']['DesktopEntry'])
        logo_pixbuf = self.__get_pixbuf_from_icon_name(
            obj.mpris_process[pid]['property']['DesktopEntry'], 32)
        if image_pixbuf:
            img = gtk.image_new_from_pixbuf(image_pixbuf)
        else:
            img = gtk.image_new_from_pixbuf(self.stream_icon)
        img.set_size_request(16, 16)
        # application title
        app_title = obj.mpris_process[pid]['property']['Identity']
        #if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Stopped':
            #app_title = obj.mpris_process[pid]['property']['Identity']
        #else:
            #app_title = "%s - %s" % (obj.mpris_process[pid]['property']['Identity'], _(obj.mpris_process[pid]['property']['PlaybackStatus']))
        label = Label(app_title, label_width=115)
        hbox = gtk.HBox(False, 5)
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(label), False, False)
        vbox.pack_start(hbox, False, False)

        # metadata info
        meta_box = gtk.HBox(False, 10)
        xesam_vbox = gtk.VBox(False)
        #art_img = gtk.Image()
        art_img = gtk.EventBox()
        art_img.set_size_request(40, 40)
        art_img.connect("expose-event", self.__draw_mpris_art_img)

        xesam_title = Label("", label_width=75)
        xesam_artist = Label("", label_width=75)
        xesam_album = Label("", label_width=75)
        xesam_vbox.pack_start(xesam_title)
        xesam_vbox.pack_start(xesam_artist)
        #xesam_vbox.pack_start(xesam_album)
        meta_box.pack_start(self.__make_align(art_img, padding_left=21, height=40), False, False)
        meta_box.pack_start(xesam_vbox)

        self.mpris_list[pid] = {}
        # mpris control
        scale = HScalebar(app_theme.get_pixbuf("sound/point.png"), show_value=False, format_value="%", value_min=0, value_max=1, line_height=3)
        scale.set_magnetic_values([(0, 0.1), (1, 0.1)])
        scale.set_size_request(70, 10)
        prev_bt = gtk.Button()
        pause_bt = gtk.Button()
        stop_bt = gtk.Button()
        next_bt = gtk.Button()

        prev_bt.set_size_request(20, 22)
        pause_bt.set_size_request(29, 30)
        #stop_bt.set_size_request(16, 16)
        next_bt.set_size_request(20, 22)

        prev_bt.pixbuf = "previous"
        if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Playing':
            pause_bt.pixbuf = "pause"
        else:
            pause_bt.pixbuf = "play"
        #stop_bt.pixbuf = self.stop_img
        next_bt.pixbuf = "next"

        scale.set_value(obj.mpris_process[pid]['property']['Volume'])

        prev_bt.connect("clicked", self.__mpris_prev_cb, obj, pid)
        pause_bt.connect("clicked", self.__mpris_pause_cb, obj, pid)
        #stop_bt.connect("clicked", self.__mpris_stop_cb, obj, pid)
        next_bt.connect("clicked", self.__mpris_next_cb, obj, pid)
        prev_bt.connect("expose-event", self.__draw_mpris_button_cb)
        pause_bt.connect("expose-event", self.__draw_mpris_button_cb)
        stop_bt.connect("expose-event", self.__draw_mpris_button_cb)
        next_bt.connect("expose-event", self.__draw_mpris_button_cb)
        scale.connect("value-changed", self.__mpris_volume_cb, obj, pid)

        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(prev_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(pause_bt, height=-1), False, False)
        #hbox.pack_start(self.__make_align(stop_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(next_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, height=-1), False, False)
        vbox.pack_start(self.__make_align(hbox, xalign=0.5, padding_top=5, padding_bottom=10, height=-1), False, False)

        self.mpris_list[pid]['app_title'] = label
        self.mpris_list[pid]['prev'] = prev_bt
        self.mpris_list[pid]['pause'] = pause_bt
        self.mpris_list[pid]['stop'] = stop_bt
        self.mpris_list[pid]['next'] = next_bt
        self.mpris_list[pid]['scale'] = scale
        self.mpris_list[pid]['meta'] = meta_box
        self.mpris_list[pid]['meta_img'] = art_img
        self.mpris_list[pid]['meta_title'] = xesam_title
        self.mpris_list[pid]['meta_artist'] = xesam_artist
        self.mpris_list[pid]['meta_album'] = xesam_album
        self.mpris_list[pid]['container'] = vbox
        self.mpris_list[pid]['logo'] = logo_pixbuf
        if not obj.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['height'] = self.mpris_base_height
        else:
            vbox.pack_start(meta_box, False, False)
            vbox.reorder_child(meta_box, 1)
            self.__set_mpris_meta_info(pid)
            self.mpris_list[pid]['height'] = self.mpris_base_height + 40
        self.__mpris_total_height += self.mpris_list[pid]['height']
        # delete playback_stream widget
        if pid in self.stream_process:
            self.stream_process[pid]['container'].destroy()
            del self.stream_list[self.stream_process[pid]['stream_id']]
            del self.stream_process[pid]
        vbox.show_all()
        self.__mpris_vbox.pack_start(vbox, False, False)

        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num > 0 or mpris_num > 0:
            self.__app_vbox.set_no_show_all(False)
            self.__app_vbox.show_all()
        self.adjust_size()
        self.emit("stream-changed")

    def __draw_mpris_art_img(self, widget, event):
        x, y, w, h = widget.allocation
        cr = widget.window.cairo_create()
        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) 
        surface_cr = gtk.gdk.CairoContext(cairo.Context(surface))
        surface_cr.set_source_rgba(0, 0, 0, 1.0)
        surface_cr.rectangle(5, 5, w-10, h-10)
        surface_cr.stroke()

        dtk_cairo_blur.gaussian_blur(surface, 3)
        cr.set_source_surface(surface, 0, 0)
        cr.paint()

        cr.set_source_rgb(1, 1, 1)
        cr.rectangle(2, 2, w-4, h-4)
        cr.fill()

        if widget.pixbuf:
            cr.set_source_pixbuf(widget.pixbuf, 4, 4)
            cr.paint()
        return True

    def __set_mpris_meta_info(self, pid):
        if 'mpris:artUrl' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            arturl = urlparse(self.mpris2.mpris_process[pid]['property']['Metadata']['mpris:artUrl'])
            if arturl.scheme == 'file' and os.path.exists(arturl.path):
                art_pixbuf = gtk.gdk.pixbuf_new_from_file(arturl.path).scale_simple(32, 32, gtk.gdk.INTERP_TILES)
                #self.mpris_list[pid]['meta_img'].set_from_pixbuf(art_pixbuf)
                self.mpris_list[pid]['meta_img'].pixbuf = art_pixbuf
        else:
            self.mpris_list[pid]['meta_img'].pixbuf = self.mpris_list[pid]['logo']
        self.mpris_list[pid]['meta_img'].queue_draw()
        if 'xesam:title' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_title'].set_text(
                markup_escape_text(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:title']))
        else:
            self.mpris_list[pid]['meta_title'].set_text("-")
        if 'xesam:artist' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_artist'].set_text(
                markup_escape_text('&'.join(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:artist'])))
        else:
            self.mpris_list[pid]['meta_artist'].set_text("-")
        if 'xesam:album' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_album'].set_text(
                markup_escape_text(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:album']))
        else:
            self.mpris_list[pid]['meta_album'].set_text("-")
        
    def mpris2_removed_cb(self, obj, pid):
        if pid in self.mpris_list:
            self.mpris_list[pid]['container'].destroy()
            self.mpris_list[pid]['meta'].destroy()
            self.__mpris_total_height -= self.mpris_list[pid]['height']
            del self.mpris_list[pid]
        if pid in self.mpris_stream:
            stream_id = self.mpris_stream[pid]
            if stream_id in pypulse.playback_info:
                self.__make_playback_box(pypulse.playback_info[stream_id], stream_id)
        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num == 0 and mpris_num == 0:
            self.__app_vbox.hide_all()
            self.__app_vbox.set_no_show_all(True)
        self.adjust_size()
        self.emit("stream-changed")

    def mpris2_changed_cb(self, obj, pid, changed):
        #print "mpris changed", pid, changed
        if pid not in self.mpris_list:
            return
        if 'Volume' in changed:
            self.mpris_list[pid]['scale'].set_value(changed['Volume'])
        if 'PlaybackStatus' in changed:
            # application title && hide meta info
            if changed['PlaybackStatus'] == 'Stopped':
                if self.mpris_list[pid]['meta'] in self.mpris_list[pid]['container'].get_children():
                    self.mpris_list[pid]['container'].remove(self.mpris_list[pid]['meta'])
                    self.__mpris_total_height -= self.mpris_list[pid]['height']
                    self.mpris_list[pid]['height'] = self.mpris_base_height
                    self.__mpris_total_height += self.mpris_list[pid]['height']
                    self.adjust_size()
                    self.emit("stream-changed")
            # button image
            if changed['PlaybackStatus'] == 'Playing':
                self.mpris_list[pid]['pause'].pixbuf = "pause"
            else:
                self.mpris_list[pid]['pause'].pixbuf = "play"
            self.mpris_list[pid]['pause'].queue_draw()
        if 'Metadata' in changed and obj.mpris_process[pid]['property']['PlaybackStatus'] != 'Stopped':
            self.__set_mpris_meta_info(pid)
            if self.mpris_list[pid]['meta'] not in self.mpris_list[pid]['container'].get_children():
                self.mpris_list[pid]['container'].pack_start(self.mpris_list[pid]['meta'], False, False)
                self.mpris_list[pid]['container'].reorder_child(self.mpris_list[pid]['meta'], 1)
                self.mpris_list[pid]['container'].show_all()
                self.__mpris_total_height -= self.mpris_list[pid]['height']
                self.mpris_list[pid]['height'] = self.mpris_base_height + 40
                self.__mpris_total_height += self.mpris_list[pid]['height']
                self.adjust_size()
                self.emit("stream-changed")

    def __mpris_prev_cb(self, bt, obj, pid):
        if pid not in obj.mpris_process:
            return
        try:
            obj.mpris_process[pid]['obj'].get_dbus_method(
                'Previous', 'org.mpris.MediaPlayer2.Player')()
        except Exception, e:
            if e._dbus_error_name == "org.freedesktop.DBus.Error.ServiceUnknown":
                self.mpris2_removed_cb(obj, pid)
class DisplayView(gtk.VBox):
    '''
    class docs
    '''
	
    def __init__(self):
        '''
        init docs
        '''
        gtk.VBox.__init__(self)

        self.brightness_id = None

        self.display_manager = DisplayManager()
        self.__xrandr_settings = self.display_manager.get_xrandr_settings()
        self.__xrandr_settings.connect("changed", self.__xrandr_changed)

        self.resize_width = 790
        self.resize_height = 200
        self.monitor_items = []
        self.__output_names = []
        self.__current_output_name = self.display_manager.get_primary_output_name()
        self.__setup_monitor_items()
        self.sizes_items = []
        self.monitor_combo = None
        if len(self.monitor_items) > 1 and self.display_manager.is_copy_monitors():
            self.__set_same_sizes()
        else:
            self.__setup_sizes_items()
        self.multi_monitors_items = [(_("Copy Display"), 1), 
                                     (_("Extend Display"), 2), 
                                     (_("Only shown in display 1"), 3), 
                                     (_("Only shown in display 2"), 4)]
        self.rotation_items = [(_("Normal"), 1), 
                               (_("Right"), 2), 
                               (_("Left"), 3), 
                               (_("Inverted"), 4)]
        self.duration_items = [("1 %s" % _("Minute"), 1), 
                               ("2 %s" % _("Minutes"), 2), 
                               ("3 %s" % _("Minutes"), 3), 
                               ("5 %s" % _("Minutes"), 5), 
                               ("10 %s" % _("Minutes"), 10), 
                               ("30 %s" % _("Minutes"), 30), 
                               ("1 %s" % _("Hour"), 60), 
                               (_("Never"), DisplayManager.BIG_NUM / 60)]
        '''
        scrolled_window
        '''
        self.scrolled_window = ScrolledWindow()
        self.scrolled_window.set_size_request(-1, 425)
        self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.main_box = gtk.VBox()
        self.main_box.set_size_request(600, -1)
        self.body_box = gtk.HBox()
        '''
        left, right align
        '''
        self.left_align = self.__setup_align(padding_top = FRAME_TOP_PADDING, 
                                             padding_left = TEXT_WINDOW_LEFT_PADDING)
        self.right_align = self.__setup_align(padding_top = FRAME_TOP_PADDING, 
                                              padding_left = 0)
        '''
        left, right box
        '''
        self.left_box = gtk.VBox(spacing = WIDGET_SPACING)
        self.right_box = gtk.VBox(spacing = WIDGET_SPACING)
        '''
        monitor operation && detect
        '''
        self.monitor_resize_align = self.__setup_align(padding_top = 11, 
                                                       padding_left = int(TEXT_WINDOW_LEFT_PADDING / 2))
        self.monitor_resize_box = MonitorResizableBox(self.display_manager)
        self.monitor_resize_box.select_output(self.__current_output_name)
        self.monitor_resize_box.connect("select-output", self.__select_output)
        self.monitor_resize_box.connect("resize", self.__resize_box)
        self.monitor_resize_align.add(self.monitor_resize_box)
        '''
        monitor display
        '''
        self.monitor_display_align = self.__setup_title_align(
            app_theme.get_pixbuf("display/monitor_display.png"), 
            _("Display"), 
            0)
        '''
        monitor
        '''
        self.monitor_align = self.__setup_align()
        self.monitor_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.monitor_label = self.__setup_label(_("Monitor"))
        self.monitor_combo = self.__setup_combo(self.monitor_items)
        self.monitor_combo.set_select_index(self.display_manager.get_primary_output_name_index(self.monitor_items))
        self.monitor_combo.connect("item-selected", self.__combo_item_selected, "monitor_combo")
        self.__widget_pack_start(self.monitor_box, 
            [self.monitor_label, 
             self.monitor_combo])
        self.monitor_align.add(self.monitor_box)
        '''
        goto individuation or power setting
        '''
        self.goto_align = self.__setup_align()
        self.goto_box = gtk.VBox(spacing = WIDGET_SPACING)
        self.goto_label = self.__setup_label(_("Relevant Settings"), 
                                             text_size = TITLE_FONT_SIZE, 
                                             width = None, 
                                             align = ALIGN_START)
        goto_color = GOTO_FG_COLOR
        self.goto_individuation_label = self.__setup_label(
            text = _("<span foreground=\"%s\" underline=\"single\">Personalization</span>") % goto_color, 
            width = None, 
            align = ALIGN_START)
        self.goto_individuation_label.connect("button-press-event", 
                                              self.__button_press, 
                                              "individuation")
        set_clickable_cursor(self.goto_individuation_label)
        self.goto_power_label = self.__setup_label(
            text = _("<span foreground=\"%s\" underline=\"single\">Power</span>") % goto_color, 
            width = None, 
            align = ALIGN_START)
        self.goto_power_label.connect("button-press-event", 
                                      self.__button_press, 
                                      "power")
        set_clickable_cursor(self.goto_power_label)
        self.__widget_pack_start(self.goto_box, 
                                 [self.goto_label, 
                                  self.goto_individuation_label, 
                                  self.goto_power_label
                                 ])
        self.goto_align.add(self.goto_box)
        '''
        sizes
        '''
        self.sizes_align = self.__setup_align()
        self.sizes_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.sizes_label = self.__setup_label(_("Resolution"))
        self.sizes_combo = self.__setup_combo(self.sizes_items)
        if self.sizes_combo:
            self.sizes_combo.set_select_index(
                self.display_manager.get_screen_size_index(self.__current_output_name, 
                                                           self.sizes_items))
            self.sizes_combo.connect("item-selected", self.__combo_item_selected, "sizes_combo")
            self.__widget_pack_start(self.sizes_box, 
                                     [self.sizes_label, self.sizes_combo])
        self.sizes_align.add(self.sizes_box)
        '''
        rotation
        '''
        self.rotation_align = self.__setup_align()
        self.rotation_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.rotation_label = self.__setup_label(_("Rotation"))
        self.rotation_combo = self.__setup_combo(self.rotation_items)
        self.rotation_combo.set_select_index(self.display_manager.get_screen_rotation_index(self.__current_output_name))
        self.rotation_combo.connect("item-selected", self.__combo_item_selected, "rotation_combo")
        self.__widget_pack_start(self.rotation_box, 
            [self.rotation_label, 
             self.rotation_combo])
        self.rotation_align.add(self.rotation_box)
        '''
        multi-monitors
        '''
        self.multi_monitors_align = self.__setup_align()
        self.multi_monitors_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.multi_monitors_label = self.__setup_label(_("Multi-Monitor"))
        self.multi_monitors_combo = self.__setup_combo(self.multi_monitors_items)
        self.multi_monitors_combo.set_select_index(self.display_manager.get_multi_monitor_index())
        self.multi_monitors_combo.connect("item-selected", self.__combo_item_selected, "multi_monitors_combo")
        self.__widget_pack_start(self.multi_monitors_box, 
            [self.multi_monitors_label, self.multi_monitors_combo])
        self.multi_monitors_align.add(self.multi_monitors_box)
        if self.display_manager.get_output_count() < 2:
            self.multi_monitors_align.set_size_request(-1, 0)
            self.multi_monitors_align.set_child_visible(False)
        '''
        monitor brightness
        '''
        self.monitor_bright_align = self.__setup_title_align(
            app_theme.get_pixbuf("display/monitor_bright.png"), 
            _("Brightness"))
        '''
        brightness
        '''
        self.brightness_align = self.__setup_align()
        self.brightness_box = gtk.HBox(spacing = 2)
        self.brightness_label_align = self.__setup_align(padding_top = 8, 
                                                         padding_left = 0, 
                                                         padding_right = 5)
        self.brightness_label = self.__setup_label(_("Brightness"))
        self.brightness_label_align.add(self.brightness_label)
        
        self.brightness_scale = HScalebar(point_dpixbuf = app_theme.get_pixbuf("scalebar/point.png"), 
                                          value_min = 0.1, 
                                          value_max = 1.0)
        self.brightness_scale.set_size_request(HSCALEBAR_WIDTH, 33)
        self.brightness_scale.set_value(self.display_manager.get_screen_brightness())
        self.brightness_scale.connect("value-changed", self.__set_brightness)
        self.__widget_pack_start(self.brightness_box, 
            [self.brightness_label_align, 
             self.brightness_scale])
        self.brightness_align.add(self.brightness_box)
        '''
        auto adjust monitor brightness
        '''
        self.auto_adjust_align = self.__setup_align()
        self.auto_adjust_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.auto_adjust_label = self.__setup_label(_("Auto-Brightness"))
        self.auto_adjust_toggle_align = self.__setup_align(padding_top = 4, padding_left = 158)
        self.auto_adjust_toggle = self.__setup_toggle()
        self.auto_adjust_toggle.set_active(self.display_manager.is_enable_close_monitor())
        self.auto_adjust_toggle.connect("toggled", self.__toggled, "auto_adjust_toggle")
        self.auto_adjust_toggle_align.add(self.auto_adjust_toggle)
        self.__widget_pack_start(self.auto_adjust_box, 
            [self.auto_adjust_label, self.auto_adjust_toggle_align])
        self.auto_adjust_align.add(self.auto_adjust_box)
        '''
        close monitor
        '''
        self.close_monitor_align = self.__setup_align()
        self.close_monitor_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.close_monitor_label = self.__setup_label(_("Turn off monitor"))
        self.close_monitor_combo = self.__setup_combo(self.duration_items)
        self.close_monitor_combo.set_select_index(self.display_manager.get_close_monitor_index(self.duration_items))
        self.close_monitor_combo.connect("item-selected", self.__combo_item_selected, "close_monitor_combo")
        self.__widget_pack_start(self.close_monitor_box, 
            [self.close_monitor_label, 
             self.close_monitor_combo])
        self.close_monitor_align.add(self.close_monitor_box)
        '''
        monitor lock
        '''
        self.monitor_lock_align = self.__setup_title_align(
            app_theme.get_pixbuf("lock/lock.png"), 
            _("Lock Screen"))
        '''
        auto monitor lock
        '''
        self.auto_lock_align = self.__setup_align()
        self.auto_lock_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.auto_lock_label = self.__setup_label(_("Lock screen automatically"))
        self.auto_lock_toggle_align = self.__setup_align(padding_top = 4, padding_left = 158)
        self.auto_lock_toggle = self.__setup_toggle()
        is_enable_lock_display = self.display_manager.is_enable_lock_display()
        self.auto_lock_toggle.set_active(is_enable_lock_display)
        self.auto_lock_toggle.connect("toggled", self.__toggled, "auto_lock_toggle")
        self.auto_lock_toggle_align.add(self.auto_lock_toggle)
        self.__widget_pack_start(self.auto_lock_box, 
            [self.auto_lock_label, self.auto_lock_toggle_align])
        self.auto_lock_align.add(self.auto_lock_box)
        '''
        lock display
        '''
        self.lock_display_align = self.__setup_align(padding_bottom = 20)
        self.lock_display_box = gtk.HBox(spacing = WIDGET_SPACING)
        self.lock_display_label = self.__setup_label(_("Lock Screen")) 
        self.lock_display_combo = self.__setup_combo(self.duration_items)
        self.lock_display_combo.set_select_index(self.display_manager.get_lock_display_index(self.duration_items))
        self.lock_display_combo.connect("item-selected", self.__combo_item_selected, "lock_display_combo")
        self.__widget_pack_start(self.lock_display_box, 
            [self.lock_display_label, 
             self.lock_display_combo])
        self.lock_display_align.add(self.lock_display_box)
        '''
        left_align pack_start
        '''
        self.__widget_pack_start(self.left_box, 
            [self.monitor_display_align, 
             self.monitor_align, 
             self.sizes_align, 
             self.rotation_align, 
             self.multi_monitors_align, 
             self.monitor_bright_align, 
             self.brightness_align, 
             #self.auto_adjust_align, 
             #self.close_monitor_align, 
             self.monitor_lock_align, 
             self.auto_lock_align, 
             self.lock_display_align])
        self.left_align.add(self.left_box)
        '''
        right_align pack_start
        '''
        self.__widget_pack_start(self.right_box, 
            [self.goto_align])
        self.right_box.set_size_request(280, -1)
        self.right_align.add(self.right_box)
        '''
        main && body box
        '''
        self.main_box.pack_start(self.monitor_resize_align, False, False)
        self.body_box.pack_start(self.left_align)
        self.body_box.pack_start(self.right_align, False, False)
        self.main_box.pack_start(self.body_box)
        '''
        this->HBox pack_start
        '''
        self.scrolled_window.add_child(self.main_box)
        self.pack_start(self.scrolled_window)

        self.__send_message("status", ("display", ""))

    def show_again(self):
        self.__send_message("status", ("display", ""))

    def reset(self):
        self.__send_message("status", ("display", _("Reset to default value")))
        self.display_manager.reset()
        self.close_monitor_combo.set_select_index(self.display_manager.get_close_monitor_index(self.duration_items))
        self.lock_display_combo.set_select_index(self.display_manager.get_lock_display_index(self.duration_items))
        self.multi_monitors_combo.set_select_index(0)

    def __handle_dbus_replay(self, *reply):
        pass

    def __handle_dbus_error(self, *error):
        pass

    def __send_message(self, message_type, message_content):
        if is_dbus_name_exists(APP_DBUS_NAME):
            bus_object = dbus.SessionBus().get_object(APP_DBUS_NAME, APP_OBJECT_NAME)
            method = bus_object.get_dbus_method("message_receiver")
            method(message_type, 
                   message_content, 
                   reply_handler=self.__handle_dbus_replay, 
                   error_handler=self.__handle_dbus_error)

    def __button_press(self, widget, event, module_id):
        self.__send_message("goto", (module_id, ""))

    def __expose(self, widget, event):
        try:
            cr = widget.window.cairo_create()                                        
            rect = widget.allocation                                                 
        
            cr.set_source_rgb(*color_hex_to_cairo(MODULE_BG_COLOR))                  
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)                    
            cr.fill()
        except e:
            print "DEBUG", e

    def __change_current_output(self, output_name, from_monitor_combo=True):
        self.__current_output_name = output_name

        if not from_monitor_combo:
            self.monitor_combo.set_select_index(self.display_manager.get_output_name_index(output_name, self.monitor_items))

        if not self.display_manager.is_copy_monitors():
            self.__setup_sizes_items()
        if len(self.sizes_items):
            self.sizes_combo.add_items(items = self.sizes_items)
        self.sizes_combo.set_select_index(self.display_manager.get_screen_size_index(
            self.__current_output_name, self.sizes_items))
    
    def __select_output(self, widget, output_name):
        print "Output name:", output_name
        self.__change_current_output(output_name, False)
    
    def __set_same_sizes(self):
        same_sizes = self.display_manager.get_same_sizes(                      
            self.display_manager.get_screen_sizes(self.monitor_items[0][1]), 
            self.display_manager.get_screen_sizes(self.monitor_items[1][1]))
        i = 0
        
        del self.sizes_items[:]                                             
        while i < len(same_sizes):                                          
            self.sizes_items.append((same_sizes[i], i))                     
                                                                                
            i += 1                                                          
    
    def __xrandr_changed(self, key):
        if key == "output-names":
            self.display_manager.init_xml()
            self.__setup_monitor_items()
            self.monitor_combo.add_items(items = self.monitor_items)
            if len(self.monitor_items) > 1:
                if self.display_manager.is_copy_monitors():
                    self.__set_same_sizes()
                    self.sizes_combo.add_items(items = self.sizes_items) 

                self.multi_monitors_align.set_size_request(-1, 30)
                self.multi_monitors_align.set_child_visible(True)
            else:
                self.multi_monitors_align.set_size_request(-1, 0)
                self.multi_monitors_align.set_child_visible(False)
            return

        if key == "brightness":
            self.brightness_scale.set_value(self.display_manager.get_screen_brightness())
            return

    def __set_brightness_value(self, value):
        self.display_manager.set_screen_brightness(self.__current_output_name, value)

    def __set_brightness(self, widget, event):
        value = self.brightness_scale.get_value()
        self.__send_message("status", 
                ("display", _("Changed brightness to %d%%") % int(value * 100)))
        if self.brightness_id:
            gobject.source_remove(self.brightness_id)
        self.brightness_id = gobject.timeout_add_seconds(1, self.__set_brightness_value, value)
    
    def __setup_monitor_items(self):
        self.__output_names = self.display_manager.get_output_names()
        del self.monitor_items[:]
        i = 0

        while (i < len(self.__output_names)):
            self.monitor_items.append(self.display_manager.get_output_name(self.__output_names[i]))
            i += 1

    def __setup_sizes_items(self):
        screen_sizes = self.display_manager.get_screen_sizes(self.__current_output_name)
        del self.sizes_items[:]
        i = 0

        while i < len(screen_sizes):
            self.sizes_items.append((screen_sizes[i], i))
            i += 1

    def __toggled(self, widget, object=None):
        if object == "auto_adjust_toggle":
            if not widget.get_active():
                self.__send_message("status", ("display", _("Changed to manual adjustment")))
                self.display_manager.set_close_monitor(DisplayManager.BIG_NUM / 60)
            else:
                self.__send_message("status", ("display", _("Changed to automatic adjustment")))
            return

        if object == "auto_lock_toggle":
            if not widget.get_active():
                self.lock_display_combo.set_sensitive(False)
                self.__send_message("status", ("display", _("Changed to manual lock")))
            else:
                self.lock_display_combo.set_sensitive(True)
                self.__send_message("status", ("display", _("Changed to automatic lock")))
            return

    def __combo_item_selected(self, widget, item_text=None, item_value=None, item_index=None, object=None):
        if object == "monitor_combo":
            self.__send_message("status", ("display", _("Changed current output to %s") % item_text))
            self.__change_current_output(item_value)
            return

        if object == "sizes_combo":
            size = self.sizes_items[item_value][0]
            self.__send_message("status", ("display", _("Changed resolution to %s") % size))
            self.display_manager.set_screen_size(self.__current_output_name, size)
            return
        
        if object == "rotation_combo":
            self.__send_message("status", ("display", _("Changed rotation to %s") % item_text))
            self.display_manager.set_screen_rotation(self.__current_output_name, item_value)
            return

        if object == "multi_monitors_combo":
            self.__send_message("status", ("display", _("Changed multi-monitor mode to %s") % item_text))
            self.display_manager.set_multi_monitor(item_value)
            return
        
        if object == "close_monitor_combo":
            self.__send_message("status", ("display", _("Idle time before turning off display has been changed to to %s") % item_text))
            self.display_manager.set_close_monitor(item_value)
            return

        if object == "lock_display_combo":
            self.__send_message("status", ("display", _("Idle time before locking display has been changed to %s") % item_text))
            if item_value == DisplayManager.BIG_NUM / 60:
                self.auto_lock_toggle.set_active(False)
            else:
                self.auto_lock_toggle.set_active(True)
            self.display_manager.set_lock_display(item_value)
            return

    def __resize_box(self, widget, height):
        self.monitor_resize_box.set_size_request(self.resize_width, height - FRAME_TOP_PADDING)

    def __setup_separator(self):
        hseparator = HSeparator(app_theme.get_shadow_color("hSeparator").get_color_info(), 0, 0)
        hseparator.set_size_request(500, HSEPARATOR_HEIGHT)
        return hseparator
    
    def __setup_title_label(self, 
                            text="", 
                            text_color=app_theme.get_color("globalTitleForeground"), 
                            text_size=TITLE_FONT_SIZE, 
                            text_x_align=ALIGN_START, 
                            label_width=180):
        return Label(text = text, 
                     text_color = text_color, 
                     text_size = text_size, 
                     text_x_align = text_x_align, 
                     label_width = label_width, 
                     enable_select = False, 
                     enable_double_click = False)
    
    def __setup_label(self, text="", text_size=CONTENT_FONT_SIZE, width=180, align=ALIGN_END, wrap_width=None):
        label = Label(text = text, 
                      text_color = None, 
                      text_size = text_size, 
                      text_x_align = align, 
                      label_width = width, 
                      wrap_width = wrap_width, 
                      enable_select = False, 
                      enable_double_click = False)
        return label

    def __setup_combo(self, items=[], width=HSCALEBAR_WIDTH):
        if len(items) == 0:
            return None

        combo = ComboBox(items = items, select_index = 0, max_width = width, fixed_width = width)
        combo.set_size_request(width, WIDGET_HEIGHT)
        return combo

    def __setup_toggle(self):
        toggle = ToggleButton(app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            app_theme.get_pixbuf("toggle_button/active_normal.png"))
        return toggle

    def __setup_title_align(self, pixbuf, text, padding_top=FRAME_TOP_PADDING, padding_left=0):
        align = self.__setup_align(padding_top = padding_top, padding_left = padding_left)          
        align_box = gtk.VBox(spacing = WIDGET_SPACING)           
        title_box = gtk.HBox(spacing = WIDGET_SPACING)        
        image = ImageBox(pixbuf)
        label = self.__setup_title_label(text)
        separator = self.__setup_separator()               
        self.__widget_pack_start(title_box, [image, label])                  
        self.__widget_pack_start(align_box, [title_box, separator])
        align.add(align_box)
        return align
    
    def __setup_align(self, 
                      xalign=0, 
                      yalign=0, 
                      xscale=0, 
                      yscale=0, 
                      padding_top=0, 
                      padding_bottom=0, 
                      padding_left=FRAME_LEFT_PADDING + int(WIDGET_SPACING / 2), 
                      padding_right=0):
        align = gtk.Alignment()
        align.set(xalign, yalign, xscale, yscale)
        align.set_padding(padding_top, padding_bottom, padding_left, padding_right)
        align.connect("expose-event", self.__expose)
        return align

    def __widget_pack_start(self, parent_widget, widgets=[], expand=False, fill=False):
        if parent_widget == None:
            return
        for item in widgets:
            parent_widget.pack_start(item, expand, fill)
class TrayGui(gtk.VBox):
    '''sound tray gui'''
    #BASE_HEIGHT = 128  # has microphone
    BASE_HEIGHT = 103   # no microphone

    __gsignals__ = {
        "stream-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())}

    def __init__(self, tray_obj=None):
        super(TrayGui, self).__init__(False)
        self.tray_obj = tray_obj

        self.stream_icon = app_theme.get_pixbuf("sound/device.png").get_pixbuf().scale_simple(16, 16, gtk.gdk.INTERP_TILES)
        self.stream_num = 0
        self.stream_list = {}    # stream widgets
        self.stream_process = {} # process id to stream widgets
        self.stream_mpris = {}   # stream id to mpris process id

        self.__mpris_total_height = 0
        self.mpris_base_height = 70
        self.mpris_list = {}     # mpris widgets
        self.mpris_stream = {}   # mpris process id to stream id
        self.mpris2 = Mpris2()
        self.mpris2.connect("new", self.mpris2_new_cb)
        self.mpris2.connect("removed", self.mpris2_removed_cb)
        self.mpris2.connect("changed", self.mpris2_changed_cb)

        hbox = gtk.HBox(False)
        hbox.set_spacing(WIDGET_SPACING)
        #separator_color = [(0, ("#000000", 0.3)), (0.5, ("#000000", 0.2)), (1, ("#777777", 0.0))]
        #hseparator = HSeparator(app_theme.get_shadow_color("hSeparator").get_color_info(), 0, 0)
        #hseparator.set_size_request(150, 3)
        separator_color = [(0, ("#777777", 0.0)), (0.5, ("#000000", 0.3)), (1, ("#777777", 0.0))]
        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 5)
        #hbox.pack_start(self.__make_align(Label(_("Device"), enable_select=False, enable_double_click=False)), False, False)
        #hbox.pack_start(self.__make_align(hseparator), True, True)
        self.pack_start(self.__make_align(Label(_("Device"), enable_select=False, enable_double_click=False), height=-1), False, False)
        self.pack_start(self.__make_align(hseparator, xalign=0.5, height=5), False, False)

        volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE

        table = gtk.Table(2, 3)
        speaker_img = ImageBox(app_theme.get_pixbuf("sound/speaker-3.png"))
        self.speaker_scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        self.speaker_scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
        self.speaker_scale.set_size_request(90, 10)
        self.speaker_mute_button = SwitchButton(
            inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        table.attach(self.__make_align(speaker_img), 0, 1, 0, 1, 4)
        table.attach(self.__make_align(self.speaker_scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), 1, 2, 0, 1, 4)
        table.attach(self.__make_align(self.speaker_mute_button), 2, 3, 0, 1, 4)

        #microphone_img = ImageBox(app_theme.get_pixbuf("sound/microphone.png"))
        #self.microphone_scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        #self.microphone_scale.set_size_request(90, 10)
        #self.microphone_mute_button = SwitchButton(
            #inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            #active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        #table.attach(self.__make_align(microphone_img), 0, 1, 1, 2, 4)
        #table.attach(self.__make_align(self.microphone_scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), 1, 2, 1, 2, 4)
        #table.attach(self.__make_align(self.microphone_mute_button), 2, 3, 1, 2, 4)

        self.pack_start(table, False, False)

        self.__app_vbox = gtk.VBox(False)
        separator_color = [(0, ("#777777", 0.0)), (0.5, ("#000000", 0.3)), (1, ("#777777", 0.0))]
        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 5)
        self.__app_vbox.pack_start(self.__make_align(Label(_("Applications"), enable_select=False, enable_double_click=False)), False, False)
        self.__app_vbox.pack_start(self.__make_align(hseparator, xalign=0.5, height=5), False, False)

        self.__mpris_vbox = gtk.VBox(False)
        self.__app_vbox.pack_start(self.__mpris_vbox)
        self.pack_start(self.__app_vbox)

        hseparator = HSeparator(separator_color, 0, 0)
        hseparator.set_size_request(140, 7)
        self.pack_start(self.__make_align(hseparator, xalign=0.5, height=7), False, False)

        self.button_more = SelectButton(_("Advanced..."), font_size=10, ali_padding=5)
        self.button_more.set_size_request(-1, 25)
        self.pack_start(self.button_more, False, False)
        #self.pack_start(self.__make_align(height=10))
        ##########################################
        self.__set_output_status()
        #self.__set_input_status()

        # widget signals
        self.speaker_mute_button.connect("toggled", self.speaker_toggled)
        #self.microphone_mute_button.connect("toggled", self.microphone_toggled)
        self.speaker_scale.connect("value-changed", self.speaker_scale_value_changed)
        #self.microphone_scale.connect("value-changed", self.microphone_scale_value_changed)
        # pulseaudio signals
        pypulse.PULSE.connect("sink-changed", self.sink_changed_cb)
        #pypulse.PULSE.connect("source-changed", self.source_changed_cb)
        pypulse.PULSE.connect("server-changed", self.server_changed_cb)
        pypulse.PULSE.connect("sink-input-new", self.sink_input_new_cb)
        pypulse.PULSE.connect("sink-input-changed", self.sink_input_changed_cb)
        pypulse.PULSE.connect("sink-input-removed", self.sink_input_removed_cb)
        playback_streams = pypulse.PULSE.get_playback_streams()
        self.stream_num = len(playback_streams.keys())
        self.mpris2.get_mpris_list()
        self.mpris_num = len(self.mpris2.mpris_process.keys())
        if self.stream_num == 0 and self.mpris_num == 0:
            self.__app_vbox.set_no_show_all(True)
        for stream in playback_streams:
            self.__make_playback_box(playback_streams[stream], stream)

    def __make_align(self, widget=None, xalign=0.0, yalign=0.5, xscale=0.0,
                     yscale=0.0, padding_top=0, padding_bottom=0, padding_left=0,
                     padding_right=0, width=-1, height=25):
        align = gtk.Alignment()
        align.set_size_request(width, height)
        align.set(xalign, yalign, xscale, yscale)
        align.set_padding(padding_top, padding_bottom, padding_left, padding_right)
        if widget:
            align.add(widget)
        return align

    def __make_playback_box(self, stream, index):
        process_id = int(stream['proplist']['application.process.id'])
        # if it has show mpris, then don't show sink_input
        if process_id in self.mpris_list:
            self.mpris_stream[process_id] = index
            self.stream_mpris[index] = process_id
            return
        self.stream_list[index] = {}
        volume_max_percent = pypulse.MAX_VOLUME_VALUE * 100 / pypulse.NORMAL_VOLUME_VALUE
        icon_name = self.__white_list_check(stream)
        if icon_name:
            if icon_name[0] == '/' and os.path.exists(icon_name):
                try:
                    img = gtk.image_new_from_pixbuf(gtk.gdk.pixbuf_new_from_file(
                        icon_name).scale_simple(16, 16, gtk.gdk.INTERP_TILES))
                except:
                    img = gtk.image_new_from_pixbuf(self.stream_icon)
            else:
                img = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
        else:
            img = gtk.image_new_from_pixbuf(self.stream_icon)
        scale = HScalebar(show_value=False, format_value="%", value_min=0, value_max=volume_max_percent)
        scale.set_magnetic_values([(0, 5), (100, 5), (volume_max_percent, 5)])
        scale.set_size_request(90, 10)
        mute_button = SwitchButton(
            inactive_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"), 
            active_disable_dpixbuf=app_theme.get_pixbuf("toggle_button/inactive_normal.png"))
        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, padding_left=5, padding_right=5, height=25), False, False)
        hbox.pack_start(self.__make_align(mute_button), False, False)
        self.stream_list[index]['scale'] = scale
        self.stream_list[index]['button'] = mute_button
        self.stream_list[index]['container'] = hbox
        self.stream_list[index]['process_id'] = process_id
        self.stream_list[index]['stream_id'] = index
        self.stream_process[process_id] = self.stream_list[index]
        self.__set_playback_status(stream, scale, mute_button)
        if stream['volume_writable']:
            scale.connect("value-changed", self.playback_stream_scale_changed_cb, index, mute_button)
            mute_button.connect("toggled", self.playback_stream_toggled_cb, index, scale)
        hbox.show_all()
        self.__app_vbox.pack_start(hbox, False, False)

    def __white_list_check(self, stream):
        icon_name = None
        # check deepin-media-player
        if 'application.process.binary' in stream['proplist']:
            if stream['proplist']['application.process.binary'] == 'mplayer':
                process_id = int(stream['proplist']['application.process.id'])
                process_list = psutil.get_process_list()
                for p in process_list:
                    if p.pid == process_id and p.ppid != 0 and p.parent.name.startswith('deepin-media'):
                        return "deepin-media-player"
        if 'application.icon_name' in stream['proplist']:
            icon_name = stream['proplist']['application.icon_name']
        # check deepin-music-player
        if 'application.name' in stream['proplist']:
            if stream['proplist']['application.name'] == 'deepin-music-player':
                icon_name = "deepin-music-player"
        return icon_name

    ####################################################
    # widget signals
    def speaker_value_changed_thread(self):
        ''' speaker hscale value changed callback thread'''
        if not self.speaker_mute_button.get_active():
            self.speaker_mute_button.set_active(True)
        current_sink = pypulse.get_fallback_sink_index()
        if current_sink is None:
            return
        volume_list = pypulse.PULSE.get_output_volume_by_index(current_sink)
        channel_list = pypulse.PULSE.get_output_channels_by_index(current_sink)
        if not volume_list or not channel_list:
            return
        balance = pypulse.get_volume_balance(channel_list['channels'], volume_list, channel_list['map'])
        volume = int((self.speaker_scale.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_output_volume_with_balance(current_sink, volume, balance)

    def speaker_scale_value_changed(self, widget, value):
        '''set output volume'''
        self.speaker_value_changed_thread()

    def microphone_value_changed_thread(self):
        ''' microphone value changed callback thread'''
        if not self.microphone_mute_button.get_active():
            self.microphone_mute_button.set_active(True)
        current_source = pypulse.get_fallback_source_index()
        if current_source is None:
            return
        channel_list = pypulse.PULSE.get_input_channels_by_index(current_source)
        if not channel_list:
            return

        volume = int((self.microphone_scale.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_input_volume(current_source, [volume] * channel_list['channels'])

    def microphone_scale_value_changed(self, widget, value):
        ''' set input volume'''
        self.microphone_value_changed_thread()

    def speaker_toggled(self, button):
        active = button.get_active()
        current_sink = pypulse.get_fallback_sink_index()
        self.speaker_scale.set_enable(active)
        if current_sink is not None:
            pypulse.PULSE.set_output_mute(current_sink, not active)

    def microphone_toggled(self, button):
        active = button.get_active()
        current_source = pypulse.get_fallback_source_index()
        self.microphone_scale.set_enable(active)
        if current_source is not None:
            pypulse.PULSE.set_input_mute(current_source, not active)

    def playback_stream_scale_changed_cb(self, widget, value, index, button):
        if not button.get_active():
            button.set_active(True)
        volume = int((widget.get_value()) / 100.0 * pypulse.NORMAL_VOLUME_VALUE)
        pypulse.PULSE.set_sink_input_volume(index, [volume] * widget.volume_channels)

    def playback_stream_toggled_cb(self, button, index, scale):
        active = button.get_active()
        scale.set_enable(active)
        pypulse.PULSE.set_sink_input_mute(index, not active)

    # pulseaudio signals callback
    def sink_changed_cb(self, obj, index):
        obj.get_devices()
        current_sink = pypulse.get_fallback_sink_index()
        if current_sink is None or current_sink != index:
            return
        self.__set_output_status()

    # update tray_icon
    def update_tray_icon(self):
        if self.tray_obj:
            current_sink = pypulse.get_fallback_sink_index()
            sinks = pypulse.PULSE.get_output_devices()
            sink_volume = pypulse.PULSE.get_output_volume()
            tip_text = "%s %d%%" % (_("Volume"), self.speaker_scale.get_value())
            if current_sink in sinks and current_sink in sink_volume:
                is_mute = sinks[current_sink]['mute']
                if is_mute:
                    tip_text = _("Mute")
                volume = max(sink_volume[current_sink]) * 100.0 / pypulse.NORMAL_VOLUME_VALUE
                if volume == 0:
                    volume_level = 0
                elif 0 < volume <= 40:
                    volume_level = 1
                elif 40 < volume <= 80:
                    volume_level = 2
                else:
                    volume_level = 3
                self.tray_obj.set_tray_icon(volume_level, is_mute)
            if self.tray_obj.tray_obj:
                self.tray_obj.tray_obj.set_tooltip_text(tip_text)

    def source_changed_cb(self, obj, index):
        obj.get_devices()
        current_source = pypulse.get_fallback_source_index()
        if current_source is None or current_source != index:
            return
        self.__set_input_status()

    def server_changed_cb(self, obj):
        obj.get_devices()
        self.__set_output_status()
        #self.__set_input_status()

    def sink_input_new_cb(self, obj, index):
        obj.get_devices()
        playback = obj.get_playback_streams()
        if index in playback:
            self.__make_playback_box(playback[index], index)
            self.stream_num = len(playback.keys())
            stream_num = len(self.stream_list.keys())
            mpris_num = len(self.mpris_list.keys())
            if stream_num > 0 or mpris_num > 0:
                self.__app_vbox.set_no_show_all(False)
                self.__app_vbox.show_all()
            self.adjust_size()
            self.emit("stream-changed")

    def sink_input_changed_cb(self, obj, index):
        obj.get_devices()
        playback = obj.get_playback_streams()
        if index not in self.stream_list and index in playback:
            self.__make_playback_box(playback[index], index)
        elif index in playback:
            self.__set_playback_status(playback[index],
                                       self.stream_list[index]['scale'],
                                       self.stream_list[index]['button'])

    def sink_input_removed_cb(self, obj, index):
        if index in self.stream_mpris:
            if self.stream_mpris[index] in self.mpris_stream:
                del self.mpris_stream[self.stream_mpris[index]]
            del self.stream_mpris[index]
        if index in self.stream_list:
            process_id = self.stream_list[index]['process_id']
            if process_id in self.stream_process:
                del self.stream_process[process_id]
            self.stream_list[index]['container'].destroy()
            del self.stream_list[index]
            self.stream_num -= 1
            stream_num = len(self.stream_list.keys())
            mpris_num = len(self.mpris_list.keys())
            if stream_num == 0 and mpris_num == 0:
                self.__app_vbox.hide_all()
                self.__app_vbox.set_no_show_all(True)
            self.adjust_size()
            self.emit("stream-changed")

    def __set_output_status(self):
        # if sinks list is empty, then can't set output volume
        current_sink = pypulse.get_fallback_sink_index()
        sinks = pypulse.PULSE.get_output_devices()
        if current_sink is None:
            self.speaker_scale.set_sensitive(False)
            self.speaker_mute_button.set_sensitive(False)
            self.speaker_scale.set_enable(False)
            self.speaker_mute_button.set_active(False)
        # set output volume
        elif current_sink in sinks:
            self.speaker_scale.set_sensitive(True)
            self.speaker_mute_button.set_sensitive(True)
            is_mute = sinks[current_sink]['mute']
            self.speaker_mute_button.set_active(not is_mute)
            self.speaker_scale.set_enable(not is_mute)
            sink_volume = pypulse.PULSE.get_output_volume()
            if current_sink in sink_volume:
                volume = max(sink_volume[current_sink])
            else:
                volume = 0
            self.speaker_scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
        self.update_tray_icon()

    def __set_input_status(self):
        # if sources list is empty, then can't set input volume
        current_source = pypulse.get_fallback_source_index()
        sources = pypulse.PULSE.get_input_devices()
        if current_source is None:
            self.microphone_scale.set_sensitive(False)
            self.microphone_mute_button.set_sensitive(False)
            self.microphone_scale.set_enable(False)
            self.microphone_mute_button.set_active(False)
        # set input volume
        elif current_source in sources:
            self.microphone_scale.set_sensitive(True)
            self.microphone_mute_button.set_sensitive(True)
            is_mute = sources[current_source]['mute']
            self.microphone_mute_button.set_active(not is_mute)
            self.microphone_scale.set_enable(not is_mute)
            source_volume = pypulse.PULSE.get_input_volume()
            if current_source in source_volume:
                volume = max(source_volume[current_source])
            else:
                volume = 0
            self.microphone_scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
    
    def __set_playback_status(self, stream, scale, button):
        if not stream['has_volume']:
            scale.set_sensitive(False)
            button.set_sensitive(False)
            scale.set_enable(False)
            button.set_active(False)
        else:
            scale.set_sensitive(True)
            button.set_sensitive(True)
            is_mute = stream['mute']
            button.set_active(not is_mute)
            scale.set_enable(not is_mute)
            volume = max(stream['volume'])
            scale.set_value(volume * 100.0 / pypulse.NORMAL_VOLUME_VALUE)
            scale.volume_channels = len(stream['volume'])

    # mpris dbus signal
    def mpris2_new_cb(self, obj, pid):
        vbox = gtk.VBox()
        img = gtk.image_new_from_icon_name(obj.mpris_process[pid]['property']['DesktopEntry'], gtk.ICON_SIZE_MENU)
        # application title
        app_title = obj.mpris_process[pid]['property']['Identity']
        #if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Stopped':
            #app_title = obj.mpris_process[pid]['property']['Identity']
        #else:
            #app_title = "%s - %s" % (obj.mpris_process[pid]['property']['Identity'], _(obj.mpris_process[pid]['property']['PlaybackStatus']))
        label = Label(app_title, label_width=115)
        hbox = gtk.HBox(False, 5)
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(label), False, False)
        vbox.pack_start(hbox, False, False)

        # metadata info
        meta_box = gtk.HBox(False, 10)
        xesam_vbox = gtk.VBox(False)
        #art_img = gtk.Image()
        art_img = gtk.EventBox()
        art_img.set_size_request(40, 40)
        art_img.connect("expose-event", self.__draw_mpris_art_img)

        xesam_title = Label("", label_width=75)
        xesam_artist = Label("", label_width=75)
        xesam_album = Label("", label_width=75)
        xesam_vbox.pack_start(xesam_title)
        xesam_vbox.pack_start(xesam_artist)
        #xesam_vbox.pack_start(xesam_album)
        meta_box.pack_start(self.__make_align(art_img, padding_left=21, height=40), False, False)
        meta_box.pack_start(xesam_vbox)

        self.mpris_list[pid] = {}
        # mpris control
        scale = HScalebar(app_theme.get_pixbuf("sound/point.png"), show_value=False, format_value="%", value_min=0, value_max=1, line_height=3)
        scale.set_magnetic_values([(0, 0.1), (1, 0.1)])
        scale.set_size_request(70, 10)
        prev_bt = gtk.Button()
        pause_bt = gtk.Button()
        stop_bt = gtk.Button()
        next_bt = gtk.Button()

        prev_bt.set_size_request(20, 22)
        pause_bt.set_size_request(29, 30)
        #stop_bt.set_size_request(16, 16)
        next_bt.set_size_request(20, 22)

        prev_bt.pixbuf = "previous"
        if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Playing':
            pause_bt.pixbuf = "pause"
        else:
            pause_bt.pixbuf = "play"
        #stop_bt.pixbuf = self.stop_img
        next_bt.pixbuf = "next"

        scale.set_value(obj.mpris_process[pid]['property']['Volume'])

        prev_bt.connect("clicked", self.__mpris_prev_cb, obj, pid)
        pause_bt.connect("clicked", self.__mpris_pause_cb, obj, pid)
        #stop_bt.connect("clicked", self.__mpris_stop_cb, obj, pid)
        next_bt.connect("clicked", self.__mpris_next_cb, obj, pid)
        prev_bt.connect("expose-event", self.__draw_mpris_button_cb)
        pause_bt.connect("expose-event", self.__draw_mpris_button_cb)
        stop_bt.connect("expose-event", self.__draw_mpris_button_cb)
        next_bt.connect("expose-event", self.__draw_mpris_button_cb)
        scale.connect("value-changed", self.__mpris_volume_cb, obj, pid)

        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(prev_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(pause_bt, height=-1), False, False)
        #hbox.pack_start(self.__make_align(stop_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(next_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, height=-1), False, False)
        vbox.pack_start(self.__make_align(hbox, xalign=0.5, padding_top=5, padding_bottom=10, height=-1), False, False)

        self.mpris_list[pid]['app_title'] = label
        self.mpris_list[pid]['prev'] = prev_bt
        self.mpris_list[pid]['pause'] = pause_bt
        self.mpris_list[pid]['stop'] = stop_bt
        self.mpris_list[pid]['next'] = next_bt
        self.mpris_list[pid]['scale'] = scale
        self.mpris_list[pid]['meta'] = meta_box
        self.mpris_list[pid]['meta_img'] = art_img
        self.mpris_list[pid]['meta_title'] = xesam_title
        self.mpris_list[pid]['meta_artist'] = xesam_artist
        self.mpris_list[pid]['meta_album'] = xesam_album
        self.mpris_list[pid]['container'] = vbox
        if not obj.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['height'] = self.mpris_base_height
        else:
            vbox.pack_start(meta_box, False, False)
            vbox.reorder_child(meta_box, 1)
            self.__set_mpris_meta_info(pid)
            self.mpris_list[pid]['height'] = self.mpris_base_height + 40
        self.__mpris_total_height += self.mpris_list[pid]['height']
        # delete playback_stream widget
        if pid in self.stream_process:
            self.stream_process[pid]['container'].destroy()
            del self.stream_list[self.stream_process[pid]['stream_id']]
            del self.stream_process[pid]
        vbox.show_all()
        self.__mpris_vbox.pack_start(vbox, False, False)

        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num > 0 or mpris_num > 0:
            self.__app_vbox.set_no_show_all(False)
            self.__app_vbox.show_all()
        self.adjust_size()
        self.emit("stream-changed")

    def __draw_mpris_art_img(self, widget, event):
        x, y, w, h = widget.allocation
        cr = widget.window.cairo_create()
        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) 
        surface_cr = gtk.gdk.CairoContext(cairo.Context(surface))
        surface_cr.set_source_rgba(0, 0, 0, 1.0)
        surface_cr.rectangle(5, 5, w-10, h-10)
        surface_cr.stroke()

        dtk_cairo_blur.gaussian_blur(surface, 3)
        cr.set_source_surface(surface, 0, 0)
        cr.paint()

        cr.set_source_rgb(1, 1, 1)
        cr.rectangle(2, 2, w-4, h-4)
        cr.fill()

        if widget.pixbuf:
            cr.set_source_pixbuf(widget.pixbuf, 4, 4)
            cr.paint()
        return True

    def __set_mpris_meta_info(self, pid):
        if 'mpris:artUrl' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            arturl = urlparse(self.mpris2.mpris_process[pid]['property']['Metadata']['mpris:artUrl'])
            if arturl.scheme == 'file' and os.path.exists(arturl.path):
                art_pixbuf = gtk.gdk.pixbuf_new_from_file(arturl.path).scale_simple(32, 32, gtk.gdk.INTERP_TILES)
                #self.mpris_list[pid]['meta_img'].set_from_pixbuf(art_pixbuf)
                self.mpris_list[pid]['meta_img'].pixbuf = art_pixbuf
        else:
            self.mpris_list[pid]['meta_img'].pixbuf = None
        self.mpris_list[pid]['meta_img'].queue_draw()
        if 'xesam:title' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_title'].set_text(
                markup_escape_text(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:title']))
        else:
            self.mpris_list[pid]['meta_title'].set_text("-")
        if 'xesam:artist' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_artist'].set_text(
                markup_escape_text('&'.join(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:artist'])))
        else:
            self.mpris_list[pid]['meta_artist'].set_text("-")
        if 'xesam:album' in self.mpris2.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['meta_album'].set_text(
                markup_escape_text(self.mpris2.mpris_process[pid]['property']['Metadata']['xesam:album']))
        else:
            self.mpris_list[pid]['meta_album'].set_text("-")
        
    def mpris2_removed_cb(self, obj, pid):
        if pid in self.mpris_list:
            self.mpris_list[pid]['container'].destroy()
            self.mpris_list[pid]['meta'].destroy()
            self.__mpris_total_height -= self.mpris_list[pid]['height']
            del self.mpris_list[pid]
        if pid in self.mpris_stream:
            stream_id = self.mpris_stream[pid]
            playback_streams = pypulse.PULSE.get_playback_streams()
            if stream_id in playback_streams:
                self.__make_playback_box(playback_streams[stream_id], stream_id)
        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num == 0 and mpris_num == 0:
            self.__app_vbox.hide_all()
            self.__app_vbox.set_no_show_all(True)
        self.adjust_size()
        self.emit("stream-changed")

    def mpris2_changed_cb(self, obj, pid, changed):
        #print "mpris changed", pid, changed
        if pid not in self.mpris_list:
            return
        if 'Volume' in changed:
            self.mpris_list[pid]['scale'].set_value(changed['Volume'])
        if 'PlaybackStatus' in changed:
            # application title && hide meta info
            if changed['PlaybackStatus'] == 'Stopped':
                if self.mpris_list[pid]['meta'] in self.mpris_list[pid]['container'].get_children():
                    self.mpris_list[pid]['container'].remove(self.mpris_list[pid]['meta'])
                    self.__mpris_total_height -= self.mpris_list[pid]['height']
                    self.mpris_list[pid]['height'] = self.mpris_base_height
                    self.__mpris_total_height += self.mpris_list[pid]['height']
                    self.adjust_size()
                    self.emit("stream-changed")
            # button image
            if changed['PlaybackStatus'] == 'Playing':
                self.mpris_list[pid]['pause'].pixbuf = "pause"
            else:
                self.mpris_list[pid]['pause'].pixbuf = "play"
            self.mpris_list[pid]['pause'].queue_draw()
        if 'Metadata' in changed and obj.mpris_process[pid]['property']['PlaybackStatus'] != 'Stopped':
            self.__set_mpris_meta_info(pid)
            if self.mpris_list[pid]['meta'] not in self.mpris_list[pid]['container'].get_children():
                self.mpris_list[pid]['container'].pack_start(self.mpris_list[pid]['meta'], False, False)
                self.mpris_list[pid]['container'].reorder_child(self.mpris_list[pid]['meta'], 1)
                self.mpris_list[pid]['container'].show_all()
                self.__mpris_total_height -= self.mpris_list[pid]['height']
                self.mpris_list[pid]['height'] = self.mpris_base_height + 40
                self.__mpris_total_height += self.mpris_list[pid]['height']
                self.adjust_size()
                self.emit("stream-changed")

    def __mpris_prev_cb(self, bt, obj, pid):
        if pid not in obj.mpris_process:
            return
        try:
            obj.mpris_process[pid]['obj'].get_dbus_method(
                'Previous', 'org.mpris.MediaPlayer2.Player')()
        except Exception, e:
            if e._dbus_error_name == "org.freedesktop.DBus.Error.ServiceUnknown":
                self.mpris2_removed_cb(obj, pid)
    def mpris2_new_cb(self, obj, pid):
        vbox = gtk.VBox()
        img = gtk.image_new_from_icon_name(obj.mpris_process[pid]['property']['DesktopEntry'], gtk.ICON_SIZE_MENU)
        # application title
        app_title = obj.mpris_process[pid]['property']['Identity']
        #if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Stopped':
            #app_title = obj.mpris_process[pid]['property']['Identity']
        #else:
            #app_title = "%s - %s" % (obj.mpris_process[pid]['property']['Identity'], _(obj.mpris_process[pid]['property']['PlaybackStatus']))
        label = Label(app_title, label_width=115)
        hbox = gtk.HBox(False, 5)
        hbox.pack_start(self.__make_align(img), False, False)
        hbox.pack_start(self.__make_align(label), False, False)
        vbox.pack_start(hbox, False, False)

        # metadata info
        meta_box = gtk.HBox(False, 10)
        xesam_vbox = gtk.VBox(False)
        #art_img = gtk.Image()
        art_img = gtk.EventBox()
        art_img.set_size_request(40, 40)
        art_img.connect("expose-event", self.__draw_mpris_art_img)

        xesam_title = Label("", label_width=75)
        xesam_artist = Label("", label_width=75)
        xesam_album = Label("", label_width=75)
        xesam_vbox.pack_start(xesam_title)
        xesam_vbox.pack_start(xesam_artist)
        #xesam_vbox.pack_start(xesam_album)
        meta_box.pack_start(self.__make_align(art_img, padding_left=21, height=40), False, False)
        meta_box.pack_start(xesam_vbox)

        self.mpris_list[pid] = {}
        # mpris control
        scale = HScalebar(app_theme.get_pixbuf("sound/point.png"), show_value=False, format_value="%", value_min=0, value_max=1, line_height=3)
        scale.set_magnetic_values([(0, 0.1), (1, 0.1)])
        scale.set_size_request(70, 10)
        prev_bt = gtk.Button()
        pause_bt = gtk.Button()
        stop_bt = gtk.Button()
        next_bt = gtk.Button()

        prev_bt.set_size_request(20, 22)
        pause_bt.set_size_request(29, 30)
        #stop_bt.set_size_request(16, 16)
        next_bt.set_size_request(20, 22)

        prev_bt.pixbuf = "previous"
        if obj.mpris_process[pid]['property']['PlaybackStatus'] == 'Playing':
            pause_bt.pixbuf = "pause"
        else:
            pause_bt.pixbuf = "play"
        #stop_bt.pixbuf = self.stop_img
        next_bt.pixbuf = "next"

        scale.set_value(obj.mpris_process[pid]['property']['Volume'])

        prev_bt.connect("clicked", self.__mpris_prev_cb, obj, pid)
        pause_bt.connect("clicked", self.__mpris_pause_cb, obj, pid)
        #stop_bt.connect("clicked", self.__mpris_stop_cb, obj, pid)
        next_bt.connect("clicked", self.__mpris_next_cb, obj, pid)
        prev_bt.connect("expose-event", self.__draw_mpris_button_cb)
        pause_bt.connect("expose-event", self.__draw_mpris_button_cb)
        stop_bt.connect("expose-event", self.__draw_mpris_button_cb)
        next_bt.connect("expose-event", self.__draw_mpris_button_cb)
        scale.connect("value-changed", self.__mpris_volume_cb, obj, pid)

        hbox = gtk.HBox()
        hbox.pack_start(self.__make_align(prev_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(pause_bt, height=-1), False, False)
        #hbox.pack_start(self.__make_align(stop_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(next_bt, height=-1), False, False)
        hbox.pack_start(self.__make_align(scale, yalign=0.0, yscale=1.0, height=-1), False, False)
        vbox.pack_start(self.__make_align(hbox, xalign=0.5, padding_top=5, padding_bottom=10, height=-1), False, False)

        self.mpris_list[pid]['app_title'] = label
        self.mpris_list[pid]['prev'] = prev_bt
        self.mpris_list[pid]['pause'] = pause_bt
        self.mpris_list[pid]['stop'] = stop_bt
        self.mpris_list[pid]['next'] = next_bt
        self.mpris_list[pid]['scale'] = scale
        self.mpris_list[pid]['meta'] = meta_box
        self.mpris_list[pid]['meta_img'] = art_img
        self.mpris_list[pid]['meta_title'] = xesam_title
        self.mpris_list[pid]['meta_artist'] = xesam_artist
        self.mpris_list[pid]['meta_album'] = xesam_album
        self.mpris_list[pid]['container'] = vbox
        if not obj.mpris_process[pid]['property']['Metadata']:
            self.mpris_list[pid]['height'] = self.mpris_base_height
        else:
            vbox.pack_start(meta_box, False, False)
            vbox.reorder_child(meta_box, 1)
            self.__set_mpris_meta_info(pid)
            self.mpris_list[pid]['height'] = self.mpris_base_height + 40
        self.__mpris_total_height += self.mpris_list[pid]['height']
        # delete playback_stream widget
        if pid in self.stream_process:
            self.stream_process[pid]['container'].destroy()
            del self.stream_list[self.stream_process[pid]['stream_id']]
            del self.stream_process[pid]
        vbox.show_all()
        self.__mpris_vbox.pack_start(vbox, False, False)

        stream_num = len(self.stream_list.keys())
        mpris_num = len(self.mpris_list.keys())
        if stream_num > 0 or mpris_num > 0:
            self.__app_vbox.set_no_show_all(False)
            self.__app_vbox.show_all()
        self.adjust_size()
        self.emit("stream-changed")
class DisplayView(gtk.VBox):
    '''
    class docs
    '''
    def __init__(self):
        '''
        init docs
        '''
        gtk.VBox.__init__(self)

        self.brightness_id = None

        self.display_manager = DisplayManager()
        self.__xrandr_settings = self.display_manager.get_xrandr_settings()
        self.__xrandr_settings.connect("changed", self.__xrandr_changed)

        self.resize_width = 790
        self.resize_height = 200
        self.monitor_items = []
        self.__output_names = []
        self.__current_output_name = self.display_manager.get_primary_output_name(
        )
        self.__setup_monitor_items()
        self.sizes_items = []
        self.monitor_combo = None
        if len(self.monitor_items
               ) > 1 and self.display_manager.is_copy_monitors():
            self.__set_same_sizes()
        else:
            self.__setup_sizes_items()
        self.multi_monitors_items = [(_("Copy Display"), 1),
                                     (_("Extend Display"), 2),
                                     (_("Only shown in display 1"), 3),
                                     (_("Only shown in display 2"), 4)]
        self.rotation_items = [(_("Normal"), 1), (_("Right"), 2),
                               (_("Left"), 3), (_("Inverted"), 4)]
        self.duration_items = [("1 %s" % _("Minute"), 1),
                               ("2 %s" % _("Minutes"), 2),
                               ("3 %s" % _("Minutes"), 3),
                               ("5 %s" % _("Minutes"), 5),
                               ("10 %s" % _("Minutes"), 10),
                               ("30 %s" % _("Minutes"), 30),
                               ("1 %s" % _("Hour"), 60),
                               (_("Never"), DisplayManager.BIG_NUM / 60)]
        '''
        scrolled_window
        '''
        self.scrolled_window = ScrolledWindow()
        self.scrolled_window.set_size_request(-1, 425)
        self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.main_box = gtk.VBox()
        self.main_box.set_size_request(600, -1)
        self.body_box = gtk.HBox()
        '''
        left, right align
        '''
        self.left_align = self.__setup_align(
            padding_top=FRAME_TOP_PADDING,
            padding_left=TEXT_WINDOW_LEFT_PADDING)
        self.right_align = self.__setup_align(padding_top=FRAME_TOP_PADDING,
                                              padding_left=0)
        '''
        left, right box
        '''
        self.left_box = gtk.VBox(spacing=WIDGET_SPACING)
        self.right_box = gtk.VBox(spacing=WIDGET_SPACING)
        '''
        monitor operation && detect
        '''
        self.monitor_resize_align = self.__setup_align(
            padding_top=11, padding_left=int(TEXT_WINDOW_LEFT_PADDING / 2))
        self.monitor_resize_box = MonitorResizableBox(self.display_manager)
        self.monitor_resize_box.select_output(self.__current_output_name)
        self.monitor_resize_box.connect("select-output", self.__select_output)
        self.monitor_resize_box.connect("resize", self.__resize_box)
        self.monitor_resize_align.add(self.monitor_resize_box)
        '''
        monitor display
        '''
        self.monitor_display_align = self.__setup_title_align(
            app_theme.get_pixbuf("display/monitor_display.png"), _("Display"),
            0)
        '''
        monitor
        '''
        self.monitor_align = self.__setup_align()
        self.monitor_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.monitor_label = self.__setup_label(_("Monitor"))
        self.monitor_combo = self.__setup_combo(self.monitor_items)
        self.monitor_combo.set_select_index(
            self.display_manager.get_primary_output_name_index(
                self.monitor_items))
        self.monitor_combo.connect("item-selected", self.__combo_item_selected,
                                   "monitor_combo")
        self.__widget_pack_start(self.monitor_box,
                                 [self.monitor_label, self.monitor_combo])
        self.monitor_align.add(self.monitor_box)
        '''
        goto individuation or power setting
        '''
        self.goto_align = self.__setup_align()
        self.goto_box = gtk.VBox(spacing=WIDGET_SPACING)
        self.goto_label = self.__setup_label(_("Relevant Settings"),
                                             text_size=TITLE_FONT_SIZE,
                                             width=None,
                                             align=ALIGN_START)
        goto_color = GOTO_FG_COLOR
        self.goto_individuation_label = self.__setup_label(text=_(
            "<span foreground=\"%s\" underline=\"single\">Personalization</span>"
        ) % goto_color,
                                                           width=None,
                                                           align=ALIGN_START)
        self.goto_individuation_label.connect("button-press-event",
                                              self.__button_press,
                                              "individuation")
        set_clickable_cursor(self.goto_individuation_label)
        self.goto_power_label = self.__setup_label(
            text=_("<span foreground=\"%s\" underline=\"single\">Power</span>")
            % goto_color,
            width=None,
            align=ALIGN_START)
        self.goto_power_label.connect("button-press-event",
                                      self.__button_press, "power")
        set_clickable_cursor(self.goto_power_label)
        self.__widget_pack_start(self.goto_box, [
            self.goto_label, self.goto_individuation_label,
            self.goto_power_label
        ])
        self.goto_align.add(self.goto_box)
        '''
        sizes
        '''
        self.sizes_align = self.__setup_align()
        self.sizes_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.sizes_label = self.__setup_label(_("Resolution"))
        self.sizes_combo = self.__setup_combo(self.sizes_items)
        if self.sizes_combo:
            self.sizes_combo.set_select_index(
                self.display_manager.get_screen_size_index(
                    self.__current_output_name, self.sizes_items))
            self.sizes_combo.connect("item-selected",
                                     self.__combo_item_selected, "sizes_combo")
            self.__widget_pack_start(self.sizes_box,
                                     [self.sizes_label, self.sizes_combo])
        self.sizes_align.add(self.sizes_box)
        '''
        rotation
        '''
        self.rotation_align = self.__setup_align()
        self.rotation_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.rotation_label = self.__setup_label(_("Rotation"))
        self.rotation_combo = self.__setup_combo(self.rotation_items)
        self.rotation_combo.set_select_index(
            self.display_manager.get_screen_rotation_index(
                self.__current_output_name))
        self.rotation_combo.connect("item-selected",
                                    self.__combo_item_selected,
                                    "rotation_combo")
        self.__widget_pack_start(self.rotation_box,
                                 [self.rotation_label, self.rotation_combo])
        self.rotation_align.add(self.rotation_box)
        '''
        multi-monitors
        '''
        self.multi_monitors_align = self.__setup_align()
        self.multi_monitors_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.multi_monitors_label = self.__setup_label(_("Multi-Monitor"))
        self.multi_monitors_combo = self.__setup_combo(
            self.multi_monitors_items)
        self.multi_monitors_combo.set_select_index(
            self.display_manager.get_multi_monitor_index())
        self.multi_monitors_combo.connect("item-selected",
                                          self.__combo_item_selected,
                                          "multi_monitors_combo")
        self.__widget_pack_start(
            self.multi_monitors_box,
            [self.multi_monitors_label, self.multi_monitors_combo])
        self.multi_monitors_align.add(self.multi_monitors_box)
        if self.display_manager.get_output_count() < 2:
            self.multi_monitors_align.set_size_request(-1, 0)
            self.multi_monitors_align.set_child_visible(False)
        '''
        monitor brightness
        '''
        self.monitor_bright_align = self.__setup_title_align(
            app_theme.get_pixbuf("display/monitor_bright.png"),
            _("Brightness"))
        '''
        brightness
        '''
        self.brightness_align = self.__setup_align()
        self.brightness_box = gtk.HBox(spacing=2)
        self.brightness_label_align = self.__setup_align(padding_top=8,
                                                         padding_left=0,
                                                         padding_right=5)
        self.brightness_label = self.__setup_label(_("Brightness"))
        self.brightness_label_align.add(self.brightness_label)

        self.brightness_scale = HScalebar(
            point_dpixbuf=app_theme.get_pixbuf("scalebar/point.png"),
            value_min=0.1,
            value_max=1.0)
        self.brightness_scale.set_size_request(HSCALEBAR_WIDTH, 33)
        self.brightness_scale.set_value(
            self.display_manager.get_screen_brightness())
        self.brightness_scale.connect("value-changed", self.__set_brightness)
        self.__widget_pack_start(
            self.brightness_box,
            [self.brightness_label_align, self.brightness_scale])
        self.brightness_align.add(self.brightness_box)
        '''
        auto adjust monitor brightness
        '''
        self.auto_adjust_align = self.__setup_align()
        self.auto_adjust_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.auto_adjust_label = self.__setup_label(_("Auto-Brightness"))
        self.auto_adjust_toggle_align = self.__setup_align(padding_top=4,
                                                           padding_left=158)
        self.auto_adjust_toggle = self.__setup_toggle()
        self.auto_adjust_toggle.set_active(
            self.display_manager.is_enable_close_monitor())
        self.auto_adjust_toggle.connect("toggled", self.__toggled,
                                        "auto_adjust_toggle")
        self.auto_adjust_toggle_align.add(self.auto_adjust_toggle)
        self.__widget_pack_start(
            self.auto_adjust_box,
            [self.auto_adjust_label, self.auto_adjust_toggle_align])
        self.auto_adjust_align.add(self.auto_adjust_box)
        '''
        close monitor
        '''
        self.close_monitor_align = self.__setup_align()
        self.close_monitor_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.close_monitor_label = self.__setup_label(_("Turn off monitor"))
        self.close_monitor_combo = self.__setup_combo(self.duration_items)
        self.close_monitor_combo.set_select_index(
            self.display_manager.get_close_monitor_index(self.duration_items))
        self.close_monitor_combo.connect("item-selected",
                                         self.__combo_item_selected,
                                         "close_monitor_combo")
        self.__widget_pack_start(
            self.close_monitor_box,
            [self.close_monitor_label, self.close_monitor_combo])
        self.close_monitor_align.add(self.close_monitor_box)
        '''
        monitor lock
        '''
        self.monitor_lock_align = self.__setup_title_align(
            app_theme.get_pixbuf("lock/lock.png"), _("Lock Screen"))
        '''
        auto monitor lock
        '''
        self.auto_lock_align = self.__setup_align()
        self.auto_lock_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.auto_lock_label = self.__setup_label(
            _("Lock screen automatically"))
        self.auto_lock_toggle_align = self.__setup_align(padding_top=4,
                                                         padding_left=158)
        self.auto_lock_toggle = self.__setup_toggle()
        is_enable_lock_display = self.display_manager.is_enable_lock_display()
        self.auto_lock_toggle.set_active(is_enable_lock_display)
        self.auto_lock_toggle.connect("toggled", self.__toggled,
                                      "auto_lock_toggle")
        self.auto_lock_toggle_align.add(self.auto_lock_toggle)
        self.__widget_pack_start(
            self.auto_lock_box,
            [self.auto_lock_label, self.auto_lock_toggle_align])
        self.auto_lock_align.add(self.auto_lock_box)
        '''
        lock display
        '''
        self.lock_display_align = self.__setup_align(padding_bottom=20)
        self.lock_display_box = gtk.HBox(spacing=WIDGET_SPACING)
        self.lock_display_label = self.__setup_label(_("Lock Screen"))
        self.lock_display_combo = self.__setup_combo(self.duration_items)
        self.lock_display_combo.set_select_index(
            self.display_manager.get_lock_display_index(self.duration_items))
        self.lock_display_combo.connect("item-selected",
                                        self.__combo_item_selected,
                                        "lock_display_combo")
        self.__widget_pack_start(
            self.lock_display_box,
            [self.lock_display_label, self.lock_display_combo])
        self.lock_display_align.add(self.lock_display_box)
        '''
        left_align pack_start
        '''
        self.__widget_pack_start(
            self.left_box,
            [
                self.monitor_display_align,
                self.monitor_align,
                self.sizes_align,
                self.rotation_align,
                self.multi_monitors_align,
                self.monitor_bright_align,
                self.brightness_align,
                #self.auto_adjust_align,
                #self.close_monitor_align,
                self.monitor_lock_align,
                self.auto_lock_align,
                self.lock_display_align
            ])
        self.left_align.add(self.left_box)
        '''
        right_align pack_start
        '''
        self.__widget_pack_start(self.right_box, [self.goto_align])
        self.right_box.set_size_request(280, -1)
        self.right_align.add(self.right_box)
        '''
        main && body box
        '''
        self.main_box.pack_start(self.monitor_resize_align, False, False)
        self.body_box.pack_start(self.left_align)
        self.body_box.pack_start(self.right_align, False, False)
        self.main_box.pack_start(self.body_box)
        '''
        this->HBox pack_start
        '''
        self.scrolled_window.add_child(self.main_box)
        self.pack_start(self.scrolled_window)

        self.__send_message("status", ("display", ""))

    def show_again(self):
        self.__send_message("status", ("display", ""))

    def reset(self):
        self.__send_message("status", ("display", _("Reset to default value")))
        self.display_manager.reset()
        self.close_monitor_combo.set_select_index(
            self.display_manager.get_close_monitor_index(self.duration_items))
        self.lock_display_combo.set_select_index(
            self.display_manager.get_lock_display_index(self.duration_items))
        self.multi_monitors_combo.set_select_index(0)

    def __handle_dbus_replay(self, *reply):
        pass

    def __handle_dbus_error(self, *error):
        pass

    def __send_message(self, message_type, message_content):
        if is_dbus_name_exists(APP_DBUS_NAME):
            bus_object = dbus.SessionBus().get_object(APP_DBUS_NAME,
                                                      APP_OBJECT_NAME)
            method = bus_object.get_dbus_method("message_receiver")
            method(message_type,
                   message_content,
                   reply_handler=self.__handle_dbus_replay,
                   error_handler=self.__handle_dbus_error)

    def __button_press(self, widget, event, module_id):
        self.__send_message("goto", (module_id, ""))

    def __expose(self, widget, event):
        try:
            cr = widget.window.cairo_create()
            rect = widget.allocation

            cr.set_source_rgb(*color_hex_to_cairo(MODULE_BG_COLOR))
            cr.rectangle(rect.x, rect.y, rect.width, rect.height)
            cr.fill()
        except e:
            print "DEBUG", e

    def __change_current_output(self, output_name, from_monitor_combo=True):
        self.__current_output_name = output_name

        if not from_monitor_combo:
            self.monitor_combo.set_select_index(
                self.display_manager.get_output_name_index(
                    output_name, self.monitor_items))

        if not self.display_manager.is_copy_monitors():
            self.__setup_sizes_items()
        if len(self.sizes_items):
            self.sizes_combo.add_items(items=self.sizes_items)
        self.sizes_combo.set_select_index(
            self.display_manager.get_screen_size_index(
                self.__current_output_name, self.sizes_items))

    def __select_output(self, widget, output_name):
        print "Output name:", output_name
        self.__change_current_output(output_name, False)

    def __set_same_sizes(self):
        same_sizes = self.display_manager.get_same_sizes(
            self.display_manager.get_screen_sizes(self.monitor_items[0][1]),
            self.display_manager.get_screen_sizes(self.monitor_items[1][1]))
        i = 0

        del self.sizes_items[:]
        while i < len(same_sizes):
            self.sizes_items.append((same_sizes[i], i))

            i += 1

    def __xrandr_changed(self, key):
        if key == "output-names":
            self.display_manager.init_xml()
            self.__setup_monitor_items()
            self.monitor_combo.add_items(items=self.monitor_items)
            if len(self.monitor_items) > 1:
                if self.display_manager.is_copy_monitors():
                    self.__set_same_sizes()
                    self.sizes_combo.add_items(items=self.sizes_items)

                self.multi_monitors_align.set_size_request(-1, 30)
                self.multi_monitors_align.set_child_visible(True)
            else:
                self.multi_monitors_align.set_size_request(-1, 0)
                self.multi_monitors_align.set_child_visible(False)
            return

        if key == "brightness":
            self.brightness_scale.set_value(
                self.display_manager.get_screen_brightness())
            return

    def __set_brightness_value(self, value):
        self.display_manager.set_screen_brightness(self.__current_output_name,
                                                   value)

    def __set_brightness(self, widget, event):
        value = self.brightness_scale.get_value()
        self.__send_message(
            "status",
            ("display", _("Changed brightness to %d%%") % int(value * 100)))
        if self.brightness_id:
            gobject.source_remove(self.brightness_id)
        self.brightness_id = gobject.timeout_add_seconds(
            1, self.__set_brightness_value, value)

    def __setup_monitor_items(self):
        self.__output_names = self.display_manager.get_output_names()
        del self.monitor_items[:]
        i = 0

        while (i < len(self.__output_names)):
            self.monitor_items.append(
                self.display_manager.get_output_name(self.__output_names[i]))
            i += 1

    def __setup_sizes_items(self):
        screen_sizes = self.display_manager.get_screen_sizes(
            self.__current_output_name)
        del self.sizes_items[:]
        i = 0

        while i < len(screen_sizes):
            self.sizes_items.append((screen_sizes[i], i))
            i += 1

    def __toggled(self, widget, object=None):
        if object == "auto_adjust_toggle":
            if not widget.get_active():
                self.__send_message(
                    "status", ("display", _("Changed to manual adjustment")))
                self.display_manager.set_close_monitor(DisplayManager.BIG_NUM /
                                                       60)
            else:
                self.__send_message(
                    "status",
                    ("display", _("Changed to automatic adjustment")))
            return

        if object == "auto_lock_toggle":
            if not widget.get_active():
                self.lock_display_combo.set_sensitive(False)
                self.__send_message("status",
                                    ("display", _("Changed to manual lock")))
            else:
                self.lock_display_combo.set_sensitive(True)
                self.__send_message(
                    "status", ("display", _("Changed to automatic lock")))
            return

    def __combo_item_selected(self,
                              widget,
                              item_text=None,
                              item_value=None,
                              item_index=None,
                              object=None):
        if object == "monitor_combo":
            self.__send_message(
                "status",
                ("display", _("Changed current output to %s") % item_text))
            self.__change_current_output(item_value)
            return

        if object == "sizes_combo":
            size = self.sizes_items[item_value][0]
            self.__send_message(
                "status", ("display", _("Changed resolution to %s") % size))
            self.display_manager.set_screen_size(self.__current_output_name,
                                                 size)
            return

        if object == "rotation_combo":
            self.__send_message(
                "status", ("display", _("Changed rotation to %s") % item_text))
            self.display_manager.set_screen_rotation(
                self.__current_output_name, item_value)
            return

        if object == "multi_monitors_combo":
            self.__send_message(
                "status",
                ("display", _("Changed multi-monitor mode to %s") % item_text))
            self.display_manager.set_multi_monitor(item_value)
            return

        if object == "close_monitor_combo":
            self.__send_message("status", (
                "display",
                _("Idle time before turning off display has been changed to to %s"
                  ) % item_text))
            self.display_manager.set_close_monitor(item_value)
            return

        if object == "lock_display_combo":
            self.__send_message(
                "status",
                ("display",
                 _("Idle time before locking display has been changed to %s") %
                 item_text))
            if item_value == DisplayManager.BIG_NUM / 60:
                self.auto_lock_toggle.set_active(False)
            else:
                self.auto_lock_toggle.set_active(True)
            self.display_manager.set_lock_display(item_value)
            return

    def __resize_box(self, widget, height):
        self.monitor_resize_box.set_size_request(self.resize_width,
                                                 height - FRAME_TOP_PADDING)

    def __setup_separator(self):
        hseparator = HSeparator(
            app_theme.get_shadow_color("hSeparator").get_color_info(), 0, 0)
        hseparator.set_size_request(500, HSEPARATOR_HEIGHT)
        return hseparator

    def __setup_title_label(
            self,
            text="",
            text_color=app_theme.get_color("globalTitleForeground"),
            text_size=TITLE_FONT_SIZE,
            text_x_align=ALIGN_START,
            label_width=180):
        return Label(text=text,
                     text_color=text_color,
                     text_size=text_size,
                     text_x_align=text_x_align,
                     label_width=label_width,
                     enable_select=False,
                     enable_double_click=False)

    def __setup_label(self,
                      text="",
                      text_size=CONTENT_FONT_SIZE,
                      width=180,
                      align=ALIGN_END,
                      wrap_width=None):
        label = Label(text=text,
                      text_color=None,
                      text_size=text_size,
                      text_x_align=align,
                      label_width=width,
                      wrap_width=wrap_width,
                      enable_select=False,
                      enable_double_click=False)
        return label

    def __setup_combo(self, items=[], width=HSCALEBAR_WIDTH):
        if len(items) == 0:
            return None

        combo = ComboBox(items=items,
                         select_index=0,
                         max_width=width,
                         fixed_width=width)
        combo.set_size_request(width, WIDGET_HEIGHT)
        return combo

    def __setup_toggle(self):
        toggle = ToggleButton(
            app_theme.get_pixbuf("toggle_button/inactive_normal.png"),
            app_theme.get_pixbuf("toggle_button/active_normal.png"))
        return toggle

    def __setup_title_align(self,
                            pixbuf,
                            text,
                            padding_top=FRAME_TOP_PADDING,
                            padding_left=0):
        align = self.__setup_align(padding_top=padding_top,
                                   padding_left=padding_left)
        align_box = gtk.VBox(spacing=WIDGET_SPACING)
        title_box = gtk.HBox(spacing=WIDGET_SPACING)
        image = ImageBox(pixbuf)
        label = self.__setup_title_label(text)
        separator = self.__setup_separator()
        self.__widget_pack_start(title_box, [image, label])
        self.__widget_pack_start(align_box, [title_box, separator])
        align.add(align_box)
        return align

    def __setup_align(self,
                      xalign=0,
                      yalign=0,
                      xscale=0,
                      yscale=0,
                      padding_top=0,
                      padding_bottom=0,
                      padding_left=FRAME_LEFT_PADDING +
                      int(WIDGET_SPACING / 2),
                      padding_right=0):
        align = gtk.Alignment()
        align.set(xalign, yalign, xscale, yscale)
        align.set_padding(padding_top, padding_bottom, padding_left,
                          padding_right)
        align.connect("expose-event", self.__expose)
        return align

    def __widget_pack_start(self,
                            parent_widget,
                            widgets=[],
                            expand=False,
                            fill=False):
        if parent_widget == None:
            return
        for item in widgets:
            parent_widget.pack_start(item, expand, fill)