2017-02-27 21:15:20 +08:00
|
|
|
/*******************************************************************************
|
2017-06-10 04:04:02 +08:00
|
|
|
** Qt Advanced Docking System
|
2017-02-27 21:15:20 +08:00
|
|
|
** Copyright (C) 2017 Uwe Kindler
|
2018-05-06 18:45:46 +08:00
|
|
|
**
|
2017-06-10 04:04:02 +08:00
|
|
|
** This library is free software; you can redistribute it and/or
|
|
|
|
** modify it under the terms of the GNU Lesser General Public
|
|
|
|
** License as published by the Free Software Foundation; either
|
|
|
|
** version 2.1 of the License, or (at your option) any later version.
|
2018-05-06 18:45:46 +08:00
|
|
|
**
|
2017-06-10 04:04:02 +08:00
|
|
|
** This library is distributed in the hope that it will be useful,
|
2017-02-27 21:15:20 +08:00
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2017-06-10 04:04:02 +08:00
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
** Lesser General Public License for more details.
|
2018-05-06 18:45:46 +08:00
|
|
|
**
|
2017-06-10 04:04:02 +08:00
|
|
|
** You should have received a copy of the GNU Lesser General Public
|
|
|
|
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
2017-02-27 21:15:20 +08:00
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
2017-02-25 05:44:02 +08:00
|
|
|
//============================================================================
|
|
|
|
/// \file ads_globals.cpp
|
|
|
|
/// \author Uwe Kindler
|
|
|
|
/// \date 24.02.2017
|
2017-02-27 21:15:20 +08:00
|
|
|
/// \brief Implementation of
|
2017-02-25 05:44:02 +08:00
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// INCLUDES
|
|
|
|
//============================================================================
|
|
|
|
#include <QVariant>
|
2018-11-08 17:04:29 +08:00
|
|
|
#include <QPainter>
|
2020-02-05 15:57:57 +08:00
|
|
|
#include <QAbstractButton>
|
2020-05-11 01:30:34 +08:00
|
|
|
#include <QStyle>
|
2017-02-25 05:44:02 +08:00
|
|
|
|
2017-03-24 17:18:25 +08:00
|
|
|
#include "DockSplitter.h"
|
2020-02-05 15:57:57 +08:00
|
|
|
#include "DockManager.h"
|
|
|
|
#include "IconProvider.h"
|
2017-02-27 01:13:56 +08:00
|
|
|
#include "ads_globals.h"
|
|
|
|
|
2019-09-12 10:44:12 +08:00
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
#include <QX11Info>
|
|
|
|
#include <QSettings>
|
|
|
|
#include <QFile>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include <QApplication>
|
2017-03-24 17:18:25 +08:00
|
|
|
|
2017-02-25 05:44:02 +08:00
|
|
|
namespace ads
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace internal
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
#ifdef Q_OS_LINUX
|
2020-08-31 14:32:56 +08:00
|
|
|
static QString _window_manager;
|
|
|
|
static QHash<QString, xcb_atom_t> _xcb_atom_cache;
|
2019-09-12 10:44:12 +08:00
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
xcb_atom_t xcb_get_atom(const char *name)
|
|
|
|
{
|
|
|
|
if (!QX11Info::isPlatformX11())
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return XCB_ATOM_NONE;
|
|
|
|
}
|
|
|
|
auto key = QString(name);
|
2020-08-31 14:32:56 +08:00
|
|
|
if(_xcb_atom_cache.contains(key))
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return _xcb_atom_cache[key];
|
|
|
|
}
|
|
|
|
xcb_connection_t *connection = QX11Info::connection();
|
|
|
|
xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
|
|
|
|
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (!reply)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return XCB_ATOM_NONE;
|
|
|
|
}
|
|
|
|
xcb_atom_t atom = reply->atom;
|
2020-08-31 14:32:56 +08:00
|
|
|
if(atom == XCB_ATOM_NONE)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
ADS_PRINT("Unknown Atom response from XServer: " << name);
|
2020-08-31 14:32:56 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
_xcb_atom_cache.insert(key, atom);
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
return atom;
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
2019-09-12 10:44:12 +08:00
|
|
|
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
|
|
|
|
{
|
|
|
|
auto connection = QX11Info::connection();
|
|
|
|
xcb_atom_t type_atom = xcb_get_atom(type);
|
|
|
|
xcb_atom_t prop_atom = xcb_get_atom(prop);
|
|
|
|
xcb_client_message_event_t event;
|
|
|
|
event.response_type = XCB_CLIENT_MESSAGE;
|
|
|
|
event.format = 32;
|
|
|
|
event.sequence = 0;
|
|
|
|
event.window = window;
|
|
|
|
event.type = type_atom;
|
|
|
|
event.data.data32[0] = set ? 1 : 0;
|
|
|
|
event.data.data32[1] = prop_atom;
|
|
|
|
event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
|
|
|
|
event.data.data32[3] = 0;
|
|
|
|
event.data.data32[4] = 0;
|
|
|
|
|
|
|
|
xcb_send_event(connection, 0, window,
|
|
|
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE,
|
|
|
|
(const char *)&event);
|
|
|
|
xcb_flush(connection);
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type)
|
|
|
|
{
|
|
|
|
if (!QX11Info::isPlatformX11())
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
xcb_connection_t *connection = QX11Info::connection();
|
|
|
|
xcb_atom_t type_atom = xcb_get_atom(type);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (type_atom == XCB_ATOM_NONE)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
xcb_get_property_cookie_t request = xcb_get_property_unchecked(connection, 0, window, type_atom, atom_type, 0, 1024);
|
|
|
|
xcb_get_property_reply_t *reply = xcb_get_property_reply(connection, request, nullptr);
|
2020-08-31 14:32:56 +08:00
|
|
|
if(reply && reply->type != atom_type)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
ADS_PRINT("ATOM TYPE MISMATCH (" << type <<"). Expected: " << atom_type << " but got " << reply->type);
|
|
|
|
free(reply);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
2019-09-12 10:44:12 +08:00
|
|
|
template <typename T>
|
2020-08-31 14:32:56 +08:00
|
|
|
void xcb_get_prop_list(WId window, const char *type, QVector<T> &ret, unsigned int atom_type)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, atom_type);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (reply && reply->format == 32 && reply->type == atom_type && reply->value_len > 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
const xcb_atom_t *data = static_cast<const T *>(xcb_get_property_value(reply));
|
|
|
|
ret.resize(reply->value_len);
|
|
|
|
memcpy((void *)&ret.first(), (void *)data, reply->value_len * sizeof(T));
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QString xcb_get_prop_string(WId window, const char *type)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
QString ret;
|
|
|
|
// try utf8 first
|
|
|
|
xcb_atom_t utf_atom = xcb_get_atom("UTF8_STRING");
|
2020-08-31 14:32:56 +08:00
|
|
|
if(utf_atom != XCB_ATOM_NONE)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, utf_atom);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (reply && reply->format == 8 && reply->type == utf_atom)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
|
|
|
|
ret = QString::fromUtf8(value, xcb_get_property_value_length(reply));
|
|
|
|
free(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
// Fall back to XCB_ATOM_STRING
|
|
|
|
xcb_get_property_reply_t *reply = _xcb_get_props(window, type, XCB_ATOM_STRING);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
|
|
|
|
ret = QString::fromLatin1(value, xcb_get_property_value_length(reply));
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
bool xcb_dump_props(WId window, const char *type)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
QVector<xcb_atom_t> atoms;
|
|
|
|
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
|
|
|
|
qDebug() << "\n\n!!!" << type << " - " << atoms.length();
|
|
|
|
xcb_connection_t *connection = QX11Info::connection();
|
2020-08-31 14:32:56 +08:00
|
|
|
for (auto atom : atoms)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
auto foo = xcb_get_atom_name(connection, atom);
|
|
|
|
auto bar = xcb_get_atom_name_reply(connection, foo, nullptr);
|
|
|
|
qDebug() << "\t" << xcb_get_atom_name_name(bar);
|
|
|
|
free(bar);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
|
|
|
|
{
|
|
|
|
if (!QX11Info::isPlatformX11())
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
xcb_atom_t prop_atom = xcb_get_atom(prop);
|
|
|
|
xcb_atom_t type_atom = xcb_get_atom(type);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (prop_atom == XCB_ATOM_NONE || type_atom == XCB_ATOM_NONE)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
QVector<xcb_atom_t> atoms;
|
|
|
|
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
|
|
|
|
int index = atoms.indexOf(prop_atom);
|
2020-08-31 14:32:56 +08:00
|
|
|
if (state && index == -1)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
atoms.push_back(prop_atom);
|
2020-08-31 14:32:56 +08:00
|
|
|
}
|
|
|
|
else if (!state && index >= 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
atoms.remove(index);
|
|
|
|
}
|
|
|
|
xcb_connection_t *connection = QX11Info::connection();
|
|
|
|
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData());
|
|
|
|
xcb_flush(connection);
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QString detectWindowManagerX11()
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
// Tries to detect the windowmanager via X11.
|
|
|
|
// See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
|
2020-08-31 14:32:56 +08:00
|
|
|
if (!QX11Info::isPlatformX11())
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
xcb_connection_t *connection = QX11Info::connection();
|
|
|
|
xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
|
2020-08-31 14:32:56 +08:00
|
|
|
if(!first_screen)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
ADS_PRINT("No screen found via XCB.");
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
// Get supporting window ()
|
|
|
|
xcb_window_t root = first_screen->root;
|
|
|
|
xcb_window_t support_win = 0;
|
|
|
|
QVector<xcb_window_t> sup_windows;
|
|
|
|
xcb_get_prop_list(root, "_NET_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_WINDOW);
|
2020-08-31 14:32:56 +08:00
|
|
|
if(sup_windows.length() == 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
// This doesn't seem to be in use anymore, but wmctrl does the same so lets play safe.
|
|
|
|
// Both XCB_ATOM_CARDINAL and XCB_ATOM_WINDOW break down to a uint32_t, so reusing sup_windows should be fine.
|
|
|
|
xcb_get_prop_list(root, "_WIN_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_CARDINAL);
|
|
|
|
}
|
2020-08-31 14:32:56 +08:00
|
|
|
if(sup_windows.length() == 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
ADS_PRINT("Failed to get the supporting window on non EWMH comform WM.");
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
support_win = sup_windows[0];
|
|
|
|
QString ret = xcb_get_prop_string(support_win, "_NET_WM_NAME");
|
2020-08-31 14:32:56 +08:00
|
|
|
if(ret.length() == 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
ADS_PRINT("Empty WM name occured.");
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-08-31 14:32:56 +08:00
|
|
|
//============================================================================
|
|
|
|
QString windowManager()
|
|
|
|
{
|
|
|
|
if(_window_manager.length() == 0)
|
|
|
|
{
|
2019-09-12 10:44:12 +08:00
|
|
|
_window_manager = detectWindowManagerX11();
|
|
|
|
}
|
|
|
|
return _window_manager;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-03-01 21:09:56 +08:00
|
|
|
//============================================================================
|
|
|
|
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To)
|
|
|
|
{
|
|
|
|
int index = Splitter->indexOf(From);
|
2019-01-26 00:28:36 +08:00
|
|
|
From->setParent(nullptr);
|
2017-03-01 21:09:56 +08:00
|
|
|
Splitter->insertWidget(index, To);
|
|
|
|
}
|
|
|
|
|
2017-02-27 01:13:56 +08:00
|
|
|
//============================================================================
|
2017-03-01 05:41:34 +08:00
|
|
|
CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area)
|
2017-02-27 01:13:56 +08:00
|
|
|
{
|
|
|
|
switch (Area)
|
|
|
|
{
|
2018-05-06 18:45:46 +08:00
|
|
|
case TopDockWidgetArea: return CDockInsertParam(Qt::Vertical, false);
|
|
|
|
case RightDockWidgetArea: return CDockInsertParam(Qt::Horizontal, true);
|
2017-02-27 01:13:56 +08:00
|
|
|
case CenterDockWidgetArea:
|
2018-05-06 18:45:46 +08:00
|
|
|
case BottomDockWidgetArea: return CDockInsertParam(Qt::Vertical, true);
|
|
|
|
case LeftDockWidgetArea: return CDockInsertParam(Qt::Horizontal, false);
|
|
|
|
default: CDockInsertParam(Qt::Vertical, false);
|
2017-02-27 01:13:56 +08:00
|
|
|
} // switch (Area)
|
|
|
|
|
2017-03-01 05:41:34 +08:00
|
|
|
return CDockInsertParam(Qt::Vertical, false);
|
2017-02-27 01:13:56 +08:00
|
|
|
}
|
|
|
|
|
2018-11-08 17:04:29 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity)
|
|
|
|
{
|
|
|
|
QPixmap TransparentPixmap(Source.size());
|
|
|
|
TransparentPixmap.fill(Qt::transparent);
|
|
|
|
QPainter p(&TransparentPixmap);
|
|
|
|
p.setOpacity(Opacity);
|
|
|
|
p.drawPixmap(0, 0, Source);
|
|
|
|
return TransparentPixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-03 19:52:57 +08:00
|
|
|
//============================================================================
|
|
|
|
void hideEmptyParentSplitters(CDockSplitter* Splitter)
|
|
|
|
{
|
|
|
|
while (Splitter && Splitter->isVisible())
|
|
|
|
{
|
|
|
|
if (!Splitter->hasVisibleContent())
|
|
|
|
{
|
|
|
|
Splitter->hide();
|
|
|
|
}
|
|
|
|
Splitter = internal::findParent<CDockSplitter*>(Splitter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-05 15:57:57 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void setButtonIcon(QAbstractButton* Button, QStyle::StandardPixmap StandarPixmap,
|
|
|
|
ads::eIcon CustomIconId)
|
|
|
|
{
|
|
|
|
// First we try to use custom icons if available
|
|
|
|
QIcon Icon = CDockManager::iconProvider().customIcon(CustomIconId);
|
|
|
|
if (!Icon.isNull())
|
|
|
|
{
|
|
|
|
Button->setIcon(Icon);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
Button->setIcon(Button->style()->standardIcon(StandarPixmap));
|
|
|
|
#else
|
|
|
|
// The standard icons does not look good on high DPI screens so we create
|
|
|
|
// our own "standard" icon here.
|
|
|
|
QPixmap normalPixmap = Button->style()->standardPixmap(StandarPixmap, 0, Button);
|
|
|
|
Icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
|
|
|
|
Icon.addPixmap(normalPixmap, QIcon::Normal);
|
|
|
|
Button->setIcon(Icon);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-05-11 01:30:34 +08:00
|
|
|
|
|
|
|
//============================================================================
|
2020-06-07 23:19:07 +08:00
|
|
|
void repolishStyle(QWidget* w, eRepolishChildOptions Options)
|
2020-05-11 01:30:34 +08:00
|
|
|
{
|
|
|
|
if (!w)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
w->style()->unpolish(w);
|
|
|
|
w->style()->polish(w);
|
2020-06-07 23:19:07 +08:00
|
|
|
|
|
|
|
if (RepolishIgnoreChildren == Options)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QWidget*> Children = w->findChildren<QWidget*>(QString(),
|
|
|
|
(RepolishDirectChildren == Options) ? Qt::FindDirectChildrenOnly: Qt::FindChildrenRecursively);
|
|
|
|
for (auto Widget : Children)
|
|
|
|
{
|
|
|
|
Widget->style()->unpolish(Widget);
|
|
|
|
Widget->style()->polish(Widget);
|
|
|
|
}
|
2020-05-11 01:30:34 +08:00
|
|
|
}
|
|
|
|
|
2017-02-25 05:44:02 +08:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace ads
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// EOF ads_globals.cpp
|