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