aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/objects/KvsObject_wrapper.cpp
blob: 147ca7d4eba19887fe12453f7f99a7270097fcfd (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
//=============================================================================
//
//   File : KvsObject_wrapper.cpp
//   Creation date : Fri Mar 18 14:30:48 CEST 2005
//   by Tonino Imbesi(Grifisx) and Alessandro Carbone(Noldor)
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2005-2008 Alessandro Carbone (elfonol at gmail dot com)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your option) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#include "KvsObject_wrapper.h"
#include "KviError.h"
#include "kvi_debug.h"

#include "KviLocale.h"
#include "KviIconManager.h"

#include "KvsObject_widget.h"
#include <qwidget.h>
#include "KviApplication.h"
#include "KviMainWindow.h"

/*
	@doc: wrapper
	@keyterms:
		wrapper object class,
	@title:
		wrapper class
	@type:
		class
	@short:
		A wrapper for the existing Qt widgets.
	@inherits:
		[class]object[/class]
		[class]widget[/class]
	@description:
		This class "wraps" existing KVIrc widgets and allows using the [class]widget[/class]
		class API to manipulate them. You can use it, for example, to set the geometry of the
		KVIrc main window or to apply some crazy graphical changes to the UI.[br]
		The KVIrc Qt widgets are arranged in trees (just as the objects). The difference is that there can be more than one
		toplevel widget and so more than one tree. You can use [fnc]$objects.dump()[/fnc] to take a look at the KVIrc Qt objects tree.[br]
		Here is a part of the tree:
		[example]
			Ptr 23786128: top level object: kvirc_frame, class KviMainWindow, visible, rect = 1678, -3, 1680, 1030
			>Ptr 23496976: object: qt_rubberband, class QRubberBand
			>Ptr 23536608: object: main_frame_splitter, class QSplitter
			>>Ptr 23795232: object: mdi_manager, class KviWindowStack
			>>>Ptr 23863200: object: qt_scrollarea_hcontainer, class QWidget
			>>>>Ptr 23418224: object: , class QScrollBar
			>>>Ptr 23864832: object: qt_scrollarea_vcontainer, class QWidget
			>>>>Ptr 22383424: object: , class QScrollBar
			>>Ptr 25750832: object: mdi_manager, class KviWindowStack
			>>>Ptr 26112928: object: , class QWidget
			>>>>Ptr 45381568: object: , class Oxygen::MdiWindowShadow
			>>>>Ptr 45952496: object: mdi_child_Azzurra_#kvirc, class KviMdiChild
			>>>>>Ptr 43714656: object: #kvirc, class KviChannelWindow
			>Ptr 18004432: object: , class KviStatusBar
			>>Ptr 18007408: object: msgstatuslabel, class QLabel
			>>>Ptr 24067088: object: , class Oxygen::TransitionWidget
			>Ptr 24503248: object: windowlist, class KviTreeWindowList
			>>Ptr 24459744: object: qt_dockwidget_floatbutton, class QDockWidgetTitleButton
			>>Ptr 24498560: object: qt_dockwidget_closebutton, class QDockWidgetTitleButton
			>>Ptr 23996288: object: tree_windowlist, class KviTreeWindowListTreeWidget
		[/example]
		As you can see the objects are identified by their names (for example "mdi_manager") and
		by their class names (for example KviChannelWindow).[br]
		To wrap a specific widget you must provide a path in the tree composed of search specifiers.[br]
		Each search specifier can have one of the following forms:[br]
		[example]
			(1) <class>
			(2) <class>::<name>
			(3) ::<name>
			(4) !Window::<window_identifier>
			(5) !Parent::N
		[/example]
		The first three forms may be preceded by the prefix [b]*[/b] which will tell KVS to perform
		a recursive search from this point. Let's see some examples.[br]
		The form (1) matches the first widget with the specified class name.[br]
		For instance:[br]
		[example]
			%Frame = $new(wrapper,0,test,KviMainWindow)
		[/example]
		This will wrap the first top level object with class KviMainWindow. Now you can use
		any [class]widget[/class] or [class]object[/class] methods on it.[br]
		[example]
			%Frame = $new(wrapper,0,test,KviMainWindow)
			%Frame->$setGeometry(20,20,400,400);
		[/example]
		If you want to wrap the KVIrc status bar you can use a composite path:[br]
		[example]
			%StatusBar = $new(wrapper,0,test,KviMainWindow,KviStatusBar)
			%StatusBar->$setProperty(autoFillBackground,1)
			%StatusBar->$setBackgroundColor(80,80,0)
		[/example]
		The form (2) matches both the class and the widget name. In this way you can differentiate
		between children that have the same class.[br]
		For instance:[br]
		[example]
			%VerticalScrollBar = $new(wrapper,0,test,KviMainWindow,QSplitter,KviWindowStack,QWidget::qt_scrollarea_vcontainer,QScrollBar)
			%VerticalScrollBar->$setProperty(invertedAppearance,1);
		[/example]
		In this way KVS was able to pick the vertical scrollbar instead of the horizontal one (which comes first in the list).
		(Now try to move a window out of the MDI area: the vertical scroll bar will be inverted!).[br]
		The form (3) matches only the name and ignores the class.[br]
		In our sample tree the following example is equivalent to the previous one.
		[example]
			%VerticalScrollBar = $new(wrapper,0,test,KviMainWindow,QSplitter,KviWindowStack,::qt_scrollarea_vcontainer,QScrollBar)
			%VerticalScrollBar->$setProperty(invertedAppearance,1);
		[/example]
		If you don't want to specify the full path to the widget you can try to use a recursive search which may skip some levels.
		Keep in mind that the recursive search is breadth-first and will return the first widget that matches.[br]
		In our sample tree the following would match the first widget with class KviChannelWindow.[br]
		[example]
			%Chan = $new(wrapper,0,test,*KviChannelWindow)
			%Chan->$setBackgroundColor(80,0,0);
		[/example]
		The following would match the first widget with name #kvirc[br]
		[example]
			%Chan = $new(wrapper,0,test,*::#kvirc)
			%Chan->$setBackgroundColor(80,0,0);
		[/example]
		The recursive search can start at any level, so if starting from the root does not work properly you might try
		specifying a part of the path and then searching recursively.[br]
		[example]
			%Chan = $new(wrapper,0,test,KviMainWindow,*::#kvirc)
			%Chan->$setBackgroundColor(80,0,0);
		[/example]
		The form (4) allows you to jump directly to a specific KVIrc channel/query/console window,
		without the need of looking it up in the tree.[br]
		[example]
			%Win = $new(wrapper,0,test,!Window::$window)
			%Win->$setBackgroundColor(80,0,0);
		[/example]
		Finally the last form allows you to jump N levels up in the tree.[br]
		If N is omitted it is assumed to be 1.
		[example]
			%Win = $new(wrapper,0,test,!Window::$window,!Parent::3)
			%Win->$setGeometry(10,10,40,40)
		[/example]
		Experiment with it :)
*/

