diskmanipulator.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. # $Id$
  2. from PyQt4 import QtCore, QtGui
  3. from qt_utils import connect
  4. class Diskmanipulator(QtCore.QObject):
  5. def __init__(self, mainwindow, mediaModel, machineManager, bridge):
  6. #QtCore.QAbstractListModel.__init__(self)
  7. QtCore.QObject.__init__(self)
  8. self.__dmDialog = None
  9. self.__mainwindow = mainwindow
  10. self.__ui = None
  11. self.__comboBox = None
  12. self.__mediaModel = mediaModel
  13. self.__machineManager = machineManager
  14. self.__bridge = bridge
  15. self.__cwd = {}
  16. self.__virtualDriveSlot = mediaModel.getMediaSlotByName('virtual_drive')
  17. self.__mediaSlot = self.__virtualDriveSlot
  18. self.__localDir = QtCore.QDir.home()
  19. self.__dirModel = QtGui.QDirModel()
  20. self.__virtualDriveSlot.slotDataChanged.connect(self.__diskChanged)
  21. mediaModel.mediaSlotAdded.connect(self.__driveAdded)
  22. mediaModel.mediaSlotRemoved.connect(self.__driveRemoved)
  23. self.__cwd['virtual_drive'] = ''
  24. ##quick hack to have some values available
  25. #self.__cwd['diska'] = '/'
  26. #self.__cwd['diskb'] = '/'
  27. #self.__cwd['hda'] = '/'
  28. def __rebuildUI(self):
  29. comboBox = self.__comboBox
  30. # Only if ui is initialized
  31. if comboBox is not None:
  32. comboBox.clear()
  33. for device in self.__mediaModel.iterDriveNames():
  34. comboBox.addItem(QtCore.QString(device))
  35. #rebuilding the combobox will show 'virtual drive'
  36. #selected so we set this as current media and
  37. #update the directory listing
  38. self.__mediaSlot = self.__virtualDriveSlot
  39. self.refreshDir()
  40. def show(self):
  41. dialog = self.__dmDialog
  42. if dialog is None:
  43. self.__dmDialog = dialog = QtGui.QDialog(
  44. # TODO: Find a better way to get the real parent :-)
  45. self.__mainwindow,
  46. QtCore.Qt.Dialog
  47. | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowSystemMenuHint
  48. )
  49. # Setup UI made in Qt Designer.
  50. from ui_diskmanipulator import Ui_diskManipulator
  51. ui = Ui_diskManipulator()
  52. ui.setupUi(dialog)
  53. self.__ui = ui
  54. self.__comboBox = ui.mediaComboBox
  55. # TODO: currently only one 'media' update handler allowed!!
  56. #bridge.registerUpdate('media', self.__updateMedium)
  57. #bridge.registerUpdatePrefix(
  58. # 'hardware',
  59. # ( 'disk', 'hd' ),
  60. # self.__updateHardware
  61. # )
  62. # TODO: how are handling the 'virtual_drive' in the above case ??
  63. # Set the msxDirTable as needed
  64. msxDir = self.__ui.msxDirTable
  65. msxDir.setRowCount(0)
  66. labels = QtCore.QStringList([ 'File Name', 'Atributes', 'Size' ])
  67. msxDir.setHorizontalHeaderLabels(labels)
  68. msxDir.verticalHeader().hide()
  69. msxDir.horizontalHeader().setResizeMode(
  70. 0, QtGui.QHeaderView.Interactive
  71. )
  72. msxDir.horizontalHeader().setResizeMode(
  73. 1, QtGui.QHeaderView.Interactive
  74. )
  75. msxDir.horizontalHeader().setResizeMode(
  76. 2, QtGui.QHeaderView.Stretch
  77. )
  78. msxDir.setShowGrid(0)
  79. msxDir.setSelectionBehavior(
  80. QtGui.QAbstractItemView.SelectionBehavior(1)
  81. )
  82. msxDir.setSelectionMode(
  83. QtGui.QAbstractItemView.SelectionMode(3)
  84. )
  85. hostDirTable = ui.hostDirView
  86. dirModel = self.__dirModel
  87. hostDirTable.setModel(dirModel)
  88. self.setLocalDir( QtCore.QDir.currentPath() )
  89. hostDirTable.setSelectionBehavior(
  90. QtGui.QAbstractItemView.SelectionBehavior(1)
  91. )
  92. hostDirTable.setSelectionMode(
  93. QtGui.QAbstractItemView.SelectionMode(3)
  94. )
  95. # Connect signals.
  96. # TODO: Find out how to do this correctly, since this doesn't work.
  97. # Maybe I should throw events from the closeEvent handler
  98. # from the mainwindow?
  99. #connect(
  100. # self.__mainwindow,
  101. # 'close()',
  102. # dialog.close
  103. # )
  104. connect(
  105. ui.openImageButton,
  106. 'clicked()',
  107. self.browseImage
  108. )
  109. connect(
  110. ui.saveasnewButton,
  111. 'clicked()',
  112. self.saveasnewImage
  113. )
  114. connect(
  115. ui.newImageButton,
  116. 'clicked()',
  117. self.newImage
  118. )
  119. connect(
  120. ui.dirReloadButton,
  121. 'clicked()',
  122. self.refreshDir
  123. )
  124. connect(
  125. ui.dirUpButton,
  126. 'clicked()',
  127. self.updir
  128. )
  129. connect(
  130. ui.dirNewButton,
  131. 'clicked()',
  132. self.mkdir
  133. )
  134. connect(
  135. ui.hostDirReloadButton,
  136. 'clicked()',
  137. self.refreshLocalDir
  138. )
  139. connect(
  140. ui.hostDirUpButton,
  141. 'clicked()',
  142. self.upLocalDir
  143. )
  144. connect(
  145. ui.hostDirNewButton,
  146. 'clicked()',
  147. self.mklocaldir
  148. )
  149. connect(
  150. ui.hostDirView,
  151. 'doubleClicked(QModelIndex)',
  152. self.doubleClickedLocalDir
  153. )
  154. connect(
  155. ui.msxDirTable,
  156. 'doubleClicked(QModelIndex)',
  157. self.doubleClickedMSXDir
  158. )
  159. connect(
  160. ui.importButton,
  161. 'clicked()',
  162. self.importFiles
  163. )
  164. connect(
  165. ui.exportButton,
  166. 'clicked()',
  167. self.exportFiles
  168. )
  169. connect(
  170. ui.mediaComboBox,
  171. 'activated(QString)',
  172. self.showMediaDir
  173. )
  174. connect(
  175. ui.hostDirComboBox,
  176. 'activated(QString)',
  177. self.setLocalDir
  178. )
  179. connect(
  180. ui.hostDirComboBox.lineEdit(),
  181. 'editingFinished()',
  182. self.editedLocalDir
  183. )
  184. self.__rebuildUI()
  185. dialog.show()
  186. dialog.raise_()
  187. dialog.activateWindow()
  188. def editedLocalDir(self):
  189. self.setLocalDir(self.__ui.hostDirComboBox.currentText())
  190. def setLocalDir(self, path):
  191. '''Show the content of the selected directory
  192. '''
  193. print 'selected:', path or '<nothing>'
  194. if not path:
  195. return
  196. historyBox = self.__ui.hostDirComboBox
  197. # if this is a dir then we alter the combobox
  198. # if the path doesn't exist (anymore) then we
  199. # remove it from the comboboc
  200. if QtCore.QDir(path).exists():
  201. # Insert path at the top of the list.
  202. historyBox.insertItem(0, path)
  203. historyBox.setCurrentIndex(0)
  204. # Remove duplicates of the path from the history.
  205. index = 1
  206. while index < historyBox.count():
  207. if historyBox.itemText(index) == path:
  208. historyBox.removeItem(index)
  209. else:
  210. index += 1
  211. self.__localDir = QtCore.QDir(path)
  212. hostDirTable = self.__ui.hostDirView
  213. hostDirTable.setRootIndex(
  214. self.__dirModel.index( path )
  215. )
  216. else:
  217. # Remove the path from the history.
  218. index = 0
  219. while index < historyBox.count():
  220. if historyBox.itemText(index) == path:
  221. historyBox.removeItem(index)
  222. else:
  223. index += 1
  224. def doubleClickedMSXDir(self, modelindex):
  225. attr = str( self.__ui.msxDirTable.item(modelindex.row(), 1).text() )
  226. if 'd' in attr:
  227. item = str(
  228. self.__ui.msxDirTable.item(modelindex.row(), 0).text()
  229. )
  230. print item
  231. if item == '.':
  232. self.refreshDir()
  233. elif item == '..':
  234. self.updir()
  235. else:
  236. slotName = self.__mediaSlot.getName()
  237. if self.__cwd[slotName] != '/':
  238. self.__cwd[slotName] += '/'
  239. self.__cwd[slotName] += item
  240. print self.__cwd[slotName]
  241. self.refreshDir()
  242. def doubleClickedLocalDir(self, modelindex):
  243. if self.__dirModel.isDir(modelindex):
  244. self.setLocalDir( self.__dirModel.filePath(modelindex) )
  245. def upLocalDir(self):
  246. self.__localDir.cdUp()
  247. self.setLocalDir(self.__localDir.path())
  248. #self.__dirModel.refresh()
  249. def refreshLocalDir(self):
  250. self.__dirModel.refresh()
  251. def saveasnewImage(self):
  252. browseTitle = 'Save As New Disk Image'
  253. imageSpec = 'Disk Images (*.dsk *.di? *.xsa *.zip *.gz);;All Files (*)'
  254. path = QtGui.QFileDialog.getSaveFileName(
  255. self.__ui.openImageButton,
  256. browseTitle,
  257. QtCore.QDir.currentPath(),
  258. imageSpec
  259. )
  260. if not path:
  261. return
  262. # save current media to path
  263. self.__bridge.command(
  264. 'diskmanipulator', 'savedsk',
  265. self.__mediaSlot.getName(), str(path)
  266. )()
  267. def newImage(self):
  268. browseTitle = 'Create New Disk Image'
  269. imageSpec = 'Disk Images (*.dsk *.di? *.xsa *.zip *.gz);;All Files (*)'
  270. path = QtGui.QFileDialog.getSaveFileName(
  271. self.__ui.openImageButton,
  272. browseTitle,
  273. QtCore.QDir.currentPath(),
  274. imageSpec
  275. )
  276. if not path:
  277. return
  278. from sizewizard import Sizewizard
  279. wizard = Sizewizard()
  280. wizard.exec_()
  281. size = wizard.getSizes()
  282. print 'wizard.getSizes()' + size
  283. # Ask if user is really, really sure
  284. if ' ' in size:
  285. text = self.tr(
  286. "<p>Are you sure you want to create this "
  287. "new partitioned disk?</p>"
  288. "<p>You can use this new diskimage as a "
  289. "harddisk for the IDE extension.<br>"
  290. "Don't forget that changing the HD will "
  291. "only work if you power off the emulated "
  292. "MSX first!</p>"
  293. )
  294. else:
  295. text = self.tr(
  296. "<p>Are you sure you want to create this "
  297. "new disk?</p>"
  298. "<p>This new disk image will automatically "
  299. "be inserted into the virtual drive</p>"
  300. )
  301. reply = QtGui.QMessageBox.question(
  302. self.__dmDialog,
  303. self.tr("Creating a new disk image"),
  304. text,
  305. QtGui.QMessageBox.Yes,
  306. QtGui.QMessageBox.Cancel)
  307. if reply == 0:
  308. return
  309. self.__bridge.command(
  310. 'diskmanipulator', 'create', str(path), *size.split(' ')
  311. )()
  312. if ' ' not in size:
  313. # insert the selected image in the 'virtual drive'
  314. self.__mediaSlot = self.__virtualDriveSlot
  315. self.__cwd['virtual_drive'] = '/'
  316. self.__bridge.command('virtual_drive', path)(self.refreshDir)
  317. # Set the combobox to the virtual_drive entry.
  318. # Go to the root of this disk and get the files, output
  319. # after the we get the reply stating that the diskimage is
  320. # inserted in the virtual_drive.
  321. def browseImage(self):
  322. browseTitle = 'Select Disk Image'
  323. imageSpec = 'Disk Images (*.dsk *.di? *.xsa *.zip *.gz);;All Files (*)'
  324. path = QtGui.QFileDialog.getOpenFileName(
  325. self.__ui.openImageButton,
  326. browseTitle,
  327. QtCore.QDir.currentPath(),
  328. imageSpec
  329. )
  330. if not path:
  331. return
  332. # Insert the selected image in the 'virtual drive'.
  333. self.__mediaSlot = self.__virtualDriveSlot
  334. self.__cwd['virtual_drive'] = '/'
  335. self.__bridge.command('virtual_drive', path)(self.refreshDir)
  336. # Set the combobox to the virtual_drive entry.
  337. # Go to the root of this disk and get the files, output
  338. # after the we get the reply stating that the diskimage
  339. # is inserted in the virtual_drive.
  340. def showMediaDir(self, media):
  341. self.__mediaSlot = self.__mediaModel.getMediaSlotByName(
  342. str(media), self.__machineManager.getCurrentMachineId()
  343. )
  344. self.refreshDir()
  345. def isUsableDisk(self, name):
  346. return name.startswith('disk') or name.startswith('hd') \
  347. or name == 'virtual_drive'
  348. def __diskChanged(self, slot):
  349. driveId = str(slot.getName())
  350. medium = slot.getMedium()
  351. if medium is None:
  352. path = ''
  353. else:
  354. path = str(medium.getPath())
  355. if self.isUsableDisk(driveId):
  356. print 'disk "%s" now contains image "%s" '% (driveId, path)
  357. if path == '':
  358. self.__cwd[driveId] = ''
  359. else:
  360. self.__cwd[driveId] = '/'
  361. # only if gui is visible ofcourse
  362. if driveId == self.__mediaSlot.getName() \
  363. and self.__comboBox is not None:
  364. self.refreshDir()
  365. def __driveAdded(self, name, machineId):
  366. driveId = str(name)
  367. if self.isUsableDisk(driveId):
  368. print 'drive "%s" added '% name
  369. self.__mediaModel.getMediaSlotByName(driveId,
  370. str(machineId)).slotDataChanged.connect(
  371. self.__diskChanged
  372. )
  373. self.__cwd[driveId] = '/'
  374. # only if gui is visible ofcourse
  375. if self.__comboBox is not None:
  376. self.__comboBox.addItem(QtCore.QString(driveId))
  377. def __driveRemoved(self, name, machineId):
  378. driveId = str(name)
  379. if self.isUsableDisk(driveId):
  380. print 'drive "%s" removed' % name
  381. comboBox = self.__comboBox
  382. # only if gui is visible ofcourse
  383. if comboBox is not None:
  384. index = comboBox.findText(QtCore.QString(driveId))
  385. comboBox.removeItem(index)
  386. if driveId == self.__mediaSlot.getName():
  387. self.__mediaSlot = self.__virtualDriveSlot
  388. self.refreshDir()
  389. def refreshDir(self):
  390. slotName = self.__mediaSlot.getName()
  391. path = self.__cwd[slotName]
  392. if path != '':
  393. self.__ui.cwdLine.setReadOnly(0)
  394. self.__ui.cwdLine.font().setItalic(0)
  395. self.__ui.cwdLine.setText(self.__cwd[slotName])
  396. self.__bridge.command(
  397. 'diskmanipulator', 'chdir',
  398. slotName, self.__cwd[slotName]
  399. )()
  400. self.__bridge.command(
  401. 'diskmanipulator', 'dir',
  402. slotName
  403. )( self.displayDir)
  404. else:
  405. # no disk inserted
  406. self.__ui.cwdLine.setReadOnly(1)
  407. self.__ui.cwdLine.font().setItalic(1)
  408. self.__ui.cwdLine.setText('<No disk inserted>')
  409. # clear will also erase the labels!
  410. self.__ui.msxDirTable.setRowCount(0)
  411. def displayDir(self, *value):
  412. '''Fills in the tablewidget with the output of the
  413. diskmanipulator dir command.
  414. '''
  415. entries = '\t'.join(value).split('\n')
  416. # clear will also erase the labels!
  417. self.__ui.msxDirTable.setRowCount(0)
  418. self.__ui.msxDirTable.setSortingEnabled(0)
  419. row = 0
  420. self.__ui.msxDirTable.setRowCount(len(entries) - 1)
  421. for entry in entries[ : -1]:
  422. data = entry.split('\t')
  423. fileNameItem = QtGui.QTableWidgetItem(data[0])
  424. # not editable etc etc
  425. fileNameItem.setFlags(
  426. QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
  427. )
  428. self.__ui.msxDirTable.setItem(row, 0, fileNameItem)
  429. fileAttrItem = QtGui.QTableWidgetItem(data[1])
  430. fileAttrItem.setFlags(
  431. QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
  432. )
  433. self.__ui.msxDirTable.setItem(row, 1, fileAttrItem)
  434. fileSizeItem = QtGui.QTableWidgetItem(data[2])
  435. fileSizeItem.setFlags(
  436. QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable
  437. )
  438. self.__ui.msxDirTable.setItem(row, 2, fileSizeItem)
  439. row += 1
  440. self.__ui.msxDirTable.setSortingEnabled(1)
  441. def updir(self):
  442. slotName = self.__mediaSlot.getName()
  443. path = self.__cwd[slotName]
  444. lijst = path.rsplit('/', 1)
  445. if lijst[1] == '': # maybe last character was already '/'...
  446. lijst = lijst[0].rsplit('/', 1)
  447. if path != '' and lijst[0] == '':
  448. path = '/'
  449. else:
  450. path = lijst[0]
  451. self.__cwd[slotName] = path
  452. self.refreshDir()
  453. def mklocaldir(self):
  454. title = 'New directory'
  455. newdir, ok = QtGui.QInputDialog.getText(
  456. self.__ui.dirNewButton,
  457. title,
  458. 'Enter folder name',
  459. QtGui.QLineEdit.Normal,
  460. )
  461. if not ok:
  462. return
  463. if self.__localDir.mkdir(newdir):
  464. self.__localDir.cd(newdir)
  465. self.setLocalDir(self.__localDir.path())
  466. def mkdir(self):
  467. title = 'New directory on MSX media'
  468. newdir, ok = QtGui.QInputDialog.getText(
  469. self.__ui.dirNewButton,
  470. title,
  471. 'Enter folder name',
  472. QtGui.QLineEdit.Normal,
  473. )
  474. if not ok:
  475. return
  476. slotName = self.__mediaSlot.getName()
  477. self.__bridge.command(
  478. 'diskmanipulator', 'chdir',
  479. slotName, self.__cwd[slotName]
  480. )()
  481. self.__bridge.command(
  482. 'diskmanipulator', 'mkdir',
  483. slotName, str( newdir )
  484. )()
  485. if self.__cwd[slotName] != '/':
  486. self.__cwd[slotName] += '/'
  487. self.__cwd[slotName] += str( newdir )
  488. self.refreshDir()
  489. def importFiles(self):
  490. # Get diskimage to work with
  491. diskimage = str( self.__comboBox.currentText() )
  492. print 'diskimage:' + diskimage
  493. # Make sure we are in the correct directory on the image
  494. path = self.__cwd[self.__mediaSlot.getName()]
  495. self.__bridge.command(
  496. 'diskmanipulator', 'chdir',
  497. diskimage, path
  498. )()
  499. #iterate over selected files
  500. path = str(self.__localDir.path())
  501. table = self.__ui.hostDirView
  502. for index in table.selectionModel().selectedIndexes():
  503. filename = str(self.__dirModel.filePath(index))
  504. print filename
  505. self.__bridge.command(
  506. 'diskmanipulator', 'import',
  507. diskimage, filename
  508. )()
  509. self.refreshDir()
  510. def exportFiles(self):
  511. diskimage = self.__comboBox.currentText()
  512. print 'diskimage:' + diskimage
  513. slotName = self.__mediaSlot.getName()
  514. self.__bridge.command(
  515. 'diskmanipulator', 'chdir',
  516. slotName, self.__cwd[slotName]
  517. )()
  518. # currently the diskmanipultor extracts entire subdirs... :-)
  519. msxdir = self.__ui.msxDirTable
  520. for index in range(msxdir.rowCount()):
  521. item = msxdir.item(index, 0)
  522. if item.isSelected():
  523. self.__bridge.command(
  524. 'diskmanipulator', 'export',
  525. diskimage,
  526. str(self.__localDir.path()),
  527. str(item.text())
  528. )( self.refreshLocalDir )