1 /***************************************************************************
2                           rwindowcatcher.h  -  description
3                              -------------------
4     begin                : Wed May 4 2005
5     copyright            : (C) 2005-2013 by Thomas Friedrichsmeier
6     email                : thomas.friedrichsmeier@kdemail.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #ifndef RKWINDOWCATCHER_H
19 #define RKWINDOWCATCHER_H
20 
21 //#define DISABLE_RKWINDOWCATCHER
22 #ifndef DISABLE_RKWINDOWCATCHER
23 
24 #include <qwidget.h>
25 #include <QMap>
26 #include <QTimer>
27 
28 #include <netwm_def.h>
29 class RKMDIWindow;
30 
31 /** This is a simple helper class helping in catching R X11 device windows. The start () and stop () functions are called from RInterface, and then this class takes care of handling those.
32 
33 The main difficulty to overcome in this context, is to find out, when an R X11 window is created, and what is its X Window id. The notes below are some thoughts on that matter. Probably mostly obsolete, now (the current approach is basically Plan C, and seems to work ok), but maybe Plans A or B or something similar will become necessary some day:
34 
35 Catch R X11 device windows
36 - Plan A:
37 	- initialization function seems to be in_do_X11
38 	- it might be possible to put a wrapper around this using R_setX11Routines
39 	- this wrapper could watch the list of devices (curDevice, numDevices), see also addDevice to find out how the list is kept internally
40 	- if a new device gets added grab its winId and capture
41 - Plan B:
42 	- it looks like there's no way to get access to R_setX11Routines or at least the needed struct R_X11Routines. (?)
43 	- the level above that seems to be do_X11
44 	- maybe we can modify the mapping from .Internal (X11) to do_X11 and insert wrapper from Plan A -> R_FunTab
45 	- proceed like in Plan A
46 	- less preferable as C-plugins might be able to call do_X11 directly (can they?)
47 - Plan C:
48 	- modify at R level (override X11 ())
49 		- notify app right before device is created
50 		- notify app right after device is created
51 	- least preferable solution as we can not be sure we catch every use.
52 		- but definitely most. This is dispatched via CurrentDevice ()->options("device"), and then evaluated in R_GlobalEnv
53 - remaining problem: how to get the window id given the device id?
54 	- http://tronche.com/gui/x/xlib/events/window-state-change/create.html#XCreateWindowEvent
55 	- for active / inactive: XPropertyEvent WM_NAME
56 	- we may catch this using KApplication::x11EventFilter
57 	- event filter should only be active during the wrapper (Plan A-C)
58 	- event filter should probably do some sanity checking
59 	- this should give us the window id corresponding to the x11-call
60 
61 @author Thomas Friedrichsmeier
62 */
63 class RKWindowCatcher : public QObject {
64 	Q_OBJECT
65 public:
66 /** ctor. Probably you'll only ever need one instance of RKWindowCatcher. */
67 	RKWindowCatcher ();
68 /** dtor */
69 	~RKWindowCatcher ();
70 
71 /** call this function to start looking out for new R X11 device windows.
72 @param prev_cur_device the device number that was active before a new device window was (potentially) created */
73 	void start (int prev_cur_device);
74 /** end looking out for new R X11 windows. If a new window was in fact created, this is captured by creating an RKCaughtX11Window
75 @param new_cur_device the new active device number, i.e. the device number of the created window */
76 	void stop (int new_cur_device);
77 /** called from the R backend when the device history needs to be updated
78 @param params the serialized parameters as supplied from R */
79 	void updateHistory (QStringList params);
80 /** Kill an R device
81 @param device_number R device number of the device to kil */
82 	void killDevice (int device_number);
83 	/** watch the given window for changes in its WM_NAME property (i.e. changes in caption), or deletion. When a change is detected, the watcher will be notified (setCaption() or deleteLater())
84 	    WARNING: Remember to call unregisterNameWatcher, when watcher is deleted! */
85 	void registerWatcher (WId watched, RKMDIWindow *watcher);
86 	/** remove a watch created with registerNameWatcher */
87 	void unregisterWatcher (WId watched);
88 	static RKWindowCatcher *instance ();
discardInstance()89 	static void discardInstance () { delete _instance; };
90 private:
91 	void pollWatchedWindowStates ();
92 	QTimer poll_timer;
93 	WId createdWindow ();
94 	int last_cur_device;
95 	QList<WId> windows_before_add;
96 	static RKWindowCatcher* _instance;
97 	QMap<WId, RKMDIWindow*> watchers_list;
98 };
99 
100 
101 #include "rkmdiwindow.h"
102 
103 #include <QHash>
104 
105 class RKCaughtX11WindowPart;
106 class KToggleAction;
107 class QAction;
108 class KSelectAction;
109 class QXEmbedCopy;
110 class QScrollArea;
111 class RKProgressControl;
112 class QX11EmbedContainer;
113 class QWinHost;
114 class RKGraphicsDevice;
115 
116 /** An R onscreen graphics device window managed by rkward. Currently, this can be X11 devices (on X11), Windows devices (on Windows), and
117  RK devices (anywhere). */
118 class RKCaughtX11Window : public RKMDIWindow {
119 	Q_OBJECT
120 public:
121 /** ctor
122 @param window_to_embed the Window id of the R X11 device window to embed
123 @param device_number the device number corresponding to that window */
124 	RKCaughtX11Window (QWindow* window_to_embed, int device_number);
125 	RKCaughtX11Window (RKGraphicsDevice *rkward_device, int device_number);
126 /** dtor */
127 	~RKCaughtX11Window ();
128 /** TODO? */
isModified()129 	bool isModified () override { return false; };
130 
131 /** reimplemented from RKMDIWindow to switch to fixed size mode, and disable the dynamic_size_action */
132 	void prepareToBeAttached () override;
133 /** see prepareToBeAttached (). Reenable the dynamic_size_action */
134 	void prepareToBeDetached () override;
135 /** returns the window corresponding the to given R device number (or 0 if no such window exists) */
getWindow(int device_number)136 	static RKCaughtX11Window* getWindow (int device_number) { return device_windows.value (device_number); };
137 	void updateHistoryActions (int history_length, int position, const QStringList &labels);
138 public slots:
139 	void deviceInteractive (bool interactive, const QString &prompt);
140 
141 /** Fixed size action was (potentially) toggled. Update to the new state */
142 	void fixedSizeToggled ();
143 /** Switch to fixed size mode, and set size1 (currently 500*500) */
144 	void setFixedSize1 ();
145 /** Switch to fixed size mode, and set size2 (currently 1000*1000) */
146 	void setFixedSize2 ();
147 /** Switch to fixed size mode, and set size3 (currently 2000*2000) */
148 	void setFixedSize3 ();
149 /** Switch to fixed size mode, and set user specified size (size read from a dialog) */
150 	void setFixedSizeManual ();
151 
152 	void activateDevice ();
153 	void copyDeviceToOutput ();
154 	void printDevice ();
155 	void copyDeviceToRObject ();
156 	void duplicateDevice ();
157 	void stopInteraction ();
158 
159 /** history navigation */
160 	void firstPlot ();
161 	void previousPlot ();
162 	void nextPlot ();
163 	void lastPlot ();
164 	void gotoPlot (int index);
165 	void forceAppendCurrentPlot ();
166 	void removeCurrentPlot ();
167 	void clearHistory ();
168 	void showPlotInfo ();
169 
170 /** reimplemented to keep window alive while saving history */
171 	bool close (bool also_delete) override;
setKilledInR()172 	void setKilledInR () { killed_in_r = true; };
173 	void setWindowStyleHint (const QString& hint) override;
174 private slots:
175 	void doEmbed ();
176 private:
177 	void forceClose ();
178 	void commonInit (int device_number);
179 	friend class RKCaughtX11WindowPart;	// needs access to the actions
180 	int device_number;
181 	bool killed_in_r;
182 	bool close_attempted;
183 	bool in_destructor;
184 	QWidget *xembed_container;
185 	QScrollArea *scroll_widget;
186 	RKProgressControl *error_dialog;
187 
188 	static QHash<int, RKCaughtX11Window*> device_windows;
189 	QWindow *embedded;
190 	bool embedding_complete;
191 	QWidget *capture;  // The captured window (0, if using an rk native device)
192 	RKGraphicsDevice *rk_native_device;
193 
194 	bool dynamic_size;
195 	KToggleAction *dynamic_size_action;
196 	QAction *plot_prev_action;
197 	QAction *plot_next_action;
198 	QAction *plot_first_action;
199 	QAction *plot_last_action;
200 	QAction *plot_force_append_action;
201 	QAction *plot_remove_action;
202 	QAction *plot_clear_history_action;
203 	QAction *plot_properties_action;
204 	KSelectAction *plot_list_action;
205 	QAction *stop_interaction;
206 
207 	QList<QAction*> actions_not_for_preview;
208 	int history_length;
209 	int history_position;
210 };
211 
212 /** Provides a KPart interface for RKCaughtX11Window. */
213 class RKCaughtX11WindowPart : public KParts::Part {
214 public:
215 /** constructor.
216 @param window The RKCatehdX11Window for this part */
217 	explicit RKCaughtX11WindowPart (RKCaughtX11Window *window);
218 /** destructor */
219 	~RKCaughtX11WindowPart ();
220 private:
221 	RKCaughtX11Window *window;
222 };
223 
224 #endif //DISABLE_RKWINDOWCATCHER
225 #endif
226