diff options
Diffstat (limited to 'mountconfig/SMBShareSelectDialog.py')
-rw-r--r-- | mountconfig/SMBShareSelectDialog.py | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/mountconfig/SMBShareSelectDialog.py b/mountconfig/SMBShareSelectDialog.py new file mode 100644 index 0000000..171cf5d --- /dev/null +++ b/mountconfig/SMBShareSelectDialog.py @@ -0,0 +1,573 @@ +########################################################################### +# 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 kdeui import * +from kdecore import * +from kio 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 = KListView(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(): + KListView.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(KListViewItem): + # 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): + KListViewItem.__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 |