1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
22
#include "tabboxhandler.h"
24
#include "clientitemdelegate.h"
25
#include "clientmodel.h"
26
#include "desktopitemdelegate.h"
27
#include "desktopmodel.h"
28
#include "itemlayoutconfig.h"
29
#include "tabboxconfig.h"
30
#include "tabboxview.h"
35
#include <QModelIndex>
41
#include <KStandardDirs>
42
#include <KWindowSystem>
49
class TabBoxHandlerPrivate
52
TabBoxHandlerPrivate(TabBoxHandler *q);
54
~TabBoxHandlerPrivate();
57
* Updates the currently shown outline.
61
* Updates the current highlight window state
63
void updateHighlightWindows();
65
* Ends window highlighting
67
void endHighlightWindows(bool abort = false);
69
ClientModel* clientModel() const;
70
DesktopModel* desktopModel() const;
71
void parseConfig(const QString& fileName);
73
TabBoxHandler *q; // public pointer
83
* Indicates if the tabbox is shown.
84
* Used to determine if the outline has to be updated, etc.
87
QMap< QString, ItemLayoutConfig > tabBoxLayouts;
88
TabBoxClient *lastRaisedClient, *lastRaisedClientSucc;
91
TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q)
96
lastRaisedClientSucc = 0;
97
config = TabBoxConfig();
98
view = new TabBoxView();
99
XSetWindowAttributes attr;
100
attr.override_redirect = 1;
101
outlineLeft = XCreateWindow(QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
102
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr);
103
outlineRight = XCreateWindow(QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
104
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr);
105
outlineTop = XCreateWindow(QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
106
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr);
107
outlineBottom = XCreateWindow(QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
108
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr);
111
parseConfig(KStandardDirs::locate("data", "kwin/DefaultTabBoxLayouts.xml"));
112
view->clientDelegate()->setConfig(tabBoxLayouts.value("Default"));
113
view->additionalClientDelegate()->setConfig(tabBoxLayouts.value("Text"));
114
view->desktopDelegate()->setConfig(tabBoxLayouts.value("Desktop"));
115
view->desktopDelegate()->setLayouts(tabBoxLayouts);
118
TabBoxHandlerPrivate::~TabBoxHandlerPrivate()
123
ClientModel* TabBoxHandlerPrivate::clientModel() const
125
return view->clientModel();
128
DesktopModel* TabBoxHandlerPrivate::desktopModel() const
130
return view->desktopModel();
133
void TabBoxHandlerPrivate::updateOutline()
135
if (config.tabBoxMode() != TabBoxConfig::ClientTabBox)
137
// if ( c == NULL || !m_isShown || !c->isShown( true ) || !c->isOnCurrentDesktop())
138
if (!isShown || view->clientModel()->data(index, ClientModel::EmptyRole).toBool()) {
142
TabBoxClient* c = static_cast< TabBoxClient* >(
143
view->clientModel()->data(index, ClientModel::ClientRole).value<void *>());
144
q->showOutline(QRect(c->x(), c->y(), c->width(), c->height()));
147
void TabBoxHandlerPrivate::updateHighlightWindows()
149
if (!isShown || config.tabBoxMode() != TabBoxConfig::ClientTabBox)
152
Display *dpy = QX11Info::display();
153
TabBoxClient *currentClient = q->client(index);
155
if (!KWindowSystem::compositingActive()) {
156
if (lastRaisedClient) {
157
if (lastRaisedClientSucc)
158
q->restack(lastRaisedClient, lastRaisedClientSucc);
159
// TODO lastRaisedClient->setMinimized( lastRaisedClientWasMinimized );
162
lastRaisedClient = currentClient;
163
if (lastRaisedClient) {
164
// TODO if ( (lastRaisedClientWasMinimized = lastRaisedClient->isMinimized()) )
165
// lastRaisedClient->setMinimized( false );
166
TabBoxClientList order = q->stackingOrder();
167
int succIdx = order.indexOf(lastRaisedClient) + 1; // this is likely related to the index parameter?!
168
lastRaisedClientSucc = (succIdx < order.count()) ? order.at(succIdx) : 0;
169
q->raiseClient(lastRaisedClient);
175
if (config.isShowTabBox()) {
180
wId = QX11Info::appRootWindow();
183
data[ 0 ] = currentClient ? currentClient->window() : 0L;
184
if (config.isShowOutline()) {
185
QVector<Window> outlineWindows = q->outlineWindowIds();
186
data.resize(2+outlineWindows.size());
187
for (int i=0; i<outlineWindows.size(); ++i) {
188
data[2+i] = outlineWindows[i];
191
Atom atom = XInternAtom(dpy, "_KDE_WINDOW_HIGHLIGHT", False);
192
XChangeProperty(dpy, wId, atom, atom, 32, PropModeReplace,
193
reinterpret_cast<unsigned char *>(data.data()), data.size());
196
void TabBoxHandlerPrivate::endHighlightWindows(bool abort)
198
if (abort && lastRaisedClient && lastRaisedClientSucc)
199
q->restack(lastRaisedClient, lastRaisedClientSucc);
200
lastRaisedClient = 0;
201
lastRaisedClientSucc = 0;
203
Display *dpy = QX11Info::display();
204
Atom atom = XInternAtom(dpy, "_KDE_WINDOW_HIGHLIGHT", False);
205
XDeleteProperty(dpy, config.isShowTabBox() ? view->winId() : QX11Info::appRootWindow(), atom);
208
/***********************************************************
209
* Based on the implementation of Kopete's
210
* contaclistlayoutmanager.cpp by Nikolaj Hald Nielsen and
212
***********************************************************/
213
void TabBoxHandlerPrivate::parseConfig(const QString& fileName)
216
if (!QFile::exists(fileName)) {
217
kDebug(1212) << "File " << fileName << " does not exist";
220
QDomDocument doc("Layouts");
221
QFile file(fileName);
222
if (!file.open(QIODevice::ReadOnly)) {
223
kDebug(1212) << "Error reading file " << fileName;
226
if (!doc.setContent(&file)) {
227
kDebug(1212) << "Error parsing file " << fileName;
233
QDomElement layouts_element = doc.firstChildElement("tabbox_layouts");
234
QDomNodeList layouts = layouts_element.elementsByTagName("layout");
236
for (int i = 0; i < layouts.size(); i++) {
237
QDomNode layout = layouts.item(i);
238
ItemLayoutConfig currentLayout;
240
// parse top elements
241
QDomElement element = layout.toElement();
242
QString layoutName = element.attribute("name", "");
244
const bool highlightIcon = (element.attribute("highlight_selected_icon", "true").compare("true", Qt::CaseInsensitive) == 0);
245
currentLayout.setHighlightSelectedIcons(highlightIcon);
247
const bool grayscaleIcon = (element.attribute("grayscale_deselected_icon", "false").compare("true", Qt::CaseInsensitive) == 0);
248
currentLayout.setGrayscaleDeselectedIcons(grayscaleIcon);
251
QDomNodeList rows = element.elementsByTagName("row");
252
for (int j = 0; j < rows.size(); j++) {
253
QDomNode rowNode = rows.item(j);
255
ItemLayoutConfigRow row;
257
QDomNodeList elements = rowNode.toElement().elementsByTagName("element");
258
for (int k = 0; k < elements.size(); k++) {
259
QDomNode elementNode = elements.item(k);
260
QDomElement currentElement = elementNode.toElement();
262
ItemLayoutConfigRowElement::ElementType type = ItemLayoutConfigRowElement::ElementType(currentElement.attribute(
263
"type", int(ItemLayoutConfigRowElement::ElementClientName)).toInt());
264
ItemLayoutConfigRowElement currentRowElement;
265
currentRowElement.setType(type);
267
// width - used by all types
268
qreal width = currentElement.attribute("width", "0.0").toDouble();
269
currentRowElement.setWidth(width);
271
case ItemLayoutConfigRowElement::ElementEmpty:
272
row.addElement(currentRowElement);
274
case ItemLayoutConfigRowElement::ElementIcon: {
275
qreal iconWidth = currentElement.attribute("icon_size", "16.0").toDouble();
276
currentRowElement.setIconSize(QSizeF(iconWidth, iconWidth));
277
currentRowElement.setRowSpan(currentElement.attribute("row_span", "true").compare(
278
"true", Qt::CaseInsensitive) == 0);
279
row.addElement(currentRowElement);
282
case ItemLayoutConfigRowElement::ElementClientList: {
283
currentRowElement.setStretch(currentElement.attribute("stretch", "false").compare(
284
"true", Qt::CaseInsensitive) == 0);
285
currentRowElement.setClientListLayoutName(currentElement.attribute("layout_name", ""));
286
QString layoutMode = currentElement.attribute("layout_mode", "horizontal");
287
if (layoutMode.compare("horizontal", Qt::CaseInsensitive) == 0)
288
currentRowElement.setClientListLayoutMode(TabBoxConfig::HorizontalLayout);
289
else if (layoutMode.compare("vertical", Qt::CaseInsensitive) == 0)
290
currentRowElement.setClientListLayoutMode(TabBoxConfig::VerticalLayout);
291
else if (layoutMode.compare("tabular", Qt::CaseInsensitive) == 0)
292
currentRowElement.setClientListLayoutMode(TabBoxConfig::HorizontalVerticalLayout);
293
row.addElement(currentRowElement);
296
default: { // text elements
297
currentRowElement.setStretch(currentElement.attribute("stretch", "false").compare(
298
"true", Qt::CaseInsensitive) == 0);
299
currentRowElement.setSmallTextSize(currentElement.attribute("small", "false").compare(
300
"true", Qt::CaseInsensitive) == 0);
301
currentRowElement.setBold(currentElement.attribute("bold", "false").compare(
302
"true", Qt::CaseInsensitive) == 0);
303
currentRowElement.setItalic(currentElement.attribute("italic", "false").compare(
304
"true", Qt::CaseInsensitive) == 0);
305
currentRowElement.setItalicMinimized(currentElement.attribute("italic_minimized", "true").compare(
306
"true", Qt::CaseInsensitive) == 0);
308
currentRowElement.setPrefix(currentElement.attribute("prefix", ""));
309
currentRowElement.setSuffix(currentElement.attribute("suffix", ""));
310
currentRowElement.setPrefixMinimized(currentElement.attribute("prefix_minimized", ""));
311
currentRowElement.setSuffixMinimzed(currentElement.attribute("suffix_minimized", ""));
313
QString halign = currentElement.attribute("horizontal_alignment", "left");
314
Qt::Alignment alignment;
315
if (halign.compare("left", Qt::CaseInsensitive) == 0)
316
alignment = Qt::AlignLeft;
317
else if (halign.compare("right", Qt::CaseInsensitive) == 0)
318
alignment = Qt::AlignRight;
320
alignment = Qt::AlignCenter;
321
QString valign = currentElement.attribute("vertical_alignment", "center");
322
if (valign.compare("top", Qt::CaseInsensitive) == 0)
323
alignment = alignment | Qt::AlignTop;
324
else if (valign.compare("bottom", Qt::CaseInsensitive) == 0)
325
alignment = alignment | Qt::AlignBottom;
327
alignment = alignment | Qt::AlignVCenter;
328
currentRowElement.setAlignment(alignment);
330
row.addElement(currentRowElement);
334
} // for loop elements
336
currentLayout.addRow(row);
338
if (!layoutName.isEmpty()) {
339
tabBoxLayouts.insert(layoutName, currentLayout);
341
} // for loop layouts
344
/***********************************************
346
***********************************************/
348
TabBoxHandler::TabBoxHandler()
351
KWin::TabBox::tabBox = this;
352
d = new TabBoxHandlerPrivate(this);
355
TabBoxHandler::~TabBoxHandler()
360
const KWin::TabBox::TabBoxConfig& TabBoxHandler::config() const
365
void TabBoxHandler::setConfig(const TabBoxConfig& config)
367
if (config.layoutName() != d->config.layoutName()) {
368
// new item layout config
369
if (d->tabBoxLayouts.contains(config.layoutName())) {
370
d->view->clientDelegate()->setConfig(d->tabBoxLayouts.value(config.layoutName()));
371
d->view->desktopDelegate()->setConfig(d->tabBoxLayouts.value(config.layoutName()));
374
if (config.selectedItemLayoutName() != d->config.selectedItemLayoutName()) {
375
// TODO: desktop layouts
376
if (d->tabBoxLayouts.contains(config.selectedItemLayoutName()))
377
d->view->additionalClientDelegate()->setConfig(d->tabBoxLayouts.value(config.selectedItemLayoutName()));
380
emit configChanged();
383
void TabBoxHandler::show()
386
d->lastRaisedClient = 0;
387
d->lastRaisedClientSucc = 0;
389
if (d->config.isShowOutline()) {
392
if (d->config.isShowTabBox()) {
394
d->view->updateGeometry();
396
if (d->config.isHighlightWindows()) {
397
d->updateHighlightWindows();
401
void TabBoxHandler::hide(bool abort)
404
if (d->config.isHighlightWindows()) {
405
d->endHighlightWindows(abort);
407
if (d->config.isShowOutline()) {
413
QModelIndex TabBoxHandler::nextPrev(bool forward) const
416
QAbstractItemModel* model;
417
switch(d->config.tabBoxMode()) {
418
case TabBoxConfig::ClientTabBox:
419
model = d->clientModel();
421
case TabBoxConfig::DesktopTabBox:
422
model = d->desktopModel();
428
int column = d->index.column() + 1;
429
int row = d->index.row();
430
if (column == model->columnCount()) {
433
if (row == model->rowCount())
436
ret = model->index(row, column);
438
ret = model->index(0, 0);
440
int column = d->index.column() - 1;
441
int row = d->index.row();
443
column = model->columnCount() - 1;
446
row = model->rowCount() - 1;
448
ret = model->index(row, column);
449
if (!ret.isValid()) {
450
row = model->rowCount() - 1;
451
for (int i = model->columnCount() - 1; i >= 0; i--) {
452
ret = model->index(row, i);
464
QModelIndex TabBoxHandler::desktopIndex(int desktop) const
466
if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)
467
return QModelIndex();
468
return d->desktopModel()->desktopIndex(desktop);
471
QList< int > TabBoxHandler::desktopList() const
473
if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)
474
return QList< int >();
475
return d->desktopModel()->desktopList();
478
int TabBoxHandler::desktop(const QModelIndex& index) const
480
if (!index.isValid() || (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox))
482
QVariant ret = d->desktopModel()->data(index, DesktopModel::DesktopRole);
489
int TabBoxHandler::currentSelectedDesktop() const
491
return desktop(d->index);
494
void TabBoxHandler::setCurrentIndex(const QModelIndex& index)
496
d->view->setCurrentIndex(index);
498
if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) {
499
if (d->config.isShowOutline()) {
502
if (d->config.isHighlightWindows()) {
503
d->updateHighlightWindows();
508
QModelIndex TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const
511
QAbstractItemModel* model;
512
switch(d->config.tabBoxMode()) {
513
case TabBoxConfig::ClientTabBox:
514
model = d->clientModel();
516
case TabBoxConfig::DesktopTabBox:
517
model = d->desktopModel();
522
int column = d->index.column();
523
int row = d->index.row();
524
switch(event->key()) {
528
column = model->columnCount() - 1;
532
if (column >= model->columnCount())
538
row = model->rowCount() - 1;
542
if (row >= model->rowCount())
546
// do not do anything for any other key
549
ret = model->index(row, column);
557
bool TabBoxHandler::containsPos(const QPoint& pos) const
559
return d->view->geometry().contains(pos);
562
QModelIndex TabBoxHandler::indexAt(const QPoint& pos) const
564
QPoint widgetPos = d->view->mapFromGlobal(pos);
565
QModelIndex ret = d->view->indexAt(widgetPos);
569
QModelIndex TabBoxHandler::index(KWin::TabBox::TabBoxClient* client) const
571
return d->clientModel()->index(client);
574
TabBoxClientList TabBoxHandler::clientList() const
576
if (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox)
577
return TabBoxClientList();
578
return d->clientModel()->clientList();
581
TabBoxClient* TabBoxHandler::client(const QModelIndex& index) const
583
if ((!index.isValid()) ||
584
(d->config.tabBoxMode() != TabBoxConfig::ClientTabBox) ||
585
(d->clientModel()->data(index, ClientModel::EmptyRole).toBool()))
587
TabBoxClient* c = static_cast< TabBoxClient* >(
588
d->clientModel()->data(index, ClientModel::ClientRole).value<void *>());
592
void TabBoxHandler::createModel(bool partialReset)
594
switch(d->config.tabBoxMode()) {
595
case TabBoxConfig::ClientTabBox:
596
d->clientModel()->createClientList(partialReset);
597
if (d->lastRaisedClient && !stackingOrder().contains(d->lastRaisedClient))
598
d->lastRaisedClient = 0;
599
if (d->lastRaisedClientSucc && !stackingOrder().contains(d->lastRaisedClientSucc))
600
d->lastRaisedClientSucc = 0;
602
case TabBoxConfig::DesktopTabBox:
603
d->desktopModel()->createDesktopList();
606
d->view->updateGeometry();
609
QModelIndex TabBoxHandler::first() const
611
QAbstractItemModel* model;
612
switch(d->config.tabBoxMode()) {
613
case TabBoxConfig::ClientTabBox:
614
model = d->clientModel();
616
case TabBoxConfig::DesktopTabBox:
617
model = d->desktopModel();
620
return QModelIndex();
622
return model->index(0, 0);
625
QWidget* TabBoxHandler::tabBoxView() const
630
TabBoxHandler* tabBox = 0;
632
TabBoxClient::TabBoxClient()
636
TabBoxClient::~TabBoxClient()
640
} // namespace TabBox