Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/UI/Qt/actionvis.py
1| # $Id: actionvis.py,v 1.12 2002/11/11 14:28:41 chalky Exp $
2| #
3| # This file is a part of Synopsis.
4| # Copyright (C) 2000, 2001 Stefan Seefeld
5| # Copyright (C) 2000, 2001 Stephen Davies
6| #
7| # Synopsis is free software; you can redistribute it and/or modify it
8| # under the terms of the GNU General Public License as published by
9| # the Free Software Foundation; either version 2 of the License, or
10| # (at your option) any later version.
11| #
12| # This program is distributed in the hope that it will be useful,
13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15| # General Public License for more details.
16| #
17| # You should have received a copy of the GNU General Public License
18| # along with this program; if not, write to the Free Software
19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20| # 02111-1307, USA.
21| #
22| # $Log: actionvis.py,v $
23| # Revision 1.12 2002/11/11 14:28:41 chalky
24| # New Source Edit dialog, with Insert Rule wizard and new s/path/rules with
25| # extra features.
26| #
27| # Revision 1.11 2002/11/02 06:36:19 chalky
28| # Upgrade to Qt3
29| #
30| # Revision 1.10 2002/10/11 06:03:23 chalky
31| # Use config from project
32| #
33| # Revision 1.9 2002/09/28 05:53:31 chalky
34| # Refactored display into separate project and browser windows. Execute projects
35| # in the background
36| #
37| # Revision 1.8 2002/08/23 04:36:35 chalky
38| # Use icons! :)
39| #
40| # Revision 1.7 2002/06/22 07:03:27 chalky
41| # Updates to GUI - better editing of Actions, and can now execute some.
42| #
43| # Revision 1.6 2002/06/12 12:58:33 chalky
44| # Updates to display - hilite items and right click menus
45| #
46| # Revision 1.5 2002/04/26 01:21:14 chalky
47| # Bugs and cleanups
48| #
49| # Revision 1.4 2001/11/09 08:06:59 chalky
50| # More GUI fixes and stuff. Double click on Source Actions for a dialog.
51| #
52| # Revision 1.3 2001/11/07 05:58:21 chalky
53| # Reorganised UI, opening a .syn file now builds a simple project to view it
54| #
55| # Revision 1.2 2001/11/06 08:47:11 chalky
56| # Silly bug, arrows, channels are saved
57| #
58| # Revision 1.1 2001/11/05 06:52:11 chalky
59| # Major backside ui changes
60| #
61|
62|
63| import sys, pickle, Synopsis, cStringIO, math
64| from qt import *
65|
66| # In later versions of python-qt aka PyQt this is in a separate module
67| if not globals().has_key('QCanvasView'):
68| from qtcanvas import *
69|
70| from Synopsis.Config import prefix
71| from Synopsis.Core import AST, Util
72| from Synopsis.Core.Action import *
73| from Synopsis.Core.Project import *
74| from Synopsis.Formatter.ASCII import ASCIIFormatter
75| from Synopsis.Formatter.ClassTree import ClassTree
76|
77| import actionwiz
78|
79| class CanvasStrategy:
80| "An interface for a strategy to handle mouse events"
81| def __init__(self, canvas, view):
82| self.canvas = canvas
83| self.view = view
84| def reset(self): pass
85| def set(self): pass
86| def press(self, event): pass
87| def release(self, event): pass
88| def move(self, event): pass
89| def doubleClick(self, event): pass
90| def key(self, event):
91| if event.key() == Qt.Key_Escape:
92| self.view.window.setMode('select')
93| else:
94| event.ignore()
95|
96| class SelectionStrategy (CanvasStrategy):
97| """The normal CanvasStrategy to handle mouse actions."""
98|
99| class MenuHandler:
100| """Wraps menu callbacks with an action or line object"""
101| def __init__(self, sel, obj):
102| self.sel = sel
103| self.obj = obj
104| def on_properties(self):
105| self.sel.on_properties(self.obj)
106| def on_delete_action(self):
107| self.sel.on_delete_action(self.obj)
108| def on_delete_line(self):
109| self.sel.on_delete_line(self.obj)
110| def on_default_formatter(self):
111| self.sel.on_default_formatter(self.obj)
112|
113| def __init__(self, canvas, view):
114| CanvasStrategy.__init__(self, canvas, view)
115| self.__drag_action = None
116| self.__icon = None
117| self.__last = None
118| self.__hilite = None # the action or line to hilight
119| self.__normal_cursor = QCursor(Qt.ArrowCursor)
120| self.__moving_cursor = QCursor(Qt.BlankCursor)
121| self.__hint_cursor = QCursor(Qt.SizeAllCursor)
122|
123| def reset(self):
124| self.__drag_item = None
125|
126| def press(self, event):
127| action = self.canvas.get_action_at(event.x(), event.y())
128| if action and event.button() == Qt.LeftButton:
129| # Drag the action if left button pressed
130| self.__drag_action = action
131| self.__last = QPoint(event.pos())
132| self.view.setCursor(self.__moving_cursor)
133| return
134| if event.button() == Qt.RightButton:
135| if action and event.button() == Qt.RightButton:
136| # Show a menu for the right button
137| handler = self.MenuHandler(self, action)
138| menu = QPopupMenu(self.view)
139| menu.insertItem("Delete Action", handler.on_delete_action)
140| if isinstance(action, FormatAction):
141| id = menu.insertItem("Default Formatter", handler.on_default_formatter)
142| check = self.canvas.project.default_formatter() is action
143| menu.setItemChecked(id, check)
144| menu.insertItem("Properties...", handler.on_properties)
145| menu.exec_loop(QCursor.pos())
146| return
147| line = self.canvas.get_line_at(event.x(), event.y())
148| if line:
149| # Show a menu for the right button
150| handler = self.MenuHandler(self, line)
151| menu = QPopupMenu(self.view)
152| menu.insertItem("Delete Channel", handler.on_delete_line)
153| menu.exec_loop(QCursor.pos())
154| return
155|
156| def release(self, event):
157| if self.__drag_action:
158| self.__drag_action = None
159| self.view.setCursor(self.__hint_cursor)
160|
161| def move(self, event):
162| # Drag an action if we're dragging
163| if self.__drag_action:
164| # Move the dragging action
165| dx = event.x() - self.__last.x()
166| dy = event.y() - self.__last.y()
167| self.view.actions.move_action_by(self.__drag_action, dx, dy)
168| self.__last = QPoint(event.pos())
169| return
170| # Hilite an action if mouse is over one
171| action = self.canvas.get_action_at(event.x(), event.y())
172| if action:
173| self.view.setCursor(self.__hint_cursor)
174| self.hilite(self.canvas.get_icon_for(action))
175| return
176| # Hilite a line if mouse is over one
177| line = self.canvas.get_line_at(event.x(), event.y())
178| if line:
179| self.hilite(line)
180| elif self.__hilite:
181| # Not doing anything..
182| self.hilite(None)
183| self.view.setCursor(self.__normal_cursor)
184|
185| def hilite(self, obj):
186| """Sets the currently hilited object. The object may be None, an Icon
187| or a Line"""
188| if obj is self.__hilite:
189| return
190| if self.__hilite:
191| self.__hilite.set_hilite(0)
192| self.__hilite = obj
193| if obj:
194| obj.set_hilite(1)
195| self.canvas.update()
196|
197| def doubleClick(self, event):
198| action = self.canvas.get_action_at(event.x(), event.y())
199| if action: self.on_properties(action)
200|
201| def key(self, event):
202| "Override default set-mode-to-select behaviour"
203| event.ignore()
204|
205| def on_delete_line(self, line):
206| print "Deleting line",line.source.name(),"->",line.dest.name()
207| button = QMessageBox.warning(self.view, "Confirm delete",\
208| 'Delete line from "%s" to "%s"?'%(line.source.name(),
209| line.dest.name()), QMessageBox.Yes, QMessageBox.No)
210| if button is QMessageBox.No: return
211| self.canvas.actions.remove_channel(line.source, line.dest)
212|
213| def on_delete_action(self, action):
214| print "Deleting action",action.name()
215| button = QMessageBox.warning(self.view, "Confirm delete",\
216| 'Delete action "%s"?'%(action.name(),),
217| QMessageBox.Yes, QMessageBox.No)
218| if button is QMessageBox.No: return
219| self.canvas.actions.remove_action(action)
220|
221| def on_default_formatter(self, action):
222| print "Changing default formatter:",action.name()
223| if self.canvas.project.default_formatter() is action:
224| self.canvas.project.set_default_formatter(None)
225| else:
226| self.canvas.project.set_default_formatter(action)
227|
228| def on_properties(self, action):
229| print action.name()
230| if isinstance(action, SourceAction):
231| editor = actionwiz.SourceActionEditor(self.view, self.canvas.project)
232| result = editor.edit(action)
233| print result
234| return
235| actionwiz.ActionDialog(self.view, action, self.canvas.project)
236|
237| class ConnectStrategy (CanvasStrategy):
238| def __init__(self, canvas, view):
239| CanvasStrategy.__init__(self, canvas, view)
240| self.__normal_cursor = QCursor(Qt.ArrowCursor)
241| self.__hint_cursor = QCursor(Qt.UpArrowCursor)
242| self.__find_cursor = QCursor(Qt.SizeHorCursor)
243| self.templine = QCanvasLine(self.canvas)
244| self.templine.setPen(QPen(Qt.blue, 1, Qt.DotLine))
245|
246| def set(self):
247| self.source = None
248| self.templine.hide()
249| self.canvas.update()
250| def reset(self):
251| self.templine.hide()
252| self.canvas.update()
253| def move(self, event):
254| # Find action under cursor and change cursor type
255| if self.source:
256| self.templine.setPoints(self.source.x()+16, self.source.y()+16, event.x(), event.y())
257| self.canvas.update()
258| action = self.canvas.get_action_at(event.x(), event.y())
259| if action and action is not self.source:
260| if self.view.actions.is_valid_channel(self.source, action):
261| self.view.setCursor(self.__hint_cursor)
262| return
263| if self.source:
264| self.view.setCursor(self.__find_cursor)
265| else:
266| self.view.setCursor(self.__normal_cursor)
267| def press(self, event):
268| action = self.canvas.get_action_at(event.x(), event.y())
269| if not action: return
270| if not self.source: self.setSource(action, event)
271| elif action is not self.source:
272| if self.view.actions.is_valid_channel(self.source, action):
273| self.setDest(action)
274| def release(self, event):
275| action = self.canvas.get_action_at(event.x(), event.y())
276| if action and self.source and action is not self.source:
277| if self.view.actions.is_valid_channel(self.source, action):
278| self.setDest(action)
279| def setSource(self, action, event):
280| self.source = action
281| self.templine.setPoints(action.x()+16, action.y()+16, event.x(), event.y())
282| self.templine.show()
283| self.canvas.update()
284| def setDest(self, action):
285| self.templine.hide()
286| self.view.actions.add_channel(self.source, action)
287| self.source = None
288| self.view.window.setMode('select')
289|
290| class AddActionStrategy (CanvasStrategy):
291| def __init__(self, canvas, view, action_type):
292| CanvasStrategy.__init__(self, canvas, view)
293| self.__drag_action = None
294| self.__normal_cursor = QCursor(Qt.ArrowCursor)
295| self.__moving_cursor = QCursor(Qt.BlankCursor)
296| self.__action_type = action_type
297|
298| def set(self):
299| action_class = getattr(Synopsis.Core.Action, '%sAction'%self.__action_type)
300| self.action = action_class(self.view.last_pos.x()-16,
301| self.view.last_pos.y()-16,
302| "New %s action"%self.__action_type)
303| try:
304| # Run the wizard to set the type of the action
305| wizard = actionwiz.AddWizard(self.view, self.action, self.canvas.project)
306| if wizard.exec_loop() == QDialog.Rejected:
307| # Abort adding
308| self.action = None
309| self.view.window.setMode('select')
310| else:
311| # Wizard will create a new more derived action object
312| self.view.actions.add_action(wizard.action)
313| self.action = wizard.action
314| self.view.setCursor(self.__moving_cursor)
315| except Exception, msg:
316| print "An error occured in add wizard:\n", msg
317| self.action = None
318| self.view.window.setMode('select')
319| import traceback
320| traceback.print_exc()
321|
322| def reset(self):
323| if self.action:
324| self.view.actions.remove_action(self.action)
325| self.view.setCursor(self.__normal_cursor)
326|
327| def move(self, event):
328| self.view.actions.move_action(self.action, event.x()-16, event.y()-16)
329|
330| def release(self, event):
331| self.action = None
332| self.view.window.setMode('select')
333|
334| class ActionPropertiesDialog (QDialog):
335| def __init__(self, parent, action):
336| QDialog.__init__(self, parent)
337| self.action = action
338|
339| class ActionColorizer (ActionVisitor):
340| def __init__(self, action=None):
341| self.color = None
342| if action: action.accept(self)
343| def visitAction(self, action): self.color=Qt.black
344| def visitSource(self, action): self.color=Qt.magenta
345| def visitParser(self, action): self.color=Qt.red
346| def visitLinker(self, action): self.color=Qt.yellow
347| def visitCacher(self, action): self.color=Qt.green
348| def visitFormat(self, action): self.color=Qt.cyan
349|
350| class ActionIcon (ActionVisitor):
351| def __init__(self, action=None):
352| self.icon = None
353| if action: action.accept(self)
354| def visitAction(self, action): pass #self.icon='syn-icon-action.png'
355| def visitSource(self, action): self.icon='syn-icon-c++.png'
356| def visitParser(self, action): self.icon='syn-icon-parse.png'
357| def visitLinker(self, action): self.icon='syn-icon-link.png'
358| def visitCacher(self, action): self.icon='syn-icon-cache.png'
359| def visitFormat(self, action): self.icon='syn-icon-html.png'
360|
361| class Icon:
362| "Encapsulates the canvas display of an Action"
363|
364| def __init__(self, canvas, action):
365| self.action = action
366| self.canvas = canvas
367| self.icon = ActionIcon(action).icon
368| if self.icon:
369| icon = prefix+'/share/synopsis/'+self.icon
370| self.pixmap = QPixmap(icon)
371| self.array = QCanvasPixmapArray([self.pixmap])
372| self.img = QCanvasSprite(self.array, canvas)
373| else:
374| self.img = QCanvasRectangle(action.x(), action.y(), 32, 32, canvas)
375| self.brush = QBrush(ActionColorizer(action).color)
376| self.img.setBrush(self.brush)
377| self.width = self.img.width()
378| self.height = self.img.height()
379| self.img.setZ(1)
380| self.text = QCanvasText(action.name(), canvas)
381| self.text.setZ(1)
382| self.img.show()
383| self.text.show()
384| self.hilite = 0
385| self.update_pos()
386| def hide(self):
387| """Hides icon on canvas"""
388| # Note, items must be deleted, which will occur when GC happens.
389| # Until them, make them invisible
390| self.img.hide()
391| self.text.hide()
392| def set_hilite(self, yesno):
393| self.hilite = yesno
394| if not hasattr(self, 'brush'): return
395| if yesno:
396| colour = self.brush.color()
397| brush = QBrush(QColor(colour.red()*3/4, colour.green()*3/4,
398| colour.blue()*3/4))
399| else:
400| brush = self.brush
401| self.img.setBrush(brush)
402| def update_pos(self):
403| self.img.move(self.action.x(), self.action.y())
404| rect = self.text.boundingRect()
405| irect = self.img.boundingRect()
406| y = irect.bottom()
407| x = irect.center().x() - rect.width()/2
408| self.text.move(x, y)
409|
410| class Line:
411| "Encapsulates the canvas display of a channel between two Actions"
412| def __init__(self, canvas, source, dest):
413| self.canvas = canvas
414| self.source = source
415| self.dest = dest
416| self.hilite = 0
417| self.line = QCanvasLine(canvas)
418| self.line.setPen(QPen(Qt.blue))
419| self.arrow = QCanvasPolygon(canvas)
420| self.arrow.setBrush(QBrush(Qt.blue))
421| self.update_pos()
422| self.line.show()
423| self.arrow.show()
424| def hide(self):
425| """Hide line on canvas"""
426| self.line.hide()
427| self.arrow.hide()
428| def set_hilite(self, yesno):
429| self.hilite = yesno
430| if yesno:
431| pen = QPen(Qt.blue, 2)
432| else:
433| pen = QPen(Qt.blue)
434| self.line.setPen(pen)
435| self.arrow.setPen(pen)
436| self.update_pos()
437| def update_pos(self):
438| source, dest = self.source, self.dest
439| src_action, dst_action = source.action, dest.action
440| # Find centers of the two icons
441| src_xrad, src_yrad = source.width/2, source.height/2
442| dst_xrad, dst_yrad = dest.width/2, dest.height/2
443| if src_xrad == 0: src_xrad, src_yrad = 10, 10
444| if dst_xrad == 0: dst_xrad, dst_yrad = 10, 10
445| x1, y1 = src_action.x()+src_xrad, src_action.y()+src_yrad
446| x2, y2 = dst_action.x()+dst_xrad, dst_action.y()+dst_yrad
447| # Calculate the distance
448| dx, dy = x2 - x1, y2 - y1
449| d = math.sqrt(dx*dx + dy*dy)
450| if d < 32:
451| # Don't draw lines that are too short (they will be behind the
452| # icons)
453| self.line.setPoints(x1, y1, x2, y2)
454| self.arrow.setPoints(QPointArray([]))
455| return
456| # Normalize direction vector
457| dx, dy = dx / d, dy / d
458| # Find the end-point for the arrow
459| if math.fabs(dx) * dst_yrad < math.fabs(dy) * dst_xrad:
460| if dy < 0:
461| # Bottom
462| x2 = x2 + dst_yrad * dx / dy
463| y2 = y2 + dst_yrad
464| else:
465| # Top
466| x2 = x2 - dst_yrad * dx / dy
467| y2 = y2 - dst_yrad
468| else:
469| if dx < 0:
470| # Left
471| y2 = y2 + dst_xrad * dy / dx
472| x2 = x2 + dst_xrad
473| else:
474| # Right
475| y2 = y2 - dst_xrad * dy / dx
476| x2 = x2 - dst_xrad
477| self.line.setPoints(x1, y1, x2, y2)
478| alen = 8 + self.hilite
479| awid = 5 + self.hilite
480| self.arrow.setPoints(QPointArray([
481| x2 + self.hilite*dx, y2 + self.hilite*dy,
482| x2 - alen*dx - awid*dy, y2 - alen*dy + awid*dx,
483| x2 - alen*dx + awid*dy, y2 - alen*dy - awid*dx]))
484|
485| class ActionCanvas (QCanvas):
486| """Extends QCanvas to automatically fill and update the canvas when
487| notified of events by an ActionManager"""
488| def __init__(self, actions, parent, project):
489| QCanvas.__init__(self, parent)
490| self.actions = actions
491| self.project = project
492| self._item_to_action_map = {}
493| self._action_to_icon_map = {}
494| self._action_lines = {}
495| self._item_to_line_map = {}
496|
497| self.actions.add_listener(self)
498|
499| def get_action_at(self, x, y):
500| "Returns the Action (if any) at the given coordinates"
501| items = self.collisions(QPoint(x, y))
502| if items and self._item_to_action_map.has_key(items[0]):
503| return self._item_to_action_map[items[0]]
504|
505| def get_line_at(self, x, y):
506| """Returns the Actions forming a line which crosses x,y, as a
507| line object"""
508| items = self.collisions(QPoint(x, y))
509| if items and self._item_to_line_map.has_key(items[0]):
510| return self._item_to_line_map[items[0]]
511|
512| def get_icon_for(self, action):
513| return self._action_to_icon_map[action]
514|
515| def action_added(self, action):
516| "Callback from ActionManager. Adds an Icon for the new Action"
517| icon = Icon(self, action)
518| self._item_to_action_map[icon.img] = action
519| self._action_to_icon_map[action] = icon
520| self.update()
521|
522| def action_removed(self, action):
523| "Callback from ActionManager. Removes the Icon for the Action"
524| icon = self._action_to_icon_map[action]
525| del self._action_to_icon_map[action]
526| del self._item_to_action_map[icon.img]
527| icon.hide()
528| self.update()
529|
530| def action_moved(self, action):
531| "Callback from ActionManager. Moves the Icon to follow the Action"
532| icon = self._action_to_icon_map[action]
533| icon.update_pos()
534| if self._action_lines.has_key(action):
535| for line in self._action_lines[action]:
536| line.update_pos()
537| self.update()
538|
539| def channel_added(self, source, dest):
540| "Callback from ActionManager. Adds a channel between the given actions"
541| src_icon = self._action_to_icon_map[source]
542| dst_icon = self._action_to_icon_map[dest]
543| line = Line(self, src_icon, dst_icon)
544| self._action_lines.setdefault(source, []).append(line)
545| self._action_lines.setdefault(dest, []).append(line)
546| self._item_to_line_map[line.line] = line
547| self.update()
548|
549| def channel_removed(self, source, dest):
550| "Callback from ActionManager. Adds a channel between the given actions"
551| # NB: check source and dest separately to handle inconsistancies
552| # better, if they should arise
553| if self._action_lines[source]:
554| for line in self._action_lines[source]:
555| if line.dest is dest:
556| self._action_lines[source].remove(line)
557| if self._item_to_line_map.has_key(line.line):
558| self._item_to_line_map[line.line].hide()
559| del self._item_to_line_map[line.line]
560| if self._action_lines[dest]:
561| for line in self._action_lines[dest]:
562| if line.source is source:
563| self._action_lines[dest].remove(line)
564| if self._item_to_line_map.has_key(line.line):
565| self._item_to_line_map[line.line].hide()
566| del self._item_to_line_map[line.line]
567| self.update()
568|
569| def action_changed(self, action):
570| "Callback from ProjectActions. Indicates changes, including rename"
571| icon = self._action_to_icon_map[action]
572| icon.text.setText(action.name())
573| icon.update_pos()
574| self.update()
575|
576|
577| class CanvasView (QCanvasView):
578| def __init__(self, canvas, parent):
579| QCanvasView.__init__(self, canvas, parent)
580| self.actions = parent.actions
581| self.window = parent
582| self.last_pos = QPoint(-50,-50)
583| self.viewport().setFocusPolicy(QWidget.ClickFocus)
584| self.__canvas = canvas
585| self.__strategies = {
586| 'select' : SelectionStrategy(canvas, self),
587| 'connect': ConnectStrategy(canvas, self),
588| 'new_source' : AddActionStrategy(canvas, self, 'Source'),
589| 'new_parse' : AddActionStrategy(canvas, self, 'Parser'),
590| 'new_link' : AddActionStrategy(canvas, self, 'Linker'),
591| 'new_cache' : AddActionStrategy(canvas, self, 'Cacher'),
592| 'new_format' : AddActionStrategy(canvas, self, 'Format')
593| }
594| self.viewport().setMouseTracking(1)
595|
596| self.__strategy = self.__strategies['select']
597| self.connect(parent, PYSIGNAL('modeChanged(string)'), self.modeChanged)
598| def modeChanged(self, tool):
599| if self.__strategies.has_key(tool):
600| self.__strategy.reset()
601| self.__strategy = self.__strategies[tool]
602| self.__strategy.set()
603| def contentsMousePressEvent(self, event):
604| self.__strategy.press(event)
605| def contentsMouseReleaseEvent(self, event):
606| self.__strategy.release(event)
607| def contentsMouseMoveEvent(self, event):
608| self.last_pos = QPoint(event.pos())
609| self.__strategy.move(event)
610| def contentsMouseDoubleClickEvent(self, event):
611| self.__strategy.doubleClick(event)
612| def keyPressEvent(self, event):
613| self.__strategy.key(event)
614|
615|
616| class CanvasWindow (QVBox):
617| def __init__(self, parent, main_window, project):
618| QVBox.__init__(self, parent)
619| self.setCaption("Canvas")
620| self.main_window = main_window
621| self.__activated = 0
622|
623| # Make the toolbar
624| self.buttons = QButtonGroup()
625| self.buttons.setExclusive(1)
626| self.tool = QToolBar("Canvas", self.main_window, self)
627| #self.tool.setHorizontalStretchable(0)
628| pixmap = QPixmap(16,16); pixmap.fill()
629| self.tool_sel = QToolButton(QIconSet(pixmap), "Select", "Select actions in the display", self.setSelect, self.tool)
630| self.tool_sel.setUsesTextLabel(1)
631| self.tool_sel.setToggleButton(1)
632| self.buttons.insert(self.tool_sel)
633|
634| pixmap = QPixmap(16,16); pixmap.fill(Qt.blue)
635| self.tool_con = QToolButton(QIconSet(pixmap), "Connect", "Connect two actions", self.setConnect, self.tool)
636| self.tool_con.setUsesTextLabel(1)
637| self.tool_con.setToggleButton(1)
638| self.buttons.insert(self.tool_con)
639|
640| #pixmap = QPixmap(16,16); pixmap.fill(Qt.red)
641| #self.tool_add = QToolButton(QIconSet(pixmap), "Add Action", "Add a new action to the project", self.newAction, self.tool)
642| #self.tool_add.setUsesTextLabel(1)
643| #self.tool_add.setToggleButton(1)
644| #self.buttons.insert(self.tool_add)
645| tools = (('Source','source','c++'), ('Parser','parse','parse'),
646| ('Linker','link','link'), ('Cache','cache', 'cache'),
647| ('Formatter','format', 'html'))
648| for name, short_name, icon in tools:
649| self._makeNewTool(name, short_name, icon)
650|
651| # Make the menu, to be inserted in the app menu upon window activation
652| self._file_menu = QPopupMenu(main_window.menuBar())
653| #self._file_menu.insertItem("New &Action", self.newAction, Qt.CTRL+Qt.Key_A)
654| self._file_menu.insertItem("&Save Project", self.saveProject, Qt.CTRL+Qt.Key_S)
655| self._file_menu.insertItem("Save Project &As...", self.saveProjectAs)
656|
657|
658| self.__tools = {
659| 'select' : self.tool_sel,
660| 'connect': self.tool_con,
661| 'new_source' : self.tool_source,
662| 'new_parse' : self.tool_parse,
663| 'new_link' : self.tool_link,
664| 'new_cache' : self.tool_cache,
665| 'new_format' : self.tool_format
666| }
667| # Make the canvas
668| self.project = project
669| self.actions = self.project.actions()
670| self.canvas = ActionCanvas(self.actions, self, project)
671| self.canvas.resize(parent.width(), parent.height())
672| self.canvas_view = CanvasView(self.canvas, self)
673|
674| self.show()
675|
676| self.connect(parent.parentWidget(), SIGNAL('windowActivated(QWidget*)'), self.windowActivated)
677|
678| self.setMode('select')
679| self.activate()
680|
681| def _makeNewTool(self, name, short_name, icon_id):
682| icon = prefix+'/share/synopsis/syn-icon-%s.png'%icon_id
683| image = QImage(icon)
684| # Resize icon to correct size
685| image.smoothScale(16, 16, QImage.ScaleMin)
686| pixmap = QPixmap(image)
687| tool = QToolButton(QIconSet(pixmap), "New %s"%name,
688| "Add a new %s action to the project"%name,
689| getattr(self, 'new%sAction'%name), self.tool)
690| tool.setUsesTextLabel(1)
691| tool.setToggleButton(1)
692| self.buttons.insert(tool)
693| # Store with appropriate name in self
694| setattr(self, 'tool_'+short_name, tool)
695|
696| def resizeEvent(self, ev):
697| QVBox.resizeEvent(self, ev)
698| self.canvas.resize(self.canvas_view.width(), self.canvas_view.height())
699|
700|
701| def setMode(self, mode):
702| self.mode = mode
703| self.__tools[mode].setOn(1)
704|
705| self.emit(PYSIGNAL('modeChanged(string)'), (self.mode,))
706|
707| def windowActivated(self, widget):
708| if self.__activated:
709| if widget is not self: self.deactivate()
710| elif widget is self: self.activate()
711|
712| def activate(self):
713| self.__activated = 1
714| self._menu_id = self.main_window.menuBar().insertItem('Canvas', self._file_menu)
715|
716| def deactivate(self):
717| self.__activated = 0
718| self.main_window.menuBar().removeItem(self._menu_id)
719|
720| def setSelect(self):
721| self.setMode('select')
722|
723| def setConnect(self):
724| self.setMode('connect')
725|
726| def newSourceAction(self):
727| self.setMode('new_source')
728|
729| def newParserAction(self):
730| self.setMode('new_parse')
731|
732| def newLinkerAction(self):
733| self.setMode('new_link')
734|
735| def newCacheAction(self):
736| self.setMode('new_cache')
737|
738| def newFormatterAction(self):
739| self.setMode('new_format')
740|
741| def saveProject(self):
742| if self.project.filename() is None:
743| filename = QFileDialog.getSaveFileName('project.synopsis', '*.synopsis',
744| self, 'ProjectSave', "Save Project...")
745| filename = str(filename)
746| if not filename:
747| # User clicked cancel
748| return
749| print "filename =",filename
750| self.project.set_filename(filename)
751| self.project.save()
752|
753| def saveProjectAs(self):
754| filename = QFileDialog.getSaveFileName('project.synopsis', '*.synopsis',
755| self, 'ProjectSave', "Save Project As...")
756| filename = str(filename)
757| if not filename:
758| # User clicked cancel
759| return
760| print "filename =",filename
761| self.project.set_filename(filename)
762| self.project.save()
763|
764|