KVSO_BEGIN_REGISTERCLASS(KvsObject_wrapper, "wrapper", "widget")

KVSO_END_REGISTERCLASS(KvsObject_wrapper)

KVSO_BEGIN_CONSTRUCTOR(KvsObject_wrapper, KvsObject_widget)

KVSO_END_CONSTRUCTOR(KvsObject_wrapper)

KVSO_BEGIN_DESTRUCTOR(KvsObject_wrapper)

KVSO_END_CONSTRUCTOR(KvsObject_wrapper)

bool KvsObject_wrapper::init(KviKvsRunTimeContext * pContext, KviKvsVariantList * pParams)
{
	if(!pParams)
		return false;

	QWidget * pWidget = nullptr;
	unsigned int i = 0;

	while(i < pParams->count())
	{
		QString szClass;
		QString szName;
		QString s = nullptr;

		pParams->at(i)->asString(s);
		i++;

		if(s.isEmpty())
			continue;

		bool bRecursive = false;

		if(s.startsWith("*"))
		{
			s.remove(0, 1);
			bRecursive = true;
			if(s.isEmpty())
			{
				pContext->error(__tr2qs_ctx("The search specifier can't be empty", "objects"));
				return false;
			}
		}

		int idx = s.indexOf("::");
		if(idx != -1)
		{
			szClass = s.left(idx);
			szName = s.right(s.length() - idx - 2);
		}
		else
		{
			szClass = s;
			szName = "";
		}

		if(
		    KviQString::equalCI(szClass, "!Window") || KviQString::equalCI(szClass, "WinId") // compat
		    )
		{
			if(pWidget)
				pContext->warning(__tr2qs_ctx("The window identifier preceded by '!Window' should be the first in the search path", "objects"));

			pWidget = g_pApp->findWindow(szName);
		}
		else if(KviQString::equalCI(szClass, "!Parent"))
		{
			if(!pWidget)
			{
				pContext->warning(__tr2qs_ctx("The '!Parent' specifier can't be used as first in the search path", "objects"));
				return false;
			}

			int iLevels = 1;
			if(!szName.isEmpty())
			{
				bool ok;
				iLevels = szName.toInt(&ok);
				if(!ok)
				{
					pContext->warning(__tr2qs_ctx("Bad number of levels for the '!Parent' specifier", "objects"));
					return false;
				}
			}

			while(iLevels > 0)
			{
				pWidget = pWidget->parentWidget();

				if(!pWidget)
				{
					pContext->warning(__tr2qs_ctx("The '!Parent' specifier was applied to a widget that has no parent", "objects"));
					return false;
				}

				iLevels--;
			}
		}
		else
		{
			if(pWidget)
				pWidget = findWidgetToWrap(szClass, szName, pWidget, bRecursive);
			else
				pWidget = findTopLevelWidgetToWrap(szClass, szName, bRecursive);
		}

		if(!pWidget)
		{
			pContext->error(__tr2qs_ctx("Failed to find one of the wrap path widgets ('%Q::%Q')", "objects"), &szClass, &szName);
			return false;
		}
	}

	if(!pWidget)
	{
		pContext->error(__tr2qs_ctx("Failed to find the widget to wrap", "objects"));
		return false;
	}

	setObject(pWidget, false);
	return true;
}

