summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lübking <thomas.luebking@gmail.com>2012-10-16 20:42:19 (GMT)
committerThomas Lübking <thomas.luebking@gmail.com>2012-11-08 23:37:34 (GMT)
commit26f069a8254f11816d6f7039d35ebcdb14527569 (patch)
treea10118d838a3e589c3cd43c308d5f565d95fded4
parent08a5b5c5242e20fd3e240625a97335e208dbb390 (diff)
scripted title manipulation / stripping
REVIEW: 106896 BUG: 308995
-rw-r--r--kwin/bridge.cpp10
-rw-r--r--kwin/client.cpp106
-rw-r--r--kwin/client.h4
-rw-r--r--kwin/data/CMakeLists.txt1
-rw-r--r--kwin/data/stripTitle.js104
-rw-r--r--kwin/options.cpp16
-rw-r--r--kwin/options.h15
7 files changed, 217 insertions, 39 deletions
diff --git a/kwin/bridge.cpp b/kwin/bridge.cpp
index 6dbf1bd..78a1fe1 100644
--- a/kwin/bridge.cpp
+++ b/kwin/bridge.cpp
@@ -53,7 +53,6 @@ BRIDGE_HELPER(bool, keepAbove, , , const)
BRIDGE_HELPER(bool, keepBelow, , , const)
BRIDGE_HELPER(bool, isMovable, , , const)
BRIDGE_HELPER(bool, isResizable, , , const)
-BRIDGE_HELPER(QString, caption, , , const)
BRIDGE_HELPER(void, processMousePressEvent, QMouseEvent* e, e,)
BRIDGE_HELPER(QRect, geometry, , , const)
BRIDGE_HELPER(void, closeWindow, , ,)
@@ -261,11 +260,16 @@ QIcon Bridge::icon(int idx) const
return icon();
}
+QString Bridge::caption() const
+{
+ return c->caption(true, true);
+}
+
QString Bridge::caption(int idx) const
{
if (c->tabGroup())
- return c->tabGroup()->clients().at(idx)->caption();
- return c->caption();
+ return c->tabGroup()->clients().at(idx)->caption(true, true);
+ return c->caption(true, true);
}
long Bridge::currentTabId() const
diff --git a/kwin/client.cpp b/kwin/client.cpp
index bdc80a7..33322dd 100644
--- a/kwin/client.cpp
+++ b/kwin/client.cpp
@@ -26,6 +26,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDateTime>
#include <QProcess>
#include <QPaintEngine>
+#ifdef KWIN_BUILD_SCRIPTING
+#include <QScriptEngine>
+#include <QScriptProgram>
+#endif
#include <unistd.h>
#include <kstandarddirs.h>
#include <QWhatsThis>
@@ -1699,41 +1703,72 @@ QChar PDF(0x202C);
void Client::setCaption(const QString& _s, bool force)
{
- QString s = _s;
- if (s != cap_normal || force) {
- bool reset_name = force;
- for (int i = 0; i < s.length(); ++i)
- if (!s[i].isPrint())
- s[i] = QChar(' ');
- cap_normal = s;
- bool was_suffix = (!cap_suffix.isEmpty());
- QString machine_suffix;
+ if (!force && _s == cap_normal)
+ return;
+ QString s(_s);
+ for (int i = 0; i < s.length(); ++i)
+ if (!s[i].isPrint())
+ s[i] = QChar(' ');
+ cap_normal = s;
+#ifdef KWIN_BUILD_SCRIPTING
+ if (options->condensedTitle()) {
+ static QScriptEngine engine;
+ static QScriptProgram stripTitle;
+ static QScriptValue script;
+ if (stripTitle.isNull()) {
+ const QString scriptFile = KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/stripTitle.js");
+ if (!scriptFile.isEmpty()) {
+ QFile f(scriptFile);
+ if (f.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ f.reset();
+ stripTitle = QScriptProgram(QString::fromLocal8Bit(f.readAll()), "stripTitle.js");
+ f.close();
+ }
+ }
+ if (stripTitle.isNull())
+ stripTitle = QScriptProgram("(function(title, wm_name, wm_class){ return title ; })", "stripTitle.js");
+ script = engine.evaluate(stripTitle);
+ }
+ QScriptValueList args;
+ args << _s << QString(resourceName()) << QString(resourceClass());
+ s = script.call(QScriptValue(), args).toString();
+ }
+#endif
+ if (!force && s == cap_deco)
+ return;
+ cap_deco = s;
+
+ bool reset_name = force;
+ bool was_suffix = (!cap_suffix.isEmpty());
+ cap_suffix.clear();
+ QString machine_suffix;
+ if (!options->condensedTitle()) { // machine doesn't qualify for "clean"
if (wmClientMachine(false) != "localhost" && !isLocalMachine(wmClientMachine(false)))
machine_suffix = QString(" <@") + wmClientMachine(true) + '>' + LRM;
- QString shortcut_suffix = !shortcut().isEmpty() ? (" {" + shortcut().toString() + '}') : QString();
- cap_suffix = machine_suffix + shortcut_suffix;
- if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(FetchNameInternalPredicate(this))) {
- int i = 2;
- do {
- cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix;
- i++;
- } while (workspace()->findClient(FetchNameInternalPredicate(this)));
- info->setVisibleName(caption().toUtf8());
- reset_name = false;
- }
- if ((was_suffix && cap_suffix.isEmpty()) || reset_name) {
- // If it was new window, it may have old value still set, if the window is reused
- info->setVisibleName("");
- info->setVisibleIconName("");
- } else if (!cap_suffix.isEmpty() && !cap_iconic.isEmpty())
- // Keep the same suffix in iconic name if it's set
- info->setVisibleIconName(QString(cap_iconic + cap_suffix).toUtf8());
-
- if (isManaged() && decoration) {
- decoration->captionChange();
- }
- emit captionChanged();
}
+ QString shortcut_suffix = !shortcut().isEmpty() ? (" {" + shortcut().toString() + '}') : QString();
+ cap_suffix = machine_suffix + shortcut_suffix;
+ if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(FetchNameInternalPredicate(this))) {
+ int i = 2;
+ do {
+ cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM;
+ i++;
+ } while (workspace()->findClient(FetchNameInternalPredicate(this)));
+ info->setVisibleName(caption().toUtf8());
+ reset_name = false;
+ }
+ if ((was_suffix && cap_suffix.isEmpty()) || reset_name) {
+ // If it was new window, it may have old value still set, if the window is reused
+ info->setVisibleName("");
+ info->setVisibleIconName("");
+ } else if (!cap_suffix.isEmpty() && !cap_iconic.isEmpty())
+ // Keep the same suffix in iconic name if it's set
+ info->setVisibleIconName(QString(cap_iconic + cap_suffix).toUtf8());
+
+ if (isManaged() && decoration) {
+ decoration->captionChange();
+ }
+ emit captionChanged();
}
void Client::updateCaption()
@@ -1763,9 +1798,12 @@ void Client::fetchIconicName()
/**
* \reimp
*/
-QString Client::caption(bool full) const
+QString Client::caption(bool full, bool stripped) const
{
- return full ? cap_normal + cap_suffix : cap_normal;
+ QString cap = stripped ? cap_deco : cap_normal;
+ if (full)
+ cap += cap_suffix;
+ return cap;
}
bool Client::tabTo(Client *other, bool behind, bool activate)
diff --git a/kwin/client.h b/kwin/client.h
index 0752391..b54f174 100644
--- a/kwin/client.h
+++ b/kwin/client.h
@@ -492,7 +492,7 @@ public:
inline bool isBlockingCompositing() { return blocks_compositing; }
void updateCompositeBlocking(bool readProperty = false);
- QString caption(bool full = true) const;
+ QString caption(bool full = true, bool stripped = false) const;
void updateCaption();
void keyPressEvent(uint key_code); // FRAME ??
@@ -892,7 +892,7 @@ private:
QTimer* shadeHoverTimer;
QTimer* delayedMoveResizeTimer;
Colormap cmap;
- QString cap_normal, cap_iconic, cap_suffix;
+ QString cap_normal, cap_iconic, cap_suffix, cap_deco;
Group* in_group;
Window window_group;
TabGroup* tab_group;
diff --git a/kwin/data/CMakeLists.txt b/kwin/data/CMakeLists.txt
index 597aedc..13c9ee6 100644
--- a/kwin/data/CMakeLists.txt
+++ b/kwin/data/CMakeLists.txt
@@ -52,4 +52,5 @@ install( FILES fsp_workarounds_1.kwinrules DESTINATION ${DATA_INSTALL_DIR}/kwi
install( FILES pop.wav DESTINATION ${SOUND_INSTALL_DIR} )
install( FILES kwin_fsp_workarounds_1.upd kwin_update_tabbox_settings.upd kwin_remove_effects.upd kwin_update_tabbox_qml_settings.upd kwin_remove_delay_focus.upd kwin_update_49.upd kwin_update_410.upd DESTINATION ${KCONF_UPDATE_INSTALL_DIR} )
install( PROGRAMS kwin_remove_delay_focus.sh DESTINATION ${KCONF_UPDATE_INSTALL_DIR} )
+install( FILES stripTitle.js DESTINATION ${DATA_INSTALL_DIR}/kwin )
diff --git a/kwin/data/stripTitle.js b/kwin/data/stripTitle.js
new file mode 100644
index 0000000..24592ff
--- /dev/null
+++ b/kwin/data/stripTitle.js
@@ -0,0 +1,104 @@
+// ==============================================================
+// Ok, *some* apps have really long and nasty window captions
+// this looks clutterd, so we allow to crop them a bit and remove
+// any information considered to be useless
+// ==============================================================
+
+function isBrowser(appName) {
+ if (appName.toLowerCase() == "konqueror")
+ return true;
+ if (appName.toLowerCase() == "rekonq")
+ return true;
+ if (appName.toLowerCase() == "qupzilla")
+ return true;
+ if (appName.toLowerCase() == "chromium")
+ return true;
+ if (appName.toLowerCase() == "firefox")
+ return true;
+ if (appName.toLowerCase() == "opera")
+ return true;
+ if (appName.toLowerCase() == "arora")
+ return true;
+ if (appName.toLowerCase() == "mozilla")
+ return true;
+ return false;
+}
+
+(function(title, wm_name, wm_class) {
+ var ret;
+
+ // == 1st off ======================================================================
+ // we assume the part beyond the last dash (if any) to be the
+ // uninteresting one (usually it's the apps name, if there's add info, that's
+ // more important to the user)
+ // --------------------------------------------------------------------------------
+ var lastPos = title.lastIndexOf(" – "); // U+2013 "EN DASH"
+ if (lastPos > -1)
+ ret = title.slice(0, lastPos);
+ else {
+ lastPos = title.lastIndexOf(" - "); // ASCII Dash
+ if (lastPos > -1)
+ ret = title.slice(0, lastPos);
+ else {
+ lastPos = title.lastIndexOf(" — "); // U+2014 "EM DASH"
+ if (lastPos > -1)
+ ret = title.slice(0, lastPos);
+ else
+ ret = title;
+ }
+ }
+
+ // == 2nd =========================================================================
+ // Browsers set the caption to "<html><title/></html> - appname"
+ // Now the page titles can be ridiculously looooong, especially on news pages
+ // -------------------------------------------------------------------------------
+ if (isBrowser(wm_name)) {
+ var parts = ret.split(" - ");
+ ret = "";
+ if (parts.length > 2) { // select last two if 3 or more sects, prelast otherwise
+ for (i = Math.max(0,parts.length-2); i < parts.length - 1; ++i)
+ ret = ret + parts[i] + " - ";
+ ret = ret + parts[parts.length - 1];
+ } else {
+ ret = ret + parts[Math.max(0,parts.length-2)];
+ }
+ }
+
+ // 3rd ============================================================================
+ // if there're any details left, cut of stuff by ": ",
+ // we remove them as well
+ // --------------------------------------------------------------------------------
+ var lastPos = ret.lastIndexOf(": ");
+ if (lastPos > -1)
+ ret = title.slice(0, lastPos);
+
+ // 4th ============================================================================
+ // if this is a http url, please get rid of protocol, assuming the user knows or doesn't care
+ // --------------------------------------------------------------------------------
+ ret = ret.replace("http://", '');
+
+ // finally =========================================================================
+ // if the remaining string still contains the app name (which should have been removed),
+ // please shape away additional info like compile time, version numbers etc.
+ // we usitlize the caption string to preserve CapiTaliZation
+ // -----------------------------------------------------------------------------------
+ lastPos = ret.indexOf(RegExp("\\b" + wm_name + "\\b"));
+ if (lastPos > -1)
+ ret = ret.substr(lastPos, wm_name.length);
+ else {
+ lastPos = ret.indexOf(RegExp("\\b" + wm_class + "\\b"));
+ if (lastPos > -1)
+ ret = ret.substr(lastPos, wm_class.length);
+ }
+
+ if (ret.length == 0)
+ ret = title; // something _terribly_ went wrong -> fall back to the original string
+
+ // but in any case get replace the stupid [modified] hint by just an asterisk
+ ret = ret.replace("[modified]", "❖");
+
+ // in general, remove leading [and trailing] blanks and special chars
+ ret = ret.replace(/^\W*/, '');
+ ret = ret.replace(/^\s*/, '').replace(/\s*$/, '');
+ return ret;
+}) \ No newline at end of file
diff --git a/kwin/options.cpp b/kwin/options.cpp
index c732596..a49ff92 100644
--- a/kwin/options.cpp
+++ b/kwin/options.cpp
@@ -191,6 +191,7 @@ Options::Options(QObject *parent)
, electric_border_corner_ratio(Options::defaultElectricBorderCornerRatio())
, borderless_maximized_windows(Options::defaultBorderlessMaximizedWindows())
, show_geometry_tip(Options::defaultShowGeometryTip())
+ , condensed_title(Options::defaultCondensedTitle())
, animationSpeed(Options::defaultAnimationSpeed())
{
}
@@ -539,6 +540,15 @@ void Options::setShowGeometryTip(bool showGeometryTip)
emit showGeometryTipChanged();
}
+void Options::setCondensedTitle(bool condensedTitle)
+{
+ if (condensed_title == condensedTitle) {
+ return;
+ }
+ condensed_title = condensedTitle;
+ emit condensedTitleChanged();
+}
+
void Options::setElectricBorderDelay(int electricBorderDelay)
{
if (electric_border_delay == electricBorderDelay) {
@@ -834,6 +844,7 @@ unsigned long Options::loadConfig()
KConfigGroup config(_config, "Windows");
setShowGeometryTip(config.readEntry("GeometryTip", Options::defaultShowGeometryTip()));
+ setCondensedTitle(config.readEntry("CondensedTitle", Options::defaultCondensedTitle()));
QString val;
@@ -1129,6 +1140,11 @@ bool Options::showGeometryTip() const
return show_geometry_tip;
}
+bool Options::condensedTitle() const
+{
+ return condensed_title;
+}
+
ElectricBorderAction Options::electricBorderAction(ElectricBorder edge) const
{
switch(edge) {
diff --git a/kwin/options.h b/kwin/options.h
index f9dbd43..f9f623f 100644
--- a/kwin/options.h
+++ b/kwin/options.h
@@ -130,6 +130,10 @@ class Options : public QObject, public KDecorationOptions
*/
Q_PROPERTY(bool showGeometryTip READ showGeometryTip WRITE setShowGeometryTip NOTIFY showGeometryTipChanged)
/**
+ * whether the visible name should be condensed
+ */
+ Q_PROPERTY(bool condensedTitle READ condensedTitle WRITE setCondensedTitle NOTIFY condensedTitleChanged)
+ /**
* Whether electric borders are enabled. With electric borders
* you can change desktop by moving the mouse pointer towards the edge
* of the screen
@@ -445,6 +449,11 @@ public:
*/
bool showGeometryTip() const;
+ /**
+ * returns whether the user prefers his caption clean
+ */
+ bool condensedTitle() const;
+
enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 };
/**
* @returns The action assigned to the specified electric border
@@ -620,6 +629,7 @@ public:
void setCommandAll3(MouseCommand commandAll3);
void setKeyCmdAllModKey(uint keyCmdAllModKey);
void setShowGeometryTip(bool showGeometryTip);
+ void setCondensedTitle(bool condensedTitle);
void setElectricBorderDelay(int electricBorderDelay);
void setElectricBorderCooldown(int electricBorderCooldown);
void setElectricBorderPushbackPixels(int electricBorderPushbackPixels);
@@ -762,6 +772,9 @@ public:
static bool defaultShowGeometryTip() {
return false;
}
+ static bool defaultCondensedTitle() {
+ return false;
+ }
static ElectricBorderAction defaultElectricBorderTop() {
return ElectricActionNone;
}
@@ -930,6 +943,7 @@ Q_SIGNALS:
void commandAll3Changed();
void keyCmdAllModKeyChanged();
void showGeometryTipChanged();
+ void condensedTitleChanged();
void electricBordersChanged();
void electricBorderDelayChanged();
void electricBorderCooldownChanged();
@@ -1042,6 +1056,7 @@ private:
float electric_border_corner_ratio;
bool borderless_maximized_windows;
bool show_geometry_tip;
+ bool condensed_title;
int animationSpeed; // 0 - instant, 5 - very slow
MouseCommand wheelToMouseCommand(MouseWheelCommand com, int delta) const;