You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tde-guidance/mountconfig/SMBShareSelectDialog.py

574 lines
23 KiB

###########################################################################
# SMBShareSelectDialog.py - Dialog for selecting an SMB share on a network#
# ------------------------------ #
# begin : Tue Oct 30 2004 #
# copyright : (C) 2004 by Simon Edwards #
# email : simon@simonzone.com #
# #
###########################################################################
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
###########################################################################
from qt import *
from tdeui import *
from tdecore import *
from tdeio import *
############################################################################
class SMBShareSelectDialog(KDialogBase):
STATUS_IDLE = 0
STATUS_SEARCH_TOP_LEVEL = 1
STATUS_SEARCH = 2
STATUS_RESOLVE = 3
########################################################################
def __init__(self,parent,name=None):
super(SMBShareSelectDialog,self).__init__(parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel)
self.updatinggui = False
self.resize(600,400)
vbox = self.makeVBoxMainWidget()
hbox = QHBox(vbox)
hbox.setSpacing(self.spacingHint())
tmplabel = QLabel(hbox)
tmplabel.setPixmap(UserIcon("hi32-samba"))
hbox.setStretchFactor(tmplabel,0)
self.headinglabel = QLabel(hbox)
self.headinglabel.setText(i18n("Select a network share"))
hbox.setStretchFactor(self.headinglabel,1)
hbox2 = QHBox(vbox)
# The main treeview where the action happens.
self.treeview = TDEListView(hbox2)
self.treeview.addColumn("(hidden)")
self.treeview.header().hide()
self.treeview.setRootIsDecorated(True)
self.connect(self.treeview,SIGNAL("expanded(QListViewItem *)"),self.slotNodeExpanded)
self.connect(self.treeview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotNodeSelected)
self.connect(self.treeview,SIGNAL("clicked(QListViewItem *)"),self.slotClicked)
self.dirlister = KDirLister()
self.dirlister.setDirOnlyMode(True)
self.dirlister.setAutoUpdate(False)
self.dirlister.setAutoErrorHandlingEnabled(True,self)
self.connect(self.dirlister,SIGNAL("newItems(const KFileItemList &)"),self.slotNewItems)
self.connect(self.dirlister,SIGNAL("completed()"),self.slotDirListCompleted)
self.connect(self.dirlister,SIGNAL("canceled()"),self.slotDirListCanceled)
self.connect(self.dirlister,SIGNAL("redirection(const KURL &,const KURL &)"),self.slotDirListRedirection)
self.enableButtonOK(False)
# The "Connect as" part
widget = QWidget(hbox2)
grid = QGridLayout(widget,6,4,KDialog.spacingHint())
grid.setRowStretch(5,1)
tmplabel = QLabel(widget)
tmplabel.setPixmap(UserIcon("hi16-password"))
grid.addWidget(tmplabel,0,0)
self.connectaslabel = QLabel(widget)
self.connectaslabel.setText("Connect to 'XXX' as:")
grid.addMultiCellWidget(self.connectaslabel,0,0,1,3)
self.guestradio = QRadioButton(widget)
self.guestradio.setChecked(True)
grid.addWidget(self.guestradio,1,1)
tmplabel = QLabel(widget)
tmplabel.setText(i18n("Guest"))
grid.addWidget(tmplabel,1,2)
self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked)
self.userradio = QRadioButton(widget)
grid.addWidget(self.userradio,2,1)
tmplabel = QLabel(widget)
tmplabel.setText(i18n("Username:"))
grid.addWidget(tmplabel,2,2)
self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked)
self.usernameedit = KLineEdit(widget)
grid.addWidget(self.usernameedit,2,3)
self.connect(self.usernameedit,SIGNAL("textChanged(const QString &)"),self.slotUsernameChanged)
tmplabel = QLabel(widget)
tmplabel.setText(i18n("Password:"))
grid.addWidget(tmplabel,3,2)
self.passwordedit = KLineEdit(widget)
grid.addWidget(self.passwordedit,3,3)
self.reconnectbutton = KPushButton(i18n("Reconnect now"),widget)
grid.addMultiCellWidget(self.reconnectbutton,4,4,1,3)
self.connect(self.reconnectbutton,SIGNAL("clicked()"),self.slotReconnectClicked)
self.dirlistertimer = None
########################################################################
def choose(self,currenturl):
self.lookupqueue = []
self.selecteditem = None
self.treeview.clear()
self.url_to_list_item_map = {}
# Fill the first level
root_url = KURL("smb:/")
self.rootitem = SMBShareListViewItem(self.treeview, i18n("Network Neighbourhood"), root_url, self)
self.searchurl = currenturl
self._updateConnectGUI()
self.enableButtonOK(False)
self._openDefaultURL()
self.spintimerid = self.startTimer(250)
self.exec_loop()
self.stopResolve()
self.killTimer(self.spintimerid)
if self.result()==self.Accepted:
currenturl = self.selecteditem.getURL()
self.url_to_list_item_map = None
return currenturl
########################################################################
def _openDefaultURL(self):
if self.searchurl is not None:
rc = self.rootitem.selectURL(self.searchurl)
if rc==self.rootitem.OPEN_SUCCESS:
self.currenturl = self.searchurl
self.searchurl = None
self.enableButtonOK(True)
elif rc==self.rootitem.OPEN_FAIL or rc==self.rootitem.OPEN_SUCCESS_INVALID:
self.searchurl = None
########################################################################
def stopResolve(self):
if self.dirlistertimer is not None:
self.killTimer(self.dirlistertimer)
self.dirlister.stop()
for item in self.lookupqueue:
item.cancelResolve()
self.lookupqueue = []
self.searchurl = None # Stop trying to open this URL too.
########################################################################
def setOpen(self,item,open):
if item.isResolved():
TDEListView.setOpen(self.treeview,item,open)
else:
item.startResolve(True)
########################################################################
def appendToResolveQueue(self,item):
if item not in self.lookupqueue:
self.lookupqueue.append(item)
self._startDirLister()
return True
else:
return False
########################################################################
def slotNodeExpanded(self,item):
self.setOpen(item,True)
########################################################################
def slotClicked(self):
if self.treeview.selectedItem() is None:
self.selecteditem = None
self._updateConnectGUI()
self.enableButtonOK(False)
########################################################################
def slotNodeSelected(self,item):
self.selecteditem = item
self._updateConnectGUI()
self.enableButtonOK(item.getLevel()==item.LEVEL_DIR)
if not self.selecteditem.isResolved():
self.selecteditem.startResolve(False)
########################################################################
def slotNewItems(self,items):
for entry in items:
newitem = SMBShareListViewItem(self.lookupqueue[0], unicode(entry.name()), KURL(entry.url()), self)
self.url_to_list_item_map[unicode(entry.url().prettyURL())] = newitem
# Notice how I copied the KURL object and QString (to a python string)
########################################################################
def slotDirListCompleted(self):
item = self.lookupqueue[0]
item.setBusyIcon(False)
del self.lookupqueue[0]
item.resolveComplete()
self._startDirLister()
self._openDefaultURL()
########################################################################
def slotDirListCanceled(self):
self.stopResolve()
########################################################################
def slotDirListRedirection(self,oldUrl,newUrl):
list_item = self.url_to_list_item_map[unicode(oldUrl.prettyURL())]
list_item.setURL(KURL(newUrl)) # The copy is important.
# Reselect the selected node. (This will force a refresh).
if self.selecteditem is not None:
self.updatinggui = True
self.slotNodeSelected(self.selecteditem)
self.updatinggui = False
########################################################################
def slotUsernameChanged(self,newtext):
self.reconnectbutton.setEnabled(self.usernameedit.text()!="")
########################################################################
def slotReconnectClicked(self):
if self.updatinggui:
return
self.updatinggui = True
if self.selecteditem is None: # Sanity check.
return
# The user wants to change how we connect to this remote machine.
machineitem = self.selecteditem.getMachineItem()
if machineitem is None:
return # Shouldn't happen.
self.stopResolve()
# Grab the URL object before we delete the listviewitem that holds it.
selectedurl = self.selecteditem.getURL()
# Close up the machine item and remove the items under the machine item.
machineitem.unresolve()
# Set the username/password for the machine item.
if self.guestradio.isChecked():
machineitem.getURL().setUser(QString.null)
machineitem.getURL().setPass(QString.null)
selectedurl.setUser(QString.null)
selectedurl.setPass(QString.null)
else:
machineitem.getURL().setUser(self.usernameedit.text())
machineitem.getURL().setPass(self.passwordedit.text())
selectedurl.setUser(self.usernameedit.text())
selectedurl.setPass(self.passwordedit.text())
self.selecteditem = None
self._updateConnectGUI()
self.searchurl = selectedurl
self._openDefaultURL()
self.updatinggui = False
########################################################################
def _startDirLister(self):
if self.dirlistertimer is None:
# Check the URL lister queue the next the event loop runs.
# Don't get all "recursed up"!
self.dirlistertimer = self.startTimer(0)
########################################################################
def timerEvent(self,event):
KDialogBase.timerEvent(self,event)
if self.spintimerid==event.timerId():
# Spin the current folder icon
if len(self.lookupqueue)!=0:
self.lookupqueue[0].setBusyIcon(True)
elif event.timerId()==self.dirlistertimer:
self.killTimer(self.dirlistertimer)
self.dirlistertimer = None
if self.dirlister.isFinished():
if len(self.lookupqueue)!=0:
self.dirlister.openURL(self.lookupqueue[0].getURL())
########################################################################
def slotGuestRadioClicked(self,state):
if self.updatinggui:
return
self.updatinggui = True
if self.selecteditem is None:
return
if state==QButton.Off:
self.guestradio.setChecked(True)
self.userradio.setChecked(False)
self.passwordedit.setEnabled(False)
self.usernameedit.setEnabled(False)
selectedurl = self.selecteditem.getURL()
self.reconnectbutton.setEnabled(unicode(selectedurl.user())!="")
self.updatinggui = False
########################################################################
def slotUserRadioClicked(self,state):
if self.updatinggui:
return
self.updatinggui = True
if state==QButton.Off:
self.userradio.setChecked(True)
self.guestradio.setChecked(False)
self.passwordedit.setEnabled(True)
self.usernameedit.setEnabled(True)
username = unicode(self.usernameedit.text())
password = unicode(self.passwordedit.text())
selectedurl = self.selecteditem.getURL()
if username!="" and password!="" and \
((unicode(selectedurl.user())!=username) or (unicode(selectedurl.pass_())!=password)):
self.reconnectbutton.setEnabled(True)
else:
self.reconnectbutton.setEnabled(False)
self.updatinggui = False
########################################################################
def _updateConnectGUI(self):
if self.selecteditem is not None:
selectedurl = self.selecteditem.getURL()
self.guestradio.setEnabled(True)
self.userradio.setEnabled(True)
self.usernameedit.setEnabled(selectedurl.hasUser())
self.passwordedit.setEnabled(selectedurl.hasUser())
self.connectaslabel.setText(i18n("Connect to '%1' as:").arg(selectedurl.host()))
if selectedurl.hasUser():
self.guestradio.setChecked(False)
self.userradio.setChecked(True)
self.usernameedit.setText(selectedurl.user())
self.passwordedit.setText(selectedurl.pass_())
else:
self.guestradio.setChecked(True)
self.userradio.setChecked(False)
self.passwordedit.setText("")
self.usernameedit.setText("")
self.reconnectbutton.setEnabled(False)
else:
self.guestradio.setChecked(True)
self.userradio.setChecked(False)
self.guestradio.setEnabled(False)
self.userradio.setEnabled(False)
self.passwordedit.setEnabled(False)
self.usernameedit.setEnabled(False)
self.connectaslabel.setText(i18n("Connect to 'machine' as:"))
self.guestradio.setChecked(True)
self.userradio.setChecked(False)
self.passwordedit.setText("")
self.usernameedit.setText("")
self.reconnectbutton.setEnabled(False)
############################################################################
class SMBShareListViewItem(TDEListViewItem):
# Return codes for selectURL()
OPEN_SUCCESS = 1
OPEN_SUCCESS_INVALID = 2
OPEN_FAIL = 0
OPEN_BUSY = 3
# Node types.
LEVEL_ROOT = 0
LEVEL_WORKGROUP = 1
LEVEL_MACHINE = 2
LEVEL_DIR = 3 # and deeper.
########################################################################
def __init__(self,parentitem,name,url,smbdialog):
TDEListViewItem.__init__(self,parentitem,name)
if not isinstance(parentitem,SMBShareListViewItem):
self._setIcon(0)
self.setSelectable(False)
else:
self._setIcon(parentitem.depth()+1)
self.setSelectable(parentitem.getLevel()>=self.LEVEL_WORKGROUP)
self.setExpandable(True)
if url.hasPath() and url.path(-1)!="/":
parts = [x for x in unicode(url.path(-1)).split("/") if x!=""]
self.component = parts[-1].lower()
elif url.hasHost():
self.component = unicode(url.host()).lower()
else:
self.component = None
self.smbdialog = smbdialog
self.resolved = False
self.url = url
self.autoopen = False
self.animationcounter = 0
########################################################################
def getURL(self):
return self.url
########################################################################
def setURL(self,url):
self.url = url
########################################################################
def getComponent(self):
return self.component
########################################################################
def isResolved(self):
return self.resolved
########################################################################
def startResolve(self,autoopen):
if self.smbdialog.appendToResolveQueue(self):
self.setBusyIcon(True)
self.autoopen = self.autoopen or autoopen
########################################################################
def cancelResolve(self):
self.setBusyIcon(False)
self.autoopen = False
self.resolved = False
while self.childCount()!=0:
self.takeItem(self.firstChild())
self.setOpen(False)
########################################################################
def unresolve(self):
self.cancelResolve()
########################################################################
def getMachineItem(self):
if self.getLevel()<=self.LEVEL_WORKGROUP:
return None
elif self.getLevel()==self.LEVEL_DIR:
return self.parent().getMachineItem()
else:
return self
########################################################################
def _setIcon(self,depth):
if depth==self.LEVEL_ROOT or depth==self.LEVEL_WORKGROUP:
self.setPixmap(0,SmallIcon("network"))
elif depth==self.LEVEL_MACHINE:
self.setPixmap(0,SmallIcon("network_local"))
else:
self.setPixmap(0,SmallIcon("folder"))
########################################################################
def setBusyIcon(self,on):
if on:
self.setPixmap(0,UserIcon("kde1"))
self.setPixmap(0,UserIcon("kde"+str(self.animationcounter+1)))
self.animationcounter += 1
self.animationcounter %= 6
else:
self._setIcon(self.depth())
########################################################################
def resolveComplete(self):
self.resolved = True
if self.childCount()==0:
self.setExpandable(False)
else:
if self.autoopen:
self.setOpen(True)
########################################################################
def getLevel(self):
if self.depth()>self.LEVEL_DIR:
return self.LEVEL_DIR
else:
return self.depth()
########################################################################
# This is one of the more nasty pieces of code. It tries to select a given
# URL in the treeview. Opening and resolving the contents of URLs as neccessary
# while at the same time trying not have list everything on the network.
# Another wrinkle is that the treeview contains a level of workgroups while
# a given URL omits the workgroup a jumps directly to the machine name.
def selectURL(self,targeturl):
path = unicode(targeturl.path(-1))
parts = [x for x in path.split("/") if x!=""]
if targeturl.hasHost():
tmp = [targeturl.host()]
tmp.extend(parts)
parts = tmp
if self.getLevel()==self.LEVEL_ROOT:
# Root item.
# We should first resolve our contents. the Workgroups.
if not self.resolved:
self.startResolve(True)
return self.OPEN_BUSY
else:
if len(parts)==0:
# The URL is really short, and is not selectable.
# So we just say that we couldn't resolve/select it.
return self.OPEN_SUCCESS_INVALID
else:
# OK, the url has some more components. Ask each of the Workgroup items
# to help resolve it.
kid = self.firstChild()
while kid is not None:
rc = kid.selectURL(targeturl)
if rc==self.OPEN_SUCCESS or rc==self.OPEN_SUCCESS_INVALID:
kid.setOpen(True)
return rc
elif rc==self.OPEN_BUSY:
return rc
kid = kid.nextSibling()
return self.OPEN_FAIL
elif self.getLevel()==self.LEVEL_WORKGROUP:
# Workgroup level
if not self.resolved:
self.startResolve(False)
return self.OPEN_BUSY
else:
# Find a child named after the next part of the URL path.
kid = self.firstChild()
partname = parts[0].lower()
while kid is not None:
if kid.getComponent()==partname:
self.setOpen(True)
return kid.selectURL(targeturl)
kid = kid.nextSibling()
return self.OPEN_FAIL
elif self.getLevel()==self.LEVEL_MACHINE:
# Machine level
if len(parts)==1:
# The URL is successfully resolved but is not selectable!
return self.OPEN_SUCCESS_INVALID
else:
# Share level
if len(parts)==self.depth()-1:
self.smbdialog.treeview.setSelected(self,True)
return self.OPEN_SUCCESS
if not self.resolved:
self.startResolve(True)
return self.OPEN_BUSY
else:
# Find a child item that matches the next part of the URL path.
kid = self.firstChild()
partname = parts[self.depth()-1].lower()
while kid is not None:
if kid.getComponent()==partname:
return kid.selectURL(targeturl)
kid = kid.nextSibling()
return self.OPEN_FAIL