summaryrefslogtreecommitdiffstats
path: root/mountconfig/SMBShareSelectDialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'mountconfig/SMBShareSelectDialog.py')
-rw-r--r--mountconfig/SMBShareSelectDialog.py573
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