5205 lines
151 KiB
C++
5205 lines
151 KiB
C++
/**
|
|
* qt4client.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* A Qt-4 based universal telephony client
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2004-2006 Null Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "qt4client.h"
|
|
#include <QtUiTools>
|
|
|
|
#ifdef _WINDOWS
|
|
#define DEFAULT_DEVICE "dsound/*"
|
|
#define PLATFORM_LOWERCASE_NAME "windows"
|
|
#elif defined(__APPLE__)
|
|
#define DEFAULT_DEVICE "coreaudio/*"
|
|
#define PLATFORM_LOWERCASE_NAME "apple"
|
|
#elif defined(__linux__)
|
|
#define DEFAULT_DEVICE "alsa/default"
|
|
#define PLATFORM_LOWERCASE_NAME "linux"
|
|
#else
|
|
#define DEFAULT_DEVICE "oss//dev/dsp"
|
|
#define PLATFORM_LOWERCASE_NAME "unknown"
|
|
#endif
|
|
|
|
namespace TelEngine {
|
|
|
|
static unsigned int s_allHiddenQuit = 0; // Quit on all hidden notification if this counter is 0
|
|
|
|
// Factory used to create objects in client's thread
|
|
class Qt4ClientFactory : public UIFactory
|
|
{
|
|
public:
|
|
Qt4ClientFactory(const char* name = "Qt4ClientFactory");
|
|
virtual void* create(const String& type, const char* name, NamedList* params = 0);
|
|
};
|
|
|
|
// Class used for temporary operations on QT widgets
|
|
// Keeps a pointer to a widget and its type
|
|
// NOTE: The methods of this class don't check the widget pointer
|
|
class QtWidget
|
|
{
|
|
public:
|
|
enum Type {
|
|
PushButton = 0,
|
|
CheckBox = 1,
|
|
Table = 2,
|
|
ListBox = 3,
|
|
ComboBox = 4,
|
|
Tab = 5,
|
|
StackWidget = 6,
|
|
TextEdit = 7,
|
|
Label = 8,
|
|
LineEdit = 9,
|
|
AbstractButton = 10,
|
|
Slider = 11,
|
|
ProgressBar = 12,
|
|
SpinBox = 13,
|
|
Calendar = 14,
|
|
Splitter = 15,
|
|
TextBrowser = 16,
|
|
Unknown, // Unknown type
|
|
Action, // QAction descendant
|
|
CustomTable, // QtTable descendant
|
|
CustomTree, // QtTree descendant
|
|
CustomWidget, // QtCustomWidget descendant
|
|
CustomObject, // QtCustomObject descendant
|
|
Missing // Invalid pointer
|
|
};
|
|
// Set widget from object
|
|
inline QtWidget(QObject* w)
|
|
: m_widget(0), m_action(0), m_object(0), m_type(Missing) {
|
|
if (!w)
|
|
return;
|
|
if (w->inherits("QWidget"))
|
|
m_widget = static_cast<QWidget*>(w);
|
|
else if (w->inherits("QAction"))
|
|
m_action = static_cast<QAction*>(w);
|
|
m_type = getType();
|
|
}
|
|
// Set widget from object and type
|
|
inline QtWidget(QWidget* w, int t)
|
|
: m_widget(w), m_action(0), m_object(0), m_type(t) {
|
|
if (!m_widget)
|
|
m_type = Missing;
|
|
}
|
|
// Set widget/action from object and name
|
|
inline QtWidget(QObject* parent, const String& name)
|
|
: m_widget(0), m_action(0), m_object(0), m_type(Missing) {
|
|
QString what = QtClient::setUtf8(name);
|
|
m_widget = qFindChild<QWidget*>(parent,what);
|
|
if (!m_widget) {
|
|
m_action = qFindChild<QAction*>(parent,what);
|
|
if (!m_action)
|
|
m_object = qFindChild<QObject*>(parent,what);
|
|
}
|
|
m_type = getType();
|
|
}
|
|
inline bool valid() const
|
|
{ return type() != Missing; }
|
|
inline bool invalid() const
|
|
{ return type() == Missing; }
|
|
inline int type() const
|
|
{ return m_type; }
|
|
inline operator QWidget*()
|
|
{ return m_widget; }
|
|
inline bool inherits(const char* classname)
|
|
{ return m_widget && m_widget->inherits(classname); }
|
|
inline bool inherits(Type t)
|
|
{ return inherits(s_types[t]); }
|
|
inline QWidget* widget()
|
|
{ return m_widget; }
|
|
inline QWidget* operator ->()
|
|
{ return m_widget; }
|
|
// Static cast methods
|
|
inline QPushButton* button()
|
|
{ return static_cast<QPushButton*>(m_widget); }
|
|
inline QCheckBox* check()
|
|
{ return static_cast<QCheckBox*>(m_widget); }
|
|
inline QTableWidget* table()
|
|
{ return static_cast<QTableWidget*>(m_widget); }
|
|
inline QListWidget* list()
|
|
{ return static_cast<QListWidget*>(m_widget); }
|
|
inline QComboBox* combo()
|
|
{ return static_cast<QComboBox*>(m_widget); }
|
|
inline QTabWidget* tab()
|
|
{ return static_cast<QTabWidget*>(m_widget); }
|
|
inline QStackedWidget* stackWidget()
|
|
{ return static_cast<QStackedWidget*>(m_widget); }
|
|
inline QTextEdit* textEdit()
|
|
{ return static_cast<QTextEdit*>(m_widget); }
|
|
inline QLabel* label()
|
|
{ return static_cast<QLabel*>(m_widget); }
|
|
inline QLineEdit* lineEdit()
|
|
{ return static_cast<QLineEdit*>(m_widget); }
|
|
inline QAbstractButton* abstractButton()
|
|
{ return static_cast<QAbstractButton*>(m_widget); }
|
|
inline QSlider* slider()
|
|
{ return static_cast<QSlider*>(m_widget); }
|
|
inline QProgressBar* progressBar()
|
|
{ return static_cast<QProgressBar*>(m_widget); }
|
|
inline QSpinBox* spinBox()
|
|
{ return static_cast<QSpinBox*>(m_widget); }
|
|
inline QCalendarWidget* calendar()
|
|
{ return static_cast<QCalendarWidget*>(m_widget); }
|
|
inline QSplitter* splitter()
|
|
{ return static_cast<QSplitter*>(m_widget); }
|
|
inline QtTable* customTable()
|
|
{ return qobject_cast<QtTable*>(m_widget); }
|
|
inline QtTree* customTree()
|
|
{ return qobject_cast<QtTree*>(m_widget); }
|
|
inline QtCustomWidget* customWidget()
|
|
{ return qobject_cast<QtCustomWidget*>(m_widget); }
|
|
inline QtCustomObject* customObject()
|
|
{ return qobject_cast<QtCustomObject*>(m_object); }
|
|
inline UIWidget* uiWidget() {
|
|
switch (type()) {
|
|
case CustomTable:
|
|
return static_cast<UIWidget*>(customTable());
|
|
case CustomWidget:
|
|
return static_cast<UIWidget*>(customWidget());
|
|
case CustomObject:
|
|
return static_cast<UIWidget*>(customObject());
|
|
case CustomTree:
|
|
return static_cast<UIWidget*>(customTree());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline QAction* action()
|
|
{ return m_action; }
|
|
|
|
// Find a combo box item
|
|
inline int findComboItem(const String& item) {
|
|
QComboBox* c = combo();
|
|
return c ? c->findText(QtClient::setUtf8(item)) : -1;
|
|
}
|
|
// Add an item to a combo box
|
|
inline bool addComboItem(const String& item, bool atStart) {
|
|
QComboBox* c = combo();
|
|
if (!c)
|
|
return false;
|
|
QString it(QtClient::setUtf8(item));
|
|
if (atStart)
|
|
c->insertItem(0,it);
|
|
else
|
|
c->addItem(it);
|
|
return true;
|
|
}
|
|
// Find a list box item
|
|
inline int findListItem(const String& item) {
|
|
QListWidget* l = list();
|
|
if (!l)
|
|
return -1;
|
|
QString it(QtClient::setUtf8(item));
|
|
for (int i = l->count(); i >= 0 ; i--) {
|
|
QListWidgetItem* tmp = l->item(i);
|
|
if (tmp && it == tmp->text())
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
// Add an item to a list box
|
|
inline bool addListItem(const String& item, bool atStart) {
|
|
QListWidget* l = list();
|
|
if (!l)
|
|
return false;
|
|
QString it(QtClient::setUtf8(item));
|
|
if (atStart)
|
|
l->insertItem(0,it);
|
|
else
|
|
l->addItem(it);
|
|
return true;
|
|
}
|
|
|
|
int getType() {
|
|
if (m_widget) {
|
|
String cls = m_widget->metaObject()->className();
|
|
for (int i = 0; i < Unknown; i++)
|
|
if (s_types[i] == cls)
|
|
return i;
|
|
if (customTable())
|
|
return CustomTable;
|
|
if (customWidget())
|
|
return CustomWidget;
|
|
if (customTree())
|
|
return CustomTree;
|
|
return Unknown;
|
|
}
|
|
if (m_action && m_action->inherits("QAction"))
|
|
return Action;
|
|
if (customObject())
|
|
return CustomObject;
|
|
return Missing;
|
|
}
|
|
static String s_types[Unknown];
|
|
protected:
|
|
QWidget* m_widget;
|
|
QAction* m_action;
|
|
QObject* m_object;
|
|
int m_type;
|
|
private:
|
|
QtWidget() {}
|
|
};
|
|
|
|
// Class used for temporary operations on QTableWidget objects
|
|
// NOTE: The methods of this class don't check the table pointer
|
|
class TableWidget : public GenObject
|
|
{
|
|
public:
|
|
TableWidget(QTableWidget* table, bool tmp = true);
|
|
TableWidget(QWidget* wid, const String& name, bool tmp = true);
|
|
TableWidget(QtWidget& table, bool tmp = true);
|
|
~TableWidget();
|
|
inline QTableWidget* table()
|
|
{ return m_table; }
|
|
inline bool valid()
|
|
{ return m_table != 0; }
|
|
inline QtTable* customTable()
|
|
{ return (valid()) ? qobject_cast<QtTable*>(m_table) : 0; }
|
|
inline const String& name()
|
|
{ return m_name; }
|
|
inline int rowCount()
|
|
{ return m_table->rowCount(); }
|
|
inline int columnCount()
|
|
{ return m_table->columnCount(); }
|
|
inline void setHeaderText(int col, const char* text) {
|
|
if (col < columnCount())
|
|
m_table->setHorizontalHeaderItem(col,
|
|
new QTableWidgetItem(QtClient::setUtf8(text)));
|
|
}
|
|
inline bool getHeaderText(int col, String& dest, bool lower = true) {
|
|
QTableWidgetItem* item = m_table->horizontalHeaderItem(col);
|
|
if (item) {
|
|
QtClient::getUtf8(dest,item->text());
|
|
if (lower)
|
|
dest.toLower();
|
|
}
|
|
return item != 0;
|
|
}
|
|
// Get the current selection's row
|
|
inline int crtRow() {
|
|
QList<QTableWidgetItem*> items = m_table->selectedItems();
|
|
if (items.size())
|
|
return items[0]->row();
|
|
return -1;
|
|
}
|
|
inline void repaint()
|
|
{ m_table->repaint(); }
|
|
inline void addRow(int index)
|
|
{ m_table->insertRow(index); }
|
|
inline void delRow(int index) {
|
|
if (index >= 0)
|
|
m_table->removeRow(index);
|
|
}
|
|
inline void addColumn(int index, int width = -1, const char* name = 0) {
|
|
m_table->insertColumn(index);
|
|
if (width >= 0)
|
|
m_table->setColumnWidth(index,width);
|
|
setHeaderText(index,name);
|
|
}
|
|
inline void setImage(int row, int col, const String& image) {
|
|
QTableWidgetItem* item = m_table->item(row,col);
|
|
if (item)
|
|
item->setIcon(QIcon(QtClient::setUtf8(image)));
|
|
}
|
|
inline void addCell(int row, int col, const String& value) {
|
|
QTableWidgetItem* item = new QTableWidgetItem(QtClient::setUtf8(value));
|
|
m_table->setItem(row,col,item);
|
|
}
|
|
inline void setCell(int row, int col, const String& value, bool addNew = true) {
|
|
QTableWidgetItem* item = m_table->item(row,col);
|
|
if (item)
|
|
item->setText(QtClient::setUtf8(value));
|
|
else if (addNew)
|
|
addCell(row,col,value);
|
|
}
|
|
inline bool getCell(int row, int col, String& dest, bool lower = false) {
|
|
QTableWidgetItem* item = m_table->item(row,col);
|
|
if (item) {
|
|
QtClient::getUtf8(dest,item->text());
|
|
if (lower)
|
|
dest.toLower();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
inline void setID(int row, const String& value)
|
|
{ setCell(row,0,value); }
|
|
// Add or set a row
|
|
void updateRow(const String& item, const NamedList* data, bool atStart);
|
|
// Update a row from a list of parameters
|
|
void updateRow(int row, const NamedList& data);
|
|
// Find a row by the first's column value. Return -1 if not found
|
|
int getRow(const String& item);
|
|
// Find a column by its label. Return -1 if not found
|
|
int getColumn(const String& name, bool caseInsentive = true);
|
|
protected:
|
|
void init(bool tmp);
|
|
private:
|
|
QTableWidget* m_table; // The table
|
|
String m_name; // Table's name
|
|
int m_sortControl; // Flag used to set/reset sorting attribute of the table
|
|
};
|
|
|
|
// Store an UI loaded from file to avoid loading it again
|
|
class UIBuffer : public String
|
|
{
|
|
public:
|
|
inline UIBuffer(const String& name, QByteArray* buf)
|
|
: String(name), m_buffer(buf)
|
|
{ s_uiCache.append(this); }
|
|
inline QByteArray* buffer()
|
|
{ return m_buffer; }
|
|
// Remove from list. Release memory
|
|
virtual void destruct();
|
|
// Return an already loaded UI. Load from file if not found.
|
|
// Add URLs paths when missing
|
|
static UIBuffer* build(const String& name);
|
|
// Find a buffer
|
|
static UIBuffer* find(const String& name);
|
|
// Buffer cache
|
|
static ObjList s_uiCache;
|
|
private:
|
|
QByteArray* m_buffer; // The buffer
|
|
};
|
|
|
|
}; // namespace TelEngine
|
|
|
|
using namespace TelEngine;
|
|
|
|
// Dynamic properies
|
|
static const String s_propsSave = "_yate_save_props"; // Save properties property name
|
|
static const String s_propColWidths = "_yate_col_widths"; // Column widths
|
|
static const String s_propSorting = "_yate_sorting"; // Table/List sorting
|
|
static const String s_propSizes = "_yate_sizes"; // Size int array
|
|
static String s_propHHeader = "dynamicHHeader"; // Tables: show/hide the horizontal header
|
|
static String s_propAction = "dynamicAction"; // Prefix for properties that would trigger some action
|
|
static String s_propWindowFlags = "_yate_windowflags"; // Window flags
|
|
static const String s_propContextMenu = "_yate_context_menu"; // Context menu name
|
|
static String s_propHideInactive = "dynamicHideOnInactive"; // Hide inactive window
|
|
static const String s_yatePropPrefix = "_yate_"; // Yate dynamic properties prefix
|
|
static NamedList s_qtStyles(""); // Qt styles classname -> internal name
|
|
//
|
|
static Qt4ClientFactory s_qt4Factory;
|
|
static Configuration s_cfg;
|
|
static Configuration s_save;
|
|
ObjList UIBuffer::s_uiCache;
|
|
|
|
// Values used to configure window title bar and border
|
|
static TokenDict s_windowFlags[] = {
|
|
{"title", Qt::WindowTitleHint},
|
|
{"sysmenu", Qt::WindowSystemMenuHint},
|
|
{"maximize", Qt::WindowMaximizeButtonHint},
|
|
{"minimize", Qt::WindowMinimizeButtonHint},
|
|
{"help", Qt::WindowContextHelpButtonHint},
|
|
{"stayontop", Qt::WindowStaysOnTopHint},
|
|
{"frameless", Qt::FramelessWindowHint},
|
|
#if QT_VERSION >= 0x040500
|
|
{"close", Qt::WindowCloseButtonHint},
|
|
#endif
|
|
{0,0}
|
|
};
|
|
|
|
// Widget attribute names
|
|
static const TokenDict s_widgetAttributes[] = {
|
|
{"macshowfocusrect", Qt::WA_MacShowFocusRect},
|
|
{0,0}
|
|
};
|
|
|
|
String QtWidget::s_types[QtWidget::Unknown] = {
|
|
"QPushButton",
|
|
"QCheckBox",
|
|
"QTableWidget",
|
|
"QListWidget",
|
|
"QComboBox",
|
|
"QTabWidget",
|
|
"QStackedWidget",
|
|
"QTextEdit",
|
|
"QLabel",
|
|
"QLineEdit",
|
|
"QAbstractButton",
|
|
"QSlider",
|
|
"QProgressBar",
|
|
"QSpinBox",
|
|
"QCalendarWidget",
|
|
"QSplitter",
|
|
"QTextBrowser",
|
|
};
|
|
|
|
// QVariant type translation dictionary
|
|
static const TokenDict s_qVarType[] = {
|
|
{"string", QVariant::String},
|
|
{"bool", QVariant::Bool},
|
|
{"int", QVariant::Int},
|
|
{"uint", QVariant::UInt},
|
|
{"stringlist", QVariant::StringList},
|
|
{"icon", QVariant::Icon},
|
|
{"pixmap", QVariant::Pixmap},
|
|
{"double", QVariant::Double},
|
|
{"keysequence", QVariant::KeySequence},
|
|
{0,0}
|
|
};
|
|
|
|
// Qt alignment flags translation
|
|
static const TokenDict s_qAlign[] = {
|
|
{"left", Qt::AlignLeft},
|
|
{"right", Qt::AlignRight},
|
|
{"hcenter", Qt::AlignHCenter},
|
|
{"justify", Qt::AlignJustify},
|
|
{"top", Qt::AlignTop},
|
|
{"bottom", Qt::AlignBottom},
|
|
{"vcenter", Qt::AlignVCenter},
|
|
{"center", Qt::AlignCenter},
|
|
{"absolute", Qt::AlignAbsolute},
|
|
{0,0}
|
|
};
|
|
|
|
// Handler for QT library messages
|
|
static void qtMsgHandler(QtMsgType type, const char* text)
|
|
{
|
|
int dbg = DebugAll;
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
dbg = DebugInfo;
|
|
break;
|
|
case QtWarningMsg:
|
|
dbg = DebugWarn;
|
|
break;
|
|
case QtCriticalMsg:
|
|
dbg = DebugGoOn;
|
|
break;
|
|
case QtFatalMsg:
|
|
dbg = DebugFail;
|
|
break;
|
|
}
|
|
Debug("QT",dbg,"%s",text);
|
|
}
|
|
|
|
// Build a list of parameters from a string
|
|
// Return the number of parameters found
|
|
static unsigned int str2Params(NamedList& params, const String& buf, char sep = '|')
|
|
{
|
|
ObjList* list = 0;
|
|
// Check if we have another separator
|
|
if (buf.startsWith("separator=")) {
|
|
sep = buf.at(10);
|
|
list = buf.substr(11).split(sep,false);
|
|
}
|
|
else
|
|
list = buf.split(sep,false);
|
|
unsigned int n = 0;
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
|
|
String* s = static_cast<String*>(o->get());
|
|
int pos = s->find('=');
|
|
if (pos < 1)
|
|
continue;
|
|
params.addParam(s->substr(0,pos),s->substr(pos + 1));
|
|
n++;
|
|
}
|
|
TelEngine::destruct(list);
|
|
return n;
|
|
}
|
|
|
|
// Utility: fix QT path separator on Windows
|
|
// (display paths using only one separator to the user)
|
|
static inline QString fixPathSep(QString str)
|
|
{
|
|
#ifdef _WINDOWS
|
|
QString tmp = str;
|
|
tmp.replace(QChar('/'),QtClient::setUtf8(Engine::pathSeparator()));
|
|
return tmp;
|
|
#else
|
|
return str;
|
|
#endif
|
|
}
|
|
|
|
// Utility: find a stacked widget's page with the given name
|
|
static int findStackedWidget(QStackedWidget& w, const String& name)
|
|
{
|
|
QString n(QtClient::setUtf8(name));
|
|
for (int i = 0; i < w.count(); i++) {
|
|
QWidget* page = w.widget(i);
|
|
if (page && n == page->objectName())
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Utility function used to get the name of a control
|
|
// The name of the control indicates actions, toggles ...
|
|
// The action name alias can contain parameters
|
|
static bool translateName(QtWidget& w, String& name, NamedList** params = 0)
|
|
{
|
|
if (w.invalid())
|
|
return false;
|
|
if (w.type() != QtWidget::Action)
|
|
QtClient::getIdentity(w.widget(),name);
|
|
else
|
|
QtClient::getIdentity(w.action(),name);
|
|
if (!name)
|
|
return true;
|
|
// Check params
|
|
int pos = name.find('|');
|
|
if (pos < 1)
|
|
return true;
|
|
if (params) {
|
|
*params = new NamedList("");
|
|
if (!str2Params(**params,name.substr(pos + 1)))
|
|
TelEngine::destruct(*params);
|
|
}
|
|
name = name.substr(0,pos);
|
|
return true;
|
|
}
|
|
|
|
// Utility: raise a select event if a list is empty
|
|
static inline void raiseSelectIfEmpty(int count, Window* wnd, const String& name)
|
|
{
|
|
if (!Client::exiting() && count <= 0 && Client::self())
|
|
Client::self()->select(wnd,name,String::empty());
|
|
}
|
|
|
|
// Add dynamic properties from a list of parameters
|
|
// Parameter format:
|
|
// property_name:property_type=property_value
|
|
static void addDynamicProps(QObject* obj, NamedList& props)
|
|
{
|
|
static String typeString = "string";
|
|
static String typeBool = "bool";
|
|
static String typeInt = "int";
|
|
|
|
if (!obj)
|
|
return;
|
|
unsigned int n = props.length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = props.getParam(i);
|
|
if (!(ns && ns->name()))
|
|
continue;
|
|
int pos = ns->name().find(':');
|
|
if (pos < 1)
|
|
continue;
|
|
|
|
String prop = ns->name().substr(0,pos);
|
|
String type = ns->name().substr(pos + 1);
|
|
QVariant var;
|
|
if (type == typeString)
|
|
var.setValue(QString(ns->c_str()));
|
|
else if (type == typeBool)
|
|
var.setValue(ns->toBoolean());
|
|
else if (type == typeInt)
|
|
var.setValue(ns->toInteger());
|
|
|
|
if (var.type() != QVariant::Invalid) {
|
|
obj->setProperty(prop,var);
|
|
DDebug(ClientDriver::self(),DebugAll,
|
|
"Object '%s': added dynamic property %s='%s' type=%s",
|
|
YQT_OBJECT_NAME(obj),prop.c_str(),ns->c_str(),var.typeName());
|
|
}
|
|
else
|
|
Debug(ClientDriver::self(),DebugStub,
|
|
"Object '%s': dynamic property '%s' type '%s' is not supported",
|
|
YQT_OBJECT_NAME(obj),prop.c_str(),type.c_str());
|
|
}
|
|
}
|
|
|
|
// Find a QSystemTrayIcon child of an object
|
|
static inline QSystemTrayIcon* findSysTrayIcon(QObject* obj, const char* name)
|
|
{
|
|
return qFindChild<QSystemTrayIcon*>(obj,QtClient::setUtf8(name));
|
|
}
|
|
|
|
// Utility used to create an object's property if not found
|
|
// Add it to a list of strings
|
|
// Return true if the list changed
|
|
static bool createProperty(QObject* obj, const char* name, QVariant::Type t,
|
|
QtWindow* wnd, QStringList* list)
|
|
{
|
|
if (!obj || TelEngine::null(name))
|
|
return false;
|
|
QVariant var = obj->property(name);
|
|
if (var.type() == QVariant::Invalid)
|
|
obj->setProperty(name,QVariant(t));
|
|
else if (var.type() != t) {
|
|
if (wnd)
|
|
Debug(QtDriver::self(),DebugNote,
|
|
"Window(%s) child '%s' already has a %s property '%s' [%p]",
|
|
wnd->toString().c_str(),YQT_OBJECT_NAME(obj),var.typeName(),name,wnd);
|
|
return false;
|
|
}
|
|
if (!list)
|
|
return false;
|
|
QString s = QtClient::setUtf8(name);
|
|
if (list->contains(s))
|
|
return false;
|
|
*list << s;
|
|
return true;
|
|
}
|
|
|
|
// Replace file path in URLs in a character array
|
|
static void addFilePathUrl(QByteArray& a, const String& file)
|
|
{
|
|
if (!file)
|
|
return;
|
|
QString path = QDir::fromNativeSeparators(QtClient::setUtf8(file));
|
|
// Truncate after last path separator (lastIndexOf() returns -1 if not found)
|
|
path.truncate(path.lastIndexOf(QString("/")) + 1);
|
|
if (!path.size())
|
|
return;
|
|
int start = 0;
|
|
int end = -1;
|
|
while ((start = a.indexOf("url(",end + 1)) > 0) {
|
|
start += 4;
|
|
end = a.indexOf(")",start);
|
|
if (end <= start)
|
|
break;
|
|
// Add
|
|
int len = end - start;
|
|
QByteArray tmp = a.mid(start,len);
|
|
if (tmp.indexOf('/') != -1)
|
|
continue;
|
|
tmp.insert(0,path);
|
|
a.replace(start,len,tmp);
|
|
}
|
|
}
|
|
|
|
// Read data from file and append it to a string buffer
|
|
// Optionally append suffix characters to file name
|
|
static bool appendStyleSheet(QString& buf, const char* file,
|
|
const char* suffix1 = 0, const char* suffix2 = 0)
|
|
{
|
|
if (TelEngine::null(file))
|
|
return false;
|
|
String shf = file;
|
|
const char* oper = 0;
|
|
int pos = shf.rfind('/');
|
|
if (pos < 0)
|
|
pos = shf.rfind('\\');
|
|
if (pos < 0)
|
|
shf = Client::s_skinPath + shf;
|
|
int level = DebugNote;
|
|
if (!(TelEngine::null(suffix1) && TelEngine::null(suffix2))) {
|
|
level = DebugAll;
|
|
int dotPos = shf.rfind('.');
|
|
if (dotPos > pos) {
|
|
String tmp = shf.substr(0,dotPos);
|
|
tmp.append(suffix1,"_");
|
|
tmp.append(suffix2,"_");
|
|
shf = tmp + shf.substr(dotPos);
|
|
}
|
|
}
|
|
DDebug(ClientDriver::self(),DebugAll,"Loading stylesheet file '%s'",shf.c_str());
|
|
QFile f(QtClient::setUtf8(shf));
|
|
if (f.open(QIODevice::ReadOnly)) {
|
|
QByteArray a = f.readAll();
|
|
if (a.size()) {
|
|
addFilePathUrl(a,shf);
|
|
buf += QString::fromUtf8(a.constData());
|
|
}
|
|
else if (f.error() != QFile::NoError)
|
|
oper = "read";
|
|
}
|
|
else
|
|
oper = "open";
|
|
if (!oper)
|
|
return true;
|
|
Debug(ClientDriver::self(),level,"Failed to %s stylesheet file '%s': %d '%s'",
|
|
oper,shf.c_str(),f.error(),f.errorString().toUtf8().constData());
|
|
return false;
|
|
}
|
|
|
|
// Split an integer string list
|
|
// Result list length can be set by indicating a length
|
|
static QList<int> buildIntList(const String& buf, int len = 0)
|
|
{
|
|
QList<int> ret;
|
|
ObjList* list = buf.split(',');
|
|
int pos = 0;
|
|
ObjList* o = list;
|
|
while (o || pos < len) {
|
|
int val = 0;
|
|
if (o) {
|
|
if (o->get())
|
|
val = o->get()->toString().toInteger();
|
|
o = o->next();
|
|
}
|
|
ret.append(val);
|
|
pos++;
|
|
if (pos == len)
|
|
break;
|
|
}
|
|
TelEngine::destruct(list);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Qt4ClientFactory
|
|
*/
|
|
Qt4ClientFactory::Qt4ClientFactory(const char* name)
|
|
: UIFactory(name)
|
|
{
|
|
m_types.append(new String("QSound"));
|
|
}
|
|
|
|
// Build QSound
|
|
void* Qt4ClientFactory::create(const String& type, const char* name, NamedList* params)
|
|
{
|
|
if (type == YSTRING("QSound"))
|
|
return new QSound(QtClient::setUtf8(name));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* TableWidget
|
|
*/
|
|
TableWidget::TableWidget(QTableWidget* table, bool tmp)
|
|
: m_table(table), m_sortControl(-1)
|
|
{
|
|
if (!m_table)
|
|
return;
|
|
init(tmp);
|
|
}
|
|
|
|
TableWidget::TableWidget(QWidget* wid, const String& name, bool tmp)
|
|
: m_table(0), m_sortControl(-1)
|
|
{
|
|
if (wid)
|
|
m_table = qFindChild<QTableWidget*>(wid,QtClient::setUtf8(name));
|
|
if (!m_table)
|
|
return;
|
|
init(tmp);
|
|
}
|
|
|
|
TableWidget::TableWidget(QtWidget& table, bool tmp)
|
|
: m_table(static_cast<QTableWidget*>((QWidget*)table)), m_sortControl(-1)
|
|
{
|
|
if (m_table)
|
|
init(tmp);
|
|
}
|
|
|
|
TableWidget::~TableWidget()
|
|
{
|
|
if (!m_table)
|
|
return;
|
|
if (m_sortControl >= 0)
|
|
m_table->setSortingEnabled((bool)m_sortControl);
|
|
m_table->repaint();
|
|
}
|
|
|
|
// Add or set a row
|
|
void TableWidget::updateRow(const String& item, const NamedList* data, bool atStart)
|
|
{
|
|
int row = getRow(item);
|
|
// Add a new one ?
|
|
if (row < 0) {
|
|
row = atStart ? 0 : rowCount();
|
|
addRow(row);
|
|
setID(row,item);
|
|
}
|
|
// Update
|
|
if (data)
|
|
updateRow(row,*data);
|
|
}
|
|
|
|
// Update a row from a list of parameters
|
|
void TableWidget::updateRow(int row, const NamedList& data)
|
|
{
|
|
int ncol = columnCount();
|
|
for (int i = 0; i < ncol; i++) {
|
|
String header;
|
|
if (!getHeaderText(i,header))
|
|
continue;
|
|
NamedString* tmp = data.getParam(header);
|
|
if (tmp)
|
|
setCell(row,i,*tmp);
|
|
// Set image
|
|
tmp = data.getParam(header + "_image");
|
|
if (tmp)
|
|
setImage(row,i,*tmp);
|
|
}
|
|
// Init vertical header
|
|
String* rowText = data.getParam(YSTRING("row_text"));
|
|
String* rowImg = data.getParam(YSTRING("row_image"));
|
|
if (rowText || rowImg) {
|
|
QTableWidgetItem* item = m_table->verticalHeaderItem(row);
|
|
if (!item) {
|
|
item = new QTableWidgetItem;
|
|
m_table->setVerticalHeaderItem(row,item);
|
|
}
|
|
if (rowText)
|
|
item->setText(QtClient::setUtf8(*rowText));
|
|
if (rowImg)
|
|
item->setIcon(QIcon(QtClient::setUtf8(*rowImg)));
|
|
}
|
|
}
|
|
|
|
// Find a row by the first's column value. Return -1 if not found
|
|
int TableWidget::getRow(const String& item)
|
|
{
|
|
int n = rowCount();
|
|
for (int i = 0; i < n; i++) {
|
|
String val;
|
|
if (getCell(i,0,val) && item == val)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Find a column by its label. Return -1 if not found
|
|
int TableWidget::getColumn(const String& name, bool caseInsensitive)
|
|
{
|
|
int n = columnCount();
|
|
for (int i = 0; i < n; i++) {
|
|
String val;
|
|
if (!getHeaderText(i,val,false))
|
|
continue;
|
|
if ((caseInsensitive && (name &= val)) || (!caseInsensitive && name == val))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void TableWidget::init(bool tmp)
|
|
{
|
|
QtClient::getUtf8(m_name,m_table->objectName());
|
|
if (tmp) {
|
|
m_sortControl = m_table->isSortingEnabled() ? 1 : 0;
|
|
if (m_sortControl)
|
|
m_table->setSortingEnabled(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* UIBuffer
|
|
*/
|
|
// Remove from list. Release memory
|
|
void UIBuffer::destruct()
|
|
{
|
|
s_uiCache.remove(this,false);
|
|
if (m_buffer) {
|
|
delete m_buffer;
|
|
m_buffer = 0;
|
|
}
|
|
String::destruct();
|
|
}
|
|
|
|
// Return an already loaded UI. Load from file if not found.
|
|
// Add URLs paths when missing
|
|
UIBuffer* UIBuffer::build(const String& name)
|
|
{
|
|
// Check if already loaded from the same location
|
|
UIBuffer* buf = find(name);
|
|
if (buf)
|
|
return buf;
|
|
|
|
// Load
|
|
QFile file(QtClient::setUtf8(name));
|
|
file.open(QIODevice::ReadOnly);
|
|
QByteArray* qArray = new QByteArray;
|
|
*qArray = file.readAll();
|
|
file.close();
|
|
if (!qArray->size()) {
|
|
delete qArray;
|
|
return 0;
|
|
}
|
|
// Add URLs path when missing
|
|
addFilePathUrl(*qArray,name);
|
|
return new UIBuffer(name,qArray);
|
|
}
|
|
|
|
// Find a buffer
|
|
UIBuffer* UIBuffer::find(const String& name)
|
|
{
|
|
ObjList* o = s_uiCache.find(name);
|
|
return o ? static_cast<UIBuffer*>(o->get()) : 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* QtWindow
|
|
*/
|
|
QtWindow::QtWindow()
|
|
: m_x(0), m_y(0), m_width(0), m_height(0),
|
|
m_maximized(false), m_mainWindow(false), m_moving(false)
|
|
{
|
|
}
|
|
|
|
QtWindow::QtWindow(const char* name, const char* description, const char* alias, QtWindow* parent)
|
|
: QWidget(parent, Qt::Window),
|
|
Window(alias ? alias : name), m_description(description), m_oldId(name),
|
|
m_x(0), m_y(0), m_width(0), m_height(0),
|
|
m_maximized(false), m_mainWindow(false), m_moving(false)
|
|
{
|
|
setObjectName(QtClient::setUtf8(m_id));
|
|
}
|
|
|
|
QtWindow::~QtWindow()
|
|
{
|
|
// Update all hidden counter for tray icons owned by this window
|
|
QList<QSystemTrayIcon*> trayIcons = qFindChildren<QSystemTrayIcon*>(wndWidget());
|
|
if (trayIcons.size() > 0) {
|
|
if (s_allHiddenQuit >= (unsigned int)trayIcons.size())
|
|
s_allHiddenQuit -= trayIcons.size();
|
|
else {
|
|
Debug(QtDriver::self(),DebugFail,
|
|
"QtWindow(%s) destroyed with all hidden counter %u greater then tray icons %d [%p]",
|
|
m_id.c_str(),s_allHiddenQuit,trayIcons.size(),this);
|
|
s_allHiddenQuit = 0;
|
|
}
|
|
}
|
|
|
|
// Save settings
|
|
if (m_saveOnClose) {
|
|
m_maximized = isMaximized();
|
|
s_save.setValue(m_id,"maximized",String::boolText(m_maximized));
|
|
// Don't save position if maximized: keep the old one
|
|
if (!m_maximized) {
|
|
s_save.setValue(m_id,"x",m_x);
|
|
s_save.setValue(m_id,"y",m_y);
|
|
s_save.setValue(m_id,"width",m_width);
|
|
s_save.setValue(m_id,"height",m_height);
|
|
}
|
|
s_save.setValue(m_id,"visible",m_visible);
|
|
// Set dynamic properties to be saved for native QT objects
|
|
QList<QTableWidget*> tables = qFindChildren<QTableWidget*>(wndWidget());
|
|
for (int i = 0; i < tables.size(); i++) {
|
|
if (qobject_cast<QtTable*>(tables[i]))
|
|
continue;
|
|
// Column widths
|
|
unsigned int n = tables[i]->columnCount();
|
|
String widths;
|
|
for (unsigned int j = 0; j < n; j++)
|
|
widths.append(String(tables[i]->columnWidth(j)),",",true);
|
|
tables[i]->setProperty(s_propColWidths,QVariant(QtClient::setUtf8(widths)));
|
|
// Sorting
|
|
String sorting;
|
|
if (tables[i]->isSortingEnabled()) {
|
|
QHeaderView* h = tables[i]->horizontalHeader();
|
|
int col = h ? h->sortIndicatorSection() : -1;
|
|
if (col >= 0)
|
|
sorting << col << "," <<
|
|
String::boolText(Qt::AscendingOrder == h->sortIndicatorOrder());
|
|
}
|
|
tables[i]->setProperty(s_propSorting,QVariant(QtClient::setUtf8(sorting)));
|
|
}
|
|
QList<QSplitter*> spl = qFindChildren<QSplitter*>(wndWidget());
|
|
for (int i = 0; i < spl.size(); i++) {
|
|
String sizes;
|
|
QtClient::intList2str(sizes,spl[i]->sizes());
|
|
QtClient::setProperty(spl[i],s_propSizes,sizes);
|
|
}
|
|
// Save child objects properties
|
|
QList<QObject*> child = qFindChildren<QObject*>(wndWidget());
|
|
for (int i = 0; i < child.size(); i++) {
|
|
NamedList props("");
|
|
if (!QtClient::getProperty(child[i],s_propsSave,props))
|
|
continue;
|
|
unsigned int n = props.length();
|
|
for (unsigned int j = 0; j < n; j++) {
|
|
NamedString* ns = props.getParam(j);
|
|
if (ns && ns->name())
|
|
QtClient::saveProperty(child[i],ns->name(),this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set windows title
|
|
void QtWindow::title(const String& text)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::title(%s) [%p]",text.c_str(),this);
|
|
Window::title(text);
|
|
QWidget::setWindowTitle(QtClient::setUtf8(text));
|
|
}
|
|
|
|
void QtWindow::context(const String& text)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::context(%s) [%p]",text.c_str(),this);
|
|
m_context = text;
|
|
}
|
|
|
|
bool QtWindow::setParams(const NamedList& params)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setParams() [%p]",this);
|
|
|
|
setUpdatesEnabled(false);
|
|
// Check for custom widget params
|
|
if (params == YSTRING("customwidget")) {
|
|
// Each parameter is a list of parameters for a custom widget
|
|
// Parameter name is the widget's name
|
|
unsigned int n = params.length();
|
|
bool ok = true;
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = params.getParam(i);
|
|
NamedList* nl = static_cast<NamedList*>(ns ? ns->getObject("NamedList") : 0);
|
|
if (!(nl && ns->name()))
|
|
continue;
|
|
// Find the widget and set its params
|
|
QtWidget w(wndWidget(),ns->name());
|
|
if (w.type() == QtWidget::CustomTable)
|
|
ok = w.customTable()->setParams(*nl) && ok;
|
|
else if (w.type() == QtWidget::CustomWidget)
|
|
ok = w.customWidget()->setParams(*nl) && ok;
|
|
else if (w.type() == QtWidget::CustomObject)
|
|
ok = w.customObject()->setParams(*nl) && ok;
|
|
else
|
|
ok = false;
|
|
}
|
|
setUpdatesEnabled(true);
|
|
return ok;
|
|
}
|
|
// Check for system tray icon params
|
|
if (params == YSTRING("systemtrayicon")) {
|
|
// Each parameter is a list of parameters for a system tray icon
|
|
// Parameter name is the widget's name
|
|
// Parameter value indicates delete/create/set an existing one
|
|
unsigned int n = params.length();
|
|
bool ok = false;
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = params.getParam(i);
|
|
if (!(ns && ns->name()))
|
|
continue;
|
|
QSystemTrayIcon* trayIcon = findSysTrayIcon(wndWidget(),ns->name());
|
|
// Delete
|
|
if (ns->null()) {
|
|
if (trayIcon) {
|
|
// Reactivate program termination when the last window was hidden
|
|
if (s_allHiddenQuit)
|
|
s_allHiddenQuit--;
|
|
else
|
|
Debug(QtDriver::self(),DebugFail,
|
|
"QtWindow(%s) all hidden counter is 0 while deleting '%s' tray icon [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(trayIcon),this);
|
|
QtClient::deleteLater(trayIcon);
|
|
}
|
|
continue;
|
|
}
|
|
NamedList* nl = YOBJECT(NamedList,ns);
|
|
if (!nl)
|
|
continue;
|
|
// Create a new one if needed
|
|
if (!trayIcon) {
|
|
if (!ns->toBoolean())
|
|
continue;
|
|
trayIcon = new QSystemTrayIcon(wndWidget());
|
|
trayIcon->setObjectName(QtClient::setUtf8(ns->name()));
|
|
QtClient::connectObjects(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
|
this,SLOT(sysTrayIconAction(QSystemTrayIcon::ActivationReason)));
|
|
// Deactivate program termination when the last window was hidden
|
|
s_allHiddenQuit++;
|
|
}
|
|
ok = true;
|
|
// Add dynamic properties
|
|
// TODO: track the properties, clear the old ones if needed
|
|
addDynamicProps(trayIcon,*nl);
|
|
// Set icon and tooltip
|
|
NamedString* tmp = nl->getParam(YSTRING("icon"));
|
|
if (tmp && *tmp)
|
|
trayIcon->setIcon(QIcon(QtClient::setUtf8(*tmp)));
|
|
tmp = nl->getParam(YSTRING("tooltip"));
|
|
if (tmp && *tmp)
|
|
trayIcon->setToolTip(QtClient::setUtf8(*tmp));
|
|
// Check context menu
|
|
NamedString* menu = nl->getParam(YSTRING("menu"));
|
|
if (menu) {
|
|
QMenu* oldMenu = trayIcon->contextMenu();
|
|
if (oldMenu)
|
|
delete oldMenu;
|
|
NamedList* nlMenu = YOBJECT(NamedList,menu);
|
|
trayIcon->setContextMenu(nlMenu ? QtClient::buildMenu(*nlMenu,*menu,this,
|
|
SLOT(action()),SLOT(toggled(bool)),this) : 0);
|
|
}
|
|
if (nl->getBoolValue(YSTRING("show"),true))
|
|
trayIcon->setVisible(true);
|
|
}
|
|
setUpdatesEnabled(true);
|
|
return ok;
|
|
}
|
|
// Parameters for the widget whose name is the list name
|
|
if(params) {
|
|
QtWidget w(wndWidget(), params);
|
|
// Handle UIWidget descendants
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw) {
|
|
bool ok = uiw->setParams(params);
|
|
setUpdatesEnabled(true);
|
|
return ok;
|
|
}
|
|
if (w.type() == QtWidget::Calendar) {
|
|
int year = params.getIntValue(YSTRING("year"));
|
|
int month = params.getIntValue(YSTRING("month"));
|
|
int day = params.getIntValue(YSTRING("day"));
|
|
w.calendar()->setCurrentPage(year, month);
|
|
w.calendar()->setSelectedDate(QDate(year, month, day));
|
|
setUpdatesEnabled(true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Window or other parameters
|
|
if (params.getBoolValue(YSTRING("modal"))) {
|
|
if (parentWindow())
|
|
setWindowModality(Qt::WindowModal);
|
|
else
|
|
setWindowModality(Qt::ApplicationModal);
|
|
}
|
|
if (params.getBoolValue(YSTRING("minimized")))
|
|
QWidget::setWindowState(Qt::WindowMinimized);
|
|
bool ok = Window::setParams(params);
|
|
setUpdatesEnabled(true);
|
|
return ok;
|
|
}
|
|
|
|
void QtWindow::setOver(const Window* parent)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setOver(%p) [%p]",parent,this);
|
|
QWidget::raise();
|
|
}
|
|
|
|
bool QtWindow::hasElement(const String& name)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::hasElement(%s) [%p]",name.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
return w.valid();
|
|
}
|
|
|
|
bool QtWindow::setActive(const String& name, bool active)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setActive(%s,%s) [%p]",
|
|
name.c_str(),String::boolText(active),this);
|
|
bool ok = (name == m_id);
|
|
if (ok) {
|
|
if (QWidget::isMinimized())
|
|
QWidget::showNormal();
|
|
QWidget::activateWindow();
|
|
QWidget::raise();
|
|
}
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return ok;
|
|
if (w.type() != QtWidget::Action)
|
|
w->setEnabled(active);
|
|
else
|
|
w.action()->setEnabled(active);
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setFocus(const String& name, bool select)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setFocus(%s,%s) [%p]",
|
|
name.c_str(),String::boolText(select),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
w->setFocus();
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
if (w.combo()->isEditable() && select)
|
|
w.combo()->lineEdit()->selectAll();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setShow(const String& name, bool visible)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setShow(%s,%s) [%p]",
|
|
name.c_str(),String::boolText(visible),this);
|
|
// Check system tray icons
|
|
QSystemTrayIcon* trayIcon = findSysTrayIcon(this,name);
|
|
if (trayIcon) {
|
|
trayIcon->setVisible(visible);
|
|
return true;
|
|
}
|
|
// Widgets
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
setUpdatesEnabled(false);
|
|
if (w.type() != QtWidget::Action)
|
|
w->setVisible(visible);
|
|
else
|
|
w.action()->setVisible(visible);
|
|
setUpdatesEnabled(true);
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setText(const String& name, const String& text,
|
|
bool richText)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setText(%s,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),text.c_str(),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->setText(text,richText);
|
|
switch (w.type()) {
|
|
case QtWidget::CheckBox:
|
|
w.check()->setText(QtClient::setUtf8(text));
|
|
return true;
|
|
case QtWidget::LineEdit:
|
|
w.lineEdit()->setText(QtClient::setUtf8(text));
|
|
return true;
|
|
case QtWidget::TextBrowser:
|
|
case QtWidget::TextEdit:
|
|
if (richText) {
|
|
w.textEdit()->clear();
|
|
w.textEdit()->insertHtml(QtClient::setUtf8(text));
|
|
}
|
|
else
|
|
w.textEdit()->setText(QtClient::setUtf8(text));
|
|
{
|
|
QScrollBar* bar = w.textEdit()->verticalScrollBar();
|
|
if (bar)
|
|
bar->setSliderPosition(bar->maximum());
|
|
}
|
|
return true;
|
|
case QtWidget::Label:
|
|
w.label()->setText(QtClient::setUtf8(text));
|
|
return true;
|
|
case QtWidget::ComboBox:
|
|
if (w.combo()->lineEdit())
|
|
w.combo()->lineEdit()->setText(QtClient::setUtf8(text));
|
|
else
|
|
setSelect(name,text);
|
|
return true;
|
|
case QtWidget::Action:
|
|
w.action()->setText(QtClient::setUtf8(text));
|
|
return true;
|
|
case QtWidget::SpinBox:
|
|
w.spinBox()->setValue(text.toInteger());
|
|
return true;
|
|
}
|
|
|
|
// Handle some known base classes having a setText() method
|
|
if (w.inherits(QtWidget::AbstractButton))
|
|
w.abstractButton()->setText(QtClient::setUtf8(text));
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setCheck(const String& name, bool checked)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setCheck(%s,%s) [%p]",
|
|
name.c_str(),String::boolText(checked),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
if (w.inherits(QtWidget::AbstractButton))
|
|
w.abstractButton()->setChecked(checked);
|
|
else if (w.type() == QtWidget::Action)
|
|
w.action()->setChecked(checked);
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setSelect(const String& name, const String& item)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setSelect(%s,%s) [%p]",
|
|
name.c_str(),item.c_str(),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->setSelect(item);
|
|
|
|
int d = 0;
|
|
switch (w.type()) {
|
|
case QtWidget::Table:
|
|
{
|
|
TableWidget t(w);
|
|
int row = t.getRow(item);
|
|
if (row < 0)
|
|
return false;
|
|
t.table()->setCurrentCell(row,0);
|
|
return true;
|
|
}
|
|
case QtWidget::ComboBox:
|
|
if (item) {
|
|
d = w.findComboItem(item);
|
|
if (d < 0)
|
|
return false;
|
|
w.combo()->setCurrentIndex(d);
|
|
}
|
|
else if (w.combo()->lineEdit())
|
|
w.combo()->lineEdit()->setText("");
|
|
else
|
|
return false;
|
|
return true;
|
|
case QtWidget::ListBox:
|
|
d = w.findListItem(item);
|
|
if (d >= 0)
|
|
w.list()->setCurrentRow(d);
|
|
return d >= 0;
|
|
case QtWidget::Slider:
|
|
w.slider()->setValue(item.toInteger());
|
|
return true;
|
|
case QtWidget::StackWidget:
|
|
d = item.toInteger(-1);
|
|
while (d < 0) {
|
|
d = findStackedWidget(*(w.stackWidget()),item);
|
|
if (d >= 0)
|
|
break;
|
|
// Check for a default widget
|
|
String def = YQT_OBJECT_NAME(w.stackWidget());
|
|
def << "_default";
|
|
d = findStackedWidget(*(w.stackWidget()),def);
|
|
break;
|
|
}
|
|
if (d >= 0 && d < w.stackWidget()->count()) {
|
|
w.stackWidget()->setCurrentIndex(d);
|
|
return true;
|
|
}
|
|
return false;
|
|
case QtWidget::ProgressBar:
|
|
d = item.toInteger();
|
|
if (d >= w.progressBar()->minimum() && d <= w.progressBar()->maximum())
|
|
w.progressBar()->setValue(d);
|
|
else if (d < w.progressBar()->minimum())
|
|
w.progressBar()->setValue(w.progressBar()->minimum());
|
|
else
|
|
w.progressBar()->setValue(w.progressBar()->maximum());
|
|
return true;
|
|
case QtWidget::Tab:
|
|
d = w.tab()->count() - 1;
|
|
for (QString tmp = QtClient::setUtf8(item); d >= 0; d--) {
|
|
QWidget* wid = w.tab()->widget(d);
|
|
if (wid && wid->objectName() == tmp)
|
|
break;
|
|
}
|
|
if (d >= 0 && d < w.tab()->count()) {
|
|
w.tab()->setCurrentIndex(d);
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QtWindow::setUrgent(const String& name, bool urgent)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::setUrgent(%s,%s) [%p]",
|
|
name.c_str(),String::boolText(urgent),this);
|
|
|
|
if (name == m_id) {
|
|
QApplication::alert(this,0);
|
|
return true;
|
|
}
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
w->raise();
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::hasOption(const String& name, const String& item)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::hasOption(%s,%s) [%p]",
|
|
name.c_str(),item.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
return -1 != w.findComboItem(item);
|
|
case QtWidget::Table:
|
|
return getTableRow(name,item);
|
|
case QtWidget::ListBox:
|
|
return -1 != w.findListItem(item);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QtWindow::addOption(const String& name, const String& item, bool atStart,
|
|
const String& text)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) addOption(%s,%s,%s,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),
|
|
String::boolText(atStart),text.c_str(),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
w.addComboItem(item,atStart);
|
|
if (atStart && w.combo()->lineEdit())
|
|
w.combo()->lineEdit()->setText(w.combo()->itemText(0));
|
|
return true;
|
|
case QtWidget::Table:
|
|
return addTableRow(name,item,0,atStart);
|
|
case QtWidget::ListBox:
|
|
return w.addListItem(item,atStart);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QtWindow::delOption(const String& name, const String& item)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) delOption(%s,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),this);
|
|
return delTableRow(name,item);
|
|
}
|
|
|
|
bool QtWindow::getOptions(const String& name, NamedList* items)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) getOptions(%s,%p) [%p]",
|
|
m_id.c_str(),name.c_str(),items,this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
if (!items)
|
|
return true;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->getOptions(*items);
|
|
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
for (int i = 0; i < w.combo()->count(); i++)
|
|
QtClient::getUtf8(*items,"",w.combo()->itemText(i),false);
|
|
break;
|
|
case QtWidget::Table:
|
|
{
|
|
TableWidget t(w.table(),false);
|
|
for (int i = 0; i < t.rowCount(); i++) {
|
|
String item;
|
|
if (t.getCell(i,0,item) && item)
|
|
items->addParam(item,"");
|
|
}
|
|
}
|
|
break;
|
|
case QtWidget::ListBox:
|
|
for (int i = 0; i < w.list()->count(); i++) {
|
|
QListWidgetItem* tmp = w.list()->item(i);
|
|
if (tmp)
|
|
QtClient::getUtf8(*items,"",tmp->text(),false);
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Append or insert text lines to a widget
|
|
bool QtWindow::addLines(const String& name, const NamedList* lines, unsigned int max,
|
|
bool atStart)
|
|
{
|
|
DDebug(ClientDriver::self(),DebugAll,"QtWindow(%s) addLines('%s',%p,%u,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),lines,max,String::boolText(atStart),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
if (!lines)
|
|
return true;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->addLines(*lines,max,atStart);
|
|
unsigned int count = lines->length();
|
|
if (!count)
|
|
return true;
|
|
|
|
switch (w.type()) {
|
|
case QtWidget::TextBrowser:
|
|
case QtWidget::TextEdit:
|
|
// Limit the maximum number of paragraphs
|
|
if (max) {
|
|
QTextDocument* doc = w.textEdit()->document();
|
|
if (!doc)
|
|
return false;
|
|
doc->setMaximumBlockCount((int)max);
|
|
}
|
|
{
|
|
// FIXME: delete lines from begining if appending and the number
|
|
// of lines exceeds the maximum allowed
|
|
QString s = w.textEdit()->toPlainText();
|
|
int pos = atStart ? 0 : s.length();
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
NamedString* ns = lines->getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
if (ns->name().endsWith("\n"))
|
|
s.insert(pos,QtClient::setUtf8(ns->name()));
|
|
else {
|
|
String tmp = ns->name() + "\n";
|
|
s.insert(pos,QtClient::setUtf8(tmp));
|
|
pos++;
|
|
}
|
|
pos += (int)ns->name().length();
|
|
}
|
|
w.textEdit()->setText(s);
|
|
// Scroll down if added at end
|
|
if (!atStart) {
|
|
QScrollBar* bar = w.textEdit()->verticalScrollBar();
|
|
if (bar)
|
|
bar->setSliderPosition(bar->maximum());
|
|
}
|
|
}
|
|
return true;
|
|
case QtWidget::Table:
|
|
// TODO: implement
|
|
break;
|
|
case QtWidget::ComboBox:
|
|
if (atStart) {
|
|
for (unsigned int i = count; i >= 0; i--) {
|
|
NamedString* ns = lines->getParam(i);
|
|
if (ns)
|
|
w.combo()->insertItem(0,QtClient::setUtf8(ns->name()));
|
|
}
|
|
if (w.combo()->lineEdit())
|
|
w.combo()->lineEdit()->setText(w.combo()->itemText(0));
|
|
}
|
|
else {
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
NamedString* ns = lines->getParam(i);
|
|
if (ns)
|
|
w.combo()->addItem(QtClient::setUtf8(ns->name()));
|
|
}
|
|
}
|
|
return true;
|
|
case QtWidget::ListBox:
|
|
// TODO: implement
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QtWindow::addTableRow(const String& name, const String& item,
|
|
const NamedList* data, bool atStart)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) addTableRow(%s,%s,%p,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),data,String::boolText(atStart),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->addTableRow(item,data,atStart);
|
|
// Handle basic QTableWidget
|
|
if (w.type() != QtWidget::Table)
|
|
return false;
|
|
TableWidget tbl(w.table());
|
|
int row = atStart ? 0 : tbl.rowCount();
|
|
tbl.addRow(row);
|
|
// Set item (the first column) and the rest of data
|
|
tbl.setID(row,item);
|
|
if (data)
|
|
tbl.updateRow(row,*data);
|
|
return true;
|
|
}
|
|
|
|
// Insert or update multiple rows in a single operation
|
|
bool QtWindow::setMultipleRows(const String& name, const NamedList& data, const String& prefix)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setMultipleRows('%s',%p,'%s') [%p]",
|
|
m_id.c_str(),name.c_str(),&data,prefix.c_str(),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
return uiw && uiw->setMultipleRows(data,prefix);
|
|
}
|
|
|
|
// Insert a row into a table owned by this window
|
|
bool QtWindow::insertTableRow(const String& name, const String& item,
|
|
const String& before, const NamedList* data)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) insertTableRow(%s,%s,%s,%p) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),before.c_str(),data,this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->insertTableRow(item,before,data);
|
|
if (w.type() != QtWidget::Table)
|
|
return false;
|
|
TableWidget tbl(w.table());
|
|
int row = tbl.getRow(before);
|
|
if (row == -1)
|
|
row = tbl.rowCount();
|
|
tbl.addRow(row);
|
|
// Set item (the first column) and the rest of data
|
|
tbl.setID(row,item);
|
|
if (data)
|
|
tbl.updateRow(row,*data);
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::delTableRow(const String& name, const String& item)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::delTableRow(%s,%s) [%p]",
|
|
name.c_str(),item.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
int row = -1;
|
|
int n = 0;
|
|
switch (w.type()) {
|
|
case QtWidget::Table:
|
|
case QtWidget::CustomTable:
|
|
{
|
|
TableWidget tbl(w.table());
|
|
QtTable* custom = tbl.customTable();
|
|
if (custom) {
|
|
if (custom->delTableRow(item))
|
|
row = 0;
|
|
}
|
|
else {
|
|
row = tbl.getRow(item);
|
|
if (row >= 0)
|
|
tbl.delRow(row);
|
|
}
|
|
n = tbl.rowCount();
|
|
}
|
|
break;
|
|
case QtWidget::ComboBox:
|
|
row = w.findComboItem(item);
|
|
if (row >= 0) {
|
|
w.combo()->removeItem(row);
|
|
n = w.combo()->count();
|
|
}
|
|
break;
|
|
case QtWidget::ListBox:
|
|
row = w.findListItem(item);
|
|
if (row >= 0) {
|
|
QStringListModel* model = (QStringListModel*)w.list()->model();
|
|
if (!(model && model->removeRow(row)))
|
|
row = -1;
|
|
n = w.list()->count();
|
|
}
|
|
break;
|
|
default:
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw && uiw->delTableRow(item)) {
|
|
row = 0;
|
|
// Don't notify empty: we don't know it
|
|
n = 1;
|
|
}
|
|
}
|
|
if (row < 0)
|
|
return false;
|
|
if (!n)
|
|
raiseSelectIfEmpty(0,this,name);
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::setTableRow(const String& name, const String& item, const NamedList* data)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) setTableRow(%s,%s,%p) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),data,this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->setTableRow(item,data);
|
|
if (w.type() != QtWidget::Table)
|
|
return false;
|
|
TableWidget tbl(w.table());
|
|
int row = tbl.getRow(item);
|
|
if (row < 0)
|
|
return false;
|
|
if (data)
|
|
tbl.updateRow(row,*data);
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::getTableRow(const String& name, const String& item, NamedList* data)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::getTableRow(%s,%s,%p) [%p]",
|
|
name.c_str(),item.c_str(),data,this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->getTableRow(item,data);
|
|
if (w.type() != QtWidget::Table)
|
|
return false;
|
|
TableWidget tbl(w.table(),false);
|
|
int row = tbl.getRow(item);
|
|
if (row < 0)
|
|
return false;
|
|
if (!data)
|
|
return true;
|
|
int n = tbl.columnCount();
|
|
for (int i = 0; i < n; i++) {
|
|
String name;
|
|
if (!tbl.getHeaderText(i,name))
|
|
continue;
|
|
String value;
|
|
if (tbl.getCell(row,i,value))
|
|
data->setParam(name,value);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Set a table row or add a new one if not found
|
|
bool QtWindow::updateTableRow(const String& name, const String& item,
|
|
const NamedList* data, bool atStart)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) updateTableRow('%s','%s',%p,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),item.c_str(),data,String::boolText(atStart),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
switch (w.type()) {
|
|
case QtWidget::Table:
|
|
case QtWidget::CustomTable:
|
|
{
|
|
TableWidget tbl(w.table());
|
|
QtTable* custom = tbl.customTable();
|
|
if (custom) {
|
|
if (custom->getTableRow(item))
|
|
return custom->setTableRow(item,data);
|
|
return custom->addTableRow(item,data,atStart);
|
|
}
|
|
tbl.updateRow(item,data,atStart);
|
|
return true;
|
|
}
|
|
case QtWidget::CustomTree:
|
|
{
|
|
QtTree* custom = w.customTree();
|
|
if (custom) {
|
|
if (custom->getTableRow(item))
|
|
return custom->setTableRow(item,data);
|
|
return custom->addTableRow(item,data,atStart);
|
|
}
|
|
return false;
|
|
}
|
|
case QtWidget::ComboBox:
|
|
return w.findComboItem(item) >= 0 || w.addComboItem(item,atStart);
|
|
case QtWidget::ListBox:
|
|
return w.findListItem(item) >= 0 || w.addListItem(item,atStart);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Add or set one or more table row(s). Screen update is locked while changing the table.
|
|
// Each data list element is a NamedPointer carrying a NamedList with item parameters.
|
|
// The name of an element is the item to update.
|
|
// Element's value not empty: update the item
|
|
// Else: delete it
|
|
bool QtWindow::updateTableRows(const String& name, const NamedList* data, bool atStart)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) updateTableRows('%s',%p,%s) [%p]",
|
|
m_id.c_str(),name.c_str(),data,String::boolText(atStart),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw) {
|
|
bool ok = uiw->updateTableRows(data,atStart);
|
|
QtTable* ct = w.customTable();
|
|
if (ct)
|
|
raiseSelectIfEmpty(ct->rowCount(),this,name);
|
|
return ok;
|
|
}
|
|
if (w.type() != QtWidget::Table)
|
|
return false;
|
|
if (!data)
|
|
return true;
|
|
TableWidget tbl(w.table());
|
|
bool ok = true;
|
|
tbl.table()->setUpdatesEnabled(false);
|
|
ObjList add;
|
|
unsigned int n = data->length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
if (Client::exiting())
|
|
break;
|
|
// Get item and the list of parameters
|
|
NamedString* ns = data->getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
// Delete ?
|
|
if (ns->null()) {
|
|
int row = tbl.getRow(ns->name());
|
|
if (row >= 0)
|
|
tbl.delRow(row);
|
|
else
|
|
ok = false;
|
|
continue;
|
|
}
|
|
// Set existing row or postpone add
|
|
int row = tbl.getRow(ns->name());
|
|
if (row >= 0) {
|
|
const NamedList* params = YOBJECT(NamedList,ns);
|
|
if (params)
|
|
tbl.updateRow(row,*params);
|
|
}
|
|
else if (ns->toBoolean())
|
|
add.append(ns)->setDelete(false);
|
|
else
|
|
ok = false;
|
|
}
|
|
n = add.count();
|
|
if (n) {
|
|
int row = tbl.rowCount();
|
|
if (row < 0)
|
|
row = 0;
|
|
// Append if not requested to insert at start or table is empty
|
|
if (!(atStart && row))
|
|
tbl.table()->setRowCount(row + n);
|
|
else {
|
|
for (unsigned int i = 0; i < n; i++)
|
|
tbl.table()->insertRow(0);
|
|
}
|
|
for (ObjList* o = add.skipNull(); o; row++, o = o->skipNext()) {
|
|
NamedString* ns = static_cast<NamedString*>(o->get());
|
|
tbl.setID(row,ns->name());
|
|
const NamedList* params = YOBJECT(NamedList,ns);
|
|
if (params)
|
|
tbl.updateRow(row,*params);
|
|
}
|
|
}
|
|
tbl.table()->setUpdatesEnabled(true);
|
|
raiseSelectIfEmpty(tbl.rowCount(),this,name);
|
|
return ok;
|
|
}
|
|
|
|
bool QtWindow::clearTable(const String& name)
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow::clearTable(%s) [%p]",name.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->clearTable();
|
|
bool ok = true;
|
|
if (w.widget())
|
|
w->setUpdatesEnabled(false);
|
|
switch (w.type()) {
|
|
case QtWidget::Table:
|
|
w.table()->setRowCount(0);
|
|
break;
|
|
case QtWidget::TextBrowser:
|
|
case QtWidget::TextEdit:
|
|
w.textEdit()->clear();
|
|
break;
|
|
case QtWidget::ListBox:
|
|
w.list()->clear();
|
|
break;
|
|
case QtWidget::ComboBox:
|
|
w.combo()->clear();
|
|
break;
|
|
default:
|
|
ok = false;
|
|
}
|
|
if (w.widget())
|
|
w->setUpdatesEnabled(true);
|
|
return ok;
|
|
}
|
|
|
|
bool QtWindow::getText(const String& name, String& text, bool richText)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) getText(%s) [%p]",
|
|
m_id.c_str(),name.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->getText(text,richText);
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
QtClient::getUtf8(text,w.combo()->currentText());
|
|
return true;
|
|
case QtWidget::LineEdit:
|
|
QtClient::getUtf8(text,w.lineEdit()->text());
|
|
return true;
|
|
case QtWidget::TextBrowser:
|
|
case QtWidget::TextEdit:
|
|
if (!richText)
|
|
QtClient::getUtf8(text,w.textEdit()->toPlainText());
|
|
else
|
|
QtClient::getUtf8(text,w.textEdit()->toHtml());
|
|
return true;
|
|
case QtWidget::Label:
|
|
QtClient::getUtf8(text,w.label()->text());
|
|
return true;
|
|
case QtWidget::Action:
|
|
QtClient::getUtf8(text,w.action()->text());
|
|
return true;
|
|
case QtWidget::SpinBox:
|
|
text = w.spinBox()->value();
|
|
return true;
|
|
default:
|
|
if (w.inherits(QtWidget::AbstractButton)) {
|
|
QtClient::getUtf8(text,w.abstractButton()->text());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QtWindow::getCheck(const String& name, bool& checked)
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow::getCheck(%s) [%p]",name.c_str(),this);
|
|
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
if (w.inherits(QtWidget::AbstractButton))
|
|
checked = w.abstractButton()->isChecked();
|
|
else if (w.type() == QtWidget::Action)
|
|
checked = w.action()->isChecked();
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool QtWindow::getSelect(const String& name, String& item)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow::getSelect(%s) [%p]",name.c_str(),this);
|
|
QtWidget w(wndWidget(),name);
|
|
if (w.invalid())
|
|
return false;
|
|
UIWidget* uiw = w.uiWidget();
|
|
if (uiw)
|
|
return uiw->getSelect(item);
|
|
switch (w.type()) {
|
|
case QtWidget::ComboBox:
|
|
if (w.combo()->lineEdit() && w.combo()->lineEdit()->selectedText().isEmpty())
|
|
return false;
|
|
QtClient::getUtf8(item,w.combo()->currentText());
|
|
return true;
|
|
case QtWidget::Table:
|
|
{
|
|
TableWidget t(w);
|
|
int row = t.crtRow();
|
|
return row >= 0 ? t.getCell(row,0,item) : false;
|
|
}
|
|
case QtWidget::ListBox:
|
|
{
|
|
QListWidgetItem* crt = w.list()->currentItem();
|
|
if (!crt)
|
|
return false;
|
|
QtClient::getUtf8(item,crt->text());
|
|
}
|
|
return true;
|
|
case QtWidget::Slider:
|
|
item = w.slider()->value();
|
|
return true;
|
|
case QtWidget::ProgressBar:
|
|
item = w.progressBar()->value();
|
|
return true;
|
|
case QtWidget::Tab:
|
|
{
|
|
item = "";
|
|
QWidget* wid = w.tab()->currentWidget();
|
|
if (wid)
|
|
QtClient::getUtf8(item,wid->objectName());
|
|
}
|
|
return true;
|
|
case QtWidget::StackWidget:
|
|
{
|
|
item = "";
|
|
QWidget* wid = w.stackWidget()->currentWidget();
|
|
if (wid)
|
|
QtClient::getUtf8(item,wid->objectName());
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Build a menu from a list of parameters
|
|
bool QtWindow::buildMenu(const NamedList& params)
|
|
{
|
|
QWidget* parent = wndWidget();
|
|
// Retrieve the owner
|
|
const String& owner = params[YSTRING("owner")];
|
|
if (owner && owner != m_id) {
|
|
parent = qFindChild<QWidget*>(wndWidget(),QtClient::setUtf8(owner));
|
|
if (!parent) {
|
|
DDebug(QtDriver::self(),DebugNote,
|
|
"QtWindow(%s) buildMenu(%s) owner '%s' not found [%p]",
|
|
m_id.c_str(),params.c_str(),owner.c_str(),this);
|
|
return false;
|
|
}
|
|
}
|
|
QWidget* target = parent;
|
|
const String& t = params[YSTRING("target")];
|
|
if (t) {
|
|
target = qFindChild<QWidget*>(wndWidget(),QtClient::setUtf8(t));
|
|
if (!target) {
|
|
DDebug(QtDriver::self(),DebugNote,
|
|
"QtWindow(%s) buildMenu(%s) target '%s' not found [%p]",
|
|
m_id.c_str(),params.c_str(),t.c_str(),this);
|
|
return false;
|
|
}
|
|
}
|
|
// Remove existing menu
|
|
removeMenu(params);
|
|
QMenu* menu = QtClient::buildMenu(params,params.getValue(YSTRING("title"),params),this,
|
|
SLOT(action()),SLOT(toggled(bool)),parent);
|
|
if (!menu) {
|
|
DDebug(QtDriver::self(),DebugNote,
|
|
"QtWindow(%s) failed to build menu '%s' target='%s' [%p]",
|
|
m_id.c_str(),params.c_str(),YQT_OBJECT_NAME(target),this);
|
|
return false;
|
|
}
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) built menu '%s' target='%s' [%p]",
|
|
m_id.c_str(),params.c_str(),YQT_OBJECT_NAME(target),this);
|
|
QMenuBar* mbOwner = qobject_cast<QMenuBar*>(target);
|
|
QMenu* mOwner = !mbOwner ? qobject_cast<QMenu*>(target) : 0;
|
|
if (mbOwner || mOwner) {
|
|
QAction* before = 0;
|
|
const String& bef = params[YSTRING("before")];
|
|
// Retrieve the action to insert before
|
|
if (bef) {
|
|
QString cmp = QtClient::setUtf8(bef);
|
|
QList<QAction*> list = target->actions();
|
|
for (int i = 0; !before && i < list.size(); i++) {
|
|
// Check action name or menu name if the action is associated with a menu
|
|
if (list[i]->objectName() == cmp)
|
|
before = list[i];
|
|
else if (list[i]->menu() && list[i]->menu()->objectName() == cmp)
|
|
before = list[i]->menu()->menuAction();
|
|
if (before && i && list[i - 1]->isSeparator() &&
|
|
params.getBoolValue(YSTRING("before_separator"),true))
|
|
before = list[i - 1];
|
|
}
|
|
}
|
|
// Insert the menu
|
|
if (mbOwner)
|
|
mbOwner->insertMenu(before,menu);
|
|
else
|
|
mOwner->insertMenu(before,menu);
|
|
}
|
|
else {
|
|
QToolButton* tb = qobject_cast<QToolButton*>(target);
|
|
if (tb)
|
|
tb->setMenu(menu);
|
|
else {
|
|
QPushButton* pb = qobject_cast<QPushButton*>(target);
|
|
if (pb)
|
|
pb->setMenu(menu);
|
|
else if (!QtClient::setProperty(target,s_propContextMenu,params))
|
|
target->addAction(menu->menuAction());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Remove a menu
|
|
bool QtWindow::removeMenu(const NamedList& params)
|
|
{
|
|
QWidget* parent = wndWidget();
|
|
// Retrieve the owner
|
|
const String& owner = params[YSTRING("owner")];
|
|
if (owner && owner != m_id) {
|
|
parent = qFindChild<QWidget*>(wndWidget(),QtClient::setUtf8(owner));
|
|
if (!parent)
|
|
return false;
|
|
}
|
|
QMenu* menu = qFindChild<QMenu*>(parent,QtClient::setUtf8(params));
|
|
if (!menu)
|
|
return false;
|
|
QtClient::deleteLater(menu);
|
|
return true;
|
|
}
|
|
|
|
// Set an element's image
|
|
bool QtWindow::setImage(const String& name, const String& image, bool fit)
|
|
{
|
|
if (!name)
|
|
return false;
|
|
if (name == m_id)
|
|
return QtClient::setImage(this,image);
|
|
QObject* obj = qFindChild<QObject*>(wndWidget(),QtClient::setUtf8(name));
|
|
return obj && QtClient::setImage(obj,image,fit);
|
|
}
|
|
|
|
// Set a property for this window or for a widget owned by it
|
|
bool QtWindow::setProperty(const String& name, const String& item, const String& value)
|
|
{
|
|
if (name == m_id)
|
|
return QtClient::setProperty(wndWidget(),item,value);
|
|
QObject* obj = qFindChild<QObject*>(wndWidget(),QtClient::setUtf8(name));
|
|
return obj ? QtClient::setProperty(obj,item,value) : false;
|
|
}
|
|
|
|
// Get a property from this window or from a widget owned by it
|
|
bool QtWindow::getProperty(const String& name, const String& item, String& value)
|
|
{
|
|
if (name == m_id)
|
|
return QtClient::getProperty(wndWidget(),item,value);
|
|
QObject* obj = qFindChild<QObject*>(wndWidget(),QtClient::setUtf8(name));
|
|
return obj ? QtClient::getProperty(obj,item,value) : false;
|
|
}
|
|
|
|
void QtWindow::moveEvent(QMoveEvent* event)
|
|
{
|
|
QWidget::moveEvent(event);
|
|
// Don't update pos if not shown normal
|
|
if (!isShownNormal())
|
|
return;
|
|
m_x = pos().x();
|
|
m_y = pos().y();
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) moved x=%d y=%d [%p]",
|
|
m_id.c_str(),m_x,m_y,this);
|
|
}
|
|
|
|
void QtWindow::resizeEvent(QResizeEvent* event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
// Don't update size if not shown normal
|
|
if (!isShownNormal())
|
|
return;
|
|
m_width = width();
|
|
m_height = height();
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) resized width=%d height=%d [%p]",
|
|
m_id.c_str(),m_width,m_height,this);
|
|
}
|
|
|
|
bool QtWindow::event(QEvent* ev)
|
|
{
|
|
static const String s_activeChg("window_active_changed");
|
|
if (ev->type() == QEvent::WindowDeactivate) {
|
|
String hideProp;
|
|
QtClient::getProperty(wndWidget(),s_propHideInactive,hideProp);
|
|
if (hideProp && hideProp.toBoolean())
|
|
setVisible(false);
|
|
m_active = false;
|
|
Client::self()->toggle(this,s_activeChg,false);
|
|
}
|
|
else if (ev->type() == QEvent::WindowActivate) {
|
|
m_active = true;
|
|
Client::self()->toggle(this,s_activeChg,true);
|
|
}
|
|
else if (ev->type() == QEvent::ApplicationDeactivate) {
|
|
if (m_active) {
|
|
m_active = false;
|
|
Client::self()->toggle(this,s_activeChg,true);
|
|
}
|
|
}
|
|
return QWidget::event(ev);
|
|
}
|
|
|
|
void QtWindow::closeEvent(QCloseEvent* event)
|
|
{
|
|
// NOTE: Don't access window's data after calling hide():
|
|
// some logics might destroy the window when hidden
|
|
|
|
// Notify window closed
|
|
String tmp;
|
|
if (Client::self() &&
|
|
QtClient::getProperty(wndWidget(),"_yate_windowclosedaction",tmp))
|
|
Client::self()->action(this,tmp);
|
|
|
|
// Hide the window when requested
|
|
if (QtClient::getBoolProperty(wndWidget(),"_yate_hideonclose")) {
|
|
event->ignore();
|
|
hide();
|
|
return;
|
|
}
|
|
|
|
QWidget::closeEvent(event);
|
|
if (m_mainWindow && Client::self()) {
|
|
Client::self()->quit();
|
|
return;
|
|
}
|
|
if (QtClient::getBoolProperty(wndWidget(),"_yate_destroyonclose")) {
|
|
XDebug(QtDriver::self(),DebugAll,
|
|
"Window(%s) closeEvent() set delete later [%p]",m_id.c_str(),this);
|
|
QObject::deleteLater();
|
|
// Safe to call hide(): the window will be deleted when control returns
|
|
// to the main loop
|
|
}
|
|
hide();
|
|
}
|
|
|
|
void QtWindow::changeEvent(QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::WindowStateChange)
|
|
m_maximized = isMaximized();
|
|
QWidget::changeEvent(event);
|
|
}
|
|
|
|
void QtWindow::action()
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) action() sender=%s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
|
|
if (!QtClient::self() || QtClient::changing())
|
|
return;
|
|
String name;
|
|
NamedList* params = 0;
|
|
if (!QtClient::getBoolProperty(sender(),"_yate_translateidentity"))
|
|
QtClient::getIdentity(sender(),name);
|
|
else {
|
|
QtWidget w(sender());
|
|
translateName(w,name,¶ms);
|
|
}
|
|
if (name)
|
|
QtClient::self()->action(this,name,params);
|
|
TelEngine::destruct(params);
|
|
}
|
|
|
|
// Toggled actions
|
|
void QtWindow::toggled(bool on)
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) toggled=%s sender=%s [%p]",
|
|
m_id.c_str(),String::boolText(on),YQT_OBJECT_NAME(sender()),this);
|
|
QtClient::updateToggleImage(sender());
|
|
if (!QtClient::self() || QtClient::changing())
|
|
return;
|
|
QtWidget w(sender());
|
|
String name;
|
|
if (translateName(w,name))
|
|
QtClient::self()->toggle(this,name,on);
|
|
}
|
|
|
|
// System tray actions
|
|
void QtWindow::sysTrayIconAction(QSystemTrayIcon::ActivationReason reason)
|
|
{
|
|
String action;
|
|
switch (reason) {
|
|
case QSystemTrayIcon::Context:
|
|
QtClient::getProperty(sender(),s_propAction + "Context",action);
|
|
break;
|
|
case QSystemTrayIcon::DoubleClick:
|
|
QtClient::getProperty(sender(),s_propAction + "DoubleClick",action);
|
|
break;
|
|
case QSystemTrayIcon::Trigger:
|
|
QtClient::getProperty(sender(),s_propAction + "Trigger",action);
|
|
break;
|
|
case QSystemTrayIcon::MiddleClick:
|
|
QtClient::getProperty(sender(),s_propAction + "MiddleClick",action);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if (action)
|
|
Client::self()->action(this,action);
|
|
}
|
|
|
|
// Choose file window was accepted
|
|
void QtWindow::chooseFileAccepted()
|
|
{
|
|
QFileDialog* dlg = qobject_cast<QFileDialog*>(sender());
|
|
if (!dlg)
|
|
return;
|
|
String action;
|
|
QtClient::getUtf8(action,dlg->objectName());
|
|
if (!action)
|
|
return;
|
|
NamedList params("");
|
|
QDir dir = dlg->directory();
|
|
if (dir.absolutePath().length())
|
|
QtClient::getUtf8(params,"dir",fixPathSep(dir.absolutePath()));
|
|
QStringList files = dlg->selectedFiles();
|
|
for (int i = 0; i < files.size(); i++)
|
|
QtClient::getUtf8(params,"file",fixPathSep(files[i]));
|
|
if (dlg->fileMode() != QFileDialog::DirectoryOnly &&
|
|
dlg->fileMode() != QFileDialog::Directory) {
|
|
QString filter = dlg->selectedFilter();
|
|
if (filter.length())
|
|
QtClient::getUtf8(params,"filter",filter);
|
|
}
|
|
Client::self()->action(this,action,¶ms);
|
|
}
|
|
|
|
// Choose file window was cancelled
|
|
void QtWindow::chooseFileRejected()
|
|
{
|
|
QFileDialog* dlg = qobject_cast<QFileDialog*>(sender());
|
|
if (!dlg)
|
|
return;
|
|
String action;
|
|
QtClient::getUtf8(action,dlg->objectName());
|
|
if (!action)
|
|
return;
|
|
Client::self()->action(this,action,0);
|
|
}
|
|
|
|
void QtWindow::openUrl(const QString& link)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(link));
|
|
}
|
|
|
|
void QtWindow::doubleClick()
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) doubleClick() sender=%s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
|
|
if (QtClient::self() && sender())
|
|
Client::self()->action(this,YQT_OBJECT_NAME(sender()));
|
|
}
|
|
|
|
// A widget's selection changed
|
|
void QtWindow::selectionChanged()
|
|
{
|
|
XDebug(QtDriver::self(),DebugAll,"QtWindow(%s) selectionChanged() sender=%s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(sender()),this);
|
|
if (!(QtClient::self() && sender()))
|
|
return;
|
|
String name = YQT_OBJECT_NAME(sender());
|
|
QtWidget w(sender());
|
|
if (w.type() != QtWidget::Calendar) {
|
|
String item;
|
|
getSelect(name,item);
|
|
Client::self()->select(this,name,item);
|
|
}
|
|
else {
|
|
NamedList p("");
|
|
QDate d = w.calendar()->selectedDate();
|
|
p.addParam("year",String(d.year()));
|
|
p.addParam("month",String(d.month()));
|
|
p.addParam("day",String(d.day()));
|
|
Client::self()->action(this,name,&p);
|
|
}
|
|
}
|
|
|
|
// Connect an object's text changed signal to window's slot
|
|
bool QtWindow::connectTextChanged(QObject* obj)
|
|
{
|
|
if (!(obj && QtClient::getBoolProperty(obj,"_yate_textchangednotify")))
|
|
return false;
|
|
QComboBox* combo = qobject_cast<QComboBox*>(obj);
|
|
if (combo)
|
|
return QtClient::connectObjects(combo,SIGNAL(editTextChanged(const QString&)),
|
|
this,SLOT(textChanged(const QString&)));
|
|
QLineEdit* lineEdit = qobject_cast<QLineEdit*>(obj);
|
|
if (lineEdit)
|
|
return QtClient::connectObjects(lineEdit,SIGNAL(textChanged(const QString&)),
|
|
this,SLOT(textChanged(const QString&)));
|
|
QTextEdit* textEdit = qobject_cast<QTextEdit*>(obj);
|
|
if (textEdit)
|
|
return QtClient::connectObjects(textEdit,SIGNAL(textChanged()),
|
|
this,SLOT(textChanged()));
|
|
const QMetaObject* meta = obj->metaObject();
|
|
Debug(DebugStub,"connectTextChanged() not implemented for class '%s'",
|
|
meta ? meta->className() : "");
|
|
return false;
|
|
}
|
|
|
|
// Notify text changed to the client
|
|
void QtWindow::notifyTextChanged(QObject* obj, const QString& text)
|
|
{
|
|
if (!(obj && QtClient::getBoolProperty(obj,"_yate_textchangednotify")))
|
|
return;
|
|
// Detect QtUIWidget item. Get its container identity if found
|
|
String item;
|
|
QtUIWidget::getListItemProp(obj,item);
|
|
QtUIWidget* uiw = item ? QtUIWidget::container(obj) : 0;
|
|
String name;
|
|
if (!uiw)
|
|
QtClient::getIdentity(obj,name);
|
|
else
|
|
uiw->getIdentity(obj,name);
|
|
if (!name)
|
|
return;
|
|
NamedList p("");
|
|
p.addParam("sender",name);
|
|
if (text.size())
|
|
QtClient::getUtf8(p,"text",text);
|
|
Client::self()->action(this,YSTRING("textchanged"),&p);
|
|
}
|
|
|
|
// Load a widget from file
|
|
QWidget* QtWindow::loadUI(const char* fileName, QWidget* parent,
|
|
const char* uiName, const char* path)
|
|
{
|
|
if (Client::exiting())
|
|
return 0;
|
|
if (!(fileName && *fileName && parent))
|
|
return 0;
|
|
|
|
if (!(path && *path))
|
|
path = Client::s_skinPath.c_str();
|
|
UIBuffer* buf = UIBuffer::build(fileName);
|
|
const char* err = 0;
|
|
if (buf && buf->buffer()) {
|
|
QBuffer b(buf->buffer());
|
|
QUiLoader loader;
|
|
loader.setWorkingDirectory(QDir(QtClient::setUtf8(path)));
|
|
QWidget* w = loader.load(&b,parent);
|
|
if (w)
|
|
return w;
|
|
err = "loader failed";
|
|
}
|
|
else
|
|
err = buf ? "file is empty" : "file not found";
|
|
// Error
|
|
TelEngine::destruct(buf);
|
|
Debug(DebugWarn,"Failed to load widget '%s' file='%s' path='%s': %s",
|
|
uiName,fileName,path,err);
|
|
return 0;
|
|
}
|
|
|
|
// Clear the UI cache
|
|
void QtWindow::clearUICache(const char* fileName)
|
|
{
|
|
if (!fileName)
|
|
UIBuffer::s_uiCache.clear();
|
|
else
|
|
TelEngine::destruct(UIBuffer::s_uiCache.find(fileName));
|
|
}
|
|
|
|
// Filter events
|
|
bool QtWindow::eventFilter(QObject* obj, QEvent* event)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
// Apply dynamic properties changes
|
|
if (event->type() == QEvent::DynamicPropertyChange) {
|
|
String name = YQT_OBJECT_NAME(obj);
|
|
QDynamicPropertyChangeEvent* ev = static_cast<QDynamicPropertyChangeEvent*>(event);
|
|
String prop = ev->propertyName().constData();
|
|
// Handle only yate dynamic properties
|
|
if (!prop.startsWith(s_yatePropPrefix,false))
|
|
return QWidget::eventFilter(obj,event);
|
|
XDebug(QtDriver::self(),DebugAll,"Window(%s) eventFilter(%s) prop=%s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(obj),prop.c_str(),this);
|
|
// Return false for now on: it's our property
|
|
QtWidget w(obj);
|
|
if (w.invalid())
|
|
return false;
|
|
String value;
|
|
if (!QtClient::getProperty(obj,prop,value))
|
|
return false;
|
|
bool ok = true;
|
|
bool handled = true;
|
|
if (prop == s_propColWidths) {
|
|
if (w.type() == QtWidget::Table) {
|
|
QHeaderView* hdr = w.table()->horizontalHeader();
|
|
bool skipLast = hdr && hdr->stretchLastSection();
|
|
ObjList* list = value.split(',',false);
|
|
int col = 0;
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext(), col++) {
|
|
if (skipLast && col == w.table()->columnCount() - 1)
|
|
break;
|
|
int width = (static_cast<String*>(o->get()))->toInteger(-1);
|
|
if (width >= 0)
|
|
w.table()->setColumnWidth(col,width);
|
|
}
|
|
TelEngine::destruct(list);
|
|
}
|
|
}
|
|
else if (prop == s_propSorting) {
|
|
if (w.type() == QtWidget::Table) {
|
|
ObjList* list = value.split(',',false);
|
|
String* tmp = static_cast<String*>((*list)[0]);
|
|
int col = tmp ? tmp->toInteger(-1) : -1;
|
|
if (col >= 0) {
|
|
tmp = static_cast<String*>((*list)[1]);
|
|
bool asc = tmp ? tmp->toBoolean(true) : true;
|
|
w.table()->sortItems(col,asc ? Qt::AscendingOrder : Qt::DescendingOrder);
|
|
}
|
|
TelEngine::destruct(list);
|
|
}
|
|
}
|
|
else if (prop == s_propSizes) {
|
|
if (w.type() == QtWidget::Splitter) {
|
|
QList<int> list = QtClient::str2IntList(value);
|
|
w.splitter()->setSizes(list);
|
|
}
|
|
}
|
|
else if (prop == s_propWindowFlags) {
|
|
QWidget* wid = (name == m_id || name == m_oldId) ? this : w.widget();
|
|
QtClient::applyWindowFlags(wid,value);
|
|
}
|
|
else if (prop == s_propHHeader) {
|
|
// Show/hide the horizontal header
|
|
ok = ((w.type() == QtWidget::Table || w.type() == QtWidget::CustomTable) &&
|
|
value.isBoolean() && w.table()->horizontalHeader());
|
|
if (ok)
|
|
w.table()->horizontalHeader()->setVisible(value.toBoolean());
|
|
}
|
|
else
|
|
ok = handled = false;
|
|
if (ok)
|
|
DDebug(ClientDriver::self(),DebugAll,
|
|
"Applied dynamic property %s='%s' for object='%s'",
|
|
prop.c_str(),value.c_str(),name.c_str());
|
|
else if (handled)
|
|
Debug(ClientDriver::self(),DebugMild,
|
|
"Failed to apply dynamic property %s='%s' for object='%s'",
|
|
prop.c_str(),value.c_str(),name.c_str());
|
|
return false;
|
|
}
|
|
if (event->type() == QEvent::KeyPress) {
|
|
String action;
|
|
bool filter = false;
|
|
if (!QtClient::filterKeyEvent(obj,static_cast<QKeyEvent*>(event),
|
|
action,filter,this))
|
|
return QWidget::eventFilter(obj,event);
|
|
if (action && Client::self())
|
|
Client::self()->action(this,action);
|
|
return filter;
|
|
}
|
|
if (event->type() == QEvent::ContextMenu) {
|
|
if (handleContextMenuEvent(static_cast<QContextMenuEvent*>(event),obj))
|
|
return false;
|
|
}
|
|
if (event->type() == QEvent::Enter) {
|
|
QtClient::updateImageFromMouse(obj,true,true);
|
|
return QWidget::eventFilter(obj,event);
|
|
}
|
|
if (event->type() == QEvent::Leave) {
|
|
QtClient::updateImageFromMouse(obj,true,false);
|
|
return QWidget::eventFilter(obj,event);
|
|
}
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
QtClient::updateImageFromMouse(obj,false,true);
|
|
return QWidget::eventFilter(obj,event);
|
|
}
|
|
if (event->type() == QEvent::MouseButtonRelease) {
|
|
QtClient::updateImageFromMouse(obj,false,false);
|
|
return QWidget::eventFilter(obj,event);
|
|
}
|
|
return QWidget::eventFilter(obj,event);
|
|
}
|
|
|
|
// Handle key pressed events
|
|
void QtWindow::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
if (!(Client::self() && event)) {
|
|
QWidget::keyPressEvent(event);
|
|
return;
|
|
}
|
|
QVariant var = wndWidget()->property("_yate_keypress_redirect");
|
|
QString child = var.toString();
|
|
if (child.size() > 0 && QtClient::sendEvent(*event,this,child)) {
|
|
QWidget* wid = qFindChild<QWidget*>(wndWidget(),child);
|
|
if (wid)
|
|
wid->setFocus();
|
|
return;
|
|
}
|
|
if (event->key() == Qt::Key_Backspace)
|
|
Client::self()->backspace(m_id,this);
|
|
QWidget::keyPressEvent(event);
|
|
}
|
|
|
|
// Show hide window. Notify the client
|
|
void QtWindow::setVisible(bool visible)
|
|
{
|
|
// Override position for notification windows
|
|
if (visible && isShownNormal() &&
|
|
QtClient::getBoolProperty(wndWidget(),"_yate_notificationwindow")) {
|
|
// Detect unavailable screen space position and move the window in the apropriate position
|
|
// bottom/right/none: move it in the right/bottom corner.
|
|
// top: move it in the right/top corner.
|
|
// left: move it in the left/bottom corner.
|
|
int pos = QtClient::PosNone;
|
|
if (QtClient::getScreenUnavailPos(this,pos)) {
|
|
if (0 != (pos & (QtClient::PosBottom | QtClient::PosRight)) || pos == QtClient::PosNone)
|
|
QtClient::moveWindow(this,QtClient::CornerBottomRight);
|
|
else if (0 != (pos & QtClient::PosTop))
|
|
QtClient::moveWindow(this,QtClient::CornerTopRight);
|
|
else
|
|
QtClient::moveWindow(this,QtClient::CornerBottomLeft);
|
|
}
|
|
}
|
|
if (visible && isMinimized())
|
|
showNormal();
|
|
else
|
|
QWidget::setVisible(visible);
|
|
// Notify the client on window visibility changes
|
|
bool changed = (m_visible != visible);
|
|
m_visible = visible;
|
|
if (changed && Client::self()) {
|
|
QVariant var;
|
|
if (wndWidget())
|
|
var = wndWidget()->property("dynamicUiActionVisibleChanged");
|
|
if (!var.toBool())
|
|
Client::self()->toggle(this,YSTRING("window_visible_changed"),m_visible);
|
|
else {
|
|
Message* m = new Message("ui.action");
|
|
m->addParam("action","window_visible_changed");
|
|
m->addParam("visible",String::boolText(m_visible));
|
|
m->addParam("window",m_id);
|
|
Engine::enqueue(m);
|
|
}
|
|
}
|
|
if (!m_visible && QtClient::getBoolProperty(wndWidget(),"_yate_destroyonhide")) {
|
|
DDebug(QtDriver::self(),DebugAll,
|
|
"Window(%s) setVisible(false) set delete later [%p]",m_id.c_str(),this);
|
|
QObject::deleteLater();
|
|
}
|
|
// Destroy owned dialogs
|
|
if (!m_visible) {
|
|
QList<QDialog*> d = qFindChildren<QDialog*>(this);
|
|
for (int i = 0; i < d.size(); i++)
|
|
d[i]->deleteLater();
|
|
}
|
|
}
|
|
|
|
// Show the window
|
|
void QtWindow::show()
|
|
{
|
|
setVisible(true);
|
|
m_maximized = m_maximized || isMaximized();
|
|
if (m_maximized)
|
|
setWindowState(Qt::WindowMaximized);
|
|
}
|
|
|
|
// Hide the window
|
|
void QtWindow::hide()
|
|
{
|
|
setVisible(false);
|
|
}
|
|
|
|
void QtWindow::size(int width, int height)
|
|
{
|
|
Debug(QtDriver::self(),DebugStub,"QtWindow(%s)::size(%d,%d) [%p]",m_id.c_str(),width,height,this);
|
|
}
|
|
|
|
void QtWindow::move(int x, int y)
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s)::move(%d,%d) [%p]",m_id.c_str(),x,y,this);
|
|
QWidget::move(x,y);
|
|
}
|
|
|
|
void QtWindow::moveRel(int dx, int dy)
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow::moveRel(%d,%d) [%p]",dx,dy,this);
|
|
}
|
|
|
|
bool QtWindow::related(const Window* wnd) const
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow::related(%p) [%p]",wnd,this);
|
|
return false;
|
|
}
|
|
|
|
void QtWindow::menu(int x, int y)
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow::menu(%d,%d) [%p]",x,y,this);
|
|
}
|
|
|
|
// Create a modal dialog
|
|
bool QtWindow::createDialog(const String& name, const String& title, const String& alias,
|
|
const NamedList* params)
|
|
{
|
|
QtDialog* d = new QtDialog(wndWidget());
|
|
if (d->show(name,title,alias,params))
|
|
return true;
|
|
d->deleteLater();
|
|
return false;
|
|
}
|
|
|
|
// Destroy a modal dialog
|
|
bool QtWindow::closeDialog(const String& name)
|
|
{
|
|
QDialog* d = qFindChild<QDialog*>(this,QtClient::setUtf8(name));
|
|
if (!d)
|
|
return false;
|
|
d->deleteLater();
|
|
return true;
|
|
}
|
|
|
|
// Load UI file and setup the window
|
|
void QtWindow::doPopulate()
|
|
{
|
|
Debug(QtDriver::self(),DebugAll,"Populating window '%s' [%p]",m_id.c_str(),this);
|
|
QWidget* formWidget = loadUI(m_description,this,m_id);
|
|
if (!formWidget)
|
|
return;
|
|
// Set window title decoration flags to avoid pos/size troubles with late decoration
|
|
QVariant var = formWidget->property(s_propWindowFlags);
|
|
if (var.type() == QVariant::Invalid) {
|
|
String flgs = "title,sysmenu,minimize,close";
|
|
// Add maximize only if allowed
|
|
if (formWidget->maximumWidth() == QWIDGETSIZE_MAX ||
|
|
formWidget->maximumHeight() == QWIDGETSIZE_MAX)
|
|
flgs.append("maximize",",");
|
|
formWidget->setProperty(s_propWindowFlags,QVariant(QtClient::setUtf8(flgs)));
|
|
}
|
|
setMinimumSize(formWidget->minimumSize().width(),formWidget->minimumSize().height());
|
|
setMaximumSize(formWidget->maximumSize().width(),formWidget->maximumSize().height());
|
|
m_x = formWidget->pos().x();
|
|
m_y = formWidget->pos().y();
|
|
m_width = formWidget->width();
|
|
m_height = formWidget->height();
|
|
move(m_x,m_y);
|
|
QWidget::resize(m_width,m_height);
|
|
QtClient::setWidget(this,formWidget);
|
|
m_widget = YQT_OBJECT_NAME(formWidget);
|
|
String wTitle;
|
|
QtClient::getUtf8(wTitle,formWidget->windowTitle());
|
|
title(wTitle);
|
|
setWindowIcon(formWidget->windowIcon());
|
|
setStyleSheet(formWidget->styleSheet());
|
|
}
|
|
|
|
// Initialize window
|
|
void QtWindow::doInit()
|
|
{
|
|
DDebug(QtDriver::self(),DebugAll,"Initializing window '%s' [%p]",
|
|
m_id.c_str(),this);
|
|
|
|
// Create window's dynamic properties from config
|
|
Configuration cfg(Engine::configFile(m_oldId),false);
|
|
NamedList* sectGeneral = cfg.getSection("general");
|
|
if (sectGeneral)
|
|
addDynamicProps(wndWidget(),*sectGeneral);
|
|
|
|
// Load window data
|
|
m_mainWindow = s_cfg.getBoolValue(m_oldId,"mainwindow");
|
|
m_saveOnClose = s_cfg.getBoolValue(m_oldId,"save",true);
|
|
if (m_id != m_oldId)
|
|
m_saveOnClose = s_cfg.getBoolValue(m_oldId,"savealias",m_saveOnClose);
|
|
NamedList* sect = s_save.getSection(m_id);
|
|
if (sect) {
|
|
m_maximized = sect->getBoolValue("maximized");
|
|
m_x = sect->getIntValue("x",m_x);
|
|
m_y = sect->getIntValue("y",m_y);
|
|
m_width = sect->getIntValue("width",m_width);
|
|
m_height = sect->getIntValue("height",m_height);
|
|
m_visible = sect->getBoolValue("visible");
|
|
}
|
|
else {
|
|
if (m_saveOnClose)
|
|
Debug(QtDriver::self(),DebugNote,"Window(%s) not found in config [%p]",
|
|
m_id.c_str(),this);
|
|
m_visible = s_cfg.getBoolValue(m_oldId,"visible");
|
|
// Make sure the window is shown in the available geometry
|
|
QDesktopWidget* d = QApplication::desktop();
|
|
if (d) {
|
|
QRect r = d->availableGeometry(this);
|
|
m_x = r.x();
|
|
m_y = r.y();
|
|
}
|
|
}
|
|
m_visible = m_mainWindow || m_visible;
|
|
if (!m_width)
|
|
m_width = wndWidget()->width();
|
|
if (!m_height)
|
|
m_height = wndWidget()->height();
|
|
move(m_x,m_y);
|
|
QWidget::resize(m_width,m_height);
|
|
|
|
// Build custom UI widgets from frames owned by this widget
|
|
QtClient::buildFrameUiWidgets(this);
|
|
|
|
// Create custom widgets from
|
|
// _yate_identity=customwidget|[separator=sep|] sep widgetclass sep widgetname [sep param=value]
|
|
QList<QFrame*> frm = qFindChildren<QFrame*>(wndWidget());
|
|
for (int i = 0; i < frm.size(); i++) {
|
|
String create;
|
|
QtClient::getProperty(frm[i],"_yate_identity",create);
|
|
if (!create.startSkip("customwidget|",false))
|
|
continue;
|
|
char sep = '|';
|
|
// Check if we have another separator
|
|
if (create.startSkip("separator=",false)) {
|
|
if (create.length() < 2)
|
|
continue;
|
|
sep = create.at(0);
|
|
create = create.substr(2);
|
|
}
|
|
ObjList* list = create.split(sep,false);
|
|
String type;
|
|
String name;
|
|
NamedList params("");
|
|
int what = 0;
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext(), what++) {
|
|
GenObject* p = o->get();
|
|
if (what == 0)
|
|
type = p->toString();
|
|
else if (what == 1)
|
|
name = p->toString();
|
|
else {
|
|
// Decode param
|
|
int pos = p->toString().find('=');
|
|
if (pos != -1)
|
|
params.addParam(p->toString().substr(0,pos),p->toString().substr(pos + 1));
|
|
}
|
|
}
|
|
TelEngine::destruct(list);
|
|
params.addParam("parentwindow",m_id);
|
|
NamedString* pw = new NamedString("parentwidget");
|
|
QtClient::getUtf8(*pw,frm[i]->objectName());
|
|
params.addParam(pw);
|
|
QObject* obj = (QObject*)UIFactory::build(type,name,¶ms);
|
|
if (!obj)
|
|
continue;
|
|
QWidget* wid = qobject_cast<QWidget*>(obj);
|
|
if (wid)
|
|
QtClient::setWidget(frm[i],wid);
|
|
else {
|
|
obj->setParent(frm[i]);
|
|
QtCustomObject* customObj = qobject_cast<QtCustomObject*>(obj);
|
|
if (customObj)
|
|
customObj->parentChanged();
|
|
}
|
|
}
|
|
|
|
// Add the first menubar to layout
|
|
QList<QMenuBar*> menuBars = qFindChildren<QMenuBar*>(wndWidget());
|
|
if (menuBars.size() && layout()) {
|
|
layout()->setMenuBar(menuBars[0]);
|
|
// Decrease minimum size policy to make sure the layout is made properly
|
|
int h = menuBars[0]->height();
|
|
int min = wndWidget()->minimumHeight();
|
|
if (min > h)
|
|
wndWidget()->setMinimumHeight(min - h);
|
|
else
|
|
wndWidget()->setMinimumHeight(0);
|
|
|
|
#ifdef Q_WS_MAC
|
|
if (m_mainWindow) {
|
|
// Create a parentless menu bar to be set as the default application menu by copying it from the main window menu
|
|
DDebug(QtDriver::self(),DebugAll,"Setting as default menu bar the menu bar of window '%s' [%p]",
|
|
m_id.c_str(),this);
|
|
QMenuBar* mainMenu = menuBars[0];
|
|
QMenuBar* defaultMenu = new QMenuBar(0);
|
|
QList<QAction*> topActions = mainMenu->actions();
|
|
for (int i = 0; i < topActions.count(); i++) {
|
|
QMenu* menu = topActions[i]->menu();
|
|
if (menu) {
|
|
QMenu* m = new QMenu(menu->title(),defaultMenu);
|
|
String tmp;
|
|
QtClient::getProperty(menu,YSTRING("_yate_menuNoCopy"),tmp);
|
|
if (tmp.toBoolean())
|
|
continue;
|
|
defaultMenu->addMenu(m);
|
|
QList<QAction*> actions = menu->actions();
|
|
for (int j = 0; j < actions.count(); j++) {
|
|
QAction* act = actions[j];
|
|
tmp.clear();
|
|
QtClient::getProperty(act,YSTRING("_yate_menuNoCopy"),tmp);
|
|
if (tmp.toBoolean())
|
|
continue;
|
|
m->addAction(act);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Create window's children dynamic properties from config
|
|
unsigned int n = cfg.sections();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedList* sect = cfg.getSection(i);
|
|
if (sect && *sect && *sect != "general")
|
|
addDynamicProps(qFindChild<QObject*>(this,sect->c_str()),*sect);
|
|
}
|
|
|
|
// Process "_yate_setaction" property for our children
|
|
QtClient::setAction(this);
|
|
|
|
// Connect actions' signal
|
|
QList<QAction*> actions = qFindChildren<QAction*>(wndWidget());
|
|
for (int i = 0; i < actions.size(); i++) {
|
|
String addToWidget;
|
|
QtClient::getProperty(actions[i],"dynamicAddToParent",addToWidget);
|
|
if (addToWidget && addToWidget.toBoolean())
|
|
QWidget::addAction(actions[i]);
|
|
if (actions[i]->isCheckable())
|
|
QtClient::connectObjects(actions[i],SIGNAL(toggled(bool)),this,SLOT(toggled(bool)));
|
|
else
|
|
QtClient::connectObjects(actions[i],SIGNAL(triggered()),this,SLOT(action()));
|
|
}
|
|
|
|
// Connect combo boxes signals
|
|
QList<QComboBox*> combos = qFindChildren<QComboBox*>(wndWidget());
|
|
for (int i = 0; i < combos.size(); i++) {
|
|
QtClient::connectObjects(combos[i],SIGNAL(activated(int)),this,SLOT(selectionChanged()));
|
|
connectTextChanged(combos[i]);
|
|
}
|
|
|
|
// Connect abstract buttons (check boxes and radio/push/tool buttons) signals
|
|
QList<QAbstractButton*> buttons = qFindChildren<QAbstractButton*>(wndWidget());
|
|
for(int i = 0; i < buttons.size(); i++)
|
|
if (QtClient::autoConnect(buttons[i]))
|
|
connectButton(buttons[i]);
|
|
|
|
// Connect group boxes signals
|
|
QList<QGroupBox*> grp = qFindChildren<QGroupBox*>(wndWidget());
|
|
for(int i = 0; i < grp.size(); i++)
|
|
if (grp[i]->isCheckable())
|
|
QtClient::connectObjects(grp[i],SIGNAL(toggled(bool)),this,SLOT(toggled(bool)));
|
|
|
|
// Connect sliders signals
|
|
QList<QSlider*> sliders = qFindChildren<QSlider*>(wndWidget());
|
|
for (int i = 0; i < sliders.size(); i++)
|
|
QtClient::connectObjects(sliders[i],SIGNAL(valueChanged(int)),this,SLOT(selectionChanged()));
|
|
|
|
// Connect calendar widget signals
|
|
QList<QCalendarWidget*> cals = qFindChildren<QCalendarWidget*>(wndWidget());
|
|
for (int i = 0; i < cals.size(); i++)
|
|
QtClient::connectObjects(cals[i],SIGNAL(selectionChanged()),this,SLOT(selectionChanged()));
|
|
|
|
// Connect list boxes signals
|
|
QList<QListWidget*> lists = qFindChildren<QListWidget*>(wndWidget());
|
|
for (int i = 0; i < lists.size(); i++) {
|
|
QtClient::connectObjects(lists[i],SIGNAL(itemDoubleClicked(QListWidgetItem*)),
|
|
this,SLOT(doubleClick()));
|
|
QtClient::connectObjects(lists[i],SIGNAL(itemActivated(QListWidgetItem*)),
|
|
this,SLOT(doubleClick()));
|
|
QtClient::connectObjects(lists[i],SIGNAL(currentRowChanged(int)),
|
|
this,SLOT(selectionChanged()));
|
|
}
|
|
|
|
// Connect tab widget signals
|
|
QList<QTabWidget*> tabs = qFindChildren<QTabWidget*>(wndWidget());
|
|
for (int i = 0; i < tabs.size(); i++)
|
|
QtClient::connectObjects(tabs[i],SIGNAL(currentChanged(int)),this,SLOT(selectionChanged()));
|
|
|
|
// Connect stacked widget signals
|
|
QList<QStackedWidget*> sw = qFindChildren<QStackedWidget*>(wndWidget());
|
|
for (int i = 0; i < sw.size(); i++)
|
|
QtClient::connectObjects(sw[i],SIGNAL(currentChanged(int)),this,SLOT(selectionChanged()));
|
|
|
|
// Connect line edit signals
|
|
QList<QLineEdit*> le = qFindChildren<QLineEdit*>(wndWidget());
|
|
for (int i = 0; i < le.size(); i++)
|
|
connectTextChanged(le[i]);
|
|
|
|
// Connect text edit signals
|
|
QList<QTextEdit*> te = qFindChildren<QTextEdit*>(wndWidget());
|
|
for (int i = 0; i < te.size(); i++)
|
|
connectTextChanged(te[i]);
|
|
|
|
// Process tables:
|
|
// Insert a column and connect signals
|
|
// Hide columns starting with "hidden:"
|
|
QList<QTableWidget*> tables = qFindChildren<QTableWidget*>(wndWidget());
|
|
for (int i = 0; i < tables.size(); i++) {
|
|
bool nonCustom = (0 == qobject_cast<QtTable*>(tables[i]));
|
|
// Horizontal header
|
|
QHeaderView* hdr = tables[i]->horizontalHeader();
|
|
// Stretch last column
|
|
bool b = QtClient::getBoolProperty(tables[i],"_yate_horizontalstretch",true);
|
|
hdr->setStretchLastSection(b);
|
|
String tmp;
|
|
QtClient::getProperty(tables[i],"_yate_horizontalheader_align",tmp);
|
|
if (tmp) {
|
|
int def = hdr->defaultAlignment();
|
|
hdr->setDefaultAlignment((Qt::Alignment)QtClient::str2align(tmp,def));
|
|
}
|
|
if (!QtClient::getBoolProperty(tables[i],"_yate_horizontalheader",true))
|
|
hdr->hide();
|
|
// Vertical header
|
|
hdr = tables[i]->verticalHeader();
|
|
int itemH = QtClient::getIntProperty(tables[i],"_yate_rowheight");
|
|
if (itemH > 0)
|
|
hdr->setDefaultSectionSize(itemH);
|
|
if (!QtClient::getBoolProperty(tables[i],"_yate_verticalheader"))
|
|
hdr->hide();
|
|
else {
|
|
int width = QtClient::getIntProperty(tables[i],"_yate_verticalheaderwidth");
|
|
if (width > 0)
|
|
hdr->setFixedWidth(width);
|
|
if (!QtClient::getBoolProperty(tables[i],"_yate_allowvheaderresize"))
|
|
hdr->setResizeMode(QHeaderView::Fixed);
|
|
}
|
|
if (nonCustom) {
|
|
// Set _yate_save_props
|
|
QVariant var = tables[i]->property(s_propsSave);
|
|
if (var.type() != QVariant::StringList) {
|
|
// Create the property if not found, ignore it if not a string list
|
|
if (var.type() == QVariant::Invalid)
|
|
var = QVariant(QVariant::StringList);
|
|
else
|
|
Debug(QtDriver::self(),DebugNote,
|
|
"Window(%s) table '%s' already has a non string list property %s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(tables[i]),s_propsSave.c_str(),this);
|
|
}
|
|
if (var.type() == QVariant::StringList) {
|
|
// Make sure saved properties exists to allow them to be restored
|
|
QStringList sl = var.toStringList();
|
|
bool changed = createProperty(tables[i],s_propColWidths,QVariant::String,this,&sl);
|
|
changed = createProperty(tables[i],s_propSorting,QVariant::String,this,&sl) || changed;
|
|
if (changed)
|
|
tables[i]->setProperty(s_propsSave,QVariant(sl));
|
|
}
|
|
}
|
|
TableWidget t(tables[i]);
|
|
// Insert the column containing the ID
|
|
t.addColumn(0,0,"hidden:id");
|
|
// Hide columns
|
|
for (int i = 0; i < t.columnCount(); i++) {
|
|
String name;
|
|
t.getHeaderText(i,name,false);
|
|
if (name.startsWith("hidden:"))
|
|
t.table()->setColumnHidden(i,true);
|
|
}
|
|
// Connect signals
|
|
QtClient::connectObjects(t.table(),SIGNAL(cellDoubleClicked(int,int)),
|
|
this,SLOT(doubleClick()));
|
|
#if 0
|
|
// This would generate action() twice since QT will signal both cell and
|
|
// table item double click
|
|
QtClient::connectObjects(t.table(),SIGNAL(itemDoubleClicked(QTableWidgetItem*)),
|
|
this,SLOT(doubleClick()));
|
|
#endif
|
|
String noSel;
|
|
getProperty(t.name(),"dynamicNoItemSelChanged",noSel);
|
|
if (!noSel.toBoolean())
|
|
QtClient::connectObjects(t.table(),SIGNAL(itemSelectionChanged()),
|
|
this,SLOT(selectionChanged()));
|
|
// Optionally connect cell clicked
|
|
// This is done when we want to generate a select() or action() from cell clicked
|
|
String cellClicked;
|
|
getProperty(t.name(),"dynamicCellClicked",cellClicked);
|
|
if (cellClicked) {
|
|
if (cellClicked == "selectionChanged")
|
|
QtClient::connectObjects(t.table(),SIGNAL(cellClicked(int,int)),
|
|
this,SLOT(selectionChanged()));
|
|
else if (cellClicked == "doubleClick")
|
|
QtClient::connectObjects(t.table(),SIGNAL(cellClicked(int,int)),
|
|
this,SLOT(doubleClick()));
|
|
}
|
|
}
|
|
|
|
// Restore saved children properties
|
|
if (sect) {
|
|
unsigned int n = sect->length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = sect->getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
String prop(ns->name());
|
|
if (!prop.startSkip("property:",false))
|
|
continue;
|
|
int pos = prop.find(":");
|
|
if (pos > 0) {
|
|
String wName = prop.substr(0,pos);
|
|
String pName = prop.substr(pos + 1);
|
|
DDebug(QtDriver::self(),DebugAll,
|
|
"Window(%s) restoring property %s=%s for child '%s' [%p]",
|
|
m_id.c_str(),pName.c_str(),ns->c_str(),wName.c_str(),this);
|
|
setProperty(wName,pName,*ns);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Install event filter and apply dynamic properties
|
|
QList<QObject*> w = qFindChildren<QObject*>(wndWidget());
|
|
w.append(wndWidget());
|
|
for (int i = 0; i < w.size(); i++) {
|
|
QList<QByteArray> props = w[i]->dynamicPropertyNames();
|
|
// Check for our dynamic properties
|
|
int j = 0;
|
|
for (j = 0; j < props.size(); j++)
|
|
if (props[j].startsWith(s_yatePropPrefix))
|
|
break;
|
|
if (j == props.size())
|
|
continue;
|
|
// Add event hook to be used when a dynamic property changes
|
|
w[i]->installEventFilter(this);
|
|
// Fake dynamic property change to apply them
|
|
for (j = 0; j < props.size(); j++) {
|
|
if (!props[j].startsWith(s_yatePropPrefix))
|
|
continue;
|
|
QDynamicPropertyChangeEvent ev(props[j]);
|
|
eventFilter(w[i],&ev);
|
|
}
|
|
}
|
|
|
|
qRegisterMetaType<QModelIndex>("QModelIndex");
|
|
qRegisterMetaType<QTextCursor>("QTextCursor");
|
|
|
|
// Force window visibility change notification by changing the visibility flag
|
|
// Some controls might need to be updated
|
|
m_visible = !m_visible;
|
|
if (m_visible) {
|
|
// Disable _yate_destroyonhide property: avoid destroying the window now
|
|
String tmp;
|
|
getProperty(m_id,"_yate_destroyonhide",tmp);
|
|
if (tmp)
|
|
setProperty(m_id,"_yate_destroyonhide",String::boolText(false));
|
|
hide();
|
|
if (tmp)
|
|
setProperty(m_id,"_yate_destroyonhide",tmp);
|
|
}
|
|
else
|
|
show();
|
|
}
|
|
|
|
// Mouse button pressed notification
|
|
void QtWindow::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
if (Qt::LeftButton == event->button() && isShownNormal()) {
|
|
m_movePos = event->globalPos();
|
|
m_moving = true;
|
|
}
|
|
}
|
|
|
|
// Mouse button release notification
|
|
void QtWindow::mouseReleaseEvent(QMouseEvent* event)
|
|
{
|
|
if (Qt::LeftButton == event->button())
|
|
m_moving = false;
|
|
}
|
|
|
|
// Move the window if the moving flag is set
|
|
void QtWindow::mouseMoveEvent(QMouseEvent* event)
|
|
{
|
|
if (!m_moving || Qt::LeftButton != event->buttons() || !isShownNormal())
|
|
return;
|
|
int cx = event->globalPos().x() - m_movePos.x();
|
|
int cy = event->globalPos().y() - m_movePos.y();
|
|
if (cx || cy) {
|
|
m_movePos = event->globalPos();
|
|
QWidget::move(x() + cx,y() + cy);
|
|
}
|
|
}
|
|
|
|
// Handle context menu events. Return true if handled
|
|
bool QtWindow::handleContextMenuEvent(QContextMenuEvent* event, QObject* obj)
|
|
{
|
|
if (!(event && obj))
|
|
return false;
|
|
String mname;
|
|
QtClient::getProperty(obj,s_propContextMenu,mname);
|
|
XDebug(ClientDriver::self(),DebugAll,
|
|
"Window(%s) handleContextMenuEvent() obj=%s menu=%s [%p]",
|
|
m_id.c_str(),YQT_OBJECT_NAME(obj),mname.c_str(),this);
|
|
QMenu* m = mname ? qFindChild<QMenu*>(wndWidget(),QtClient::setUtf8(mname)) : 0;
|
|
if (m)
|
|
m->exec(event->globalPos());
|
|
return m != 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* QtDialog
|
|
*/
|
|
// Destructor. Notify the client if not exiting
|
|
QtDialog::~QtDialog()
|
|
{
|
|
QtWindow* w = parentWindow();
|
|
if (w && m_notifyOnClose && Client::valid())
|
|
QtClient::self()->action(w,buildActionName(m_notifyOnClose,m_notifyOnClose));
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) QtDialog(%s) destroyed [%p]",
|
|
w ? w->id().c_str() : "",YQT_OBJECT_NAME(this),w);
|
|
}
|
|
|
|
// Initialize dialog. Load the widget.
|
|
// Connect non checkable actions to own slot.
|
|
// Connect checkable actions/buttons to parent window's slot
|
|
// Display the dialog on success
|
|
bool QtDialog::show(const String& name, const String& title, const String& alias,
|
|
const NamedList* params)
|
|
{
|
|
QtWindow* w = parentWindow();
|
|
if (!w)
|
|
return false;
|
|
QWidget* widget = QtWindow::loadUI(Client::s_skinPath + s_cfg.getValue(name,"description"),this,name);
|
|
if (!widget)
|
|
return false;
|
|
QtClient::getProperty(widget,"_yate_notifyonclose",m_notifyOnClose);
|
|
setObjectName(QtClient::setUtf8(alias ? alias : name));
|
|
setMinimumSize(widget->minimumSize().width(),widget->minimumSize().height());
|
|
setMaximumSize(widget->maximumSize().width(),widget->maximumSize().height());
|
|
resize(widget->width(),widget->height());
|
|
QtClient::setWidget(this,widget);
|
|
if (title)
|
|
setWindowTitle(QtClient::setUtf8(title));
|
|
else if (widget->windowTitle().length())
|
|
setWindowTitle(widget->windowTitle());
|
|
else
|
|
setWindowTitle(w->windowTitle());
|
|
// Connect abstract buttons (check boxes and radio/push/tool buttons) signals
|
|
QList<QAbstractButton*> buttons = qFindChildren<QAbstractButton*>(widget);
|
|
for(int i = 0; i < buttons.size(); i++) {
|
|
if (!QtClient::autoConnect(buttons[i]))
|
|
continue;
|
|
if (!buttons[i]->isCheckable())
|
|
QtClient::connectObjects(buttons[i],SIGNAL(clicked()),this,SLOT(action()));
|
|
else
|
|
QtClient::connectObjects(buttons[i],SIGNAL(toggled(bool)),w,SLOT(toggled(bool)));
|
|
}
|
|
// Connect actions' signal
|
|
QList<QAction*> actions = qFindChildren<QAction*>(widget);
|
|
for (int i = 0; i < actions.size(); i++) {
|
|
if (!QtClient::autoConnect(actions[i]))
|
|
continue;
|
|
if (!actions[i]->isCheckable())
|
|
QtClient::connectObjects(actions[i],SIGNAL(triggered()),this,SLOT(action()));
|
|
else
|
|
QtClient::connectObjects(actions[i],SIGNAL(toggled(bool)),w,SLOT(toggled(bool)));
|
|
}
|
|
String* flags = 0;
|
|
String tmp;
|
|
QtClient::getProperty(widget,s_propWindowFlags,tmp);
|
|
if (tmp)
|
|
flags = &tmp;
|
|
if (params) {
|
|
if (!flags)
|
|
flags = params->getParam(s_propWindowFlags);
|
|
m_closable = params->getBoolValue(YSTRING("closable"),"true");
|
|
w->setParams(*params);
|
|
}
|
|
if (flags)
|
|
QtClient::applyWindowFlags(this,*flags);
|
|
setWindowModality(Qt::WindowModal);
|
|
QDialog::show();
|
|
return true;
|
|
}
|
|
|
|
// Notify client
|
|
void QtDialog::action()
|
|
{
|
|
QtWindow* w = parentWindow();
|
|
if (!w)
|
|
return;
|
|
DDebug(QtDriver::self(),DebugAll,"QtWindow(%s) dialog action '%s' [%p]",
|
|
w->id().c_str(),YQT_OBJECT_NAME(sender()),w);
|
|
if (!QtClient::self() || QtClient::changing())
|
|
return;
|
|
String name;
|
|
QtClient::getIdentity(sender(),name);
|
|
if (name && QtClient::self()->action(w,buildActionName(name,name)))
|
|
deleteLater();
|
|
}
|
|
|
|
// Delete the dialog
|
|
void QtDialog::closeEvent(QCloseEvent* event)
|
|
{
|
|
if (m_closable) {
|
|
QDialog::closeEvent(event);
|
|
deleteLater();
|
|
}
|
|
else
|
|
event->ignore();
|
|
}
|
|
|
|
// Destroy the dialog
|
|
void QtDialog::reject()
|
|
{
|
|
if (!m_closable)
|
|
return;
|
|
QDialog::reject();
|
|
deleteLater();
|
|
}
|
|
|
|
|
|
/**
|
|
* QtClient
|
|
*/
|
|
QtClient::QtClient()
|
|
: Client("Qt Client")
|
|
{
|
|
m_oneThread = Engine::config().getBoolValue("client","onethread",true);
|
|
|
|
s_save = Engine::configFile("qt4client",true);
|
|
s_save.load();
|
|
// Fill QT styles
|
|
s_qtStyles.addParam("IaOraKde","iaorakde");
|
|
s_qtStyles.addParam("QWindowsStyle","windows");
|
|
s_qtStyles.addParam("QMacStyle","mac");
|
|
s_qtStyles.addParam("QMotifStyle","motif");
|
|
s_qtStyles.addParam("QCDEStyle","cde");
|
|
s_qtStyles.addParam("QWindowsXPStyle","windowsxp");
|
|
s_qtStyles.addParam("QCleanlooksStyle","cleanlooks");
|
|
s_qtStyles.addParam("QPlastiqueStyle","plastique");
|
|
s_qtStyles.addParam("QGtkStyle","gtk");
|
|
s_qtStyles.addParam("IaOraQt","iaoraqt");
|
|
s_qtStyles.addParam("OxygenStyle","oxygen");
|
|
s_qtStyles.addParam("PhaseStyle","phase");
|
|
}
|
|
|
|
QtClient::~QtClient()
|
|
{
|
|
}
|
|
|
|
void QtClient::cleanup()
|
|
{
|
|
Client::cleanup();
|
|
m_events.clear();
|
|
Client::save(s_save);
|
|
QtWindow::clearUICache();
|
|
m_app->quit();
|
|
if (!m_app->startingUp())
|
|
delete m_app;
|
|
}
|
|
|
|
void QtClient::run()
|
|
{
|
|
const char* style = Engine::config().getValue("client","style");
|
|
if (style && !QApplication::setStyle(QString::fromUtf8(style)))
|
|
Debug(ClientDriver::self(),DebugWarn,"Could not set Qt style '%s'",style);
|
|
int argc = 0;
|
|
char* argv = 0;
|
|
m_app = new QApplication(argc,&argv);
|
|
m_app->setQuitOnLastWindowClosed(false);
|
|
updateAppStyleSheet();
|
|
String imgRead;
|
|
QList<QByteArray> imgs = QImageReader::supportedImageFormats();
|
|
for (int i = 0; i < imgs.size(); i++)
|
|
imgRead.append(imgs[i].constData(),",");
|
|
imgRead = "read image formats '" + imgRead + "'";
|
|
Debug(ClientDriver::self(),DebugInfo,"QT client start running (version=%s) %s",
|
|
qVersion(),imgRead.c_str());
|
|
if (!QSound::isAvailable())
|
|
Debug(ClientDriver::self(),DebugWarn,"QT sounds are not available");
|
|
// Create events proxy
|
|
m_events.append(new QtEventProxy(QtEventProxy::Timer));
|
|
m_events.append(new QtEventProxy(QtEventProxy::AllHidden,m_app));
|
|
if (Engine::exiting())
|
|
return;
|
|
Client::run();
|
|
}
|
|
|
|
void QtClient::main()
|
|
{
|
|
if (!Engine::exiting())
|
|
m_app->exec();
|
|
}
|
|
|
|
void QtClient::lock()
|
|
{}
|
|
|
|
void QtClient::unlock()
|
|
{}
|
|
|
|
void QtClient::allHidden()
|
|
{
|
|
Debug(QtDriver::self(),DebugInfo,"QtClient::allHiden() counter=%d",s_allHiddenQuit);
|
|
if (s_allHiddenQuit > 0)
|
|
return;
|
|
quit();
|
|
}
|
|
|
|
bool QtClient::createWindow(const String& name, const String& alias)
|
|
{
|
|
String parent = s_cfg.getValue(name,"parent");
|
|
QtWindow* parentWnd = 0;
|
|
if (!TelEngine::null(parent)) {
|
|
ObjList* o = m_windows.find(parent);
|
|
if (o)
|
|
parentWnd = YOBJECT(QtWindow,o->get());
|
|
}
|
|
QtWindow* w = new QtWindow(name,s_skinPath + s_cfg.getValue(name,"description"),alias,parentWnd);
|
|
if (w) {
|
|
Debug(QtDriver::self(),DebugAll,"Created window name=%s alias=%s with parent=(%s [%p]) (%p)",
|
|
name.c_str(),alias.c_str(),parent.c_str(),parentWnd,w);
|
|
// Remove the old window
|
|
ObjList* o = m_windows.find(w->id());
|
|
if (o)
|
|
Client::self()->closeWindow(w->id(),false);
|
|
w->populate();
|
|
m_windows.append(w);
|
|
return true;
|
|
}
|
|
else
|
|
Debug(QtDriver::self(),DebugGoOn,"Could not create window name=%s alias=%s",
|
|
name.c_str(),alias.c_str());
|
|
return false;
|
|
}
|
|
|
|
void QtClient::loadWindows(const char* file)
|
|
{
|
|
if (!file)
|
|
s_cfg = s_skinPath + "qt4client.rc";
|
|
else
|
|
s_cfg = String(file);
|
|
s_cfg.load();
|
|
Debug(QtDriver::self(),DebugInfo,"Loading Windows");
|
|
unsigned int n = s_cfg.sections();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedList* l = s_cfg.getSection(i);
|
|
if (l && l->getBoolValue(YSTRING("enabled"),true))
|
|
createWindow(*l);
|
|
}
|
|
}
|
|
|
|
bool QtClient::isUIThread()
|
|
{
|
|
return (QApplication::instance() && QApplication::instance()->thread() == QThread::currentThread());
|
|
}
|
|
|
|
// Open a file open dialog window
|
|
// Parameters that can be specified include 'caption',
|
|
// 'dir', 'filter', 'selectedfilter', 'confirmoverwrite', 'choosedir'
|
|
bool QtClient::chooseFile(Window* parent, NamedList& params)
|
|
{
|
|
QtWindow* wnd = static_cast<QtWindow*>(parent);
|
|
QFileDialog* dlg = new QFileDialog(wnd,setUtf8(params.getValue(YSTRING("caption"))),
|
|
setUtf8(params.getValue(YSTRING("dir"))));
|
|
|
|
if (wnd)
|
|
dlg->setWindowIcon(wnd->windowIcon());
|
|
|
|
// Connect signals
|
|
String* action = params.getParam(YSTRING("action"));
|
|
if (wnd && !null(action)) {
|
|
dlg->setObjectName(setUtf8(*action));
|
|
QtClient::connectObjects(dlg,SIGNAL(accepted()),wnd,SLOT(chooseFileAccepted()));
|
|
QtClient::connectObjects(dlg,SIGNAL(rejected()),wnd,SLOT(chooseFileRejected()));
|
|
}
|
|
|
|
// Destroy it when closed
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
// This dialog should always stay on top
|
|
dlg->setWindowFlags(dlg->windowFlags() | Qt::WindowStaysOnTopHint);
|
|
|
|
if (params.getBoolValue(YSTRING("modal"),true))
|
|
dlg->setWindowModality(Qt::WindowModal);
|
|
|
|
// Filters
|
|
NamedString* f = params.getParam(YSTRING("filters"));
|
|
if (f) {
|
|
QStringList filters;
|
|
ObjList* obj = f->split('|',false);
|
|
for (ObjList* o = obj->skipNull(); o; o = o->skipNext())
|
|
filters.append(QtClient::setUtf8(o->get()->toString()));
|
|
TelEngine::destruct(obj);
|
|
dlg->setFilters(filters);
|
|
}
|
|
QString flt = QtClient::setUtf8(params.getValue(YSTRING("selectedfilter")));
|
|
if (flt.length())
|
|
dlg->selectFilter(flt);
|
|
|
|
if (params.getBoolValue(YSTRING("save")))
|
|
dlg->setAcceptMode(QFileDialog::AcceptSave);
|
|
else
|
|
dlg->setAcceptMode(QFileDialog::AcceptOpen);
|
|
|
|
// Choose options
|
|
if (params.getBoolValue(YSTRING("choosefile"),true)) {
|
|
if (params.getBoolValue(YSTRING("chooseanyfile")))
|
|
dlg->setFileMode(QFileDialog::AnyFile);
|
|
else if (params.getBoolValue(YSTRING("multiplefiles")))
|
|
dlg->setFileMode(QFileDialog::ExistingFiles);
|
|
else
|
|
dlg->setFileMode(QFileDialog::ExistingFile);
|
|
}
|
|
else
|
|
dlg->setFileMode(QFileDialog::DirectoryOnly);
|
|
|
|
dlg->selectFile(QtClient::setUtf8(params.getValue(YSTRING("selectedfile"))));
|
|
|
|
dlg->setVisible(true);
|
|
return true;
|
|
}
|
|
|
|
bool QtClient::action(Window* wnd, const String& name, NamedList* params)
|
|
{
|
|
String tmp = name;
|
|
if (tmp.startSkip("openurl:",false))
|
|
return openUrl(tmp);
|
|
return Client::action(wnd,name,params);
|
|
}
|
|
|
|
// Create a sound object. Append it to the global list
|
|
bool QtClient::createSound(const char* name, const char* file, const char* device)
|
|
{
|
|
if (!(QSound::isAvailable() && name && *name && file && *file))
|
|
return false;
|
|
Lock lock(ClientSound::s_soundsMutex);
|
|
if (ClientSound::s_sounds.find(name))
|
|
return false;
|
|
ClientSound::s_sounds.append(new QtSound(name,file,device));
|
|
DDebug(ClientDriver::self(),DebugAll,"Added sound=%s file=%s device=%s",
|
|
name,file,device);
|
|
return true;
|
|
}
|
|
|
|
// Build a date/time string from UTC time
|
|
bool QtClient::formatDateTime(String& dest, unsigned int secs,
|
|
const char* format, bool utc)
|
|
{
|
|
if (!(format && *format))
|
|
return false;
|
|
QtClient::getUtf8(dest,formatDateTime(secs,format,utc));
|
|
return true;
|
|
}
|
|
|
|
// Build a date/time QT string from UTC time
|
|
QString QtClient::formatDateTime(unsigned int secs, const char* format, bool utc)
|
|
{
|
|
QDateTime time;
|
|
if (utc)
|
|
time.setTimeSpec(Qt::UTC);
|
|
time.setTime_t(secs);
|
|
return time.toString(format);
|
|
}
|
|
|
|
// Retrieve an object's QtWindow parent
|
|
QtWindow* QtClient::parentWindow(QObject* obj)
|
|
{
|
|
for (; obj; obj = obj->parent()) {
|
|
QtWindow* w = qobject_cast<QtWindow*>(obj);
|
|
if (w)
|
|
return w;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Save an object's property into parent window's section. Clear it on failure
|
|
bool QtClient::saveProperty(QObject* obj, const String& prop, QtWindow* owner)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
if (!owner)
|
|
owner = parentWindow(obj);
|
|
if (!owner)
|
|
return false;
|
|
String value;
|
|
bool ok = getProperty(obj,prop,value);
|
|
String pName;
|
|
pName << "property:" << YQT_OBJECT_NAME(obj) << ":" << prop;
|
|
if (ok)
|
|
s_save.setValue(owner->id(),pName,value);
|
|
else
|
|
s_save.clearKey(owner->id(),pName);
|
|
return ok;
|
|
}
|
|
|
|
// Set or an object's property
|
|
bool QtClient::setProperty(QObject* obj, const char* name, const String& value)
|
|
{
|
|
if (!(obj && name && *name))
|
|
return false;
|
|
QVariant var = obj->property(name);
|
|
const char* err = 0;
|
|
bool ok = false;
|
|
switch (var.type()) {
|
|
case QVariant::String:
|
|
ok = obj->setProperty(name,QVariant(QtClient::setUtf8(value)));
|
|
break;
|
|
case QVariant::Bool:
|
|
ok = obj->setProperty(name,QVariant(value.toBoolean()));
|
|
break;
|
|
case QVariant::Int:
|
|
ok = obj->setProperty(name,QVariant(value.toInteger()));
|
|
break;
|
|
case QVariant::UInt:
|
|
ok = obj->setProperty(name,QVariant((unsigned int)value.toInteger()));
|
|
break;
|
|
case QVariant::Icon:
|
|
ok = obj->setProperty(name,QVariant(QIcon(QtClient::setUtf8(value))));
|
|
break;
|
|
case QVariant::Pixmap:
|
|
ok = obj->setProperty(name,QVariant(QPixmap(QtClient::setUtf8(value))));
|
|
break;
|
|
case QVariant::Double:
|
|
ok = obj->setProperty(name,QVariant(value.toDouble()));
|
|
break;
|
|
case QVariant::KeySequence:
|
|
ok = obj->setProperty(name,QVariant(QtClient::setUtf8(value)));
|
|
break;
|
|
case QVariant::StringList:
|
|
{
|
|
QStringList qList;
|
|
if (value)
|
|
qList.append(setUtf8(value));
|
|
ok = obj->setProperty(name,QVariant(qList));
|
|
}
|
|
break;
|
|
case QVariant::Invalid:
|
|
err = "no such property";
|
|
break;
|
|
default:
|
|
err = "unsupported type";
|
|
}
|
|
if (ok)
|
|
DDebug(ClientDriver::self(),DebugAll,"Set property %s=%s for object '%s'",
|
|
name,value.c_str(),YQT_OBJECT_NAME(obj));
|
|
else
|
|
DDebug(ClientDriver::self(),DebugNote,
|
|
"Failed to set %s=%s (type=%s) for object '%s': %s",
|
|
name,value.c_str(),var.typeName(),YQT_OBJECT_NAME(obj),err);
|
|
return ok;
|
|
}
|
|
|
|
// Get an object's property
|
|
bool QtClient::getProperty(QObject* obj, const char* name, String& value)
|
|
{
|
|
if (!(obj && name && *name))
|
|
return false;
|
|
QVariant var = obj->property(name);
|
|
if (var.type() == QVariant::StringList) {
|
|
NamedList* l = static_cast<NamedList*>(value.getObject("NamedList"));
|
|
if (l)
|
|
copyParams(*l,var.toStringList());
|
|
else
|
|
getUtf8(value,var.toStringList().join(","));
|
|
DDebug(ClientDriver::self(),DebugAll,"Got list property %s for object '%s'",
|
|
name,YQT_OBJECT_NAME(obj));
|
|
return true;
|
|
}
|
|
if (var.canConvert(QVariant::String)) {
|
|
QtClient::getUtf8(value,var.toString());
|
|
DDebug(ClientDriver::self(),DebugAll,"Got property %s=%s for object '%s'",
|
|
name,value.c_str(),YQT_OBJECT_NAME(obj));
|
|
return true;
|
|
}
|
|
DDebug(ClientDriver::self(),DebugNote,
|
|
"Failed to get property '%s' (type=%s) for object '%s': %s",
|
|
name,var.typeName(),YQT_OBJECT_NAME(obj),
|
|
((var.type() == QVariant::Invalid) ? "no such property" : "unsupported type"));
|
|
return false;
|
|
}
|
|
|
|
// Copy a string list to a list of parameters
|
|
void QtClient::copyParams(NamedList& dest, const QStringList& src)
|
|
{
|
|
for (int i = 0; i < src.size(); i++) {
|
|
if (!src[i].length())
|
|
continue;
|
|
int pos = src[i].indexOf('=');
|
|
String name;
|
|
if (pos >= 0) {
|
|
getUtf8(name,src[i].left(pos));
|
|
getUtf8(dest,name,src[i].right(src[i].length() - pos - 1));
|
|
}
|
|
else {
|
|
getUtf8(name,src[i]);
|
|
dest.addParam(name,"");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy a list of parameters to string list
|
|
void QtClient::copyParams(QStringList& dest, const NamedList& src)
|
|
{
|
|
unsigned int n = src.length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = src.getParam(i);
|
|
if (ns)
|
|
dest.append(setUtf8(ns->name() + "=" + *ns));
|
|
}
|
|
}
|
|
|
|
// Build QObject properties from list
|
|
void QtClient::buildProps(QObject* obj, const String& props)
|
|
{
|
|
if (!(obj && props))
|
|
return;
|
|
ObjList* list = props.split(',',false);
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
|
|
String* s = static_cast<String*>(o->get());
|
|
int pos = s->find('=');
|
|
if (pos < 1)
|
|
continue;
|
|
String ptype = s->substr(pos + 1);
|
|
QVariant::Type t = (QVariant::Type)lookup(ptype,s_qVarType,QVariant::Invalid);
|
|
if (t == QVariant::Invalid) {
|
|
Debug(ClientDriver::self(),DebugStub,
|
|
"QtClient::buildProps() unhandled type '%s'",ptype.c_str());
|
|
continue;
|
|
}
|
|
String pname = s->substr(0,pos);
|
|
QVariant existing = obj->property(pname);
|
|
if (existing.type() == QVariant::Invalid) {
|
|
obj->setProperty(pname,QVariant(t));
|
|
continue;
|
|
}
|
|
Debug(ClientDriver::self(),DebugNote,
|
|
"Can't create property '%s' type=%s for object (%p,%s): already exists",
|
|
pname.c_str(),ptype.c_str(),obj,YQT_OBJECT_NAME(obj));
|
|
}
|
|
TelEngine::destruct(list);
|
|
}
|
|
|
|
// Build custom UI widgets from frames owned by a widget
|
|
void QtClient::buildFrameUiWidgets(QWidget* parent)
|
|
{
|
|
if (!parent)
|
|
return;
|
|
QList<QFrame*> frm = qFindChildren<QFrame*>(parent);
|
|
for (int i = 0; i < frm.size(); i++) {
|
|
if (!getBoolProperty(frm[i],"_yate_uiwidget"))
|
|
continue;
|
|
String name;
|
|
String type;
|
|
getProperty(frm[i],"_yate_uiwidget_name",name);
|
|
getProperty(frm[i],"_yate_uiwidget_class",type);
|
|
if (!(name && type))
|
|
continue;
|
|
NamedList params("");
|
|
getProperty(frm[i],"_yate_uiwidget_params",params);
|
|
QtWindow* w = static_cast<QtWindow*>(parent->window());
|
|
if (w)
|
|
params.setParam("parentwindow",w->id());
|
|
getUtf8(params,"parentwidget",frm[i]->objectName(),true);
|
|
QObject* obj = (QObject*)UIFactory::build(type,name,¶ms);
|
|
if (!obj)
|
|
continue;
|
|
QWidget* wid = qobject_cast<QWidget*>(obj);
|
|
if (wid)
|
|
QtClient::setWidget(frm[i],wid);
|
|
else {
|
|
obj->setParent(frm[i]);
|
|
QtCustomObject* customObj = qobject_cast<QtCustomObject*>(obj);
|
|
if (customObj)
|
|
customObj->parentChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Associate actions to buttons with '_yate_setaction' property set
|
|
void QtClient::setAction(QWidget* parent)
|
|
{
|
|
if (!parent)
|
|
return;
|
|
QList<QToolButton*> tb = qFindChildren<QToolButton*>(parent);
|
|
for (int i = 0; i < tb.size(); i++) {
|
|
QVariant var = tb[i]->property("_yate_setaction");
|
|
if (var.toString().isEmpty())
|
|
continue;
|
|
QAction* a = qFindChild<QAction*>(parent,var.toString());
|
|
if (a)
|
|
tb[i]->setDefaultAction(a);
|
|
}
|
|
}
|
|
|
|
// Build a menu object from a list of parameters
|
|
QMenu* QtClient::buildMenu(const NamedList& params, const char* text, QObject* receiver,
|
|
const char* triggerSlot, const char* toggleSlot, QWidget* parent,
|
|
const char* aboutToShowSlot)
|
|
{
|
|
QMenu* menu = 0;
|
|
unsigned int n = params.length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* param = params.getParam(i);
|
|
if (!(param && param->name().startsWith("item:")))
|
|
continue;
|
|
|
|
if (!menu)
|
|
menu = new QMenu(setUtf8(text),parent);
|
|
|
|
NamedList* p = YOBJECT(NamedList,param);
|
|
if (p) {
|
|
QMenu* subMenu = buildMenu(*p,*param ? param->c_str() : p->getValue(YSTRING("title"),*p),
|
|
receiver,triggerSlot,toggleSlot,menu);
|
|
if (subMenu)
|
|
menu->addMenu(subMenu);
|
|
continue;
|
|
}
|
|
String name = param->name().substr(5);
|
|
if (*param) {
|
|
QAction* a = menu->addAction(QtClient::setUtf8(*param));
|
|
a->setObjectName(QtClient::setUtf8(name));
|
|
a->setParent(menu);
|
|
setImage(a,params["image:" + name]);
|
|
}
|
|
else if (!name)
|
|
menu->addSeparator()->setParent(menu);
|
|
else {
|
|
// Check if the action is already there
|
|
QAction* a = 0;
|
|
if (parent && parent->window())
|
|
a = qFindChild<QAction*>(parent->window(),QtClient::setUtf8(name));
|
|
if (a)
|
|
menu->addAction(a);
|
|
else
|
|
Debug(ClientDriver::self(),DebugNote,
|
|
"buildMenu(%s) action '%s' not found",params.c_str(),name.c_str());
|
|
}
|
|
}
|
|
|
|
if (!menu)
|
|
return 0;
|
|
|
|
// Set name
|
|
menu->setObjectName(setUtf8(params));
|
|
setImage(menu,params["image:" + params]);
|
|
// Apply properties
|
|
// Format: property:object_name:property_name=value
|
|
if (parent)
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* param = params.getParam(i);
|
|
if (!(param && param->name().startsWith("property:")))
|
|
continue;
|
|
int pos = param->name().find(':',9);
|
|
if (pos < 9)
|
|
continue;
|
|
QObject* obj = qFindChild<QObject*>(parent,setUtf8(param->name().substr(9,pos - 9)));
|
|
if (obj)
|
|
setProperty(obj,param->name().substr(pos + 1),*param);
|
|
}
|
|
// Connect signals (direct children only: actions from sub-menus are already connected)
|
|
QList<QAction*> list = qFindChildren<QAction*>(menu);
|
|
for (int i = 0; i < list.size(); i++) {
|
|
if (list[i]->isSeparator() || list[i]->parent() != menu)
|
|
continue;
|
|
if (list[i]->isCheckable())
|
|
QtClient::connectObjects(list[i],SIGNAL(toggled(bool)),receiver,toggleSlot);
|
|
else
|
|
QtClient::connectObjects(list[i],SIGNAL(triggered()),receiver,triggerSlot);
|
|
}
|
|
if (!TelEngine::null(aboutToShowSlot))
|
|
QtClient::connectObjects(menu,SIGNAL(aboutToShow()),receiver,aboutToShowSlot);
|
|
|
|
return menu;
|
|
}
|
|
|
|
// Wrapper for QObject::connect() used to put a debug mesage on failure
|
|
bool QtClient::connectObjects(QObject* sender, const char* signal,
|
|
QObject* receiver, const char* slot)
|
|
{
|
|
if (!(sender && signal && *signal && receiver && slot && *slot))
|
|
return false;
|
|
bool ok = QObject::connect(sender,signal,receiver,slot);
|
|
if (ok)
|
|
DDebug(QtDriver::self(),DebugAll,
|
|
"Connected sender=%s signal=%s to receiver=%s slot=%s",
|
|
YQT_OBJECT_NAME(sender),signal,YQT_OBJECT_NAME(receiver),slot);
|
|
else
|
|
Debug(QtDriver::self(),DebugWarn,
|
|
"Failed to connect sender=%s signal=%s to receiver=%s slot=%s",
|
|
YQT_OBJECT_NAME(sender),signal,YQT_OBJECT_NAME(receiver),slot);
|
|
return ok;
|
|
}
|
|
|
|
// Insert a widget into another one replacing any existing children
|
|
bool QtClient::setWidget(QWidget* parent, QWidget* child)
|
|
{
|
|
if (!(parent && child))
|
|
return false;
|
|
QVBoxLayout* layout = new QVBoxLayout;
|
|
layout->setSpacing(0);
|
|
String margins;
|
|
QtClient::getProperty(parent,"_yate_layout_margins",margins);
|
|
if (!margins)
|
|
layout->setContentsMargins(0,0,0,0);
|
|
else {
|
|
QList<int> m = buildIntList(margins,4);
|
|
layout->setContentsMargins(m[0],m[1],m[2],m[3]);
|
|
}
|
|
layout->addWidget(child);
|
|
QLayout* l = parent->layout();
|
|
if (l)
|
|
delete l;
|
|
parent->setLayout(layout);
|
|
return true;
|
|
}
|
|
|
|
// Set an object's image property from image file
|
|
bool QtClient::setImage(QObject* obj, const String& img, bool fit)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
QPixmap pixmap(setUtf8(img));
|
|
return setImage(obj,pixmap,fit);
|
|
}
|
|
|
|
// Set an object's image property from raw data.
|
|
bool QtClient::setImage(QObject* obj, const DataBlock& data, const String& format, bool fit)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
QPixmap pixmap;
|
|
String f = format;
|
|
f.startSkip("image/",false);
|
|
if (!pixmap.loadFromData((const uchar*)data.data(),data.length(),f))
|
|
return false;
|
|
return setImage(obj,pixmap,fit);
|
|
}
|
|
|
|
// Set an object's image property from QPixmap
|
|
bool QtClient::setImage(QObject* obj, const QPixmap& img, bool fit)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
if (obj->isWidgetType()) {
|
|
QLabel* l = qobject_cast<QLabel*>(obj);
|
|
if (l) {
|
|
if (fit && !l->hasScaledContents() &&
|
|
(img.width() > l->width() || img.height() > l->height())) {
|
|
QPixmap tmp;
|
|
if (l->width() <= l->height())
|
|
tmp = img.scaledToWidth(l->width());
|
|
else
|
|
tmp = img.scaledToHeight(l->height());
|
|
l->setPixmap(tmp);
|
|
}
|
|
else
|
|
l->setPixmap(img);
|
|
}
|
|
else {
|
|
QAbstractButton* b = qobject_cast<QAbstractButton*>(obj);
|
|
if (b)
|
|
b->setIcon(img);
|
|
else {
|
|
QMenu* m = qobject_cast<QMenu*>(obj);
|
|
if (m)
|
|
m->setIcon(img);
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
QAction* a = qobject_cast<QAction*>(obj);
|
|
if (a) {
|
|
a->setIcon(img);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Update a toggable object's image from properties
|
|
void QtClient::updateToggleImage(QObject* obj)
|
|
{
|
|
QtWidget w(obj);
|
|
QAbstractButton* b = 0;
|
|
if (w.inherits(QtWidget::AbstractButton))
|
|
b = w.abstractButton();
|
|
if (!(b && b->isCheckable()))
|
|
return;
|
|
String icon;
|
|
bool set = false;
|
|
if (b->isChecked())
|
|
set = QtClient::getProperty(w,"_yate_pressed_icon",icon);
|
|
else
|
|
set = QtClient::getProperty(w,"_yate_normal_icon",icon);
|
|
if (set)
|
|
QtClient::setImage(obj,Client::s_skinPath + icon);
|
|
}
|
|
|
|
// Update an object's image from properties on mouse events
|
|
void QtClient::updateImageFromMouse(QObject* obj, bool inOut, bool on)
|
|
{
|
|
QtWidget w(obj);
|
|
QAbstractButton* b = 0;
|
|
if (w.inherits(QtWidget::AbstractButton))
|
|
b = w.abstractButton();
|
|
if (!b)
|
|
return;
|
|
if (!b->isEnabled())
|
|
return;
|
|
String icon;
|
|
bool set = false;
|
|
if (inOut) {
|
|
if (on)
|
|
set = QtClient::getProperty(obj,"_yate_hover_icon",icon);
|
|
else {
|
|
if (b->isCheckable() && b->isChecked())
|
|
set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
|
|
set = set || QtClient::getProperty(obj,"_yate_normal_icon",icon);
|
|
}
|
|
}
|
|
else {
|
|
if (on) {
|
|
if (!b->isCheckable())
|
|
set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
|
|
}
|
|
else {
|
|
set = QtClient::getProperty(obj,"_yate_hover_icon",icon);
|
|
if (!set && b->isCheckable() && b->isChecked())
|
|
set = QtClient::getProperty(obj,"_yate_pressed_icon",icon);
|
|
set = set || QtClient::getProperty(obj,"_yate_normal_icon",icon);
|
|
}
|
|
}
|
|
if (set)
|
|
QtClient::setImage(obj,Client::s_skinPath + icon);
|
|
}
|
|
|
|
// Process a key press event. Retrieve an action associated with the key
|
|
bool QtClient::filterKeyEvent(QObject* obj, QKeyEvent* event, String& action,
|
|
bool& filter, QObject* parent)
|
|
{
|
|
static int mask = Qt::SHIFT | Qt::CTRL | Qt::ALT;
|
|
if (!(obj && event))
|
|
return false;
|
|
// Try to match key and modifiers
|
|
QKeySequence ks(event->key());
|
|
String prop;
|
|
getUtf8(prop,ks.toString());
|
|
prop = "dynamicAction" + prop;
|
|
// Get modifiers from property and check them against event
|
|
QVariant v = obj->property(prop + "Modifiers");
|
|
int tmp = 0;
|
|
if (v.type() == QVariant::String) {
|
|
QKeySequence ks(v.toString());
|
|
for (unsigned int i = 0; i < ks.count(); i++)
|
|
tmp |= ks[i];
|
|
}
|
|
if (tmp != (mask & event->modifiers()))
|
|
return false;
|
|
// We matched the key and modifiers
|
|
// Set filter flag
|
|
filter = getBoolProperty(obj,prop + "Filter");
|
|
// Retrieve the action
|
|
getProperty(obj,prop,action);
|
|
if (!action)
|
|
return true;
|
|
if (!parent)
|
|
return true;
|
|
parent = qFindChild<QObject*>(parent,setUtf8(action));
|
|
if (!parent)
|
|
return true;
|
|
// Avoid notifying a disabled action
|
|
bool ok = true;
|
|
if (parent->isWidgetType())
|
|
ok = (qobject_cast<QWidget*>(parent))->isEnabled();
|
|
else {
|
|
QAction* a = qobject_cast<QAction*>(parent);
|
|
ok = !a || a->isEnabled();
|
|
}
|
|
if (!ok)
|
|
action.clear();
|
|
return true;
|
|
}
|
|
|
|
// Safely delete a QObject (reset its parent, calls it's deleteLater() method)
|
|
void QtClient::deleteLater(QObject* obj)
|
|
{
|
|
if (!obj)
|
|
return;
|
|
obj->disconnect();
|
|
if (obj->isWidgetType())
|
|
(static_cast<QWidget*>(obj))->setParent(0);
|
|
else
|
|
obj->setParent(0);
|
|
obj->deleteLater();
|
|
}
|
|
|
|
// Retrieve unavailable space position (if any) in the screen containing a given widget.
|
|
QDesktopWidget* QtClient::getScreenUnavailPos(QWidget* w, int& pos)
|
|
{
|
|
if (!w)
|
|
return 0;
|
|
QDesktopWidget* d = QApplication::desktop();
|
|
if (!d)
|
|
return 0;
|
|
pos = PosNone;
|
|
QRect rScreen = d->screenGeometry(w);
|
|
QRect rClient = d->availableGeometry(w);
|
|
int dx = rClient.x() - rScreen.x();
|
|
if (dx > 0)
|
|
pos |= PosLeft;
|
|
int dy = rClient.y() - rScreen.y();
|
|
if (dy > 0)
|
|
pos |= PosTop;
|
|
int dw = rScreen.width() - rClient.width();
|
|
if (dw > 0 && (!dx || (dx > 0 && dw > dx)))
|
|
pos |= PosRight;
|
|
int dh = rScreen.height() - rClient.height();
|
|
if (dh > 0 && (!dy || (dy > 0 && dh > dy)))
|
|
pos |= PosBottom;
|
|
return d;
|
|
}
|
|
|
|
// Move a window to a specified position
|
|
void QtClient::moveWindow(QtWindow* w, int pos)
|
|
{
|
|
if (!w)
|
|
return;
|
|
QDesktopWidget* d = QApplication::desktop();
|
|
if (!d)
|
|
return;
|
|
QRect r = d->availableGeometry(w);
|
|
int x = r.x();
|
|
int y = r.y();
|
|
QSize sz = w->frameSize();
|
|
if (pos == CornerBottomRight) {
|
|
if (r.width() > sz.width())
|
|
x += r.width() - sz.width();
|
|
if (r.height() > sz.height())
|
|
y += r.height() - sz.height();
|
|
}
|
|
else if (pos == CornerTopRight) {
|
|
if (r.width() > sz.width())
|
|
x += r.width() - sz.width();
|
|
}
|
|
else if (pos == CornerBottomLeft) {
|
|
if (r.height() > sz.height())
|
|
y += r.height() - sz.height();
|
|
}
|
|
else if (pos != CornerTopLeft)
|
|
return;
|
|
w->move(x,y);
|
|
}
|
|
|
|
// Split a string. Returns a list of int values
|
|
QList<int> QtClient::str2IntList(const String& str, int defVal, bool emptyOk)
|
|
{
|
|
QList<int> list;
|
|
ObjList* l = str.split(',',emptyOk);
|
|
for (ObjList* o = l->skipNull(); o; o = o->skipNext())
|
|
list.append(o->get()->toString().toInteger(defVal));
|
|
TelEngine::destruct(l);
|
|
return list;
|
|
}
|
|
|
|
// Build a comma separated list of integers
|
|
void QtClient::intList2str(String& str, QList<int> list)
|
|
{
|
|
for (int i = 0; i < list.size(); i++)
|
|
str.append(String(list[i]),",");
|
|
}
|
|
|
|
// Apply a comma separated list of window flags to a widget
|
|
void QtClient::applyWindowFlags(QWidget* w, const String& value)
|
|
{
|
|
if (!w)
|
|
return;
|
|
// Set window flags from enclosed widget:
|
|
// custom window title/border/sysmenu config
|
|
ObjList* f = value.split(',',false);
|
|
int flags = Qt::CustomizeWindowHint | w->windowFlags();
|
|
// Clear settable flags
|
|
TokenDict* dict = s_windowFlags;
|
|
for (int i = 0; dict[i].token; i++)
|
|
flags &= ~dict[i].value;
|
|
// Set flags
|
|
for (ObjList* o = f->skipNull(); o; o = o->skipNext())
|
|
flags |= lookup(o->get()->toString(),s_windowFlags,0);
|
|
TelEngine::destruct(f);
|
|
w->setWindowFlags((Qt::WindowFlags)flags);
|
|
}
|
|
|
|
// Build a QT Alignment mask from a comma separated list of flags
|
|
int QtClient::str2align(const String& flags, int initVal)
|
|
{
|
|
ObjList* list = flags.split(',',false);
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
|
|
int val = ::lookup((static_cast<String*>(o->get()))->c_str(),s_qAlign);
|
|
if (0 != (val & Qt::AlignHorizontal_Mask))
|
|
initVal &= ~Qt::AlignHorizontal_Mask;
|
|
if (0 != (val & Qt::AlignVertical_Mask))
|
|
initVal &= ~Qt::AlignVertical_Mask;
|
|
initVal |= val;
|
|
}
|
|
TelEngine::destruct(list);
|
|
return initVal;
|
|
}
|
|
|
|
// Send an event to an object's child
|
|
bool QtClient::sendEvent(QEvent& e, QObject* parent, const QString& name)
|
|
{
|
|
if (!(parent && e.isAccepted()))
|
|
return false;
|
|
QObject* child = qFindChild<QObject*>(parent,name);
|
|
if (!child)
|
|
return false;
|
|
e.setAccepted(false);
|
|
bool ok = QCoreApplication::sendEvent(child,&e);
|
|
if (!ok)
|
|
e.setAccepted(true);
|
|
return ok;
|
|
}
|
|
|
|
// Retrieve a pixmap from global application cache.
|
|
// Load and add it to the cache if not found
|
|
bool QtClient::getPixmapFromCache(QPixmap& pixmap, const QString& file)
|
|
{
|
|
if (file.isEmpty())
|
|
return false;
|
|
QPixmap* cached = QPixmapCache::find(file);
|
|
if (cached) {
|
|
pixmap = *cached;
|
|
return true;
|
|
}
|
|
if (!pixmap.load(file))
|
|
return false;
|
|
#ifdef XDEBUG
|
|
String f;
|
|
getUtf8(f,file);
|
|
Debug(ClientDriver::self(),DebugAll,"Loaded '%s' in pixmap cache",f.c_str());
|
|
#endif
|
|
QPixmapCache::insert(file,pixmap);
|
|
return true;
|
|
}
|
|
|
|
// Update application style sheet from config
|
|
// Build style sheet from files:
|
|
// stylesheet.css
|
|
// stylesheet_stylename.css
|
|
// stylesheet_osname.css
|
|
// stylesheet_osname_stylename.css
|
|
void QtClient::updateAppStyleSheet()
|
|
{
|
|
if (!qApp) {
|
|
Debug(ClientDriver::self(),DebugWarn,"Update app stylesheet called without app");
|
|
return;
|
|
}
|
|
String shf = Engine::config().getValue("client","stylesheet_file","stylesheet.css");
|
|
if (!shf)
|
|
return;
|
|
QString sh;
|
|
if (!appendStyleSheet(sh,shf))
|
|
return;
|
|
String styleName;
|
|
QStyle* style = qApp->style();
|
|
const QMetaObject* meta = style ? style->metaObject() : 0;
|
|
if (meta) {
|
|
styleName = s_qtStyles.getValue(meta->className());
|
|
if (!styleName)
|
|
styleName = meta->className();
|
|
}
|
|
if (styleName)
|
|
appendStyleSheet(sh,shf,styleName);
|
|
String osname;
|
|
osname << "os" << PLATFORM_LOWERCASE_NAME;
|
|
appendStyleSheet(sh,shf,osname);
|
|
if (styleName)
|
|
appendStyleSheet(sh,shf,osname,styleName);
|
|
qApp->setStyleSheet(sh);
|
|
}
|
|
|
|
// Set widget attributes from list
|
|
void QtClient::setWidgetAttributes(QWidget* w, const String& attrs)
|
|
{
|
|
if (!(w && attrs))
|
|
return;
|
|
ObjList* list = attrs.split(',',false);
|
|
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
|
|
const String& attr = *static_cast<String*>(o->get());
|
|
bool on = (attr[0] != '!');
|
|
const char* name = attr.c_str();
|
|
int val = lookup(on ? name : name + 1,s_widgetAttributes);
|
|
if (val)
|
|
w->setAttribute((Qt::WidgetAttribute)val,on);
|
|
}
|
|
TelEngine::destruct(list);
|
|
}
|
|
|
|
// Adjust widget height
|
|
void QtClient::setWidgetHeight(QWidget* w, const String& height)
|
|
{
|
|
if (!w)
|
|
return;
|
|
int h = 0;
|
|
if (height.isBoolean()) {
|
|
h = QtClient::getIntProperty(w,"_yate_height_delta",-1);
|
|
if (h > 0) {
|
|
if (height.toBoolean())
|
|
h += w->height();
|
|
else if (h < w->height())
|
|
h = w->height() - h;
|
|
else
|
|
h = 0;
|
|
}
|
|
}
|
|
else
|
|
h = height.toInteger();
|
|
if (h < 0)
|
|
return;
|
|
QSizePolicy sp = w->sizePolicy();
|
|
sp.setVerticalPolicy(QSizePolicy::Fixed);
|
|
w->setSizePolicy(sp);
|
|
w->setMinimumHeight(h);
|
|
w->setMaximumHeight(h);
|
|
}
|
|
|
|
|
|
/**
|
|
* QtDriver
|
|
*/
|
|
QtDriver::QtDriver(bool buildClientThread)
|
|
: m_init(false), m_clientThread(buildClientThread)
|
|
{
|
|
qInstallMsgHandler(qtMsgHandler);
|
|
}
|
|
|
|
QtDriver::~QtDriver()
|
|
{
|
|
qInstallMsgHandler(0);
|
|
}
|
|
|
|
void QtDriver::initialize()
|
|
{
|
|
Output("Initializing module Qt4 client");
|
|
s_device = Engine::config().getValue("client","device",DEFAULT_DEVICE);
|
|
if (!QtClient::self()) {
|
|
debugCopy();
|
|
QtClient::setSelf(new QtClient);
|
|
if (m_clientThread)
|
|
QtClient::self()->startup();
|
|
}
|
|
if (!m_init) {
|
|
m_init = true;
|
|
setup();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* QtEventProxy
|
|
*/
|
|
QtEventProxy::QtEventProxy(Type type, QApplication* app)
|
|
{
|
|
#define SET_NAME(n) { m_name = n; setObjectName(QtClient::setUtf8(m_name)); }
|
|
switch (type) {
|
|
case Timer:
|
|
SET_NAME("qtClientTimerProxy");
|
|
{
|
|
QTimer* timer = new QTimer(this);
|
|
timer->setObjectName("qtClientIdleTimer");
|
|
QtClient::connectObjects(timer,SIGNAL(timeout()),this,SLOT(timerTick()));
|
|
timer->start(0);
|
|
}
|
|
break;
|
|
case AllHidden:
|
|
SET_NAME("qtClientAllHidden");
|
|
if (app)
|
|
QtClient::connectObjects(app,SIGNAL(lastWindowClosed()),this,SLOT(allHidden()));
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
#undef SET_NAME
|
|
}
|
|
|
|
void QtEventProxy::timerTick()
|
|
{
|
|
if (Client::self())
|
|
Client::self()->idleActions();
|
|
Thread::idle();
|
|
}
|
|
|
|
void QtEventProxy::allHidden()
|
|
{
|
|
if (Client::self())
|
|
Client::self()->allHidden();
|
|
}
|
|
|
|
/*
|
|
* QtUIWidget
|
|
*/
|
|
// Retrieve item type definition from [type:]value. Create it if not found
|
|
QtUIWidgetItemProps* QtUIWidget::getItemProps(QString& in, String& value)
|
|
{
|
|
String type;
|
|
int pos = in.indexOf(':');
|
|
if (pos >= 0) {
|
|
QtClient::getUtf8(type,in.left(pos));
|
|
QtClient::getUtf8(value,in.right(in.length() - pos - 1));
|
|
}
|
|
else
|
|
QtClient::getUtf8(value,in);
|
|
QtUIWidgetItemProps* p = QtUIWidget::getItemProps(type);
|
|
if (!p) {
|
|
p = new QtUIWidgetItemProps(type);
|
|
m_itemProps.append(p);
|
|
}
|
|
DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) getItemProps(%s,%s) got (%p) ui=%s [%p]",
|
|
name().c_str(),in.toUtf8().constData(),value.c_str(),p,p->m_ui.c_str(),this);
|
|
return p;
|
|
}
|
|
|
|
// Set widget's parameters.
|
|
// Handle an 'applyall' parameter carrying a NamedList to apply to all items
|
|
bool QtUIWidget::setParams(const NamedList& params)
|
|
{
|
|
bool ok = false;
|
|
NamedString* ns = params.getParam(YSTRING("applyall"));
|
|
if (ns) {
|
|
NamedList* list = YOBJECT(NamedList,ns);
|
|
if (list) {
|
|
ok = true;
|
|
applyAllParams(*list);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Apply a list of parameters to all container items
|
|
void QtUIWidget::applyAllParams(const NamedList& params)
|
|
{
|
|
QList<QObject*> list = getContainerItems();
|
|
for (int i = 0; i < list.size(); i++)
|
|
setParams(list[i],params);
|
|
}
|
|
|
|
// Find an item widget by id
|
|
QWidget* QtUIWidget::findItem(const String& id)
|
|
{
|
|
QString item = QtClient::setUtf8(id);
|
|
QList<QObject*> list = getContainerItems();
|
|
for (int i = 0; i < list.size(); i++) {
|
|
if (!list[i]->isWidgetType())
|
|
continue;
|
|
String item;
|
|
getListItemIdProp(list[i],item);
|
|
if (id == item)
|
|
return static_cast<QWidget*>(list[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Retrieve the object identity from '_yate_identity' property or name
|
|
// Retrieve the object item from '_yate_widgetlistitem' property.
|
|
// Set 'identity' to object_identity[:item_name]
|
|
void QtUIWidget::getIdentity(QObject* obj, String& identity)
|
|
{
|
|
if (!obj)
|
|
return;
|
|
String ident;
|
|
QtClient::getIdentity(obj,ident);
|
|
if (!ident)
|
|
return;
|
|
String item;
|
|
getListItemProp(obj,item);
|
|
identity.append(ident,":");
|
|
identity.append(item,":");
|
|
}
|
|
|
|
// Update a widget and children from a list a parameters
|
|
bool QtUIWidget::setParams(QObject* parent, const NamedList& params)
|
|
{
|
|
static const String s_property = "property";
|
|
static const String s_active = "active";
|
|
static const String s_image = "image";
|
|
static const String s_show = "show";
|
|
static const String s_display = "display";
|
|
static const String s_check = "check";
|
|
static const String s_select = "select";
|
|
static const String s_addlines = "addlines";
|
|
static const String s_setrichtext = "setrichtext";
|
|
static const String s_updatetablerows = "updatetablerows";
|
|
static const String s_cleartable = "cleartable";
|
|
static const String s_rawimage = "rawimage";
|
|
static const String s_setparams = "setparams";
|
|
static const String s_setmenu = "setmenu";
|
|
static const String s_height = "height";
|
|
|
|
if (!parent)
|
|
return false;
|
|
QtWindow* wnd = QtClient::parentWindow(parent);
|
|
if (!wnd)
|
|
return false;
|
|
#ifdef DEBUG
|
|
String tmp;
|
|
params.dump(tmp," ");
|
|
Debug(ClientDriver::self(),DebugAll,"QtUIWidget(%s)::setParams(%p,%s) %s",
|
|
name().c_str(),parent,YQT_OBJECT_NAME(parent),tmp.c_str());
|
|
#endif
|
|
String pName(YQT_OBJECT_NAME(parent));
|
|
bool ok = true;
|
|
unsigned int n = params.length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = params.getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
XDebug(ClientDriver::self(),DebugInfo,"QtUIWidget(%s)::setParams() %s=%s",
|
|
name().c_str(),ns->name().c_str(),ns->c_str());
|
|
String buf;
|
|
int pos = ns->name().find(':');
|
|
if (pos < 0) {
|
|
if (ns->name() != s_setmenu)
|
|
ok = wnd->setText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
|
|
else
|
|
buildWidgetItemMenu(qobject_cast<QWidget*>(parent),YOBJECT(NamedList,ns));
|
|
continue;
|
|
}
|
|
String n(ns->name().substr(0,pos));
|
|
String cName = ns->name().substr(pos + 1);
|
|
if (n == s_property) {
|
|
// Handle property[:child]:property_name
|
|
int pos = cName.find(':');
|
|
if (pos >= 0) {
|
|
QString tmp = buildQChildName(pName,cName.substr(0,pos));
|
|
QObject* c = qFindChild<QObject*>(parent,tmp);
|
|
ok = c && QtClient::setProperty(c,cName.substr(pos + 1),*ns) && ok;
|
|
}
|
|
else
|
|
ok = QtClient::setProperty(parent,cName,*ns) && ok;
|
|
}
|
|
else if (n == s_active)
|
|
ok = wnd->setActive(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
|
|
else if (n == s_image)
|
|
ok = wnd->setImage(buildChildName(buf,pName,cName),*ns) && ok;
|
|
else if (n == s_show || n == s_display)
|
|
ok = wnd->setShow(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
|
|
else if (n == s_check)
|
|
ok = wnd->setCheck(buildChildName(buf,pName,cName),ns->toBoolean()) && ok;
|
|
else if (n == s_select)
|
|
ok = wnd->setSelect(buildChildName(buf,pName,cName),*ns) && ok;
|
|
if (n == s_setparams) {
|
|
NamedList* p = YOBJECT(NamedList,ns);
|
|
if (!p)
|
|
continue;
|
|
QtWidget w(parent,buildChildName(buf,pName,cName));
|
|
UIWidget* uiw = w.uiWidget();
|
|
ok = uiw && uiw->setParams(*p) && ok;
|
|
}
|
|
else if (n == s_addlines) {
|
|
NamedList* p = YOBJECT(NamedList,ns);
|
|
if (p)
|
|
ok = wnd->addLines(buildChildName(buf,pName,cName),p,0,ns->toBoolean()) && ok;
|
|
}
|
|
else if (n == s_setrichtext)
|
|
ok = wnd->setText(buildChildName(buf,pName,cName),*ns,true) && ok;
|
|
else if (n == s_updatetablerows) {
|
|
NamedList* p = YOBJECT(NamedList,ns);
|
|
if (p)
|
|
ok = wnd->updateTableRows(buildChildName(buf,pName,cName),p,ns->toBoolean()) && ok;
|
|
}
|
|
else if (n == s_cleartable)
|
|
ok = wnd->clearTable(buildChildName(buf,pName,cName)) && ok;
|
|
else if (n == s_rawimage) {
|
|
DataBlock* data = YOBJECT(DataBlock,ns);
|
|
if (data) {
|
|
QString tmp = buildQChildName(pName,cName.substr(0,pos));
|
|
QObject* c = qFindChild<QObject*>(parent,tmp);
|
|
ok = c && QtClient::setImage(c,*data,*ns) && ok;
|
|
}
|
|
}
|
|
else if (n == s_setmenu)
|
|
buildWidgetItemMenu(qobject_cast<QWidget*>(parent),YOBJECT(NamedList,ns),cName);
|
|
else if (n == s_height) {
|
|
QString tmp = buildQChildName(pName,cName);
|
|
QWidget* w = qFindChild<QWidget*>(qobject_cast<QWidget*>(parent),tmp);
|
|
QtClient::setWidgetHeight(w,*ns);
|
|
}
|
|
else
|
|
ok = wnd->setText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
|
|
}
|
|
// Set item parameters
|
|
NamedString* yparams = params.getParam(YSTRING("_yate_itemparams"));
|
|
if (!TelEngine::null(yparams)) {
|
|
QVariant var = parent->property(yparams->name().c_str());
|
|
if (var.type() == QVariant::Invalid || var.type() == QVariant::StringList) {
|
|
QStringList list;
|
|
if (var.type() == QVariant::StringList)
|
|
list = var.toStringList();
|
|
NamedList tmp("");
|
|
tmp.copyParams(params,*yparams);
|
|
QtClient::copyParams(list,tmp);
|
|
parent->setProperty(yparams->name().c_str(),QVariant(list));
|
|
}
|
|
else
|
|
ok = false;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Get an item object's parameters
|
|
bool QtUIWidget::getParams(QObject* parent, NamedList& params)
|
|
{
|
|
static const String s_property = "property";
|
|
static const String s_getcheck = "getcheck";
|
|
static const String s_getselect = "getselect";
|
|
static const String s_getrichtext = "getrichtext";
|
|
|
|
if (!parent)
|
|
return false;
|
|
QtWindow* wnd = QtClient::parentWindow(parent);
|
|
if (!wnd)
|
|
return false;
|
|
DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s)::getParams(%p,%s)",
|
|
name().c_str(),parent,YQT_OBJECT_NAME(parent));
|
|
String pName;
|
|
QtClient::getUtf8(pName,parent->objectName());
|
|
bool ok = true;
|
|
unsigned int n = params.length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = params.getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
String buf;
|
|
int pos = ns->name().find(':');
|
|
if (pos < 0) {
|
|
ok = wnd->getText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
|
|
continue;
|
|
}
|
|
String n(ns->name().substr(0,pos));
|
|
String cName = ns->name().substr(pos + 1);
|
|
if (n == s_property) {
|
|
// Handle property[:child]:property_name
|
|
int pos = cName.find(':');
|
|
if (pos >= 0) {
|
|
QString tmp = buildQChildName(pName,cName.substr(0,pos));
|
|
QObject* c = qFindChild<QObject*>(parent,tmp);
|
|
ok = c && QtClient::getProperty(c,cName.substr(pos + 1),*ns) && ok;
|
|
}
|
|
else
|
|
ok = QtClient::getProperty(parent,cName,*ns) && ok;
|
|
}
|
|
else if (n == s_getselect)
|
|
ok = wnd->getSelect(buildChildName(buf,pName,cName),*ns) && ok;
|
|
else if (n == s_getcheck) {
|
|
bool on = false;
|
|
ok = wnd->getCheck(buildChildName(buf,pName,cName),on) && ok;
|
|
*ns = String::boolText(on);
|
|
}
|
|
else if (n == s_getrichtext)
|
|
ok = wnd->getText(buildChildName(buf,pName,cName),*ns,true) && ok;
|
|
else
|
|
ok = wnd->getText(buildChildName(buf,pName,ns->name()),*ns,false) && ok;
|
|
}
|
|
// Get item parameters
|
|
QtClient::getProperty(parent,"_yate_itemparams",params);
|
|
return ok;
|
|
}
|
|
|
|
// Build item widget menu
|
|
QMenu* QtUIWidget::buildWidgetItemMenu(QWidget* w, const NamedList* params,
|
|
const String& child, bool set)
|
|
{
|
|
if (!(w && params))
|
|
return 0;
|
|
QWidget* parent = w;
|
|
// Retrieve the item owner
|
|
QWidget* pItem = 0;
|
|
String item;
|
|
getListItemIdProp(w,item);
|
|
if (item)
|
|
pItem = findItem(item);
|
|
else {
|
|
getListItemProp(w,item);
|
|
pItem = item ? findItem(item) : 0;
|
|
}
|
|
XDebug(ClientDriver::self(),DebugAll,
|
|
"QtUIWidget(%s)::buildMenu() widget=%s item=%s [%p]",
|
|
this->name().c_str(),YQT_OBJECT_NAME(w),item.c_str(),this);
|
|
String pName(YQT_OBJECT_NAME(w));
|
|
const String& owner = (*params)[YSTRING("owner")];
|
|
if (owner && owner != item) {
|
|
QString tmp = buildQChildName(pName,owner);
|
|
parent = qFindChild<QWidget*>(w,tmp);
|
|
if (!parent) {
|
|
Debug(QtDriver::self(),DebugNote,
|
|
"QtUIWidget(%s) buildMenu() owner '%s' not found [%p]",
|
|
name().c_str(),owner.c_str(),this);
|
|
return 0;
|
|
}
|
|
}
|
|
QWidget* target = parent;
|
|
String t = child ? child : (*params)[YSTRING("target")];
|
|
if (t) {
|
|
QString tmp = buildQChildName(pName,t);
|
|
target = qFindChild<QWidget*>(w,tmp);
|
|
if (!target) {
|
|
Debug(QtDriver::self(),DebugNote,
|
|
"QtUIWidget(%s) buildMenu() target '%s' not found [%p]",
|
|
name().c_str(),t.c_str(),this);
|
|
return 0;
|
|
}
|
|
}
|
|
QString menuName = buildQChildName(pName,t + "_menu");
|
|
// Remove existing menu
|
|
QMenu* menu = qFindChild<QMenu*>(parent,menuName);
|
|
if (menu) {
|
|
delete menu;
|
|
menu = 0;
|
|
}
|
|
// Build the menu
|
|
QObject* thisObj = getQObject();
|
|
if (!thisObj)
|
|
return 0;
|
|
String actionSlot;
|
|
String toggleSlot;
|
|
String selectSlot;
|
|
getSlots(actionSlot,toggleSlot,selectSlot);
|
|
if (!(actionSlot || toggleSlot))
|
|
return 0;
|
|
bool addActions = set && target->contextMenuPolicy() == Qt::ActionsContextMenu;
|
|
unsigned int n = params->length();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* param = params->getParam(i);
|
|
if (!(param && param->name().startsWith("item:")))
|
|
continue;
|
|
if (!menu)
|
|
menu = new QMenu(QtClient::setUtf8(params->getValue(YSTRING("title"))),parent);
|
|
NamedList* p = YOBJECT(NamedList,param);
|
|
if (p) {
|
|
QMenu* subMenu = QtClient::buildMenu(*p,
|
|
*param ? param->c_str() : p->getValue(YSTRING("title"),*p),
|
|
thisObj,actionSlot,toggleSlot,menu);
|
|
if (subMenu) {
|
|
menu->addMenu(subMenu);
|
|
if (addActions)
|
|
target->addAction(subMenu->menuAction());
|
|
}
|
|
continue;
|
|
}
|
|
QAction* a = 0;
|
|
String name = param->name().substr(5);
|
|
if (*param) {
|
|
a = menu->addAction(QtClient::setUtf8(*param));
|
|
a->setObjectName(buildQChildName(pName,name));
|
|
a->setParent(menu);
|
|
QtClient::setImage(a,(*params)["image:" + name]);
|
|
}
|
|
else if (!name) {
|
|
a = menu->addSeparator();
|
|
a->setParent(menu);
|
|
}
|
|
else if (pItem) {
|
|
// Check if the action is already there
|
|
QString aName = buildQChildName(pItem->objectName(),QtClient::setUtf8(name));
|
|
a = qFindChild<QAction*>(pItem,aName);
|
|
if (a)
|
|
menu->addAction(a);
|
|
}
|
|
if (a) {
|
|
if (addActions)
|
|
target->addAction(a);
|
|
}
|
|
else
|
|
Debug(ClientDriver::self(),DebugNote,
|
|
"QtUIWidget(%s)::buildMenu() action '%s' not found for item=%s [%p]",
|
|
this->name().c_str(),name.c_str(),item.c_str(),this);
|
|
}
|
|
if (!menu)
|
|
return 0;
|
|
// Set name
|
|
menu->setObjectName(menuName);
|
|
// Apply properties
|
|
// Format: property:object_name:property_name=value
|
|
if (parent)
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* param = params->getParam(i);
|
|
if (!(param && param->name().startsWith("property:")))
|
|
continue;
|
|
int pos = param->name().find(':',9);
|
|
if (pos < 9)
|
|
continue;
|
|
QString n = buildQChildName(pName,param->name().substr(9,pos - 9));
|
|
QObject* obj = qFindChild<QObject*>(parent,n);
|
|
if (obj)
|
|
QtClient::setProperty(obj,param->name().substr(pos + 1),*param);
|
|
}
|
|
// Connect signals (direct children only: actions from sub-menus are already connected)
|
|
QList<QAction*> list = qFindChildren<QAction*>(menu);
|
|
for (int i = 0; i < list.size(); i++) {
|
|
if (list[i]->isSeparator() || list[i]->parent() != menu)
|
|
continue;
|
|
if (list[i]->isCheckable())
|
|
QtClient::connectObjects(list[i],SIGNAL(toggled(bool)),thisObj,toggleSlot);
|
|
else
|
|
QtClient::connectObjects(list[i],SIGNAL(triggered()),thisObj,actionSlot);
|
|
}
|
|
if (addActions)
|
|
return menu;
|
|
QMenu* mOwner = qobject_cast<QMenu*>(target);
|
|
if (mOwner)
|
|
mOwner->insertMenu(0,menu);
|
|
else {
|
|
QToolButton* tb = qobject_cast<QToolButton*>(target);
|
|
if (tb)
|
|
tb->setMenu(menu);
|
|
else {
|
|
QPushButton* pb = qobject_cast<QPushButton*>(target);
|
|
if (pb)
|
|
pb->setMenu(menu);
|
|
else if (!QtClient::setProperty(target,s_propContextMenu,params))
|
|
target->addAction(menu->menuAction());
|
|
}
|
|
}
|
|
return menu;
|
|
}
|
|
|
|
// Build a container child name from parent property
|
|
bool QtUIWidget::buildQChildNameProp(QString& dest, QObject* parent, const char* prop)
|
|
{
|
|
if (!(parent && prop))
|
|
return false;
|
|
QVariant var = parent->property(prop);
|
|
if (!var.isValid() || var.toString().size() <= 0)
|
|
return false;
|
|
dest = buildQChildName(parent->objectName(),var.toString());
|
|
return true;
|
|
}
|
|
|
|
// Retrieve the top level QtUIWidget container parent of an object
|
|
QtUIWidget* QtUIWidget::container(QObject* obj)
|
|
{
|
|
if (!obj)
|
|
return 0;
|
|
QtUIWidget* uiw = 0;
|
|
while (0 != (obj = obj->parent())) {
|
|
QtWidget w(obj);
|
|
UIWidget* u = w.uiWidget();
|
|
if (u)
|
|
uiw = static_cast<QtUIWidget*>(u);
|
|
}
|
|
return uiw;
|
|
}
|
|
|
|
// Utility used in QtUIWidget::initNavigation
|
|
static bool initNavAction(QObject* obj, const String& name, const String& actionSlot)
|
|
{
|
|
if (!(obj && name))
|
|
return false;
|
|
QtWindow* wnd = QtClient::parentWindow(obj);
|
|
QObject* child = qFindChild<QObject*>(wnd,QtClient::setUtf8(name));
|
|
if (!child)
|
|
return false;
|
|
QAbstractButton* b = 0;
|
|
QAction* a = 0;
|
|
if (child->isWidgetType())
|
|
b = qobject_cast<QAbstractButton*>(child);
|
|
else
|
|
a = qobject_cast<QAction*>(child);
|
|
if (b || a) {
|
|
if (b)
|
|
QtClient::connectObjects(b,SIGNAL(clicked()),obj,actionSlot);
|
|
else
|
|
QtClient::connectObjects(a,SIGNAL(triggered()),obj,actionSlot);
|
|
}
|
|
return b || a;
|
|
}
|
|
|
|
// Initialize navigation controls
|
|
void QtUIWidget::initNavigation(const NamedList& params)
|
|
{
|
|
String actionSlot;
|
|
String toggleSlot;
|
|
String selectSlot;
|
|
getSlots(actionSlot,toggleSlot,selectSlot);
|
|
QObject* qObj = getQObject();
|
|
if (qObj && actionSlot) {
|
|
m_prev = params.getValue(YSTRING("navigate_prev"));
|
|
if (!initNavAction(qObj,m_prev,actionSlot))
|
|
m_prev = "";
|
|
m_next = params.getValue(YSTRING("navigate_next"));
|
|
if (!initNavAction(qObj,m_next,actionSlot))
|
|
m_next = "";
|
|
}
|
|
m_info = params.getValue(YSTRING("navigate_info"));
|
|
m_infoFormat = params.getValue(YSTRING("navigate_info_format"));
|
|
m_title = params.getValue(YSTRING("navigate_title"));
|
|
updateNavigation();
|
|
}
|
|
|
|
// Update navigation controls
|
|
void QtUIWidget::updateNavigation()
|
|
{
|
|
if (!(m_prev || m_next || m_info || m_title))
|
|
return;
|
|
QtWindow* wnd = QtClient::parentWindow(getQObject());
|
|
if (!wnd)
|
|
return;
|
|
NamedList p("");
|
|
int crt = currentItemIndex();
|
|
if (crt < 0)
|
|
crt = 0;
|
|
else
|
|
crt++;
|
|
int n = itemCount();
|
|
if (n < crt)
|
|
n = crt;
|
|
if (m_prev || m_next) {
|
|
if (m_prev)
|
|
p.addParam("active:" + m_prev,String::boolText(crt > 1));
|
|
if (m_next)
|
|
p.addParam("active:" + m_next,String::boolText(crt < n));
|
|
}
|
|
if (m_info) {
|
|
String tmp = m_infoFormat;
|
|
NamedList pp("");
|
|
pp.addParam("index",String(crt));
|
|
pp.addParam("count",String(n));
|
|
pp.replaceParams(tmp);
|
|
p.addParam(m_info,tmp);
|
|
}
|
|
if (m_title) {
|
|
String crt;
|
|
getSelect(crt);
|
|
NamedList pp("");
|
|
if (crt)
|
|
getTableRow(crt,&pp);
|
|
p.addParam(m_title,pp[YSTRING("title")]);
|
|
}
|
|
wnd->setParams(p);
|
|
}
|
|
|
|
// Trigger a custom action from an item
|
|
void QtUIWidget::triggerAction(const String& item, const String& action, QObject* sender)
|
|
{
|
|
if (!(Client::self() && action))
|
|
return;
|
|
if (!sender)
|
|
sender = getQObject();
|
|
String s;
|
|
getIdentity(sender,s);
|
|
if (!s)
|
|
return;
|
|
NamedList params("");
|
|
params.addParam("item",item);
|
|
params.addParam("list",s);
|
|
QtClient::self()->action(QtClient::parentWindow(sender),action,¶ms);
|
|
}
|
|
|
|
// Handle a child's action
|
|
void QtUIWidget::onAction(QObject* sender)
|
|
{
|
|
if (!Client::self())
|
|
return;
|
|
String s;
|
|
getIdentity(sender,s);
|
|
if (!s)
|
|
return;
|
|
int dir = 0;
|
|
if (s == m_next)
|
|
dir = 1;
|
|
else if (s == m_prev)
|
|
dir = -1;
|
|
if (dir) {
|
|
int crt = currentItemIndex();
|
|
if (crt >= 0)
|
|
setSelectIndex(crt + dir);
|
|
return;
|
|
}
|
|
DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising action %s",
|
|
name().c_str(),s.c_str());
|
|
Client::self()->action(QtClient::parentWindow(sender),s);
|
|
}
|
|
|
|
// Handle a child's toggle notification
|
|
void QtUIWidget::onToggle(QObject* sender, bool on)
|
|
{
|
|
if (!Client::self())
|
|
return;
|
|
QtClient::updateToggleImage(sender);
|
|
String s;
|
|
getIdentity(sender,s);
|
|
if (!s)
|
|
return;
|
|
DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising toggle %s",
|
|
name().c_str(),s.c_str());
|
|
Client::self()->toggle(QtClient::parentWindow(sender),s,on);
|
|
}
|
|
|
|
// Handle a child's selection change
|
|
void QtUIWidget::onSelect(QObject* sender, const String* item)
|
|
{
|
|
if (!Client::self())
|
|
return;
|
|
String s;
|
|
getIdentity(sender,s);
|
|
if (!s)
|
|
return;
|
|
QtWindow* wnd = QtClient::parentWindow(sender);
|
|
String tmp;
|
|
if (!item) {
|
|
item = &tmp;
|
|
if (wnd)
|
|
wnd->getSelect(YQT_OBJECT_NAME(sender),tmp);
|
|
}
|
|
DDebug(ClientDriver::self(),DebugAll,"QtUIWidget(%s) raising select %s",
|
|
name().c_str(),s.c_str());
|
|
Client::self()->select(wnd,s,*item);
|
|
}
|
|
|
|
// Filter wathed events for children.
|
|
// Handle child image changing on mouse events
|
|
void QtUIWidget::onChildEvent(QObject* watched, QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::Enter)
|
|
QtClient::updateImageFromMouse(watched,true,true);
|
|
else if (event->type() == QEvent::Leave)
|
|
QtClient::updateImageFromMouse(watched,true,false);
|
|
else if (event->type() == QEvent::MouseButtonPress)
|
|
QtClient::updateImageFromMouse(watched,false,true);
|
|
else if (event->type() == QEvent::MouseButtonRelease)
|
|
QtClient::updateImageFromMouse(watched,false,false);
|
|
}
|
|
|
|
// Load an item's widget. Rename children. Connect actions
|
|
QWidget* QtUIWidget::loadWidget(QWidget* parent, const String& name, const String& ui)
|
|
{
|
|
// Build a new widget name to make sure there are no duplicates:
|
|
// Some containers (like QTreeWidget) calls deleteLater() for widget's
|
|
// set to items which might lead to wrong widget update
|
|
// Make sure the widget name contains only 'standard' characters
|
|
// to avoid errors when replaced in style sheets
|
|
MD5 md5(name);
|
|
String wName;
|
|
buildChildName(wName,md5.hexDigest());
|
|
wName << "_" << (unsigned int)Time::now();
|
|
QWidget* w = QtWindow::loadUI(Client::s_skinPath + ui,parent,ui);
|
|
DDebug(ClientDriver::self(),w ? DebugAll : DebugNote,
|
|
"QtUIWidget(%s)::loadWidget(%p,%s,%s) widget=%p",
|
|
this->name().c_str(),parent,wName.c_str(),ui.c_str(),w);
|
|
if (!w)
|
|
return 0;
|
|
QObject* qObj = getQObject();
|
|
QtWindow* wnd = getWindow();
|
|
// Install event filter in parent window
|
|
if (!m_wndEvHooked && wnd && qObj) {
|
|
QVariant var = w->property("_yate_keypress_redirect");
|
|
if (var.isValid()) {
|
|
m_wndEvHooked = true;
|
|
wnd->installEventFilter(qObj);
|
|
}
|
|
}
|
|
String actionSlot;
|
|
String toggleSlot;
|
|
String selectSlot;
|
|
getSlots(actionSlot,toggleSlot,selectSlot);
|
|
QString wListItem = QtClient::setUtf8(name);
|
|
w->setObjectName(QtClient::setUtf8(wName));
|
|
setListItemIdProp(w,wListItem);
|
|
// Build custom UI widgets
|
|
QtClient::buildFrameUiWidgets(w);
|
|
// Process "_yate_setaction" property before changing names
|
|
QtClient::setAction(w);
|
|
// Process children
|
|
QList<QObject*> c = qFindChildren<QObject*>(w);
|
|
for (int i = 0; i < c.size(); i++) {
|
|
// Set object item owner name
|
|
setListItemProp(c[i],wListItem);
|
|
// Rename child
|
|
String n;
|
|
QtClient::getUtf8(n,c[i]->objectName());
|
|
c[i]->setObjectName(buildQChildName(wName,n));
|
|
// Install event filters
|
|
if (qObj && QtClient::getBoolProperty(c[i],"_yate_filterevents"))
|
|
c[i]->installEventFilter(qObj);
|
|
// Connect text changed to window's slot
|
|
bool connect = QtClient::autoConnect(c[i]);
|
|
if (wnd && connect)
|
|
wnd->connectTextChanged(c[i]);
|
|
// Connect signals
|
|
if (!(qObj && connect && (actionSlot || toggleSlot || selectSlot)))
|
|
continue;
|
|
// Use isWidgetType() (faster then qobject_cast)
|
|
if (c[i]->isWidgetType()) {
|
|
// Connect abstract buttons (check boxes and radio/push/tool buttons) signals
|
|
QAbstractButton* b = qobject_cast<QAbstractButton*>(c[i]);
|
|
if (b) {
|
|
if (!b->isCheckable())
|
|
QtClient::connectObjects(b,SIGNAL(clicked()),qObj,actionSlot);
|
|
else
|
|
QtClient::connectObjects(b,SIGNAL(toggled(bool)),qObj,toggleSlot);
|
|
continue;
|
|
}
|
|
// Connect group boxes
|
|
QGroupBox* gb = qobject_cast<QGroupBox*>(c[i]);
|
|
if (gb) {
|
|
if (gb->isCheckable())
|
|
QtClient::connectObjects(gb,SIGNAL(toggled(bool)),qObj,toggleSlot);
|
|
continue;
|
|
}
|
|
// Connect combo boxes
|
|
QComboBox* combo = qobject_cast<QComboBox*>(c[i]);
|
|
if (combo) {
|
|
QtClient::connectObjects(combo,SIGNAL(activated(int)),qObj,selectSlot);
|
|
continue;
|
|
}
|
|
// Connect list boxes
|
|
QListWidget* lst = qobject_cast<QListWidget*>(c[i]);
|
|
if (lst) {
|
|
QtClient::connectObjects(lst,SIGNAL(currentRowChanged(int)),qObj,selectSlot);
|
|
continue;
|
|
}
|
|
// Connect sliders
|
|
QSlider* sld = qobject_cast<QSlider*>(c[i]);
|
|
if (sld) {
|
|
QtClient::connectObjects(sld,SIGNAL(valueChanged(int)),qObj,selectSlot);
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
// Connect actions signals
|
|
QAction* a = qobject_cast<QAction*>(c[i]);
|
|
if (a) {
|
|
if (!a->isCheckable())
|
|
QtClient::connectObjects(a,SIGNAL(triggered()),qObj,actionSlot);
|
|
else
|
|
QtClient::connectObjects(a,SIGNAL(toggled(bool)),qObj,toggleSlot);
|
|
continue;
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
// Apply a QWidget style sheet. Replace ${name} with widget name in style
|
|
void QtUIWidget::applyWidgetStyle(QWidget* w, const String& style)
|
|
{
|
|
if (!(w && style))
|
|
return;
|
|
QString s = QtClient::setUtf8(style);
|
|
s.replace("${name}",w->objectName());
|
|
w->setStyleSheet(s);
|
|
}
|
|
|
|
// Filter key press events. Retrieve an action associated with the key.
|
|
// Check if the object is allowed to process the key.
|
|
// Raise the action
|
|
bool QtUIWidget::filterKeyEvent(QObject* watched, QKeyEvent* event, bool& filter)
|
|
{
|
|
String action;
|
|
if (!QtClient::filterKeyEvent(watched,event,action,filter))
|
|
return false;
|
|
if (!action)
|
|
return true;
|
|
String item;
|
|
getListItemProp(watched,item);
|
|
// Avoid raising a disabled actions
|
|
if (item) {
|
|
bool ok = true;
|
|
QWidget* w = findItem(item);
|
|
if (w) {
|
|
QString n = buildQChildName(w->objectName(),QtClient::setUtf8(action));
|
|
QObject* act = qFindChild<QObject*>(w,n);
|
|
if (act) {
|
|
if (act->isWidgetType())
|
|
ok = (qobject_cast<QWidget*>(act))->isEnabled();
|
|
else {
|
|
QAction* a = qobject_cast<QAction*>(act);
|
|
ok = !a || a->isEnabled();
|
|
}
|
|
}
|
|
}
|
|
if (!ok)
|
|
return true;
|
|
// Append container item to action
|
|
action.append(item,":");
|
|
}
|
|
Client::self()->action(QtClient::parentWindow(getQObject()),action);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* QtSound
|
|
*/
|
|
bool QtSound::doStart()
|
|
{
|
|
doStop();
|
|
if (Client::self())
|
|
Client::self()->createObject((void**)&m_sound,"QSound",m_file);
|
|
if (m_sound)
|
|
DDebug(ClientDriver::self(),DebugAll,"Sound(%s) started file=%s",
|
|
c_str(),m_file.c_str());
|
|
else {
|
|
Debug(ClientDriver::self(),DebugNote,"Sound(%s) failed to start file=%s",
|
|
c_str(),m_file.c_str());
|
|
return false;
|
|
}
|
|
m_sound->setLoops(m_repeat ? m_repeat : -1);
|
|
m_sound->play();
|
|
return true;
|
|
}
|
|
|
|
void QtSound::doStop()
|
|
{
|
|
if (!m_sound)
|
|
return;
|
|
m_sound->stop();
|
|
delete m_sound;
|
|
DDebug(ClientDriver::self(),DebugAll,"Sound(%s) stopped",c_str());
|
|
m_sound = 0;
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|