Show
Ignore:
Timestamp:
12/12/07 09:44:46 (12 months ago)
Author:
asterix
Message:

merge diff from trunk

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • branches/pep/src/chat_control.py

    r8478 r9123  
    11##      chat_control.py 
    22## 
    3 ## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org> 
     3## Copyright (C) 2006 Yann Leboulanger <asterix@lagaule.org> 
    44## Copyright (C) 2006-2007 Nikos Kouremenos <kourem@gmail.com> 
    55## Copyright (C) 2006 Travis Shirk <travis@pobox.com> 
    6 ## Copyright (C) 2006 Dimitur Kirov <dkirov@gmail.com> 
     6##                   Dimitur Kirov <dkirov@gmail.com> 
    77## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net> 
    8 ## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com> 
     8##                    Julien Pivotto <roidelapluie@gmail.com> 
     9##                    Stephan Erb <steve-e@h3c.de>  
    910## 
    10 ## This program is free software; you can redistribute it and/or modify 
     11## This file is part of Gajim. 
     12## 
     13## Gajim is free software; you can redistribute it and/or modify 
    1114## it under the terms of the GNU General Public License as published 
    12 ## by the Free Software Foundation; version 2 only. 
     15## by the Free Software Foundation; version 3 only. 
    1316## 
    14 ## This program is distributed in the hope that it will be useful, 
     17## Gajim is distributed in the hope that it will be useful, 
    1518## but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1619## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1720## GNU General Public License for more details. 
     21## 
     22## You should have received a copy of the GNU General Public License 
     23## along with Gajim.  If not, see <http://www.gnu.org/licenses/>. 
    1824## 
    1925 
     
    2834import history_window 
    2935import notify 
     36import re 
    3037 
    3138from common import gajim 
     
    4653        HAS_GTK_SPELL = False 
    4754 
    48  
    4955# the next script, executed in the "po" directory, 
    5056# generates the following list. 
    5157##!/bin/sh 
    52 #LANG=$(for i in *.po; do  j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done) 
     58#LANG=$(for i in *.po; do j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done) 
    5359#echo "{_('en'):'en'",$LANG"}" 
    5460langs = {_('English'): 'en', _('Belarusian'): 'be', _('Bulgarian'): 'bg', _('Breton'): 'br', _('Czech'): 'cs', _('German'): 'de', _('Greek'): 'el', _('British'): 'en_GB', _('Esperanto'): 'eo', _('Spanish'): 'es', _('Basque'): 'eu', _('French'): 'fr', _('Croatian'): 'hr', _('Italian'): 'it', _('Norwegian (b)'): 'nb', _('Dutch'): 'nl', _('Norwegian'): 'no', _('Polish'): 'pl', _('Portuguese'): 'pt', _('Brazilian Portuguese'): 'pt_BR', _('Russian'): 'ru', _('Serbian'): 'sr', _('Slovak'): 'sk', _('Swedish'): 'sv', _('Chinese (Ch)'): 'zh_CN'} 
    55  
    5661 
    5762################################################################################ 
     
    5964        '''A base class containing a banner, ConversationTextview, MessageTextView 
    6065        ''' 
     66        def make_href(self, match): 
     67                url_color = gajim.config.get('urlmsgcolor') 
     68                return '<a href="%s"><span color="%s">%s</span></a>' % (match.group(), 
     69                        url_color, match.group()) 
     70 
    6171        def get_font_attrs(self): 
    62                 ''' get pango font  attributes for banner from theme settings ''' 
     72                ''' get pango font attributes for banner from theme settings ''' 
    6373                theme = gajim.config.get('roster_theme') 
    6474                bannerfont = gajim.config.get_per('themes', theme, 'bannerfont') 
     
    119129        event_keymod): 
    120130                pass # Derived should implement this rather than connecting to the event itself. 
     131 
     132        def status_url_clicked(self, widget, url): 
     133                helpers.launch_browser_mailer('url', url) 
    121134 
    122135        def __init__(self, type_id, parent_win, widget_name, contact, acct,  
     
    138151                        self._on_banner_eventbox_button_press_event) 
    139152                self.handlers[id] = widget 
     153 
     154                self.urlfinder = re.compile(r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]") 
     155 
     156                if gajim.HAVE_PYSEXY: 
     157                        import sexy 
     158                        self.banner_status_label = sexy.UrlLabel() 
     159                        self.banner_status_label.connect('url_activated', self.status_url_clicked) 
     160                else: 
     161                        self.banner_status_label = gtk.Label() 
     162                self.banner_status_label.set_selectable(True) 
     163                self.banner_status_label.set_alignment(0,0.5) 
     164 
     165                banner_vbox = self.xml.get_widget('banner_vbox') 
     166                banner_vbox.pack_start(self.banner_status_label) 
     167                self.banner_status_label.show() 
     168 
    140169                # Init DND 
    141170                self.TARGET_TYPE_URI_LIST = 80 
     
    152181                # Create textviews and connect signals 
    153182                self.conv_textview = ConversationTextview(self.account) 
     183                id = self.conv_textview.tv.connect('key_press_event', 
     184                        self._conv_textview_key_press_event) 
     185                self.handlers[id] = self.conv_textview.tv 
    154186                # FIXME: DND on non editable TextView, find a better way 
    155187                self.drag_entered = False 
     
    247279                        except (gobject.GError, RuntimeError), msg: 
    248280                                dialogs.AspellDictError(lang) 
    249                 self.style_event_id = 0 
    250281                self.conv_textview.tv.show() 
    251282                self._paint_banner() 
     
    253284                # For JEP-0172 
    254285                self.user_nick = None 
     286 
     287                self.smooth = True 
    255288 
    256289        def on_msg_textview_populate_popup(self, textview, menu): 
     
    322355                banner_name_label = self.xml.get_widget('banner_name_label') 
    323356                self.disconnect_style_event(banner_name_label) 
     357                self.disconnect_style_event(self.banner_status_label) 
    324358                if bgcolor: 
    325359                        banner_eventbox.modify_bg(gtk.STATE_NORMAL,  
     
    331365                        banner_name_label.modify_fg(gtk.STATE_NORMAL, 
    332366                                gtk.gdk.color_parse(textcolor)) 
     367                        self.banner_status_label.modify_fg(gtk.STATE_NORMAL, 
     368                                gtk.gdk.color_parse(textcolor)) 
    333369                        default_fg = False 
    334370                else: 
     
    337373                        self._on_style_set_event(banner_name_label, None, default_fg, 
    338374                                default_bg) 
    339          
     375                        self._on_style_set_event(self.banner_status_label, None, default_fg, 
     376                                default_bg) 
     377 
    340378        def disconnect_style_event(self, widget): 
    341                 if self.style_event_id: 
    342                         widget.disconnect(self.style_event_id) 
    343                         del self.handlers[self.style_event_id] 
    344                         self.style_event_id = 0  
     379                # Try to find the event_id 
     380                found = False 
     381                for id in self.handlers: 
     382                        if self.handlers[id] == widget: 
     383                                found = True 
     384                                break 
     385                if found: 
     386                        widget.disconnect(id) 
     387                        del self.handlers[id] 
    345388         
    346389        def connect_style_event(self, widget, set_fg = False, set_bg = False): 
    347390                self.disconnect_style_event(widget) 
    348                 self.style_event_id = widget.connect('style-set', 
    349                         self._on_style_set_event, set_fg, set_bg) 
    350                 self.handlers[self.style_event_id] = widget 
    351          
     391                id = widget.connect('style-set', self._on_style_set_event, set_fg, set_bg) 
     392                self.handlers[id] = widget 
     393 
    352394        def _on_style_set_event(self, widget, style, *opts): 
    353395                '''set style of widget from style class *.Frame.Eventbox  
     
    363405                        widget.modify_fg(gtk.STATE_NORMAL, fg_color) 
    364406                self.connect_style_event(widget, opts[0], opts[1]) 
    365          
     407 
     408        def _conv_textview_key_press_event(self, widget, event): 
     409                if gtk.gtk_version < (2, 12, 0): 
     410                        return 
     411                if event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK): 
     412                        return False 
     413                self.parent_win.notebook.emit('key_press_event', event) 
     414 
    366415        def _on_keypress_event(self, widget, event): 
    367416                if event.state & gtk.gdk.CONTROL_MASK: 
     
    390439                                self.parent_win.notebook.emit('key_press_event', event) 
    391440                                return True 
     441 
    392442                elif event.keyval == gtk.keysyms.m and \ 
    393443                        (event.state & gtk.gdk.MOD1_MASK): # alt + m opens emoticons menu 
     
    403453                                        cursor = msg_tv.get_iter_location(buf.get_iter_at_mark( 
    404454                                                buf.get_insert())) 
    405                                         cursor =  msg_tv.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 
     455                                        cursor = msg_tv.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 
    406456                                                cursor.x, cursor.y) 
    407457                                        x = origin[0] + cursor[0] 
     
    520570        def _on_drag_data_received(self, widget, context, x, y, selection, 
    521571                target_type, timestamp): 
    522                 pass  # Derived classes SHOULD implement this method 
     572                pass # Derived classes SHOULD implement this method 
    523573 
    524574        def _on_drag_leave(self, widget, context, time): 
     
    615665                if kind in ('incoming', 'incoming_queue'): 
    616666                        gc_message = False 
    617                         if self.type_id  == message_control.TYPE_GC: 
     667                        if self.type_id == message_control.TYPE_GC: 
    618668                                gc_message = True 
    619669 
     
    713763                        jid = self.contact.jid 
    714764 
    715                 if gajim.interface.instances['logs'].has_key(jid): 
    716                         gajim.interface.instances['logs'][jid].window.present() 
    717                 else: 
    718                         gajim.interface.instances['logs'][jid] = \ 
     765                if gajim.interface.instances.has_key('logs'): 
     766                        gajim.interface.instances['logs'].window.present() 
     767                        gajim.interface.instances['logs'].open_history(jid, self.account) 
     768                else: 
     769                        gajim.interface.instances['logs'] = \ 
    719770                                history_window.HistoryWindow(jid, self.account) 
    720771 
     
    796847                # minimum for conversation_textview and maximum for message_textview 
    797848                # we set to automatic the scrollbar policy 
    798                 diff_y =  message_height - requisition.height 
     849                diff_y = message_height - requisition.height 
    799850                if diff_y != 0: 
    800851                        if conversation_height + diff_y < min_height: 
     
    813864                                        gtk.POLICY_NEVER) 
    814865                                self.msg_scrolledwindow.set_property('height-request', -1) 
    815  
    816                 self.conv_textview.bring_scroll_to_end(diff_y - 18) 
    817                  
     866                        self.conv_textview.bring_scroll_to_end(diff_y - 18, False) 
     867                else: 
     868                        self.conv_textview.bring_scroll_to_end(diff_y - 18, self.smooth) 
     869                self.smooth = True # reinit the flag 
    818870                # enable scrollbar automatic policy for horizontal scrollbar 
    819871                # if message we have in message_textview is too big 
     
    896948                                return 
    897949                        self.sent_history_pos = self.sent_history_pos - 1 
     950                        self.smooth = False 
    898951                        conv_buf.set_text(self.sent_history[self.sent_history_pos]) 
    899952                elif direction == 'down': 
     
    905958 
    906959                        self.sent_history_pos = self.sent_history_pos + 1 
     960                        self.smooth = False 
    907961                        conv_buf.set_text(self.sent_history[self.sent_history_pos]) 
    908962 
     
    9491003        old_msg_kind = None # last kind of the printed message 
    9501004        CHAT_CMDS = ['clear', 'compact', 'help', 'me', 'ping', 'say'] 
    951          
    952         def __init__(self, parent_win, contact, acct, resource = None): 
     1005 
     1006        def __init__(self, parent_win, contact, acct, session, resource = None): 
    9531007                ChatControlBase.__init__(self, self.TYPE_ID, parent_win, 
    9541008                        'chat_child_vbox', contact, acct, resource) 
    955                          
     1009 
    9561010                # for muc use: 
    9571011                # widget = self.xml.get_widget('muc_window_actions_button') 
     
    9691023                self.show_bigger_avatar_timeout_id = None 
    9701024                self.bigger_avatar_window = None 
    971                 self.show_avatar(self.contact.resource)                  
     1025                self.show_avatar(self.contact.resource) 
    9721026 
    9731027                # chatstate timers and state 
     
    9831037                        self._on_message_tv_buffer_changed) 
    9841038                self.handlers[id] = message_tv_buffer 
    985                  
     1039 
    9861040                widget = self.xml.get_widget('avatar_eventbox') 
    9871041                id = widget.connect('enter-notify-event', 
     
    10031057                if self.contact.jid in gajim.encrypted_chats[self.account]: 
    10041058                        self.xml.get_widget('gpg_togglebutton').set_active(True) 
    1005                  
     1059 
     1060                self.set_session(session) 
     1061 
    10061062                self.status_tooltip = gtk.Tooltips() 
    10071063                self.update_ui() 
     
    10461102                        id = menuitem.connect('activate',  
    10471103                                gtkgui_helpers.on_avatar_save_as_menuitem_activate, 
    1048                                 self.contact.jid, self.account, self.contact.get_shown_name() +   
     1104                                self.contact.jid, self.account, self.contact.get_shown_name() + \ 
    10491105                                        '.jpeg') 
    10501106                        self.handlers[id] = menuitem 
     
    11471203                status = contact.status 
    11481204                if status is not None: 
    1149                         self.status_tooltip.set_tip(banner_eventbox, status) 
    1150                         self.status_tooltip.enable() 
    11511205                        banner_name_label.set_ellipsize(pango.ELLIPSIZE_END) 
    1152                         status = helpers.reduce_chars_newlines(status, max_lines = 1) 
    1153                 status_escaped = gobject.markup_escape_text(status) 
     1206                        self.banner_status_label.set_ellipsize(pango.ELLIPSIZE_END) 
     1207                        status_reduced = helpers.reduce_chars_newlines(status, max_lines = 1) 
     1208                status_escaped = gobject.markup_escape_text(status_reduced) 
    11541209 
    11551210                font_attrs, font_attrs_small = self.get_font_attrs() 
     
    11801235                        label_text = '<span %s>%s</span><span %s>%s</span>' % \ 
    11811236                                (font_attrs, name, font_attrs_small, acct_info) 
     1237 
    11821238                if status_escaped: 
    1183                         label_text += '\n<span %s>%s</span>' %\ 
    1184                                 (font_attrs_small, status_escaped) 
    1185                 else: 
     1239                        if gajim.HAVE_PYSEXY: 
     1240                                status_text = self.urlfinder.sub(self.make_href, status_escaped) 
     1241                                status_text = '<span %s>%s</span>' % (font_attrs_small, status_text) 
     1242                        else: 
     1243                                status_text = '<span %s>%s</span>' % (font_attrs_small, status_escaped) 
     1244                        self.status_tooltip.set_tip(banner_eventbox, status) 
     1245                        self.banner_status_label.show() 
     1246                        self.banner_status_label.set_no_show_all(False) 
     1247                else: 
     1248                        status_text = '' 
    11861249                        self.status_tooltip.disable() 
     1250                        self.banner_status_label.hide() 
     1251                        self.banner_status_label.set_no_show_all(True) 
     1252 
     1253                self.banner_status_label.set_markup(status_text) 
    11871254                # setup the label that holds name and jid 
    11881255                banner_name_label.set_markup(label_text) 
     
    12321299 
    12331300                if command == 'me': 
    1234                         return False # This is not really a command 
     1301                        if len(message_array): 
     1302                                return False # /me is not really a command 
     1303                        else: 
     1304                                self.get_command_help(command) 
     1305                                return True # do not send "/me" as message 
    12351306 
    12361307                if command == 'help': 
     
    12961367                contact = self.contact 
    12971368 
     1369                encrypted = bool(self.session) and self.session.enable_encryption 
     1370 
    12981371                keyID = '' 
    1299                 encrypted = False 
    13001372                if self.xml.get_widget('gpg_togglebutton').get_active(): 
    13011373                        keyID = contact.keyID 
    13021374                        encrypted = True 
    1303  
    13041375 
    13051376                chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \ 
     
    13141385                                # because we want it sent with REAL message 
    13151386                                # (not standlone) eg. one that has body 
    1316                                  
     1387 
    13171388                                if contact.our_chatstate: 
    13181389                                        # We already asked for xep 85, don't ask it twice 
     
    13941465                self.kbd_activity_in_last_30_secs = False 
    13951466 
     1467        def on_cancel_session_negotiation(self): 
     1468                msg = _('Session negotiation cancelled') 
     1469                ChatControlBase.print_conversation_line(self, msg, 'status', '', None) 
     1470 
     1471        # print esession settings to textview 
     1472        def print_esession_details(self): 
     1473                if self.session and self.session.enable_encryption: 
     1474                        msg = _('E2E encryption enabled') 
     1475                        ChatControlBase.print_conversation_line(self, msg, 'status', '', None) 
     1476 
     1477                        if self.session.loggable: 
     1478                                msg = _('Session WILL be logged') 
     1479                        else: 
     1480                                msg = _('Session WILL NOT be logged') 
     1481 
     1482                        ChatControlBase.print_conversation_line(self, msg, 'status', '', None) 
     1483                else: 
     1484                        msg = _('E2E encryption disabled') 
     1485                        ChatControlBase.print_conversation_line(self, msg, 'status', '', None) 
     1486 
    13961487        def print_conversation(self, text, frm = '', tim = None, 
    13971488                encrypted = False, subject = None, xhtml = None): 
     
    14131504                        name = '' 
    14141505                else: 
    1415                         ec = gajim.encrypted_chats[self.account] 
    1416                         if encrypted and jid not in ec: 
    1417                                 msg = _('Encryption enabled') 
    1418                                 ChatControlBase.print_conversation_line(self, msg,  
    1419                                         'status', '', tim) 
    1420                                 ec.append(jid) 
    1421                         elif not encrypted and jid in ec: 
    1422                                 msg = _('Encryption disabled') 
    1423                                 ChatControlBase.print_conversation_line(self, msg, 
    1424                                         'status', '', tim) 
    1425                                 ec.remove(jid) 
    1426                         self.xml.get_widget('gpg_togglebutton').set_active(encrypted) 
     1506                        if self.session and self.session.enable_encryption: 
     1507                                if not encrypted: 
     1508                                        msg = _('The following message was NOT encrypted') 
     1509                                        ChatControlBase.print_conversation_line(self, msg,  
     1510                                                'status', '', tim) 
     1511                        else: 
     1512                                # GPG encryption 
     1513                                ec = gajim.encrypted_chats[self.account] 
     1514                                if encrypted and jid not in ec: 
     1515                                        msg = _('OpenPGP Encryption enabled') 
     1516                                        ChatControlBase.print_conversation_line(self, msg,  
     1517                                                'status', '', tim) 
     1518                                        ec.append(jid) 
     1519                                elif not encrypted and jid in ec: 
     1520                                        msg = _('OpenPGP Encryption disabled') 
     1521                                        ChatControlBase.print_conversation_line(self, msg, 
     1522                                                'status', '', tim) 
     1523                                        ec.remove(jid) 
     1524                                self.xml.get_widget('gpg_togglebutton').set_active(encrypted) 
     1525 
    14271526                        if not frm: 
    14281527                                kind = 'incoming' 
     
    15311630                xml = gtkgui_helpers.get_glade('chat_control_popup_menu.glade') 
    15321631                menu = xml.get_widget('chat_control_popup_menu') 
    1533                  
     1632 
    15341633                history_menuitem = xml.get_widget('history_menuitem') 
    15351634                toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem') 
     1635                toggle_e2e_menuitem = xml.get_widget('toggle_e2e_menuitem') 
    15361636                add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem') 
    15371637                send_file_menuitem = xml.get_widget('send_file_menuitem') 
    15381638                information_menuitem = xml.get_widget('information_menuitem') 
    1539                  
     1639                convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat') 
     1640                muc_icon = gajim.interface.roster.load_icon('muc_active') 
     1641                if muc_icon: 
     1642                        convert_to_gc_menuitem.set_image(muc_icon)  
     1643 
     1644                ag = gtk.accel_groups_from_object(self.parent_win.window)[0] 
     1645                history_menuitem.add_accelerator('activate', ag, gtk.keysyms.h, gtk.gdk.CONTROL_MASK, 
     1646                        gtk.ACCEL_VISIBLE) 
     1647                information_menuitem.add_accelerator('activate', ag, gtk.keysyms.i, 
     1648                        gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) 
     1649 
    15401650                contact = self.parent_win.get_active_contact() 
    15411651                jid = contact.jid 
    1542                  
    1543                 # history_menuitem 
    1544                 if gajim.jid_is_transport(jid): 
    1545                         history_menuitem.set_sensitive(False) 
    1546                  
     1652 
    15471653                # check if gpg capabitlies or else make gpg toggle insensitive 
    15481654                gpg_btn = self.xml.get_widget('gpg_togglebutton') 
     
    15511657                toggle_gpg_menuitem.set_active(isactive) 
    15521658                toggle_gpg_menuitem.set_property('sensitive', is_sensitive) 
    1553                  
     1659 
     1660                # TODO: check that the remote client supports e2e 
     1661                if not gajim.HAVE_PYCRYPTO: 
     1662                        toggle_e2e_menuitem.set_sensitive(False) 
     1663                else: 
     1664                        isactive = int(self.session != None and self.session.enable_encryption) 
     1665                        toggle_e2e_menuitem.set_active(isactive) 
     1666 
    15541667                # If we don't have resource, we can't do file transfer 
    15551668                # in transports, contact holds our info we need to disable it too 
     
    15611674                else: 
    15621675                        send_file_menuitem.set_sensitive(False) 
    1563                  
     1676 
     1677                # check if it's possible to convert to groupchat 
     1678                if gajim.get_transport_name_from_jid(jid) or \ 
     1679                gajim.connections[self.account].is_zeroconf: 
     1680                        convert_to_gc_menuitem.set_sensitive(False) 
     1681 
    15641682                # add_to_roster_menuitem 
    15651683                if _('Not in Roster') in contact.groups: 
     
    15691687                        add_to_roster_menuitem.hide() 
    15701688                        add_to_roster_menuitem.set_no_show_all(True) 
    1571                  
    1572                  
     1689 
    15731690                # connect signals 
    15741691                id = history_menuitem.connect('activate',  
     
    15831700                id = toggle_gpg_menuitem.connect('activate',  
    15841701                        self._on_toggle_gpg_menuitem_activate) 
     1702                id = toggle_e2e_menuitem.connect('activate',  
     1703                        self._on_toggle_e2e_menuitem_activate) 
    15851704                self.handlers[id] = toggle_gpg_menuitem  
    15861705                id = information_menuitem.connect('activate',  
    15871706                        self._on_contact_information_menuitem_activate) 
    15881707                self.handlers[id] = information_menuitem 
    1589                 menu.connect('selection-done', lambda w:w.destroy())     
     1708                id = convert_to_gc_menuitem.connect('activate', 
     1709                        self._on_convert_to_gc_menuitem_activate) 
     1710                self.handlers[id] = convert_to_gc_menuitem 
     1711                menu.connect('selection-done', self.destroy_menu, history_menuitem, 
     1712                        information_menuitem) 
    15901713                return menu 
     1714 
     1715        def destroy_menu(self, menu, history_menuitem, information_menuitem): 
     1716                # destroy accelerators 
     1717                ag = gtk.accel_groups_from_object(self.parent_win.window)[0] 
     1718                history_menuitem.remove_accelerator(ag, gtk.keysyms.h, gtk.gdk.CONTROL_MASK) 
     1719                information_menuitem.remove_accelerator(ag, gtk.keysyms.i, gtk.gdk.CONTROL_MASK) 
     1720                # destroy menu 
     1721