1 /***************************************************************************
2  *   Copyright (C) 2005 by David Saxton                                    *
3  *   david@bluehaze.org                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10 
11 #ifndef ITEMDOCUMENT_H
12 #define ITEMDOCUMENT_H
13 
14 #include <set>
15 #include <document.h>
16 #include <canvas.h>
17 #include "canvasitems.h"
18 
19 #include <QMap>
20 #include <QStack>
21 // #include <q3valuevector.h>
22 
23 class Canvas;
24 class CanvasTip;
25 class Connector;
26 class CMManager;
27 class ECNode;
28 class Item;
29 class ItemDocumentData;
30 class ItemGroup;
31 class KTechlab;
32 class Operation;
33 
34 class KActionMenu;
35 class KtlQCanvasItem;
36 
37 typedef QStack<ItemDocumentData*> IDDStack;
38 typedef QPointer<Item> GuardedItem;
39 typedef QMap< int, GuardedItem > IntItemMap;
40 typedef QMap< QString, Item* > ItemMap;
41 typedef QList<GuardedItem> ItemList;
42 typedef QList<QPoint> QPointList;
43 
44 /**
45 @author David Saxton
46 */
47 class ItemDocument : public Document
48 {
49 	Q_OBJECT
50 
51 	friend class KtlTestsAppFixture;
52 
53 	public:
54 		ItemDocument( const QString &caption, const char *name = nullptr );
55 		~ItemDocument() override;
56 
57 		class Z
58 		{
59 			public:
60 				enum
61 				{
62 					Select			= 10000000,
63 					Connector		= 20000000,
64 					Item			= 30000000,
65 					RaisedItem		= 40000000,
66 					ResizeHandle 		= 50000000,
67 					Tip			= 60000000,
68 					ConnectorCreateLine	= 70000000,
69 
70 					// How much "Z" separates items stacked on each other
71 					DeltaItem		= 10000
72 				};
73 		};
74 
75 		/**
76 		 * Some things (such as the canvas getting resized, connectors being
77 		 * invalidated, need to be done after editing operations have finished,
78 		 * etc, and they also need to be done in the order given in the
79 		 * enumeration below.
80 		 */
81 		class ItemDocumentEvent
82 		{
83 			public: enum type
84 			{
85 				ResizeCanvasToItems		= 1 << 0,
86 				UpdateNodeGroups		= 1 << 1,
87 				RerouteInvalidatedConnectors	= 1 << 2,
88 				UpdateZOrdering			= 1 << 3
89 			};
90 		};
91 
92 		void fileSave() override;
93 		void fileSaveAs() override;
94 		void print() override;
95 		bool openURL( const QUrl &url ) override;
96 		/**
97 		 * Attempt to register the item, returning true iff successful
98 		 */
99 		virtual bool registerItem( KtlQCanvasItem *qcanvasItem );
100 		/**
101 		 * Will attempt to create an item with the given id at position p. Some item
102 		 * (such as PIC/START) have restrictions, and can only have one instance of
103 		 * themselves on the canvas, and adds the operation to the undo list
104 		 */
105 		virtual Item* addItem( const QString &id, const QPoint &p, bool newItem ) = 0;
106 		/**
107 		 * @returns A pointer to the canvas
108 		 */
canvas()109 		Canvas *canvas() const { return m_canvas; }
110 		/**
111 		 * Attemtps to register a unique id for the canvas view of an item on the
112 		 * canvas. If the id does not already exist, will return true; otherwise
113 		 * the function will return false.
114 		 */
115 		bool registerUID( const QString & uid );
116 		/**
117 		 * Generates a unique id based on a possibly unique component name.
118 		 */
119 		QString generateUID( QString name );
120 		/**
121 		 * Unlists the given id as one that is used.
122 		 * @see registerUID
123 		 */
124 		virtual void unregisterUID( const QString & uid );
125 		/**
126 		 * @return Whether or not the item is valid; i.e. is appropriate to the
127 		 * document being edited, and does not have other special restrictions
128 		 * on it (such as only allowing one instance of the Start part in
129 		 * FlowCode).
130 		 */
131 		virtual bool isValidItem( Item *item ) = 0;
132 		/**
133 		 * @return Whether or not the item is valid; i.e. is appropriate to the
134 		 * document being edited, and does not have other special restrictions
135 		 * on it (such as only allowing one instance of the Start part in
136 		 * FlowCode).
137 		 */
138 		virtual bool isValidItem( const QString &itemId ) = 0;
139 		/**
140 		 * Increases the "height" of the given list of items by "one".
141 		 */
142 		void raiseZ( const ItemList & itemList );
143 		/**
144 		 * Decreases the "height" of the given list of items by "one".
145 		 */
146 		void lowerZ( const ItemList & itemList );
147 		/**
148 		 * @return ItemGroup that is used as the select list for this document.
149 		 */
150 		virtual ItemGroup *selectList() const = 0;
151 		/**
152 		 * Deselects any currently selected items
153 		 */
154 		void unselectAll();
155 		/**
156 		 * Select a list of KtlQCanvasItem's
157 		 */
158 		void select( const KtlQCanvasItemList & list );
159 		/**
160 		 * Select a KtlQCanvasItem
161 		 */
162 		void select( KtlQCanvasItem * item );
163 		/**
164 		 * Unselects the item
165 		 */
166 		void unselect( KtlQCanvasItem *qcanvasItem );
167 		/**
168 		 * Deletes anything waiting to be deleted.
169 		 */
170 		virtual void flushDeleteList() = 0;
171 		/**
172 		 * Returns a rubber-band rectangle that contains all of the items on the
173 		 * canvas, padded out by a small border.
174 		 */
175 		QRect canvasBoundingRect() const;
176 		/**
177 		 * Returns a pointer to a Item on the canvas with the given id,
178 		 * or nullptr if no such Item exists.
179 		 */
180 		Item* itemWithID( const QString & );
181 		/**
182 		 * Returns true if the user can perform an undo action
183 		 * (i.e. the undo stack is not empty)
184 		 */
185 		bool isUndoAvailable() const override;
186 		/**
187 		 * Returns true if the user can perform an redo action
188 		 * (i.e. the redo stack is not empty)
189 		 */
190 		bool isRedoAvailable() const override;
191 		/**
192 		 * Returns the top item at point (x, y), or nullptr if there is no item there
193 		 */
194 		KtlQCanvasItem* itemAtTop( const QPoint &pos ) const ;
195 		/**
196 		 * Called when the canvas is clicked on with the right mouse button.
197 		 * Popups up a menu for editing operations
198 		 */
199 		virtual void canvasRightClick( const QPoint &pos, KtlQCanvasItem* item );
200 		/**
201 		 * List of items in the ItemDocument
202 		 */
203 		ItemList itemList() const;
204 		/**
205 		 * Set the given KtlQCanvasItem (which will attempt to be casted to known
206 		 * items to be deleted.
207 		 */
208 		virtual void appendDeleteList( KtlQCanvasItem * ) = 0;
209 		/**
210 		 * Save the current state of the document to the undo/redo history.
211 		 * @param actionTicket if this is non-negative, and the last state save
212 		 * also had the same actionTicket, then the next state save will
213 		 * overwrite the previous state save.
214 		 * @see getActionTicket
215 		 */
216 		void requestStateSave( int actionTicket = -1 );
217 
218 		/**
219 		 * Clears the undo / redo history
220 		 */
221 		void clearHistory();
222 		/**
223 		 * Requests an event to be done after other stuff (editing, etc) is finished.
224 		 */
225 		void requestEvent( ItemDocumentEvent::type type );
226 		/**
227 		 * Called from Canvas (when KtlQCanvas::advance is called).
228 		 */
229 		virtual void update();
230 
231 	/**
232 	 * Returns a unique id, for use in requestStateSave
233 	 */
getActionTicket()234 	int getActionTicket() const { return m_nextActionTicket++; }
235 
236 public slots:
237 		void undo() override;
238 		void redo() override;
239 		void cut() override;
240 		void paste() override;
241 		/**
242 		 * Ask the canvas to be resized to the current items on the canvas.
243 		 */
244 		void requestCanvasResize();
245 		/**
246 		 * Selects everything in the view.
247 		 */
248 		void selectAll() override = 0;
249 		/**
250 		 * Increases the "height" of the selected items.
251 		 */
252 		void raiseZ();
253 		/**
254 		 * Decreases the "height" of the selected items.
255 		 */
256 		void lowerZ();
257 		/**
258 		 * Brings up a file dialog requesting the location of the file to export
259 		 * to, and then exports an image of the canvas.
260 		 */
261 		void exportToImage();
262     protected:
263         void exportToImageDraw( const QRect & saveArea, QPaintDevice &pDev);
264     public slots:
265 		/**
266 		 * Deletes whatever is selected.
267 		 */
deleteSelection()268 		virtual void deleteSelection() {};
269 		/**
270 		 * Called when the user presses Escape (or similar)
271 		 */
272 		void cancelCurrentOperation();
273 		/**
274 		 * Sets the y-positions of the selected items to the average of the
275 		 * initial y-positions.
276 		 */
277 
278 // TODO: decide whether these should be moved to ICNdocument...
279 		void alignHorizontally();
280 		/**
281 		 * Sets the x-positions of the selected items to the average of the
282 		 * initial x-positions.
283 		 */
284 		void alignVertically();
285 		/**
286 		 * Averages out the horizontal spacing between the selected items.
287 		 */
288 		void distributeHorizontally();
289 		/**
290 		 * Averages out the vertical spacing between the selected items.
291 		 */
292 		void distributeVertically();
293 		/**
294 		 * Adds an items not in the Z ordering to the ordering, and removes any
295 		 * items from the Z ordering if they have parents. Then, calls all items
296 		 * found in the ordering to tell them their Z position.
297 		 */
298 // ##################
299 
300 
301 		void slotUpdateZOrdering();
302 		/**
303 		 * Call this with ItemDocument::DrawAction to start drawing the given thing
304 		 */
305 		void slotSetDrawAction( QAction *selected );
306 		/**
307 		 * Sets the editing mode to repeatedly creating a CNItem
308 		 * with the given id. Usually called when the user double-clicks on
309 		 * the component box.
310 		 */
311 		void slotSetRepeatedItemId( const QString &id );
312 		/**
313 		 * Unsets the editing mode from repeatedly creating a CNItem
314 		 */
315 		void slotUnsetRepeatedItemId();
316 		/**
317 		 * Called when the user changes the configuration.
318 		 * This, for example, will tell the CNItems on the canvas to update
319 		 * their configuration.
320 		 */
321 		void slotUpdateConfiguration() override;
322 		/**
323 		 * Enables / disables / selects various actions depending on
324 		 * what is selected or not.
325 		 */
326 		virtual void slotInitItemActions();
327 		/**
328 		 * Process queued events (see ItemDocument::ItemDocumentEvent).
329 		 */
330 		void processItemDocumentEvents();
331 
332 	signals:
333 		/**
334 		 * Emitted when the selection changes.
335 		 */
336 		void selectionChanged();
337 
338 	protected slots:
339 		/**
340 		 * Called after the canvas is resized to set the scrollbars of the
341 		 * ItemViews to either always show or always hidden.
342 		 */
343 		void updateItemViewScrollbars();
344 
345 	protected:
346 		/**
347 		 * Called from registerItem when a new item is added.
348 		 */
349 		virtual void itemAdded( Item * item );
350 		void handleNewView( View *view ) override;
351 		/**
352 		 * Set to true to remove buttons and grid and so on from the canvas, set false to put them back
353 		 */
354 		void setSVGExport( bool svgExport );
355 		void writeFile();
356 		/**
357 		 * Reinherit this if you want to add any options to the right-click context
358 		 */
359 		virtual void fillContextMenu( const QPoint & pos );
360 		/**
361 		 * Reads the background settings (grid-colour, underlying colour) from the Config settings,
362 		 * and generates the background pixmap from those settings
363 		 */
364 		void updateBackground();
365 		/**
366 		 * Sets the canvas size to both (a) containing all items present on the
367 		 * canvas, and (b) no smaller than the smallest view of the canvas. This
368 		 * function should only be called by processItemDocumentEvents - a resize
369 		 * request must be made with requestEvent.
370 		 */
371 		void resizeCanvasToItems();
372 
373 		Canvas		*m_canvas;
374 
375 		CMManager	*m_cmManager;
376 		CanvasTip	*m_canvasTip;
377 
378 		ItemList	 m_itemDeleteList;
379 		ItemMap		 m_itemList;
380 
381 		QString		 m_fileExtensionInfo; // For displaying in the save file dialog
382 
383 
384 
385 private:
386 	/**
387 	 * This clears a given stack and deletes all pointers, but the one to m_currentState.
388 	 */
389 	void cleanClearStack( IDDStack &stack );
390 
391 	static int	  m_nextActionTicket;
392 
393 	unsigned	  m_queuedEvents; // OR'ed together list of ItemDocumentEvent::type
394 	unsigned	  m_nextIdNum;
395 	int		  m_currentActionTicket;
396 	bool		  m_bIsLoading;
397 
398 	ItemDocumentData *m_currentState;
399 	ItemDocumentData *m_savedState; // Pointer to the document data that holds the state when it saved
400 
401 	KActionMenu	 *m_pAlignmentAction;
402 
403 	IntItemMap	  m_zOrder;
404 
405 	std::set<QString> m_idList; // used to ensure unique IDs to try to make sure save files are valid.
406 
407 	QTimer		*m_pEventTimer;
408 	QTimer		*m_pUpdateItemViewScrollbarsTimer;
409 
410 	IDDStack	 m_undoStack;
411 	IDDStack	 m_redoStack;
412 
413 	friend class ICNView;
414 	friend class ItemView;
415 };
416 
417 
418 /**
419 @author David Saxton
420 */
421 class Canvas : public KtlQCanvas
422 {
423 	Q_OBJECT
424 	public:
425 		Canvas( ItemDocument *itemDocument, const char * name = nullptr );
426 
427 		/**
428 		 * Sets a message to be displayed on the canvas for a brief period of
429 		 * time. If this is called with an empty message, then any existing
430 		 * message will be removed.
431 		 */
432 		void setMessage( const QString & message );
433 		void update() override;
434 		void resize( const QRect & size ) override;
435 
436 	signals:
437 		/**
438 		 * Emitted when the canvas rectangle-size changes.
439 		 */
440 		void resized( const QRect & oldSize, const QRect & newSize );
441 
442 	public slots:
slotSetAllChanged()443 		void slotSetAllChanged() { setAllChanged(); }
444 
445 	protected:
446 		void drawBackground ( QPainter & painter, const QRect & clip ) override;
447 		void drawForeground ( QPainter & painter, const QRect & clip ) override;
448 
449 		ItemDocument *p_itemDocument;
450 
451 		QString m_message;
452 		QTimer * m_pMessageTimeout;
453 };
454 
455 
456 /**
457 @author David Saxton
458 */
459 class CanvasTip : public KtlQCanvasRectangle
460 {
461 	public:
462 		CanvasTip( ItemDocument *itemDocument, KtlQCanvas *qcanvas );
463 		~CanvasTip() override;
464 
465 		void displayVI( ECNode *node, const QPoint &pos );
466 		void displayVI( Connector *connector, const QPoint &pos );
467 
468 	protected:
469 		void draw( QPainter &p ) override;
470 		void setText( const QString & text );
471 		bool updateVI();
472 		void display( const QPoint &pos );
473 		QString displayText( unsigned num ) const;
474 
475 		QVector<double> m_v;
476 		QVector<double> m_i;
477 		ItemDocument *p_itemDocument;
478 		QString m_text;
479 };
480 
481 
482 #endif
483