QWidget * KvsObject_wrapper::findTopLevelWidgetToWrap(const QString & szClass, const QString & szName, bool bRecursive)
{
	QWidgetList list = g_pApp->topLevelWidgets();
	if(list.isEmpty())
		return nullptr;

	Q_FOREACH(QWidget * w, list)
	{
		//qDebug("TLW: %s::%s (look for %s::%s)",w->metaObject()->className(),w->objectName().toUtf8().data(),szClass.toUtf8().data(),szName.toUtf8().data());
		if(
		    (
		        szClass.isEmpty() || KviQString::equalCI(w->metaObject()->className(), szClass))
		    && (szName.isEmpty() || KviQString::equalCI(w->objectName(), szName)))
			return w;
	}

	if(bRecursive)
	{
		Q_FOREACH(QWidget * w, list)
		{
			w = findWidgetToWrap(szClass, szName, w, bRecursive);
			if(w)
				return w;
		}
	}

	return nullptr;
}

QWidget * KvsObject_wrapper::findWidgetToWrap(const QString & szClass, const QString & szName, QWidget * pParent, bool bRecursive)
{
	QList<QObject *> list = pParent->children();
	if(list.isEmpty())
		return nullptr;

	Q_FOREACH(QObject * obj, list)
	{
		if(!obj->isWidgetType())
			continue;

		QWidget * w = (QWidget *)obj;
		if(
		    (
		        szClass.isEmpty() || KviQString::equalCI(w->metaObject()->className(), szClass))
		    && (szName.isEmpty() || KviQString::equalCI(w->objectName(), szName)))
			return w;
	}

	if(bRecursive)
	{
		Q_FOREACH(QObject * obj, list)
		{
			if(!obj->isWidgetType())
				continue;

			QWidget * w = findWidgetToWrap(szClass, szName, (QWidget *)obj, bRecursive);
			if(w)
				return w;
		}
	}

	return nullptr;
}