1 /**
2 * qt4client.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * A Qt-4 based universal telephony client
6 *
7 * Yet Another Telephony Engine - a fully featured software PBX and IVR
8 * Copyright (C) 2004-2014 Null Team
9 *
10 * This software is distributed under multiple licenses;
11 * see the COPYING file in the main directory for licensing
12 * information for this specific distribution.
13 *
14 * This use of this software may be subject to additional restrictions.
15 * See the LEGAL file in the main directory for details.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 #include "qt4client.h"
23 #include <QtUiTools>
24
25 #ifdef _WINDOWS
26 #define DEFAULT_DEVICE "dsound/*"
27 #define PLATFORM_LOWERCASE_NAME "windows"
28 #elif defined(__APPLE__)
29 #define DEFAULT_DEVICE "coreaudio/*"
30 #define PLATFORM_LOWERCASE_NAME "apple"
31 #elif defined(__linux__)
32 #define DEFAULT_DEVICE "alsa/default"
33 #define PLATFORM_LOWERCASE_NAME "linux"
34 #else
35 #define DEFAULT_DEVICE "oss//dev/dsp"
36 #define PLATFORM_LOWERCASE_NAME "unknown"
37 #endif
38
39 namespace TelEngine {
40
41 static unsigned int s_allHiddenQuit = 0; // Quit on all hidden notification if this counter is 0
42
43 // Factory used to create objects in client's thread
44 class Qt4ClientFactory : public UIFactory
45 {
46 public:
47 Qt4ClientFactory(const char* name = "Qt4ClientFactory");
48 virtual void* create(const String& type, const char* name, NamedList* params = 0);
49 };
50
51 // Class used for temporary operations on QT widgets
52 // Keeps a pointer to a widget and its type
53 // NOTE: The methods of this class don't check the widget pointer
54 class QtWidget
55 {
56 public:
57 enum Type {
58 PushButton = 0,
59 CheckBox = 1,
60 Table = 2,
61 ListBox = 3,
62 ComboBox = 4,
63 Tab = 5,
64 StackWidget = 6,
65 TextEdit = 7,
66 Label = 8,
67 LineEdit = 9,
68 AbstractButton = 10,
69 Slider = 11,
70 ProgressBar = 12,
71 SpinBox = 13,
72 Calendar = 14,
73 Splitter = 15,
74 TextBrowser = 16,
75 Unknown, // Unknown type
76 Action, // QAction descendant
77 CustomTable, // QtTable descendant
78 CustomTree, // QtTree descendant
79 CustomWidget, // QtCustomWidget descendant
80 CustomObject, // QtCustomObject descendant
81 Missing // Invalid pointer
82 };
83 // Set widget from object
QtWidget(QObject * w)84 inline QtWidget(QObject* w)
85 : m_widget(0), m_action(0), m_object(0), m_type(Missing) {
86 if (!w)
87 return;
88 if (w->inherits("QWidget"))
89 m_widget = static_cast<QWidget*>(w);
90 else if (w->inherits("QAction"))
91 m_action = static_cast<QAction*>(w);
92 m_type = getType();
93 }
94 // Set widget from object and type
QtWidget(QWidget * w,int t)95 inline QtWidget(QWidget* w, int t)
96 : m_widget(w), m_action(0), m_object(0), m_type(t) {
97 if (!m_widget)
98 m_type = Missing;
99 }
100 // Set widget/action from object and name
QtWidget(QObject * parent,const String & name)101 inline QtWidget(QObject* parent, const String& name)
102 : m_widget(0), m_action(0), m_object(0), m_type(Missing) {
103 QString what = QtClient::setUtf8(name);
104 m_widget = qFindChild<QWidget*>(parent,what);
105 if (!m_widget) {
106 m_action = qFindChild<QAction*>(parent,what);
107 if (!m_action)
108 m_object = qFindChild<QObject*>(parent,what);
109 }
110 m_type = getType();
111 }
valid() const112 inline bool valid() const
113 { return type() != Missing; }
invalid() const114 inline bool invalid() const
115 { return type() == Missing; }
type() const116 inline int type() const
117 { return m_type; }
operator QWidget*()118 inline operator QWidget*()
119 { return m_widget; }
inherits(const char * classname)120 inline bool inherits(const char* classname)
121 { return m_widget && m_widget->inherits(classname); }
inherits(Type t)122 inline bool inherits(Type t)
123 { return inherits(s_types[t]); }
widget()124 inline QWidget* widget()
125 { return m_widget; }
operator ->()126 inline QWidget* operator ->()
127 { return m_widget; }
128 // Static cast methods
button()129 inline QPushButton* button()
130 { return static_cast<QPushButton*>(m_widget); }
check()131 inline QCheckBox* check()
132 { return static_cast<QCheckBox*>(m_widget); }
table()133 inline QTableWidget* table()
134 { return static_cast<QTableWidget*>(m_widget); }
list()135 inline QListWidget* list()
136 { return static_cast<QListWidget*>(m_widget); }
combo()137 inline QComboBox* combo()
138 { return static_cast<QComboBox*>(m_widget); }
tab()139 inline QTabWidget* tab()
140 { return static_cast<QTabWidget*>(m_widget); }
stackWidget()141 inline QStackedWidget* stackWidget()
142 { return static_cast<QStackedWidget*>(m_widget); }
textEdit()143 inline QTextEdit* textEdit()
144 { return static_cast<QTextEdit*>(m_widget); }
label()145 inline QLabel* label()
146 { return static_cast<QLabel*>(m_widget); }
lineEdit()147 inline QLineEdit* lineEdit()
148 { return static_cast<QLineEdit*>(m_widget); }
abstractButton()149 inline QAbstractButton* abstractButton()
150 { return static_cast<QAbstractButton*>(m_widget); }
slider()151 inline QSlider* slider()
152 { return static_cast<QSlider*>(m_widget); }
progressBar()153 inline QProgressBar* progressBar()
154 { return static_cast<QProgressBar*>(m_widget); }
spinBox()155 inline QSpinBox* spinBox()
156 { return static_cast<QSpinBox*>(m_widget); }
calendar()157 inline QCalendarWidget* calendar()
158 { return static_cast<QCalendarWidget*>(m_widget); }
splitter()159 inline QSplitter* splitter()
160 { return static_cast<QSplitter*>(m_widget); }
customTable()161 inline QtTable* customTable()
162 { return qobject_cast<QtTable*>(m_widget); }
customTree()163 inline QtTree* customTree()
164 { return qobject_cast<QtTree*>(m_widget); }
customWidget()165 inline QtCustomWidget* customWidget()
166 { return qobject_cast<QtCustomWidget*>(m_widget); }
customObject()167 inline QtCustomObject* customObject()
168 { return qobject_cast<QtCustomObject*>(m_object); }
uiWidget()169 inline UIWidget* uiWidget() {
170 switch (type()) {
171 case CustomTable:
172 return static_cast<UIWidget*>(customTable());
173 case CustomWidget:
174 return static_cast<UIWidget*>(customWidget());
175 case CustomObject:
176 return static_cast<UIWidget*>(customObject());
177 case CustomTree:
178 return static_cast<UIWidget*>(customTree());
179 }
180 return 0;
181 }
182
action()183 inline QAction* action()
184 { return m_action; }
185
186 // Find a combo box item
findComboItem(const String & item)187 inline int findComboItem(const String& item) {
188 QComboBox* c = combo();
189 return c ? c->findText(QtClient::setUtf8(item)) : -1;
190 }
191 // Add an item to a combo box
addComboItem(const String & item,bool atStart)192 inline bool addComboItem(const String& item, bool atStart) {
193 QComboBox* c = combo();
194 if (!c)
195 return false;
196 QString it(QtClient::setUtf8(item));
197 if (atStart)
198 c->insertItem(0,it);
199 else
200 c->addItem(it);
201 return true;
202 }
203 // Find a list box item
findListItem(const String & item)204 inline int findListItem(const String& item) {
205 QListWidget* l = list();
206 if (!l)
207 return -1;
208 QString it(QtClient::setUtf8(item));
209 for (int i = l->count(); i >= 0 ; i--) {
210 QListWidgetItem* tmp = l->item(i);
211 if (tmp && it == tmp->text())
212 return i;
213 }
214 return -1;
215 }
216 // Add an item to a list box
addListItem(const String & item,bool atStart)217 inline bool addListItem(const String& item, bool atStart) {
218 QListWidget* l = list();
219 if (!l)
220 return false;
221 QString it(QtClient::setUtf8(item));
222 if (atStart)
223 l->insertItem(0,it);
224 else
225 l->addItem(it);
226 return true;
227 }
228
getType()229 int getType() {
230 if (m_widget) {
231 String cls = m_widget->metaObject()->className();
232 for (int i = 0; i < Unknown; i++)
233 if (s_types[i] == cls)
234 return i;
235 if (customTable())
236 return CustomTable;
237 if (customWidget())
238 return CustomWidget;
239 if (customTree())
240 return CustomTree;
241 return Unknown;
242 }
243 if (m_action && m_action->inherits("QAction"))
244 return Action;
245 if (customObject())
246 return CustomObject;
247 return Missing;
248 }
249 static String s_types[Unknown];
250 protected:
251 QWidget* m_widget;
252 QAction* m_action;
253 QObject* m_object;
254 int m_type;
255 private:
QtWidget()256 QtWidget() {}
257 };
258
259 // Class used for temporary operations on QTableWidget objects
260 // NOTE: The methods of this class don't check the table pointer
261 class TableWidget : public GenObject
262 {
263 public:
264 TableWidget(QTableWidget* table, bool tmp = true);
265 TableWidget(QWidget* wid, const String& name, bool tmp = true);
266 TableWidget(QtWidget& table, bool tmp = true);
267 ~TableWidget();
table()268 inline QTableWidget* table()
269 { return m_table; }
valid()270 inline bool valid()
271 { return m_table != 0; }
customTable()272 inline QtTable* customTable()
273 { return (valid()) ? qobject_cast<QtTable*>(m_table) : 0; }
name()274 inline const String& name()
275 { return m_name; }
rowCount()276 inline int rowCount()
277 { return m_table->rowCount(); }
columnCount()278 inline int columnCount()
279 { return m_table->columnCount(); }
setHeaderText(int col,const char * text)280 inline void setHeaderText(int col, const char* text) {
281 if (col < columnCount())
282 m_table->setHorizontalHeaderItem(col,
283 new QTableWidgetItem(QtClient::setUtf8(text)));
284 }
getHeaderText(int col,String & dest,bool lower=true)285 inline bool getHeaderText(int col, String& dest, bool lower = true) {
286 QTableWidgetItem* item = m_table->horizontalHeaderItem(col);
287 if (item) {
288 QtClient::getUtf8(dest,item->text());
289 if (lower)
290 dest.toLower();
291 }
292 return item != 0;
293 }
294 // Get the current selection's row
crtRow()295 inline int crtRow() {
296 QList<QTableWidgetItem*> items = m_table->selectedItems();
297 if (items.size())
298 return items[0]->row();
299 return -1;
300 }
repaint()301 inline void repaint()
302 { m_table->repaint(); }
addRow(int index)303 inline void addRow(int index)
304 { m_table->insertRow(index); }
delRow(int index)305 inline void delRow(int index) {
306 if (index >= 0)
307 m_table->removeRow(index);
308 }
addColumn(int index,int width=-1,const char * name=0)309 inline void addColumn(int index, int width = -1, const char* name = 0) {
310 m_table->insertColumn(index);
311 if (width >= 0)
312 m_table->setColumnWidth(index,width);
313 setHeaderText(index,name);
314 }
setImage(int row,int col,const String & image)315 inline void setImage(int row, int col, const String& image) {
316 QTableWidgetItem* item = m_table->item(row,col);
317 if (item)
318 item->setIcon(QIcon(QtClient::setUtf8(image)));
319 }
addCell(int row,int col,const String & value)320 inline void addCell(int row, int col, const String& value) {
321 QTableWidgetItem* item = new QTableWidgetItem(QtClient::setUtf8(value));
322 m_table->setItem(row,col,item);
323 }
setCell(int row,int col,const String & value,bool addNew=true)324 inline void setCell(int row, int col, const String& value, bool addNew = true) {
325 QTableWidgetItem* item = m_table->item(row,col);
326 if (item)
327 item->setText(QtClient::setUtf8(value));
328 else if (addNew)
329 addCell(row,col,value);
330 }
getCell(int row,int col,String & dest,bool lower=false)331 inline bool getCell(int row, int col, String& dest, bool lower = false) {
332 QTableWidgetItem* item = m_table->item(row,col);
333 if (item) {
334 QtClient::getUtf8(dest,item->text());
335 if (lower)
336 dest.toLower();
337 return true;
338 }
339 return false;
340 }
setID(int row,const String & value)341 inline void setID(int row, const String& value)
342 { setCell(row,0,value); }
343 // Add or set a row
344 void updateRow(const String& item, const NamedList* data, bool atStart);
345 // Update a row from a list of parameters
346 void updateRow(int row, const NamedList& data);
347 // Find a row by the first's column value. Return -1 if not found
348 int getRow(const String& item);
349 // Find a column by its label. Return -1 if not found
350 int getColumn(const String& name, bool caseInsentive = true);
351 protected:
352 void init(bool tmp);
353 private:
354 QTableWidget* m_table; // The table
355 String m_name; // Table's name
356 int m_sortControl; // Flag used to set/reset sorting attribute of the table
357 };
358
359 // Store an UI loaded from file to avoid loading it again
360 class UIBuffer : public String
361 {
362 public:
UIBuffer(const String & name,QByteArray * buf)363 inline UIBuffer(const String& name, QByteArray* buf)
364 : String(name), m_buffer(buf)
365 { s_uiCache.append(this); }
buffer()366 inline QByteArray* buffer()
367 { return m_buffer; }
368 // Remove from list. Release memory
369 virtual void destruct();
370 // Return an already loaded UI. Load from file if not found.
371 // Add URLs paths when missing
372 static UIBuffer* build(const String& name);
373 // Find a buffer
374 static UIBuffer* find(const String& name);
375 // Buffer cache
376 static ObjList s_uiCache;
377 private:
378 QByteArray* m_buffer; // The buffer
379 };
380
381 }; // namespace TelEngine
382
383 using namespace TelEngine;
384
385 // Dynamic properies
386 static const String s_propsSave = "_yate_save_props"; // Save properties property name
387 static const String s_propColWidths = "_yate_col_widths"; // Column widths
388 static const String s_propSorting = "_yate_sorting"; // Table/List sorting
389 static const String s_propSizes = "_yate_sizes"; // Size int array
390 static const String s_propShowWndWhenActive = "_yate_showwnd_onactive"; // Show another window when a window become active
391 static String s_propHHeader = "dynamicHHeader"; // Tables: show/hide the horizontal header
392 static String s_propAction = "dynamicAction"; // Prefix for properties that would trigger some action
393 static String s_propWindowFlags = "_yate_windowflags"; // Window flags
394 static const String s_propContextMenu = "_yate_context_menu"; // Context menu name
395 static String s_propHideInactive = "dynamicHideOnInactive"; // Hide inactive window
396 static const String s_yatePropPrefix = "_yate_"; // Yate dynamic properties prefix
397 static NamedList s_qtStyles(""); // Qt styles classname -> internal name
398 //
399 static Qt4ClientFactory s_qt4Factory;
400 static Configuration s_cfg;
401 static Configuration s_save;
402 ObjList UIBuffer::s_uiCache;
403
404 // Values used to configure window title bar and border
405 static TokenDict s_windowFlags[] = {
406 // Window type
407 {"popup", Qt::Popup},
408 {"tool", Qt::Tool},
409 {"subwindow", Qt::SubWindow},
410 #ifdef _WINDOWS
411 {"notificationtype", Qt::Tool},
412 #else
413 {"notificationtype", Qt::SubWindow},
414 #endif
415 // Window flags
416 {"title", Qt::WindowTitleHint},
417 {"sysmenu", Qt::WindowSystemMenuHint},
418 {"maximize", Qt::WindowMaximizeButtonHint},
419 {"minimize", Qt::WindowMinimizeButtonHint},
420 {"help", Qt::WindowContextHelpButtonHint},
421 {"stayontop", Qt::WindowStaysOnTopHint},
422 {"frameless", Qt::FramelessWindowHint},
423 #if QT_VERSION >= 0x040500
424 {"close", Qt::WindowCloseButtonHint},
425 #endif
426 {0,0}
427 };
428
429 // Widget attribute names
430 static const TokenDict s_widgetAttributes[] = {
431 {"macshowfocusrect", Qt::WA_MacShowFocusRect},
432 {0,0}
433 };
434
435 String QtWidget::s_types[QtWidget::Unknown] = {
436 "QPushButton",
437 "QCheckBox",
438 "QTableWidget",
439 "QListWidget",
440 "QComboBox",
441 "QTabWidget",
442 "QStackedWidget",
443 "QTextEdit",
444 "QLabel",
445 "QLineEdit",
446 "QAbstractButton",
447 "QSlider",
448 "QProgressBar",
449 "QSpinBox",
450 "QCalendarWidget",
451 "QSplitter",
452 "QTextBrowser",
453 };
454
455 // QVariant type translation dictionary
456 static const TokenDict s_qVarType[] = {
457 {"string", QVariant::String},
458 {"bool", QVariant::Bool},
459 {"int", QVariant::Int},
460 {"uint", QVariant::UInt},
461 {"stringlist", QVariant::StringList},
462 {"icon", QVariant::Icon},
463 {"pixmap", QVariant::Pixmap},
464 {"double", QVariant::Double},
465 {"keysequence", QVariant::KeySequence},
466 {0,0}
467 };
468
469 // Qt alignment flags translation
470 static const TokenDict s_qAlign[] = {
471 {"left", Qt::AlignLeft},
472 {"right", Qt::AlignRight},
473 {"hcenter", Qt::AlignHCenter},
474 {"justify", Qt::AlignJustify},
475 {"top", Qt::AlignTop},
476 {"bottom", Qt::AlignBottom},
477 {"vcenter", Qt::AlignVCenter},
478 {"center", Qt::AlignCenter},
479 {"absolute", Qt::AlignAbsolute},
480 {0,0}
481 };
482
483 // Qt alignment flags translation
484 static const TokenDict s_qEditTriggers[] = {
485 {"currentchanged", QAbstractItemView::CurrentChanged},
486 {"doubleclick", QAbstractItemView::DoubleClicked},
487 {"selclick", QAbstractItemView::SelectedClicked},
488 {"editkeypress", QAbstractItemView::EditKeyPressed},
489 {"anykeypress", QAbstractItemView::AnyKeyPressed},
490 {"all", QAbstractItemView::AllEditTriggers},
491 {0,0}
492 };
493
494 // QtClientSort name
495 static const TokenDict s_sorting[] = {
496 {"ascending", QtClient::SortAsc},
497 {"descending", QtClient::SortDesc},
498 {"none", QtClient::SortNone},
499 {0,0}
500 };
501
502 // Handler for QT library messages
qtMsgHandler(QtMsgType type,const char * text)503 static void qtMsgHandler(QtMsgType type, const char* text)
504 {
505 int dbg = DebugAll;
506 switch (type) {
507 case QtDebugMsg:
508 dbg = DebugInfo;
509 break;
510 case QtWarningMsg:
511 dbg = DebugWarn;
512 break;
513 case QtCriticalMsg:
514 dbg = DebugCrit;
515 break;
516 case QtFatalMsg:
517 dbg = DebugFail;
518 break;
519 }
520 Debug("QT",dbg,"%s",text);
521 }
522
523 // Build a list of parameters from a string
524 // Return the number of parameters found
str2Params(NamedList & params,const String & buf,char sep='|')525 static unsigned int str2Params(NamedList& params, const String& buf, char sep = '|')
526 {
527 ObjList* list = 0;
528 // Check if we have another separator
529 if (buf.startsWith("separator=")) {
530 sep = buf.at(10);
531 list = buf.substr(11).split(sep,false);
532 }
533 else
534 list = buf.split(sep,false);
535 unsigned int n = 0;
536 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
537 String* s = static_cast<String*>(o->get());
538 int pos = s->find('=');
539 if (pos < 1)
540 continue;
541 params.addParam(s->substr(0,pos),s->substr(pos + 1));
542 n++;
543 }
544 TelEngine::destruct(list);
545 return n;
546 }
547
548 // Utility: fix QT path separator on Windows
549 // (display paths using only one separator to the user)
fixPathSep(QString str)550 static inline QString fixPathSep(QString str)
551 {
552 #ifdef _WINDOWS
553 QString tmp = str;
554 tmp.replace(QChar('/'),QtClient::setUtf8(Engine::pathSeparator()));
555 return tmp;
556 #else
557 return str;
558 #endif
559 }
560
561 // Utility: find a stacked widget's page with the given name
findStackedWidget(QStackedWidget & w,const String & name)562 static int findStackedWidget(QStackedWidget& w, const String& name)
563 {
564 QString n(QtClient::setUtf8(name));
565 for (int i = 0; i < w.count(); i++) {
566 QWidget* page = w.widget(i);
567 if (page && n == page->objectName())
568 return i;
569 }
570 return -1;
571 }
572
573 // Utility function used to get the name of a control
574 // The name of the control indicates actions, toggles ...
575 // The action name alias can contain parameters
translateName(QtWidget & w,String & name,NamedList ** params=0)576 static bool translateName(QtWidget& w, String& name, NamedList** params = 0)
577 {
578 if (w.invalid())
579 return false;
580 if (w.type() != QtWidget::Action)
581 QtClient::getIdentity(w.widget(),name);
582 else
583 QtClient::getIdentity(w.action(),name);
584 if (!name)
585 return true;
586 // Check params
587 int pos = name.find('|');
588 if (pos < 1)
589 return true;
590 if (params) {
591 *params = new NamedList("");
592 if (!str2Params(**params,name.substr(pos + 1)))
593 TelEngine::destruct(*params);
594 }
595 name = name.substr(0,pos);
596 return true;
597 }
598
599 // Utility: raise a select event if a list is empty
raiseSelectIfEmpty(int count,Window * wnd,const String & name)600 static inline void raiseSelectIfEmpty(int count, Window* wnd, const String& name)
601 {
602 if (!Client::exiting() && count <= 0 && Client::self())
603 Client::self()->select(wnd,name,String::empty());
604 }
605
606 // Add dynamic properties from a list of parameters
607 // Parameter format:
608 // property_name:property_type=property_value
addDynamicProps(QObject * obj,NamedList & props)609 static void addDynamicProps(QObject* obj, NamedList& props)
610 {
611 static String typeString = "string";
612 static String typeBool = "bool";
613 static String typeInt = "int";
614
615 if (!obj)
616 return;
617 unsigned int n = props.length();
618 for (unsigned int i = 0; i < n; i++) {
619 NamedString* ns = props.getParam(i);
620 if (!(ns && ns->name()))
621 continue;
622 int pos = ns->name().find(':');
623 if (pos < 1)
624 continue;
625
626 String prop = ns->name().substr(0,pos);
627 String type = ns->name().substr(pos + 1);
628 QVariant var;
629 if (type == typeString)
630 var.setValue(QString(ns->c_str()));
631 else if (type == typeBool)
632 var.setValue(ns->toBoolean());
633 else if (type == typeInt)
634 var.setValue(ns->toInteger());
635
636 if (var.type() != QVariant::Invalid) {
637 obj->setProperty(prop,var);
638 DDebug(ClientDriver::self(),DebugAll,
639 "Object '%s': added dynamic property %s='%s' type=%s",
640 YQT_OBJECT_NAME(obj),prop.c_str(),ns->c_str(),var.typeName());
641 }
642 else
643 Debug(ClientDriver::self(),DebugStub,
644 "Object '%s': dynamic property '%s' type '%s' is not supported",
645 YQT_OBJECT_NAME(obj),prop.c_str(),type.c_str());
646 }
647 }
648
649 // Find a QSystemTrayIcon child of an object
findSysTrayIcon(QObject * obj,const char * name)650 static inline QSystemTrayIcon* findSysTrayIcon(QObject* obj, const char* name)
651 {
652 return qFindChild<QSystemTrayIcon*>(obj,QtClient::setUtf8(name));
653 }
654
655 // Utility used to create an object's property if not found
656 // Add it to a list of strings
657 // Return true if the list changed
createProperty(QObject * obj,const char * name,QVariant::Type t,QtWindow * wnd,QStringList * list)658 static bool createProperty(QObject* obj, const char* name, QVariant::Type t,
659 QtWindow* wnd, QStringList* list)
660 {
661 if (!obj || TelEngine::null(name))
662 return false;
663 QVariant var = obj->property(name);
664 if (var.type() == QVariant::Invalid)
665 obj->setProperty(name,QVariant(t));
666 else if (var.type() != t) {
667 if (wnd)
668 Debug(QtDriver::self(),DebugNote,
669 "Window(%s) child '%s' already has a %s property '%s' [%p]",
670 wnd->toString().c_str(),YQT_OBJECT_NAME(obj),var.typeName(),name,wnd);
671 return false;
672 }
673 if (!list)
674 return false;
675 QString s = QtClient::setUtf8(name);
676 if (list->contains(s))
677 return false;
678 *list << s;
679 return true;
680 }
681
682 // Replace file path in URLs in a character array
addFilePathUrl(QByteArray & a,const String & file)683 static void addFilePathUrl(QByteArray& a, const String& file)
684 {
685 if (!file)
686 return;
687 QString path = QDir::fromNativeSeparators(QtClient::setUtf8(file));
688 // Truncate after last path separator (lastIndexOf() returns -1 if not found)
689 path.truncate(path.lastIndexOf(QString("/")) + 1);
690 if (!path.size())
691 return;
692 int start = 0;
693 int end = -1;
694 while ((start = a.indexOf("url(",end + 1)) > 0) {
695 start += 4;
696 end = a.indexOf(")",start);
697 if (end <= start)
698 break;
699 // Add
700 int len = end - start;
701 QByteArray tmp = a.mid(start,len);
702 if (tmp.indexOf('/') != -1)
703 continue;
704 tmp.insert(0,path);
705 a.replace(start,len,tmp);
706 }
707 }
708
709 // Read data from file and append it to a string buffer
710 // Optionally append suffix characters to file name
appendStyleSheet(QString & buf,const char * file,const char * suffix1=0,const char * suffix2=0)711 static bool appendStyleSheet(QString& buf, const char* file,
712 const char* suffix1 = 0, const char* suffix2 = 0)
713 {
714 if (TelEngine::null(file))
715 return false;
716 String shf = file;
717 const char* oper = 0;
718 int pos = shf.rfind('/');
719 if (pos < 0)
720 pos = shf.rfind('\\');
721 if (pos < 0)
722 shf = Client::s_skinPath + shf;
723 int level = DebugNote;
724 if (!(TelEngine::null(suffix1) && TelEngine::null(suffix2))) {
725 level = DebugAll;
726 int dotPos = shf.rfind('.');
727 if (dotPos > pos) {
728 String tmp = shf.substr(0,dotPos);
729 tmp.append(suffix1,"_");
730 tmp.append(suffix2,"_");
731 shf = tmp + shf.substr(dotPos);
732 }
733 }
734 DDebug(ClientDriver::self(),DebugAll,"Loading stylesheet file '%s'",shf.c_str());
735 QFile f(QtClient::setUtf8(shf));
736 if (f.open(QIODevice::ReadOnly)) {
737 QByteArray a = f.readAll();
738 if (a.size()) {
739 addFilePathUrl(a,shf);
740 buf += QString::fromUtf8(a.constData());
741 }
742 else if (f.error() != QFile::NoError)
743 oper = "read";
744 }
745 else
746 oper = "open";
747 if (!oper)
748 return true;
749 Debug(ClientDriver::self(),level,"Failed to %s stylesheet file '%s': %d '%s'",
750 oper,shf.c_str(),f.error(),f.errorString().toUtf8().constData());
751 return false;
752 }
753
754 // Split an integer string list
755 // Result list length can be set by indicating a length
buildIntList(const String & buf,int len=0)756 static QList<int> buildIntList(const String& buf, int len = 0)
757 {
758 QList<int> ret;
759 ObjList* list = buf.split(',');
760 int pos = 0;
761 ObjList* o = list;
762 while (o || pos < len) {
763 int val = 0;
764 if (o) {
765 if (o->get())
766 val = o->get()->toString().toInteger();
767 o = o->next();
768 }
769 ret.append(val);
770 pos++;
771 if (pos == len)
772 break;
773 }
774 TelEngine::destruct(list);
775 return ret;
776 }
777
778 // Retrieve an object's property
779 // Check platform dependent value
getPropPlatform(QObject * obj,const String & name,String & val)780 static inline bool getPropPlatform(QObject* obj, const String& name, String& val)
781 {
782 if (!(obj && name))
783 return false;
784 if (QtClient::getProperty(obj,name,val))
785 return true;
786 return QtClient::getProperty(obj,name + "_os" + PLATFORM_LOWERCASE_NAME,val);
787 }
788
789
790 /**
791 * Qt4ClientFactory
792 */
Qt4ClientFactory(const char * name)793 Qt4ClientFactory::Qt4ClientFactory(const char* name)
794 : UIFactory(name)
795 {
796 m_types.append(new String("QSound"));
797 }
798
799 // Build QSound
create(const String & type,const char * name,NamedList * params)800 void* Qt4ClientFactory::create(const String& type, const char* name, NamedList* params)
801 {
802 if (type == YSTRING("QSound"))
803 return new QSound(QtClient::setUtf8(name));
804 return 0;
805 }
806
807
808 /**
809 * TableWidget
810 */
TableWidget(QTableWidget * table,bool tmp)811 TableWidget::TableWidget(QTableWidget* table, bool tmp)
812 : m_table(table), m_sortControl(-1)
813 {
814 if (!m_table)
815 return;
816 init(tmp);
817 }
818
TableWidget(QWidget * wid,const String & name,bool tmp)819 TableWidget::TableWidget(QWidget* wid, const String& name, bool tmp)
820 : m_table(0), m_sortControl(-1)
821 {
822 if (wid)
823 m_table = qFindChild<QTableWidget*>(wid,QtClient::setUtf8(name));
824 if (!m_table)
825 return;
826 init(tmp);
827 }
828
TableWidget(QtWidget & table,bool tmp)829 TableWidget::TableWidget(QtWidget& table, bool tmp)
830 : m_table(static_cast<QTableWidget*>((QWidget*)table)), m_sortControl(-1)
831 {
832 if (m_table)
833 init(tmp);
834 }
835
~TableWidget()836 TableWidget::~TableWidget()
837 {
838 if (!m_table)
839 return;
840 if (m_sortControl >= 0)
841 m_table->setSortingEnabled((bool)m_sortControl);
842 m_table->repaint();
843 }
844
845 // Add or set a row
updateRow(const String & item,const NamedList * data,bool atStart)846 void TableWidget::updateRow(const String& item, const NamedList* data, bool atStart)
847 {
848 int row = getRow(item);
849 // Add a new one ?
850 if (row < 0) {
851 row = atStart ? 0 : rowCount();
852 addRow(row);
853 setID(row,item);
854 }
855 // Update
856 if (data)
857 updateRow(row,*data);
858 }
859
860 // Update a row from a list of parameters
updateRow(int row,const NamedList & data)861 void TableWidget::updateRow(int row, const NamedList& data)
862 {
863 int ncol = columnCount();
864 for (int i = 0; i < ncol; i++) {
865 String header;
866 if (!getHeaderText(i,header))
867 continue;
868 NamedString* tmp = data.getParam(header);
869 if (tmp)
870 setCell(row,i,*tmp);
871 // Set image
872 tmp = data.getParam(header + "_image");
873 if (tmp)
874 setImage(row,i,*tmp);
875 }
876 // Init vertical header
877 String* rowText = data.getParam(YSTRING("row_text"));
878 String* rowImg = data.getParam(YSTRING("row_image"));
879 if (rowText || rowImg) {
880 QTableWidgetItem* item = m_table->verticalHeaderItem(row);
881 if (!item) {
882 item = new QTableWidgetItem;
883 m_table->setVerticalHeaderItem(row,item);
884 }
885 if (rowText)
886 item->setText(QtClient::setUtf8(*rowText));
887 if (rowImg)
888 item->setIcon(QIcon(QtClient::setUtf8(*rowImg)));
889 }
890 }
891
892 // Find a row by the first's column value. Return -1 if not found
getRow(const String & item)893 int TableWidget::getRow(const String& item)
894 {
895 int n = rowCount();
896 for (int i = 0; i < n; i++) {
897 String val;
898 if (getCell(i,0,val) && item == val)
899 return i;
900 }
901 return -1;
902 }
903
904 // Find a column by its label. Return -1 if not found
getColumn(const String & name,bool caseInsensitive)905 int TableWidget::getColumn(const String& name, bool caseInsensitive)
906 {
907 int n = columnCount();
908 for (int i = 0; i < n; i++) {
909 String val;
910 if (!getHeaderText(i,val,false))
911 continue;
912 if ((caseInsensitive && (name &= val)) || (!caseInsensitive && name == val))
913 return i;
914 }
915 return -1;
916 }
917
init(bool tmp)918 void TableWidget::init(bool tmp)
919 {
920 QtClient::getUtf8(m_name,m_table->objectName());
921 if (tmp) {
922 m_sortControl = m_table->isSortingEnabled() ? 1 : 0;
923 if (m_sortControl)
924 m_table->setSortingEnabled(false);
925 }
926 }
927
928 /**
929 * UIBuffer
930 */
931 // Remove from list. Release memory
destruct()932 void UIBuffer::destruct()
933 {
934 s_uiCache.remove(this,false);
935 if (m_buffer) {
936 delete m_buffer;
937 m_buffer = 0;
938 }
939 String::destruct();
940 }
941
942 // Return an already loaded UI. Load from file if not found.
943 // Add URLs paths when missing
build(const String & name)944 UIBuffer* UIBuffer::build(const String& name)
945 {
946 // Check if already loaded from the same location
947 UIBuffer* buf = find(name);
948 if (buf)
949 return buf;
950
951 // Load
952 QFile file(QtClient::setUtf8(name));
953 file.open(QIODevice::ReadOnly);
954 QByteArray* qArray = new QByteArray;
955 *qArray = file.readAll();
956 file.close();
957 if (!qArray->size()) {
958 delete qArray;
959 return 0;
960 }
961 // Add URLs path when missing
962 addFilePathUrl(*qArray,name);
963 return new UIBuffer(name,qArray);
964 }
965
966 // Find a buffer
find(const String & name)967 UIBuffer* UIBuffer::find(const String& name)
968 {
969 ObjList* o = s_uiCache.find(name);
970 return o ? static_cast<UIBuffer*>(o->get()) : 0;
971 }
972
973
974 /**
975 * QtWindow
976 */
QtWindow()977 QtWindow::QtWindow()
978 : m_x(0), m_y(0), m_width(0), m_height(0),
979 m_maximized(false), m_mainWindow(false), m_moving(0)
980 {
981 }
982
QtWindow(const char * name,const char * description,const char * alias,QtWindow * parent)983 QtWindow::QtWindow(const char* name, const char* description, const char* alias, QtWindow* parent)
984 : QWidget(parent, Qt::Window),
985 Window(alias ? alias : name), m_description(description), m_oldId(name),
986 m_x(0), m_y(0), m_width(0), m_height(0),
987 m_maximized(false), m_mainWindow(false), m_moving(0)
988 {
989 setObjectName(QtClient::setUtf8(m_id));
990 }
991
~QtWindow()992 QtWindow::~QtWindow()
993 {
994 // Update all hidden counter for tray icons owned by this window
995 QList<QSystemTrayIcon*> trayIcons = qFindChildren<QSystemTrayIcon*>(this);
996 if (trayIcons.size() > 0) {
997 if (s_allHiddenQuit >= (unsigned int)trayIcons.size())
998 s_allHiddenQuit -= trayIcons.size();
999 else {
1000 Debug(QtDriver::self(),DebugFail,
1001 "QtWindow(%s) destroyed with all hidden counter %u greater then tray icons %d [%p]",
1002 m_id.c_str(),s_allHiddenQuit,trayIcons.size(),this);
1003 s_allHiddenQuit = 0;
1004 }
1005 }
1006
1007 // Save settings
1008 if (m_saveOnClose) {
1009 m_maximized = isMaximized();
1010 s_save.setValue(m_id,"maximized",String::boolText(m_maximized));
1011 // Don't save position if maximized: keep the old one
1012 if (!m_maximized) {
1013 s_save.setValue(m_id,"x",m_x);
1014 s_save.setValue(m_id,"y",m_y);
1015 s_save.setValue(m_id,"width",m_width);
1016 s_save.setValue(m_id,"height",m_height);
1017 }
1018 s_save.setValue(m_id,"visible",m_visible);
1019 // Set dynamic properties to be saved for native QT objects
1020 QList<QTableWidget*> tables = qFindChildren<QTableWidget*>(this);
1021 for (int i = 0; i < tables.size(); i++) {
1022 if (qobject_cast<QtTable*>(tables[i]))
1023 continue;
1024 // Column widths
1025 unsigned int n = tables[i]->columnCount();
1026 String widths;
1027 for (unsigned int j = 0; j < n; j++)
1028 widths.append(String(tables[i]->columnWidth(j)),",",true);
1029 tables[i]->setProperty(s_propColWidths,QVariant(QtClient::setUtf8(widths)));
1030 // Sorting
1031 String sorting;
1032 if (tables[i]->isSortingEnabled()) {
1033 QHeaderView* h = tables[i]->horizontalHeader();
1034 int col = h ? h->sortIndicatorSection() : -1;
1035 if (col >= 0)
1036 sorting << col << "," <<
1037 String::boolText(Qt::AscendingOrder == h->sortIndicatorOrder());
1038 }
1039 tables[i]->setProperty(s_propSorting,QVariant(QtClient::setUtf8(sorting)));
1040 }
1041 QList<QSplitter*> spl = qFindChildren<QSplitter*>(this);
1042 for (int i = 0; i < spl.size(); i++) {
1043 String sizes;
1044 QtClient::intList2str(sizes,spl[i]->sizes());
1045 QtClient::setProperty(spl[i],s_propSizes,sizes);
1046 }
1047 // Save child objects properties
1048 QList<QObject*> child = qFindChildren<QObject*>(this);
1049 for (int i = 0; i < child.size(); i++) {
1050 NamedList props("");
1051 if (!QtClient::getProperty(child[i],s_propsSave,props))
1052 continue;
1053 unsigned int n = props.length();
1054 for (unsigned int j = 0; j < n; j++) {
1055 NamedString* ns = props.getParam(j);
1056 if (ns && ns->name())
1057 QtClient::saveProperty(child[i],ns->name(),this);
1058 }
1059 }
1060 }
1061 }
1062
1063 // Set windows title
title(const String & text)1064 void QtWindow::title(const String& text)
1065 {
1066 XDebug(QtDriver::self(),DebugAll,"QtWindow::title(%s) [%p]",text.c_str(),this);
1067 Window::title(text);
1068 QWidget::setWindowTitle(QtClient::setUtf8(text));
1069 }
1070
context(const String & text)1071 void QtWindow::context(const String& text)
1072 {
1073 XDebug(QtDriver::self(),DebugAll,"QtWindow::context(%s) [%p]",text.c_str(),this);
1074 m_context = text;
1075 }
1076
setParams(const NamedList & params)1077 bool QtWindow::setParams(const NamedList& params)
1078 {
1079 XDebug(QtDriver::self(),DebugAll,"QtWindow::setParams() [%p]",this);
1080
1081 setUpdatesEnabled(false);
1082 // Check for custom widget params
1083 if (params == YSTRING("customwidget")) {
1084 // Each parameter is a list of parameters for a custom widget
1085 // Parameter name is the widget's name
1086 unsigned int n = params.length();
1087 bool ok = true;
1088 for (unsigned int i = 0; i < n; i++) {
1089 NamedString* ns = params.getParam(i);
1090 NamedList* nl = static_cast<NamedList*>(ns ? ns->getObject(YATOM("NamedList")) : 0);
1091 if (!(nl && ns->name()))
1092 continue;
1093 // Find the widget and set its params
1094 QtWidget w(this,ns->name());
1095 if (w.type() == QtWidget::CustomTable)
1096 ok = w.customTable()->setParams(*nl) && ok;
1097 else if (w.type() == QtWidget::CustomWidget)
1098 ok = w.customWidget()->setParams(*nl) && ok;
1099 else if (w.type() == QtWidget::CustomObject)
1100 ok = w.customObject()->setParams(*nl) && ok;
1101 else
1102 ok = false;
1103 }
1104 setUpdatesEnabled(true);
1105 return ok;
1106 }
1107 // Check for system tray icon params
1108 if (params == YSTRING("systemtrayicon")) {
1109 // Each parameter is a list of parameters for a system tray icon
1110 // Parameter name is the widget's name
1111 // Parameter value indicates delete/create/set an existing one
1112 unsigned int n = params.length();
1113 bool ok = false;
1114 for (unsigned int i = 0; i < n; i++) {
1115 NamedString* ns = params.getParam(i);
1116 if (!(ns && ns->name()))
1117 continue;
1118 QSystemTrayIcon* trayIcon = findSysTrayIcon(this,ns->name());
1119 // Delete
1120 if (ns->null()) {
1121 if (trayIcon) {
1122 // Reactivate program termination when the last window was hidden
1123 if (s_allHiddenQuit)
1124 s_allHiddenQuit--;
1125 else
1126 Debug(QtDriver::self(),DebugFail,
1127 "QtWindow(%s) all hidden counter is 0 while deleting '%s' tray icon [%p]",
1128 m_id.c_str(),YQT_OBJECT_NAME(trayIcon),this);
1129 QtClient::deleteLater(trayIcon);
1130 }
1131 continue;
1132 }
1133 NamedList* nl = YOBJECT(NamedList,ns);
1134 if (!nl)
1135 continue;
1136 // Create a new one if needed
1137 if (!trayIcon) {
1138 if (!ns->toBoolean())
1139 continue;
1140 trayIcon = new QSystemTrayIcon(this);
1141 trayIcon->setObjectName(QtClient::setUtf8(ns->name()));
1142 QtClient::connectObjects(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
1143 this,SLOT(sysTrayIconAction(QSystemTrayIcon::ActivationReason)));
1144 // Deactivate program termination when the last window was hidden
1145 s_allHiddenQuit++;
1146 }
1147 ok = true;
1148 // Add dynamic properties
1149 // TODO: track the properties, clear the old ones if needed
1150 addDynamicProps(trayIcon,*nl);
1151 // Set icon and tooltip
1152 NamedString* tmp = nl->getParam(YSTRING("icon"));
1153 if (tmp && *tmp)
1154 trayIcon->setIcon(QIcon(QtClient::setUtf8(*tmp)));
1155 tmp = nl->getParam(YSTRING("tooltip"));
1156 if (tmp && *tmp)
1157 trayIcon->setToolTip(QtClient::setUtf8(*tmp));
1158 // Check context menu
1159 NamedString* menu = nl->getParam(YSTRING("menu"));
1160 if (menu) {
1161 QMenu* oldMenu = trayIcon->contextMenu();
1162 if (oldMenu)
1163 delete oldMenu;
1164 NamedList* nlMenu = YOBJECT(NamedList,menu);
1165 trayIcon->setContextMenu(nlMenu ? QtClient::buildMenu(*nlMenu,*menu,this,
1166 SLOT(action()),SLOT(toggled(bool)),this) : 0);
1167 }
1168 if (nl->getBoolValue(YSTRING("show"),true))
1169 trayIcon->setVisible(true);
1170 }
1171 setUpdatesEnabled(true);
1172 return ok;
1173 }
1174 // Parameters for the widget whose name is the list name
1175 if(params) {
1176 QtWidget w(this, params);
1177 // Handle UIWidget descendants
1178 UIWidget* uiw = w.uiWidget();
1179 if (uiw) {
1180 bool ok = uiw->setParams(params);
1181 setUpdatesEnabled(true);
1182 return ok;
1183 }
1184 if (w.type() == QtWidget::Calendar) {
1185 int year = params.getIntValue(YSTRING("year"));
1186 int month = params.getIntValue(YSTRING("month"));
1187 int day = params.getIntValue(YSTRING("day"));
1188 w.calendar()->setCurrentPage(year, month);
1189 w.calendar()->setSelectedDate(QDate(year, month, day));
1190 setUpdatesEnabled(true);
1191 return true;
1192 }
1193 }
1194
1195 // Window or other parameters
1196 if (params.getBoolValue(YSTRING("modal"))) {
1197 if (parentWindow())
1198 setWindowModality(Qt::WindowModal);
1199 else
1200 setWindowModality(Qt::ApplicationModal);
1201 }
1202 if (params.getBoolValue(YSTRING("minimized")))
1203 QWidget::setWindowState(Qt::WindowMinimized);
1204 bool ok = Window::setParams(params);
1205 setUpdatesEnabled(true);
1206 return ok;
1207 }
1208
setOver(const Window * parent)1209 void QtWindow::setOver(const Window* parent)
1210 {
1211 XDebug(QtDriver::self(),DebugAll,"QtWindow::setOver(%p) [%p]",parent,this);
1212 QWidget::raise();
1213 }
1214
hasElement(const String & name)1215 bool QtWindow::hasElement(const String& name)
1216 {
1217 XDebug(QtDriver::self(),DebugAll,"QtWindow::hasElement(%s) [%p]",name.c_str(),this);
1218 QtWidget w(this,name);
1219 return w.valid();
1220 }
1221
setActive(const String & name,bool active)1222 bool QtWindow::setActive(const String& name, bool active)
1223 {
1224 XDebug(QtDriver::self(),DebugAll,"QtWindow::setActive(%s,%s) [%p]",
1225 name.c_str(),String::boolText(active),this);
1226 bool ok = (name == m_id);
1227 if (ok) {
1228 if (QWidget::isMinimized())
1229 QWidget::showNormal();
1230 QWidget::activateWindow();
1231 QWidget::raise();
1232 }
1233 QtWidget w(this,name);
1234 if (w.invalid())
1235 return ok;
1236 if (w.type() != QtWidget::Action)
1237 w->setEnabled(active);
1238 else
1239 w.action()->setEnabled(active);
1240 return true;
1241 }
1242
setFocus(const String & name,bool select)1243 bool QtWindow::setFocus(const String& name, bool select)
1244 {
1245 XDebug(QtDriver::self(),DebugAll,"QtWindow::setFocus(%s,%s) [%p]",
1246 name.c_str(),String::boolText(select),this);
1247 QtWidget w(this,name);
1248 if (w.invalid())
1249 return false;
1250 w->setFocus();
1251 switch (w.type()) {
1252 case QtWidget::ComboBox:
1253 if (w.combo()->isEditable() && select)
1254 w.combo()->lineEdit()->selectAll();
1255 break;
1256 }
1257 return true;
1258 }
1259
setShow(const String & name,bool visible)1260 bool QtWindow::setShow(const String& name, bool visible)
1261 {
1262 XDebug(QtDriver::self(),DebugAll,"QtWindow::setShow(%s,%s) [%p]",
1263 name.c_str(),String::boolText(visible),this);
1264 // Check system tray icons
1265 QSystemTrayIcon* trayIcon = findSysTrayIcon(this,name);
1266 if (trayIcon) {
1267 trayIcon->setVisible(visible);
1268 return true;
1269 }
1270 // Widgets
1271 QtWidget w(this,name);
1272 if (w.invalid())
1273 return false;
1274 setUpdatesEnabled(false);
1275 if (w.type() != QtWidget::Action)
1276 w->setVisible(visible);
1277 else
1278 w.action()->setVisible(visible);
1279 setUpdatesEnabled(true);
1280 return true;
1281 }
1282
setText(const String & name,const String & text,bool richText)1283 bool QtWindow::setText(const String& name, const String& text,
1284 bool richText)
1285 {
1286 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setText(%s,%s) [%p]",
1287 m_id.c_str(),name.c_str(),text.c_str(),this);
1288
1289 QtWidget w(this,name);
1290 if (w.invalid())
1291 return false;
1292 UIWidget* uiw = w.uiWidget();
1293 if (uiw)
1294 return uiw->setText(text,richText);
1295 switch (w.type()) {
1296 case QtWidget::CheckBox:
1297 w.check()->setText(QtClient::setUtf8(text));
1298 return true;
1299 case QtWidget::LineEdit:
1300 w.lineEdit()->setText(QtClient::setUtf8(text));
1301 return true;
1302 case QtWidget::TextBrowser:
1303 case QtWidget::TextEdit:
1304 if (richText) {
1305 w.textEdit()->clear();
1306 w.textEdit()->insertHtml(QtClient::setUtf8(text));
1307 }
1308 else
1309 w.textEdit()->setText(QtClient::setUtf8(text));
1310 {
1311 QScrollBar* bar = w.textEdit()->verticalScrollBar();
1312 if (bar)
1313 bar->setSliderPosition(bar->maximum());
1314 }
1315 return true;
1316 case QtWidget::Label:
1317 w.label()->setText(QtClient::setUtf8(text));
1318 return true;
1319 case QtWidget::ComboBox:
1320 if (w.combo()->lineEdit())
1321 w.combo()->lineEdit()->setText(QtClient::setUtf8(text));
1322 else
1323 setSelect(name,text);
1324 return true;
1325 case QtWidget::Action:
1326 w.action()->setText(QtClient::setUtf8(text));
1327 return true;
1328 case QtWidget::SpinBox:
1329 w.spinBox()->setValue(text.toInteger());
1330 return true;
1331 }
1332
1333 // Handle some known base classes having a setText() method
1334 if (w.inherits(QtWidget::AbstractButton))
1335 w.abstractButton()->setText(QtClient::setUtf8(text));
1336 else
1337 return false;
1338 return true;
1339 }
1340
setCheck(const String & name,bool checked)1341 bool QtWindow::setCheck(const String& name, bool checked)
1342 {
1343 XDebug(QtDriver::self(),DebugAll,"QtWindow::setCheck(%s,%s) [%p]",
1344 name.c_str(),String::boolText(checked),this);
1345 QtWidget w(this,name);
1346 if (w.invalid())
1347 return false;
1348 if (w.inherits(QtWidget::AbstractButton))
1349 w.abstractButton()->setChecked(checked);
1350 else if (w.type() == QtWidget::Action)
1351 w.action()->setChecked(checked);
1352 else
1353 return false;
1354 return true;
1355 }
1356
setSelect(const String & name,const String & item)1357 bool QtWindow::setSelect(const String& name, const String& item)
1358 {
1359 XDebug(QtDriver::self(),DebugAll,"QtWindow::setSelect(%s,%s) [%p]",
1360 name.c_str(),item.c_str(),this);
1361
1362 QtWidget w(this,name);
1363 if (w.invalid())
1364 return false;
1365 UIWidget* uiw = w.uiWidget();
1366 if (uiw)
1367 return uiw->setSelect(item);
1368
1369 int d = 0;
1370 switch (w.type()) {
1371 case QtWidget::Table:
1372 {
1373 TableWidget t(w);
1374 int row = t.getRow(item);
1375 if (row < 0)
1376 return false;
1377 t.table()->setCurrentCell(row,0);
1378 return true;
1379 }
1380 case QtWidget::ComboBox:
1381 if (item) {
1382 d = w.findComboItem(item);
1383 if (d < 0)
1384 return false;
1385 w.combo()->setCurrentIndex(d);
1386 }
1387 else if (w.combo()->lineEdit())
1388 w.combo()->lineEdit()->setText("");
1389 else
1390 return false;
1391 return true;
1392 case QtWidget::ListBox:
1393 d = w.findListItem(item);
1394 if (d >= 0)
1395 w.list()->setCurrentRow(d);
1396 return d >= 0;
1397 case QtWidget::Slider:
1398 w.slider()->setValue(item.toInteger());
1399 return true;
1400 case QtWidget::StackWidget:
1401 d = item.toInteger(-1);
1402 while (d < 0) {
1403 d = findStackedWidget(*(w.stackWidget()),item);
1404 if (d >= 0)
1405 break;
1406 // Check for a default widget
1407 String def = YQT_OBJECT_NAME(w.stackWidget());
1408 def << "_default";
1409 d = findStackedWidget(*(w.stackWidget()),def);
1410 break;
1411 }
1412 if (d >= 0 && d < w.stackWidget()->count()) {
1413 w.stackWidget()->setCurrentIndex(d);
1414 return true;
1415 }
1416 return false;
1417 case QtWidget::ProgressBar:
1418 d = item.toInteger();
1419 if (d >= w.progressBar()->minimum() && d <= w.progressBar()->maximum())
1420 w.progressBar()->setValue(d);
1421 else if (d < w.progressBar()->minimum())
1422 w.progressBar()->setValue(w.progressBar()->minimum());
1423 else
1424 w.progressBar()->setValue(w.progressBar()->maximum());
1425 return true;
1426 case QtWidget::Tab:
1427 d = w.tab()->count() - 1;
1428 for (QString tmp = QtClient::setUtf8(item); d >= 0; d--) {
1429 QWidget* wid = w.tab()->widget(d);
1430 if (wid && wid->objectName() == tmp)
1431 break;
1432 }
1433 if (d >= 0 && d < w.tab()->count()) {
1434 w.tab()->setCurrentIndex(d);
1435 return true;
1436 }
1437 return false;
1438
1439 }
1440 return false;
1441 }
1442
setUrgent(const String & name,bool urgent)1443 bool QtWindow::setUrgent(const String& name, bool urgent)
1444 {
1445 XDebug(QtDriver::self(),DebugAll,"QtWindow::setUrgent(%s,%s) [%p]",
1446 name.c_str(),String::boolText(urgent),this);
1447
1448 if (name == m_id) {
1449 QApplication::alert(this,0);
1450 return true;
1451 }
1452
1453 QtWidget w(this,name);
1454 if (w.invalid())
1455 return false;
1456 w->raise();
1457 return true;
1458 }
1459
hasOption(const String & name,const String & item)1460 bool QtWindow::hasOption(const String& name, const String& item)
1461 {
1462 XDebug(QtDriver::self(),DebugAll,"QtWindow::hasOption(%s,%s) [%p]",
1463 name.c_str(),item.c_str(),this);
1464 QtWidget w(this,name);
1465 if (w.invalid())
1466 return false;
1467 switch (w.type()) {
1468 case QtWidget::ComboBox:
1469 return -1 != w.findComboItem(item);
1470 case QtWidget::Table:
1471 return getTableRow(name,item);
1472 case QtWidget::ListBox:
1473 return -1 != w.findListItem(item);
1474 }
1475 return false;
1476 }
1477
addOption(const String & name,const String & item,bool atStart,const String & text)1478 bool QtWindow::addOption(const String& name, const String& item, bool atStart,
1479 const String& text)
1480 {
1481 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) addOption(%s,%s,%s,%s) [%p]",
1482 m_id.c_str(),name.c_str(),item.c_str(),
1483 String::boolText(atStart),text.c_str(),this);
1484
1485 QtWidget w(this,name);
1486 switch (w.type()) {
1487 case QtWidget::ComboBox:
1488 w.addComboItem(item,atStart);
1489 if (atStart && w.combo()->lineEdit())
1490 w.combo()->lineEdit()->setText(w.combo()->itemText(0));
1491 return true;
1492 case QtWidget::Table:
1493 return addTableRow(name,item,0,atStart);
1494 case QtWidget::ListBox:
1495 return w.addListItem(item,atStart);
1496 }
1497 return false;
1498 }
1499
delOption(const String & name,const String & item)1500 bool QtWindow::delOption(const String& name, const String& item)
1501 {
1502 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) delOption(%s,%s) [%p]",
1503 m_id.c_str(),name.c_str(),item.c_str(),this);
1504 return delTableRow(name,item);
1505 }
1506
getOptions(const String & name,NamedList * items)1507 bool QtWindow::getOptions(const String& name, NamedList* items)
1508 {
1509 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) getOptions(%s,%p) [%p]",
1510 m_id.c_str(),name.c_str(),items,this);
1511
1512 QtWidget w(this,name);
1513 if (w.invalid())
1514 return false;
1515 if (!items)
1516 return true;
1517 UIWidget* uiw = w.uiWidget();
1518 if (uiw)
1519 return uiw->getOptions(*items);
1520
1521 switch (w.type()) {
1522 case QtWidget::ComboBox:
1523 for (int i = 0; i < w.combo()->count(); i++)
1524 QtClient::getUtf8(*items,"",w.combo()->itemText(i),false);
1525 break;
1526 case QtWidget::Table:
1527 {
1528 TableWidget t(w.table(),false);
1529 for (int i = 0; i < t.rowCount(); i++) {
1530 String item;
1531 if (t.getCell(i,0,item) && item)
1532 items->addParam(item,"");
1533 }
1534 }
1535 break;
1536 case QtWidget::ListBox:
1537 for (int i = 0; i < w.list()->count(); i++) {
1538 QListWidgetItem* tmp = w.list()->item(i);
1539 if (tmp)
1540 QtClient::getUtf8(*items,"",tmp->text(),false);
1541 }
1542 break;
1543 }
1544 return true;
1545 }
1546
1547 // Append or insert text lines to a widget
addLines(const String & name,const NamedList * lines,unsigned int max,bool atStart)1548 bool QtWindow::addLines(const String& name, const NamedList* lines, unsigned int max,
1549 bool atStart)
1550 {
1551 DDebug(ClientDriver::self(),DebugAll,"QtWindow(%s) addLines('%s',%p,%u,%s) [%p]",
1552 m_id.c_str(),name.c_str(),lines,max,String::boolText(atStart),this);
1553
1554 QtWidget w(this,name);
1555 if (w.invalid())
1556 return false;
1557 if (!lines)
1558 return true;
1559 UIWidget* uiw = w.uiWidget();
1560 if (uiw)
1561 return uiw->addLines(*lines,max,atStart);
1562 unsigned int count = lines->length();
1563 if (!count)
1564 return true;
1565
1566 switch (w.type()) {
1567 case QtWidget::TextBrowser:
1568 case QtWidget::TextEdit:
1569 // Limit the maximum number of paragraphs
1570 if (max) {
1571 QTextDocument* doc = w.textEdit()->document();
1572 if (!doc)
1573 return false;
1574 doc->setMaximumBlockCount((int)max);
1575 }
1576 {
1577 // FIXME: delete lines from begining if appending and the number
1578 // of lines exceeds the maximum allowed
1579 QString s = w.textEdit()->toPlainText();
1580 int pos = atStart ? 0 : s.length();
1581 for (unsigned int i = 0; i < count; i++) {
1582 NamedString* ns = lines->getParam(i);
1583 if (!ns)
1584 continue;
1585 if (ns->name().endsWith("\n"))
1586 s.insert(pos,QtClient::setUtf8(ns->name()));
1587 else {
1588 String tmp = ns->name() + "\n";
1589 s.insert(pos,QtClient::setUtf8(tmp));
1590 pos++;
1591 }
1592 pos += (int)ns->name().length();
1593 }
1594 w.textEdit()->setText(s);
1595 // Scroll down if added at end
1596 if (!atStart) {
1597 QScrollBar* bar = w.textEdit()->verticalScrollBar();
1598 if (bar)
1599 bar->setSliderPosition(bar->maximum());
1600 }
1601 }
1602 return true;
1603 case QtWidget::Table:
1604 // TODO: implement
1605 break;
1606 case QtWidget::ComboBox:
1607 if (atStart) {
1608 for (; count; count--) {
1609 NamedString* ns = lines->getParam(count - 1);
1610 if (ns)
1611 w.combo()->insertItem(0,QtClient::setUtf8(ns->name()));
1612 }
1613 if (w.combo()->lineEdit())
1614 w.combo()->lineEdit()->setText(w.combo()->itemText(0));
1615 }
1616 else {
1617 for (unsigned int i = 0; i < count; i++) {
1618 NamedString* ns = lines->getParam(i);
1619 if (ns)
1620 w.combo()->addItem(QtClient::setUtf8(ns->name()));
1621 }
1622 }
1623 return true;
1624 case QtWidget::ListBox:
1625 // TODO: implement
1626 break;
1627 }
1628 return false;
1629 }
1630
addTableRow(const String & name,const String & item,const NamedList * data,bool atStart)1631 bool QtWindow::addTableRow(const String& name, const String& item,
1632 const NamedList* data, bool atStart)
1633 {
1634 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) addTableRow(%s,%s,%p,%s) [%p]",
1635 m_id.c_str(),name.c_str(),item.c_str(),data,String::boolText(atStart),this);
1636
1637 QtWidget w(this,name);
1638 if (w.invalid())
1639 return false;
1640 UIWidget* uiw = w.uiWidget();
1641 if (uiw)
1642 return uiw->addTableRow(item,data,atStart);
1643 // Handle basic QTableWidget
1644 if (w.type() != QtWidget::Table)
1645 return false;
1646 TableWidget tbl(w.table());
1647 int row = atStart ? 0 : tbl.rowCount();
1648 tbl.addRow(row);
1649 // Set item (the first column) and the rest of data
1650 tbl.setID(row,item);
1651 if (data)
1652 tbl.updateRow(row,*data);
1653 return true;
1654 }
1655
1656 // Insert or update multiple rows in a single operation
setMultipleRows(const String & name,const NamedList & data,const String & prefix)1657 bool QtWindow::setMultipleRows(const String& name, const NamedList& data, const String& prefix)
1658 {
1659 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setMultipleRows('%s',%p,'%s') [%p]",
1660 m_id.c_str(),name.c_str(),&data,prefix.c_str(),this);
1661
1662 QtWidget w(this,name);
1663 if (w.invalid())
1664 return false;
1665 UIWidget* uiw = w.uiWidget();
1666 return uiw && uiw->setMultipleRows(data,prefix);
1667 }
1668
1669 // Insert a row into a table owned by this window
insertTableRow(const String & name,const String & item,const String & before,const NamedList * data)1670 bool QtWindow::insertTableRow(const String& name, const String& item,
1671 const String& before, const NamedList* data)
1672 {
1673 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) insertTableRow(%s,%s,%s,%p) [%p]",
1674 m_id.c_str(),name.c_str(),item.c_str(),before.c_str(),data,this);
1675
1676 QtWidget w(this,name);
1677 if (w.invalid())
1678 return false;
1679 UIWidget* uiw = w.uiWidget();
1680 if (uiw)
1681 return uiw->insertTableRow(item,before,data);
1682 if (w.type() != QtWidget::Table)
1683 return false;
1684 TableWidget tbl(w.table());
1685 int row = tbl.getRow(before);
1686 if (row == -1)
1687 row = tbl.rowCount();
1688 tbl.addRow(row);
1689 // Set item (the first column) and the rest of data
1690 tbl.setID(row,item);
1691 if (data)
1692 tbl.updateRow(row,*data);
1693 return true;
1694 }
1695
delTableRow(const String & name,const String & item)1696 bool QtWindow::delTableRow(const String& name, const String& item)
1697 {
1698 XDebug(QtDriver::self(),DebugAll,"QtWindow::delTableRow(%s,%s) [%p]",
1699 name.c_str(),item.c_str(),this);
1700 QtWidget w(this,name);
1701 if (w.invalid())
1702 return false;
1703 int row = -1;
1704 int n = 0;
1705 switch (w.type()) {
1706 case QtWidget::Table:
1707 case QtWidget::CustomTable:
1708 {
1709 TableWidget tbl(w.table());
1710 QtTable* custom = tbl.customTable();
1711 if (custom) {
1712 if (custom->delTableRow(item))
1713 row = 0;
1714 }
1715 else {
1716 row = tbl.getRow(item);
1717 if (row >= 0)
1718 tbl.delRow(row);
1719 }
1720 n = tbl.rowCount();
1721 }
1722 break;
1723 case QtWidget::ComboBox:
1724 row = w.findComboItem(item);
1725 if (row >= 0) {
1726 w.combo()->removeItem(row);
1727 n = w.combo()->count();
1728 }
1729 break;
1730 case QtWidget::ListBox:
1731 row = w.findListItem(item);
1732 if (row >= 0) {
1733 QStringListModel* model = (QStringListModel*)w.list()->model();
1734 if (!(model && model->removeRow(row)))
1735 row = -1;
1736 n = w.list()->count();
1737 }
1738 break;
1739 default:
1740 UIWidget* uiw = w.uiWidget();
1741 if (uiw && uiw->delTableRow(item)) {
1742 row = 0;
1743 // Don't notify empty: we don't know it
1744 n = 1;
1745 }
1746 }
1747 if (row < 0)
1748 return false;
1749 if (!n)
1750 raiseSelectIfEmpty(0,this,name);
1751 return true;
1752 }
1753
setTableRow(const String & name,const String & item,const NamedList * data)1754 bool QtWindow::setTableRow(const String& name, const String& item, const NamedList* data)
1755 {
1756 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setTableRow(%s,%s,%p) [%p]",
1757 m_id.c_str(),name.c_str(),item.c_str(),data,this);
1758
1759 QtWidget w(this,name);
1760 if (w.invalid())
1761 return false;
1762 UIWidget* uiw = w.uiWidget();
1763 if (uiw)
1764 return uiw->setTableRow(item,data);
1765 if (w.type() != QtWidget::Table)
1766 return false;
1767 TableWidget tbl(w.table());
1768 int row = tbl.getRow(item);
1769 if (row < 0)
1770 return false;
1771 if (data)
1772 tbl.updateRow(row,*data);
1773 return true;
1774 }
1775
getTableRow(const String & name,const String & item,NamedList * data)1776 bool QtWindow::getTableRow(const String& name, const String& item, NamedList* data)
1777 {
1778 XDebug(QtDriver::self(),DebugAll,"QtWindow::getTableRow(%s,%s,%p) [%p]",
1779 name.c_str(),item.c_str(),data,this);
1780
1781 QtWidget w(this,name);
1782 if (w.invalid())
1783 return false;
1784 UIWidget* uiw = w.uiWidget();
1785 if (uiw)
1786 return uiw->getTableRow(item,data);
1787 if (w.type() != QtWidget::Table)
1788 return false;
1789 TableWidget tbl(w.table(),false);
1790 int row = tbl.getRow(item);
1791 if (row < 0)
1792 return false;
1793 if (!data)
1794 return true;
1795 int n = tbl.columnCount();
1796 for (int i = 0; i < n; i++) {
1797 String name;
1798 if (!tbl.getHeaderText(i,name))
1799 continue;
1800 String value;
1801 if (tbl.getCell(row,i,value))
1802 data->setParam(name,value);
1803 }
1804 return true;
1805 }
1806
1807 // Set a table row or add a new one if not found
updateTableRow(const String & name,const String & item,const NamedList * data,bool atStart)1808 bool QtWindow::updateTableRow(const String& name, const String& item,
1809 const NamedList* data, bool atStart)
1810 {
1811 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) updateTableRow('%s','%s',%p,%s) [%p]",
1812 m_id.c_str(),name.c_str(),item.c_str(),data,String::boolText(atStart),this);
1813 QtWidget w(this,name);
1814 if (w.invalid())
1815 return false;
1816 switch (w.type()) {
1817 case QtWidget::Table:
1818 case QtWidget::CustomTable:
1819 {
1820 TableWidget tbl(w.table());
1821 QtTable* custom = tbl.customTable();
1822 if (custom) {
1823 if (custom->getTableRow(item))
1824 return custom->setTableRow(item,data);
1825 return custom->addTableRow(item,data,atStart);
1826 }
1827 tbl.updateRow(item,data,atStart);
1828 return true;
1829 }
1830 case QtWidget::CustomTree:
1831 {
1832 QtTree* custom = w.customTree();
1833 if (custom) {
1834 if (custom->getTableRow(item))
1835 return custom->setTableRow(item,data);
1836 return custom->addTableRow(item,data,atStart);
1837 }
1838 return false;
1839 }
1840 case QtWidget::ComboBox:
1841 return w.findComboItem(item) >= 0 || w.addComboItem(item,atStart);
1842 case QtWidget::ListBox:
1843 return w.findListItem(item) >= 0 || w.addListItem(item,atStart);
1844 }
1845 return false;
1846 }
1847
1848 // Add or set one or more table row(s). Screen update is locked while changing the table.
1849 // Each data list element is a NamedPointer carrying a NamedList with item parameters.
1850 // The name of an element is the item to update.
1851 // Element's value not empty: update the item
1852 // Else: delete it
updateTableRows(const String & name,const NamedList * data,bool atStart)1853 bool QtWindow::updateTableRows(const String& name, const NamedList* data, bool atStart)
1854 {
1855 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) updateTableRows('%s',%p,%s) [%p]",
1856 m_id.c_str(),name.c_str(),data,String::boolText(atStart),this);
1857
1858 QtWidget w(this,name);
1859 if (w.invalid())
1860 return false;
1861 UIWidget* uiw = w.uiWidget();
1862 if (uiw) {
1863 bool ok = uiw->updateTableRows(data,atStart);
1864 QtTable* ct = w.customTable();
1865 if (ct)
1866 raiseSelectIfEmpty(ct->rowCount(),this,name);
1867 return ok;
1868 }
1869 if (w.type() != QtWidget::Table)
1870 return false;
1871 if (!data)
1872 return true;
1873 TableWidget tbl(w.table());
1874 bool ok = true;
1875 tbl.table()->setUpdatesEnabled(false);
1876 ObjList add;
1877 unsigned int n = data->length();
1878 for (unsigned int i = 0; i < n; i++) {
1879 if (Client::exiting())
1880 break;
1881 // Get item and the list of parameters
1882 NamedString* ns = data->getParam(i);
1883 if (!ns)
1884 continue;
1885 // Delete ?
1886 if (ns->null()) {
1887 int row = tbl.getRow(ns->name());
1888 if (row >= 0)
1889 tbl.delRow(row);
1890 else
1891 ok = false;
1892 continue;
1893 }
1894 // Set existing row or postpone add
1895 int row = tbl.getRow(ns->name());
1896 if (row >= 0) {
1897 const NamedList* params = YOBJECT(NamedList,ns);
1898 if (params)
1899 tbl.updateRow(row,*params);
1900 }
1901 else if (ns->toBoolean())
1902 add.append(ns)->setDelete(false);
1903 else
1904 ok = false;
1905 }
1906 n = add.count();
1907 if (n) {
1908 int row = tbl.rowCount();
1909 if (row < 0)
1910 row = 0;
1911 // Append if not requested to insert at start or table is empty
1912 if (!(atStart && row))
1913 tbl.table()->setRowCount(row + n);
1914 else {
1915 for (unsigned int i = 0; i < n; i++)
1916 tbl.table()->insertRow(0);
1917 }
1918 for (ObjList* o = add.skipNull(); o; row++, o = o->skipNext()) {
1919 NamedString* ns = static_cast<NamedString*>(o->get());
1920 tbl.setID(row,ns->name());
1921 const NamedList* params = YOBJECT(NamedList,ns);
1922 if (params)
1923 tbl.updateRow(row,*params);
1924 }
1925 }
1926 tbl.table()->setUpdatesEnabled(true);
1927 raiseSelectIfEmpty(tbl.rowCount(),this,name);
1928 return ok;
1929 }
1930
clearTable(const String & name)1931 bool QtWindow::clearTable(const String& name)
1932 {
1933 DDebug(QtDriver::self(),DebugAll,"QtWindow::clearTable(%s) [%p]",name.c_str(),this);
1934 QtWidget w(this,name);
1935 if (w.invalid())
1936 return false;
1937 UIWidget* uiw = w.uiWidget();
1938 if (uiw)
1939 return uiw->clearTable();
1940 bool ok = true;
1941 if (w.widget())
1942 w->setUpdatesEnabled(false);
1943 switch (w.type()) {
1944 case QtWidget::Table:
1945 w.table()->setRowCount(0);
1946 break;
1947 case QtWidget::TextBrowser:
1948 case QtWidget::TextEdit:
1949 w.textEdit()->clear();
1950 break;
1951 case QtWidget::ListBox:
1952 w.list()->clear();
1953 break;
1954 case QtWidget::ComboBox:
1955 w.combo()->clear();
1956 break;
1957 default:
1958 ok = false;
1959 }
1960 if (w.widget())
1961 w->setUpdatesEnabled(true);
1962 return ok;
1963 }
1964
1965 // Show or hide control busy state
setBusy(const String & name,bool on)1966 bool QtWindow::setBusy(const String& name, bool on)
1967 {
1968 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setBusy(%s,%u) [%p]",
1969 m_id.c_str(),name.c_str(),on,this);
1970 if (name == m_id)
1971 return QtBusyWidget::showBusyChild(this,on);
1972 QtWidget w(this,name);
1973 if (w.invalid())
1974 return false;
1975 UIWidget* uiw = w.uiWidget();
1976 if (uiw)
1977 return uiw->setBusy(on);
1978 if (w.widget())
1979 return QtBusyWidget::showBusyChild(w.widget(),on);
1980 return false;
1981 }
1982
getText(const String & name,String & text,bool richText)1983 bool QtWindow::getText(const String& name, String& text, bool richText)
1984 {
1985 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) getText(%s) [%p]",
1986 m_id.c_str(),name.c_str(),this);
1987 QtWidget w(this,name);
1988 if (w.invalid())
1989 return false;
1990 UIWidget* uiw = w.uiWidget();
1991 if (uiw)
1992 return uiw->getText(text,richText);
1993 switch (w.type()) {
1994 case QtWidget::ComboBox:
1995 QtClient::getUtf8(text,w.combo()->currentText());
1996 return true;
1997 case QtWidget::LineEdit:
1998 QtClient::getUtf8(text,w.lineEdit()->text());
1999 return true;
2000 case QtWidget::TextBrowser:
2001 case QtWidget::TextEdit:
2002 if (!richText)
2003 QtClient::getUtf8(text,w.textEdit()->toPlainText());
2004 else
2005 QtClient::getUtf8(text,w.textEdit()->toHtml());
2006 return true;
2007 case QtWidget::Label:
2008 QtClient::getUtf8(text,w.label()->text());
2009 return true;
2010 case QtWidget::Action:
2011 QtClient::getUtf8(text,w.action()->text());
2012 return true;
2013 case QtWidget::SpinBox:
2014 text = w.spinBox()->value();
2015 return true;
2016 default:
2017 if (w.inherits(QtWidget::AbstractButton)) {
2018 QtClient::getUtf8(text,w.abstractButton()->text());
2019 return true;
2020 }
2021 }
2022 return false;
2023 }
2024
getCheck(const String & name,bool & checked)2025 bool QtWindow::getCheck(const String& name, bool& checked)
2026 {
2027 DDebug(QtDriver::self(),DebugAll,"QtWindow::getCheck(%s) [%p]",name.c_str(),this);
2028
2029 QtWidget w(this,name);
2030 if (w.invalid())
2031 return false;
2032 if (w.inherits(QtWidget::AbstractButton))
2033 checked = w.abstractButton()->isChecked();
2034 else if (w.type() == QtWidget::Action)
2035 checked = w.action()->isChecked();
2036 else
2037 return false;
2038 return true;
2039 }
2040
getSelect(const String & name,String & item)2041 bool QtWindow::getSelect(const String& name, String& item)
2042 {
2043 XDebug(QtDriver::self(),DebugAll,"QtWindow::getSelect(%s) [%p]",name.c_str(),this);
2044 QtWidget w(this,name);
2045 if (w.invalid())
2046 return false;
2047 UIWidget* uiw = w.uiWidget();
2048 if (uiw)
2049 return uiw->getSelect(item);
2050 switch (w.type()) {
2051 case QtWidget::ComboBox:
2052 if (w.combo()->lineEdit() && w.combo()->lineEdit()->selectedText().isEmpty())
2053 return false;
2054 QtClient::getUtf8(item,w.combo()->currentText());
2055 return true;
2056 case QtWidget::Table:
2057 {
2058 TableWidget t(w);
2059 int row = t.crtRow();
2060 return row >= 0 ? t.getCell(row,0,item) : false;
2061 }
2062 case QtWidget::ListBox:
2063 {
2064 QListWidgetItem* crt = w.list()->currentItem();
2065 if (!crt)
2066 return false;
2067 QtClient::getUtf8(item,crt->text());
2068 }
2069 return true;
2070 case QtWidget::Slider:
2071 item = w.slider()->value();
2072 return true;
2073 case QtWidget::ProgressBar:
2074 item = w.progressBar()->value();
2075 return true;
2076 case QtWidget::Tab:
2077 {
2078 item = "";
2079 QWidget* wid = w.tab()->currentWidget();
2080 if (wid)
2081 QtClient::getUtf8(item,wid->objectName());
2082 }
2083 return true;
2084 case QtWidget::StackWidget:
2085 {
2086 item = "";
2087 QWidget* wid = w.stackWidget()->currentWidget();
2088 if (wid)
2089 QtClient::getUtf8(item,wid->objectName());
2090 }
2091 return true;
2092 }
2093 return false;
2094 }
2095
2096 // Retrieve an element's multiple selection
getSelect(const String & name,NamedList & items)2097 bool QtWindow::getSelect(const String& name, NamedList& items)
2098 {
2099 XDebug(QtDriver::self(),DebugAll,"QtWindow::getSelect(%p) [%p]",&items,this);
2100 QtWidget w(this,name);
2101 if (w.invalid())
2102 return false;
2103 UIWidget* uiw = w.uiWidget();
2104 if (uiw)
2105 return uiw->getSelect(items);
2106 switch (w.type()) {
2107 case QtWidget::ComboBox:
2108 case QtWidget::Table:
2109 case QtWidget::ListBox:
2110 case QtWidget::Slider:
2111 case QtWidget::ProgressBar:
2112 case QtWidget::Tab:
2113 case QtWidget::StackWidget:
2114 DDebug(QtDriver::self(),DebugStub,"QtWindow::getSelect(%p) not implemented for '%s; [%p]",
2115 &items,w.widget()->metaObject()->className(),this);
2116 }
2117 return false;
2118 }
2119
2120 // Build a menu from a list of parameters
buildMenu(const NamedList & params)2121 bool QtWindow::buildMenu(const NamedList& params)
2122 {
2123 QWidget* parent = this;
2124 // Retrieve the owner
2125 const String& owner = params[YSTRING("owner")];
2126 if (owner && owner != m_id) {
2127 parent = qFindChild<QWidget*>(this,QtClient::setUtf8(owner));
2128 if (!parent) {
2129 DDebug(QtDriver::self(),DebugNote,
2130 "QtWindow(%s) buildMenu(%s) owner '%s' not found [%p]",
2131 m_id.c_str(),params.c_str(),owner.c_str(),this);
2132 return false;
2133 }
2134 }
2135 QWidget* target = parent;
2136 const String& t = params[YSTRING("target")];
2137 if (t) {
2138 target = qFindChild<QWidget*>(this,QtClient::setUtf8(t));
2139 if (!target) {
2140 DDebug(QtDriver::self(),DebugNote,
2141 "QtWindow(%s) buildMenu(%s) target '%s' not found [%p]",
2142 m_id.c_str(),params.c_str(),t.c_str(),this);
2143 return false;
2144 }
2145 }
2146 // Remove existing menu
2147 removeMenu(params);
2148 QMenu* menu = QtClient::buildMenu(params,params.getValue(YSTRING("title"),params),this,
2149 SLOT(action()),SLOT(toggled(bool)),parent);
2150 if (!menu) {
2151 DDebug(QtDriver::self(),DebugNote,
2152 "QtWindow(%s) failed to build menu '%s' target='%s' [%p]",
2153 m_id.c_str(),params.c_str(),YQT_OBJECT_NAME(target),this);
2154 return false;
2155 }
2156 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) built menu '%s' target='%s' [%p]",
2157 m_id.c_str(),params.c_str(),YQT_OBJECT_NAME(target),this);
2158 QMenuBar* mbOwner = qobject_cast<QMenuBar*>(target);
2159 QMenu* mOwner = !mbOwner ? qobject_cast<QMenu*>(target) : 0;
2160 if (mbOwner || mOwner) {
2161 QAction* before = 0;
2162 const String& bef = params[YSTRING("before")];
2163 // Retrieve the action to insert before
2164 if (bef) {
2165 QString cmp = QtClient::setUtf8(bef);
2166 QList<QAction*> list = target->actions();
2167 for (int i = 0; !before && i < list.size(); i++) {
2168 // Check action name or menu name if the action is associated with a menu
2169 if (list[i]->objectName() == cmp)
2170 before = list[i];
2171 else if (list[i]->menu() && list[i]->menu()->objectName() == cmp)
2172 before = list[i]->menu()->menuAction();
2173 if (before && i && list[i - 1]->isSeparator() &&
2174 params.getBoolValue(YSTRING("before_separator"),true))
2175 before = list[i - 1];
2176 }
2177 }
2178 // Insert the menu
2179 if (mbOwner)
2180 mbOwner->insertMenu(before,menu);
2181 else
2182 mOwner->insertMenu(before,menu);
2183 }
2184 else {
2185 QToolButton* tb = qobject_cast<QToolButton*>(target);
2186 if (tb)
2187 tb->setMenu(menu);
2188 else {
2189 QPushButton* pb = qobject_cast<QPushButton*>(target);
2190 if (pb)
2191 pb->setMenu(menu);
2192 else if (!QtClient::setProperty(target,s_propContextMenu,params))
2193 target->addAction(menu->menuAction());
2194 }
2195 }
2196 return true;
2197 }
2198
2199 // Remove a menu
removeMenu(const NamedList & params)2200 bool QtWindow::removeMenu(const NamedList& params)
2201 {
2202 QWidget* parent = this;
2203 // Retrieve the owner
2204 const String& owner = params[YSTRING("owner")];
2205 if (owner && owner != m_id) {
2206 parent = qFindChild<QWidget*>(this,QtClient::setUtf8(owner));
2207 if (!parent)
2208 return false;
2209 }
2210 QMenu* menu = qFindChild<QMenu*>(parent,QtClient::setUtf8(params));
2211 if (!menu)
2212 return false;
2213 QtClient::deleteLater(menu);
2214 return true;
2215 }
2216
2217 // Set an element's image
setImage(const String & name,const String & image,bool fit)2218 bool QtWindow::setImage(const String& name, const String& image, bool fit)
2219 {
2220 if (!name)
2221 return false;
2222 if (name == m_id)
2223 return QtClient::setImage(this,image);
2224 QObject* obj = qFindChild<QObject*>(this,QtClient::setUtf8(name));
2225 return obj && QtClient::setImage(obj,image,fit);
2226 }
2227
2228 // Set a property for this window or for a widget owned by it
setProperty(const String & name,const String & item,const String & value)2229 bool QtWindow::setProperty(const String& name, const String& item, const String& value)
2230 {
2231 if (name == m_id)
2232 return QtClient::setProperty(wndWidget(),item,value);
2233 QObject* obj = qFindChild<QObject*>(this,QtClient::setUtf8(name));
2234 return obj ? QtClient::setProperty(obj,item,value) : false;
2235 }
2236
2237 // Get a property from this window or from a widget owned by it
getProperty(const String & name,const String & item,String & value)2238 bool QtWindow::getProperty(const String& name, const String& item, String& value)
2239 {
2240 if (name == m_id)
2241 return QtClient::getProperty(wndWidget(),item,value);
2242 QObject* obj = qFindChild<QObject*>(this,QtClient::setUtf8(name));
2243 return obj ? QtClient::getProperty(obj,item,value) : false;
2244 }
2245
moveEvent(QMoveEvent * event)2246 void QtWindow::moveEvent(QMoveEvent* event)
2247 {
2248 QWidget::moveEvent(event);
2249 // Don't update pos if not shown normal
2250 if (!isShownNormal())
2251 return;
2252 m_x = pos().x();
2253 m_y = pos().y();
2254 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) moved x=%d y=%d [%p]",
2255 m_id.c_str(),m_x,m_y,this);
2256 }
2257
resizeEvent(QResizeEvent * event)2258 void QtWindow::resizeEvent(QResizeEvent* event)
2259 {
2260 QWidget::resizeEvent(event);
2261 // Don't update size if not shown normal
2262 if (!isShownNormal())
2263 return;
2264 m_width = width();
2265 m_height = height();
2266 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) resized width=%d height=%d [%p]",
2267 m_id.c_str(),m_width,m_height,this);
2268 }
2269
event(QEvent * ev)2270 bool QtWindow::event(QEvent* ev)
2271 {
2272 static const String s_activeChg("window_active_changed");
2273 if (ev->type() == QEvent::WindowDeactivate) {
2274 String hideProp;
2275 QtClient::getProperty(wndWidget(),s_propHideInactive,hideProp);
2276 if (hideProp && hideProp.toBoolean())
2277 setVisible(false);
2278 m_active = false;
2279 Client::self()->toggle(this,s_activeChg,false);
2280 }
2281 else if (ev->type() == QEvent::WindowActivate) {
2282 m_active = true;
2283 Client::self()->toggle(this,s_activeChg,true);
2284 String wName;
2285 if (getPropPlatform(wndWidget(),s_propShowWndWhenActive,wName) && wName)
2286 Client::setVisible(wName);
2287 }
2288 else if (ev->type() == QEvent::ApplicationDeactivate) {
2289 if (m_active) {
2290 m_active = false;
2291 Client::self()->toggle(this,s_activeChg,true);
2292 }
2293 }
2294 return QWidget::event(ev);
2295 }
2296
closeEvent(QCloseEvent * event)2297 void QtWindow::closeEvent(QCloseEvent* event)
2298 {
2299 // NOTE: Don't access window's data after calling hide():
2300 // some logics might destroy the window when hidden
2301
2302 // Notify window closed
2303 String tmp;
2304 if (Client::self() &&
2305 QtClient::getProperty(wndWidget(),"_yate_windowclosedaction",tmp))
2306 Client::self()->action(this,tmp);
2307
2308 // Hide the window when requested
2309 if (QtClient::getBoolProperty(wndWidget(),"_yate_hideonclose")) {
2310 event->ignore();
2311 hide();
2312 return;
2313 }
2314
2315 QWidget::closeEvent(event);
2316 if (m_mainWindow && Client::self()) {
2317 Client::self()->quit();
2318 return;
2319 }
2320 if (QtClient::getBoolProperty(wndWidget(),"_yate_destroyonclose")) {
2321 XDebug(QtDriver::self(),DebugAll,
2322 "Window(%s) closeEvent() set delete later [%p]",m_id.c_str(),this);
2323 QObject::deleteLater();
2324 // Safe to call hide(): the window will be deleted when control returns
2325 // to the main loop
2326 }
2327 hide();
2328 }
2329
changeEvent(QEvent * event)2330 void QtWindow::changeEvent(QEvent* event)
2331 {
2332 if (event->type() == QEvent::WindowStateChange)
2333 m_maximized = isMaximized();
2334 QWidget::changeEvent(event);
2335 }
2336
action()2337 void QtWindow::action()
2338 {
2339 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) action() sender=%s [%p]",
2340 m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
2341 if (!QtClient::self() || QtClient::changing())
2342 return;
2343 String name;
2344 NamedList* params = 0;
2345 if (!QtClient::getBoolProperty(sender(),"_yate_translateidentity"))
2346 QtClient::getIdentity(sender(),name);
2347 else {
2348 QtWidget w(sender());
2349 translateName(w,name,¶ms);
2350 }
2351 if (name)
2352 QtClient::self()->action(this,name,params);
2353 TelEngine::destruct(params);
2354 }
2355
2356 // Toggled actions
toggled(bool on)2357 void QtWindow::toggled(bool on)
2358 {
2359 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) toggled=%s sender=%s [%p]",
2360 m_id.c_str(),String::boolText(on),YQT_OBJECT_NAME(sender()),this);
2361 QtClient::updateToggleImage(sender());
2362 if (!QtClient::self() || QtClient::changing())
2363 return;
2364 QtWidget w(sender());
2365 String name;
2366 if (translateName(w,name))
2367 QtClient::self()->toggle(this,name,on);
2368 }
2369
2370 // System tray actions
sysTrayIconAction(QSystemTrayIcon::ActivationReason reason)2371 void QtWindow::sysTrayIconAction(QSystemTrayIcon::ActivationReason reason)
2372 {
2373 String action;
2374 switch (reason) {
2375 case QSystemTrayIcon::Context:
2376 QtClient::getProperty(sender(),s_propAction + "Context",action);
2377 break;
2378 case QSystemTrayIcon::DoubleClick:
2379 QtClient::getProperty(sender(),s_propAction + "DoubleClick",action);
2380 break;
2381 case QSystemTrayIcon::Trigger:
2382 QtClient::getProperty(sender(),s_propAction + "Trigger",action);
2383 break;
2384 case QSystemTrayIcon::MiddleClick:
2385 QtClient::getProperty(sender(),s_propAction + "MiddleClick",action);
2386 break;
2387 default:
2388 return;
2389 }
2390 if (action)
2391 Client::self()->action(this,action);
2392 }
2393
2394 // Choose file window was accepted
chooseFileAccepted()2395 void QtWindow::chooseFileAccepted()
2396 {
2397 QFileDialog* dlg = qobject_cast<QFileDialog*>(sender());
2398 if (!dlg)
2399 return;
2400 String action;
2401 QtClient::getUtf8(action,dlg->objectName());
2402 if (!action)
2403 return;
2404 NamedList params("");
2405 QDir dir = dlg->directory();
2406 if (dir.absolutePath().length())
2407 QtClient::getUtf8(params,"dir",fixPathSep(dir.absolutePath()));
2408 QStringList files = dlg->selectedFiles();
2409 for (int i = 0; i < files.size(); i++)
2410 QtClient::getUtf8(params,"file",fixPathSep(files[i]));
2411 if (dlg->fileMode() != QFileDialog::DirectoryOnly &&
2412 dlg->fileMode() != QFileDialog::Directory) {
2413 QString filter = dlg->selectedFilter();
2414 if (filter.length())
2415 QtClient::getUtf8(params,"filter",filter);
2416 }
2417 Client::self()->action(this,action,¶ms);
2418 }
2419
2420 // Choose file window was cancelled
chooseFileRejected()2421 void QtWindow::chooseFileRejected()
2422 {
2423 QFileDialog* dlg = qobject_cast<QFileDialog*>(sender());
2424 if (!dlg)
2425 return;
2426 String action;
2427 QtClient::getUtf8(action,dlg->objectName());
2428 if (!action)
2429 return;
2430 Client::self()->action(this,action,0);
2431 }
2432
openUrl(const QString & link)2433 void QtWindow::openUrl(const QString& link)
2434 {
2435 QDesktopServices::openUrl(QUrl(link));
2436 }
2437
doubleClick()2438 void QtWindow::doubleClick()
2439 {
2440 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) doubleClick() sender=%s [%p]",
2441 m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
2442 if (QtClient::self() && sender())
2443 Client::self()->action(this,YQT_OBJECT_NAME(sender()));
2444 }
2445
2446 // A widget's selection changed
selectionChanged()2447 void QtWindow::selectionChanged()
2448 {
2449 XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) selectionChanged() sender=%s [%p]",
2450 m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
2451 if (!(QtClient::self() && sender()))
2452 return;
2453 String name = YQT_OBJECT_NAME(sender());
2454 QtWidget w(sender());
2455 if (w.type() != QtWidget::Calendar) {
2456 String item;
2457 getSelect(name,item);
2458 Client::self()->select(this,name,item);
2459 }
2460 else {
2461 NamedList p("");
2462 QDate d = w.calendar()->selectedDate();
2463 p.addParam("year",String(d.year()));
2464 p.addParam("month",String(d.month()));
2465 p.addParam("day",String(d.day()));
2466 Client::self()->action(this,name,&p);
2467 }
2468 }
2469
2470 // Connect an object's text changed signal to window's slot
connectTextChanged(QObject * obj)2471 bool QtWindow::connectTextChanged(QObject* obj)
2472 {
2473 if (!(obj && QtClient::getBoolProperty(obj,"_yate_textchangednotify")))
2474 return false;
2475 QComboBox* combo = qobject_cast<QComboBox*>(obj);
2476 if (combo)
2477 return QtClient::connectObjects(combo,SIGNAL(editTextChanged(const QString&)),
2478 this,SLOT(textChanged(const QString&)));
2479 QLineEdit* lineEdit = qobject_cast<QLineEdit*>(obj);
2480 if (lineEdit)
2481 return QtClient::connectObjects(lineEdit,SIGNAL(textChanged(const QString&)),
2482 this,SLOT(textChanged(const QString&)));
2483 QTextEdit* textEdit = qobject_cast<QTextEdit*>(obj);
2484 if (textEdit)
2485 return QtClient::connectObjects(textEdit,SIGNAL(textChanged()),
2486 this,SLOT(textChanged()));
2487 const QMetaObject* meta = obj->metaObject();
2488 Debug(DebugStub,"connectTextChanged() not implemented for class '%s'",
2489 meta ? meta->className() : "");
2490 return false;
2491 }
2492
2493 // Notify text changed to the client
notifyTextChanged(QObject * obj,const QString & text)2494 void QtWindow::notifyTextChanged(QObject* obj, const QString& text)
2495 {
2496 if (!(obj && QtClient::getBoolProperty(obj,"_yate_textchangednotify")))
2497 return;
2498 // Detect QtUIWidget item. Get its container identity if found
2499 String item;
2500 QtUIWidget::getListItemProp(obj,item);
2501 QtUIWidget* uiw = item ? QtUIWidget::container(obj) : 0;
2502 String name;
2503 if (!uiw)
2504 QtClient::getIdentity(obj,name);
2505 else
2506 uiw->getIdentity(obj,name);
2507 if (!name)
2508 return;
2509 NamedList p("");
2510 p.addParam("sender",name);
2511 if (text.size())
2512 QtClient::getUtf8(p,"text",text);
2513 Client::self()->action(this,YSTRING("textchanged"),&p);
2514 }
2515
2516 // Load a widget from file
loadUI(const char * fileName,QWidget * parent,const char * uiName,const char * path)2517 QWidget* QtWindow::loadUI(const char* fileName, QWidget* parent,
2518 const char* uiName, const char* path)
2519 {
2520 if (Client::exiting())
2521 return 0;
2522 if (!(fileName && *fileName && parent))
2523 return 0;
2524
2525 if (!(path && *path))
2526 path = Client::s_skinPath.c_str();
2527 UIBuffer* buf = UIBuffer::build(fileName);
2528 const char* err = 0;
2529 if (buf && buf->buffer()) {
2530 QBuffer b(buf->buffer());
2531 QUiLoader loader;
2532 loader.setWorkingDirectory(QDir(QtClient::setUtf8(path)));
2533 QWidget* w = loader.load(&b,parent);
2534 if (w)
2535 return w;
2536 err = "loader failed";
2537 }
2538 else
2539 err = buf ? "file is empty" : "file not found";
2540 // Error
2541 TelEngine::destruct(buf);
2542 Debug(DebugWarn,"Failed to load widget '%s' file='%s' path='%s': %s",
2543 uiName,fileName,path,err);
2544 return 0;
2545 }
2546
2547 // Clear the UI cache
clearUICache(const char * fileName)2548 void QtWindow::clearUICache(const char* fileName)
2549 {
2550 if (!fileName)
2551 UIBuffer::s_uiCache.clear();
2552 else
2553 TelEngine::destruct(UIBuffer::s_uiCache.find(fileName));
2554 }
2555
2556 // Filter events
eventFilter(QObject * obj,QEvent * event)2557 bool QtWindow::eventFilter(QObject* obj, QEvent* event)
2558 {
2559 if (!obj)
2560 return false;
2561 // Apply dynamic properties changes
2562 if (event->type() == QEvent::DynamicPropertyChange) {
2563 String name = YQT_OBJECT_NAME(obj);
2564 QDynamicPropertyChangeEvent* ev = static_cast<QDynamicPropertyChangeEvent*>(event);
2565 String prop = ev->propertyName().constData();
2566 // Handle only yate dynamic properties
2567 if (!prop.startsWith(s_yatePropPrefix,false))
2568 return QWidget::eventFilter(obj,event);
2569 XDebug(QtDriver::self(),DebugAll,"Window(%s) eventFilter(%s) prop=%s [%p]",
2570 m_id.c_str(),YQT_OBJECT_NAME(obj),prop.c_str(),this);
2571 // Return false for now on: it's our property
2572 QtWidget w(obj);
2573 if (w.invalid())
2574 return false;
2575 String value;
2576 if (!QtClient::getProperty(obj,prop,value))
2577 return false;
2578 bool ok = true;
2579 bool handled = true;
2580 if (prop == s_propColWidths) {
2581 if (w.type() == QtWidget::Table) {
2582 QHeaderView* hdr = w.table()->horizontalHeader();
2583 bool skipLast = hdr && hdr->stretchLastSection();
2584 ObjList* list = value.split(',',false);
2585 int col = 0;
2586 for (ObjList* o = list->skipNull(); o; o = o->skipNext(), col++) {
2587 if (skipLast && col == w.table()->columnCount() - 1)
2588 break;
2589 int width = (static_cast<String*>(o->get()))->toInteger(-1);
2590 if (width >= 0)
2591 w.table()->setColumnWidth(col,width);
2592 }
2593 TelEngine::destruct(list);
2594 }
2595 }
2596 else if (prop == s_propSorting) {
2597 if (w.type() == QtWidget::Table) {
2598 ObjList* list = value.split(',',false);
2599 String* tmp = static_cast<String*>((*list)[0]);
2600 int col = tmp ? tmp->toInteger(-1) : -1;
2601 if (col >= 0) {
2602 tmp = static_cast<String*>((*list)[1]);
2603 bool asc = tmp ? tmp->toBoolean(true) : true;
2604 w.table()->sortItems(col,asc ? Qt::AscendingOrder : Qt::DescendingOrder);
2605 }
2606 TelEngine::destruct(list);
2607 }
2608 }
2609 else if (prop == s_propSizes) {
2610 if (w.type() == QtWidget::Splitter) {
2611 QList<int> list = QtClient::str2IntList(value);
2612 w.splitter()->setSizes(list);
2613 }
2614 }
2615 else if (prop == s_propWindowFlags) {
2616 QWidget* wid = (name == m_id || name == m_oldId) ? this : w.widget();
2617 QtClient::applyWindowFlags(wid,value);
2618 }
2619 else if (prop == s_propHHeader) {
2620 // Show/hide the horizontal header
2621 ok = ((w.type() == QtWidget::Table || w.type() == QtWidget::CustomTable) &&
2622 value.isBoolean() && w.table()->horizontalHeader());
2623 if (ok)
2624 w.table()->horizontalHeader()->setVisible(value.toBoolean());
2625 }
2626 else
2627 ok = handled = false;
2628 if (ok)
2629 DDebug(ClientDriver::self(),DebugAll,
2630 "Applied dynamic property %s='%s' for object='%s'",
2631 prop.c_str(),value.c_str(),name.c_str());
2632 else if (handled)
2633 Debug(ClientDriver::self(),DebugMild,
2634 "Failed to apply dynamic property %s='%s' for object='%s'",
2635 prop.c_str(),value.c_str(),name.c_str());
2636 return false;
2637 }
2638 if (event->type() == QEvent::KeyPress) {
2639 String action;
2640 bool filter = false;
2641 if (!QtClient::filterKeyEvent(obj,static_cast<QKeyEvent*>(event),
2642 action,filter,this))
2643 return QWidget::eventFilter(obj,event);
2644 if (action && Client::self())
2645 Client::self()->action(this,action);
2646 return filter;
2647 }
2648 if (event->type() == QEvent::ContextMenu) {
2649 if (handleContextMenuEvent(static_cast<QContextMenuEvent*>(event),obj))
2650 return false;
2651 }
2652 if (event->type() == QEvent::Enter) {
2653 QtClient::updateImageFromMouse(obj,true,true);
2654 return QWidget::eventFilter(obj,event);
2655 }
2656 if (event->type() == QEvent::Leave) {
2657 QtClient::updateImageFromMouse(obj,true,false);
2658 return QWidget::eventFilter(obj,event);
2659 }
2660 if (event->type() == QEvent::MouseButtonPress) {
2661 QtClient::updateImageFromMouse(obj,false,true);
2662 return QWidget::eventFilter(obj,event);
2663 }
2664 if (event->type() == QEvent::MouseButtonRelease) {
2665 QtClient::updateImageFromMouse(obj,false,false);
2666 return QWidget::eventFilter(obj,event);
2667 }
2668 return QWidget::eventFilter(obj,event);
2669 }
2670
2671 // Handle key pressed events
keyPressEvent(QKeyEvent * event)2672 void QtWindow::keyPressEvent(QKeyEvent* event)
2673 {
2674 if (!(Client::self() && event)) {
2675 QWidget::keyPressEvent(event);
2676 return;
2677 }
2678 QVariant var = this->property("_yate_keypress_redirect");
2679 QString child = var.toString();
2680 if (child.size() > 0 && QtClient::sendEvent(*event,this,child)) {
2681 QWidget* wid = qFindChild<QWidget*>(this,child);
2682 if (wid)
2683 wid->setFocus();
2684 return;
2685 }
2686 if (event->key() == Qt::Key_Backspace)
2687 Client::self()->backspace(m_id,this);
2688 QWidget::keyPressEvent(event);
2689 }
2690
2691 // Show hide window. Notify the client
setVisible(bool visible)2692 void QtWindow::setVisible(bool visible)
2693 {
2694 // Override position for notification windows
2695 if (visible && isShownNormal() &&
2696 QtClient::getBoolProperty(wndWidget(),"_yate_notificationwindow")) {
2697 // Don't move
2698 m_moving = -1;
2699 #ifndef Q_WS_MAC
2700 // Detect unavailable screen space position and move the window in the apropriate position
2701 // bottom/right/none: move it in the right/bottom corner.
2702 // top: move it in the right/top corner.
2703 // left: move it in the left/bottom corner.
2704 int pos = QtClient::PosNone;
2705 if (QtClient::getScreenUnavailPos(this,pos)) {
2706 if (0 != (pos & (QtClient::PosBottom | QtClient::PosRight)) || pos == QtClient::PosNone)
2707 QtClient::moveWindow(this,QtClient::CornerBottomRight);
2708 else if (0 != (pos & QtClient::PosTop))
2709 QtClient::moveWindow(this,QtClient::CornerTopRight);
2710 else
2711 QtClient::moveWindow(this,QtClient::CornerBottomLeft);
2712 }
2713 #else
2714 QtClient::moveWindow(this,QtClient::CornerTopRight);
2715 #endif
2716 }
2717 if (visible && isMinimized())
2718 showNormal();
2719 else
2720 QWidget::setVisible(visible);
2721 // Notify the client on window visibility changes
2722 bool changed = (m_visible != visible);
2723 m_visible = visible;
2724 if (changed && Client::self()) {
2725 QVariant var;
2726 if (this)
2727 var = this->property("dynamicUiActionVisibleChanged");
2728 if (!var.toBool())
2729 Client::self()->toggle(this,YSTRING("window_visible_changed"),m_visible);
2730 else {
2731 Message* m = new Message("ui.action");
2732 m->addParam("action","window_visible_changed");
2733 m->addParam("visible",String::boolText(m_visible));
2734 m->addParam("window",m_id);
2735 Engine::enqueue(m);
2736 }
2737 }
2738 if (!m_visible && QtClient::getBoolProperty(wndWidget(),"_yate_destroyonhide")) {
2739 DDebug(QtDriver::self(),DebugAll,
2740 "Window(%s) setVisible(false) set delete later [%p]",m_id.c_str(),this);
2741 QObject::deleteLater();
2742 }
2743 // Destroy owned dialogs
2744 if (!m_visible) {
2745 QList<QDialog*> d = qFindChildren<QDialog*>(this);
2746 for (int i = 0; i < d.size(); i++)
2747 d[i]->deleteLater();
2748 }
2749 }
2750
2751 // Show the window
show()2752 void QtWindow::show()
2753 {
2754 setVisible(true);
2755 m_maximized = m_maximized || isMaximized();
2756 if (m_maximized)
2757 setWindowState(Qt::WindowMaximized);
2758 }
2759
2760 // Hide the window
hide()2761 void QtWindow::hide()
2762 {
2763 setVisible(false);
2764 }
2765
size(int width,int height)2766 void QtWindow::size(int width, int height)
2767 {
2768 Debug(QtDriver::self(),DebugStub,"QtWindow(%s)::size(%d,%d) [%p]",m_id.c_str(),width,height,this);
2769 }
2770
move(int x,int y)2771 void QtWindow::move(int x, int y)
2772 {
2773 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s)::move(%d,%d) [%p]",m_id.c_str(),x,y,this);
2774 QWidget::move(x,y);
2775 }
2776
moveRel(int dx,int dy)2777 void QtWindow::moveRel(int dx, int dy)
2778 {
2779 DDebug(QtDriver::self(),DebugAll,"QtWindow::moveRel(%d,%d) [%p]",dx,dy,this);
2780 }
2781
related(const Window * wnd) const2782 bool QtWindow::related(const Window* wnd) const
2783 {
2784 DDebug(QtDriver::self(),DebugAll,"QtWindow::related(%p) [%p]",wnd,this);
2785 return false;
2786 }
2787
menu(int x,int y)2788 void QtWindow::menu(int x, int y)
2789 {
2790 DDebug(QtDriver::self(),DebugAll,"QtWindow::menu(%d,%d) [%p]",x,y,this);
2791 }
2792
2793 // Create a modal dialog
createDialog(const String & name,const String & title,const String & alias,const NamedList * params)2794 bool QtWindow::createDialog(const String& name, const String& title, const String& alias,
2795 const NamedList* params)
2796 {
2797 QtDialog* d = new QtDialog(this);
2798 if (d->show(name,title,alias,params))
2799 return true;
2800 d->deleteLater();
2801 return false;
2802 }
2803
2804 // Destroy a modal dialog
closeDialog(const String & name)2805 bool QtWindow::closeDialog(const String& name)
2806 {
2807 QDialog* d = qFindChild<QDialog*>(this,QtClient::setUtf8(name));
2808 if (!d)
2809 return false;
2810 d->deleteLater();
2811 return true;
2812 }
2813
2814 // Load UI file and setup the window
doPopulate()2815 void QtWindow::doPopulate()
2816 {
2817 Debug(QtDriver::self(),DebugAll,"Populating window '%s' [%p]",m_id.c_str(),this);
2818 QWidget* formWidget = loadUI(m_description,this,m_id);
2819 if (!formWidget)
2820 return;
2821 // Set window title decoration flags to avoid pos/size troubles with late decoration
2822 QVariant var = formWidget->property(s_propWindowFlags);
2823 if (var.type() == QVariant::Invalid) {
2824 String flgs = "title,sysmenu,minimize,close";
2825 // Add maximize only if allowed
2826 if (formWidget->maximumWidth() == QWIDGETSIZE_MAX ||
2827 formWidget->maximumHeight() == QWIDGETSIZE_MAX)
2828 flgs.append("maximize",",");
2829 formWidget->setProperty(s_propWindowFlags,QVariant(QtClient::setUtf8(flgs)));
2830 }
2831 setMinimumSize(formWidget->minimumSize().width(),formWidget->minimumSize().height());
2832 setMaximumSize(formWidget->maximumSize().width(),formWidget->maximumSize().height());
2833 m_x = formWidget->pos().x();
2834 m_y = formWidget->pos().y();
2835 m_width = formWidget->width();
2836 m_height = formWidget->height();
2837 move(m_x,m_y);
2838 QWidget::resize(m_width,m_height);
2839 QtClient::setWidget(this,formWidget);
2840 m_widget = YQT_OBJECT_NAME(formWidget);
2841 String wTitle;
2842 QtClient::getUtf8(wTitle,formWidget->windowTitle());
2843 title(wTitle);
2844 setWindowIcon(formWidget->windowIcon());
2845 setStyleSheet(formWidget->styleSheet());
2846 }
2847
2848 // Initialize window
doInit()2849 void QtWindow::doInit()
2850 {
2851 DDebug(QtDriver::self(),DebugAll,"Initializing window '%s' [%p]",
2852 m_id.c_str(),this);
2853
2854 // Create window's dynamic properties from config
2855 Configuration cfg(Engine::configFile(m_oldId),false);
2856 NamedList* sectGeneral = cfg.getSection("general");
2857 if (sectGeneral)
2858 addDynamicProps(this,*sectGeneral);
2859
2860 // Load window data
2861 m_mainWindow = s_cfg.getBoolValue(m_oldId,"mainwindow");
2862 m_saveOnClose = s_cfg.getBoolValue(m_oldId,"save",true);
2863 if (m_id != m_oldId)
2864 m_saveOnClose = s_cfg.getBoolValue(m_oldId,"savealias",m_saveOnClose);
2865 NamedList* sect = s_save.getSection(m_id);
2866 if (sect) {
2867 m_maximized = sect->getBoolValue("maximized");
2868 m_x = sect->getIntValue("x",m_x);
2869 m_y = sect->getIntValue("y",m_y);
2870 m_width = sect->getIntValue("width",m_width);
2871 m_height = sect->getIntValue("height",m_height);
2872 m_visible = sect->getBoolValue("visible");
2873 }
2874 else {
2875 if (m_saveOnClose)
2876 Debug(QtDriver::self(),DebugNote,"Window(%s) not found in config [%p]",
2877 m_id.c_str(),this);
2878 m_visible = s_cfg.getBoolValue(m_oldId,"visible");
2879 // Make sure the window is shown in the available geometry
2880 QDesktopWidget* d = QApplication::desktop();
2881 if (d) {
2882 QRect r = d->availableGeometry(this);
2883 m_x = r.x();
2884 m_y = r.y();
2885 }
2886 }
2887 m_visible = m_mainWindow || m_visible;
2888 if (!m_width)
2889 m_width = this->width();
2890 if (!m_height)
2891 m_height = this->height();
2892 move(m_x,m_y);
2893 QWidget::resize(m_width,m_height);
2894
2895 // Build custom UI widgets from frames owned by this widget
2896 QtClient::buildFrameUiWidgets(this);
2897
2898 // Create custom widgets from
2899 // _yate_identity=customwidget|[separator=sep|] sep widgetclass sep widgetname [sep param=value]
2900 QList<QFrame*> frm = qFindChildren<QFrame*>(this);
2901 for (int i = 0; i < frm.size(); i++) {
2902 String create;
2903 QtClient::getProperty(frm[i],"_yate_identity",create);
2904 if (!create.startSkip("customwidget|",false))
2905 continue;
2906 char sep = '|';
2907 // Check if we have another separator
2908 if (create.startSkip("separator=",false)) {
2909 if (create.length() < 2)
2910 continue;
2911 sep = create.at(0);
2912 create = create.substr(2);
2913 }
2914 ObjList* list = create.split(sep,false);
2915 String type;
2916 String name;
2917 NamedList params("");
2918 int what = 0;
2919 for (ObjList* o = list->skipNull(); o; o = o->skipNext(), what++) {
2920 GenObject* p = o->get();
2921 if (what == 0)
2922 type = p->toString();
2923 else if (what == 1)
2924 name = p->toString();
2925 else {
2926 // Decode param
2927 int pos = p->toString().find('=');
2928 if (pos != -1)
2929 params.addParam(p->toString().substr(0,pos),p->toString().substr(pos + 1));
2930 }
2931 }
2932 TelEngine::destruct(list);
2933 params.addParam("parentwindow",m_id);
2934 NamedString* pw = new NamedString("parentwidget");
2935 QtClient::getUtf8(*pw,frm[i]->objectName());
2936 params.addParam(pw);
2937 QObject* obj = (QObject*)UIFactory::build(type,name,¶ms);
2938 if (!obj)
2939 continue;
2940 QWidget* wid = qobject_cast<QWidget*>(obj);
2941 if (wid)
2942 QtClient::setWidget(frm[i],wid);
2943 else {
2944 obj->setParent(frm[i]);
2945 QtCustomObject* customObj = qobject_cast<QtCustomObject*>(obj);
2946 if (customObj)
2947 customObj->parentChanged();
2948 }
2949 }
2950
2951 // Add the first menubar to layout
2952 QList<QMenuBar*> menuBars = qFindChildren<QMenuBar*>(this);
2953 if (menuBars.size() && layout()) {
2954 layout()->setMenuBar(menuBars[0]);
2955 // Decrease minimum size policy to make sure the layout is made properly
2956 if (wndWidget()) {
2957 int h = menuBars[0]->height();
2958 int min = wndWidget()->minimumHeight();
2959 if (min > h)
2960 wndWidget()->setMinimumHeight(min - h);
2961 else
2962 wndWidget()->setMinimumHeight(0);
2963 }
2964 #ifdef Q_WS_MAC
2965 if (m_mainWindow) {
2966 // Create a parentless menu bar to be set as the default application menu by copying it from the main window menu
2967 DDebug(QtDriver::self(),DebugAll,"Setting as default menu bar the menu bar of window '%s' [%p]",
2968 m_id.c_str(),this);
2969 QMenuBar* mainMenu = menuBars[0];
2970 QMenuBar* defaultMenu = new QMenuBar(0);
2971 QList<QAction*> topActions = mainMenu->actions();
2972 for (int i = 0; i < topActions.count(); i++) {
2973 QMenu* menu = topActions[i]->menu();
2974 if (menu) {
2975 QMenu* m = new QMenu(menu->title(),defaultMenu);
2976 String tmp;
2977 QtClient::getProperty(menu,YSTRING("_yate_menuNoCopy"),tmp);
2978 if (tmp.toBoolean())
2979 continue;
2980 defaultMenu->addMenu(m);
2981 QList<QAction*> actions = menu->actions();
2982 for (int j = 0; j < actions.count(); j++) {
2983 QAction* act = actions[j];
2984 tmp.clear();
2985 QtClient::getProperty(act,YSTRING("_yate_menuNoCopy"),tmp);
2986 if (tmp.toBoolean())
2987 continue;
2988 m->addAction(act);
2989 }
2990 }
2991 }
2992 }
2993 #endif
2994 }
2995
2996 // Create window's children dynamic properties from config
2997 unsigned int n = cfg.sections();
2998 for (unsigned int i = 0; i < n; i++) {
2999 NamedList* sect = cfg.getSection(i);
3000 if (sect && *sect && *sect != "general")
3001 addDynamicProps(qFindChild<QObject*>(this,sect->c_str()),*sect);
3002 }
3003
3004 // Process "_yate_setaction" property for our children
3005 QtClient::setAction(this);
3006
3007 // Connect actions' signal
3008 QList<QAction*> actions = qFindChildren<QAction*>(this);
3009 for (int i = 0; i < actions.size(); i++) {
3010 String addToWidget;
3011 QtClient::getProperty(actions[i],"dynamicAddToParent",addToWidget);
3012 if (addToWidget && addToWidget.toBoolean())
3013 QWidget::addAction(actions[i]);
3014 if (actions[i]->isCheckable())
3015 QtClient::connectObjects(actions[i],SIGNAL(toggled(bool)),this,SLOT(toggled(bool)));
3016 else
3017 QtClient::connectObjects(actions[i],SIGNAL(triggered()),this,SLOT(action()));
3018 }
3019
3020 // Connect combo boxes signals
3021 QList<QComboBox*> combos = qFindChildren<QComboBox*>(this);
3022 for (int i = 0; i < combos.size(); i++) {
3023 QtClient::connectObjects(combos[i],SIGNAL(activated(int)),this,SLOT(selectionChanged()));
3024 connectTextChanged(combos[i]);
3025 }
3026
3027 // Connect abstract buttons (check boxes and radio/push/tool buttons) signals
3028 QList<QAbstractButton*> buttons = qFindChildren<QAbstractButton*>(this);
3029 for(int i = 0; i < buttons.size(); i++)
3030 if (QtClient::autoConnect(buttons[i]))
3031 connectButton(buttons[i]);
3032
3033 // Connect group boxes signals
3034 QList<QGroupBox*> grp = qFindChildren<QGroupBox*>(this);
3035 for(int i = 0; i < grp.size(); i++)
3036 if (grp[i]->isCheckable())
3037 QtClient::connectObjects(grp[i],SIGNAL(toggled(bool)),this,SLOT(toggled(bool)));
3038
3039 // Connect sliders signals
3040 QList<QSlider*> sliders = qFindChildren<QSlider*>(this);
3041 for (int i = 0; i < sliders.size(); i++)
3042 QtClient::connectObjects(sliders[i],SIGNAL(valueChanged(int)),this,SLOT(selectionChanged()));
3043
3044 // Connect calendar widget signals
3045 QList<QCalendarWidget*> cals = qFindChildren<QCalendarWidget*>(this);
3046 for (int i = 0; i < cals.size(); i++)
3047 QtClient::connectObjects(cals[i],SIGNAL(selectionChanged()),this,SLOT(selectionChanged()));
3048
3049 // Connect list boxes signals
3050 QList<QListWidget*> lists = qFindChildren<QListWidget*>(this);
3051 for (int i = 0; i < lists.size(); i++) {
3052 QtClient::connectObjects(lists[i],SIGNAL(itemDoubleClicked(QListWidgetItem*)),
3053 this,SLOT(doubleClick()));
3054 QtClient::connectObjects(lists[i],SIGNAL(itemActivated(QListWidgetItem*)),
3055 this,SLOT(doubleClick()));
3056 QtClient::connectObjects(lists[i],SIGNAL(currentRowChanged(int)),
3057 this,SLOT(selectionChanged()));
3058 }
3059
3060 // Connect tab widget signals
3061 QList<QTabWidget*> tabs = qFindChildren<QTabWidget*>(this);
3062 for (int i = 0; i < tabs.size(); i++)
3063 QtClient::connectObjects(tabs[i],SIGNAL(currentChanged(int)),this,SLOT(selectionChanged()));
3064
3065 // Connect stacked widget signals
3066 QList<QStackedWidget*> sw = qFindChildren<QStackedWidget*>(this);
3067 for (int i = 0; i < sw.size(); i++)
3068 QtClient::connectObjects(sw[i],SIGNAL(currentChanged(int)),this,SLOT(selectionChanged()));
3069
3070 // Connect line edit signals
3071 QList<QLineEdit*> le = qFindChildren<QLineEdit*>(this);
3072 for (int i = 0; i < le.size(); i++)
3073 connectTextChanged(le[i]);
3074
3075 // Connect text edit signals
3076 QList<QTextEdit*> te = qFindChildren<QTextEdit*>(this);
3077 for (int i = 0; i < te.size(); i++)
3078 connectTextChanged(te[i]);
3079
3080 // Process tables:
3081 // Insert a column and connect signals
3082 // Hide columns starting with "hidden:"
3083 QList<QTableWidget*> tables = qFindChildren<QTableWidget*>(this);
3084 for (int i = 0; i < tables.size(); i++) {
3085 bool nonCustom = (0 == qobject_cast<QtTable*>(tables[i]));
3086 // Horizontal header
3087 QHeaderView* hdr = tables[i]->horizontalHeader();
3088 // Stretch last column
3089 bool b = QtClient::getBoolProperty(tables[i],"_yate_horizontalstretch",true);
3090 hdr->setStretchLastSection(b);
3091 String tmp;
3092 QtClient::getProperty(tables[i],"_yate_horizontalheader_align",tmp);
3093 if (tmp) {
3094 int def = hdr->defaultAlignment();
3095 hdr->setDefaultAlignment((Qt::Alignment)QtClient::str2align(tmp,def));
3096 }
3097 if (!QtClient::getBoolProperty(tables[i],"_yate_horizontalheader",true))
3098 hdr->hide();
3099 // Vertical header
3100 hdr = tables[i]->verticalHeader();
3101 int itemH = QtClient::getIntProperty(tables[i],"_yate_rowheight");
3102 if (itemH > 0)
3103 hdr->setDefaultSectionSize(itemH);
3104 if (!QtClient::getBoolProperty(tables[i],"_yate_verticalheader"))
3105 hdr->hide();
3106 else {
3107 int width = QtClient::getIntProperty(tables[i],"_yate_verticalheaderwidth");
3108 if (width > 0)
3109 hdr->setFixedWidth(width);
3110 if (!QtClient::getBoolProperty(tables[i],"_yate_allowvheaderresize"))
3111 hdr->setResizeMode(QHeaderView::Fixed);
3112 }
3113 if (nonCustom) {
3114 // Set _yate_save_props
3115 QVariant var = tables[i]->property(s_propsSave);
3116 if (var.type() != QVariant::StringList) {
3117 // Create the property if not found, ignore it if not a string list
3118 if (var.type() == QVariant::Invalid)
3119 var = QVariant(QVariant::StringList);
3120 else
3121 Debug(QtDriver::self(),DebugNote,
3122 "Window(%s) table '%s' already has a non string list property %s [%p]",
3123 m_id.c_str(),YQT_OBJECT_NAME(tables[i]),s_propsSave.c_str(),this);
3124 }
3125 if (var.type() == QVariant::StringList) {
3126 // Make sure saved properties exists to allow them to be restored
3127 QStringList sl = var.toStringList();
3128 bool changed = createProperty(tables[i],s_propColWidths,QVariant::String,this,&sl);
3129 changed = createProperty(tables[i],s_propSorting,QVariant::String,this,&sl) || changed;
3130 if (changed)
3131 tables[i]->setProperty(s_propsSave,QVariant(sl));
3132 }
3133 }
3134 TableWidget t(tables[i]);
3135 // Insert the column containing the ID
3136 t.addColumn(0,0,"hidden:id");
3137 // Hide columns
3138 for (int i = 0; i < t.columnCount(); i++) {
3139 String name;
3140 t.getHeaderText(i,name,false);
3141 if (name.startsWith("hidden:"))
3142 t.table()->setColumnHidden(i,true);
3143 }
3144 // Connect signals
3145 QtClient::connectObjects(t.table(),SIGNAL(cellDoubleClicked(int,int)),
3146 this,SLOT(doubleClick()));
3147 #if 0
3148 // This would generate action() twice since QT will signal both cell and
3149 // table item double click
3150 QtClient::connectObjects(t.table(),SIGNAL(itemDoubleClicked(QTableWidgetItem*)),
3151 this,SLOT(doubleClick()));
3152 #endif
3153 String noSel;
3154 getProperty(t.name(),"dynamicNoItemSelChanged",noSel);
3155 if (!noSel.toBoolean())
3156 QtClient::connectObjects(t.table(),SIGNAL(itemSelectionChanged()),
3157 this,SLOT(selectionChanged()));
3158 // Optionally connect cell clicked
3159 // This is done when we want to generate a select() or action() from cell clicked
3160 String cellClicked;
3161 getProperty(t.name(),"dynamicCellClicked",cellClicked);
3162 if (cellClicked) {
3163 if (cellClicked == "selectionChanged")
3164 QtClient::connectObjects(t.table(),SIGNAL(cellClicked(int,int)),
3165 this,SLOT(selectionChanged()));
3166 else if (cellClicked == "doubleClick")
3167 QtClient::connectObjects(t.table(),SIGNAL(cellClicked(int,int)),
3168 this,SLOT(doubleClick()));
3169 }
3170 }
3171
3172 // Restore saved children properties
3173 if (sect) {
3174 unsigned int n = sect->length();
3175 for (unsigned int i = 0; i < n; i++) {
3176 NamedString* ns = sect->getParam(i);
3177 if (!ns)
3178 continue;
3179 String prop(ns->name());
3180 if (!prop.startSkip("property:",false))
3181 continue;
3182 int pos = prop.find(":");
3183 if (pos > 0) {
3184 String wName = prop.substr(0,pos);
3185 String pName = prop.substr(pos + 1);
3186 DDebug(QtDriver::self(),DebugAll,
3187 "Window(%s) restoring property %s=%s for child '%s' [%p]",
3188 m_id.c_str(),pName.c_str(),ns->c_str(),wName.c_str(),this);
3189 setProperty(wName,pName,*ns);
3190 }
3191 }
3192 }
3193
3194 // Install event filter and apply dynamic properties
3195 QList<QObject*> w = qFindChildren<QObject*>(this);
3196 w.append(this);
3197 for (int i = 0; i < w.size(); i++) {
3198 QList<QByteArray> props = w[i]->dynamicPropertyNames();
3199 // Check for our dynamic properties
3200 int j = 0;
3201 for (j = 0; j < props.size(); j++)
3202 if (props[j].startsWith(s_yatePropPrefix))
3203 break;
3204 if (j == props.size())
3205 continue;
3206 // Add event hook to be used when a dynamic property changes
3207 w[i]->installEventFilter(this);
3208 // Fake dynamic property change to apply them
3209 for (j = 0; j < props.size(); j++) {
3210 if (!props[j].startsWith(s_yatePropPrefix))
3211 continue;
3212 QDynamicPropertyChangeEvent ev(props[j]);
3213 eventFilter(w[i],&ev);
3214 }
3215 }
3216
3217 qRegisterMetaType<QModelIndex>("QModelIndex");
3218 qRegisterMetaType<QTextCursor>("QTextCursor");
3219
3220 // Force window visibility change notification by changing the visibility flag
3221 // Some controls might need to be updated
3222 m_visible = !m_visible;
3223 if (m_visible) {
3224 // Disable _yate_destroyonhide property: avoid destroying the window now
3225 String tmp;
3226 getProperty(m_id,"_yate_destroyonhide",tmp);
3227 if (tmp)
3228 setProperty(m_id,"_yate_destroyonhide",String::boolText(false));
3229 hide();
3230 if (tmp)
3231 setProperty(m_id,"_yate_destroyonhide",tmp);
3232 }
3233 else
3234 show();
3235 }
3236
3237 // Mouse button pressed notification
mousePressEvent(QMouseEvent * event)3238 void QtWindow::mousePressEvent(QMouseEvent* event)
3239 {
3240 if (m_moving >= 0 && Qt::LeftButton == event->button() && isShownNormal()) {
3241 m_movePos = event->globalPos();
3242 m_moving = 1;
3243 }
3244 }
3245
3246 // Mouse button release notification
mouseReleaseEvent(QMouseEvent * event)3247 void QtWindow::mouseReleaseEvent(QMouseEvent* event)
3248 {
3249 if (m_moving >= 0 && Qt::LeftButton == event->button())
3250 m_moving = 0;
3251 }
3252
3253 // Move the window if the moving flag is set
mouseMoveEvent(QMouseEvent * event)3254 void QtWindow::mouseMoveEvent(QMouseEvent* event)
3255 {
3256 if (m_moving <= 0 || Qt::LeftButton != event->buttons() || !isShownNormal())
3257 return;
3258 int cx = event->globalPos().x() - m_movePos.x();
3259 int cy = event->globalPos().y() - m_movePos.y();
3260 if (cx || cy) {
3261 m_movePos = event->globalPos();
3262 QWidget::move(x() + cx,y() + cy);
3263 }
3264 }
3265
3266 // Handle context menu events. Return true if handled
handleContextMenuEvent(QContextMenuEvent * event,QObject * obj)3267 bool QtWindow::handleContextMenuEvent(QContextMenuEvent* event, QObject* obj)
3268 {
3269 if (!(event && obj))
3270 return false;
3271 String mname;
3272 QtClient::getProperty(obj,s_propContextMenu,mname);
3273 XDebug(ClientDriver::self(),DebugAll,
3274 "Window(%s) handleContextMenuEvent() obj=%s menu=%s [%p]",
3275 m_id.c_str(),YQT_OBJECT_NAME(obj),mname.c_str(),this);
3276 QMenu* m = mname ? qFindChild<QMenu*>(this,QtClient::setUtf8(mname)) : 0;
3277 if (m)
3278 m->exec(event->globalPos());
3279 return m != 0;
3280 }
3281
3282
3283 /*
3284 * QtDialog
3285 */
3286 // Destructor. Notify the client if not exiting
~QtDialog()3287 QtDialog::~QtDialog()
3288 {
3289 QtWindow* w = parentWindow();
3290 if (w && m_notifyOnClose && Client::valid())
3291 QtClient::self()->action(w,buildActionName(m_notifyOnClose,m_notifyOnClose));
3292 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) QtDialog(%s) destroyed [%p]",
3293 w ? w->id().c_str() : "",YQT_OBJECT_NAME(this),w);
3294 }
3295
3296 // Initialize dialog. Load the widget.
3297 // Connect non checkable actions to own slot.
3298 // Connect checkable actions/buttons to parent window's slot
3299 // Display the dialog on success
show(const String & name,const String & title,const String & alias,const NamedList * params)3300 bool QtDialog::show(const String& name, const String& title, const String& alias,
3301 const NamedList* params)
3302 {
3303 QtWindow* w = parentWindow();
3304 if (!w)
3305 return false;
3306 QWidget* widget = QtWindow::loadUI(Client::s_skinPath + s_cfg.getValue(name,"description"),this,name);
3307 if (!widget)
3308 return false;
3309 QtClient::getProperty(widget,"_yate_notifyonclose",m_notifyOnClose);
3310 setObjectName(QtClient::setUtf8(alias ? alias : name));
3311 setMinimumSize(widget->minimumSize().width(),widget->minimumSize().height());
3312 setMaximumSize(widget->maximumSize().width(),widget->maximumSize().height());
3313 resize(widget->width(),widget->height());
3314 QtClient::setWidget(this,widget);
3315 if (title)
3316 setWindowTitle(QtClient::setUtf8(title));
3317 else if (widget->windowTitle().length())
3318 setWindowTitle(widget->windowTitle());
3319 else
3320 setWindowTitle(w->windowTitle());
3321 // Connect abstract buttons (check boxes and radio/push/tool buttons) signals
3322 QList<QAbstractButton*> buttons = qFindChildren<QAbstractButton*>(widget);
3323 for(int i = 0; i < buttons.size(); i++) {
3324 if (!QtClient::autoConnect(buttons[i]))
3325 continue;
3326 if (!buttons[i]->isCheckable())
3327 QtClient::connectObjects(buttons[i],SIGNAL(clicked()),this,SLOT(action()));
3328 else
3329 QtClient::connectObjects(buttons[i],SIGNAL(toggled(bool)),w,SLOT(toggled(bool)));
3330 }
3331 // Connect actions' signal
3332 QList<QAction*> actions = qFindChildren<QAction*>(widget);
3333 for (int i = 0; i < actions.size(); i++) {
3334 if (!QtClient::autoConnect(actions[i]))
3335 continue;
3336 if (!actions[i]->isCheckable())
3337 QtClient::connectObjects(actions[i],SIGNAL(triggered()),this,SLOT(action()));
3338 else
3339 QtClient::connectObjects(actions[i],SIGNAL(toggled(bool)),w,SLOT(toggled(bool)));
3340 }
3341 String* flags = 0;
3342 String tmp;
3343 QtClient::getProperty(widget,s_propWindowFlags,tmp);
3344 if (tmp)
3345 flags = &tmp;
3346 if (params) {
3347 if (!flags)
3348 flags = params->getParam(s_propWindowFlags);
3349 m_closable = params->getBoolValue(YSTRING("closable"),"true");
3350 w->setParams(*params);
3351 }
3352 if (flags)
3353 QtClient::applyWindowFlags(this,*flags);
3354 setWindowModality(Qt::WindowModal);
3355 QDialog::show();
3356 return true;
3357 }
3358
3359 // Notify client
action()3360 void QtDialog::action()
3361 {
3362 QtWindow* w = parentWindow();
3363 if (!w)
3364 return;
3365 DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) dialog action '%s' [%p]",
3366 w->id().c_str(),YQT_OBJECT_NAME(sender()),w);
3367 if (!QtClient::self() || QtClient::changing())
3368 return;
3369 String name;
3370 QtClient::getIdentity(sender(),name);
3371 if (name && QtClient::self()->action(w,buildActionName(name,name)))
3372 deleteLater();
3373 }
3374
3375 // Delete the dialog
closeEvent(QCloseEvent * event)3376 void QtDialog::closeEvent(QCloseEvent* event)
3377 {
3378 if (m_closable) {
3379 QDialog::closeEvent(event);
3380 deleteLater();
3381 }
3382 else
3383 event->ignore();
3384 }
3385
3386 // Destroy the dialog
reject()3387 void QtDialog::reject()
3388 {
3389 if (!m_closable)
3390 return;
3391 QDialog::reject();
3392 deleteLater();
3393 }
3394
3395
3396 /**
3397 * QtClient
3398 */
QtClient()3399 QtClient::QtClient()
3400 : Client("Qt Client")
3401 {
3402 m_oneThread = Engine::config().getBoolValue("client","onethread",true);
3403
3404 s_save = Engine::configFile("qt4client",true);
3405 s_save.load();
3406 // Fill QT styles
3407 s_qtStyles.addParam("IaOraKde","iaorakde");
3408 s_qtStyles.addParam("QWindowsStyle","windows");
3409 s_qtStyles.addParam("QMacStyle","mac");
3410 s_qtStyles.addParam("QMotifStyle","motif");
3411 s_qtStyles.addParam("QCDEStyle","cde");
3412 s_qtStyles.addParam("QWindowsXPStyle","windowsxp");
3413 s_qtStyles.addParam("QCleanlooksStyle","cleanlooks");
3414 s_qtStyles.addParam("QPlastiqueStyle","plastique");
3415 s_qtStyles.addParam("QGtkStyle","gtk");
3416 s_qtStyles.addParam("IaOraQt","iaoraqt");
3417 s_qtStyles.addParam("OxygenStyle","oxygen");
3418 s_qtStyles.addParam("PhaseStyle","phase");
3419 }
3420
~QtClient()3421 QtClient::~QtClient()
3422 {
3423 }
3424
cleanup()3425 void QtClient::cleanup()
3426 {
3427 Client::cleanup();
3428 m_events.clear();
3429 Client::save(s_save);
3430 QtWindow::clearUICache();
3431 m_app->quit();
3432 if (!m_app->startingUp())
3433 delete m_app;
3434 }
3435
run()3436 void QtClient::run()
3437 {
3438 const char* style = Engine::config().getValue("client","style");
3439 if (style && !QApplication::setStyle(QString::fromUtf8(style)))
3440 Debug(ClientDriver::self(),DebugWarn,"Could not set Qt style '%s'",style);
3441 int argc = 0;
3442 char* argv = 0;
3443 m_app = new QApplication(argc,&argv);
3444 m_app->setQuitOnLastWindowClosed(false);
3445 updateAppStyleSheet();
3446 String imgRead;
3447 QList<QByteArray> imgs = QImageReader::supportedImageFormats();
3448 for (int i = 0; i < imgs.size(); i++)
3449 imgRead.append(imgs[i].constData(),",");
3450 imgRead = "read image formats '" + imgRead + "'";
3451 Debug(ClientDriver::self(),DebugInfo,"QT client start running (version=%s) %s",
3452 qVersion(),imgRead.c_str());
3453 if (!QSound::isAvailable())
3454 Debug(ClientDriver::self(),DebugWarn,"QT sounds are not available");
3455 // Create events proxy
3456 m_events.append(new QtEventProxy(QtEventProxy::Timer));
3457 m_events.append(new QtEventProxy(QtEventProxy::AllHidden,m_app));
3458 if (Engine::exiting())
3459 return;
3460 Client::run();
3461 }
3462
main()3463 void QtClient::main()
3464 {
3465 if (!Engine::exiting())
3466 m_app->exec();
3467 }
3468
lock()3469 void QtClient::lock()
3470 {}
3471
unlock()3472 void QtClient::unlock()
3473 {}
3474
allHidden()3475 void QtClient::allHidden()
3476 {
3477 Debug(QtDriver::self(),DebugInfo,"QtClient::allHiden() counter=%d",s_allHiddenQuit);
3478 if (s_allHiddenQuit > 0)
3479 return;
3480 quit();
3481 }
3482
createWindow(const String & name,const String & alias)3483 bool QtClient::createWindow(const String& name, const String& alias)
3484 {
3485 String parent = s_cfg.getValue(name,"parent");
3486 QtWindow* parentWnd = 0;
3487 if (!TelEngine::null(parent)) {
3488 ObjList* o = m_windows.find(parent);
3489 if (o)
3490 parentWnd = YOBJECT(QtWindow,o->get());
3491 }
3492 QtWindow* w = new QtWindow(name,s_skinPath + s_cfg.getValue(name,"description"),alias,parentWnd);
3493 if (w) {
3494 Debug(QtDriver::self(),DebugAll,"Created window name=%s alias=%s with parent=(%s [%p]) (%p)",
3495 name.c_str(),alias.c_str(),parent.c_str(),parentWnd,w);
3496 // Remove the old window
3497 ObjList* o = m_windows.find(w->id());
3498 if (o)
3499 Client::self()->closeWindow(w->id(),false);
3500 w->populate();
3501 m_windows.append(w);
3502 return true;
3503 }
3504 else
3505 Debug(QtDriver::self(),DebugCrit,"Could not create window name=%s alias=%s",
3506 name.c_str(),alias.c_str());
3507 return false;
3508 }
3509
loadWindows(const char * file)3510 void QtClient::loadWindows(const char* file)
3511 {
3512 if (!file)
3513 s_cfg = s_skinPath + "qt4client.rc";
3514 else
3515 s_cfg = String(file);
3516 s_cfg.load();
3517 Debug(QtDriver::self(),DebugInfo,"Loading Windows");
3518 unsigned int n = s_cfg.sections();
3519 for (unsigned int i = 0; i < n; i++) {
3520 NamedList* l = s_cfg.getSection(i);
3521 if (l && l->getBoolValue(YSTRING("enabled"),true))
3522 createWindow(*l);
3523 }
3524 }
3525
isUIThread()3526 bool QtClient::isUIThread()
3527 {
3528 return (QApplication::instance() && QApplication::instance()->thread() == QThread::currentThread());
3529 }
3530
3531 // Open a file open dialog window
3532 // Parameters that can be specified include 'caption',
3533 // 'dir', 'filter', 'selectedfilter', 'confirmoverwrite', 'choosedir'
chooseFile(Window * parent,NamedList & params)3534 bool QtClient::chooseFile(Window* parent, NamedList& params)
3535 {
3536 QtWindow* wnd = static_cast<QtWindow*>(parent);
3537 QFileDialog* dlg = new QFileDialog(wnd,setUtf8(params.getValue(YSTRING("caption"))),
3538 setUtf8(params.getValue(YSTRING("dir"))));
3539
3540 if (wnd)
3541 dlg->setWindowIcon(wnd->windowIcon());
3542
3543 // Connect signals
3544 String* action = params.getParam(YSTRING("action"));
3545 if (wnd && !null(action)) {
3546 dlg->setObjectName(setUtf8(*action));
3547 QtClient::connectObjects(dlg,SIGNAL(accepted()),wnd,SLOT(chooseFileAccepted()));
3548 QtClient::connectObjects(dlg,SIGNAL(rejected()),wnd,SLOT(chooseFileRejected()));
3549 }
3550
3551 // Destroy it when closed
3552 dlg->setAttribute(Qt::WA_DeleteOnClose);
3553 // This dialog should always stay on top
3554 dlg->setWindowFlags(dlg->windowFlags() | Qt::WindowStaysOnTopHint);
3555
3556 if (params.getBoolValue(YSTRING("modal"),true))
3557 dlg->setWindowModality(Qt::WindowModal);
3558
3559 // Filters
3560 NamedString* f = params.getParam(YSTRING("filters"));
3561 if (f) {
3562 QStringList filters;
3563 ObjList* obj = f->split('|',false);
3564 for (ObjList* o = obj->skipNull(); o; o = o->skipNext())
3565 filters.append(QtClient::setUtf8(o->get()->toString()));
3566 TelEngine::destruct(obj);
3567 dlg->setFilters(filters);
3568 }
3569 QString flt = QtClient::setUtf8(params.getValue(YSTRING("selectedfilter")));
3570 if (flt.length())
3571 dlg->selectFilter(flt);
3572
3573 if (params.getBoolValue(YSTRING("save")))
3574 dlg->setAcceptMode(QFileDialog::AcceptSave);
3575 else
3576 dlg->setAcceptMode(QFileDialog::AcceptOpen);
3577
3578 // Choose options
3579 if (params.getBoolValue(YSTRING("choosefile"),true)) {
3580 if (params.getBoolValue(YSTRING("chooseanyfile")))
3581 dlg->setFileMode(QFileDialog::AnyFile);
3582 else if (params.getBoolValue(YSTRING("multiplefiles")))
3583 dlg->setFileMode(QFileDialog::ExistingFiles);
3584 else
3585 dlg->setFileMode(QFileDialog::ExistingFile);
3586 }
3587 else
3588 dlg->setFileMode(QFileDialog::DirectoryOnly);
3589
3590 dlg->selectFile(QtClient::setUtf8(params.getValue(YSTRING("selectedfile"))));
3591
3592 dlg->setVisible(true);
3593 return true;
3594 }
3595
action(Window * wnd,const String & name,NamedList * params)3596 bool QtClient::action(Window* wnd, const String& name, NamedList* params)
3597 {
3598 String tmp = name;
3599 if (tmp.startSkip("openurl:",false))
3600 return openUrl(tmp);
3601 return Client::action(wnd,name,params);
3602 }
3603
3604 // Create a sound object. Append it to the global list
createSound(const char * name,const char * file,const char * device)3605 bool QtClient::createSound(const char* name, const char* file, const char* device)
3606 {
3607 if (!(QSound::isAvailable() && name && *name && file && *file))
3608 return false;
3609 Lock lock(ClientSound::s_soundsMutex);
3610 if (ClientSound::s_sounds.find(name))
3611 return false;
3612 ClientSound::s_sounds.append(new QtSound(name,file,device));
3613 DDebug(ClientDriver::self(),DebugAll,"Added sound=%s file=%s device=%s",
3614 name,file,device);
3615 return true;
3616 }
3617
3618 // Build a date/time string from UTC time
formatDateTime(String & dest,unsigned int secs,const char * format,bool utc)3619 bool QtClient::formatDateTime(String& dest, unsigned int secs,
3620 const char* format, bool utc)
3621 {
3622 if (!(format && *format))
3623 return false;
3624 QtClient::getUtf8(dest,formatDateTime(secs,format,utc));
3625 return true;
3626 }
3627
3628 // Build a date/time QT string from UTC time
formatDateTime(unsigned int secs,const char * format,bool utc)3629 QString QtClient::formatDateTime(unsigned int secs, const char* format, bool utc)
3630 {
3631 QDateTime time;
3632 if (utc)
3633 time.setTimeSpec(Qt::UTC);
3634 time.setTime_t(secs);
3635 return time.toString(format);
3636 }
3637
3638 // Retrieve an object's QtWindow parent
parentWindow(QObject * obj)3639 QtWindow* QtClient::parentWindow(QObject* obj)
3640 {
3641 for (; obj; obj = obj->parent()) {
3642 QtWindow* w = qobject_cast<QtWindow*>(obj);
3643 if (w)
3644 return w;
3645 }
3646 return 0;
3647 }
3648
3649 // Save an object's property into parent window's section. Clear it on failure
saveProperty(QObject * obj,const String & prop,QtWindow * owner)3650 bool QtClient::saveProperty(QObject* obj, const String& prop, QtWindow* owner)
3651 {
3652 if (!obj)
3653 return false;
3654 if (!owner)
3655 owner = parentWindow(obj);
3656 if (!owner)
3657 return false;
3658 String value;
3659 bool ok = getProperty(obj,prop,value);
3660 String pName;
3661 pName << "property:" << YQT_OBJECT_NAME(obj) << ":" << prop;
3662 if (ok)
3663 s_save.setValue(owner->id(),pName,value);
3664 else
3665 s_save.clearKey(owner->id(),pName);
3666 return ok;
3667 }
3668
3669 // Set or an object's property
setProperty(QObject * obj,const char * name,const String & value)3670 bool QtClient::setProperty(QObject* obj, const char* name, const String& value)
3671 {
3672 if (!(obj && name && *name))
3673 return false;
3674 QVariant var = obj->property(name);
3675 const char* err = 0;
3676 bool ok = false;
3677 switch (var.type()) {
3678 case QVariant::String:
3679 ok = obj->setProperty(name,QVariant(QtClient::setUtf8(value)));
3680 break;
3681 case QVariant::Bool:
3682 ok = obj->setProperty(name,QVariant(value.toBoolean()));
3683 break;
3684 case QVariant::Int:
3685 ok = obj->setProperty(name,QVariant(value.toInteger()));
3686 break;
3687 case QVariant::UInt:
3688 ok = obj->setProperty(name,QVariant((unsigned int)value.toInteger()));
3689 break;
3690 case QVariant::Icon:
3691 ok = obj->setProperty(name,QVariant(QIcon(QtClient::setUtf8(value))));
3692 break;
3693 case QVariant::Pixmap:
3694 ok = obj->setProperty(name,QVariant(QPixmap(QtClient::setUtf8(value))));
3695 break;
3696 case QVariant::Double:
3697 ok = obj->setProperty(name,QVariant(value.toDouble()));
3698 break;
3699 case QVariant::KeySequence:
3700 ok = obj->setProperty(name,QVariant(QtClient::setUtf8(value)));
3701 break;
3702 case QVariant::StringList:
3703 {
3704 QStringList qList;
3705 if (value)
3706 qList.append(setUtf8(value));
3707 ok = obj->setProperty(name,QVariant(qList));
3708 }
3709 break;
3710 case QVariant::Invalid:
3711 err = "no such property";
3712 break;
3713 default:
3714 err = "unsupported type";
3715 }
3716 YIGNORE(err);
3717 if (ok)
3718 DDebug(ClientDriver::self(),DebugAll,"Set property %s=%s for object '%s'",
3719 name,value.c_str(),YQT_OBJECT_NAME(obj));
3720 else
3721 DDebug(ClientDriver::self(),DebugNote,
3722 "Failed to set %s=%s (type=%s) for object '%s': %s",
3723 name,value.c_str(),var.typeName(),YQT_OBJECT_NAME(obj),err);
3724 return ok;
3725 }
3726
3727 // Get an object's property
getProperty(QObject * obj,const char * name,String & value)3728 bool QtClient::getProperty(QObject* obj, const char* name, String& value)
3729 {
3730 if (!(obj && name && *name))
3731 return false;
3732 QVariant var = obj->property(name);
3733 if (var.type() == QVariant::StringList) {
3734 NamedList* l = static_cast<NamedList*>(value.getObject(YATOM("NamedList")));
3735 if (l)
3736 copyParams(*l,var.toStringList());
3737 else
3738 getUtf8(value,var.toStringList().join(","));
3739 DDebug(ClientDriver::self(),DebugAll,"Got list property %s for object '%s'",
3740 name,YQT_OBJECT_NAME(obj));
3741 return true;
3742 }
3743 if (var.canConvert(QVariant::String)) {
3744 QtClient::getUtf8(value,var.toString());
3745 DDebug(ClientDriver::self(),DebugAll,"Got property %s=%s for object '%s'",
3746 name,value.c_str(),YQT_OBJECT_NAME(obj));
3747 return true;
3748 }
3749 DDebug(ClientDriver::self(),DebugNote,
3750 "Failed to get property '%s' (type=%s) for object '%s': %s",
3751 name,var.typeName(),YQT_OBJECT_NAME(obj),
3752 ((var.type() == QVariant::Invalid) ? "no such property" : "unsupported type"));
3753 return false;
3754 }
3755
3756 // Copy a string list to a list of parameters
copyParams(NamedList & dest,const QStringList & src)3757 void QtClient::copyParams(NamedList& dest, const QStringList& src)
3758 {
3759 for (int i = 0; i < src.size(); i++) {
3760 if (!src[i].length())
3761 continue;
3762 int pos = src[i].indexOf('=');
3763 String name;
3764 if (pos >= 0) {
3765 getUtf8(name,src[i].left(pos));
3766 getUtf8(dest,name,src[i].right(src[i].length() - pos - 1));
3767 }
3768 else {
3769 getUtf8(name,src[i]);
3770 dest.addParam(name,"");
3771 }
3772 }
3773 }
3774
3775 // Copy a list of parameters to string list
copyParams(QStringList & dest,const NamedList & src)3776 void QtClient::copyParams(QStringList& dest, const NamedList& src)
3777 {
3778 unsigned int n = src.length();
3779 for (unsigned int i = 0; i < n; i++) {
3780 NamedString* ns = src.getParam(i);
3781 if (ns)
3782 dest.append(setUtf8(ns->name() + "=" + *ns));
3783 }
3784 }
3785
3786 // Build QObject properties from list
buildProps(QObject * obj,const String & props)3787 void QtClient::buildProps(QObject* obj, const String& props)
3788 {
3789 if (!(obj && props))
3790 return;
3791 ObjList* list = props.split(',',false);
3792 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
3793 String* s = static_cast<String*>(o->get());
3794 int pos = s->find('=');
3795 if (pos < 1)
3796 continue;
3797 String ptype = s->substr(pos + 1);
3798 QVariant::Type t = (QVariant::Type)lookup(ptype,s_qVarType,QVariant::Invalid);
3799 if (t == QVariant::Invalid) {
3800 Debug(ClientDriver::self(),DebugStub,
3801 "QtClient::buildProps() unhandled type '%s'",ptype.c_str());
3802 continue;
3803 }
3804 String pname = s->substr(0,pos);
3805 QVariant existing = obj->property(pname);
3806 if (existing.type() == QVariant::Invalid) {
3807 obj->setProperty(pname,QVariant(t));
3808 continue;
3809 }
3810 Debug(ClientDriver::self(),DebugNote,
3811 "Can't create property '%s' type=%s for object (%p,%s): already exists",
3812 pname.c_str(),ptype.c_str(),obj,YQT_OBJECT_NAME(obj));
3813 }
3814 TelEngine::destruct(list);
3815 }
3816
3817 // Build custom UI widgets from frames owned by a widget
buildFrameUiWidgets(QWidget * parent)3818 void QtClient::buildFrameUiWidgets(QWidget* parent)
3819 {
3820 if (!parent)
3821 return;
3822 QList<QFrame*> frm = qFindChildren<QFrame*>(parent);
3823 for (int i = 0; i < frm.size(); i++) {
3824 if (!getBoolProperty(frm[i],"_yate_uiwidget"))
3825 continue;
3826 String name;
3827 String type;
3828 getProperty(frm[i],"_yate_uiwidget_name",name);
3829 getProperty(frm[i],"_yate_uiwidget_class",type);
3830 if (!(name && type))
3831 continue;
3832 NamedList params("");
3833 getProperty(frm[i],"_yate_uiwidget_params",params);
3834 QtWindow* w = static_cast<QtWindow*>(parent->window());
3835 if (w)
3836 params.setParam("parentwindow",w->id());
3837 getUtf8(params,"parentwidget",frm[i]->objectName(),true);
3838 QObject* obj = (QObject*)UIFactory::build(type,name,¶ms);
3839 if (!obj)
3840 continue;
3841 QWidget* wid = qobject_cast<QWidget*>(obj);
3842 if (wid)
3843 QtClient::setWidget(frm[i],wid);
3844 else {
3845 obj->setParent(frm[i]);
3846 QtCustomObject* customObj = qobject_cast<QtCustomObject*>(obj);
3847 if (customObj)
3848 customObj->parentChanged();
3849 }
3850 }
3851 }
3852
3853 // Associate actions to buttons with '_yate_setaction' property set
setAction(QWidget * parent)3854 void QtClient::setAction(QWidget* parent)
3855 {
3856 if (!parent)
3857 return;
3858 QList<QToolButton*> tb = qFindChildren<QToolButton*>(parent);
3859 for (int i = 0; i < tb.size(); i++) {
3860 QVariant var = tb[i]->property("_yate_setaction");
3861 if (var.toString().isEmpty())
3862 continue;
3863 QAction* a = qFindChild<QAction*>(parent,var.toString());
3864 if (a)
3865 tb[i]->setDefaultAction(a);
3866 }
3867 }
3868
3869 // Build a menu object from a list of parameters
buildMenu(const NamedList & params,const char * text,QObject * receiver,const char * triggerSlot,const char * toggleSlot,QWidget * parent,const char * aboutToShowSlot)3870 QMenu* QtClient::buildMenu(const NamedList& params, const char* text, QObject* receiver,
3871 const char* triggerSlot, const char* toggleSlot, QWidget* parent,
3872 const char* aboutToShowSlot)
3873 {
3874 QMenu* menu = 0;
3875 unsigned int n = params.length();
3876 for (unsigned int i = 0; i < n; i++) {
3877 NamedString* param = params.getParam(i);
3878 if (!(param && param->name().startsWith("item:")))
3879 continue;
3880
3881 if (!menu)
3882 menu = new QMenu(setUtf8(text),parent);
3883
3884 NamedList* p = YOBJECT(NamedList,param);
3885 if (p) {
3886 QMenu* subMenu = buildMenu(*p,*param ? param->c_str() : p->getValue(YSTRING("title"),*p),
3887 receiver,triggerSlot,toggleSlot,menu);
3888 if (subMenu)
3889 menu->addMenu(subMenu);
3890 continue;
3891 }
3892 String name = param->name().substr(5);
3893 if (*param) {
3894 QAction* a = menu->addAction(QtClient::setUtf8(*param));
3895 a->setObjectName(QtClient::setUtf8(name));
3896 a->setParent(menu);
3897 setImage(a,params["image:" + name]);
3898 }
3899 else if (!name)
3900 menu->addSeparator()->setParent(menu);
3901 else {
3902 // Check if the action is already there
3903 QAction* a = 0;
3904 if (parent && parent->window())
3905 a = qFindChild<QAction*>(parent->window(),QtClient::setUtf8(name));
3906 if (a)
3907 menu->addAction(a);
3908 else
3909 Debug(ClientDriver::self(),DebugNote,
3910 "buildMenu(%s) action '%s' not found",params.c_str(),name.c_str());
3911 }
3912 }
3913
3914 if (!menu)
3915 return 0;
3916
3917 // Set name
3918 menu->setObjectName(setUtf8(params));
3919 setImage(menu,params["image:" + params]);
3920 // Apply properties
3921 // Format: property:object_name:property_name=value
3922 if (parent)
3923 for (unsigned int i = 0; i < n; i++) {
3924 NamedString* param = params.getParam(i);
3925 if (!(param && param->name().startsWith("property:")))
3926 continue;
3927 int pos = param->name().find(':',9);
3928 if (pos < 9)
3929 continue;
3930 QObject* obj = qFindChild<QObject*>(parent,setUtf8(param->name().substr(9,pos - 9)));
3931 if (obj)
3932 setProperty(obj,param->name().substr(pos + 1),*param);
3933 }
3934 // Connect signals (direct children only: actions from sub-menus are already connected)
3935 QList<QAction*> list = qFindChildren<QAction*>(menu);
3936 for (int i = 0; i < list.size(); i++) {
3937 if (list[i]->isSeparator() || list[i]->parent() != menu)
3938 continue;
3939 if (list[i]->isCheckable())
3940 QtClient::connectObjects(list[i],SIGNAL(toggled(bool)),receiver,toggleSlot);
3941 else
3942 QtClient::connectObjects(list[i],SIGNAL(triggered()),receiver,triggerSlot);
3943 }
3944 if (!TelEngine::null(aboutToShowSlot))
3945 QtClient::connectObjects(menu,SIGNAL(aboutToShow()),receiver,aboutToShowSlot);
3946
3947 return menu;
3948 }
3949
3950 // Wrapper for QObject::connect() used to put a debug mesage on failure
connectObjects(QObject * sender,const char * signal,QObject * receiver,const char * slot)3951 bool QtClient::connectObjects(QObject* sender, const char* signal,
3952 QObject* receiver, const char* slot)
3953 {
3954 if (!(sender && signal && *signal && receiver && slot && *slot))
3955 return false;
3956 bool ok = QObject::connect(sender,signal,receiver,slot);
3957 if (ok)
3958 DDebug(QtDriver::self(),DebugAll,
3959 "Connected sender=%s signal=%s to receiver=%s slot=%s",
3960 YQT_OBJECT_NAME(sender),signal,YQT_OBJECT_NAME(receiver),slot);
3961 else
3962 Debug(QtDriver::self(),DebugWarn,
3963 "Failed to connect sender=%s signal=%s to receiver=%s slot=%s",
3964 YQT_OBJECT_NAME(sender),signal,YQT_OBJECT_NAME(receiver),slot);
3965 return ok;
3966 }
3967
3968 // Insert a widget into another one replacing any existing children
setWidget(QWidget * parent,QWidget * child)3969 bool QtClient::setWidget(QWidget* parent, QWidget* child)
3970 {
3971 if (!(parent && child))
3972 return false;
3973 QVBoxLayout* layout = new QVBoxLayout;
3974 layout->setSpacing(0);
3975 String margins;
3976 QtClient::getProperty(parent,"_yate_layout_margins",margins);
3977 if (!margins)
3978 layout->setContentsMargins(0,0,0,0);
3979 else {
3980 QList<int> m = buildIntList(margins,4);
3981 layout->setContentsMargins(m[0],m[1],m[2],m[3]);
3982 }
3983 layout->addWidget(child);
3984 QLayout* l = parent->layout();
3985 if (l)
3986 delete l;
3987 parent->setLayout(layout);
3988 return true;
3989 }
3990
3991 // Set an object's image property from image file
setImage(QObject * obj,const String & img,bool fit)3992 bool QtClient::setImage(QObject* obj, const String& img, bool fit)
3993 {
3994 if (!obj)
3995 return false;
3996 QPixmap pixmap(setUtf8(img));
3997 return setImage(obj,pixmap,fit);
3998 }
3999
4000 // Set an object's image property from raw data.
setImage(QObject * obj,const DataBlock & data,const String & format,bool fit)4001 bool QtClient::setImage(QObject* obj, const DataBlock& data, const String& format, bool fit)
4002 {
4003 if (!obj)
4004 return false;
4005 QPixmap pixmap;
4006 String f = format;
4007 f.startSkip("image/",false);
4008 if (!pixmap.loadFromData((const uchar*)data.data(),data.length(),f))
4009 return false;
4010 return setImage(obj,pixmap,fit);
4011 }
4012
4013 // Set an object's image property from QPixmap
setImage(QObject * obj,const QPixmap & img,bool fit)4014 bool QtClient::setImage(QObject* obj, const QPixmap& img, bool fit)
4015 {
4016 if (!obj)
4017 return false;
4018 if (obj->isWidgetType()) {
4019 QLabel* l = qobject_cast<QLabel*>(obj);
4020 if (l) {
4021 if (fit && !l->hasScaledContents() &&
4022 (img.width() > l->width() || img.height() > l->height())) {
4023 QPixmap tmp;
4024 if (l->width() <= l->height())
4025 tmp = img.scaledToWidth(l->width());
4026 else
4027 tmp = img.scaledToHeight(l->height());
4028 l->setPixmap(tmp);
4029 }
4030 else
4031 l->setPixmap(img);
4032 }
4033 else {
4034 QAbstractButton* b = qobject_cast<QAbstractButton*>(obj);
4035 if (b)
4036 b->setIcon(img);
4037 else {
4038 QMenu* m = qobject_cast<QMenu*>(obj);
4039 if (m)
4040 m->setIcon(img);
4041 else
4042 return false;
4043 }
4044 }
4045 return true;
4046 }
4047 QAction* a = qobject_cast<QAction*>(obj);
4048 if (a) {
4049 a->setIcon(img);
4050 return true;
4051 }
4052 return false;
4053 }
4054
4055 // Update a toggable object's image from properties
updateToggleImage(QObject * obj)4056 void QtClient::updateToggleImage(QObject* obj)
4057 {
4058 QtWidget w(obj);
4059 QAbstractButton* b = 0;
4060 if (w.inherits(QtWidget::AbstractButton))
4061 b = w.abstractButton();
4062 if (!(b && b->isCheckable()))
4063 return;
4064 String icon;
4065 bool set = false;
4066 if (b->isChecked())
4067 set = QtClient::getProperty(w,"_yate_pressed_icon",icon);
4068 else
4069 set = QtClient::getProperty(w,"_yate_normal_icon",icon);
4070 if (set)
4071 QtClient::setImage(obj,Client::s_skinPath + icon);
4072 }
4073
4074 // Update an object's image from properties on mouse events
updateImageFromMouse(QObject * obj,bool inOut,bool on)4075 void QtClient::updateImageFromMouse(QObject* obj, bool inOut, bool on)
4076 {
4077 QtWidget w(obj);
4078 QAbstractButton* b = 0;
4079 if (w.inherits(QtWidget::AbstractButton))
4080 b = w.abstractButton();
4081 if (!b)
4082 return;
4083 if (!b->isEnabled())
4084 return;
4085 String icon;
4086 bool set = false;
4087 if (inOut) {
4088 if (on)
4089 set = QtClient::getProperty(obj,"_yate_hover_icon",icon);
4090 else {
4091 if (b->isCheckable() && b->isChecked())
4092 set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
4093 set = set || QtClient::getProperty(obj,"_yate_normal_icon",icon);
4094 }
4095 }
4096 else {
4097 if (on) {
4098 if (!b->isCheckable())
4099 set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
4100 }
4101 else {
4102 set = QtClient::getProperty(obj,"_yate_hover_icon",icon);
4103 if (!set && b->isCheckable() && b->isChecked())
4104 set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
4105 set = set || QtClient::getProperty(obj,"_yate_normal_icon",icon);
4106 }
4107 }
4108 if (set)
4109 QtClient::setImage(obj,Client::s_skinPath + icon);
4110 }
4111
4112 // Process a key press event. Retrieve an action associated with the key
filterKeyEvent(QObject * obj,QKeyEvent * event,String & action,bool & filter,QObject * parent)4113 bool QtClient::filterKeyEvent(QObject* obj, QKeyEvent* event, String& action,
4114 bool& filter, QObject* parent)
4115 {
4116 static int mask = Qt::SHIFT | Qt::CTRL | Qt::ALT;
4117 if (!(obj && event))
4118 return false;
4119 // Try to match key and modifiers
4120 QKeySequence ks(event->key());
4121 String prop;
4122 getUtf8(prop,ks.toString());
4123 prop = "dynamicAction" + prop;
4124 // Get modifiers from property and check them against event
4125 QVariant v = obj->property(prop + "Modifiers");
4126 int tmp = 0;
4127 if (v.type() == QVariant::String) {
4128 QKeySequence ks(v.toString());
4129 for (unsigned int i = 0; i < ks.count(); i++)
4130 tmp |= ks[i];
4131 }
4132 if (tmp != (mask & event->modifiers()))
4133 return false;
4134 // We matched the key and modifiers
4135 // Set filter flag
4136 filter = getBoolProperty(obj,prop + "Filter");
4137 // Retrieve the action
4138 getProperty(obj,prop,action);
4139 if (!action)
4140 return true;
4141 if (!parent)
4142 return true;
4143 parent = qFindChild<QObject*>(parent,setUtf8(action));
4144 if (!parent)
4145 return true;
4146 // Avoid notifying a disabled action
4147 bool ok = true;
4148 if (parent->isWidgetType())
4149 ok = (qobject_cast<QWidget*>(parent))->isEnabled();
4150 else {
4151 QAction* a = qobject_cast<QAction*>(parent);
4152 ok = !a || a->isEnabled();
4153 }
4154 if (!ok)
4155 action.clear();
4156 return true;
4157 }
4158
4159 // Safely delete a QObject (reset its parent, calls it's deleteLater() method)
deleteLater(QObject * obj)4160 void QtClient::deleteLater(QObject* obj)
4161 {
4162 if (!obj)
4163 return;
4164 obj->disconnect();
4165 if (obj->isWidgetType())
4166 (static_cast<QWidget*>(obj))->setParent(0);
4167 else
4168 obj->setParent(0);
4169 obj->deleteLater();
4170 }
4171
4172 // Retrieve unavailable space position (if any) in the screen containing a given widget.
getScreenUnavailPos(QWidget * w,int & pos)4173 QDesktopWidget* QtClient::getScreenUnavailPos(QWidget* w, int& pos)
4174 {
4175 if (!w)
4176 return 0;
4177 QDesktopWidget* d = QApplication::desktop();
4178 if (!d)
4179 return 0;
4180 pos = PosNone;
4181 QRect rScreen = d->screenGeometry(w);
4182 QRect rClient = d->availableGeometry(w);
4183 int dx = rClient.x() - rScreen.x();
4184 if (dx > 0)
4185 pos |= PosLeft;
4186 int dy = rClient.y() - rScreen.y();
4187 if (dy > 0)
4188 pos |= PosTop;
4189 int dw = rScreen.width() - rClient.width();
4190 if (dw > 0 && (!dx || (dx > 0 && dw > dx)))
4191 pos |= PosRight;
4192 int dh = rScreen.height() - rClient.height();
4193 if (dh > 0 && (!dy || (dy > 0 && dh > dy)))
4194 pos |= PosBottom;
4195 return d;
4196 }
4197
4198 // Move a window to a specified position
moveWindow(QtWindow * w,int pos)4199 void QtClient::moveWindow(QtWindow* w, int pos)
4200 {
4201 if (!w)
4202 return;
4203 QDesktopWidget* d = QApplication::desktop();
4204 if (!d)
4205 return;
4206 QRect r = d->availableGeometry(w);
4207 int x = r.x();
4208 int y = r.y();
4209 QSize sz = w->frameSize();
4210 if (pos == CornerBottomRight) {
4211 if (r.width() > sz.width())
4212 x += r.width() - sz.width();
4213 if (r.height() > sz.height())
4214 y += r.height() - sz.height();
4215 }
4216 else if (pos == CornerTopRight) {
4217 if (r.width() > sz.width())
4218 x += r.width() - sz.width();
4219 }
4220 else if (pos == CornerBottomLeft) {
4221 if (r.height() > sz.height())
4222 y += r.height() - sz.height();
4223 }
4224 else if (pos != CornerTopLeft)
4225 return;
4226 w->move(x,y);
4227 }
4228
4229 // Build a QStringList from a list of strings
str2list(const String & str,char sep,bool emptyOk)4230 QStringList QtClient::str2list(const String& str, char sep, bool emptyOk)
4231 {
4232 QStringList l;
4233 if (!str)
4234 return l;
4235 ObjList* list = str.split(sep,emptyOk);
4236 for (ObjList* o = list->skipNull(); o; o = o->skipNext())
4237 l.append(setUtf8(static_cast<String*>(o->get())->c_str()));
4238 TelEngine::destruct(list);
4239 return l;
4240 }
4241
4242 // Split a string. Returns a list of int values
str2IntList(const String & str,int defVal,bool emptyOk)4243 QList<int> QtClient::str2IntList(const String& str, int defVal, bool emptyOk)
4244 {
4245 QList<int> list;
4246 ObjList* l = str.split(',',emptyOk);
4247 for (ObjList* o = l->skipNull(); o; o = o->skipNext())
4248 list.append(o->get()->toString().toInteger(defVal));
4249 TelEngine::destruct(l);
4250 return list;
4251 }
4252
4253 // Build a comma separated list of integers
intList2str(String & str,QList<int> list)4254 void QtClient::intList2str(String& str, QList<int> list)
4255 {
4256 for (int i = 0; i < list.size(); i++)
4257 str.append(String(list[i]),",");
4258 }
4259
4260 // Get sorting from string
str2sort(const String & str,int defVal)4261 int QtClient::str2sort(const String& str, int defVal)
4262 {
4263 return lookup(str,s_sorting,defVal);
4264 }
4265
4266 // Apply a comma separated list of window flags to a widget
applyWindowFlags(QWidget * w,const String & value)4267 void QtClient::applyWindowFlags(QWidget* w, const String& value)
4268 {
4269 if (!w)
4270 return;
4271 // Set window flags from enclosed widget:
4272 // custom window title/border/sysmenu config
4273 ObjList* f = value.split(',',false);
4274 int flags = Qt::CustomizeWindowHint | w->windowFlags();
4275 // Clear settable flags
4276 TokenDict* dict = s_windowFlags;
4277 for (int i = 0; dict[i].token; i++)
4278 flags &= ~dict[i].value;
4279 // Set flags
4280 for (ObjList* o = f->skipNull(); o; o = o->skipNext())
4281 flags |= lookup(o->get()->toString(),s_windowFlags,0);
4282 TelEngine::destruct(f);
4283 w->setWindowFlags((Qt::WindowFlags)flags);
4284 }
4285
4286 // Build a QT Alignment mask from a comma separated list of flags
str2align(const String & flags,int initVal)4287 int QtClient::str2align(const String& flags, int initVal)
4288 {
4289 ObjList* list = flags.split(',',false);
4290 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
4291 int val = ::lookup((static_cast<String*>(o->get()))->c_str(),s_qAlign);
4292 if (0 != (val & Qt::AlignHorizontal_Mask))
4293 initVal &= ~Qt::AlignHorizontal_Mask;
4294 if (0 != (val & Qt::AlignVertical_Mask))
4295 initVal &= ~Qt::AlignVertical_Mask;
4296 initVal |= val;
4297 }
4298 TelEngine::destruct(list);
4299 return initVal;
4300 }
4301
4302 // Retrieve QT selection mode from a string value
str2selmode(const String & value,QAbstractItemView::SelectionMode defVal)4303 QAbstractItemView::SelectionMode QtClient::str2selmode(const String& value,
4304 QAbstractItemView::SelectionMode defVal)
4305 {
4306 if (!value)
4307 return defVal;
4308 if (value == YSTRING("none"))
4309 return QAbstractItemView::NoSelection;
4310 if (value == YSTRING("single"))
4311 return QAbstractItemView::SingleSelection;
4312 if (value == YSTRING("multi"))
4313 return QAbstractItemView::MultiSelection;
4314 if (value == YSTRING("extended"))
4315 return QAbstractItemView::ExtendedSelection;
4316 if (value == YSTRING("contiguous"))
4317 return QAbstractItemView::ContiguousSelection;
4318 return defVal;
4319 }
4320
4321 // Retrieve QT edit triggers from a string value
str2editTriggers(const String & value,QAbstractItemView::EditTrigger defVal)4322 QAbstractItemView::EditTriggers QtClient::str2editTriggers(const String& value,
4323 QAbstractItemView::EditTrigger defVal)
4324 {
4325 return (QAbstractItemView::EditTriggers)Client::decodeFlags(s_qEditTriggers,value,defVal);
4326 }
4327
4328 // Send an event to an object's child
sendEvent(QEvent & e,QObject * parent,const QString & name)4329 bool QtClient::sendEvent(QEvent& e, QObject* parent, const QString& name)
4330 {
4331 if (!(parent && e.isAccepted()))
4332 return false;
4333 QObject* child = qFindChild<QObject*>(parent,name);
4334 if (!child)
4335 return false;
4336 e.setAccepted(false);
4337 bool ok = QCoreApplication::sendEvent(child,&e);
4338 if (!ok)
4339 e.setAccepted(true);
4340 return ok;
4341 }
4342
4343 // Retrieve a pixmap from global application cache.
4344 // Load and add it to the cache if not found
getPixmapFromCache(QPixmap & pixmap,const QString & file)4345 bool QtClient::getPixmapFromCache(QPixmap& pixmap, const QString& file)
4346 {
4347 if (file.isEmpty())
4348 return false;
4349 QPixmap* cached = QPixmapCache::find(file);
4350 if (cached) {
4351 pixmap = *cached;
4352 return true;
4353 }
4354 if (!pixmap.load(file))
4355 return false;
4356 #ifdef XDEBUG
4357 String f;
4358 getUtf8(f,file);
4359 Debug(ClientDriver::self(),DebugAll,"Loaded '%s' in pixmap cache",f.c_str());
4360 #endif
4361 QPixmapCache::insert(file,pixmap);
4362 return true;
4363 }
4364
4365 // Update application style sheet from config
4366 // Build style sheet from files:
4367 // stylesheet.css
4368 // stylesheet_stylename.css
4369 // stylesheet_osname.css
4370 // stylesheet_osname_stylename.css
updateAppStyleSheet()4371 void QtClient::updateAppStyleSheet()
4372 {
4373 if (!qApp) {
4374 Debug(ClientDriver::self(),DebugWarn,"Update app stylesheet called without app");
4375 return;
4376 }
4377 String shf = Engine::config().getValue("client","stylesheet_file","stylesheet.css");
4378 if (!shf)
4379 return;
4380 QString sh;
4381 if (!appendStyleSheet(sh,shf))
4382 return;
4383 String styleName;
4384 QStyle* style = qApp->style();
4385 const QMetaObject* meta = style ? style->metaObject() : 0;
4386 if (meta) {
4387 styleName = s_qtStyles.getValue(meta->className());
4388 if (!styleName)
4389 styleName = meta->className();
4390 }
4391 if (styleName)
4392 appendStyleSheet(sh,shf,styleName);
4393 String osname;
4394 osname << "os" << PLATFORM_LOWERCASE_NAME;
4395 appendStyleSheet(sh,shf,osname);
4396 if (styleName)
4397 appendStyleSheet(sh,shf,osname,styleName);
4398 qApp->setStyleSheet(sh);
4399 }
4400
4401 // Set widget attributes from list
setWidgetAttributes(QWidget * w,const String & attrs)4402 void QtClient::setWidgetAttributes(QWidget* w, const String& attrs)
4403 {
4404 if (!(w && attrs))
4405 return;
4406 ObjList* list = attrs.split(',',false);
4407 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
4408 const String& attr = *static_cast<String*>(o->get());
4409 bool on = (attr[0] != '!');
4410 const char* name = attr.c_str();
4411 int val = lookup(on ? name : name + 1,s_widgetAttributes);
4412 if (val)
4413 w->setAttribute((Qt::WidgetAttribute)val,on);
4414 }
4415 TelEngine::destruct(list);
4416 }
4417
4418 // Adjust widget height
setWidgetHeight(QWidget * w,const String & height)4419 void QtClient::setWidgetHeight(QWidget* w, const String& height)
4420 {
4421 if (!w)
4422 return;
4423 int h = 0;
4424 if (height.isBoolean()) {
4425 h = QtClient::getIntProperty(w,"_yate_height_delta",-1);
4426 if (h > 0) {
4427 if (height.toBoolean())
4428 h += w->height();
4429 else if (h < w->height())
4430 h = w->height() - h;
4431 else
4432 h = 0;
4433 }
4434 }
4435 else
4436 h = height.toInteger();
4437 if (h < 0)
4438 return;
4439 QSizePolicy sp = w->sizePolicy();
4440 sp.setVerticalPolicy(QSizePolicy::Fixed);
4441 w->setSizePolicy(sp);
4442 w->setMinimumHeight(h);
4443 w->setMaximumHeight(h);
4444 }
4445
4446 // Build a busy widget child for a given widget
buildBusy(QWidget * parent,QWidget * target,const String & ui,const NamedList & params)4447 QWidget* QtClient::buildBusy(QWidget* parent, QWidget* target, const String& ui,
4448 const NamedList& params)
4449 {
4450 QtBusyWidget* w = new QtBusyWidget(parent);
4451 w->init(ui,params,target);
4452 return w;
4453 }
4454
4455 // Load a movie
loadMovie(const char * file,QObject * parent,const char * path)4456 QMovie* QtClient::loadMovie(const char* file, QObject* parent, const char* path)
4457 {
4458 static NamedList s_failed("");
4459
4460 if (TelEngine::null(file))
4461 return 0;
4462 String tmp = path;
4463 if (!path)
4464 tmp = Client::s_skinPath;
4465 else if (tmp && !tmp.endsWith(Engine::pathSeparator()))
4466 tmp << Engine::pathSeparator();
4467 tmp << file;
4468 QMovie* m = new QMovie(setUtf8(tmp),QByteArray(),parent);
4469 NamedString* ns = s_failed.getParam(tmp);
4470 if (m->isValid()) {
4471 if (ns)
4472 s_failed.clearParam(ns);
4473 return m;
4474 }
4475 if (!ns) {
4476 s_failed.addParam(tmp,"");
4477 String error;
4478 error << "Failed to load movie '" << tmp << "'";
4479 Debug(QtDriver::self(),DebugNote,"%s",error.c_str());
4480 if (self())
4481 self()->addToLog(error);
4482 }
4483 delete m;
4484 return 0;
4485 }
4486
4487 // Fill a list from URL parameters
fillUrlParams(const QUrl & url,NamedList & list,QString * path,bool pathToList)4488 void QtClient::fillUrlParams(const QUrl& url, NamedList& list, QString* path,
4489 bool pathToList)
4490 {
4491 safeGetUtf8(list,"protocol",url.scheme());
4492 safeGetUtf8(list,"host",url.host());
4493 if (url.port() >= 0)
4494 list.addParam("port",String(url.port()));
4495 safeGetUtf8(list,"username",url.userName());
4496 safeGetUtf8(list,"password",url.password());
4497 QString tmp;
4498 if (!path) {
4499 tmp = url.path();
4500 path = &tmp;
4501 }
4502 if (pathToList)
4503 list.assign(path->toUtf8().constData());
4504 else
4505 safeGetUtf8(list,"path",*path);
4506 QList<QPair<QString, QString> > items = url.queryItems();
4507 for (int i = 0; i < items.size(); i++)
4508 list.addParam(items[i].first.toUtf8().constData(),items[i].second.toUtf8().constData());
4509 }
4510
4511 // Dump MIME data for debug purposes
dumpMime(String & buf,const QMimeData * m)4512 void QtClient::dumpMime(String& buf, const QMimeData* m)
4513 {
4514 static const char* indent = "\r\n ";
4515 if (!m)
4516 return;
4517 QStringList fmts = m->formats();
4518 if (fmts.size() > 0) {
4519 buf.append("FORMATS:","\r\n") << indent;
4520 QString s = fmts.join(indent);
4521 buf << s.toUtf8().constData();
4522 }
4523 if (m->html().length() > 0)
4524 buf.append("HTML: ","\r\n") << m->html().toUtf8().constData();
4525 if (m->text().length() > 0)
4526 buf.append("TEXT: ","\r\n") << m->text().toUtf8().constData();
4527 QList<QUrl> urls = m->urls();
4528 if (urls.size() > 0) {
4529 buf.append("URLS:","\r\n");
4530 for (int i = 0; i < urls.size(); i++)
4531 buf << indent << urls[i].toString().toUtf8().constData();
4532 }
4533 }
4534
4535
4536 /**
4537 * QtDriver
4538 */
QtDriver(bool buildClientThread)4539 QtDriver::QtDriver(bool buildClientThread)
4540 : m_init(false), m_clientThread(buildClientThread)
4541 {
4542 qInstallMsgHandler(qtMsgHandler);
4543 }
4544
~QtDriver()4545 QtDriver::~QtDriver()
4546 {
4547 qInstallMsgHandler(0);
4548 }
4549
initialize()4550 void QtDriver::initialize()
4551 {
4552 Output("Initializing module Qt4 client");
4553 s_device = Engine::config().getValue("client","device",DEFAULT_DEVICE);
4554 if (!QtClient::self()) {
4555 debugCopy();
4556 QtClient::setSelf(new QtClient);
4557 if (m_clientThread)
4558 QtClient::self()->startup();
4559 }
4560 if (!m_init) {
4561 m_init = true;
4562 setup();
4563 }
4564 }
4565
4566 /**
4567 * QtEventProxy
4568 */
QtEventProxy(Type type,QApplication * app)4569 QtEventProxy::QtEventProxy(Type type, QApplication* app)
4570 {
4571 #define SET_NAME(n) { m_name = n; setObjectName(QtClient::setUtf8(m_name)); }
4572 switch (type) {
4573 case Timer:
4574 SET_NAME("qtClientTimerProxy");
4575 {
4576 QTimer* timer = new QTimer(this);
4577 timer->setObjectName("qtClientIdleTimer");
4578 QtClient::connectObjects(timer,SIGNAL(timeout()),this,SLOT(timerTick()));
4579 timer->start(0);
4580 }
4581 break;
4582 case AllHidden:
4583 SET_NAME("qtClientAllHidden");
4584 if (app)
4585 QtClient::connectObjects(app,SIGNAL(lastWindowClosed()),this,SLOT(allHidden()));
4586 break;
4587 default:
4588 return;
4589 }
4590 #undef SET_NAME
4591 }
4592
timerTick()4593 void QtEventProxy::timerTick()
4594 {
4595 if (Client::self())
4596 Client::self()->idleActions();
4597 Thread::idle();
4598 }
4599
allHidden()4600 void QtEventProxy::allHidden()
4601 {
4602 if (Client::self())
4603 Client::self()->allHidden();
4604 }
4605
4606
4607 //
4608 // QtUrlBuilder
4609 //
QtUrlBuilder(QObject * parent,const String & format,const String & queryParams)4610 QtUrlBuilder::QtUrlBuilder(QObject* parent, const String& format,
4611 const String& queryParams)
4612 : QObject(parent),
4613 m_format(format),
4614 m_queryParams(0)
4615 {
4616 if (queryParams) {
4617 m_queryParams = queryParams.split(',',false);
4618 if (!m_queryParams->skipNull())
4619 TelEngine::destruct(m_queryParams);
4620 }
4621 }
4622
~QtUrlBuilder()4623 QtUrlBuilder::~QtUrlBuilder()
4624 {
4625 TelEngine::destruct(m_queryParams);
4626 }
4627
4628 // Build URL
build(const NamedList & params) const4629 QUrl QtUrlBuilder::build(const NamedList& params) const
4630 {
4631 String tmp;
4632 if (m_format) {
4633 tmp = m_format;
4634 params.replaceParams(tmp);
4635 }
4636 QUrl url(QtClient::setUtf8(tmp));
4637 if (m_queryParams) {
4638 NamedIterator iter(params);
4639 for (const NamedString* ns = 0; 0 != (ns = iter.get());)
4640 if (m_queryParams->find(ns->name()))
4641 url.addQueryItem(QtClient::setUtf8(ns->name()),QtClient::setUtf8(*ns));
4642 }
4643 return url;
4644 }
4645
4646
4647 /*
4648 * QtUIWidget
4649 */
4650 // Retrieve item type definition from [type:]value. Create it if not found
getItemProps(QString & in,String & value)4651 QtUIWidgetItemProps* QtUIWidget::getItemProps(QString& in, String& value)
4652 {
4653 String type;
4654 int pos = in.indexOf(':');
4655 if (pos >= 0) {
4656 QtClient::getUtf8(type,in.left(pos));
4657 QtClient::getUtf8(value,in.right(in.length() - pos - 1));
4658 }
4659 else
4660 QtClient::getUtf8(value,in);
4661 QtUIWidgetItemProps* p = QtUIWidget::getItemProps(type);
4662 if (!p) {
4663 p = new QtUIWidgetItemProps(type);
4664 m_itemProps.append(p);
4665 }
4666 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) getItemProps(%s,%s) got (%p) ui=%s [%p]",
4667 name().c_str(),in.toUtf8().constData(),value.c_str(),p,p->m_ui.c_str(),this);
4668 return p;
4669 }
4670
4671 // Set widget's parameters.
4672 // Handle an 'applyall' parameter carrying a NamedList to apply to all items
setParams(const NamedList & params)4673 bool QtUIWidget::setParams(const NamedList& params)
4674 {
4675 bool ok = false;
4676 NamedIterator iter(params);
4677 for (const NamedString* ns = 0; 0 != (ns = iter.get());) {
4678 if (ns->name() == YSTRING("applyall")) {
4679 const NamedList* list = YOBJECT(NamedList,ns);
4680 if (list) {
4681 ok = true;
4682 applyAllParams(*list);
4683 }
4684 }
4685 else if (ns->name().startsWith("beginedit:"))
4686 beginEdit(ns->name().substr(10),ns);
4687 }
4688 return ok;
4689 }
4690
4691 // Apply a list of parameters to all container items
applyAllParams(const NamedList & params)4692 void QtUIWidget::applyAllParams(const NamedList& params)
4693 {
4694 QList<QObject*> list = getContainerItems();
4695 for (int i = 0; i < list.size(); i++)
4696 setParams(list[i],params);
4697 }
4698
4699 // Find an item widget by id
findItem(const String & id)4700 QWidget* QtUIWidget::findItem(const String& id)
4701 {
4702 QString item = QtClient::setUtf8(id);
4703 QList<QObject*> list = getContainerItems();
4704 for (int i = 0; i < list.size(); i++) {
4705 if (!list[i]->isWidgetType())
4706 continue;
4707 String item;
4708 getListItemIdProp(list[i],item);
4709 if (id == item)
4710 return static_cast<QWidget*>(list[i]);
4711 }
4712 return 0;
4713 }
4714
4715 // Retrieve the object identity from '_yate_identity' property or name
4716 // Retrieve the object item from '_yate_widgetlistitem' property.
4717 // Set 'identity' to object_identity[:item_name]
getIdentity(QObject * obj,String & identity)4718 void QtUIWidget::getIdentity(QObject* obj, String& identity)
4719 {
4720 if (!obj)
4721 return;
4722 String ident;
4723 QtClient::getIdentity(obj,ident);
4724 if (!ident)
4725 return;
4726 String item;
4727 getListItemProp(obj,item);
4728 identity.append(ident,":");
4729 identity.append(item,":");
4730 }
4731
4732 // Update a widget and children from a list a parameters
setParams(QObject * parent,const NamedList & params)4733 bool QtUIWidget::setParams(QObject* parent, const NamedList& params)
4734 {
4735 static const String s_property = "property";
4736 static const String s_active = "active";
4737 static const String s_image = "image";
4738 static const String s_show = "show";
4739 static const String s_display = "display";
4740 static const String s_check = "check";
4741 static const String s_select = "select";
4742 static const String s_addlines = "addlines";
4743 static const String s_setrichtext = "setrichtext";
4744 static const String s_updatetablerows = "updatetablerows";
4745 static const String s_cleartable = "cleartable";
4746 static const String s_rawimage = "rawimage";
4747 static const String s_setparams = "setparams";
4748 static const String s_setmenu = "setmenu";
4749 static const String s_height = "height";
4750
4751 if (!parent)
4752 return false;
4753 QtWindow* wnd = QtClient::parentWindow(parent);
4754 if (!wnd)
4755 return false;
4756 #ifdef DEBUG
4757 String tmp;
4758 params.dump(tmp," ");
4759 Debug(ClientDriver::self(),DebugAll,"QtUIWidget(%s)::setParams(%p,%s) %s",
4760 name().c_str(),parent,YQT_OBJECT_NAME(parent),tmp.c_str());
4761 #endif
4762 String pName(YQT_OBJECT_NAME(parent));
4763 bool ok = true;
4764 unsigned int n = params.length();
4765 for (unsigned int i = 0; i < n; i++) {
4766 NamedString* ns = params.getParam(i);
4767 if (!ns)
4768 continue;
4769 XDebug(ClientDriver::self(),DebugInfo,"QtUIWidget(%s)::setParams() %s=%s",
4770 name().c_str(),ns->name().c_str(),ns->c_str());
4771 String buf;
4772 int pos = ns->name().find(':');
4773 if (pos < 0) {
4774 if (ns->name() != s_setmenu)
4775 ok = wnd->setText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
4776 else
4777 buildWidgetItemMenu(qobject_cast<QWidget*>(parent),YOBJECT(NamedList,ns));
4778 continue;
4779 }
4780 String n(ns->name().substr(0,pos));
4781 String cName = ns->name().substr(pos + 1);
4782 if (n == s_property) {
4783 // Handle property[:child]:property_name
4784 int pos = cName.find(':');
4785 if (pos >= 0) {
4786 QString tmp = buildQChildName(pName,cName.substr(0,pos));
4787 QObject* c = qFindChild<QObject*>(parent,tmp);
4788 ok = c && QtClient::setProperty(c,cName.substr(pos + 1),*ns) && ok;
4789 }
4790 else
4791 ok = QtClient::setProperty(parent,cName,*ns) && ok;
4792 }
4793 else if (n == s_active)
4794 ok = wnd->setActive(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
4795 else if (n == s_image)
4796 ok = wnd->setImage(buildChildName(buf,pName,cName),*ns) && ok;
4797 else if (n == s_show || n == s_display)
4798 ok = wnd->setShow(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
4799 else if (n == s_check)
4800 ok = wnd->setCheck(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
4801 else if (n == s_select)
4802 ok = wnd->setSelect(buildChildName(buf,pName,cName),*ns) && ok;
4803 if (n == s_setparams) {
4804 NamedList* p = YOBJECT(NamedList,ns);
4805 if (!p)
4806 continue;
4807 QtWidget w(parent,buildChildName(buf,pName,cName));
4808 UIWidget* uiw = w.uiWidget();
4809 ok = uiw && uiw->setParams(*p) && ok;
4810 }
4811 else if (n == s_addlines) {
4812 NamedList* p = YOBJECT(NamedList,ns);
4813 if (p)
4814 ok = wnd->addLines(buildChildName(buf,pName,cName),p,0,ns->toBoolean()) && ok;
4815 }
4816 else if (n == s_setrichtext)
4817 ok = wnd->setText(buildChildName(buf,pName,cName),*ns,true) && ok;
4818 else if (n == s_updatetablerows) {
4819 NamedList* p = YOBJECT(NamedList,ns);
4820 if (p)
4821 ok = wnd->updateTableRows(buildChildName(buf,pName,cName),p,ns->toBoolean()) && ok;
4822 }
4823 else if (n == s_cleartable)
4824 ok = wnd->clearTable(buildChildName(buf,pName,cName)) && ok;
4825 else if (n == s_rawimage) {
4826 DataBlock* data = YOBJECT(DataBlock,ns);
4827 if (data) {
4828 QString tmp = buildQChildName(pName,cName.substr(0,pos));
4829 QObject* c = qFindChild<QObject*>(parent,tmp);
4830 ok = c && QtClient::setImage(c,*data,*ns) && ok;
4831 }
4832 }
4833 else if (n == s_setmenu)
4834 buildWidgetItemMenu(qobject_cast<QWidget*>(parent),YOBJECT(NamedList,ns),cName);
4835 else if (n == s_height) {
4836 QString tmp = buildQChildName(pName,cName);
4837 QWidget* w = qFindChild<QWidget*>(qobject_cast<QWidget*>(parent),tmp);
4838 QtClient::setWidgetHeight(w,*ns);
4839 }
4840 else
4841 ok = wnd->setText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
4842 }
4843 // Set item parameters
4844 NamedString* yparams = params.getParam(YSTRING("_yate_itemparams"));
4845 if (!TelEngine::null(yparams)) {
4846 QVariant var = parent->property(yparams->name().c_str());
4847 if (var.type() == QVariant::Invalid || var.type() == QVariant::StringList) {
4848 QStringList list;
4849 if (var.type() == QVariant::StringList)
4850 list = var.toStringList();
4851 NamedList tmp("");
4852 tmp.copyParams(params,*yparams);
4853 QtClient::copyParams(list,tmp);
4854 parent->setProperty(yparams->name().c_str(),QVariant(list));
4855 }
4856 else
4857 ok = false;
4858 }
4859 return ok;
4860 }
4861
4862 // Get an item object's parameters
getParams(QObject * parent,NamedList & params)4863 bool QtUIWidget::getParams(QObject* parent, NamedList& params)
4864 {
4865 static const String s_property = "property";
4866 static const String s_getcheck = "getcheck";
4867 static const String s_getselect = "getselect";
4868 static const String s_getrichtext = "getrichtext";
4869
4870 if (!parent)
4871 return false;
4872 QtWindow* wnd = QtClient::parentWindow(parent);
4873 if (!wnd)
4874 return false;
4875 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s)::getParams(%p,%s)",
4876 name().c_str(),parent,YQT_OBJECT_NAME(parent));
4877 String pName;
4878 QtClient::getUtf8(pName,parent->objectName());
4879 bool ok = true;
4880 unsigned int n = params.length();
4881 for (unsigned int i = 0; i < n; i++) {
4882 NamedString* ns = params.getParam(i);
4883 if (!ns)
4884 continue;
4885 String buf;
4886 int pos = ns->name().find(':');
4887 if (pos < 0) {
4888 ok = wnd->getText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
4889 continue;
4890 }
4891 String n(ns->name().substr(0,pos));
4892 String cName = ns->name().substr(pos + 1);
4893 if (n == s_property) {
4894 // Handle property[:child]:property_name
4895 int pos = cName.find(':');
4896 if (pos >= 0) {
4897 QString tmp = buildQChildName(pName,cName.substr(0,pos));
4898 QObject* c = qFindChild<QObject*>(parent,tmp);
4899 ok = c && QtClient::getProperty(c,cName.substr(pos + 1),*ns) && ok;
4900 }
4901 else
4902 ok = QtClient::getProperty(parent,cName,*ns) && ok;
4903 }
4904 else if (n == s_getselect)
4905 ok = wnd->getSelect(buildChildName(buf,pName,cName),*ns) && ok;
4906 else if (n == s_getcheck) {
4907 bool on = false;
4908 ok = wnd->getCheck(buildChildName(buf,pName,cName),on) && ok;
4909 *ns = String::boolText(on);
4910 }
4911 else if (n == s_getrichtext)
4912 ok = wnd->getText(buildChildName(buf,pName,cName),*ns,true) && ok;
4913 else
4914 ok = wnd->getText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
4915 }
4916 // Get item parameters
4917 QtClient::getProperty(parent,"_yate_itemparams",params);
4918 return ok;
4919 }
4920
4921 // Show or hide control busy state
setBusy(bool on)4922 bool QtUIWidget::setBusy(bool on)
4923 {
4924 QObject* o = getQObject();
4925 QWidget* w = (o && o->isWidgetType()) ? static_cast<QWidget*>(o) : 0;
4926 return w && QtBusyWidget::showBusyChild(w,on);
4927 }
4928
4929 // Apply properties for QAbstractItemView descendents
applyItemViewProps(const NamedList & params)4930 void QtUIWidget::applyItemViewProps(const NamedList& params)
4931 {
4932 static const String s_selMode = "_yate_selection_mode";
4933 static const String s_editTriggers = "_yate_edit_triggers";
4934
4935 QObject* obj = getQObject();
4936 QAbstractItemView* av = qobject_cast<QAbstractItemView*>(obj);
4937 if (!av)
4938 return;
4939 NamedIterator iter(params);
4940 for (const NamedString* ns = 0; 0 != (ns = iter.get());) {
4941 if (ns->name() == s_selMode)
4942 av->setSelectionMode(QtClient::str2selmode(*ns));
4943 else if (ns->name() == s_editTriggers)
4944 av->setEditTriggers(QtClient::str2editTriggers(*ns));
4945 }
4946 }
4947
4948 // Begin item edit. The default behaviour start edit for QAbstractItemView descendants
beginEdit(const String & item,const String * what)4949 bool QtUIWidget::beginEdit(const String& item, const String* what)
4950 {
4951 QObject* obj = getQObject();
4952 QAbstractItemView* av = qobject_cast<QAbstractItemView*>(obj);
4953 if (!av)
4954 return false;
4955 QModelIndex idx = modelIndex(item,what);
4956 if (!idx.isValid())
4957 return false;
4958 av->setCurrentIndex(idx);
4959 av->edit(idx);
4960 return true;
4961 }
4962
4963 // Build item widget menu
buildWidgetItemMenu(QWidget * w,const NamedList * params,const String & child,bool set)4964 QMenu* QtUIWidget::buildWidgetItemMenu(QWidget* w, const NamedList* params,
4965 const String& child, bool set)
4966 {
4967 if (!(w && params))
4968 return 0;
4969 QWidget* parent = w;
4970 // Retrieve the item owner
4971 QWidget* pItem = 0;
4972 String item;
4973 getListItemIdProp(w,item);
4974 if (item)
4975 pItem = findItem(item);
4976 else {
4977 getListItemProp(w,item);
4978 pItem = item ? findItem(item) : 0;
4979 }
4980 XDebug(ClientDriver::self(),DebugAll,
4981 "QtUIWidget(%s)::buildMenu() widget=%s item=%s [%p]",
4982 this->name().c_str(),YQT_OBJECT_NAME(w),item.c_str(),this);
4983 String pName(YQT_OBJECT_NAME(w));
4984 const String& owner = (*params)[YSTRING("owner")];
4985 if (owner && owner != item) {
4986 QString tmp = buildQChildName(pName,owner);
4987 parent = qFindChild<QWidget*>(w,tmp);
4988 if (!parent) {
4989 Debug(QtDriver::self(),DebugNote,
4990 "QtUIWidget(%s) buildMenu() owner '%s' not found [%p]",
4991 name().c_str(),owner.c_str(),this);
4992 return 0;
4993 }
4994 }
4995 QWidget* target = parent;
4996 String t = child ? child : (*params)[YSTRING("target")];
4997 if (t) {
4998 QString tmp = buildQChildName(pName,t);
4999 target = qFindChild<QWidget*>(w,tmp);
5000 if (!target) {
5001 Debug(QtDriver::self(),DebugNote,
5002 "QtUIWidget(%s) buildMenu() target '%s' not found [%p]",
5003 name().c_str(),t.c_str(),this);
5004 return 0;
5005 }
5006 }
5007 QString menuName = buildQChildName(pName,t + "_menu");
5008 // Remove existing menu
5009 QMenu* menu = qFindChild<QMenu*>(parent,menuName);
5010 if (menu) {
5011 delete menu;
5012 menu = 0;
5013 }
5014 // Build the menu
5015 QObject* thisObj = getQObject();
5016 if (!thisObj)
5017 return 0;
5018 String actionSlot;
5019 String toggleSlot;
5020 String selectSlot;
5021 getSlots(actionSlot,toggleSlot,selectSlot);
5022 if (!(actionSlot || toggleSlot))
5023 return 0;
5024 bool addActions = set && target->contextMenuPolicy() == Qt::ActionsContextMenu;
5025 unsigned int n = params->length();
5026 for (unsigned int i = 0; i < n; i++) {
5027 NamedString* param = params->getParam(i);
5028 if (!(param && param->name().startsWith("item:")))
5029 continue;
5030 if (!menu)
5031 menu = new QMenu(QtClient::setUtf8(params->getValue(YSTRING("title"))),parent);
5032 NamedList* p = YOBJECT(NamedList,param);
5033 if (p) {
5034 QMenu* subMenu = QtClient::buildMenu(*p,
5035 *param ? param->c_str() : p->getValue(YSTRING("title"),*p),
5036 thisObj,actionSlot,toggleSlot,menu);
5037 if (subMenu) {
5038 menu->addMenu(subMenu);
5039 if (addActions)
5040 target->addAction(subMenu->menuAction());
5041 }
5042 continue;
5043 }
5044 QAction* a = 0;
5045 String name = param->name().substr(5);
5046 if (*param) {
5047 a = menu->addAction(QtClient::setUtf8(*param));
5048 a->setObjectName(buildQChildName(pName,name));
5049 a->setParent(menu);
5050 QtClient::setImage(a,(*params)["image:" + name]);
5051 }
5052 else if (!name) {
5053 a = menu->addSeparator();
5054 a->setParent(menu);
5055 }
5056 else if (pItem) {
5057 // Check if the action is already there
5058 QString aName = buildQChildName(pItem->objectName(),QtClient::setUtf8(name));
5059 a = qFindChild<QAction*>(pItem,aName);
5060 if (a)
5061 menu->addAction(a);
5062 }
5063 if (a) {
5064 if (addActions)
5065 target->addAction(a);
5066 }
5067 else
5068 Debug(ClientDriver::self(),DebugNote,
5069 "QtUIWidget(%s)::buildMenu() action '%s' not found for item=%s [%p]",
5070 this->name().c_str(),name.c_str(),item.c_str(),this);
5071 }
5072 if (!menu)
5073 return 0;
5074 // Set name
5075 menu->setObjectName(menuName);
5076 // Apply properties
5077 // Format: property:object_name:property_name=value
5078 if (parent)
5079 for (unsigned int i = 0; i < n; i++) {
5080 NamedString* param = params->getParam(i);
5081 if (!(param && param->name().startsWith("property:")))
5082 continue;
5083 int pos = param->name().find(':',9);
5084 if (pos < 9)
5085 continue;
5086 QString n = buildQChildName(pName,param->name().substr(9,pos - 9));
5087 QObject* obj = qFindChild<QObject*>(parent,n);
5088 if (obj)
5089 QtClient::setProperty(obj,param->name().substr(pos + 1),*param);
5090 }
5091 // Connect signals (direct children only: actions from sub-menus are already connected)
5092 QList<QAction*> list = qFindChildren<QAction*>(menu);
5093 for (int i = 0; i < list.size(); i++) {
5094 if (list[i]->isSeparator() || list[i]->parent() != menu)
5095 continue;
5096 if (list[i]->isCheckable())
5097 QtClient::connectObjects(list[i],SIGNAL(toggled(bool)),thisObj,toggleSlot);
5098 else
5099 QtClient::connectObjects(list[i],SIGNAL(triggered()),thisObj,actionSlot);
5100 }
5101 if (addActions)
5102 return menu;
5103 QMenu* mOwner = qobject_cast<QMenu*>(target);
5104 if (mOwner)
5105 mOwner->insertMenu(0,menu);
5106 else {
5107 QToolButton* tb = qobject_cast<QToolButton*>(target);
5108 if (tb)
5109 tb->setMenu(menu);
5110 else {
5111 QPushButton* pb = qobject_cast<QPushButton*>(target);
5112 if (pb)
5113 pb->setMenu(menu);
5114 else if (!QtClient::setProperty(target,s_propContextMenu,params))
5115 target->addAction(menu->menuAction());
5116 }
5117 }
5118 return menu;
5119 }
5120
5121 // Build a container child name from parent property
buildQChildNameProp(QString & dest,QObject * parent,const char * prop)5122 bool QtUIWidget::buildQChildNameProp(QString& dest, QObject* parent, const char* prop)
5123 {
5124 if (!(parent && prop))
5125 return false;
5126 QVariant var = parent->property(prop);
5127 if (!var.isValid() || var.toString().size() <= 0)
5128 return false;
5129 dest = buildQChildName(parent->objectName(),var.toString());
5130 return true;
5131 }
5132
5133 // Retrieve the top level QtUIWidget container parent of an object
container(QObject * obj)5134 QtUIWidget* QtUIWidget::container(QObject* obj)
5135 {
5136 if (!obj)
5137 return 0;
5138 QtUIWidget* uiw = 0;
5139 while (0 != (obj = obj->parent())) {
5140 QtWidget w(obj);
5141 UIWidget* u = w.uiWidget();
5142 if (u)
5143 uiw = static_cast<QtUIWidget*>(u);
5144 }
5145 return uiw;
5146 }
5147
5148 // Utility used in QtUIWidget::initNavigation
initNavAction(QObject * obj,const String & name,const String & actionSlot)5149 static bool initNavAction(QObject* obj, const String& name, const String& actionSlot)
5150 {
5151 if (!(obj && name))
5152 return false;
5153 QtWindow* wnd = QtClient::parentWindow(obj);
5154 QObject* child = qFindChild<QObject*>(wnd,QtClient::setUtf8(name));
5155 if (!child)
5156 return false;
5157 QAbstractButton* b = 0;
5158 QAction* a = 0;
5159 if (child->isWidgetType())
5160 b = qobject_cast<QAbstractButton*>(child);
5161 else
5162 a = qobject_cast<QAction*>(child);
5163 if (b || a) {
5164 if (b)
5165 QtClient::connectObjects(b,SIGNAL(clicked()),obj,actionSlot);
5166 else
5167 QtClient::connectObjects(a,SIGNAL(triggered()),obj,actionSlot);
5168 }
5169 return b || a;
5170 }
5171
5172 // Initialize navigation controls
initNavigation(const NamedList & params)5173 void QtUIWidget::initNavigation(const NamedList& params)
5174 {
5175 String actionSlot;
5176 String toggleSlot;
5177 String selectSlot;
5178 getSlots(actionSlot,toggleSlot,selectSlot);
5179 QObject* qObj = getQObject();
5180 if (qObj && actionSlot) {
5181 m_prev = params.getValue(YSTRING("navigate_prev"));
5182 if (!initNavAction(qObj,m_prev,actionSlot))
5183 m_prev = "";
5184 m_next = params.getValue(YSTRING("navigate_next"));
5185 if (!initNavAction(qObj,m_next,actionSlot))
5186 m_next = "";
5187 }
5188 m_info = params.getValue(YSTRING("navigate_info"));
5189 m_infoFormat = params.getValue(YSTRING("navigate_info_format"));
5190 m_title = params.getValue(YSTRING("navigate_title"));
5191 updateNavigation();
5192 }
5193
5194 // Update navigation controls
updateNavigation()5195 void QtUIWidget::updateNavigation()
5196 {
5197 if (!(m_prev || m_next || m_info || m_title))
5198 return;
5199 QtWindow* wnd = QtClient::parentWindow(getQObject());
5200 if (!wnd)
5201 return;
5202 NamedList p("");
5203 int crt = currentItemIndex();
5204 if (crt < 0)
5205 crt = 0;
5206 else
5207 crt++;
5208 int n = itemCount();
5209 if (n < crt)
5210 n = crt;
5211 if (m_prev || m_next) {
5212 if (m_prev)
5213 p.addParam("active:" + m_prev,String::boolText(crt > 1));
5214 if (m_next)
5215 p.addParam("active:" + m_next,String::boolText(crt < n));
5216 }
5217 if (m_info) {
5218 String tmp = m_infoFormat;
5219 NamedList pp("");
5220 pp.addParam("index",String(crt));
5221 pp.addParam("count",String(n));
5222 pp.replaceParams(tmp);
5223 p.addParam(m_info,tmp);
5224 }
5225 if (m_title) {
5226 String crt;
5227 getSelect(crt);
5228 NamedList pp("");
5229 if (crt)
5230 getTableRow(crt,&pp);
5231 p.addParam(m_title,pp[YSTRING("title")]);
5232 }
5233 wnd->setParams(p);
5234 }
5235
5236 // Trigger a custom action from an item
triggerAction(const String & item,const String & action,QObject * sender,NamedList * params)5237 bool QtUIWidget::triggerAction(const String& item, const String& action, QObject* sender,
5238 NamedList* params)
5239 {
5240 if (!(Client::self() && action))
5241 return false;
5242 if (!sender)
5243 sender = getQObject();
5244 String s;
5245 getIdentity(sender,s);
5246 if (!s)
5247 return false;
5248 NamedList p("");
5249 if (!params)
5250 params = &p;
5251 params->addParam("item",item,false);
5252 params->addParam("widget",s);
5253 return QtClient::self()->action(QtClient::parentWindow(sender),action,params);
5254 }
5255
5256 // Trigger a custom action from already built list params
triggerAction(const String & action,NamedList & params,QObject * sender)5257 bool QtUIWidget::triggerAction(const String& action, NamedList& params, QObject* sender)
5258 {
5259 if (!(Client::self() && action))
5260 return false;
5261 if (!sender)
5262 sender = getQObject();
5263 String s;
5264 getIdentity(sender,s);
5265 if (!s)
5266 return false;
5267 params.setParam("widget",s);
5268 return QtClient::self()->action(QtClient::parentWindow(sender),action,¶ms);
5269 }
5270
5271 // Handle a child's action
onAction(QObject * sender)5272 void QtUIWidget::onAction(QObject* sender)
5273 {
5274 if (!Client::self())
5275 return;
5276 String s;
5277 getIdentity(sender,s);
5278 if (!s)
5279 return;
5280 int dir = 0;
5281 if (s == m_next)
5282 dir = 1;
5283 else if (s == m_prev)
5284 dir = -1;
5285 if (dir) {
5286 int crt = currentItemIndex();
5287 if (crt >= 0)
5288 setSelectIndex(crt + dir);
5289 return;
5290 }
5291 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising action %s",
5292 name().c_str(),s.c_str());
5293 Client::self()->action(QtClient::parentWindow(sender),s);
5294 }
5295
5296 // Handle a child's toggle notification
onToggle(QObject * sender,bool on)5297 void QtUIWidget::onToggle(QObject* sender, bool on)
5298 {
5299 if (!Client::self())
5300 return;
5301 QtClient::updateToggleImage(sender);
5302 String s;
5303 getIdentity(sender,s);
5304 if (!s)
5305 return;
5306 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising toggle %s",
5307 name().c_str(),s.c_str());
5308 Client::self()->toggle(QtClient::parentWindow(sender),s,on);
5309 }
5310
5311 // Handle a child's selection change
onSelect(QObject * sender,const String * item)5312 void QtUIWidget::onSelect(QObject* sender, const String* item)
5313 {
5314 if (!Client::self())
5315 return;
5316 String s;
5317 getIdentity(sender,s);
5318 if (!s)
5319 return;
5320 QtWindow* wnd = QtClient::parentWindow(sender);
5321 String tmp;
5322 if (!item) {
5323 item = &tmp;
5324 if (wnd)
5325 wnd->getSelect(YQT_OBJECT_NAME(sender),tmp);
5326 }
5327 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising select %s",
5328 name().c_str(),s.c_str());
5329 Client::self()->select(wnd,s,*item);
5330 }
5331
5332 // Handle a child's multiple selection change
onSelectMultiple(QObject * sender,const NamedList * items)5333 void QtUIWidget::onSelectMultiple(QObject* sender, const NamedList* items)
5334 {
5335 if (!Client::self())
5336 return;
5337 String s;
5338 getIdentity(sender,s);
5339 if (!s)
5340 return;
5341 QtWindow* wnd = QtClient::parentWindow(sender);
5342 DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising select multiple",
5343 name().c_str());
5344 if (items) {
5345 Client::self()->select(wnd,s,*items);
5346 return;
5347 }
5348 NamedList tmp("");
5349 if (wnd)
5350 wnd->getSelect(YQT_OBJECT_NAME(sender),tmp);
5351 Client::self()->select(wnd,s,tmp);
5352 }
5353
5354 // Filter wathed events for children.
5355 // Handle child image changing on mouse events
onChildEvent(QObject * watched,QEvent * event)5356 bool QtUIWidget::onChildEvent(QObject* watched, QEvent* event)
5357 {
5358 if (event->type() == QEvent::Enter)
5359 QtClient::updateImageFromMouse(watched,true,true);
5360 else if (event->type() == QEvent::Leave)
5361 QtClient::updateImageFromMouse(watched,true,false);
5362 else if (event->type() == QEvent::MouseButtonPress)
5363 QtClient::updateImageFromMouse(watched,false,true);
5364 else if (event->type() == QEvent::MouseButtonRelease)
5365 QtClient::updateImageFromMouse(watched,false,false);
5366 return false;
5367 }
5368
5369 // Load an item's widget. Rename children. Connect actions
loadWidget(QWidget * parent,const String & name,const String & ui)5370 QWidget* QtUIWidget::loadWidget(QWidget* parent, const String& name, const String& ui)
5371 {
5372 // Build a new widget name to make sure there are no duplicates:
5373 // Some containers (like QTreeWidget) calls deleteLater() for widget's
5374 // set to items which might lead to wrong widget update
5375 // Make sure the widget name contains only 'standard' characters
5376 // to avoid errors when replaced in style sheets
5377 MD5 md5(name);
5378 String wName;
5379 buildChildName(wName,md5.hexDigest());
5380 wName << "_" << (unsigned int)Time::now();
5381 QWidget* w = QtWindow::loadUI(Client::s_skinPath + ui,parent,ui);
5382 DDebug(ClientDriver::self(),w ? DebugAll : DebugNote,
5383 "QtUIWidget(%s)::loadWidget(%p,%s,%s) widget=%p",
5384 this->name().c_str(),parent,wName.c_str(),ui.c_str(),w);
5385 if (!w)
5386 return 0;
5387 QObject* qObj = getQObject();
5388 QtWindow* wnd = getWindow();
5389 // Install event filter in parent window
5390 if (!m_wndEvHooked && wnd && qObj) {
5391 QVariant var = w->property("_yate_keypress_redirect");
5392 if (var.isValid()) {
5393 m_wndEvHooked = true;
5394 wnd->installEventFilter(qObj);
5395 }
5396 }
5397 String actionSlot;
5398 String toggleSlot;
5399 String selectSlot;
5400 getSlots(actionSlot,toggleSlot,selectSlot);
5401 QString wListItem = QtClient::setUtf8(name);
5402 w->setObjectName(QtClient::setUtf8(wName));
5403 setListItemIdProp(w,wListItem);
5404 // Build custom UI widgets
5405 QtClient::buildFrameUiWidgets(w);
5406 // Process "_yate_setaction" property before changing names
5407 QtClient::setAction(w);
5408 // Process children
5409 QList<QObject*> c = qFindChildren<QObject*>(w);
5410 for (int i = 0; i < c.size(); i++) {
5411 // Set object item owner name
5412 setListItemProp(c[i],wListItem);
5413 // Rename child
5414 String n;
5415 QtClient::getUtf8(n,c[i]->objectName());
5416 c[i]->setObjectName(buildQChildName(wName,n));
5417 // Install event filters
5418 if (qObj && QtClient::getBoolProperty(c[i],"_yate_filterevents"))
5419 c[i]->installEventFilter(qObj);
5420 // Connect text changed to window's slot
5421 bool connect = QtClient::autoConnect(c[i]);
5422 if (wnd && connect)
5423 wnd->connectTextChanged(c[i]);
5424 // Connect signals
5425 if (!(qObj && connect && (actionSlot || toggleSlot || selectSlot)))
5426 continue;
5427 // Use isWidgetType() (faster then qobject_cast)
5428 if (c[i]->isWidgetType()) {
5429 // Connect abstract buttons (check boxes and radio/push/tool buttons) signals
5430 QAbstractButton* b = qobject_cast<QAbstractButton*>(c[i]);
5431 if (b) {
5432 if (!b->isCheckable())
5433 QtClient::connectObjects(b,SIGNAL(clicked()),qObj,actionSlot);
5434 else
5435 QtClient::connectObjects(b,SIGNAL(toggled(bool)),qObj,toggleSlot);
5436 continue;
5437 }
5438 // Connect group boxes
5439 QGroupBox* gb = qobject_cast<QGroupBox*>(c[i]);
5440 if (gb) {
5441 if (gb->isCheckable())
5442 QtClient::connectObjects(gb,SIGNAL(toggled(bool)),qObj,toggleSlot);
5443 continue;
5444 }
5445 // Connect combo boxes
5446 QComboBox* combo = qobject_cast<QComboBox*>(c[i]);
5447 if (combo) {
5448 QtClient::connectObjects(combo,SIGNAL(activated(int)),qObj,selectSlot);
5449 continue;
5450 }
5451 // Connect list boxes
5452 QListWidget* lst = qobject_cast<QListWidget*>(c[i]);
5453 if (lst) {
5454 QtClient::connectObjects(lst,SIGNAL(currentRowChanged(int)),qObj,selectSlot);
5455 continue;
5456 }
5457 // Connect sliders
5458 QSlider* sld = qobject_cast<QSlider*>(c[i]);
5459 if (sld) {
5460 QtClient::connectObjects(sld,SIGNAL(valueChanged(int)),qObj,selectSlot);
5461 continue;
5462 }
5463 continue;
5464 }
5465 // Connect actions signals
5466 QAction* a = qobject_cast<QAction*>(c[i]);
5467 if (a) {
5468 if (!a->isCheckable())
5469 QtClient::connectObjects(a,SIGNAL(triggered()),qObj,actionSlot);
5470 else
5471 QtClient::connectObjects(a,SIGNAL(toggled(bool)),qObj,toggleSlot);
5472 continue;
5473 }
5474 }
5475 return w;
5476 }
5477
5478 // Apply a QWidget style sheet. Replace ${name} with widget name in style
applyWidgetStyle(QWidget * w,const String & style)5479 void QtUIWidget::applyWidgetStyle(QWidget* w, const String& style)
5480 {
5481 if (!(w && style))
5482 return;
5483 QString s = QtClient::setUtf8(style);
5484 s.replace("${name}",w->objectName());
5485 w->setStyleSheet(s);
5486 }
5487
5488 // Filter key press events. Retrieve an action associated with the key.
5489 // Check if the object is allowed to process the key.
5490 // Raise the action
filterKeyEvent(QObject * watched,QKeyEvent * event,bool & filter)5491 bool QtUIWidget::filterKeyEvent(QObject* watched, QKeyEvent* event, bool& filter)
5492 {
5493 String action;
5494 if (!QtClient::filterKeyEvent(watched,event,action,filter))
5495 return false;
5496 if (!action)
5497 return true;
5498 String item;
5499 getListItemProp(watched,item);
5500 // Avoid raising a disabled actions
5501 if (item) {
5502 bool ok = true;
5503 QWidget* w = findItem(item);
5504 if (w) {
5505 QString n = buildQChildName(w->objectName(),QtClient::setUtf8(action));
5506 QObject* act = qFindChild<QObject*>(w,n);
5507 if (act) {
5508 if (act->isWidgetType())
5509 ok = (qobject_cast<QWidget*>(act))->isEnabled();
5510 else {
5511 QAction* a = qobject_cast<QAction*>(act);
5512 ok = !a || a->isEnabled();
5513 }
5514 }
5515 }
5516 if (!ok)
5517 return true;
5518 // Append container item to action
5519 action.append(item,":");
5520 }
5521 Client::self()->action(QtClient::parentWindow(getQObject()),action);
5522 return true;
5523 }
5524
5525
5526 /**
5527 * QtSound
5528 */
doStart()5529 bool QtSound::doStart()
5530 {
5531 doStop();
5532 if (Client::self())
5533 Client::self()->createObject((void**)&m_sound,"QSound",m_file);
5534 if (m_sound)
5535 DDebug(ClientDriver::self(),DebugAll,"Sound(%s) started file=%s",
5536 c_str(),m_file.c_str());
5537 else {
5538 Debug(ClientDriver::self(),DebugNote,"Sound(%s) failed to start file=%s",
5539 c_str(),m_file.c_str());
5540 return false;
5541 }
5542 m_sound->setLoops(m_repeat ? m_repeat : -1);
5543 m_sound->play();
5544 return true;
5545 }
5546
doStop()5547 void QtSound::doStop()
5548 {
5549 if (!m_sound)
5550 return;
5551 m_sound->stop();
5552 delete m_sound;
5553 DDebug(ClientDriver::self(),DebugAll,"Sound(%s) stopped",c_str());
5554 m_sound = 0;
5555 }
5556
5557
5558 //
5559 // QtDragAndDrop
5560 //
5561 // Reset data
reset()5562 void QtDragAndDrop::reset()
5563 {
5564 m_started = false;
5565 }
5566
5567 // Check a string value for 'drag', 'drop', 'both'
checkEnable(const String & s,bool & drag,bool & drop)5568 void QtDragAndDrop::checkEnable(const String& s, bool& drag, bool& drop)
5569 {
5570 drag = (s == YSTRING("drag"));
5571 drop = !drag && (s == YSTRING("drop"));
5572 if (!(drag || drop))
5573 drag = drop = (s == YSTRING("both"));
5574 }
5575
5576 //
5577 // QtDrop
5578 //
5579 const String QtDrop::s_askClientAcceptDrop = "_yate_event_drop_accept";
5580 const String QtDrop::s_notifyClientDrop = "_yate_event_drop";
5581 const QString QtDrop::s_fileScheme = "file";
5582
5583 const TokenDict QtDrop::s_acceptDropName[] = {
5584 {"always", Always},
5585 {"ask", Ask},
5586 {"none", 0},
5587 {0,0}
5588 };
5589
QtDrop(QObject * parent,const NamedList * params)5590 QtDrop::QtDrop(QObject* parent, const NamedList* params)
5591 : QtDragAndDrop(parent),
5592 m_dropParams(""),
5593 m_acceptFiles(false),
5594 m_acceptDirs(false)
5595 {
5596 if (!params)
5597 return;
5598 NamedIterator iter(*params);
5599 for (const NamedString* ns = 0; 0 != (ns = iter.get());) {
5600 if (ns->name() == YSTRING("_yate_accept_drop_schemes"))
5601 QtClient::addStrListUnique(m_schemes,QtClient::str2list(*ns));
5602 else if (ns->name() == YSTRING("_yate_accept_drop_file"))
5603 m_acceptFiles = ns->toBoolean();
5604 else if (ns->name() == YSTRING("_yate_accept_drop_dir"))
5605 m_acceptDirs = ns->toBoolean();
5606 }
5607 }
5608
5609 // Update parameters from drag enter event
start(QDragEnterEvent & e)5610 bool QtDrop::start(QDragEnterEvent& e)
5611 {
5612 static const String s_prefix = "drop:";
5613
5614 reset();
5615 const QMimeData* m = e.mimeData();
5616 if (!(m && m->hasUrls()))
5617 return false;
5618 int nUrls = m->urls().size();
5619 unsigned int nItems = 0;
5620 for (int i = 0; i < nUrls; i++) {
5621 QString scheme = m->urls()[i].scheme();
5622 if (m_schemes.size() > 0 && !m_schemes.contains(scheme)) {
5623 reset();
5624 return false;
5625 }
5626 QString path = m->urls()[i].path();
5627 String what = scheme.toUtf8().constData();
5628 if (scheme == s_fileScheme) {
5629 #ifdef _WINDOWS
5630 path = path.mid(1);
5631 #endif
5632 path = QDir::toNativeSeparators(path);
5633 QFileInfo fi(path);
5634 if (fi.isDir()) {
5635 if (!m_acceptDirs) {
5636 reset();
5637 return false;
5638 }
5639 what = "directory";
5640 }
5641 else if (fi.isFile() && !m_acceptFiles) {
5642 reset();
5643 return false;
5644 }
5645 }
5646 nItems++;
5647 NamedList* nl = new NamedList("");
5648 QtClient::fillUrlParams(m->urls()[i],*nl,&path);
5649 m_dropParams.addParam(new NamedPointer(s_prefix + what,nl,*nl));
5650 }
5651 if (!nItems) {
5652 reset();
5653 return false;
5654 }
5655 if (e.source()) {
5656 QtWindow* wnd = QtClient::parentWindow(e.source());
5657 if (wnd) {
5658 m_dropParams.addParam("source_window",wnd->toString());
5659 QtClient::getUtf8(m_dropParams,"source",e.source()->objectName());
5660 }
5661 }
5662 m_started = true;
5663 return true;
5664 }
5665
5666 // Reset data
reset()5667 void QtDrop::reset()
5668 {
5669 m_dropParams.clearParams();
5670 QtDragAndDrop::reset();
5671 }
5672
5673
5674 //
5675 // QtListDrop
5676 //
QtListDrop(QObject * parent,const NamedList * params)5677 QtListDrop::QtListDrop(QObject* parent, const NamedList* params)
5678 : QtDrop(parent,params),
5679 m_acceptOnEmpty(None)
5680 {
5681 }
5682
5683 // Update accept
updateAcceptType(const String list,int type)5684 void QtListDrop::updateAcceptType(const String list, int type)
5685 {
5686 if (!list)
5687 return;
5688 ObjList* l = list.split(',',false);
5689 for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
5690 NamedInt* ni = new NamedInt(*static_cast<String*>(o->get()),type);
5691 NamedInt::addToListUniqueName(m_acceptItemTypes,ni);
5692 }
5693 TelEngine::destruct(l);
5694 }
5695
5696 // Update accept from parameters list
updateAccept(const NamedList & params)5697 void QtListDrop::updateAccept(const NamedList& params)
5698 {
5699 NamedIterator iter(params);
5700 for (const NamedString* ns = 0; 0 != (ns = iter.get());) {
5701 if (ns->name() == YSTRING("_yate_accept_drop_onempty"))
5702 m_acceptOnEmpty = this->acceptDropType(*ns,None);
5703 else if (ns->name() == YSTRING("_yate_accept_drop_item_type_always"))
5704 updateAcceptType(*ns,Always);
5705 else if (ns->name() == YSTRING("_yate_accept_drop_item_type_none"))
5706 updateAcceptType(*ns,None);
5707 else if (ns->name() == YSTRING("_yate_accept_drop_item_type_ask"))
5708 updateAcceptType(*ns,Ask);
5709 }
5710 }
5711
5712 // Reset data
reset()5713 void QtListDrop::reset()
5714 {
5715 m_acceptItemTypes.clear();
5716 QtDrop::reset();
5717 }
5718
5719
5720 //
5721 // QtBusyWidget
5722 //
5723 const QString QtBusyWidget::s_busySuffix("_yate_busy_widget_generated");
5724
5725 // Constructor
QtBusyWidget(QWidget * parent)5726 QtBusyWidget::QtBusyWidget(QWidget* parent)
5727 : QtCustomWidget(0,parent),
5728 m_target(0), m_shown(false), m_delayMs(0), m_delayTimer(0),
5729 m_movieLabel(0)
5730 {
5731 if (parent)
5732 setObjectName(parent->objectName() + s_busySuffix);
5733 QWidget::hide();
5734 }
5735
5736 // Initialize
init(const String & ui,const NamedList & params,QWidget * target)5737 void QtBusyWidget::init(const String& ui, const NamedList& params, QWidget* target)
5738 {
5739 hideBusy();
5740 m_target = target;
5741 m_movieLabel = 0;
5742 unsigned int delay = 0;
5743 QWidget* w = ui ? loadWidget(this,"",ui) : 0;
5744 if (w) {
5745 QtClient::setWidget(this,w);
5746 int tmp = QtClient::getIntProperty(w,"_yate_busywidget_delay");
5747 if (tmp > 0)
5748 delay = tmp;
5749 QList<QWidget*> c = qFindChildren<QWidget*>(w);
5750 for (int i = 0; i < c.size(); i++) {
5751 QLabel* l = qobject_cast<QLabel*>(c[i]);
5752 if (l) {
5753 if (!m_movieLabel) {
5754 String file;
5755 QtClient::getProperty(l,"_yate_movie_file",file);
5756 if (file) {
5757 l->setMovie(QtClient::loadMovie(file,l));
5758 if (l->movie())
5759 m_movieLabel = l;
5760 }
5761 }
5762 }
5763 }
5764 }
5765 m_delayMs = params.getIntValue(YSTRING("_yate_busywidget_delay"),delay,0);
5766 }
5767
5768 // Show the widget
showBusy()5769 void QtBusyWidget::showBusy()
5770 {
5771 if (m_shown)
5772 return;
5773 m_shown = true;
5774 if (m_delayMs)
5775 m_delayTimer = startTimer(m_delayMs);
5776 if (!m_delayTimer)
5777 internalShow();
5778 }
5779
5780 // Hide the widget
hideBusy()5781 void QtBusyWidget::hideBusy()
5782 {
5783 if (!m_shown)
5784 return;
5785 m_shown = false;
5786 stopDelayTimer();
5787 if (m_target)
5788 m_target->removeEventFilter(this);
5789 setContent(false);
5790 lower();
5791 hide();
5792 }
5793
5794 // Filter wathed events
onChildEvent(QObject * watched,QEvent * event)5795 bool QtBusyWidget::onChildEvent(QObject* watched, QEvent* event)
5796 {
5797 if (m_target && m_target == watched) {
5798 if (event->type() == QEvent::Resize)
5799 resize(m_target->size());
5800 }
5801 return false;
5802 }
5803
timerEvent(QTimerEvent * ev)5804 void QtBusyWidget::timerEvent(QTimerEvent* ev)
5805 {
5806 if (m_delayTimer && ev->timerId() == m_delayTimer) {
5807 stopDelayTimer();
5808 internalShow();
5809 return;
5810 }
5811 QtCustomWidget::timerEvent(ev);
5812 }
5813
5814 // Show/hide busy content
setContent(bool on)5815 void QtBusyWidget::setContent(bool on)
5816 {
5817 QMovie* movie = m_movieLabel ? m_movieLabel->movie() : 0;
5818 if (!movie)
5819 return;
5820 if (on)
5821 movie->start();
5822 else
5823 movie->stop();
5824 }
5825
internalShow()5826 void QtBusyWidget::internalShow()
5827 {
5828 if (m_target) {
5829 resize(m_target->size());
5830 m_target->installEventFilter(this);
5831 }
5832 setContent(true);
5833 raise();
5834 show();
5835 }
5836
5837 /* vi: set ts=8 sw=4 sts=4 noet: */
5838