add russion translation, thx Peter
[enigma2-plugins.git] / networkbrowser / src / NetworkBrowser.py
1 # -*- coding: utf-8 -*-
2 # for localized messages
3 from __init__ import _
4 from enigma import eTimer, getDesktop
5 from Screens.Screen import Screen
6 from Screens.MessageBox import MessageBox
7 from Components.Label import Label
8 from Components.ActionMap import ActionMap, NumberActionMap
9 from Components.Sources.List import List
10 from Components.Network import iNetwork
11 from Components.Input import Input
12 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
13 from Tools.LoadPixmap import LoadPixmap
14 from cPickle import dump, load
15 from os import path as os_path, stat, mkdir, remove
16 from time import time
17 from stat import ST_MTIME
18
19 import netscan
20 from MountManager import AutoMountManager
21 from AutoMount import iAutoMount
22 from MountEdit import AutoMountEdit
23 from UserDialog import UserDialog
24
25 def write_cache(cache_file, cache_data):
26         #Does a cPickle dump
27         if not os_path.isdir( os_path.dirname(cache_file) ):
28                 try:
29                         mkdir( os_path.dirname(cache_file) )
30                 except OSError:
31                         print os_path.dirname(cache_file), 'is a file'
32         fd = open(cache_file, 'w')
33         dump(cache_data, fd, -1)
34         fd.close()
35
36 def valid_cache(cache_file, cache_ttl):
37         #See if the cache file exists and is still living
38         try:
39                 mtime = stat(cache_file)[ST_MTIME]
40         except:
41                 return 0
42         curr_time = time()
43         if (curr_time - mtime) > cache_ttl:
44                 return 0
45         else:
46                 return 1
47
48 def load_cache(cache_file):
49         #Does a cPickle load
50         fd = open(cache_file)
51         cache_data = load(fd)
52         fd.close()
53         return cache_data
54
55 class NetworkDescriptor:
56         def __init__(self, name = "NetworkServer", description = ""):
57                 self.name = name
58                 self.description = description
59
60 class NetworkBrowser(Screen):
61         skin = """
62                 <screen name="NetworkBrowser" position="90,80" size="560,450" title="Network Neighbourhood">
63                         <ePixmap pixmap="skin_default/bottombar.png" position="10,360" size="540,120" zPosition="1" transparent="1" alphatest="on" />
64                         <widget source="list" render="Listbox" position="10,10" size="540,350" zPosition="10" scrollbarMode="showOnDemand">
65                                 <convert type="TemplatedMultiContent">
66                                         {"template": [
67                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 0), size = (48, 48), png = 1), # index 1 is the expandable/expanded/verticalline icon
68                                                         MultiContentEntryText(pos = (50, 4), size = (420, 26), font=2, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the Hostname
69                                                         MultiContentEntryText(pos = (140, 5), size = (320, 25), font=0, flags = RT_HALIGN_LEFT, text = 3), # index 3 is the sharename
70                                                         MultiContentEntryText(pos = (140, 26), size = (320, 17), font=1, flags = RT_HALIGN_LEFT, text = 4), # index 4 is the sharedescription
71                                                         MultiContentEntryPixmapAlphaTest(pos = (45, 0), size = (48, 48), png = 5), # index 5 is the nfs/cifs icon
72                                                         MultiContentEntryPixmapAlphaTest(pos = (90, 0), size = (48, 48), png = 6), # index 6 is the isMounted icon
73                                                 ],
74                                         "fonts": [gFont("Regular", 20),gFont("Regular", 14),gFont("Regular", 24)],
75                                         "itemHeight": 50
76                                         }
77                                 </convert>
78                         </widget>
79                         <ePixmap pixmap="skin_default/buttons/button_green.png" position="30,370" zPosition="10" size="15,16" transparent="1" alphatest="on" />
80                         <widget name="mounttext" position="50,370" size="250,21" zPosition="10" font="Regular;21" transparent="1" />
81                         <ePixmap pixmap="skin_default/buttons/button_blue.png" position="30,395" zPosition="10" size="15,16" transparent="1" alphatest="on" />
82                         <widget name="searchtext" position="50,395" size="150,21" zPosition="10" font="Regular;21" transparent="1" />
83                         <widget name="infotext" position="300,375" size="250,21" zPosition="10" font="Regular;21" transparent="1" />
84                         <ePixmap pixmap="skin_default/buttons/button_red.png" position="410,420" zPosition="10" size="15,16" transparent="1" alphatest="on" />
85                         <widget name="closetext" position="430,420" size="120,21" zPosition="10" font="Regular;21" transparent="1" />
86                         <ePixmap pixmap="skin_default/buttons/button_yellow.png" position="30,420" zPosition="10" size="15,16" transparent="1" alphatest="on" />
87                         <widget name="rescantext" position="50,420" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
88                 </screen>"""
89
90         def __init__(self, session, iface,plugin_path):
91                 Screen.__init__(self, session)
92                 self.skin_path = plugin_path
93                 self.session = session
94                 self.iface = iface
95                 if self.iface is None:
96                         self.iface = 'eth0'
97                 self.networklist = None
98                 self.device = None
99                 self.mounts = None
100                 self.expanded = []
101                 self.cache_ttl = 604800 #Seconds cache is considered valid, 7 Days should be ok
102                 self.cache_file = '/etc/enigma2/networkbrowser.cache' #Path to cache directory
103
104                 self["closetext"] = Label(_("Close"))
105                 self["mounttext"] = Label(_("Mounts management"))
106                 self["rescantext"] = Label(_("Rescan network"))
107                 self["infotext"] = Label(_("Press OK to mount!"))
108                 self["searchtext"] = Label(_("Scan IP"))
109
110                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
111                 {
112                         "ok": self.go,
113                         "back": self.close,
114                         "red": self.close,
115                         "green": self.keyGreen,
116                         "yellow": self.keyYellow,
117                         "blue": self.keyBlue,
118                 })
119
120                 self.list = []
121                 self.statuslist = []
122                 self.listindex = 0
123                 self["list"] = List(self.list)
124                 self["list"].onSelectionChanged.append(self.selectionChanged)
125
126                 self.onLayoutFinish.append(self.startRun)
127                 self.onShown.append(self.setWindowTitle)
128                 self.onClose.append(self.cleanup)
129                 self.Timer = eTimer()
130                 self.Timer.callback.append(self.TimerFire)
131
132         def cleanup(self):
133                 del self.Timer
134                 iAutoMount.stopMountConsole()
135                 iNetwork.stopRestartConsole()
136                 iNetwork.stopGetInterfacesConsole()
137
138         def startRun(self):
139                 self.setStatus('update')
140                 self.mounts = iAutoMount.getMountsList()
141                 self["infotext"].hide()
142                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
143                 if self.cache_ttl > 0 and self.vc != 0:
144                         self.process_NetworkIPs()
145                 else:
146                         self.Timer.start(3000)
147
148         def TimerFire(self):
149                 self.Timer.stop()
150                 self.process_NetworkIPs()
151
152         def setWindowTitle(self):
153                 self.setTitle(_("Browse network neighbourhood"))
154
155         def keyGreen(self):
156                 self.session.open(AutoMountManager, None, self.skin_path)
157
158         def keyYellow(self):
159                 if (os_path.exists(self.cache_file) == True):
160                         remove(self.cache_file)
161                 self.startRun()
162
163         def keyBlue(self):
164                 self.session.openWithCallback(self.scanIPclosed,ScanIP)
165
166         def scanIPclosed(self,result):
167                 if result:
168                         print "got IP:",result
169                         nwlist = []
170                         if len(result):
171                                 strIP = str(result) + "/24"
172                                 nwlist.append(netscan.netzInfo(strIP))
173                                 self.networklist = nwlist[0]
174                 if len(self.networklist) > 0:
175                         self.updateHostsList()
176
177         def setStatus(self,status = None):
178                 if status:
179                         self.statuslist = []
180                         if status == 'update':
181                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/update.png"))
182                                 self.statuslist.append(( ['info'], statuspng, _("Searching your network. Please wait..."), None, None, None, None ))
183                                 self['list'].setList(self.statuslist)
184                         elif status == 'error':
185                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/error.png"))
186                                 self.statuslist.append(( ['info'], statuspng, _("No network devices found!"), None, None, None, None ))
187                                 self['list'].setList(self.statuslist)
188
189         def process_NetworkIPs(self):
190                 self.inv_cache = 0
191                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
192                 if self.cache_ttl > 0 and self.vc != 0:
193                         print 'Loading network cache from ',self.cache_file
194                         try:
195                                 self.networklist = load_cache(self.cache_file)
196                         except:
197                                 self.inv_cache = 1
198                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
199                         print 'Getting fresh network list'
200                         self.networklist = self.getNetworkIPs()
201                         write_cache(self.cache_file, self.networklist)
202                 if len(self.networklist) > 0:
203                         self.updateHostsList()
204                 else:
205                         self.setStatus('error')
206
207         def getNetworkIPs(self):
208                 nwlist = []
209                 sharelist = []
210                 self.IP = iNetwork.getAdapterAttribute(self.iface, "ip")
211                 if len(self.IP):
212                         strIP = str(self.IP[0]) + "." + str(self.IP[1]) + "." + str(self.IP[2]) + ".0/24"
213                         nwlist.append(netscan.netzInfo(strIP))
214                 tmplist = nwlist[0]
215                 return tmplist
216
217         def getNetworkShares(self,hostip,hostname,devicetype):
218                 sharelist = []
219                 self.sharecache_file = None
220                 self.sharecache_file = '/etc/enigma2/' + hostname.strip() + '.cache' #Path to cache directory
221                 if os_path.exists(self.sharecache_file):
222                         print 'Loading userinfo from ',self.sharecache_file
223                         try:
224                                 self.hostdata = load_cache(self.sharecache_file)
225                                 username = self.hostdata['username']
226                                 password = self.hostdata['password']
227                         except:
228                                 username = "username"
229                                 password = "password"
230                 else:
231                         username = "username"
232                         password = "password"
233
234                 if devicetype == 'unix':
235                         smblist=netscan.smbShare(hostip,hostname,username,password)
236                         for x in smblist:
237                                 if len(x) == 6:
238                                         if x[3] != 'IPC$':
239                                                 sharelist.append(x)
240                         nfslist=netscan.nfsShare(hostip,hostname)
241                         for x in nfslist:
242                                 if len(x) == 6:
243                                         sharelist.append(x)
244                 else:
245                         smblist=netscan.smbShare(hostip,hostname,username,password)
246                         for x in smblist:
247                                 if len(x) == 6:
248                                         if x[3] != 'IPC$':
249                                                 sharelist.append(x)
250                 return sharelist
251
252         def updateHostsList(self):
253                 self.list = []
254                 self.network = {}
255                 for x in self.networklist:
256                         if not self.network.has_key(x[2]):
257                                 self.network[x[2]] = []
258                         self.network[x[2]].append((NetworkDescriptor(name = x[1], description = x[2]), x))
259                 
260                 for x in self.network.keys():
261                         hostentry = self.network[x][0][1]
262                         name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
263                         expandableIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
264                         self.list.append(( hostentry, expandableIcon, name, None, None, None, None ))
265
266                 if len(self.list):
267                         for entry in self.list:
268                                 entry[0][2]= "%3s.%3s.%3s.%3s" % tuple(entry[0][2].split("."))
269                         self.list.sort(key=lambda x: x[0][2])
270                         for entry in self.list:
271                                 entry[0][2]= entry[0][2].replace(" ", "")
272                 self["list"].setList(self.list)
273                 self["list"].setIndex(self.listindex)
274
275         def updateNetworkList(self):
276                 self.list = []
277                 self.network = {}
278                 self.mounts = iAutoMount.getMountsList() # reloading mount list
279                 for x in self.networklist:
280                         if not self.network.has_key(x[2]):
281                                 self.network[x[2]] = []
282                         self.network[x[2]].append((NetworkDescriptor(name = x[1], description = x[2]), x))
283                 self.network.keys().sort()
284                 for x in self.network.keys():
285                         if self.network[x][0][1][3] == '00:00:00:00:00:00':
286                                 self.device = 'unix'
287                         else:
288                                 self.device = 'windows'
289                         if x in self.expanded:
290                                 networkshares = self.getNetworkShares(x,self.network[x][0][1][1].strip(),self.device)
291                                 hostentry = self.network[x][0][1]
292                                 name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
293                                 expandedIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
294                                 self.list.append(( hostentry, expandedIcon, name, None, None, None, None ))
295                                 for share in networkshares:
296                                         self.list.append(self.BuildNetworkShareEntry(share))
297                         else: # HOSTLIST - VIEW
298                                 hostentry = self.network[x][0][1]
299                                 name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
300                                 expandableIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
301                                 self.list.append(( hostentry, expandableIcon, name, None, None, None, None ))
302                 if len(self.list):
303                         for entry in self.list:
304                                 entry[0][2]= "%3s.%3s.%3s.%3s" % tuple(entry[0][2].split("."))
305                         self.list.sort(key=lambda x: x[0][2])
306                         for entry in self.list:
307                                 entry[0][2]= entry[0][2].replace(" ", "")
308                 self["list"].setList(self.list)
309                 self["list"].setIndex(self.listindex)
310
311         def BuildNetworkShareEntry(self,share):
312                 verticallineIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/verticalLine.png"))
313                 sharetype = share[0]
314                 localsharename = share[1]
315                 sharehost = share[2]
316
317                 if sharetype == 'smbShare':
318                         sharedir = share[3]
319                         sharedescription = share[5]
320                 else:
321                         sharedir = share[4]
322                         sharedescription = share[3]
323
324                 if sharetype == 'nfsShare':
325                         newpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/i-nfs.png"))
326                 else:
327                         newpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/i-smb.png"))
328
329                 self.isMounted = False
330                 for sharename, sharedata in self.mounts.items():
331                         if sharedata['ip'] == sharehost:
332                                 if sharetype == 'nfsShare' and sharedata['mounttype'] == 'nfs':
333                                         if sharedir == sharedata['sharedir']:
334                                                 if sharedata["isMounted"] is True:
335                                                         self.isMounted = True
336                                 if sharetype == 'smbShare' and sharedata['mounttype'] == 'cifs':
337                                         if sharedir == sharedata['sharedir']:
338                                                 if sharedata["isMounted"] is True:
339                                                         self.isMounted = True
340                 if self.isMounted is True:
341                         isMountedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/ok.png"))
342                 else:
343                         isMountedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/cancel.png"))
344
345                 return((share, verticallineIcon, None, sharedir, sharedescription, newpng, isMountedpng))
346
347         def selectionChanged(self):
348                 current = self["list"].getCurrent()
349                 self.listindex = self["list"].getIndex()
350                 if current:
351                         if current[0][0] in ("nfsShare", "smbShare"):
352                                 self["infotext"].show()
353                         else:
354                                 self["infotext"].hide()
355
356         def go(self):
357                 sel = self["list"].getCurrent()
358                 if sel is None:
359                         return
360                 if len(sel[0]) <= 1:
361                         return
362                 selectedhost = sel[0][2]
363                 selectedhostname = sel[0][1]
364
365                 self.hostcache_file = None
366                 if sel[0][0] == 'host': # host entry selected
367                         if selectedhost in self.expanded:
368                                 self.expanded.remove(selectedhost)
369                                 self.updateNetworkList()
370                         else:
371                                 self.hostcache_file = '/etc/enigma2/' + selectedhostname.strip() + '.cache' #Path to cache directory
372                                 if os_path.exists(self.hostcache_file):
373                                         print 'Loading userinfo cache from ',self.hostcache_file
374                                         try:
375                                                 self.hostdata = load_cache(self.hostcache_file)
376                                                 self.passwordQuestion(False)
377                                         except:
378                                                 self.session.openWithCallback(self.passwordQuestion, MessageBox, (_("Do you want to enter a username and password for this host?\n") ) )
379                                 else:
380                                         self.session.openWithCallback(self.passwordQuestion, MessageBox, (_("Do you want to enter a username and password for this host?\n") ) )
381                 if sel[0][0] == 'nfsShare': # share entry selected
382                         self.openMountEdit(sel[0])
383                 if sel[0][0] == 'smbShare': # share entry selected
384                         self.openMountEdit(sel[0])
385
386         def passwordQuestion(self, ret = False):
387                 sel = self["list"].getCurrent()
388                 selectedhost = sel[0][2]
389                 selectedhostname = sel[0][1]
390                 if (ret == True):
391                         self.session.openWithCallback(self.UserDialogClosed, UserDialog, self.skin_path, selectedhostname.strip())
392                 else:
393                         if sel[0][0] == 'host': # host entry selected
394                                 if selectedhost in self.expanded:
395                                         self.expanded.remove(selectedhost)
396                                 else:
397                                         self.expanded.append(selectedhost)
398                                 self.updateNetworkList()
399                         if sel[0][0] == 'nfsShare': # share entry selected
400                                 self.openMountEdit(sel[0])
401                         if sel[0][0] == 'smbShare': # share entry selected
402                                 self.openMountEdit(sel[0])
403
404         def UserDialogClosed(self, *ret):
405                 if ret is not None and len(ret):
406                         self.go()
407
408         def openMountEdit(self, selection):
409                 if selection is not None and len(selection):
410                         mounts = iAutoMount.getMountsList()
411                         if selection[0] == 'nfsShare': # share entry selected
412                                 #Initialize blank mount enty
413                                 data = { 'isMounted': False, 'active': False, 'ip': False, 'sharename': False, 'sharedir': False, 'username': False, 'password': False, 'mounttype' : False, 'options' : False }
414                                 # add data
415                                 data['mounttype'] = 'nfs'
416                                 data['active'] = True
417                                 data['ip'] = selection[2]
418                                 data['sharename'] = selection[1]
419                                 data['sharedir'] = selection[4]
420                                 data['options'] = "rw,nolock"
421
422                                 for sharename, sharedata in mounts.items():
423                                         if sharedata['ip'] == selection[2] and sharedata['sharedir'] == selection[4]:
424                                                 data = sharedata
425                                 self.session.openWithCallback(self.MountEditClosed,AutoMountEdit, self.skin_path, data)
426                         if selection[0] == 'smbShare': # share entry selected
427                                 #Initialize blank mount enty
428                                 data = { 'isMounted': False, 'active': False, 'ip': False, 'sharename': False, 'sharedir': False, 'username': False, 'password': False, 'mounttype' : False, 'options' : False }
429                                 # add data
430                                 data['mounttype'] = 'cifs'
431                                 data['active'] = True
432                                 data['ip'] = selection[2]
433                                 data['sharename'] = selection[1]
434                                 data['sharedir'] = selection[3]
435                                 data['options'] = "rw"
436                                 self.sharecache_file = None
437                                 self.sharecache_file = '/etc/enigma2/' + selection[1].strip() + '.cache' #Path to cache directory
438                                 if os_path.exists(self.sharecache_file):
439                                         print 'Loading userinfo from ',self.sharecache_file
440                                         try:
441                                                 self.hostdata = load_cache(self.sharecache_file)
442                                                 data['username'] = self.hostdata['username']
443                                                 data['password'] = self.hostdata['password']
444                                         except:
445                                                 data['username'] = "username"
446                                                 data['password'] = "password"
447                                 else:
448                                         data['username'] = "username"
449                                         data['password'] = "password"
450
451                                 for sharename, sharedata in mounts.items():
452                                         if sharedata['ip'] == selection[2].strip() and sharedata['sharedir'] == selection[3].strip():
453                                                 data = sharedata
454                                 self.session.openWithCallback(self.MountEditClosed,AutoMountEdit, self.skin_path, data)
455
456         def MountEditClosed(self, returnValue = None):
457                 if returnValue == None:
458                         self.updateNetworkList()
459
460 class ScanIP(Screen):
461         skin = """
462                 <screen name="IPKGSource" position="100,100" size="550,80" title="IPKG source" >
463                         <widget name="text" position="10,10" size="530,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
464                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
465                         <widget name="closetext" position="20,50" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
466                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
467                         <widget name="edittext" position="170,50" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
468                 </screen>"""
469
470         def __init__(self, session):
471                 Screen.__init__(self, session)
472                 self.session = session
473                 text = ""
474
475                 desk = getDesktop(0)
476                 x= int(desk.size().width())
477                 y= int(desk.size().height())
478                 #print "[IPKGSource] mainscreen: current desktop size: %dx%d" % (x,y)
479
480                 self["closetext"] = Label(_("Cancel"))
481                 self["edittext"] = Label(_("OK"))
482
483                 if (y>=720):
484                         self["text"] = Input(text, maxSize=False, type=Input.TEXT)
485                 else:
486                         self["text"] = Input(text, maxSize=False, visible_width = 55, type=Input.TEXT)
487
488                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions","ShortcutActions"],
489                 {
490                         "ok": self.go,
491                         "back": self.exit,
492                         "red": self.exit,
493                         "green": self.go,
494                         "left": self.keyLeft,
495                         "right": self.keyRight,
496                         "home": self.keyHome,
497                         "end": self.keyEnd,
498                         "deleteForward": self.keyDeleteForward,
499                         "deleteBackward": self.keyDeleteBackward,
500                         "1": self.keyNumberGlobal,
501                         "2": self.keyNumberGlobal,
502                         "3": self.keyNumberGlobal,
503                         "4": self.keyNumberGlobal,
504                         "5": self.keyNumberGlobal,
505                         "6": self.keyNumberGlobal,
506                         "7": self.keyNumberGlobal,
507                         "8": self.keyNumberGlobal,
508                         "9": self.keyNumberGlobal,
509                         "0": self.keyNumberGlobal
510                 }, -1)
511
512                 self.onLayoutFinish.append(self.layoutFinished)
513
514         def exit(self):
515                 self.close(None)
516
517         def layoutFinished(self):
518                 self.setWindowTitle()
519                 self["text"].right()
520
521         def setWindowTitle(self):
522                 self.setTitle(_("Enter IP to scan..."))
523
524         def go(self):
525                 text = self["text"].getText()
526                 if text:
527                         self.close(text)
528
529         def keyLeft(self):
530                 self["text"].left()
531
532         def keyRight(self):
533                 self["text"].right()
534
535         def keyHome(self):
536                 self["text"].home()
537
538         def keyEnd(self):
539                 self["text"].end()
540
541         def keyDeleteForward(self):
542                 self["text"].delete()
543
544         def keyDeleteBackward(self):
545                 self["text"].deleteBackward()
546
547         def keyNumberGlobal(self, number):
548                 self["text"].number(number)
549