Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2012 Mathias Gottschlag <mgottschlag@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
/**
* Class which keeps track of all tiles in the system. The class automatically
* puts tab groups in one single tile. Tracking of new and removed clients is
* done here as well.
* @class
*/
function TileList() {
/**
* List of currently existing tiles.
*/
this.tiles = [];
/**
* Signal which is triggered whenever a new tile is added to the list.
*/
this.tileAdded = new Signal();
/**
* Signal which is triggered whenever a tile is removed from the list.
*/
this.tileRemoved = new Signal();
// We connect to the global workspace callbacks which are triggered when
// clients are added/removed in order to be able to keep track of the
// new/deleted tiles
var self = this;
workspace.clientAdded.connect(function(client) {
self._onClientAdded(client);
});
workspace.clientRemoved.connect(function(client) {
self._onClientRemoved(client);
});
}
TileList.prototype.connectSignals = function(client) {
var self = this;
if (client.tiling_connected1 != true) {
// First handle fullscreen and shade as they can change and affect the tiling or floating decision
client.fullScreenChanged.connect(function() {
if (client.fullScreen == true) {
client.tiling_floating = true;
client.keepAbove = true;
self._onClientRemoved(client);
} else {
client.keepAbove = false;
self._onClientAdded(client);
}
});
client.shadeChanged.connect(function() {
if (client.shade == true) {
client.tiling_floating = true;
self._onClientRemoved(client);
} else {
self.addClient(client);
}
});
client.tiling_connected1 = true;
}
if (TileList._isIgnored(client)) {
return;
}
if (client.tiling_connected2 == true) {
return;
}
client.tabGroupChanged.connect(function() {
self._onClientTabGroupChanged(client);
});
// We also have to connect other client signals here instead of in Tile
// because the tile of a client might change over time
var getTile = function(client) {
return self.getTile(client);
var tile = getTile(client);
if (tile != null) {
tile.onClientGeometryChanged(client);
}
});
client.clientStartUserMovedResized.connect(function() {
var tile = getTile(client);
if (tile != null) {
tile.onClientStartUserMovedResized(client);
}
});
client.clientStepUserMovedResized.connect(function() {
var tile = getTile(client);
if (tile != null) {
tile.onClientStepUserMovedResized(client);
}
});
client.clientFinishUserMovedResized.connect(function() {
var tile = getTile(client);
if (tile != null) {
tile.onClientFinishUserMovedResized(client);
}
});
client['clientMaximizedStateChanged(KWin::Client*,bool,bool)'].connect(
var tile = getTile(client);
if (tile != null) {
tile.onClientMaximizedStateChanged(client, h, v);
}
client.desktopChanged.connect(function() {
var tile = getTile(client);
if (tile != null) {
tile.onClientDesktopChanged(client);
}
});
client.clientMinimized.connect(function(client) {
try {
self._onClientRemoved(client);
} catch(err) {
print(err, "in mimimized");
}
});
client.clientUnminimized.connect(function(client) {
try {
self._onClientAdded(client);
} catch(err) {
print(err, "in Unminimized");
}
client.tiling_connected2 = true;
};
/**
* Adds another client to the tile list. When this is called, the tile list also
* adds callback functions to the relevant client signals to trigger tile change
* events when necessary. This function might trigger a tileAdded event.
*
* @param client Client which is added to the tile list.
*/
TileList.prototype.addClient = function(client) {
if (TileList._isIgnored(client)) {
client.tiling_floating = true;
return;
}
this.connectSignals(client);
// shade can't be activated without borders, so it's okay to handle it here
if (client.fullScreen == true || client.shade == true) {
client.keepBelow = false;
client.keepAbove = true;
return;
}
client.keepAbove = false;
client.keepBelow = true;
var noBorder = readConfig("noBorder", false);
if (noBorder == true) {
client.noBorder = true;
}
// Check whether the client is part of an existing tile
if (this._indexWithClient(client) == -1) {
this._addTile(client);
};
TileList.prototype.retile = function() {
var existingClients = workspace.clientList();
var self = this;
existingClients.forEach(function(client) {
self.addClient(client);
});
};
/**
* Returns the tile in which a certain client is located.
*
* @param client Client for which the tile shall be returned.
* @return Tile in which the client is located.
*/
TileList.prototype.getTile = function(client) {
var index = this._indexWithClient(client);
if (index > -1) {
return this.tiles[index];
}
return null;
};
TileList.prototype._onClientAdded = function(client) {
this.addClient(client);
};
TileList.prototype._onClientRemoved = function(client) {
// HACK: Set this client to active even if it floats, as it can only be set floating when it is active (with FFM)
var cactive = false;
if (options.focusPolicy < 2) {
if (workspace.activeClient == client) {
cactive = true;
}
}
try {
// Unset keepBelow because we set it when tiling
client.keepBelow = false;
var tileIndex = this._indexWithClient(client);
if (tile != null) {
if (tile.clients.length == 1) {
// Remove the tile if this was the last client in it
this._removeTile(tileIndex);
} else {
// Remove the client from its tile
tile.removeClient(client);
}
// Don't remove tileIndex, so we can move the window to its position in case it comes back (after minimize etc)
//client.tiling_tileIndex = - 1;
if (client.tiling_floating == true) {
client.noBorder = false;
if (cactive == true) {
workspace.activeClient = client;
}
}
} catch(err) {
print(err, "in onClientRemoved with", client.resourceClass.toString());
};
TileList.prototype._onClientTabGroupChanged = function(client) {
print("Tabgroups are currently not supported", client.windowId);
client.tiling_floating = true;
this._onClientRemoved(client);
client.syncTabGroupFor("tiling_floating", true);
client.syncTabGroupFor("tiling_tileIndex", true);
} catch(err) {
print(err, "in TileList._onClientTabGroupChanged");
}
};
TileList.prototype._addTile = function(client) {
var tileIndex = this.tiles.length;
if (client.tiling_tileIndex > -1) {
tileIndex = client.tiling_tileIndex;
}
var newTile = new Tile(client, tileIndex);
this.tiles.push(newTile);
this.tileAdded.emit(newTile);
};
TileList.prototype._removeTile = function(tileIndex) {
// Don't modify tileIndex here - this is a list of _all_ tiles, while tileIndex is the index on the desktop
if (tileIndex > -1) {
this.tiles.splice(tileIndex, 1);
this.tileRemoved.emit(tile);
} catch(err) {
print(err, "in TileList._removeTile");
}
};
/**
* Returns true for clients which shall not be handled by the tiling script at
* all, e.g. the panel.
*/
TileList._isIgnored = function(client) {
// Application workarounds should be put here
// Qt gives us a method-less QVariant(QStringList) if we ask for an array
// Ask for a string instead (which can and should still be a StringList for the UI)
var fl = "yakuake,krunner,Plasma,Plasma-desktop,plasma-desktop,Plugin-container,plugin-container,Wine";
// TODO: This could break if an entry contains whitespace or a comma - it needs to be validated on the qt side
var floaters = String(readConfig("floaters", fl)).replace(/ /g,"").split(",");
if (floaters.indexOf(client.resourceClass.toString()) > -1) {
client.syncTabGroupFor("kwin_tiling_floats", true);
return true;
}
if (client.specialWindow == true) {
return true;
}
return false;
};