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.
574 lines
23 KiB
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("trinity1"))
|
|
self.setPixmap(0,UserIcon("trinity"+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
|