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

Commits:

12 changed files:

Changes:

  • gajim/data/gui/contact_info.ui
    1 1
     <?xml version="1.0" encoding="UTF-8"?>
    
    2
    -<!-- Generated with glade 3.38.2 -->
    
    2
    +<!-- Generated with glade 3.40.0 -->
    
    3 3
     <interface>
    
    4 4
       <requires lib="gtk+" version="3.24"/>
    
    5 5
       <!-- n-columns=2 n-rows=6 -->
    
    ... ... @@ -725,6 +725,39 @@
    725 725
                     <property name="position">1</property>
    
    726 726
                   </packing>
    
    727 727
                 </child>
    
    728
    +            <child>
    
    729
    +              <object class="GtkScrolledWindow" id="encryption_scrolled">
    
    730
    +                <property name="visible">True</property>
    
    731
    +                <property name="can-focus">True</property>
    
    732
    +                <property name="hscrollbar-policy">never</property>
    
    733
    +                <child>
    
    734
    +                  <object class="GtkViewport">
    
    735
    +                    <property name="visible">True</property>
    
    736
    +                    <property name="can-focus">False</property>
    
    737
    +                    <child>
    
    738
    +                      <object class="GtkBox" id="encryption_box">
    
    739
    +                        <property name="visible">True</property>
    
    740
    +                        <property name="can-focus">False</property>
    
    741
    +                        <property name="margin-start">36</property>
    
    742
    +                        <property name="margin-end">36</property>
    
    743
    +                        <property name="margin-top">36</property>
    
    744
    +                        <property name="margin-bottom">36</property>
    
    745
    +                        <property name="orientation">vertical</property>
    
    746
    +                        <child>
    
    747
    +                          <placeholder/>
    
    748
    +                        </child>
    
    749
    +                      </object>
    
    750
    +                    </child>
    
    751
    +                  </object>
    
    752
    +                </child>
    
    753
    +              </object>
    
    754
    +              <packing>
    
    755
    +                <property name="name">encryption-omemo</property>
    
    756
    +                <property name="title" translatable="yes">Encryption (OMEMO)</property>
    
    757
    +                <property name="icon-name">channel-secure-symbolic</property>
    
    758
    +                <property name="position">2</property>
    
    759
    +              </packing>
    
    760
    +            </child>
    
    728 761
                 <child>
    
    729 762
                   <object class="GtkBox" id="groups_page_box">
    
    730 763
                     <property name="visible">True</property>
    
    ... ... @@ -873,7 +906,7 @@
    873 906
                     <property name="name">groups</property>
    
    874 907
                     <property name="title" translatable="yes">Groups</property>
    
    875 908
                     <property name="icon-name">view-pin-symbolic</property>
    
    876
    -                <property name="position">2</property>
    
    909
    +                <property name="position">3</property>
    
    877 910
                   </packing>
    
    878 911
                 </child>
    
    879 912
                 <child>
    
    ... ... @@ -934,7 +967,7 @@
    934 967
                     <property name="name">notes</property>
    
    935 968
                     <property name="title" translatable="yes">Notes</property>
    
    936 969
                     <property name="icon-name">x-office-address-book-symbolic</property>
    
    937
    -                <property name="position">3</property>
    
    970
    +                <property name="position">4</property>
    
    938 971
                   </packing>
    
    939 972
                 </child>
    
    940 973
                 <child>
    
    ... ... @@ -1004,7 +1037,7 @@
    1004 1037
                     <property name="name">devices</property>
    
    1005 1038
                     <property name="title" translatable="yes">Devices</property>
    
    1006 1039
                     <property name="icon-name">computer-symbolic</property>
    
    1007
    -                <property name="position">4</property>
    
    1040
    +                <property name="position">5</property>
    
    1008 1041
                   </packing>
    
    1009 1042
                 </child>
    
    1010 1043
                 <style>
    

  • gajim/data/gui/groupchat_details.ui
    1 1
     <?xml version="1.0" encoding="UTF-8"?>
    
    2
    -<!-- Generated with glade 3.38.2 -->
    
    2
    +<!-- Generated with glade 3.40.0 -->
    
    3 3
     <interface>
    
    4 4
       <requires lib="gtk+" version="3.24"/>
    
    5 5
       <!-- n-columns=2 n-rows=1 -->
    
    ... ... @@ -129,6 +129,39 @@
    129 129
                     <property name="position">1</property>
    
    130 130
                   </packing>
    
    131 131
                 </child>
    
    132
    +            <child>
    
    133
    +              <object class="GtkScrolledWindow" id="encryption_scrolled">
    
    134
    +                <property name="visible">True</property>
    
    135
    +                <property name="can-focus">True</property>
    
    136
    +                <property name="hscrollbar-policy">never</property>
    
    137
    +                <child>
    
    138
    +                  <object class="GtkViewport">
    
    139
    +                    <property name="visible">True</property>
    
    140
    +                    <property name="can-focus">False</property>
    
    141
    +                    <child>
    
    142
    +                      <object class="GtkBox" id="encryption_box">
    
    143
    +                        <property name="visible">True</property>
    
    144
    +                        <property name="can-focus">False</property>
    
    145
    +                        <property name="margin-start">36</property>
    
    146
    +                        <property name="margin-end">36</property>
    
    147
    +                        <property name="margin-top">36</property>
    
    148
    +                        <property name="margin-bottom">36</property>
    
    149
    +                        <property name="orientation">vertical</property>
    
    150
    +                        <child>
    
    151
    +                          <placeholder/>
    
    152
    +                        </child>
    
    153
    +                      </object>
    
    154
    +                    </child>
    
    155
    +                  </object>
    
    156
    +                </child>
    
    157
    +              </object>
    
    158
    +              <packing>
    
    159
    +                <property name="name">encryption-omemo</property>
    
    160
    +                <property name="title" translatable="yes">Encryption (OMEMO)</property>
    
    161
    +                <property name="icon-name">channel-secure-symbolic</property>
    
    162
    +                <property name="position">2</property>
    
    163
    +              </packing>
    
    164
    +            </child>
    
    132 165
                 <child>
    
    133 166
                   <object class="GtkBox" id="manage_box">
    
    134 167
                     <property name="visible">True</property>
    
    ... ... @@ -151,7 +184,7 @@
    151 184
                     <property name="name">manage</property>
    
    152 185
                     <property name="title" translatable="yes">Manage</property>
    
    153 186
                     <property name="icon-name">preferences-other-symbolic</property>
    
    154
    -                <property name="position">2</property>
    
    187
    +                <property name="position">3</property>
    
    155 188
                   </packing>
    
    156 189
                 </child>
    
    157 190
                 <child>
    
    ... ... @@ -170,7 +203,7 @@
    170 203
                     <property name="name">affiliations</property>
    
    171 204
                     <property name="title" translatable="yes">Affiliations</property>
    
    172 205
                     <property name="icon-name">system-users-symbolic</property>
    
    173
    -                <property name="position">3</property>
    
    206
    +                <property name="position">4</property>
    
    174 207
                   </packing>
    
    175 208
                 </child>
    
    176 209
                 <child>
    
    ... ... @@ -189,7 +222,7 @@
    189 222
                     <property name="name">outcasts</property>
    
    190 223
                     <property name="title" translatable="yes">Outcasts</property>
    
    191 224
                     <property name="icon-name">system-users-symbolic</property>
    
    192
    -                <property name="position">4</property>
    
    225
    +                <property name="position">5</property>
    
    193 226
                   </packing>
    
    194 227
                 </child>
    
    195 228
                 <child>
    
    ... ... @@ -205,7 +238,7 @@
    205 238
                     <property name="name">config</property>
    
    206 239
                     <property name="title" translatable="yes">Configuration</property>
    
    207 240
                     <property name="icon-name">document-properties-symbolic</property>
    
    208
    -                <property name="position">5</property>
    
    241
    +                <property name="position">6</property>
    
    209 242
                   </packing>
    
    210 243
                 </child>
    
    211 244
                 <style>
    

  • gajim/data/gui/omemo_trust_manager.ui
    1
    +<?xml version="1.0" encoding="UTF-8"?>
    
    2
    +<!-- Generated with glade 3.40.0 -->
    
    3
    +<interface>
    
    4
    +  <requires lib="gtk+" version="3.24"/>
    
    5
    +  <object class="GtkPopover" id="search_popover">
    
    6
    +    <property name="can-focus">False</property>
    
    7
    +    <child>
    
    8
    +      <object class="GtkSearchEntry" id="search">
    
    9
    +        <property name="visible">True</property>
    
    10
    +        <property name="can-focus">True</property>
    
    11
    +        <property name="caps-lock-warning">False</property>
    
    12
    +        <property name="primary-icon-name">edit-find-symbolic</property>
    
    13
    +        <property name="primary-icon-activatable">False</property>
    
    14
    +        <property name="primary-icon-sensitive">False</property>
    
    15
    +        <signal name="search-changed" handler="_on_search_changed" swapped="no"/>
    
    16
    +      </object>
    
    17
    +    </child>
    
    18
    +  </object>
    
    19
    +  <object class="GtkPopover" id="qr_code_popover">
    
    20
    +    <property name="can-focus">False</property>
    
    21
    +    <property name="relative-to">qr_menu_button</property>
    
    22
    +    <property name="constrain-to">none</property>
    
    23
    +    <child>
    
    24
    +      <object class="GtkBox">
    
    25
    +        <property name="visible">True</property>
    
    26
    +        <property name="can-focus">False</property>
    
    27
    +        <property name="orientation">vertical</property>
    
    28
    +        <property name="spacing">12</property>
    
    29
    +        <child>
    
    30
    +          <object class="GtkLabel">
    
    31
    +            <property name="visible">True</property>
    
    32
    +            <property name="can-focus">False</property>
    
    33
    +            <property name="label" translatable="yes">This Device</property>
    
    34
    +            <style>
    
    35
    +              <class name="bold16"/>
    
    36
    +            </style>
    
    37
    +          </object>
    
    38
    +          <packing>
    
    39
    +            <property name="expand">False</property>
    
    40
    +            <property name="fill">True</property>
    
    41
    +            <property name="position">0</property>
    
    42
    +          </packing>
    
    43
    +        </child>
    
    44
    +        <child>
    
    45
    +          <object class="GtkLabel" id="comparing_instructions">
    
    46
    +            <property name="visible">True</property>
    
    47
    +            <property name="can-focus">False</property>
    
    48
    +            <property name="margin-bottom">13</property>
    
    49
    +            <property name="wrap">True</property>
    
    50
    +            <property name="max-width-chars">42</property>
    
    51
    +            <property name="xalign">0</property>
    
    52
    +            <style>
    
    53
    +              <class name="dim-label"/>
    
    54
    +            </style>
    
    55
    +          </object>
    
    56
    +          <packing>
    
    57
    +            <property name="expand">False</property>
    
    58
    +            <property name="fill">True</property>
    
    59
    +            <property name="position">1</property>
    
    60
    +          </packing>
    
    61
    +        </child>
    
    62
    +        <child>
    
    63
    +          <object class="GtkLabel" id="our_fingerprint_2">
    
    64
    +            <property name="visible">True</property>
    
    65
    +            <property name="can-focus">False</property>
    
    66
    +            <property name="selectable">True</property>
    
    67
    +            <style>
    
    68
    +              <class name="fingerprint"/>
    
    69
    +            </style>
    
    70
    +          </object>
    
    71
    +          <packing>
    
    72
    +            <property name="expand">False</property>
    
    73
    +            <property name="fill">True</property>
    
    74
    +            <property name="position">2</property>
    
    75
    +          </packing>
    
    76
    +        </child>
    
    77
    +        <child>
    
    78
    +          <object class="GtkImage" id="qr_code_image">
    
    79
    +            <property name="visible">True</property>
    
    80
    +            <property name="can-focus">False</property>
    
    81
    +            <property name="icon_size">6</property>
    
    82
    +          </object>
    
    83
    +          <packing>
    
    84
    +            <property name="expand">False</property>
    
    85
    +            <property name="fill">True</property>
    
    86
    +            <property name="position">3</property>
    
    87
    +          </packing>
    
    88
    +        </child>
    
    89
    +        <child>
    
    90
    +          <object class="GtkLabel" id="qr_dependency_missing">
    
    91
    +            <property name="visible">True</property>
    
    92
    +            <property name="can-focus">False</property>
    
    93
    +            <property name="label" translatable="yes">For verification via QR-Code you have to install &lt;tt&gt;python-qrcode&lt;/tt&gt;.</property>
    
    94
    +            <property name="use-markup">True</property>
    
    95
    +            <property name="wrap">True</property>
    
    96
    +            <property name="max-width-chars">42</property>
    
    97
    +            <property name="xalign">0</property>
    
    98
    +            <style>
    
    99
    +              <class name="dim-label"/>
    
    100
    +            </style>
    
    101
    +          </object>
    
    102
    +          <packing>
    
    103
    +            <property name="expand">False</property>
    
    104
    +            <property name="fill">True</property>
    
    105
    +            <property name="position">4</property>
    
    106
    +          </packing>
    
    107
    +        </child>
    
    108
    +        <style>
    
    109
    +          <class name="padding-18"/>
    
    110
    +        </style>
    
    111
    +      </object>
    
    112
    +    </child>
    
    113
    +  </object>
    
    114
    +  <object class="GtkStack" id="stack">
    
    115
    +    <property name="width-request">400</property>
    
    116
    +    <property name="visible">True</property>
    
    117
    +    <property name="can-focus">False</property>
    
    118
    +    <property name="transition-type">crossfade</property>
    
    119
    +    <property name="interpolate-size">True</property>
    
    120
    +    <child>
    
    121
    +      <object class="GtkBox">
    
    122
    +        <property name="visible">True</property>
    
    123
    +        <property name="can-focus">False</property>
    
    124
    +        <property name="orientation">vertical</property>
    
    125
    +        <property name="spacing">12</property>
    
    126
    +        <child>
    
    127
    +          <object class="GtkLabel">
    
    128
    +            <property name="visible">True</property>
    
    129
    +            <property name="can-focus">False</property>
    
    130
    +            <property name="label" translatable="yes">This Device</property>
    
    131
    +            <property name="xalign">0</property>
    
    132
    +            <style>
    
    133
    +              <class name="bold"/>
    
    134
    +            </style>
    
    135
    +          </object>
    
    136
    +          <packing>
    
    137
    +            <property name="expand">False</property>
    
    138
    +            <property name="fill">True</property>
    
    139
    +            <property name="position">0</property>
    
    140
    +          </packing>
    
    141
    +        </child>
    
    142
    +        <child>
    
    143
    +          <object class="GtkListBox">
    
    144
    +            <property name="visible">True</property>
    
    145
    +            <property name="can-focus">False</property>
    
    146
    +            <property name="selection-mode">none</property>
    
    147
    +            <child>
    
    148
    +              <object class="GtkListBoxRow">
    
    149
    +                <property name="visible">True</property>
    
    150
    +                <property name="can-focus">True</property>
    
    151
    +                <property name="selectable">False</property>
    
    152
    +                <child>
    
    153
    +                  <object class="GtkBox">
    
    154
    +                    <property name="visible">True</property>
    
    155
    +                    <property name="can-focus">False</property>
    
    156
    +                    <property name="spacing">12</property>
    
    157
    +                    <child>
    
    158
    +                      <object class="GtkBox">
    
    159
    +                        <property name="visible">True</property>
    
    160
    +                        <property name="can-focus">False</property>
    
    161
    +                        <property name="orientation">vertical</property>
    
    162
    +                        <property name="spacing">3</property>
    
    163
    +                        <child>
    
    164
    +                          <object class="GtkLabel">
    
    165
    +                            <property name="visible">True</property>
    
    166
    +                            <property name="can-focus">False</property>
    
    167
    +                            <property name="label" translatable="yes">Fingerprint for this Device</property>
    
    168
    +                            <property name="xalign">0</property>
    
    169
    +                          </object>
    
    170
    +                          <packing>
    
    171
    +                            <property name="expand">False</property>
    
    172
    +                            <property name="fill">True</property>
    
    173
    +                            <property name="position">0</property>
    
    174
    +                          </packing>
    
    175
    +                        </child>
    
    176
    +                        <child>
    
    177
    +                          <object class="GtkLabel" id="our_fingerprint_1">
    
    178
    +                            <property name="visible">True</property>
    
    179
    +                            <property name="can-focus">False</property>
    
    180
    +                            <property name="selectable">True</property>
    
    181
    +                            <property name="xalign">0</property>
    
    182
    +                            <style>
    
    183
    +                              <class name="fingerprint"/>
    
    184
    +                            </style>
    
    185
    +                          </object>
    
    186
    +                          <packing>
    
    187
    +                            <property name="expand">False</property>
    
    188
    +                            <property name="fill">True</property>
    
    189
    +                            <property name="position">1</property>
    
    190
    +                          </packing>
    
    191
    +                        </child>
    
    192
    +                      </object>
    
    193
    +                      <packing>
    
    194
    +                        <property name="expand">False</property>
    
    195
    +                        <property name="fill">True</property>
    
    196
    +                        <property name="position">0</property>
    
    197
    +                      </packing>
    
    198
    +                    </child>
    
    199
    +                    <child>
    
    200
    +                      <object class="GtkMenuButton" id="qr_menu_button">
    
    201
    +                        <property name="visible">True</property>
    
    202
    +                        <property name="can-focus">True</property>
    
    203
    +                        <property name="receives-default">True</property>
    
    204
    +                        <property name="tooltip-text" translatable="yes">Scan QR Code</property>
    
    205
    +                        <property name="halign">end</property>
    
    206
    +                        <property name="valign">center</property>
    
    207
    +                        <property name="direction">up</property>
    
    208
    +                        <property name="popover">qr_code_popover</property>
    
    209
    +                        <child>
    
    210
    +                          <object class="GtkImage">
    
    211
    +                            <property name="visible">True</property>
    
    212
    +                            <property name="can-focus">False</property>
    
    213
    +                            <property name="icon-name">qr-code-scan</property>
    
    214
    +                          </object>
    
    215
    +                        </child>
    
    216
    +                        <style>
    
    217
    +                          <class name="image-button"/>
    
    218
    +                        </style>
    
    219
    +                      </object>
    
    220
    +                      <packing>
    
    221
    +                        <property name="expand">False</property>
    
    222
    +                        <property name="fill">True</property>
    
    223
    +                        <property name="pack-type">end</property>
    
    224
    +                        <property name="position">1</property>
    
    225
    +                      </packing>
    
    226
    +                    </child>
    
    227
    +                    <child>
    
    228
    +                      <object class="GtkButton" id="manage_trust_button">
    
    229
    +                        <property name="can-focus">True</property>
    
    230
    +                        <property name="receives-default">True</property>
    
    231
    +                        <property name="no-show-all">True</property>
    
    232
    +                        <property name="tooltip-text" translatable="yes">Manage your Devices…</property>
    
    233
    +                        <property name="valign">center</property>
    
    234
    +                        <signal name="clicked" handler="_on_manage_trust_clicked" swapped="no"/>
    
    235
    +                        <child>
    
    236
    +                          <object class="GtkImage">
    
    237
    +                            <property name="visible">True</property>
    
    238
    +                            <property name="can-focus">False</property>
    
    239
    +                            <property name="icon-name">channel-secure-symbolic</property>
    
    240
    +                          </object>
    
    241
    +                        </child>
    
    242
    +                        <style>
    
    243
    +                          <class name="image-button"/>
    
    244
    +                        </style>
    
    245
    +                      </object>
    
    246
    +                      <packing>
    
    247
    +                        <property name="expand">False</property>
    
    248
    +                        <property name="fill">True</property>
    
    249
    +                        <property name="pack-type">end</property>
    
    250
    +                        <property name="position">2</property>
    
    251
    +                      </packing>
    
    252
    +                    </child>
    
    253
    +                  </object>
    
    254
    +                </child>
    
    255
    +              </object>
    
    256
    +            </child>
    
    257
    +            <style>
    
    258
    +              <class name="settings-box"/>
    
    259
    +            </style>
    
    260
    +          </object>
    
    261
    +          <packing>
    
    262
    +            <property name="expand">False</property>
    
    263
    +            <property name="fill">True</property>
    
    264
    +            <property name="position">1</property>
    
    265
    +          </packing>
    
    266
    +        </child>
    
    267
    +        <child>
    
    268
    +          <object class="GtkLabel" id="list_heading">
    
    269
    +            <property name="visible">True</property>
    
    270
    +            <property name="can-focus">False</property>
    
    271
    +            <property name="margin-top">12</property>
    
    272
    +            <property name="wrap">True</property>
    
    273
    +            <property name="max-width-chars">42</property>
    
    274
    +            <property name="xalign">0</property>
    
    275
    +            <style>
    
    276
    +              <class name="bold"/>
    
    277
    +            </style>
    
    278
    +          </object>
    
    279
    +          <packing>
    
    280
    +            <property name="expand">False</property>
    
    281
    +            <property name="fill">True</property>
    
    282
    +            <property name="position">2</property>
    
    283
    +          </packing>
    
    284
    +        </child>
    
    285
    +        <child>
    
    286
    +          <object class="GtkBox" id="list_heading_box">
    
    287
    +            <property name="visible">True</property>
    
    288
    +            <property name="can-focus">False</property>
    
    289
    +            <property name="spacing">12</property>
    
    290
    +            <child>
    
    291
    +              <object class="GtkSwitch" id="show_inactive_switch">
    
    292
    +                <property name="visible">True</property>
    
    293
    +                <property name="can-focus">True</property>
    
    294
    +                <property name="valign">center</property>
    
    295
    +                <signal name="notify::active" handler="_on_show_inactive" swapped="no"/>
    
    296
    +              </object>
    
    297
    +              <packing>
    
    298
    +                <property name="expand">False</property>
    
    299
    +                <property name="fill">True</property>
    
    300
    +                <property name="pack-type">end</property>
    
    301
    +                <property name="position">0</property>
    
    302
    +              </packing>
    
    303
    +            </child>
    
    304
    +            <child>
    
    305
    +              <object class="GtkLabel">
    
    306
    +                <property name="visible">True</property>
    
    307
    +                <property name="can-focus">False</property>
    
    308
    +                <property name="label" translatable="yes">Show Inactive Devices</property>
    
    309
    +              </object>
    
    310
    +              <packing>
    
    311
    +                <property name="expand">False</property>
    
    312
    +                <property name="fill">True</property>
    
    313
    +                <property name="pack-type">end</property>
    
    314
    +                <property name="position">1</property>
    
    315
    +              </packing>
    
    316
    +            </child>
    
    317
    +            <child>
    
    318
    +              <object class="GtkMenuButton" id="search_button">
    
    319
    +                <property name="can-focus">True</property>
    
    320
    +                <property name="receives-default">True</property>
    
    321
    +                <property name="no-show-all">True</property>
    
    322
    +                <property name="tooltip-text" translatable="yes">Search…</property>
    
    323
    +                <property name="halign">start</property>
    
    324
    +                <property name="valign">center</property>
    
    325
    +                <property name="popover">search_popover</property>
    
    326
    +                <child>
    
    327
    +                  <object class="GtkImage">
    
    328
    +                    <property name="visible">True</property>
    
    329
    +                    <property name="can-focus">False</property>
    
    330
    +                    <property name="icon-name">edit-find-symbolic</property>
    
    331
    +                  </object>
    
    332
    +                </child>
    
    333
    +              </object>
    
    334
    +              <packing>
    
    335
    +                <property name="expand">False</property>
    
    336
    +                <property name="fill">True</property>
    
    337
    +                <property name="position">2</property>
    
    338
    +              </packing>
    
    339
    +            </child>
    
    340
    +          </object>
    
    341
    +          <packing>
    
    342
    +            <property name="expand">False</property>
    
    343
    +            <property name="fill">True</property>
    
    344
    +            <property name="position">3</property>
    
    345
    +          </packing>
    
    346
    +        </child>
    
    347
    +        <child>
    
    348
    +          <object class="GtkListBox" id="list">
    
    349
    +            <property name="visible">True</property>
    
    350
    +            <property name="can-focus">False</property>
    
    351
    +            <property name="selection-mode">none</property>
    
    352
    +          </object>
    
    353
    +          <packing>
    
    354
    +            <property name="expand">True</property>
    
    355
    +            <property name="fill">True</property>
    
    356
    +            <property name="position">4</property>
    
    357
    +          </packing>
    
    358
    +        </child>
    
    359
    +        <child>
    
    360
    +          <object class="GtkButton" id="clear_devices_button">
    
    361
    +            <property name="label" translatable="yes">Clear Devices…</property>
    
    362
    +            <property name="can-focus">True</property>
    
    363
    +            <property name="receives-default">True</property>
    
    364
    +            <property name="no-show-all">True</property>
    
    365
    +            <property name="halign">start</property>
    
    366
    +            <signal name="clicked" handler="_on_clear_devices_clicked" swapped="no"/>
    
    367
    +            <style>
    
    368
    +              <class name="destructive-action"/>
    
    369
    +            </style>
    
    370
    +          </object>
    
    371
    +          <packing>
    
    372
    +            <property name="expand">False</property>
    
    373
    +            <property name="fill">True</property>
    
    374
    +            <property name="position">5</property>
    
    375
    +          </packing>
    
    376
    +        </child>
    
    377
    +        <style>
    
    378
    +          <class name="omemo-trust-manager"/>
    
    379
    +        </style>
    
    380
    +      </object>
    
    381
    +      <packing>
    
    382
    +        <property name="name">manage-keys</property>
    
    383
    +      </packing>
    
    384
    +    </child>
    
    385
    +    <child>
    
    386
    +      <object class="GtkBox">
    
    387
    +        <property name="visible">True</property>
    
    388
    +        <property name="can-focus">False</property>
    
    389
    +        <property name="halign">center</property>
    
    390
    +        <property name="valign">center</property>
    
    391
    +        <property name="orientation">vertical</property>
    
    392
    +        <child>
    
    393
    +          <object class="GtkLabel">
    
    394
    +            <property name="visible">True</property>
    
    395
    +            <property name="can-focus">False</property>
    
    396
    +            <property name="label" translatable="yes">Account is not connected.</property>
    
    397
    +            <property name="wrap">True</property>
    
    398
    +            <property name="max-width-chars">42</property>
    
    399
    +            <style>
    
    400
    +              <class name="dim-label"/>
    
    401
    +            </style>
    
    402
    +          </object>
    
    403
    +          <packing>
    
    404
    +            <property name="expand">False</property>
    
    405
    +            <property name="fill">True</property>
    
    406
    +            <property name="position">0</property>
    
    407
    +          </packing>
    
    408
    +        </child>
    
    409
    +      </object>
    
    410
    +      <packing>
    
    411
    +        <property name="name">no-connection</property>
    
    412
    +        <property name="position">1</property>
    
    413
    +      </packing>
    
    414
    +    </child>
    
    415
    +  </object>
    
    416
    +</interface>

  • gajim/data/icons/hicolor/scalable/categories/qr-code-scan.svg
    1
    +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    
    2
    +<svg
    
    3
    +   width="296"
    
    4
    +   height="296"
    
    5
    +   viewBox="0 0 296 296"
    
    6
    +   version="1.1"
    
    7
    +   id="svg879"
    
    8
    +   sodipodi:docname="qr-code-scan.svg"
    
    9
    +   xml:space="preserve"
    
    10
    +   inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
    
    11
    +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    
    12
    +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    
    13
    +   xmlns="http://www.w3.org/2000/svg"
    
    14
    +   xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
    
    15
    +     id="namedview881"
    
    16
    +     pagecolor="#ffffff"
    
    17
    +     bordercolor="#666666"
    
    18
    +     borderopacity="1.0"
    
    19
    +     inkscape:showpageshadow="2"
    
    20
    +     inkscape:pageopacity="0.0"
    
    21
    +     inkscape:pagecheckerboard="0"
    
    22
    +     inkscape:deskcolor="#d1d1d1"
    
    23
    +     showgrid="false"
    
    24
    +     inkscape:zoom="1.1820013"
    
    25
    +     inkscape:cx="-30.033808"
    
    26
    +     inkscape:cy="205.58353"
    
    27
    +     inkscape:window-width="1920"
    
    28
    +     inkscape:window-height="998"
    
    29
    +     inkscape:window-x="0"
    
    30
    +     inkscape:window-y="0"
    
    31
    +     inkscape:window-maximized="1"
    
    32
    +     inkscape:current-layer="svg879" /><defs
    
    33
    +     id="defs5"><rect
    
    34
    +       id="p"
    
    35
    +       width="8"
    
    36
    +       height="8" /></defs><g
    
    37
    +     id="g3488"
    
    38
    +     transform="matrix(1.1697246,0,0,1.1697246,-27.551074,-25.261865)"><path
    
    39
    +       style="fill:#000000;stroke-width:0.476103"
    
    40
    +       d="M 251.48889,66.946387 V 46.712014 H 231.25452 211.02015 V 36.475802 26.23959 h 21.16314 c 22.41235,0 24.39388,0.156166 28.55025,2.250069 3.06508,1.544132 6.94528,5.251442 8.51507,8.135659 2.59204,4.762428 2.71271,6.062675 2.71271,29.230841 V 87.18076 h -10.23621 -10.23622 z"
    
    41
    +       id="path3464" /><path
    
    42
    +       style="fill:#000000;stroke-width:0.476103"
    
    43
    +       d="m 28.196638,66.017616 c 0,-22.412343 0.156166,-24.393876 2.250069,-28.550247 1.544132,-3.065083 5.251442,-6.945286 8.135659,-8.515074 4.762428,-2.592038 6.062675,-2.712705 29.230841,-2.712705 H 89.137808 V 36.475802 46.712014 H 68.903435 48.669062 V 66.946387 87.18076 H 38.43285 28.196638 Z"
    
    44
    +       id="path3462" /><path
    
    45
    +       style="fill:#000000;stroke-width:0.476103"
    
    46
    +       d="M 170.5514,97.175398 V 66.701287 l 30.35156,0.12255 30.35156,0.12255 0.12255,30.351559 0.12255,30.351564 H 201.02551 170.5514 Z m 40.46875,0.241574 V 87.18076 h -10.23621 -10.23622 v 9.918809 c 0,5.455351 0.14283,10.061641 0.31741,10.236211 0.17457,0.17457 4.78086,0.3174 10.23621,0.3174 h 9.91881 z"
    
    47
    +       id="path3460" /><path
    
    48
    +       style="fill:#000000;stroke-width:0.476103"
    
    49
    +       d="M 140.08082,107.41513 V 87.18076 h 9.99816 9.99816 v 20.23437 20.23438 h -9.99816 -9.99816 z"
    
    50
    +       id="path3458" /><path
    
    51
    +       style="fill:#000000;stroke-width:0.476103"
    
    52
    +       d="m 68.780885,97.297946 0.12255,-30.351559 30.351559,-0.12255 30.351556,-0.12255 V 97.175398 127.64951 H 99.132446 68.658335 Z m 40.719795,0 0.12849,-10.117186 H 99.38349 89.137808 v 10.245682 10.245678 l 10.117186,-0.12849 10.117186,-0.1285 z"
    
    53
    +       id="path3456" /><path
    
    54
    +       style="fill:#000000;stroke-width:0.476103"
    
    55
    +       d="m 68.665384,148.12193 v -9.99816 h 10.236212 10.236212 v 9.99816 9.99816 H 78.901596 68.665384 Z"
    
    56
    +       id="path3454" /><path
    
    57
    +       style="fill:#000000;stroke-width:0.476103"
    
    58
    +       d="m 139.97126,168.47533 -0.12849,-10.11719 -10.11719,-0.12849 -30.662081,-0.28013 v -9.98869 -9.98869 l 40.779271,0.15163 h 20.23437 v 20.23437 20.23437 h -9.98869 -9.98869 z"
    
    59
    +       id="path3452"
    
    60
    +       sodipodi:nodetypes="ccccccccccccc" /><path
    
    61
    +       style="fill:#000000;stroke-width:0.476103"
    
    62
    +       d="m 211.02015,193.82781 v -15.2353 h -10.23621 -10.23622 v 15.2353 15.23529 h 10.23622 10.23621 z"
    
    63
    +       id="path3474" /><path
    
    64
    +       style="fill:#000000;stroke-width:0.476103"
    
    65
    +       d="m 140.08082,209.0631 v -20.47242 h 15.23529 15.23529 v -4.99908 -4.99909 h 9.99816 9.99816 V 168.3563 158.12009 h -9.99816 -9.99816 v -9.99816 -9.99816 h 9.99816 9.99816 v 9.99816 9.99816 h 10.23622 10.23621 v -9.99816 -9.99816 h 10.23621 10.23621 v 9.99816 9.99816 h -10.23621 -10.23621 v 10.23621 10.23621 h 10.23621 10.23621 v 15.2353 15.23529 h -10.23621 -10.23621 v 10.24568 10.24568 l -10.11719,-0.12849 -10.11718,-0.1285 -0.1285,-10.11718 -0.1285,-10.11719 h -15.22582 -15.22582 v 10.23621 10.23621 h -9.99816 -9.99816 z"
    
    66
    +       id="path3450" /><path
    
    67
    +       style="fill:#000000;stroke-width:0.476103"
    
    68
    +       d="m 68.982786,229.21812 c -0.174571,-0.17457 -0.317402,-13.88633 -0.317402,-30.47058 v -30.15319 h 30.470585 30.470581 v 30.47059 30.47058 H 99.453372 c -16.584254,0 -30.296015,-0.14283 -30.470586,-0.3174 z m 40.517894,-30.27221 -0.1285,-10.11718 -10.117186,-0.1285 -10.117186,-0.12849 v 10.24568 10.24568 h 10.245682 10.24568 z"
    
    69
    +       id="path3448" /><path
    
    70
    +       style="fill:#000000;stroke-width:0.476103"
    
    71
    +       d="m 211.02015,259.76806 v -10.23621 h 20.23437 20.23437 V 229.29747 209.0631 h 10.23622 10.23621 v 21.3246 c 0,23.16817 -0.12067,24.46841 -2.71271,29.23084 -1.56979,2.88422 -5.44999,6.59153 -8.51507,8.13566 -4.15637,2.0939 -6.1379,2.25007 -28.55025,2.25007 h -21.16314 z"
    
    72
    +       id="path3446" /><path
    
    73
    +       style="fill:#000000;stroke-width:0.476103"
    
    74
    +       d="m 43.551277,269.26862 c -1.374571,-0.37014 -3.39419,-1.12956 -4.488044,-1.6876 -2.760243,-1.40817 -6.658458,-5.21488 -8.15389,-7.96248 -2.592038,-4.76243 -2.712705,-6.06267 -2.712705,-29.23084 V 209.0631 H 38.43285 48.669062 v 20.23437 20.23438 h 20.234373 20.234373 v 10.23621 10.23621 l -21.543656,-0.0313 c -16.989841,-0.0247 -22.071931,-0.17358 -24.042875,-0.70431 z"
    
    75
    +       id="path3346" /></g></svg>

  • gajim/data/style/gajim.css
    ... ... @@ -577,7 +577,7 @@ list.settings > row:not(:last-child) {
    577 577
     }
    
    578 578
     list.settings row { min-height: 52px; }
    
    579 579
     list.settings > row > box {
    
    580
    -    margin: 6px 18px;
    
    580
    +    margin: 10px 20px 10px 10px;
    
    581 581
     }
    
    582 582
     
    
    583 583
     /* Settings Window */
    
    ... ... @@ -625,7 +625,7 @@ #SendFileDialog grid list > row { padding: 10px 20px 10px 10px; }
    625 625
     /* Accounts Window */
    
    626 626
     .accounts-menu { border-right: 1px solid; border-color: @borders;}
    
    627 627
     .accounts-settings-border {border: 1px solid; border-color: @borders;}
    
    628
    -.accounts-settings stack > box { margin:30px; }
    
    628
    +.accounts-settings stack > box.settings-page { margin:30px; }
    
    629 629
     .accounts-label-row {
    
    630 630
         padding-top: 10px;
    
    631 631
         padding-bottom: 10px;
    
    ... ... @@ -766,6 +766,8 @@ .large-header {
    766 766
         font-size: 1.75rem;
    
    767 767
         font-weight: bold;
    
    768 768
     }
    
    769
    +.monospace { font-family: monospace; }
    
    770
    +
    
    769 771
     .insensitive-fg-color {color: @insensitive_fg_color;}
    
    770 772
     
    
    771 773
     /* Padding/Margins */
    
    ... ... @@ -799,6 +801,28 @@ .selected-color { color: @theme_selected_bg_color; }
    799 801
     .encrypted-color { color: rgb(75, 181, 67); }
    
    800 802
     .unencrypted-color { color: @error_color; }
    
    801 803
     
    
    804
    +/* OMEMO */
    
    805
    +.fingerprint {
    
    806
    +    color: @insensitive_fg_color;
    
    807
    +    font-family: monospace;
    
    808
    +    font-size: 90%;
    
    809
    +}
    
    810
    +.omemo-inactive-color { color: @insensitive_fg_color; }
    
    811
    +
    
    812
    +.omemo-trust-manager list row {
    
    813
    +    border-bottom: 1px solid;
    
    814
    +    border-color: @unfocused_borders;
    
    815
    +    padding: 10px 20px 10px 10px;
    
    816
    +}
    
    817
    +.omemo-trust-manager list row:last-child { border-bottom: 0px}
    
    818
    +.omemo-trust-manager list {
    
    819
    +    border: 1px solid;
    
    820
    +    border-color:@unfocused_borders;
    
    821
    +}
    
    822
    +.omemo-trust-manager list > row { outline: none; }
    
    823
    +
    
    824
    +.omemo-trust-popover row { padding: 10px 15px 10px 10px; }
    
    825
    +
    
    802 826
     /*Receipts*/
    
    803 827
     .receipt-received-color { color: rgb(75, 181, 67); }
    
    804 828
     
    

  • gajim/gtk/accounts.py
    ... ... @@ -40,6 +40,7 @@
    40 40
     from .const import Setting
    
    41 41
     from .const import SettingKind
    
    42 42
     from .const import SettingType
    
    43
    +from .omemo_trust_manager import OMEMOTrustManager
    
    43 44
     from .settings import SettingsDialog
    
    44 45
     from .settings import SettingsBox
    
    45 46
     from .settings import PopoverSetting
    
    ... ... @@ -317,6 +318,7 @@ def __init__(self, account: str) -> None:
    317 318
             self.add(BackMenuItem())
    
    318 319
             self.add(PageMenuItem('general', _('General')))
    
    319 320
             self.add(PageMenuItem('privacy', _('Privacy')))
    
    321
    +        self.add(PageMenuItem('encryption', _('Encryption (OMEMO)')))
    
    320 322
             self.add(PageMenuItem('connection', _('Connection')))
    
    321 323
             self.add(PageMenuItem('advanced', _('Advanced')))
    
    322 324
             self.add(RemoveMenuItem())
    
    ... ... @@ -411,6 +413,8 @@ def __init__(self, name: str, label: str) -> None:
    411 413
                 icon = 'preferences-system-symbolic'
    
    412 414
             elif name == 'privacy':
    
    413 415
                 icon = 'preferences-system-privacy-symbolic'
    
    416
    +        elif name == 'encryption':
    
    417
    +            icon = 'channel-secure-symbolic'
    
    414 418
             elif name == 'connection':
    
    415 419
                 icon = 'preferences-system-network-symbolic'
    
    416 420
             elif name == 'advanced':
    
    ... ... @@ -436,8 +440,9 @@ def __init__(self,
    436 440
             self._settings = settings
    
    437 441
     
    
    438 442
             self._settings.add_page(GeneralPage(account))
    
    439
    -        self._settings.add_page(ConnectionPage(account))
    
    440 443
             self._settings.add_page(PrivacyPage(account))
    
    444
    +        self._settings.add_page(EncryptionPage(account))
    
    445
    +        self._settings.add_page(ConnectionPage(account))
    
    441 446
             self._settings.add_page(AdvancedPage(account))
    
    442 447
     
    
    443 448
             self._account_row = AccountRow(account)
    
    ... ... @@ -616,6 +621,7 @@ def __init__(self, account: str, settings: list[Setting]) -> None:
    616 621
             Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
    
    617 622
             self.set_valign(Gtk.Align.START)
    
    618 623
             self.set_vexpand(True)
    
    624
    +        self.get_style_context().add_class('settings-page')
    
    619 625
             self.account = account
    
    620 626
     
    
    621 627
             self.listbox = SettingsBox(account)
    
    ... ... @@ -891,6 +897,40 @@ def _reset_send_read_marker(self, button: Gtk.Button) -> None:
    891 897
                 'send_marker', None, context='private')
    
    892 898
     
    
    893 899
     
    
    900
    +class EncryptionPage(GenericSettingPage):
    
    901
    +
    
    902
    +    name = 'encryption'
    
    903
    +
    
    904
    +    def __init__(self, account: str) -> None:
    
    905
    +        settings = [
    
    906
    +            Setting(SettingKind.SWITCH,
    
    907
    +                    _('Blind Trust'),
    
    908
    +                    SettingType.ACCOUNT_CONFIG,
    
    909
    +                    'omemo_blind_trust',
    
    910
    +                    desc=_('Blindly trust new devices until you verify them'))
    
    911
    +        ]
    
    912
    +        GenericSettingPage.__init__(self, account, settings)
    
    913
    +
    
    914
    +        heading = Gtk.Label(label=_('Trust Management'))
    
    915
    +        heading.get_style_context().add_class('bold')
    
    916
    +        heading.set_xalign(0)
    
    917
    +        self.add(heading)
    
    918
    +
    
    919
    +        btbv_label = Gtk.Label()
    
    920
    +        btbv_label.set_xalign(0)
    
    921
    +        markup = '<a href="%s">%s</a>' % (
    
    922
    +            'https://dev.gajim.org/gajim/gajim-plugins/-/'
    
    923
    +            'wikis/omemogajimplugin',
    
    924
    +            _('Read more about blind trust'))
    
    925
    +        btbv_label.set_markup(markup)
    
    926
    +        self.add(btbv_label)
    
    927
    +
    
    928
    +        omemo_trust_manager = OMEMOTrustManager(account)
    
    929
    +        omemo_trust_manager.set_margin_top(18)
    
    930
    +        self.pack_end(omemo_trust_manager, True, True, 0)
    
    931
    +        self.reorder_child(omemo_trust_manager, 0)
    
    932
    +
    
    933
    +
    
    894 934
     class ConnectionPage(GenericSettingPage):
    
    895 935
     
    
    896 936
         name = 'connection'
    

  • gajim/gtk/builder.pyi
    ... ... @@ -283,6 +283,8 @@ class ContactInfoBuilder(Builder):
    283 283
         to_subscription_button: Gtk.Button
    
    284 284
         contact_settings_box: Gtk.Box
    
    285 285
         remove_history_button: Gtk.Button
    
    286
    +    encryption_scrolled: Gtk.ScrolledWindow
    
    287
    +    encryption_box: Gtk.Box
    
    286 288
         groups_page_box: Gtk.Box
    
    287 289
         groups_treeview: Gtk.TreeView
    
    288 290
         tree_selection: Gtk.TreeSelection
    
    ... ... @@ -412,6 +414,8 @@ class GroupchatDetailsBuilder(Builder):
    412 414
         main_stack: Gtk.Stack
    
    413 415
         info_box: Gtk.Box
    
    414 416
         settings_box: Gtk.Box
    
    417
    +    encryption_scrolled: Gtk.ScrolledWindow
    
    418
    +    encryption_box: Gtk.Box
    
    415 419
         manage_box: Gtk.Box
    
    416 420
         affiliation_box: Gtk.Box
    
    417 421
         outcasts_box: Gtk.Box
    
    ... ... @@ -589,6 +593,26 @@ class MessageActionsBoxBuilder(Builder):
    589 593
         input_scrolled: Gtk.ScrolledWindow
    
    590 594
     
    
    591 595
     
    
    596
    +class OmemoTrustManagerBuilder(Builder):
    
    597
    +    search_popover: Gtk.Popover
    
    598
    +    search: Gtk.SearchEntry
    
    599
    +    qr_code_popover: Gtk.Popover
    
    600
    +    comparing_instructions: Gtk.Label
    
    601
    +    our_fingerprint_2: Gtk.Label
    
    602
    +    qr_code_image: Gtk.Image
    
    603
    +    qr_dependency_missing: Gtk.Label
    
    604
    +    stack: Gtk.Stack
    
    605
    +    our_fingerprint_1: Gtk.Label
    
    606
    +    qr_menu_button: Gtk.MenuButton
    
    607
    +    manage_trust_button: Gtk.Button
    
    608
    +    list_heading: Gtk.Label
    
    609
    +    list_heading_box: Gtk.Box
    
    610
    +    show_inactive_switch: Gtk.Switch
    
    611
    +    search_button: Gtk.MenuButton
    
    612
    +    list: Gtk.ListBox
    
    613
    +    clear_devices_button: Gtk.Button
    
    614
    +
    
    615
    +
    
    592 616
     class PasswordDialogBuilder(Builder):
    
    593 617
         pass_box: Gtk.Box
    
    594 618
         header: Gtk.Label
    
    ... ... @@ -649,6 +673,7 @@ class PreferencesBuilder(Builder):
    649 673
         stack: Gtk.Stack
    
    650 674
         window_behaviour: Gtk.Grid
    
    651 675
         plugins: Gtk.Grid
    
    676
    +    general: Gtk.Grid
    
    652 677
         chats: Gtk.Grid
    
    653 678
         group_chats: Gtk.Grid
    
    654 679
         file_preview: Gtk.Grid
    
    ... ... @@ -1016,6 +1041,8 @@ def get_builder(file_name: Literal['manage_sounds.ui'], widgets: list[str] = ...
    1016 1041
     @overload
    
    1017 1042
     def get_builder(file_name: Literal['message_actions_box.ui'], widgets: list[str] = ...) -> MessageActionsBoxBuilder: ...
    
    1018 1043
     @overload
    
    1044
    +def get_builder(file_name: Literal['omemo_trust_manager.ui'], widgets: list[str] = ...) -> OmemoTrustManagerBuilder: ...
    
    1045
    +@overload
    
    1019 1046
     def get_builder(file_name: Literal['password_dialog.ui'], widgets: list[str] = ...) -> PasswordDialogBuilder: ...
    
    1020 1047
     @overload
    
    1021 1048
     def get_builder(file_name: Literal['pep_config.ui'], widgets: list[str] = ...) -> PepConfigBuilder: ...
    

  • gajim/gtk/chat_stack.py
    ... ... @@ -718,7 +718,7 @@ def _on_send_message(self) -> None:
    718 718
             assert contact is not None
    
    719 719
     
    
    720 720
             encryption = contact.settings.get('encryption')
    
    721
    -        if encryption:
    
    721
    +        if encryption and encryption != 'OMEMO':
    
    722 722
                 if encryption not in app.plugin_manager.encryption_plugins:
    
    723 723
                     ErrorDialog(_('Encryption error'),
    
    724 724
                                 _('Missing necessary encryption plugin'))
    

  • gajim/gtk/contact_info.py
    ... ... @@ -49,6 +49,7 @@
    49 49
     from .contact_settings import ContactSettings
    
    50 50
     from .dialogs import ConfirmationDialog
    
    51 51
     from .dialogs import DialogButton
    
    52
    +from .omemo_trust_manager import OMEMOTrustManager
    
    52 53
     from .sidebar_switcher import SideBarSwitcher
    
    53 54
     from .builder import get_builder
    
    54 55
     from .util import connect_destroy
    
    ... ... @@ -109,6 +110,7 @@ def __init__(self,
    109 110
     
    
    110 111
             if isinstance(self.contact, BareContact):
    
    111 112
                 self._fill_settings_page(self.contact)
    
    113
    +            self._fill_encryption_page(self.contact)
    
    112 114
                 self._fill_device_info(self.contact)
    
    113 115
                 self._fill_groups_page(self.contact)
    
    114 116
                 self._fill_note_page(self.contact)
    
    ... ... @@ -120,7 +122,6 @@ def __init__(self,
    120 122
             self.connect('key-press-event', self._on_key_press)
    
    121 123
             self.connect('destroy', self._on_destroy)
    
    122 124
     
    
    123
    -        # pylint: disable=line-too-long
    
    124 125
             connect_destroy(self._ui.tree_selection,
    
    125 126
                             'changed', self._on_group_selection_changed)
    
    126 127
             connect_destroy(self._ui.toggle_renderer,
    
    ... ... @@ -134,7 +135,6 @@ def __init__(self,
    134 135
                 ('unsubscribed-presence-received', ged.GUI1,
    
    135 136
                  self._on_unsubscribed_presence_received),
    
    136 137
             ])
    
    137
    -        # pylint: enable=line-too-long
    
    138 138
     
    
    139 139
             self.add(self._ui.main_grid)
    
    140 140
             self.show_all()
    
    ... ... @@ -185,6 +185,11 @@ def _fill_information_page(self, contact: ContactT) -> None:
    185 185
     
    
    186 186
             self._switcher.set_row_visible('information', True)
    
    187 187
     
    
    188
    +    def _fill_encryption_page(self, contact: ContactT) -> None:
    
    189
    +        self._ui.encryption_box.add(
    
    190
    +            OMEMOTrustManager(self.contact.account, self.contact))
    
    191
    +        self._switcher.set_row_visible('encryption-omemo', True)
    
    192
    +
    
    188 193
         def _fill_note_page(self, contact: BareContact) -> None:
    
    189 194
             if not contact.is_in_roster:
    
    190 195
                 return
    

  • gajim/gtk/groupchat_details.py
    ... ... @@ -34,6 +34,7 @@
    34 34
     from .groupchat_config import GroupchatConfig
    
    35 35
     from .groupchat_manage import GroupchatManage
    
    36 36
     from .groupchat_settings import GroupChatSettings
    
    37
    +from .omemo_trust_manager import OMEMOTrustManager
    
    37 38
     from .sidebar_switcher import SideBarSwitcher
    
    38 39
     from .structs import RemoveHistoryActionParams
    
    39 40
     
    
    ... ... @@ -71,6 +72,7 @@ def __init__(self,
    71 72
     
    
    72 73
             self._add_groupchat_info()
    
    73 74
             self._add_groupchat_settings()
    
    75
    +        self._add_groupchat_encryption()
    
    74 76
             self._add_groupchat_manage()
    
    75 77
             self._add_affiliations()
    
    76 78
             self._add_outcasts()
    
    ... ... @@ -173,6 +175,10 @@ def _add_groupchat_settings(self) -> None:
    173 175
     
    
    174 176
             self._ui.settings_box.add(scrolled_window)
    
    175 177
     
    
    178
    +    def _add_groupchat_encryption(self) -> None:
    
    179
    +        self._ui.encryption_box.add(
    
    180
    +            OMEMOTrustManager(self._contact.account, self._contact))
    
    181
    +
    
    176 182
         def _add_affiliations(self) -> None:
    
    177 183
             affiliations = GroupchatAffiliation(self._client, self._contact)
    
    178 184
             self._ui.affiliation_box.add(affiliations)
    

  • gajim/gtk/message_actions_box.py
    ... ... @@ -42,6 +42,7 @@
    42 42
     from gajim.common.client import Client
    
    43 43
     from gajim.common.const import SimpleClientState
    
    44 44
     from gajim.common.const import Direction
    
    45
    +from gajim.common.modules.contacts import BareContact
    
    45 46
     from gajim.common.modules.contacts import GroupchatContact
    
    46 47
     from gajim.common.modules.contacts import GroupchatParticipant
    
    47 48
     from gajim.common.types import ChatContactT
    
    ... ... @@ -59,7 +60,7 @@
    59 60
     from .menus import get_self_contact_menu
    
    60 61
     from .menus import get_singlechat_menu
    
    61 62
     from .message_input import MessageInputTextView
    
    62
    -
    
    63
    +from .util import open_window
    
    63 64
     
    
    64 65
     log = logging.getLogger('gajim.gui.messageactionsbox')
    
    65 66
     
    
    ... ... @@ -297,7 +298,7 @@ def _change_encryption(self,
    297 298
             if current_state == new_state:
    
    298 299
                 return
    
    299 300
     
    
    300
    -        if new_state:
    
    301
    +        if new_state and new_state != 'OMEMO':
    
    301 302
                 plugin = app.plugin_manager.encryption_plugins.get(new_state)
    
    302 303
                 if plugin is None:
    
    303 304
                     # TODO: Add GUI error here
    
    ... ... @@ -353,6 +354,9 @@ def _update_encryption_details_button(self) -> None:
    353 354
                     app.window.get_control(),
    
    354 355
                     encryption_state)
    
    355 356
     
    
    357
    +        if state == 'OMEMO':
    
    358
    +            encryption_state['authenticated'] = True
    
    359
    +
    
    356 360
             visible, enc_type, authenticated = encryption_state.values()
    
    357 361
             assert isinstance(visible, bool)
    
    358 362
     
    
    ... ... @@ -378,6 +382,24 @@ def _update_encryption_details_button(self) -> None:
    378 382
         def _on_encryption_details_clicked(self, _button: Gtk.Button) -> None:
    
    379 383
             contact = self.get_current_contact()
    
    380 384
             encryption = contact.settings.get('encryption')
    
    385
    +        if encryption == 'OMEMO':
    
    386
    +            if contact.is_groupchat:
    
    387
    +                open_window('GroupchatDetails',
    
    388
    +                            contact=contact,
    
    389
    +                            page='encryption-omemo')
    
    390
    +                return
    
    391
    +
    
    392
    +            if isinstance(contact, BareContact) and contact.is_self:
    
    393
    +                window = open_window('AccountsWindow')
    
    394
    +                window.select_account(contact.account)
    
    395
    +                return
    
    396
    +
    
    397
    +            open_window('ContactInfo',
    
    398
    +                        account=contact.account,
    
    399
    +                        contact=contact,
    
    400
    +                        page='encryption-omemo')
    
    401
    +            return
    
    402
    +
    
    381 403
             app.plugin_manager.extension_point(
    
    382 404
                 f'encryption_dialog{encryption}', app.window.get_control())
    
    383 405
     
    

  • gajim/gtk/omemo_trust_manager.py
    1
    +# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of Gajim.
    
    4
    +#
    
    5
    +# OMEMO Gajim Plugin is free software; you can redistribute it and/or modify
    
    6
    +# it under the terms of the GNU General Public License as published
    
    7
    +# by the Free Software Foundation; version 3 only.
    
    8
    +#
    
    9
    +# OMEMO Gajim Plugin is distributed in the hope that it will be useful,
    
    10
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    11
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    12
    +# GNU General Public License for more details.
    
    13
    +#
    
    14
    +# You should have received a copy of the GNU General Public License
    
    15
    +# along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    16
    +
    
    17
    +from __future__ import annotations
    
    18
    +
    
    19
    +from typing import Any
    
    20
    +from typing import cast
    
    21
    +from typing import Optional
    
    22
    +from typing import Union
    
    23
    +
    
    24
    +import os
    
    25
    +import time
    
    26
    +import locale
    
    27
    +import logging
    
    28
    +import tempfile
    
    29
    +from packaging.version import Version as V
    
    30
    +
    
    31
    +from pkg_resources import get_distribution
    
    32
    +
    
    33
    +from axolotl.identitykeypair import IdentityKeyPair  # pyright: ignore
    
    34
    +
    
    35
    +from nbxmpp.protocol import JID
    
    36
    +
    
    37
    +from gi.repository import Gtk
    
    38
    +from gi.repository import GdkPixbuf
    
    39
    +
    
    40
    +from gajim.common import app
    
    41
    +from gajim.common import ged
    
    42
    +from gajim.common import types
    
    43
    +from gajim.common.events import AccountConnected
    
    44
    +from gajim.common.events import AccountDisconnected
    
    45
    +from gajim.common.i18n import _
    
    46
    +from gajim.common.ged import EventHelper
    
    47
    +from gajim.common.modules.contacts import BareContact
    
    48
    +from gajim.common.modules.omemobackend.util import Trust
    
    49
    +from gajim.common.modules.omemobackend.util import IdentityKeyExtended
    
    50
    +from gajim.common.modules.omemobackend.util import get_fingerprint
    
    51
    +
    
    52
    +from .builder import get_builder
    
    53
    +from .dialogs import ConfirmationDialog
    
    54
    +from .dialogs import DialogButton
    
    55
    +from .util import open_window
    
    56
    +
    
    57
    +log = logging.getLogger('gajim.gui.omemo_trust_dialog')
    
    58
    +
    
    59
    +
    
    60
    +TRUST_DATA = {
    
    61
    +    Trust.UNTRUSTED: ('dialog-error-symbolic',
    
    62
    +                      _('Untrusted'),
    
    63
    +                      'error-color'),
    
    64
    +    Trust.UNDECIDED: ('security-low-symbolic',
    
    65
    +                      _('Not Decided'),
    
    66
    +                      'warning-color'),
    
    67
    +    Trust.VERIFIED: ('security-high-symbolic',
    
    68
    +                     _('Verified'),
    
    69
    +                     'encrypted-color'),
    
    70
    +    Trust.BLIND: ('security-medium-symbolic',
    
    71
    +                  _('Blind Trust'),
    
    72
    +                  'encrypted-color')
    
    73
    +}
    
    74
    +
    
    75
    +
    
    76
    +class OMEMOTrustManager(Gtk.Box, EventHelper):
    
    77
    +    def __init__(self,
    
    78
    +                 account: str,
    
    79
    +                 contact: Optional[types.ChatContactT] = None
    
    80
    +                 ) -> None:
    
    81
    +
    
    82
    +        Gtk.Box.__init__(self)
    
    83
    +        EventHelper.__init__(self)
    
    84
    +
    
    85
    +        self._account = account
    
    86
    +        self._contact = contact
    
    87
    +
    
    88
    +        self._ui = get_builder('omemo_trust_manager.ui')
    
    89
    +        self.add(self._ui.stack)
    
    90
    +
    
    91
    +        self._ui.list.set_filter_func(self._filter_func, None)
    
    92
    +        self._ui.list.set_sort_func(self._sort_func, None)
    
    93
    +
    
    94
    +        self._ui.connect_signals(self)
    
    95
    +
    
    96
    +        self.connect('destroy', self._on_destroy)
    
    97
    +        self.show_all()
    
    98
    +
    
    99
    +        self.register_events([
    
    100
    +            ('account-connected', ged.GUI2, self._on_account_state),
    
    101
    +            ('account-disconnected', ged.GUI2, self._on_account_state)
    
    102
    +        ])
    
    103
    +
    
    104
    +        if not app.account_is_connected(account):
    
    105
    +            self._ui.stack.set_visible_child_name('no-connection')
    
    106
    +            return
    
    107
    +
    
    108
    +        self._update()
    
    109
    +
    
    110
    +    def _update(self) -> None:
    
    111
    +        client = app.get_client(self._account)
    
    112
    +        if self._contact is None:
    
    113
    +            self._contact = client.get_module('Contacts').get_contact(
    
    114
    +                client.get_own_jid().bare)
    
    115
    +
    
    116
    +        if isinstance(self._contact, BareContact) and self._contact.is_self:
    
    117
    +            header_text = _('Other devices connected with your account')
    
    118
    +            popover_qr_text = _('Compare this code with the one shown on your '
    
    119
    +                                'contact’s screen to ensure the safety of '
    
    120
    +                                'your end-to-end encrypted chat.')
    
    121
    +        else:
    
    122
    +            header_text = _('Devices connected with %s') % self._contact.name
    
    123
    +            popover_qr_text = _('Compare this code with the one shown on your '
    
    124
    +                                'contact’s screen to ensure the safety of '
    
    125
    +                                'your end-to-end encrypted chat '
    
    126
    +                                'with %s.') % self._contact.name
    
    127
    +
    
    128
    +        self._ui.list_heading.set_text(header_text)
    
    129
    +        self._ui.comparing_instructions.set_text(popover_qr_text)
    
    130
    +
    
    131
    +        self._omemo = client.get_module('OMEMO')
    
    132
    +
    
    133
    +        self._identity_key = cast(
    
    134
    +            IdentityKeyPair, self._omemo.backend.storage.getIdentityKeyPair())
    
    135
    +        our_fpr_formatted = get_fingerprint(self._identity_key, formatted=True)
    
    136
    +        self._ui.our_fingerprint_1.set_text(our_fpr_formatted)
    
    137
    +        self._ui.our_fingerprint_2.set_text(our_fpr_formatted)
    
    138
    +
    
    139
    +        self.update()
    
    140
    +        self._load_qrcode()
    
    141
    +
    
    142
    +    def update(self) -> None:
    
    143
    +        assert self._contact is not None
    
    144
    +        self._ui.list.foreach(self._ui.list.remove)
    
    145
    +
    
    146
    +        if isinstance(self._contact, BareContact) and self._contact.is_self:
    
    147
    +            self._ui.clear_devices_button.show()
    
    148
    +            self._ui.list_heading_box.set_halign(Gtk.Align.START)
    
    149
    +        else:
    
    150
    +            self._ui.manage_trust_button.show()
    
    151
    +            if self._contact.is_groupchat:
    
    152
    +                self._ui.search_button.show()
    
    153
    +
    
    154
    +        self._load_fingerprints(self._contact)
    
    155
    +
    
    156
    +    def _on_destroy(self, *args: Any) -> None:
    
    157
    +        app.check_finalize(self)
    
    158
    +
    
    159
    +    def _on_account_state(self,
    
    160
    +                          event: Union[AccountConnected, AccountDisconnected]
    
    161
    +                          ) -> None:
    
    162
    +
    
    163
    +        if isinstance(event, AccountConnected):
    
    164
    +            self._update()
    
    165
    +            self._ui.stack.set_visible_child_name('manage-keys')
    
    166
    +        else:
    
    167
    +            self._ui.stack.set_visible_child_name('no-connection')
    
    168
    +
    
    169
    +    def _filter_func(self, row: KeyRow, _user_data: Any) -> bool:
    
    170
    +        search_text = self._ui.search.get_text()
    
    171
    +        if search_text and search_text.lower() not in str(row.jid):
    
    172
    +            return False
    
    173
    +        if self._ui.show_inactive_switch.get_active():
    
    174
    +            return True
    
    175
    +        return row.active
    
    176
    +
    
    177
    +    @staticmethod
    
    178
    +    def _sort_func(row1: KeyRow, row2: KeyRow, _user_data: Any) -> int:
    
    179
    +        result = locale.strcoll(str(row1.jid), str(row2.jid))
    
    180
    +        if result != 0:
    
    181
    +            return result
    
    182
    +
    
    183
    +        if row1.active != row2.active:
    
    184
    +            return -1 if row1.active else 1
    
    185
    +
    
    186
    +        if row1.trust != row2.trust:
    
    187
    +            return -1 if row1.trust > row2.trust else 1
    
    188
    +        return 0
    
    189
    +
    
    190
    +    def _on_search_changed(self, _entry: Gtk.SearchEntry) -> None:
    
    191
    +        self._ui.list.invalidate_filter()
    
    192
    +
    
    193
    +    def _load_fingerprints(self, contact: types.ChatContactT) -> None:
    
    194
    +        if contact.is_groupchat:
    
    195
    +            members = list(self._omemo.backend.get_muc_members(
    
    196
    +                str(contact.jid)))
    
    197
    +            sessions = self._omemo.backend.storage.getSessionsFromJids(members)
    
    198
    +            results = self._omemo.backend.storage.getMucFingerprints(members)
    
    199
    +        else:
    
    200
    +            sessions = self._omemo.backend.storage.getSessionsFromJid(
    
    201
    +                str(contact.jid))
    
    202
    +            results = self._omemo.backend.storage.getFingerprints(
    
    203
    +                str(contact.jid))
    
    204
    +
    
    205
    +        rows: dict[IdentityKeyExtended, KeyRow] = {}
    
    206
    +        for result in results:
    
    207
    +            rows[result.public_key] = KeyRow(contact,
    
    208
    +                                             result.recipient_id,
    
    209
    +                                             result.public_key,
    
    210
    +                                             result.trust,
    
    211
    +                                             result.timestamp)
    
    212
    +
    
    213
    +        for item in sessions:
    
    214
    +            if item.record.isFresh():
    
    215
    +                return
    
    216
    +            identity_key = item.record.getSessionState().getRemoteIdentityKey()
    
    217
    +            identity_key = IdentityKeyExtended(identity_key.getPublicKey())
    
    218
    +            try:
    
    219
    +                key_row = rows[identity_key]
    
    220
    +            except KeyError:
    
    221
    +                log.warning('Could not find session identitykey %s',
    
    222
    +                            item.device_id)
    
    223
    +                self._omemo.backend.storage.deleteSession(item.recipient_id,
    
    224
    +                                                          item.device_id)
    
    225
    +                continue
    
    226
    +
    
    227
    +            key_row.active = item.active
    
    228
    +            key_row.device_id = item.device_id
    
    229
    +
    
    230
    +        for row in rows.values():
    
    231
    +            self._ui.list.add(row)
    
    232
    +
    
    233
    +    @staticmethod
    
    234
    +    def _get_qrcode(jid: str, sid: int, identity_key: IdentityKeyPair) -> str:
    
    235
    +        fingerprint = get_fingerprint(identity_key)
    
    236
    +        path = os.path.join(tempfile.gettempdir(),
    
    237
    +                            'omemo_{}.png'.format(jid))
    
    238
    +
    
    239
    +        ver_string = 'xmpp:{}?omemo-sid-{}={}'.format(jid, sid, fingerprint)
    
    240
    +        log.debug('Verification String: %s', ver_string)
    
    241
    +
    
    242
    +        import qrcode
    
    243
    +        qr = qrcode.QRCode(version=None,
    
    244
    +                           error_correction=qrcode.constants.ERROR_CORRECT_L,
    
    245
    +                           box_size=6,
    
    246
    +                           border=4)
    
    247
    +        qr.add_data(ver_string)
    
    248
    +        qr.make(fit=True)
    
    249
    +
    
    250
    +        fill_color = 'black'
    
    251
    +        back_color = 'white'
    
    252
    +        if V(get_distribution('qrcode').version) < V('6.0'):
    
    253
    +            # meaning of fill_color and back_color were switched
    
    254
    +            # before this commit in qrcode between versions 5.3
    
    255
    +            # and 6.0: https://github.com/lincolnloop/python-qrcode/
    
    256
    +            # commit/01f440d64b7d1f61bb75161ce118b86eca85b15c
    
    257
    +            back_color, fill_color = fill_color, back_color
    
    258
    +
    
    259
    +        img = qr.make_image(fill_color=fill_color, back_color=back_color)
    
    260
    +        img.save(path)
    
    261
    +        return path
    
    262
    +
    
    263
    +    def _load_qrcode(self) -> None:
    
    264
    +        try:
    
    265
    +            client = app.get_client(self._account)
    
    266
    +            path = self._get_qrcode(client.get_own_jid().bare,
    
    267
    +                                    self._omemo.backend.own_device,
    
    268
    +                                    self._identity_key)
    
    269
    +        except ImportError:
    
    270
    +            log.exception('Failed to generate QR code')
    
    271
    +            self._ui.qr_code_image.hide()
    
    272
    +            self._ui.qr_dependency_missing.show()
    
    273
    +        else:
    
    274
    +            pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
    
    275
    +            self._ui.qr_code_image.set_from_pixbuf(pixbuf)
    
    276
    +            self._ui.qr_code_image.show()
    
    277
    +            self._ui.qr_dependency_missing.hide()
    
    278
    +
    
    279
    +    def _on_show_inactive(self, switch: Gtk.Switch, _param: Any) -> None:
    
    280
    +        self._ui.list.invalidate_filter()
    
    281
    +
    
    282
    +    def _on_clear_devices_clicked(self, _button: Gtk.Button) -> None:
    
    283
    +        def _clear():
    
    284
    +            self._omemo.clear_devicelist()
    
    285
    +
    
    286
    +        window = self.get_window()
    
    287
    +        assert isinstance(window, Gtk.Window)
    
    288
    +
    
    289
    +        ConfirmationDialog(
    
    290
    +            _('Clear Devices'),
    
    291
    +            _('Clear Devices Now?'),
    
    292
    +            _('This will clear the devices store for your account.'),
    
    293
    +            [DialogButton.make('Cancel'),
    
    294
    +             DialogButton.make('Accept',
    
    295
    +                               text=_('_Clear Devices'),
    
    296
    +                               callback=_clear)],
    
    297
    +            transient_for=window).show()
    
    298
    +
    
    299
    +    def _on_manage_trust_clicked(self, _button: Gtk.Button) -> None:
    
    300
    +        assert self._contact is not None
    
    301
    +        window = open_window('AccountsWindow')
    
    302
    +        window.select_account(self._contact.account)
    
    303
    +
    
    304
    +
    
    305
    +class KeyRow(Gtk.ListBoxRow):
    
    306
    +    def __init__(self,
    
    307
    +                 contact: types.ChatContactT,
    
    308
    +                 jid: JID,
    
    309
    +                 identity_key: IdentityKeyExtended,
    
    310
    +                 trust: Trust,
    
    311
    +                 last_seen: Optional[float]
    
    312
    +                 ) -> None:
    
    313
    +
    
    314
    +        Gtk.ListBoxRow.__init__(self)
    
    315
    +        self.set_activatable(False)
    
    316
    +
    
    317
    +        self._active = False
    
    318
    +        self._device_id: Optional[int] = None
    
    319
    +        self._identity_key = identity_key
    
    320
    +        self.trust = trust
    
    321
    +        self.jid = jid
    
    322
    +
    
    323
    +        client = app.get_client(contact.account)
    
    324
    +        self._omemo = client.get_module('OMEMO')
    
    325
    +
    
    326
    +        grid = Gtk.Grid()
    
    327
    +        grid.set_column_spacing(12)
    
    328
    +
    
    329
    +        self._trust_button = TrustButton(self)
    
    330
    +        grid.attach(self._trust_button, 1, 1, 1, 3)
    
    331
    +
    
    332
    +        self.fingerprint = Gtk.Label(
    
    333
    +            label=self._identity_key.get_fingerprint(formatted=True))
    
    334
    +        self.fingerprint.get_style_context().add_class('omemo-mono')
    
    335
    +        self.fingerprint.get_style_context().add_class('omemo-inactive-color')
    
    336
    +        self.fingerprint.set_selectable(True)
    
    337
    +        self.fingerprint.set_halign(Gtk.Align.START)
    
    338
    +        self.fingerprint.set_valign(Gtk.Align.START)
    
    339
    +        self.fingerprint.set_hexpand(True)
    
    340
    +        grid.attach(self.fingerprint, 2, 1, 1, 1)
    
    341
    +
    
    342
    +        if last_seen is not None:
    
    343
    +            last_seen_str = time.strftime('%d-%m-%Y %H:%M:%S',
    
    344
    +                                          time.localtime(last_seen))
    
    345
    +        else:
    
    346
    +            last_seen_str = _('Never')
    
    347
    +        last_seen_label = Gtk.Label(label=_('Last seen: %s') % last_seen_str)
    
    348
    +        last_seen_label.set_halign(Gtk.Align.START)
    
    349
    +        last_seen_label.set_valign(Gtk.Align.START)
    
    350
    +        last_seen_label.set_hexpand(True)
    
    351
    +        last_seen_label.get_style_context().add_class('small-label')
    
    352
    +        last_seen_label.get_style_context().add_class('dim-label')
    
    353
    +        grid.attach(last_seen_label, 2, 3, 1, 1)
    
    354
    +
    
    355
    +        self.add(grid)
    
    356
    +        self.show_all()
    
    357
    +
    
    358
    +    def delete_fingerprint(self, *args: Any) -> None:
    
    359
    +        listbox = cast(Gtk.ListBox, self.get_parent())
    
    360
    +        window = cast(Gtk.Window, listbox.get_window())
    
    361
    +
    
    362
    +        def _remove():
    
    363
    +            self._omemo.backend.remove_device(str(self.jid), self.device_id)
    
    364
    +            self._omemo.backend.storage.deleteSession(
    
    365
    +                str(self.jid), self.device_id)
    
    366
    +            self._omemo.backend.storage.deleteIdentity(
    
    367
    +                str(self.jid), self._identity_key)
    
    368
    +
    
    369
    +            listbox.remove(self)
    
    370
    +            self.destroy()
    
    371
    +
    
    372
    +        ConfirmationDialog(
    
    373
    +            _('Delete'),
    
    374
    +            _('Delete Fingerprint'),
    
    375
    +            _('Doing so will permanently delete this Fingerprint'),
    
    376
    +            [DialogButton.make('Cancel'),
    
    377
    +             DialogButton.make('Remove',
    
    378
    +                               text=_('Delete'),
    
    379
    +                               callback=_remove)],
    
    380
    +            transient_for=window).show()
    
    381
    +
    
    382
    +    def set_trust(self) -> None:
    
    383
    +        icon_name, tooltip, css_class = TRUST_DATA[self.trust]
    
    384
    +        image = cast(Gtk.Image, self._trust_button.get_child())
    
    385
    +        image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
    
    386
    +        image.get_style_context().add_class(css_class)
    
    387
    +        image.set_tooltip_text(tooltip)
    
    388
    +
    
    389
    +        self._omemo.backend.storage.setTrust(
    
    390
    +            str(self.jid), self._identity_key, self.trust)
    
    391
    +
    
    392
    +    @property
    
    393
    +    def active(self) -> bool:
    
    394
    +        return self._active
    
    395
    +
    
    396
    +    @active.setter
    
    397
    +    def active(self, active: bool) -> None:
    
    398
    +        context = self.fingerprint.get_style_context()
    
    399
    +        self._active = bool(active)
    
    400
    +        if self._active:
    
    401
    +            context.remove_class('omemo-inactive-color')
    
    402
    +        else:
    
    403
    +            context.add_class('omemo-inactive-color')
    
    404
    +        self._trust_button.update()
    
    405
    +
    
    406
    +    @property
    
    407
    +    def device_id(self) -> Optional[int]:
    
    408
    +        return self._device_id
    
    409
    +
    
    410
    +    @device_id.setter
    
    411
    +    def device_id(self, device_id: int) -> None:
    
    412
    +        self._device_id = device_id
    
    413
    +
    
    414
    +
    
    415
    +class TrustButton(Gtk.MenuButton):
    
    416
    +    def __init__(self, row: KeyRow) -> None:
    
    417
    +        Gtk.MenuButton.__init__(self)
    
    418
    +        self._row = row
    
    419
    +        self._css_class = ''
    
    420
    +        self.set_popover(TrustPopver(row))
    
    421
    +        self.set_valign(Gtk.Align.CENTER)
    
    422
    +        self.update()
    
    423
    +
    
    424
    +    def update(self) -> None:
    
    425
    +        icon_name, tooltip, css_class = TRUST_DATA[self._row.trust]
    
    426
    +        image = cast(Gtk.Image, self.get_child())
    
    427
    +        image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
    
    428
    +        image.get_style_context().remove_class(self._css_class)
    
    429
    +
    
    430
    +        if not self._row.active:
    
    431
    +            css_class = 'omemo-inactive-color'
    
    432
    +            tooltip = '%s - %s' % (_('Inactive'), tooltip)
    
    433
    +
    
    434
    +        image.get_style_context().add_class(css_class)
    
    435
    +        self._css_class = css_class
    
    436
    +        self.set_tooltip_text(tooltip)
    
    437
    +
    
    438
    +
    
    439
    +class TrustPopver(Gtk.Popover):
    
    440
    +    def __init__(self, row: KeyRow) -> None:
    
    441
    +        Gtk.Popover.__init__(self)
    
    442
    +        self._row = row
    
    443
    +        self._listbox = Gtk.ListBox()
    
    444
    +        self._listbox.set_selection_mode(Gtk.SelectionMode.NONE)
    
    445
    +        self.update()
    
    446
    +        self.add(self._listbox)
    
    447
    +        self._listbox.show_all()
    
    448
    +        self._listbox.connect('row-activated', self._activated)
    
    449
    +        self.get_style_context().add_class('omemo-trust-popover')
    
    450
    +
    
    451
    +    def _activated(self, _listbox: Gtk.ListBox, row: MenuOption) -> None:
    
    452
    +        self.popdown()
    
    453
    +        if row.type_ is None:
    
    454
    +            self._row.delete_fingerprint()
    
    455
    +        else:
    
    456
    +            self._row.trust = row.type_
    
    457
    +            self._row.set_trust()
    
    458
    +            trust_button = cast(TrustButton, self.get_relative_to())
    
    459
    +            trust_button.update()
    
    460
    +            self.update()
    
    461
    +
    
    462
    +    def update(self) -> None:
    
    463
    +        self._listbox.foreach(self._listbox.remove)
    
    464
    +        if self._row.trust != Trust.VERIFIED:
    
    465
    +            self._listbox.add(VerifiedOption())
    
    466
    +        if self._row.trust != Trust.BLIND:
    
    467
    +            self._listbox.add(BlindOption())
    
    468
    +        if self._row.trust != Trust.UNTRUSTED:
    
    469
    +            self._listbox.add(NotTrustedOption())
    
    470
    +        self._listbox.add(DeleteOption())
    
    471
    +
    
    472
    +
    
    473
    +class MenuOption(Gtk.ListBoxRow):
    
    474
    +    def __init__(self,
    
    475
    +                 icon: str,
    
    476
    +                 label_text: str,
    
    477
    +                 color: str,
    
    478
    +                 type_: Optional[Trust] = None
    
    479
    +                 ) -> None:
    
    480
    +
    
    481
    +        Gtk.ListBoxRow.__init__(self)
    
    482
    +
    
    483
    +        self.type_ = type_
    
    484
    +        self.icon = icon
    
    485
    +        self.label = label_text
    
    486
    +        self.color = color
    
    487
    +
    
    488
    +        box = Gtk.Box()
    
    489
    +        box.set_spacing(6)
    
    490
    +
    
    491
    +        image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
    
    492
    +        label = Gtk.Label(label=label_text)
    
    493
    +        image.get_style_context().add_class(color)
    
    494
    +
    
    495
    +        box.add(image)
    
    496
    +        box.add(label)
    
    497
    +        self.add(box)
    
    498
    +        self.show_all()
    
    499
    +
    
    500
    +
    
    501
    +class BlindOption(MenuOption):
    
    502
    +    def __init__(self) -> None:
    
    503
    +        MenuOption.__init__(self,
    
    504
    +                            'security-medium-symbolic',
    
    505
    +                            _('Blind Trust'),
    
    506
    +                            'encrypted-color',
    
    507
    +                            Trust.BLIND)
    
    508
    +
    
    509
    +
    
    510
    +class VerifiedOption(MenuOption):
    
    511
    +    def __init__(self) -> None:
    
    512
    +        MenuOption.__init__(self,
    
    513
    +                            'security-high-symbolic',
    
    514
    +                            _('Verified'),
    
    515
    +                            'encrypted-color',
    
    516
    +                            Trust.VERIFIED)
    
    517
    +
    
    518
    +
    
    519
    +class NotTrustedOption(MenuOption):
    
    520
    +    def __init__(self) -> None:
    
    521
    +        MenuOption.__init__(self,
    
    522
    +                            'dialog-error-symbolic',
    
    523
    +                            _('Untrusted'),
    
    524
    +                            'error-color',
    
    525
    +                            Trust.UNTRUSTED)
    
    526
    +
    
    527
    +
    
    528
    +class DeleteOption(MenuOption):
    
    529
    +    def __init__(self) -> None:
    
    530
    +        MenuOption.__init__(self,
    
    531
    +                            'user-trash-symbolic',
    
    532
    +                            _('Delete'),
    
    533
    +                            '')