~matttbe/wicd/ubuntu_python27

« back to all changes in this revision

Viewing changes to .pc/08-fix_help_message.patch/curses/wicd-curses.py

  • Committer: Bazaar Package Importer
  • Author(s): David Paleino
  • Date: 2010-03-05 18:12:51 UTC
  • mfrom: (8.2.8 sid)
  • Revision ID: james.westby@ubuntu.com-20100305181251-0fcsn0sty5oy8wlq
Tags: 1.7.0+ds1-2
Fix RC bug: daemon doesn't start anymore because copy.deepcopy()
fails with the iniparse object, coming from 20-use_iniparse.patch.
Bug 568326 reopened. (Closes: #572599)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -* coding: utf-8 -*-
 
3
 
 
4
""" wicd-curses. (curses/urwid-based) console interface to wicd
 
5
 
 
6
Provides a console UI for wicd, so that people with broken X servers can
 
7
at least get a network connection.  Or those who don't like using X and/or GTK.
 
8
 
 
9
"""
 
10
 
 
11
#       Copyright (C) 2008-2009 Andrew Psaltis
 
12
 
 
13
#       This program is free software; you can redistribute it and/or modify
 
14
#       it under the terms of the GNU General Public License as published by
 
15
#       the Free Software Foundation; either version 2 of the License, or
 
16
#       (at your option) any later version.
 
17
#       
 
18
#       This program is distributed in the hope that it will be useful,
 
19
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
#       GNU General Public License for more details.
 
22
#       
 
23
#       You should have received a copy of the GNU General Public License
 
24
#       along with this program; if not, write to the Free Software
 
25
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
26
#       MA 02110-1301, USA.
 
27
 
 
28
"""
 
29
    This contains/will contain A LOT of code from the other parts of wicd.
 
30
 
 
31
    This is probably due to the fact that I did not really know what I was doing
 
32
    when I started writing this.  It works, so I guess that's all that matters.
 
33
 
 
34
    Comments, criticisms, patches, bug reports all welcome!
 
35
"""
 
36
# Filter out a confusing urwid warning in python 2.6.
 
37
# This is valid as of urwid version 0.9.8.4
 
38
import warnings 
 
39
warnings.filterwarnings("ignore","The popen2 module is deprecated.  Use the subprocess module.") 
 
40
# UI stuff
 
41
import urwid
 
42
 
 
43
# DBus communication stuff
 
44
from dbus import DBusException
 
45
from dbus import version as dbus_version
 
46
# It took me a while to figure out that I have to use this.
 
47
import gobject
 
48
 
 
49
# Other important wicd-related stuff
 
50
from wicd import wpath
 
51
from wicd import misc
 
52
from wicd import dbusmanager
 
53
 
 
54
# Internal Python stuff
 
55
import sys
 
56
from time import sleep, strftime, ctime
 
57
 
 
58
# Curses UIs for other stuff
 
59
from curses_misc import *
 
60
from prefs_curses import PrefsDialog
 
61
import netentry_curses
 
62
 
 
63
from netentry_curses import WirelessSettingsDialog, WiredSettingsDialog,AdvancedSettingsDialog
 
64
 
 
65
from optparse import OptionParser
 
66
from os import system
 
67
 
 
68
# Stuff about getting the script configurer running
 
69
#from grp import getgrgid
 
70
#from os import getgroups,system
 
71
 
 
72
#import logging
 
73
#import logging.handler
 
74
 
 
75
CURSES_REV=wpath.curses_revision
 
76
 
 
77
# Fix strings in wicd-curses
 
78
from wicd.translations import language
 
79
for i in language.keys():
 
80
    language[i] = language[i].decode('utf8')
 
81
 
 
82
########################################
 
83
##### SUPPORT CLASSES
 
84
########################################
 
85
# Yay for decorators!
 
86
def wrap_exceptions(func):
 
87
    def wrapper(*args, **kargs):
 
88
            try:
 
89
                return func(*args, **kargs)
 
90
            except KeyboardInterrupt:
 
91
                #gobject.source_remove(redraw_tag)
 
92
                loop.quit()
 
93
                ui.stop()
 
94
                print >> sys.stderr, "\n"+language['terminated']
 
95
                #raise
 
96
            except DBusException:
 
97
                loop.quit()
 
98
                ui.stop()
 
99
                print >> sys.stderr,"\n"+language['dbus_fail']
 
100
                raise
 
101
            except :
 
102
                # Quit the loop
 
103
                #if 'loop' in locals():
 
104
                loop.quit()
 
105
                # Zap the screen
 
106
                ui.stop()
 
107
                # Print out standard notification:
 
108
                print >> sys.stderr, "\n" + language['exception']
 
109
                # Flush the buffer so that the notification is always above the
 
110
                # backtrace
 
111
                sys.stdout.flush()
 
112
                # Raise the exception
 
113
                raise
 
114
 
 
115
    wrapper.__name__ = func.__name__
 
116
    wrapper.__module__ = func.__module__
 
117
    wrapper.__dict__ = func.__dict__
 
118
    wrapper.__doc__ = func.__doc__
 
119
    return wrapper
 
120
 
 
121
########################################
 
122
##### SUPPORT FUNCTIONS
 
123
########################################
 
124
 
 
125
# Look familiar?  These two functions are clones of functions found in wicd's
 
126
# gui.py file, except that now set_status is a function passed to them.
 
127
@wrap_exceptions
 
128
def check_for_wired(wired_ip,set_status):
 
129
    """ Determine if wired is active, and if yes, set the status. """
 
130
    if wired_ip and wired.CheckPluggedIn():
 
131
        set_status(language['connected_to_wired'].replace('$A',wired_ip))
 
132
        return True
 
133
    else:
 
134
        return False
 
135
 
 
136
@wrap_exceptions
 
137
def check_for_wireless(iwconfig, wireless_ip, set_status):
 
138
    """ Determine if wireless is active, and if yes, set the status. """
 
139
    if not wireless_ip:
 
140
        return False
 
141
 
 
142
    network = wireless.GetCurrentNetwork(iwconfig)
 
143
    if not network:
 
144
        return False
 
145
 
 
146
    network = unicode(network)
 
147
    if daemon.GetSignalDisplayType() == 0:
 
148
        strength = wireless.GetCurrentSignalStrength(iwconfig)
 
149
    else:
 
150
        strength = wireless.GetCurrentDBMStrength(iwconfig)
 
151
 
 
152
    if strength is None:
 
153
        return False
 
154
    strength = str(strength)            
 
155
    ip = str(wireless_ip)
 
156
    set_status(language['connected_to_wireless'].replace
 
157
                    ('$A', network).replace
 
158
                    ('$B', daemon.FormatSignalForPrinting(strength)).replace
 
159
                    ('$C', wireless_ip))
 
160
    return True
 
161
 
 
162
 
 
163
# Generate the list of networks.
 
164
# Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those
 
165
# DBUS interfaces do. :P
 
166
# Whatever calls this must be exception-wrapped if it is run if the UI is up
 
167
def gen_network_list():
 
168
    wiredL = wired.GetWiredProfileList()
 
169
    wlessL = []
 
170
    # This one makes a list of NetLabels
 
171
    for network_id in range(0, wireless.GetNumberOfNetworks()):
 
172
        is_active = wireless.GetCurrentSignalStrength("") != 0 and wireless.GetCurrentNetworkID(wireless.GetIwconfig())==network_id and wireless.GetWirelessIP('') != None
 
173
 
 
174
        label = NetLabel(network_id,is_active)
 
175
        wlessL.append(label)
 
176
    return (wiredL,wlessL)
 
177
 
 
178
def about_dialog(body):
 
179
    # This looks A LOT better when it is actually displayed.  I promise :-).
 
180
    # The ASCII Art "Wicd" was made from the "smslant" font on one of those
 
181
    # online ASCII big text generators.
 
182
    theText = [
 
183
('green',"   ///       \\\\\\"),"       _      ___        __\n",
 
184
('green',"  ///         \\\\\\"),"     | | /| / (_)______/ /\n",
 
185
('green'," ///           \\\\\\"),"    | |/ |/ / / __/ _  / \n",
 
186
('green',"/||  //     \\\\  ||\\"),"   |__/|__/_/\__/\_,_/  \n",
 
187
('green',"|||  ||"),"(|^|)",('green',"||  |||"),
 
188
"         ($VERSION)       \n".replace("$VERSION",daemon.Hello()),
 
189
 
 
190
('green',"\\||  \\\\")," |+| ",('green',"//  ||/    \n"),
 
191
('green'," \\\\\\"),"    |+|    ",('green',"///"),"      http://wicd.net\n",
 
192
('green',"  \\\\\\"),"   |+|   ",('green',"///"),"      ",language["brought_to_you"],"\n",
 
193
('green',"   \\\\\\"),"  |+|  ",('green',"///"),"       Adam Blackburn\n",
 
194
"     ___|+|___         Dan O'Reilly\n",
 
195
"    |---------|        Andrew Psaltis\n",
 
196
"-----------------------------------------------------"]
 
197
    about = TextDialog(theText,16,55,header=('header','About Wicd'))
 
198
    about.run(ui,body)
 
199
 
 
200
# Modeled after htop's help
 
201
def help_dialog(body):
 
202
    textT  = urwid.Text(('header','wicd-curses help'),'right') 
 
203
    textSH = urwid.Text(['This is ',('blue','wicd-curses-'+CURSES_REV),' using wicd ',unicode(daemon.Hello()),'\n'])
 
204
 
 
205
    textH = urwid.Text([
 
206
"For more detailed help, consult the wicd-curses(8) man page.\n",
 
207
('bold','->'),' and ',('bold','<-')," are the right and left arrows respectively.\n"])
 
208
 
 
209
    text1 = urwid.Text([
 
210
('bold','  H h ?'),": Display this help dialog\n",
 
211
('bold','enter C'),": Connect to selected network\n",
 
212
('bold','      D'),": Disconnect from all networks\n",
 
213
('bold','    ESC'),": Stop a connection in progress\n",
 
214
('bold','   F5 R'),": Refresh network list\n",
 
215
('bold','      P'),": Prefrences dialog\n",
 
216
    ])
 
217
    text2 = urwid.Text([
 
218
('bold','      I'),": Scan for hidden networks\n",
 
219
('bold','      S'),": Select scripts\n",
 
220
('bold','      O'),": Set up Ad-hoc network\n",
 
221
('bold','     ->'),": Configure selected network\n",
 
222
('bold','      A'),": Display 'about' dialog\n",
 
223
('bold',' F8 q Q'),": Quit wicd-curses\n",
 
224
    ])
 
225
    textF = urwid.Text('Press any key to return.')
 
226
    
 
227
    # textJ = urwid.Text(('important','Nobody expects the Spanish Inquisition!'))
 
228
 
 
229
    blank = urwid.Text('')
 
230
 
 
231
    cols = urwid.Columns([text1,text2])
 
232
    pile = urwid.Pile([textH,cols])
 
233
    fill = urwid.Filler(pile)
 
234
    frame = urwid.Frame(fill,header=urwid.Pile([textT,textSH]),footer=textF)
 
235
    dim = ui.get_cols_rows()
 
236
    while True:
 
237
        ui.draw_screen(dim, frame.render(dim, True))
 
238
            
 
239
        keys = ui.get_input()
 
240
        # Don't stop because someone let go of the mouse on the frame
 
241
        mouse_release = False
 
242
        for k in keys:
 
243
            if urwid.is_mouse_event(k) and k[0] == "mouse release":
 
244
                mouse_release = True
 
245
                break
 
246
        if mouse_release :
 
247
            continue
 
248
        if 'window resize' in keys:
 
249
            dim = ui.get_cols_rows()
 
250
        elif keys:
 
251
            break
 
252
 
 
253
def run_configscript(parent,netname,nettype):
 
254
    configfile = wpath.etc+netname+'-settings.conf'
 
255
    if nettype != 'wired':
 
256
        header = 'profile'
 
257
    else:
 
258
        header ='BSSID'
 
259
    if nettype == 'wired':
 
260
        profname = nettype
 
261
    else:
 
262
        profname = wireless.GetWirelessProperty( int(netname),'bssid')
 
263
    theText = [ 
 
264
            language['cannot_edit_scripts_1'].replace('$A',configfile).replace('$B',header),
 
265
"\n\n["+profname+"]\n\n",
 
266
# Translation needs to be changed to accomidate this text below.
 
267
"""You can also configure the wireless networks by looking for the "[<ESSID>]" field in the config file.  
 
268
 
 
269
Once there, you can adjust (or add) the "beforescript", "afterscript", "predisconnectscript" and "postdisconnectscript" variables as needed, to change the preconnect, postconnect, predisconnect and postdisconnect scripts respectively.  Note that you will be specifying the full path to the scripts - not the actual script contents.  You will need to add/edit the script contents separately.  Refer to the wicd manual page for more information."""]
 
270
    dialog = TextDialog(theText,20,80)
 
271
    dialog.run(ui,parent)
 
272
    # This code works with many distributions, but not all of them.  So, to
 
273
    # limit complications, it has been deactivated.  If you want to run it,
 
274
    # be my guest.  Be sure to deactivate the above stuff first.
 
275
    """
 
276
    loop.quit()
 
277
    ui.stop()
 
278
    argv = netname + ' ' +nettype
 
279
 
 
280
    #cmd = '/usr/lib/configscript_curses.py '+argv
 
281
    cmd = wpath.lib+'configscript_curses.py '+argv
 
282
    # Check whether we can sudo.  Hopefully this is complete
 
283
    glist = []
 
284
    for i in getgroups():
 
285
        glist.append(getgrgid(i)[0])
 
286
    if 'root' in glist:
 
287
        precmd = ''
 
288
        precmdargv = ''
 
289
        postcmd = ''
 
290
    elif 'admin' in glist or 'wheel' in glist or 'sudo' in glist:
 
291
        precmd = 'sudo'
 
292
        precmdargv = ''
 
293
        postcmd = ''
 
294
    else:
 
295
        precmd = 'su'
 
296
        precmdargv = ' -c "'
 
297
        postcmd = '"'
 
298
    print "Calling command: " + precmd + precmdargv + cmd + postcmd
 
299
    sys.stdout.flush()
 
300
    system(precmd+precmdargv+cmd+postcmd)
 
301
    raw_input("Press enter!")
 
302
    main()
 
303
    """
 
304
 
 
305
def gen_list_header():
 
306
    if daemon.GetSignalDisplayType() == 0:
 
307
        # Allocate 25 cols for the ESSID name
 
308
        essidgap = 25
 
309
    else:
 
310
        # Need 3 more to accomodate dBm strings
 
311
        essidgap = 28
 
312
    return 'C %s %*s %9s %17s %6s %s' % ('STR ',essidgap,'ESSID','ENCRYPT','BSSID','MODE','CHNL')
 
313
 
 
314
########################################
 
315
##### URWID SUPPORT CLASSES
 
316
########################################
 
317
 
 
318
# Wireless network label
 
319
class NetLabel(urwid.WidgetWrap):
 
320
    def __init__(self, id, is_active):
 
321
        # Pick which strength measure to use based on what the daemon says
 
322
        # gap allocates more space to the first module
 
323
        if daemon.GetSignalDisplayType() == 0:
 
324
            strenstr = 'quality'
 
325
            gap = 4 # Allow for 100%
 
326
        else:
 
327
            strenstr = 'strength'
 
328
            gap = 7 # -XX dbm = 7
 
329
        self.id = id
 
330
        # All of that network property stuff
 
331
        self.stren = daemon.FormatSignalForPrinting(
 
332
                str(wireless.GetWirelessProperty(id, strenstr)))
 
333
        self.essid = wireless.GetWirelessProperty(id, 'essid')
 
334
        self.bssid = wireless.GetWirelessProperty(id, 'bssid')
 
335
 
 
336
        if wireless.GetWirelessProperty(id, 'encryption'):
 
337
            self.encrypt = wireless.GetWirelessProperty(id,'encryption_method')
 
338
        else:
 
339
            self.encrypt = language['unsecured']
 
340
 
 
341
        self.mode  = wireless.GetWirelessProperty(id, 'mode') # Master, Ad-Hoc
 
342
        self.channel = wireless.GetWirelessProperty(id, 'channel')
 
343
        theString = '  %-*s %25s %9s %17s %6s %4s' % (gap,
 
344
                self.stren,self.essid,self.encrypt,self.bssid,self.mode,self.channel)
 
345
        if is_active:
 
346
            theString = '>'+theString[1:]
 
347
            w = urwid.AttrWrap(SelText(theString),'connected','connected focus')
 
348
        else:
 
349
            w = urwid.AttrWrap(SelText(theString),'body','focus')
 
350
 
 
351
        self.__super.__init__(w)
 
352
    def selectable(self):
 
353
        return True
 
354
    def keypress(self,size,key):
 
355
        return self._w.keypress(size,key)
 
356
    def connect(self):
 
357
        wireless.ConnectWireless(self.id)
 
358
        
 
359
class WiredComboBox(ComboBox):
 
360
    """
 
361
    list : the list of wired network profiles.  The rest is self-explanitory.
 
362
    """
 
363
    def __init__(self,list):
 
364
        self.ADD_PROFILE = '---'+language["add_new_profile"]+'---'
 
365
        self.__super.__init__(use_enter=False)
 
366
        self.set_list(list)
 
367
 
 
368
    def set_list(self,list):
 
369
        self.theList = list
 
370
        id=0
 
371
        wiredL=[]
 
372
        is_active = wireless.GetWirelessIP('') == None and wired.GetWiredIP('') != None
 
373
        for profile in list:
 
374
            theString = '%4s   %25s' % (id, profile)
 
375
            # Tag if no wireless IP present, and wired one is
 
376
            if is_active:
 
377
                theString = '>'+theString[1:]
 
378
                
 
379
            wiredL.append(theString)
 
380
            id+=1
 
381
        wiredL.append(self.ADD_PROFILE)
 
382
        if is_active:
 
383
            self.attrs = ('connected','editnfc')
 
384
            self.focus_attr = 'connected focus'
 
385
        else :
 
386
            self.attrs = ('body','editnfc')
 
387
            self.focus_attr = 'focus'
 
388
        self.list = wiredL
 
389
        if self.theList != []:
 
390
            wired.ReadWiredNetworkProfile(self.get_selected_profile())
 
391
 
 
392
    def keypress(self,size,key):
 
393
        prev_focus = self.get_focus()[1]
 
394
        key = ComboBox.keypress(self,size,key)
 
395
        if key == ' ':
 
396
            if self.get_focus()[1] == len(self.list)-1:
 
397
                dialog = InputDialog(('header',language["add_new_wired_profile"]),7,30)
 
398
                exitcode,name = dialog.run(ui,self.parent)
 
399
                if exitcode == 0:
 
400
                    name = name.strip()
 
401
                    if not name:
 
402
                        error(ui,self.parent,'Invalid profile name')
 
403
                        self.set_focus(prev_focus)
 
404
                        return key
 
405
 
 
406
                    wired.CreateWiredNetworkProfile(name,False)
 
407
                    self.set_list(wired.GetWiredProfileList())
 
408
                    self.rebuild_combobox()
 
409
                self.set_focus(prev_focus)
 
410
            else:
 
411
                wired.ReadWiredNetworkProfile(self.get_selected_profile())
 
412
        if key == 'delete':
 
413
            if len(self.theList) == 1:
 
414
                error(self.ui,self.parent,language["no_delete_last_profile"])
 
415
                return key
 
416
            wired.DeleteWiredNetworkProfile(self.get_selected_profile())
 
417
            # Return to the top of the list if something is deleted.
 
418
 
 
419
            if wired.GetDefaultWiredNetwork() != None:
 
420
                self.set_focus(self.theList.index(wired.GetDefaultWiredNetwork()))
 
421
            else:
 
422
                prev_focus -= 1
 
423
                self.set_focus(prev_focus)
 
424
            self.set_list(wired.GetWiredProfileList())
 
425
            self.rebuild_combobox()
 
426
        if key == 'f2':
 
427
            dialog = InputDialog(('header',language["rename_wired_profile"]),7,30,
 
428
                    edit_text=unicode(self.get_selected_profile()))
 
429
            exitcode,name = dialog.run(ui,self.parent)
 
430
            if exitcode == 0:
 
431
                # Save the new one, then kill the old one
 
432
                wired.SaveWiredNetworkProfile(name)
 
433
                wired.DeleteWiredNetworkProfile(self.get_selected_profile())
 
434
                self.set_list(wired.GetWiredProfileList())
 
435
                self.set_focus(self.theList.index(name))
 
436
                self.rebuild_combobox()
 
437
        return key
 
438
 
 
439
    def get_selected_profile(self):
 
440
        """Get the selected wired profile"""
 
441
        loc = self.get_focus()[1]
 
442
        return self.theList[loc]
 
443
 
 
444
# Dialog2 that initiates an Ad-Hoc network connection
 
445
class AdHocDialog(Dialog2):
 
446
    def __init__(self):
 
447
        essid_t = language['essid']
 
448
        ip_t = language['ip']
 
449
        channel_t = language['channel']
 
450
        key_t = "    " + language['key']
 
451
        use_ics_t = language['use_ics']
 
452
        use_encrypt_t = language['use_wep_encryption']
 
453
 
 
454
        self.essid_edit = DynEdit(essid_t)
 
455
        self.ip_edit = DynEdit(ip_t)
 
456
        self.channel_edit = DynIntEdit(channel_t)
 
457
        self.key_edit = DynEdit(key_t,sensitive=False)
 
458
 
 
459
        self.use_ics_chkb = urwid.CheckBox(use_ics_t)
 
460
        self.use_encrypt_chkb = urwid.CheckBox(use_encrypt_t,
 
461
                on_state_change=self.encrypt_callback)
 
462
 
 
463
        blank = urwid.Text('')
 
464
 
 
465
        # Set defaults
 
466
        self.essid_edit.set_edit_text("My_Adhoc_Network")
 
467
        self.ip_edit.set_edit_text("169.254.12.10")
 
468
        self.channel_edit.set_edit_text("3")
 
469
 
 
470
        l = [self.essid_edit,self.ip_edit,self.channel_edit,blank,
 
471
                self.use_ics_chkb,self.use_encrypt_chkb,self.key_edit]
 
472
        body = urwid.ListBox(l)
 
473
 
 
474
        header = ('header',language['create_adhoc_network'])
 
475
        Dialog2.__init__(self, header, 15, 50, body)
 
476
        self.add_buttons([('OK',1),('Cancel',-1)])
 
477
        self.frame.set_focus('body')
 
478
 
 
479
    def encrypt_callback(self,chkbox,new_state,user_info=None):
 
480
        self.key_edit.set_sensitive(new_state)
 
481
 
 
482
    def unhandled_key(self, size, k):
 
483
        if k in ('up','page up'):
 
484
            self.frame.set_focus('body')
 
485
        if k in ('down','page down'):
 
486
            self.frame.set_focus('footer')
 
487
        if k == 'enter':
 
488
            # pass enter to the "ok" button
 
489
            self.frame.set_focus('footer')
 
490
            self.buttons.set_focus(0)
 
491
            self.view.keypress( size, k )
 
492
    def on_exit(self,exitcode):
 
493
        data = ( self.essid_edit.get_edit_text(),
 
494
                 self.ip_edit.get_edit_text().strip(),
 
495
                 self.channel_edit.get_edit_text(),
 
496
                 self.use_ics_chkb.get_state(),
 
497
                 self.use_encrypt_chkb.get_state(),
 
498
                 self.key_edit.get_edit_text())
 
499
        return exitcode, data
 
500
 
 
501
########################################
 
502
##### APPLICATION INTERFACE CLASS
 
503
########################################
 
504
# The Whole Shebang
 
505
class appGUI():
 
506
    """The UI itself, all glory belongs to it!"""
 
507
    def __init__(self):
 
508
        global loop
 
509
        self.size = ui.get_cols_rows()
 
510
        # Happy screen saying that you can't do anything because we're scanning
 
511
        # for networks.  :-)
 
512
        self.screen_locker = urwid.Filler(urwid.Text(('important',language['scanning_stand_by']), align='center'))
 
513
        self.no_wlan = urwid.Filler(urwid.Text(('important',language['no_wireless_networks_found']), align='center'))
 
514
        self.TITLE = language['wicd_curses']
 
515
        self.WIRED_IDX = 1
 
516
        self.WLESS_IDX = 3
 
517
 
 
518
        header = urwid.AttrWrap(urwid.Text(self.TITLE,align='right'), 'header')
 
519
        self.wiredH=urwid.Filler(urwid.Text("Wired Network(s)"))
 
520
        self.list_header=urwid.AttrWrap(urwid.Text(gen_list_header()),'listbar')
 
521
        self.wlessH=NSelListBox([urwid.Text("Wireless Network(s)"),self.list_header])
 
522
 
 
523
        # Init this earlier to make update_status happy
 
524
        self.update_tag = None
 
525
 
 
526
        # FIXME: This should be two variables
 
527
        self.focusloc = [1,0]
 
528
 
 
529
        # These are empty to make sure that things go my way.
 
530
        wiredL,wlessL = [],[]
 
531
 
 
532
        self.frame = None
 
533
        self.diag = None
 
534
 
 
535
        self.wiredCB = urwid.Filler(WiredComboBox(wiredL))
 
536
        self.wlessLB = urwid.ListBox(wlessL)
 
537
        self.update_netlist(force_check=True,firstrun=True)
 
538
        
 
539
        # Keymappings proposed by nanotube in #wicd
 
540
        keys = [
 
541
                ('H' ,'Help'  ,None),
 
542
                ('right','Config',None),
 
543
                #('  ','         ',None),
 
544
                ('C' ,'Connect',None),
 
545
                ('D' ,'Disconn',None),
 
546
                ('R' ,'Refresh',None),
 
547
                ('P' ,'Prefs',None),
 
548
                ('I' ,'Hidden',None),
 
549
                ('A' ,'About',None),
 
550
                ('Q' ,'Quit',loop.quit)
 
551
               ]
 
552
 
 
553
        self.primaryCols = OptCols(keys,self.handle_keys)
 
554
        self.status_label = urwid.AttrWrap(urwid.Text(''),'important')
 
555
        self.footer2 = urwid.Columns([self.status_label])
 
556
        self.footerList = urwid.Pile([self.primaryCols,self.footer2])
 
557
 
 
558
        self.frame = urwid.Frame(self.thePile,
 
559
                                 header=header,
 
560
                                 footer=self.footerList)
 
561
        self.wiredCB.get_body().build_combobox(self.frame,ui,3)
 
562
 
 
563
        # Init the other columns used in the program
 
564
        self.init_other_optcols()
 
565
 
 
566
        self.frame.set_body(self.thePile)
 
567
        # Booleans gallore!
 
568
        self.prev_state    = False
 
569
        self.connecting    = False
 
570
        self.screen_locked = False
 
571
        self.do_diag_lock = False #Whether the screen is locked beneath a dialog
 
572
        self.diag_type = 'none' # The type of dialog that is up
 
573
        self.scanning = False
 
574
 
 
575
        self.pref = None
 
576
 
 
577
        self.update_status()
 
578
 
 
579
        #self.max_wait = ui.max_wait
 
580
 
 
581
    def doScan(self, sync=False):
 
582
        self.scanning = True
 
583
        wireless.Scan(False)
 
584
 
 
585
    def init_other_optcols(self):
 
586
        # The "tabbed" preferences dialog
 
587
        self.prefCols = OptCols( [ ('f10','OK'),
 
588
                                   ('page up','Tab Left',),
 
589
                                   ('page down', 'Tab Right'),
 
590
                                   ('esc','Cancel') ], self.handle_keys)
 
591
        self.confCols = OptCols( [ ('f10','OK'),
 
592
                                   ('esc','Cancel') ],self.handle_keys)
 
593
 
 
594
    # Does what it says it does
 
595
    def lock_screen(self):
 
596
        if self.diag_type == 'pref':
 
597
            self.do_diag_lock = True
 
598
            return True
 
599
        self.frame.set_body(self.screen_locker)
 
600
        self.screen_locked = True
 
601
        self.update_ui()
 
602
 
 
603
    def unlock_screen(self):
 
604
        if self.do_diag_lock:
 
605
            self.do_diag_lock = False
 
606
            return True
 
607
        self.update_netlist(force_check=True)
 
608
        if not self.diag:
 
609
            self.frame.set_body(self.thePile)
 
610
        self.screen_locked = False
 
611
        self.update_ui()
 
612
 
 
613
    def raise_hidden_network_dialog(self):
 
614
        dialog = InputDialog(('header',language["select_hidden_essid"]),7,30,language['scan'])
 
615
        exitcode,hidden = dialog.run(ui,self.frame)
 
616
        if exitcode != -1:
 
617
            # That dialog will sit there for a while if I don't get rid of it
 
618
            self.update_ui()
 
619
            wireless.SetHiddenNetworkESSID(misc.noneToString(hidden))
 
620
            wireless.Scan(False)
 
621
        wireless.SetHiddenNetworkESSID("")
 
622
        
 
623
    def update_focusloc(self):
 
624
        # Location of last known focus is remapped to current location.
 
625
        # This might need to be cleaned up later.
 
626
 
 
627
        if self.thePile.get_focus() == self.wiredCB: 
 
628
            wlessorwired = self.WIRED_IDX
 
629
            where = self.thePile.get_focus().get_body().get_focus()[1]
 
630
        else: #self.thePile.get_focus() == self.wlessLB :
 
631
            wlessorwired = self.WLESS_IDX
 
632
            if self.wlessLB == self.no_wlan:
 
633
                where = None
 
634
            else: 
 
635
                where = self.thePile.get_focus().get_focus()[1]
 
636
                #where = self.wlessLB.get_focus()[1]
 
637
        self.focusloc = [wlessorwired,where]
 
638
    
 
639
    # Be clunky until I get to a later stage of development.
 
640
    # Update the list of networks.  Usually called by DBus.
 
641
    @wrap_exceptions
 
642
    def update_netlist(self,state=None, x=None, force_check=False,firstrun=False):
 
643
        # Don't even try to do this if we are running a dialog
 
644
        if self.diag:
 
645
            return
 
646
        # Run focus-collecting code if we are not running this for the first
 
647
        # time
 
648
        if not firstrun:
 
649
            self.update_focusloc()
 
650
            self.list_header.set_text(gen_list_header())
 
651
        """ Updates the overall network list."""
 
652
        if not state:
 
653
            state, x = daemon.GetConnectionStatus()
 
654
        if force_check or self.prev_state != state:
 
655
            wiredL,wlessL = gen_network_list()
 
656
 
 
657
            self.wiredCB.get_body().set_list(wiredL)
 
658
            self.wiredCB.get_body().build_combobox(self.frame,ui,3)
 
659
            if len(wlessL) != 0:
 
660
                if self.wlessLB == self.no_wlan:
 
661
                    self.wlessLB = urwid.ListBox(wlessL)
 
662
                else:
 
663
                    self.wlessLB.body = urwid.SimpleListWalker(wlessL)
 
664
            else:
 
665
                self.wlessLB = self.no_wlan
 
666
            if daemon.GetAlwaysShowWiredInterface() or wired.CheckPluggedIn():
 
667
                self.thePile = urwid.Pile([('fixed',1,self.wiredH),
 
668
                                           ('fixed',1,self.wiredCB),
 
669
                                           ('fixed',2,self.wlessH),
 
670
                                                      self.wlessLB] )
 
671
                if not firstrun:
 
672
                    self.frame.body = self.thePile
 
673
 
 
674
                self.thePile.set_focus(self.focusloc[0])
 
675
                if self.focusloc[0] == self.WIRED_IDX:
 
676
                    self.thePile.get_focus().get_body().set_focus(self.focusloc[1])
 
677
                else:
 
678
                    if self.wlessLB != self.no_wlan:
 
679
                        self.thePile.get_focus().set_focus(self.focusloc[1])
 
680
                    else:
 
681
                        self.thePile.set_focus(self.wiredCB)
 
682
            else:
 
683
                self.thePile = urwid.Pile([('fixed',2,self.wlessH),self.wlessLB] )
 
684
                if not firstrun:
 
685
                    self.frame.body = self.thePile
 
686
                if self.focusloc[1] == None:
 
687
                    self.focusloc[1] = 0
 
688
                if self.wlessLB != self.no_wlan:
 
689
                    self.wlessLB.set_focus(self.focusloc[1])
 
690
 
 
691
        self.prev_state = state
 
692
        if not firstrun:
 
693
            self.update_ui()
 
694
        if firstrun:
 
695
            if wired.GetDefaultWiredNetwork() != None:
 
696
                self.wiredCB.get_body().set_focus(wired.GetWiredProfileList().index(wired.GetDefaultWiredNetwork()))
 
697
 
 
698
    # Update the footer/status bar
 
699
    conn_status = False
 
700
    @wrap_exceptions
 
701
    def update_status(self):
 
702
        wired_connecting = wired.CheckIfWiredConnecting()
 
703
        wireless_connecting = wireless.CheckIfWirelessConnecting()
 
704
        self.connecting = wired_connecting or wireless_connecting
 
705
        
 
706
        fast = not daemon.NeedsExternalCalls()
 
707
        if self.connecting: 
 
708
            if not self.conn_status:
 
709
                self.conn_status = True
 
710
                gobject.timeout_add(250,self.set_connecting_status,fast)
 
711
            return True
 
712
        else:
 
713
            if check_for_wired(wired.GetWiredIP(''),self.set_status):
 
714
                return True
 
715
            if not fast:
 
716
                iwconfig = wireless.GetIwconfig()
 
717
            else:
 
718
                iwconfig = ''
 
719
            if check_for_wireless(iwconfig, wireless.GetWirelessIP(""),
 
720
                    self.set_status):
 
721
                return True
 
722
            else:
 
723
                self.set_status(language['not_connected'])
 
724
                self.update_ui()
 
725
                return True
 
726
 
 
727
    def set_connecting_status(self,fast):
 
728
        wired_connecting = wired.CheckIfWiredConnecting()
 
729
        wireless_connecting = wireless.CheckIfWirelessConnecting()
 
730
        if wireless_connecting:
 
731
            if not fast:
 
732
                iwconfig = wireless.GetIwconfig()
 
733
            else:
 
734
                iwconfig = ''
 
735
            # set_status is rigged to return false when it is not
 
736
            # connecting to anything, so this should work.
 
737
            return self.set_status(wireless.GetCurrentNetwork(iwconfig) +
 
738
                    ': ' +
 
739
                    language[str(wireless.CheckWirelessConnectingMessage())],
 
740
                    True)
 
741
        if wired_connecting:
 
742
            return self.set_status( language['wired_network'] +
 
743
                    ': ' +
 
744
                    language[str(wired.CheckWiredConnectingMessage())],
 
745
                    True)
 
746
        else:
 
747
            self.conn_status=False
 
748
            return False
 
749
 
 
750
    # Cheap little indicator stating that we are actually connecting
 
751
    twirl = ['|','/','-','\\']
 
752
    tcount = 0 # Counter for said indicator
 
753
    def set_status(self,text,from_idle=False):
 
754
        # Set the status text, usually called by the update_status method
 
755
        # from_idle : a check to see if we are being called directly from the
 
756
        # mainloop
 
757
        # If we are being called as the result of trying to connect to
 
758
        # something, and we aren't connecting to something, return False
 
759
        # immediately.
 
760
        if from_idle and not self.connecting:
 
761
            self.update_status()
 
762
            self.conn_status=False
 
763
            return False
 
764
        toAppend = ''
 
765
        # If we are connecting and being called from the idle function, spin
 
766
        # the wheel.
 
767
        if from_idle and self.connecting:
 
768
            # This is probably the wrong way to do this, but it works for now.
 
769
            self.tcount+=1
 
770
            toAppend=self.twirl[self.tcount % 4]
 
771
        self.status_label.set_text(text+' '+toAppend)
 
772
        self.update_ui()
 
773
        return True
 
774
 
 
775
    def dbus_scan_finished(self):
 
776
        # I'm pretty sure that I'll need this later.
 
777
        #if not self.connecting:
 
778
        #    gobject.idle_add(self.refresh_networks, None, False, None)
 
779
        self.unlock_screen()
 
780
        self.scanning = False
 
781
 
 
782
    def dbus_scan_started(self):
 
783
        self.scanning = True
 
784
        if self.diag_type == 'conf':
 
785
            self.restore_primary()
 
786
        self.lock_screen()
 
787
 
 
788
    def restore_primary(self):
 
789
        self.diag_type = 'none'
 
790
        if self.do_diag_lock or self.scanning:
 
791
            self.frame.set_body(self.screen_locker)
 
792
            self.do_diag_lock = False
 
793
        else:
 
794
            self.frame.set_body(self.thePile)
 
795
        self.diag = None
 
796
        self.frame.set_footer(urwid.Pile([self.primaryCols,self.footer2]))
 
797
        self.update_ui()
 
798
 
 
799
    def handle_keys(self,keys):
 
800
        if not self.diag:
 
801
            # Handle keystrokes
 
802
            if "f8" in keys or 'Q' in keys or 'q' in keys:
 
803
                loop.quit()
 
804
                #return False
 
805
            if "f5" in keys or 'R' in keys:
 
806
                self.lock_screen()
 
807
                self.doScan()
 
808
            if "D" in keys:
 
809
                # Disconnect from all networks.
 
810
                daemon.Disconnect()
 
811
                self.update_netlist()
 
812
            if 'right' in keys:
 
813
                if not self.scanning:
 
814
                    focus = self.thePile.get_focus()
 
815
                    self.frame.set_footer(urwid.Pile([self.confCols,self.footer2]))
 
816
                    if focus == self.wiredCB:
 
817
                        self.diag = WiredSettingsDialog(self.wiredCB.get_body().get_selected_profile())
 
818
                        self.frame.set_body(self.diag)
 
819
                    else:
 
820
                        # wireless list only other option
 
821
                        wid,pos  = self.thePile.get_focus().get_focus()
 
822
                        self.diag = WirelessSettingsDialog(pos,self.frame)
 
823
                        self.diag.ready_widgets(ui,self.frame)
 
824
                        self.frame.set_body(self.diag)
 
825
                    self.diag_type = 'conf'
 
826
            if "enter" in keys or 'C' in keys:
 
827
                if not self.scanning:
 
828
                    focus = self.frame.body.get_focus()
 
829
                    if focus == self.wiredCB:
 
830
                        self.special = focus
 
831
                        self.connect("wired",0)
 
832
                    else:
 
833
                        # wless list only other option, if it is around
 
834
                        if self.wlessLB != self.no_wlan:
 
835
                            wid,pos = self.thePile.get_focus().get_focus()
 
836
                            self.connect("wireless",pos)
 
837
            if "esc" in keys:
 
838
                # Force disconnect here if connection in progress
 
839
                if self.connecting:
 
840
                    daemon.CancelConnect()
 
841
                    # Prevents automatic reconnecting if that option is enabled
 
842
                    daemon.SetForcedDisconnect(True)
 
843
            if "P" in keys:
 
844
                if not self.pref:
 
845
                    self.pref = PrefsDialog(self.frame,(0,1),ui,
 
846
                            dbusmanager.get_dbus_ifaces()) 
 
847
                self.pref.load_settings()
 
848
                self.pref.ready_widgets(ui,self.frame)
 
849
                self.frame.set_footer(urwid.Pile([self.prefCols,self.footer2]))
 
850
                self.diag = self.pref
 
851
                self.diag_type = 'pref'
 
852
                self.frame.set_body(self.diag)
 
853
                # Halt here, keypress gets passed to the dialog otherwise
 
854
                return True
 
855
            if "A" in keys:
 
856
                about_dialog(self.frame)
 
857
            if "I" in keys:
 
858
                self.raise_hidden_network_dialog()
 
859
            if "H" in keys or 'h' in keys or '?' in keys:
 
860
                # FIXME I shouldn't need this, OptCols messes up this one
 
861
                # particular button
 
862
                if not self.diag:
 
863
                    help_dialog(self.frame)
 
864
            if "S" in keys:
 
865
                focus = self.thePile.get_focus()
 
866
                if focus == self.wiredCB:
 
867
                    nettype = 'wired'
 
868
                    netname = self.wiredCB.get_body().get_selected_profile()
 
869
                else:
 
870
                    nettype = 'wireless'
 
871
                    netname = str(self.wlessLB.get_focus()[1])
 
872
                run_configscript(self.frame,netname,nettype)
 
873
            if "O" in keys:
 
874
                exitcode,data = AdHocDialog().run(ui,self.frame)
 
875
                #data = (essid,ip,channel,use_ics,use_encrypt,key_edit)
 
876
                if exitcode == 1:
 
877
                    wireless.CreateAdHocNetwork(data[0],
 
878
                                                data[2],
 
879
                                                data[1], "WEP",
 
880
                                                data[5],
 
881
                                                data[4], False)
 
882
            
 
883
        for k in keys:
 
884
            if urwid.is_mouse_event(k):
 
885
                event, button, col, row = k
 
886
                self.frame.mouse_event( self.size,
 
887
                        event, button, col, row,
 
888
                        focus=True)
 
889
                continue
 
890
            k = self.frame.keypress(self.size,k)
 
891
            if self.diag:
 
892
                if  k == 'esc' or k == 'q' or k == 'Q':
 
893
                    self.restore_primary()
 
894
                    break
 
895
                if k == 'f10':
 
896
                    self.diag.save_settings()
 
897
                    self.restore_primary()
 
898
                    break
 
899
            if k == "window resize":
 
900
                self.size = ui.get_cols_rows()
 
901
                continue
 
902
 
 
903
    def call_update_ui(self,source,cb_condition):           
 
904
        self.update_ui(True)                                
 
905
        return True                                         
 
906
                
 
907
    # Redraw the screen
 
908
    @wrap_exceptions
 
909
    def update_ui(self,from_key=False):
 
910
        if not ui._started:
 
911
            return False
 
912
 
 
913
        input_data = ui.get_input_nonblocking()
 
914
        # Resolve any "alarms" in the waiting
 
915
        self.handle_keys(input_data[1])
 
916
 
 
917
        # Update the screen
 
918
        canvas = self.frame.render( (self.size),True )
 
919
        ui.draw_screen((self.size),canvas)
 
920
        # Get the input data
 
921
        if self.update_tag != None:
 
922
            gobject.source_remove(self.update_tag)
 
923
        #if from_key:
 
924
        return False
 
925
 
 
926
    def connect(self, nettype, networkid, networkentry=None):
 
927
        """ Initiates the connection process in the daemon. """
 
928
        if nettype == "wireless":
 
929
            wireless.ConnectWireless(networkid)
 
930
        elif nettype == "wired":
 
931
            wired.ConnectWired()
 
932
        self.update_status()
 
933
 
 
934
########################################
 
935
##### INITIALIZATION FUNCTIONS
 
936
########################################
 
937
 
 
938
def main():
 
939
    global ui, dlogger
 
940
    # We are not python.
 
941
    misc.RenameProcess('wicd-curses')
 
942
 
 
943
    import urwid.raw_display
 
944
    ui = urwid.raw_display.Screen()
 
945
 
 
946
    #if options.debug:
 
947
    #    dlogger = logging.getLogger("Debug")
 
948
    #    dlogger.setLevel(logging.DEBUG)
 
949
    #    dlogger.debug("wicd-curses debug logging started")
 
950
 
 
951
    # Default Color scheme.
 
952
    # Other potential color schemes can be found at:
 
953
    # http://excess.org/urwid/wiki/RecommendedPalette
 
954
 
 
955
    # Thanks to nanotube on #wicd for helping with this
 
956
    ui.register_palette([
 
957
        ('body','default','default'),
 
958
        ('focus','black','light gray'),
 
959
        ('header','light blue','default'),
 
960
        ('important','light red','default'),
 
961
        ('connected','dark green','default'),
 
962
        ('connected focus','black','dark green'),
 
963
        ('editcp', 'default', 'default', 'standout'),
 
964
        ('editbx', 'light gray', 'dark blue'),
 
965
        ('editfc', 'white','dark blue', 'bold'),
 
966
        ('editnfc','brown','default','bold'),
 
967
        ('tab active','dark green','light gray'),
 
968
        ('infobar','light gray','dark blue'),
 
969
        ('listbar','light blue','default'),
 
970
        # Simple colors around text
 
971
        ('green','dark green','default'),
 
972
        ('blue','light blue','default'),
 
973
        ('red','dark red','default'),
 
974
        ('bold','white','black','bold')])
 
975
    # This is a wrapper around a function that calls another a function that
 
976
    # is a wrapper around a infinite loop.  Fun.
 
977
    urwid.set_encoding('utf8')
 
978
    ui.run_wrapper(run)
 
979
 
 
980
@wrap_exceptions
 
981
def run():
 
982
    global loop
 
983
    loop = gobject.MainLoop()
 
984
    
 
985
    ui.set_mouse_tracking()
 
986
    app = appGUI()
 
987
 
 
988
    # Connect signals and whatnot to UI screen control functions
 
989
    bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal',
 
990
                            'org.wicd.daemon.wireless')
 
