Daniel Brötzmann pushed to branch mainwindow at gajim / gajim

Commits:

13 changed files:

Changes:

  • gajim/common/jingle_ft.py
    ... ... @@ -101,13 +101,6 @@ def __init__(self, session, transport=None, file_props=None,
    101 101
             self.session = session
    
    102 102
             self.media = 'file'
    
    103 103
             self.nominated_cand = {}
    
    104
    -        if app.contacts.is_gc_contact(session.connection.name,
    
    105
    -                                        session.peerjid):
    
    106
    -            roomjid = session.peerjid.split('/')[0]
    
    107
    -            dstaddr = hashlib.sha1(('%s%s%s' % (self.file_props.sid,
    
    108
    -                                                session.ourjid, roomjid))
    
    109
    -                                   .encode('utf-8')).hexdigest()
    
    110
    -            self.file_props.dstaddr = dstaddr
    
    111 104
             self.state = State.NOT_STARTED
    
    112 105
             self.states = {
    
    113 106
                 State.INITIALIZED   : StateInitialized(self),
    

  • gajim/common/jingle_session.py
    ... ... @@ -516,7 +516,17 @@ def __on_session_initiate(self, stanza, jingle, error, action):
    516 516
             """
    
    517 517
             if self.state != JingleStates.ENDED:
    
    518 518
                 raise OutOfOrder
    
    519
    -        self.initiator = jingle['initiator']
    
    519
    +
    
    520
    +        # In "session-initiate", the <jingle/> element SHOULD possess an
    
    521
    +        # 'initiator' attribute. If it differs from the JID 'from', jingle
    
    522
    +        # 'initiator' SHOULD be ignored.
    
    523
    +        if jingle['initiator'] is None:
    
    524
    +            self.initiator = stanza['from']
    
    525
    +        elif jingle['initiator'] != stanza['from']:
    
    526
    +            self.initiator = stanza['from']
    
    527
    +        else:
    
    528
    +            self.initiator = jingle['initiator']
    
    529
    +
    
    520 530
             self.responder = self.ourjid
    
    521 531
             self.peerjid = self.initiator
    
    522 532
             self.accepted = False   # user did not accept this session yet
    

  • gajim/common/modules/jingle.py
    ... ... @@ -207,16 +207,7 @@ def start_audio_video(self, jid):
    207 207
     
    
    208 208
         def start_file_transfer(self, jid, file_props, request=False):
    
    209 209
             logger.info("start file transfer with file: %s", file_props)
    
    210
    -        contact = app.contacts.get_contact_with_highest_priority(
    
    211
    -            self._account, app.get_jid_without_resource(jid))
    
    212
    -        if app.contacts.is_gc_contact(self._account, jid):
    
    213
    -            gcc = jid.split('/')
    
    214
    -            if len(gcc) == 2:
    
    215
    -                contact = app.contacts.get_gc_contact(self._account,
    
    216
    -                                                      gcc[0],
    
    217
    -                                                      gcc[1])
    
    218
    -        if contact is None:
    
    219
    -            return None
    
    210
    +        contact = self._con.get_module('Contacts').get_contact(jid)
    
    220 211
             use_security = contact.supports(Namespace.JINGLE_XTLS)
    
    221 212
             jingle = JingleSession(self._con,
    
    222 213
                                    weinitiate=True,
    

  • gajim/data/gui/file_transfer_send.ui
    1
    +<?xml version="1.0" encoding="UTF-8"?>
    
    2
    +<!-- Generated with glade 3.38.2 -->
    
    3
    +<interface>
    
    4
    +  <requires lib="gtk+" version="3.24"/>
    
    5
    +  <object class="GtkStack" id="send_stack">
    
    6
    +    <property name="visible">True</property>
    
    7
    +    <property name="can-focus">False</property>
    
    8
    +    <property name="transition-type">slide-left-right</property>
    
    9
    +    <child>
    
    10
    +      <object class="GtkBox">
    
    11
    +        <property name="visible">True</property>
    
    12
    +        <property name="can-focus">False</property>
    
    13
    +        <property name="border-width">18</property>
    
    14
    +        <property name="orientation">vertical</property>
    
    15
    +        <property name="spacing">6</property>
    
    16
    +        <child>
    
    17
    +          <object class="GtkLabel">
    
    18
    +            <property name="visible">True</property>
    
    19
    +            <property name="can-focus">False</property>
    
    20
    +            <property name="label" translatable="yes">Files to send</property>
    
    21
    +            <property name="xalign">0</property>
    
    22
    +            <style>
    
    23
    +              <class name="dim-label"/>
    
    24
    +            </style>
    
    25
    +          </object>
    
    26
    +          <packing>
    
    27
    +            <property name="expand">False</property>
    
    28
    +            <property name="fill">True</property>
    
    29
    +            <property name="position">1</property>
    
    30
    +          </packing>
    
    31
    +        </child>
    
    32
    +        <child>
    
    33
    +          <object class="GtkBox">
    
    34
    +            <property name="visible">True</property>
    
    35
    +            <property name="can-focus">False</property>
    
    36
    +            <property name="hexpand">True</property>
    
    37
    +            <property name="vexpand">True</property>
    
    38
    +            <property name="orientation">vertical</property>
    
    39
    +            <child>
    
    40
    +              <object class="GtkScrolledWindow">
    
    41
    +                <property name="visible">True</property>
    
    42
    +                <property name="can-focus">True</property>
    
    43
    +                <property name="hscrollbar-policy">never</property>
    
    44
    +                <property name="shadow-type">in</property>
    
    45
    +                <child>
    
    46
    +                  <object class="GtkViewport">
    
    47
    +                    <property name="visible">True</property>
    
    48
    +                    <property name="can-focus">False</property>
    
    49
    +                    <child>
    
    50
    +                      <object class="GtkListBox" id="listbox">
    
    51
    +                        <property name="visible">True</property>
    
    52
    +                        <property name="can-focus">False</property>
    
    53
    +                        <property name="vexpand">True</property>
    
    54
    +                        <property name="selection-mode">multiple</property>
    
    55
    +                        <property name="activate-on-single-click">False</property>
    
    56
    +                        <signal name="add" handler="_on_files_changed" swapped="no"/>
    
    57
    +                        <signal name="remove" handler="_on_files_changed" swapped="no"/>
    
    58
    +                      </object>
    
    59
    +                    </child>
    
    60
    +                  </object>
    
    61
    +                </child>
    
    62
    +              </object>
    
    63
    +              <packing>
    
    64
    +                <property name="expand">True</property>
    
    65
    +                <property name="fill">True</property>
    
    66
    +                <property name="position">0</property>
    
    67
    +              </packing>
    
    68
    +            </child>
    
    69
    +            <child>
    
    70
    +              <object class="GtkToolbar">
    
    71
    +                <property name="visible">True</property>
    
    72
    +                <property name="can-focus">False</property>
    
    73
    +                <property name="show-arrow">False</property>
    
    74
    +                <property name="icon_size">1</property>
    
    75
    +                <child>
    
    76
    +                  <object class="GtkToolButton">
    
    77
    +                    <property name="visible">True</property>
    
    78
    +                    <property name="can-focus">False</property>
    
    79
    +                    <property name="tooltip-text" translatable="yes">Add Files</property>
    
    80
    +                    <property name="use-underline">True</property>
    
    81
    +                    <property name="icon-name">list-add-symbolic</property>
    
    82
    +                    <signal name="clicked" handler="_select_files" swapped="no"/>
    
    83
    +                  </object>
    
    84
    +                  <packing>
    
    85
    +                    <property name="expand">False</property>
    
    86
    +                    <property name="homogeneous">False</property>
    
    87
    +                  </packing>
    
    88
    +                </child>
    
    89
    +                <child>
    
    90
    +                  <object class="GtkToolButton">
    
    91
    +                    <property name="visible">True</property>
    
    92
    +                    <property name="can-focus">False</property>
    
    93
    +                    <property name="tooltip-text" translatable="yes">Remove Files</property>
    
    94
    +                    <property name="use-underline">True</property>
    
    95
    +                    <property name="icon-name">list-remove-symbolic</property>
    
    96
    +                    <signal name="clicked" handler="_remove_files" swapped="no"/>
    
    97
    +                  </object>
    
    98
    +                  <packing>
    
    99
    +                    <property name="expand">False</property>
    
    100
    +                    <property name="homogeneous">True</property>
    
    101
    +                  </packing>
    
    102
    +                </child>
    
    103
    +                <style>
    
    104
    +                  <class name="inline-toolbar"/>
    
    105
    +                </style>
    
    106
    +              </object>
    
    107
    +              <packing>
    
    108
    +                <property name="expand">False</property>
    
    109
    +                <property name="fill">True</property>
    
    110
    +                <property name="position">1</property>
    
    111
    +              </packing>
    
    112
    +            </child>
    
    113
    +          </object>
    
    114
    +          <packing>
    
    115
    +            <property name="expand">False</property>
    
    116
    +            <property name="fill">True</property>
    
    117
    +            <property name="position">2</property>
    
    118
    +          </packing>
    
    119
    +        </child>
    
    120
    +        <child>
    
    121
    +          <object class="GtkLabel">
    
    122
    +            <property name="visible">True</property>
    
    123
    +            <property name="can-focus">False</property>
    
    124
    +            <property name="label" translatable="yes">Description</property>
    
    125
    +            <property name="xalign">0</property>
    
    126
    +            <style>
    
    127
    +              <class name="dim-label"/>
    
    128
    +            </style>
    
    129
    +          </object>
    
    130
    +          <packing>
    
    131
    +            <property name="expand">False</property>
    
    132
    +            <property name="fill">True</property>
    
    133
    +            <property name="position">3</property>
    
    134
    +          </packing>
    
    135
    +        </child>
    
    136
    +        <child>
    
    137
    +          <object class="GtkScrolledWindow">
    
    138
    +            <property name="height-request">70</property>
    
    139
    +            <property name="visible">True</property>
    
    140
    +            <property name="can-focus">True</property>
    
    141
    +            <property name="hscrollbar-policy">never</property>
    
    142
    +            <property name="shadow-type">in</property>
    
    143
    +            <child>
    
    144
    +              <object class="GtkTextView" id="description">
    
    145
    +                <property name="visible">True</property>
    
    146
    +                <property name="can-focus">True</property>
    
    147
    +                <property name="wrap-mode">word-char</property>
    
    148
    +                <property name="left-margin">6</property>
    
    149
    +                <property name="right-margin">6</property>
    
    150
    +                <property name="top-margin">6</property>
    
    151
    +                <property name="bottom-margin">6</property>
    
    152
    +              </object>
    
    153
    +            </child>
    
    154
    +          </object>
    
    155
    +          <packing>
    
    156
    +            <property name="expand">False</property>
    
    157
    +            <property name="fill">True</property>
    
    158
    +            <property name="position">4</property>
    
    159
    +          </packing>
    
    160
    +        </child>
    
    161
    +        <child>
    
    162
    +          <object class="GtkButton" id="files_send">
    
    163
    +            <property name="label" translatable="yes">_Send Files</property>
    
    164
    +            <property name="visible">True</property>
    
    165
    +            <property name="sensitive">False</property>
    
    166
    +            <property name="can-focus">True</property>
    
    167
    +            <property name="receives-default">True</property>
    
    168
    +            <property name="halign">center</property>
    
    169
    +            <property name="use-underline">True</property>
    
    170
    +            <signal name="clicked" handler="_on_send_clicked" swapped="no"/>
    
    171
    +            <style>
    
    172
    +              <class name="suggested-action"/>
    
    173
    +            </style>
    
    174
    +          </object>
    
    175
    +          <packing>
    
    176
    +            <property name="expand">False</property>
    
    177
    +            <property name="fill">True</property>
    
    178
    +            <property name="position">5</property>
    
    179
    +          </packing>
    
    180
    +        </child>
    
    181
    +      </object>
    
    182
    +      <packing>
    
    183
    +        <property name="name">file_selection</property>
    
    184
    +      </packing>
    
    185
    +    </child>
    
    186
    +    <child>
    
    187
    +      <object class="GtkBox" id="resource_box">
    
    188
    +        <property name="visible">True</property>
    
    189
    +        <property name="can-focus">False</property>
    
    190
    +        <property name="border-width">18</property>
    
    191
    +        <property name="orientation">vertical</property>
    
    192
    +        <property name="spacing">6</property>
    
    193
    +        <child>
    
    194
    +          <object class="GtkLabel">
    
    195
    +            <property name="visible">True</property>
    
    196
    +            <property name="can-focus">False</property>
    
    197
    +            <property name="label" translatable="yes">Choose Device</property>
    
    198
    +            <style>
    
    199
    +              <class name="large-header"/>
    
    200
    +            </style>
    
    201
    +          </object>
    
    202
    +          <packing>
    
    203
    +            <property name="expand">False</property>
    
    204
    +            <property name="fill">True</property>
    
    205
    +            <property name="position">0</property>
    
    206
    +          </packing>
    
    207
    +        </child>
    
    208
    +        <child>
    
    209
    +          <object class="GtkButton" id="resource_send">
    
    210
    +            <property name="label" translatable="yes">_Send Files</property>
    
    211
    +            <property name="visible">True</property>
    
    212
    +            <property name="sensitive">False</property>
    
    213
    +            <property name="can-focus">True</property>
    
    214
    +            <property name="receives-default">False</property>
    
    215
    +            <property name="halign">center</property>
    
    216
    +            <property name="use-underline">True</property>
    
    217
    +            <signal name="clicked" handler="_on_send_to_resource" swapped="no"/>
    
    218
    +            <style>
    
    219
    +              <class name="suggested-action"/>
    
    220
    +            </style>
    
    221
    +          </object>
    
    222
    +          <packing>
    
    223
    +            <property name="expand">False</property>
    
    224
    +            <property name="fill">True</property>
    
    225
    +            <property name="pack-type">end</property>
    
    226
    +            <property name="position">1</property>
    
    227
    +          </packing>
    
    228
    +        </child>
    
    229
    +        <child>
    
    230
    +          <object class="GtkLabel" id="resource_instructions">
    
    231
    +            <property name="visible">True</property>
    
    232
    +            <property name="can-focus">False</property>
    
    233
    +            <property name="justify">center</property>
    
    234
    +            <property name="wrap">True</property>
    
    235
    +            <property name="max-width-chars">50</property>
    
    236
    +            <style>
    
    237
    +              <class name="dim-label"/>
    
    238
    +            </style>
    
    239
    +          </object>
    
    240
    +          <packing>
    
    241
    +            <property name="expand">False</property>
    
    242
    +            <property name="fill">True</property>
    
    243
    +            <property name="position">2</property>
    
    244
    +          </packing>
    
    245
    +        </child>
    
    246
    +        <child>
    
    247
    +          <placeholder/>
    
    248
    +        </child>
    
    249
    +      </object>
    
    250
    +      <packing>
    
    251
    +        <property name="name">resource_selection</property>
    
    252
    +        <property name="position">1</property>
    
    253
    +      </packing>
    
    254
    +    </child>
    
    255
    +  </object>
    
    256
    +</interface>

  • gajim/data/gui/filetransfers_send_file_dialog.ui deleted
    1
    -<?xml version="1.0" encoding="UTF-8"?>
    
    2
    -<!-- Generated with glade 3.36.0 -->
    
    3
    -<interface>
    
    4
    -  <requires lib="gtk+" version="3.22"/>
    
    5
    -  <object class="GtkGrid" id="send_file_grid">
    
    6
    -    <property name="visible">True</property>
    
    7
    -    <property name="can_focus">False</property>
    
    8
    -    <property name="border_width">18</property>
    
    9
    -    <property name="row_spacing">6</property>
    
    10
    -    <property name="column_spacing">12</property>
    
    11
    -    <child>
    
    12
    -      <object class="GtkLabel">
    
    13
    -        <property name="visible">True</property>
    
    14
    -        <property name="can_focus">False</property>
    
    15
    -        <property name="label" translatable="yes">Description</property>
    
    16
    -        <property name="xalign">0</property>
    
    17
    -        <style>
    
    18
    -          <class name="dim-label"/>
    
    19
    -        </style>
    
    20
    -      </object>
    
    21
    -      <packing>
    
    22
    -        <property name="left_attach">0</property>
    
    23
    -        <property name="top_attach">2</property>
    
    24
    -        <property name="width">2</property>
    
    25
    -      </packing>
    
    26
    -    </child>
    
    27
    -    <child>
    
    28
    -      <object class="GtkScrolledWindow">
    
    29
    -        <property name="height_request">70</property>
    
    30
    -        <property name="visible">True</property>
    
    31
    -        <property name="can_focus">True</property>
    
    32
    -        <property name="hscrollbar_policy">never</property>
    
    33
    -        <property name="shadow_type">in</property>
    
    34
    -        <child>
    
    35
    -          <object class="GtkTextView" id="description">
    
    36
    -            <property name="visible">True</property>
    
    37
    -            <property name="can_focus">True</property>
    
    38
    -            <property name="wrap_mode">word-char</property>
    
    39
    -            <property name="left_margin">6</property>
    
    40
    -            <property name="right_margin">6</property>
    
    41
    -            <property name="top_margin">6</property>
    
    42
    -            <property name="bottom_margin">6</property>
    
    43
    -          </object>
    
    44
    -        </child>
    
    45
    -      </object>
    
    46
    -      <packing>
    
    47
    -        <property name="left_attach">0</property>
    
    48
    -        <property name="top_attach">3</property>
    
    49
    -        <property name="width">2</property>
    
    50
    -      </packing>
    
    51
    -    </child>
    
    52
    -    <child>
    
    53
    -      <object class="GtkButton">
    
    54
    -        <property name="label" translatable="yes">_Send Files</property>
    
    55
    -        <property name="visible">True</property>
    
    56
    -        <property name="can_focus">True</property>
    
    57
    -        <property name="receives_default">True</property>
    
    58
    -        <property name="tooltip_text" translatable="yes">Send</property>
    
    59
    -        <property name="halign">center</property>
    
    60
    -        <property name="use_underline">True</property>
    
    61
    -        <signal name="clicked" handler="_send" swapped="no"/>
    
    62
    -        <style>
    
    63
    -          <class name="suggested-action"/>
    
    64
    -        </style>
    
    65
    -      </object>
    
    66
    -      <packing>
    
    67
    -        <property name="left_attach">0</property>
    
    68
    -        <property name="top_attach">4</property>
    
    69
    -        <property name="width">2</property>
    
    70
    -      </packing>
    
    71
    -    </child>
    
    72
    -    <child>
    
    73
    -      <object class="GtkLabel">
    
    74
    -        <property name="visible">True</property>
    
    75
    -        <property name="can_focus">False</property>
    
    76
    -        <property name="label" translatable="yes">Files to send</property>
    
    77
    -        <property name="xalign">0</property>
    
    78
    -        <style>
    
    79
    -          <class name="dim-label"/>
    
    80
    -        </style>
    
    81
    -      </object>
    
    82
    -      <packing>
    
    83
    -        <property name="left_attach">0</property>
    
    84
    -        <property name="top_attach">0</property>
    
    85
    -        <property name="width">2</property>
    
    86
    -      </packing>
    
    87
    -    </child>
    
    88
    -    <child>
    
    89
    -      <object class="GtkBox">
    
    90
    -        <property name="visible">True</property>
    
    91
    -        <property name="can_focus">False</property>
    
    92
    -        <property name="hexpand">True</property>
    
    93
    -        <property name="vexpand">True</property>
    
    94
    -        <property name="orientation">vertical</property>
    
    95
    -        <child>
    
    96
    -          <object class="GtkScrolledWindow">
    
    97
    -            <property name="visible">True</property>
    
    98
    -            <property name="can_focus">True</property>
    
    99
    -            <property name="hscrollbar_policy">never</property>
    
    100
    -            <property name="shadow_type">in</property>
    
    101
    -            <child>
    
    102
    -              <object class="GtkViewport">
    
    103
    -                <property name="visible">True</property>
    
    104
    -                <property name="can_focus">False</property>
    
    105
    -                <child>
    
    106
    -                  <object class="GtkListBox" id="listbox">
    
    107
    -                    <property name="visible">True</property>
    
    108
    -                    <property name="can_focus">False</property>
    
    109
    -                    <property name="vexpand">True</property>
    
    110
    -                    <property name="selection_mode">multiple</property>
    
    111
    -                    <property name="activate_on_single_click">False</property>
    
    112
    -                  </object>
    
    113
    -                </child>
    
    114
    -              </object>
    
    115
    -            </child>
    
    116
    -          </object>
    
    117
    -          <packing>
    
    118
    -            <property name="expand">False</property>
    
    119
    -            <property name="fill">True</property>
    
    120
    -            <property name="position">0</property>
    
    121
    -          </packing>
    
    122
    -        </child>
    
    123
    -        <child>
    
    124
    -          <object class="GtkToolbar">
    
    125
    -            <property name="visible">True</property>
    
    126
    -            <property name="can_focus">False</property>
    
    127
    -            <property name="show_arrow">False</property>
    
    128
    -            <property name="icon_size">1</property>
    
    129
    -            <child>
    
    130
    -              <object class="GtkToolButton">
    
    131
    -                <property name="visible">True</property>
    
    132
    -                <property name="can_focus">False</property>
    
    133
    -                <property name="tooltip_text" translatable="yes">Add Files</property>
    
    134
    -                <property name="use_underline">True</property>
    
    135
    -                <property name="icon_name">list-add-symbolic</property>
    
    136
    -                <signal name="clicked" handler="_select_files" swapped="no"/>
    
    137
    -              </object>
    
    138
    -              <packing>
    
    139
    -                <property name="expand">False</property>
    
    140
    -                <property name="homogeneous">False</property>
    
    141
    -              </packing>
    
    142
    -            </child>
    
    143
    -            <child>
    
    144
    -              <object class="GtkToolButton">
    
    145
    -                <property name="visible">True</property>
    
    146
    -                <property name="can_focus">False</property>
    
    147
    -                <property name="tooltip_text" translatable="yes">Remove Files</property>
    
    148
    -                <property name="use_underline">True</property>
    
    149
    -                <property name="icon_name">list-remove-symbolic</property>
    
    150
    -                <signal name="clicked" handler="_remove_files" swapped="no"/>
    
    151
    -              </object>
    
    152
    -              <packing>
    
    153
    -                <property name="expand">False</property>
    
    154
    -                <property name="homogeneous">True</property>
    
    155
    -              </packing>
    
    156
    -            </child>
    
    157
    -            <style>
    
    158
    -              <class name="inline-toolbar"/>
    
    159
    -            </style>
    
    160
    -          </object>
    
    161
    -          <packing>
    
    162
    -            <property name="expand">False</property>
    
    163
    -            <property name="fill">True</property>
    
    164
    -            <property name="position">1</property>
    
    165
    -          </packing>
    
    166
    -        </child>
    
    167
    -      </object>
    
    168
    -      <packing>
    
    169
    -        <property name="left_attach">0</property>
    
    170
    -        <property name="top_attach">1</property>
    
    171
    -        <property name="width">2</property>
    
    172
    -      </packing>
    
    173
    -    </child>
    
    174
    -  </object>
    
    175
    -</interface>

  • gajim/data/style/gajim.css
    ... ... @@ -426,6 +426,11 @@ .search-view-header label {
    426 426
         color: @insensitive_fg_color;
    
    427 427
     }
    
    428 428
     
    
    429
    +.resource-selector list row {
    
    430
    +    padding: 6px;
    
    431
    +    border-bottom: 1px solid @borders;
    
    432
    +}
    
    433
    +
    
    429 434
     /*  Profile window */
    
    430 435
     #NicknameEntry:disabled {
    
    431 436
         font-size: 28px;
    

  • gajim/gtk/call_widget.py
    ... ... @@ -184,6 +184,9 @@ def _on_jingle_request(self, event):
    184 184
             content_types = []
    
    185 185
             for item in event.contents:
    
    186 186
                 content_types.append(item.media)
    
    187
    +        if not any(item in ('audio', 'video') for item in content_types):
    
    188
    +            return
    
    189
    +
    
    187 190
             if 'audio' in content_types:
    
    188 191
                 self._set_jingle_state(
    
    189 192
                     'audio',
    

  • gajim/gtk/controls/chat.py
    ... ... @@ -245,7 +245,9 @@ def update_actions(self):
    245 245
     
    
    246 246
             # Send file (Jingle)
    
    247 247
             jingle_support = self.contact.supports(Namespace.JINGLE_FILE_TRANSFER_5)
    
    248
    -        jingle_conditions = jingle_support and self.contact.is_available
    
    248
    +        jingle_conditions = bool(jingle_support and
    
    249
    +                                 self.contact.is_available and
    
    250
    +                                 not self.contact.is_pm_contact)
    
    249 251
             jingle = self._get_action('send-file-jingle-')
    
    250 252
             jingle.set_enabled(online and jingle_conditions)
    
    251 253
     
    

  • gajim/gtk/file_transfer_send.py
    1
    +# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
    
    2
    +# Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
    
    3
    +# Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
    
    4
    +# Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
    
    5
    +#
    
    6
    +# This file is part of Gajim.
    
    7
    +#
    
    8
    +# Gajim is free software; you can redistribute it and/or modify
    
    9
    +# it under the terms of the GNU General Public License as published
    
    10
    +# by the Free Software Foundation; version 3 only.
    
    11
    +#
    
    12
    +# Gajim is distributed in the hope that it will be useful,
    
    13
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    14
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    15
    +# GNU General Public License for more details.
    
    16
    +#
    
    17
    +# You should have received a copy of the GNU General Public License
    
    18
    +# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    19
    +
    
    20
    +from pathlib import Path
    
    21
    +
    
    22
    +from gi.repository import Gdk
    
    23
    +from gi.repository import Gtk
    
    24
    +from gi.repository import Pango
    
    25
    +
    
    26
    +from nbxmpp import Namespace
    
    27
    +
    
    28
    +from gajim.common import app
    
    29
    +from gajim.common.i18n import _
    
    30
    +
    
    31
    +from .filechoosers import FileChooserDialog
    
    32
    +from .resource_selector import ResourceSelector
    
    33
    +from .util import get_builder
    
    34
    +
    
    35
    +
    
    36
    +class SendFileDialog(Gtk.ApplicationWindow):
    
    37
    +    def __init__(self, contact, send_callback, transient_for):
    
    38
    +        Gtk.ApplicationWindow.__init__(self)
    
    39
    +        self.set_application(app.app)
    
    40
    +        self.set_show_menubar(False)
    
    41
    +        self.set_resizable(True)
    
    42
    +        self.set_default_size(500, 350)
    
    43
    +        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
    
    44
    +        self.set_transient_for(transient_for)
    
    45
    +        self.set_title(_('Choose a File to Send…'))
    
    46
    +        self.set_destroy_with_parent(True)
    
    47
    +
    
    48
    +        self._contact = contact
    
    49
    +        self._send_callback = send_callback
    
    50
    +
    
    51
    +        self._ui = get_builder('file_transfer_send.ui')
    
    52
    +        self.add(self._ui.send_stack)
    
    53
    +
    
    54
    +        self._ui.resource_instructions.set_text(
    
    55
    +            _('%s is online with multiple devices.\n'
    
    56
    +              'Choose the device you would like to send the '
    
    57
    +              'file to.') % self._contact.name)
    
    58
    +
    
    59
    +        self._resource_selector = ResourceSelector(
    
    60
    +            contact,
    
    61
    +            constraints=[Namespace.JINGLE_FILE_TRANSFER_5])
    
    62
    +        self._resource_selector.connect(
    
    63
    +            'selection-changed', self._on_resource_selection)
    
    64
    +        self._ui.resource_box.pack_start(self._resource_selector, 1, 0, 0)
    
    65
    +
    
    66
    +        self.connect('key-press-event', self._key_press_event)
    
    67
    +        self._ui.connect_signals(self)
    
    68
    +        self.show_all()
    
    69
    +
    
    70
    +    def _on_files_changed(self, _listbox, _row):
    
    71
    +        sensitive = bool(len(self._ui.listbox.get_children()) > 0)
    
    72
    +        self._ui.files_send.set_sensitive(sensitive)
    
    73
    +
    
    74
    +    def _on_resource_selection(self, _selector, state):
    
    75
    +        self._ui.resource_send.set_sensitive(state)
    
    76
    +
    
    77
    +    def _on_send_to_resource(self, _button):
    
    78
    +        resource_jid = self._resource_selector.get_jid()
    
    79
    +        self._send_files(resource_jid)
    
    80
    +
    
    81
    +    def _on_send_clicked(self, _button):
    
    82
    +        count = len(self._contact.get_resources())
    
    83
    +        if count == 0 or count > 1:
    
    84
    +            self._ui.send_stack.set_visible_child_name('resource_selection')
    
    85
    +            return
    
    86
    +
    
    87
    +        resource_jid = self._contact.get_resources()[0].jid
    
    88
    +        self._send_files(resource_jid)
    
    89
    +
    
    90
    +    def _send_files(self, resource_jid):
    
    91
    +        for file in self._ui.listbox.get_children():
    
    92
    +            self._send_callback(
    
    93
    +                resource_jid, str(file.path), self._get_description())
    
    94
    +        self.destroy()
    
    95
    +
    
    96
    +    def _select_files(self, _button):
    
    97
    +        FileChooserDialog(self._set_files,
    
    98
    +                          select_multiple=True,
    
    99
    +                          transient_for=self,
    
    100
    +                          path=app.settings.get('last_send_dir'))
    
    101
    +
    
    102
    +    def _remove_files(self, _button):
    
    103
    +        selected = self._ui.listbox.get_selected_rows()
    
    104
    +        for item in selected:
    
    105
    +            self._ui.listbox.remove(item)
    
    106
    +
    
    107
    +    def _set_files(self, file_names):
    
    108
    +        for file in file_names:
    
    109
    +            row = FileRow(file)
    
    110
    +            if row.path.is_dir():
    
    111
    +                continue
    
    112
    +            last_dir = row.path.parent
    
    113
    +            self._ui.listbox.add(row)
    
    114
    +        self._ui.listbox.show_all()
    
    115
    +        app.settings.set('last_send_dir', str(last_dir))
    
    116
    +
    
    117
    +    def _get_description(self):
    
    118
    +        buffer_ = self._ui.description.get_buffer()
    
    119
    +        start, end = buffer_.get_bounds()
    
    120
    +        return buffer_.get_text(start, end, False)
    
    121
    +
    
    122
    +    def _key_press_event(self, _widget, event):
    
    123
    +        if event.keyval == Gdk.KEY_Escape:
    
    124
    +            self.destroy()
    
    125
    +
    
    126
    +
    
    127
    +class FileRow(Gtk.ListBoxRow):
    
    128
    +    def __init__(self, path):
    
    129
    +        Gtk.ListBoxRow.__init__(self)
    
    130
    +        self.path = Path(path)
    
    131
    +        label = Gtk.Label(label=self.path.name)
    
    132
    +        label.set_ellipsize(Pango.EllipsizeMode.END)
    
    133
    +        label.set_xalign(0)
    
    134
    +        self.add(label)

  • gajim/gtk/filetransfer.py
    ... ... @@ -21,7 +21,6 @@
    21 21
     import time
    
    22 22
     import logging
    
    23 23
     from functools import partial
    
    24
    -from pathlib import Path
    
    25 24
     from enum import IntEnum, unique
    
    26 25
     from datetime import datetime
    
    27 26
     
    
    ... ... @@ -30,6 +29,8 @@
    30 29
     from gi.repository import GLib
    
    31 30
     from gi.repository import Pango
    
    32 31
     
    
    32
    +from nbxmpp import JID
    
    33
    +
    
    33 34
     from gajim import gtkgui_helpers
    
    34 35
     
    
    35 36
     from gajim.common import app
    
    ... ... @@ -47,7 +48,7 @@
    47 48
     from .dialogs import InformationDialog
    
    48 49
     from .dialogs import ErrorDialog
    
    49 50
     from .filechoosers import FileSaveDialog
    
    50
    -from .filechoosers import FileChooserDialog
    
    51
    +from .file_transfer_send import SendFileDialog
    
    51 52
     from .tooltips import FileTransfersTooltip
    
    52 53
     from .util import get_builder
    
    53 54
     
    
    ... ... @@ -228,10 +229,11 @@ def on_open(widget, file_props):
    228 229
             sectext = _('File name: %s') % GLib.markup_escape_text(file_name)
    
    229 230
             sectext += '\n' + _('Size: %s') % GLib.format_size_full(
    
    230 231
                 file_props.size, self.units)
    
    232
    +        client = app.get_client(file_props.tt_account)
    
    231 233
             if file_props.type_ == 'r':
    
    232
    -            jid = file_props.sender.split('/')[0]
    
    233
    -            sender_name = app.contacts.get_first_contact_from_jid(
    
    234
    -                file_props.tt_account, jid).get_shown_name()
    
    234
    +            jid = JID.from_string(file_props.sender)
    
    235
    +            sender_name = client.get_module('Contacts').get_contact(
    
    236
    +                jid.bare).name
    
    235 237
                 sender = sender_name
    
    236 238
             else:
    
    237 239
                 # You is a reply of who sent a file
    
    ... ... @@ -239,9 +241,9 @@ def on_open(widget, file_props):
    239 241
             sectext += '\n' + _('Sender: %s') % sender
    
    240 242
             sectext += '\n' + _('Recipient: ')
    
    241 243
             if file_props.type_ == 's':
    
    242
    -            jid = file_props.receiver.split('/')[0]
    
    243
    -            receiver_name = app.contacts.get_first_contact_from_jid(
    
    244
    -                file_props.tt_account, jid).get_shown_name()
    
    244
    +            jid = JID.from_string(file_props.receiver)
    
    245
    +            receiver_name = client.get_module('Contacts').get_contact(
    
    246
    +                jid.bare).name
    
    245 247
                 recipient = receiver_name
    
    246 248
             else:
    
    247 249
                 # You is a reply of who received a file
    
    ... ... @@ -250,7 +252,7 @@ def on_open(widget, file_props):
    250 252
             if file_props.type_ == 'r':
    
    251 253
                 sectext += '\n' + _('Saved in: %s') % file_path
    
    252 254
     
    
    253
    -        dialog = HigDialog(app.interface.roster.window,
    
    255
    +        dialog = HigDialog(app.window,
    
    254 256
                                Gtk.MessageType.INFO,
    
    255 257
                                Gtk.ButtonsType.NONE,
    
    256 258
                                _('File transfer completed'),
    
    ... ... @@ -298,17 +300,10 @@ def show_stopped(self, jid, file_props, error_msg=''):
    298 300
             self._ui.transfers_list.get_selection().unselect_all()
    
    299 301
     
    
    300 302
         def show_hash_error(self, jid, file_props, account):
    
    301
    -
    
    302
    -        def on_yes(dummy, fjid, file_props, account):
    
    303
    +        def _on_yes():
    
    303 304
                 # Delete old file
    
    304 305
                 os.remove(file_props.file_name)
    
    305
    -            jid, resource = app.get_room_and_nick_from_fjid(fjid)
    
    306
    -            if resource:
    
    307
    -                contact = app.contacts.get_contact(account, jid, resource)
    
    308
    -            else:
    
    309
    -                contact = app.contacts.get_contact_with_highest_priority(
    
    310
    -                    account, jid)
    
    311
    -                fjid = contact.get_full_jid()
    
    306
    +
    
    312 307
                 # Request the file to the sender
    
    313 308
                 sid = helpers.get_random_string()
    
    314 309
                 new_file_props = FilesProp.getNewFileProp(account, sid)
    
    ... ... @@ -319,12 +314,15 @@ def on_yes(dummy, fjid, file_props, account):
    319 314
                 new_file_props.date = file_props.date
    
    320 315
                 new_file_props.hash_ = file_props.hash_
    
    321 316
                 new_file_props.type_ = 'r'
    
    322
    -            tsid = app.connections[account].get_module(
    
    323
    -                'Jingle').start_file_transfer(fjid, new_file_props, True)
    
    317
    +            tsid = client.get_module('Jingle').start_file_transfer(
    
    318
    +                jid, new_file_props, True)
    
    324 319
     
    
    325 320
                 new_file_props.transport_sid = tsid
    
    326 321
                 self.add_transfer(account, contact, new_file_props)
    
    327 322
     
    
    323
    +        client = app.get_client(account)
    
    324
    +        contact = client.get_module('Contacts').get_contact(jid)
    
    325
    +
    
    328 326
             if file_props.type_ == 'r':
    
    329 327
                 file_name = os.path.basename(file_props.file_name)
    
    330 328
             else:
    
    ... ... @@ -339,16 +337,14 @@ def on_yes(dummy, fjid, file_props, account):
    339 337
                                    text=_('_No')),
    
    340 338
                  DialogButton.make('Accept',
    
    341 339
                                    text=_('_Download Again'),
    
    342
    -                               callback=on_yes,
    
    343
    -                               args=[jid,
    
    344
    -                                     file_props,
    
    345
    -                                     account])]).show()
    
    340
    +                               callback=_on_yes)]).show()
    
    346 341
     
    
    347 342
         def show_file_send_request(self, account, contact):
    
    348 343
             send_callback = partial(self.send_file, account, contact)
    
    349
    -        SendFileDialog(send_callback, self.window)
    
    344
    +        SendFileDialog(contact, send_callback, self.window)
    
    350 345
     
    
    351
    -    def send_file(self, account, contact, file_path, file_desc=''):
    
    346
    +    def send_file(self, account, contact, resource_jid, file_path,
    
    347
    +                  file_desc=''):
    
    352 348
             """
    
    353 349
             Start the real transfer(upload) of the file
    
    354 350
             """
    
    ... ... @@ -358,20 +354,15 @@ def send_file(self, account, contact, file_path, file_desc=''):
    358 354
                 ErrorDialog(pritext, sextext)
    
    359 355
                 return
    
    360 356
     
    
    361
    -        if isinstance(contact, str):
    
    362
    -            if contact.find('/') == -1:
    
    363
    -                return
    
    364
    -            (jid, resource) = contact.split('/', 1)
    
    365
    -            contact = app.contacts.create_contact(jid=jid, account=account,
    
    366
    -                                                  resource=resource)
    
    367 357
             file_name = os.path.split(file_path)[1]
    
    368 358
             file_props = self.get_send_file_props(account, contact, file_path,
    
    369 359
                                                   file_name, file_desc)
    
    370 360
             if file_props is None:
    
    371 361
                 return False
    
    372 362
     
    
    373
    -        app.connections[account].get_module('Jingle').start_file_transfer(
    
    374
    -            contact.get_full_jid(), file_props)
    
    363
    +        client = app.get_client(account)
    
    364
    +        client.get_module('Jingle').start_file_transfer(
    
    365
    +            str(resource_jid), file_props)
    
    375 366
             self.add_transfer(account, contact, file_props)
    
    376 367
     
    
    377 368
             return True
    
    ... ... @@ -383,8 +374,8 @@ def _start_receive(self, file_path, account, contact, file_props):
    383 374
             file_props.file_name = file_path
    
    384 375
             file_props.type_ = 'r'
    
    385 376
             self.add_transfer(account, contact, file_props)
    
    386
    -        app.connections[account].get_module('Bytestream').send_file_approval(
    
    387
    -            file_props)
    
    377
    +        client = app.get_client(account)
    
    378
    +        client.get_module('Bytestream').send_file_approval(file_props)
    
    388 379
     
    
    389 380
         def on_file_request_accepted(self, account, contact, file_props):
    
    390 381
             def _on_accepted(account, contact, file_props, file_path):
    
    ... ... @@ -417,7 +408,7 @@ def _on_replace():
    417 408
                             file_path, account, contact, file_props)
    
    418 409
     
    
    419 410
                     def _on_cancel():
    
    420
    -                    con.get_module('Bytestream').send_file_rejection(
    
    411
    +                    client.get_module('Bytestream').send_file_rejection(
    
    421 412
                             file_props)
    
    422 413
     
    
    423 414
                     ConfirmationDialog(
    
    ... ... @@ -447,10 +438,10 @@ def _on_cancel():
    447 438
                 self._start_receive(file_path, account, contact, file_props)
    
    448 439
     
    
    449 440
             # Show file save as dialog
    
    450
    -        con = app.connections[account]
    
    441
    +        client = app.get_client(account)
    
    451 442
             accept_cb = partial(_on_accepted, account, contact, file_props)
    
    452
    -        cancel_cb = partial(con.get_module('Bytestream').send_file_rejection,
    
    453
    -                            file_props)
    
    443
    +        cancel_cb = partial(
    
    444
    +            client.get_module('Bytestream').send_file_rejection, file_props)
    
    454 445
             FileSaveDialog(accept_cb,
    
    455 446
                            cancel_cb,
    
    456 447
                            path=app.settings.get('last_save_dir'),
    
    ... ... @@ -478,12 +469,12 @@ def _on_ok():
    478 469
                 self.on_file_request_accepted(account, contact, file_props)
    
    479 470
     
    
    480 471
             def _on_cancel():
    
    481
    -            app.connections[account].get_module(
    
    472
    +            app.get_client(account).get_module(
    
    482 473
                     'Bytestream').send_file_rejection(file_props)
    
    483 474
     
    
    484 475
             ConfirmationDialog(
    
    485 476
                 _('File Transfer Request'),
    
    486
    -            _('%s wants to send you a file') % contact.get_shown_name(),
    
    477
    +            _('%s wants to send you a file') % contact.name,
    
    487 478
                 sectext,
    
    488 479
                 [DialogButton.make('Cancel',
    
    489 480
                                    callback=_on_cancel),
    
    ... ... @@ -518,13 +509,13 @@ def set_status(self, file_props, status):
    518 509
                 text += received_size + '/' + full_size
    
    519 510
                 self.model.set(iter_, Column.PROGRESS, text)
    
    520 511
     
    
    521
    -            def pulse():
    
    522
    -                p = self.model.get(iter_, Column.PULSE)[0]
    
    523
    -                if p == GLib.MAXINT32:
    
    512
    +            def _pulse():
    
    513
    +                pulse_value = self.model.get(iter_, Column.PULSE)[0]
    
    514
    +                if pulse_value == GLib.MAXINT32:
    
    524 515
                         return False
    
    525
    -                self.model.set(iter_, Column.PULSE, p + 1)
    
    516
    +                self.model.set(iter_, Column.PULSE, pulse_value + 1)
    
    526 517
                     return True
    
    527
    -            GLib.timeout_add(100, pulse)
    
    518
    +            GLib.timeout_add(100, _pulse)
    
    528 519
             elif status == 'hash_error':
    
    529 520
                 text = _('File error') + '\n'
    
    530 521
                 received_size = GLib.format_size_full(
    
    ... ... @@ -569,6 +560,9 @@ def _get_eta_and_speed(self, full_size, transfered_size, file_props):
    569 560
             if not file_props.transfered_size:
    
    570 561
                 return 0., 0.
    
    571 562
     
    
    563
    +        if file_props.elapsed_time == 0:
    
    564
    +            return 0., 0.
    
    565
    +
    
    572 566
             if len(file_props.transfered_size) == 1:
    
    573 567
                 speed = round(float(transfered_size) / file_props.elapsed_time)
    
    574 568
             else:
    
    ... ... @@ -595,7 +589,7 @@ def _remove_transfer(self, iter_, sid, file_props):
    595 589
                 account = file_props.tt_account
    
    596 590
                 if account in app.connections:
    
    597 591
                     # there is a connection to the account
    
    598
    -                app.connections[account].get_module(
    
    592
    +                app.get_client(account).get_module(
    
    599 593
                         'Bytestream').remove_transfer(file_props)
    
    600 594
                 if file_props.type_ == 'r':  # we receive a file
    
    601 595
                     other = file_props.sender
    
    ... ... @@ -611,8 +605,7 @@ def _remove_transfer(self, iter_, sid, file_props):
    611 605
                     for event in app.events.get_events(account, jid, [ev_type]):
    
    612 606
                         if event.file_props.sid == file_props.sid:
    
    613 607
                             app.events.remove_events(account, jid, event)
    
    614
    -                        app.interface.roster.draw_contact(jid, account)
    
    615
    -                        app.interface.roster.show_title()
    
    608
    +
    
    616 609
             FilesProp.deleteFileProp(file_props)
    
    617 610
             del file_props
    
    618 611
     
    
    ... ... @@ -678,8 +671,9 @@ def set_progress(self, typ, sid, transfered_size, iter_=None):
    678 671
                 self.model.set(iter_, 0, self.icons[status])
    
    679 672
                 if transfered_size == full_size:
    
    680 673
                     # If we are receiver and this is a jingle session
    
    681
    -                if file_props.type_ == 'r' and  \
    
    682
    -                file_props.session_type == 'jingle' and file_props.hash_:
    
    674
    +                if (file_props.type_ == 'r' and
    
    675
    +                        file_props.session_type == 'jingle' and
    
    676
    +                        file_props.hash_):
    
    683 677
                         # Show that we are computing the hash
    
    684 678
                         self.set_status(file_props, 'computing')
    
    685 679
                     else:
    
    ... ... @@ -753,7 +747,7 @@ def add_transfer(self, account, contact, file_props):
    753 747
             else:
    
    754 748
                 file_name = file_props.name
    
    755 749
             text_props = GLib.markup_escape_text(file_name) + '\n'
    
    756
    -        text_props += contact.get_shown_name()
    
    750
    +        text_props += contact.name
    
    757 751
             self.model.set(iter_,
    
    758 752
                            1,
    
    759 753
                            text_labels,
    
    ... ... @@ -916,11 +910,11 @@ def _on_cancel_button_clicked(self, widget):
    916 910
             account = file_props.tt_account
    
    917 911
             if account not in app.connections:
    
    918 912
                 return
    
    919
    -        con = app.connections[account]
    
    913
    +        client = app.get_client(account)
    
    920 914
             # Check if we are in a IBB transfer
    
    921 915
             if file_props.direction:
    
    922
    -            con.get_module('IBB').send_close(file_props)
    
    923
    -        con.get_module('Bytestream').disconnect_transfer(file_props)
    
    916
    +            client.get_module('IBB').send_close(file_props)
    
    917
    +        client.get_module('Bytestream').disconnect_transfer(file_props)
    
    924 918
             self.set_status(file_props, 'stop')
    
    925 919
     
    
    926 920
         def _on_notify_ft_complete_toggled(self, widget, *args):
    
    ... ... @@ -1028,70 +1022,3 @@ def _on_remove_menuitem_activate(self, widget):
    1028 1022
         def _on_file_transfers_window_key_press_event(self, widget, event):
    
    1029 1023
             if event.keyval == Gdk.KEY_Escape:  # ESCAPE
    
    1030 1024
                 self.window.hide()
    1031
    -
    
    1032
    -
    
    1033
    -class SendFileDialog(Gtk.ApplicationWindow):
    
    1034
    -    def __init__(self, send_callback, transient_for):
    
    1035
    -        Gtk.ApplicationWindow.__init__(self)
    
    1036
    -        self.set_name('SendFileDialog')
    
    1037
    -        self.set_application(app.app)
    
    1038
    -        self.set_show_menubar(False)
    
    1039
    -        self.set_resizable(True)
    
    1040
    -        self.set_default_size(500, 350)
    
    1041
    -        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
    
    1042
    -        self.set_transient_for(transient_for)
    
    1043
    -        self.set_title(_('Choose a File to Send…'))
    
    1044
    -        self.set_destroy_with_parent(True)
    
    1045
    -
    
    1046
    -        self._send_callback = send_callback
    
    1047
    -
    
    1048
    -        self._ui = get_builder('filetransfers_send_file_dialog.ui')
    
    1049
    -        self.add(self._ui.send_file_grid)
    
    1050
    -        self.connect('key-press-event', self._key_press_event)
    
    1051
    -        self._ui.connect_signals(self)
    
    1052
    -        self.show_all()
    
    1053
    -
    
    1054
    -    def _send(self, button):
    
    1055
    -        for file in self._ui.listbox.get_children():
    
    1056
    -            self._send_callback(str(file.path), self._get_description())
    
    1057
    -        self.destroy()
    
    1058
    -
    
    1059
    -    def _select_files(self, button):
    
    1060
    -        FileChooserDialog(self._set_files,
    
    1061
    -                          select_multiple=True,
    
    1062
    -                          transient_for=self,
    
    1063
    -                          path=app.settings.get('last_send_dir'))
    
    1064
    -
    
    1065
    -    def _remove_files(self, button):
    
    1066
    -        selected = self._ui.listbox.get_selected_rows()
    
    1067
    -        for item in selected:
    
    1068
    -            self._ui.listbox.remove(item)
    
    1069
    -
    
    1070
    -    def _set_files(self, filenames):
    
    1071
    -        for file in filenames:
    
    1072
    -            row = FileRow(file)
    
    1073
    -            if row.path.is_dir():
    
    1074
    -                continue
    
    1075
    -            last_dir = row.path.parent
    
    1076
    -            self._ui.listbox.add(row)
    
    1077
    -        self._ui.listbox.show_all()
    
    1078
    -        app.settings.set('last_send_dir', str(last_dir))
    
    1079
    -
    
    1080
    -    def _get_description(self):
    
    1081
    -        buffer_ = self._ui.description.get_buffer()
    
    1082
    -        start, end = buffer_.get_bounds()
    
    1083
    -        return buffer_.get_text(start, end, False)
    
    1084
    -
    
    1085
    -    def _key_press_event(self, widget, event):
    
    1086
    -        if event.keyval == Gdk.KEY_Escape:
    
    1087
    -            self.destroy()
    
    1088
    -
    
    1089
    -
    
    1090
    -class FileRow(Gtk.ListBoxRow):
    
    1091
    -    def __init__(self, path):
    
    1092
    -        Gtk.ListBoxRow.__init__(self)
    
    1093
    -        self.path = Path(path)
    
    1094
    -        label = Gtk.Label(label=self.path.name)
    
    1095
    -        label.set_ellipsize(Pango.EllipsizeMode.END)
    
    1096
    -        label.set_xalign(0)
    
    1097
    -        self.add(label)

  • gajim/gtk/resource_selector.py
    1
    +# This file is part of Gajim.
    
    2
    +#
    
    3
    +# Gajim is free software; you can redistribute it and/or modify
    
    4
    +# it under the terms of the GNU General Public License as published
    
    5
    +# by the Free Software Foundation; version 3 only.
    
    6
    +#
    
    7
    +# Gajim is distributed in the hope that it will be useful,
    
    8
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    +# GNU General Public License for more details.
    
    11
    +#
    
    12
    +# You should have received a copy of the GNU General Public License
    
    13
    +# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14
    +
    
    15
    +import locale
    
    16
    +import logging
    
    17
    +
    
    18
    +from gi.repository import GObject
    
    19
    +from gi.repository import Gtk
    
    20
    +
    
    21
    +from gajim.common import app
    
    22
    +from gajim.common.i18n import _
    
    23
    +
    
    24
    +log = logging.getLogger('gajim.gui.resource_selector')
    
    25
    +
    
    26
    +
    
    27
    +class ResourceSelector(Gtk.ScrolledWindow):
    
    28
    +
    
    29
    +    __gsignals__ = {
    
    30
    +        'selection-changed': (
    
    31
    +            GObject.SignalFlags.RUN_LAST,
    
    32
    +            None,
    
    33
    +            (bool, )),
    
    34
    +    }
    
    35
    +
    
    36
    +    def __init__(self, contact, constraints=None):
    
    37
    +        Gtk.ScrolledWindow.__init__(self)
    
    38
    +        self.set_shadow_type(Gtk.ShadowType.IN)
    
    39
    +        self.set_size_request(-1, 200)
    
    40
    +        self.get_style_context().add_class('resource-selector')
    
    41
    +
    
    42
    +        self._listbox = Gtk.ListBox()
    
    43
    +        self._listbox.set_sort_func(self._sort_func)
    
    44
    +        self._listbox.connect('row-selected', self._on_row_selected)
    
    45
    +        self.add(self._listbox)
    
    46
    +
    
    47
    +        self._contact = contact
    
    48
    +        self._contact.connect('presence-update', self._on_update)
    
    49
    +        self._contact.connect('caps-update', self._on_update)
    
    50
    +
    
    51
    +        # Constraints include Namespaces a resource has to support
    
    52
    +        self._constraints = constraints or []
    
    53
    +
    
    54
    +        self._set_placeholder()
    
    55
    +        self._add_entries()
    
    56
    +
    
    57
    +        self.show_all()
    
    58
    +
    
    59
    +    @staticmethod
    
    60
    +    def _sort_func(row1, row2):
    
    61
    +        return locale.strcoll(
    
    62
    +            row1.device_text.lower(), row2.device_text.lower())
    
    63
    +
    
    64
    +    def _on_row_selected(self, _listbox, row):
    
    65
    +        state = bool(row is not None)
    
    66
    +        self.emit('selection-changed', state)
    
    67
    +
    
    68
    +    def _set_placeholder(self):
    
    69
    +        image = Gtk.Image.new_from_icon_name(
    
    70
    +            'dialog-warning-symbolic', Gtk.IconSize.DND)
    
    71
    +        label = Gtk.Label(label=_('No devices online'))
    
    72
    +        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
    
    73
    +        box.set_valign(Gtk.Align.CENTER)
    
    74
    +        box.get_style_context().add_class('dim-label')
    
    75
    +        box.add(image)
    
    76
    +        box.add(label)
    
    77
    +        box.show_all()
    
    78
    +        self._listbox.set_placeholder(box)
    
    79
    +
    
    80
    +    def _add_entries(self):
    
    81
    +        for resource in self._contact.iter_resources():
    
    82
    +            self._listbox.add(ResourceRow(resource, self._constraints))
    
    83
    +
    
    84
    +    def _on_update(self, _contact, _signal_name):
    
    85
    +        for child in self._listbox.get_children():
    
    86
    +            self._listbox.remove(child)
    
    87
    +        self._add_entries()
    
    88
    +
    
    89
    +    def get_jid(self):
    
    90
    +        return self._listbox.get_selected_row().jid
    
    91
    +
    
    92
    +
    
    93
    +class ResourceRow(Gtk.ListBoxRow):
    
    94
    +    def __init__(self, resource_contact, constraints):
    
    95
    +        Gtk.ListBoxRow.__init__(self)
    
    96
    +
    
    97
    +        self.jid = resource_contact.jid
    
    98
    +
    
    99
    +        icon_name = 'computer-symbolic'
    
    100
    +        tooltip_text = _('Computer')
    
    101
    +        self.device_text = resource_contact.jid.resource
    
    102
    +
    
    103
    +        disco_info = app.storage.cache.get_last_disco_info(
    
    104
    +            resource_contact.jid)
    
    105
    +        if disco_info is not None:
    
    106
    +            name, type_ = self._get_client_identity(disco_info)
    
    107
    +            if name is not None:
    
    108
    +                self.device_text = f'{name} ({resource_contact.jid.resource})'
    
    109
    +            if type_ is not None:
    
    110
    +                if type_ == 'phone':
    
    111
    +                    icon_name = 'phone-symbolic'
    
    112
    +                    tooltip_text = _('Phone')
    
    113
    +
    
    114
    +        image = Gtk.Image()
    
    115
    +        image.set_from_icon_name(icon_name, Gtk.IconSize.DND)
    
    116
    +        image.set_tooltip_text(tooltip_text)
    
    117
    +
    
    118
    +        name_label = Gtk.Label()
    
    119
    +        name_label.set_text(self.device_text)
    
    120
    +
    
    121
    +        box = Gtk.Box(spacing=12)
    
    122
    +        box.add(image)
    
    123
    +        box.add(name_label)
    
    124
    +
    
    125
    +        for constraint in constraints:
    
    126
    +            if not resource_contact.supports(constraint):
    
    127
    +                self.set_sensitive(False)
    
    128
    +                self.set_tooltip_text(_('This devices is not compatible.'))
    
    129
    +
    
    130
    +        self.add(box)
    
    131
    +        self.show_all()
    
    132
    +
    
    133
    +    @staticmethod
    
    134
    +    def _get_client_identity(disco_info):
    
    135
    +        for identity in disco_info.identities:
    
    136
    +            if identity.category == 'client':
    
    137
    +                return identity.name, identity.type
    
    138
    +            return None, None

  • gajim/gtk/tooltips.py
    ... ... @@ -33,6 +33,8 @@
    33 33
     from gi.repository import GLib
    
    34 34
     from gi.repository import Pango
    
    35 35
     
    
    36
    +from nbxmpp import JID
    
    37
    +
    
    36 38
     from gajim.common import app
    
    37 39
     from gajim.common import helpers
    
    38 40
     from gajim.common.const import AvatarSize
    
    ... ... @@ -304,20 +306,19 @@ def _create_tooltip(self, file_props, _sid):
    304 306
                 file_name = file_props.name
    
    305 307
             properties.append((_('File Name: '),
    
    306 308
                                GLib.markup_escape_text(file_name)))
    
    309
    +
    
    310
    +        client = app.get_client(file_props.tt_account)
    
    307 311
             if file_props.type_ == 'r':
    
    308 312
                 type_ = Q_('?Noun:Download')
    
    309 313
                 actor = _('Sender: ')
    
    310
    -            sender = file_props.sender.split('/')[0]
    
    311
    -            name = app.contacts.get_first_contact_from_jid(
    
    312
    -                file_props.tt_account, sender).get_shown_name()
    
    314
    +            sender = JID.from_string(file_props.sender)
    
    315
    +            name = client.get_module('Contacts').get_contact(sender.bare).name
    
    313 316
             else:
    
    314 317
                 type_ = Q_('?Noun:Upload')
    
    315 318
                 actor = _('Recipient: ')
    
    316
    -            receiver = file_props.receiver
    
    317
    -            if hasattr(receiver, 'name'):
    
    318
    -                name = receiver.get_shown_name()
    
    319
    -            else:
    
    320
    -                name = receiver.split('/')[0]
    
    319
    +            receiver = JID.from_string(file_props.receiver)
    
    320
    +            name = client.get_module('Contacts').get_contact(
    
    321
    +                receiver.bare).name
    
    321 322
             properties.append((Q_('?transfer type:Type: '), type_))
    
    322 323
             properties.append((actor, GLib.markup_escape_text(name)))
    
    323 324
     
    

  • gajim/gui_interface.py
    ... ... @@ -757,14 +757,10 @@ def handle_event_file_request_error(self, event):
    757 757
                     text=event.file_props.name)
    
    758 758
     
    
    759 759
         def handle_event_file_request(self, event):
    
    760
    -        # TODO
    
    761 760
             account = event.conn.name
    
    762
    -        if event.jid not in app.contacts.get_jid_list(account):
    
    763
    -            contact = app.contacts.create_not_in_roster_contact(
    
    764
    -                jid=event.jid, account=account)
    
    765
    -            app.contacts.add_contact(account, contact)
    
    766
    -            self.roster.add_contact(event.jid, account)
    
    767
    -        contact = app.contacts.get_first_contact_from_jid(account, event.jid)
    
    761
    +        client = app.get_client(account)
    
    762
    +        contact = client.get_module('Contacts').get_contact(event.jid)
    
    763
    +
    
    768 764
             if event.file_props.session_type == 'jingle':
    
    769 765
                 request = \
    
    770 766
                     event.stanza.getTag('jingle').getTag('content').getTag(
    
    ... ... @@ -781,8 +777,7 @@ def handle_event_file_request(self, event):
    781 777
             file_event = events.FileRequestEvent(event.file_props)
    
    782 778
             self.add_event(account, event.jid, file_event)
    
    783 779
             if helpers.allow_showing_notification(account):
    
    784
    -            txt = _('%s wants to send you a file.') % app.get_name_from_jid(
    
    785
    -                account, event.jid)
    
    780
    +            txt = _('%s wants to send you a file.') % contact.name
    
    786 781
                 event_type = _('File Transfer Request')
    
    787 782
                 app.notification.popup(
    
    788 783
                     event_type,
    
    ... ... @@ -823,11 +818,11 @@ def handle_event_file_rcv_completed(self, account, file_props):
    823 818
             if file_props.error == 0:
    
    824 819
                 file_transfers.set_progress(
    
    825 820
                     file_props.type_, file_props.sid, file_props.received_len)
    
    826
    -            jid = app.get_jid_without_resource(str(file_props.receiver))
    
    821
    +            jid = JID.from_string(file_props.receiver)
    
    827 822
                 app.nec.push_incoming_event(
    
    828 823
                     NetworkEvent('file-transfer-completed',
    
    829 824
                                  file_props=file_props,
    
    830
    -                             jid=jid))
    
    825
    +                             jid=jid.bare))
    
    831 826
     
    
    832 827
             else:
    
    833 828
                 file_transfers.set_status(file_props, 'stop')
    
    ... ... @@ -947,56 +942,55 @@ def _popup_ft_result(self, account, jid, file_props):
    947 942
                 self.add_event(account, jid, event)
    
    948 943
     
    
    949 944
             if file_props is not None:
    
    945
    +            client = app.get_client(account)
    
    946
    +
    
    950 947
                 if file_props.type_ == 'r':
    
    951
    -                # Get the name of the sender, as it is in the roster
    
    952
    -                sender = file_props.sender.split('/')[0]
    
    953
    -                name = app.contacts.get_first_contact_from_jid(
    
    954
    -                    account, sender).get_shown_name()
    
    948
    +                jid = JID.from_string(file_props.sender)
    
    949
    +                contact = client.get_module('contacts').get_contact(jid.bare)
    
    955 950
                     filename = os.path.basename(file_props.file_name)
    
    956 951
     
    
    957 952
                     if event_type == _('File Transfer Completed'):
    
    958 953
                         txt = _('%(filename)s received from %(name)s.') % {
    
    959 954
                             'filename': filename,
    
    960
    -                        'name': name}
    
    955
    +                        'name': contact.name}
    
    961 956
                         icon_name = 'emblem-default'
    
    962 957
                     elif event_type == _('File Transfer Stopped'):
    
    963 958
                         txt = _('File transfer of %(filename)s from %(name)s '
    
    964 959
                                 'stopped.') % {
    
    965 960
                                     'filename': filename,
    
    966
    -                                'name': name}
    
    961
    +                                'name': contact.name}
    
    967 962
                         icon_name = 'process-stop'
    
    968 963
                     else:  # File transfer hash error
    
    969 964
                         txt = _('File transfer of %(filename)s from %(name)s '
    
    970 965
                                 'failed.') % {
    
    971 966
                                     'filename': filename,
    
    972
    -                                'name': name}
    
    967
    +                                'name': contact.name}
    
    973 968
                         icon_name = 'process-stop'
    
    974 969
                 else:
    
    975 970
                     receiver = file_props.receiver
    
    976 971
                     if hasattr(receiver, 'jid'):
    
    977 972
                         receiver = receiver.jid
    
    978
    -                receiver = receiver.split('/')[0]
    
    979
    -                # Get the name of the contact, as it is in the roster
    
    980
    -                name = app.contacts.get_first_contact_from_jid(
    
    981
    -                    account, receiver).get_shown_name()
    
    973
    +
    
    974
    +                jid = JID.from_string(receiver)
    
    975
    +                contact = client.get_module('contacts').get_contact(jid.bare)
    
    982 976
                     filename = os.path.basename(file_props.file_name)
    
    983 977
                     if event_type == _('File Transfer Completed'):
    
    984 978
                         txt = _('You successfully sent %(filename)s to '
    
    985 979
                                 '%(name)s.') % {
    
    986 980
                                     'filename': filename,
    
    987
    -                                'name': name}
    
    981
    +                                'name': contact.name}
    
    988 982
                         icon_name = 'emblem-default'
    
    989 983
                     elif event_type == _('File Transfer Stopped'):
    
    990 984
                         txt = _('File transfer of %(filename)s to %(name)s '
    
    991 985
                                 'stopped.') % {
    
    992 986
                                     'filename': filename,
    
    993
    -                                'name': name}
    
    987
    +                                'name': contact.name}
    
    994 988
                         icon_name = 'process-stop'
    
    995 989
                     else:  # File transfer hash error
    
    996 990
                         txt = _('File transfer of %(filename)s to %(name)s '
    
    997 991
                                 'failed.') % {
    
    998 992
                                     'filename': filename,
    
    999
    -                                'name': name}
    
    993
    +                                'name': contact.name}
    
    1000 994
                         icon_name = 'process-stop'
    
    1001 995
             else:
    
    1002 996
                 txt = ''