In the interests of scratching my own itch, attached is a patch that
allows basic copy-and-paste of widgets. It basically copies the set of
commands used to create the subtree being copied. It seems to work OK
but has the following known limitations:
It doesn't copy actual data. This is (I think) quite hard (but /not/
_very_ hard) to do if you want to prevent clashes with existing data of
the same name in the target document. So I'm getting the simple thing
done first.
Doesn't allow copying of the root widget. This isn't technically hard, I
just haven't decided where the best place to implement the logic that
says the a root widget being copied can be pasted into the root widget
of the target document is.
Additionally, there are some UI bugs (which were already there but not
so obvious). Principally the Edit menu doesn't work when the focus is on
the console. Does it sound reasonable to factor out the code for
creating menus and toolbars into its own module, rather than have it
interspersed among the various windows? When subwindows got focus, they
would be expected to deal with things like updating the menu
functionality to match expectations (e.g. Copy in the console window
should copy selected text, and so on).
? dialogs/__init__.pyc
? dialogs/aboutdialog.pyc
? dialogs/dataeditdialog.pyc
? dialogs/exceptiondialog.pyc
? dialogs/importdialog.pyc
? document/__init__.pyc
? document/commandinterface.pyc
? document/commandinterpreter.pyc
? document/doc.pyc
? document/simpleread.pyc
? setting/__init__.pyc
? setting/collections.pyc
? setting/controls.pyc
? setting/setting.pyc
? setting/settingdb.pyc
? setting/settings.pyc
? utils/__init__.pyc
? utils/fitlm.pyc
? utils/points.pyc
? utils/pref.pyc
? utils/preftypes.pyc
? utils/textrender.pyc
? utils/utilfuncs.pyc
? utils/version.pyc
? widgets/__init__.pyc
? widgets/axis.pyc
? widgets/axisticks.pyc
? widgets/containers.pyc
? widgets/fit.pyc
? widgets/graph.pyc
? widgets/key.pyc
? widgets/page.pyc
? widgets/plotters.pyc
? widgets/root.pyc
? widgets/widget.pyc
? widgets/widgetfactory.pyc
? windows/__init__.pyc
? windows/action.pyc
? windows/consolewindow.pyc
? windows/mainwindow.pyc
? windows/plotwindow.pyc
? windows/treeeditwindow.pyc
Index: widgets/widget.py
===================================================================
RCS file: /cvs/veusz/veusz/widgets/widget.py,v
retrieving revision 1.34
diff -u -r1.34 widget.py
--- widgets/widget.py 2 May 2005 16:54:56 -0000 1.34
+++ widgets/widget.py 21 May 2005 18:14:05 -0000
@@ -43,7 +43,9 @@
self.parent = parent
if not self.isAllowedParent(parent):
+ print self.typename, parent.typename, parent.name
raise RuntimeError, "parent is of incorrect type"
+
if name == None:
name = self.chooseName()
Index: windows/action.py
===================================================================
RCS file: /cvs/veusz/veusz/windows/action.py,v
retrieving revision 1.7
diff -u -r1.7 action.py
--- windows/action.py 15 May 2005 17:16:45 -0000 1.7
+++ windows/action.py 21 May 2005 18:14:05 -0000
@@ -32,7 +32,7 @@
menu items, and so on."""
def __init__(self, parent, onaction, iconfilename = None, menutext = None,
- tooltiptext = None, statusbartext = None):
+ tooltiptext = None, statusbartext = None, accel=None):
qt.QObject.__init__(self)
self.parent = parent
@@ -41,7 +41,11 @@
self.tooltiptext = tooltiptext
self.statusbartext = statusbartext
self.items = []
-
+ if accel:
+ self.accel = qt.QKeySequence(accel)
+ else:
+ self.accel = None
+
if self.statusbartext == None:
if self.menutext != None:
# if there is no text, use the status bar text (removing ...)
@@ -94,6 +98,8 @@
num = widget.insertItem(self.iconset, self.menutext)
else:
num = widget.insertItem(self.menutext)
+ if self.accel:
+ widget.setAccel(self.accel, num)
widget.connectItem(num, self.slotAction)
self.items.append( (widget, num) )
Index: windows/treeeditwindow.py
===================================================================
RCS file: /cvs/veusz/veusz/windows/treeeditwindow.py,v
retrieving revision 1.40
diff -u -r1.40 treeeditwindow.py
--- windows/treeeditwindow.py 15 May 2005 17:16:45 -0000 1.40
+++ windows/treeeditwindow.py 21 May 2005 18:14:06 -0000
@@ -407,18 +407,27 @@
self.contextpopup = qt.QPopupMenu(self)
- for name, icon, tooltip, menutext, slot in (
+ for name, icon, tooltip, menutext, accel, slot in (
+ ('cut', 'stock-cut.png', 'Cut the selected item',
+ '&Cut', 'Ctrl+X',
+ self.slotWidgetCut),
+ ('copy', 'sto,ck-copy.png', 'Copy the selected item',
+ '&Copy', 'Ctrl+C',
+ self.slotWidgetCopy),
+ ('paste', 'stock-paste.png', 'Paste from the clipboard',
+ '&Paste','Ctrl+V',
+ self.slotWidgetPaste),
('moveup', 'stock-go-up.png', 'Move the selected item up',
- 'Move &up',
+ 'Move &up','',
lambda a: self.slotWidgetMove(a, -1) ),
('movedown', 'stock-go-down.png', 'Move the selected item down',
- 'Move &down',
+ 'Move &down','',
lambda a: self.slotWidgetMove(a, 1) ),
('delete', 'stock-delete.png', 'Remove the selected item',
- '&Delete',
+ '&Delete','',
self.slotWidgetDelete),
('rename', 'icon-rename.png', 'Rename the selected item',
- '&Rename',
+ '&Rename','',
self.slotWidgetRename)
):
@@ -426,12 +435,18 @@
iconfilename = icon,
menutext = menutext,
statusbartext = tooltip,
- tooltiptext = tooltip)
+ tooltiptext = tooltip,
+ accel=accel)
a.addTo(self.edittool)
a.addTo(self.contextpopup)
a.addTo(editmenu)
self.editactions[name] = a
+ self.connect( editmenu, qt.SIGNAL('aboutToShow()'),
+ self.slotAboutToShowEditMenu )
+ self.connect( self.contextpopup, qt.SIGNAL('aboutToShow()'),
+ self.slotAboutToShowEditMenu )
+
def _getWidgetOrder(self):
"""Return a list of the widgets, most important first.
"""
@@ -534,10 +549,20 @@
# delete shouldn't allow root to be deleted
isnotroot = not isinstance(selw, widgets.Root)
-
+
+ self.editactions['cut'].enable(isnotroot)
+ self.editactions['copy'].enable(isnotroot)
self.editactions['delete'].enable(isnotroot)
self.editactions['rename'].enable(isnotroot)
+ self.editactions['moveup'].enable(isnotroot)
+ self.editactions['movedown'].enable(isnotroot)
+ if isnotroot:
+ #Cut and copy aren't currently possible on a non-widget
+ cancopy = not(item.widget==None)
+ self.editactions['cut'].enable(cancopy)
+ self.editactions['copy'].enable(cancopy)
+
def slotItemSelected(self, item):
"""Called when an item is selected in the listview."""
@@ -636,12 +661,36 @@
widgettype is the type of widget
"""
+ self.makeWidget(widgettype)
+
+ def makeWidget(self, widgettype, autoadd=True):
+ """Called when an add widget button is clicked.
+ widgettype is the type of widget
+ autoadd specifies whether to add default children
+ """
+
# if no widget selected, bomb out
if self.itemselected == None:
return
+ parent = self.getSutiableParent(widgettype)
+
+ assert parent != None
+
+ # make the new widget and update the document
+ w = widgetfactory.thefactory.makeWidget(widgettype, parent,
+ autoadd=autoadd)
+ self.document.setModified()
+
+ # select the widget
+ self.selectWidget(w)
+ def getSutiableParent(self, widgettype, initialParent = None):
# get the widget to act as the parent
- parent = self.itemselected.widget
+ if not initialParent:
+ parent = self.itemselected.widget
+ else:
+ parent = initialParent
+
if parent == None:
parent = self.itemselected.parent.widget
assert parent != None
@@ -652,14 +701,7 @@
while parent != None and not wc.willAllowParent(parent):
parent = parent.parent
- assert parent != None
-
- # make the new widget and update the document
- w = widgetfactory.thefactory.makeWidget(widgettype, parent)
- self.document.setModified()
-
- # select the widget
- self.selectWidget(w)
+ return parent
def slotActionClicked(self):
"""Called when an action button is clicked."""
@@ -676,6 +718,64 @@
console = self.parent.console
console.runFunction( action )
+ def getClipboardData(self):
+ """Return veusz clipboard data or False if no data is avaliable
+ The first line of the returned data is a widget type, the
+ remaining lines are commands to customise the widget and add children
+ """
+ clipboard = qt.qApp.clipboard()
+ cbSource = clipboard.data(clipboard.Clipboard)
+ if not(cbSource.provides("text/x-vnd.veusz-clipboard")):
+ #Bail if the clipboard doesn't provide the data type we want
+ return False
+ data = unicode(cbSource.encodedData("text/x-vnd.veusz-clipboard"))
+ data = data.split('\n')
+ return data
+
+ def _makeDragObject(self, widget):
+ """Make a QStoredDrag object representing the subtree with the
+ current selection at the root"""
+ if widget:
+ clipboardData =qt.QStoredDrag("text/x-vnd.veusz-clipboard")
+ data = str('\n'.join((widget.typename,
+ widget.getSaveText())))
+ clipboardData.setEncodedData(data)
+ return clipboardData
+ else:
+ return None
+
+ def slotWidgetCut(self, a):
+ """Cut the selected widget"""
+ self.slotWidgetCopy(a)
+ self.slotWidgetDelete(a)
+
+ def slotWidgetCopy(self, a):
+ clipboard = qt.qApp.clipboard()
+ dragObj = self._makeDragObject(self.itemselected.widget)
+ clipboard.setData(dragObj, clipboard.Clipboard)
+
+ def slotWidgetPaste(self, a):
+ """Paste something from the clipboard"""
+ data = self.getClipboardData()
+ if data:
+ #The first line of the clipboard data is the widget type
+ widgettype = data[0]
+
+ #Add the first widget being pasted
+ self.makeWidget(widgettype,autoadd=False)
+
+ interpreter = self.parent.interpreter
+
+ #Select the current widget in the interpreter
+ tmpCurrentwidget = interpreter.interface.currentwidget
+ interpreter.interface.currentwidget = self.itemselected.widget
+
+ #Use the command interface to create the subwidgets
+ for command in data[1:]:
+ interpreter.run(command)
+ #reset the interpreter widget
+ interpreter.interface.currentwidget = tmpCurrentwidget
+
def slotWidgetDelete(self, a):
"""Delete the widget selected."""
@@ -751,6 +851,26 @@
"""Pop up a context menu when an item is clicked on the list view."""
self.contextpopup.exec_loop(pos)
+
+ def slotAboutToShowEditMenu(self):
+
+ #Paste is only possible if a veusz item is on the clipboard
+ #and the clipboard data has a tree that can be inserted
+ #at the current widget
+
+ data = self.getClipboardData()
+
+ if not(data):
+ #Disable the paste menu
+ self.editactions['paste'].enable(False)
+ else:
+ #The first line of the clipboard data is the widget type
+ widgettype = data[0]
+ #Check if we can paste into the current widget or a parent
+ if self.getSutiableParent(widgettype,self.itemselected.widget):
+ self.editactions['paste'].enable(True)
+ else:
+ self.editactions['paste'].enable(False)
def slotWidgetRename(self, action):
"""Initiate renaming a widget."""