991
    bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal',
 
992
                            'org.wicd.daemon.wireless')
 
993
    # I've left this commented out many times.
 
994
    bus.add_signal_receiver(app.update_netlist, 'StatusChanged',
 
995
                            'org.wicd.daemon')
 
996
    # Update the connection status on the bottom every 2 s.
 
997
    gobject.timeout_add(2000,app.update_status)
 
998
 
 
999
    # Get input file descriptors and add callbacks to the ui-updating function
 
1000
    fds = ui.get_input_descriptors()
 
1001
    for fd in fds:
 
1002
        gobject.io_add_watch(fd, gobject.IO_IN,app.call_update_ui)
 
1003
    app.update_ui()
 
1004
    loop.run()
 
1005
 
 
1006
# Mostly borrowed from gui.py
 
1007
def setup_dbus(force=True):
 
1008
    global bus, daemon, wireless, wired
 
1009
    try:
 
1010
        dbusmanager.connect_to_dbus()
 
1011
    except DBusException:
 
1012
        print >> sys.stderr, language['cannot_connect_to_daemon']
 
1013
    bus = dbusmanager.get_bus()
 
1014
    dbus_ifaces = dbusmanager.get_dbus_ifaces()
 
1015
    daemon = dbus_ifaces['daemon']
 
1016
    wireless = dbus_ifaces['wireless']
 
1017
    wired = dbus_ifaces['wired']
 
1018
 
 
1019
    netentry_curses.dbus_init(dbus_ifaces)
 
1020
    return True
 
1021
 
 
1022
setup_dbus()
 
1023
 
 
1024
########################################
 
1025
##### MAIN ENTRY POINT
 
1026
########################################
 
1027
if __name__ == '__main__':
 
1028
    try:
 
1029
        parser = OptionParser(version="wicd-curses-%s (using wicd %s)" % (CURSES_REV,daemon.Hello()))
 
1030
    except Exception, e:
 
1031
        if "DBus.Error.AccessDenied" in e.get_dbus_name():
 
1032
            print language['access_denied_wc'].replace('$A','\033[1;34m'+wpath.wicd_group+'\033[0m')
 
1033
            sys.exit(1)
 
1034
        else:
 
1035
            raise
 
1036
    #parser.add_option("-d", "--debug",action="store_true"
 
1037
    #        ,dest='debug',help="enable logging of wicd-curses (currently does nothing)")
 
1038
    (options,args) = parser.parse_args()
 
1039
    main()