Philipp Hörist pushed to branch master at gajim / gajim

Commits:

7 changed files:

Changes:

  • gajim/app_actions.py
    ... ... @@ -31,6 +31,7 @@ from gajim.gtk.server_info import ServerInfoDialog
    31 31
     from gajim.gtk.mam_preferences import MamPreferences
    
    32 32
     from gajim.gtk.preferences import Preferences
    
    33 33
     from gajim.gtk.join_groupchat import JoinGroupchatWindow
    
    34
    +from gajim.gtk.groupchat_creation import CreateGroupchatWindow
    
    34 35
     from gajim.gtk.start_chat import StartChatDialog
    
    35 36
     from gajim.gtk.add_contact import AddNewContactWindow
    
    36 37
     from gajim.gtk.single_message import SingleMessageWindow
    
    ... ... @@ -151,6 +152,14 @@ def on_join_gc(_action, param):
    151 152
             window.present()
    
    152 153
     
    
    153 154
     
    
    155
    +def on_create_gc(_action, param):
    
    156
    +    window = app.get_app_window(CreateGroupchatWindow)
    
    157
    +    if window is None:
    
    158
    +        CreateGroupchatWindow(param.get_string() or None)
    
    159
    +    else:
    
    160
    +        window.present()
    
    161
    +
    
    162
    +
    
    154 163
     def on_add_contact(_action, param):
    
    155 164
         account, jid = param.get_strv()
    
    156 165
         if not jid:
    

  • gajim/application.py
    ... ... @@ -424,6 +424,10 @@ class GajimApplication(Gtk.Application):
    424 424
             act.connect("activate", app_actions.open_link)
    
    425 425
             self.add_action(act)
    
    426 426
     
    
    427
    +        act = Gio.SimpleAction.new('create-groupchat', GLib.VariantType.new('s'))
    
    428
    +        act.connect("activate", app_actions.on_create_gc)
    
    429
    +        self.add_action(act)
    
    430
    +
    
    427 431
             act = Gio.SimpleAction.new('browse-history',
    
    428 432
                                        GLib.VariantType.new('a{sv}'))
    
    429 433
             act.connect("activate", app_actions.on_browse_history)
    

  • gajim/common/const.py
    ... ... @@ -2,6 +2,7 @@ from enum import IntEnum, Enum, unique
    2 2
     from collections import namedtuple
    
    3 3
     
    
    4 4
     from gajim.common.i18n import _
    
    5
    +from gajim.common.i18n import Q_
    
    5 6
     
    
    6 7
     EncryptionData = namedtuple('EncryptionData', 'additional_data')
    
    7 8
     EncryptionData.__new__.__defaults__ = (None,)  # type: ignore
    
    ... ... @@ -209,6 +210,25 @@ class MUCJoinedState(Enum):
    209 210
             return self.name
    
    210 211
     
    
    211 212
     
    
    213
    +MUC_CREATION_EXAMPLES = [
    
    214
    +    (Q_('?Group chat name:Team'),
    
    215
    +     Q_('?Group chat description:Project discussion'),
    
    216
    +     Q_('?Group chat address:team')),
    
    217
    +    (Q_('?Group chat name:Family'),
    
    218
    +     Q_('?Group chat description:Spring gathering'),
    
    219
    +     Q_('?Group chat address:family')),
    
    220
    +    (Q_('?Group chat name:Vacation'),
    
    221
    +     Q_('?Group chat description:Trip planning'),
    
    222
    +     Q_('?Group chat address:vacation')),
    
    223
    +    (Q_('?Group chat name:Repairs'),
    
    224
    +     Q_('?Group chat description:Local help group'),
    
    225
    +     Q_('?Group chat address:repairs')),
    
    226
    +    (Q_('?Group chat name:News'),
    
    227
    +     Q_('?Group chat description:Local news and reports'),
    
    228
    +     Q_('?Group chat address:news')),
    
    229
    +]
    
    230
    +
    
    231
    +
    
    212 232
     EME_MESSAGES = {
    
    213 233
         'urn:xmpp:otr:0':
    
    214 234
             _('This message was encrypted with OTR '
    

  • gajim/data/gui/application_menu.ui
    ... ... @@ -77,6 +77,12 @@
    77 77
             <attribute name="action">app.start-chat</attribute>
    
    78 78
             <attribute name="accel">&lt;Primary&gt;N</attribute>
    
    79 79
           </item>
    
    80
    +      <item>
    
    81
    +        <attribute name="label" translatable="yes">Create Group Chat</attribute>
    
    82
    +        <attribute name="action">app.create-groupchat</attribute>
    
    83
    +        <attribute name="accel">&lt;Primary&gt;G</attribute>
    
    84
    +        <attribute name="target"></attribute>
    
    85
    +      </item>
    
    80 86
           <item>
    
    81 87
             <attribute name="label" translatable="yes">Bookmarks</attribute>
    
    82 88
             <attribute name="action">app.bookmarks</attribute>
    

  • gajim/data/gui/groupchat_creation.ui
    1
    +<?xml version="1.0" encoding="UTF-8"?>
    
    2
    +<!-- Generated with glade 3.22.1 -->
    
    3
    +<interface>
    
    4
    +  <requires lib="gtk+" version="3.20"/>
    
    5
    +  <object class="GtkListStore" id="account_liststore">
    
    6
    +    <columns>
    
    7
    +      <!-- column-name id -->
    
    8
    +      <column type="gchararray"/>
    
    9
    +      <!-- column-name label -->
    
    10
    +      <column type="gchararray"/>
    
    11
    +    </columns>
    
    12
    +  </object>
    
    13
    +  <object class="GtkPopover" id="public_private_popover">
    
    14
    +    <property name="can_focus">False</property>
    
    15
    +    <child>
    
    16
    +      <object class="GtkBox">
    
    17
    +        <property name="visible">True</property>
    
    18
    +        <property name="can_focus">False</property>
    
    19
    +        <property name="halign">center</property>
    
    20
    +        <property name="border_width">12</property>
    
    21
    +        <property name="orientation">vertical</property>
    
    22
    +        <property name="spacing">6</property>
    
    23
    +        <child>
    
    24
    +          <object class="GtkLabel">
    
    25
    +            <property name="visible">True</property>
    
    26
    +            <property name="can_focus">False</property>
    
    27
    +            <property name="label" translatable="yes">Public or Private?</property>
    
    28
    +            <style>
    
    29
    +              <class name="bold16"/>
    
    30
    +            </style>
    
    31
    +          </object>
    
    32
    +          <packing>
    
    33
    +            <property name="expand">False</property>
    
    34
    +            <property name="fill">True</property>
    
    35
    +            <property name="position">0</property>
    
    36
    +          </packing>
    
    37
    +        </child>
    
    38
    +        <child>
    
    39
    +          <object class="GtkLabel">
    
    40
    +            <property name="visible">True</property>
    
    41
    +            <property name="can_focus">False</property>
    
    42
    +            <property name="label" translatable="yes">&lt;b&gt;Private:&lt;/b&gt; If you plan to chat encrypted, you would typically want to create a private group chat. You have to invite people so they can join.</property>
    
    43
    +            <property name="use_markup">True</property>
    
    44
    +            <property name="wrap">True</property>
    
    45
    +            <property name="width_chars">40</property>
    
    46
    +            <property name="max_width_chars">40</property>
    
    47
    +          </object>
    
    48
    +          <packing>
    
    49
    +            <property name="expand">False</property>
    
    50
    +            <property name="fill">True</property>
    
    51
    +            <property name="position">1</property>
    
    52
    +          </packing>
    
    53
    +        </child>
    
    54
    +        <child>
    
    55
    +          <object class="GtkLabel">
    
    56
    +            <property name="visible">True</property>
    
    57
    +            <property name="can_focus">False</property>
    
    58
    +            <property name="label" translatable="yes">&lt;b&gt;Public:&lt;/b&gt; Anyone who knows the address can join.</property>
    
    59
    +            <property name="use_markup">True</property>
    
    60
    +          </object>
    
    61
    +          <packing>
    
    62
    +            <property name="expand">False</property>
    
    63
    +            <property name="fill">True</property>
    
    64
    +            <property name="position">2</property>
    
    65
    +          </packing>
    
    66
    +        </child>
    
    67
    +      </object>
    
    68
    +    </child>
    
    69
    +  </object>
    
    70
    +  <object class="GtkBox" id="create_group_chat">
    
    71
    +    <property name="width_request">400</property>
    
    72
    +    <property name="visible">True</property>
    
    73
    +    <property name="can_focus">False</property>
    
    74
    +    <property name="border_width">18</property>
    
    75
    +    <property name="orientation">vertical</property>
    
    76
    +    <property name="spacing">18</property>
    
    77
    +    <child>
    
    78
    +      <object class="GtkLabel">
    
    79
    +        <property name="visible">True</property>
    
    80
    +        <property name="can_focus">False</property>
    
    81
    +        <property name="label" translatable="yes">Create Group Chat</property>
    
    82
    +        <property name="wrap">True</property>
    
    83
    +        <style>
    
    84
    +          <class name="large-header"/>
    
    85
    +        </style>
    
    86
    +      </object>
    
    87
    +      <packing>
    
    88
    +        <property name="expand">False</property>
    
    89
    +        <property name="fill">True</property>
    
    90
    +        <property name="position">0</property>
    
    91
    +      </packing>
    
    92
    +    </child>
    
    93
    +    <child>
    
    94
    +      <object class="GtkGrid" id="grid">
    
    95
    +        <property name="visible">True</property>
    
    96
    +        <property name="can_focus">False</property>
    
    97
    +        <property name="margin_top">12</property>
    
    98
    +        <property name="row_spacing">6</property>
    
    99
    +        <property name="column_spacing">12</property>
    
    100
    +        <child>
    
    101
    +          <object class="GtkLabel">
    
    102
    +            <property name="visible">True</property>
    
    103
    +            <property name="can_focus">False</property>
    
    104
    +            <property name="halign">end</property>
    
    105
    +            <property name="label" translatable="yes">_Name</property>
    
    106
    +            <property name="use_underline">True</property>
    
    107
    +            <property name="mnemonic_widget">name_entry</property>
    
    108
    +            <style>
    
    109
    +              <class name="dim-label"/>
    
    110
    +            </style>
    
    111
    +          </object>
    
    112
    +          <packing>
    
    113
    +            <property name="left_attach">0</property>
    
    114
    +            <property name="top_attach">1</property>
    
    115
    +          </packing>
    
    116
    +        </child>
    
    117
    +        <child>
    
    118
    +          <object class="GtkLabel">
    
    119
    +            <property name="visible">True</property>
    
    120
    +            <property name="can_focus">False</property>
    
    121
    +            <property name="halign">end</property>
    
    122
    +            <property name="label" translatable="yes">_Description</property>
    
    123
    +            <property name="use_underline">True</property>
    
    124
    +            <property name="mnemonic_widget">description_entry</property>
    
    125
    +            <style>
    
    126
    +              <class name="dim-label"/>
    
    127
    +            </style>
    
    128
    +          </object>
    
    129
    +          <packing>
    
    130
    +            <property name="left_attach">0</property>
    
    131
    +            <property name="top_attach">2</property>
    
    132
    +          </packing>
    
    133
    +        </child>
    
    134
    +        <child>
    
    135
    +          <object class="GtkEntry" id="name_entry">
    
    136
    +            <property name="width_request">200</property>
    
    137
    +            <property name="visible">True</property>
    
    138
    +            <property name="can_focus">True</property>
    
    139
    +            <property name="hexpand">True</property>
    
    140
    +          </object>
    
    141
    +          <packing>
    
    142
    +            <property name="left_attach">1</property>
    
    143
    +            <property name="top_attach">1</property>
    
    144
    +          </packing>
    
    145
    +        </child>
    
    146
    +        <child>
    
    147
    +          <object class="GtkEntry" id="description_entry">
    
    148
    +            <property name="visible">True</property>
    
    149
    +            <property name="can_focus">True</property>
    
    150
    +            <property name="show_emoji_icon">True</property>
    
    151
    +          </object>
    
    152
    +          <packing>
    
    153
    +            <property name="left_attach">1</property>
    
    154
    +            <property name="top_attach">2</property>
    
    155
    +          </packing>
    
    156
    +        </child>
    
    157
    +        <child>
    
    158
    +          <object class="GtkLabel">
    
    159
    +            <property name="visible">True</property>
    
    160
    +            <property name="can_focus">False</property>
    
    161
    +            <property name="halign">end</property>
    
    162
    +            <property name="valign">center</property>
    
    163
    +            <property name="margin_top">12</property>
    
    164
    +            <property name="label" translatable="yes">_Public</property>
    
    165
    +            <property name="use_underline">True</property>
    
    166
    +            <property name="mnemonic_widget">public_switch</property>
    
    167
    +            <style>
    
    168
    +              <class name="dim-label"/>
    
    169
    +            </style>
    
    170
    +          </object>
    
    171
    +          <packing>
    
    172
    +            <property name="left_attach">0</property>
    
    173
    +            <property name="top_attach">3</property>
    
    174
    +          </packing>
    
    175
    +        </child>
    
    176
    +        <child>
    
    177
    +          <object class="GtkBox">
    
    178
    +            <property name="visible">True</property>
    
    179
    +            <property name="can_focus">False</property>
    
    180
    +            <property name="margin_top">12</property>
    
    181
    +            <property name="spacing">12</property>
    
    182
    +            <child>
    
    183
    +              <object class="GtkSwitch" id="public_switch">
    
    184
    +                <property name="visible">True</property>
    
    185
    +                <property name="can_focus">True</property>
    
    186
    +                <property name="valign">center</property>
    
    187
    +              </object>
    
    188
    +              <packing>
    
    189
    +                <property name="expand">False</property>
    
    190
    +                <property name="fill">True</property>
    
    191
    +                <property name="position">0</property>
    
    192
    +              </packing>
    
    193
    +            </child>
    
    194
    +            <child>
    
    195
    +              <object class="GtkMenuButton">
    
    196
    +                <property name="visible">True</property>
    
    197
    +                <property name="can_focus">True</property>
    
    198
    +                <property name="receives_default">True</property>
    
    199
    +                <property name="halign">end</property>
    
    200
    +                <property name="relief">none</property>
    
    201
    +                <property name="popover">public_private_popover</property>
    
    202
    +                <child>
    
    203
    +                  <object class="GtkImage">
    
    204
    +                    <property name="visible">True</property>
    
    205
    +                    <property name="can_focus">False</property>
    
    206
    +                    <property name="icon_name">dialog-information-symbolic</property>
    
    207
    +                  </object>
    
    208
    +                </child>
    
    209
    +              </object>
    
    210
    +              <packing>
    
    211
    +                <property name="expand">False</property>
    
    212
    +                <property name="fill">True</property>
    
    213
    +                <property name="position">1</property>
    
    214
    +              </packing>
    
    215
    +            </child>
    
    216
    +          </object>
    
    217
    +          <packing>
    
    218
    +            <property name="left_attach">1</property>
    
    219
    +            <property name="top_attach">3</property>
    
    220
    +          </packing>
    
    221
    +        </child>
    
    222
    +        <child>
    
    223
    +          <object class="GtkLabel">
    
    224
    +            <property name="visible">True</property>
    
    225
    +            <property name="can_focus">False</property>
    
    226
    +            <property name="halign">end</property>
    
    227
    +            <property name="label" translatable="yes">_Address</property>
    
    228
    +            <property name="use_underline">True</property>
    
    229
    +            <property name="mnemonic_widget">address_entry</property>
    
    230
    +            <style>
    
    231
    +              <class name="dim-label"/>
    
    232
    +            </style>
    
    233
    +          </object>
    
    234
    +          <packing>
    
    235
    +            <property name="left_attach">0</property>
    
    236
    +            <property name="top_attach">4</property>
    
    237
    +          </packing>
    
    238
    +        </child>
    
    239
    +        <child>
    
    240
    +          <object class="GtkEntry" id="address_entry">
    
    241
    +            <property name="visible">True</property>
    
    242
    +            <property name="can_focus">True</property>
    
    243
    +            <signal name="activate" handler="_on_address_entry_activate" swapped="no"/>
    
    244
    +            <signal name="changed" handler="_on_address_entry_changed" swapped="no"/>
    
    245
    +          </object>
    
    246
    +          <packing>
    
    247
    +            <property name="left_attach">1</property>
    
    248
    +            <property name="top_attach">4</property>
    
    249
    +          </packing>
    
    250
    +        </child>
    
    251
    +        <child>
    
    252
    +          <object class="GtkComboBox" id="account_combo">
    
    253
    +            <property name="visible">True</property>
    
    254
    +            <property name="can_focus">False</property>
    
    255
    +            <property name="no_show_all">True</property>
    
    256
    +            <property name="valign">center</property>
    
    257
    +            <property name="model">account_liststore</property>
    
    258
    +            <property name="id_column">0</property>
    
    259
    +            <signal name="changed" handler="_on_account_combo_changed" swapped="no"/>
    
    260
    +            <child>
    
    261
    +              <object class="GtkCellRendererText"/>
    
    262
    +              <attributes>
    
    263
    +                <attribute name="text">1</attribute>
    
    264
    +              </attributes>
    
    265
    +            </child>
    
    266
    +          </object>
    
    267
    +          <packing>
    
    268
    +            <property name="left_attach">1</property>
    
    269
    +            <property name="top_attach">0</property>
    
    270
    +          </packing>
    
    271
    +        </child>
    
    272
    +        <child>
    
    273
    +          <object class="GtkLabel" id="account_label">
    
    274
    +            <property name="visible">True</property>
    
    275
    +            <property name="can_focus">False</property>
    
    276
    +            <property name="no_show_all">True</property>
    
    277
    +            <property name="halign">end</property>
    
    278
    +            <property name="label" translatable="yes">_Account</property>
    
    279
    +            <property name="use_underline">True</property>
    
    280
    +            <property name="mnemonic_widget">account_combo</property>
    
    281
    +            <style>
    
    282
    +              <class name="dim-label"/>
    
    283
    +            </style>
    
    284
    +          </object>
    
    285
    +          <packing>
    
    286
    +            <property name="left_attach">0</property>
    
    287
    +            <property name="top_attach">0</property>
    
    288
    +          </packing>
    
    289
    +        </child>
    
    290
    +      </object>
    
    291
    +      <packing>
    
    292
    +        <property name="expand">False</property>
    
    293
    +        <property name="fill">True</property>
    
    294
    +        <property name="position">1</property>
    
    295
    +      </packing>
    
    296
    +    </child>
    
    297
    +    <child>
    
    298
    +      <object class="GtkBox">
    
    299
    +        <property name="visible">True</property>
    
    300
    +        <property name="can_focus">False</property>
    
    301
    +        <property name="halign">end</property>
    
    302
    +        <property name="margin_top">12</property>
    
    303
    +        <property name="spacing">12</property>
    
    304
    +        <child>
    
    305
    +          <object class="GtkSpinner" id="spinner">
    
    306
    +            <property name="visible">True</property>
    
    307
    +            <property name="can_focus">False</property>
    
    308
    +          </object>
    
    309
    +          <packing>
    
    310
    +            <property name="expand">False</property>
    
    311
    +            <property name="fill">True</property>
    
    312
    +            <property name="position">0</property>
    
    313
    +          </packing>
    
    314
    +        </child>
    
    315
    +        <child>
    
    316
    +          <object class="GtkButton" id="create_button">
    
    317
    +            <property name="label" translatable="yes">_Create</property>
    
    318
    +            <property name="visible">True</property>
    
    319
    +            <property name="sensitive">False</property>
    
    320
    +            <property name="can_focus">True</property>
    
    321
    +            <property name="receives_default">True</property>
    
    322
    +            <property name="halign">end</property>
    
    323
    +            <property name="use_underline">True</property>
    
    324
    +            <signal name="clicked" handler="_on_create_clicked" swapped="no"/>
    
    325
    +            <style>
    
    326
    +              <class name="suggested-action"/>
    
    327
    +            </style>
    
    328
    +          </object>
    
    329
    +          <packing>
    
    330
    +            <property name="expand">False</property>
    
    331
    +            <property name="fill">True</property>
    
    332
    +            <property name="position">1</property>
    
    333
    +          </packing>
    
    334
    +        </child>
    
    335
    +      </object>
    
    336
    +      <packing>
    
    337
    +        <property name="expand">False</property>
    
    338
    +        <property name="fill">True</property>
    
    339
    +        <property name="pack_type">end</property>
    
    340
    +        <property name="position">3</property>
    
    341
    +      </packing>
    
    342
    +    </child>
    
    343
    +  </object>
    
    344
    +</interface>

  • gajim/gtk/groupchat_creation.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 logging
    
    16
    +import random
    
    17
    +
    
    18
    +from gi.repository import Gtk
    
    19
    +from gi.repository import Gdk
    
    20
    +
    
    21
    +from nbxmpp.protocol import InvalidJid
    
    22
    +from nbxmpp.protocol import JID
    
    23
    +from nbxmpp.util import is_error_result
    
    24
    +
    
    25
    +from gajim.common import app
    
    26
    +from gajim.common.const import MUC_CREATION_EXAMPLES
    
    27
    +from gajim.common.i18n import _
    
    28
    +
    
    29
    +from gajim.gtk.dialogs import ErrorDialog
    
    30
    +from gajim.gtk.util import get_builder
    
    31
    +from gajim.gtk.util import ensure_not_destroyed
    
    32
    +
    
    33
    +log = logging.getLogger('gajim.gtk.groupchat_creation')
    
    34
    +
    
    35
    +
    
    36
    +class CreateGroupchatWindow(Gtk.ApplicationWindow):
    
    37
    +    def __init__(self, account):
    
    38
    +        Gtk.ApplicationWindow.__init__(self)
    
    39
    +        self.set_name('CreateGroupchat')
    
    40
    +        self.set_application(app.app)
    
    41
    +        self.set_position(Gtk.WindowPosition.CENTER)
    
    42
    +        self.set_show_menubar(False)
    
    43
    +        self.set_resizable(True)
    
    44
    +        self.set_title(_('Create Group Chat'))
    
    45
    +
    
    46
    +        self._ui = get_builder('groupchat_creation.ui')
    
    47
    +        self.add(self._ui.create_group_chat)
    
    48
    +
    
    49
    +        self._destroyed = False
    
    50
    +
    
    51
    +        not_muc_error = _('Address does not belong to a group chat server')
    
    52
    +        self._error_messages = {
    
    53
    +            'remote-server-not-found': _('Remote server not found'),
    
    54
    +            'remote-server-timeout': _('Remote server timeout'),
    
    55
    +            'service-unavailable': not_muc_error,
    
    56
    +            'subscription-required': not_muc_error,
    
    57
    +            'not-muc-service': not_muc_error,
    
    58
    +            'already-exists': _('Group chat already exists')
    
    59
    +        }
    
    60
    +
    
    61
    +        self._account = self._fill_account_combo(account)
    
    62
    +
    
    63
    +        self._create_entry_completion()
    
    64
    +        self._fill_placeholders()
    
    65
    +
    
    66
    +        self._ui.connect_signals(self)
    
    67
    +        self.connect('key-press-event', self._on_key_press_event)
    
    68
    +        self.connect('destroy', self._on_destroy)
    
    69
    +
    
    70
    +        self.show_all()
    
    71
    +        self.set_focus(self._ui.address_entry)
    
    72
    +
    
    73
    +    def _fill_account_combo(self, account):
    
    74
    +        accounts = app.get_enabled_accounts_with_labels()
    
    75
    +        account_liststore = self._ui.account_combo.get_model()
    
    76
    +        for acc in accounts:
    
    77
    +            account_liststore.append(acc)
    
    78
    +
    
    79
    +        # Hide account combobox if there is only one account
    
    80
    +        if len(accounts) == 1:
    
    81
    +            self._ui.account_combo.hide()
    
    82
    +            self._ui.account_label.hide()
    
    83
    +
    
    84
    +        if account is None:
    
    85
    +            account = accounts[0][0]
    
    86
    +
    
    87
    +        self._ui.account_combo.set_active_id(account)
    
    88
    +        return account
    
    89
    +
    
    90
    +    def _create_entry_completion(self):
    
    91
    +        entry_completion = Gtk.EntryCompletion()
    
    92
    +        model = Gtk.ListStore(str)
    
    93
    +        entry_completion.set_model(model)
    
    94
    +        entry_completion.set_text_column(0)
    
    95
    +
    
    96
    +        entry_completion.set_inline_completion(True)
    
    97
    +        entry_completion.set_popup_single_match(False)
    
    98
    +
    
    99
    +        self._ui.address_entry.set_completion(entry_completion)
    
    100
    +
    
    101
    +    def _fill_placeholders(self):
    
    102
    +        placeholder = random.choice(MUC_CREATION_EXAMPLES)
    
    103
    +        server = app.get_muc_domain(self._account) or 'muc.example.com'
    
    104
    +
    
    105
    +        self._ui.name_entry.set_placeholder_text(
    
    106
    +            placeholder[0] + _(' (optional)...'))
    
    107
    +        self._ui.description_entry.set_placeholder_text(
    
    108
    +            placeholder[1] + _(' (optional)...'))
    
    109
    +        self._ui.address_entry.set_placeholder_text(
    
    110
    +            '%s@%s' % (placeholder[2], server))
    
    111
    +
    
    112
    +    def _on_key_press_event(self, _widget, event):
    
    113
    +        if event.keyval == Gdk.KEY_Escape:
    
    114
    +            self.destroy()
    
    115
    +
    
    116
    +    def _on_account_combo_changed(self, combo):
    
    117
    +        self._account = combo.get_active_id()
    
    118
    +        self._fill_placeholders()
    
    119
    +
    
    120
    +    def _update_entry_completion(self, entry, text):
    
    121
    +        text = entry.get_text()
    
    122
    +        if '@' in text:
    
    123
    +            text = text.split('@', 1)[0]
    
    124
    +
    
    125
    +        model = entry.get_completion().get_model()
    
    126
    +        model.clear()
    
    127
    +
    
    128
    +        server = app.get_muc_domain(self._account) or 'muc.example.com'
    
    129
    +        model.append(['%s@%s' % (text, server)])
    
    130
    +
    
    131
    +    def _validate_jid(self, text):
    
    132
    +        if not text:
    
    133
    +            self._set_warning_icon(False)
    
    134
    +            self._ui.create_button.set_sensitive(False)
    
    135
    +            return
    
    136
    +
    
    137
    +        try:
    
    138
    +            jid = JID(text)
    
    139
    +            if jid.getResource():
    
    140
    +                raise InvalidJid
    
    141
    +
    
    142
    +        except InvalidJid:
    
    143
    +            self._set_warning(_('Invalid Address'))
    
    144
    +        else:
    
    145
    +            self._set_warning_icon(False)
    
    146
    +            self._ui.create_button.set_sensitive(True)
    
    147
    +
    
    148
    +    def _set_processing_state(self, enabled):
    
    149
    +        if enabled:
    
    150
    +            self._ui.spinner.start()
    
    151
    +            self._ui.create_button.set_sensitive(False)
    
    152
    +        else:
    
    153
    +            self._ui.spinner.stop()
    
    154
    +        self._ui.grid.set_sensitive(not enabled)
    
    155
    +
    
    156
    +    def _set_warning_icon(self, enabled):
    
    157
    +        icon = 'dialog-warning-symbolic' if enabled else None
    
    158
    +        self._ui.address_entry.set_icon_from_icon_name(
    
    159
    +            Gtk.EntryIconPosition.SECONDARY, icon)
    
    160
    +
    
    161
    +    def _set_warning_tooltip(self, text):
    
    162
    +        self._ui.address_entry.set_icon_tooltip_text(
    
    163
    +            Gtk.EntryIconPosition.SECONDARY, text)
    
    164
    +
    
    165
    +    def _set_warning(self, text):
    
    166
    +        self._set_warning_icon(True)
    
    167
    +        self._set_warning_tooltip(text)
    
    168
    +        self._ui.create_button.set_sensitive(False)
    
    169
    +
    
    170
    +    def _set_warning_from_error(self, error):
    
    171
    +        text = self._error_messages.get(error.type, str(error))
    
    172
    +        self._set_warning(text)
    
    173
    +
    
    174
    +    def _set_warning_from_error_code(self, error_code):
    
    175
    +        self._set_warning(self._error_messages[error_code])
    
    176
    +
    
    177
    +    def _on_address_entry_changed(self, entry):
    
    178
    +        text = entry.get_text()
    
    179
    +        self._update_entry_completion(entry, text)
    
    180
    +        self._validate_jid(text)
    
    181
    +
    
    182
    +    def _on_server_entry_changed(self, _widget):
    
    183
    +        self._update_create_button()
    
    184
    +
    
    185
    +    def _on_address_entry_activate(self, _widget):
    
    186
    +        self._on_create_clicked()
    
    187
    +
    
    188
    +    def _on_create_clicked(self, *args):
    
    189
    +        if not app.account_is_connected(self._account):
    
    190
    +            ErrorDialog(
    
    191
    +                _('Not Connected'),
    
    192
    +                _('You have to be connected to create a group chat.'))
    
    193
    +            return
    
    194
    +
    
    195
    +        room_jid = self._ui.address_entry.get_text()
    
    196
    +
    
    197
    +        self._set_processing_state(True)
    
    198
    +        con = app.connections[self._account]
    
    199
    +        con.get_module('Discovery').disco_info(
    
    200
    +            room_jid, callback=self._disco_info_received)
    
    201
    +
    
    202
    +    @ensure_not_destroyed
    
    203
    +    def _disco_info_received(self, result):
    
    204
    +        if is_error_result(result):
    
    205
    +            if result.type == 'item-not-found':
    
    206
    +                self._create_muc(result.jid)
    
    207
    +                return
    
    208
    +            self._set_warning_from_error(result)
    
    209
    +
    
    210
    +        else:
    
    211
    +            self._set_warning_from_error_code(
    
    212
    +                'already-exists' if result.is_muc else 'not-muc-service')
    
    213
    +
    
    214
    +        self._set_processing_state(False)
    
    215
    +
    
    216
    +    def _create_muc(self, room_jid):
    
    217
    +        name = self._ui.name_entry.get_text()
    
    218
    +        description = self._ui.description_entry.get_text()
    
    219
    +        is_public = self._ui.public_switch.get_active()
    
    220
    +
    
    221
    +        config = {
    
    222
    +            # XEP-0045 options
    
    223
    +            'muc#roomconfig_roomname': name,
    
    224
    +            'muc#roomconfig_roomdesc': description,
    
    225
    +            'muc#roomconfig_publicroom': is_public,
    
    226
    +            'muc#roomconfig_membersonly': not is_public,
    
    227
    +            'muc#roomconfig_whois': 'moderators' if is_public else 'anyone',
    
    228
    +            'muc#roomconfig_changesubject': not is_public,
    
    229
    +
    
    230
    +            # Ejabberd options
    
    231
    +            'public_list': is_public,
    
    232
    +        }
    
    233
    +
    
    234
    +        # Create new group chat by joining
    
    235
    +        app.interface.join_gc_room(
    
    236
    +            self._account,
    
    237
    +            str(room_jid),
    
    238
    +            app.nicks[self._account],
    
    239
    +            None,
    
    240
    +            config=config)
    
    241
    +
    
    242
    +        self.destroy()
    
    243
    +
    
    244
    +    def _on_destroy(self, *args):
    
    245
    +        self._destroyed = True

  • gajim/roster_window.py
    ... ... @@ -3558,6 +3558,13 @@ class RosterWindow:
    3558 3558
             app.app.activate_action('%s-join-groupchat' % account,
    
    3559 3559
                                     GLib.Variant('as', [account, '']))
    
    3560 3560
     
    
    3561
    +    def on_create_gc_activate(self, widget, account):
    
    3562
    +        """
    
    3563
    +        When the create gc menuitem is clicked, show the create gc window
    
    3564
    +        """
    
    3565
    +        app.app.activate_action('create-groupchat',
    
    3566
    +                                GLib.Variant('s', account))
    
    3567
    +
    
    3561 3568
         def on_show_transports_action(self, action, param):
    
    3562 3569
             app.config.set('show_transports_group', param.get_boolean())
    
    3563 3570
             action.set_state(param)
    
    ... ... @@ -5342,7 +5349,9 @@ class RosterWindow:
    5342 5349
             """
    
    5343 5350
             item = Gtk.MenuItem.new_with_mnemonic(_('_Join New Group Chat'))
    
    5344 5351
             item.connect('activate', self.on_join_gc_activate, account)
    
    5345
    -
    
    5352
    +        gc_sub_menu.append(item)
    
    5353
    +        item = Gtk.MenuItem.new_with_mnemonic(_('_Create Group Chat'))
    
    5354
    +        item.connect('activate', self.on_create_gc_activate, account)
    
    5346 5355
             gc_sub_menu.append(item)
    
    5347 5356
     
    
    5348 5357
             # User has at least one bookmark.