diff options
Diffstat (limited to 'kwin')
271 files changed, 70365 insertions, 0 deletions
diff --git a/kwin/COMPLIANCE b/kwin/COMPLIANCE new file mode 100644 index 000000000..e63455e4f --- /dev/null +++ b/kwin/COMPLIANCE @@ -0,0 +1,247 @@ +W A R N I N G: +-------------- +This document is a work in progress and is in no way complete or accurate! +Its current purpose is in aiding the KWin NetWM audit for a future KWin release. + +NetWM Compliance Document: +========================== + +Listed below are all the NetWM (or EWM) hints decided upon on freedesktop.org +(as of version 1.3draft, Nov 27, 2002) and KWin's current level of +compliance with the spec. Some parts also involve the pager and clients which +this document will cater for as well where applicable. + +If you modify the level of NetWM compliance (via modification of kwin/*, +kdecore/netwm.* or kdecore/kwin.* etc.), or notice any new hints that +were added after version 1.2, please modify this document appropriately. +Properties are ordered in the table in the order they are found in the +specification. To list any important notes regarding a property, just +add them as follows: + +_NET_NUMBER_OF_DESKTOPS root window property done + +----------------------------------------------------------------+ + | This property SHOULD be updated by the Window Manager to | + | indicate the number of virtual desktops. KWin DOES update this | + | property when the pager changes the number of desktops. | + +----------------------------------------------------------------+ + +If you have any questions regarding the specification, feel free to ask on the KWin +mailing list <kwin@kde.org>, or on the Window Manager Spec list <wm-spec-list@gnome.org>. + -- Karol <kszwed@kde.org> + +( + compliance : + - = none, + / = partial, + + = complete, + * = KWin is compliant, but something else needs checking (e.g. Qt) + ? = unknown +) + + +NETWM spec compliance (whole document): +version 1.2 +====================== + ++ 1. ++ 2.3. Feature not implemented. ++ 2.4. Feature not implemented. ++ 2.5. ++ 2. (rest of the section) ++ 3.1. + This property is complete in the sence that all implemented properties + are listed here. + CHECKME : check it's complete +/ 3.2. + The spec requires that _NET_CLIENT_LIST contains the windows in their + initial mapping order, which is currently not true for NET::Desktop + windows. + Note that xprop lists only first element in WINDOW type properties. ++ 3.3. + Note that KWin does not use the virtual root windows technique, + so it doesn't set _NET_VIRTUAL_ROOTS at all. ++ 3.4. + KWin doesn't implement large desktops, so it ignores + the message, and only sets the property to the screen size. ++ 3.5. + KWin doesn't implement viewports, so it correctly sets + the property to (0,0) pairs and ignores the message. ++ 3.6. ++ 3.7. ++ 3.8. + KWin currently extends the message a bit, with data.l[0] being 1 or 2, + meaning 'from application'/'from pager', and data.l[1] contains + timestamp. This is used for focus stealing prevention purposes, and + will be proposed for next version of the spec. ++ 3.9. ++ 3.10. ++ 3.11. + KWin doesn't use the virtual roots technique for creating virtual + desktops, so it doesn't set the property. +- 3.12. +- 3.13. ++ 4.1. ++ 4.2. ++ 4.3. + Due to implementation details KWin actually allows moving or resizing + by keyboard when requested to be done by mouse, and vice versa. ++ 5.1. ++ 5.2. ++ 5.3. ++ 5.4. ++ 5.5. +/ 5.6. The handling of _NET_WM_WINDOW_TYPE itself is correct and complete. + Supported window types: DESKTOP, DOCK, TOOLBAR, MENU, UTILITY, + SPLASH, DIALOG, NORMAL. + UTILITY should get better placement. + TOOLBAR - many parts in KDE still treat this as "tool" window. + - should the decoration be shown for the toolbars? + KDE extensions: + _KDE_NET_WM_WINDOW_TYPE_TOPMENU - this is used for implementing + standalone menubars for applications. Only the menubar + that is transient for the currently active window will be shown. + See KMenuBar class in libkdeui for details. + _KDE_NET_WM_WINDOW_TYPE_OVERRIDE - this seems to mean "this window + should be borderless", but it's actually used also for other + things, like fullscreen windows. The plan is to get rid of this + flawed thing as soon as possible. +/ 5.7. + The handling of _NET_WM_STATE itself is correct and complete. + Supported states: MODAL, MAXIMIZED_VERT, MAXIMIZED_HORZ, SHADED, + SKIP_TASKBAR, SKIP_PAGER, HIDDEN, ABOVE, BELOW. + STICKY is not supported, because KWin doesn't implement viewports. + BELOW - in order to make 'allow windows to cover the panel' feature + in Kicker work KWin interprets this state a bit differently + for DOCK windows. While normal DOCK windows are in their + extra layer above normal windows, making them BELOW doesn't + move them below normal windows, but only to the same layer, so + that windows can hide Kicker, but Kicker can be also raised + above the windows. A bit hacky, but it's not really against + the spec, and I have no better idea. + KDE extensions: + _NET_WM_STATE_STAYS_ON_TOP - has the same meaning like ABOVE, + and is deprecated in favour of it; it lacks the _KDE prefix +* 5.8. + The handling of _NET_WM_ALLOWED_ACTIONS itself is correct and complete. + Supported actions: MOVE, RESIZE, MINIMIZE, SHADE, MAXIMIZE_HORZ, + MAXIMIZE_VERT, CHANGE_DESKTOP, CLOSE + STICK is not supported, because KWin does not implement viewports. + Kicker etc. need to be updated. ++ 5.9. +* 5.10. + Property is not used in KWin. + Kicker needs to be checked. +* 5.11. + KWin's handling of the property is correct. + Qt should be checked. ++ 5.12. +- 5.13. + Property is not used in KWin, KWin never provides icons for iconified windows. + Kicker or its taskbar don't set it either. However, the property is flawed, + and should be replaced by manager selection or similar mechanism. ++ 6.1. ++ 6. (rest) ++ 7.4. + The urgency hint is mapped to the _NET_WM_DEMANDS_ATTENTION flag. +* 7.5. + Qt often sets maximum size smaller than minimum size. This seems to be caused + by delayed layout calculations. +* 7.6. + Kicker should be checked. +? 7.7. ++ 7. (rest of the section) + +ICCCM spec compliance (whole document): +version 2.0 +====================== + +/ 1.2.3. + KWin uses KWIN_RUNNING atom that's missing the leading underscore. + Some parts of KDE perhaps may be missing the leading underscore. +/ 1.2.6. + Should be checked. ++ 1. (rest of the section) ++ 2.8. kmanagerselection.* in kdecore ++ 2. (rest of the section) + Not a KWin thing. +* - patch sent to TT to make QClipboard sufficiently compliant ++ 3. + Feature not supported, obsolete. ++ 4.1.1 ++ 4.1.2 (intro) ++ 4.1.2.1 + Used as a fallback for _NET_WM_NAME. ++ 4.1.2.2 + Used as a fallback for _NET_WM_ICON_NAME. +? 4.1.2.3 +? - PSize, PPosition, USize, UPosition +? - clients - Qt simply sets both ++ - PWinGravity - window geometry constraints have higher priority than gravity +/ - PBaseSize - PMinSize is not used as a fallback for size increments ++ - (the rest) +/ 4.1.2.4 ++ - input - see 4.1.7 ++ - initial_state ++ - icon - feature not supported ++ - window_group ++ - urgency - mapped to _NET_WM_DEMANDS_ATTENTION +/ 4.1.2.5 - it should be checked it's used correctly in Kicker etc. +/ 4.1.2.6 - should be checked + NETWM section 7.3. is supported too, even though it's a slight ICCCM violation. ++ 4.1.2.7 +- 4.1.2.8 + See 4.1.8. ++ 4.1.2.9 - handled by Xlib call ++ 4.1.3.1 ++ 4.1.3.2 + Feature not supported (4.1.2.4 - icons) +* 4.1.4 it should be checked Qt/KDE clients handle this properly +/ 4.1.5 + This needs fixing. ++ 4.1.6 ++ 4.1.7 +- 4.1.8 + KWin only installs colormap required by the active window. +- 4.1.9 + Feature not supported, except for WM_ICON_NAME as a fallback for _NET_WM_ICON_NAME. ++ 4.1.10 ++ 4.1.11 + Window groups are only used for supporting NETWM section 7.3. ++ 4.2.5 +/ 4.2.7 + Qt doesn't set revert-to to Parent. ++ 4.2.8.1 frozen clients may be XKill-ed upon a user request though ++ 4.3 +? 4.4 ++ 4. (rest of the section) ++ 5.3. not KWin related ++ 5. (rest of the section ) +? 6.1. clients thing +? 6.2. clients thing - Qt perhaps should force rule 2. ++ 6.3. +? 6. (rest of the section) ++ 7. - no idea what it is, but it doesn't seem to be KWin related ++ 8. + + +KDE-specific extensions (for completeness): + +Property Name Type +========================================================================== +_KDE_WM_CHANGE_STATE root window message +_KDE_NET_SYSTEM_TRAY_WINDOWS root window property +_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR window property +_KDE_NET_WM_FRAME_STRUT window property +_NET_WM_CONTEXT_HELP + - Qt extension + - has no vendor prefix even though it's not part of the spec +_NET_WM_STATE_STAYS_ON_TOP + - KDE extension + - has no vendor prefix even though it's not part of the spec + - deprecated in favor of _NET_WM_STATE_KEEP_ABOVE +_KDE_NET_WM_WINDOW_TYPE_OVERRIDE + - window type, makes the window borderless + - unclear semantics, used also for fullscreen windows + - deprecated in favor of other window types + +========================================================================== diff --git a/kwin/CONFIGURING b/kwin/CONFIGURING new file mode 100644 index 000000000..4a2774149 --- /dev/null +++ b/kwin/CONFIGURING @@ -0,0 +1,73 @@ +CONTENTS: +========= + + +1. Pre-configuring window-specific settings + + + + + +1. Pre-configuring window-specific settings +=========================================== + +Window-specific settings is a feature of KWin that allows specifying some +settings only for a specific window or windows. See the Window-specific +settings section in the KWin configuration and the Special settings +menu entries in Alt+F3/Advanced menu. + +One aspect of window-specific settings is the ability to specify various +workarounds for (usually broken) applications that otherwise don't work +properly with KWin. This section describes how to create additional +window-specific settings that will be automatically used by all users +without any need of manual configuration. + +Example case: + +Application FooBar does not specify any maximum size for its main window, +but when resized to larger size than 1600x1200 it crashes because of a bug. +Manual configuration of a window-specific setting that avoids this problem +is opening and activating this window, selecting +Alt+F3/Advanced/Special window settings, activating tab Workarounds, enabling +setting Maximum size, changing it to Force and entering "1600,1200" as +the maximum size, which will make KWin force this size as the maximum size. + +To create such window-specific setting automatically without a need of doing +it manually for every user (for example when doing a large deployment), follow +these steps: + +- Back up your $KDEHOME/share/config/kwinrulesrc ($KDEHOME usually being $HOME/.kde) + and remove it +- Run 'dcop kwin default reconfigure' +- Create manually all window-specific settings that should be included (see above) +- When done, check in Window-specific settings configuration module + (Alt+F3/Configure window behavior/Window-specific settings) that all rules are + included +- Create a copy of $KDEHOME/share/config/kwinrulesrc and restore the original one +- Rename the copy (i.e. the newly created kwinrulesrc) to have its unique name + (e.g. foobar_fix_maxsize in this example case) +- Be careful with manual modifications of the file, especially make sure the count= + field in the [General] group is updated if needed +- Create a file for kconfig_update like this (named kwin_foobar_fix_maxsize.upd + in this example): + +# kwin_foobar_fix_maxsize.upd start # +Id=foobar_fix_maxsize +File=kwinrules_update +Group=Dummy +Options=overwrite +ScriptArguments=foobar_fix_maxsize +Script=kwin_update_default_rules + +# kwin_foobar_fix_maxsize.upd end # + +- The kconfig_file (kwin_foobar_fix_maxsize.upd) is to be placed + in $KDEDIR/share/apps/kconf_update/ +- The file with the window-specific settings (foobar_fix_maxsize) is to be placed + in $KDEDIR/share/apps/kwin/default_rules/ + + +All KDE user accounts should have these new window-specific settings added +automatically during next KDE startup (or within few seconds if they are active). +They can be checked again in the Window-specific settings configuration module of KWin. + diff --git a/kwin/HACKING b/kwin/HACKING new file mode 100644 index 000000000..1665aea56 --- /dev/null +++ b/kwin/HACKING @@ -0,0 +1,174 @@ +Mailing list and bugzilla: +========================== + +The KWin mailing list is kwin@kde.org . It's rather low traffic. + +The bugs.kde.org product for KWin is 'kwin'. Currently the components are 'general' (KWin core), +'decorations' (decoration plugins), 'compatibility' (problems with non-KDE WMs/apps) and +'eyecandy' (transparency and similar effects). +There are also two kcontrol components 'kcmkwindecoration' and 'kcmkwinoptions' related +to KWin's KControl modules. + + +KWin parts: +=========== + +There are four parts of KWin: +- The KWin core, located in kdebase/kwin/*, which implements the actual functionality. +- The decoration plugins, located in kdebase/kwin/clients and kdeartwork/kwin-styles, which + are responsible for the visual representation of the windows. +- The libkdecoration library, located in kdebase/kwin/lib/*, which is used for communication + between the core and the decoration, and also implements some shared functionality + for the decorations. +- KControl modules, located in kdebase/kwin/kcmkwin. + + +KWin decorations: +================= + +If you want to develop a decoration plugin for KWin, a HOWTO is available at +http://www.usermode.org/docs/kwintheme.html . It is currently not possible to create +a new decoration without knowledge of C++, but it should be possible to write a themeable +decoration (I'm not aware of any such decoration though). + + +Restarting KWin: +================ + +Since KWin takes care of focus handling, first killing KWin and then launching new instance +can cause focus trouble. Therefore it's possible to run 'kwin --replace', which will start +new KWin instance and tell the old one to quit. + + +Handling the case when KWin crashes: +==================================== + +Again, without KWin running there may be focus problems. The simplest way to solve them +is to add the 'Run Command' applet to Kicker - it can receive focus even without KWin running. +If you can't add the applet or can reach it for some reason, switch to text console, and run +'DISPLAY=:0 kwin --replace' (and then you can run 'kwin --replace' again from X). + +If KWin is temporarily unusable because of some change and e.g. crashes during startup, it +is possible to run another window manager, for example Metacity, OpenBox or FVWM (the command +is similar to restarting KWin, i.e. 'metacity --replace', 'openbox --replace' or 'fvwm -replace'). + + +Debugging KWin: +=============== + +Focus problems once more. It is not possible to debug KWin in gdb in the X session that KWin is managing, +because that'd block focus and window operations. It is necessary to switch to a text console +and attach to the running KWin instance from there, or launch it as 'DISPLAY=:0 gdb kwin'. + +Since KWin is such an important component of KDE, it is usually better to start another X for development. +Note that XNest is quite buggy and is therefore not recommended to use. + + +Window manager spec: +==================== + +The EWMH window manager specification, also known as NETWM, is located at the freedesktop.org site, +http://www.freedesktop.org/wiki/Standards_2fwm_2dspec . It defines how the window manager +communicates information with the applications and other desktop utilities such as the taskbar +or pager. + + +KWin structure: +=============== + +KWin has relatively few classes. The two main classes are Client, which represents windows +on the screen, and Workspace, which represents the whole screen and manages windows. Both these +classes are rather large, because they fulfil complicated tasks. In other to reduce size +of their source files these some functionality is in separate .cpp file grouped by the purpose: + +- workspace.* - core of class Workspace +- client.* - core of class Client +- activation.cpp - focus handling and window activation +- events.cpp - event handling is in events.cpp +- geometry.cpp - geometry-related code +- layers.cpp - stacking-related code +- manage.cpp - code dealing with new windows +- placement.cpp - window placements algorithms +- rules.cpp - code for window-specific settings +- sm.cpp - session management code +- useractions.cpp - handling of the Alt+F3 menu, shortcuts and other user actions + +The rest of the files contain additional helper classes: + +- atoms.* - so-called atoms (symbolic names for constants in X) +- bridge.* - communication with the decoration plugin +- geometrytip.* - window displaying window geometry while moving/resizing +- group.* - grouping related windows together (warning! This is currently really messy and scary code + that should be rewritten). +- killwindow.* - handling of the Ctrl+Esc feature +- kwinbindings.cpp - KWin's keyboard shortcuts (used by kdebase/kcontrol/keys) +- notifications.* - for KNotify +- options.* - all configuration options for KWin are stored in this class +- plugins.* - loading of the right decoration plugin +- popupinfo.* - showing temporary information such as virtual desktop name when switching desktops +- tabbox.* - the Alt+Tab dialog +- utils.* - various small utility functions/classes + +KWin also uses code from kdelibs, specifically files netwm.cpp, netwm.h, netwm_def.h and netwm_p.h +from kdelibs/kdecore. These files implement support for the EWMH window manager specification, +originally called NETWM (hence the filenames). + + +Developing KWin: +================ + +So, you feel brave, huh? But KWin is not THAT difficult. Some parts, especially the X-related ones, +can be very complicated, but for many parts even knowledge of X and Xlib is not necessary. Most X +code is wrapped in helper functions, and I can handle problems there ;) . However, although many +features don't require touching X/Xlib directly, still X/Xlib may impose their semantics on the way +things are done. When in doubt, simply ask. + +All patches for KWin core should be sent to kwin@kde.org for review first. Even seemingly harmless +changes may have extensive consequences. + +Various notes: + +- kdDebug has overloaded operator << for the Client class, so you can e.g. use 'kdDebug() << this << endl;' +in class Client and it will print information about the window. + +- KWin itself cannot create any normal windows, because it would have trouble managing its own windows. +For such cases (which should be rare) a small external helper application is needed (kdialog should often +do, and for special cases such a utility needs to be written like kwin/killer). + + +X documentation: +================ + +As already said, many parts of KWin don't need knowledge of Xlib or even how X actually works. +Some parts do, and it may be also useful to have at least a basic understand for general +understanding. A reference manual for Xlib can be found e.g. +at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/xlib.pdf , a tutorial explaining basic can be found +e.g. at ttp://users.actcom.co.il/~choo/lupg/tutorials/xlib-programming/xlib-programming.html +(note that you don't need to know that all - e.g. GC's are very rarely needed and the +section on fonts is today outdated). + + +Coding style: +============= + +There are only three rules for patches for KWin: + +- the code should be relatively nice and clean. Seriously. Any messy code can be hard to comprehend, +but if the code is in a window manager it will be twice as difficult. + +- unless the functionality of the code is obvious, there should be either at least a short comment explaining +what it does, or it should be obvious from the commit log. If there's a hack needed, if there's a potentional +problem, if something is just a temporary fix, say so. Comments like "this clever trick is necessary" +don't count. See rule #1 above for reasons. I needed more than two years to understand all of KWin, +and there were parts I never got and had to rewrite in order to fix a problem with them. + +- put matching opening { and closing } in the same column. That's the only formatting rule I ask for. +I don't really care if they're aligned with the block one level higher or if they're aligned with the block +they surround like I do (which is the only thing about the "weird coding style in KWin") or if you align them +with something else, just put them in the same column. If I can handle about half a dozen different formatting +styles when working on various parts of KDE, this shouldn't be much work for you (and you can do that only +right before sending the patch). I don't care where you do and don't put spaces or what exactly you call local +variables, as long as I can read it (trying to make it look like the rest of the code is bonus points though ;) ). + + +kwin@kde.org diff --git a/kwin/KWinInterface.h b/kwin/KWinInterface.h new file mode 100644 index 000000000..d368ec368 --- /dev/null +++ b/kwin/KWinInterface.h @@ -0,0 +1,42 @@ +#ifndef KWIN_INTERFACE_H +#define KWIN_INTERFACE_H + +#include <dcopobject.h> + +class KWinInterface : virtual public DCOPObject + { + K_DCOP + + k_dcop: + + virtual ASYNC cascadeDesktop() = 0; + virtual ASYNC unclutterDesktop() = 0; + virtual ASYNC reconfigure() = 0; + virtual ASYNC killWindow() = 0; + virtual void refresh() = 0; + virtual void doNotManage(QString)= 0; + virtual void showWindowMenuAt(unsigned long winId, int x, int y)= 0; + virtual void setDesktopLayout(int orientation, int x, int y)= 0; + virtual bool setCurrentDesktop(int)= 0; + virtual int currentDesktop() const = 0; + virtual void nextDesktop() = 0; + virtual void previousDesktop() = 0; + virtual void circulateDesktopApplications() = 0; + // kompmgr stuff + virtual void startKompmgr() = 0; + virtual void stopKompmgr() = 0; + virtual bool kompmgrIsRunning() = 0; + virtual void setOpacity(unsigned long winId, unsigned int opacityPercent) = 0; + virtual void setShadowSize(unsigned long winId, unsigned int shadowSizePercent) = 0; + virtual void setUnshadowed(unsigned long winId) = 0; + + k_dcop_signals: + + virtual void kompmgrStarted() = 0; + virtual void kompmgrStopped() = 0; + + // never emitted + virtual void dcopResetAllClients(); + }; + +#endif diff --git a/kwin/LICENSE b/kwin/LICENSE new file mode 100644 index 000000000..2f2e4b2fd --- /dev/null +++ b/kwin/LICENSE @@ -0,0 +1,21 @@ +Since KDE3.2, KWin is licensed under the terms of the General Public License. +See file "COPYING" in the toplevel directory for the exact licensing terms. + +KWin versions in KDE3.1.x and older used the following license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/kwin/Makefile.am b/kwin/Makefile.am new file mode 100644 index 000000000..c652c81e9 --- /dev/null +++ b/kwin/Makefile.am @@ -0,0 +1,38 @@ +INCLUDES = -I$(srcdir)/lib $(all_includes) + +if include_kompmgr +KOMPMGR=kompmgr +endif + +SUBDIRS = lib . killer kcmkwin pics clients oldheaders data $(KOMPMGR) + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kwin.la + +kwin_la_SOURCES = workspace.cpp client.cpp placement.cpp atoms.cpp \ + utils.cpp layers.cpp main.cpp popupinfo.cpp tabbox.cpp \ + options.cpp plugins.cpp events.cpp KWinInterface.skel \ + killwindow.cpp geometrytip.cpp sm.cpp group.cpp bridge.cpp \ + manage.cpp notifications.cpp activation.cpp useractions.cpp \ + geometry.cpp rules.cpp + +kwin_la_LIBADD = $(LIB_KDEUI) lib/libkdecorations.la +kwin_la_LDFLAGS = $(all_libraries) -module -avoid-version + +include_HEADERS = KWinInterface.h + +KDE_ICON = kwin + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) -kaliasLocal *.h *.cpp killer/*.cpp lib/*.cpp -o $(podir)/kwin.pot + +kwin_datadir = $(kde_datadir)/kwin + +kwin_data_DATA= eventsrc + +kde_kcfg_DATA = kwin.kcfg + +include ../admin/Doxyfile.am diff --git a/kwin/NEWCOLORSCHEME.README b/kwin/NEWCOLORSCHEME.README new file mode 100644 index 000000000..ce2ca9bbd --- /dev/null +++ b/kwin/NEWCOLORSCHEME.README @@ -0,0 +1,44 @@ +KWin can now handle some new color scheme entries in addition to the ones +handled by KWM. Note that these are suggestions and all the colors may not +be used by all KWin styles. These all currently go into the [WM] group. + +frame, inactiveFrame : Window frame (was fixed to general KDE bg in KWM). + +handle, inactiveHandle : Window handles (sometimes called "grips"). + +activeBackground, inactiveBackground : Tilebars (bad name, but it's what KWM +uses). This is for styles that use a solid filled rectangle for the titlebar +such as the standard style and KStep. + +activeGroove, inactiveGroove: This is for titlebars that instead of a solid +rectangle use some sort of groove or small bevels layered on the frame. +An example of this is System. + +activeBlend, inactiveBlend : Titlebar blend for styles that use a rectangle +titlebar fill. + +activeForeground, inactiveForeground : Tilebar text for styles that use a +rectangle titlebar fill. + +activeGrooveText, inactiveGrooveText: Titlebar text for styles that use +grooved titlebar styles instead of the solid fill. This should contrast with +the frame color. + +activeTitleBtnBg, inactiveTitleButtonBg : Button background color for up and +down states. + +activeTitleBtnBlend, inactiveTitleBtnBlend : Button blend. + +activeTitleBtnFg, activeTitleBtnFg : Some style's buttons don't use the +above button background colors but instead draw the button foreground +transparently on the frame. The best example of this is the standard KDE +style. Use this to specify a color for such styles, which should contrast with +the frame - not the button bg. + +activeTitleBtnFullFg, inactiveTitleBtnFullFg: This is for styles that have +full support for button background settings. Examples are KStep and System. +This should contrast with the button background. + +Daniel M. Duley +mosfet@kde.org +mosfet@linuxmandrake.com diff --git a/kwin/README b/kwin/README new file mode 100644 index 000000000..30e3e9a15 --- /dev/null +++ b/kwin/README @@ -0,0 +1,206 @@ +- The mailing list for KWin is kwin@kde.org (https://mail.kde.org/mailman/listinfo/kwin). + +- If you want to develop KWin, see file HACKING. + +- If you want to check KWin's compliance with specifications, see file COMPLIANCE. + +- File CONFIGURATION includes some details on configuring KWin. + +- Below is some info for application developers about application interaction + with the window manager, but it'd need some cleanup. + + + + + + + + + This README is meant as an explanation of various window manager related +mechanisms that application developers need to be aware of. As some of these +concepts may be difficult to understand for people not having the required +background knowledge (since sometimes it's difficult even for people who +do have the knowledge), the mechanisms are first briefly explained, and +then an example of fixing the various problems is given. + + For comments, questions, suggestions and whatever use the kwin@kde.org +mailing list. + + +Table of contents: +================== + +- Window relations + - how to make the window manager know which windows belong together +- Focus stealing prevention + - how to solve cases where focus stealing prevention doesn't work + properly automatically + + + +Window relations: +================= + +(For now, this explanation of window relations is mainly meant for +focus stealing prevention. To be extended later.) + + All windows created by an application should be organized in a tree +with the root being the application's main window. Note that this is about +toplevel windows, not widgets inside the windows. For example, if you +have KWrite running, with a torn-off toolbar (i.e. a standalone toolbar), +a file save dialog open, and the file save dialog showing a dialog +for creating a directory, the window hiearchy should look like this: + + + KWrite mainwindow + / \ + / \ + file save dialog torn-off toolbar + \ + \ + create directory dialog + + Each subwindow (i.e. all except for the KWrite mainwindow) points to its +main window (which in turn may have another main window, as in the case +of the file save dialog). When the window manager knows these relations, +it can better arrange the windows (keeping subwindows above their +main windows, preventing activation of a main window of a modal dialog, +and similar). Failing to provide this information to the window manager +may have various results, for example having dialogs positioned below +the main window, + +The window property used by subwindows to point to their mainwindows is +called WM_TRANSIENT_FOR. It can be seen by running +'xprop | grep WM_TRANSIENT_FOR' and clicking on a window. If the property +is not present, the window does not (claim to) have any mainwindow. +If the property is present, it's value is the window id of its main window; +window id of any window can be found out by running 'xwininfo'. A window +having WM_TRANSIENT_FOR poiting to another window is said to be transient +for that window. + + In some cases, the WM_TRANSIENT_FOR property may not point to any other +existing window, having value of 0, or pointing to the screen number +('xwininfo -root'). These special values mean that the window is transient +for all other windows in its window group. This should be used only +in rare cases, everytime a specific main window is known, WM_TRANSIENT_FOR +should be pointing to it instead of using one of these special values. +(The explanation why is beyond the scope of this document - just accept it +as a fact.) + + With Qt, the WM_TRANSIENT_FOR property is set by Qt automatically, based +on the toplevel widget's parent. If the toplevel widget is of a normal +type (i.e. not a dialog, toolbar, etc.), Qt doesn't set WM_TRANSIENT_FOR +on it. For special widgets, such as dialogs, WM_TRANSIENT_FOR is set +to point to the widget's parent, if it has a specific parent, otherwise +WM_TRANSIENT_FOR points to the root window. + + As already said above, WM_TRANSIENT_FOR poiting to the root window should +be usually avoided, so everytime the widget's main widget is known, the widget +should get it passed as a parent in its constructor. +(TODO KDialog etc. classes should not have a default argument for the parent +argument, and comments like 'just pass 0 as the parent' should go.) + + + +Focus stealing prevention: +========================== + + Since KDE3.2 KWin has a feature called focus stealing prevention. As the name +suggests, it prevents unexpected changes of focus. With older versions of KWin, +if any application opened a new dialog, it became active, and +if the application's main window was on another virtual desktop, also +the virtual desktop was changed. This was annoying, and also sometimes led +to dialogs mistakenly being closed because they received keyboard input that +was meant for the previously active window. + + The basic principle of focus stealing prevention is that the window with most +recent user activity wins. Any window of an application will become active +when being shown only if this application was the most recently used one. +KWin itself, and some of the related kdecore classes should take care +of the common cases, so usually there's no need for any special handling +in applications. Qt/KDE applications, that is. Applications using other +toolkits should in most cases work fine too. If they don't support +the window property _NET_WM_USER_TIME, the window manager may fail to detect +the user timestamp properly, resulting either in other windows becoming active +while the user works with this application, or this application may sometimes +steal focus (this second case should be very rare though). + + There are also cases where KDE applications needs special handling. The two +most common cases are when windows relations are not setup properly to make +KWin realize that they belong to the same application, and when the user +activity is not represented by manipulating with the application windows +themselves. + + Also note that focus stealing prevention implemented in the window manager +can only help with focus stealing between different applications. +If an application itself suddenly pops up a dialog, KWin cannot do anything about +it, and its the application's job to handle this case. + + +Window relations: +----------------- + + The common case here is when a dialog is shown for an application, but this +dialog is not provided by the application itself, but by some other process. +For example, dialogs with warnings about accepted cookies are provided +by KCookieJar, instead of being shown by Konqueror. In the normal case, +from KWin's point of view the cookie dialog would be an attempt of another +application to show a dialog, and KWin wouldn't allow activation of this +window. + + The solution is to tell the window manager about the relation between +the Konqueror main window and the cookie dialog, by making the dialog +point to the mainwindow. Note that this is not special to focus stealing +prevention, subwindows such as dialogs, toolbars and similar should always +point to their mainwindow. See the section on window relations for full +description. + + The WM_TRANSIENT_FOR property that's set on dialogs to point to their +mainwindow should in the cookie dialog case point to the Konqueror window +for which it has been shown. This is solved in kcookiejar by including +the window id in the DCOP call. When the cookie dialog is shown, its +WM_TRANSIENT_FOR property is manually set using the XSetTransientForHint() +call (see kdelibs/kioslave/http/kcookiejar/kcookiewin.cpp). The arguments +to XSetTransientForHint() call are the X display (i.e. qt_xdisplay()), +the window id on which the WM_TRANSIENT_FOR property is to be set +(i.e. use QWidget::winId()), and the window id of the mainwindow. + + + Simple short HOWTO: + + To put it simply: Let's say you have a daemon application that has +DCOP call "showDialog( QString text )", and when this is called, it shows +a dialog with the given text. This won't work properly with focus stealing +prevention. The DCOP call should be changed to +"showDialog( QString text, long id )". The caller should pass something like +myMainWindow->winId() as the second argument. In the daemon, before +the dialog is shown, a call to XSetTransientHint() should be added: + + XSetTransientForHint( qt_xdisplay(), dialog->winId(), id_of_mainwindow ); + + That's it. + +Non-standard user activity: +--------------------------- + + The most common case in KDE will be DCOP calls. For example, KDesktop's DCOP +call "KDesktopIface popupExecuteCommand". Executing this DCOP call e.g. +from Konsole as 'dcop kdesktop KDesktopIface popupExecuteCommand" will lead +to showing the minicli, but the last user activity timestamp gained from events +sent by X server will be older than user activity timestamp of Konsole, and +would normally result in minicli not being active. Therefore, before showing +the minicli, kdesktop needs to call KApplication::updateUserTimestamp(). + + However, this shouldn't be done with all DCOP calls. If a DCOP call is not +a result of direct user action, calling KApplication::updateUserTimestamp() +would lead to focus stealing. For example, let's assume for a moment +that KMail would use this DCOP call in case it detects the modem is not +connected, allowing to you to start KPPP or whatever tool you use. If KMail +would be configured to check mail every 10 minutes, this would lead to minicli +possibly suddenly showing up at every check. Basically, doing the above change +to kdesktop's minicli means that the popupExecuteCommand() DCOP call is only +for user scripting. (TODO write about focus transferring?) + + Simply said, KApplication::updateUserTimestamp() should be called only +as a result of user action. Unfortunately, I'm not aware of any universal +way how to handle this, so every case will have to be considered separately. diff --git a/kwin/activation.cpp b/kwin/activation.cpp new file mode 100644 index 000000000..2551519ec --- /dev/null +++ b/kwin/activation.cpp @@ -0,0 +1,922 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to window activation and focus + stealing prevention. + +*/ + +#include "client.h" +#include "workspace.h" + +#include <fixx11h.h> +#include <qpopupmenu.h> +#include <kxerrorhandler.h> +#include <kstartupinfo.h> +#include <kstringhandler.h> +#include <klocale.h> + +#include "notifications.h" +#include "atoms.h" +#include "group.h" +#include "rules.h" + +extern Time qt_x_time; + +namespace KWinInternal +{ + +/* + Prevention of focus stealing: + + KWin tries to prevent unwanted changes of focus, that would result + from mapping a new window. Also, some nasty applications may try + to force focus change even in cases when ICCCM 4.2.7 doesn't allow it + (e.g. they may try to activate their main window because the user + definitely "needs" to see something happened - misusing + of QWidget::setActiveWindow() may be such case). + + There are 4 ways how a window may become active: + - the user changes the active window (e.g. focus follows mouse, clicking + on some window's titlebar) - the change of focus will + be done by KWin, so there's nothing to solve in this case + - the change of active window will be requested using the _NET_ACTIVE_WINDOW + message (handled in RootInfo::changeActiveWindow()) - such requests + will be obeyed, because this request is meant mainly for e.g. taskbar + asking the WM to change the active window as a result of some user action. + Normal applications should use this request only rarely in special cases. + See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER. + - the change of active window will be done by performing XSetInputFocus() + on a window that's not currently active. ICCCM 4.2.7 describes when + the application may perform change of input focus. In order to handle + misbehaving applications, KWin will try to detect focus changes to + windows that don't belong to currently active application, and restore + focus back to the currently active window, instead of activating the window + that got focus (unfortunately there's no way to FocusChangeRedirect similar + to e.g. SubstructureRedirect, so there will be short time when the focus + will be changed). The check itself that's done is + Workspace::allowClientActivation() (see below). + - a new window will be mapped - this is the most complicated case. If + the new window belongs to the currently active application, it may be safely + mapped on top and activated. The same if there's no active window, + or the active window is the desktop. These checks are done by + Workspace::allowClientActivation(). + Following checks need to compare times. One time is the timestamp + of last user action in the currently active window, the other time is + the timestamp of the action that originally caused mapping of the new window + (e.g. when the application was started). If the first time is newer than + the second one, the window will not be activated, as that indicates + futher user actions took place after the action leading to this new + mapped window. This check is done by Workspace::allowClientActivation(). + There are several ways how to get the timestamp of action that caused + the new mapped window (done in Client::readUserTimeMapTimestamp()) : + - the window may have the _NET_WM_USER_TIME property. This way + the application may either explicitly request that the window is not + activated (by using 0 timestamp), or the property contains the time + of last user action in the application. + - KWin itself tries to detect time of last user action in every window, + by watching KeyPress and ButtonPress events on windows. This way some + events may be missed (if they don't propagate to the toplevel window), + but it's good as a fallback for applications that don't provide + _NET_WM_USER_TIME, and missing some events may at most lead + to unwanted focus stealing. + - the timestamp may come from application startup notification. + Application startup notification, if it exists for the new mapped window, + should include time of the user action that caused it. + - if there's no timestamp available, it's checked whether the new window + belongs to some already running application - if yes, the timestamp + will be 0 (i.e. refuse activation) + - if the window is from session restored window, the timestamp will + be 0 too, unless this application was the active one at the time + when the session was saved, in which case the window will be + activated if there wasn't any user interaction since the time + KWin was started. + - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp + is used. For every toplevel window that is created (see CreateNotify + handling), this property is set to the at that time current time. + Since at this time it's known that the new window doesn't belong + to any existing application (better said, the application doesn't + have any other window mapped), it is either the very first window + of the application, or its the only window of the application + that was hidden before. The latter case is handled by removing + the property from windows before withdrawing them, making + the timestamp empty for next mapping of the window. In the sooner + case, the timestamp will be used. This helps in case when + an application is launched without application startup notification, + it creates its mainwindow, and starts its initialization (that + may possibly take long time). The timestamp used will be older + than any user action done after launching this application. + - if no timestamp is found at all, the window is activated. + The check whether two windows belong to the same application (same + process) is done in Client::belongToSameApplication(). Not 100% reliable, + but hopefully 99,99% reliable. + + As a somewhat special case, window activation is always enabled when + session saving is in progress. When session saving, the session + manager allows only one application to interact with the user. + Not allowing window activation in such case would result in e.g. dialogs + not becoming active, so focus stealing prevention would cause here + more harm than good. + + Windows that attempted to become active but KWin prevented this will + be marked as demanding user attention. They'll get + the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark + them specially (blink, etc.). The state will be reset when the window + eventually really becomes active. + + There are one more ways how a window can become obstrusive, window stealing + focus: By showing above the active window, by either raising itself, + or by moving itself on the active desktop. + - KWin will refuse raising non-active window above the active one, + unless they belong to the same application. Applications shouldn't + raise their windows anyway (unless the app wants to raise one + of its windows above another of its windows). + - KWin activates windows moved to the current desktop (as that seems + logical from the user's point of view, after sending the window + there directly from KWin, or e.g. using pager). This means + applications shouldn't send their windows to another desktop + (SELI TODO - but what if they do?) + + Special cases I can think of: + - konqueror reusing, i.e. kfmclient tells running Konqueror instance + to open new window + - without focus stealing prevention - no problem + - with ASN (application startup notification) - ASN is forwarded, + and because it's newer than the instance's user timestamp, + it takes precedence + - without ASN - user timestamp needs to be reset, otherwise it would + be used, and it's old; moreover this new window mustn't be detected + as window belonging to already running application, or it wouldn't + be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly) + hack + - konqueror preloading, i.e. window is created in advance, and kfmclient + tells this Konqueror instance to show it later + - without focus stealing prevention - no problem + - with ASN - ASN is forwarded, and because it's newer than the instance's + user timestamp, it takes precedence + - without ASN - user timestamp needs to be reset, otherwise it would + be used, and it's old; also, creation timestamp is changed to + the time the instance starts (re-)initializing the window, + this ensures creation timestamp will still work somewhat even in this case + - KUniqueApplication - when the window is already visible, and the new instance + wants it to activate + - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem + - with ASN - ASN is forwarded, and set on the already visible window, KWin + treats the window as new with that ASN + - without ASN - _NET_ACTIVE_WINDOW as application request is used, + and there's no really usable timestamp, only timestamp + from the time the (new) application instance was started, + so KWin will activate the window *sigh* + - the bad thing here is that there's absolutely no chance to recognize + the case of starting this KUniqueApp from Konsole (and thus wanting + the already visible window to become active) from the case + when something started this KUniqueApp without ASN (in which case + the already visible window shouldn't become active) + - the only solution is using ASN for starting applications, at least silent + (i.e. without feedback) + - when one application wants to activate another application's window (e.g. KMail + activating already running KAddressBook window ?) + - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem + - with ASN - can't be here, it's the KUniqueApp case then + - without ASN - _NET_ACTIVE_WINDOW as application request should be used, + KWin will activate the new window depending on the timestamp and + whether it belongs to the currently active application + + _NET_ACTIVE_WINDOW usage: + data.l[0]= 1 ->app request + = 2 ->pager request + = 0 - backwards compatibility + data.l[1]= timestamp +*/ + + +//**************************************** +// Workspace +//**************************************** + + +/*! + Informs the workspace about the active client, i.e. the client that + has the focus (or None if no client has the focus). This functions + is called by the client itself that gets focus. It has no other + effect than fixing the focus chain and the return value of + activeClient(). And of course, to propagate the active client to the + world. + */ +void Workspace::setActiveClient( Client* c, allowed_t ) + { + if ( active_client == c ) + return; + if( active_popup && active_popup_client != c && set_active_client_recursion == 0 ) + closeActivePopup(); + StackingUpdatesBlocker blocker( this ); + ++set_active_client_recursion; + updateFocusMousePosition( QCursor::pos()); + if( active_client != NULL ) + { // note that this may call setActiveClient( NULL ), therefore the recursion counter + active_client->setActive( false, !c || !c->isModal() || c != active_client->transientFor() ); + } + active_client = c; + Q_ASSERT( c == NULL || c->isActive()); + if( active_client != NULL ) + last_active_client = active_client; + if ( active_client ) + { + updateFocusChains( active_client, FocusChainMakeFirst ); + active_client->demandAttention( false ); + } + pending_take_activity = NULL; + + updateCurrentTopMenu(); + updateToolWindows( false ); + if( c ) + disableGlobalShortcutsForClient( c->rules()->checkDisableGlobalShortcuts( false )); + else + disableGlobalShortcutsForClient( false ); + + updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active + + rootInfo->setActiveWindow( active_client? active_client->window() : 0 ); + updateColormap(); + --set_active_client_recursion; + } + +/*! + Tries to activate the client \a c. This function performs what you + expect when clicking the respective entry in a taskbar: showing and + raising the client (this may imply switching to the another virtual + desktop) and putting the focus onto it. Once X really gave focus to + the client window as requested, the client itself will call + setActiveClient() and the operation is complete. This may not happen + with certain focus policies, though. + + \sa stActiveClient(), requestFocus() + */ +void Workspace::activateClient( Client* c, bool force ) + { + if( c == NULL ) + { + focusToNull(); + setActiveClient( NULL, Allowed ); + return; + } + raiseClient( c ); + if (!c->isOnDesktop(currentDesktop()) ) + { + ++block_focus; + setCurrentDesktop( c->desktop() ); + --block_focus; + } + if( c->isMinimized()) + c->unminimize(); + +// TODO force should perhaps allow this only if the window already contains the mouse + if( options->focusPolicyIsReasonable() || force ) + requestFocus( c, force ); + + // Don't update user time for clients that have focus stealing workaround. + // As they usually belong to the current active window but fail to provide + // this information, updating their user time would make the user time + // of the currently active window old, and reject further activation for it. + // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround), + // and then kdesktop shows dialog about SSL certificate. + // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp(). + if( !c->ignoreFocusStealing()) + c->updateUserTime(); + } + +/*! + Tries to activate the client by asking X for the input focus. This + function does not perform any show, raise or desktop switching. See + Workspace::activateClient() instead. + + \sa Workspace::activateClient() + */ +void Workspace::requestFocus( Client* c, bool force ) + { + takeActivity( c, ActivityFocus | ( force ? ActivityFocusForce : 0 ), false); + } + +void Workspace::takeActivity( Client* c, int flags, bool handled ) + { + // the 'if( c == active_client ) return;' optimization mustn't be done here + if (!focusChangeEnabled() && ( c != active_client) ) + flags &= ~ActivityFocus; + + if ( !c ) + { + focusToNull(); + return; + } + + if( flags & ActivityFocus ) + { + Client* modal = c->findModal(); + if( modal != NULL && modal != c ) + { + if( !modal->isOnDesktop( c->desktop())) + { + modal->setDesktop( c->desktop()); + if( modal->desktop() != c->desktop()) // forced desktop + activateClient( modal ); + } + // if the click was inside the window (i.e. handled is set), + // but it has a modal, there's no need to use handled mode, because + // the modal doesn't get the click anyway + // raising of the original window needs to be still done + if( flags & ActivityRaise ) + raiseClient( c ); + c = modal; + handled = false; + } + cancelDelayFocus(); + } + if ( !( flags & ActivityFocusForce ) && ( c->isTopMenu() || c->isDock() || c->isSplash()) ) + flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced + if( c->isShade()) + { + if( c->wantsInput() && ( flags & ActivityFocus )) + { + // client cannot accept focus, but at least the window should be active (window menu, et. al. ) + c->setActive( true ); + focusToNull(); + } + flags &= ~ActivityFocus; + handled = false; // no point, can't get clicks + } + if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed + { + kdWarning( 1212 ) << "takeActivity: not shown" << endl; + return; + } + c->takeActivity( flags, handled, Allowed ); + } + +void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags ) + { + if( pending_take_activity != c ) // pending_take_activity is reset when doing restack or activation + return; + if(( flags & ActivityRaise ) != 0 ) + raiseClient( c ); + if(( flags & ActivityFocus ) != 0 && c->isShown( false )) + c->takeFocus( Allowed ); + pending_take_activity = NULL; + } + +/*! + Informs the workspace that the client \a c has been hidden. If it + was the active client (or to-become the active client), + the workspace activates another one. + + \a c may already be destroyed + */ +void Workspace::clientHidden( Client* c ) + { + assert( !c->isShown( true ) || !c->isOnCurrentDesktop()); + activateNextClient( c ); + } + +// deactivates 'c' and activates next client +bool Workspace::activateNextClient( Client* c ) + { + // if 'c' is not the active or the to-become active one, do nothing + if( !( c == active_client + || ( should_get_focus.count() > 0 && c == should_get_focus.last()))) + return false; + closeActivePopup(); + if( c != NULL ) + { + if( c == active_client ) + setActiveClient( NULL, Allowed ); + should_get_focus.remove( c ); + } + if( focusChangeEnabled()) + { + if ( options->focusPolicyIsReasonable()) + { // search the focus_chain for a client to transfer focus to + // if 'c' is transient, transfer focus to the first suitable mainwindow + Client* get_focus = NULL; + const ClientList mainwindows = ( c != NULL ? c->mainClients() : ClientList()); + for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); + it != focus_chain[currentDesktop()].end(); + --it ) + { + if( !(*it)->isShown( false ) || !(*it)->isOnCurrentDesktop()) + continue; + if( mainwindows.contains( *it )) + { + get_focus = *it; + break; + } + if( get_focus == NULL ) + get_focus = *it; + } + if( get_focus == NULL ) + get_focus = findDesktop( true, currentDesktop()); + if( get_focus != NULL ) + requestFocus( get_focus ); + else + focusToNull(); + } + else + return false; + } + else + // if blocking focus, move focus to the desktop later if needed + // in order to avoid flickering + focusToNull(); + return true; + } + + +void Workspace::gotFocusIn( const Client* c ) + { + if( should_get_focus.contains( const_cast< Client* >( c ))) + { // remove also all sooner elements that should have got FocusIn, + // but didn't for some reason (and also won't anymore, because they were sooner) + while( should_get_focus.first() != c ) + should_get_focus.pop_front(); + should_get_focus.pop_front(); // remove 'c' + } + } + +void Workspace::setShouldGetFocus( Client* c ) + { + should_get_focus.append( c ); + updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active + } + +// focus_in -> the window got FocusIn event +// session_active -> the window was active when saving session +bool Workspace::allowClientActivation( const Client* c, Time time, bool focus_in ) + { + // options->focusStealingPreventionLevel : + // 0 - none - old KWin behaviour, new windows always get focus + // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed + // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed, + // this is the default + // 3 - high - new window gets focus only if it belongs to the active application, + // or when no window is currently active + // 4 - extreme - no window gets focus without user intervention + if( time == -1U ) + time = c->userTime(); + int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); + if( session_saving && level <= 2 ) // <= normal + { + return true; + } + Client* ac = mostRecentlyActivatedClient(); + if( focus_in ) + { + if( should_get_focus.contains( const_cast< Client* >( c ))) + return true; // FocusIn was result of KWin's action + // Before getting FocusIn, the active Client already + // got FocusOut, and therefore got deactivated. + ac = last_active_client; + } + if( time == 0 ) // explicitly asked not to get focus + return false; + if( level == 0 ) // none + return true; + if( level == 4 ) // extreme + return false; + if( !c->isOnCurrentDesktop()) + return false; // allow only with level == 0 + if( c->ignoreFocusStealing()) + return true; + if( ac == NULL || ac->isDesktop()) + { +// kdDebug( 1212 ) << "Activation: No client active, allowing" << endl; + return true; // no active client -> always allow + } + // TODO window urgency -> return true? + if( Client::belongToSameApplication( c, ac, true )) + { +// kdDebug( 1212 ) << "Activation: Belongs to active application" << endl; + return true; + } + if( level == 3 ) // high + return false; + if( time == -1U ) // no time known + { +// kdDebug( 1212 ) << "Activation: No timestamp at all" << endl; + if( level == 1 ) // low + return true; + // no timestamp at all, don't activate - because there's also creation timestamp + // done on CreateNotify, this case should happen only in case application + // maps again already used window, i.e. this won't happen after app startup + return false; + } + // level == 2 // normal + Time user_time = ac->userTime(); +// kdDebug( 1212 ) << "Activation, compared:" << c << ":" << time << ":" << user_time +// << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; + return timestampCompare( time, user_time ) >= 0; // time >= user_time + } + +// basically the same like allowClientActivation(), this time allowing +// a window to be fully raised upon its own request (XRaiseWindow), +// if refused, it will be raised only on top of windows belonging +// to the same application +bool Workspace::allowFullClientRaising( const Client* c, Time time ) + { + int level = c->rules()->checkFSP( options->focusStealingPreventionLevel ); + if( session_saving && level <= 2 ) // <= normal + { + return true; + } + Client* ac = mostRecentlyActivatedClient(); + if( level == 0 ) // none + return true; + if( level == 4 ) // extreme + return false; + if( ac == NULL || ac->isDesktop()) + { +// kdDebug( 1212 ) << "Raising: No client active, allowing" << endl; + return true; // no active client -> always allow + } + if( c->ignoreFocusStealing()) + return true; + // TODO window urgency -> return true? + if( Client::belongToSameApplication( c, ac, true )) + { +// kdDebug( 1212 ) << "Raising: Belongs to active application" << endl; + return true; + } + if( level == 3 ) // high + return false; + Time user_time = ac->userTime(); +// kdDebug( 1212 ) << "Raising, compared:" << time << ":" << user_time +// << ":" << ( timestampCompare( time, user_time ) >= 0 ) << endl; + return timestampCompare( time, user_time ) >= 0; // time >= user_time + } + +// called from Client after FocusIn that wasn't initiated by KWin and the client +// wasn't allowed to activate +void Workspace::restoreFocus() + { + // this updateXTime() is necessary - as FocusIn events don't have + // a timestamp *sigh*, kwin's timestamp would be older than the timestamp + // that was used by whoever caused the focus change, and therefore + // the attempt to restore the focus would fail due to old timestamp + updateXTime(); + if( should_get_focus.count() > 0 ) + requestFocus( should_get_focus.last()); + else if( last_active_client ) + requestFocus( last_active_client ); + } + +void Workspace::clientAttentionChanged( Client* c, bool set ) + { + if( set ) + { + attention_chain.remove( c ); + attention_chain.prepend( c ); + } + else + attention_chain.remove( c ); + } + +// This is used when a client should be shown active immediately after requestFocus(), +// without waiting for the matching FocusIn that will really make the window the active one. +// Used only in special cases, e.g. for MouseActivateRaiseandMove with transparent windows, +bool Workspace::fakeRequestedActivity( Client* c ) + { + if( should_get_focus.count() > 0 && should_get_focus.last() == c ) + { + if( c->isActive()) + return false; + c->setActive( true ); + return true; + } + return false; + } + +void Workspace::unfakeActivity( Client* c ) + { + if( should_get_focus.count() > 0 && should_get_focus.last() == c ) + { // TODO this will cause flicker, and probably is not needed + if( last_active_client != NULL ) + last_active_client->setActive( true ); + else + c->setActive( false ); + } + } + + +//******************************************** +// Client +//******************************************** + +/*! + Updates the user time (time of last action in the active window). + This is called inside kwin for every action with the window + that qualifies for user interaction (clicking on it, activate it + externally, etc.). + */ +void Client::updateUserTime( Time time ) + { // copied in Group::updateUserTime + if( time == CurrentTime ) + time = qt_x_time; + if( time != -1U + && ( user_time == CurrentTime + || timestampCompare( time, user_time ) > 0 )) // time > user_time + user_time = time; + group()->updateUserTime( user_time ); + } + +Time Client::readUserCreationTime() const + { + long result = -1; // Time == -1 means none + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + KXErrorHandler handler; // ignore errors? + status = XGetWindowProperty( qt_xdisplay(), window(), + atoms->kde_net_wm_user_creation_time, 0, 10000, FALSE, XA_CARDINAL, + &type, &format, &nitems, &extra, &data ); + if (status == Success ) + { + if (data && nitems > 0) + result = *((long*) data); + XFree(data); + } + return result; + } + +void Client::demandAttention( bool set ) + { + if( isActive()) + set = false; + if( demands_attention == set ) + return; + demands_attention = set; + if( demands_attention ) + { + // Demand attention flag is often set right from manage(), when focus stealing prevention + // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place + // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry + // to be set. + // Delayed call to KNotify also solves the problem of having X server grab in manage(), + // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X. + Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; + // Setting the demands attention state needs to be done directly in KWin, because + // KNotify would try to set it, resulting in a call to KNotify again, etc. + if( Notify::makeDemandAttention( e )) + info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); + + if( demandAttentionKNotifyTimer == NULL ) + { + demandAttentionKNotifyTimer = new QTimer( this ); + connect( demandAttentionKNotifyTimer, SIGNAL( timeout()), SLOT( demandAttentionKNotify())); + } + demandAttentionKNotifyTimer->start( 1000, true ); + } + else + info->setState( set ? NET::DemandsAttention : 0, NET::DemandsAttention ); + workspace()->clientAttentionChanged( this, set ); + } + +void Client::demandAttentionKNotify() + { + Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; + Notify::raise( e, i18n( "Window '%1' demands attention." ).arg( KStringHandler::csqueeze(caption())), this ); + demandAttentionKNotifyTimer->stop(); + demandAttentionKNotifyTimer->deleteLater(); + demandAttentionKNotifyTimer = NULL; + } + +// TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it +KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*, + // ignore already existing splashes, toolbars, utilities, menus and topmenus, + // as the app may show those before the main window + !cl->isSplash() && !cl->isToolbar() && !cl->isTopMenu() && !cl->isUtility() && !cl->isMenu() + && Client::belongToSameApplication( cl, value, true ) && cl != value); + +Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, + bool session ) const + { + Time time = info->userTime(); +// kdDebug( 1212 ) << "User timestamp, initial:" << time << endl; + // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0 + // helps e.g. with konqy reusing + if( asn_data != NULL && time != 0 ) + { + // prefer timestamp from ASN id (timestamp from data is obsolete way) + if( asn_id->timestamp() != 0 + && ( time == -1U || timestampCompare( asn_id->timestamp(), time ) > 0 )) + { + time = asn_id->timestamp(); + } + else if( asn_data->timestamp() != -1U + && ( time == -1U || timestampCompare( asn_data->timestamp(), time ) > 0 )) + { + time = asn_data->timestamp(); + } + } +// kdDebug( 1212 ) << "User timestamp, ASN:" << time << endl; + if( time == -1U ) + { // The window doesn't have any timestamp. + // If it's the first window for its application + // (i.e. there's no other window from the same app), + // use the _KDE_NET_WM_USER_CREATION_TIME trick. + // Otherwise, refuse activation of a window + // from already running application if this application + // is not the active one (unless focus stealing prevention is turned off). + Client* act = workspace()->mostRecentlyActivatedClient(); + if( act != NULL && !belongToSameApplication( act, this, true )) + { + bool first_window = true; + if( isTransient()) + { + if( act->hasTransient( this, true )) + ; // is transient for currently active window, even though it's not + // the same app (e.g. kcookiejar dialog) -> allow activation + else if( groupTransient() && + findClientInList( mainClients(), SameApplicationActiveHackPredicate( this )) == NULL ) + ; // standalone transient + else + first_window = false; + } + else + { + if( workspace()->findClient( SameApplicationActiveHackPredicate( this ))) + first_window = false; + } + // don't refuse if focus stealing prevention is turned off + if( !first_window && rules()->checkFSP( options->focusStealingPreventionLevel ) > 0 ) + { +// kdDebug( 1212 ) << "User timestamp, already exists:" << 0 << endl; + return 0; // refuse activation + } + } + // Creation time would just mess things up during session startup, + // as possibly many apps are started up at the same time. + // If there's no active window yet, no timestamp will be needed, + // as plain Workspace::allowClientActivation() will return true + // in such case. And if there's already active window, + // it's better not to activate the new one. + // Unless it was the active window at the time + // of session saving and there was no user interaction yet, + // this check will be done in manage(). + if( session ) + return -1U; + if( ignoreFocusStealing() && act != NULL ) + time = act->userTime(); + else + time = readUserCreationTime(); + } +// kdDebug( 1212 ) << "User timestamp, final:" << this << ":" << time << endl; + return time; + } + +Time Client::userTime() const + { + Time time = user_time; + if( time == 0 ) // doesn't want focus after showing + return 0; + assert( group() != NULL ); + if( time == -1U + || ( group()->userTime() != -1U + && timestampCompare( group()->userTime(), time ) > 0 )) + time = group()->userTime(); + return time; + } + +/*! + Sets the client's active state to \a act. + + This function does only change the visual appearance of the client, + it does not change the focus setting. Use + Workspace::activateClient() or Workspace::requestFocus() instead. + + If a client receives or looses the focus, it calls setActive() on + its own. + + */ +void Client::setActive( bool act, bool updateOpacity_) + { + if ( active == act ) + return; + active = act; + workspace()->setActiveClient( act ? this : NULL, Allowed ); + + if (updateOpacity_) updateOpacity(); + if (isModal() && transientFor()) + { + if (!act) transientFor()->updateOpacity(); + else if (!transientFor()->custom_opacity) transientFor()->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); + } + updateShadowSize(); + + if ( active ) + Notify::raise( Notify::Activate ); + + if( !active ) + cancelAutoRaise(); + + if( !active && shade_mode == ShadeActivated ) + setShade( ShadeNormal ); + + StackingUpdatesBlocker blocker( workspace()); + workspace()->updateClientLayer( this ); // active windows may get different layer + // TODO optimize? mainClients() may be a bit expensive + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + if( (*it)->isFullScreen()) // fullscreens go high even if their transient is active + workspace()->updateClientLayer( *it ); + if( decoration != NULL ) + decoration->activeChange(); + updateMouseGrab(); + updateUrgency(); // demand attention again if it's still urgent + } + +void Client::startupIdChanged() + { + KStartupInfoId asn_id; + KStartupInfoData asn_data; + bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); + if( !asn_valid ) + return; + // If the ASN contains desktop, move it to the desktop, otherwise move it to the current + // desktop (since the new ASN should make the window act like if it's a new application + // launched). However don't affect the window's desktop if it's set to be on all desktops. + int desktop = workspace()->currentDesktop(); + if( asn_data.desktop() != 0 ) + desktop = asn_data.desktop(); + if( !isOnAllDesktops()) + workspace()->sendClientToDesktop( this, desktop, true ); + Time timestamp = asn_id.timestamp(); + if( timestamp == 0 && asn_data.timestamp() != -1U ) + timestamp = asn_data.timestamp(); + if( timestamp != 0 ) + { + bool activate = workspace()->allowClientActivation( this, timestamp ); + if( asn_data.desktop() != 0 && !isOnCurrentDesktop()) + activate = false; // it was started on different desktop than current one + if( activate ) + workspace()->activateClient( this ); + else + demandAttention(); + } + } + +void Client::updateUrgency() + { + if( urgency ) + demandAttention(); + } + +void Client::shortcutActivated() + { + workspace()->activateClient( this, true ); // force + } + +//**************************************** +// Group +//**************************************** + +void Group::startupIdChanged() + { + KStartupInfoId asn_id; + KStartupInfoData asn_data; + bool asn_valid = workspace()->checkStartupNotification( leader_wid, asn_id, asn_data ); + if( !asn_valid ) + return; + if( asn_id.timestamp() != 0 && user_time != -1U + && timestampCompare( asn_id.timestamp(), user_time ) > 0 ) + { + user_time = asn_id.timestamp(); + } + else if( asn_data.timestamp() != -1U && user_time != -1U + && timestampCompare( asn_data.timestamp(), user_time ) > 0 ) + { + user_time = asn_data.timestamp(); + } + } + +void Group::updateUserTime( Time time ) + { // copy of Client::updateUserTime + if( time == CurrentTime ) + time = qt_x_time; + if( time != -1U + && ( user_time == CurrentTime + || timestampCompare( time, user_time ) > 0 )) // time > user_time + user_time = time; + } + +} // namespace diff --git a/kwin/atoms.cpp b/kwin/atoms.cpp new file mode 100644 index 000000000..08d95c9d6 --- /dev/null +++ b/kwin/atoms.cpp @@ -0,0 +1,108 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + + +#include <qapplication.h> +#include "atoms.h" +#include <assert.h> + +namespace KWinInternal +{ + +Atoms::Atoms() + { + + const int max = 50; + Atom* atoms[max]; + char* names[max]; + Atom atoms_return[max]; + int n = 0; + + atoms[n] = &kwin_running; + names[n++] = (char *) "KWIN_RUNNING"; + + atoms[n] = &wm_protocols; + names[n++] = (char *) "WM_PROTOCOLS"; + + atoms[n] = &wm_delete_window; + names[n++] = (char *) "WM_DELETE_WINDOW"; + + atoms[n] = &wm_take_focus; + names[n++] = (char *) "WM_TAKE_FOCUS"; + + atoms[n] = &wm_change_state; + names[n++] = (char *) "WM_CHANGE_STATE"; + + atoms[n] = &wm_client_leader; + names[n++] = (char *) "WM_CLIENT_LEADER"; + + atoms[n] = &motif_wm_hints; + names[n++] = (char *) "_MOTIF_WM_HINTS"; + + atoms[n] = &net_wm_context_help; + names[n++] = (char *) "_NET_WM_CONTEXT_HELP"; + + atoms[n] = &net_wm_ping; + names[n++] = (char *) "_NET_WM_PING"; + + atoms[n] = &kde_wm_change_state; + names[n++] = (char *) "_KDE_WM_CHANGE_STATE"; + + atoms[n] = &net_wm_user_time; + names[n++] = (char *) "_NET_WM_USER_TIME"; + atoms[n] = &kde_net_wm_user_creation_time; + names[n++] = (char *) "_KDE_NET_WM_USER_CREATION_TIME"; + + atoms[n] = &kde_system_tray_embedding; + names[n++] = (char*) "_KDE_SYSTEM_TRAY_EMBEDDING"; + + atoms[n] = &net_wm_take_activity; + names[n++] = (char*) "_NET_WM_TAKE_ACTIVITY"; + + atoms[n] = &net_wm_window_opacity; + names[n++] = (char*) "_KDE_WM_WINDOW_OPACITY"; + + atoms[n] = &net_wm_window_shadow; + names[n++] = (char*) "_KDE_WM_WINDOW_SHADOW"; + + atoms[n] = &net_wm_window_shade; + names[n++] = (char*) "_KDE_WM_WINDOW_SHADE"; + + atoms[n] = &net_wm_window_shapable; + names[n++] = (char*) "_KDE_WM_WINDOW_SHAPABLE"; + + atoms[n] = &net_wm_window_decohash; + names[n++] = (char*) "_KDE_WM_WINDOW_DECOHASH"; + + Atom fake; + atoms[n] = &fake; + names[n++] = (char *) "_DT_SM_WINDOW_INFO"; + atoms[n] = &fake; + names[n++] = (char *) "_MOTIF_WM_INFO"; // #172028 + + atoms[n] = &xdnd_aware; + names[n++] = (char*) "XdndAware"; + atoms[n] = &xdnd_position; + names[n++] = (char*) "XdndPosition"; + + atoms[n] = &net_frame_extents; + names[n++] = (char*) "_NET_FRAME_EXTENTS"; + atoms[n] = &kde_net_wm_frame_strut; + names[n++] = (char*) "_KDE_NET_WM_FRAME_STRUT"; + + assert( n <= max ); + + XInternAtoms( qt_xdisplay(), names, n, FALSE, atoms_return ); + for (int i = 0; i < n; i++ ) + *atoms[i] = atoms_return[i]; + } + +} // namespace diff --git a/kwin/atoms.h b/kwin/atoms.h new file mode 100644 index 000000000..caed47b79 --- /dev/null +++ b/kwin/atoms.h @@ -0,0 +1,56 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_ATOMS_H +#define KWIN_ATOMS_H +#include <X11/Xlib.h> + +namespace KWinInternal +{ + +class Atoms + { + public: + Atoms(); + + Atom kwin_running; + + Atom wm_protocols; + Atom wm_delete_window; + Atom wm_take_focus; + Atom wm_change_state; + Atom wm_client_leader; + + Atom motif_wm_hints; + Atom net_wm_context_help; + Atom net_wm_ping; + Atom kde_wm_change_state; + Atom net_wm_user_time; + Atom kde_net_wm_user_creation_time; + Atom kde_system_tray_embedding; + Atom net_wm_take_activity; + Atom net_wm_window_opacity; + Atom net_wm_window_shadow; + Atom net_wm_window_shade; + Atom net_wm_window_shapable; + Atom net_wm_window_decohash; + Atom xdnd_aware; + Atom xdnd_position; + Atom net_frame_extents; + Atom kde_net_wm_frame_strut; + }; + + +extern Atoms* atoms; + +} // namespace + +#endif diff --git a/kwin/bridge.cpp b/kwin/bridge.cpp new file mode 100644 index 000000000..6b37c0eaa --- /dev/null +++ b/kwin/bridge.cpp @@ -0,0 +1,206 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "bridge.h" + +#include "client.h" +#include "options.h" + +namespace KWinInternal +{ + +Bridge::Bridge( Client* cl ) + : c( cl ) + { + } + +#define BRIDGE_HELPER( rettype, prototype, args1, args2, cst ) \ +rettype Bridge::prototype ( args1 ) cst \ + { \ + return c->prototype( args2 ); \ + } + +BRIDGE_HELPER( bool, isActive,,, const ) +BRIDGE_HELPER( bool, isCloseable,,, const ) +BRIDGE_HELPER( bool, isMaximizable,,, const ) +BRIDGE_HELPER( Bridge::MaximizeMode, maximizeMode,,, const ) +BRIDGE_HELPER( bool, isMinimizable,,, const ) +BRIDGE_HELPER( bool, providesContextHelp,,, const ) +BRIDGE_HELPER( int, desktop,,, const ) +BRIDGE_HELPER( bool, isModal,,, const ) +BRIDGE_HELPER( bool, isShadeable,,, const ) +BRIDGE_HELPER( bool, isShade,,, const ) +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,,, ) +BRIDGE_HELPER( void, maximize, MaximizeMode m, m, ) +BRIDGE_HELPER( void, minimize,,, ) +BRIDGE_HELPER( void, showContextHelp,,, ) +BRIDGE_HELPER( void, setDesktop, int desktop, desktop, ) + +void Bridge::setKeepAbove( bool set ) + { + if( c->keepAbove() != set ) + c->workspace()->performWindowOperation( c, KeepAboveOp ); + } + +void Bridge::setKeepBelow( bool set ) + { + if( c->keepBelow() != set ) + c->workspace()->performWindowOperation( c, KeepBelowOp ); + } + +NET::WindowType Bridge::windowType( unsigned long supported_types ) const + { + return c->windowType( false, supported_types ); + } + +QIconSet Bridge::icon() const + { + return QIconSet( c->miniIcon(), c->icon()); + } + +bool Bridge::isSetShade() const + { + return c->shadeMode() != ShadeNone; + } + +void Bridge::showWindowMenu( QPoint p ) + { + c->workspace()->showWindowMenu( p, c ); + } + +void Bridge::showWindowMenu( const QRect &p ) + { + c->workspace()->showWindowMenu( p, c ); + } + +void Bridge::performWindowOperation( WindowOperation op ) + { + c->workspace()->performWindowOperation( c, op ); + } + +void Bridge::setMask( const QRegion& r, int mode ) + { + c->setMask( r, mode ); + } + +bool Bridge::isPreview() const + { + return false; + } + +QRect Bridge::iconGeometry() const + { + NETRect r = c->info->iconGeometry(); + return QRect( r.pos.x, r.pos.y, r.size.width, r.size.height ); + } + +QWidget* Bridge::workspaceWidget() const + { + return c->workspace()->desktopWidget(); + } + +WId Bridge::windowId() const + { + return c->window(); + } + +void Bridge::titlebarDblClickOperation() + { + c->workspace()->performWindowOperation( c, options->operationTitlebarDblClick()); + } + +void Bridge::titlebarMouseWheelOperation( int delta ) + { + c->performMouseCommand( options->operationTitlebarMouseWheel( delta ), QCursor::pos()); + } + +void Bridge::setShade( bool set ) + { + c->setShade( set ? ShadeNormal : ShadeNone ); + } + +int Bridge::currentDesktop() const + { + return c->workspace()->currentDesktop(); + } + +QWidget* Bridge::initialParentWidget() const + { + return NULL; + } + +Qt::WFlags Bridge::initialWFlags() const + { + return 0; + } + +void Bridge::helperShowHide( bool show ) + { + if( show ) + c->rawShow(); + else + c->rawHide(); + } + +QRegion Bridge::unobscuredRegion( const QRegion& r ) const + { + QRegion reg( r ); + const ClientList stacking_order = c->workspace()->stackingOrder(); + ClientList::ConstIterator it = stacking_order.find( c ); + ++it; + for(; + it != stacking_order.end(); + ++it ) + { + if( !(*it)->isShown( true )) + continue; // these don't obscure the window + if( c->isOnAllDesktops()) + { + if( !(*it)->isOnCurrentDesktop()) + continue; + } + else + { + if( !(*it)->isOnDesktop( c->desktop())) + continue; + } + /* the clients all have their mask-regions in local coords + so we have to translate them to a shared coord system + we choose ours */ + int dx = (*it)->x() - c->x(); + int dy = (*it)->y() - c->y(); + QRegion creg = (*it)->mask(); + creg.translate(dx, dy); + reg -= creg; + if (reg.isEmpty()) + { + // early out, we are completely obscured + break; + } + } + return reg; + } + +void Bridge::grabXServer( bool grab ) + { + if( grab ) + KWinInternal::grabXServer(); + else + KWinInternal::ungrabXServer(); + } + +} // namespace diff --git a/kwin/bridge.h b/kwin/bridge.h new file mode 100644 index 000000000..155970f04 --- /dev/null +++ b/kwin/bridge.h @@ -0,0 +1,75 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_BRIDGE_H +#define KWIN_BRIDGE_H + +#include <kdecoration_p.h> + +namespace KWinInternal +{ + +class Client; + +class Bridge : public KDecorationBridge + { + public: + Bridge( Client* cl ); + virtual bool isActive() const; + virtual bool isCloseable() const; + virtual bool isMaximizable() const; + virtual MaximizeMode maximizeMode() const; + virtual bool isMinimizable() const; + virtual bool providesContextHelp() const; + virtual int desktop() const; + virtual bool isModal() const; + virtual bool isShadeable() const; + virtual bool isShade() const; + virtual bool isSetShade() const; + virtual bool keepAbove() const; + virtual bool keepBelow() const; + virtual bool isMovable() const; + virtual bool isResizable() const; + virtual NET::WindowType windowType( unsigned long supported_types ) const; + virtual QIconSet icon() const; + virtual QString caption() const; + virtual void processMousePressEvent( QMouseEvent* ); + virtual void showWindowMenu( QPoint ); + virtual void showWindowMenu( const QRect & ); + virtual void performWindowOperation( WindowOperation ); + virtual void setMask( const QRegion&, int ); + virtual bool isPreview() const; + virtual QRect geometry() const; + virtual QRect iconGeometry() const; + virtual QRegion unobscuredRegion( const QRegion& r ) const; + virtual QWidget* workspaceWidget() const; + virtual WId windowId() const; + virtual void closeWindow(); + virtual void maximize( MaximizeMode mode ); + virtual void minimize(); + virtual void showContextHelp(); + virtual void setDesktop( int desktop ); + virtual void titlebarDblClickOperation(); + virtual void titlebarMouseWheelOperation( int delta ); + virtual void setShade( bool set ); + virtual void setKeepAbove( bool ); + virtual void setKeepBelow( bool ); + virtual int currentDesktop() const; + virtual QWidget* initialParentWidget() const; + virtual Qt::WFlags initialWFlags() const; + virtual void helperShowHide( bool show ); + virtual void grabXServer( bool grab ); + private: + Client* c; + }; + +} // namespace + +#endif diff --git a/kwin/client.cpp b/kwin/client.cpp new file mode 100644 index 000000000..fe8c59c58 --- /dev/null +++ b/kwin/client.cpp @@ -0,0 +1,2165 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "client.h" + +#include <qapplication.h> +#include <qpainter.h> +#include <qdatetime.h> +#include <kprocess.h> +#include <unistd.h> +#include <kstandarddirs.h> +#include <qwhatsthis.h> +#include <kwin.h> +#include <kiconloader.h> +#include <stdlib.h> + +#include "bridge.h" +#include "group.h" +#include "workspace.h" +#include "atoms.h" +#include "notifications.h" +#include "rules.h" + +#include <X11/extensions/shape.h> + +// put all externs before the namespace statement to allow the linker +// to resolve them properly + +extern Atom qt_wm_state; +extern Time qt_x_time; +extern Atom qt_window_role; +extern Atom qt_sm_client_id; + +namespace KWinInternal +{ + +/* + + Creating a client: + - only by calling Workspace::createClient() + - it creates a new client and calls manage() for it + + Destroying a client: + - destroyClient() - only when the window itself has been destroyed + - releaseWindow() - the window is kept, only the client itself is destroyed + +*/ + + +/*! + \class Client client.h + + \brief The Client class encapsulates a window decoration frame. + +*/ + +/*! + This ctor is "dumb" - it only initializes data. All the real initialization + is done in manage(). + */ +Client::Client( Workspace *ws ) + : QObject( NULL ), + client( None ), + wrapper( None ), + frame( None ), + decoration( NULL ), + wspace( ws ), + bridge( new Bridge( this )), + move_faked_activity( false ), + move_resize_grab_window( None ), + transient_for( NULL ), + transient_for_id( None ), + original_transient_for_id( None ), + in_group( NULL ), + window_group( None ), + in_layer( UnknownLayer ), + ping_timer( NULL ), + process_killer( NULL ), + user_time( CurrentTime ), // not known yet + allowed_actions( 0 ), + postpone_geometry_updates( 0 ), + pending_geometry_update( false ), + shade_geometry_change( false ), + border_left( 0 ), + border_right( 0 ), + border_top( 0 ), + border_bottom( 0 ), + opacity_( 0 ), + demandAttentionKNotifyTimer( NULL ) +// SELI do all as initialization + { + autoRaiseTimer = 0; + shadeHoverTimer = 0; + + // set the initial mapping state + mapping_state = WithdrawnState; + desk = 0; // no desktop yet + + mode = PositionCenter; + buttonDown = FALSE; + moveResizeMode = FALSE; + + info = NULL; + + shade_mode = ShadeNone; + active = FALSE; + deleting = false; + keep_above = FALSE; + keep_below = FALSE; + is_shape = FALSE; + motif_noborder = false; + motif_may_move = TRUE; + motif_may_resize = TRUE; + motif_may_close = TRUE; + fullscreen_mode = FullScreenNone; + skip_taskbar = FALSE; + original_skip_taskbar = false; + minimized = false; + hidden = false; + modal = false; + noborder = false; + user_noborder = false; + urgency = false; + ignore_focus_stealing = false; + demands_attention = false; + check_active_modal = false; + + Pdeletewindow = 0; + Ptakefocus = 0; + Ptakeactivity = 0; + Pcontexthelp = 0; + Pping = 0; + input = FALSE; + skip_pager = FALSE; + + max_mode = MaximizeRestore; + maxmode_restore = MaximizeRestore; + + cmap = None; + + frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) + client_size = QSize( 100, 100 ); + custom_opacity = false; + rule_opacity_active = 0;; //translucency rules + rule_opacity_inactive = 0; //dito. + + // SELI initialize xsizehints?? + } + +/*! + "Dumb" destructor. + */ +Client::~Client() + { + assert(!moveResizeMode); + assert( client == None ); + assert( frame == None && wrapper == None ); + assert( decoration == NULL ); + assert( postpone_geometry_updates == 0 ); + assert( !check_active_modal ); + delete info; + delete bridge; + } + +// use destroyClient() or releaseWindow(), Client instances cannot be deleted directly +void Client::deleteClient( Client* c, allowed_t ) + { + delete c; + } + +/*! + Releases the window. The client has done its job and the window is still existing. + */ +void Client::releaseWindow( bool on_shutdown ) + { + assert( !deleting ); + deleting = true; + workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules + StackingUpdatesBlocker blocker( workspace()); + if (!custom_opacity) setOpacity(FALSE); + if (moveResizeMode) + leaveMoveResize(); + finishWindowRules(); + ++postpone_geometry_updates; + // grab X during the release to make removing of properties, setting to withdrawn state + // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) + grabXServer(); + setMappingState( WithdrawnState ); + setModal( false ); // otherwise its mainwindow wouldn't get focus + hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) + if( !on_shutdown ) + workspace()->clientHidden( this ); + XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect + destroyDecoration(); + cleanGrouping(); + if( !on_shutdown ) + { + workspace()->removeClient( this, Allowed ); + // only when the window is being unmapped, not when closing down KWin + // (NETWM sections 5.5,5.7) + info->setDesktop( 0 ); + desk = 0; + info->setState( 0, info->state()); // reset all state flags + } + XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time); + XDeleteProperty( qt_xdisplay(), client, atoms->net_frame_extents ); + XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_frame_strut ); + XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y()); + XRemoveFromSaveSet( qt_xdisplay(), client ); + XSelectInput( qt_xdisplay(), client, NoEventMask ); + if( on_shutdown ) + { // map the window, so it can be found after another WM is started + XMapWindow( qt_xdisplay(), client ); + // TODO preserve minimized, shaded etc. state? + } + else + { + // Make sure it's not mapped if the app unmapped it (#65279). The app + // may do map+unmap before we initially map the window by calling rawShow() from manage(). + XUnmapWindow( qt_xdisplay(), client ); + } + client = None; + XDestroyWindow( qt_xdisplay(), wrapper ); + wrapper = None; + XDestroyWindow( qt_xdisplay(), frame ); + frame = None; + --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + checkNonExistentClients(); + deleteClient( this, Allowed ); + ungrabXServer(); + } + +// like releaseWindow(), but this one is called when the window has been already destroyed +// (e.g. the application closed it) +void Client::destroyClient() + { + assert( !deleting ); + deleting = true; + workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules + StackingUpdatesBlocker blocker( workspace()); + if (moveResizeMode) + leaveMoveResize(); + finishWindowRules(); + ++postpone_geometry_updates; + setModal( false ); + hidden = true; // so that it's not considered visible anymore + workspace()->clientHidden( this ); + destroyDecoration(); + cleanGrouping(); + workspace()->removeClient( this, Allowed ); + client = None; // invalidate + XDestroyWindow( qt_xdisplay(), wrapper ); + wrapper = None; + XDestroyWindow( qt_xdisplay(), frame ); + frame = None; + --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + checkNonExistentClients(); + deleteClient( this, Allowed ); + } + +void Client::updateDecoration( bool check_workspace_pos, bool force ) + { + if( !force && (( decoration == NULL && noBorder()) + || ( decoration != NULL && !noBorder()))) + return; + bool do_show = false; + postponeGeometryUpdates( true ); + if( force ) + destroyDecoration(); + if( !noBorder()) + { + setMask( QRegion()); // reset shape mask + decoration = workspace()->createDecoration( bridge ); + // TODO check decoration's minimum size? + decoration->init(); + decoration->widget()->installEventFilter( this ); + XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 ); + decoration->widget()->lower(); + decoration->borders( border_left, border_right, border_top, border_bottom ); + options->onlyDecoTranslucent ? + setDecoHashProperty(border_top, border_right, border_bottom, border_left): + unsetDecoHashProperty(); + int save_workarea_diff_x = workarea_diff_x; + int save_workarea_diff_y = workarea_diff_y; + move( calculateGravitation( false )); + plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); + workarea_diff_x = save_workarea_diff_x; + workarea_diff_y = save_workarea_diff_y; + do_show = true; + } + else + destroyDecoration(); + if( check_workspace_pos ) + checkWorkspacePosition(); + postponeGeometryUpdates( false ); + if( do_show ) + decoration->widget()->show(); + updateFrameExtents(); + } + +void Client::destroyDecoration() + { + if( decoration != NULL ) + { + delete decoration; + decoration = NULL; + QPoint grav = calculateGravitation( true ); + border_left = border_right = border_top = border_bottom = 0; + setMask( QRegion()); // reset shape mask + int save_workarea_diff_x = workarea_diff_x; + int save_workarea_diff_y = workarea_diff_y; + plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); + move( grav ); + workarea_diff_x = save_workarea_diff_x; + workarea_diff_y = save_workarea_diff_y; + } + } + +void Client::checkBorderSizes() + { + if( decoration == NULL ) + return; + int new_left, new_right, new_top, new_bottom; + decoration->borders( new_left, new_right, new_top, new_bottom ); + if( new_left == border_left && new_right == border_right + && new_top == border_top && new_bottom == border_bottom ) + return; + GeometryUpdatesPostponer blocker( this ); + move( calculateGravitation( true )); + border_left = new_left; + border_right = new_right; + border_top = new_top; + border_bottom = new_bottom; + if (border_left != new_left || + border_right != new_right || + border_top != new_top || + border_bottom != new_bottom) + options->onlyDecoTranslucent ? + setDecoHashProperty(new_top, new_right, new_bottom, new_left): + unsetDecoHashProperty(); + move( calculateGravitation( false )); + plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); + checkWorkspacePosition(); + } + +void Client::detectNoBorder() + { + if( Shape::hasShape( window())) + { + noborder = true; + return; + } + switch( windowType()) + { + case NET::Desktop : + case NET::Dock : + case NET::TopMenu : + case NET::Splash : + noborder = true; + break; + case NET::Unknown : + case NET::Normal : + case NET::Toolbar : + case NET::Menu : + case NET::Dialog : + case NET::Utility : + noborder = false; + break; + default: + assert( false ); + } + // NET::Override is some strange beast without clear definition, usually + // just meaning "noborder", so let's treat it only as such flag, and ignore it as + // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it) + if( info->windowType( SUPPORTED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override ) + noborder = true; + } + +void Client::detectShapable() + { + if( Shape::hasShape( window())) + return; + switch( windowType()) + { + case NET::Desktop : + case NET::Dock : + case NET::TopMenu : + case NET::Splash : + break; + case NET::Unknown : + case NET::Normal : + case NET::Toolbar : + case NET::Menu : + case NET::Dialog : + case NET::Utility : + setShapable(FALSE); + break; + default: + assert( false ); + } + } + +void Client::updateFrameExtents() + { + NETStrut strut; + strut.left = border_left; + strut.right = border_right; + strut.top = border_top; + strut.bottom = border_bottom; + info->setFrameExtents( strut ); + } + +// Resizes the decoration, and makes sure the decoration widget gets resize event +// even if the size hasn't changed. This is needed to make sure the decoration +// re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, +// the decoration may turn on/off some borders, but the actual size +// of the decoration stays the same). +void Client::resizeDecoration( const QSize& s ) + { + if( decoration == NULL ) + return; + QSize oldsize = decoration->widget()->size(); + decoration->resize( s ); + if( oldsize == s ) + { + QResizeEvent e( s, oldsize ); + QApplication::sendEvent( decoration->widget(), &e ); + } + } + +bool Client::noBorder() const + { + return noborder || isFullScreen() || user_noborder || motif_noborder; + } + +bool Client::userCanSetNoBorder() const + { + return !noborder && !isFullScreen() && !isShade(); + } + +bool Client::isUserNoBorder() const + { + return user_noborder; + } + +void Client::setUserNoBorder( bool set ) + { + if( !userCanSetNoBorder()) + return; + set = rules()->checkNoBorder( set ); + if( user_noborder == set ) + return; + user_noborder = set; + updateDecoration( true, false ); + updateWindowRules(); + } + +void Client::updateShape() + { + // workaround for #19644 - shaped windows shouldn't have decoration + if( shape() && !noBorder()) + { + noborder = true; + updateDecoration( true ); + } + if ( shape() ) + { + XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding, + clientPos().x(), clientPos().y(), + window(), ShapeBounding, ShapeSet); + setShapable(TRUE); + } + // !shape() mask setting is done in setMask() when the decoration + // calls it or when the decoration is created/destroyed + + if( Shape::version() >= 0x11 ) // 1.1, has input shape support + { // There appears to be no way to find out if a window has input + // shape set or not, so always propagate the input shape + // (it's the same like the bounding shape by default). + // Also, build the shape using a helper window, not directly + // in the frame window, because the sequence set-shape-to-frame, + // remove-shape-of-client, add-input-shape-of-client has the problem + // that after the second step there's a hole in the input shape + // until the real shape of the client is added and that can make + // the window lose focus (which is a problem with mouse focus policies) + static Window helper_window = None; + if( helper_window == None ) + helper_window = XCreateSimpleWindow( qt_xdisplay(), qt_xrootwin(), + 0, 0, 1, 1, 0, 0, 0 ); + XResizeWindow( qt_xdisplay(), helper_window, width(), height()); + XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 0, 0, + frameId(), ShapeBounding, ShapeSet ); + XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, + clientPos().x(), clientPos().y(), + window(), ShapeBounding, ShapeSubtract ); + XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, + clientPos().x(), clientPos().y(), + window(), ShapeInput, ShapeUnion ); + XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput, 0, 0, + helper_window, ShapeInput, ShapeSet ); + } + } + +void Client::setMask( const QRegion& reg, int mode ) + { + _mask = reg; + if( reg.isNull()) + XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, + None, ShapeSet ); + else if( mode == X::Unsorted ) + XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, + reg.handle(), ShapeSet ); + else + { + QMemArray< QRect > rects = reg.rects(); + XRectangle* xrects = new XRectangle[ rects.count() ]; + for( unsigned int i = 0; + i < rects.count(); + ++i ) + { + xrects[ i ].x = rects[ i ].x(); + xrects[ i ].y = rects[ i ].y(); + xrects[ i ].width = rects[ i ].width(); + xrects[ i ].height = rects[ i ].height(); + } + XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, + xrects, rects.count(), ShapeSet, mode ); + delete[] xrects; + } + updateShape(); + } + +QRegion Client::mask() const + { + if( _mask.isEmpty()) + return QRegion( 0, 0, width(), height()); + return _mask; + } + +void Client::setShapable(bool b) + { + long tmp = b?1:0; + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L); + } + +void Client::hideClient( bool hide ) + { + if( hidden == hide ) + return; + hidden = hide; + updateVisibility(); + } + +/* + Returns whether the window is minimizable or not + */ +bool Client::isMinimizable() const + { + if( isSpecialWindow()) + return false; + if( isTransient()) + { // #66868 - let other xmms windows be minimized when the mainwindow is minimized + bool shown_mainwindow = false; + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + { + if( (*it)->isShown( true )) + shown_mainwindow = true; + } + if( !shown_mainwindow ) + return true; + } + // this is here because kicker's taskbar doesn't provide separate entries + // for windows with an explicitly given parent + // TODO perhaps this should be redone + if( transientFor() != NULL ) + return false; + if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ? + return false; + return true; + } + +/*! + Minimizes this client plus its transients + */ +void Client::minimize( bool avoid_animation ) + { + if ( !isMinimizable() || isMinimized()) + return; + + Notify::raise( Notify::Minimize ); + + // SELI mainClients().isEmpty() ??? - and in unminimize() too + if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation ) + animateMinimizeOrUnminimize( true ); // was visible or shaded + + minimized = true; + + updateVisibility(); + updateAllowedActions(); + workspace()->updateMinimizedOfTransients( this ); + updateWindowRules(); + workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); + } + +void Client::unminimize( bool avoid_animation ) + { + if( !isMinimized()) + return; + + Notify::raise( Notify::UnMinimize ); + minimized = false; + if( isOnCurrentDesktop() && isShown( true )) + { + if( mainClients().isEmpty() && !avoid_animation ) + animateMinimizeOrUnminimize( FALSE ); + } + updateVisibility(); + updateAllowedActions(); + workspace()->updateMinimizedOfTransients( this ); + updateWindowRules(); + } + +extern bool blockAnimation; + +void Client::animateMinimizeOrUnminimize( bool minimize ) + { + if ( blockAnimation ) + return; + if ( !options->animateMinimize ) + return; + + if( decoration != NULL && decoration->animateMinimize( minimize )) + return; // decoration did it + + // the function is a bit tricky since it will ensure that an + // animation action needs always the same time regardless of the + // performance of the machine or the X-Server. + + float lf,rf,tf,bf,step; + + int speed = options->animateMinimizeSpeed; + if ( speed > 10 ) + speed = 10; + if ( speed < 0 ) + speed = 0; + + step = 40. * (11 - speed ); + + NETRect r = info->iconGeometry(); + QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); + if ( !icongeom.isValid() ) + return; + + QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); + + QRect before, after; + if ( minimize ) + { + before = QRect( x(), y(), width(), pm.height() ); + after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); + } + else + { + before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); + after = QRect( x(), y(), width(), pm.height() ); + } + + lf = (after.left() - before.left())/step; + rf = (after.right() - before.right())/step; + tf = (after.top() - before.top())/step; + bf = (after.bottom() - before.bottom())/step; + + grabXServer(); + + QRect area = before; + QRect area2; + QPixmap pm2; + + QTime t; + t.start(); + float diff; + + QPainter p ( workspace()->desktopWidget() ); + bool need_to_clear = FALSE; + QPixmap pm3; + do + { + if (area2 != area) + { + pm = animationPixmap( area.width() ); + pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() ); + p.drawPixmap( area.x(), area.y(), pm ); + if ( need_to_clear ) + { + p.drawPixmap( area2.x(), area2.y(), pm3 ); + need_to_clear = FALSE; + } + area2 = area; + } + XFlush(qt_xdisplay()); + XSync( qt_xdisplay(), FALSE ); + diff = t.elapsed(); + if (diff > step) + diff = step; + area.setLeft(before.left() + int(diff*lf)); + area.setRight(before.right() + int(diff*rf)); + area.setTop(before.top() + int(diff*tf)); + area.setBottom(before.bottom() + int(diff*bf)); + if (area2 != area ) + { + if ( area2.intersects( area ) ) + p.drawPixmap( area2.x(), area2.y(), pm2 ); + else + { // no overlap, we can clear later to avoid flicker + pm3 = pm2; + need_to_clear = TRUE; + } + } + } while ( t.elapsed() < step); + if (area2 == area || need_to_clear ) + p.drawPixmap( area2.x(), area2.y(), pm2 ); + + p.end(); + ungrabXServer(); + } + + +/*! + The pixmap shown during (un)minimalization animation + */ +QPixmap Client::animationPixmap( int w ) + { + QFont font = options->font(isActive()); + QFontMetrics fm( font ); + QPixmap pm( w, fm.lineSpacing() ); + pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); + QPainter p( &pm ); + p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); + p.setFont(options->font(isActive())); + p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); + return pm; + } + + +bool Client::isShadeable() const + { + return !isSpecialWindow() && !noBorder(); + } + +void Client::setShade( ShadeMode mode ) + { + if( !isShadeable()) + return; + mode = rules()->checkShade( mode ); + if( shade_mode == mode ) + return; + bool was_shade = isShade(); + ShadeMode was_shade_mode = shade_mode; + shade_mode = mode; + if( was_shade == isShade()) + { + if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes + decoration->shadeChange(); + return; // no real change in shaded state + } + + if( shade_mode == ShadeNormal ) + { + if ( isShown( true ) && isOnCurrentDesktop()) + Notify::raise( Notify::ShadeUp ); + } + else if( shade_mode == ShadeNone ) + { + if( isShown( true ) && isOnCurrentDesktop()) + Notify::raise( Notify::ShadeDown ); + } + + assert( decoration != NULL ); // noborder windows can't be shaded + GeometryUpdatesPostponer blocker( this ); + // decorations may turn off some borders when shaded + decoration->borders( border_left, border_right, border_top, border_bottom ); + + int as = options->animateShade? 10 : 1; +// TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere + if ( isShade()) + { // shade_mode == ShadeNormal + // we're about to shade, texx xcompmgr to prepare + long _shade = 1; + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); + // shade + int h = height(); + shade_geometry_change = true; + QSize s( sizeForClientSize( QSize( clientSize()))); + s.setHeight( border_top + border_bottom ); + XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify + XUnmapWindow( qt_xdisplay(), wrapper ); + XUnmapWindow( qt_xdisplay(), client ); + XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); + //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it + //done xcompmgr workaround +// FRAME repaint( FALSE ); +// bool wasStaticContents = testWFlags( WStaticContents ); +// setWFlags( WStaticContents ); + int step = QMAX( 4, QABS( h - s.height() ) / as )+1; + do + { + h -= step; + XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); + resizeDecoration( QSize( s.width(), h )); + QApplication::syncX(); + } while ( h > s.height() + step ); +// if ( !wasStaticContents ) +// clearWFlags( WStaticContents ); + plainResize( s ); + shade_geometry_change = false; + if( isActive()) + { + if( was_shade_mode == ShadeHover ) + workspace()->activateNextClient( this ); + else + workspace()->focusToNull(); + } + // tell xcompmgr shade's done + _shade = 2; + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); + } + else + { + int h = height(); + shade_geometry_change = true; + QSize s( sizeForClientSize( clientSize())); +// FRAME bool wasStaticContents = testWFlags( WStaticContents ); +// setWFlags( WStaticContents ); + int step = QMAX( 4, QABS( h - s.height() ) / as )+1; + do + { + h += step; + XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); + resizeDecoration( QSize( s.width(), h )); + // assume a border + // we do not have time to wait for X to send us paint events +// FRAME repaint( 0, h - step-5, width(), step+5, TRUE); + QApplication::syncX(); + } while ( h < s.height() - step ); +// if ( !wasStaticContents ) +// clearWFlags( WStaticContents ); + shade_geometry_change = false; + plainResize( s ); + if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) + setActive( TRUE ); + XMapWindow( qt_xdisplay(), wrapperId()); + XMapWindow( qt_xdisplay(), window()); + XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade); + if ( isActive() ) + workspace()->requestFocus( this ); + } + checkMaximizeGeometry(); + info->setState( isShade() ? NET::Shaded : 0, NET::Shaded ); + info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); + updateVisibility(); + updateAllowedActions(); + workspace()->updateMinimizedOfTransients( this ); + decoration->shadeChange(); + updateWindowRules(); + } + +void Client::shadeHover() + { + setShade( ShadeHover ); + cancelShadeHover(); + } + +void Client::cancelShadeHover() + { + delete shadeHoverTimer; + shadeHoverTimer = 0; + } + +void Client::toggleShade() + { + // if the mode is ShadeHover or ShadeActive, cancel shade too + setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone ); + } + +void Client::updateVisibility() + { + if( deleting ) + return; + bool show = true; + if( hidden ) + { + setMappingState( IconicState ); + info->setState( NET::Hidden, NET::Hidden ); + setSkipTaskbar( true, false ); // also hide from taskbar + rawHide(); + show = false; + } + else + { + setSkipTaskbar( original_skip_taskbar, false ); + } + if( minimized ) + { + setMappingState( IconicState ); + info->setState( NET::Hidden, NET::Hidden ); + rawHide(); + show = false; + } + if( show ) + info->setState( 0, NET::Hidden ); + if( !isOnCurrentDesktop()) + { + setMappingState( IconicState ); + rawHide(); + show = false; + } + if( show ) + { + bool belongs_to_desktop = false; + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + if( (*it)->isDesktop()) + { + belongs_to_desktop = true; + break; + } + if( !belongs_to_desktop && workspace()->showingDesktop()) + workspace()->resetShowingDesktop( true ); + if( isShade()) + setMappingState( IconicState ); + else + setMappingState( NormalState ); + rawShow(); + } + } + +/*! + Sets the client window's mapping state. Possible values are + WithdrawnState, IconicState, NormalState. + */ +void Client::setMappingState(int s) + { + assert( client != None ); + assert( !deleting || s == WithdrawnState ); + if( mapping_state == s ) + return; + bool was_unmanaged = ( mapping_state == WithdrawnState ); + mapping_state = s; + if( mapping_state == WithdrawnState ) + { + XDeleteProperty( qt_xdisplay(), window(), qt_wm_state ); + return; + } + assert( s == NormalState || s == IconicState ); + + unsigned long data[2]; + data[0] = (unsigned long) s; + data[1] = (unsigned long) None; + XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32, + PropModeReplace, (unsigned char *)data, 2); + + if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry + postponeGeometryUpdates( false ); + } + +/*! + Reimplemented to map the managed window in the window wrapper. + Proper mapping state should be set before showing the client. + */ +void Client::rawShow() + { + if( decoration != NULL ) + decoration->widget()->show(); // not really necessary, but let it know the state + XMapWindow( qt_xdisplay(), frame ); + if( !isShade()) + { + XMapWindow( qt_xdisplay(), wrapper ); + XMapWindow( qt_xdisplay(), client ); + } + } + +/*! + Reimplemented to unmap the managed window in the window wrapper. + Also informs the workspace. + Proper mapping state should be set before hiding the client. +*/ +void Client::rawHide() + { +// Here it may look like a race condition, as some other client might try to unmap +// the window between these two XSelectInput() calls. However, they're supposed to +// use XWithdrawWindow(), which also sends a synthetic event to the root window, +// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify +// will be missed is also very minimal, so I don't think it's needed to grab the server +// here. + XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify + XUnmapWindow( qt_xdisplay(), frame ); + XUnmapWindow( qt_xdisplay(), wrapper ); + XUnmapWindow( qt_xdisplay(), client ); + XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); + if( decoration != NULL ) + decoration->widget()->hide(); // not really necessary, but let it know the state + workspace()->clientHidden( this ); + } + +void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) + { + XEvent ev; + long mask; + + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = a; + ev.xclient.format = 32; + ev.xclient.data.l[0] = protocol; + ev.xclient.data.l[1] = qt_x_time; + ev.xclient.data.l[2] = data1; + ev.xclient.data.l[3] = data2; + ev.xclient.data.l[4] = data3; + mask = 0L; + if (w == qt_xrootwin()) + mask = SubstructureRedirectMask; /* magic! */ + XSendEvent(qt_xdisplay(), w, False, mask, &ev); + } + +/* + Returns whether the window may be closed (have a close button) + */ +bool Client::isCloseable() const + { + return rules()->checkCloseable( motif_may_close && !isSpecialWindow()); + } + +/*! + Closes the window by either sending a delete_window message or + using XKill. + */ +void Client::closeWindow() + { + if( !isCloseable()) + return; + // Update user time, because the window may create a confirming dialog. + updateUserTime(); + if ( Pdeletewindow ) + { + Notify::raise( Notify::Close ); + sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window); + pingWindow(); + } + else + { + // client will not react on wm_delete_window. We have not choice + // but destroy his connection to the XServer. + killWindow(); + } + } + + +/*! + Kills the window via XKill + */ +void Client::killWindow() + { + kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl; + // not sure if we need an Notify::Kill or not.. until then, use + // Notify::Close + Notify::raise( Notify::Close ); + + if( isDialog()) + Notify::raise( Notify::TransDelete ); + if( isNormalWindow()) + Notify::raise( Notify::Delete ); + killProcess( false ); + // always kill this client at the server + XKillClient(qt_xdisplay(), window() ); + destroyClient(); + } + +// send a ping to the window using _NET_WM_PING if possible +// if it doesn't respond within a reasonable time, it will be +// killed +void Client::pingWindow() + { + if( !Pping ) + return; // can't ping :( + if( options->killPingTimeout == 0 ) + return; // turned off + if( ping_timer != NULL ) + return; // pinging already + ping_timer = new QTimer( this ); + connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout())); + ping_timer->start( options->killPingTimeout, true ); + ping_timestamp = qt_x_time; + workspace()->sendPingToWindow( window(), ping_timestamp ); + } + +void Client::gotPing( Time timestamp ) + { + // just plain compare is not good enough because of 64bit and truncating and whatnot + if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 ) + return; + delete ping_timer; + ping_timer = NULL; + if( process_killer != NULL ) + { + process_killer->kill(); + delete process_killer; + process_killer = NULL; + } + } + +void Client::pingTimeout() + { + kdDebug( 1212 ) << "Ping timeout:" << caption() << endl; + delete ping_timer; + ping_timer = NULL; + killProcess( true, ping_timestamp ); + } + +void Client::killProcess( bool ask, Time timestamp ) + { + if( process_killer != NULL ) + return; + Q_ASSERT( !ask || timestamp != CurrentTime ); + QCString machine = wmClientMachine( true ); + pid_t pid = info->pid(); + if( pid <= 0 || machine.isEmpty()) // needed properties missing + return; + kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl; + if( !ask ) + { + if( machine != "localhost" ) + { + KProcess proc; + proc << "xon" << machine << "kill" << pid; + proc.start( KProcess::DontCare ); + } + else + ::kill( pid, SIGTERM ); + } + else + { // SELI TODO handle the window created by handler specially (on top,urgent?) + process_killer = new KProcess( this ); + *process_killer << KStandardDirs::findExe( "kwin_killer_helper" ) + << "--pid" << QCString().setNum( pid ) << "--hostname" << machine + << "--windowname" << caption().utf8() + << "--applicationname" << resourceClass() + << "--wid" << QCString().setNum( window()) + << "--timestamp" << QCString().setNum( timestamp ); + connect( process_killer, SIGNAL( processExited( KProcess* )), + SLOT( processKillerExited())); + if( !process_killer->start( KProcess::NotifyOnExit )) + { + delete process_killer; + process_killer = NULL; + return; + } + } + } + +void Client::processKillerExited() + { + kdDebug( 1212 ) << "Killer exited" << endl; + delete process_killer; + process_killer = NULL; + } + +void Client::setSkipTaskbar( bool b, bool from_outside ) + { + int was_wants_tab_focus = wantsTabFocus(); + if( from_outside ) + { + b = rules()->checkSkipTaskbar( b ); + original_skip_taskbar = b; + } + if ( b == skipTaskbar() ) + return; + skip_taskbar = b; + info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar ); + updateWindowRules(); + if( was_wants_tab_focus != wantsTabFocus()) + workspace()->updateFocusChains( this, + isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate ); + } + +void Client::setSkipPager( bool b ) + { + b = rules()->checkSkipPager( b ); + if ( b == skipPager() ) + return; + skip_pager = b; + info->setState( b?NET::SkipPager:0, NET::SkipPager ); + updateWindowRules(); + } + +void Client::setModal( bool m ) + { // Qt-3.2 can have even modal normal windows :( + if( modal == m ) + return; + modal = m; + if( !modal ) + return; + // changing modality for a mapped window is weird (?) + // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG + } + +void Client::setDesktop( int desktop ) + { + if( desktop != NET::OnAllDesktops ) // do range check + desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop )); + desktop = rules()->checkDesktop( desktop ); + if( desk == desktop ) + return; + int was_desk = desk; + desk = desktop; + info->setDesktop( desktop ); + if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops )) + { // onAllDesktops changed + if ( isShown( true )) + Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops ); + workspace()->updateOnAllDesktopsOfTransients( this ); + } + if( decoration != NULL ) + decoration->desktopChange(); + workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); + updateVisibility(); + updateWindowRules(); + } + +void Client::setOnAllDesktops( bool b ) + { + if(( b && isOnAllDesktops()) + || ( !b && !isOnAllDesktops())) + return; + if( b ) + setDesktop( NET::OnAllDesktops ); + else + setDesktop( workspace()->currentDesktop()); + } + +bool Client::isOnCurrentDesktop() const + { + return isOnDesktop( workspace()->currentDesktop()); + } + +// performs activation and/or raising of the window +void Client::takeActivity( int flags, bool handled, allowed_t ) + { + if( !handled || !Ptakeactivity ) + { + if( flags & ActivityFocus ) + takeFocus( Allowed ); + if( flags & ActivityRaise ) + workspace()->raiseClient( this ); + return; + } + +#ifndef NDEBUG + static Time previous_activity_timestamp; + static Client* previous_client; + if( previous_activity_timestamp == qt_x_time && previous_client != this ) + { + kdDebug( 1212 ) << "Repeated use of the same X timestamp for activity" << endl; + kdDebug( 1212 ) << kdBacktrace() << endl; + } + previous_activity_timestamp = qt_x_time; + previous_client = this; +#endif + workspace()->sendTakeActivity( this, qt_x_time, flags ); + } + +// performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS +void Client::takeFocus( allowed_t ) + { +#ifndef NDEBUG + static Time previous_focus_timestamp; + static Client* previous_client; + if( previous_focus_timestamp == qt_x_time && previous_client != this ) + { + kdDebug( 1212 ) << "Repeated use of the same X timestamp for focus" << endl; + kdDebug( 1212 ) << kdBacktrace() << endl; + } + previous_focus_timestamp = qt_x_time; + previous_client = this; +#endif + if ( rules()->checkAcceptFocus( input )) + { + XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time ); + } + if ( Ptakefocus ) + sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); + workspace()->setShouldGetFocus( this ); + } + +/*! + Returns whether the window provides context help or not. If it does, + you should show a help menu item or a help button like '?' and call + contextHelp() if this is invoked. + + \sa contextHelp() + */ +bool Client::providesContextHelp() const + { + return Pcontexthelp; + } + + +/*! + Invokes context help on the window. Only works if the window + actually provides context help. + + \sa providesContextHelp() + */ +void Client::showContextHelp() + { + if ( Pcontexthelp ) + { + sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help); + QWhatsThis::enterWhatsThisMode(); // SELI? + } + } + + +/*! + Fetches the window's caption (WM_NAME property). It will be + stored in the client's caption(). + */ +void Client::fetchName() + { + setCaption( readName()); + } + +QString Client::readName() const + { + if ( info->name() && info->name()[ 0 ] != '\0' ) + return QString::fromUtf8( info->name() ); + else + return KWin::readNameProperty( window(), XA_WM_NAME ); + } + +KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); + +void Client::setCaption( const QString& s, bool force ) + { + if ( s != cap_normal || force ) + { + bool reset_name = force; + for( unsigned int i = 0; + i < s.length(); + ++i ) + if( !s[ i ].isPrint()) + s[ i ] = ' '; + cap_normal = s; + bool was_suffix = ( !cap_suffix.isEmpty()); + QString machine_suffix; + if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false ))) + machine_suffix = " <@" + wmClientMachine( true ) + ">"; + QString shortcut_suffix = !shortcut().isNull() ? ( " {" + shortcut().toString() + "}" ) : ""; + cap_suffix = machine_suffix + shortcut_suffix; + if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) + { + int i = 2; + do + { + cap_suffix = machine_suffix + " <" + QString::number(i) + ">" + shortcut_suffix; + i++; + } while ( workspace()->findClient( FetchNameInternalPredicate( this ))); + info->setVisibleName( caption().utf8() ); + 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( "" ); // remove + info->setVisibleIconName( "" ); // remove + } + else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set + info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() ); + + if( isManaged() && decoration != NULL ) + decoration->captionChange(); + } + } + +void Client::updateCaption() + { + setCaption( cap_normal, true ); + } + +void Client::fetchIconicName() + { + QString s; + if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) + s = QString::fromUtf8( info->iconName() ); + else + s = KWin::readNameProperty( window(), XA_WM_ICON_NAME ); + if ( s != cap_iconic ) + { + bool was_set = !cap_iconic.isEmpty(); + cap_iconic = s; + if( !cap_suffix.isEmpty()) + { + if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set + info->setVisibleIconName( ( s + cap_suffix ).utf8() ); + else if( was_set ) + info->setVisibleIconName( "" ); //remove + } + } + } + +/*!\reimp + */ +QString Client::caption( bool full ) const + { + return full ? cap_normal + cap_suffix : cap_normal; + } + +void Client::getWMHints() + { + XWMHints *hints = XGetWMHints(qt_xdisplay(), window() ); + input = true; + window_group = None; + urgency = false; + if ( hints ) + { + if( hints->flags & InputHint ) + input = hints->input; + if( hints->flags & WindowGroupHint ) + window_group = hints->window_group; + urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield + XFree( (char*)hints ); + } + checkGroup(); + updateUrgency(); + updateAllowedActions(); // group affects isMinimizable() + } + +void Client::getMotifHints() + { + bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose; + Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose ); + motif_noborder = mnoborder; + if( !hasNETSupport()) // NETWM apps should set type and size constraints + { + motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well + motif_may_move = mmove; + } + else + motif_may_resize = motif_may_move = true; + // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too + // mmaximize; - ignore, bogus - maximizing is basically just resizing + motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway + if( isManaged()) + updateDecoration( true ); // check if noborder state has changed + } + +void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon ) + { + // get the icons, allow scaling + if( icon != NULL ) + *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); + if( miniicon != NULL ) + if( icon == NULL || !icon->isNull()) + *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); + else + *miniicon = QPixmap(); + } + +void Client::getIcons() + { + // first read icons from the window itself + readIcons( window(), &icon_pix, &miniicon_pix ); + if( icon_pix.isNull()) + { // then try window group + icon_pix = group()->icon(); + miniicon_pix = group()->miniIcon(); + } + if( icon_pix.isNull() && isTransient()) + { // then mainclients + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end() && icon_pix.isNull(); + ++it ) + { + icon_pix = (*it)->icon(); + miniicon_pix = (*it)->miniIcon(); + } + } + if( icon_pix.isNull()) + { // and if nothing else, load icon from classhint or xapp icon + icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp ); + miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp ); + } + if( isManaged() && decoration != NULL ) + decoration->iconChange(); + } + +void Client::getWindowProtocols() + { + Atom *p; + int i,n; + + Pdeletewindow = 0; + Ptakefocus = 0; + Ptakeactivity = 0; + Pcontexthelp = 0; + Pping = 0; + + if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n)) + { + for (i = 0; i < n; i++) + if (p[i] == atoms->wm_delete_window) + Pdeletewindow = 1; + else if (p[i] == atoms->wm_take_focus) + Ptakefocus = 1; + else if (p[i] == atoms->net_wm_take_activity) + Ptakeactivity = 1; + else if (p[i] == atoms->net_wm_context_help) + Pcontexthelp = 1; + else if (p[i] == atoms->net_wm_ping) + Pping = 1; + if (n>0) + XFree(p); + } + } + +static int nullErrorHandler(Display *, XErrorEvent *) + { + return 0; + } + +/*! + Returns WM_WINDOW_ROLE property for a given window. + */ +QCString Client::staticWindowRole(WId w) + { + return getStringProperty(w, qt_window_role).lower(); + } + +/*! + Returns SM_CLIENT_ID property for a given window. + */ +QCString Client::staticSessionId(WId w) + { + return getStringProperty(w, qt_sm_client_id); + } + +/*! + Returns WM_COMMAND property for a given window. + */ +QCString Client::staticWmCommand(WId w) + { + return getStringProperty(w, XA_WM_COMMAND, ' '); + } + +/*! + Returns WM_CLIENT_LEADER property for a given window. + */ +Window Client::staticWmClientLeader(WId w) + { + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + Window result = w; + XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); + status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000, + FALSE, XA_WINDOW, &type, &format, + &nitems, &extra, &data ); + XSetErrorHandler(oldHandler); + if (status == Success ) + { + if (data && nitems > 0) + result = *((Window*) data); + XFree(data); + } + return result; + } + + +void Client::getWmClientLeader() + { + wmClientLeaderWin = staticWmClientLeader(window()); + } + +/*! + Returns sessionId for this client, + taken either from its window or from the leader window. + */ +QCString Client::sessionId() + { + QCString result = staticSessionId(window()); + if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) + result = staticSessionId(wmClientLeaderWin); + return result; + } + +/*! + Returns command property for this client, + taken either from its window or from the leader window. + */ +QCString Client::wmCommand() + { + QCString result = staticWmCommand(window()); + if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) + result = staticWmCommand(wmClientLeaderWin); + return result; + } + +void Client::getWmClientMachine() + { + client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE); + if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) + client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE); + if( client_machine.isEmpty()) + client_machine = "localhost"; + } + +/*! + Returns client machine for this client, + taken either from its window or from the leader window. +*/ +QCString Client::wmClientMachine( bool use_localhost ) const + { + QCString result = client_machine; + if( use_localhost ) + { // special name for the local machine (localhost) + if( result != "localhost" && isLocalMachine( result )) + result = "localhost"; + } + return result; + } + +/*! + Returns client leader window for this client. + Returns the client window itself if no leader window is defined. +*/ +Window Client::wmClientLeader() const + { + if (wmClientLeaderWin) + return wmClientLeaderWin; + return window(); + } + +bool Client::wantsTabFocus() const + { + return ( isNormalWindow() || isDialog()) && wantsInput() && !skip_taskbar; + } + + +bool Client::wantsInput() const + { + return rules()->checkAcceptFocus( input || Ptakefocus ); + } + +bool Client::isDesktop() const + { + return windowType() == NET::Desktop; + } + +bool Client::isDock() const + { + return windowType() == NET::Dock; + } + +bool Client::isTopMenu() const + { + return windowType() == NET::TopMenu; + } + + +bool Client::isMenu() const + { + return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. + } + +bool Client::isToolbar() const + { + return windowType() == NET::Toolbar; + } + +bool Client::isSplash() const + { + return windowType() == NET::Splash; + } + +bool Client::isUtility() const + { + return windowType() == NET::Utility; + } + +bool Client::isDialog() const + { + return windowType() == NET::Dialog; + } + +bool Client::isNormalWindow() const + { + return windowType() == NET::Normal; + } + +bool Client::isSpecialWindow() const + { + return isDesktop() || isDock() || isSplash() || isTopMenu() + || isToolbar(); // TODO + } + +NET::WindowType Client::windowType( bool direct, int supported_types ) const + { + NET::WindowType wt = info->windowType( supported_types ); + if( direct ) + return wt; + NET::WindowType wt2 = rules()->checkType( wt ); + if( wt != wt2 ) + { + wt = wt2; + info->setWindowType( wt ); // force hint change + } + // hacks here + if( wt == NET::Menu ) + { + // ugly hack to support the times when NET::Menu meant NET::TopMenu + // if it's as wide as the screen, not very high and has its upper-left + // corner a bit above the screen's upper-left cornet, it's a topmenu + if( x() == 0 && y() < 0 && y() > -10 && height() < 100 + && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 ) + wt = NET::TopMenu; + } + // TODO change this to rule + const char* const oo_prefix = "openoffice.org"; // QCString has no startsWith() + // oo_prefix is lowercase, because resourceClass() is forced to be lowercase + if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog ) + wt = NET::Normal; // see bug #66065 + if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec + wt = isTransient() ? NET::Dialog : NET::Normal; + return wt; + } + +/*! + Sets an appropriate cursor shape for the logical mouse position \a m + + */ +void Client::setCursor( Position m ) + { + if( !isResizable() || isShade()) + { + m = PositionCenter; + } + switch ( m ) + { + case PositionTopLeft: + case PositionBottomRight: + setCursor( sizeFDiagCursor ); + break; + case PositionBottomLeft: + case PositionTopRight: + setCursor( sizeBDiagCursor ); + break; + case PositionTop: + case PositionBottom: + setCursor( sizeVerCursor ); + break; + case PositionLeft: + case PositionRight: + setCursor( sizeHorCursor ); + break; + default: + if( buttonDown && isMovable()) + setCursor( sizeAllCursor ); + else + setCursor( arrowCursor ); + break; + } + } + +// TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit? +void Client::setCursor( const QCursor& c ) + { + if( c.handle() == cursor.handle()) + return; + cursor = c; + if( decoration != NULL ) + decoration->widget()->setCursor( cursor ); + XDefineCursor( qt_xdisplay(), frameId(), cursor.handle()); + } + +Client::Position Client::mousePosition( const QPoint& p ) const + { + if( decoration != NULL ) + return decoration->mousePosition( p ); + return PositionCenter; + } + +void Client::updateAllowedActions( bool force ) + { + if( !isManaged() && !force ) + return; + unsigned long old_allowed_actions = allowed_actions; + allowed_actions = 0; + if( isMovable()) + allowed_actions |= NET::ActionMove; + if( isResizable()) + allowed_actions |= NET::ActionResize; + if( isMinimizable()) + allowed_actions |= NET::ActionMinimize; + if( isShadeable()) + allowed_actions |= NET::ActionShade; + // sticky state not supported + if( isMaximizable()) + allowed_actions |= NET::ActionMax; + if( userCanSetFullScreen()) + allowed_actions |= NET::ActionFullScreen; + allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.) + if( isCloseable()) + allowed_actions |= NET::ActionClose; + if( old_allowed_actions == allowed_actions ) + return; + // TODO this could be delayed and compressed - it's only for pagers etc. anyway + info->setAllowedActions( allowed_actions ); + // TODO this should also tell the decoration, so that it can update the buttons + } + +void Client::autoRaise() + { + workspace()->raiseClient( this ); + cancelAutoRaise(); + } + +void Client::cancelAutoRaise() + { + delete autoRaiseTimer; + autoRaiseTimer = 0; + } + +void Client::setOpacity(bool translucent, uint opacity) + { + if (isDesktop()) + return; // xcompmgr does not like non solid desktops and the user could set it accidently by mouse scrolling +// qWarning("setting opacity for %d",qt_xdisplay()); + //rule out activated translulcency with 100% opacity + if (!translucent || opacity == 0xFFFFFFFF) + { + opacity_ = 0xFFFFFFFF; + XDeleteProperty (qt_xdisplay(), frameId(), atoms->net_wm_window_opacity); + XDeleteProperty (qt_xdisplay(), window(), atoms->net_wm_window_opacity); // ??? frameId() is necessary for visible changes, window() is the winId() that would be set by apps - we set both to be sure the app knows what's currently displayd + } + else{ + if(opacity == opacity_) + return; + opacity_ = opacity; + long data = opacity; // 32bit XChangeProperty needs long + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); + XChangeProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); + } + } + +void Client::setShadowSize(uint shadowSize) + { + // ignoring all individual settings - if we control a window, we control it's shadow + // TODO somehow handle individual settings for docks (besides custom sizes) + long data = shadowSize; + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shadow, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); + } + +void Client::updateOpacity() +// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) + { + if (!(isNormalWindow() || isDialog() || isUtility() )|| custom_opacity) + return; + if (isActive()) + { + if( ruleOpacityActive() ) + setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); + else + setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); + if (isBMP()) + // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) + { + ClientList tmpGroupMembers = group()->members(); + ClientList activeGroupMembers; + activeGroupMembers.append(this); + tmpGroupMembers.remove(this); + ClientList::Iterator it = tmpGroupMembers.begin(); + while (it != tmpGroupMembers.end()) + // search for next attached and not activated client and repeat if found + { + if ((*it) != this && (*it)->isBMP()) + // potential "to activate" client found + { +// qWarning("client found"); + if ((*it)->touches(this)) // first test, if the new client touches the just activated one + { +// qWarning("found client touches me"); + if( ruleOpacityActive() ) + (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); + else + (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); +// qWarning("activated, search restarted (1)"); + (*it)->setShadowSize(options->activeWindowShadowSize); + activeGroupMembers.append(*it); + tmpGroupMembers.remove(it); + it = tmpGroupMembers.begin(); // restart, search next client + continue; + } + else + { // pot. client does not touch c, so we have to search if it touches some other activated client + bool found = false; + for( ClientList::ConstIterator it2 = activeGroupMembers.begin(); it2 != activeGroupMembers.end(); it2++ ) + { + if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) + { +// qWarning("found client touches other active client"); + if( ruleOpacityActive() ) + (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); + else + (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); + (*it)->setShadowSize(options->activeWindowShadowSize); + activeGroupMembers.append(*it); + tmpGroupMembers.remove(it); + it = tmpGroupMembers.begin(); // reset potential client search + found = true; +// qWarning("activated, search restarted (2)"); + break; // skip this loop + } + } + if (found) continue; + } + } + it++; + } + } + else if (isNormalWindow()) + // activate dependend minor windows as well + { + for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) + if ((*it)->isDialog() || (*it)->isUtility()) + if( (*it)->ruleOpacityActive() ) + (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive()); + else + (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); + } + } + else + { + if( ruleOpacityInactive() ) + setOpacity(rule_opacity_inactive < 0xFFFFFFFF, rule_opacity_inactive); + else + setOpacity(options->translucentInactiveWindows && !(keepAbove() && options->keepAboveAsActive), + options->inactiveWindowOpacity); + // deactivate dependend minor windows as well + if (isBMP()) + // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) + { + ClientList tmpGroupMembers = group()->members(); + ClientList inactiveGroupMembers; + inactiveGroupMembers.append(this); + tmpGroupMembers.remove(this); + ClientList::Iterator it = tmpGroupMembers.begin(); + while ( it != tmpGroupMembers.end() ) + // search for next attached and not activated client and repeat if found + { + if ((*it) != this && (*it)->isBMP()) + // potential "to activate" client found + { +// qWarning("client found"); + if ((*it)->touches(this)) // first test, if the new client touches the just activated one + { +// qWarning("found client touches me"); + if( (*it)->ruleOpacityInactive() ) + (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); + else + (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); + (*it)->setShadowSize(options->inactiveWindowShadowSize); +// qWarning("deactivated, search restarted (1)"); + inactiveGroupMembers.append(*it); + tmpGroupMembers.remove(it); + it = tmpGroupMembers.begin(); // restart, search next client + continue; + } + else // pot. client does not touch c, so we have to search if it touches some other activated client + { + bool found = false; + for( ClientList::ConstIterator it2 = inactiveGroupMembers.begin(); it2 != inactiveGroupMembers.end(); it2++ ) + { + if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) + { +// qWarning("found client touches other inactive client"); + if( (*it)->ruleOpacityInactive() ) + (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); + else + (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); + (*it)->setShadowSize(options->inactiveWindowShadowSize); +// qWarning("deactivated, search restarted (2)"); + inactiveGroupMembers.append(*it); + tmpGroupMembers.remove(it); + it = tmpGroupMembers.begin(); // reset potential client search + found = true; + break; // skip this loop + } + } + if (found) continue; + } + } + it++; + } + } + else if (isNormalWindow()) + { + for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) + if ((*it)->isUtility()) //don't deactivate dialogs... + if( (*it)->ruleOpacityInactive() ) + (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); + else + (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); + } + } + } + +void Client::updateShadowSize() +// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) + { + if (!(isNormalWindow() || isDialog() || isUtility() )) + return; + if (isActive()) + setShadowSize(options->activeWindowShadowSize); + else + setShadowSize(options->inactiveWindowShadowSize); + } + +uint Client::ruleOpacityInactive() + { + return rule_opacity_inactive;// != 0 ; + } + +uint Client::ruleOpacityActive() + { + return rule_opacity_active;// != 0; + } + +bool Client::getWindowOpacity() //query translucency settings from X, returns true if window opacity is set + { + unsigned char *data = 0; + Atom actual; + int format, result; + unsigned long n, left; + result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data); + if (result == Success && data != None && format == 32 ) + { + opacity_ = *reinterpret_cast< long* >( data ); + custom_opacity = true; +// setOpacity(opacity_ < 0xFFFFFFFF, opacity_); + XFree ((char*)data); + return TRUE; + } + return FALSE; + } + +void Client::setCustomOpacityFlag(bool custom) + { + custom_opacity = custom; + } + +uint Client::opacity() + { + return opacity_; + } + +int Client::opacityPercentage() + { + return int(100*((double)opacity_/0xffffffff)); + } + +bool Client::touches(const Client* c) +// checks if this client borders c, needed to test beep media player window state + { + if (y() == c->y() + c->height()) // this bottom to c + return TRUE; + if (y() + height() == c->y()) // this top to c + return TRUE; + if (x() == c->x() + c->width()) // this right to c + return TRUE; + if (x() + width() == c->x()) // this left to c + return TRUE; + return FALSE; + } + +void Client::setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth) +{ + long data = (topHeight < 255 ? topHeight : 255) << 24 | + (rightWidth < 255 ? rightWidth : 255) << 16 | + (bottomHeight < 255 ? bottomHeight : 255) << 8 | + (leftWidth < 255 ? leftWidth : 255); + XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_decohash, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); +} + +void Client::unsetDecoHashProperty() +{ + XDeleteProperty( qt_xdisplay(), frameId(), atoms->net_wm_window_decohash); +} + +#ifndef NDEBUG +kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) + { + if( cl == NULL ) + return stream << "\'NULL_CLIENT\'"; + return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; + } +kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) + { + stream << "LIST:("; + bool first = true; + for( ClientList::ConstIterator it = list.begin(); + it != list.end(); + ++it ) + { + if( !first ) + stream << ":"; + first = false; + stream << *it; + } + stream << ")"; + return stream; + } +kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) + { + stream << "LIST:("; + bool first = true; + for( ConstClientList::ConstIterator it = list.begin(); + it != list.end(); + ++it ) + { + if( !first ) + stream << ":"; + first = false; + stream << *it; + } + stream << ")"; + return stream; + } +#endif + +QPixmap * kwin_get_menu_pix_hack() + { + static QPixmap p; + if ( p.isNull() ) + p = SmallIcon( "bx2" ); + return &p; + } + +} // namespace + +#include "client.moc" diff --git a/kwin/client.h b/kwin/client.h new file mode 100644 index 000000000..d0d8e9d54 --- /dev/null +++ b/kwin/client.h @@ -0,0 +1,946 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_CLIENT_H +#define KWIN_CLIENT_H + +#include <qframe.h> +#include <qvbox.h> +#include <qpixmap.h> +#include <netwm.h> +#include <kdebug.h> +#include <assert.h> +#include <kshortcut.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <fixx11h.h> + +#include "utils.h" +#include "options.h" +#include "workspace.h" +#include "kdecoration.h" +#include "rules.h" + +class QTimer; +class KProcess; +class KStartupInfoData; + +namespace KWinInternal +{ + +class Workspace; +class Client; +class WinInfo; +class SessionInfo; +class Bridge; + +class Client : public QObject, public KDecorationDefines + { + Q_OBJECT + public: + Client( Workspace *ws ); + Window window() const; + Window frameId() const; + Window wrapperId() const; + Window decorationId() const; + + Workspace* workspace() const; + const Client* transientFor() const; + Client* transientFor(); + bool isTransient() const; + bool groupTransient() const; + bool wasOriginallyGroupTransient() const; + ClientList mainClients() const; // call once before loop , is not indirect + bool hasTransient( const Client* c, bool indirect ) const; + const ClientList& transients() const; // is not indirect + void checkTransient( Window w ); + Client* findModal(); + const Group* group() const; + Group* group(); + void checkGroup( Group* gr = NULL, bool force = false ); + void changeClientLeaderGroup( Group* gr ); + // prefer isXXX() instead + NET::WindowType windowType( bool direct = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const; + const WindowRules* rules() const; + void removeRule( Rules* r ); + void setupWindowRules( bool ignore_temporary ); + void applyWindowRules(); + void updateWindowRules(); + + QRect geometry() const; + QSize size() const; + QSize minSize() const; + QSize maxSize() const; + QPoint pos() const; + QRect rect() const; + int x() const; + int y() const; + int width() const; + int height() const; + QPoint clientPos() const; // inside of geometry() + QSize clientSize() const; + + bool windowEvent( XEvent* e ); + virtual bool eventFilter( QObject* o, QEvent* e ); + + bool manage( Window w, bool isMapped ); + + void releaseWindow( bool on_shutdown = false ); + + enum Sizemode // how to resize the window in order to obey constains (mainly aspect ratios) + { + SizemodeAny, + SizemodeFixedW, // try not to affect width + SizemodeFixedH, // try not to affect height + SizemodeMax // try not to make it larger in either direction + }; + QSize adjustedSize( const QSize&, Sizemode mode = SizemodeAny ) const; + QSize adjustedSize() const; + + QPixmap icon() const; + QPixmap miniIcon() const; + + bool isActive() const; + void setActive( bool, bool updateOpacity = true ); + + int desktop() const; + void setDesktop( int ); + bool isOnDesktop( int d ) const; + bool isOnCurrentDesktop() const; + bool isOnAllDesktops() const; + void setOnAllDesktops( bool set ); + + // !isMinimized() && not hidden, i.e. normally visible on some virtual desktop + bool isShown( bool shaded_is_shown ) const; + + bool isShade() const; // true only for ShadeNormal + ShadeMode shadeMode() const; // prefer isShade() + void setShade( ShadeMode mode ); + bool isShadeable() const; + + bool isMinimized() const; + bool isMaximizable() const; + QRect geometryRestore() const; + MaximizeMode maximizeModeRestore() const; + MaximizeMode maximizeMode() const; + bool isMinimizable() const; + void setMaximize( bool vertically, bool horizontally ); + + void setFullScreen( bool set, bool user ); + bool isFullScreen() const; + bool isFullScreenable( bool fullscreen_hack = false ) const; + bool userCanSetFullScreen() const; + QRect geometryFSRestore() const { return geom_fs_restore; } // only for session saving + int fullScreenMode() const { return fullscreen_mode; } // only for session saving + + bool isUserNoBorder() const; + void setUserNoBorder( bool set ); + bool userCanSetNoBorder() const; + bool noBorder() const; + + bool skipTaskbar( bool from_outside = false ) const; + void setSkipTaskbar( bool set, bool from_outside ); + + bool skipPager() const; + void setSkipPager( bool ); + + bool keepAbove() const; + void setKeepAbove( bool ); + bool keepBelow() const; + void setKeepBelow( bool ); + Layer layer() const; + Layer belongsToLayer() const; + void invalidateLayer(); + + void setModal( bool modal ); + bool isModal() const; + + // auxiliary functions, depend on the windowType + bool wantsTabFocus() const; + bool wantsInput() const; + bool hasNETSupport() const; + bool isMovable() const; + bool isDesktop() const; + bool isDock() const; + bool isToolbar() const; + bool isTopMenu() const; + bool isMenu() const; + bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' + bool isDialog() const; + bool isSplash() const; + bool isUtility() const; + // returns true for "special" windows and false for windows which are "normal" + // (normal=window which has a border, can be moved by the user, can be closed, etc.) + // true for Desktop, Dock, Splash, Override and TopMenu (and Toolbar??? - for now) + // false for Normal, Dialog, Utility and Menu (and Toolbar??? - not yet) TODO + bool isSpecialWindow() const; + + bool isResizable() const; + bool isCloseable() const; // may be closed by the user (may have a close button) + + void takeActivity( int flags, bool handled, allowed_t ); // takes ActivityFlags as arg (in utils.h) + void takeFocus( allowed_t ); + void demandAttention( bool set = true ); + + void setMask( const QRegion& r, int mode = X::Unsorted ); + QRegion mask() const; + + void updateDecoration( bool check_workspace_pos, bool force = false ); + void checkBorderSizes(); + + // shape extensions + bool shape() const; + void updateShape(); + + void setGeometry( int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet ); + void setGeometry( const QRect& r, ForceGeometry_t force = NormalGeometrySet ); + void move( int x, int y, ForceGeometry_t force = NormalGeometrySet ); + void move( const QPoint & p, ForceGeometry_t force = NormalGeometrySet ); + // plainResize() simply resizes + void plainResize( int w, int h, ForceGeometry_t force = NormalGeometrySet ); + void plainResize( const QSize& s, ForceGeometry_t force = NormalGeometrySet ); + // resizeWithChecks() resizes according to gravity, and checks workarea position + void resizeWithChecks( int w, int h, ForceGeometry_t force = NormalGeometrySet ); + void resizeWithChecks( const QSize& s, ForceGeometry_t force = NormalGeometrySet ); + void keepInArea( QRect area, bool partial = false ); + + void growHorizontal(); + void shrinkHorizontal(); + void growVertical(); + void shrinkVertical(); + + bool providesContextHelp() const; + KShortcut shortcut() const; + void setShortcut( const QString& cut ); + + bool performMouseCommand( Options::MouseCommand, QPoint globalPos, bool handled = false ); + + QCString windowRole() const; + QCString sessionId(); + QCString resourceName() const; + QCString resourceClass() const; + QCString wmCommand(); + QCString wmClientMachine( bool use_localhost ) const; + Window wmClientLeader() const; + pid_t pid() const; + + QRect adjustedClientArea( const QRect& desktop, const QRect& area ) const; + + Colormap colormap() const; + + // updates visibility depending on being shaded, virtual desktop, etc. + void updateVisibility(); + // hides a client - basically like minimize, but without effects, it's simply hidden + void hideClient( bool hide ); + + QString caption( bool full = true ) const; + void updateCaption(); + + void keyPressEvent( uint key_code ); // FRAME ?? + void updateMouseGrab(); + Window moveResizeGrabWindow() const; + + const QPoint calculateGravitation( bool invert, int gravity = 0 ) const; // FRAME public? + + void NETMoveResize( int x_root, int y_root, NET::Direction direction ); + void NETMoveResizeWindow( int flags, int x, int y, int width, int height ); + void restackWindow( Window above, int detail, NET::RequestSource source, Time timestamp, bool send_event = false ); + + void gotPing( Time timestamp ); + + static QCString staticWindowRole(WId); + static QCString staticSessionId(WId); + static QCString staticWmCommand(WId); + static QCString staticWmClientMachine(WId); + static Window staticWmClientLeader(WId); + + void checkWorkspacePosition(); + void updateUserTime( Time time = CurrentTime ); + Time userTime() const; + bool hasUserTimeSupport() const; + bool ignoreFocusStealing() const; + + // does 'delete c;' + static void deleteClient( Client* c, allowed_t ); + + static bool resourceMatch( const Client* c1, const Client* c2 ); + static bool belongToSameApplication( const Client* c1, const Client* c2, bool active_hack = false ); + static void readIcons( Window win, QPixmap* icon, QPixmap* miniicon ); + + void minimize( bool avoid_animation = false ); + void unminimize( bool avoid_animation = false ); + void closeWindow(); + void killWindow(); + void maximize( MaximizeMode ); + void toggleShade(); + void showContextHelp(); + void cancelShadeHover(); + void cancelAutoRaise(); + void destroyClient(); + void checkActiveModal(); + void setOpacity(bool translucent, uint opacity = 0); + void setShadowSize(uint shadowSize); + void updateOpacity(); + void updateShadowSize(); + bool hasCustomOpacity(){return custom_opacity;} + void setCustomOpacityFlag(bool custom = true); + bool getWindowOpacity(); + int opacityPercentage(); + void checkAndSetInitialRuledOpacity(); + uint ruleOpacityInactive(); + uint ruleOpacityActive(); + unsigned int opacity(); + bool isBMP(); + void setBMP(bool b); + bool touches(const Client* c); + void setShapable(bool b); + bool hasStrut() const; + + private slots: + void autoRaise(); + void shadeHover(); + void shortcutActivated(); + + private: + friend class Bridge; // FRAME + virtual void processMousePressEvent( QMouseEvent* e ); + + private: // TODO cleanup the order of things in the .h file + // use Workspace::createClient() + virtual ~Client(); // use destroyClient() or releaseWindow() + + Position mousePosition( const QPoint& ) const; + void setCursor( Position m ); + void setCursor( const QCursor& c ); + + void animateMinimizeOrUnminimize( bool minimize ); + QPixmap animationPixmap( int w ); + // transparent stuff + void drawbound( const QRect& geom ); + void clearbound(); + void doDrawbound( const QRect& geom, bool clear ); + + // handlers for X11 events + bool mapRequestEvent( XMapRequestEvent* e ); + void unmapNotifyEvent( XUnmapEvent*e ); + void destroyNotifyEvent( XDestroyWindowEvent*e ); + void configureRequestEvent( XConfigureRequestEvent* e ); + void propertyNotifyEvent( XPropertyEvent* e ); + void clientMessageEvent( XClientMessageEvent* e ); + void enterNotifyEvent( XCrossingEvent* e ); + void leaveNotifyEvent( XCrossingEvent* e ); + void focusInEvent( XFocusInEvent* e ); + void focusOutEvent( XFocusOutEvent* e ); + + bool buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); + bool buttonReleaseEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); + bool motionNotifyEvent( Window w, int state, int x, int y, int x_root, int y_root ); + + void processDecorationButtonPress( int button, int state, int x, int y, int x_root, int y_root ); + + private slots: + void pingTimeout(); + void processKillerExited(); + void demandAttentionKNotify(); + + private: + // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 + void setMappingState( int s ); + int mappingState() const; + bool isIconicState() const; + bool isNormalState() const; + bool isManaged() const; // returns false if this client is not yet managed + void updateAllowedActions( bool force = false ); + QSize sizeForClientSize( const QSize&, Sizemode mode = SizemodeAny, bool noframe = false ) const; + void changeMaximize( bool horizontal, bool vertical, bool adjust ); + void checkMaximizeGeometry(); + int checkFullScreenHack( const QRect& geom ) const; // 0 - none, 1 - one xinerama screen, 2 - full area + void updateFullScreenHack( const QRect& geom ); + void getWmNormalHints(); + void getMotifHints(); + void getIcons(); + void getWmClientLeader(); + void getWmClientMachine(); + void fetchName(); + void fetchIconicName(); + QString readName() const; + void setCaption( const QString& s, bool force = false ); + bool hasTransientInternal( const Client* c, bool indirect, ConstClientList& set ) const; + void finishWindowRules(); + void setShortcutInternal( const KShortcut& cut ); + + void updateWorkareaDiffs(); + void checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ); + static int computeWorkareaDiff( int left, int right, int a_left, int a_right ); + void configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool ); + NETExtendedStrut strut() const; + int checkShadeGeometry( int w, int h ); + void postponeGeometryUpdates( bool postpone ); + + bool startMoveResize(); + void finishMoveResize( bool cancel ); + void leaveMoveResize(); + void checkUnrestrictedMoveResize(); + void handleMoveResize( int x, int y, int x_root, int y_root ); + void positionGeometryTip(); + void grabButton( int mod ); + void ungrabButton( int mod ); + void resetMaximize(); + void resizeDecoration( const QSize& s ); + void setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth); + void unsetDecoHashProperty(); + + void pingWindow(); + void killProcess( bool ask, Time timestamp = CurrentTime ); + void updateUrgency(); + static void sendClientMessage( Window w, Atom a, Atom protocol, + long data1 = 0, long data2 = 0, long data3 = 0 ); + + void embedClient( Window w, const XWindowAttributes &attr ); + void detectNoBorder(); + void detectShapable(); + void destroyDecoration(); + void updateFrameExtents(); + + void rawShow(); // just shows it + void rawHide(); // just hides it + + Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, + bool session ) const; + Time readUserCreationTime() const; + static bool sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ); + void startupIdChanged(); + + Window client; + Window wrapper; + Window frame; + KDecoration* decoration; + Workspace* wspace; + Bridge* bridge; + int desk; + bool buttonDown; + bool moveResizeMode; + bool move_faked_activity; + Window move_resize_grab_window; + bool unrestrictedMoveResize; + bool isMove() const + { + return moveResizeMode && mode == PositionCenter; + } + bool isResize() const + { + return moveResizeMode && mode != PositionCenter; + } + + Position mode; + QPoint moveOffset; + QPoint invertedMoveOffset; + QRect moveResizeGeom; + QRect initialMoveResizeGeom; + XSizeHints xSizeHint; + void sendSyntheticConfigureNotify(); + int mapping_state; + void readTransient(); + Window verifyTransientFor( Window transient_for, bool set ); + void addTransient( Client* cl ); + void removeTransient( Client* cl ); + void removeFromMainClients(); + void cleanGrouping(); + void checkGroupTransients(); + void setTransient( Window new_transient_for_id ); + Client* transient_for; + Window transient_for_id; + Window original_transient_for_id; + ClientList transients_list; // SELI make this ordered in stacking order? + ShadeMode shade_mode; + uint active :1; + uint deleting : 1; // true when doing cleanup and destroying the client + uint keep_above : 1; // NET::KeepAbove (was stays_on_top) + uint is_shape :1; + uint skip_taskbar :1; + uint original_skip_taskbar :1; // unaffected by KWin + uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol? + uint Ptakefocus :1;// does the window understand the TakeFocus protocol? + uint Ptakeactivity : 1; // does it support _NET_WM_TAKE_ACTIVITY + uint Pcontexthelp : 1; // does the window understand the ContextHelp protocol? + uint Pping : 1; // does it support _NET_WM_PING? + uint input :1; // does the window want input in its wm_hints + uint skip_pager : 1; + uint motif_noborder : 1; + uint motif_may_resize : 1; + uint motif_may_move :1; + uint motif_may_close : 1; + uint keep_below : 1; // NET::KeepBelow + uint minimized : 1; + uint hidden : 1; // forcibly hidden by calling hide() + uint modal : 1; // NET::Modal + uint noborder : 1; + uint user_noborder : 1; + uint urgency : 1; // XWMHints, UrgencyHint + uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client + uint demands_attention : 1; + WindowRules client_rules; + void getWMHints(); + void readIcons(); + void getWindowProtocols(); + QPixmap icon_pix; + QPixmap miniicon_pix; + QCursor cursor; + // FullScreenHack - non-NETWM fullscreen (noborder,size of desktop) + // DON'T reorder - saved to config files !!! + enum FullScreenMode { FullScreenNone, FullScreenNormal, FullScreenHack }; + FullScreenMode fullscreen_mode; + MaximizeMode max_mode; + QRect geom_restore; + QRect geom_fs_restore; + MaximizeMode maxmode_restore; + int workarea_diff_x, workarea_diff_y; + WinInfo* info; + QTimer* autoRaiseTimer; + QTimer* shadeHoverTimer; + Colormap cmap; + QCString resource_name; + QCString resource_class; + QCString client_machine; + QString cap_normal, cap_iconic, cap_suffix; + WId wmClientLeaderWin; + QCString window_role; + Group* in_group; + Window window_group; + Layer in_layer; + QTimer* ping_timer; + KProcess* process_killer; + Time ping_timestamp; + Time user_time; + unsigned long allowed_actions; + QRect frame_geometry; + QSize client_size; + int postpone_geometry_updates; // >0 - new geometry is remembered, but not actually set + bool pending_geometry_update; + bool shade_geometry_change; + int border_left, border_right, border_top, border_bottom; + QRegion _mask; + static bool check_active_modal; // see Client::checkActiveModal() + KShortcut _shortcut; + friend struct FetchNameInternalPredicate; + friend struct CheckIgnoreFocusStealingProcedure; + friend struct ResetupRulesProcedure; + friend class GeometryUpdatesPostponer; + void show() { assert( false ); } // SELI remove after Client is no longer QWidget + void hide() { assert( false ); } + uint opacity_; + uint savedOpacity_; + bool custom_opacity; + uint rule_opacity_active; //translucency rules + uint rule_opacity_inactive; //dto. + //int shadeOriginalHeight; + bool isBMP_; + QTimer* demandAttentionKNotifyTimer; + + friend bool performTransiencyCheck(); + }; + +// helper for Client::postponeGeometryUpdates() being called in pairs (true/false) +class GeometryUpdatesPostponer + { + public: + GeometryUpdatesPostponer( Client* c ) + : cl( c ) { cl->postponeGeometryUpdates( true ); } + ~GeometryUpdatesPostponer() + { cl->postponeGeometryUpdates( false ); } + private: + Client* cl; + }; + + +// NET WM Protocol handler class +class WinInfo : public NETWinInfo + { + private: + typedef KWinInternal::Client Client; // because of NET::Client + public: + WinInfo( Client* c, Display * display, Window window, + Window rwin, const unsigned long pr[], int pr_size ); + virtual void changeDesktop(int desktop); + virtual void changeState( unsigned long state, unsigned long mask ); + private: + Client * m_client; + }; + +inline Window Client::window() const + { + return client; + } + +inline Window Client::frameId() const + { + return frame; + } + +inline Window Client::wrapperId() const + { + return wrapper; + } + +inline Window Client::decorationId() const + { + return decoration != NULL ? decoration->widget()->winId() : None; + } + +inline Workspace* Client::workspace() const + { + return wspace; + } + +inline const Client* Client::transientFor() const + { + return transient_for; + } + +inline Client* Client::transientFor() + { + return transient_for; + } + +inline bool Client::groupTransient() const + { + return transient_for_id == workspace()->rootWin(); + } + +// needed because verifyTransientFor() may set transient_for_id to root window, +// if the original value has a problem (window doesn't exist, etc.) +inline bool Client::wasOriginallyGroupTransient() const + { + return original_transient_for_id == workspace()->rootWin(); + } + +inline bool Client::isTransient() const + { + return transient_for_id != None; + } + +inline const ClientList& Client::transients() const + { + return transients_list; + } + +inline const Group* Client::group() const + { + return in_group; + } + +inline Group* Client::group() + { + return in_group; + } + +inline int Client::mappingState() const + { + return mapping_state; + } + +inline QCString Client::resourceName() const + { + return resource_name; // it is always lowercase + } + +inline QCString Client::resourceClass() const + { + return resource_class; // it is always lowercase + } + +inline +bool Client::isMinimized() const + { + return minimized; + } + +inline bool Client::isActive() const + { + return active; + } + +/*! + Returns the virtual desktop within the workspace() the client window + is located in, 0 if it isn't located on any special desktop (not mapped yet), + or NET::OnAllDesktops. Do not use desktop() directly, use + isOnDesktop() instead. + */ +inline int Client::desktop() const + { + return desk; + } + +inline bool Client::isOnAllDesktops() const + { + return desk == NET::OnAllDesktops; + } +/*! + Returns whether the client is on the virtual desktop \a d. + This is always TRUE for onAllDesktops clients. + */ +inline bool Client::isOnDesktop( int d ) const + { + return desk == d || /*desk == 0 ||*/ isOnAllDesktops(); + } + +inline +bool Client::isShown( bool shaded_is_shown ) const + { + return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden; + } + +inline +bool Client::isShade() const + { + return shade_mode == ShadeNormal; + } + +inline +ShadeMode Client::shadeMode() const + { + return shade_mode; + } + +inline QPixmap Client::icon() const + { + return icon_pix; + } + +inline QPixmap Client::miniIcon() const + { + return miniicon_pix; + } + +inline QRect Client::geometryRestore() const + { + return geom_restore; + } + +inline Client::MaximizeMode Client::maximizeModeRestore() const + { + return maxmode_restore; + } + +inline Client::MaximizeMode Client::maximizeMode() const + { + return max_mode; + } + +inline bool Client::skipTaskbar( bool from_outside ) const + { + return from_outside ? original_skip_taskbar : skip_taskbar; + } + +inline bool Client::skipPager() const + { + return skip_pager; + } + +inline bool Client::keepAbove() const + { + return keep_above; + } + +inline bool Client::keepBelow() const + { + return keep_below; + } + +inline bool Client::shape() const + { + return is_shape; + } + + +inline bool Client::isFullScreen() const + { + return fullscreen_mode != FullScreenNone; + } + +inline bool Client::isModal() const + { + return modal; + } + +inline bool Client::hasNETSupport() const + { + return info->hasNETSupport(); + } + +inline Colormap Client::colormap() const + { + return cmap; + } + +inline pid_t Client::pid() const + { + return info->pid(); + } + +inline void Client::invalidateLayer() + { + in_layer = UnknownLayer; + } + +inline bool Client::isIconicState() const + { + return mapping_state == IconicState; + } + +inline bool Client::isNormalState() const + { + return mapping_state == NormalState; + } + +inline bool Client::isManaged() const + { + return mapping_state != WithdrawnState; + } + +inline QCString Client::windowRole() const + { + return window_role; + } + +inline QRect Client::geometry() const + { + return frame_geometry; + } + +inline QSize Client::size() const + { + return frame_geometry.size(); + } + +inline QPoint Client::pos() const + { + return frame_geometry.topLeft(); + } + +inline int Client::x() const + { + return frame_geometry.x(); + } + +inline int Client::y() const + { + return frame_geometry.y(); + } + +inline int Client::width() const + { + return frame_geometry.width(); + } + +inline int Client::height() const + { + return frame_geometry.height(); + } + +inline QRect Client::rect() const + { + return QRect( 0, 0, width(), height()); + } + +inline QPoint Client::clientPos() const + { + return QPoint( border_left, border_top ); + } + +inline QSize Client::clientSize() const + { + return client_size; + } + +inline void Client::setGeometry( const QRect& r, ForceGeometry_t force ) + { + setGeometry( r.x(), r.y(), r.width(), r.height(), force ); + } + +inline void Client::move( const QPoint & p, ForceGeometry_t force ) + { + move( p.x(), p.y(), force ); + } + +inline void Client::plainResize( const QSize& s, ForceGeometry_t force ) + { + plainResize( s.width(), s.height(), force ); + } + +inline void Client::resizeWithChecks( const QSize& s, ForceGeometry_t force ) + { + resizeWithChecks( s.width(), s.height(), force ); + } + +inline bool Client::hasUserTimeSupport() const + { + return info->userTime() != -1U; + } + +inline bool Client::ignoreFocusStealing() const + { + return ignore_focus_stealing; + } + +inline const WindowRules* Client::rules() const + { + return &client_rules; + } + +KWIN_PROCEDURE( CheckIgnoreFocusStealingProcedure, cl->ignore_focus_stealing = options->checkIgnoreFocusStealing( cl )); + +inline Window Client::moveResizeGrabWindow() const + { + return move_resize_grab_window; + } + +inline KShortcut Client::shortcut() const + { + return _shortcut; + } + +inline bool Client::isBMP() + { + return isBMP_; + } + +inline void Client::setBMP(bool b) + { + isBMP_ = b; + } + +inline void Client::removeRule( Rules* rule ) + { + client_rules.remove( rule ); + } + +#ifdef NDEBUG +inline +kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; } +inline +kndbgstream& operator<<( kndbgstream& stream, const ClientList& ) { return stream; } +inline +kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; } +#else +kdbgstream& operator<<( kdbgstream& stream, const Client* ); +kdbgstream& operator<<( kdbgstream& stream, const ClientList& ); +kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& ); +#endif + +KWIN_COMPARE_PREDICATE( WindowMatchPredicate, Window, cl->window() == value ); +KWIN_COMPARE_PREDICATE( FrameIdMatchPredicate, Window, cl->frameId() == value ); +KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Window, cl->wrapperId() == value ); + +} // namespace + +#endif diff --git a/kwin/clients/Makefile.am b/kwin/clients/Makefile.am new file mode 100644 index 000000000..6d00b0001 --- /dev/null +++ b/kwin/clients/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = plastik b2 default keramik laptop modernsystem quartz redmond web +# need to be ported: kwmtheme (almost done) +# only for testing: test + +messages: rc.cpp + $(EXTRACTRC) `find . -name \*.ui` >> rc.cpp + $(XGETTEXT) `find . -name \*.cpp` -o $(podir)/kwin_clients.pot + -rm rc.cpp diff --git a/kwin/clients/PORTING b/kwin/clients/PORTING new file mode 100644 index 000000000..9a5fb9f6b --- /dev/null +++ b/kwin/clients/PORTING @@ -0,0 +1,159 @@ +It's suggested you check sources of some KDE CVS decoration if in doubts or in need of an example. +Also, the API is documented in the .h header files. + +Makefile.am: +- Change kwin_ to kwin3_ (in LDFLAGS, LIBADD, kde_module_LTLIBRARIES, SOURCES). +- Make sure LDFLAGS contains $(KDE_PLUGIN) and -module . +- Add -lkdecorations to LIBADD. +- Do NOT rename the directory where the .desktop file is installed ( $(kde_datadir)/kwin/ ). + +.desktop file: +- Change kwin_ to kwin3_ in X-KDE-Library. + +Sources: +- There are no kwin/something.h includes, and don't use the KWinInternal namespace. +- Use QToolTip instead of KWinToolTip. +- Use QButton instead of KWinButton, QToolButton instead of KWinToolButton and QWidget + instead of KWinWidgetButton. +- For tooltips, use simply QToolTip::add(). +- Change Client* to MyClient* (or whatever is your main client class) in your MyButton. +- Pass parent->widget() to QButton constructor in your MyButton constructor. +- Make your MyClient class inherit from KDecoration instead of Client. +- Make MyClient constructor take KDecorationBridge* and KDecorationFactory* as arguments, + and pass these arguments to KDecoration constructor. +- Except for data members initialization, make the constructor empty, move everything + to void MyClient::init(). +- As the first thing in init(), call createMainWidget(); if your client class took some + flags such as WResizeNoErase, pass them to this function. +- Then, do 'widget()->installEventFilter( this );'. +- Implement MyClient::eventFilter() - as MyClient is now no longer QWidget, you need the event + filter to call all the functions that used to be called directly. Usually, it's something + like: +===== +bool MyClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Wheel: + wheelEvent( static_cast< QWheelEvent* >( e )); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Show: + showEvent( static_cast< QShowEvent* >( e ) ); + return true; + + default: + return false; + } +} +===== +- In MyClient, 'this' will have to be often replaced with 'widget()', pay special attention + to cases where this won't cause compile error (e.g. in connect() calls, which take QObject* ). +- Also, many calls may need 'widget()->' prepended. +- Layout is created in init(), so call createLayout() directly there (if it's implemented). +- Remove calls to Client methods (Client::resizeEvent() and so on). +- Replace Options:: with KDecorationOptions:: . +- Replace 'options' with 'options()' in MyClient (which is KDecoration::options()), if often used + outside of MyClient, you may want to create (this assumes your code is in its namespace): +===== +inline const KDecorationOptions* options() { return KDecoration::options(); } +===== +- Options for colors need 'Color' prepended (e.g. 'ColorButtonBg'). +- Replace miniIcon() with getting the right pixmap from icon() (usually + 'icon().pixmap( QIconSet::Small, QIconSet::Normal )' ). +- Replace stickyChange() with desktopChange(), and test isOnAllDesktops(). +- Replace Sticky with OnAllDestops. +- Replace iconify with minimize. +- Change activeChange(bool) to activeChange(), and use isActive() to check the state. + Similar for desktopChange, captionChange(), iconChange(), maximizeChange(). +- Replace 'contextHelp()' with 'showContextHelp()'. +- WindowWrapperShowEvent() is gone, simply use showEvent() filtered by the event filter if needed. +- Change 'animateIconifyOrDeiconify()' to 'animateMinize()', if it's empty, simply remove it. + Make sure it doesn't reenter the event loop (no kapp->processEvents()). +- Buttons should use explicit setCursor() if they don't want cursor set by mousePosition(). + I.e. usually call setCursor( ArrowCursor ) in your MyButton. +- In the part where you insert windowWrapper() into the layout, i.e. something like +===== + layout->addWidget( windowWrapper()); +===== + replace it with something like +===== + if( isPreview()) + layout->addWidget( new QLabel( i18n( "<center><b>MyDecoration</b></center>" ), widget())); + else + layout->addItem( new QSpacerItem( 0, 0 )); +===== +- Implement MyClient::minimumSize(). +- Handling maximization - to change vertical or horizontal maximalization, use e.g. + 'maximize( maximizeMode() ^ MaximizeVertical', to change normal maximalization, i.e. after + left-clicking on the button, use + 'maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull );' (which also + means that there's also no maximize() slot). + Also, if your decoration button has only two visual states representing the maximalization state, + it's recommended that it shows the maximized state only for MaximizeFull state. +- Make sure the decoration matches the window state after init() is finished, that is, that + the buttons represent correctly the maximalization, on-all-desktops etc. states. As the + simplest solution, you can call maximizeChange(), desktopChange(), etc. at the end + of init(). +- Use 'titlebarDblClickOperation()' for performing the application after doubleclicking + the titlebar. +- Implement borders() returning the width of the top,left,right and bottom border. You may + check values like 'maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows()' + to check whether you can disable some borders completely. + Note that your painting code must of course match these sizes. +- If your code uses XGrabServer() or XUnGrabServer(), replace them with (un)grabXServer(). +- In cases where you call some function from the KDecoration API that can possibly destroy + the decoration (e.g. showWindowMenu() or closeWindow()), make sure to use exists() if some more + code will follow this call. Refer to showWindowMenu() documentation for an example. +- Create class MyFactory inheriting from KDecorationFactory, and move the code that was + in 'extern "C"' to it: From init() to constructor, from deinit() to destructor, from allocate() + or create() to createDecoration(). Pass the KDecorationBridge* argument and 'this' to created + MyClient objects. If createDecoration() needs to know the window type (e.g. it used the tool + argument), use windowType() similarly like in KDecoration, and pass it the KDecorationBridge* + argument. +- Add something like this: +===== +extern "C" +{ + KDecorationFactory *create_factory() + { + return new MyNamespace::MyFactory(); + } +} +===== +- The reset handling has changed: There's no signal resetClients(), and no + slotResetAllClientsDelayed(). If your MyClient has some slotReset(), make it + reset( unsigned long ), where the argument is mask of things that have changed ( SettingXYZ ). + If you have some global function that handles resetting, make it + MyFactory::reset( unsigned long ). Try to minimize the effects of the changed things, + e.g. if only the color setting has changed, doing a repaint is often enough, and there's no need + to recreate the decorations. If you need to recreate the decorations, return true + from MyFactory::reset(), otherwise, you may call resetDecorations() to call reset() in all + MyClient instances. +- Implement resize() to resize the decoration to the given size + (usually 'widget()->resize( s );' is enough). +- Review mousePosition() if it's implemented. Position constants need 'Position' prepended, + e.g. Top -> PositionTop. +- Note that you cannot use "appdata" with KStandardDirs, as the decoration will be used + also in other applications than kwin. +- Implement all missing pure virtual functions. For mousePosition(), you may call + KDecoration::mousePosition() if it's sufficient. diff --git a/kwin/clients/REQUIREMENTS_FOR_CVS b/kwin/clients/REQUIREMENTS_FOR_CVS new file mode 100644 index 000000000..778e2fe2d --- /dev/null +++ b/kwin/clients/REQUIREMENTS_FOR_CVS @@ -0,0 +1,20 @@ +If you are looking to include a C++ KWin style client in CVS make sure you +follow the following requirements: + +A) You must follow the current color scheme for all decorations. *No* fixed +pixmaps are allowed for the clients. If you wish to draw your decorations +use as few shades as possible, then use kpixmap2bitmap in kdegraphics +to convert them into individual bitmaps. Once this is done you can +draw the bitmaps using a colorgroup with kColorBitmaps. + +If your client is just a set of pixmaps that doesn't follow any of the options +I suggest you make a KWM theme so the user gets those options to +configure the pixmaps and look. Making a plain pixmapped dedicated style +makes no sense since it is less configurable than KWM themes and cannot follow +client plugin options. + +B) You must follow at least the color settings in the Options class. + +Daniel M. Duley +mosfet@kde.org + diff --git a/kwin/clients/b2/Makefile.am b/kwin/clients/b2/Makefile.am new file mode 100644 index 000000000..88a9b1608 --- /dev/null +++ b/kwin/clients/b2/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_b2.la + +kwin3_b2_la_SOURCES = b2client.cpp +kwin3_b2_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +# kwin_b2_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) +kwin3_b2_la_LIBADD = ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = b2client.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = b2.desktop + +EXTRA_DIST = $(lnk_DATA) + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/b2/b2.desktop b/kwin/clients/b2/b2.desktop new file mode 100644 index 000000000..c368d7fa7 --- /dev/null +++ b/kwin/clients/b2/b2.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=B II +Name[hi]=बी II +Name[lo]= B II +Name[te]=బి II +Name[th]=ชุดตà¸à¹à¸•à¹ˆà¸‡ B II +X-KDE-Library=kwin3_b2 diff --git a/kwin/clients/b2/b2client.cpp b/kwin/clients/b2/b2client.cpp new file mode 100644 index 000000000..6a5d821b2 --- /dev/null +++ b/kwin/clients/b2/b2client.cpp @@ -0,0 +1,1433 @@ +/* + * B-II KWin Client + * + * Changes: + * Customizable button positions by Karol Szwed <gallium@kde.org> + * + * Thin frame in fixed size windows, titlebar gradient support, accessibility + * improvements, customizable menu double click action and button hover + * effects are + * Copyright (c) 2003,2004 Luciano Montanaro <mikelima@cirulla.net> + */ + +#include "b2client.h" +#include <qapplication.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kicontheme.h> +#include <kiconeffect.h> +#include <kdrawutil.h> +#include <klocale.h> +#include <kconfig.h> +#include <qbitmap.h> +#include <qlabel.h> +#include <qtooltip.h> + +#include <X11/Xlib.h> + +namespace B2 { + +#include "bitmaps.h" + +enum { + Norm = 0, + Hover, Down, INorm, IHover, IDown, + NumStates +}; + +enum { + P_CLOSE = 0, + P_MAX, P_NORMALIZE, P_ICONIFY, P_PINUP, P_MENU, P_HELP, P_SHADE, P_RESIZE, + P_NUM_BUTTON_TYPES +}; + +#define NUM_PIXMAPS (P_NUM_BUTTON_TYPES * NumStates) + +static KPixmap *pixmap[NUM_PIXMAPS]; + +// active +#define PIXMAP_A(i) (pixmap[(i) * NumStates + Norm]) +// active, hover +#define PIXMAP_AH(i) (pixmap[(i) * NumStates + Hover]) +// active, down +#define PIXMAP_AD(i) (pixmap[(i) * NumStates + Down]) +// inactive +#define PIXMAP_I(i) (pixmap[(i) * NumStates + INorm]) +// inactive, hover +#define PIXMAP_IH(i) (pixmap[(i) * NumStates + IHover]) +// inactive, down +#define PIXMAP_ID(i) (pixmap[(i) * NumStates + IDown]) + +static KPixmap* titleGradient[2] = {0, 0}; + +static int thickness = 4; // Frame thickness +static int buttonSize = 16; + +enum DblClickOperation { + NoOp = 0, + MinimizeOp, + ShadeOp, + CloseOp +}; + +static DblClickOperation menu_dbl_click_op = NoOp; + +static bool pixmaps_created = false; +static bool colored_frame = false; +static bool do_draw_handle = true; +static bool drawSmallBorders = false; + +// ===================================== + +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new B2::B2ClientFactory(); +} + +// ===================================== + +static inline const KDecorationOptions *options() +{ + return KDecoration::options(); +} + +static void redraw_pixmaps(); + +static void read_config(B2ClientFactory *f) +{ + // Force button size to be in a reasonable range. + // If the frame width is large, the button size must be large too. + buttonSize = (QFontMetrics(options()->font(true)).height() + 1) & 0x3e; + if (buttonSize < 16) buttonSize = 16; + + KConfig conf("kwinb2rc"); + conf.setGroup("General"); + colored_frame = conf.readBoolEntry("UseTitleBarBorderColors", false); + do_draw_handle = conf.readBoolEntry("DrawGrabHandle", true); + drawSmallBorders = !options()->moveResizeMaximizedWindows(); + + QString opString = conf.readEntry("MenuButtonDoubleClickOperation", "NoOp"); + if (opString == "Close") { + menu_dbl_click_op = B2::CloseOp; + } else if (opString == "Minimize") { + menu_dbl_click_op = B2::MinimizeOp; + } else if (opString == "Shade") { + menu_dbl_click_op = B2::ShadeOp; + } else { + menu_dbl_click_op = B2::NoOp; + } + + switch (options()->preferredBorderSize(f)) { + case KDecoration::BorderTiny: + thickness = 2; + break; + case KDecoration::BorderLarge: + thickness = 5; + break; + case KDecoration::BorderVeryLarge: + thickness = 8; + break; + case KDecoration::BorderHuge: + thickness = 12; + break; + case KDecoration::BorderVeryHuge: + case KDecoration::BorderOversized: + case KDecoration::BorderNormal: + default: + thickness = 4; + } +} + +static void drawB2Rect(KPixmap *pix, const QColor &primary, bool down) +{ + QPainter p(pix); + QColor hColor = primary.light(150); + QColor lColor = primary.dark(150); + + if (down) qSwap(hColor, lColor); + + if (QPixmap::defaultDepth() > 8) { + KPixmapEffect::gradient(*pix, hColor, lColor, + KPixmapEffect::DiagonalGradient); + } + else + pix->fill(primary); + int x2 = pix->width() - 1; + int y2 = pix->height() - 1; + p.setPen(lColor); + p.drawLine(0, 0, x2, 0); + p.drawLine(0, 0, 0, y2); + p.drawLine(1, x2 - 1, x2 - 1, y2 - 1); + p.drawLine(x2 - 1, 1, x2 - 1, y2 - 1); + p.setPen(hColor); + p.drawRect(1, 1, x2, y2); + +} + +QPixmap* kwin_get_menu_pix_hack() +{ + //return menu_pix; FIXME + return PIXMAP_A(P_MENU); +} + +static void create_pixmaps() +{ + if (pixmaps_created) + return; + pixmaps_created = true; + + int i; + int bsize = buttonSize - 2; + if (bsize < 16) bsize = 16; + + for (i = 0; i < NUM_PIXMAPS; i++) { + pixmap[i] = new KPixmap; + switch (i / NumStates) { + case P_MAX: // will be initialized by copying P_CLOSE + case P_RESIZE: + break; + case P_ICONIFY: + pixmap[i]->resize(10, 10); break; + case P_SHADE: + case P_CLOSE: + pixmap[i]->resize(bsize, bsize); break; + default: + pixmap[i]->resize(16, 16); break; + } + } + + // there seems to be no way to load X bitmaps from data properly, so + // we need to create new ones for each mask :P + QBitmap pinupMask(16, 16, pinup_mask_bits, true); + PIXMAP_A(P_PINUP)->setMask(pinupMask); + PIXMAP_I(P_PINUP)->setMask(pinupMask); + QBitmap pindownMask(16, 16, pindown_mask_bits, true); + PIXMAP_AD(P_PINUP)->setMask(pindownMask); + PIXMAP_ID(P_PINUP)->setMask(pindownMask); + + QBitmap menuMask(16, 16, menu_mask_bits, true); + for (i = 0; i < NumStates; i++) + pixmap[P_MENU * NumStates + i]->setMask(menuMask); + + QBitmap helpMask(16, 16, help_mask_bits, true); + for (i = 0; i < NumStates; i++) + pixmap[P_HELP * NumStates + i]->setMask(helpMask); + + QBitmap normalizeMask(16, 16, true); + // draw normalize icon mask + QPainter mask; + mask.begin(&normalizeMask); + + QBrush one(Qt::color1); + mask.fillRect(normalizeMask.width() - 12, normalizeMask.height() - 12, + 12, 12, one); + mask.fillRect(0, 0, 10, 10, one); + mask.end(); + + for (i = 0; i < NumStates; i++) + pixmap[P_NORMALIZE * NumStates + i]->setMask(normalizeMask); + + QBitmap shadeMask(bsize, bsize, true); + mask.begin(&shadeMask); + mask.fillRect(0, 0, bsize, 6, one); + mask.end(); + for (i = 0; i < NumStates; i++) + pixmap[P_SHADE * NumStates + i]->setMask(shadeMask); + + titleGradient[0] = 0; + titleGradient[1] = 0; + + redraw_pixmaps(); +} + +static void delete_pixmaps() +{ + for (int i = 0; i < NUM_PIXMAPS; i++) { + delete pixmap[i]; + pixmap[i] = 0; + } + for (int i = 0; i < 2; i++) { + delete titleGradient[i]; + titleGradient[i] = 0; + } + pixmaps_created = false; +} + +// ===================================== + +B2ClientFactory::B2ClientFactory() +{ + read_config(this); + create_pixmaps(); +} + +B2ClientFactory::~B2ClientFactory() +{ + delete_pixmaps(); +} + +KDecoration *B2ClientFactory::createDecoration(KDecorationBridge *b) +{ + return new B2::B2Client(b, this); +} + +bool B2ClientFactory::reset(unsigned long changed) +{ + bool needsReset = SettingColors ? true : false; + // TODO Do not recreate decorations if it is not needed. Look at + // ModernSystem for how to do that + read_config(this); + if (changed & SettingFont) { + delete_pixmaps(); + create_pixmaps(); + needsReset = true; + } + redraw_pixmaps(); + // For now just return true. + return needsReset; +} + +bool B2ClientFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonResize: + return true; + default: + return false; + }; +} + +QValueList< B2ClientFactory::BorderSize > B2ClientFactory::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderTiny << BorderNormal << + BorderLarge << BorderVeryLarge << BorderHuge; +} + +// ===================================== + +void B2Client::maxButtonClicked() +{ + maximize(button[BtnMax]->last_button); +} + +void B2Client::shadeButtonClicked() +{ + setShade(!isSetShade()); +} + +void B2Client::resizeButtonPressed() +{ + performWindowOperation(ResizeOp); +} + +B2Client::B2Client(KDecorationBridge *b, KDecorationFactory *f) + : KDecoration(b, f), bar_x_ofs(0), in_unobs(0) +{ +} + +void B2Client::init() +{ + const QString tips[] = { + i18n("Menu"), + isOnAllDesktops() ? + i18n("Not on all desktops") : i18n("On all desktops"), + i18n("Minimize"), i18n("Maximize"), + i18n("Close"), i18n("Help"), + isSetShade() ? i18n("Unshade") : i18n("Shade"), + i18n("Resize") + }; + + // Check this early, otherwise the preview will be rendered badly. + resizable = isResizable(); + + createMainWidget(WResizeNoErase | WRepaintNoErase); + widget()->installEventFilter(this); + + widget()->setBackgroundMode(NoBackground); + + // Set button pointers to NULL so we know what has been created + for (int i = 0; i < BtnCount; i++) + button[i] = NULL; + + g = new QGridLayout(widget(), 3, 3); + // Left and right border width + + leftSpacer = new QSpacerItem(thickness, 16, + QSizePolicy::Fixed, QSizePolicy::Expanding); + rightSpacer = new QSpacerItem(thickness, 16, + QSizePolicy::Fixed, QSizePolicy::Expanding); + + g->addItem(leftSpacer, 1, 0); + g->addItem(rightSpacer, 1, 2); + + // Top border height + topSpacer = new QSpacerItem(10, buttonSize + 4, + QSizePolicy::Expanding, QSizePolicy::Fixed); + g->addItem(topSpacer, 0, 1); + + // Bottom border height. + bottomSpacer = new QSpacerItem(10, + thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Fixed); + g->addItem(bottomSpacer, 2, 1); + if (isPreview()) { + QLabel *previewLabel = new QLabel( + i18n("<b><center>B II preview</center></b>"), + widget()); + g->addWidget(previewLabel, 1, 1); + + } else { + g->addItem(new QSpacerItem(0, 0), 1, 1); + } + + // titlebar + g->setRowSpacing(0, buttonSize + 4); + + titlebar = new B2Titlebar(this); + titlebar->setMinimumWidth(buttonSize + 4); + titlebar->setFixedHeight(buttonSize + 4); + + QBoxLayout *titleLayout = new QBoxLayout(titlebar, + QBoxLayout::LeftToRight, 0, 1, 0); + titleLayout->addSpacing(3); + + if (options()->customButtonPositions()) { + addButtons(options()->titleButtonsLeft(), tips, titlebar, titleLayout); + titleLayout->addItem(titlebar->captionSpacer); + addButtons(options()->titleButtonsRight(), tips, titlebar, titleLayout); + } else { + addButtons("MSH", tips, titlebar, titleLayout); + titleLayout->addItem(titlebar->captionSpacer); + addButtons("IAX", tips, titlebar, titleLayout); + } + + titleLayout->addSpacing(3); + + QColor c = options()->colorGroup(KDecoration::ColorTitleBar, isActive()). + color(QColorGroup::Button); + + for (int i = 0; i < BtnCount; i++) { + if (button[i]) + button[i]->setBg(c); + } + + titlebar->updateGeometry(); + positionButtons(); + titlebar->recalcBuffer(); + titlebar->installEventFilter(this); +} + +void B2Client::addButtons(const QString& s, const QString tips[], + B2Titlebar* tb, QBoxLayout* titleLayout) +{ + if (s.length() <= 0) + return; + + for (unsigned int i = 0; i < s.length(); i++) { + switch (s[i].latin1()) { + case 'M': // Menu button + if (!button[BtnMenu]) { + button[BtnMenu] = new B2Button(this, tb, tips[BtnMenu], + LeftButton | RightButton); + button[BtnMenu]->setPixmaps(P_MENU); + button[BtnMenu]->setUseMiniIcon(); + connect(button[BtnMenu], SIGNAL(pressed()), + this, SLOT(menuButtonPressed())); + titleLayout->addWidget(button[BtnMenu]); + } + break; + case 'S': // Sticky button + if (!button[BtnSticky]) { + button[BtnSticky] = new B2Button(this, tb, tips[BtnSticky]); + button[BtnSticky]->setPixmaps(P_PINUP); + button[BtnSticky]->setToggle(); + button[BtnSticky]->setDown(isOnAllDesktops()); + connect(button[BtnSticky], SIGNAL(clicked()), + this, SLOT(toggleOnAllDesktops())); + titleLayout->addWidget(button[BtnSticky]); + } + break; + case 'H': // Help button + if (providesContextHelp() && (!button[BtnHelp])) { + button[BtnHelp] = new B2Button(this, tb, tips[BtnHelp]); + button[BtnHelp]->setPixmaps(P_HELP); + connect(button[BtnHelp], SIGNAL(clicked()), + this, SLOT(showContextHelp())); + titleLayout->addWidget(button[BtnHelp]); + } + break; + case 'I': // Minimize button + if (isMinimizable() && (!button[BtnIconify])) { + button[BtnIconify] = new B2Button(this, tb,tips[BtnIconify]); + button[BtnIconify]->setPixmaps(P_ICONIFY); + connect(button[BtnIconify], SIGNAL(clicked()), + this, SLOT(minimize())); + titleLayout->addWidget(button[BtnIconify]); + } + break; + case 'A': // Maximize button + if (isMaximizable() && (!button[BtnMax])) { + button[BtnMax] = new B2Button(this, tb, tips[BtnMax], + LeftButton | MidButton | RightButton); + button[BtnMax]->setPixmaps(maximizeMode() == MaximizeFull ? + P_NORMALIZE : P_MAX); + connect(button[BtnMax], SIGNAL(clicked()), + this, SLOT(maxButtonClicked())); + titleLayout->addWidget(button[BtnMax]); + } + break; + case 'X': // Close button + if (isCloseable() && !button[BtnClose]) { + button[BtnClose] = new B2Button(this, tb, tips[BtnClose]); + button[BtnClose]->setPixmaps(P_CLOSE); + connect(button[BtnClose], SIGNAL(clicked()), + this, SLOT(closeWindow())); + titleLayout->addWidget(button[BtnClose]); + } + break; + case 'L': // Shade button + if (isShadeable() && !button[BtnShade]) { + button[BtnShade] = new B2Button(this, tb, tips[BtnShade]); + button[BtnShade]->setPixmaps(P_SHADE); + connect(button[BtnShade], SIGNAL(clicked()), + this, SLOT(shadeButtonClicked())); + titleLayout->addWidget(button[BtnShade]); + } + break; + case 'R': // Resize button + if (resizable && !button[BtnResize]) { + button[BtnResize] = new B2Button(this, tb, tips[BtnResize]); + button[BtnResize]->setPixmaps(P_RESIZE); + connect(button[BtnResize], SIGNAL(pressed()), + this, SLOT(resizeButtonPressed())); + titleLayout->addWidget(button[BtnResize]); + } + break; + case '_': // Additional spacing + titleLayout->addSpacing(4); + break; + } + } +} + +bool B2Client::mustDrawHandle() const +{ + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return do_draw_handle && resizable; + } +} + +void B2Client::iconChange() +{ + if (button[BtnMenu]) + button[BtnMenu]->repaint(false); +} + +// Gallium: New button show/hide magic for customizable +// button positions. +void B2Client::calcHiddenButtons() +{ + // Hide buttons in this order: + // Shade, Sticky, Help, Resize, Maximize, Minimize, Close, Menu + B2Button* btnArray[] = { + button[BtnShade], button[BtnSticky], button[BtnHelp], button[BtnResize], + button[BtnMax], button[BtnIconify], button[BtnClose], button[BtnMenu] + }; + int minWidth = 120; + int currentWidth = width(); + int count = 0; + int i; + + // Determine how many buttons we need to hide + while (currentWidth < minWidth) { + currentWidth += buttonSize + 1; // Allow for spacer (extra 1pix) + count++; + } + // Bound the number of buttons to hide + if (count > BtnCount) count = BtnCount; + + // Hide the required buttons + for (i = 0; i < count; i++) { + if (btnArray[i] && btnArray[i]->isVisible()) + btnArray[i]->hide(); + } + // Show the rest of the buttons + for (i = count; i < BtnCount; i++) { + if (btnArray[i] && (!btnArray[i]->isVisible())) + btnArray[i]->show(); + } +} + +void B2Client::resizeEvent(QResizeEvent * /*e*/) +{ + calcHiddenButtons(); + titlebar->layout()->activate(); + positionButtons(); + + /* may be the resize cut off some space occupied by titlebar, which + was moved, so instead of reducing it, we first try to move it */ + titleMoveAbs(bar_x_ofs); + + doShape(); + widget()->repaint(); // the frame is misrendered without this +} + +void B2Client::captionChange() +{ + positionButtons(); + titleMoveAbs(bar_x_ofs); + doShape(); + titlebar->recalcBuffer(); + titlebar->repaint(false); +} + +void B2Client::paintEvent(QPaintEvent* e) +{ + QPainter p(widget()); + + KDecoration::ColorType frameColorGroup = colored_frame ? + KDecoration::ColorTitleBar : KDecoration::ColorFrame; + + QRect t = titlebar->geometry(); + + // Frame height, this is used a lot of times + int fHeight = height() - t.height(); + + // distance from the bottom border - it is different if window is resizable + int bb = mustDrawHandle() ? 4 : 0; + int bDepth = thickness + bb; + + QColorGroup fillColor = options()->colorGroup(frameColorGroup, isActive()); + QBrush fillBrush(options()->color(frameColorGroup, isActive())); + + // outer frame rect + p.drawRect(0, t.bottom() - thickness + 1, + width(), fHeight - bb + thickness); + + if (thickness >= 2) { + // inner window rect + p.drawRect(thickness - 1, t.bottom(), + width() - 2 * (thickness - 1), fHeight - bDepth + 2); + + if (thickness >= 3) { + // frame shade panel + qDrawShadePanel(&p, 1, t.bottom() - thickness + 2, + width() - 2, fHeight - 2 - bb + thickness, fillColor, false); + if (thickness == 4) { + p.setPen(fillColor.background()); + p.drawRect(thickness - 2, t.bottom() - 1, + width() - 2 * (thickness - 2), fHeight + 4 - bDepth); + } else if (thickness > 4) { + qDrawShadePanel(&p, thickness - 2, + t.bottom() - 1, width() - 2 * (thickness - 2), + fHeight + 4 - bDepth, fillColor, true); + if (thickness >= 5) { + // draw frame interior + p.fillRect(2, t.bottom() - thickness + 3, + width() - 4, thickness - 4, fillBrush); + p.fillRect(2, height() - bDepth + 2, + width() - 4, thickness - 4, fillBrush); + p.fillRect(2, t.bottom() - 1, + thickness - 4, fHeight - bDepth + 4, fillBrush); + p.fillRect(width() - thickness + 2, t.bottom() - 1, + thickness - 4, fHeight - bDepth + 4, fillBrush); + } + } + } + } + + // bottom handle rect + if (mustDrawHandle()) { + p.setPen(Qt::black); + int hx = width() - 40; + int hw = 40; + + p.drawLine(width() - 1, height() - thickness - 4, + width() - 1, height() - 1); + p.drawLine(hx, height() - 1, width() - 1, height() - 1); + p.drawLine(hx, height() - 4, hx, height() - 1); + + p.fillRect(hx + 1, height() - thickness - 3, + hw - 2, thickness + 2, fillBrush); + + p.setPen(fillColor.dark()); + p.drawLine(width() - 2, height() - thickness - 4, + width() - 2, height() - 2); + p.drawLine(hx + 1, height() - 2, width() - 2, height() - 2); + + p.setPen(fillColor.light()); + p.drawLine(hx + 1, height() - thickness - 2, + hx + 1, height() - 3); + p.drawLine(hx + 1, height() - thickness - 3, + width() - 3, height() - thickness - 3); + } + + /* OK, we got a paint event, which means parts of us are now visible + which were not before. We try the titlebar if it is currently fully + obscured, and if yes, try to unobscure it, in the hope that some + of the parts which we just painted were in the titlebar area. + It can happen, that the titlebar, as it got the FullyObscured event + had no chance of becoming partly visible. The problem is, that + we now might have the space available, but the titlebar gets no + visibilitinotify events until its state changes, so we just try + */ + if (titlebar->isFullyObscured()) { + /* We first see, if our repaint contained the titlebar area */ + QRegion reg(QRect(0, 0, width(), buttonSize + 4)); + reg = reg.intersect(e->region()); + if (!reg.isEmpty()) + unobscureTitlebar(); + } +} + +void B2Client::doShape() +{ + QRect t = titlebar->geometry(); + QRegion mask(widget()->rect()); + // top to the tilebar right + if (bar_x_ofs) { + // left from bar + mask -= QRect(0, 0, bar_x_ofs, t.height() - thickness); + // top left point + mask -= QRect(0, t.height() - thickness, 1, 1); + } + if (t.right() < width() - 1) { + mask -= QRect(width() - 1, + t.height() - thickness, 1, 1); //top right point + mask -= QRect(t.right() + 1, 0, + width() - t.right() - 1, t.height() - thickness); + } + // bottom right point + mask -= QRect(width() - 1, height() - 1, 1, 1); + if (mustDrawHandle()) { + // bottom left point + mask -= QRect(0, height() - 5, 1, 1); + // handle left point + mask -= QRect(width() - 40, height() - 1, 1, 1); + // bottom left + mask -= QRect(0, height() - 4, width() - 40, 4); + } else { + // bottom left point + mask -= QRect(0, height() - 1, 1, 1); + } + + setMask(mask); +} + +void B2Client::showEvent(QShowEvent *) +{ + calcHiddenButtons(); + positionButtons(); + doShape(); +} + +KDecoration::Position B2Client::mousePosition(const QPoint& p) const +{ + const int range = 16; + QRect t = titlebar->geometry(); + t.setHeight(buttonSize + 4 - thickness); + int ly = t.bottom(); + int lx = t.right(); + int bb = mustDrawHandle() ? 0 : 5; + + if (p.x() > t.right()) { + if (p.y() <= ly + range && p.x() >= width() - range) + return PositionTopRight; + else if (p.y() <= ly + thickness) + return PositionTop; + } else if (p.x() < bar_x_ofs) { + if (p.y() <= ly + range && p.x() <= range) + return PositionTopLeft; + else if (p.y() <= ly + thickness) + return PositionTop; + } else if (p.y() < ly) { + if (p.x() > bar_x_ofs + thickness && + p.x() < lx - thickness && p.y() > thickness) + return KDecoration::mousePosition(p); + if (p.x() > bar_x_ofs + range && p.x() < lx - range) + return PositionTop; + if (p.y() <= range) { + if (p.x() <= bar_x_ofs + range) + return PositionTopLeft; + else return PositionTopRight; + } else { + if (p.x() <= bar_x_ofs + range) + return PositionLeft; + else return PositionRight; + } + } + + if (p.y() >= height() - 8 + bb) { + /* the normal Client:: only wants border of 4 pixels */ + if (p.x() <= range) return PositionBottomLeft; + if (p.x() >= width() - range) return PositionBottomRight; + return PositionBottom; + } + + return KDecoration::mousePosition(p); +} + +void B2Client::titleMoveAbs(int new_ofs) +{ + if (new_ofs < 0) new_ofs = 0; + if (new_ofs + titlebar->width() > width()) { + new_ofs = width() - titlebar->width(); + } + if (bar_x_ofs != new_ofs) { + bar_x_ofs = new_ofs; + positionButtons(); + doShape(); + widget()->repaint(0, 0, width(), buttonSize + 4, false); + titlebar->repaint(false); + } +} + +void B2Client::titleMoveRel(int xdiff) +{ + titleMoveAbs(bar_x_ofs + xdiff); +} + +void B2Client::desktopChange() +{ + bool on = isOnAllDesktops(); + if (B2Button *b = button[BtnSticky]) { + b->setDown(on); + QToolTip::remove(b); + QToolTip::add(b, + on ? i18n("Not on all desktops") : i18n("On all desktops")); + } +} + +void B2Client::maximizeChange() +{ + bool m = maximizeMode() == MaximizeFull; + if (button[BtnMax]) { + button[BtnMax]->setPixmaps(m ? P_NORMALIZE : P_MAX); + button[BtnMax]->repaint(); + QToolTip::remove(button[BtnMax]); + QToolTip::add(button[BtnMax], + m ? i18n("Restore") : i18n("Maximize")); + } + bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Minimum); + + g->activate(); + doShape(); + widget()->repaint(false); +} + +void B2Client::activeChange() +{ + widget()->repaint(false); + titlebar->repaint(false); + + QColor c = options()->colorGroup( + KDecoration::ColorTitleBar, isActive()).color(QColorGroup::Button); + + for (int i = 0; i < BtnCount; i++) + if (button[i]) { + button[i]->setBg(c); + button[i]->repaint(false); + } +} + +void B2Client::shadeChange() +{ + bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Minimum); + g->activate(); + doShape(); + if (B2Button *b = button[BtnShade]) { + QToolTip::remove(b); + QToolTip::add(b, isSetShade() ? i18n("Unshade") : i18n("Shade")); + } +} + +QSize B2Client::minimumSize() const +{ + int left, right, top, bottom; + borders(left, right, top, bottom); + return QSize(left + right + 2 * buttonSize, top + bottom); +} + +void B2Client::resize(const QSize& s) +{ + widget()->resize(s); +} + +void B2Client::borders(int &left, int &right, int &top, int &bottom) const +{ + left = right = thickness; + top = buttonSize + 4; + bottom = thickness + (mustDrawHandle() ? 4 : 0); +} + +void B2Client::menuButtonPressed() +{ + static B2Client *lastClient = NULL; + + bool dbl = (lastClient == this && + time.elapsed() <= QApplication::doubleClickInterval()); + lastClient = this; + time.start(); + if (!dbl) { + KDecorationFactory* f = factory(); + QRect menuRect = button[BtnMenu]->rect(); + QPoint menuTop = button[BtnMenu]->mapToGlobal(menuRect.topLeft()); + QPoint menuBottom = button[BtnMenu]->mapToGlobal(menuRect.bottomRight()); + showWindowMenu(QRect(menuTop, menuBottom)); + if (!f->exists(this)) // 'this' was destroyed + return; + button[BtnMenu]->setDown(false); + } else { + switch (menu_dbl_click_op) { + case B2::MinimizeOp: + minimize(); + break; + case B2::ShadeOp: + setShade(!isSetShade()); + break; + case B2::CloseOp: + closeWindow(); + break; + case B2::NoOp: + default: + break; + } + } +} + +void B2Client::unobscureTitlebar() +{ + /* we just noticed, that we got obscured by other windows + so we look at all windows above us (stacking_order) merging their + masks, intersecting it with our titlebar area, and see if we can + find a place not covered by any window */ + if (in_unobs) { + return; + } + in_unobs = 1; + QRegion reg(QRect(0,0,width(), buttonSize + 4)); + reg = unobscuredRegion(reg); + if (!reg.isEmpty()) { + // there is at least _one_ pixel from our title area, which is not + // obscured, we use the first rect we find + // for a first test, we use boundingRect(), later we may refine + // to rect(), and search for the nearest, or biggest, or smthg. + titleMoveAbs(reg.boundingRect().x()); + } + in_unobs = 0; +} + +static void redraw_pixmaps() +{ + int i; + QColorGroup aGrp = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColorGroup iGrp = options()->colorGroup(KDecoration::ColorButtonBg, false); + + // close + drawB2Rect(PIXMAP_A(P_CLOSE), aGrp.button(), false); + drawB2Rect(PIXMAP_AH(P_CLOSE), aGrp.button(), true); + drawB2Rect(PIXMAP_AD(P_CLOSE), aGrp.button(), true); + + drawB2Rect(PIXMAP_I(P_CLOSE), iGrp.button(), false); + drawB2Rect(PIXMAP_IH(P_CLOSE), iGrp.button(), true); + drawB2Rect(PIXMAP_ID(P_CLOSE), iGrp.button(), true); + + // shade + KPixmap thinBox; + thinBox.resize(buttonSize - 2, 6); + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 2); + bool is_down = ((i & 1) == 1); + KPixmap *pix = pixmap[P_SHADE * NumStates + i]; + QColor color = is_act ? aGrp.button() : iGrp.button(); + drawB2Rect(&thinBox, color, is_down); + pix->fill(Qt::black); + bitBlt(pix, 0, 0, &thinBox, + 0, 0, thinBox.width(), thinBox.height(), Qt::CopyROP, true); + } + + // maximize + for (i = 0; i < NumStates; i++) { + *pixmap[P_MAX * NumStates + i] = *pixmap[P_CLOSE * NumStates + i]; + pixmap[P_MAX * NumStates + i]->detach(); + } + + // normalize + iconify + KPixmap smallBox; + smallBox.resize(10, 10); + KPixmap largeBox; + largeBox.resize(12, 12); + + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 3); + bool is_down = (i == Down || i == IDown); + KPixmap *pix = pixmap[P_NORMALIZE * NumStates + i]; + drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down); + drawB2Rect(&largeBox, is_act ? aGrp.button() : iGrp.button(), is_down); + pix->fill(options()->color(KDecoration::ColorTitleBar, is_act)); + bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox, + 0, 0, 12, 12, Qt::CopyROP, true); + bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + + bitBlt(pixmap[P_ICONIFY * NumStates + i], 0, 0, + &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + } + + // resize + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 3); + bool is_down = (i == Down || i == IDown); + *pixmap[P_RESIZE * NumStates + i] = *pixmap[P_CLOSE * NumStates + i]; + pixmap[P_RESIZE * NumStates + i]->detach(); + drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down); + bitBlt(pixmap[P_RESIZE * NumStates + i], + 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + } + + + QPainter p; + // x for close + menu + help + for (int j = 0; j < 3; j++) { + int pix; + unsigned const char *light, *dark; + switch (j) { + case 0: + pix = P_CLOSE; light = close_white_bits; dark = close_dgray_bits; + break; + case 1: + pix = P_MENU; light = menu_white_bits; dark = menu_dgray_bits; + break; + default: + pix = P_HELP; light = help_light_bits; dark = help_dark_bits; + break; + } + int off = (pixmap[pix * NumStates]->width() - 16) / 2; + for (i = 0; i < NumStates; i++) { + p.begin(pixmap[pix * NumStates + i]); + kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, off, off, 16, 16, true, + light, NULL, NULL, dark, NULL, NULL); + p.end(); + } + } + + // pin + for (i = 0; i < NumStates; i++) { + bool isDown = (i == Down || i == IDown); + unsigned const char *white = isDown ? pindown_white_bits : pinup_white_bits; + unsigned const char *gray = isDown ? pindown_gray_bits : pinup_gray_bits; + unsigned const char *dgray =isDown ? pindown_dgray_bits : pinup_dgray_bits; + p.begin(pixmap[P_PINUP * NumStates + i]); + kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, 0, 0, 16, 16, true, white, + gray, NULL, dgray, NULL, NULL); + p.end(); + } + + // Apply the hilight effect to the 'Hover' icons + KIconEffect ie; + QPixmap hilighted; + for (i = 0; i < P_NUM_BUTTON_TYPES; i++) { + int offset = i * NumStates; + hilighted = ie.apply(*pixmap[offset + Norm], + KIcon::Small, KIcon::ActiveState); + *pixmap[offset + Hover] = hilighted; + + hilighted = ie.apply(*pixmap[offset + INorm], + KIcon::Small, KIcon::ActiveState); + *pixmap[offset + IHover] = hilighted; + } + + + // Create the titlebar gradients + if (QPixmap::defaultDepth() > 8) { + QColor titleColor[4] = { + options()->color(KDecoration::ColorTitleBar, true), + options()->color(KDecoration::ColorFrame, true), + + options()->color(KDecoration::ColorTitleBlend, false), + options()->color(KDecoration::ColorTitleBar, false) + }; + + if (colored_frame) { + titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true); + titleColor[1] = options()->color(KDecoration::ColorTitleBar, true); + } + + for (i = 0; i < 2; i++) { + if (titleColor[2 * i] != titleColor[2 * i + 1]) { + if (!titleGradient[i]) { + titleGradient[i] = new KPixmap; + } + titleGradient[i]->resize(64, buttonSize + 3); + KPixmapEffect::gradient(*titleGradient[i], + titleColor[2 * i], titleColor[2 * i + 1], + KPixmapEffect::VerticalGradient); + } else { + delete titleGradient[i]; + titleGradient[i] = 0; + } + } + } +} + +void B2Client::positionButtons() +{ + QFontMetrics fm(options()->font(isActive())); + QString cap = caption(); + if (cap.length() < 5) // make sure the titlebar has sufficiently wide + cap = "XXXXX"; // area for dragging the window + int textLen = fm.width(cap); + + QRect t = titlebar->captionSpacer->geometry(); + int titleWidth = titlebar->width() - t.width() + textLen + 2; + if (titleWidth > width()) titleWidth = width(); + + titlebar->resize(titleWidth, buttonSize + 4); + titlebar->move(bar_x_ofs, 0); +} + +// Transparent bound stuff. + +static QRect *visible_bound; +static QPointArray bound_shape; + +bool B2Client::drawbound(const QRect& geom, bool clear) +{ + if (clear) { + if (!visible_bound) return true; + } + + if (!visible_bound) { + visible_bound = new QRect(geom); + QRect t = titlebar->geometry(); + int frameTop = geom.top() + t.bottom(); + int barLeft = geom.left() + bar_x_ofs; + int barRight = barLeft + t.width() - 1; + if (barRight > geom.right()) barRight = geom.right(); + // line width is 5 pixels, so compensate for the 2 outer pixels (#88657) + QRect g = geom; + g.setLeft( g.left() + 2 ); + g.setTop( g.top() + 2 ); + g.setRight( g.right() - 2 ); + g.setBottom( g.bottom() - 2 ); + frameTop += 2; + barLeft += 2; + barRight -= 2; + + bound_shape.putPoints(0, 8, + g.left(), frameTop, + barLeft, frameTop, + barLeft, g.top(), + barRight, g.top(), + barRight, frameTop, + g.right(), frameTop, + g.right(), g.bottom(), + g.left(), g.bottom()); + } else { + *visible_bound = geom; + } + QPainter p(workspaceWidget()); + p.setPen(QPen(Qt::white, 5)); + p.setRasterOp(Qt::XorROP); + p.drawPolygon(bound_shape); + + if (clear) { + delete visible_bound; + visible_bound = 0; + } + return true; +} + +bool B2Client::eventFilter(QObject *o, QEvent *e) +{ + if (o != widget()) + return false; + switch (e->type()) { + case QEvent::Resize: + resizeEvent(static_cast< QResizeEvent* >(e)); + return true; + case QEvent::Paint: + paintEvent(static_cast< QPaintEvent* >(e)); + return true; + case QEvent::MouseButtonDblClick: + titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e)); + return true; + case QEvent::Wheel: + titlebar->wheelEvent(static_cast< QWheelEvent* >(e)); + return true; + case QEvent::MouseButtonPress: + processMousePressEvent(static_cast< QMouseEvent* >(e)); + return true; + case QEvent::Show: + showEvent(static_cast< QShowEvent* >(e)); + return true; + default: + break; + } + return false; +} + +// ===================================== + +B2Button::B2Button(B2Client *_client, QWidget *parent, + const QString& tip, const int realizeBtns) + : QButton(parent, 0), hover(false) +{ + setBackgroundMode(NoBackground); + setCursor(arrowCursor); + realizeButtons = realizeBtns; + client = _client; + useMiniIcon = false; + setFixedSize(buttonSize, buttonSize); + QToolTip::add(this, tip); +} + + +QSize B2Button::sizeHint() const +{ + return QSize(buttonSize, buttonSize); +} + +QSizePolicy B2Button::sizePolicy() const +{ + return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); +} + +void B2Button::drawButton(QPainter *p) +{ + KPixmap* gradient = titleGradient[client->isActive() ? 0 : 1]; + if (gradient) { + p->drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2); + } else { + p->fillRect(rect(), bg); + } + if (useMiniIcon) { + QPixmap miniIcon = client->icon().pixmap(QIconSet::Small, + client->isActive() ? QIconSet::Normal : QIconSet::Disabled); + p->drawPixmap((width() - miniIcon.width()) / 2, + (height() - miniIcon.height()) / 2, miniIcon); + } else { + int type; + if (client->isActive()) { + if (isOn() || isDown()) + type = Down; + else if (hover) + type = Hover; + else + type = Norm; + } else { + if (isOn() || isDown()) + type = IDown; + else if (hover) + type = IHover; + else + type = INorm; + } + p->drawPixmap((width() - icon[type]->width()) / 2, + (height() - icon[type]->height()) / 2, *icon[type]); + } +} + +void B2Button::setPixmaps(int button_id) +{ + button_id *= NumStates; + for (int i = 0; i < NumStates; i++) { + icon[i] = B2::pixmap[button_id + i]; + } + repaint(false); +} + +void B2Button::mousePressEvent(QMouseEvent * e) +{ + last_button = e->button(); + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button() & realizeButtons) ? LeftButton : NoButton, + e->state()); + QButton::mousePressEvent(&me); +} + +void B2Button::mouseReleaseEvent(QMouseEvent * e) +{ + last_button = e->button(); + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button() & realizeButtons) ? LeftButton : NoButton, + e->state()); + QButton::mouseReleaseEvent(&me); +} + +void B2Button::enterEvent(QEvent *e) +{ + hover = true; + repaint(false); + QButton::enterEvent(e); +} + +void B2Button::leaveEvent(QEvent *e) +{ + hover = false; + repaint(false); + QButton::leaveEvent(e); +} + +// ===================================== + +B2Titlebar::B2Titlebar(B2Client *parent) + : QWidget(parent->widget(), 0, WStyle_Customize | WRepaintNoErase), + client(parent), + set_x11mask(false), isfullyobscured(false), shift_move(false) +{ + setBackgroundMode(NoBackground); + captionSpacer = new QSpacerItem(buttonSize, buttonSize + 4, + QSizePolicy::Expanding, QSizePolicy::Fixed); +} + +bool B2Titlebar::x11Event(XEvent *e) +{ + if (!set_x11mask) { + set_x11mask = true; + XSelectInput(qt_xdisplay(), winId(), + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | + ButtonMotionMask | + EnterWindowMask | LeaveWindowMask | + FocusChangeMask | + ExposureMask | + PropertyChangeMask | + StructureNotifyMask | SubstructureRedirectMask | + VisibilityChangeMask); + } + switch (e->type) { + case VisibilityNotify: + isfullyobscured = false; + if (e->xvisibility.state == VisibilityFullyObscured) { + isfullyobscured = true; + client->unobscureTitlebar(); + } + break; + default: + break; + } + return QWidget::x11Event(e); +} + +void B2Titlebar::drawTitlebar(QPainter &p, bool state) +{ + KPixmap* gradient = titleGradient[state ? 0 : 1]; + + QRect t = rect(); + // black titlebar frame + p.setPen(Qt::black); + p.drawLine(0, 0, 0, t.bottom()); + p.drawLine(0, 0, t.right(), 0); + p.drawLine(t.right(), 0, t.right(), t.bottom()); + + // titlebar fill + const QColorGroup cg = + options()->colorGroup(KDecoration::ColorTitleBar, state); + QBrush brush(cg.background()); + if (gradient) brush.setPixmap(*gradient); + qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1, + cg, false, 1, 0, &brush); + + // and the caption + p.setPen(options()->color(KDecoration::ColorFont, state)); + p.setFont(options()->font(state)); + t = captionSpacer->geometry(); + p.drawText(t, AlignLeft | AlignVCenter, client->caption()); +} + +void B2Titlebar::recalcBuffer() +{ + titleBuffer.resize(width(), height()); + + QPainter p(&titleBuffer); + drawTitlebar(p, true); + oldTitle = caption(); +} + +void B2Titlebar::resizeEvent(QResizeEvent *) +{ + recalcBuffer(); + repaint(false); +} + + +void B2Titlebar::paintEvent(QPaintEvent *) +{ + if(client->isActive()) + bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(), + titleBuffer.height(), Qt::CopyROP, true); + else { + QPainter p(this); + drawTitlebar(p, false); + } +} + +void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton && e->y() < height()) { + client->titlebarDblClickOperation(); + } +} + +void B2Titlebar::wheelEvent(QWheelEvent *e) +{ + if (client->isSetShade() || rect().contains(e->pos())) + client->titlebarMouseWheelOperation( e->delta()); +} + +void B2Titlebar::mousePressEvent(QMouseEvent * e) +{ + shift_move = e->state() & ShiftButton; + if (shift_move) { + moveOffset = e->globalPos(); + } else { + e->ignore(); + } +} + +void B2Titlebar::mouseReleaseEvent(QMouseEvent * e) +{ + if (shift_move) shift_move = false; + else e->ignore(); +} + +void B2Titlebar::mouseMoveEvent(QMouseEvent * e) +{ + if (shift_move) { + int oldx = mapFromGlobal(moveOffset).x(); + int xdiff = e->globalPos().x() - moveOffset.x(); + moveOffset = e->globalPos(); + if (oldx >= 0 && oldx <= rect().right()) { + client->titleMoveRel(xdiff); + } + } else { + e->ignore(); + } +} + +} // namespace B2 + +#include "b2client.moc" + +// vim: sw=4 + diff --git a/kwin/clients/b2/b2client.h b/kwin/clients/b2/b2client.h new file mode 100644 index 000000000..008b65af3 --- /dev/null +++ b/kwin/clients/b2/b2client.h @@ -0,0 +1,166 @@ +/* + * B-II KWin Client + * + * Changes: + * Customizable button positions by Karol Szwed <gallium@kde.org> + * Ported to the kde3.2 API by Luciano Montanaro <mikelima@cirulla.net> + */ + +#ifndef __B2CLIENT_H +#define __B2CLIENT_H + +#include <qvariant.h> +#include <qdatetime.h> +#include <qbutton.h> +#include <qbitmap.h> +#include <kpixmap.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +class QSpacerItem; +class QBoxLayout; +class QGridLayout; + +namespace B2 { + +class B2Client; + +class B2Button : public QButton +{ +public: + B2Button(B2Client *_client=0, QWidget *parent=0, const QString& tip=NULL, const int realizeBtns = LeftButton); + ~B2Button() {}; + + void setBg(const QColor &c){bg = c;} + void setPixmaps(KPixmap *pix, KPixmap *pixDown, KPixmap *iPix, + KPixmap *iPixDown); + void setPixmaps(int button_id); + void setToggle(){setToggleType(Toggle);} + void setActive(bool on){setOn(on);} + void setUseMiniIcon(){useMiniIcon = true;} + QSize sizeHint() const; + QSizePolicy sizePolicy() const; +protected: + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + + void mousePressEvent( QMouseEvent* e ); + void mouseReleaseEvent( QMouseEvent* e ); +private: + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + + bool useMiniIcon; + KPixmap *icon[6]; + QColor bg; //only use one color (the rest is pixmap) so forget QPalette ;) + +public: + B2Client* client; + ButtonState last_button; + int realizeButtons; + bool hover; +}; + +class B2Titlebar : public QWidget +{ + friend class B2Client; +public: + B2Titlebar(B2Client *parent); + ~B2Titlebar(){;} + bool isFullyObscured() const {return isfullyobscured;} + void recalcBuffer(); + QSpacerItem *captionSpacer; +protected: + void paintEvent( QPaintEvent* ); + bool x11Event(XEvent *e); + void mouseDoubleClickEvent( QMouseEvent * ); + void wheelEvent(QWheelEvent *); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *ev); +private: + void drawTitlebar(QPainter &p, bool state); + + B2Client *client; + QString oldTitle; + KPixmap titleBuffer; + QPoint moveOffset; + bool set_x11mask; + bool isfullyobscured; + bool shift_move; +}; + +class B2Client : public KDecoration +{ + Q_OBJECT + friend class B2Titlebar; +public: + B2Client(KDecorationBridge *b, KDecorationFactory *f); + ~B2Client(){;} + void init(); + void unobscureTitlebar(); + void titleMoveAbs(int new_ofs); + void titleMoveRel(int xdiff); + // transparent stuff + virtual bool drawbound(const QRect& geom, bool clear); +protected: + void resizeEvent( QResizeEvent* ); + void paintEvent( QPaintEvent* ); + void showEvent( QShowEvent* ); + void windowWrapperShowEvent( QShowEvent* ); + void captionChange(); + void desktopChange(); + void shadeChange(); + void activeChange(); + void maximizeChange(); + void iconChange(); + void doShape(); + Position mousePosition( const QPoint& p ) const; + void resize(const QSize&); + void borders(int &, int &, int &, int &) const; + QSize minimumSize() const; + bool eventFilter(QObject *, QEvent *); +private slots: + void menuButtonPressed(); + //void slotReset(); + void maxButtonClicked(); + void shadeButtonClicked(); + void resizeButtonPressed(); +private: + void addButtons(const QString& s, const QString tips[], + B2Titlebar* tb, QBoxLayout* titleLayout); + void positionButtons(); + void calcHiddenButtons(); + bool mustDrawHandle() const; + + enum ButtonType{BtnMenu=0, BtnSticky, BtnIconify, BtnMax, BtnClose, + BtnHelp, BtnShade, BtnResize, BtnCount}; + B2Button* button[BtnCount]; + QGridLayout *g; + // Border spacers + QSpacerItem *topSpacer; + QSpacerItem *bottomSpacer; + QSpacerItem *leftSpacer; + QSpacerItem *rightSpacer; + B2Titlebar *titlebar; + int bar_x_ofs; + int in_unobs; + QTime time; + bool resizable; +}; + +class B2ClientFactory : public QObject, public KDecorationFactory +{ +public: + B2ClientFactory(); + virtual ~B2ClientFactory(); + virtual KDecoration *createDecoration(KDecorationBridge *); + virtual bool reset(unsigned long changed); + virtual bool supports( Ability ability ); + QValueList< B2ClientFactory::BorderSize > borderSizes() const; +}; + +} + +#endif diff --git a/kwin/clients/b2/bitmaps.h b/kwin/clients/b2/bitmaps.h new file mode 100644 index 000000000..2f610f4b2 --- /dev/null +++ b/kwin/clients/b2/bitmaps.h @@ -0,0 +1,98 @@ +#ifndef __STDCLIENT_BITMAPS_H +#define __STDCLIENT_BITMAPS_H + +/** + * The standard client has the capability to color it's titlebar buttons + * according to the new color scheme. In order to do this it needs a bitmap + * for each shade which it draws into a pixmap with the appropriate color. + * These are all the bitmaps. + */ + +static const unsigned char close_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x04, 0x08, 0x08, 0x04, 0x10, 0x02, + 0x20, 0x01, 0x40, 0x00, 0x40, 0x00, 0x20, 0x01, 0x10, 0x02, 0x08, 0x04, + 0x04, 0x08, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char close_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30, 0x30, 0x18, 0x60, 0x0c, + 0xc0, 0x06, 0x80, 0x03, 0x80, 0x03, 0xc0, 0x06, 0x60, 0x0c, 0x30, 0x18, + 0x18, 0x30, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0x04, 0x20, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x1f, 0xf0, 0x3f, 0xf0, 0x3f, + 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xf0, 0x0f, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0xc0, 0x31, 0xc0, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xc0, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char help_mask_bits[] = { + 0x00,0x00,0x00,0x00,0xe0,0x03,0xf0,0x07,0x70,0x0e,0x60,0x0e,0x00,0x0f,0x80, + 0x07,0xc0,0x03,0xc0,0x01,0x80,0x01,0xc0,0x00,0xc0,0x01,0x80,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x4c,0x0b,0x08,0x58,0x65,0x09,0x08,0x90,0x00,0x00, + 0x00,0x09,0x04,0x00,0x00,0x72,0x6f,0x6f,0x74,0x00,0x24,0x31,0x24,0x47,0x6b, + 0x65,0x44,0x78,0x63 }; + +static const unsigned char help_dark_bits[] = { + 0x00,0x00,0x00,0x00,0xe0,0x03,0x30,0x06,0x30,0x06,0x00,0x06,0x00,0x03,0x80, + 0x01,0xc0,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0xc0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x65,0x64,0x28,0x29,0x00,0x00,0x00,0x00,0x90,0x00,0x00, + 0x00,0x21,0x00,0x00,0x00,0x34,0xfe,0x12,0x2b,0x00,0x00,0xff,0xff,0x58,0xc0, + 0x01,0x2b,0x45,0xfe }; + +static const unsigned char help_light_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x01,0x40,0x08,0x60,0x08,0x00,0x0c,0x00, + 0x06,0x00,0x03,0x00,0x01,0x80,0x01,0x00,0x00,0x00,0x01,0x80,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x4c,0x0b,0x08,0x58,0x65,0x09,0x08,0x90,0x00,0x00, + 0x00,0x09,0x04,0x00,0x00,0x72,0x6f,0x6f,0x74,0x00,0x24,0x31,0x24,0x47,0x6b, + 0x65,0x44,0x78,0x63 }; + +#endif + diff --git a/kwin/clients/b2/config/Makefile.am b/kwin/clients/b2/config/Makefile.am new file mode 100644 index 000000000..9ad21f7d4 --- /dev/null +++ b/kwin/clients/b2/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_b2_config.la + +kwin_b2_config_la_SOURCES = config.cpp +kwin_b2_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_b2_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/b2/config/config.cpp b/kwin/clients/b2/config/config.cpp new file mode 100644 index 000000000..d16a90307 --- /dev/null +++ b/kwin/clients/b2/config/config.cpp @@ -0,0 +1,165 @@ +/* + * This file contains the B2 configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <qvbox.h> +#include <klocale.h> + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new B2Config(conf, parent)); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +B2Config::B2Config( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_b2_config"); + b2Config = new KConfig("kwinb2rc"); + gb = new QVBox(parent); + + cbColorBorder = new QCheckBox( + i18n("Draw window frames using &titlebar colors"), gb); + QWhatsThis::add(cbColorBorder, + i18n("When selected, the window borders " + "are drawn using the titlebar colors; otherwise, they are " + "drawn using normal border colors.")); + + // Grab Handle + showGrabHandleCb = new QCheckBox( + i18n("Draw &resize handle"), gb); + QWhatsThis::add(showGrabHandleCb, + i18n("When selected, decorations are drawn with a \"grab handle\" " + "in the bottom right corner of the windows; " + "otherwise, no grab handle is drawn.")); + + // Double click menu option support + actionsGB = new QHGroupBox(i18n("Actions Settings"), gb); + QLabel *menuDblClickLabel = new QLabel(actionsGB); + menuDblClickLabel->setText(i18n("Double click on menu button:")); + menuDblClickOp = new QComboBox(actionsGB); + menuDblClickOp->insertItem(i18n("Do Nothing")); + menuDblClickOp->insertItem(i18n("Minimize Window")); + menuDblClickOp->insertItem(i18n("Shade Window")); + menuDblClickOp->insertItem(i18n("Close Window")); + + QWhatsThis::add(menuDblClickOp, + i18n("An action can be associated to a double click " + "of the menu button. Leave it to none if in doubt.")); + + // Load configuration options + load(conf); + + // Ensure we track user changes properly + connect(cbColorBorder, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged())); + connect(showGrabHandleCb, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged())); + connect(menuDblClickOp, SIGNAL(activated(int)), + this, SLOT(slotSelectionChanged())); + // Make the widgets visible in kwindecoration + gb->show(); +} + + +B2Config::~B2Config() +{ + delete b2Config; + delete gb; +} + + +void B2Config::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void B2Config::load(KConfig * /*conf*/) +{ + b2Config->setGroup("General"); + + bool override = b2Config->readBoolEntry("UseTitleBarBorderColors", false); + cbColorBorder->setChecked(override); + + override = b2Config->readBoolEntry( "DrawGrabHandle", true ); + showGrabHandleCb->setChecked(override); + + QString returnString = b2Config->readEntry( + "MenuButtonDoubleClickOperation", "NoOp"); + + int op; + if (returnString == "Close") { + op = 3; + } else if (returnString == "Shade") { + op = 2; + } else if (returnString == "Minimize") { + op = 1; + } else { + op = 0; + } + + menuDblClickOp->setCurrentItem(op); + +} + +static QString opToString(int op) +{ + switch (op) { + case 1: + return "Minimize"; + case 2: + return "Shade"; + case 3: + return "Close"; + case 0: + default: + return "NoOp"; + } +} + + +// Saves the configurable options to the kwinrc config file +void B2Config::save(KConfig * /*conf*/) +{ + b2Config->setGroup("General"); + b2Config->writeEntry("UseTitleBarBorderColors", cbColorBorder->isChecked()); + b2Config->writeEntry("DrawGrabHandle", showGrabHandleCb->isChecked()); + b2Config->writeEntry("MenuButtonDoubleClickOperation", + opToString(menuDblClickOp->currentItem())); + // Ensure others trying to read this config get updated + b2Config->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void B2Config::defaults() +{ + cbColorBorder->setChecked(false); + showGrabHandleCb->setChecked(true); + menuDblClickOp->setCurrentItem(0); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/b2/config/config.h b/kwin/clients/b2/config/config.h new file mode 100644 index 000000000..9985f3f4f --- /dev/null +++ b/kwin/clients/b2/config/config.h @@ -0,0 +1,50 @@ +/* + * This file contains the B2 configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef _KDE_B2CONFIG_H +#define _KDE_B2CONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qhgroupbox.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <kconfig.h> + +class B2Config: public QObject +{ + Q_OBJECT + + public: + B2Config( KConfig* conf, QWidget* parent ); + ~B2Config(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig* b2Config; + QCheckBox* cbColorBorder; + QCheckBox* showGrabHandleCb; + QHGroupBox* actionsGB; + QComboBox* menuDblClickOp; + QWidget* gb; +}; + +#endif + +// vim: ts=4 diff --git a/kwin/clients/default/Makefile.am b/kwin/clients/default/Makefile.am new file mode 100644 index 000000000..3f9b1e7e7 --- /dev/null +++ b/kwin/clients/default/Makefile.am @@ -0,0 +1,14 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_default.la + +kwin3_default_la_SOURCES = kdedefault.cpp +kwin3_default_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_default_la_LIBADD = $(LIB_KDECORE) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = kdedefault.h + diff --git a/kwin/clients/default/config/Makefile.am b/kwin/clients/default/config/Makefile.am new file mode 100644 index 000000000..a6e09522a --- /dev/null +++ b/kwin/clients/default/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_default_config.la + +kwin_default_config_la_SOURCES = config.cpp +kwin_default_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_default_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/default/config/config.cpp b/kwin/clients/default/config/config.cpp new file mode 100644 index 000000000..2ad494fa9 --- /dev/null +++ b/kwin/clients/default/config/config.cpp @@ -0,0 +1,131 @@ +/* + * + * KDE2 Default configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <kdialog.h> +#include <klocale.h> +#include <qpixmap.h> +#include <qvbox.h> + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new KDEDefaultConfig(conf, parent)); + } +} + +// NOTE: +// 'conf' is a pointer to the kwindecoration modules open kwin config, +// and is by default set to the "Style" group. +// 'parent' is the parent of the QObject, which is a VBox inside the +// Configure tab in kwindecoration + +KDEDefaultConfig::KDEDefaultConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_clients"); + highcolor = QPixmap::defaultDepth() > 8; + gb = new QVBox( parent ); + gb->setSpacing( KDialog::spacingHint() ); + + cbShowStipple = new QCheckBox( i18n("Draw titlebar &stipple effect"), gb ); + QWhatsThis::add( cbShowStipple, + i18n("When selected, active titlebars are drawn " + "with a stipple (dotted) effect; otherwise, they are " + "drawn without the stipple.")); + + cbShowGrabBar = new QCheckBox( i18n("Draw g&rab bar below windows"), gb ); + QWhatsThis::add( cbShowGrabBar, + i18n("When selected, decorations are drawn with a \"grab bar\" " + "below windows; otherwise, no grab bar is drawn.")); + + // Only show the gradient checkbox for highcolor displays + if (highcolor) + { + cbUseGradients = new QCheckBox( i18n("Draw &gradients"), gb ); + QWhatsThis::add( cbUseGradients, + i18n("When selected, decorations are drawn with gradients " + "for high-color displays; otherwise, no gradients are drawn.") ); + } + + // Load configuration options + load( conf ); + + // Ensure we track user changes properly + connect( cbShowStipple, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + connect( cbShowGrabBar, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + if (highcolor) + connect( cbUseGradients, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + + // Make the widgets visible in kwindecoration + gb->show(); +} + + +KDEDefaultConfig::~KDEDefaultConfig() +{ + delete gb; +} + + +void KDEDefaultConfig::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void KDEDefaultConfig::load( KConfig* conf ) +{ + conf->setGroup("KDEDefault"); + bool override = conf->readBoolEntry( "ShowTitleBarStipple", true ); + cbShowStipple->setChecked( override ); + + override = conf->readBoolEntry( "ShowGrabBar", true ); + cbShowGrabBar->setChecked( override ); + + if (highcolor) { + override = conf->readBoolEntry( "UseGradients", true ); + cbUseGradients->setChecked( override ); + } +} + + +// Saves the configurable options to the kwinrc config file +void KDEDefaultConfig::save( KConfig* conf ) +{ + conf->setGroup("KDEDefault"); + conf->writeEntry( "ShowTitleBarStipple", cbShowStipple->isChecked() ); + conf->writeEntry( "ShowGrabBar", cbShowGrabBar->isChecked() ); + + if (highcolor) + conf->writeEntry( "UseGradients", cbUseGradients->isChecked() ); + // No need to conf->sync() - kwindecoration will do it for us +} + + +// Sets UI widget defaults which must correspond to style defaults +void KDEDefaultConfig::defaults() +{ + cbShowStipple->setChecked( true ); + cbShowGrabBar->setChecked( true ); + + if (highcolor) + cbUseGradients->setChecked( true ); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/default/config/config.h b/kwin/clients/default/config/config.h new file mode 100644 index 000000000..248d851df --- /dev/null +++ b/kwin/clients/default/config/config.h @@ -0,0 +1,49 @@ +/* + * + * KDE2 Default configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef _KDE_DEFAULT_CONFIG_H +#define _KDE_DEFAULT_CONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <kconfig.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qvbox.h> + +class KDEDefaultConfig: public QObject +{ + Q_OBJECT + + public: + KDEDefaultConfig( KConfig* conf, QWidget* parent ); + ~KDEDefaultConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + QCheckBox* cbShowStipple; + QCheckBox* cbShowGrabBar; + QCheckBox* cbUseGradients; + QVBox* gb; + bool highcolor; +}; + +#endif +// vim: ts=4 diff --git a/kwin/clients/default/kdedefault.cpp b/kwin/clients/default/kdedefault.cpp new file mode 100644 index 000000000..aeb1b2065 --- /dev/null +++ b/kwin/clients/default/kdedefault.cpp @@ -0,0 +1,1069 @@ +/* + * + * KDE2 Default KWin client + * + * Copyright (C) 1999, 2001 Daniel Duley <mosfet@kde.org> + * Matthias Ettrich <ettrich@kde.org> + * Karol Szwed <gallium@kde.org> + * + * Draws mini titlebars for tool windows. + * Many features are now customizable. + */ + +#include "kdedefault.h" + +#include <kconfig.h> +#include <kglobal.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kdrawutil.h> +#include <klocale.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qtooltip.h> +#include <qapplication.h> +#include <qlabel.h> +#include <kdebug.h> + +namespace Default +{ + +static const unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char close_bits[] = { + 0x00, 0x00, 0x84, 0x00, 0xce, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x78, 0x00, + 0xfc, 0x00, 0xce, 0x01, 0x84, 0x00, 0x00, 0x00}; + +static const unsigned char maximize_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, + 0x86, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char minmax_bits[] = { + 0x7f, 0x00, 0x7f, 0x00, 0x63, 0x00, 0xfb, 0x03, 0xfb, 0x03, 0x1f, 0x03, + 0x1f, 0x03, 0x18, 0x03, 0xf8, 0x03, 0xf8, 0x03}; + +static const unsigned char question_bits[] = { + 0x00, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00}; + +static const unsigned char above_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char above_off_bits[] = { + 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00 }; + +static const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00 }; + +static const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00 }; + +static const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x1f, 0xf0, 0x3f, 0xf0, 0x3f, + 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xf0, 0x0f, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0xc0, 0x31, 0xc0, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xc0, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// =========================================================================== + +static QPixmap* titlePix; +static KPixmap* titleBuffer; +static KPixmap* aUpperGradient; +static KPixmap* iUpperGradient; + +static KPixmap* pinDownPix; +static KPixmap* pinUpPix; +static KPixmap* ipinDownPix; +static KPixmap* ipinUpPix; + +static KPixmap* rightBtnUpPix[2]; +static KPixmap* rightBtnDownPix[2]; +static KPixmap* irightBtnUpPix[2]; +static KPixmap* irightBtnDownPix[2]; + +static KPixmap* leftBtnUpPix[2]; +static KPixmap* leftBtnDownPix[2]; +static KPixmap* ileftBtnUpPix[2]; +static KPixmap* ileftBtnDownPix[2]; + +static KDEDefaultHandler* clientHandler; +static int toolTitleHeight; +static int normalTitleHeight; +static int borderWidth; +static int grabBorderWidth; +static bool KDEDefault_initialized = false; +static bool useGradients; +static bool showGrabBar; +static bool showTitleBarStipple; + + +// =========================================================================== + +KDEDefaultHandler::KDEDefaultHandler() +{ + clientHandler = this; + readConfig( false ); + createPixmaps(); + KDEDefault_initialized = true; +} + + +KDEDefaultHandler::~KDEDefaultHandler() +{ + KDEDefault_initialized = false; + freePixmaps(); + clientHandler = NULL; +} + +KDecoration* KDEDefaultHandler::createDecoration( KDecorationBridge* b ) +{ + return new KDEDefaultClient( b, this ); +} + +bool KDEDefaultHandler::reset( unsigned long changed ) +{ + KDEDefault_initialized = false; + changed |= readConfig( true ); + if( changed & SettingColors ) + { // pixmaps need to be recreated + freePixmaps(); + createPixmaps(); + } + KDEDefault_initialized = true; + // SettingButtons is handled by KCommonDecoration + bool need_recreate = ( changed & ( SettingDecoration | SettingFont | SettingBorder )) != 0; + if( need_recreate ) // something else than colors changed + return true; + resetDecorations( changed ); + return false; +} + + +unsigned long KDEDefaultHandler::readConfig( bool update ) +{ + unsigned long changed = 0; + KConfig* conf = KGlobal::config(); + conf->setGroup("KDEDefault"); + + bool new_showGrabBar = conf->readBoolEntry("ShowGrabBar", true); + bool new_showTitleBarStipple = conf->readBoolEntry("ShowTitleBarStipple", true); + bool new_useGradients = conf->readBoolEntry("UseGradients", true); + int new_titleHeight = QFontMetrics(options()->font(true)).height(); + int new_toolTitleHeight = QFontMetrics(options()->font(true, true)).height()-2; + + int new_borderWidth; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + new_borderWidth = 8; + break; + case BorderVeryLarge: + new_borderWidth = 12; + break; + case BorderHuge: + new_borderWidth = 18; + break; + case BorderVeryHuge: + new_borderWidth = 27; + break; + case BorderOversized: + new_borderWidth = 40; + break; + case BorderTiny: + case BorderNormal: + default: + new_borderWidth = 4; + } + + if (new_titleHeight < 16) new_titleHeight = 16; + if (new_titleHeight < new_borderWidth) new_titleHeight = new_borderWidth; + if (new_toolTitleHeight < 12) new_toolTitleHeight = 12; + if (new_toolTitleHeight < new_borderWidth) new_toolTitleHeight = new_borderWidth; + + if( update ) + { + if( new_showGrabBar != showGrabBar + || new_titleHeight != normalTitleHeight + || new_toolTitleHeight != toolTitleHeight + || new_borderWidth != borderWidth ) + changed |= SettingDecoration; // need recreating the decoration + if( new_showTitleBarStipple != showTitleBarStipple + || new_useGradients != useGradients + || new_titleHeight != normalTitleHeight + || new_toolTitleHeight != toolTitleHeight ) + changed |= SettingColors; // just recreate the pixmaps and repaint + } + + showGrabBar = new_showGrabBar; + showTitleBarStipple = new_showTitleBarStipple; + useGradients = new_useGradients; + normalTitleHeight = new_titleHeight; + toolTitleHeight = new_toolTitleHeight; + borderWidth = new_borderWidth; + grabBorderWidth = (borderWidth > 15) ? borderWidth + 15 : 2*borderWidth; + return changed; +} + + +// This paints the button pixmaps upon loading the style. +void KDEDefaultHandler::createPixmaps() +{ + bool highcolor = useGradients && (QPixmap::defaultDepth() > 8); + + // Make the titlebar stipple optional + if (showTitleBarStipple) + { + QPainter p; + QPainter maskPainter; + int i, x, y; + titlePix = new QPixmap(132, normalTitleHeight+2); + QBitmap mask(132, normalTitleHeight+2); + mask.fill(Qt::color0); + + p.begin(titlePix); + maskPainter.begin(&mask); + maskPainter.setPen(Qt::color1); + for(i=0, y=2; i < 9; ++i, y+=4) + for(x=1; x <= 132; x+=3) + { + p.setPen(options()->color(ColorTitleBar, true).light(150)); + p.drawPoint(x, y); + maskPainter.drawPoint(x, y); + p.setPen(options()->color(ColorTitleBar, true).dark(150)); + p.drawPoint(x+1, y+1); + maskPainter.drawPoint(x+1, y+1); + } + maskPainter.end(); + p.end(); + titlePix->setMask(mask); + } else + titlePix = NULL; + + QColor activeTitleColor1(options()->color(ColorTitleBar, true)); + QColor activeTitleColor2(options()->color(ColorTitleBlend, true)); + + QColor inactiveTitleColor1(options()->color(ColorTitleBar, false)); + QColor inactiveTitleColor2(options()->color(ColorTitleBlend, false)); + + // Create titlebar gradient images if required + aUpperGradient = NULL; + iUpperGradient = NULL; + + if(highcolor) + { + // Create the titlebar gradients + if (activeTitleColor1 != activeTitleColor2) + { + aUpperGradient = new KPixmap; + aUpperGradient->resize(128, normalTitleHeight+2); + KPixmapEffect::gradient(*aUpperGradient, + activeTitleColor1, + activeTitleColor2, + KPixmapEffect::VerticalGradient); + } + + if (inactiveTitleColor1 != inactiveTitleColor2) + { + iUpperGradient = new KPixmap; + iUpperGradient->resize(128, normalTitleHeight+2); + + KPixmapEffect::gradient(*iUpperGradient, + inactiveTitleColor1, + inactiveTitleColor2, + KPixmapEffect::VerticalGradient); + } + } + + // Set the sticky pin pixmaps; + QColorGroup g; + QPainter p; + + // Active pins + g = options()->colorGroup( ColorButtonBg, true ); + pinUpPix = new KPixmap(); + pinUpPix->resize(16, 16); + p.begin( pinUpPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + pinUpPix->setMask( QBitmap(16, 16, pinup_mask_bits, true) ); + + pinDownPix = new KPixmap(); + pinDownPix->resize(16, 16); + p.begin( pinDownPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + pinDownPix->setMask( QBitmap(16, 16, pindown_mask_bits, true) ); + + // Inactive pins + g = options()->colorGroup( ColorButtonBg, false ); + ipinUpPix = new KPixmap(); + ipinUpPix->resize(16, 16); + p.begin( ipinUpPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + ipinUpPix->setMask( QBitmap(16, 16, pinup_mask_bits, true) ); + + ipinDownPix = new KPixmap(); + ipinDownPix->resize(16, 16); + p.begin( ipinDownPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + ipinDownPix->setMask( QBitmap(16, 16, pindown_mask_bits, true) ); + + // Create a title buffer for flicker-free painting + titleBuffer = new KPixmap(); + + // Cache all possible button states + leftBtnUpPix[true] = new KPixmap(); + leftBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + leftBtnDownPix[true] = new KPixmap(); + leftBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnUpPix[true] = new KPixmap(); + ileftBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnDownPix[true] = new KPixmap(); + ileftBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + + rightBtnUpPix[true] = new KPixmap(); + rightBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + rightBtnDownPix[true] = new KPixmap(); + rightBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + irightBtnUpPix[true] = new KPixmap(); + irightBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + irightBtnDownPix[true] = new KPixmap(); + irightBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + + leftBtnUpPix[false] = new KPixmap(); + leftBtnUpPix[false]->resize(toolTitleHeight, normalTitleHeight); + leftBtnDownPix[false] = new KPixmap(); + leftBtnDownPix[false]->resize(toolTitleHeight, normalTitleHeight); + ileftBtnUpPix[false] = new KPixmap(); + ileftBtnUpPix[false]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnDownPix[false] = new KPixmap(); + ileftBtnDownPix[false]->resize(normalTitleHeight, normalTitleHeight); + + rightBtnUpPix[false] = new KPixmap(); + rightBtnUpPix[false]->resize(toolTitleHeight, toolTitleHeight); + rightBtnDownPix[false] = new KPixmap(); + rightBtnDownPix[false]->resize(toolTitleHeight, toolTitleHeight); + irightBtnUpPix[false] = new KPixmap(); + irightBtnUpPix[false]->resize(toolTitleHeight, toolTitleHeight); + irightBtnDownPix[false] = new KPixmap(); + irightBtnDownPix[false]->resize(toolTitleHeight, toolTitleHeight); + + // Draw the button state pixmaps + g = options()->colorGroup( ColorTitleBar, true ); + drawButtonBackground( leftBtnUpPix[true], g, false ); + drawButtonBackground( leftBtnDownPix[true], g, true ); + drawButtonBackground( leftBtnUpPix[false], g, false ); + drawButtonBackground( leftBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorButtonBg, true ); + drawButtonBackground( rightBtnUpPix[true], g, false ); + drawButtonBackground( rightBtnDownPix[true], g, true ); + drawButtonBackground( rightBtnUpPix[false], g, false ); + drawButtonBackground( rightBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorTitleBar, false ); + drawButtonBackground( ileftBtnUpPix[true], g, false ); + drawButtonBackground( ileftBtnDownPix[true], g, true ); + drawButtonBackground( ileftBtnUpPix[false], g, false ); + drawButtonBackground( ileftBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorButtonBg, false ); + drawButtonBackground( irightBtnUpPix[true], g, false ); + drawButtonBackground( irightBtnDownPix[true], g, true ); + drawButtonBackground( irightBtnUpPix[false], g, false ); + drawButtonBackground( irightBtnDownPix[false], g, true ); +} + + +void KDEDefaultHandler::freePixmaps() +{ + // Free button pixmaps + if (rightBtnUpPix[true]) + delete rightBtnUpPix[true]; + if(rightBtnDownPix[true]) + delete rightBtnDownPix[true]; + if (irightBtnUpPix[true]) + delete irightBtnUpPix[true]; + if (irightBtnDownPix[true]) + delete irightBtnDownPix[true]; + + if (leftBtnUpPix[true]) + delete leftBtnUpPix[true]; + if(leftBtnDownPix[true]) + delete leftBtnDownPix[true]; + if (ileftBtnUpPix[true]) + delete ileftBtnUpPix[true]; + if (ileftBtnDownPix[true]) + delete ileftBtnDownPix[true]; + + if (rightBtnUpPix[false]) + delete rightBtnUpPix[false]; + if(rightBtnDownPix[false]) + delete rightBtnDownPix[false]; + if (irightBtnUpPix[false]) + delete irightBtnUpPix[false]; + if (irightBtnDownPix[false]) + delete irightBtnDownPix[false]; + + if (leftBtnUpPix[false]) + delete leftBtnUpPix[false]; + if(leftBtnDownPix[false]) + delete leftBtnDownPix[false]; + if (ileftBtnUpPix[false]) + delete ileftBtnUpPix[false]; + if (ileftBtnDownPix[false]) + delete ileftBtnDownPix[false]; + + // Title images + if (titleBuffer) + delete titleBuffer; + if (titlePix) + delete titlePix; + if (aUpperGradient) + delete aUpperGradient; + if (iUpperGradient) + delete iUpperGradient; + + // Sticky pin images + if (pinUpPix) + delete pinUpPix; + if (ipinUpPix) + delete ipinUpPix; + if (pinDownPix) + delete pinDownPix; + if (ipinDownPix) + delete ipinDownPix; +} + + +void KDEDefaultHandler::drawButtonBackground(KPixmap *pix, + const QColorGroup &g, bool sunken) +{ + QPainter p; + int w = pix->width(); + int h = pix->height(); + int x2 = w-1; + int y2 = h-1; + + bool highcolor = useGradients && (QPixmap::defaultDepth() > 8); + QColor c = g.background(); + + // Fill the background with a gradient if possible + if (highcolor) + KPixmapEffect::gradient(*pix, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + else + pix->fill(c); + + p.begin(pix); + // outer frame + p.setPen(g.mid()); + p.drawLine(0, 0, x2, 0); + p.drawLine(0, 0, 0, y2); + p.setPen(g.light()); + p.drawLine(x2, 0, x2, y2); + p.drawLine(0, x2, y2, x2); + p.setPen(g.dark()); + p.drawRect(1, 1, w-2, h-2); + p.setPen(sunken ? g.mid() : g.light()); + p.drawLine(2, 2, x2-2, 2); + p.drawLine(2, 2, 2, y2-2); + p.setPen(sunken ? g.light() : g.mid()); + p.drawLine(x2-2, 2, x2-2, y2-2); + p.drawLine(2, x2-2, y2-2, x2-2); +} + +QValueList< KDEDefaultHandler::BorderSize > KDEDefaultHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +bool KDEDefaultHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +// =========================================================================== + +KDEDefaultButton::KDEDefaultButton(ButtonType type, KDEDefaultClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode( QWidget::NoBackground ); + + isMouseOver = false; + deco = NULL; + large = !decoration()->isToolWindow(); +} + + +KDEDefaultButton::~KDEDefaultButton() +{ + if (deco) + delete deco; +} + + +void KDEDefaultButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap(0); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + +void KDEDefaultButton::setBitmap(const unsigned char *bitmap) +{ + delete deco; + deco = 0; + + if (bitmap) { + deco = new QBitmap(10, 10, bitmap, true); + deco->setMask( *deco ); + } +} + + +void KDEDefaultButton::drawButton(QPainter *p) +{ + if (!KDEDefault_initialized) + return; + + const bool active = decoration()->isActive(); + + if (deco) { + // Fill the button background with an appropriate button image + KPixmap btnbg; + + if (isLeft() ) { + if (isDown()) + btnbg = active ? + *leftBtnDownPix[large] : *ileftBtnDownPix[large]; + else + btnbg = active ? + *leftBtnUpPix[large] : *ileftBtnUpPix[large]; + } else { + if (isDown()) + btnbg = active ? + *rightBtnDownPix[large] : *irightBtnDownPix[large]; + else + btnbg = active ? + *rightBtnUpPix[large] : *irightBtnUpPix[large]; + } + + p->drawPixmap( 0, 0, btnbg ); + + } else if ( isLeft() ) { + + // Fill the button background with an appropriate color/gradient + // This is for sticky and menu buttons + KPixmap* grad = active ? aUpperGradient : iUpperGradient; + if (!grad) { + QColor c = KDecoration::options()->color(KDecoration::ColorTitleBar, active); + p->fillRect(0, 0, width(), height(), c ); + } else + p->drawPixmap( 0, 0, *grad, 0,1, width(), height() ); + + } else { + // Draw a plain background for menus or sticky buttons on RHS + QColor c = KDecoration::options()->color(KDecoration::ColorFrame, active); + p->fillRect(0, 0, width(), height(), c); + } + + + // If we have a decoration bitmap, then draw that + // otherwise we paint a menu button (with mini icon), or a sticky button. + if( deco ) { + // Select the appropriate button decoration color + bool darkDeco = qGray( KDecoration::options()->color( + isLeft() ? KDecoration::ColorTitleBar : KDecoration::ColorButtonBg, + active).rgb() ) > 127; + + if (isMouseOver) + p->setPen( darkDeco ? Qt::darkGray : Qt::lightGray ); + else + p->setPen( darkDeco ? Qt::black : Qt::white ); + + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco); + + } else { + KPixmap btnpix; + + if (type()==OnAllDesktopsButton) { + if (active) + btnpix = isOn() ? *pinDownPix : *pinUpPix; + else + btnpix = isOn() ? *ipinDownPix : *ipinUpPix; + } else + btnpix = decoration()->icon().pixmap( QIconSet::Small, QIconSet::Normal ); + + // Intensify the image if required + if (isMouseOver) { + btnpix = KPixmapEffect::intensity(btnpix, 0.8); + } + + // Smooth scale the pixmap for small titlebars + // This is slow, but we assume this isn't done too often + if ( width() < 16 ) { + btnpix.convertFromImage(btnpix.convertToImage().smoothScale(12, 12)); + p->drawPixmap( 0, 0, btnpix ); + } + else + p->drawPixmap( width()/2-8, height()/2-8, btnpix ); + } +} + + +void KDEDefaultButton::enterEvent(QEvent *e) +{ + isMouseOver=true; + repaint(false); + QButton::enterEvent(e); +} + + +void KDEDefaultButton::leaveEvent(QEvent *e) +{ + isMouseOver=false; + repaint(false); + QButton::leaveEvent(e); +} + + +// =========================================================================== + +KDEDefaultClient::KDEDefaultClient( KDecorationBridge* b, KDecorationFactory* f ) + : KCommonDecoration( b, f )/*, + m_closing(false)*/ +{ +} + +QString KDEDefaultClient::visibleName() const +{ + return i18n("KDE2"); +} + +QString KDEDefaultClient::defaultButtonsLeft() const +{ + return "MS"; +} + +QString KDEDefaultClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool KDEDefaultClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return true; + case DB_WindowMask: + return true; + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int KDEDefaultClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + return borderWidth; + + case LM_BorderBottom: + return mustDrawHandle() ? grabBorderWidth : borderWidth; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + return borderWidth; + + case LM_TitleEdgeTop: + return 3; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 1; + + case LM_TitleHeight: + return titleHeight; + + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + if ( !isToolWindow() ) + return borderWidth/2; + // fall though + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *KDEDefaultClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new KDEDefaultButton(MenuButton, this, "menu"); + case OnAllDesktopsButton: + return new KDEDefaultButton(OnAllDesktopsButton, this, "on_all_desktops"); + case HelpButton: + return new KDEDefaultButton(HelpButton, this, "help"); + case MinButton: + return new KDEDefaultButton(MinButton, this, "minimize"); + case MaxButton: + return new KDEDefaultButton(MaxButton, this, "maximize"); + case CloseButton: + return new KDEDefaultButton(CloseButton, this, "close"); + case AboveButton: + return new KDEDefaultButton(AboveButton, this, "above"); + case BelowButton: + return new KDEDefaultButton(BelowButton, this, "below"); + case ShadeButton: + return new KDEDefaultButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void KDEDefaultClient::init() +{ + // Finally, toolWindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight; + } + else { + titleHeight = normalTitleHeight; + } + + KCommonDecoration::init(); +} + +void KDEDefaultClient::reset( unsigned long changed) +{ + widget()->repaint(); + + KCommonDecoration::reset(changed); +} + +bool KDEDefaultClient::mustDrawHandle() const +{ + bool drawSmallBorders = !options()->moveResizeMaximizedWindows(); + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return showGrabBar && isResizable(); + } +} + +void KDEDefaultClient::paintEvent( QPaintEvent* ) +{ + if (!KDEDefault_initialized) + return; + + QColorGroup g; + int offset; + + KPixmap* upperGradient = isActive() ? aUpperGradient : iUpperGradient; + + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width() - 1; + int y2 = r.height() - 1; + int w = r.width(); + int h = r.height(); + + // Determine where to place the extended left titlebar + int leftFrameStart = (h > 42) ? y+titleHeight+26: y+titleHeight; + + // Determine where to make the titlebar color transition + r = titleRect(); + int rightOffset = r.x()+r.width()+1; + + // Create a disposable pixmap buffer for the titlebar + // very early before drawing begins so there is no lag + // during painting pixels. + titleBuffer->resize( rightOffset-3, titleHeight+1 ); + + // Draw an outer black frame + p.setPen(Qt::black); + p.drawRect(x,y,w,h); + + // Draw part of the frame that is the titlebar color + g = options()->colorGroup(ColorTitleBar, isActive()); + p.setPen(g.light()); + p.drawLine(x+1, y+1, rightOffset-1, y+1); + p.drawLine(x+1, y+1, x+1, leftFrameStart+borderWidth-4); + + // Draw titlebar colour separator line + p.setPen(g.dark()); + p.drawLine(rightOffset-1, y+1, rightOffset-1, titleHeight+2); + + p.fillRect(x+2, y+titleHeight+3, + borderWidth-4, leftFrameStart+borderWidth-y-titleHeight-8, + options()->color(ColorTitleBar, isActive() )); + + // Finish drawing the titlebar extension + p.setPen(Qt::black); + p.drawLine(x+1, leftFrameStart+borderWidth-4, x+borderWidth-2, leftFrameStart-1); + p.setPen(g.mid()); + p.drawLine(x+borderWidth-2, y+titleHeight+3, x+borderWidth-2, leftFrameStart-2); + + // Fill out the border edges + g = options()->colorGroup(ColorFrame, isActive()); + p.setPen(g.light()); + p.drawLine(rightOffset, y+1, x2-1, y+1); + p.drawLine(x+1, leftFrameStart+borderWidth-3, x+1, y2-1); + p.setPen(g.dark()); + p.drawLine(x2-1, y+1, x2-1, y2-1); + p.drawLine(x+1, y2-1, x2-1, y2-1); + + p.setPen(options()->color(ColorFrame, isActive())); + QPointArray a; + QBrush brush( options()->color(ColorFrame, isActive()), Qt::SolidPattern ); + p.setBrush( brush ); // use solid, yellow brush + a.setPoints( 4, x+2, leftFrameStart+borderWidth-4, + x+borderWidth-2, leftFrameStart, + x+borderWidth-2, y2-2, + x+2, y2-2); + p.drawPolygon( a ); + p.fillRect(x2-borderWidth+2, y+titleHeight+3, + borderWidth-3, y2-y-titleHeight-4, + options()->color(ColorFrame, isActive() )); + + // Draw the bottom handle if required + if (mustDrawHandle()) + { + if(w > 50) + { + qDrawShadePanel(&p, x+1, y2-grabBorderWidth+2, 2*borderWidth+12, grabBorderWidth-2, + g, false, 1, &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, x+2*borderWidth+13, y2-grabBorderWidth+2, w-4*borderWidth-26, grabBorderWidth-2, + g, false, 1, isActive() ? + &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, x2-2*borderWidth-12, y2-grabBorderWidth+2, 2*borderWidth+12, grabBorderWidth-2, + g, false, 1, &g.brush(QColorGroup::Mid)); + } else + qDrawShadePanel(&p, x+1, y2-grabBorderWidth+2, w-2, grabBorderWidth-2, + g, false, 1, isActive() ? + &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + offset = grabBorderWidth; + } else + { + p.fillRect(x+2, y2-borderWidth+2, w-4, borderWidth-3, + options()->color(ColorFrame, isActive() )); + offset = borderWidth; + } + + // Draw a frame around the wrapped widget. + p.setPen( g.dark() ); + p.drawRect( x+borderWidth-1, y+titleHeight+3, w-2*borderWidth+2, h-titleHeight-offset-2 ); + + // Draw the title bar. + r = titleRect(); + + // Obtain titlebar blend colours + QColor c1 = options()->color(ColorTitleBar, isActive() ); + QColor c2 = options()->color(ColorFrame, isActive() ); + + // Fill with frame color behind RHS buttons + p.fillRect( rightOffset, y+2, x2-rightOffset-1, titleHeight+1, c2); + + QPainter p2( titleBuffer, this ); + + // Draw the titlebar gradient + if (upperGradient) + p2.drawTiledPixmap(0, 0, rightOffset-3, titleHeight+1, *upperGradient); + else + p2.fillRect(0, 0, rightOffset-3, titleHeight+1, c1); + + // Draw the title text on the pixmap, and with a smaller font + // for toolwindows than the default. + QFont fnt = options()->font(true); + + if ( isToolWindow() ) + fnt.setPointSize( fnt.pointSize()-2 ); // Shrink font by 2pt + + p2.setFont( fnt ); + + // Draw the titlebar stipple if active and available + if (isActive() && titlePix) + { + QFontMetrics fm(fnt); + int captionWidth = fm.width(caption()); + if (caption().isRightToLeft()) + p2.drawTiledPixmap( r.x(), 0, r.width()-captionWidth-4, + titleHeight+1, *titlePix ); + else + p2.drawTiledPixmap( r.x()+captionWidth+3, 0, r.width()-captionWidth-4, + titleHeight+1, *titlePix ); + } + + p2.setPen( options()->color(ColorFont, isActive()) ); + p2.drawText(r.x(), 1, r.width()-1, r.height(), + (caption().isRightToLeft() ? AlignRight : AlignLeft) | AlignVCenter, + caption() ); + + bitBlt( widget(), 2, 2, titleBuffer ); + + p2.end(); + + // Ensure a shaded window has no unpainted areas + // Is this still needed? +#if 1 + p.setPen(c2); + p.drawLine(x+borderWidth, y+titleHeight+4, x2-borderWidth, y+titleHeight+4); +#endif +} + +QRegion KDEDefaultClient::cornerShape(WindowCorner corner) +{ + switch (corner) { + case WC_TopLeft: + return QRect(0, 0, 1, 1); + + case WC_TopRight: + return QRect(width()-1, 0, 1, 1); + + case WC_BottomLeft: + return QRect(0, height()-1, 1, 1); + + case WC_BottomRight: + return QRect(width()-1, height()-1, 1, 1); + + default: + return QRegion(); + } +} + +} // namespace + +// Extended KWin plugin interface +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new Default::KDEDefaultHandler(); +} + +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/default/kdedefault.h b/kwin/clients/default/kdedefault.h new file mode 100644 index 000000000..53ad23fa2 --- /dev/null +++ b/kwin/clients/default/kdedefault.h @@ -0,0 +1,103 @@ +/* + * + * KDE2 Default KWin client + * + * Copyright (C) 1999, 2001 Daniel Duley <mosfet@kde.org> + * Matthias Ettrich <ettrich@kde.org> + * Karol Szwed <gallium@kde.org> + * + * Draws mini titlebars for tool windows. + * Many features are now customizable. + */ + +#ifndef _KDE_DEFAULT_H +#define _KDE_DEFAULT_H + +#include <qbutton.h> +#include <qbitmap.h> +#include <qdatetime.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +class QSpacerItem; +class QBoxLayout; +class QGridLayout; + +namespace Default { + +class KDEDefaultClient; + +class KDEDefaultHandler: public KDecorationFactory +{ + public: + KDEDefaultHandler(); + ~KDEDefaultHandler(); + KDecoration* createDecoration( KDecorationBridge* b ); + bool reset( unsigned long changed ); + virtual QValueList< BorderSize > borderSizes() const; + virtual bool supports( Ability ability ); + + private: + unsigned long readConfig( bool update ); + void createPixmaps(); + void freePixmaps(); + void drawButtonBackground(KPixmap *pix, + const QColorGroup &g, bool sunken); +}; + + +// class KDEDefaultButton : public QButton, public KDecorationDefines +class KDEDefaultButton : public KCommonDecorationButton +{ + public: + KDEDefaultButton(ButtonType type, KDEDefaultClient *parent, const char *name); + ~KDEDefaultButton(); + + void reset(unsigned long changed); + + void setBitmap(const unsigned char *bitmap); + + protected: + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void drawButton(QPainter *p); + void drawButtonLabel(QPainter*) {;} + + QBitmap* deco; + bool large; + bool isMouseOver; +}; + + +class KDEDefaultClient : public KCommonDecoration +{ + public: + KDEDefaultClient( KDecorationBridge* b, KDecorationFactory* f ); + ~KDEDefaultClient() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual QRegion cornerShape(WindowCorner corner); + + void init(); + void reset( unsigned long changed ); + + protected: + void paintEvent( QPaintEvent* ); + + private: + bool mustDrawHandle() const; + int titleHeight; +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/keramik/Makefile.am b/kwin/clients/keramik/Makefile.am new file mode 100644 index 000000000..d5969643c --- /dev/null +++ b/kwin/clients/keramik/Makefile.am @@ -0,0 +1,44 @@ +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +noinst_PROGRAMS = embedtool + +noinst_HEADERS = tiles.h + +embedtool_SOURCES = embedtool.cpp +embedtool_LDADD = $(LIB_QT) +embedtool_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +kde_module_LTLIBRARIES = kwin3_keramik.la + +kwin3_keramik_la_SOURCES = keramik.cpp +kwin3_keramik_la_COMPILE_FIRST = tiles.h +kwin3_keramik_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_keramik_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +#kwin3_keramik_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) + +METASOURCES = AUTO +noinst_headers = keramik.h tiles.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = keramik.desktop + +EXTRA_DIST = $(lnk_DATA) + +tiles.h: pics/caption-large-left.png pics/caption-small-right.png pics/titlebar-center.png \ + pics/titlebutton-square.png pics/border-left.png pics/caption-large-right.png \ + pics/grabbar-center.png pics/titlebar-left.png pics/border-right.png \ + pics/caption-small-center.png pics/grabbar-left.png pics/titlebar-right.png \ + pics/caption-large-center.png pics/caption-small-left.png pics/grabbar-right.png \ + pics/titlebutton-round.png pics/bottom-left.png pics/bottom-right.png \ + pics/bottom-center.png \ + pics/titlebutton-square-large.png pics/titlebutton-square-huge.png \ + pics/titlebutton-round-large.png pics/titlebutton-round-huge.png + +tiles.h: embedtool + pics=`ls $(srcdir)/pics/*.png 2>/dev/null` ;\ + ./embedtool $$pics + +keramik.lo: tiles.h + diff --git a/kwin/clients/keramik/config/Makefile.am b/kwin/clients/keramik/config/Makefile.am new file mode 100644 index 000000000..3021635ca --- /dev/null +++ b/kwin/clients/keramik/config/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_keramik_config.la + +kwin_keramik_config_la_SOURCES = config.cpp keramikconfig.ui +kwin_keramik_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_keramik_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h keramikconfig.h + +lnkdir = $(kde_datadir)/kwin + diff --git a/kwin/clients/keramik/config/config.cpp b/kwin/clients/keramik/config/config.cpp new file mode 100644 index 000000000..c548ca184 --- /dev/null +++ b/kwin/clients/keramik/config/config.cpp @@ -0,0 +1,110 @@ +/* + * + * Keramik KWin client configuration module + * + * Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org> + * + * Based on the Quartz configuration module, + * Copyright (c) 2001 Karol Szwed <gallium@kde.org> + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <kglobal.h> +#include <klocale.h> + +#include <qcheckbox.h> + +#include "config.h" +#include "config.moc" + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return ( new KeramikConfig( conf, parent ) ); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +KeramikConfig::KeramikConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_clients"); + c = new KConfig( "kwinkeramikrc" ); + + ui = new KeramikConfigUI( parent ); + connect( ui->showAppIcons, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->smallCaptions, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->largeGrabBars, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->useShadowedText, SIGNAL(clicked()), SIGNAL(changed()) ); + + load( conf ); + ui->show(); +} + + +KeramikConfig::~KeramikConfig() +{ + delete ui; + delete c; +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void KeramikConfig::load( KConfig* ) +{ + c->setGroup("General"); + ui->showAppIcons->setChecked( c->readBoolEntry("ShowAppIcons", true) ); + ui->smallCaptions->setChecked( c->readBoolEntry("SmallCaptionBubbles", false) ); + ui->largeGrabBars->setChecked( c->readBoolEntry("LargeGrabBars", true) ); + ui->useShadowedText->setChecked( c->readBoolEntry("UseShadowedText", true) ); +} + + +// Saves the configurable options to the kwinrc config file +void KeramikConfig::save( KConfig* ) +{ + c->setGroup( "General" ); + c->writeEntry( "ShowAppIcons", ui->showAppIcons->isChecked() ); + c->writeEntry( "SmallCaptionBubbles", ui->smallCaptions->isChecked() ); + c->writeEntry( "LargeGrabBars", ui->largeGrabBars->isChecked() ); + c->writeEntry( "UseShadowedText", ui->useShadowedText->isChecked() ); + c->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void KeramikConfig::defaults() +{ + ui->showAppIcons->setChecked( true ); + ui->smallCaptions->setChecked( false ); + ui->largeGrabBars->setChecked( true ); + ui->useShadowedText->setChecked( true ); + + emit changed(); +} + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/config/config.h b/kwin/clients/keramik/config/config.h new file mode 100644 index 000000000..71090531d --- /dev/null +++ b/kwin/clients/keramik/config/config.h @@ -0,0 +1,57 @@ +/* + * Keramik KWin client configuration module + * + * Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org> + * + * Based on the Quartz configuration module, + * Copyright (c) 2001 Karol Szwed <gallium@kde.org> + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KWIN_KERAMIK_CONFIG_H +#define __KWIN_KERAMIK_CONFIG_H + +#include <kconfig.h> + +#include "keramikconfig.h" + +class KeramikConfig: public QObject +{ + Q_OBJECT + + public: + KeramikConfig( KConfig* conf, QWidget* parent ); + ~KeramikConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + private: + KeramikConfigUI *ui; + KConfig *c; +}; + + +#endif + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/config/keramikconfig.ui b/kwin/clients/keramik/config/keramikconfig.ui new file mode 100644 index 000000000..f074a00b8 --- /dev/null +++ b/kwin/clients/keramik/config/keramikconfig.ui @@ -0,0 +1,76 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>KeramikConfigUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KeramikConfigUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>287</width> + <height>102</height> + </rect> + </property> + <property name="caption"> + <string>Keramik</string> + </property> + <vbox> + <property name="margin"> + <cstring>0</cstring> + </property> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>showAppIcons</cstring> + </property> + <property name="text"> + <string>Display the window &icon in the caption bubble</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the window icon to be displayed in the caption bubble next to the titlebar text.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>smallCaptions</cstring> + </property> + <property name="text"> + <string>Draw &small caption bubbles on active windows</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the caption bubble to have the same size on active windows that it has on inactive ones. This option is useful for laptops or low resolution displays where you want maximize the amount of space available to the window contents.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>largeGrabBars</cstring> + </property> + <property name="text"> + <string>Draw g&rab bars below windows</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want a grab bar to be drawn below windows. When this option is not selected only a thin border will be drawn in its place.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>useShadowedText</cstring> + </property> + <property name="text"> + <string>Use shadowed &text</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the titlebar text to have a 3D look with a shadow behind it.</string> + </property> + </widget> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kwin/clients/keramik/embedtool.cpp b/kwin/clients/keramik/embedtool.cpp new file mode 100644 index 000000000..b0e5f1c72 --- /dev/null +++ b/kwin/clients/keramik/embedtool.cpp @@ -0,0 +1,230 @@ +/* + * Keramik KWin embed tool (version 1.0) + * + * Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org> + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <qimage.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdatetime.h> + +#include <iostream> + +static int primes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229 +}; + +struct EmbedImage { + QString string; + int width; + int height; + bool alpha; + QString name; +}; + +class KeramikEmbedder { +public: + KeramikEmbedder(); + ~KeramikEmbedder(); + + void embed( const char * ); + void writeIndex(); + +private: + QFile *file; + QPtrList<EmbedImage> *index; + QTextStream stream; +}; + +KeramikEmbedder::KeramikEmbedder() +{ + QDateTime date( QDateTime::currentDateTime() ); + QString datestring( date.toString() ); + + file = new QFile( "tiles.h" ); + file->open( IO_WriteOnly | IO_Truncate ); + + stream.setDevice( file ); + + stream << "/*\n"; + stream << " * Generated by embedtool 1.0 on " << datestring << endl; + stream << " */\n\n"; + + stream << "#ifndef __TILES_H\n"; + stream << "#define __TILES_H\n\n"; + stream << "#include <qimage.h>\n"; + stream << "#include <qdict.h>\n\n"; + stream << "namespace Keramik {\n\n"; + + index = new QPtrList<EmbedImage>; + index->setAutoDelete( true ); +} + +KeramikEmbedder::~KeramikEmbedder() +{ + stream << "} // namespace Keramik\n\n"; + stream << "#endif // __TILES_H\n\n"; + stream << "// vim: set noet ts=4 sw=4:\n"; + + file->close(); + delete file; + delete index; +} + +void KeramikEmbedder::embed( const char *name ) +{ + QFileInfo fileinfo( name ); + QString basename( fileinfo.baseName() ); + QString codename( basename ); + QImage image( name ); + + codename = codename.replace( QRegExp("[^a-zA-Z0-9]"), "_" ); + + stream << "\tstatic const QRgb " << codename << "_data[] = {" << endl << "\t\t"; + stream.setf( QTextStream::hex | QTextStream::right ); + stream.fill( '0' ); + + int pixels = image.width() * image.height(); + Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( image.bits() ); + bool hasAlpha = false; + + + for ( int i = 0, j = 0; i < pixels; i++ ) { + if ( qAlpha( *data ) && qAlpha( *data ) != 0xff ) + hasAlpha = true; + + stream << "0x" << qSetW(8) << *(data++); + + if ( i != pixels-1 ) { + stream << ','; + + if ( j++ > 4 ) { + j = 0; + stream << endl << "\t\t"; + } else + stream << ' '; + } + } + + stream.reset(); + + stream << endl << "\t}; // " << codename << "_data" << endl << endl; + + EmbedImage *imginfo = new EmbedImage; + imginfo->width = image.width(); + imginfo->height = image.height(); + imginfo->alpha = hasAlpha; + imginfo->name = codename; + imginfo->string = basename; + index->append( imginfo ); +} + +void KeramikEmbedder::writeIndex() +{ + stream << "\tstruct EmbedImage {\n"; + stream << "\t\tconst char *name;\n"; + stream << "\t\tint width;\n"; + stream << "\t\tint height;\n"; + stream << "\t\tbool alpha;\n"; + stream << "\t\tconst QRgb *data;\n"; + stream << "\t};\n\n"; + + uint i = 0; + stream << "\tstatic const EmbedImage image_db[] = {\n"; + for ( EmbedImage *image = index->first(); image; image = index->next() ) + { + stream << "\t\t{ \"" << image->string << "\", " + << image->width << ", " << image->height << + ", " << (image->alpha ? "true" : "false") + << ", " << image->name << "_data }"; + if ( i++ < index->count() - 1 ) + stream << ','; + stream << endl; + } + stream << "\t};\n\n"; + + uint prime = 0; + + for ( i = 0; i < 50; i++ ) + if ( (prime = primes[i]) >= index->count() ) + break; + + stream << "\tclass KeramikImageDb {\n"; + stream << "\tprivate:\n"; + stream << "\t\tstatic KeramikImageDb *m_inst;\n"; + stream << "\t\tQDict<QImage> *db;\n\n"; + stream << "\t\tKeramikImageDb() {\n"; + stream << "\t\t\tdb = new QDict<QImage>( " << prime << " );\n"; + stream << "\t\t\tdb->setAutoDelete( true );\n\n"; + stream << "\t\t\tfor ( int i = 0; i < " << index->count() << "; i++ ) {\n"; + stream << "\t\t\t\tQImage *img = new QImage( (uchar*)image_db[i].data,\n"; + stream << "\t\t\t\t\t\timage_db[i].width, image_db[i].height,\n"; + stream << "\t\t\t\t\t\t32, NULL, 0, QImage::LittleEndian );\n\n"; + stream << "\t\t\t\tif ( image_db[i].alpha )\n"; + stream << "\t\t\t\t\timg->setAlphaBuffer( true );\n\n"; + stream << "\t\t\t\tdb->insert( image_db[i].name, img );\n"; + stream << "\t\t\t}\n"; + stream << "\t\t}\n\n"; + stream << "\t\t~KeramikImageDb() {\n"; + stream << "\t\t\tdelete db;\n"; + stream << "\t\t}\n\n"; + stream << "\tpublic:\n"; + stream << "\t\tstatic KeramikImageDb* instance() {\n"; + stream << "\t\t\tif ( ! m_inst ) m_inst = new KeramikImageDb;\n"; + stream << "\t\t\treturn m_inst;\n"; + stream << "\t\t}\n\n"; + stream << "\t\tstatic void release() {\n"; + stream << "\t\t\tif ( m_inst ) delete m_inst;\n"; + stream << "\t\t\tm_inst = NULL;\n"; + stream << "\t\t}\n\n"; + stream << "\t\tQImage *image( const QString &name ) const {\n"; + stream << "\t\t\treturn db->find( name );\n"; + stream << "\t\t}\n\n"; + stream << "\t}; // class KeramikImageDb\n\n"; + stream << "\tKeramikImageDb *KeramikImageDb::m_inst = NULL;\n\n"; +} + +int main( int argv, char **argc ) +{ + if ( argv < 2 ) { + std::cout << "Insufficient arguments" << std::endl; + return 1; + } + + KeramikEmbedder embedder; + + for ( int i = 1; i < argv; i++ ) + { + std::cout << argc[i] << std::endl; + embedder.embed( argc[i] ); + } + + embedder.writeIndex(); + + return 0; +} + +// vim: set noet ts=4 sw=4: + diff --git a/kwin/clients/keramik/keramik.cpp b/kwin/clients/keramik/keramik.cpp new file mode 100644 index 000000000..57a51bce1 --- /dev/null +++ b/kwin/clients/keramik/keramik.cpp @@ -0,0 +1,1834 @@ +/* + * + * Keramik KWin client (version 0.8) + * + * Copyright (C) 2002 Fredrik H�lund <fredrik@kde.org> + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <kconfig.h> +#include <klocale.h> +#include <kiconeffect.h> + +#include <qpainter.h> +#include <qlayout.h> +#include <qbitmap.h> +#include <qstyle.h> +#include <qtooltip.h> +#include <qwidget.h> +#include <qlabel.h> + +#include <X11/Xlib.h> + +#include "keramik.h" +#include "keramik.moc" + + + +// ------------------------------------------------------------------------------------------- + +static void flip( QPixmap *&pix ) +{ + QPixmap *tmp = new QPixmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) ); + delete pix; + pix = tmp; +} + +static void flip( QBitmap *&pix ) +{ + QBitmap *tmp = new QBitmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) ); + delete pix; + pix = tmp; +} + +namespace Keramik +{ + + const int buttonMargin = 9; // Margin between the window edge and the buttons + const int buttonSpacing = 4; // Spacing between the titlebar buttons + const int iconSpacing = 5; // Spacing between the icon and the text label + + // Default button layout + const char default_left[] = "M"; + const char default_right[] = "HIAX"; + + // Titlebar button bitmaps + const unsigned char menu_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0xf0, 0x07, 0x00, + 0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char on_all_desktops_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char not_on_all_desktops_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char help_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0xf0, 0x07, 0x00, 0x30, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char minimize_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char maximize_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char restore_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char close_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x0c, 0x00, 0x70, 0x0e, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, + 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0x70, 0x0e, 0x00, 0x30, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char above_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char above_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, + 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, + 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, + 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + KeramikHandler *clientHandler = NULL; + bool keramik_initialized = false; + + + +// ------------------------------------------------------------------------------------------- + + + +KeramikHandler::KeramikHandler() +{ + for ( int i = 0; i < NumTiles; i++ ) { + activeTiles[i] = NULL; + inactiveTiles[i] = NULL; + } + + settings_cache = NULL; + + imageDb = KeramikImageDb::instance(); + + // Create the button deco bitmaps + buttonDecos[ Menu ] = new QBitmap( 17, 17, menu_bits, true ); + buttonDecos[ OnAllDesktops ] = new QBitmap( 17, 17, on_all_desktops_bits, true ); + buttonDecos[ NotOnAllDesktops ] = new QBitmap( 17, 17, not_on_all_desktops_bits, true ); + buttonDecos[ Help ] = new QBitmap( 17, 17, help_bits, true ); + buttonDecos[ Minimize ] = new QBitmap( 17, 17, minimize_bits, true ); + buttonDecos[ Maximize ] = new QBitmap( 17, 17, maximize_bits, true ); + buttonDecos[ Restore ] = new QBitmap( 17, 17, restore_bits, true ); + buttonDecos[ Close ] = new QBitmap( 17, 17, close_bits, true ); + buttonDecos[ AboveOn ] = new QBitmap( 17, 17, above_on_bits, true ); + buttonDecos[ AboveOff ] = new QBitmap( 17, 17, above_off_bits, true ); + buttonDecos[ BelowOn ] = new QBitmap( 17, 17, below_on_bits, true ); + buttonDecos[ BelowOff ] = new QBitmap( 17, 17, below_off_bits, true ); + buttonDecos[ ShadeOn ] = new QBitmap( 17, 17, shade_on_bits, true ); + buttonDecos[ ShadeOff ] = new QBitmap( 17, 17, shade_off_bits, true ); + + // Selfmask the bitmaps + for ( int i = 0; i < NumButtonDecos; i++ ) + buttonDecos[i]->setMask( *buttonDecos[i] ); + + // Flip the bitmaps horizontally in right-to-left mode + if ( QApplication::reverseLayout() ) { + for ( int i = 0; i < Help; ++i ) + ::flip( buttonDecos[i] ); + + for ( int i = Help + 1; i < NumButtonDecos; ++i ) + ::flip( buttonDecos[i] ); + } + + readConfig(); + createPixmaps(); + + keramik_initialized = true; +} + + +KeramikHandler::~KeramikHandler() +{ + keramik_initialized = false; + destroyPixmaps(); + + for ( int i = 0; i < NumButtonDecos; i++ ) + delete buttonDecos[i]; + + delete settings_cache; + + KeramikImageDb::release(); + imageDb = NULL; + clientHandler = NULL; +} + + +void KeramikHandler::createPixmaps() +{ + int heightOffset; + int widthOffset; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + widthOffset = 4; + heightOffset = 0; + break; + case BorderVeryLarge: + widthOffset = 8; + heightOffset = 0; + break; + case BorderHuge: + widthOffset = 14; + heightOffset = 0; + break; + case BorderVeryHuge: + widthOffset = 23; + heightOffset = 10; + break; + case BorderOversized: + widthOffset = 36; + heightOffset = 25; + break; + case BorderTiny: + case BorderNormal: + default: + widthOffset = 0; + heightOffset = 0; + } + int fontHeight = QFontMetrics(options()->font(true)).height(); + if (fontHeight > heightOffset + 20) + heightOffset = fontHeight - 20; + + QString size = (heightOffset < 8) ? "" : (heightOffset < 20) ? "-large" : "-huge"; + + QColor titleColor, captionColor, buttonColor; + QImage *titleCenter = NULL, *captionLeft = NULL, + *captionRight = NULL, *captionCenter = NULL; + + + // Active tiles + // ------------------------------------------------------------------------- + captionColor = KDecoration::options()->color( ColorTitleBar, true ); + titleColor = KDecoration::options()->color( ColorTitleBlend, true ); + + // Load the titlebar corners. + activeTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); + activeTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); + + // Load the titlebar center tile image (this will be used as + // the background for the caption bubble tiles). + titleCenter = loadImage( "titlebar-center", titleColor ); + + // Load the small version of the caption bubble corner & center images. + captionLeft = loadImage( "caption-small-left", captionColor ); + captionRight = loadImage( "caption-small-right", captionColor ); + captionCenter = loadImage( "caption-small-center", captionColor ); + + // Create the caption bubble tiles (by blending the images onto the titlebar) + activeTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); + activeTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); + activeTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + // Now do the same with the large version + captionLeft = loadImage( "caption-large-left", captionColor ); + captionRight = loadImage( "caption-large-right", captionColor ); + captionCenter = loadImage( "caption-large-center", captionColor ); + + activeTiles[ CaptionLargeLeft ] = composite( captionLeft, titleCenter ); + activeTiles[ CaptionLargeRight ] = composite( captionRight, titleCenter ); + activeTiles[ CaptionLargeCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + // Create the titlebar center tile + activeTiles[ TitleCenter ] = new QPixmap( *titleCenter ); + + delete titleCenter; + + // Load the left & right border pixmaps + activeTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); + activeTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); + + // Load the bottom grabbar pixmaps + if ( largeGrabBars ) { + activeTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); + activeTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); + activeTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); + } else { + activeTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); + activeTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); + activeTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); + } + + // Inactive tiles + // ------------------------------------------------------------------------- + captionColor = KDecoration::options()->color( ColorTitleBar, false ); + titleColor = KDecoration::options()->color( ColorTitleBlend, false ); + + inactiveTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); + inactiveTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); + + titleCenter = loadImage( "titlebar-center", titleColor ); + + captionLeft = loadImage( "caption-small-left", captionColor ); + captionRight = loadImage( "caption-small-right", captionColor ); + captionCenter = loadImage( "caption-small-center", captionColor ); + + inactiveTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); + inactiveTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); + inactiveTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + inactiveTiles[ TitleCenter ] = new QPixmap( *titleCenter ); + + delete titleCenter; + + inactiveTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); + inactiveTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); + + if ( largeGrabBars ) { + inactiveTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); + inactiveTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); + inactiveTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); + } else { + inactiveTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); + inactiveTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); + inactiveTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); + } + + // Buttons + // ------------------------------------------------------------------------- + buttonColor = QColor(); //KDecoration::options()->color( ButtonBg, true ); + + titleButtonRound = loadPixmap( "titlebutton-round"+size, buttonColor ); + titleButtonSquare = loadPixmap( "titlebutton-square"+size, buttonColor ); + + + // Prepare the tiles for use + // ------------------------------------------------------------------------- + if ( QApplication::reverseLayout() ) { + + // Fix lighting + flip( activeTiles[CaptionSmallLeft], activeTiles[CaptionSmallRight] ); + flip( inactiveTiles[CaptionSmallLeft], inactiveTiles[CaptionSmallRight] ); + + flip( activeTiles[CaptionLargeLeft], activeTiles[CaptionLargeRight] ); + + flip( activeTiles[TitleLeft], activeTiles[TitleRight] ); + flip( inactiveTiles[TitleLeft], inactiveTiles[TitleRight] ); + + flip( activeTiles[BorderLeft], activeTiles[BorderRight] ); + flip( inactiveTiles[BorderLeft], inactiveTiles[BorderRight] ); + + flip( activeTiles[GrabBarLeft], activeTiles[GrabBarRight] ); + flip( inactiveTiles[GrabBarLeft], inactiveTiles[GrabBarRight] ); + + ::flip( titleButtonRound ); + ::flip( titleButtonSquare ); + } + + // Pretile the center & border tiles for optimal performance + pretile( activeTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ CaptionLargeCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ TitleCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ GrabBarCenter ], 128, Qt::Horizontal ); + pretile( activeTiles[ BorderLeft ], 128, Qt::Vertical ); + pretile( activeTiles[ BorderRight ], 128, Qt::Vertical ); + + pretile( inactiveTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); + pretile( inactiveTiles[ TitleCenter ], 64, Qt::Horizontal ); + pretile( inactiveTiles[ GrabBarCenter ], 128, Qt::Horizontal ); + pretile( inactiveTiles[ BorderLeft ], 128, Qt::Vertical ); + pretile( inactiveTiles[ BorderRight ], 128, Qt::Vertical ); + + if (heightOffset > 0) { + addHeight (heightOffset, activeTiles[TitleLeft]); + addHeight (heightOffset, activeTiles[TitleCenter]); + addHeight (heightOffset, activeTiles[TitleRight]); + addHeight (heightOffset, activeTiles[CaptionSmallLeft]); + addHeight (heightOffset, activeTiles[CaptionSmallCenter]); + addHeight (heightOffset, activeTiles[CaptionSmallRight]); + addHeight (heightOffset, activeTiles[CaptionLargeLeft]); + addHeight (heightOffset, activeTiles[CaptionLargeCenter]); + addHeight (heightOffset, activeTiles[CaptionLargeRight]); + + addHeight (heightOffset, inactiveTiles[TitleLeft]); + addHeight (heightOffset, inactiveTiles[TitleCenter]); + addHeight (heightOffset, inactiveTiles[TitleRight]); + addHeight (heightOffset, inactiveTiles[CaptionSmallLeft]); + addHeight (heightOffset, inactiveTiles[CaptionSmallCenter]); + addHeight (heightOffset, inactiveTiles[CaptionSmallRight]); + } + + if (widthOffset > 0) { + addWidth (widthOffset, activeTiles[BorderLeft], true, activeTiles[GrabBarCenter]); + addWidth (widthOffset, activeTiles[BorderRight], false, activeTiles[GrabBarCenter]); + addWidth (widthOffset, inactiveTiles[BorderLeft], true, inactiveTiles[GrabBarCenter]); + addWidth (widthOffset, inactiveTiles[BorderRight], false, inactiveTiles[GrabBarCenter]); + + if (largeGrabBars) + widthOffset = widthOffset*3/2; + + addHeight (widthOffset, activeTiles[GrabBarLeft]); + addHeight (widthOffset, activeTiles[GrabBarCenter]); + addHeight (widthOffset, activeTiles[GrabBarRight]); + addHeight (widthOffset, inactiveTiles[GrabBarLeft]); + addHeight (widthOffset, inactiveTiles[GrabBarCenter]); + addHeight (widthOffset, inactiveTiles[GrabBarRight]); + } +} + + + +void KeramikHandler::destroyPixmaps() +{ + for ( int i = 0; i < NumTiles; i++ ) { + delete activeTiles[i]; + delete inactiveTiles[i]; + activeTiles[i] = NULL; + inactiveTiles[i] = NULL; + } + + delete titleButtonRound; + delete titleButtonSquare; +} + + +void KeramikHandler::addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix) { + int w = pix->width()+width; + int h = pix->height(); + + QPixmap *tmp = new QPixmap (w, h); + tmp->fill (); + QPainter p; + p.begin (tmp); + + for (int i = 0; i < h; i++) + p.drawPixmap (0, i, *bottomPix, i%2, 0, w,1); + + if (left) + p.drawPixmap(0, 0, *pix); + else + p.drawPixmap(width, 0, *pix); + + p.end(); + + delete pix; + pix = tmp; +} + + +void KeramikHandler::addHeight (int height, QPixmap *&pix) { + int w = pix->width(); + int h = pix->height()+height; + + QPixmap *tmp = new QPixmap (w, h); + QPainter p; + p.begin (tmp); + if (pix->height() > 10) { + p.drawPixmap(0, 0, *pix, 0, 0, w, 11); + for (int i = 0; i < height; i+=2) + p.drawPixmap(0, 11+i, *pix, 0, 11, w, 2); + p.drawPixmap(0, 11+height, *pix, 0, 11, w, -1); + } + else { + int lines = h-3; + int factor = pix->height()-3; + for (int i = 0; i < lines; i++) + p.drawPixmap(0, i, *pix, 0, i*factor/lines, w, 1); + p.drawPixmap(0, lines, *pix, 0, factor, w, 3); + } + p.end(); + + delete pix; + pix = tmp; +} + + +void KeramikHandler::flip( QPixmap *&pix1, QPixmap *&pix2 ) +{ + // Flip the pixmaps horizontally + QPixmap *tmp = new QPixmap( pix1->xForm( QWMatrix(-1,0,0,1,pix1->width(),0) ) ); + + delete pix1; + pix1 = new QPixmap( pix2->xForm( QWMatrix(-1,0,0,1,pix2->width(),0) ) ); + + delete pix2; + pix2 = tmp; +} + + +void KeramikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir ) +{ + QPixmap *newpix; + QPainter p; + + if ( dir == Qt::Horizontal ) + newpix = new QPixmap( size, pix->height() ); + else + newpix = new QPixmap( pix->width(), size ); + + p.begin( newpix ); + p.drawTiledPixmap( newpix->rect(), *pix ) ; + p.end(); + + delete pix; + pix = newpix; +} + + +void KeramikHandler::readConfig() +{ + KConfig *c = new KConfig( "kwinkeramikrc" ); + + c->setGroup( "General" ); + showIcons = c->readBoolEntry( "ShowAppIcons", true ); + shadowedText = c->readBoolEntry( "UseShadowedText", true ); + smallCaptionBubbles = c->readBoolEntry( "SmallCaptionBubbles", false ); + largeGrabBars = c->readBoolEntry( "LargeGrabBars", true ); + + if ( ! settings_cache ) { + settings_cache = new SettingsCache; + settings_cache->largeGrabBars = largeGrabBars; + settings_cache->smallCaptionBubbles = smallCaptionBubbles; + } + + delete c; +} + + +QPixmap *KeramikHandler::composite( QImage *over, QImage *under ) +{ + QImage dest( over->width(), over->height(), 32 ); + int width = over->width(), height = over->height(); + + // Clear the destination image + Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dest.bits() ); + for (int i = 0; i < width * height; i++) + *(data++) = 0; + + // Copy the under image (bottom aligned) to the destination image + for (int y1 = height - under->height(), y2 = 0; y1 < height; y1++, y2++ ) + { + register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.scanLine(y1) ); + register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( under->scanLine(y2) ); + + for ( int x = 0; x < width; x++ ) + *(dst++) = *(src++); + } + + // Blend the over image onto the destination + register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.bits() ); + register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( over->bits() ); + for ( int i = 0; i < width * height; i++ ) + { + int r1 = qRed( *dst ), g1 = qGreen( *dst ), b1 = qBlue( *dst ); + int r2 = qRed( *src ), g2 = qGreen( *src ), b2 = qBlue( *src ); + int a = qAlpha( *src ); + + if ( a == 0xff ) + *dst = *src; + + else if ( a != 0x00 ) + *dst = qRgba( Q_UINT8( r1 + (((r2 - r1) * a) >> 8) ), + Q_UINT8( g1 + (((g2 - g1) * a) >> 8) ), + Q_UINT8( b1 + (((b2 - b1) * a) >> 8) ), + 0xff ); + + else if ( qAlpha(*dst) == 0x00 ) + *dst = 0; + + src++; dst++; + } + + // Create the final pixmap and return it + return new QPixmap( dest ); +} + + +QImage *KeramikHandler::loadImage( const QString &name, const QColor &col ) +{ + if ( col.isValid() ) { + QImage *img = new QImage( imageDb->image(name)->copy() ); + KIconEffect::colorize( *img, col, 1.0 ); + return img; + } else + return new QImage( imageDb->image(name)->copy() ); +} + + +QPixmap *KeramikHandler::loadPixmap( const QString &name, const QColor &col ) +{ + QImage *img = loadImage( name, col ); + QPixmap *pix = new QPixmap( *img ); + delete img; + + return pix; +} + + +bool KeramikHandler::reset( unsigned long changed ) +{ + keramik_initialized = false; + + bool needHardReset = false; + bool pixmapsInvalid = false; + + // Re-read the config file + readConfig(); + + if ( changed & SettingBorder ) + { + pixmapsInvalid = true; + needHardReset = true; + } + if ( changed & SettingFont ) + { + pixmapsInvalid = true; + needHardReset = true; + } + // Check if the color scheme has changed + if ( changed & SettingColors ) + { + pixmapsInvalid = true; + } + // Check if button positions have changed + + if ( changed & SettingButtons ) { + needHardReset = true; + } + + // Check if tooltips options have changed + if ( changed & SettingTooltips ) { + needHardReset = true; + } + + if ( (settings_cache->largeGrabBars != largeGrabBars) ) { + pixmapsInvalid = true; + needHardReset = true; + } + + if ( (settings_cache->smallCaptionBubbles != smallCaptionBubbles) ) { + needHardReset = true; + } + + // Update our config cache + settings_cache->largeGrabBars = largeGrabBars; + settings_cache->smallCaptionBubbles = smallCaptionBubbles; + + // Do we need to recreate the pixmaps? + if ( pixmapsInvalid ) { + destroyPixmaps(); + createPixmaps(); + } + + keramik_initialized = true; + + // Do we need to "hit the wooden hammer" ? + if ( !needHardReset ) + resetDecorations( changed ); + return needHardReset; +} + + +bool KeramikHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + + +const QPixmap *KeramikHandler::tile( TilePixmap tilePix, bool active ) const +{ + return ( active ? activeTiles[ tilePix ] : inactiveTiles[ tilePix ] ); +} + +KDecoration* KeramikHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new KeramikClient( bridge, this ); +} + +QValueList< KeramikHandler::BorderSize > KeramikHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + + +// ------------------------------------------------------------------------------------------- + + + +KeramikButton::KeramikButton( KeramikClient* c, const char *name, Button btn, const QString &tip, const int realizeBtns ) + : QButton( c->widget(), name ), + client( c ), button( btn ), hover( false ), lastbutton( NoButton ) +{ + realizeButtons = realizeBtns; + + QToolTip::add( this, tip ); // FRAME + setBackgroundMode( NoBackground ); + setCursor( arrowCursor ); + int size = clientHandler->roundButton()->height(); + setFixedSize( size, size ); + + setToggleButton( (button == OnAllDesktopsButton) ); +} + + +KeramikButton::~KeramikButton() +{ + // Empty. +} + + +void KeramikButton::enterEvent( QEvent *e ) +{ + QButton::enterEvent( e ); + + hover = true; + repaint( false ); +} + + +void KeramikButton::leaveEvent( QEvent *e ) +{ + QButton::leaveEvent( e ); + + hover = false; + repaint( false ); +} + + +void KeramikButton::mousePressEvent( QMouseEvent *e ) +{ + lastbutton = e->button(); + QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); + QButton::mousePressEvent( &me ); +} + + +void KeramikButton::mouseReleaseEvent( QMouseEvent *e ) +{ + lastbutton = e->button(); + QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); + QButton::mouseReleaseEvent( &me ); +} + + +void KeramikButton::drawButton( QPainter *p ) +{ + const QPixmap *pix; + const QBitmap *deco; + int size = clientHandler->roundButton()->height(); + + // Get the bevel from the client handler + if ( button == MenuButton || button == OnAllDesktopsButton || button == HelpButton ) + pix = clientHandler->roundButton(); + else + pix = clientHandler->squareButton(); + + // Draw the button background + const QPixmap *background = clientHandler->tile( TitleCenter, client->isActive() ); + p->drawPixmap( 0, 0, *background, + 0, (background->height()-size+1)/2, size, size ); + + if ( isDown() ) { + // Pressed + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(2*size, 0, size, size), pix->rect() ) ); + p->translate( QApplication::reverseLayout() ? -1 : 1, 1 ); + } else if ( hover ) + // Mouse over + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(size, 0, size, size), pix->rect() ) ); + else + // Normal + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(0, 0, size, size), pix->rect() ) ); + + + // Draw the button deco on the bevel + switch ( button ) { + case MenuButton: + deco = clientHandler->buttonDeco( Menu ); + break; + + case OnAllDesktopsButton: + deco = clientHandler->buttonDeco( client->isOnAllDesktops() ? NotOnAllDesktops : OnAllDesktops ); + break; + + case HelpButton: + deco = clientHandler->buttonDeco( Help ); + // The '?' won't be flipped around in the ctor, so we need to + // shift it to the right to compensate for the button shadow + // being on the left side of the button in RTL mode. + if ( QApplication::reverseLayout() ) + p->translate( 2, 0 ); + break; + + case MinButton: + deco = clientHandler->buttonDeco( Minimize ); + break; + + case MaxButton: + deco = clientHandler->buttonDeco( client->maximizeMode() == KeramikClient::MaximizeFull ? Restore : Maximize ); + break; + + case CloseButton: + deco = clientHandler->buttonDeco( Close ); + break; + + case AboveButton: + deco = clientHandler->buttonDeco( client->keepAbove() ? AboveOn : AboveOff ); + break; + + case BelowButton: + deco = clientHandler->buttonDeco( client->keepBelow() ? BelowOn : BelowOff ); + break; + + case ShadeButton: + deco = clientHandler->buttonDeco( client->isSetShade() ? ShadeOn : ShadeOff ); + break; + + default: + deco = NULL; + } + + p->setPen( Qt::black ); // ### hardcoded color + p->drawPixmap( (size-17)/2, (size-17)/2, *deco ); +} + + + +// ------------------------------------------------------------------------------------------ + + + +KeramikClient::KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory ) + : KDecoration( bridge, factory ), + activeIcon( NULL ), inactiveIcon( NULL ), captionBufferDirty( true ), maskDirty( true ) +{ +} + +void KeramikClient::init() +{ + connect( this, SIGNAL( keepAboveChanged( bool )), SLOT( keepAboveChange( bool ))); + connect( this, SIGNAL( keepBelowChanged( bool )), SLOT( keepBelowChange( bool ))); + + createMainWidget( WStaticContents | WResizeNoErase | WRepaintNoErase ); + widget()->installEventFilter( this ); + + // Minimize flicker + widget()->setBackgroundMode( NoBackground ); + + for ( int i=0; i < NumButtons; i++ ) + button[i] = NULL; + + createLayout(); +} + +void KeramikClient::createLayout() +{ + + QVBoxLayout *mainLayout = new QVBoxLayout( widget() ); + QBoxLayout *titleLayout = new QBoxLayout( 0, QBoxLayout::LeftToRight, 0, 0, 0 ); + QHBoxLayout *windowLayout = new QHBoxLayout(); + + largeTitlebar = ( !maximizedVertical() && clientHandler->largeCaptionBubbles() ); + largeCaption = ( isActive() && largeTitlebar ); + + int grabBarHeight = clientHandler->grabBarHeight(); + int topSpacing = ( largeTitlebar ? 4 : 1 ); + int leftBorderWidth = clientHandler->tile( BorderLeft, true )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, true )->width(); + topSpacer = new QSpacerItem( 10, topSpacing, + QSizePolicy::Expanding, QSizePolicy::Minimum ); + + mainLayout->addItem( topSpacer ); + + mainLayout->addLayout( titleLayout ); // Titlebar + mainLayout->addLayout( windowLayout, 1 ); // Left border + window + right border + mainLayout->addSpacing( grabBarHeight ); // Bottom grab bar + + titleLayout->setSpacing( buttonSpacing ); + + titleLayout->addSpacing( buttonMargin ); // Left button margin + addButtons( titleLayout, options()->customButtonPositions() ? + options()->titleButtonsLeft() : QString(default_left) ); + + titlebar = new QSpacerItem( 10, clientHandler->titleBarHeight(largeTitlebar) + - topSpacing, QSizePolicy::Expanding, QSizePolicy::Minimum ); + titleLayout->addItem( titlebar ); + + titleLayout->addSpacing( buttonSpacing ); + addButtons( titleLayout, options()->customButtonPositions() ? + options()->titleButtonsRight() : QString(default_right) ); + titleLayout->addSpacing( buttonMargin - 1 ); // Right button margin + + windowLayout->addSpacing( leftBorderWidth ); // Left border + if( isPreview()) + windowLayout->addWidget( new QLabel( i18n( "<center><b>Keramik preview</b></center>" ), widget())); + else + windowLayout->addItem( new QSpacerItem( 0, 0 )); //no widget in the middle + windowLayout->addSpacing( rightBorderWidth ); // Right border +} + + +KeramikClient::~KeramikClient() +{ + delete activeIcon; + delete inactiveIcon; + + activeIcon = inactiveIcon = NULL; +} + + +void KeramikClient::reset( unsigned long ) +{ + if ( clientHandler->largeCaptionBubbles() && !largeTitlebar ) + { + // We're switching from small caption bubbles to large + if ( !maximizedVertical() ) { + topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeTitlebar = true; + largeCaption = isActive(); + + widget()->layout()->activate(); + + // Compensate for the titlebar size change + + // TODO This is wrong, this may break size increments (see bug #53784). + // FRAME + widget()->setGeometry( widget()->x(), widget()->y() - 3, width(), height() + 3 ); + } + } + else if ( !clientHandler->largeCaptionBubbles() && largeTitlebar ) + { + // We're switching from large caption bubbles to small + topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeTitlebar = largeCaption = false; + + widget()->layout()->activate(); + + // Compensate for the titlebar size change + // FRAME + widget()->setGeometry( widget()->x(), widget()->y() + 3, width(), height() - 3 ); + } + + calculateCaptionRect(); + + captionBufferDirty = maskDirty = true; + + // Only repaint the window if it's visible + // (i.e. not minimized and on the current desktop) + if ( widget()->isVisible() ) { + widget()->repaint( false ); + + for ( int i = 0; i < NumButtons; i++ ) + if ( button[i] ) button[i]->repaint( false ); + } +} + + +void KeramikClient::addButtons( QBoxLayout *layout, const QString &s ) +{ + for ( uint i=0; i < s.length(); i++ ) + { + switch ( s[i].latin1() ) + { + // Menu button + case 'M' : + if ( !button[MenuButton] ) { + button[MenuButton] = new KeramikButton( this, "menu", MenuButton, i18n("Menu"), LeftButton|RightButton ); + connect( button[MenuButton], SIGNAL( pressed() ), SLOT( menuButtonPressed() ) ); + layout->addWidget( button[MenuButton] ); + } + break; + + // OnAllDesktops button + case 'S' : + if ( !button[OnAllDesktopsButton] ) { + button[OnAllDesktopsButton] = new KeramikButton( this, "on_all_desktops", + OnAllDesktopsButton, isOnAllDesktops()?i18n("Not on all desktops"):i18n("On all desktops") ); + if(isOnAllDesktops()) + button[OnAllDesktopsButton]->toggle(); + connect( button[OnAllDesktopsButton], SIGNAL( clicked() ), SLOT( toggleOnAllDesktops() ) ); + layout->addWidget( button[OnAllDesktopsButton] ); + } + break; + + // Help button + case 'H' : + if ( !button[HelpButton] && providesContextHelp() ) { + button[HelpButton] = new KeramikButton( this, "help", HelpButton, i18n("Help") ); + connect( button[HelpButton], SIGNAL( clicked() ), SLOT( showContextHelp() ) ); + layout->addWidget( button[HelpButton] ); + } + break; + + // Minimize button + case 'I' : + if ( !button[MinButton] && isMinimizable() ) { + button[MinButton] = new KeramikButton( this, "minimize", MinButton, i18n("Minimize") ); + connect( button[MinButton], SIGNAL( clicked() ), SLOT( minimize() ) ); + layout->addWidget( button[MinButton] ); + } + break; + + // Maximize button + case 'A' : + if ( !button[MaxButton] && isMaximizable() ) { + button[MaxButton] = new KeramikButton( this, "maximize", MaxButton, i18n("Maximize"), LeftButton|MidButton|RightButton ); + connect( button[MaxButton], SIGNAL( clicked() ), SLOT( slotMaximize() ) ); + layout->addWidget( button[MaxButton] ); + } + break; + + // Close button + case 'X' : + if ( !button[CloseButton] && isCloseable() ) { + button[CloseButton] = new KeramikButton( this, "close", CloseButton, i18n("Close") ); + connect( button[CloseButton], SIGNAL( clicked() ), SLOT( closeWindow() ) ); + layout->addWidget( button[CloseButton] ); + } + break; + + // Above button + case 'F' : + if ( !button[AboveButton]) { + button[AboveButton] = new KeramikButton( this, "above", AboveButton, i18n("Keep Above Others") ); + connect( button[AboveButton], SIGNAL( clicked() ), SLOT( slotAbove() ) ); + layout->addWidget( button[AboveButton] ); + } + break; + + // Below button + case 'B' : + if ( !button[BelowButton]) { + button[BelowButton] = new KeramikButton( this, "below", BelowButton, i18n("Keep Below Others") ); + connect( button[BelowButton], SIGNAL( clicked() ), SLOT( slotBelow() ) ); + layout->addWidget( button[BelowButton] ); + } + break; + + // Shade button + case 'L' : + if ( !button[ShadeButton] && isShadeable() ) { + button[ShadeButton] = new KeramikButton( this, "shade", ShadeButton, + isSetShade() ? i18n("Unshade") : i18n( "Shade" )); + connect( button[ShadeButton], SIGNAL( clicked() ), SLOT( slotShade() ) ); + layout->addWidget( button[ShadeButton] ); + } + break; + + // Additional spacing + case '_' : + layout->addSpacing( buttonSpacing ); + break; + } + } +} + + +void KeramikClient::updateMask() +{ + if ( !keramik_initialized ) + return; + + // To maximize performance this code uses precalculated bounding rects + // to set the window mask. This saves us from having to allocate a 1bpp + // pixmap, paint the mask on it and then have the X server iterate + // over the pixels to compute the bounding rects from it. + + QRegion r; + register int w, y = 0; + int nrects; + + if ( QApplication::reverseLayout() ) { + + // If the caption bubble is visible and extends above the titlebar + if ( largeCaption && captionRect.width() >= 25 ) { + register int x = captionRect.left(); + w = captionRect.width(); + r += QRegion( x + 11, y++, w - 19, 1 ); + r += QRegion( x + 9, y++, w - 15, 1 ); + r += QRegion( x + 7, y++, w - 12, 1 ); + } else { + nrects = 8; + + // Do we have a large titlebar with a retracted caption bubble? + // (i.e. the style is set to use large caption bubbles, we're + // not maximized and not active) + if ( largeTitlebar ) + y = 3; + } + + w = width(); // FRAME + + // The rounded titlebar corners + r += QRegion( 9, y++, w - 17, 1 ); + r += QRegion( 7, y++, w - 13, 1 ); + r += QRegion( 5, y++, w - 9, 1 ); + r += QRegion( 4, y++, w - 7, 1 ); + r += QRegion( 3, y++, w - 5, 1 ); + r += QRegion( 2, y++, w - 4, 1 ); + r += QRegion( 1, y++, w - 2, 2 ); + } else { + + // If the caption bubble is visible and extends above the titlebar + if ( largeCaption && captionRect.width() >= 25 ) { + nrects = 11; + register int x = captionRect.left(); + w = captionRect.width(); + r += QRegion( x + 8, y++, w - 19, 1 ); + r += QRegion( x + 6, y++, w - 15, 1 ); + r += QRegion( x + 5, y++, w - 12, 1 ); + } else { + nrects = 8; + + // Do we have a large titlebar with a retracted caption bubble? + // (i.e. the style is set to use large caption bubbles, we're + // not maximized and not active) + if ( largeTitlebar ) + y = 3; + } + + w = width(); // FRAME + + // The rounded titlebar corners + r += QRegion( 8, y++, w - 17, 1 ); + r += QRegion( 6, y++, w - 13, 1 ); + r += QRegion( 4, y++, w - 9, 1 ); + r += QRegion( 3, y++, w - 7, 1 ); + r += QRegion( 2, y++, w - 5, 1 ); + r += QRegion( 2, y++, w - 4, 1 ); + r += QRegion( 1, y++, w - 2, 2 ); + } + + y++; + + // The part of the window below the titlebar + r += QRegion( 0, y, w, height() - y ); + + setMask( r, YXBanded ); + + maskDirty = false; +} + + +void KeramikClient::updateCaptionBuffer() +{ + if ( !keramik_initialized ) + return; + + bool active = isActive(); + QPixmap *icon = NULL; + + if ( captionBuffer.size() != captionRect.size() ) + captionBuffer.resize( captionRect.size() ); + + if ( captionBuffer.isNull() ) + return; + + QPainter p( &captionBuffer ); + + // Draw the caption bubble + if ( active && largeCaption ) { + p.drawPixmap( 0, 0, *clientHandler->tile( CaptionLargeLeft, true ) ); + p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), + *clientHandler->tile( CaptionLargeCenter, true ) ); + p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionLargeRight, true ) ); + } else { + p.drawPixmap( 0, 0, *clientHandler->tile( CaptionSmallLeft, active ) ); + p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), + *clientHandler->tile( CaptionSmallCenter, active ) ); + p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionSmallRight, active ) ); + } + + if ( clientHandler->showAppIcons() ) + { + if ( active ) { + if ( ! activeIcon ) + activeIcon = new QPixmap( this->icon().pixmap( QIconSet::Small, QIconSet::Normal )); // FRAME + icon = activeIcon; + } else { + if ( ! inactiveIcon ) { + QImage img = this->icon().pixmap( QIconSet::Small, QIconSet::Normal ).convertToImage(); + KIconEffect::semiTransparent( img ); + inactiveIcon = new QPixmap( img ); + } + icon = inactiveIcon; + } + } + + p.setFont( options()->font( active ) ); + int tw = p.fontMetrics().width( caption() ) + + ( clientHandler->showAppIcons() ? 16 + iconSpacing : 0 ); + + int xpos = QMAX( (captionRect.width() - tw) / 3, 8 ); + QRect tr = QStyle::visualRect( QRect(xpos, 1, captionRect.width() - xpos - 10, + captionRect.height() - 4), captionBuffer.rect() ); + + //p.setPen( Qt::red ); // debug + //p.drawRect( tr ); // debug + + // Application icon + if ( clientHandler->showAppIcons() ) + { + QRect iconRect = QStyle::visualRect( QRect(tr.x(), + 1 + (captionRect.height() - 4 - 16) / 2, 16, 16), tr ); + QRect r( icon->rect() ); + r.moveCenter( iconRect.center() ); + + if ( tr.width() > 16 ) { + p.drawPixmap( r, *icon ); + } else { + QRect sr( 0, 0, icon->width(), icon->height() ); + + if ( QApplication::reverseLayout() ) + sr.addCoords( icon->width() - tr.width(), 0, 0, 0 ); + else + sr.addCoords( 0, 0, -( icon->width() - tr.width() ), 0 ); + + p.drawPixmap( r.x() + sr.x(), r.y() + sr.y(), *icon, + sr.x(), sr.y(), sr.width(), sr.height() ); + } + + //p.drawRect( r ); // debug + + if ( QApplication::reverseLayout() ) + tr.addCoords( 0, 0, -(16 + iconSpacing), 0 ); + else + tr.addCoords( (16 + iconSpacing), 0, 0, 0 ); + } + + // Draw the titlebar text + int flags = AlignVCenter | SingleLine; + flags |= ( QApplication::reverseLayout() ? AlignRight : AlignLeft ); + + if ( clientHandler->useShadowedText() ) + { + p.translate( QApplication::reverseLayout() ? -1 : 1, 1 ); + //p.setPen( options()->color(ColorTitleBar, active).dark() ); + if (qGray(options()->color(ColorFont, active).rgb()) < 100) + p.setPen( QColor(200,200,200) ); + else + p.setPen( black ); + p.drawText( tr, flags, caption() ); + p.translate( QApplication::reverseLayout() ? 1 : -1, -1 ); + } + + p.setPen( options()->color( ColorFont, active ) ); + p.drawText( tr, flags, caption() ); + + captionBufferDirty = false; +} + + +void KeramikClient::calculateCaptionRect() +{ + QFontMetrics fm( options()->font(isActive()) ); + int cw = fm.width( caption() ) + 95; + int titleBaseY = ( largeTitlebar ? 3 : 0 ); + + if ( clientHandler->showAppIcons() ) + cw += 16 + 4; // icon width + space + + cw = QMIN( cw, titlebar->geometry().width() ); + captionRect = QStyle::visualRect( QRect(titlebar->geometry().x(), (largeCaption ? 0 : titleBaseY), + cw, clientHandler->titleBarHeight(largeCaption) ), + titlebar->geometry() ); +} + + +void KeramikClient::captionChange() +{ + QRect r( captionRect ); + calculateCaptionRect(); + + if ( r.size() != captionRect.size() ) + maskDirty = true; + + captionBufferDirty = true; + + widget()->repaint( r | captionRect, false ); +} + + +void KeramikClient::iconChange() +{ + if ( clientHandler->showAppIcons() ) { + + // Force updateCaptionBuffer() to recreate the cached icons + delete activeIcon; + + delete inactiveIcon; + + activeIcon = inactiveIcon = NULL; + + captionBufferDirty = true; + widget()->repaint( captionRect, false ); + } +} + + +void KeramikClient::activeChange() +{ + bool active = isActive(); + // Note: It's assumed that the same font will always be used for both active + // and inactive windows, since the fonts kcm hasn't supported setting + // different fonts for different window states for some time. + if ( largeTitlebar ) { + largeCaption = ( active && !maximizedVertical() ); + calculateCaptionRect(); + maskDirty = true; + } + + captionBufferDirty = true; + + widget()->repaint( false ); + + for ( int i=0; i < NumButtons; i++ ) + if ( button[i] ) button[i]->repaint( false ); +} + + +void KeramikClient::maximizeChange() +{ + if ( clientHandler->largeCaptionBubbles() ) + { + if ( maximizeMode() & MaximizeVertical ) { + // We've been maximized - shrink the titlebar by 3 pixels + topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeCaption = largeTitlebar = false; + + calculateCaptionRect(); + captionBufferDirty = maskDirty = true; + + widget()->layout()->activate(); + widget()->repaint( false ); + } else if (( maximizeMode() & MaximizeVertical ) == 0 && !largeTitlebar ) { + // We've been restored - enlarge the titlebar by 3 pixels + topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeCaption = largeTitlebar = true; + + calculateCaptionRect(); + captionBufferDirty = maskDirty = true; + + widget()->layout()->activate(); + widget()->repaint( false ); + } + } + + if ( button[ MaxButton ] ) { + QToolTip::remove( button[ MaxButton ] ); + QToolTip::add( button[ MaxButton ], maximizeMode() == MaximizeFull ? i18n("Restore") : i18n("Maximize") ); + button[ MaxButton ]->repaint(); + } +} + + +void KeramikClient::desktopChange() +{ + if ( button[ OnAllDesktopsButton ] ) + { + button[ OnAllDesktopsButton ]->repaint( true ); + QToolTip::remove( button[ OnAllDesktopsButton ] ); + QToolTip::add( button[ OnAllDesktopsButton ], isOnAllDesktops() ? i18n("Not on all desktops") : i18n("On all desktops") ); + } +} + + +void KeramikClient::shadeChange() +{ + if ( button[ ShadeButton ] ) + { + button[ ShadeButton ]->repaint( true ); + QToolTip::remove( button[ ShadeButton ] ); + QToolTip::add( button[ ShadeButton ], isSetShade() ? i18n("Unshade") : i18n("Shade") ); + } +} + + +void KeramikClient::keepAboveChange( bool ) +{ + if ( button[ AboveButton ] ) + button[ AboveButton ]->repaint( true ); +} + + +void KeramikClient::keepBelowChange( bool ) +{ + if ( button[ BelowButton ] ) + button[ BelowButton ]->repaint( true ); +} + + +void KeramikClient::menuButtonPressed() +{ + QPoint menuTop ( button[MenuButton]->rect().topLeft() ); + QPoint menuBottom ( button[MenuButton]->rect().bottomRight() ); + menuTop += QPoint(-6, -3); + menuBottom += QPoint(6, 3); + KDecorationFactory* f = factory(); + showWindowMenu( QRect( button[MenuButton]->mapToGlobal( menuTop ), + button[MenuButton]->mapToGlobal( menuBottom )) ); + if( !f->exists( this )) // 'this' was destroyed + return; + button[MenuButton]->setDown(false); +} + + +void KeramikClient::slotMaximize() +{ + maximize( button[ MaxButton ]->lastButton() ); +} + + +void KeramikClient::slotAbove() +{ + setKeepAbove( !keepAbove()); + button[ AboveButton ]->repaint( true ); +} + + +void KeramikClient::slotBelow() +{ + setKeepBelow( !keepBelow()); + button[ BelowButton ]->repaint( true ); +} + + +void KeramikClient::slotShade() +{ + setShade( !isSetShade()); + button[ ShadeButton ]->repaint( true ); +} + + +void KeramikClient::paintEvent( QPaintEvent *e ) +{ + if ( !keramik_initialized ) + return; + + QPainter p( widget()); + QRect updateRect( e->rect() ); + bool active = isActive(); + + int titleBaseY = ( largeTitlebar ? 3 : 0 ); + int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar ); + int grabBarHeight = clientHandler->grabBarHeight(); + int leftBorderWidth = clientHandler->tile( BorderLeft, active )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, active )->width(); + + if ( maskDirty ) + updateMask(); + + // Titlebar + // ----------------------------------------------------------------------- + if ( updateRect.y() < titleBarHeight ) + { + int titleBarBaseHeight = titleBarHeight - titleBaseY; + + if ( captionBufferDirty ) + updateCaptionBuffer(); + + // Top left corner + if ( updateRect.x() < 15 ) + p.drawPixmap( 0, titleBaseY, + *clientHandler->tile( TitleLeft, active ) ); + + // Space between the top left corner and the caption bubble + if ( updateRect.x() < captionRect.left() && updateRect.right() >= 15 ) { + int x1 = QMAX( 15, updateRect.x() ); + int x2 = QMIN( captionRect.left(), updateRect.right() ); + + p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, + *clientHandler->tile( TitleCenter, active ) ); + } + + // Caption bubble + if ( updateRect.x() <= captionRect.right() && updateRect.right() > 15 ) { + if ( captionRect.width() >= 25 ) + p.drawPixmap( captionRect.left(), active ? 0 : titleBaseY, captionBuffer ); + else + p.drawTiledPixmap( captionRect.x(), titleBaseY, captionRect.width(), + titleBarBaseHeight, *clientHandler->tile( TitleCenter, active ) ); + } + + // Space between the caption bubble and the top right corner + if ( updateRect.right() > captionRect.right() && updateRect.x() < width() - 15 ) { // FRAME + int x1 = QMAX( captionRect.right() + 1, updateRect.x() ); + int x2 = QMIN( width() - 15, updateRect.right() ); + + p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, + *clientHandler->tile( TitleCenter, active ) ); + } + + // Top right corner + if ( updateRect.right() >= width() - 15 ) + p.drawPixmap( width() - 15, titleBaseY, + *clientHandler->tile( TitleRight, active ) ); + } + + // Borders + // ----------------------------------------------------------------------- + if ( updateRect.bottom() >= titleBarHeight && + updateRect.top() < height() - grabBarHeight ) + { + int top = QMAX( titleBarHeight, updateRect.top() ); + int bottom = QMIN( updateRect.bottom(), height() - grabBarHeight ); + + // Left border + if ( updateRect.x() < leftBorderWidth ) + p.drawTiledPixmap( 0, top, leftBorderWidth, bottom - top + 1, + *clientHandler->tile( BorderLeft, active ) ); + + // Right border + if ( e->rect().right() > width() - rightBorderWidth - 1 ) + p.drawTiledPixmap( width() - rightBorderWidth, top, rightBorderWidth, + bottom - top + 1, *clientHandler->tile( BorderRight, active ) ); + } + + // Bottom grab bar + // ----------------------------------------------------------------------- + if ( updateRect.bottom() >= height() - grabBarHeight ) { + // Bottom left corner + if ( updateRect.x() < 9 ) + p.drawPixmap( 0, height() - grabBarHeight, + *clientHandler->tile( GrabBarLeft, active ) ); + + // Space between the left corner and the right corner + if ( updateRect.x() < width() - 9 ) { + int x1 = QMAX( 9, updateRect.x() ); + int x2 = QMIN( width() - 9, updateRect.right() ); + + p.drawTiledPixmap( x1, height() - grabBarHeight, x2 - x1 + 1, + grabBarHeight, *clientHandler->tile( GrabBarCenter, active ) ); + } + + // Bottom right corner + if ( updateRect.right() > width() - 9 ) + p.drawPixmap( width() - 9, height() - grabBarHeight, + *clientHandler->tile( GrabBarRight, active ) ); + } + + // Extra drawline for the 1 pixel empty space QLayout leaves when a window is shaded. + p.setPen( options()->color( ColorTitleBlend, active ) ); + p.drawLine( leftBorderWidth, height() - grabBarHeight - 1, + width() - rightBorderWidth - 1, height() - grabBarHeight - 1 ); +} + + +void KeramikClient::resizeEvent( QResizeEvent *e ) +{ +// FRAME Client::resizeEvent( e ); + + QRect r( captionRect ); + calculateCaptionRect(); + + if ( r.size() != captionRect.size() ) + captionBufferDirty = true; + + maskDirty = true; + + if ( widget()->isVisible() ) + { + widget()->update( widget()->rect() ); + int dx = 0; + int dy = 0; + + if ( e->oldSize().width() != width() ) + dx = 32 + QABS( e->oldSize().width() - width() ); + + if ( e->oldSize().height() != height() ) + dy = 8 + QABS( e->oldSize().height() - height() ); + + if ( dy ) + widget()->update( 0, height() - dy + 1, width(), dy ); + + if ( dx ) + { + widget()->update( width() - dx + 1, 0, dx, height() ); + widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) ); + widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, + titlebar->geometry().bottom() ) ) ); + // Titlebar needs no paint event + QApplication::postEvent( this, new QPaintEvent( titlebar->geometry(), FALSE ) ); + } + } +} + + +void KeramikClient::mouseDoubleClickEvent( QMouseEvent *e ) +{ + if ( e->button() == LeftButton + && QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) ) + titlebarDblClickOperation(); +} + +void KeramikClient::wheelEvent( QWheelEvent *e ) +{ + if (isSetShade() + || QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) ) + titlebarMouseWheelOperation( e->delta()); +} + +KeramikClient::Position KeramikClient::mousePosition( const QPoint &p ) const +{ + int titleBaseY = (largeTitlebar ? 3 : 0); + + int leftBorder = clientHandler->tile( BorderLeft, true )->width(); + int rightBorder = width() - clientHandler->tile( BorderRight, true )->width() - 1; + int bottomBorder = height() - clientHandler->grabBarHeight() - 1; + int bottomCornerSize = 3*clientHandler->tile( BorderRight, true )->width()/2 + 24; + + // Test if the mouse is over the titlebar area + if ( p.y() < titleBaseY + 11 ) { + // Test for the top left corner + if ( p.x() < leftBorder + 11 ) { + if ( (p.y() < titleBaseY + 3 && p.x() < leftBorder + 11) || + (p.y() < titleBaseY + 6 && p.x() < leftBorder + 6) || + (p.y() < titleBaseY + 11 && p.x() < leftBorder + 3) ) + return PositionTopLeft; + } + + // Test for the top right corner + if ( p.x() > rightBorder - 11 ) { + if ( (p.y() < titleBaseY + 3 && p.x() > rightBorder - 11) || + (p.y() < titleBaseY + 6 && p.x() > rightBorder - 6) || + (p.y() < titleBaseY + 11 && p.x() > rightBorder - 3) ) + return PositionTopRight; + } + + // Test for the top border + if ( p.y() <= 3 || (p.y() <= titleBaseY+3 && + (p.x() < captionRect.left() || p.x() > captionRect.right()) ) ) + return PositionTop; + + // The cursor must be over the center of the titlebar. + return PositionCenter; + } + + // Test the sides + else if ( p.y() < bottomBorder ) { + // Test for the left side + if ( p.x() < leftBorder ) { + if ( p.y() < height() - bottomCornerSize ) + return PositionLeft; + else + return PositionBottomLeft; + } + + // Test for the right side + else if ( p.x() > rightBorder ) { + if ( p.y() < height() - bottomCornerSize ) + return PositionRight; + else + return PositionBottomRight; + } + + // The cursor must be over the center of the window + return PositionCenter; + } + + // Test the grab bar / bottom border + else { + // Test for the bottom left corner + if ( p.x() < bottomCornerSize ) + return PositionBottomLeft; + + // Test for the bottom right corner + else if ( p.x() > width() - bottomCornerSize - 1 ) + return PositionBottomRight; + + // The cursor must be over the bottom border + return PositionBottom; + } + + // We should never get here + return PositionCenter; +} + + +void KeramikClient::resize( const QSize& s ) +{ + widget()->resize( s ); +} + + +void KeramikClient::borders( int& left, int& right, int& top, int& bottom ) const +{ + int titleBarHeight = clientHandler->titleBarHeight( clientHandler->largeCaptionBubbles() ); + int grabBarHeight = clientHandler->grabBarHeight(); + int leftBorderWidth = clientHandler->tile( BorderLeft, isActive() )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, isActive() )->width(); + + left = leftBorderWidth; + right = rightBorderWidth; + top = titleBarHeight; + bottom = grabBarHeight; + + if ( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows()) + left = right = 0; + if( maximizeMode() & MaximizeVertical) + { + top = clientHandler->titleBarHeight( false ); + if( !options()->moveResizeMaximizedWindows()) + bottom = 0; + } +} + + +QSize KeramikClient::minimumSize() const +{ + return widget()->minimumSize(); +} + + +bool KeramikClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Wheel: + wheelEvent( static_cast< QWheelEvent* >( e )); + return true; + + default: + return false; + } +} + +} // namespace Keramik + + + +// ------------------------------------------------------------------------------------------- + + + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + Keramik::clientHandler = new Keramik::KeramikHandler(); + return Keramik::clientHandler; + } +} + + + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/keramik.desktop b/kwin/clients/keramik/keramik.desktop new file mode 100644 index 000000000..c58f8d522 --- /dev/null +++ b/kwin/clients/keramik/keramik.desktop @@ -0,0 +1,31 @@ +[Desktop Entry] +Name=Keramik +Name[ar]=قرميدي +Name[be]=Кераміка +Name[bn]=কেরামিক +Name[cs]=Keramika +Name[eo]=Ceramiko +Name[fa]=کرامیک +Name[fy]=Keramyk +Name[hi]=के-रामिक +Name[it]=Ceramica +Name[lo]=ເຄຣາມິຠ- K +Name[lv]=Keramika +Name[mk]=Керамик +Name[mn]=Ваар +Name[nb]=Keramikk +Name[ne]=केरामिक +Name[nn]=Keramikk +Name[pa]=ਕੀਰਾਮਿਕ +Name[se]=Bálseduodji +Name[sr]=Керамика +Name[sr@Latn]=Keramika +Name[ta]=கெராமிக௠+Name[te]=కెరామికౠ+Name[th]=เครามิภ+Name[uk]=Керамік +Name[uz]=Keramika +Name[uz@cyrillic]=Керамика +Name[vi]=Gốm +X-KDE-Library=kwin3_keramik + diff --git a/kwin/clients/keramik/keramik.h b/kwin/clients/keramik/keramik.h new file mode 100644 index 000000000..40a24a058 --- /dev/null +++ b/kwin/clients/keramik/keramik.h @@ -0,0 +1,201 @@ +/* + * + * Keramik KWin client (version 0.8) + * + * Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org> + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __KERAMIK_H +#define __KERAMIK_H + +#include <qbutton.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +#include "tiles.h" + +class QSpacerItem; + +namespace Keramik { + + enum TilePixmap { TitleLeft=0, TitleCenter, TitleRight, + CaptionSmallLeft, CaptionSmallCenter, CaptionSmallRight, + CaptionLargeLeft, CaptionLargeCenter, CaptionLargeRight, + GrabBarLeft, GrabBarCenter, GrabBarRight, + BorderLeft, BorderRight, NumTiles }; + + enum Button { MenuButton=0, OnAllDesktopsButton, HelpButton, MinButton, + MaxButton, CloseButton, AboveButton, BelowButton, ShadeButton, + NumButtons }; + + enum ButtonDeco { Menu=0, OnAllDesktops, NotOnAllDesktops, Help, Minimize, Maximize, + Restore, Close, AboveOn, AboveOff, BelowOn, BelowOff, ShadeOn, ShadeOff, + NumButtonDecos }; + + struct SettingsCache + { + bool largeGrabBars:1; + bool smallCaptionBubbles:1; + }; + + class KeramikHandler : public KDecorationFactory + { + public: + KeramikHandler(); + ~KeramikHandler(); + + virtual QValueList< BorderSize > borderSizes() const; + virtual bool reset( unsigned long changed ); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool supports( Ability ability ); + + bool showAppIcons() const { return showIcons; } + bool useShadowedText() const { return shadowedText; } + bool largeCaptionBubbles() const { return !smallCaptionBubbles; } + + int titleBarHeight( bool large ) const { + return ( large ? activeTiles[CaptionLargeCenter]->height() + : activeTiles[CaptionSmallCenter]->height() ); + } + + int grabBarHeight() const + { return activeTiles[GrabBarCenter]->height(); } + + const QPixmap *roundButton() const { return titleButtonRound; } + const QPixmap *squareButton() const { return titleButtonSquare; } + const QBitmap *buttonDeco( ButtonDeco deco ) const + { return buttonDecos[ deco ]; } + + inline const QPixmap *tile( TilePixmap tilePix, bool active ) const; + + private: + void readConfig(); + void createPixmaps(); + void destroyPixmaps(); + + void addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix); + void addHeight (int height, QPixmap *&pix); + void flip( QPixmap *&, QPixmap *& ); + void pretile( QPixmap *&, int, Qt::Orientation ); + QPixmap *composite( QImage *, QImage * ); + QImage *loadImage( const QString &, const QColor & ); + QPixmap *loadPixmap( const QString &, const QColor & ); + + bool showIcons:1, shadowedText:1, + smallCaptionBubbles:1, largeGrabBars:1; + SettingsCache *settings_cache; + KeramikImageDb *imageDb; + + QPixmap *activeTiles[ NumTiles ]; + QPixmap *inactiveTiles[ NumTiles ]; + QBitmap *buttonDecos[ NumButtonDecos ]; + + QPixmap *titleButtonRound, *titleButtonSquare; + + }; // class KeramikHandler + + class KeramikClient; + class KeramikButton : public QButton + { + public: + KeramikButton( KeramikClient *, const char *, Button, const QString &, const int realizeBtns = LeftButton ); + ~KeramikButton(); + + ButtonState lastButton() const { return lastbutton; } + + private: + void enterEvent( QEvent * ); + void leaveEvent( QEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void drawButton( QPainter * ); + + private: + KeramikClient *client; + Button button; + bool hover; + ButtonState lastbutton; + int realizeButtons; + }; // class KeramikButton + + + class KeramikClient : public KDecoration + { + Q_OBJECT + + public: + + KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory ); + ~KeramikClient(); + virtual void init(); + virtual void reset( unsigned long changed ); + virtual Position mousePosition( const QPoint& p ) const; + virtual void borders( int& left, int& right, int& top, int& bottom ) const; + virtual void resize( const QSize& s ); + virtual QSize minimumSize() const; + virtual bool eventFilter( QObject* o, QEvent* e ); + virtual void activeChange(); + virtual void captionChange(); + virtual void maximizeChange(); + virtual void desktopChange(); + virtual void shadeChange(); + + private: + void createLayout(); + void addButtons( QBoxLayout*, const QString & ); + void updateMask(); // FRAME + void updateCaptionBuffer(); + void iconChange(); + void resizeEvent( QResizeEvent *); // FRAME + void paintEvent( QPaintEvent *); // FRAME + void mouseDoubleClickEvent( QMouseEvent * ); // FRAME + void wheelEvent( QWheelEvent *); //FRAME + int width() const { return widget()->width(); } + int height() const { return widget()->height(); } + + void calculateCaptionRect(); + + inline bool maximizedVertical() const { + return ( maximizeMode() & MaximizeVertical ); + } + + private slots: + void menuButtonPressed(); + void slotMaximize(); + void slotAbove(); + void slotBelow(); + void slotShade(); + void keepAboveChange( bool ); + void keepBelowChange( bool ); + + private: + QSpacerItem *topSpacer, *titlebar; + KeramikButton *button[ NumButtons ]; + QRect captionRect; + QPixmap captionBuffer; + QPixmap *activeIcon, *inactiveIcon; + bool captionBufferDirty:1, maskDirty:1; + bool largeCaption:1, largeTitlebar:1; + }; // class KeramikClient + +} // namespace Keramik + +#endif // ___KERAMIK_H + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/pics/border-left.png b/kwin/clients/keramik/pics/border-left.png Binary files differnew file mode 100644 index 000000000..298a0aa9a --- /dev/null +++ b/kwin/clients/keramik/pics/border-left.png diff --git a/kwin/clients/keramik/pics/border-right.png b/kwin/clients/keramik/pics/border-right.png Binary files differnew file mode 100644 index 000000000..1ca876b0d --- /dev/null +++ b/kwin/clients/keramik/pics/border-right.png diff --git a/kwin/clients/keramik/pics/bottom-center.png b/kwin/clients/keramik/pics/bottom-center.png Binary files differnew file mode 100644 index 000000000..d6d002534 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-center.png diff --git a/kwin/clients/keramik/pics/bottom-left.png b/kwin/clients/keramik/pics/bottom-left.png Binary files differnew file mode 100644 index 000000000..16a2ab982 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-left.png diff --git a/kwin/clients/keramik/pics/bottom-right.png b/kwin/clients/keramik/pics/bottom-right.png Binary files differnew file mode 100644 index 000000000..2d4045432 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-right.png diff --git a/kwin/clients/keramik/pics/caption-large-center.png b/kwin/clients/keramik/pics/caption-large-center.png Binary files differnew file mode 100644 index 000000000..786276b55 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-center.png diff --git a/kwin/clients/keramik/pics/caption-large-left.png b/kwin/clients/keramik/pics/caption-large-left.png Binary files differnew file mode 100644 index 000000000..7d96bdcea --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-left.png diff --git a/kwin/clients/keramik/pics/caption-large-right.png b/kwin/clients/keramik/pics/caption-large-right.png Binary files differnew file mode 100644 index 000000000..3055d13a7 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-right.png diff --git a/kwin/clients/keramik/pics/caption-small-center.png b/kwin/clients/keramik/pics/caption-small-center.png Binary files differnew file mode 100644 index 000000000..78636dfd1 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-center.png diff --git a/kwin/clients/keramik/pics/caption-small-left.png b/kwin/clients/keramik/pics/caption-small-left.png Binary files differnew file mode 100644 index 000000000..cb7e69d73 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-left.png diff --git a/kwin/clients/keramik/pics/caption-small-right.png b/kwin/clients/keramik/pics/caption-small-right.png Binary files differnew file mode 100644 index 000000000..9fc74640e --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-right.png diff --git a/kwin/clients/keramik/pics/grabbar-center.png b/kwin/clients/keramik/pics/grabbar-center.png Binary files differnew file mode 100644 index 000000000..b623b5df2 --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-center.png diff --git a/kwin/clients/keramik/pics/grabbar-left.png b/kwin/clients/keramik/pics/grabbar-left.png Binary files differnew file mode 100644 index 000000000..653f5ccfb --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-left.png diff --git a/kwin/clients/keramik/pics/grabbar-right.png b/kwin/clients/keramik/pics/grabbar-right.png Binary files differnew file mode 100644 index 000000000..248d55410 --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-right.png diff --git a/kwin/clients/keramik/pics/titlebar-center.png b/kwin/clients/keramik/pics/titlebar-center.png Binary files differnew file mode 100644 index 000000000..bac31dc55 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-center.png diff --git a/kwin/clients/keramik/pics/titlebar-left.png b/kwin/clients/keramik/pics/titlebar-left.png Binary files differnew file mode 100644 index 000000000..bc8ee5ca3 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-left.png diff --git a/kwin/clients/keramik/pics/titlebar-right.png b/kwin/clients/keramik/pics/titlebar-right.png Binary files differnew file mode 100644 index 000000000..d34a465f5 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-right.png diff --git a/kwin/clients/keramik/pics/titlebutton-round-huge.png b/kwin/clients/keramik/pics/titlebutton-round-huge.png Binary files differnew file mode 100644 index 000000000..c5ca19342 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round-huge.png diff --git a/kwin/clients/keramik/pics/titlebutton-round-large.png b/kwin/clients/keramik/pics/titlebutton-round-large.png Binary files differnew file mode 100644 index 000000000..9c3267bf9 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round-large.png diff --git a/kwin/clients/keramik/pics/titlebutton-round.png b/kwin/clients/keramik/pics/titlebutton-round.png Binary files differnew file mode 100644 index 000000000..dd2369af3 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round.png diff --git a/kwin/clients/keramik/pics/titlebutton-square-huge.png b/kwin/clients/keramik/pics/titlebutton-square-huge.png Binary files differnew file mode 100644 index 000000000..a908a9f27 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square-huge.png diff --git a/kwin/clients/keramik/pics/titlebutton-square-large.png b/kwin/clients/keramik/pics/titlebutton-square-large.png Binary files differnew file mode 100644 index 000000000..6e3ada47b --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square-large.png diff --git a/kwin/clients/keramik/pics/titlebutton-square.png b/kwin/clients/keramik/pics/titlebutton-square.png Binary files differnew file mode 100644 index 000000000..871cf751a --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square.png diff --git a/kwin/clients/kwmtheme/Makefile.am b/kwin/clients/kwmtheme/Makefile.am new file mode 100644 index 000000000..f6c611180 --- /dev/null +++ b/kwin/clients/kwmtheme/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS=cli_installer +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin3_kwmtheme.la + +kwin3_kwmtheme_la_SOURCES = kwmthemeclient.cpp +kwin3_kwmtheme_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_kwmtheme_la_LIBADD = -lkdecorations + +METASOURCES = AUTO +noinst_HEADERS = kwmthemeclient.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = kwmtheme.desktop + diff --git a/kwin/clients/kwmtheme/cli_installer/Makefile.am b/kwin/clients/kwmtheme/cli_installer/Makefile.am new file mode 100644 index 000000000..feb31c424 --- /dev/null +++ b/kwin/clients/kwmtheme/cli_installer/Makefile.am @@ -0,0 +1,18 @@ + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +####### This part is very kwmtheme specific +# you can add here more. This one gets installed +bin_PROGRAMS = kwmtheme + +# Which sources should be compiled for kwmtheme. +kwmtheme_SOURCES = main.cpp + +# the library search path. +kwmtheme_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# the libraries to link against. Be aware of the order. First the libraries, +# that depend on the following ones. +kwmtheme_LDADD = $(LIB_KDECORE) + diff --git a/kwin/clients/kwmtheme/cli_installer/main.cpp b/kwin/clients/kwmtheme/cli_installer/main.cpp new file mode 100644 index 000000000..bdc04a1de --- /dev/null +++ b/kwin/clients/kwmtheme/cli_installer/main.cpp @@ -0,0 +1,166 @@ +#include <qfile.h> +#include <qdir.h> +#include <kapplication.h> +#include <ksimpleconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kcmdlineargs.h> +#include <klocale.h> + +static const char description[] = + I18N_NOOP("Installs a KWM theme"); + +static KCmdLineOptions options[] = +{ + { "+[file]", I18N_NOOP("Path to a theme config file"), 0 }, + KCmdLineLastOption +}; + +void copy(const QString &src, const QString &dest) +{ + QFile copyInput(src); + QFile copyOutput(dest); + if(!copyInput.open(IO_ReadOnly)){ + kdWarning() << "Couldn't open " << src << endl; + return; + } + if(!copyOutput.open(IO_WriteOnly)){ + kdWarning() << "Couldn't open " << dest << endl; + copyInput.close(); + return; + } + while(!copyInput.atEnd()){ + copyOutput.putch(copyInput.getch()); + } + copyInput.close(); + copyOutput.close(); +} + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, "kwmtheme", description, "0.1"); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app(argc, argv); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if(!args->count()){ + kdWarning() << "You need to specify the path to a theme config file!" << endl; + return(1); + } + + QString srcStr = QString(QFile::decodeName(args->arg(0))); + QFile f(srcStr); + QString tmpStr; + + if(!f.exists()){ + kdWarning() << "Specified theme config file doesn't exist!" << endl; + return(2); + } + + QStringList appDirs = KGlobal::dirs()->findDirs("data", "kwin"); + QString localDirStr = *(appDirs.end()); + if(localDirStr.isEmpty()){ + localDirStr = KGlobal::dirs()->saveLocation("data", "kwin"); + } + localDirStr += "/pics/"; + if(!QFile::exists(localDirStr)) + QDir().mkdir(localDirStr); + + QFileInfo fi(f); + KSimpleConfig input(fi.absFilePath()); + srcStr = fi.dirPath(true) + "/"; + KConfig *output = KGlobal::config(); + input.setGroup("Window Border"); + output->setGroup("General"); + + tmpStr = input.readEntry("shapePixmapTop"); + if(!tmpStr.isEmpty()){ + copy(srcStr+tmpStr, localDirStr+tmpStr); + } + output->writeEntry("wm_top", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottom"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottom", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_left", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_right", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapTopLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_topleft", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapTopRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_topright", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottomLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottomleft", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottomRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottomright", tmpStr, true, true); + + + input.setGroup("Window Titlebar"); + output->writeEntry("TitleAlignment", input.readEntry("TitleAlignment"), true, true); + output->writeEntry("PixmapUnderTitleText", input.readEntry("PixmapUnderTitleText"), true, true); + output->writeEntry("TitleFrameShaded", input.readEntry("TitleFrameShaded"), true, true); + + tmpStr = input.readEntry("MenuButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("menu", tmpStr, true, true); + tmpStr = input.readEntry("PinUpButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("pinup", tmpStr, true, true); + tmpStr = input.readEntry("PinDownButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("pindown", tmpStr, true, true); + tmpStr = input.readEntry("CloseButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("close", tmpStr, true, true); + tmpStr = input.readEntry("MaximizeButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("maximize", tmpStr, true, true); + tmpStr = input.readEntry("MaximizeDownButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("maximizedown", tmpStr, true, true); + tmpStr = input.readEntry("MinimizeButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("iconify", tmpStr, true, true); + tmpStr = input.readEntry("TitlebarPixmapActive"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("TitlebarPixmapActive", tmpStr, true, true); + tmpStr = input.readEntry("TitlebarPixmapInactive"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("TitlebarPixmapInactive", tmpStr, true, true); + + input.setGroup("Window Button Layout"); + output->setGroup("Buttons"); + output->writeEntry("ButtonA", input.readEntry("ButtonA"), true, true); + output->writeEntry("ButtonB", input.readEntry("ButtonB"), true, true); + output->writeEntry("ButtonC", input.readEntry("ButtonC"), true, true); + output->writeEntry("ButtonD", input.readEntry("ButtonD"), true, true); + output->writeEntry("ButtonE", input.readEntry("ButtonE"), true, true); + output->writeEntry("ButtonF", input.readEntry("ButtonF"), true, true); + + output->sync(); + + return(0); +} + diff --git a/kwin/clients/kwmtheme/kwmtheme.desktop b/kwin/clients/kwmtheme/kwmtheme.desktop new file mode 100644 index 000000000..caa8a6ea8 --- /dev/null +++ b/kwin/clients/kwmtheme/kwmtheme.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Name=KWM Theme +Name[af]=KWM Tema +Name[ar]=سمة KWM +Name[az]=KWM Örtüsü +Name[be]=ТÑма KWM +Name[bn]=KWM থীম +Name[br]=Gwiskad KWM +Name[bs]=KWM Tema +Name[ca]=Tema KWM +Name[cs]=Téma KWM +Name[csb]=Témë KWM +Name[cy]=Thema KWM +Name[da]=KWM-tema +Name[de]=KWM-Design +Name[el]=ΘÎμα KWM +Name[eo]=KWM-etoso +Name[es]=Tema de KWM +Name[et]=KWM teema +Name[eu]=KWM gaia +Name[fa]=چهره KWM +Name[fi]=KWM-teema +Name[fr]=Thème KWM +Name[fy]=KWM-tema +Name[ga]=Téama KWM +Name[gl]=Tema do KWM +Name[hi]=केडबलà¥à¤¯à¥‚à¤à¤® पà¥à¤°à¤¸à¤‚ग +Name[hr]=KWM tema +Name[hu]=KWM téma +Name[id]=Theme KWM +Name[is]=KWM þema +Name[it]=Tema KWM +Name[ja]=KWM テーマ +Name[ka]=KWM სტილი +Name[kk]=KWM нақышы +Name[km]=ស្បែក KWM +Name[ko]=KWM 테마 +Name[lo]=à»àºšàºšàº•àº»àº§àºˆàº±àº”àºàº²àº™àº«àº™à»‰àº²àº•à»ˆàº²àº‡ KWM +Name[lt]=KWM tema +Name[lv]=KWM TÄ“ma +Name[mk]=KWM тема +Name[mn]=KWM-Ð¥ÑлбÑÑ€ +Name[ms]=Temas KWM +Name[mt]=Tema KWM +Name[nb]=KWM-tema +Name[nds]=KWM-Muster +Name[ne]=KWM विषयवसà¥à¤¤à¥ +Name[nl]=KWM-thema +Name[nn]=KWM-tema +Name[nso]=Molaetsa wa KWM +Name[oc]=Tema KWM +Name[pa]=KWM ਸਰੂਪ +Name[pl]=Motyw KWM +Name[pt]=Tema KWM +Name[pt_BR]=Tema KWM +Name[ro]=Tematică KWM +Name[ru]=Стиль KWM +Name[rw]=Insanganyamatsiko KWM +Name[se]=KWM-fáddá +Name[sk]=Téma KWM +Name[sl]=Tema KWM +Name[sr]=KWM тема +Name[sr@Latn]=KWM tema +Name[ss]=Indzikimba ye KWM +Name[sv]=KWM-tema +Name[ta]=KWM தலைபà¯à®ªà¯ +Name[tg]=УÑлуби KWM +Name[th]=ชุดตà¸à¹à¸•à¹ˆà¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ KWM +Name[tr]=KWM Teması +Name[tt]=KWM Tışlaw +Name[uk]=Тема KWM +Name[uz]=KWM mavzusi +Name[uz@cyrillic]=KWM мавзуÑи +Name[ven]=Thero ya KWM +Name[vi]=Sắc thái KWM +Name[wa]=Tinme KWM +Name[xh]=Umxholo we KWM +Name[zh_CN]=KWM 主题 +Name[zh_TW]=KWM 主題 +Name[zu]=Ingqikithi ye-KWM +X-KDE-Library=kwin3_kwmtheme diff --git a/kwin/clients/kwmtheme/kwmthemeclient.cpp b/kwin/clients/kwmtheme/kwmthemeclient.cpp new file mode 100644 index 000000000..a422f31fb --- /dev/null +++ b/kwin/clients/kwmtheme/kwmthemeclient.cpp @@ -0,0 +1,936 @@ +#include <kconfig.h> +#include "kwmthemeclient.h" +#include <kglobal.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <qpainter.h> +#include <kpixmapeffect.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <klocale.h> +#include <qbitmap.h> +#include <qstyle.h> +#include <qlabel.h> +#include <qtooltip.h> + +namespace KWMTheme { + + +/* static QPixmap stretchPixmap(QPixmap& src, bool stretchVert){ + QPixmap dest; + QBitmap *srcMask, *destMask; + int w, h, w2, h2; + QPainter p; + + if (src.isNull()) return src; + + w = src.width(); + h = src.height(); + + if (stretchVert){ + w2 = w; + for (h2=h; h2<100; h2=h2<<1) + ; + } + else{ + h2 = h; + for (w2=w; w2<100; w2=w2<<1) + ; + } + if (w2==w && h2==h) return src; + + dest = src; + dest.resize(w2, h2); + + p.begin(&dest); + p.drawTiledPixmap(0, 0, w2, h2, src); + p.end(); + + srcMask = (QBitmap*)src.mask(); + if (srcMask){ + destMask = (QBitmap*)dest.mask(); + p.begin(destMask); + p.drawTiledPixmap(0, 0, w2, h2, *srcMask); + p.end(); + } + return dest; +} */ + + +inline const KDecorationOptions* options() { return KDecoration::options(); } + +enum FramePixmap{FrameTop=0, FrameBottom, FrameLeft, FrameRight, FrameTopLeft, + FrameTopRight, FrameBottomLeft, FrameBottomRight}; + +static QPixmap *framePixmaps[8]; +static QPixmap *menuPix, *iconifyPix, *closePix, *maxPix, *minmaxPix, + *pinupPix, *pindownPix; +static KPixmap *aTitlePix = 0; +static KPixmap *iTitlePix = 0; +static KPixmapEffect::GradientType grType; +static int maxExtent, titleAlign; +static bool titleGradient = true; +static bool pixmaps_created = false; +static bool titleSunken = false; +static bool titleTransparent; + +static void create_pixmaps() +{ + const char *keys[] = {"wm_top", "wm_bottom", "wm_left", "wm_right", + "wm_topleft", "wm_topright", "wm_bottomleft", "wm_bottomright"}; + + if(pixmaps_created) + return; + pixmaps_created = true; + + KConfig *config = KGlobal::config(); + config->setGroup("General"); + QString tmpStr; + + for(int i=0; i < 8; ++i) + { + framePixmaps[i] = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry(keys[i], " "))); + if(framePixmaps[i]->isNull()) + kdWarning() << "Unable to load frame pixmap for " << keys[i] << endl; + } +/* + *framePixmaps[FrameTop] = stretchPixmap(*framePixmaps[FrameTop], false); + *framePixmaps[FrameBottom] = stretchPixmap(*framePixmaps[FrameBottom], false); + *framePixmaps[FrameLeft] = stretchPixmap(*framePixmaps[FrameLeft], true); + *framePixmaps[FrameRight] = stretchPixmap(*framePixmaps[FrameRight], true); +*/ + maxExtent = framePixmaps[FrameTop]->height(); + if(framePixmaps[FrameBottom]->height() > maxExtent) + maxExtent = framePixmaps[FrameBottom]->height(); + if(framePixmaps[FrameLeft]->width() > maxExtent) + maxExtent = framePixmaps[FrameLeft]->width(); + if(framePixmaps[FrameRight]->width() > maxExtent) + maxExtent = framePixmaps[FrameRight]->width(); + + maxExtent++; + + menuPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("menu", " "))); + iconifyPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("iconify", " "))); + maxPix = new QPixmap(locate("appdata", + "pics/"+config->readEntry("maximize", " "))); + minmaxPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("maximizedown", " "))); + closePix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("close", " "))); + pinupPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("pinup", " "))); + pindownPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("pindown", " "))); + if(menuPix->isNull()) + menuPix->load(locate("data", "kwin/pics/menu.png")); + if(iconifyPix->isNull()) + iconifyPix->load(locate("data", "kwin/pics/iconify.png")); + if(maxPix->isNull()) + maxPix->load(locate("data", "kwin/pics/maximize.png")); + if(minmaxPix->isNull()) + minmaxPix->load(locate("data", "kwin/pics/maximizedown.png")); + if(closePix->isNull()) + closePix->load(locate("data", "kwin/pics/close.png")); + if(pinupPix->isNull()) + pinupPix->load(locate("data", "kwin/pics/pinup.png")); + if(pindownPix->isNull()) + pindownPix->load(locate("data", "kwin/pics/pindown.png")); + + tmpStr = config->readEntry("TitleAlignment"); + if(tmpStr == "right") + titleAlign = Qt::AlignRight | Qt::AlignVCenter; + else if(tmpStr == "middle") + titleAlign = Qt::AlignCenter; + else + titleAlign = Qt::AlignLeft | Qt::AlignVCenter; + titleSunken = config->readBoolEntry("TitleFrameShaded", true); + // titleSunken = true; // is this fixed? + titleTransparent = config->readBoolEntry("PixmapUnderTitleText", true); + + tmpStr = config->readEntry("TitlebarLook"); + if(tmpStr == "shadedVertical"){ + aTitlePix = new KPixmap; + aTitlePix->resize(32, 20); + KPixmapEffect::gradient(*aTitlePix, + options()->color(KDecorationOptions::ColorTitleBar, true), + options()->color(KDecorationOptions::ColorTitleBlend, true), + KPixmapEffect::VerticalGradient); + iTitlePix = new KPixmap; + iTitlePix->resize(32, 20); + KPixmapEffect::gradient(*iTitlePix, + options()->color(KDecorationOptions::ColorTitleBar, false), + options()->color(KDecorationOptions::ColorTitleBlend, false), + KPixmapEffect::VerticalGradient); + titleGradient = false; // we can just tile this + + } + else if(tmpStr == "shadedHorizontal") + grType = KPixmapEffect::HorizontalGradient; + else if(tmpStr == "shadedDiagonal") + grType = KPixmapEffect::DiagonalGradient; + else if(tmpStr == "shadedCrossDiagonal") + grType = KPixmapEffect::CrossDiagonalGradient; + else if(tmpStr == "shadedPyramid") + grType = KPixmapEffect::PyramidGradient; + else if(tmpStr == "shadedRectangle") + grType = KPixmapEffect::RectangleGradient; + else if(tmpStr == "shadedPipeCross") + grType = KPixmapEffect::PipeCrossGradient; + else if(tmpStr == "shadedElliptic") + grType = KPixmapEffect::EllipticGradient; + else{ + titleGradient = false; + tmpStr = config->readEntry("TitlebarPixmapActive", ""); + if(!tmpStr.isEmpty()){ + aTitlePix = new KPixmap; + aTitlePix->load(locate("data", "kwin/pics/" + tmpStr)); + } + else + aTitlePix = NULL; + tmpStr = config->readEntry("TitlebarPixmapInactive", ""); + if(!tmpStr.isEmpty()){ + iTitlePix = new KPixmap; + iTitlePix->load(locate("data", "kwin/pics/" + tmpStr)); + } + else + iTitlePix = NULL; + } +} + +static void delete_pixmaps() +{ + for(int i=0; i < 8; ++i) + delete framePixmaps[i]; + + delete menuPix; + delete iconifyPix; + delete closePix; + delete maxPix; + delete minmaxPix; + delete pinupPix; + delete pindownPix; + delete aTitlePix; + aTitlePix = 0; + delete iTitlePix; + iTitlePix = 0; + + titleGradient = true; + pixmaps_created = false; + titleSunken = false; +} + +void MyButton::drawButtonLabel(QPainter *p) +{ + if(pixmap()){ + // If we have a theme who's button covers the entire width or + // entire height, we shift down/right by 1 pixel so we have + // some visual notification of button presses. i.e. for MGBriezh + int offset = (isDown() && ((pixmap()->width() >= width()) || + (pixmap()->height() >= height()))) ? 1 : 0; + style().drawItem(p, QRect( offset, offset, width(), height() ), + AlignCenter, colorGroup(), + true, pixmap(), QString::null); + } +} + +KWMThemeClient::KWMThemeClient( KDecorationBridge* b, KDecorationFactory* f ) + : KDecoration( b, f ) +{ +} + +void KWMThemeClient::init() +{ + createMainWidget( WResizeNoErase | WStaticContents ); + widget()->installEventFilter( this ); + + stickyBtn = maxBtn = mnuBtn = 0; + layout = new QGridLayout(widget()); + layout->addColSpacing(0, maxExtent); + layout->addColSpacing(2, maxExtent); + + layout->addRowSpacing(0, maxExtent); + + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, + QSizePolicy::Expanding)); + + if( isPreview()) + layout->addWidget( new QLabel( i18n( "<center><b>KWMTheme</b></center>" ), widget()), 2, 1); + else + layout->addItem( new QSpacerItem( 0, 0 ), 2, 1); + + // Without the next line, shading flickers + layout->addItem( new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding) ); + layout->addRowSpacing(3, maxExtent); + layout->setRowStretch(2, 10); + layout->setColStretch(1, 10); + + QBoxLayout* hb = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0); + layout->addLayout( hb, 1, 1 ); + + KConfig *config = KGlobal::config(); + config->setGroup("Buttons"); + QString val; + MyButton *btn; + int i; + static const char *defaultButtons[]={"Menu","Sticky","Off","Iconify", + "Maximize","Close"}; + static const char keyOffsets[]={"ABCDEF"}; + for(i=0; i < 6; ++i){ + if(i == 3){ + titlebar = new QSpacerItem(10, 20, QSizePolicy::Expanding, + QSizePolicy::Minimum ); + hb->addItem( titlebar ); + } + QString key("Button"); + key += QChar(keyOffsets[i]); + val = config->readEntry(key, defaultButtons[i]); + if(val == "Menu"){ + mnuBtn = new MyButton(widget(), "menu"); + QToolTip::add( mnuBtn, i18n("Menu")); + iconChange(); + hb->addWidget(mnuBtn); + mnuBtn->setFixedSize(20, 20); + connect(mnuBtn, SIGNAL(pressed()), this, + SLOT(menuButtonPressed())); + } + else if(val == "Sticky"){ + stickyBtn = new MyButton(widget(), "sticky"); + QToolTip::add( stickyBtn, i18n("Sticky")); + if (isOnAllDesktops()) + stickyBtn->setPixmap(*pindownPix); + else + stickyBtn->setPixmap(*pinupPix); + connect(stickyBtn, SIGNAL( clicked() ), this, SLOT(toggleOnAllDesktops())); + hb->addWidget(stickyBtn); + stickyBtn->setFixedSize(20, 20); + } + else if((val == "Iconify") && isMinimizable()){ + btn = new MyButton(widget(), "iconify"); + QToolTip::add( btn, i18n("Minimize")); + btn->setPixmap(*iconifyPix); + connect(btn, SIGNAL(clicked()), this, SLOT(minimize())); + hb->addWidget(btn); + btn->setFixedSize(20, 20); + } + else if((val == "Maximize") && isMaximizable()){ + maxBtn = new MyButton(widget(), "max"); + QToolTip::add( maxBtn, i18n("Maximize")); + maxBtn->setPixmap(*maxPix); + connect(maxBtn, SIGNAL(clicked()), this, SLOT(maximize())); + hb->addWidget(maxBtn); + maxBtn->setFixedSize(20, 20); + } + else if((val == "Close") && isCloseable()){ + btn = new MyButton(widget(), "close"); + QToolTip::add( btn, i18n("Close")); + btn->setPixmap(*closePix); + connect(btn, SIGNAL(clicked()), this, SLOT(closeWindow())); + hb->addWidget(btn); + btn->setFixedSize(20, 20); + } + else{ + if((val != "Off") && + ((val == "Iconify") && !isMinimizable()) && + ((val == "Maximize") && !isMaximizable())) + kdWarning() << "KWin: Unrecognized button value: " << val << endl; + + } + } + if(titleGradient){ + aGradient = new KPixmap; + iGradient = new KPixmap; + } + else{ + aGradient = 0; + iGradient = 0; + } + widget()->setBackgroundMode(NoBackground); +} + +void KWMThemeClient::drawTitle(QPainter &dest) +{ + QRect titleRect = titlebar->geometry(); + QRect r(0, 0, titleRect.width(), titleRect.height()); + QPixmap buffer; + + if(buffer.width() == r.width()) + return; + + buffer.resize(r.size()); + QPainter p; + p.begin(&buffer); + + if(titleSunken){ + qDrawShadeRect(&p, r, options()->colorGroup(KDecorationOptions::ColorFrame, isActive()), + true, 1, 0); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + } + + KPixmap *fill = isActive() ? aTitlePix : iTitlePix; + if(fill) + p.drawTiledPixmap(r, *fill); + else if(titleGradient){ + fill = isActive() ? aGradient : iGradient; + if(fill->width() != r.width()){ + fill->resize(r.width(), 20); + KPixmapEffect::gradient(*fill, + options()->color(KDecorationOptions::ColorTitleBar, isActive()), + options()->color(KDecorationOptions::ColorTitleBlend, isActive()), + grType); + } + p.drawTiledPixmap(r, *fill); + } + else{ + p.fillRect(r, options()->colorGroup(KDecorationOptions::ColorTitleBar, isActive()). + brush(QColorGroup::Button)); + } + p.setFont(options()->font(isActive())); + p.setPen(options()->color(KDecorationOptions::ColorFont, isActive())); + // Add left & right margin + r.setLeft(r.left()+5); + r.setRight(r.right()-5); + p.drawText(r, titleAlign, caption()); + p.end(); + + dest.drawPixmap(titleRect.x(), titleRect.y(), buffer); +} + + +void KWMThemeClient::resizeEvent( QResizeEvent* ) +{ + doShape(); + widget()->repaint(); +} + +void KWMThemeClient::captionChange() +{ + widget()->repaint( titlebar->geometry(), false ); +} + +void KWMThemeClient::paintEvent( QPaintEvent *) +{ + QPainter p; + p.begin(widget()); + int x,y; + // first the corners + int w1 = framePixmaps[FrameTopLeft]->width(); + int h1 = framePixmaps[FrameTopLeft]->height(); + if (w1 > width()/2) w1 = width()/2; + if (h1 > height()/2) h1 = height()/2; + p.drawPixmap(0,0,*framePixmaps[FrameTopLeft], + 0,0,w1, h1); + int w2 = framePixmaps[FrameTopRight]->width(); + int h2 = framePixmaps[FrameTopRight]->height(); + if (w2 > width()/2) w2 = width()/2; + if (h2 > height()/2) h2 = height()/2; + p.drawPixmap(width()-w2,0,*framePixmaps[FrameTopRight], + framePixmaps[FrameTopRight]->width()-w2,0,w2, h2); + + int w3 = framePixmaps[FrameBottomLeft]->width(); + int h3 = framePixmaps[FrameBottomLeft]->height(); + if (w3 > width()/2) w3 = width()/2; + if (h3 > height()/2) h3 = height()/2; + p.drawPixmap(0,height()-h3,*framePixmaps[FrameBottomLeft], + 0,framePixmaps[FrameBottomLeft]->height()-h3,w3, h3); + + int w4 = framePixmaps[FrameBottomRight]->width(); + int h4 = framePixmaps[FrameBottomRight]->height(); + if (w4 > width()/2) w4 = width()/2; + if (h4 > height()/2) h4 = height()/2; + p.drawPixmap(width()-w4,height()-h4,*(framePixmaps[FrameBottomRight]), + framePixmaps[FrameBottomRight]->width()-w4, + framePixmaps[FrameBottomRight]->height()-h4, + w4, h4); + + QPixmap pm; + QWMatrix m; + int n,s,w; + //top + pm = *framePixmaps[FrameTop]; + + if (pm.width() > 0){ + s = width()-w2-w1; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w1; + while (1){ + if (pm.width() < width()-w2-x){ + p.drawPixmap(x,maxExtent-pm.height()-1, + pm); + x += pm.width(); + } + else { + p.drawPixmap(x,maxExtent-pm.height()-1, + pm, + 0,0,width()-w2-x,pm.height()); + break; + } + } + } + + //bottom + pm = *framePixmaps[FrameBottom]; + + if (pm.width() > 0){ + s = width()-w4-w3; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w3; + while (1){ + if (pm.width() < width()-w4-x){ + p.drawPixmap(x,height()-maxExtent+1,pm); + x += pm.width(); + } + else { + p.drawPixmap(x,height()-maxExtent+1,pm, + 0,0,width()-w4-x,pm.height()); + break; + } + } + } + + //left + pm = *framePixmaps[FrameLeft]; + + if (pm.height() > 0){ + s = height()-h3-h1; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h1; + while (1){ + if (pm.height() < height()-h3-y){ + p.drawPixmap(maxExtent-pm.width()-1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(maxExtent-pm.width()-1, y, + pm, + 0,0, pm.width(), + height()-h3-y); + break; + } + } + } + + //right + pm = *framePixmaps[FrameRight]; + + if (pm.height() > 0){ + s = height()-h4-h2; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h2; + while (1){ + if (pm.height() < height()-h4-y){ + p.drawPixmap(width()-maxExtent+1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(width()-maxExtent+1, y, + pm, + 0,0, pm.width(), + height()-h4-y); + break; + } + } + } + drawTitle(p); + + QColor c = widget()->colorGroup().background(); + + // KWM evidently had a 1 pixel border around the client window. We + // emulate it here, but should be removed at some point in order to + // seamlessly mesh widget themes + p.setPen(c); + p.drawRect(maxExtent-1, maxExtent-1, width()-(maxExtent-1)*2, + height()-(maxExtent-1)*2); + + // We fill the area behind the wrapped widget to ensure that + // shading animation is drawn as smoothly as possible + QRect r(layout->cellGeometry(2, 1)); + p.fillRect( r.x(), r.y(), r.width(), r.height(), c); + p.end(); +} + +void KWMThemeClient::doShape() +{ + + QBitmap shapemask(width(), height()); + shapemask.fill(color0); + QPainter p; + p.begin(&shapemask); + p.setBrush(color1); + p.setPen(color1); + int x,y; + // first the corners + int w1 = framePixmaps[FrameTopLeft]->width(); + int h1 = framePixmaps[FrameTopLeft]->height(); + if (w1 > width()/2) w1 = width()/2; + if (h1 > height()/2) h1 = height()/2; + if (framePixmaps[FrameTopLeft]->mask()) + p.drawPixmap(0,0,*framePixmaps[FrameTopLeft]->mask(), + 0,0,w1, h1); + else + p.fillRect(0,0,w1,h1,color1); + int w2 = framePixmaps[FrameTopRight]->width(); + int h2 = framePixmaps[FrameTopRight]->height(); + if (w2 > width()/2) w2 = width()/2; + if (h2 > height()/2) h2 = height()/2; + if (framePixmaps[FrameTopRight]->mask()) + p.drawPixmap(width()-w2,0,*framePixmaps[FrameTopRight]->mask(), + framePixmaps[FrameTopRight]->width()-w2,0,w2, h2); + else + p.fillRect(width()-w2,0,w2, h2,color1); + + int w3 = framePixmaps[FrameBottomLeft]->width(); + int h3 = framePixmaps[FrameBottomLeft]->height(); + if (w3 > width()/2) w3 = width()/2; + if (h3 > height()/2) h3 = height()/2; + if (framePixmaps[FrameBottomLeft]->mask()) + p.drawPixmap(0,height()-h3,*framePixmaps[FrameBottomLeft]->mask(), + 0,framePixmaps[FrameBottomLeft]->height()-h3,w3, h3); + else + p.fillRect(0,height()-h3,w3,h3,color1); + + int w4 = framePixmaps[FrameBottomRight]->width(); + int h4 = framePixmaps[FrameBottomRight]->height(); + if (w4 > width()/2) w4 = width()/2; + if (h4 > height()/2) h4 = height()/2; + if (framePixmaps[FrameBottomRight]->mask()) + p.drawPixmap(width()-w4,height()-h4,*framePixmaps[FrameBottomRight]->mask(), + framePixmaps[FrameBottomRight]->width()-w4, + framePixmaps[FrameBottomRight]->height()-h4, + w4, h4); + else + p.fillRect(width()-w4,height()-h4,w4,h4,color1); + + QPixmap pm; + QWMatrix m; + int n,s,w; + //top + if (framePixmaps[FrameTop]->mask()) + { + pm = *framePixmaps[FrameTop]->mask(); + + s = width()-w2-w1; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w1; + while (1){ + if (pm.width() < width()-w2-x){ + p.drawPixmap(x,maxExtent-pm.height()-1, + pm); + x += pm.width(); + } + else { + p.drawPixmap(x,maxExtent-pm.height()-1, + pm, + 0,0,width()-w2-x,pm.height()); + break; + } + } + } + + //bottom + if (framePixmaps[FrameBottom]->mask()) + { + pm = *framePixmaps[FrameBottom]->mask(); + + s = width()-w4-w3; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w3; + while (1){ + if (pm.width() < width()-w4-x){ + p.drawPixmap(x,height()-maxExtent+1,pm); + x += pm.width(); + } + else { + p.drawPixmap(x,height()-maxExtent+1,pm, + 0,0,width()-w4-x,pm.height()); + break; + } + } + } + + //left + if (framePixmaps[FrameLeft]->mask()) + { + pm = *framePixmaps[FrameLeft]->mask(); + + s = height()-h3-h1; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h1; + while (1){ + if (pm.height() < height()-h3-y){ + p.drawPixmap(maxExtent-pm.width()-1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(maxExtent-pm.width()-1, y, + pm, + 0,0, pm.width(), + height()-h3-y); + break; + } + } + } + + //right + if (framePixmaps[FrameRight]->mask()) + { + pm = *framePixmaps[FrameRight]->mask(); + + s = height()-h4-h2; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h2; + while (1){ + if (pm.height() < height()-h4-y){ + p.drawPixmap(width()-maxExtent+1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(width()-maxExtent+1, y, + pm, + 0,0, pm.width(), + height()-h4-y); + break; + } + } + } + p.fillRect(maxExtent-1, maxExtent-1, width()-2*maxExtent+2, height()-2*maxExtent+2, color1); + setMask(shapemask); +} + + +void KWMThemeClient::showEvent(QShowEvent *) +{ + doShape(); + widget()->repaint(false); +} + +void KWMThemeClient::mouseDoubleClickEvent( QMouseEvent * e ) +{ + if (e->button() == LeftButton && titlebar->geometry().contains( e->pos() ) ) + titlebarDblClickOperation(); +} + +void KWMThemeClient::desktopChange() +{ + if (stickyBtn) { + bool on = isOnAllDesktops(); + stickyBtn->setPixmap(on ? *pindownPix : *pinupPix); + QToolTip::remove( stickyBtn ); + QToolTip::add( stickyBtn, on ? i18n("Unsticky") : i18n("Sticky") ); + } +} + +void KWMThemeClient::maximizeChange() +{ + if (maxBtn) { + bool m = maximizeMode() == MaximizeFull; + maxBtn->setPixmap(m ? *minmaxPix : *maxPix); + QToolTip::remove( maxBtn ); + QToolTip::add( maxBtn, m ? i18n("Restore") : i18n("Maximize")); + } +} + +void KWMThemeClient::slotMaximize() +{ + maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull ); +} + +void KWMThemeClient::activeChange() +{ + widget()->update(); +} + +KDecoration::Position KWMThemeClient::mousePosition(const QPoint &p) const +{ + Position m = KDecoration::mousePosition(p); + // corners + if(p.y() < framePixmaps[FrameTop]->height() && + p.x() < framePixmaps[FrameLeft]->width()){ + m = PositionTopLeft; + } + else if(p.y() < framePixmaps[FrameTop]->height() && + p.x() > width()-framePixmaps[FrameRight]->width()){ + m = PositionTopRight; + } + else if(p.y() > height()-framePixmaps[FrameBottom]->height() && + p.x() < framePixmaps[FrameLeft]->width()){ + m = PositionBottomLeft; + } + else if(p.y() > height()-framePixmaps[FrameBottom]->height() && + p.x() > width()-framePixmaps[FrameRight]->width()){ + m = PositionBottomRight; + } // edges + else if(p.y() < framePixmaps[FrameTop]->height()) + m = PositionTop; + else if(p.y() > height()-framePixmaps[FrameBottom]->height()) + m = PositionBottom; + else if(p.x() < framePixmaps[FrameLeft]->width()) + m = PositionLeft; + else if(p.x() > width()-framePixmaps[FrameRight]->width()) + m = PositionRight; + return(m); +} + +void KWMThemeClient::menuButtonPressed() +{ + mnuBtn->setDown(false); // will stay down if I don't do this + QPoint pos = mnuBtn->mapToGlobal(mnuBtn->rect().bottomLeft()); + showWindowMenu( pos ); +} + +void KWMThemeClient::iconChange() +{ + if(mnuBtn){ + if( icon().pixmap( QIconSet::Small, QIconSet::Normal ).isNull()){ + mnuBtn->setPixmap(*menuPix); + } + else{ + mnuBtn->setPixmap(icon().pixmap( QIconSet::Small, QIconSet::Normal )); + } + } +} + +bool KWMThemeClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Show: + showEvent( static_cast< QShowEvent* >( e ) ); + return true; + + default: + return false; + } +} + +QSize KWMThemeClient::minimumSize() const +{ + return widget()->minimumSize().expandedTo( QSize( 100, 50 )); +} + +void KWMThemeClient::resize( const QSize& s ) +{ + widget()->resize( s ); +} + +void KWMThemeClient::borders( int& left, int& right, int& top, int& bottom ) const +{ + left = + right = + top = + bottom = + +TODO +} + +KWMThemeFactory::KWMThemeFactory() +{ + create_pixmaps(); +} + +KWMThemeFactory::~KWMThemeFactory() +{ + delete_pixmaps(); +} + +KDecoration* KWMThemeFactory::createDecoration( KDecorationBridge* b ) +{ + return new KWMThemeClient( b, this ); +} + +bool KWMThemeFactory::reset( unsigned long mask ) +{ + bool needHardReset = false; + +TODO + + // doesn't obey the Border size setting + if( mask & ( SettingFont | SettingButtons )) + needHardReset = true; + + if( mask & ( SettingFont | SettingColors )) { + KWMTheme::delete_pixmaps(); + KWMTheme::create_pixmaps(); + } + + if( !needHardReset ) + resetDecorations( mask ); + return needHardReset; +} + +} + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + return new KWMTheme::KWMThemeFactory(); + } +} + +#include "kwmthemeclient.moc" diff --git a/kwin/clients/kwmtheme/kwmthemeclient.h b/kwin/clients/kwmtheme/kwmthemeclient.h new file mode 100644 index 000000000..b1d623965 --- /dev/null +++ b/kwin/clients/kwmtheme/kwmthemeclient.h @@ -0,0 +1,74 @@ +#ifndef __KWMTHEMECLIENT_H +#define __KWMTHEMECLIENT_H + +#include <qbutton.h> +#include <qtoolbutton.h> +#include <kpixmap.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +class QLabel; +class QSpacerItem; +class QGridLayout; + +namespace KWMTheme { + +class MyButton : public QToolButton +{ +public: + MyButton(QWidget *parent=0, const char *name=0) + : QToolButton(parent, name){setAutoRaise(true);setCursor( arrowCursor ); } +protected: + void drawButtonLabel(QPainter *p); +}; + +class KWMThemeClient : public KDecoration +{ + Q_OBJECT +public: + KWMThemeClient( KDecorationBridge* b, KDecorationFactory* f ); + ~KWMThemeClient(){;} + void init(); + void resize( const QSize& s ); + QSize minimumSize() const; + void borders( int& left, int& right, int& top, int& bottom ) const; +protected: + void doShape(); + void drawTitle(QPainter &p); + void resizeEvent( QResizeEvent* ); + void paintEvent( QPaintEvent* ); + void showEvent( QShowEvent* ); + void mouseDoubleClickEvent( QMouseEvent * ); + bool eventFilter( QObject* o, QEvent* e ); + void captionChange(); + void desktopChange(); + void maximizeChange(); + void iconChange(); + void activeChange(); + void shadeChange() {}; + Position mousePosition(const QPoint &) const; +protected slots: + //void slotReset(); + void menuButtonPressed(); + void slotMaximize(); +private: + QPixmap buffer; + KPixmap *aGradient, *iGradient; + MyButton *maxBtn, *stickyBtn, *mnuBtn; + QSpacerItem *titlebar; + QGridLayout* layout; +}; + +class KWMThemeFactory : public KDecorationFactory +{ +public: + KWMThemeFactory(); + ~KWMThemeFactory(); + KDecoration* createDecoration( KDecorationBridge* b ); + bool reset( unsigned long mask ); +}; + +} + +#endif + diff --git a/kwin/clients/laptop/Makefile.am b/kwin/clients/laptop/Makefile.am new file mode 100644 index 000000000..eb870720a --- /dev/null +++ b/kwin/clients/laptop/Makefile.am @@ -0,0 +1,17 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_laptop.la + +kwin3_laptop_la_SOURCES = laptopclient.cpp +kwin3_laptop_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_laptop_la_LIBADD = ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = laptopclient.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = laptop.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/laptop/laptop.desktop b/kwin/clients/laptop/laptop.desktop new file mode 100644 index 000000000..38c5386f1 --- /dev/null +++ b/kwin/clients/laptop/laptop.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=Laptop +Name[af]=Draagbare rekenaar +Name[ar]=الØاسوب النقّال +Name[az]=Dizüstü Kompüter +Name[be]=Ðоўтбук +Name[bg]=Лаптоп +Name[bn]=লà§à¦¯à¦¾à¦ªà¦Ÿà¦ª +Name[br]=Hezoug +Name[ca]=Portà til +Name[cs]=Notebook +Name[cy]=Gluniadur +Name[da]=Bærbar +Name[el]=ΦοÏητό +Name[eo]=Tekokomputilo +Name[es]=Portátil +Name[eu]=Ordenagailu eramangarria +Name[fa]=رایانۀ Ú©ÛŒÙÛŒ +Name[fi]=Kannettava +Name[fr]=Ordinateur portable +Name[fy]=Skoatkompjûter +Name[ga]=RÃomhaire Glúine +Name[gl]=Portátil +Name[he]=מחשב × ×™×™×“ +Name[hi]=लैपटॉप +Name[hsb]=laptop +Name[hu]=Noteszgép +Name[is]=Ferðavél +Name[it]=Portatile +Name[ja]=ラップトップ +Name[ka]=ლეპტáƒáƒžáƒ˜ +Name[kk]=Ðоутбук +Name[km]=កុំព្យូទáŸážšâ€‹áž™áž½ážšâ€‹ážŠáŸƒ +Name[ko]=랩탑 +Name[lo]=à»àº¥àºšàº—àºàºš +Name[lt]=NeÅ¡iojamas kompiuteris +Name[lv]=Laptops +Name[mk]=Лаптоп +Name[mn]=Лаптоп +Name[ms]=Komputer riba +Name[nb]=Bærbar +Name[nds]=Klappreekner +Name[ne]=लà¥à¤¯à¤¾à¤ªà¤Ÿà¤ª +Name[nn]=Berbar +Name[oc]=Portatil +Name[pa]=ਲੈਪਟਾਪ +Name[pt]=Portátil +Name[ru]=Ðоутбук +Name[rw]=Mudasobwa Igendanwa +Name[se]=Mátkedihtor +Name[sl]=Prenosnik +Name[sr]=Лаптоп +Name[sv]=Bärbar dator +Name[ta]=மடிகà¯à®•à®£à®¿à®©à®¿ +Name[te]=లాపౠటాపౠ+Name[tg]=Ðоутбук +Name[th]=à¹à¸¥à¸›à¸—à¸à¸› +Name[tr]=Dizüstü +Name[tt]=Qulsanaq +Name[uk]=Мобільний комп'ютер (лептоп) +Name[uz@cyrillic]=Лаптоп +Name[ven]=Khomupwutha pfarwa +Name[vi]=Máy xách tay +Name[wa]=PoirtÃ¥ve +Name[xh]=Umphezulu osongiweyo +Name[zh_CN]=笔记本电脑 +Name[zh_TW]=ç†è¨˜åž‹é›»è…¦ +Name[zu]=Ikhomputha ephathwayo eyisicaba +X-KDE-Library=kwin3_laptop diff --git a/kwin/clients/laptop/laptopclient.cpp b/kwin/clients/laptop/laptopclient.cpp new file mode 100644 index 000000000..4d959ff8c --- /dev/null +++ b/kwin/clients/laptop/laptopclient.cpp @@ -0,0 +1,761 @@ +/* + * Laptop KWin Decoration + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Port of this decoration to KDE 3.2, accessibility enhancement are + * Copyright (c) 2003 Luciano Montanaro <mikelima@cirulla.net> + */ + +#include <kconfig.h> // up here to avoid X11 header conflict :P +#include "laptopclient.h" +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kdrawutil.h> +#include <kglobal.h> +#include <klocale.h> +#include <qbitmap.h> + +namespace Laptop { + +static const unsigned char iconify_bits[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18}; + +static const unsigned char close_bits[] = { + 0x42, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0x42}; + +static const unsigned char maximize_bits[] = { + 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0xff, 0xff }; + +static const unsigned char r_minmax_bits[] = { + 0x0c, 0x18, 0x33, 0x67, 0xcf, 0x9f, 0x3f, 0x3f}; + +static const unsigned char l_minmax_bits[] = { + 0x30, 0x18, 0xcc, 0xe6, 0xf3, 0xf9, 0xfc, 0xfc}; + +static const unsigned char question_bits[] = { + 0x3c, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x18}; + +static const unsigned char unsticky_bits[] = { + 0x3c, 0x42, 0x99, 0xbd, 0xbd, 0x99, 0x42, 0x3c}; + +static const unsigned char sticky_bits[] = { + 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; + +static QPixmap *titlePix; +static KPixmap *aUpperGradient; +static KPixmap *iUpperGradient; +// buttons active, inactive, up, down, and 2 sizes :P +static KPixmap *btnPix1; +static KPixmap *iBtnPix1; +static KPixmap *btnDownPix1; +static KPixmap *iBtnDownPix1; +static KPixmap *btnPix2; +static KPixmap *btnDownPix2; +static KPixmap *iBtnPix2; +static KPixmap *iBtnDownPix2; +static QColor btnForeground; + +static int titleHeight = 14; +static int btnWidth1 = 17; +static int btnWidth2 = 27; + +static int handleSize = 8; // the resize handle size in pixels + +static bool pixmaps_created = false; + +// ===================================== + +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new Laptop::LaptopClientFactory(); +} + +// ===================================== + +static inline const KDecorationOptions* options() +{ + return KDecoration::options(); +} + +static void drawButtonFrame(KPixmap *pix, const QColorGroup &g, bool sunken) +{ + QPainter p; + int w = pix->width(); + int h = pix->height(); + int x2 = w-1; + int y2 = h-1; + p.begin(pix); + + if(sunken){ + qDrawShadePanel(&p, 0, 0, w, h, g, true, 2); + } + else{ + p.setPen(g.dark()); + p.drawRect(0, 0, w-1, h-1); + p.setPen(g.light()); + p.drawLine(x2, 0, x2, y2); + p.drawLine(0, y2, x2, y2); + p.drawLine(1, 1, x2-2, 1); + p.drawLine(1, 1, 1, y2-2); + p.end(); + } +} + +static void create_pixmaps() +{ + if(pixmaps_created) + return; + pixmaps_created = true; + + titleHeight = QFontMetrics(options()->font(true)).height() + 2; + if (titleHeight < handleSize) titleHeight = handleSize; + titleHeight &= ~1; // Make title height even + if (titleHeight < 14) titleHeight = 14; + + btnWidth1 = titleHeight + 3; + btnWidth2 = 3*titleHeight/2 + 6; + + // titlebar + QPainter p; + QPainter maskPainter; + int i, x, y; + titlePix = new QPixmap(33, 12); + QBitmap mask(33, 12); + mask.fill(Qt::color0); + + p.begin(titlePix); + maskPainter.begin(&mask); + maskPainter.setPen(Qt::color1); + for(i=0, y=2; i < 3; ++i, y+=4){ + for(x=1; x <= 33; x+=3){ + p.setPen(options()->color(KDecoration::ColorTitleBar, true).light(150)); + p.drawPoint(x, y); + maskPainter.drawPoint(x, y); + p.setPen(options()->color(KDecoration::ColorTitleBar, true).dark(150)); + p.drawPoint(x+1, y+1); + maskPainter.drawPoint(x+1, y+1); + } + } + p.end(); + maskPainter.end(); + titlePix->setMask(mask); + + if(QPixmap::defaultDepth() > 8){ + aUpperGradient = new KPixmap; + aUpperGradient->resize(32, titleHeight+2); + iUpperGradient = new KPixmap; + iUpperGradient->resize(32, titleHeight+2); + QColor bgColor = options()->color(KDecoration::ColorTitleBar, true); + KPixmapEffect::gradient(*aUpperGradient, + bgColor.light(120), + bgColor.dark(120), + KPixmapEffect::VerticalGradient); + bgColor = options()->color(KDecoration::ColorTitleBar, false); + KPixmapEffect::gradient(*iUpperGradient, + bgColor.light(120), + bgColor.dark(120), + KPixmapEffect::VerticalGradient); + } + // buttons (active/inactive, sunken/unsunken, 2 sizes each) + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColor c = g.background(); + btnPix1 = new KPixmap; + btnPix1->resize(btnWidth1, titleHeight); + btnDownPix1 = new KPixmap; + btnDownPix1->resize(btnWidth1, titleHeight); + btnPix2 = new KPixmap; + btnPix2->resize(btnWidth2, titleHeight); + btnDownPix2 = new KPixmap; + btnDownPix2->resize(btnWidth2, titleHeight); + iBtnPix1 = new KPixmap; + iBtnPix1->resize(btnWidth1, titleHeight); + iBtnDownPix1 = new KPixmap; + iBtnDownPix1->resize(btnWidth1, titleHeight); + iBtnPix2 = new KPixmap; + iBtnPix2->resize(btnWidth2, titleHeight); + iBtnDownPix2 = new KPixmap; + iBtnDownPix2->resize(btnWidth2, titleHeight); + if(QPixmap::defaultDepth() > 8){ + KPixmapEffect::gradient(*btnPix1, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnDownPix1, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnPix2, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnDownPix2, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + KPixmapEffect::gradient(*iBtnPix1, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnDownPix1, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnPix2, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnDownPix2, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + } + else{ + btnPix1->fill(c.rgb()); + btnDownPix1->fill(c.rgb()); + btnPix2->fill(c.rgb()); + btnDownPix2->fill(c.rgb()); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + iBtnPix1->fill(c.rgb()); + iBtnDownPix1->fill(c.rgb()); + iBtnPix2->fill(c.rgb()); + iBtnDownPix2->fill(c.rgb()); + } + g = options()->colorGroup(KDecoration::ColorButtonBg, true); + c = g.background(); + drawButtonFrame(btnPix1, g, false); + drawButtonFrame(btnDownPix1, g, true); + drawButtonFrame(btnPix2, g, false); + drawButtonFrame(btnDownPix2, g, true); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + drawButtonFrame(iBtnPix1, g, false); + drawButtonFrame(iBtnDownPix1, g, true); + drawButtonFrame(iBtnPix2, g, false); + drawButtonFrame(iBtnDownPix2, g, true); + + if(qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 128) + btnForeground = Qt::black; + else + btnForeground = Qt::white; +} + +static void delete_pixmaps() +{ + delete titlePix; + if(aUpperGradient){ + delete aUpperGradient; + delete iUpperGradient; + delete btnPix1; + delete btnDownPix1; + delete iBtnPix1; + delete iBtnDownPix1; + delete btnPix2; + delete btnDownPix2; + delete iBtnPix2; + delete iBtnDownPix2; + } + pixmaps_created = false; +} + +// ===================================== + +LaptopButton::LaptopButton(ButtonType type, LaptopClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode(QWidget::NoBackground); +} + +void LaptopButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + if (isOn() ) { + setBitmap(isLeft() ? l_minmax_bits : r_minmax_bits); + } else { + setBitmap(maximize_bits); + } + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void LaptopButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + deco = QBitmap(8, 8, bitmap, true); + else { + deco = QBitmap(8,8); + deco.fill(Qt::color0); + } + deco.setMask(deco); + repaint(); +} + +void LaptopButton::drawButton(QPainter *p) +{ + bool smallBtn = width() == btnWidth1; + if(btnPix1){ + if(decoration()->isActive()){ + if(isDown()) + p->drawPixmap(0, 0, smallBtn ? *btnDownPix1 : *btnDownPix2); + else + p->drawPixmap(0, 0, smallBtn ? *btnPix1 : *btnPix2); + } + else{ + if(isDown()) + p->drawPixmap(0, 0, smallBtn ? *iBtnDownPix1 : *iBtnDownPix2); + else + p->drawPixmap(0, 0, smallBtn ? *iBtnPix1 : *iBtnPix2); + } + } + else{ + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, decoration()->isActive()); + int w = width(); + int h = height(); + p->fillRect(1, 1, w-2, h-2, isDown() ? g.mid() : g.button()); + p->setPen(isDown() ? g.dark() : g.light()); + p->drawLine(0, 0, w-1, 0); + p->drawLine(0, 0, 0, w-1); + p->setPen(isDown() ? g.light() : g.dark()); + p->drawLine(w-1, 0, w-1, h-1); + p->drawLine(0, h-1, w-1, h-1); + } + + p->setPen(btnForeground); + int xOff = (width()-8)/2; + int yOff = (height()-8)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco); +} + +// ===================================== + +void LaptopClient::reset(unsigned long changed) +{ + KCommonDecoration::reset(changed); +} + +LaptopClient::LaptopClient(KDecorationBridge *b, KDecorationFactory *f) + : KCommonDecoration(b, f) +{ +} + +LaptopClient::~LaptopClient() +{ +} + +QString LaptopClient::visibleName() const +{ + return i18n("Laptop"); +} + +QString LaptopClient::defaultButtonsLeft() const +{ + return "X"; +} + +QString LaptopClient::defaultButtonsRight() const +{ + return "HSIA"; +} + +bool LaptopClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int LaptopClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + switch (lm) { + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + case LM_BorderLeft: + case LM_BorderRight: + return 4; + + case LM_BorderBottom: + return mustDrawHandle() ? handleSize : 4; + + case LM_TitleEdgeTop: + return 3; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 0; + + case LM_ButtonWidth: + { + if (btn && (btn->type()==HelpButton||btn->type()==OnAllDesktopsButton) ) { + return btnWidth1; + } else { + return btnWidth2; + } + } + + case LM_ButtonHeight: + case LM_TitleHeight: + if (isToolWindow() ) + return titleHeight-2; + else + return titleHeight; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 0; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *LaptopClient::createButton(ButtonType type) +{ + switch (type) { + case OnAllDesktopsButton: + return new LaptopButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new LaptopButton(HelpButton, this, "help"); + + case MinButton: + return new LaptopButton(MinButton, this, "minimize"); + + case MaxButton: + return new LaptopButton(MaxButton, this, "maximize"); + + case CloseButton: + return new LaptopButton(CloseButton, this, "close"); + + default: + return 0; + } +} + +void LaptopClient::init() +{ + bufferDirty = true; + + KCommonDecoration::init(); +} + +void LaptopClient::captionChange() +{ + bufferDirty = true; + + KCommonDecoration::captionChange(); +} + +void LaptopClient::paintEvent( QPaintEvent* ) +{ + QPainter p(widget()); + QColorGroup g = options()->colorGroup(KDecoration::ColorFrame, isActive()); + + QRect r(widget()->rect()); + p.setPen(Qt::black); + p.drawRect(r); + + // fill mid frame... + p.setPen(g.background() ); + p.drawLine(r.x()+2, r.y()+2, r.right()-2, r.y()+2); + p.drawLine(r.left()+2, r.y()+3, r.left()+2, r.bottom()-layoutMetric(LM_BorderBottom)+1 ); + p.drawLine(r.right()-2, r.y()+3, r.right()-2, r.bottom()-layoutMetric(LM_BorderBottom)+1 ); + p.drawLine(r.left()+3, r.y()+3, r.left()+3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); + p.drawLine(r.right()-3, r.y()+3, r.right()-3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); + if (!mustDrawHandle() ) + p.drawLine(r.left()+1, r.bottom()-2, r.right()-1, r.bottom()-2); + + // outer frame + p.setPen(g.light()); + p.drawLine(r.x()+1, r.y()+1, r.right()-1, r.y()+1); + p.drawLine(r.x()+1, r.y()+1, r.x()+1, r.bottom()-1); + p.setPen(g.dark()); + p.drawLine(r.right()-1, r.y()+1, r.right()-1, r.bottom()-1); + p.drawLine(r.x()+1, r.bottom()-1, r.right()-1, r.bottom()-1); + + int th = titleHeight; + int bb = handleSize + 2; // Bottom border + int bs = handleSize - 2; // inner size of bottom border + if (!mustDrawHandle()) { + bb = 6; + bs = 0; + } + if ( isToolWindow() ) + th -= 2; + + // inner rect + p.drawRect(r.x() + 3, r.y() + th + 3, r.width() - 6, r.height() - th - bb); + + // handles + if (mustDrawHandle()) { + if (r.width() > 3*handleSize + 20) { + int range = 8 + 3*handleSize/2; + qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, range, + handleSize - 2, g, false, 1, &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, r.x() + range + 1, r.bottom() - bs, + r.width() - 2*range - 2, handleSize - 2, g, false, 1, + isActive() ? &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, r.right() - range, r.bottom() - bs, + range, bs, g, false, 1, &g.brush(QColorGroup::Mid)); + } else { + qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, + r.width() - 2, bs, g, false, 1, + isActive() ? &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + } + } + + r = titleRect(); + + if(isActive()){ + updateActiveBuffer(); + p.drawPixmap(r.x(), r.y(), activeBuffer); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + } + else{ + if(iUpperGradient) + p.drawTiledPixmap(r.x(), r.y(), r.width(), r.height()-1, + *iUpperGradient); + else + p.fillRect(r.x(), r.y(), r.width(), r.height()-1, + options()->color(KDecoration::ColorTitleBar, false)); + + p.setFont(options()->font(false, isToolWindow() )); + QFontMetrics fm(options()->font(false)); + g = options()->colorGroup(KDecoration::ColorTitleBar, false); + if(iUpperGradient) + p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, + r.y(), fm.width(caption())+8, r.height()-1, + *iUpperGradient); + else + p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, r.y(), + fm.width(caption())+8, r.height()-1, + g.brush(QColorGroup::Background)); + p.setPen(g.mid()); + p.drawLine(r.x(), r.y(), r.right(), r.y()); + p.drawLine(r.x(), r.y(), r.x(), r.bottom()); + p.setPen(g.button()); + p.drawLine(r.right(), r.y(), r.right(), r.bottom()); + p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); + p.setPen(options()->color(KDecoration::ColorFont, false)); + p.drawText(r.x(), r.y(), r.width(), r.height()-1, + AlignCenter, caption() ); + g = options()->colorGroup(KDecoration::ColorFrame, true); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + } +} + +QRegion LaptopClient::cornerShape(WindowCorner corner) +{ + switch (corner) { + case WC_TopLeft: + return QRect(0, 0, 1, 1); + + case WC_TopRight: + return QRect(width()-1, 0, 1, 1); + + case WC_BottomLeft: + return QRect(0, height()-1, 1, 1); + + case WC_BottomRight: + return QRect(width()-1, height()-1, 1, 1); + + default: + return QRegion(); + } + +} + +bool LaptopClient::mustDrawHandle() const +{ + bool drawSmallBorders = !options()->moveResizeMaximizedWindows(); + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return isResizable(); + } +} + +void LaptopClient::updateActiveBuffer( ) +{ + QRect rTitle = titleRect(); + if( !bufferDirty && (lastBufferWidth == rTitle.width())) + return; + if ( rTitle.width() <= 0 || rTitle.height() <= 0 ) + return; + lastBufferWidth = rTitle.width(); + bufferDirty = false; + + activeBuffer.resize(rTitle.width(), + rTitle.height()); + QPainter p; + QRect r(0, 0, activeBuffer.width(), activeBuffer.height()); + p.begin(&activeBuffer); + if(aUpperGradient){ + p.drawTiledPixmap(r, *aUpperGradient); + } + else{ + p.fillRect(r, options()->color(KDecoration::ColorTitleBar, true)); + } + if(titlePix) + p.drawTiledPixmap(r, *titlePix); + + p.setFont(options()->font(true, isToolWindow() )); + QFontMetrics fm(options()->font(true)); + QColorGroup g = options()->colorGroup(KDecoration::ColorTitleBar, true); + if(aUpperGradient) + p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, + r.y(), fm.width(caption())+8, r.height()-1, + *aUpperGradient); + else + p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, 0, + fm.width(caption())+8, r.height(), + g.brush(QColorGroup::Background)); + p.setPen(g.mid()); + p.drawLine(r.x(), r.y(), r.right(), r.y()); + p.drawLine(r.x(), r.y(), r.x(), r.bottom()); + p.setPen(g.button()); + p.drawLine(r.right(), r.y(), r.right(), r.bottom()); + p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); + p.setPen(options()->color(KDecoration::ColorFont, true)); + p.drawText(r.x(), r.y(), r.width(), r.height()-1, + AlignCenter, caption() ); + g = options()->colorGroup(KDecoration::ColorFrame, true); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + p.end(); +} + +static const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | + NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | + NET::DialogMask | /*NET::OverrideMask |*/ NET::TopMenuMask | + NET::UtilityMask | NET::SplashMask; + +bool LaptopClient::isTransient() const +{ + NET::WindowType type = windowType(SUPPORTED_WINDOW_TYPES_MASK); + return type == NET::Dialog; +} + +// ===================================== + +LaptopClientFactory::LaptopClientFactory() +{ + create_pixmaps(); +} + +LaptopClientFactory::~LaptopClientFactory() +{ + delete_pixmaps(); +} + +KDecoration *LaptopClientFactory::createDecoration(KDecorationBridge *b) +{ + findPreferredHandleSize(); + return new Laptop::LaptopClient(b, this); +} + +bool LaptopClientFactory::reset(unsigned long changed) +{ + findPreferredHandleSize(); + + // TODO Do not recreate decorations if it is not needed. Look at + // ModernSystem for how to do that + Laptop::delete_pixmaps(); + Laptop::create_pixmaps(); + + bool needHardReset = true; + if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool LaptopClientFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + return true; + default: + return false; + }; +} + +QValueList< LaptopClientFactory::BorderSize > +LaptopClientFactory::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +void LaptopClientFactory::findPreferredHandleSize() +{ + switch (options()->preferredBorderSize(this)) { + case KDecoration::BorderLarge: + handleSize = 11; + break; + case KDecoration::BorderVeryLarge: + handleSize = 16; + break; + case KDecoration::BorderHuge: + handleSize = 24; + break; + case KDecoration::BorderVeryHuge: + handleSize = 32; + break; + case KDecoration::BorderOversized: + handleSize = 40; + break; + case KDecoration::BorderTiny: + case KDecoration::BorderNormal: + default: + handleSize = 8; + } +} + +} // Laptop namespace + +// vim: sw=4 diff --git a/kwin/clients/laptop/laptopclient.h b/kwin/clients/laptop/laptopclient.h new file mode 100644 index 000000000..b2e8c3555 --- /dev/null +++ b/kwin/clients/laptop/laptopclient.h @@ -0,0 +1,76 @@ +/* + * Laptop KWin Client + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Ported to the kde3.2 API by Luciano Montanaro <mikelima@cirulla.net> + */ +#ifndef __KDECLIENT_H +#define __KDECLIENT_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +namespace Laptop { + +class LaptopClient; + +class LaptopButton : public KCommonDecorationButton +{ +public: + LaptopButton(ButtonType type, LaptopClient *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + virtual void reset(unsigned long changed); + +protected: + virtual void drawButton(QPainter *p); + QBitmap deco; +}; + +class LaptopClient : public KCommonDecoration +{ +public: + LaptopClient( KDecorationBridge* b, KDecorationFactory* f ); + ~LaptopClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual QRegion cornerShape(WindowCorner corner); + + void init(); +protected: + void paintEvent( QPaintEvent* ); + void reset( unsigned long ); + void updateActiveBuffer(); + void captionChange(); +private: + bool mustDrawHandle() const; + bool isTransient() const; +private: + KPixmap activeBuffer; + int lastBufferWidth; + bool bufferDirty; +}; + +class LaptopClientFactory : public QObject, public KDecorationFactory +{ +public: + LaptopClientFactory(); + virtual ~LaptopClientFactory(); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; +private: + void findPreferredHandleSize(); +}; + +} + +#endif diff --git a/kwin/clients/modernsystem/Makefile.am b/kwin/clients/modernsystem/Makefile.am new file mode 100644 index 000000000..75e0249f8 --- /dev/null +++ b/kwin/clients/modernsystem/Makefile.am @@ -0,0 +1,19 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = config + +kde_module_LTLIBRARIES = kwin3_modernsys.la + +kwin3_modernsys_la_SOURCES = modernsys.cpp +kwin3_modernsys_la_LIBADD = ../../lib/libkdecorations.la +kwin3_modernsys_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module + +METASOURCES = AUTO +noinst_HEADERS = modernsys.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = modernsystem.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/modernsystem/btnhighcolor.h b/kwin/clients/modernsystem/btnhighcolor.h new file mode 100644 index 000000000..fa323b9eb --- /dev/null +++ b/kwin/clients/modernsystem/btnhighcolor.h @@ -0,0 +1,93 @@ +/* XPM */ +static const char * btnhighcolor_xpm[] = { +"14 15 75 1", +" c None", +". c #6E6E6E", +"+ c #757575", +"@ c #878787", +"# c #7D7D7D", +"$ c #9E9E9E", +"% c #B9B9B9", +"& c #C6C6C6", +"* c #BABABA", +"= c #A5A5A5", +"- c #7F7F7F", +"; c #848484", +"> c #A7A7A7", +", c #BFBFBF", +"' c #D1D1D1", +") c #D7D7D7", +"! c #DADADA", +"~ c #CBCBCB", +"{ c #ABABAB", +"] c #B3B3B3", +"^ c #C2C2C2", +"/ c #CACACA", +"( c #C9C9C9", +"_ c #B6B6B6", +": c #9A9A9A", +"< c #999999", +"[ c #B0B0B0", +"} c #C4C4C4", +"| c #C3C3C3", +"1 c #C0C0C0", +"2 c #AEAEAE", +"3 c #969696", +"4 c #C1C1C1", +"5 c #CCCCCC", +"6 c #C5C5C5", +"7 c #BEBEBE", +"8 c #AAAAAA", +"9 c #CECECE", +"0 c #D4D4D4", +"a c #DBDBDB", +"b c #DEDEDE", +"c c #D5D5D5", +"d c #D3D3D3", +"e c #BCBCBC", +"f c #CDCDCD", +"g c #E0E0E0", +"h c #E4E4E4", +"i c #E8E8E8", +"j c #EBEBEB", +"k c #E9E9E9", +"l c #E6E6E6", +"m c #DDDDDD", +"n c #E1E1E1", +"o c #EDEDED", +"p c #F1F1F1", +"q c #F5F5F5", +"r c #F8F8F8", +"s c #F6F6F6", +"t c #F3F3F3", +"u c #EEEEEE", +"v c #E5E5E5", +"w c #DCDCDC", +"x c #B7B7B7", +"y c #E2E2E2", +"z c #FDFDFD", +"A c #FFFFFF", +"B c #FCFCFC", +"C c #F7F7F7", +"D c #B5B5B5", +"E c #F2F2F2", +"F c #FAFAFA", +"G c #9B9B9B", +"H c #FBFBFB", +"I c #A9A9A9", +"J c #747474", +" .... ", +" ..+@@+.. ", +" .#$%&&*=-. ", +" .;>,')!)~{#. ", +" .$]^///(&_:. ", +".<[*^}||11*23.", +".[4&5555~(678.", +".,90!aba)cd~e.", +".faghijklhm06.", +".'nopqrstuvw/.", +".xyprzAzBCunD.", +" .'EzAAAAFpf. ", +" .GcHAAAAF0<. ", +" ..I5kk5I.. ", +" J..... "}; diff --git a/kwin/clients/modernsystem/buttondata.h b/kwin/clients/modernsystem/buttondata.h new file mode 100644 index 000000000..1af5fb8dd --- /dev/null +++ b/kwin/clients/modernsystem/buttondata.h @@ -0,0 +1,42 @@ +/* Image bits processed by KPixmap2Bitmaps */ + +#define lowcolor_mask_width 14 +#define lowcolor_mask_height 15 +static const unsigned char lowcolor_mask_bits[] = { + 0xf0,0x03,0xf8,0x07,0xfc,0xcf,0xfe,0x1f,0xfe,0x1f,0xff,0xff,0xff,0xff,0xff, + 0x3f,0xff,0x3f,0xff,0xbf,0xfe,0xdf,0xfe,0x9f,0xfc,0x0f,0xf8,0x07,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x7c,0xfe,0x87,0x40,0x00, + 0x00,0x64,0x00,0x20,0x00,0x64,0x00,0x86,0xfe,0x87,0x40,0x00,0x00,0x65,0x00 }; + +#define lowcolor_6a696a_width 14 +#define lowcolor_6a696a_height 15 +static const unsigned char lowcolor_6a696a_bits[] = { + 0xf0,0x03,0x18,0x06,0x04,0xcc,0x06,0x18,0x02,0x10,0x00,0xc0,0x00,0xc0,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0xc0,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, + 0x00,0x00,0x00,0x80,0x24,0x0e,0x08,0x61,0x00,0x00,0x00,0xf0,0xd9,0x0c,0x08 }; + +#define lowcolor_949194_width 14 +#define lowcolor_949194_height 15 +static const unsigned char lowcolor_949194_bits[] = { + 0x00,0x40,0xe0,0x01,0x08,0x02,0x00,0x04,0x04,0x08,0x07,0x78,0x03,0xf0,0x01, + 0xe0,0x01,0x60,0x01,0x20,0x00,0xc0,0x02,0x90,0x04,0x08,0x08,0x04,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0xc8,0x51,0x0c,0x08,0x0e, + 0x01,0x00,0x00,0x37,0x00,0x00,0x00,0x58,0x5f,0x49,0x6d,0x61,0x67,0x65,0x54 }; + +#define lowcolor_b4b6b4_width 14 +#define lowcolor_b4b6b4_height 15 +static const unsigned char lowcolor_b4b6b4_bits[] = { + 0x00,0x40,0x00,0x00,0x10,0x00,0x08,0x02,0x18,0x06,0xb8,0x47,0x0c,0xce,0x0e, + 0xd8,0x06,0x58,0x02,0x10,0x02,0xd0,0x00,0x80,0x00,0x00,0x10,0x02,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x08,0x00,0x02,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x38,0x5b,0x0c,0x08 }; + +#define lowcolor_e6e6e6_width 14 +#define lowcolor_e6e6e6_height 15 +static const unsigned char lowcolor_e6e6e6_bits[] = { + 0x00,0x40,0x00,0x00,0x00,0x00,0xe0,0x01,0x00,0x00,0x00,0x40,0x00,0xc0,0x00, + 0xc0,0x00,0x40,0xe0,0xc0,0xe0,0xc1,0xe0,0x81,0xf0,0x03,0xc0,0x00,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x08,0x19,0x0d,0x08,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00 }; + diff --git a/kwin/clients/modernsystem/config/Makefile.am b/kwin/clients/modernsystem/config/Makefile.am new file mode 100644 index 000000000..79bf21290 --- /dev/null +++ b/kwin/clients/modernsystem/config/Makefile.am @@ -0,0 +1,14 @@ + +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_modernsys_config.la + +kwin_modernsys_config_la_SOURCES = config.cpp +kwin_modernsys_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_modernsys_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + diff --git a/kwin/clients/modernsystem/config/config.cpp b/kwin/clients/modernsystem/config/config.cpp new file mode 100644 index 000000000..542494d28 --- /dev/null +++ b/kwin/clients/modernsystem/config/config.cpp @@ -0,0 +1,130 @@ +// Melchior FRANZ <mfranz@kde.org> -- 2001-04-22 + +#include <kapplication.h> +#include <kconfig.h> +#include <kdialog.h> +#include <klocale.h> +#include <kglobal.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include "config.h" + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config(KConfig* conf, QWidget* parent) + { + return(new ModernSysConfig(conf, parent)); + } +} + + +// 'conf' is a pointer to the kwindecoration modules open kwin config, +// and is by default set to the "Style" group. +// +// 'parent' is the parent of the QObject, which is a VBox inside the +// Configure tab in kwindecoration + +ModernSysConfig::ModernSysConfig(KConfig* conf, QWidget* parent) : QObject(parent) +{ + clientrc = new KConfig("kwinmodernsysrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + mainw = new QWidget(parent); + vbox = new QVBoxLayout(mainw); + vbox->setSpacing(6); + vbox->setMargin(0); + + handleBox = new QWidget(mainw); + QGridLayout* layout = new QGridLayout(handleBox, 0, KDialog::spacingHint()); + + cbShowHandle = new QCheckBox(i18n("&Show window resize handle"), handleBox); + QWhatsThis::add(cbShowHandle, + i18n("When selected, all windows are drawn with a resize " + "handle at the lower right corner. This makes window resizing " + "easier, especially for trackballs and other mouse replacements " + "on laptops.")); + layout->addMultiCellWidget(cbShowHandle, 0, 0, 0, 1); + connect(cbShowHandle, SIGNAL(clicked()), this, SLOT(slotSelectionChanged())); + + sliderBox = new QVBox(handleBox); + handleSizeSlider = new QSlider(0, 4, 1, 0, QSlider::Horizontal, sliderBox); + QWhatsThis::add(handleSizeSlider, + i18n("Here you can change the size of the resize handle.")); + handleSizeSlider->setTickInterval(1); + handleSizeSlider->setTickmarks(QSlider::Below); + connect(handleSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSelectionChanged())); + + hbox = new QHBox(sliderBox); + hbox->setSpacing(6); + + bool rtl = kapp->reverseLayout(); + label1 = new QLabel(i18n("Small"), hbox); + label1->setAlignment(rtl ? AlignRight : AlignLeft); + label2 = new QLabel(i18n("Medium"), hbox); + label2->setAlignment(AlignHCenter); + label3 = new QLabel(i18n("Large"), hbox); + label3->setAlignment(rtl ? AlignLeft : AlignRight); + + vbox->addWidget(handleBox); + vbox->addStretch(1); + +// layout->setColSpacing(0, 30); + layout->addItem(new QSpacerItem(30, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0); + layout->addWidget(sliderBox, 1, 1); + + load(conf); + mainw->show(); +} + + +ModernSysConfig::~ModernSysConfig() +{ + delete mainw; + delete clientrc; +} + + +void ModernSysConfig::slotSelectionChanged() +{ + bool i = cbShowHandle->isChecked(); + if (i != hbox->isEnabled()) { + hbox->setEnabled(i); + handleSizeSlider->setEnabled(i); + } + emit changed(); +} + + +void ModernSysConfig::load(KConfig* /*conf*/) +{ + clientrc->setGroup("General"); + bool i = clientrc->readBoolEntry("ShowHandle", true ); + cbShowHandle->setChecked(i); + hbox->setEnabled(i); + handleSizeSlider->setEnabled(i); + handleWidth = clientrc->readUnsignedNumEntry("HandleWidth", 6); + handleSize = clientrc->readUnsignedNumEntry("HandleSize", 30); + handleSizeSlider->setValue(QMIN((handleWidth - 6) / 2, 4)); + +} + + +void ModernSysConfig::save(KConfig* /*conf*/) +{ + clientrc->setGroup("General"); + clientrc->writeEntry("ShowHandle", cbShowHandle->isChecked()); + clientrc->writeEntry("HandleWidth", 6 + 2 * handleSizeSlider->value()); + clientrc->writeEntry("HandleSize", 30 + 4 * handleSizeSlider->value()); + clientrc->sync(); +} + + +void ModernSysConfig::defaults() +{ + cbShowHandle->setChecked(true); + hbox->setEnabled(true); + handleSizeSlider->setEnabled(true); + handleSizeSlider->setValue(0); +} + +#include "config.moc" diff --git a/kwin/clients/modernsystem/config/config.h b/kwin/clients/modernsystem/config/config.h new file mode 100644 index 000000000..4f4b97123 --- /dev/null +++ b/kwin/clients/modernsystem/config/config.h @@ -0,0 +1,50 @@ +#ifndef __KDE_MODSYSTEMCONFIG_H +#define __KDE_MODSYSTEMCONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qslider.h> +#include <qlabel.h> + +class ModernSysConfig : public QObject +{ + Q_OBJECT + + public: + ModernSysConfig(KConfig* conf, QWidget* parent); + ~ModernSysConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load(KConfig* conf); + void save(KConfig* conf); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig *clientrc; + QWidget *mainw; + QVBoxLayout *vbox; + QWidget *handleBox; + QCheckBox *cbShowHandle; + QVBox *sliderBox; + QSlider *handleSizeSlider; + QHBox *hbox; + QLabel *label1; + QLabel *label2; + QLabel *label3; + + unsigned handleWidth; + unsigned handleSize; + +}; + + +#endif diff --git a/kwin/clients/modernsystem/modernsys.cpp b/kwin/clients/modernsystem/modernsys.cpp new file mode 100644 index 000000000..d75816343 --- /dev/null +++ b/kwin/clients/modernsystem/modernsys.cpp @@ -0,0 +1,739 @@ +// Daniel M. DULEY <mosfet@kde.org> original work +// Melchior FRANZ <a8603365@unet.univie.ac.at> configuration options + +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kdrawutil.h> +#include <qbitmap.h> +#include <qtooltip.h> +#include <qapplication.h> +#include <qlabel.h> +#include "modernsys.h" + +#include "buttondata.h" +#include "btnhighcolor.h" +#include <qimage.h> + +namespace ModernSystem { + +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00}; + +static unsigned char close_bits[] = { + 0x00, 0x66, 0x7e, 0x3c, 0x3c, 0x7e, 0x66, 0x00}; + +static unsigned char maximize_bits[] = { + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00}; + +static unsigned char r_minmax_bits[] = { + 0x0c, 0x18, 0x33, 0x67, 0xcf, 0x9f, 0x3f, 0x3f}; + +static unsigned char l_minmax_bits[] = { + 0x30, 0x18, 0xcc, 0xe6, 0xf3, 0xf9, 0xfc, 0xfc}; + +static unsigned char unsticky_bits[] = { + 0x3c, 0x42, 0x99, 0xbd, 0xbd, 0x99, 0x42, 0x3c}; + +static unsigned char sticky_bits[] = { + 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; + +static unsigned char question_bits[] = { + 0x3c, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x18}; + +static unsigned char above_on_bits[] = { + 0x7e, 0x00, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00}; + +static unsigned char above_off_bits[] = { + 0x18, 0x3c, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}; + +static unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x3c, 0x18}; + +static unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x00, 0x7e}; + +static unsigned char shade_off_bits[] = { + 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static unsigned char shade_on_bits[] = { + 0x00, 0x7e, 0x7e, 0x42, 0x42, 0x42, 0x7e, 0x00}; + +static unsigned char menu_bits[] = { + 0xff, 0x81, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff}; + +static unsigned char btnhighcolor_mask_bits[] = { + 0xe0,0x41,0xf8,0x07,0xfc,0x0f,0xfe,0xdf,0xfe,0x1f,0xff,0x3f,0xff,0xff,0xff, + 0x3f,0xff,0x3f,0xff,0xff,0xff,0xff,0xfe,0x9f,0xfe,0x1f,0xfc,0x0f,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x20,0x99,0x0f,0x08,0xc4, + 0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x58,0x5f,0x43,0x68,0x61,0x6e,0x67,0x65 }; + +static KPixmap *aUpperGradient=0; +static KPixmap *iUpperGradient=0; +static QPixmap *buttonPix=0; +static QPixmap *buttonPixDown=0; +static QPixmap *iButtonPix=0; +static QPixmap *iButtonPixDown=0; + +static QColor *buttonFg; +static bool pixmaps_created = false; + +static QBitmap *lcDark1; +static QBitmap *lcDark2; +static QBitmap *lcDark3; +static QBitmap *lcLight1; +static QImage *btnSource; + +static bool show_handle; +static int handle_size; +static int handle_width; +static int border_width; +static int title_height; + +static inline const KDecorationOptions* options() +{ + return KDecoration::options(); +} + +static void make_button_fx(const QColorGroup &g, QPixmap *pix, bool light=false) +{ + pix->fill(g.background()); + QPainter p(pix); + + if(QPixmap::defaultDepth() > 8){ + int i, destH, destS, destV, srcH, srcS, srcV; + QColor btnColor = g.background(); + + if(btnSource->depth() < 32) + *btnSource = btnSource->convertDepth(32); + if(light) + btnColor = btnColor.light(120); + btnColor.hsv(&destH, &destS, &destV); + QImage btnDest(14, 15, 32); + + unsigned int *srcData = (unsigned int *)btnSource->bits(); + unsigned int *destData = (unsigned int *)btnDest.bits(); + QColor srcColor; + for(i=0; i < btnSource->width()*btnSource->height(); ++i){ + srcColor.setRgb(srcData[i]); + srcColor.hsv(&srcH, &srcS, &srcV); + srcColor.setHsv(destH, destS, srcV); + destData[i] = srcColor.rgb(); + } + pix->convertFromImage(btnDest); + + } + else{ + if(!lcDark1->mask()){ + lcDark1->setMask(*lcDark1); + lcDark2->setMask(*lcDark2); + lcDark3->setMask(*lcDark3); + lcLight1->setMask(*lcLight1); + } + p.setPen(g.dark()); + p.drawPixmap(0, 0, *lcDark2); + p.drawPixmap(0, 0, *lcDark1); + p.setPen(g.mid()); + p.drawPixmap(0, 0, *lcDark3); + p.setPen(g.light()); + p.drawPixmap(0, 0, *lcLight1); + } +} + + +static void create_pixmaps() +{ + if(pixmaps_created) + return; + pixmaps_created = true; + + lcDark1 = new QBitmap(14, 15, lowcolor_6a696a_bits, true); + lcDark2 = new QBitmap(14, 15, lowcolor_949194_bits, true); + lcDark3 = new QBitmap(14, 15, lowcolor_b4b6b4_bits, true); + lcLight1 = new QBitmap(14, 15, lowcolor_e6e6e6_bits, true); + btnSource = new QImage(btnhighcolor_xpm); + + if(QPixmap::defaultDepth() > 8){ + aUpperGradient = new KPixmap; + aUpperGradient->resize(32, title_height+2); + iUpperGradient = new KPixmap; + iUpperGradient->resize(32, title_height+2); + KPixmapEffect::gradient(*aUpperGradient, + options()->color(KDecoration::ColorTitleBar, true).light(130), + options()->color(KDecoration::ColorTitleBlend, true), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iUpperGradient, + options()->color(KDecoration::ColorTitleBar, false).light(130), + options()->color(KDecoration::ColorTitleBlend, false), + KPixmapEffect::VerticalGradient); + } + // buttons + QColorGroup btnColor(options()->colorGroup(KDecoration::ColorButtonBg, true)); + buttonPix = new QPixmap(14, 15); + make_button_fx(btnColor, buttonPix); + buttonPixDown = new QPixmap(14, 15); + make_button_fx(btnColor, buttonPixDown, true); + + btnColor = options()->colorGroup(KDecoration::ColorButtonBg, false); + iButtonPix = new QPixmap(14, 15); + make_button_fx(btnColor, iButtonPix); + iButtonPixDown = new QPixmap(14, 15); + make_button_fx(btnColor, iButtonPixDown, true); + + + if(qGray(btnColor.background().rgb()) < 150) + buttonFg = new QColor(Qt::white); + else + buttonFg = new QColor(Qt::black); + + delete lcDark1; + delete lcDark2; + delete lcDark3; + delete lcLight1; + delete btnSource; +} + +static void delete_pixmaps() +{ + if(aUpperGradient){ + delete aUpperGradient; + delete iUpperGradient; + } + delete buttonPix; + delete buttonPixDown; + delete iButtonPix; + delete iButtonPixDown; + + delete buttonFg; + + pixmaps_created = false; +} + +void ModernSysFactory::read_config() +{ + bool showh; + int hsize, hwidth, bwidth, theight; + + KConfig c("kwinmodernsysrc"); + c.setGroup("General"); + showh = c.readBoolEntry("ShowHandle", true); + + hwidth = c.readUnsignedNumEntry("HandleWidth", 6); + hsize = c.readUnsignedNumEntry("HandleSize", 30); + if (!(showh && hsize && hwidth)) { + showh = false; + hwidth = hsize = 0; + } + + switch(options()->preferredBorderSize( this )) { + case BorderLarge: + bwidth = 8; + hwidth = hwidth * 7/5; + hsize = hsize * 7/5; + break; + case BorderVeryLarge: + bwidth = 12; + hwidth = hwidth * 17/10 + 2; + hsize = hsize * 17/10; + break; + case BorderHuge: + bwidth = 18; + hwidth = hwidth * 2 + 6; + hsize = hsize * 2; + break; + /* + // If we allow these large sizes we need to change the + // correlation between the border width and the handle size. + case BorderVeryHuge: + bwidth = 27; + hwidth = hwidth * 5/2 + 15; + hsize = hsize * 5/2; + break; + case BorderOversized: + bwidth = 40; + hwidth = hwidth * 3 + 22; + hsize = hsize * 3; + break; + */ + case BorderNormal: + default: + bwidth = 4; + } + + theight = QFontMetrics(options()->font(true)).height() + 2; + if (theight < 16) + theight = 16; + if (theight < bwidth) + theight = bwidth; + + show_handle = showh; + handle_width = hwidth; + handle_size = hsize; + border_width = bwidth; + title_height = theight; +} + +QValueList< ModernSysFactory::BorderSize > ModernSysFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge; + // as long as the buttons don't scale don't offer the largest two sizes. + // BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +ModernButton::ModernButton(ButtonType type, ModernSys *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode( NoBackground ); + + QBitmap mask(14, 15, QPixmap::defaultDepth() > 8 ? + btnhighcolor_mask_bits : lowcolor_mask_bits, true); + resize(14, 15); + + setMask(mask); +} + +void ModernButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? (isLeft()?l_minmax_bits:r_minmax_bits) : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + case MenuButton: + setBitmap(menu_bits); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void ModernButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + deco = QBitmap(8, 8, bitmap, true); + else { + deco = QBitmap(8,8); + deco.fill(Qt::color0); + } + deco.setMask(deco); +} + +void ModernButton::drawButton(QPainter *p) +{ + if(decoration()->isActive()){ + if(buttonPix) + p->drawPixmap(0, 0, isDown() ? *buttonPixDown : *buttonPix); + } + else{ + if(iButtonPix) + p->drawPixmap(0, 0, isDown() ? *iButtonPixDown : *iButtonPix); + } + if(!deco.isNull()){ + p->setPen(*buttonFg); + p->drawPixmap(isDown() ? 4 : 3, isDown() ? 5 : 4, deco); + } +} + +void ModernSys::reset( unsigned long changed) +{ + KCommonDecoration::reset(changed); + + titleBuffer.resize(0, 0); + recalcTitleBuffer(); + resetButtons(); + widget()->update(); +} + +ModernSys::ModernSys( KDecorationBridge* b, KDecorationFactory* f ) + : KCommonDecoration( b, f ) +{ +} + +QString ModernSys::visibleName() const +{ + return i18n("Modern System"); +} + +QString ModernSys::defaultButtonsLeft() const +{ + return "X"; +} + +QString ModernSys::defaultButtonsRight() const +{ + return "HSIA"; +} + +bool ModernSys::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int ModernSys::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + // bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + return border_width + (reverse ? handle_width : 0); + + case LM_BorderRight: + return border_width + (reverse ? 0 : handle_width); + + case LM_BorderBottom: + return border_width + handle_width; + + case LM_TitleEdgeLeft: + return layoutMetric(LM_BorderLeft,respectWindowState)+3; + case LM_TitleEdgeRight: + return layoutMetric(LM_BorderRight,respectWindowState)+3; + + case LM_TitleEdgeTop: + return 2; + + case LM_TitleEdgeBottom: + return 2; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 4; + + case LM_TitleHeight: + return title_height; + + case LM_ButtonWidth: + return 14; + case LM_ButtonHeight: + return 15; + + case LM_ButtonSpacing: + return 1; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *ModernSys::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new ModernButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new ModernButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new ModernButton(HelpButton, this, "help"); + + case MinButton: + return new ModernButton(MinButton, this, "minimize"); + + case MaxButton: + return new ModernButton(MaxButton, this, "maximize"); + + case CloseButton: + return new ModernButton(CloseButton, this, "close"); + + case AboveButton: + return new ModernButton(AboveButton, this, "above"); + + case BelowButton: + return new ModernButton(BelowButton, this, "below"); + + case ShadeButton: + return new ModernButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void ModernSys::init() +{ + reverse = QApplication::reverseLayout(); + + KCommonDecoration::init(); + + recalcTitleBuffer(); +} + +void ModernSys::recalcTitleBuffer() +{ + if(oldTitle == caption() && width() == titleBuffer.width()) + return; + + QFontMetrics fm(options()->font(true)); + titleBuffer.resize(width(), title_height+2); + QPainter p; + p.begin(&titleBuffer); + if(aUpperGradient) + p.drawTiledPixmap(0, 0, width(), title_height+2, *aUpperGradient); + else + p.fillRect(0, 0, width(), title_height+2, + options()->colorGroup(ColorTitleBar, true). + brush(QColorGroup::Button)); + + QRect t = titleRect(); // titlebar->geometry(); + t.setTop( 2 ); + t.setLeft( t.left() ); + t.setRight( t.right() - 2 ); + + QRegion r(t.x(), 0, t.width(), title_height+2); + r -= QRect(t.x()+((t.width()-fm.width(caption()))/2)-4, + 0, fm.width(caption())+8, title_height+2); + p.setClipRegion(r); + int i, ly; + ly = (title_height % 3 == 0) ? 3 : 4; + for(i=0; i < (title_height-2)/3; ++i, ly+=3){ + p.setPen(options()->color(ColorTitleBar, true).light(150)); + p.drawLine(0, ly, width()-1, ly); + p.setPen(options()->color(ColorTitleBar, true).dark(120)); + p.drawLine(0, ly+1, width()-1, ly+1); + } + p.setClipRect(t); + p.setPen(options()->color(ColorFont, true)); + p.setFont(options()->font(true)); + + p.drawText(t.x()+((t.width()-fm.width(caption()))/2)-4, + 0, fm.width(caption())+8, title_height+2, AlignCenter, caption()); + p.setClipping(false); + p.end(); + oldTitle = caption(); +} + +void ModernSys::updateCaption() +{ + widget()->update(titleRect() ); +} + +void ModernSys::drawRoundFrame(QPainter &p, int x, int y, int w, int h) +{ + kDrawRoundButton(&p, x, y, w, h, + options()->colorGroup(ColorFrame, isActive()), false); + +} + +void ModernSys::paintEvent( QPaintEvent* ) +{ + // update title buffer... + if (oldTitle != caption() || width() != titleBuffer.width() ) + recalcTitleBuffer(); + + int hs = handle_size; + int hw = handle_width; + + QPainter p( widget() ); + QRect t = titleRect(); // titlebar->geometry(); + + QBrush fillBrush(widget()->colorGroup().brush(QColorGroup::Background).pixmap() ? + widget()->colorGroup().brush(QColorGroup::Background) : + options()->colorGroup(ColorFrame, isActive()). + brush(QColorGroup::Button)); + + p.fillRect(1, title_height+3, width()-2, height()-(title_height+3), fillBrush); + p.fillRect(width()-6, 0, width()-1, height(), fillBrush); + + t.setTop( 2 ); + t.setLeft( t.left() ); + t.setRight( t.right() - 2 ); + + int w = width() - hw; // exclude handle + int h = height() - hw; + + // titlebar + QColorGroup g = options()->colorGroup(ColorTitleBar, isActive()); + if(isActive()){ + p.drawPixmap(1, 1, titleBuffer, 0, 0, w-2, title_height+2); + } + else{ + if(iUpperGradient) + p.drawTiledPixmap(1, 1, w-2, title_height+2, *iUpperGradient); + else + p.fillRect(1, 1, w-2, title_height+2, fillBrush); + p.setPen(options()->color(ColorFont, isActive())); + p.setFont(options()->font(isActive())); + p.drawText(t, AlignCenter, caption() ); + } + + // titlebar highlight + p.setPen(g.light()); + p.drawLine(1, 1, 1, title_height+3); + p.drawLine(1, 1, w-3, 1); + p.setPen(g.dark()); + p.drawLine(w-2, 1, w-2, title_height+3); + p.drawLine(0, title_height+2, w-2, title_height+2); + + // frame + g = options()->colorGroup(ColorFrame, isActive()); + p.setPen(g.light()); + p.drawLine(1, title_height+3, 1, h-2); + p.setPen(g.dark()); + p.drawLine(2, h-2, w-2, h-2); + p.drawLine(w-2, title_height+3, w-2, h-2); + //p.drawPoint(w-3, title_height+3); + //p.drawPoint(2, title_height+3); + + qDrawShadePanel(&p, border_width-1, title_height+3, w-2*border_width+2, h-title_height-border_width-2, g, true); + + if (show_handle) { + p.setPen(g.dark()); + p.drawLine(width()-3, height()-hs-1, width()-3, height()-3); + p.drawLine(width()-hs-1, height()-3, width()-3, height()-3); + + p.setPen(g.light()); + p.drawLine(width()-hw, height()-hs-1, width()-hw, height()-hw); + p.drawLine(width()-hs-1, height()-hw, width()-hw, height()-hw); + p.drawLine(width()-hw, height()-hs-1, width()-4, height()-hs-1); + p.drawLine(width()-hs-1, height()-hw, width()-hs-1, height()-4); + + p.setPen(Qt::black); + p.drawRect(0, 0, w, h); + + // handle outline + p.drawLine(width()-hw, height()-hs, width(), height()-hs); + p.drawLine(width()-2, height()-hs, width()-2, height()-2); + p.drawLine(width()-hs, height()-2, width()-2, height()-2); + p.drawLine(width()-hs, height()-hw, width()-hs, height()-2); + } else { + p.setPen(Qt::black); + p.drawRect(0, 0, w, h); + } +} + +void ModernSys::updateWindowShape() +{ + int hs = handle_size; + int hw = handle_width; + QRegion mask; + mask += QRect(0, 0, width()-hw, height()-hw); + //single points + mask -= QRect(0, 0, 1, 1); + mask -= QRect(width()-hw-1, 0, 1, 1); + mask -= QRect(0, height()-hw-1, 1, 1); + + if (show_handle) { + mask += QRect(width()-hs, height()-hs, hs-1, hs-1); + mask -= QRect(width()-2, height()-2, 1, 1); + mask -= QRect(width()-2, height()-hs, 1, 1); + mask -= QRect(width()-hs, height()-2, 1, 1); + } else + mask -= QRect(width()-1, height()-1, 1, 1); + + setMask(mask); +} + +ModernSysFactory::ModernSysFactory() +{ + read_config(); + create_pixmaps(); +} + +ModernSysFactory::~ModernSysFactory() +{ + ModernSystem::delete_pixmaps(); +} + +KDecoration* ModernSysFactory::createDecoration( KDecorationBridge* b ) +{ + return(new ModernSys(b, this)); +} + +bool ModernSysFactory::reset( unsigned long changed ) +{ + read_config(); + + bool needHardReset = true; + if( changed & (SettingColors | SettingBorder | SettingFont) ) + { + delete_pixmaps(); + create_pixmaps(); + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if( needHardReset ) + return true; + else + { + resetDecorations( changed ); + return false; // no recreating of decorations + } +} + +bool ModernSysFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonMenu: + return true; + default: + return false; + }; +} + +} + +// KWin extended plugin interface +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new ModernSystem::ModernSysFactory(); +} + +// vim:ts=4:sw=4 diff --git a/kwin/clients/modernsystem/modernsys.h b/kwin/clients/modernsystem/modernsys.h new file mode 100644 index 000000000..d185be048 --- /dev/null +++ b/kwin/clients/modernsystem/modernsys.h @@ -0,0 +1,72 @@ +#ifndef __MODSYSTEMCLIENT_H +#define __MODSYSTEMCLIENT_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +class QLabel; +class QSpacerItem; + +namespace ModernSystem { + +class ModernSys; + +class ModernButton : public KCommonDecorationButton +{ +public: + ModernButton(ButtonType type, ModernSys *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + virtual void reset(unsigned long changed); +protected: + + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + QBitmap deco; +}; + +class ModernSys : public KCommonDecoration +{ +public: + ModernSys( KDecorationBridge* b, KDecorationFactory* f ); + ~ModernSys(){;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void updateWindowShape(); + virtual void updateCaption(); + + void init(); +protected: + void drawRoundFrame(QPainter &p, int x, int y, int w, int h); + void paintEvent( QPaintEvent* ); + void recalcTitleBuffer(); + void reset( unsigned long ); +private: + QPixmap titleBuffer; + QString oldTitle; + bool reverse; +}; + +class ModernSysFactory : public QObject, public KDecorationFactory +{ +public: + ModernSysFactory(); + virtual ~ModernSysFactory(); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + QValueList< BorderSize > borderSizes() const; +private: + void read_config(); +}; + +} + +#endif diff --git a/kwin/clients/modernsystem/modernsystem.desktop b/kwin/clients/modernsystem/modernsystem.desktop new file mode 100644 index 000000000..d6247a390 --- /dev/null +++ b/kwin/clients/modernsystem/modernsystem.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=Modern System +Name[af]=Moderne Stelsel +Name[ar]=نظام معاصر +Name[az]=Modern Sistem +Name[be]=СучаÑÐ½Ð°Ñ ÑÑ–ÑÑ‚Ñма +Name[bn]=মডারà§à¦¨ সিসà§à¦Ÿà§‡à¦® +Name[br]=Reizhiad Nevez +Name[ca]=Sistema modern +Name[cs]=Modernà systém +Name[csb]=Mòdernô systema +Name[cy]=Cysawd Cyfoes +Name[da]=Moderne system +Name[el]=ΜοντÎÏνο σÏστημα +Name[eo]=Moderna Sistemo +Name[es]=Sistema moderno +Name[eu]=Sistema modernoa +Name[fa]=سیستم نوین +Name[fi]=Moderni järjestelmä +Name[fr]=Système Moderne +Name[fy]=Modern systeem +Name[ga]=Córas Nua-Aimseartha +Name[gl]=Sistema Moderno +Name[hi]=आधà¥à¤¨à¤¿à¤• तंतà¥à¤° +Name[hr]=Suvremeni sustav +Name[is]=NútÃmaleg vél +Name[it]=Sistema Moderno +Name[ja]=モダンシステム+Name[ka]=თáƒáƒœáƒáƒ›áƒ”დრáƒáƒ•áƒ” სისტემრ+Name[kk]=Заманауи жүйе +Name[km]=ប្រពáŸáž“្ធ​ទំនើប +Name[ko]=ëª¨ë˜ ì‹œìŠ¤í…œ +Name[lt]=Moderni sistema +Name[lv]=Moderna sistÄ“ma +Name[mk]=Модерен ÑиÑтем +Name[ms]=Sistem Moden +Name[mt]=Sistema Moderna +Name[nb]=Moderne System +Name[nds]=Modeern Systeem +Name[ne]=आधà¥à¤¨à¤¿à¤• पà¥à¤°à¤£à¤¾à¤²à¥€ +Name[nl]=Modern systeem +Name[nn]=Moderne System +Name[pa]=ਨਵਾਂ ਸਿਸਟਮ +Name[pl]=Nowoczesny system +Name[pt]=Sistema Moderno +Name[pt_BR]=Sistema Moderno +Name[ro]=Sistem moderm +Name[ru]=Ð¡Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ ÑиÑтема +Name[rw]=Sisitemu Igezweho +Name[se]=Ãigeguovdilis vuogádat +Name[sk]=Moderný systém +Name[sl]=Moderni sistem +Name[sr]=Модерни ÑиÑтем +Name[sr@Latn]=Moderni sistem +Name[sv]=Modernt system +Name[ta]=நவீன அமைபà¯à®ªà¯ +Name[te]=ఆధà±à°¨à°¿à°• à°µà±à°¯à°µà°¸à±à°¥ +Name[tg]=СиÑтемаи навтарин +Name[th]=à¹à¸šà¸š Moden System +Name[tr]=Modern Sistem +Name[tt]=Zamança Sistem +Name[uk]=СучаÑна ÑиÑтема +Name[uz]=Zamonaviy tizim +Name[uz@cyrillic]=Замонавий тизим +Name[vi]=Hệ thống Hiện đại +Name[wa]=Sistinme modiene +Name[zh_CN]=现代系统 +Name[zh_TW]=ç¾ä»£ç³»çµ± +X-KDE-Library=kwin3_modernsys diff --git a/kwin/clients/plastik/Makefile.am b/kwin/clients/plastik/Makefile.am new file mode 100644 index 000000000..78e71f763 --- /dev/null +++ b/kwin/clients/plastik/Makefile.am @@ -0,0 +1,19 @@ +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = config + +KDE_CXXFLAGS = -DQT_PLUGIN + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kwindir = $(kde_datadir)/kwin/ +kwin_DATA = plastik.desktop + +kde_module_LTLIBRARIES = kwin3_plastik.la +kwin3_plastik_la_SOURCES = plastik.cpp plastikclient.cpp plastikbutton.cpp misc.cpp +kwin3_plastik_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_plastik_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +kwin3_plastik_la_METASOURCES = AUTO + +DISTCLEANFILES = $(kwin3_plastik_la_METASOURCES) + diff --git a/kwin/clients/plastik/config/Makefile.am b/kwin/clients/plastik/config/Makefile.am new file mode 100644 index 000000000..78a4b0502 --- /dev/null +++ b/kwin/clients/plastik/config/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_plastik_config.la + +kwin_plastik_config_la_SOURCES = config.cpp configdialog.ui +kwin_plastik_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_plastik_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h +DISTCLEANFILES = $(METASOURCES) + +lnkdir = $(kde_datadir)/kwin + diff --git a/kwin/clients/plastik/config/config.cpp b/kwin/clients/plastik/config/config.cpp new file mode 100644 index 000000000..91ec501d0 --- /dev/null +++ b/kwin/clients/plastik/config/config.cpp @@ -0,0 +1,123 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qslider.h> +#include <qspinbox.h> +#include <qwhatsthis.h> + +#include <kconfig.h> +#include <klocale.h> +#include <kglobal.h> + +#include "config.h" +#include "configdialog.h" + +PlastikConfig::PlastikConfig(KConfig* config, QWidget* parent) + : QObject(parent), m_config(0), m_dialog(0) +{ + // create the configuration object + m_config = new KConfig("kwinplastikrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + + // create and show the configuration dialog + m_dialog = new ConfigDialog(parent); + m_dialog->show(); + + // load the configuration + load(config); + + // setup the connections + connect(m_dialog->titleAlign, SIGNAL(clicked(int)), + this, SIGNAL(changed())); + connect(m_dialog->animateButtons, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->menuClose, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->titleShadow, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->coloredBorder, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); +} + +PlastikConfig::~PlastikConfig() +{ + if (m_dialog) delete m_dialog; + if (m_config) delete m_config; +} + +void PlastikConfig::load(KConfig*) +{ + m_config->setGroup("General"); + + + QString value = m_config->readEntry("TitleAlignment", "AlignLeft"); + QRadioButton *button = (QRadioButton*)m_dialog->titleAlign->child(value.latin1()); + if (button) button->setChecked(true); + bool animateButtons = m_config->readBoolEntry("AnimateButtons", true); + m_dialog->animateButtons->setChecked(animateButtons); + bool menuClose = m_config->readBoolEntry("CloseOnMenuDoubleClick", true); + m_dialog->menuClose->setChecked(menuClose); + bool titleShadow = m_config->readBoolEntry("TitleShadow", true); + m_dialog->titleShadow->setChecked(titleShadow); + bool coloredBorder = m_config->readBoolEntry("ColoredBorder", true); + m_dialog->coloredBorder->setChecked(coloredBorder); +} + +void PlastikConfig::save(KConfig*) +{ + m_config->setGroup("General"); + + QRadioButton *button = (QRadioButton*)m_dialog->titleAlign->selected(); + if (button) m_config->writeEntry("TitleAlignment", QString(button->name())); + m_config->writeEntry("AnimateButtons", m_dialog->animateButtons->isChecked() ); + m_config->writeEntry("CloseOnMenuDoubleClick", m_dialog->menuClose->isChecked() ); + m_config->writeEntry("TitleShadow", m_dialog->titleShadow->isChecked() ); + m_config->writeEntry("ColoredBorder", m_dialog->coloredBorder->isChecked() ); + m_config->sync(); +} + +void PlastikConfig::defaults() +{ + QRadioButton *button = + (QRadioButton*)m_dialog->titleAlign->child("AlignLeft"); + if (button) button->setChecked(true); + m_dialog->animateButtons->setChecked(true); + m_dialog->menuClose->setChecked(false); + m_dialog->titleShadow->setChecked(true); + m_dialog->coloredBorder->setChecked(true); +} + +////////////////////////////////////////////////////////////////////////////// +// Plugin Stuff // +////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + KDE_EXPORT QObject* allocate_config(KConfig* config, QWidget* parent) { + return (new PlastikConfig(config, parent)); + } +} + +#include "config.moc" diff --git a/kwin/clients/plastik/config/config.h b/kwin/clients/plastik/config/config.h new file mode 100644 index 000000000..540a27cda --- /dev/null +++ b/kwin/clients/plastik/config/config.h @@ -0,0 +1,53 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef KNIFTYCONFIG_H +#define KNIFTYCONFIG_H + +#include <qobject.h> + +class QButtonGroup; +class QGroupBox; +class KConfig; +class ConfigDialog; + +class PlastikConfig : public QObject +{ + Q_OBJECT +public: + PlastikConfig(KConfig* config, QWidget* parent); + ~PlastikConfig(); + +signals: + void changed(); + +public slots: + void load(KConfig *config); + void save(KConfig *config); + void defaults(); + +private: + KConfig *m_config; + ConfigDialog *m_dialog; +}; + +#endif // KNIFTYCONFIG_H diff --git a/kwin/clients/plastik/config/configdialog.ui b/kwin/clients/plastik/config/configdialog.ui new file mode 100644 index 000000000..642891b31 --- /dev/null +++ b/kwin/clients/plastik/config/configdialog.ui @@ -0,0 +1,119 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ConfigDialog</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>541</width> + <height>170</height> + </rect> + </property> + <property name="caption"> + <string>Config Dialog</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>titleAlign</cstring> + </property> + <property name="title"> + <string>Title &Alignment</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignLeft</cstring> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignHCenter</cstring> + </property> + <property name="text"> + <string>Center</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignRight</cstring> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>coloredBorder</cstring> + </property> + <property name="text"> + <string>Colored window border</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if the window border should be painted in the titlebar color. Otherwise it will be painted in the background color.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>titleShadow</cstring> + </property> + <property name="text"> + <string>Use shadowed &text</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the titlebar text to have a 3D look with a shadow behind it.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>animateButtons</cstring> + </property> + <property name="text"> + <string>Animate buttons</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the buttons to fade in when the mouse pointer hovers over them and fade out again when it moves away.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>menuClose</cstring> + </property> + <property name="text"> + <string>Close windows by double clicking the menu button</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want windows to be closed when you double click the menu button, similar to Microsoft Windows.</string> + </property> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>AlignLeft</tabstop> + <tabstop>AlignHCenter</tabstop> + <tabstop>AlignRight</tabstop> + <tabstop>animateButtons</tabstop> + <tabstop>titleShadow</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kwin/clients/plastik/misc.cpp b/kwin/clients/plastik/misc.cpp new file mode 100644 index 000000000..da491b2ba --- /dev/null +++ b/kwin/clients/plastik/misc.cpp @@ -0,0 +1,85 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <kpixmap.h> +#include <kpixmapeffect.h> + +#include <qcolor.h> +#include <qimage.h> +#include <qpainter.h> + +#include "misc.h" + +QColor hsvRelative(const QColor& baseColor, int relativeH, int relativeS, int relativeV) +{ + int h, s, v; + baseColor.hsv(&h, &s, &v); + + h += relativeH; + s += relativeS; + v += relativeV; + + if(h < 0) { h = 0; } + else if(h > 359) { h = 359; } + if(s < 0) { s = 0; } + else if(s > 255) { s = 255; } + if(v < 0) { v = 0; } + else if(v > 255) { v = 255; } + + QColor c; + c.setHsv( h, s, v ); + return c; +} + +QColor alphaBlendColors(const QColor &bgColor, const QColor &fgColor, const int a) +{ + + // normal button... + QRgb rgb = bgColor.rgb(); + QRgb rgb_b = fgColor.rgb(); + int alpha = a; + if(alpha>255) alpha = 255; + if(alpha<0) alpha = 0; + int inv_alpha = 255 - alpha; + + QColor result = QColor( qRgb(qRed(rgb_b)*inv_alpha/255 + qRed(rgb)*alpha/255, + qGreen(rgb_b)*inv_alpha/255 + qGreen(rgb)*alpha/255, + qBlue(rgb_b)*inv_alpha/255 + qBlue(rgb)*alpha/255) ); + + return result; +} + +QImage recolorImage(QImage *img, QColor color) { + QImage destImg(img->width(),img->height(),32); + destImg.setAlphaBuffer(true); + for (int x = 0; x < img->width(); x++) { + for (int y = 0; y < img->height(); y++) { + if(img->pixel(x,y) == qRgb(0,0,255) ) { + destImg.setPixel(x,y,color.rgb() ); // set to the new color + } else { + destImg.setPixel(x,y,qRgba(0,0,0,0) ); // set transparent... + } + } + } + + return destImg; +} diff --git a/kwin/clients/plastik/misc.h b/kwin/clients/plastik/misc.h new file mode 100644 index 000000000..6c06b282b --- /dev/null +++ b/kwin/clients/plastik/misc.h @@ -0,0 +1,30 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef MISC_H +#define MISC_H + +QColor hsvRelative(const QColor& baseColor, int relativeH, int relativeS = 0, int relativeV = 0); +QColor alphaBlendColors(const QColor &backgroundColor, const QColor &foregroundColor, const int alpha); +QImage recolorImage(QImage *img, QColor color); + +#endif // MISC_H diff --git a/kwin/clients/plastik/plastik.cpp b/kwin/clients/plastik/plastik.cpp new file mode 100644 index 000000000..25e6d2563 --- /dev/null +++ b/kwin/clients/plastik/plastik.cpp @@ -0,0 +1,598 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <qbitmap.h> +#include <qpainter.h> +#include <qimage.h> + +#include <kconfig.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> + +#include "misc.h" +#include "plastik.h" +#include "plastik.moc" +#include "plastikclient.h" +#include "plastikbutton.h" + +namespace KWinPlastik +{ + +PlastikHandler::PlastikHandler() +{ + memset(m_pixmaps, 0, sizeof(QPixmap*)*NumPixmaps*2*2); // set elements to 0 + memset(m_bitmaps, 0, sizeof(QBitmap*)*NumButtonIcons*2); + + reset(0); +} + +PlastikHandler::~PlastikHandler() +{ + for (int t=0; t < 2; ++t) + for (int a=0; a < 2; ++a) + for (int i=0; i < NumPixmaps; ++i) + delete m_pixmaps[t][a][i]; + for (int t=0; t < 2; ++t) + for (int i=0; i < NumButtonIcons; ++i) + delete m_bitmaps[t][i]; +} + +bool PlastikHandler::reset(unsigned long changed) +{ + // we assume the active font to be the same as the inactive font since the control + // center doesn't offer different settings anyways. + m_titleFont = KDecoration::options()->font(true, false); // not small + m_titleFontTool = KDecoration::options()->font(true, true); // small + + switch(KDecoration::options()->preferredBorderSize( this )) { + case BorderTiny: + m_borderSize = 3; + break; + case BorderLarge: + m_borderSize = 8; + break; + case BorderVeryLarge: + m_borderSize = 12; + break; + case BorderHuge: + m_borderSize = 18; + break; + case BorderVeryHuge: + m_borderSize = 27; + break; + case BorderOversized: + m_borderSize = 40; + break; + case BorderNormal: + default: + m_borderSize = 4; + } + + // check if we are in reverse layout mode + m_reverse = QApplication::reverseLayout(); + + // read in the configuration + readConfig(); + + // pixmaps probably need to be updated, so delete the cache. + for (int t=0; t < 2; ++t) { + for (int a=0; a < 2; ++a) { + for (int i=0; i < NumPixmaps; i++) { + if (m_pixmaps[t][a][i]) { + delete m_pixmaps[t][a][i]; + m_pixmaps[t][a][i] = 0; + } + } + } + } + for (int t=0; t < 2; ++t) { + for (int i=0; i < NumButtonIcons; i++) { + if (m_bitmaps[t][i]) { + delete m_bitmaps[t][i]; + m_bitmaps[t][i] = 0; + } + } + } + + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + // TODO: besides the Color and Font settings I can maybe handle more changes + // without a hard reset. I will do this later... + if (changed & SettingColors || changed & SettingFont) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +KDecoration* PlastikHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new PlastikClient( bridge, this ); +} + +bool PlastikHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +void PlastikHandler::readConfig() +{ + // create a config object + KConfig config("kwinplastikrc"); + config.setGroup("General"); + + // grab settings + m_titleShadow = config.readBoolEntry("TitleShadow", true); + + QFontMetrics fm(m_titleFont); // active font = inactive font + int titleHeightMin = config.readNumEntry("MinTitleHeight", 16); + // The title should strech with bigger font sizes! + m_titleHeight = QMAX(titleHeightMin, fm.height() + 4); // 4 px for the shadow etc. + // have an even title/button size so the button icons are fully centered... + if ( m_titleHeight%2 == 0) + m_titleHeight++; + + fm = QFontMetrics(m_titleFontTool); // active font = inactive font + int titleHeightToolMin = config.readNumEntry("MinTitleHeightTool", 13); + // The title should strech with bigger font sizes! + m_titleHeightTool = QMAX(titleHeightToolMin, fm.height() ); // don't care about the shadow etc. + // have an even title/button size so the button icons are fully centered... + if ( m_titleHeightTool%2 == 0) + m_titleHeightTool++; + + QString value = config.readEntry("TitleAlignment", "AlignLeft"); + if (value == "AlignLeft") m_titleAlign = Qt::AlignLeft; + else if (value == "AlignHCenter") m_titleAlign = Qt::AlignHCenter; + else if (value == "AlignRight") m_titleAlign = Qt::AlignRight; + + m_coloredBorder = config.readBoolEntry("ColoredBorder", true); + m_animateButtons = config.readBoolEntry("AnimateButtons", true); + m_menuClose = config.readBoolEntry("CloseOnMenuDoubleClick", true); +} + +QColor PlastikHandler::getColor(KWinPlastik::ColorType type, const bool active) +{ + switch (type) { + case WindowContour: + return KDecoration::options()->color(ColorTitleBar, active).dark(200); + case TitleGradient1: + return hsvRelative(KDecoration::options()->color(ColorTitleBar, active), 0,-10,+10); + break; + case TitleGradient2: + return hsvRelative(KDecoration::options()->color(ColorTitleBar, active), 0,0,-25); + break; + case TitleGradient3: + return KDecoration::options()->color(ColorTitleBar, active); + break; + case ShadeTitleLight: + return alphaBlendColors(KDecoration::options()->color(ColorTitleBar, active), + Qt::white, active?205:215); + break; + case ShadeTitleDark: + return alphaBlendColors(KDecoration::options()->color(ColorTitleBar, active), + Qt::black, active?205:215); + break; + case Border: + return KDecoration::options()->color(ColorFrame, active); + case TitleFont: + return KDecoration::options()->color(ColorFont, active); + default: + return Qt::black; + } +} + +void PlastikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir ) const +{ + QPixmap *newpix; + QPainter p; + + if ( dir == Qt::Horizontal ) + newpix = new QPixmap( size, pix->height() ); + else + newpix = new QPixmap( pix->width(), size ); + + p.begin( newpix ); + p.drawTiledPixmap( newpix->rect(), *pix ) ; + p.end(); + + delete pix; + pix = newpix; +} + +const QPixmap &PlastikHandler::pixmap(Pixmaps type, bool active, bool toolWindow) +{ + if (m_pixmaps[toolWindow][active][type]) + return *m_pixmaps[toolWindow][active][type]; + + QPixmap *pm = 0; + + switch (type) { + case TitleBarTileTop: + case TitleBarTile: + { + const int titleBarTileHeight = (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + // gradient used as well in TitleBarTileTop as TitleBarTile + const int gradientHeight = 2 + titleBarTileHeight-1; + QPixmap gradient(1, gradientHeight); + QPainter painter(&gradient); + KPixmap tempPixmap; + tempPixmap.resize(1, 4); + KPixmapEffect::gradient(tempPixmap, + getColor(TitleGradient1, active), + getColor(TitleGradient2, active), + KPixmapEffect::VerticalGradient); + painter.drawPixmap(0,0, tempPixmap); + tempPixmap.resize(1, gradientHeight-4); + KPixmapEffect::gradient(tempPixmap, + getColor(TitleGradient2, active), + getColor(TitleGradient3, active), + KPixmapEffect::VerticalGradient); + painter.drawPixmap(0,4, tempPixmap); + painter.end(); + + // actual titlebar tiles + if (type == TitleBarTileTop) { + pm = new QPixmap(1, 4); + painter.begin(pm); + // contour + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0,0); + // top highlight + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawPoint(0,1); + // gradient + painter.drawPixmap(0, 2, gradient); + painter.end(); + } else { + pm = new QPixmap(1, titleBarTileHeight); + painter.begin(pm); + painter.drawPixmap(0, 0, gradient, 0,2); + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + } else { + painter.setPen(getColor(TitleGradient3, active) ); + } + painter.drawPoint(0,titleBarTileHeight-1); + painter.end(); + } + + pretile(pm, 64, Qt::Horizontal); + + break; + } + + case TitleBarLeft: + { + const int w = m_borderSize; + const int h = 4 + (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + + pm = new QPixmap(w, h); + QPainter painter(pm); + + painter.drawTiledPixmap(0,0, w, 4, pixmap(TitleBarTileTop, active, toolWindow) ); + painter.drawTiledPixmap(0,4, w, h-4, pixmap(TitleBarTile, active, toolWindow) ); + + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(0,0, 0,h); + painter.drawPoint(1,1); + + const QColor highlightTitleLeft = getColor(ShadeTitleLight, active); + painter.setPen(highlightTitleLeft); + painter.drawLine(1,2, 1,h); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,h-1, w-1,h-1); + } + + // outside the region normally masked by doShape + painter.setPen(QColor(0,0,0) ); + painter.drawLine(0, 0, 1, 0 ); + painter.drawPoint(0, 1); + + break; + } + + case TitleBarRight: + { + const int w = m_borderSize; + const int h = 4 + (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + + pm = new QPixmap(w, h); + QPainter painter(pm); + + painter.drawTiledPixmap(0,0, w, 4, pixmap(TitleBarTileTop, active, toolWindow) ); + painter.drawTiledPixmap(0,4, w, h-4, pixmap(TitleBarTile, active, toolWindow) ); + + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(w-1,0, w-1,h); + painter.drawPoint(w-2,1); + + const QColor highlightTitleRight = getColor(ShadeTitleDark, active); + painter.setPen(highlightTitleRight); + painter.drawLine(w-2,2, w-2,h); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,h-1, w-3,h-1); + } + + // outside the region normally masked by doShape + painter.setPen(QColor(0,0,0) ); + painter.drawLine(w-2, 0, w-1, 0 ); + painter.drawPoint(w-1, 1); + + break; + } + + case BorderLeftTile: + { + const int w = m_borderSize; + + pm = new QPixmap(w, 1); + QPainter painter(pm); + if (m_coloredBorder) { + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, 0); + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawPoint(1, 0); + if (w > 3) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,0, w-2,0); + } + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(w-1,0); + } else { + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, 0); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleLight, active), 130) ); + painter.drawPoint(1, 0); + painter.setPen(getColor(Border, active) ); + painter.drawLine(2,0, w-1,0); + } + + painter.end(); + + pretile(pm, 64, Qt::Vertical); + + break; + } + + case BorderRightTile: + { + const int w = m_borderSize; + + pm = new QPixmap(w, 1); + QPainter painter(pm); + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(0,0); + if (w > 3) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(1,0, w-3,0); + } + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawPoint(w-2, 0); + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(w-1, 0); + } else { + painter.setPen(getColor(Border, active) ); + painter.drawLine(0,0, w-3,0); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawPoint(w-2, 0); + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(w-1, 0); + } + painter.end(); + + pretile(pm, 64, Qt::Vertical); + + break; + } + + case BorderBottomLeft: + { + const int w = m_borderSize; + const int h = m_borderSize; + + pm = new QPixmap(w, h); + QPainter painter(pm); + painter.drawTiledPixmap(0,0,w,h, pixmap(BorderBottomTile, active, toolWindow) ); + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(0,0, 0,h); + if (m_coloredBorder) { + if (h > 3) { + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawLine(1,0, 1,h-2); + } + + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,0, w-1,0); + } else { + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleLight, active), 130) ); + painter.drawLine(1,0, 1,h-2); + } + + painter.end(); + + break; + } + + case BorderBottomRight: + { + const int w = m_borderSize; + const int h = m_borderSize; + + pm = new QPixmap(w, h); + QPainter painter(pm); + painter.drawTiledPixmap(0,0,w,h, pixmap(BorderBottomTile, active, toolWindow) ); + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(w-1,0, w-1,h); + if (m_coloredBorder) { + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawLine(w-2,0, w-2,h-2); + + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,0, w-3,0); + } else { + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawLine(w-2,0, w-2,h-2); + } + + painter.end(); + + break; + } + + case BorderBottomTile: + default: + { + const int h = m_borderSize; + + pm = new QPixmap(1, m_borderSize); + QPainter painter(pm); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(0,0); + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,1, 0,h-3); + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawPoint(0, h-2); + } else { + painter.setPen(getColor(Border, active) ); + painter.drawLine(0,0, 0,h-3); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawPoint(0, h-2); + } + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, h-1); + painter.end(); + + pretile(pm, 64, Qt::Horizontal); + + break; + } + } + + m_pixmaps[toolWindow][active][type] = pm; + return *pm; +} + +const QBitmap &PlastikHandler::buttonBitmap(ButtonIcon type, const QSize &size, bool toolWindow) +{ + int typeIndex = type; + + // btn icon size... + int reduceW = 0, reduceH = 0; + if(size.width()>14) { + reduceW = static_cast<int>(2*(size.width()/3.5) ); + } + else + reduceW = 6; + if(size.height()>14) + reduceH = static_cast<int>(2*(size.height()/3.5) ); + else + reduceH = 6; + + int w = size.width() - reduceW; + int h = size.height() - reduceH; + + if (m_bitmaps[toolWindow][typeIndex] && m_bitmaps[toolWindow][typeIndex]->size()==QSize(w,h) ) + return *m_bitmaps[toolWindow][typeIndex]; + + // no matching pixmap found, create a new one... + + delete m_bitmaps[toolWindow][typeIndex]; + m_bitmaps[toolWindow][typeIndex] = 0; + + QBitmap bmp = IconEngine::icon(type /*icon*/, QMIN(w,h) ); + QBitmap *bitmap = new QBitmap(bmp); + m_bitmaps[toolWindow][typeIndex] = bitmap; + return *bitmap; +} + +QValueList< PlastikHandler::BorderSize > +PlastikHandler::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderTiny << BorderNormal << + BorderLarge << BorderVeryLarge << BorderHuge << + BorderVeryHuge << BorderOversized; +} + +// make the handler accessible to other classes... +static PlastikHandler *handler = 0; +PlastikHandler* Handler() +{ + return handler; +} + +} // KWinPlastik + +////////////////////////////////////////////////////////////////////////////// +// Plugin Stuff // +////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + KWinPlastik::handler = new KWinPlastik::PlastikHandler(); + return KWinPlastik::handler; + } +} diff --git a/kwin/clients/plastik/plastik.desktop b/kwin/clients/plastik/plastik.desktop new file mode 100644 index 000000000..55800ad9b --- /dev/null +++ b/kwin/clients/plastik/plastik.desktop @@ -0,0 +1,37 @@ +[Desktop Entry] +Icon= +Name=Plastik +Name[af]=Plastiek +Name[ar]=بلاستيك +Name[be]=ПлаÑтык +Name[bn]=পà§à¦²à¦¾à¦¸à§à¦Ÿà¦¿à¦• +Name[eo]=Plastiko +Name[fa]=پلاستیک +Name[fy]=Plastyk +Name[hi]=पà¥à¤²à¤¾à¤¸à¥à¤Ÿà¤¿à¤• +Name[hr]=Plastika +Name[is]=Plast +Name[it]=Plastica +Name[ka]=ПлаÑтик +Name[kk]=ПлаÑтик +Name[km]=ប្ល៉ាស្ទិក +Name[lt]=Plastikinis +Name[lv]=Plastika +Name[mk]=ПлаÑтик +Name[nb]=Plast +Name[ne]=पà¥à¤²à¤¾à¤¸à¥à¤Ÿà¤¿à¤• +Name[nn]=Plast +Name[pa]=ਪਲਾਸਟਿਕ +Name[ro]=Plastic +Name[ru]=ПлаÑтик +Name[se]=Plastihkka +Name[sr]=ПлаÑтика +Name[sr@Latn]=Plastika +Name[ta]=திடà¯à®Ÿà®®à¯ +Name[te]=à°ªà±à°²à°¾à°¸à±à°Ÿà°¿à°•à± +Name[th]=พลาสติภ+Name[uk]=ПлаÑтик +Name[uz@cyrillic]=ПлаÑтик +Name[vi]=Chất dẻo +Name[zh_CN]=å¡‘æ–™ +X-KDE-Library=kwin3_plastik diff --git a/kwin/clients/plastik/plastik.h b/kwin/clients/plastik/plastik.h new file mode 100644 index 000000000..16972c9ac --- /dev/null +++ b/kwin/clients/plastik/plastik.h @@ -0,0 +1,127 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIK_H +#define PLASTIK_H + +#include <qfont.h> + +#include <kdecoration.h> +#include <kdecorationfactory.h> + +namespace KWinPlastik { + +enum ColorType { + WindowContour=0, + TitleGradient1, // top + TitleGradient2, + TitleGradient3, // bottom + ShadeTitleLight, + ShadeTitleDark, + Border, + TitleFont +}; + +enum Pixmaps { + TitleBarTileTop=0, + TitleBarTile, + TitleBarLeft, + TitleBarRight, + BorderLeftTile, + BorderRightTile, + BorderBottomTile, + BorderBottomLeft, + BorderBottomRight, + NumPixmaps +}; + +enum ButtonIcon { + CloseIcon = 0, + MaxIcon, + MaxRestoreIcon, + MinIcon, + HelpIcon, + OnAllDesktopsIcon, + NotOnAllDesktopsIcon, + KeepAboveIcon, + NoKeepAboveIcon, + KeepBelowIcon, + NoKeepBelowIcon, + ShadeIcon, + UnShadeIcon, + NumButtonIcons +}; + +class PlastikHandler: public QObject, public KDecorationFactory +{ + Q_OBJECT +public: + PlastikHandler(); + ~PlastikHandler(); + virtual bool reset( unsigned long changed ); + + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool supports( Ability ability ); + + const QPixmap &pixmap(Pixmaps type, bool active, bool toolWindow); + const QBitmap &buttonBitmap(ButtonIcon type, const QSize &size, bool toolWindow); + + int titleHeight() { return m_titleHeight; } + int titleHeightTool() { return m_titleHeightTool; } + const QFont &titleFont() { return m_titleFont; } + const QFont &titleFontTool() { return m_titleFontTool; } + bool titleShadow() { return m_titleShadow; } + int borderSize() { return m_borderSize; } + bool animateButtons() { return m_animateButtons; } + bool menuClose() { return m_menuClose; } + Qt::AlignmentFlags titleAlign() { return m_titleAlign; } + bool reverseLayout() { return m_reverse; } + QColor getColor(KWinPlastik::ColorType type, const bool active = true); + + QValueList< PlastikHandler::BorderSize > borderSizes() const; +private: + void readConfig(); + + void pretile(QPixmap *&pix, int size, Qt::Orientation dir) const; + + bool m_coloredBorder; + bool m_titleShadow; + bool m_animateButtons; + bool m_menuClose; + bool m_reverse; + int m_borderSize; + int m_titleHeight; + int m_titleHeightTool; + QFont m_titleFont; + QFont m_titleFontTool; + Qt::AlignmentFlags m_titleAlign; + + // pixmap cache + QPixmap *m_pixmaps[2][2][NumPixmaps]; // button pixmaps have normal+pressed state... + QBitmap *m_bitmaps[2][NumButtonIcons]; +}; + +PlastikHandler* Handler(); + +} // KWinPlastik + +#endif // PLASTIK_H diff --git a/kwin/clients/plastik/plastikbutton.cpp b/kwin/clients/plastik/plastikbutton.cpp new file mode 100644 index 000000000..5917465ab --- /dev/null +++ b/kwin/clients/plastik/plastikbutton.cpp @@ -0,0 +1,629 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +// #include <kwin/options.h> + +#include <qbitmap.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> +#include <qtimer.h> + +#include "plastikbutton.h" +#include "plastikbutton.moc" +#include "plastikclient.h" +#include "misc.h" + +namespace KWinPlastik +{ + +static const uint TIMERINTERVAL = 50; // msec +static const uint ANIMATIONSTEPS = 4; + +PlastikButton::PlastikButton(ButtonType type, PlastikClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name), + m_client(parent), + m_iconType(NumButtonIcons), + hover(false) +{ + setBackgroundMode(NoBackground); + + // no need to reset here as the button will be resetted on first resize. + + animTmr = new QTimer(this); + connect(animTmr, SIGNAL(timeout() ), this, SLOT(animate() ) ); + animProgress = 0; +} + +PlastikButton::~PlastikButton() +{ +} + +void PlastikButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + m_iconType = CloseIcon; + break; + case HelpButton: + m_iconType = HelpIcon; + break; + case MinButton: + m_iconType = MinIcon; + break; + case MaxButton: + if (isOn()) { + m_iconType = MaxRestoreIcon; + } else { + m_iconType = MaxIcon; + } + break; + case OnAllDesktopsButton: + if (isOn()) { + m_iconType = NotOnAllDesktopsIcon; + } else { + m_iconType = OnAllDesktopsIcon; + } + break; + case ShadeButton: + if (isOn()) { + m_iconType = UnShadeIcon; + } else { + m_iconType = ShadeIcon; + } + break; + case AboveButton: + if (isOn()) { + m_iconType = NoKeepAboveIcon; + } else { + m_iconType = KeepAboveIcon; + } + break; + case BelowButton: + if (isOn()) { + m_iconType = NoKeepBelowIcon; + } else { + m_iconType = KeepBelowIcon; + } + break; + default: + m_iconType = NumButtonIcons; // empty... + break; + } + + this->update(); + } +} + +void PlastikButton::animate() +{ + animTmr->stop(); + + if(hover) { + if(animProgress < ANIMATIONSTEPS) { + if (Handler()->animateButtons() ) { + animProgress++; + } else { + animProgress = ANIMATIONSTEPS; + } + animTmr->start(TIMERINTERVAL, true); // single-shot + } + } else { + if(animProgress > 0) { + if (Handler()->animateButtons() ) { + animProgress--; + } else { + animProgress = 0; + } + animTmr->start(TIMERINTERVAL, true); // single-shot + } + } + + repaint(false); +} + +void PlastikButton::enterEvent(QEvent *e) +{ + QButton::enterEvent(e); + + hover = true; + animate(); +} + +void PlastikButton::leaveEvent(QEvent *e) +{ + QButton::leaveEvent(e); + + hover = false; + animate(); +} + +void PlastikButton::drawButton(QPainter *painter) +{ + QRect r(0,0,width(),height()); + + bool active = m_client->isActive(); + KPixmap tempKPixmap; + + QColor highlightColor; + if(type() == CloseButton) { + highlightColor = QColor(255,64,0); + } else { + highlightColor = Qt::white; + } + + QColor contourTop = alphaBlendColors(Handler()->getColor(TitleGradient2, active), + Qt::black, 215); + QColor contourBottom = alphaBlendColors(Handler()->getColor(TitleGradient3, active), + Qt::black, 215); + QColor sourfaceTop = alphaBlendColors(Handler()->getColor(TitleGradient2, active), + Qt::white, 210); + QColor sourfaceBottom = alphaBlendColors(Handler()->getColor(TitleGradient3, active), + Qt::white, 210); + + int highlightAlpha = static_cast<int>(255-((60/static_cast<double>(ANIMATIONSTEPS))* + static_cast<double>(animProgress) ) ); + contourTop = alphaBlendColors(contourTop, highlightColor, highlightAlpha ); + contourBottom = alphaBlendColors(contourBottom, highlightColor, highlightAlpha); + sourfaceTop = alphaBlendColors(sourfaceTop, highlightColor, highlightAlpha); + sourfaceBottom = alphaBlendColors(sourfaceBottom, highlightColor, highlightAlpha); + + if (isDown() ) { + contourTop = alphaBlendColors(contourTop, Qt::black, 200); + contourBottom = alphaBlendColors(contourBottom, Qt::black, 200); + sourfaceTop = alphaBlendColors(sourfaceTop, Qt::black, 200); + sourfaceBottom = alphaBlendColors(sourfaceBottom, Qt::black, 200); + } + + QPixmap buffer; + buffer.resize(width(), height()); + QPainter bP(&buffer); + + // fake the titlebar background + bP.drawTiledPixmap(0, 0, width(), width(), m_client->getTitleBarTile(active) ); + + if (type() != MenuButton || hover || animProgress != 0) { + // contour + bP.setPen(contourTop); + bP.drawLine(r.x()+2, r.y(), r.right()-2, r.y() ); + bP.drawPoint(r.x()+1, r.y()+1); + bP.drawPoint(r.right()-1, r.y()+1); + bP.setPen(contourBottom); + bP.drawLine(r.x()+2, r.bottom(), r.right()-2, r.bottom() ); + bP.drawPoint(r.x()+1, r.bottom()-1); + bP.drawPoint(r.right()-1, r.bottom()-1); + // sides of the contour + tempKPixmap.resize(1, r.height()-2*2); + KPixmapEffect::gradient(tempKPixmap, + contourTop, + contourBottom, + KPixmapEffect::VerticalGradient); + bP.drawPixmap(r.x(), r.y()+2, tempKPixmap); + bP.drawPixmap(r.right(), r.y()+2, tempKPixmap); + // sort of anti-alias for the contour + bP.setPen(alphaBlendColors(Handler()->getColor(TitleGradient2, active), + contourTop, 150) ); + bP.drawPoint(r.x()+1, r.y()); + bP.drawPoint(r.right()-1, r.y()); + bP.drawPoint(r.x(), r.y()+1); + bP.drawPoint(r.right(), r.y()+1); + bP.setPen(alphaBlendColors(Handler()->getColor(TitleGradient3, active), + contourBottom, 150) ); + bP.drawPoint(r.x()+1, r.bottom()); + bP.drawPoint(r.right()-1, r.bottom()); + bP.drawPoint(r.x(), r.bottom()-1); + bP.drawPoint(r.right(), r.bottom()-1); + // sourface + // fill top and bottom + bP.setPen(sourfaceTop); + bP.drawLine(r.x()+2, r.y()+1, r.right()-2, r.y()+1 ); + bP.setPen(sourfaceBottom); + bP.drawLine(r.x()+2, r.bottom()-1, r.right()-2, r.bottom()-1 ); + // fill the rest! :) + tempKPixmap.resize(1, r.height()-2*2); + KPixmapEffect::gradient(tempKPixmap, + sourfaceTop, + sourfaceBottom, + KPixmapEffect::VerticalGradient); + bP.drawTiledPixmap(r.x()+1, r.y()+2, r.width()-2, r.height()-4, tempKPixmap); + } + + if (type() == MenuButton) + { + QPixmap menuIcon(m_client->icon().pixmap( QIconSet::Small, QIconSet::Normal)); + if (width() < menuIcon.width() || height() < menuIcon.height() ) { + menuIcon.convertFromImage( menuIcon.convertToImage().smoothScale(width(), height())); + } + bP.drawPixmap((width()-menuIcon.width())/2, (height()-menuIcon.height())/2, menuIcon); + } + else + { + int dX,dY; + const QBitmap &icon = Handler()->buttonBitmap(m_iconType, size(), decoration()->isToolWindow() ); + dX = r.x()+(r.width()-icon.width())/2; + dY = r.y()+(r.height()-icon.height())/2; + if (isDown() ) { + dY++; + } + + if(!isDown() && Handler()->titleShadow() ) { + QColor shadowColor; + if (qGray(Handler()->getColor(TitleFont,active).rgb()) < 100) + shadowColor = QColor(255, 255, 255); + else + shadowColor = QColor(0,0,0); + bP.setPen(alphaBlendColors(sourfaceTop, shadowColor, 180) ); + bP.drawPixmap(dX+1, dY+1, icon); + } + + bP.setPen(Handler()->getColor(TitleFont,active) ); + bP.drawPixmap(dX, dY, icon); + } + + bP.end(); + painter->drawPixmap(0, 0, buffer); +} + +QBitmap IconEngine::icon(ButtonIcon icon, int size) +{ + if (size%2 == 0) + --size; + + QBitmap bitmap(size,size); + bitmap.fill(Qt::color0); + QPainter p(&bitmap); + + p.setPen(Qt::color1); + + QRect r = bitmap.rect(); + + // line widths + int lwTitleBar = 1; + if (r.width() > 16) { + lwTitleBar = 4; + } else if (r.width() > 4) { + lwTitleBar = 2; + } + int lwArrow = 1; + if (r.width() > 16) { + lwArrow = 4; + } else if (r.width() > 7) { + lwArrow = 2; + } + + switch(icon) { + case CloseIcon: + { + int lineWidth = 1; + if (r.width() > 16) { + lineWidth = 3; + } else if (r.width() > 4) { + lineWidth = 2; + } + + drawObject(p, DiagonalLine, r.x(), r.y(), r.width(), lineWidth); + drawObject(p, CrossDiagonalLine, r.x(), r.bottom(), r.width(), lineWidth); + + break; + } + + case MaxIcon: + { + int lineWidth2 = 1; // frame + if (r.width() > 16) { + lineWidth2 = 2; + } else if (r.width() > 4) { + lineWidth2 = 1; + } + + drawObject(p, HorizontalLine, r.x(), r.top(), r.width(), lwTitleBar); + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width(), lineWidth2); + drawObject(p, VerticalLine, r.x(), r.top(), r.height(), lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height(), lineWidth2); + + break; + } + + case MaxRestoreIcon: + { + int lineWidth2 = 1; // frame + if (r.width() > 16) { + lineWidth2 = 2; + } else if (r.width() > 4) { + lineWidth2 = 1; + } + + int margin1, margin2; + margin1 = margin2 = lineWidth2*2; + if (r.width() < 8) + margin1 = 1; + + // background window + drawObject(p, HorizontalLine, r.x()+margin1, r.top(), r.width()-margin1, lineWidth2); + drawObject(p, HorizontalLine, r.right()-margin2, r.bottom()-(lineWidth2-1)-margin1, margin2, lineWidth2); + drawObject(p, VerticalLine, r.x()+margin1, r.top(), margin2, lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height()-margin1, lineWidth2); + + // foreground window + drawObject(p, HorizontalLine, r.x(), r.top()+margin2, r.width()-margin2, lwTitleBar); + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width()-margin2, lineWidth2); + drawObject(p, VerticalLine, r.x(), r.top()+margin2, r.height(), lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1)-margin2, r.top()+margin2, r.height(), lineWidth2); + + break; + } + + case MinIcon: + { + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lwTitleBar-1), r.width(), lwTitleBar); + + break; + } + + case HelpIcon: + { + int center = r.x()+r.width()/2 -1; + int side = r.width()/4; + + // paint a question mark... code is quite messy, to be cleaned up later...! :o + + if (r.width() > 16) { + int lineWidth = 3; + + // top bar + drawObject(p, HorizontalLine, center-side+3, r.y(), 2*side-3-1, lineWidth); + // top bar rounding + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+5, 6, lineWidth); + drawObject(p, DiagonalLine, center+side-3, r.y(), 5, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+3, r.height()-(2*lineWidth+side+2+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth, side+2, lineWidth); + drawObject(p, HorizontalLine, center, r.bottom()-3*lineWidth+2, lineWidth, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth); + } else if (r.width() > 8) { + int lineWidth = 2; + + // top bar + drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side-1, lineWidth); + // top bar rounding + if (r.width() > 9) { + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+3, 3, lineWidth); + } else { + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+2, 3, lineWidth); + } + drawObject(p, DiagonalLine, center+side-1, r.y(), 3, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+2, r.height()-(2*lineWidth+side+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth+1, side+2, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth); + } else { + int lineWidth = 1; + + // top bar + drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side, lineWidth); + // top bar rounding + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+1, 2, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+1, r.y(), r.height()-(side+2+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2, side+2, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom(), 1, 1); + } + + break; + } + + case NotOnAllDesktopsIcon: + { + int lwMark = r.width()-lwTitleBar*2-2; + if (lwMark < 1) + lwMark = 3; + + drawObject(p, HorizontalLine, r.x()+(r.width()-lwMark)/2, r.y()+(r.height()-lwMark)/2, lwMark, lwMark); + + // Fall through to OnAllDesktopsIcon intended! + } + case OnAllDesktopsIcon: + { + // horizontal bars + drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.y(), r.width()-2*lwTitleBar, lwTitleBar); + drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.bottom()-(lwTitleBar-1), r.width()-2*lwTitleBar, lwTitleBar); + // vertical bars + drawObject(p, VerticalLine, r.x(), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar); + drawObject(p, VerticalLine, r.right()-(lwTitleBar-1), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar); + + + break; + } + + case NoKeepAboveIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, CrossDiagonalLine, r.x(), center+2*lwArrow, center-r.x(), lwArrow); + drawObject(p, DiagonalLine, r.x()+center, r.y()+1+2*lwArrow, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.y()+2*lwArrow, (lwArrow-2)*2, lwArrow); + + // Fall through to KeepAboveIcon intended! + } + case KeepAboveIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, CrossDiagonalLine, r.x(), center, center-r.x(), lwArrow); + drawObject(p, DiagonalLine, r.x()+center, r.y()+1, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.y(), (lwArrow-2)*2, lwArrow); + + break; + } + + case NoKeepBelowIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, DiagonalLine, r.x(), center-2*lwArrow, center-r.x(), lwArrow); + drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1-2*lwArrow, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1)-2*lwArrow, (lwArrow-2)*2, lwArrow); + + // Fall through to KeepBelowIcon intended! + } + case KeepBelowIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, DiagonalLine, r.x(), center, center-r.x(), lwArrow); + drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1), (lwArrow-2)*2, lwArrow); + + break; + } + + case ShadeIcon: + { + drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lwTitleBar); + + break; + } + + case UnShadeIcon: + { + int lw1 = 1; + int lw2 = 1; + if (r.width() > 16) { + lw1 = 4; + lw2 = 2; + } else if (r.width() > 7) { + lw1 = 2; + lw2 = 1; + } + + int h = QMAX( (r.width()/2), (lw1+2*lw2) ); + + // horizontal bars + drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lw1); + drawObject(p, HorizontalLine, r.x(), r.x()+h-(lw2-1), r.width(), lw2); + // vertical bars + drawObject(p, VerticalLine, r.x(), r.y(), h, lw2); + drawObject(p, VerticalLine, r.right()-(lw2-1), r.y(), h, lw2); + + break; + } + + default: + break; + } + + p.end(); + + bitmap.setMask(bitmap); + + return bitmap; +} + +void IconEngine::drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth) +{ + switch(object) { + case DiagonalLine: + if (lineWidth <= 1) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y+i); + } + } else if (lineWidth <= 2) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y+i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y+i); + p.drawPoint(x+i,y+1+i); + } + } else { + for (int i = 1; i < (length-1); ++i) { + p.drawPoint(x+i,y+i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y+i); + p.drawPoint(x+i,y+1+i); + } + for (int i = 0; i < (length-2); ++i) { + p.drawPoint(x+2+i,y+i); + p.drawPoint(x+i,y+2+i); + } + } + break; + case CrossDiagonalLine: + if (lineWidth <= 1) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y-i); + } + } else if (lineWidth <= 2) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y-i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y-i); + p.drawPoint(x+i,y-1-i); + } + } else { + for (int i = 1; i < (length-1); ++i) { + p.drawPoint(x+i,y-i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y-i); + p.drawPoint(x+i,y-1-i); + } + for (int i = 0; i < (length-2); ++i) { + p.drawPoint(x+2+i,y-i); + p.drawPoint(x+i,y-2-i); + } + } + break; + case HorizontalLine: + for (int i = 0; i < lineWidth; ++i) { + p.drawLine(x,y+i, x+length-1, y+i); + } + break; + case VerticalLine: + for (int i = 0; i < lineWidth; ++i) { + p.drawLine(x+i,y, x+i, y+length-1); + } + break; + default: + break; + } +} + +} // KWinPlastik diff --git a/kwin/clients/plastik/plastikbutton.h b/kwin/clients/plastik/plastikbutton.h new file mode 100644 index 000000000..0be8dddea --- /dev/null +++ b/kwin/clients/plastik/plastikbutton.h @@ -0,0 +1,90 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIKBUTTON_H +#define PLASTIKBUTTON_H + +#include <qbutton.h> +#include <qimage.h> +#include "plastik.h" + +#include <kcommondecoration.h> + +class QTimer; + +namespace KWinPlastik { + +class PlastikClient; + +class PlastikButton : public KCommonDecorationButton +{ + Q_OBJECT +public: + PlastikButton(ButtonType type, PlastikClient *parent, const char *name); + ~PlastikButton(); + + void reset(unsigned long changed); + PlastikClient * client() { return m_client; } + +protected slots: + void animate(); + +private: + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void drawButton(QPainter *painter); + +private: + PlastikClient *m_client; + ButtonIcon m_iconType; + bool hover; + + QTimer *animTmr; + uint animProgress; +}; + +/** + * This class creates bitmaps which can be used as icons on buttons. The icons + * are "hardcoded". + * Over the previous "Gimp->xpm->QImage->recolor->SmoothScale->QPixmap" solution + * it has the important advantage that icons are more scalable and at the same + * time sharp and not blurred. + */ +class IconEngine +{ + public: + static QBitmap icon(ButtonIcon icon, int size); + + private: + enum Object { + HorizontalLine, + VerticalLine, + DiagonalLine, + CrossDiagonalLine + }; + + static void drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth); +}; + +} // namespace KWinPlastik + +#endif // PLASTIKBUTTON_H diff --git a/kwin/clients/plastik/plastikclient.cpp b/kwin/clients/plastik/plastikclient.cpp new file mode 100644 index 000000000..722761a5f --- /dev/null +++ b/kwin/clients/plastik/plastikclient.cpp @@ -0,0 +1,529 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <klocale.h> + +#include <qbitmap.h> +#include <qdatetime.h> +#include <qfontmetrics.h> +#include <qimage.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qdesktopwidget.h> + +#include "plastikclient.h" +#include "plastikbutton.h" +#include "misc.h" + +namespace KWinPlastik +{ + +PlastikClient::PlastikClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration (bridge, factory), + s_titleFont(QFont() ) +{ + memset(m_captionPixmaps, 0, sizeof(QPixmap*)*2); +} + +PlastikClient::~PlastikClient() +{ + clearCaptionPixmaps(); +} + +QString PlastikClient::visibleName() const +{ + return i18n("Plastik"); +} + +QString PlastikClient::defaultButtonsLeft() const +{ + return "M"; +} + +QString PlastikClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool PlastikClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return Handler()->menuClose(); + + case DB_WindowMask: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int PlastikClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + { + if (respectWindowState && maximized) { + return 0; + } else { + return Handler()->borderSize(); + } + } + + case LM_TitleEdgeTop: + { + if (respectWindowState && maximized) { + return 0; + } else { + return 4; + } + } + + case LM_TitleEdgeBottom: + { +// if (respectWindowState && maximized) { +// return 1; +// } else { + return 2; +// } + } + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + { + if (respectWindowState && maximized) { + return 0; + } else { + return 6; + } + } + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 5; + + case LM_ButtonWidth: + case LM_ButtonHeight: + case LM_TitleHeight: + { + if (respectWindowState && isToolWindow()) { + return Handler()->titleHeightTool(); + } else { + return Handler()->titleHeight(); + } + } + + case LM_ButtonSpacing: + return 1; + + case LM_ButtonMarginTop: + return 0; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *PlastikClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new PlastikButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new PlastikButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new PlastikButton(HelpButton, this, "help"); + + case MinButton: + return new PlastikButton(MinButton, this, "minimize"); + + case MaxButton: + return new PlastikButton(MaxButton, this, "maximize"); + + case CloseButton: + return new PlastikButton(CloseButton, this, "close"); + + case AboveButton: + return new PlastikButton(AboveButton, this, "above"); + + case BelowButton: + return new PlastikButton(BelowButton, this, "below"); + + case ShadeButton: + return new PlastikButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void PlastikClient::init() +{ + s_titleFont = isToolWindow() ? Handler()->titleFontTool() : Handler()->titleFont(); + + clearCaptionPixmaps(); + + KCommonDecoration::init(); +} + +QRegion PlastikClient::cornerShape(WindowCorner corner) +{ + int w = widget()->width(); + int h = widget()->height(); + + switch (corner) { + case WC_TopLeft: + if (layoutMetric(LM_TitleEdgeLeft) > 0) + return QRegion(0, 0, 1, 2) + QRegion(1, 0, 1, 1); + else + return QRegion(); + + case WC_TopRight: + if (layoutMetric(LM_TitleEdgeRight) > 0) + return QRegion(w-1, 0, 1, 2) + QRegion(w-2, 0, 1, 1); + else + return QRegion(); + + case WC_BottomLeft: + if (layoutMetric(LM_BorderBottom) > 0) + return QRegion(0, h-1, 1, 1); + else + return QRegion(); + + case WC_BottomRight: + if (layoutMetric(LM_BorderBottom) > 0) + return QRegion(w-1, h-1, 1, 1); + else + return QRegion(); + + default: + return QRegion(); + } + +} + +void PlastikClient::paintEvent(QPaintEvent *e) +{ + QRegion region = e->region(); + + PlastikHandler *handler = Handler(); + + if (oldCaption != caption() ) + clearCaptionPixmaps(); + + bool active = isActive(); + bool toolWindow = isToolWindow(); + + QPainter painter(widget() ); + + // often needed coordinates + QRect r = widget()->rect(); + + int r_w = r.width(); +// int r_h = r.height(); + int r_x, r_y, r_x2, r_y2; + r.coords(&r_x, &r_y, &r_x2, &r_y2); + const int borderLeft = layoutMetric(LM_BorderLeft); + const int borderRight = layoutMetric(LM_BorderRight); + const int borderBottom = layoutMetric(LM_BorderBottom); + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + + const int borderBottomTop = r_y2-borderBottom+1; + const int borderLeftRight = r_x+borderLeft-1; + const int borderRightLeft = r_x2-borderRight+1; + const int titleEdgeBottomBottom = r_y+titleEdgeTop+titleHeight+titleEdgeBottom-1; + + const int sideHeight = borderBottomTop-titleEdgeBottomBottom-1; + + QRect Rtitle = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + + QRect tempRect; + + // topSpacer + if(titleEdgeTop > 0) + { + tempRect.setRect(r_x+2, r_y, r_w-2*2, titleEdgeTop ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTileTop, active, toolWindow) ); + } + } + + // leftTitleSpacer + int titleMarginLeft = 0; + int titleMarginRight = 0; + if(titleEdgeLeft > 0) + { + tempRect.setRect(r_x, r_y, borderLeft, titleEdgeTop+titleHeight+titleEdgeBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarLeft, active, toolWindow) ); + titleMarginLeft = borderLeft; + } + } + + // rightTitleSpacer + if(titleEdgeRight > 0) + { + tempRect.setRect(borderRightLeft, r_y, borderRight, titleEdgeTop+titleHeight+titleEdgeBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarRight, active, toolWindow) ); + titleMarginRight = borderRight; + } + } + + // titleSpacer + const QPixmap &caption = captionPixmap(); + if(Rtitle.width() > 0) + { + m_captionRect = captionRect(); // also update m_captionRect! + if (m_captionRect.isValid() && region.contains(m_captionRect) ) + { + painter.drawTiledPixmap(m_captionRect, caption); + } + + // left to the title + tempRect.setRect(r_x+titleMarginLeft, m_captionRect.top(), + m_captionRect.left() - (r_x+titleMarginLeft), m_captionRect.height() ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTile, active, toolWindow) ); + } + + // right to the title + tempRect.setRect(m_captionRect.right()+1, m_captionRect.top(), + (r_x2-titleMarginRight) - m_captionRect.right(), m_captionRect.height() ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTile, active, toolWindow) ); + } + + } + + // leftSpacer + if(borderLeft > 0 && sideHeight > 0) + { + tempRect.setCoords(r_x, titleEdgeBottomBottom+1, borderLeftRight, borderBottomTop-1); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderLeftTile, active, toolWindow) ); + } + } + + // rightSpacer + if(borderRight > 0 && sideHeight > 0) + { + tempRect.setCoords(borderRightLeft, titleEdgeBottomBottom+1, r_x2, borderBottomTop-1); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderRightTile, active, toolWindow) ); + } + } + + // bottomSpacer + if(borderBottom > 0) + { + int l = r_x; + int r = r_x2; + + tempRect.setRect(r_x, borderBottomTop, borderLeft, borderBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomLeft, active, toolWindow) ); + l = tempRect.right()+1; + } + + tempRect.setRect(borderRightLeft, borderBottomTop, borderLeft, borderBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomRight, active, toolWindow) ); + r = tempRect.left()-1; + } + + tempRect.setCoords(l, borderBottomTop, r, r_y2); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomTile, active, toolWindow) ); + } + } +} + +QRect PlastikClient::captionRect() const +{ + const QPixmap &caption = captionPixmap(); + QRect r = widget()->rect(); + + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int marginLeft = layoutMetric(LM_TitleBorderLeft); + const int marginRight = layoutMetric(LM_TitleBorderRight); + + const int titleLeft = r.left() + titleEdgeLeft + buttonsLeftWidth() + marginLeft; + const int titleWidth = r.width() - + titleEdgeLeft - layoutMetric(LM_TitleEdgeRight) - + buttonsLeftWidth() - buttonsRightWidth() - + marginLeft - marginRight; + + Qt::AlignmentFlags a = Handler()->titleAlign(); + + int tX, tW; // position/width of the title buffer + if (caption.width() > titleWidth) { + tW = titleWidth; + } else { + tW = caption.width(); + } + if (a == Qt::AlignLeft || (caption.width() > titleWidth) ) { + // Align left + tX = titleLeft; + } else if (a == Qt::AlignHCenter) { + // Align center + tX = titleLeft+(titleWidth- caption.width() )/2; + } else { + // Align right + tX = titleLeft+titleWidth-caption.width(); + } + + return QRect(tX, r.top()+titleEdgeTop, tW, titleHeight+titleEdgeBottom); +} + +void PlastikClient::updateCaption() +{ + QRect oldCaptionRect = m_captionRect; + + if (oldCaption != caption() ) + clearCaptionPixmaps(); + + m_captionRect = PlastikClient::captionRect(); + + if (oldCaptionRect.isValid() && m_captionRect.isValid() ) + widget()->update(oldCaptionRect|m_captionRect); + else + widget()->update(); +} + +void PlastikClient::reset( unsigned long changed ) +{ + if (changed & SettingColors) + { + // repaint the whole thing + clearCaptionPixmaps(); + widget()->update(); + updateButtons(); + } else if (changed & SettingFont) { + // font has changed -- update title height and font + s_titleFont = isToolWindow() ? Handler()->titleFontTool() : Handler()->titleFont(); + + updateLayout(); + + // then repaint + clearCaptionPixmaps(); + widget()->update(); + } + + KCommonDecoration::reset(changed); +} + +const QPixmap &PlastikClient::getTitleBarTile(bool active) const +{ + return Handler()->pixmap(TitleBarTile, active, isToolWindow() ); +} + +const QPixmap &PlastikClient::captionPixmap() const +{ + bool active = isActive(); + + if (m_captionPixmaps[active]) { + return *m_captionPixmaps[active]; + } + + // not found, create new pixmap... + + const uint maxCaptionLength = 300; // truncate captions longer than this! + QString c(caption() ); + if (c.length() > maxCaptionLength) { + c.truncate(maxCaptionLength); + c.append(" [...]"); + } + + QFontMetrics fm(s_titleFont); + int captionWidth = fm.width(c); + int captionHeight = fm.height(); + + const int th = layoutMetric(LM_TitleHeight, false) + layoutMetric(LM_TitleEdgeBottom, false); + + QPainter painter; + + const int thickness = 2; + + QPixmap *captionPixmap = new QPixmap(captionWidth+2*thickness, th); + + painter.begin(captionPixmap); + painter.drawTiledPixmap(captionPixmap->rect(), + Handler()->pixmap(TitleBarTile, active, isToolWindow()) ); + + painter.setFont(s_titleFont); + QPoint tp(1, captionHeight-1); + if(Handler()->titleShadow()) + { + QColor shadowColor; + if (qGray(Handler()->getColor(TitleFont,active).rgb()) < 100) + shadowColor = QColor(255, 255, 255); + else + shadowColor = QColor(0,0,0); + + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 205) ); + painter.drawText(tp+QPoint(1,2), c); + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 225) ); + painter.drawText(tp+QPoint(2,2), c); + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 165) ); + painter.drawText(tp+QPoint(1,1), c); + } + painter.setPen(Handler()->getColor(TitleFont,active) ); + painter.drawText(tp, c ); + painter.end(); + + m_captionPixmaps[active] = captionPixmap; + return *captionPixmap; +} + +void PlastikClient::clearCaptionPixmaps() +{ + for (int i = 0; i < 2; ++i) { + delete m_captionPixmaps[i]; + m_captionPixmaps[i] = 0; + } + + oldCaption = caption(); +} + +} // KWinPlastik diff --git a/kwin/clients/plastik/plastikclient.h b/kwin/clients/plastik/plastikclient.h new file mode 100644 index 000000000..28b611b8e --- /dev/null +++ b/kwin/clients/plastik/plastikclient.h @@ -0,0 +1,73 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIKCLIENT_H +#define PLASTIKCLIENT_H + +#include <kcommondecoration.h> + +#include "plastik.h" + +namespace KWinPlastik { + +class PlastikButton; + +class PlastikClient : public KCommonDecoration +{ +public: + PlastikClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~PlastikClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual QRegion cornerShape(WindowCorner corner); + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void init(); + virtual void reset( unsigned long changed ); + + virtual void paintEvent(QPaintEvent *e); + virtual void updateCaption(); + + const QPixmap &getTitleBarTile(bool active) const; + +private: + QRect captionRect() const; + + const QPixmap &captionPixmap() const; + void clearCaptionPixmaps(); + + mutable QPixmap *m_captionPixmaps[2]; + + QRect m_captionRect; + QString oldCaption; + + // settings... + QFont s_titleFont; +}; + +} // KWinPlastik + +#endif // PLASTIKCLIENT_H diff --git a/kwin/clients/quartz/Makefile.am b/kwin/clients/quartz/Makefile.am new file mode 100644 index 000000000..d66643ba5 --- /dev/null +++ b/kwin/clients/quartz/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_quartz.la + +kwin3_quartz_la_SOURCES = quartz.cpp +kwin3_quartz_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_quartz_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = quartz.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = quartz.desktop + +EXTRA_DIST = $(lnk_DATA) + + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/quartz/config/Makefile.am b/kwin/clients/quartz/config/Makefile.am new file mode 100644 index 000000000..eb64880ee --- /dev/null +++ b/kwin/clients/quartz/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_quartz_config.la + +kwin_quartz_config_la_SOURCES = config.cpp +kwin_quartz_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_quartz_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/quartz/config/config.cpp b/kwin/clients/quartz/config/config.cpp new file mode 100644 index 000000000..8c7070b9d --- /dev/null +++ b/kwin/clients/quartz/config/config.cpp @@ -0,0 +1,104 @@ +/* + * + * This file contains the quartz configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <klocale.h> + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new QuartzConfig(conf, parent)); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +QuartzConfig::QuartzConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + quartzConfig = new KConfig("kwinquartzrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + gb = new QVBox( parent ); + cbColorBorder = new QCheckBox( + i18n("Draw window frames using &titlebar colors"), gb ); + QWhatsThis::add( cbColorBorder, + i18n("When selected, the window decoration borders " + "are drawn using the titlebar colors; otherwise, they are " + "drawn using normal border colors instead.") ); + cbExtraSmall = new QCheckBox( i18n("Quartz &extra slim"), gb ); + QWhatsThis::add( cbExtraSmall, + i18n("Quartz window decorations with extra-small title bar.") ); + // Load configuration options + load( conf ); + + // Ensure we track user changes properly + connect( cbColorBorder, SIGNAL(clicked()), this, SLOT(slotSelectionChanged()) ); + connect( cbExtraSmall, SIGNAL(clicked()), this, SLOT(slotSelectionChanged()) ); + + // Make the widgets visible in kwindecoration + gb->show(); +} + + +QuartzConfig::~QuartzConfig() +{ + delete gb; + delete quartzConfig; +} + + +void QuartzConfig::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void QuartzConfig::load( KConfig* /*conf*/ ) +{ + quartzConfig->setGroup("General"); + bool override = quartzConfig->readBoolEntry( "UseTitleBarBorderColors", true ); + cbColorBorder->setChecked( override ); + override = quartzConfig->readBoolEntry( "UseQuartzExtraSlim", false ); + cbExtraSmall->setChecked( override ); +} + + +// Saves the configurable options to the kwinrc config file +void QuartzConfig::save( KConfig* /*conf*/ ) +{ + quartzConfig->setGroup("General"); + quartzConfig->writeEntry( "UseTitleBarBorderColors", cbColorBorder->isChecked() ); + quartzConfig->writeEntry( "UseQuartzExtraSlim", cbExtraSmall->isChecked() ); + // Ensure others trying to read this config get updated + quartzConfig->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void QuartzConfig::defaults() +{ + cbColorBorder->setChecked( true ); + cbExtraSmall->setChecked( false ); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/quartz/config/config.h b/kwin/clients/quartz/config/config.h new file mode 100644 index 000000000..815cc9644 --- /dev/null +++ b/kwin/clients/quartz/config/config.h @@ -0,0 +1,47 @@ +/* + * + * This file contains the quartz configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef __KDE_QUARTZCONFIG_H +#define __KDE_QUARTZCONFIG_H + +#include <qcheckbox.h> +#include <qvbox.h> +#include <kconfig.h> + +class QuartzConfig: public QObject +{ + Q_OBJECT + + public: + QuartzConfig( KConfig* conf, QWidget* parent ); + ~QuartzConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig* quartzConfig; + QCheckBox* cbColorBorder; + QCheckBox* cbExtraSmall; + QVBox* gb; +}; + + +#endif + +// vim: ts=4 diff --git a/kwin/clients/quartz/quartz.cpp b/kwin/clients/quartz/quartz.cpp new file mode 100644 index 000000000..857971780 --- /dev/null +++ b/kwin/clients/quartz/quartz.cpp @@ -0,0 +1,797 @@ +/* + * + * Gallium-Quartz KWin client + * + * Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the KDE default client. + * + * Includes mini titlebars for ToolWindow Support. + * Button positions are now customizable. + * + */ + +#include <kconfig.h> +#include <kdrawutil.h> +#include <kglobal.h> +#include <klocale.h> +#include <kpixmapeffect.h> +#include <qbitmap.h> +#include <qdrawutil.h> +#include <qimage.h> +#include <qapplication.h> + +#include "quartz.h" + + +namespace Quartz { + +static const unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char close_bits[] = { + 0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00, + 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char maximize_bits[] = { + 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00}; + +static const unsigned char minmax_bits[] = { + 0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00, + 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00}; + +static const unsigned char question_bits[] = { + 0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char above_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char above_off_bits[] = { + 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00}; + +static const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +/////////////////////////////////////////////////////////////////////////// + +// Titlebar button positions +bool onAllDesktopsButtonOnLeft = true; +bool coloredFrame = true; +bool extraSlim = false; + +KPixmap* titleBlocks = NULL; +KPixmap* ititleBlocks = NULL; +KPixmap* pinDownPix = NULL; +KPixmap* pinUpPix = NULL; +KPixmap* ipinDownPix = NULL; +KPixmap* ipinUpPix = NULL; +static int normalTitleHeight; +static int toolTitleHeight; +static int borderWidth; + +bool quartz_initialized = false; +QuartzHandler* clientHandler; + +/////////////////////////////////////////////////////////////////////////// + + +QuartzHandler::QuartzHandler() +{ + quartz_initialized = false; + readConfig(); + createPixmaps(); + quartz_initialized = true; +} + + +QuartzHandler::~QuartzHandler() +{ + quartz_initialized = false; + freePixmaps(); +} + + +KDecoration* QuartzHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new QuartzClient( bridge, this ); +} + + +bool QuartzHandler::reset(unsigned long changed) +{ + quartz_initialized = false; + freePixmaps(); + readConfig(); + createPixmaps(); + quartz_initialized = true; + + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + if (changed & SettingColors) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } + return true; +} + + +bool QuartzHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonSpacer: + return true; + default: + return false; + }; +} + + +void QuartzHandler::readConfig() +{ + KConfig conf("kwinquartzrc"); + conf.setGroup("General"); + coloredFrame = conf.readBoolEntry( "UseTitleBarBorderColors", true ); + extraSlim = conf.readBoolEntry( "UseQuartzExtraSlim", false ); + + // A small hack to make the on all desktops button look nicer + onAllDesktopsButtonOnLeft = KDecoration::options()->titleButtonsLeft().contains( 'S' ); + if ( QApplication::reverseLayout() ) + onAllDesktopsButtonOnLeft = !onAllDesktopsButtonOnLeft; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + borderWidth = 8; + break; + case BorderVeryLarge: + borderWidth = 12; + break; + case BorderHuge: + borderWidth = 18; + break; + case BorderVeryHuge: + borderWidth = 27; + break; + case BorderOversized: + borderWidth = 40; + break; + case BorderTiny: + case BorderNormal: + default: + borderWidth = extraSlim?2:4; + } + + normalTitleHeight = QFontMetrics(options()->font(true)).height(); + int nTH_limit=extraSlim?14:18; + normalTitleHeight = QFontMetrics(options()->font(true)).height()-(extraSlim?1:0); + if (normalTitleHeight < nTH_limit) normalTitleHeight = nTH_limit; + if (normalTitleHeight < borderWidth) normalTitleHeight = borderWidth; + + toolTitleHeight = QFontMetrics(options()->font(true, true)).height(); + if (toolTitleHeight < 12) toolTitleHeight = 12; + if (toolTitleHeight < borderWidth) toolTitleHeight = borderWidth; +} + + +// This does the colour transition magic. (You say "Oh, is that it?") +// This may be made configurable at a later stage +void QuartzHandler::drawBlocks( KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2 ) +{ + QPainter px; + + px.begin( pi ); + + // Draw a background gradient first + KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient); + + int factor = (pi->height()-2)/4; + int square = factor - (factor+2)/4; + + int x = pi->width() - 5*factor - square; + int y = (pi->height() - 4*factor)/2; + + px.fillRect( x, y, square, square, c1.light(120) ); + px.fillRect( x, y+factor, square, square, c1 ); + px.fillRect( x, y+2*factor, square, square, c1.light(110) ); + px.fillRect( x, y+3*factor, square, square, c1 ); + + px.fillRect( x+factor, y, square, square, c1.light(110) ); + px.fillRect( x+factor, y+factor, square, square, c2.light(110) ); + px.fillRect( x+factor, y+2*factor, square, square, c1.light(120) ); + px.fillRect( x+factor, y+3*factor, square, square, c2.light(130) ); + + px.fillRect( x+2*factor, y+factor, square, square, c1.light(110) ); + px.fillRect( x+2*factor, y+2*factor, square, square, c2.light(120) ); + px.fillRect( x+2*factor, y+3*factor, square, square, c2.light(150) ); + + px.fillRect( x+3*factor, y, square, square, c1.dark(110) ); + px.fillRect( x+3*factor, y+2*factor, square, square, c2.light(120) ); + px.fillRect( x+3*factor, y+3*factor, square, square, c1.dark(120) ); + + px.fillRect( x+4*factor, y+factor, square, square, c1.light(110) ); + px.fillRect( x+4*factor, y+3*factor, square, square, c1.dark(110) ); + + px.fillRect( x+5*factor, y+2*factor, square, square, c2.light(120)); + px.fillRect( x+5*factor, y+3*factor, square, square, c2.light(110) ); +} + + +// This paints the button pixmaps upon loading the style. +void QuartzHandler::createPixmaps() +{ + // Obtain titlebar blend colours, and create the block stuff on pixmaps. + QColorGroup g2 = options()->colorGroup(ColorTitleBlend, true); + QColor c2 = g2.background(); + g2 = options()->colorGroup(ColorTitleBar, true ); + QColor c = g2.background().light(130); + + titleBlocks = new KPixmap(); + titleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight ); + drawBlocks( titleBlocks, *titleBlocks, c, c2 ); + + g2 = options()->colorGroup(ColorTitleBlend, false); + c2 = g2.background(); + g2 = options()->colorGroup(ColorTitleBar, false ); + c = g2.background().light(130); + + ititleBlocks = new KPixmap(); + ititleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight ); + drawBlocks( ititleBlocks, *ititleBlocks, c, c2 ); + + // Set the on all desktops pin pixmaps; + QColorGroup g; + QPainter p; + + g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, true ); + c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background(); + g2 = options()->colorGroup( ColorButtonBg, true ); + + pinUpPix = new KPixmap(); + pinUpPix->resize(16, 16); + p.begin( pinUpPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + + pinDownPix = new KPixmap(); + pinDownPix->resize(16, 16); + p.begin( pinDownPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + + + // Inactive pins + g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, false ); + c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background(); + g2 = options()->colorGroup( ColorButtonBg, false ); + + ipinUpPix = new KPixmap(); + ipinUpPix->resize(16, 16); + p.begin( ipinUpPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + + ipinDownPix = new KPixmap(); + ipinDownPix->resize(16, 16); + p.begin( ipinDownPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); +} + + +void QuartzHandler::freePixmaps() +{ + delete titleBlocks; + delete ititleBlocks; + + // On all desktops pin images + delete pinUpPix; + delete ipinUpPix; + delete pinDownPix; + delete ipinDownPix; +} + + +QValueList< QuartzHandler::BorderSize > QuartzHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + + +QuartzButton::QuartzButton(ButtonType type, QuartzClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + // Eliminate any possible background flicker + setBackgroundMode( QWidget::NoBackground ); + + deco = 0; +} + + +QuartzButton::~QuartzButton() +{ + delete deco; +} + +void QuartzButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap(0); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void QuartzButton::setBitmap(const unsigned char *bitmap) +{ + delete deco; + deco = 0; + + if (bitmap) { + deco = new QBitmap(10, 10, bitmap, true); + deco->setMask( *deco ); + repaint( false ); + } +} + +void QuartzButton::drawButton(QPainter *p) +{ + // Never paint if the pixmaps have not been created + if (!quartz_initialized) + return; + + QColor c; + + if (isLeft() ) + c = KDecoration::options()->color(KDecoration::ColorTitleBar, decoration()->isActive()).light(130); + else + c = KDecoration::options()->color(KDecoration::ColorTitleBlend, decoration()->isActive()); + + // Fill the button background with an appropriate color + p->fillRect(0, 0, width(), height(), c ); + + // If we have a decoration bitmap, then draw that + // otherwise we paint a menu button (with mini icon), or a onAllDesktops button. + if( deco ) + { + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->setPen( Qt::black ); + p->drawPixmap(isDown() ? xOff+2: xOff+1, isDown() ? yOff+2 : yOff+1, *deco); + p->setPen( KDecoration::options()->color(KDecoration::ColorButtonBg, decoration()->isActive()).light(150) ); + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco); + } else + { + QPixmap btnpix; + int Offset = 0; + + if (type() == OnAllDesktopsButton) + { + if (isDown()) + Offset = 1; + + // Select the right onAllDesktops button to paint + if (decoration()->isActive()) + btnpix = isOn() ? *pinDownPix : *pinUpPix; + else + btnpix = isOn() ? *ipinDownPix : *ipinUpPix; + + } else + btnpix = decoration()->icon().pixmap( QIconSet::Small, QIconSet::Normal); + + // Shrink the miniIcon for tiny titlebars. + if ( height() < 16) + { + QPixmap tmpPix; + + // Smooth scale the image + tmpPix.convertFromImage( btnpix.convertToImage().smoothScale(height(), height())); + p->drawPixmap( 0, 0, tmpPix ); + } else { + Offset += (height() - 16)/2; + p->drawPixmap( Offset, Offset, btnpix ); + } + } +} + +/////////////////////////////////////////////////////////////////////////// + +QuartzClient::QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration (bridge, factory) +{ +} + +QString QuartzClient::visibleName() const +{ + return i18n("Quartz"); +} + +QString QuartzClient::defaultButtonsLeft() const +{ + return "M"; +} + +QString QuartzClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool QuartzClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return false; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int QuartzClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + { + if (respectWindowState && maximized) { + return 0; + } else { + return borderSize; + } + } + + case LM_TitleEdgeTop: + return borderSize-1; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 5; + + case LM_TitleHeight: + return titleHeight; + + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight-2; + + case LM_ButtonSpacing: + return 1; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *QuartzClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new QuartzButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new QuartzButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new QuartzButton(HelpButton, this, "help"); + + case MinButton: + return new QuartzButton(MinButton, this, "minimize"); + + case MaxButton: + return new QuartzButton(MaxButton, this, "maximize"); + + case CloseButton: + return new QuartzButton(CloseButton, this, "close"); + + case AboveButton: + return new QuartzButton(AboveButton, this, "above"); + + case BelowButton: + return new QuartzButton(BelowButton, this, "below"); + + case ShadeButton: + return new QuartzButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + + +void QuartzClient::init() +{ + // Finally, toolWindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight; + largeButtons = false; + } + else { + titleHeight = normalTitleHeight; + largeButtons = true; + } + + borderSize = borderWidth; + + KCommonDecoration::init(); +} + +void QuartzClient::reset( unsigned long changed ) +{ + if (changed & SettingColors || changed & SettingFont) + { + // repaint the whole thing + widget()->repaint(false); + } + + KCommonDecoration::reset(changed); +} + + +// Quartz Paint magic goes here. +void QuartzClient::paintEvent( QPaintEvent* ) +{ + // Never paint if the pixmaps have not been created + if (!quartz_initialized) + return; + + const bool maxFull = (maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows(); + + QColorGroup g; + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width() - 1; + int y2 = r.height() - 1; + int w = r.width(); + int h = r.height(); + + // Draw part of the frame that is the title color + + if( coloredFrame ) + g = options()->colorGroup(ColorTitleBar, isActive()); + else + g = options()->colorGroup(ColorFrame, isActive()); + + // Draw outer highlights and lowlights + p.setPen( g.light().light(120) ); + p.drawLine( x, y, x2-1, y ); + p.drawLine( x, y+1, x, y2-1 ); + p.setPen( g.dark().light(120) ); + p.drawLine( x2, y, x2, y2 ); + p.drawLine( x, y2, x2, y2 ); + + // Fill out the border edges + QColor frameColor; + if ( coloredFrame) + frameColor = g.background().light(130); + else + frameColor = g.background(); + if (borderSize > 2) { + p.fillRect(x+1, y+1, w-2, borderSize-2, frameColor); // top + if (!maxFull) { + p.fillRect(x+1, y+h-(borderSize-1), w-2, borderSize-2, frameColor); // bottom + p.fillRect(x+1, y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // left + p.fillRect(x+w-(borderSize), y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // right + } + } + + // Draw a frame around the wrapped widget. + p.setPen( g.background() ); + if (maxFull) { + p.drawLine(x+1, y+titleHeight+(borderSize-1), w-2, y+titleHeight+(borderSize-1)); + } else { + p.drawRect( x+(borderSize-1), y+titleHeight+(borderSize-1), w-2*(borderSize-1), h-titleHeight-2*(borderSize-1) ); + } + + // Drawing this extra line removes non-drawn areas when shaded + p.drawLine( x+borderSize, y2-borderSize, x2-borderSize, y2-borderSize); + + // Highlight top corner + p.setPen( g.light().light(160) ); + p.drawPoint( x, y ); + p.setPen( g.light().light(140) ); + p.drawPoint( x+1, y ); + p.drawPoint( x, y+1 ); + + // Draw the title bar. + // =================== + int r_x, r_y, r_x2, r_y2; + widget()->rect().coords(&r_x, &r_y, &r_x2, &r_y2); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int ttlHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottomBottom = r_y+titleEdgeTop+ttlHeight+titleEdgeBottom-1; + r = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + + // Obtain titlebar blend colours + QColor c1 = options()->color(ColorTitleBar, isActive() ).light(130); + QColor c2 = options()->color(ColorTitleBlend, isActive() ); + + // Create a disposable pixmap buffer for the titlebar + KPixmap* titleBuffer = new KPixmap; + titleBuffer->resize( maxFull?w-2:(w-2*(borderSize-1)), titleHeight ); + + QPainter p2( titleBuffer, this ); + + // subtract titleBlocks pixmap width and some + int rightoffset = r.x()+r.width()-titleBlocks->width()-borderSize; + + p2.fillRect( 0, 0, w, r.height(), c1 ); + p2.fillRect( rightoffset, 0, maxFull?w-rightoffset:w-rightoffset-2*(borderSize-1), r.height(), c2 ); + + // 8 bit displays will be a bit dithered, but they still look ok. + if ( isActive() ) + p2.drawPixmap( rightoffset, 0, *titleBlocks ); + else + p2.drawPixmap( rightoffset, 0, *ititleBlocks ); + + // Draw the title text on the pixmap, and with a smaller font + // for toolwindows than the default. + QFont fnt; + if ( largeButtons ) { + fnt = options()->font(true, false); // font not small + } else { + fnt = options()->font(true, true); // font small + fnt.setWeight( QFont::Normal ); // and disable bold + } + p2.setFont( fnt ); + + p2.setPen( options()->color(ColorFont, isActive() )); + p2.drawText(r.x()+4-borderSize, 0, r.width()-3, r.height(), + AlignLeft | AlignVCenter, caption() ); + p2.end(); + + p.drawPixmap( maxFull?1:borderSize-1, borderSize-1, *titleBuffer ); + + delete titleBuffer; +} + +} + +// Extended KWin plugin interface +///////////////////////////////// +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + Quartz::clientHandler = new Quartz::QuartzHandler(); + return Quartz::clientHandler; + } +} + + + +#include "quartz.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/quartz/quartz.desktop b/kwin/clients/quartz/quartz.desktop new file mode 100644 index 000000000..30a177973 --- /dev/null +++ b/kwin/clients/quartz/quartz.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Name=Quartz +Name[az]=Kvarts +Name[be]=Кварц +Name[bn]=কোয়ারà§à¦Ÿà§â€Œà¦œ +Name[csb]=Kwarc +Name[cy]=Cwarts +Name[eo]=Kvarco +Name[es]=Cuarzo +Name[eu]=Kuartzoa +Name[fa]=کوارتز +Name[ga]=Grianchloch +Name[hi]=कà¥à¤µà¤¾à¤°à¥à¤Ÿà¥à¤œ +Name[hr]=Kvarc +Name[ka]=კვáƒáƒ ცი +Name[kk]=Кварц +Name[ko]=ìˆ˜ì • +Name[lo]=à»àºšàºšàº„ວàºàº— +Name[lv]=Kvarcs +Name[mk]=Кварц +Name[mt]=Kwartz +Name[ne]=कà¥à¤µà¤¾à¤°à¥à¤œ +Name[pl]=Kwarc +Name[pt_BR]=Quartzo +Name[ro]=CuarÈ› +Name[ru]=Кварц +Name[rw]=Ibuye +Name[ta]=கà¯à®µà®¾à®°à¯à®Ÿà¯à®¸à¯ +Name[te]=à°•à±à°µà°¾à°°à±à°œà± +Name[tg]=ÐšÐ²Ð°Ñ€Ñ‚Ñ +Name[th]=à¹à¸šà¸šà¸„วà¸à¸—ซ์ +Name[tr]=Kuartz +Name[uk]=Кварц +Name[uz]=Chaqmoqtosh +Name[uz@cyrillic]=Чақмоқтош +Name[ven]=Musuku +Name[vi]=Thạch anh +Name[zh_TW]=石英 +X-KDE-Library=kwin3_quartz diff --git a/kwin/clients/quartz/quartz.h b/kwin/clients/quartz/quartz.h new file mode 100644 index 000000000..37f3dff6e --- /dev/null +++ b/kwin/clients/quartz/quartz.h @@ -0,0 +1,95 @@ +/* + * Gallium-Quartz KWin client + * + * Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the KDE default client. + * + * Includes mini titlebars for ToolWindow Support. + * Button positions are now customizable. + * + */ + +#ifndef __KDEGALLIUM_QUARTZ_H +#define __KDEGALLIUM_QUARTZ_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include "../../lib/kcommondecoration.h" +#include "../../lib/kdecorationfactory.h" + +class QSpacerItem; +class QBoxLayout; + +namespace Quartz { + +class QuartzClient; + +class QuartzHandler: public QObject, public KDecorationFactory +{ + Q_OBJECT + public: + QuartzHandler(); + ~QuartzHandler(); + + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset(unsigned long changed); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; + + private: + void readConfig(); + void createPixmaps(); + void freePixmaps(); + void drawBlocks(KPixmap* pi, KPixmap &p, const QColor &c1, const QColor &c2); +}; + + +class QuartzButton : public KCommonDecorationButton +{ + public: + QuartzButton(ButtonType type, QuartzClient *parent, const char *name); + ~QuartzButton(); + void setBitmap(const unsigned char *bitmap); + + void reset(unsigned long changed); + + protected: + void drawButton(QPainter *p); + + QBitmap* deco; +}; + + +class QuartzClient : public KCommonDecoration +{ + public: + QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~QuartzClient() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void init(); + + protected: + virtual void reset( unsigned long changed ); + void paintEvent( QPaintEvent* ); + + private: + int titleHeight, borderSize; + bool largeButtons; +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/redmond/Makefile.am b/kwin/clients/redmond/Makefile.am new file mode 100644 index 000000000..85d0236d2 --- /dev/null +++ b/kwin/clients/redmond/Makefile.am @@ -0,0 +1,17 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_redmond.la + +kwin3_redmond_la_SOURCES = redmond.cpp +kwin3_redmond_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_redmond_la_LIBADD = $(LIB_KDECORE) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = redmond.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = redmond.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/redmond/redmond.cpp b/kwin/clients/redmond/redmond.cpp new file mode 100644 index 000000000..e41eb6353 --- /dev/null +++ b/kwin/clients/redmond/redmond.cpp @@ -0,0 +1,689 @@ +/* + * + * Redmond KWin client + * + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the default KWin client. + * + * Updated to support toolwindows 3/2001 (KS) + * + */ + +#include "redmond.h" + +#include <qdrawutil.h> +#include <qdatetime.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kdrawutil.h> +#include <klocale.h> + +#include <qbitmap.h> +#include <qimage.h> +#include <qapplication.h> + +namespace Redmond { + +static const char *kdelogo[] = { +/* columns rows colors chars-per-pixel */ +"16 16 8 1", +" c None", +". c #000000", +"+ c #A0A0A4", +"@ c #FFFFFF", +"# c #585858", +"$ c #C0C0C0", +"% c #808080", +"& c #DCDCDC", +" ", +" .. .. ", +" .+@. .@#. ", +" .@@@. .@@@# ", +" .@@@..$@@$. ", +" .@@@.@@@$. ", +" .@@@%@@$. ", +" .@@@&@@. ", +" .@@@@@@. ", +" .@@@$@@&. ", +" .@@@.@@@. ", +" .@@@.+@@@. ", +" .@@@..$@@&. ", +" .@@%. .@@@. ", +" .... ... ", +" "}; + +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}; + +static unsigned char close_bits[] = { + 0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00, + 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00}; + +static unsigned char maximize_bits[] = { + 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00}; + +static unsigned char minmax_bits[] = { + 0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00, + 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00}; + +static unsigned char question_bits[] = { + 0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00}; + + +// Up / Down titlebar button images +static KPixmap *btnPix1; +static KPixmap *iBtnPix1; +static KPixmap *btnDownPix1; +static KPixmap *iBtnDownPix1; + +static KPixmap *miniBtnPix1; +static KPixmap *iMiniBtnPix1; +static KPixmap *miniBtnDownPix1; +static KPixmap *iMiniBtnDownPix1; + +static QPixmap *defaultMenuPix; +static QColor *btnForeground; +static bool pixmaps_created = false; + +static int toolTitleHeight; +static int normalTitleHeight; +static int borderWidth; + +static inline const KDecorationOptions *options() +{ + return KDecoration::options(); +} + +static void drawButtonFrame( KPixmap *pix, const QColorGroup &g, bool sunken ) +{ + QPainter p; + int x2 = pix->width() - 1; + int y2 = pix->height() - 1; + p.begin(pix); + + // titlebar button frame + p.setPen( sunken ? g.dark().dark(155) : g.light()); + p.drawLine(0, 0, x2-1, 0); + p.drawLine(0, 0, 0, y2-1); + + if (sunken) + { + p.setPen( g.mid().dark(135) ); + p.drawLine(1, 1, x2-2, 1); + p.drawLine(1, 1, 1, y2-2); + } + + p.setPen( sunken ? g.light() : g.mid().dark(135)); + p.drawLine(1, y2-1, x2-1, y2-1); + p.drawLine(x2-1, 1, x2-1, y2-1); + + p.setPen( sunken ? g.light() : g.dark().dark(155)); + p.drawLine(0, y2, x2, y2); + p.drawLine(x2, 0, x2, y2); +} + + +static void create_pixmaps () +{ + if (pixmaps_created) + return; + + pixmaps_created = true; + + bool highcolor = QPixmap::defaultDepth() > 8; + + btnPix1 = new KPixmap; + btnDownPix1 = new KPixmap; + iBtnPix1 = new KPixmap; + iBtnDownPix1 = new KPixmap; + miniBtnPix1 = new KPixmap; + miniBtnDownPix1 = new KPixmap; + iMiniBtnPix1 = new KPixmap; + iMiniBtnDownPix1 = new KPixmap; + defaultMenuPix = new QPixmap(kdelogo); + + // buttons (active/inactive, sunken/unsunken) + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColor c = g.background(); + btnPix1->resize(normalTitleHeight, normalTitleHeight-2); + btnDownPix1->resize(normalTitleHeight, normalTitleHeight-2); + iBtnPix1->resize(normalTitleHeight, normalTitleHeight-2); + iBtnDownPix1->resize(normalTitleHeight, normalTitleHeight-2); + + miniBtnPix1->resize(toolTitleHeight, toolTitleHeight); + miniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight); + iMiniBtnPix1->resize(toolTitleHeight, toolTitleHeight); + iMiniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight); + + if (highcolor && false) { + KPixmapEffect::gradient(*btnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*btnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + + KPixmapEffect::gradient(*miniBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*miniBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + KPixmapEffect::gradient(*iBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iMiniBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iMiniBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + } else { + btnPix1->fill(c.rgb()); + btnDownPix1->fill(c.rgb()); + miniBtnPix1->fill(c.rgb()); + miniBtnDownPix1->fill(c.rgb()); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + iBtnPix1->fill(c.rgb()); + iBtnDownPix1->fill(c.rgb()); + iMiniBtnPix1->fill(c.rgb()); + iMiniBtnDownPix1->fill(c.rgb()); + } + + g = options()->colorGroup(KDecoration::ColorButtonBg, true); + drawButtonFrame(btnPix1, g, false); + drawButtonFrame(btnDownPix1, g, true); + drawButtonFrame(miniBtnPix1, g, false); + drawButtonFrame(miniBtnDownPix1, g, true); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + drawButtonFrame(iBtnPix1, g, false); + drawButtonFrame(iBtnDownPix1, g, true); + drawButtonFrame(iMiniBtnPix1, g, false); + drawButtonFrame(iMiniBtnDownPix1, g, true); + + // Make sure button pixmaps contrast with the current colour scheme. + if (qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 127) + btnForeground = new QColor(Qt::black); + else + btnForeground = new QColor(Qt::white); +} + +void delete_pixmaps() +{ + delete btnPix1; + delete btnDownPix1; + delete iBtnPix1; + delete iBtnDownPix1; + delete miniBtnPix1; + delete miniBtnDownPix1; + delete iMiniBtnPix1; + delete iMiniBtnDownPix1; + delete defaultMenuPix; + delete btnForeground; + pixmaps_created = false; +} + +RedmondButton::RedmondButton(ButtonType type, RedmondDeco *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + // Eliminate background flicker + setBackgroundMode( NoBackground ); + + miniBtn = decoration()->isToolWindow(); +} + +void RedmondButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case MenuButton: + { + QPixmap miniIcon = decoration()->icon().pixmap(QIconSet::Small, QIconSet::Normal); + if (!miniIcon.isNull()) { + setPixmap(miniIcon); + } else { + setPixmap(*defaultMenuPix); + } + break; + } + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + +void RedmondButton::setBitmap(const unsigned char *bitmap) +{ + pix.resize(0, 0); + + if (bitmap) + deco = QBitmap(10, 10, bitmap, true); + else { + deco = QBitmap(10,10); + deco.fill(Qt::color0); + } + deco.setMask(deco); +} + + +void RedmondButton::setPixmap( const QPixmap &p ) +{ + deco.resize(0, 0); + pix = p; + + repaint(false); +} + + +void RedmondButton::drawButton(QPainter *p) +{ + if ( pix.isNull() ) { + if ( decoration()->isActive() ) { + if ( isDown() ) + p->drawPixmap(0, 0, miniBtn ? *miniBtnDownPix1 : *btnDownPix1); + else + p->drawPixmap(0, 0, miniBtn ? *miniBtnPix1 : *btnPix1); + } else { + if ( isDown() ) + p->drawPixmap(0, 0, miniBtn ? *iMiniBtnDownPix1 : *iBtnDownPix1); + else + p->drawPixmap(0, 0, miniBtn ? *iMiniBtnPix1 : *iBtnPix1); + } + + p->setPen( *btnForeground ); + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco); + } else { + if (isLeft() ) { + p->fillRect(0, 0, width(), height(), + options()->color(KDecoration::ColorTitleBar, decoration()->isActive())); + } else { + p->fillRect(0, 0, width(), height(), + options()->color(KDecoration::ColorTitleBlend, decoration()->isActive())); + } + + if ( type()==MenuButton && height() < 16) { + QPixmap tmpPix; + + // Smooth scale the menu button pixmap + tmpPix.convertFromImage( + pix.convertToImage().smoothScale(height(), height() )); + + p->drawPixmap( 0, 0, tmpPix ); + } else { + int xOff = (width() -pix.width() )/2; + int yOff = (height()-pix.height())/2; + p->drawPixmap(xOff, yOff, pix ); + } + } +} + + +RedmondDeco::RedmondDeco(KDecorationBridge *b, KDecorationFactory *f) + : KCommonDecoration(b, f) +{ +} + +QString RedmondDeco::visibleName() const +{ + return i18n("Redmond"); +} + +QString RedmondDeco::defaultButtonsLeft() const +{ + return "M"; +} + +QString RedmondDeco::defaultButtonsRight() const +{ + return "HIA_X"; +} + +bool RedmondDeco::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return true; + + case DB_WindowMask: + return false; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int RedmondDeco::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows()); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + return border ? borderWidth : 0; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + return border ? borderWidth+2 : 2; + + case LM_TitleEdgeTop: + return border ? borderWidth+2 : 2; + + case LM_TitleEdgeBottom: + return border ? 1 : 0; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return border ? 1 : 0; + + case LM_TitleHeight: + return titleHeight-2; + + case LM_ButtonWidth: + return titleHeight-2; + case LM_ButtonHeight: + if (isToolWindow() || (btn && btn->type()==MenuButton) ) { + return titleHeight-2; + } else { + return titleHeight-2-2; + } + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 2; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *RedmondDeco::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new RedmondButton(MenuButton, this, "menu"); + case HelpButton: + return new RedmondButton(HelpButton, this, "help"); + case MinButton: + return new RedmondButton(MinButton, this, "minimize"); + case MaxButton: + return new RedmondButton(MaxButton, this, "maximize"); + case CloseButton: + return new RedmondButton(CloseButton, this, "close"); + + default: + return 0; + } +} + +void RedmondDeco::init() +{ +// Finally, toolwindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight+2; + } else { + titleHeight = normalTitleHeight+2; + } + + KCommonDecoration::init(); +} + +void RedmondDeco::reset( unsigned long changed ) +{ + KCommonDecoration::reset(changed); +} + +void RedmondDeco::paintEvent( QPaintEvent* ) +{ + bool hicolor = QPixmap::defaultDepth() > 8; + int fontoffset = 1; + + // Modify borderWith used by titlebar to 0, when maximized and not move or resize able + bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows()); + int modBorderWidth = border ? borderWidth : 0; + + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width()-1; + int y2 = r.height()-1; + int w = r.width(); + int h = r.height(); + + // Draw part of the frame that is the frame color + // ============================================== + QColorGroup g = options()->colorGroup(KDecoration::ColorFrame, isActive()); + p.setPen( g.background() ); + p.drawLine( x, y, x2-1, y ); + p.drawLine( x, y, x, y2-1 ); + + // Draw line under title bar + p.drawLine( x+borderWidth, y+titleHeight+borderWidth, x2-borderWidth, y+titleHeight+borderWidth ); + // Draw a hidden line that appears during shading + p.drawLine( x+borderWidth, y2-borderWidth, x2-borderWidth, y2-borderWidth ); + + // Fill out the border edges + for (int i = 1; i < borderWidth; i++) + p.drawRect( x+i, y+i, w-2*i, h-2*i ); + + // Draw highlights and lowlights + p.setPen(g.light()); + for (int i = 1; i <= borderWidth/3; i++) { + p.drawLine( x+i, y+i, x2-i-1, y+i); + p.drawLine( x+i, y+i, x+i, y2-i-1); + } + + p.setPen(g.mid().dark(135)); + for (int i = 1; i <= borderWidth/3; i++) { + p.drawLine( x2-i, y+i+1, x2-i, y2-i); + p.drawLine( x+i+1, y2-i, x2-i, y2-i); + } + + // Draw black edges + p.setPen( g.dark().dark(155) ); + p.drawLine(x2, y, x2, y2); + p.drawLine(x, y2, x2, y2); + + // Draw the title bar. + // =================== + r = titleRect(); +// QFontMetrics fm(options()->font(true)); + + // Obtain blend colours. + QColor c1 = options()->color(KDecoration::ColorTitleBar, isActive() ); + QColor c2 = options()->color(KDecoration::ColorTitleBlend, isActive() ); + + QFont fnt = options()->font(true, isToolWindow() ); + if (isToolWindow() ) { + fnt.setWeight( QFont::Normal ); + fontoffset = 0; + } + + // Paint without a buffer if the colours are the same to + // improve performance, and only draw gradients on hicolor displays. + if ((c1 != c2) && hicolor) { + // KS - Add gradient caching if needed at a later stage. + + // Create a disposable pixmap buffer for the title blend + KPixmap* titleBuffer = new KPixmap; + titleBuffer->resize(w-2*modBorderWidth, titleHeight); + + if (titleBuffer->depth() > 16) { + KPixmapEffect::gradient(*titleBuffer, c1, c2, + KPixmapEffect::HorizontalGradient); + } else { + // This enables dithering on 15 and 16bit displays, preventing + // some pretty horrible banding effects + QImage image = KImageEffect::gradient(titleBuffer->size(), c1, c2, + KImageEffect::HorizontalGradient); + + titleBuffer->convertFromImage(image, Qt::OrderedDither); + } + + QPainter p2( titleBuffer, this ); + + // Since drawing the gradient is (relatively) slow, it is best + // to draw the title text on the pixmap. + + p2.setFont( fnt ); + p2.setPen( options()->color(KDecoration::ColorFont, isActive() )); + p2.drawText( r.x(), fontoffset, r.width()-3, r.height()-1, + AlignLeft | AlignVCenter, caption() ); + p2.end(); + + p.drawPixmap( modBorderWidth, modBorderWidth, *titleBuffer ); + + delete titleBuffer; + + } else { + // Assume lower ended hardware, so don't use buffers. + // Don't draw a gradient either. + p.fillRect( modBorderWidth, modBorderWidth, w-2*modBorderWidth, titleHeight, c1 ); + + // Draw the title text. + p.setFont( fnt ); + p.setPen(options()->color(KDecoration::ColorFont, isActive() )); + p.drawText(r.x()+4, r.y()+fontoffset-2, r.width()-3, r.height()-1, + AlignLeft | AlignVCenter, caption() ); + } + +} + +void RedmondDecoFactory::readConfig() { + normalTitleHeight = QFontMetrics(options()->font(true)).height(); + QFont toolFont = options()->font(true, true); + toolFont.setWeight(QFont::Normal); + toolTitleHeight = QFontMetrics(toolFont).height(); + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + borderWidth = 8; + if (normalTitleHeight < 20) normalTitleHeight = 20; + if (toolTitleHeight < 20) toolTitleHeight = 20; + break; + case BorderVeryLarge: + borderWidth = 12; + if (normalTitleHeight < 24) normalTitleHeight = 24; + if (toolTitleHeight < 24) toolTitleHeight = 24; + break; + case BorderHuge: + borderWidth = 18; + if (normalTitleHeight < 28) normalTitleHeight = 28; + if (toolTitleHeight < 28) toolTitleHeight = 28; + break; + case BorderVeryHuge: + borderWidth = 27; + if (normalTitleHeight < 33) normalTitleHeight = 33; + if (toolTitleHeight < 33) toolTitleHeight = 33; + break; + case BorderOversized: + borderWidth = 40; + if (normalTitleHeight < 40) normalTitleHeight = 40; + if (toolTitleHeight < 40) toolTitleHeight = 40; + break; + case BorderTiny: + case BorderNormal: + default: + borderWidth = 4; + if (normalTitleHeight < 16) normalTitleHeight = 16; + if (toolTitleHeight < 16) toolTitleHeight = 16; + } +} + +RedmondDecoFactory::RedmondDecoFactory() +{ + readConfig(); + create_pixmaps(); +} + +RedmondDecoFactory::~RedmondDecoFactory() +{ + Redmond::delete_pixmaps(); +} + +KDecoration *RedmondDecoFactory::createDecoration( KDecorationBridge *b ) +{ + return new RedmondDeco(b, this); +} + +bool RedmondDecoFactory::reset( unsigned long changed ) +{ + // SettingButtons is handled by KCommonDecoration + if ( changed & ( SettingFont | SettingBorder | SettingColors | SettingButtons ) ) { + delete_pixmaps(); + readConfig(); + create_pixmaps(); + resetDecorations(changed); + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool RedmondDecoFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonSpacer: + return true; + default: + return false; + } +} + +QValueList< RedmondDecoFactory::BorderSize > RedmondDecoFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +} + +extern "C" KDE_EXPORT KDecorationFactory *create_factory() +{ + return new Redmond::RedmondDecoFactory(); +} + + +#include "redmond.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/redmond/redmond.desktop b/kwin/clients/redmond/redmond.desktop new file mode 100644 index 000000000..ae7628876 --- /dev/null +++ b/kwin/clients/redmond/redmond.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Name=Redmond +Name[bn]=রেডমনà§à¦¡ +Name[eo]=Redmondo +Name[fa]=ردموند +Name[hi]=रेडमणà¥à¤¡ +Name[ka]=რედმáƒáƒœáƒ“ი +Name[ko]=ë ˆë“œëª¬ë“œ +Name[lo]=ເລດມàºàº™ +Name[lv]=Redmonda +Name[mk]=Редмонд +Name[ne]=रेडमोनà¥à¤¡ +Name[pa]=ਰੀਡਮੋਂਡ +Name[ta]=ரெடà¯à®®à®¾à®©à¯à®Ÿà¯ +Name[te]=రెడౠమండౠ+Name[tg]=Райдмонд +Name[th]=à¹à¸šà¸šà¹€à¸£à¸”มà¸à¸™à¸”์ +Name[uz@cyrillic]=Редмонд +X-KDE-Library=kwin3_redmond diff --git a/kwin/clients/redmond/redmond.h b/kwin/clients/redmond/redmond.h new file mode 100644 index 000000000..340fdd1b0 --- /dev/null +++ b/kwin/clients/redmond/redmond.h @@ -0,0 +1,91 @@ +/* + * + * Redmond KWin client + * + * Copyright 2001-2003 + * Ported to kwin_iii by Chris Lee <clee@kde.org> + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the default KWin client. + * + * Updated to support the new API 9/2003 (CL) + * Updated to emulate More Accurately 9/2003 (CL) + * Updated to support toolwindows 3/2001 (KS) + * + */ + +#ifndef __KDE_REDMOND_H +#define __KDE_REDMOND_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +namespace Redmond { + +class RedmondDeco; + +class RedmondButton : public KCommonDecorationButton +{ + Q_OBJECT +public: + RedmondButton(ButtonType type, RedmondDeco *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + void setPixmap(const QPixmap &p); + void reset(unsigned long changed); + +protected: + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + + QBitmap deco; + QPixmap pix; + bool miniBtn; +}; + + +class RedmondDeco : public KCommonDecoration +{ +public: + RedmondDeco(KDecorationBridge *, KDecorationFactory *); + ~RedmondDeco() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + void init(); + +protected: + virtual void reset( unsigned long changed ); + + void paintEvent(QPaintEvent*); + +private: + int titleHeight; +}; + +class RedmondDecoFactory : public QObject, public KDecorationFactory +{ + Q_OBJECT +public: + RedmondDecoFactory(); + virtual ~RedmondDecoFactory(); + virtual KDecoration *createDecoration(KDecorationBridge *); + virtual bool reset(unsigned long); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; +private: + void readConfig(); +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/test/Makefile.am b/kwin/clients/test/Makefile.am new file mode 100644 index 000000000..a5a3fcf67 --- /dev/null +++ b/kwin/clients/test/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_test.la + +kwin3_test_la_SOURCES = test.cpp +kwin3_test_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_test_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +#kwin_keramik_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) + +METASOURCES = AUTO + +kwin_test_lnkdir = $(kde_datadir)/kwin +kwin_test_lnk_DATA = test.desktop + +EXTRA_DIST = $(kwin_test_lnk_DATA) diff --git a/kwin/clients/test/test.cpp b/kwin/clients/test/test.cpp new file mode 100644 index 000000000..abac77e64 --- /dev/null +++ b/kwin/clients/test/test.cpp @@ -0,0 +1,343 @@ +#include "test.h" + +#include <qtooltip.h> +#include <kglobal.h> +#include <kdebug.h> + +namespace KWinTest +{ + +Decoration::Decoration( KDecorationBridge* bridge, KDecorationFactory* factory ) + : KDecoration( bridge, factory ), + button( NULL ) + { + } + +void Decoration::init() + { + createMainWidget(); + widget()->setEraseColor( red ); + widget()->installEventFilter( this ); + if( isCloseable()) + { + button = new QPushButton( widget()); + button->show(); + button->setCursor( arrowCursor ); + button->move( 0, 0 ); + connect( button, SIGNAL( clicked()), SLOT( closeWindow())); + QToolTip::add( button, "Zelva Mana" ); + } + } + +Decoration::MousePosition Decoration::mousePosition( const QPoint& p ) const + { + const int range = 16; + const int border = 4; + + MousePosition m = Nowhere; + + int width = widget()->width(); + int height = widget()->height(); + if ( ( p.x() > border && p.x() < width - border ) + && ( p.y() > border && p.y() < height - border ) ) + return Center; + + if ( p.y() <= range && p.x() <= range) + m = TopLeft2; + else if ( p.y() >= height-range && p.x() >= width-range) + m = BottomRight2; + else if ( p.y() >= height-range && p.x() <= range) + m = BottomLeft2; + else if ( p.y() <= range && p.x() >= width-range) + m = TopRight2; + else if ( p.y() <= border ) + m = Top; + else if ( p.y() >= height-border ) + m = Bottom; + else if ( p.x() <= border ) + m = Left; + else if ( p.x() >= width-border ) + m = Right; + else + m = Center; + return m; + } + +void Decoration::borders( int& left, int& right, int& top, int& bottom ) const + { + if( options()->preferredBorderSize( factory()) == BorderTiny ) + { + left = right = bottom = 1; + top = 5; + } + else + { + left = right = options()->preferredBorderSize( factory()) * 5; + top = options()->preferredBorderSize( factory()) * 10; + bottom = options()->preferredBorderSize( factory()) * 2; + } + if( isShade()) + bottom = 0; + if( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows()) + left = right = 0; + if( ( maximizeMode() & MaximizeVertical ) && !options()->moveResizeMaximizedWindows()) + bottom = 0; + } + +void Decoration::reset( unsigned long ) + { + } + +void Decoration::resize( const QSize& s ) + { + widget()->resize( s ); + } + +QSize Decoration::minimumSize() const + { + return QSize( 100, 50 ); + } + +bool Decoration::eventFilter( QObject* o, QEvent* e ) + { + if( o == widget()) + { + switch( e->type()) + { + case QEvent::MouseButtonPress: + { // FRAME + processMousePressEvent( static_cast< QMouseEvent* >( e )); + return true; + } + case QEvent::Show: + break; + case QEvent::Hide: + break; + default: + break; + } + } + return false; + } + +} +#include <qapplication.h> +#include <qpainter.h> +#include <X11/Xlib.h> +#include <math.h> +#include <unistd.h> +namespace KWinTest +{ + +// taken from riscos +bool Decoration::animateMinimize(bool iconify) +{ + int style = 1; + switch (style) { + + case 1: + { + // Double twisting double back, with pike ;) + + if (!iconify) // No animation for restore. + return true; + + // Go away quick. + helperShowHide( false ); + qApp->syncX(); + + QRect r = iconGeometry(); + + if (!r.isValid()) + return true; + + // Algorithm taken from Window Maker (http://www.windowmaker.org) + + int sx = geometry().x(); + int sy = geometry().y(); + int sw = width(); + int sh = height(); + int dx = r.x(); + int dy = r.y(); + int dw = r.width(); + int dh = r.height(); + + double steps = 12; + + double xstep = double((dx-sx)/steps); + double ystep = double((dy-sy)/steps); + double wstep = double((dw-sw)/steps); + double hstep = double((dh-sh)/steps); + + double cx = sx; + double cy = sy; + double cw = sw; + double ch = sh; + + double finalAngle = 3.14159265358979323846; + + double delta = finalAngle / steps; + + QPainter p( workspaceWidget()); + p.setRasterOp(Qt::NotROP); + + for (double angle = 0; ; angle += delta) { + + if (angle > finalAngle) + angle = finalAngle; + + double dx = (cw / 10) - ((cw / 5) * sin(angle)); + double dch = (ch / 2) * cos(angle); + double midy = cy + (ch / 2); + + QPoint p1(int(cx + dx), int(midy - dch)); + QPoint p2(int(cx + cw - dx), p1.y()); + QPoint p3(int(cx + dw + dx), int(midy + dch)); + QPoint p4(int(cx - dx), p3.y()); + + grabXServer(); + + p.drawLine(p1, p2); + p.drawLine(p2, p3); + p.drawLine(p3, p4); + p.drawLine(p4, p1); + + p.flush(); + + usleep(500); + + p.drawLine(p1, p2); + p.drawLine(p2, p3); + p.drawLine(p3, p4); + p.drawLine(p4, p1); + + ungrabXServer(); + +// FRAME qApp->processEvents(); // FRAME ??? + + cx += xstep; + cy += ystep; + cw += wstep; + ch += hstep; + + if (angle >= finalAngle) + break; + } + } + break; + + case 2: + { + // KVirc style ? Maybe. For qwertz. + + if (!iconify) // No animation for restore. + return true; + + // Go away quick. + helperShowHide( false ); + + qApp->syncX(); + + int stepCount = 12; + + QRect r(geometry()); + + int dx = r.width() / (stepCount * 2); + int dy = r.height() / (stepCount * 2); + + QPainter p( workspaceWidget()); + p.setRasterOp(Qt::NotROP); + + for (int step = 0; step < stepCount; step++) { + + r.moveBy(dx, dy); + r.setWidth(r.width() - 2 * dx); + r.setHeight(r.height() - 2 * dy); + + grabXServer(); + + p.drawRect(r); + p.flush(); + usleep(200); + p.drawRect(r); + + ungrabXServer(); + +// FRAME qApp->processEvents(); + } + } + break; + + + default: + { + QRect icongeom = iconGeometry(); + + if (!icongeom.isValid()) + return true; + + QRect wingeom = geometry(); + + QPainter p( workspaceWidget()); + + p.setRasterOp(Qt::NotROP); + +#if 0 + if (iconify) + p.setClipRegion( + QRegion( workspaceWidget()->rect()) - wingeom + ); +#endif + + grabXServer(); + + p.drawLine(wingeom.bottomRight(), icongeom.bottomRight()); + p.drawLine(wingeom.bottomLeft(), icongeom.bottomLeft()); + p.drawLine(wingeom.topLeft(), icongeom.topLeft()); + p.drawLine(wingeom.topRight(), icongeom.topRight()); + + p.flush(); + + qApp->syncX(); + + usleep(30000); + + p.drawLine(wingeom.bottomRight(), icongeom.bottomRight()); + p.drawLine(wingeom.bottomLeft(), icongeom.bottomLeft()); + p.drawLine(wingeom.topLeft(), icongeom.topLeft()); + p.drawLine(wingeom.topRight(), icongeom.topRight()); + + ungrabXServer(); + } + break; + } + return true; +} + +KDecoration* Factory::createDecoration( KDecorationBridge* bridge ) + { + NET::WindowType type = windowType( SUPPORTED_WINDOW_TYPES_MASK, bridge ); + if( type == NET::Dialog ) + ; + return new Decoration( bridge, this ); + } + +bool Factory::reset( unsigned long changed ) + { + resetDecorations( changed ); + return false; + } + +} // namespace + +extern "C" +{ + +KDE_EXPORT KDecorationFactory *create_factory() + { + return new KWinTest::Factory(); + } + +} + +#include "test.moc" diff --git a/kwin/clients/test/test.desktop b/kwin/clients/test/test.desktop new file mode 100644 index 000000000..ed340f573 --- /dev/null +++ b/kwin/clients/test/test.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=KWin test +Name[af]=KWin toets +Name[ar]=اختبار KWin +Name[az]=KWin sınağı +Name[be]=ТÑÑÑ‚ KWin +Name[bn]=Kwin পরীকà§à¦·à¦¾ +Name[ca]=Test de KWin +Name[csb]=Test KWin +Name[cy]=arbrawf KWin +Name[da]=KWin-test +Name[de]=KWin-Test +Name[el]=KWin τεστ +Name[eo]=Testo de KDE-fenestroadministrilo +Name[es]=Prueba de KWin +Name[eu]=KWin froga +Name[fa]=آزمون KWin +Name[fi]=KWin-testi +Name[fr]=Test de KWin +Name[ga]=Tástáil KWim +Name[gl]=Proba de KWin +Name[he]=× ×™×¡×™×•×Ÿ KWin +Name[hi]=के-विन जांच +Name[hr]=KWina proba +Name[hu]=KWin-teszt +Name[is]=KWin prófun +Name[it]=Prova KWin +Name[ja]=KWin テスト +Name[ka]=KWin შემáƒáƒ¬áƒ›áƒ”ბრ+Name[kk]=KWin Ñынауы +Name[km]=សាកល្បង KWin +Name[ko]=KWin 테스트 +Name[lt]=KWin patikrinimas +Name[lv]=KWin tests +Name[mk]=ТеÑÑ‚ за KWin +Name[mn]=KWin шалгалт +Name[ms]=Ujian KWin +Name[nds]=KWin-Test +Name[ne]=के विन परीकà¥à¤·à¤£ +Name[nn]=KWin-test +Name[pa]=KWin ਜਾਂਚ +Name[pl]=Test KWin +Name[pt]=Teste do KWin +Name[pt_BR]=Teste do KWin +Name[ro]=Test KWin +Name[ru]=Проверка KWin +Name[rw]= Isuzuma rya KWin +Name[se]=KWin-geahÄÄaleapmi +Name[sk]=Test KWin +Name[sl]=Preizkus KWin +Name[sr]=Проба KWin-а +Name[sr@Latn]=Proba KWin-a +Name[sv]=Kwin-test +Name[ta]=KWin சோதனை +Name[te]=కెవినౠపరికà±à°· +Name[tg]=Тафтиши KWin +Name[th]=ทดสà¸à¸š KWin +Name[tr]=KWin Testi +Name[tt]=KWin sınaw +Name[uk]=ТеÑÑ‚ KWin +Name[uz]=KWin sinash +Name[uz@cyrillic]=KWin Ñинаш +Name[vi]=Thá» KWin +Name[wa]=Saye KPurnea +Name[zh_CN]=KWin 测试 +Name[zh_TW]=KWin 測試 +X-KDE-Library=kwin3_test diff --git a/kwin/clients/test/test.h b/kwin/clients/test/test.h new file mode 100644 index 000000000..56e21dbc0 --- /dev/null +++ b/kwin/clients/test/test.h @@ -0,0 +1,49 @@ +#ifndef KWIN_TEST +#define KWIN_TEST + +#include <kdecoration.h> +#include <kdecorationfactory.h> +#include <qpushbutton.h> + +namespace KWinTest +{ + +const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask; + +class Decoration + : public KDecoration + { + Q_OBJECT + public: + Decoration( KDecorationBridge* bridge, KDecorationFactory* factory ); + virtual void init(); + virtual MousePosition mousePosition( const QPoint& p ) const; + virtual void borders( int& left, int& right, int& top, int& bottom ) const; + virtual void resize( const QSize& s ); + virtual QSize minimumSize() const; + virtual void activeChange() {}; + virtual void captionChange() {}; + virtual void maximizeChange() {}; + virtual void desktopChange() {}; + virtual void shadeChange() {}; + virtual void iconChange() {}; + virtual bool eventFilter( QObject* o, QEvent* e ); + virtual void reset( unsigned long changed ); + virtual bool animateMinimize( bool minimize ); + private: + QPushButton* button; + }; + +class Factory + : public KDecorationFactory + { + public: + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + }; + +} // namespace + +#endif diff --git a/kwin/clients/web/Makefile.am b/kwin/clients/web/Makefile.am new file mode 100644 index 000000000..3619301f4 --- /dev/null +++ b/kwin/clients/web/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) +kde_module_LTLIBRARIES = kwin3_web.la + +kwin3_web_la_SOURCES = Web.cpp WebButton.cpp + +noinst_HEADERS = Web.h WebButton.h + +kwin3_web_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_web_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +METASOURCES = AUTO + +linkdir = $(kde_datadir)/kwin/ +link_DATA = web.desktop +EXTRA_DIST = $(link_DATA) + diff --git a/kwin/clients/web/Web.cpp b/kwin/clients/web/Web.cpp new file mode 100644 index 000000000..7d63b7d72 --- /dev/null +++ b/kwin/clients/web/Web.cpp @@ -0,0 +1,385 @@ +/* + 'Web' kwin client + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qpainter.h> + +#include <kconfig.h> + +#include "Web.h" +#include "WebButton.h" + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + return new Web::WebFactory(); + } +} + +namespace Web { + +WebClient::WebClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration(bridge, factory) +{ + // Empty. +} + +WebClient::~WebClient() +{ + // Empty. +} + +QString WebClient::visibleName() const +{ + return i18n("Web"); +} + +QString WebClient::defaultButtonsLeft() const +{ + return "S"; +} + +QString WebClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool WebClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int WebClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ +// bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + return borderSize_; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + case LM_TitleEdgeTop: + case LM_TitleEdgeBottom: + return 0; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 0; + + case LM_TitleHeight: + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight_; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 0; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *WebClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new WebButton(MenuButton, this, "menu", shape_); + + case OnAllDesktopsButton: + return new WebButton(OnAllDesktopsButton, this, "on_all_desktops", shape_); + + case HelpButton: + return new WebButton(HelpButton, this, "help", shape_); + + case MinButton: + return new WebButton(MinButton, this, "minimize", shape_); + + case MaxButton: + return new WebButton(MaxButton, this, "maximize", shape_); + + case CloseButton: + return new WebButton(CloseButton, this, "close", shape_); + + case AboveButton: + return new WebButton(AboveButton, this, "above", shape_); + + case BelowButton: + return new WebButton(BelowButton, this, "below", shape_); + + case ShadeButton: + return new WebButton(ShadeButton, this, "shade", shape_); + + default: + return 0; + } +} + + void +WebClient::init() +{ + // title height + const int textVMargin = 2; + QFontMetrics fm(options()->font(isActive(), isToolWindow())); + + // border size + switch(options()->preferredBorderSize( factory())) { + case BorderLarge: + borderSize_ = 8; + break; + case BorderVeryLarge: + borderSize_ = 12; + break; + case BorderHuge: + borderSize_ = 18; + break; + case BorderVeryHuge: + borderSize_ = 27; + break; + case BorderOversized: + borderSize_ = 40; + break; + case BorderNormal: + default: + borderSize_ = 4; + } + titleHeight_ = QMAX(QMAX(14, fm.height() + textVMargin * 2), borderSize_); + if (0 != titleHeight_ % 2) + titleHeight_ += 1; + + KConfig c("kwinwebrc"); + c.setGroup("General"); + shape_ = c.readBoolEntry("Shape", true); + + KCommonDecoration::init(); +} + + void +WebClient::reset( unsigned long changed ) +{ + if (changed & SettingColors) + { + // repaint the whole thing + widget()->repaint(false); + } else if (changed & SettingFont) { + // font has changed -- update title height + // title height + const int textVMargin = 2; + QFontMetrics fm(options()->font(isActive(), isToolWindow())); + titleHeight_ = QMAX(QMAX(14, fm.height() + textVMargin * 2), borderSize_); + if (0 != titleHeight_ % 2) + titleHeight_ += 1; + + widget()->repaint(false); + } + + KCommonDecoration::reset(changed); +} + + void +WebClient::paintEvent(QPaintEvent * pe) +{ + int r_x, r_y, r_x2, r_y2; + widget()->rect().coords(&r_x, &r_y, &r_x2, &r_y2); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int ttlHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottomBottom = r_y+titleEdgeTop+ttlHeight+titleEdgeBottom-1; + QRect titleRect = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + titleRect.setTop(1); + + QPainter p(widget()); + + p.setPen(Qt::black); + p.setBrush(options()->colorGroup(ColorFrame, isActive()).background()); + + p.setClipRegion(pe->region() - titleRect); + + p.drawRect(widget()->rect()); + + p.setClipRegion(pe->region()); + + p.fillRect(titleRect, options()->color(ColorTitleBar, isActive())); + + if (shape_) + { + int r(width()); + int b(height()); + + // Draw edge of top-left corner inside the area removed by the mask. + + p.drawPoint(3, 1); + p.drawPoint(4, 1); + p.drawPoint(2, 2); + p.drawPoint(1, 3); + p.drawPoint(1, 4); + + // Draw edge of top-right corner inside the area removed by the mask. + + p.drawPoint(r - 5, 1); + p.drawPoint(r - 4, 1); + p.drawPoint(r - 3, 2); + p.drawPoint(r - 2, 3); + p.drawPoint(r - 2, 4); + + // Draw edge of bottom-left corner inside the area removed by the mask. + + p.drawPoint(1, b - 5); + p.drawPoint(1, b - 4); + p.drawPoint(2, b - 3); + p.drawPoint(3, b - 2); + p.drawPoint(4, b - 2); + + // Draw edge of bottom-right corner inside the area removed by the mask. + + p.drawPoint(r - 2, b - 5); + p.drawPoint(r - 2, b - 4); + p.drawPoint(r - 3, b - 3); + p.drawPoint(r - 4, b - 2); + p.drawPoint(r - 5, b - 2); + } + + p.setFont(options()->font(isActive(), isToolWindow())); + + p.setPen(options()->color(ColorFont, isActive())); + + p.drawText(titleRect, AlignCenter, caption()); +} + +void WebClient::updateWindowShape() +{ + if (!shape_) + return; + + QRegion mask(0, 0, width(), height()); + + int r(width()); + int b(height()); + + // Remove top-left corner. + + mask -= QRegion(0, 0, 5, 1); + mask -= QRegion(0, 1, 3, 1); + mask -= QRegion(0, 2, 2, 1); + mask -= QRegion(0, 3, 1, 2); + + // Remove top-right corner. + + mask -= QRegion(r - 5, 0, 5, 1); + mask -= QRegion(r - 3, 1, 3, 1); + mask -= QRegion(r - 2, 2, 2, 1); + mask -= QRegion(r - 1, 3, 1, 2); + + // Remove bottom-left corner. + + mask -= QRegion(0, b - 5, 1, 3); + mask -= QRegion(0, b - 3, 2, 1); + mask -= QRegion(0, b - 2, 3, 1); + mask -= QRegion(0, b - 1, 5, 1); + + // Remove bottom-right corner. + + mask -= QRegion(r - 5, b - 1, 5, 1); + mask -= QRegion(r - 3, b - 2, 3, 1); + mask -= QRegion(r - 2, b - 3, 2, 1); + mask -= QRegion(r - 1, b - 5, 1, 2); + + setMask(mask); +} + +KDecoration* WebFactory::createDecoration( KDecorationBridge* b ) +{ + return(new WebClient(b, this)); +} + +bool WebFactory::reset(unsigned long changed) +{ + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + if (changed & SettingColors || changed & SettingFont) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool WebFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonMenu: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +QValueList< WebFactory::BorderSize > WebFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +} + +#include "Web.moc" +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/Web.h b/kwin/clients/web/Web.h new file mode 100644 index 000000000..183829e8d --- /dev/null +++ b/kwin/clients/web/Web.h @@ -0,0 +1,87 @@ +/* + 'Web' kwin client + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KWIN_WEB_H +#define KWIN_WEB_H + +#include "../../lib/kcommondecoration.h" +#include "../../lib/kdecorationfactory.h" + +class QLabel; +class QSpacerItem; +class QBoxLayout; + +namespace Web +{ + + class WebButton; + + class WebClient : public KCommonDecoration + { + public: + + WebClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~WebClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void updateWindowShape(); + + virtual void init(); + + protected: + virtual void reset( unsigned long changed ); + + virtual void paintEvent(QPaintEvent *); + + private: + + int titleHeight_, borderSize_; + + bool shape_; + + QBitmap _buttonBitmap(ButtonType t) const; + }; + + class WebFactory : public QObject, public KDecorationFactory + { + Q_OBJECT + + public: + + WebFactory() {}; + virtual ~WebFactory() {}; + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; + }; +} + +#endif +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/WebButton.cpp b/kwin/clients/web/WebButton.cpp new file mode 100644 index 000000000..66bedc19f --- /dev/null +++ b/kwin/clients/web/WebButton.cpp @@ -0,0 +1,287 @@ +/* + 'Web' kwin client + + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qpainter.h> + +#include "WebButton.h" +#include "Web.h" + +namespace Web { + + static unsigned char close_bits[] = { + 0x42, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0x42 + }; + static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x3c, 0x18, 0x00 + }; + static unsigned char maximize_bits[] = { + 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00 + }; + static unsigned char unmaximize_bits[] = { + 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f + }; + static unsigned char sticky_bits[] = { + 0x20, 0x70, 0xfa, 0x7e, 0x3c, 0x1c, 0x32, 0x01 + }; + static unsigned char unsticky_bits[] = { + 0x1c, 0x1c, 0x1c, 0x3e, 0x7f, 0x08, 0x08, 0x08 + }; + static unsigned char help_bits[] = { + 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x3c + }; + static unsigned char shade_on_bits[] = { + 0xff, 0xff, 0x81, 0x81, 0x99, 0xbd, 0x81, 0xff + }; + static unsigned char shade_off_bits[] = { + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static unsigned char above_on_bits[] = { + 0xff, 0x7e, 0x3c, 0x18, 0x00, 0xff, 0xff, 0x00 + }; + static unsigned char above_off_bits[] = { + 0x18, 0x3c, 0x7e, 0xff, 0x00, 0xff, 0xff, 0x00 + }; + static unsigned char below_on_bits[] = { + 0x00, 0xff, 0xff, 0x00, 0x18, 0x3c, 0x7e, 0xff + }; + static unsigned char below_off_bits[] = { + 0x00, 0xff, 0xff, 0x00, 0xff, 0x7e, 0x3c, 0x18 + }; + static unsigned char menu_bits[] = { + 0xff, 0x81, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff + }; + +WebButton::WebButton(ButtonType type, WebClient *parent, const char *name, bool shape) + : KCommonDecorationButton (type, parent, name), + mouseOver_ (false), + shape_ (shape), + deco_ (parent) +{ + setBackgroundMode(NoBackground); +} + +WebButton::~WebButton() +{ + // Empty. +} + +void WebButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(help_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? unmaximize_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + case MenuButton: + setBitmap(menu_bits); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + void +WebButton::enterEvent(QEvent * e) +{ + mouseOver_ = true; + repaint(); + QButton::enterEvent(e); +} + + void +WebButton::leaveEvent(QEvent * e) +{ + mouseOver_ = false; + repaint(); + QButton::leaveEvent(e); +} + + void +WebButton::drawButton(QPainter *p) +{ + QPen highlightPen; + + if (isDown() ) + highlightPen = QPen(colorGroup().light()); + + else + { + if (mouseOver_) + highlightPen = QPen(colorGroup().highlight()); + else + highlightPen = QPen(NoPen); + } + + p->fillRect(rect(), colorGroup().background()); + + Position position_; + if (0 == mapToParent(rect().topLeft() ).x() ) + position_ = Left; + else if (deco_->width()-1 == mapToParent(rect().topRight() ).x() ) + position_ = Right; + else + position_ = Mid; + switch ( position_ ) + { + case Left: + { + // Draw edge. + + p->setPen(Qt::black); + + p->drawLine(0, 0, width(), 0); + p->drawLine(0, 1, 0, height() - 1); + if (shape_) + { + p->drawPoint(3, 1); + p->drawPoint(4, 1); + p->drawPoint(2, 2); + p->drawPoint(1, 3); + p->drawPoint(1, 4); + } + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + if (shape_) + p->setClipRegion(QRegion(rect()) - QRect(0, 0, 6, 6)); + + p->drawRect(2, 2, width() - 4, height() - 4); + if (shape_) + { + p->setClipRect(rect()); + p->drawPoint(4, 3); + p->drawPoint(5, 3); + p->drawPoint(3, 4); + p->drawPoint(3, 5); + } + } + + break; + + case Right: + { + // Draw edge. + + p->setPen(Qt::black); + p->drawLine(0, 0, width(), 0); + p->drawLine(width() - 1, 1, width() - 1, height() - 1); + if (shape_) + { + p->drawPoint(width() - 5, 1); + p->drawPoint(width() - 4, 1); + p->drawPoint(width() - 3, 2); + p->drawPoint(width() - 2, 3); + p->drawPoint(width() - 2, 4); + } + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + if (shape_) + p->setClipRegion(QRegion(rect()) - QRect(width() - 6, 0, 6, 6)); + + p->drawRect(2, 2, width() - 4, height() - 4); + if (shape_) + { + p->setClipRect(rect()); + p->drawPoint(width() - 5, 3); + p->drawPoint(width() - 6, 3); + p->drawPoint(width() - 4, 4); + p->drawPoint(width() - 4, 5); + } + } + + break; + + case Mid: + default: + { + // Draw edge. + + p->setPen(Qt::black); + p->drawLine(0, 0, width(), 0); + + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + p->drawRect(2, 2, width() - 4, height() - 4); + } + + break; + } + + // Draw icon. + + QPoint center(rect().center()); + + int bwby2(bitmap_.width() / 2); // Bitmap Width BY 2 + int bhby2(bitmap_.height() / 2); // Bitmap Height BY 2 + + p->setBrush(NoBrush); + p->setPen(Qt::black); + + p->drawPixmap(center.x() - bwby2 + 1, center.y() - bhby2 + 1, bitmap_); +} + + void +WebButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + bitmap_ = QBitmap(8,8, bitmap, true); + else + bitmap_ = QBitmap(8,8); + bitmap_.setMask(bitmap_); +} + +} + +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/WebButton.h b/kwin/clients/web/WebButton.h new file mode 100644 index 000000000..6c1530f42 --- /dev/null +++ b/kwin/clients/web/WebButton.h @@ -0,0 +1,70 @@ +/* + 'Web' kwin client + + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KWIN_WEB_BUTTON_H +#define KWIN_WEB_BUTTON_H + +#include <qwidget.h> +#include <qbitmap.h> +#include <klocale.h> + +#include "../../lib/kcommondecoration.h" + +namespace Web +{ + class WebClient; + + class WebButton : public KCommonDecorationButton + { + public: + + enum Position + { + Left, Mid, Right + }; + + WebButton(ButtonType type, WebClient *parent, const char *name, bool shape); + + virtual ~WebButton(); + + virtual void reset(unsigned long changed); + + protected: + void setBitmap(const unsigned char *bitmap); + + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void drawButton(QPainter *p); + + private: + QBitmap bitmap_; + + bool mouseOver_; + + bool shape_; + WebClient* deco_; + }; +} + +#endif + +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/web.desktop b/kwin/clients/web/web.desktop new file mode 100644 index 000000000..12ced34f4 --- /dev/null +++ b/kwin/clients/web/web.desktop @@ -0,0 +1,47 @@ +[Desktop Entry] +Name=Web +Name[ar]=الشبكة +Name[az]=Veb +Name[be]=Сеціва +Name[bn]=ওয়েব +Name[br]=Gwiad +Name[csb]=Séc +Name[cy]=Gwe +Name[da]=Net +Name[el]=Ιστός +Name[eo]=TTT +Name[et]=Veeb +Name[fa]=وب +Name[ga]=Gréasán +Name[hi]=वेब +Name[hr]=Internet +Name[is]=Vefur +Name[km]=បណ្ដាញ +Name[lo]=à»àºšàºšà»€àº§à»Šàºš +Name[lv]=TÄ«mekļa +Name[mk]=Веб +Name[mn]=Веб +Name[nb]=Nett +Name[ne]=वेब +Name[nn]=Vev +Name[pa]=ਵੈੱਬ +Name[pl]=Sieć +Name[rw]=Urubugamakuru +Name[se]=Fierpmádat +Name[sl]=Splet +Name[sr]=Веб +Name[sr@Latn]=Veb +Name[sv]=Webb +Name[ta]=வலை +Name[te]=వెబౠ+Name[tg]=Ð’Ñб +Name[th]=à¹à¸šà¸šà¹€à¸§à¹‡à¸š +Name[uk]=Тенета +Name[uz]=Veb +Name[uz@cyrillic]=Веб +Name[ven]=Webu +Name[vi]=Mạng +Name[wa]=Waibe +Name[zh_TW]=網é +Name[zu]=I-Web +X-KDE-Library=kwin3_web diff --git a/kwin/cr16-app-kwin.png b/kwin/cr16-app-kwin.png Binary files differnew file mode 100644 index 000000000..8ca1d67d6 --- /dev/null +++ b/kwin/cr16-app-kwin.png diff --git a/kwin/cr32-app-kwin.png b/kwin/cr32-app-kwin.png Binary files differnew file mode 100644 index 000000000..e6f5ac0d8 --- /dev/null +++ b/kwin/cr32-app-kwin.png diff --git a/kwin/cr48-app-kwin.png b/kwin/cr48-app-kwin.png Binary files differnew file mode 100644 index 000000000..03c44b313 --- /dev/null +++ b/kwin/cr48-app-kwin.png diff --git a/kwin/data/Makefile.am b/kwin/data/Makefile.am new file mode 100644 index 000000000..abdc244f2 --- /dev/null +++ b/kwin/data/Makefile.am @@ -0,0 +1,25 @@ +kconf_PROGRAMS = kwin_update_window_settings kwin_update_default_rules +kconfdir = $(libdir)/kconf_update_bin + +kwin_update_window_settings_SOURCES = update_window_settings.cpp +kwin_update_window_settings_LDADD = $(LIB_KDECORE) $(KDE_RPATH) +kwin_update_window_settings_LDFLAGS = $(all_libraries) + +kwin_update_default_rules_SOURCES = update_default_rules.cpp +kwin_update_default_rules_LDADD = $(LIB_KDECORE) $(KDE_RPATH) +kwin_update_default_rules_LDFLAGS = $(all_libraries) + +INCLUDES = $(all_includes) + +METASOURCES = AUTO + +sounddir= $(kde_sounddir) +sound_DATA = pop.wav + +update_DATA = kwin.upd kwinsticky.upd kwiniconify.upd kwin3_plugin.upd kwin_focus1.upd \ + kwinupdatewindowsettings.upd kwin_focus2.upd kwin_fsp_workarounds_1.upd +update_SCRIPTS = pluginlibFix.pl kwin3_plugin.pl kwin_focus1.sh kwin_focus2.sh +updatedir = $(kde_datadir)/kconf_update + +kwin_default_rules_DATA = fsp_workarounds_1.kwinrules +kwin_default_rulesdir = $(kde_datadir)/kwin/default_rules diff --git a/kwin/data/fsp_workarounds_1.kwinrules b/kwin/data/fsp_workarounds_1.kwinrules new file mode 100644 index 000000000..11aa0b563 --- /dev/null +++ b/kwin/data/fsp_workarounds_1.kwinrules @@ -0,0 +1,41 @@ +[1] +Description=(Default) Disable focus stealing prevention for XV +Description[bg]=(По подразбиране) Изключване открадването на фокуÑа за XV +Description[ca]=(Omissió) Desactiva la prevenció del robatori de focus pel XV +Description[da]=(Standard) Deaktivér forhindring af at stjæle fokus for XV +Description[de]=(Voreinstellung) Vorbeugung gegen Fokusverlust für XV +Description[el]=(Î ÏοκαθοÏισμÎνο) ΑπενεÏγοποίηση αποτÏοπής κλεψίματος εστίασης για το XV +Description[eo]=(DefaÅlta) Malebligu la preventon de fokus-elprenado por XV +Description[es]=(Predefinido) Deshabilitar la prevención de robo del foco para XV +Description[et]=(Vaikimisi) keelatakse XV fookuse röövimise vältimine +Description[fr]=(Défaut) Désactive le vol du focus pour XV +Description[fy]=(standert) Foarkomme fan ôfpakke fan toetseboerdfokus foar XV +Description[hr]=(Zadano) Onemogući spreÄavanje preuzimanja fokusa za XV +Description[hu]=(Alapértelmezés) A fókuszelvételi védelem kikapcsolása az XV-nél +Description[is]=(Sjálfgefið) Aftengja vörn gegn fókusþjófnaði fyrir XV +Description[it]=(Predefinita) Disabilita la prevenzione della cattura del fuoco per XV +Description[ja]=(デフォルト) XV ã®ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ç§»å‹•é˜²æ¢ã‚’無効ã«ã™ã‚‹ +Description[km]=(លំនាំដើម) បិទ​ការ​ផ្ដោážáž¢áž¶ážšáž˜áŸ’មណ០ដោយ​លួច​ការពារ​សម្រាប់ XV +Description[ko]=(기본값) XVì˜ ì´ˆì ëºì•„가기 막기 사용하지 않기 +Description[nb]=(Standard) SlÃ¥ av fokusoverstyring fra XV +Description[nds]=(Standard) För XV de Fokusverleer-Schuul utmaken +Description[nl]=(Standaard) Voorkomen van afpakken toetsenbordfocus voor XV +Description[pl]=(DomyÅ›lne) wyÅ‚Ä…czenie przechwytywania aktywnoÅ›ci dla XV +Description[pt]=(Predefinido) Desactivar a prevenção de roubo do primeiro plano do XV +Description[pt_BR]=(Padrão) Desativa prevenção de roubo de foco para o XV +Description[ru]=(по умолчанию) отключить предотвращение потери Ð´Ð»Ñ XV +Description[sk]=(Å tandard) ZnemožniÅ¥ kradnutie aktivity okna XV +Description[sl]=(privzeto) OnemogoÄi prepreÄevanje kraje fokusa za XV +Description[sr]=(Подразумевано) ИÑкључи Ñпречавање преузимања фокуÑа за XV +Description[sr@Latn]=(Podrazumevano) IskljuÄi spreÄavanje preuzimanja fokusa za XV +Description[sv]=(Förval) Inaktivera möjlighet att förhindra stöld av fokus för XV +Description[wa]=(Prémetou) Dismete li hapaedje di focus po XV +Description[zh_TW]=(é è¨ï¼‰ç‚º XV 關閉é¿å…焦點被å·çš„功能 +fsplevel=0 +fsplevelrule=2 +wmclass=^xv .* +wmclasscomplete=true +wmclassmatch=3 + +[General] +count=1 diff --git a/kwin/data/kwin.upd b/kwin/data/kwin.upd new file mode 100644 index 000000000..a987c513b --- /dev/null +++ b/kwin/data/kwin.upd @@ -0,0 +1,14 @@ +Id=kde3.0r1 +File=kwinrc +Options=overwrite +Script=pluginlibFix.pl,perl + +# Xinerama fixups +Id=kde3.2Xinerama +File=kwinrc,kdeglobals +Group=Windows +Key=XineramaEnabled +Key=XineramaMovementEnabled +Key=XineramaPlacementEnabled +Key=XineramaMaximizeEnabled + diff --git a/kwin/data/kwin3_plugin.pl b/kwin/data/kwin3_plugin.pl new file mode 100644 index 000000000..791ea7f33 --- /dev/null +++ b/kwin/data/kwin3_plugin.pl @@ -0,0 +1,8 @@ +#!/usr/bin/perl +foreach (<>) { + if(/^PluginLib=kwin_(.*)$/) { + print "PluginLib=kwin3_$1\n"; + next; + } + print $_; +} diff --git a/kwin/data/kwin3_plugin.upd b/kwin/data/kwin3_plugin.upd new file mode 100644 index 000000000..faf7b7971 --- /dev/null +++ b/kwin/data/kwin3_plugin.upd @@ -0,0 +1,4 @@ +Id=kde3.2 +File=kwinrc +Options=overwrite +Script=kwin3_plugin.pl,perl diff --git a/kwin/data/kwin_focus1.sh b/kwin/data/kwin_focus1.sh new file mode 100644 index 000000000..6ffe0e039 --- /dev/null +++ b/kwin/data/kwin_focus1.sh @@ -0,0 +1,13 @@ +#! /bin/sh +was= +while read line; do + if echo "$line" | grep '^IgnoreFocusStealingClasses=' >/dev/null 2>/dev/null; then + echo "$line" | sed 's/\(^IgnoreFocusStealingClasses=.*$\)/\1,kio_uiserver/' + was=1 + else + echo "$line" + fi +done +if test -z "$was"; then + echo "IgnoreFocusStealingClasses=kio_uiserver" +fi diff --git a/kwin/data/kwin_focus1.upd b/kwin/data/kwin_focus1.upd new file mode 100644 index 000000000..902ac0fad --- /dev/null +++ b/kwin/data/kwin_focus1.upd @@ -0,0 +1,5 @@ +Id=kwin_focus1 +File=kwinrc +Group=Windows +Options=overwrite +Script=kwin_focus1.sh,sh diff --git a/kwin/data/kwin_focus2.sh b/kwin/data/kwin_focus2.sh new file mode 100644 index 000000000..c3d6f9eea --- /dev/null +++ b/kwin/data/kwin_focus2.sh @@ -0,0 +1,8 @@ +#! /bin/sh +while read line; do + if echo "$line" | grep '^IgnoreFocusStealingClasses=' >/dev/null 2>/dev/null; then + echo "$line" | sed 's/,kded//' | sed 's/kded,//' | sed 's/,kget//' | sed 's/kget,//' + else + echo "$line" + fi +done diff --git a/kwin/data/kwin_focus2.upd b/kwin/data/kwin_focus2.upd new file mode 100644 index 000000000..681c2c404 --- /dev/null +++ b/kwin/data/kwin_focus2.upd @@ -0,0 +1,5 @@ +Id=kwin_focus2 +File=kwinrc +Group=Windows +Options=overwrite +Script=kwin_focus2.sh,sh diff --git a/kwin/data/kwin_fsp_workarounds_1.upd b/kwin/data/kwin_fsp_workarounds_1.upd new file mode 100644 index 000000000..2d725d2d2 --- /dev/null +++ b/kwin/data/kwin_fsp_workarounds_1.upd @@ -0,0 +1,8 @@ +Id=kde351 +# the file is intentionally a dummy, as the binary will update kwinrulesrc, +# file kwinrules_update will just remember it has been done +File=kwinrules_update +Group=Dummy +Options=overwrite +ScriptArguments=fsp_workarounds_1.kwinrules +Script=kwin_update_default_rules diff --git a/kwin/data/kwiniconify.upd b/kwin/data/kwiniconify.upd new file mode 100644 index 000000000..1946a1a91 --- /dev/null +++ b/kwin/data/kwiniconify.upd @@ -0,0 +1,8 @@ +Id=iconifyupd3.1 +File=kwin.eventsrc +Group=iconify,minimize +AllKeys +Group=deiconify,unminimize +AllKeys +RemoveGroup=iconify +RemoveGroup=deiconify diff --git a/kwin/data/kwinsticky.upd b/kwin/data/kwinsticky.upd new file mode 100644 index 000000000..3dc37ba47 --- /dev/null +++ b/kwin/data/kwinsticky.upd @@ -0,0 +1,8 @@ +Id=stickyupd3.1 +File=kwin.eventsrc +Group=sticky,on_all_desktops +AllKeys +Group=unsticky,not_on_all_desktops +AllKeys +RemoveGroup=sticky +RemoveGroup=unsticky diff --git a/kwin/data/kwinupdatewindowsettings.upd b/kwin/data/kwinupdatewindowsettings.upd new file mode 100644 index 000000000..4e5e6f28a --- /dev/null +++ b/kwin/data/kwinupdatewindowsettings.upd @@ -0,0 +1,7 @@ +Id=kde33b1 +# the file is intentionally a dummy, as the binary will update kwinrc and kwinrulesrc +# the kwin_update will just remember it has been done +File=kwin_update +Group=Dummy +Options=overwrite +Script=kwin_update_window_settings diff --git a/kwin/data/pluginlibFix.pl b/kwin/data/pluginlibFix.pl new file mode 100755 index 000000000..cb3859aeb --- /dev/null +++ b/kwin/data/pluginlibFix.pl @@ -0,0 +1,8 @@ +#!/usr/bin/perl +foreach (<>) { + if(/^PluginLib=libkwin(.*)$/) { + print "PluginLib=kwin_$1\n"; + next; + } + print $_; +} diff --git a/kwin/data/pop.wav b/kwin/data/pop.wav Binary files differnew file mode 100644 index 000000000..adf5c6c21 --- /dev/null +++ b/kwin/data/pop.wav diff --git a/kwin/data/update_default_rules.cpp b/kwin/data/update_default_rules.cpp new file mode 100644 index 000000000..78adc6164 --- /dev/null +++ b/kwin/data/update_default_rules.cpp @@ -0,0 +1,56 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +// read addtional window rules and add them to kwinrulesrc + +#include <dcopclient.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kinstance.h> +#include <kstandarddirs.h> + +int main( int argc, char* argv[] ) + { + if( argc != 2 ) + return 1; + KInstance inst( "kwin_update_default_rules" ); + QString file = locate( "data", QString( "kwin/default_rules/" ) + argv[ 1 ] ); + if( file.isEmpty()) + { + kdWarning() << "File " << argv[ 1 ] << " not found!" << endl; + return 1; + } + KConfig src_cfg( file ); + KConfig dest_cfg( "kwinrulesrc" ); + src_cfg.setGroup( "General" ); + dest_cfg.setGroup( "General" ); + int count = src_cfg.readNumEntry( "count", 0 ); + int pos = dest_cfg.readNumEntry( "count", 0 ); + for( int group = 1; + group <= count; + ++group ) + { + QMap< QString, QString > entries = src_cfg.entryMap( QString::number( group )); + ++pos; + dest_cfg.deleteGroup( QString::number( pos )); + dest_cfg.setGroup( QString::number( pos )); + for( QMap< QString, QString >::ConstIterator it = entries.begin(); + it != entries.end(); + ++it ) + dest_cfg.writeEntry( it.key(), *it ); + } + dest_cfg.setGroup( "General" ); + dest_cfg.writeEntry( "count", pos ); + src_cfg.sync(); + dest_cfg.sync(); + DCOPClient client; + client.attach(); + client.send("kwin*", "", "reconfigure()", ""); + } diff --git a/kwin/data/update_window_settings.cpp b/kwin/data/update_window_settings.cpp new file mode 100644 index 000000000..369c1e4ea --- /dev/null +++ b/kwin/data/update_window_settings.cpp @@ -0,0 +1,168 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +// updates per-window settings from KDE3.2 to KDE3.3 + +#include <netwm_def.h> +#include <kconfig.h> +#include <kinstance.h> +#include <dcopclient.h> + +struct SessionInfo + { + QCString sessionId; + QCString windowRole; + QCString wmCommand; + QCString wmClientMachine; + QCString resourceName; + QCString resourceClass; + + QRect geometry; + QRect restore; + QRect fsrestore; + int maximized; + int fullscreen; + int desktop; + bool minimized; + bool onAllDesktops; + bool shaded; + bool keepAbove; + bool keepBelow; + bool skipTaskbar; + bool skipPager; + bool userNoBorder; + NET::WindowType windowType; + bool active; // means 'was active in the saved session', not used otherwise + bool fake; // fake session, i.e. 'save window settings', not SM restored + }; + +QPtrList<SessionInfo> fakeSession; + +static const char* const window_type_names[] = + { + "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", + "Override", "TopMenu", "Utility", "Splash" + }; + // change also the two functions below when adding new entries + +NET::WindowType txtToWindowType( const char* txt ) + { + for( int i = NET::Unknown; + i <= NET::Splash; + ++i ) + if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1 + return static_cast< NET::WindowType >( i ); + return static_cast< NET::WindowType >( -2 ); // undefined + } + +void loadFakeSessionInfo( KConfig* config ) + { + fakeSession.clear(); + config->setGroup("FakeSession" ); + int count = config->readNumEntry( "count" ); + for ( int i = 1; i <= count; i++ ) + { + QString n = QString::number(i); + SessionInfo* info = new SessionInfo; + fakeSession.append( info ); + info->windowRole = config->readEntry( QString("windowRole")+n ).latin1(); + info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); + info->resourceClass = config->readEntry( QString("resourceClass")+n ).lower().latin1(); + info->wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1(); + info->geometry = config->readRectEntry( QString("geometry")+n ); + info->restore = config->readRectEntry( QString("restore")+n ); + info->fsrestore = config->readRectEntry( QString("fsrestore")+n ); + info->maximized = config->readNumEntry( QString("maximize")+n, 0 ); + info->fullscreen = config->readNumEntry( QString("fullscreen")+n, 0 ); + info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); + info->minimized = config->readBoolEntry( QString("iconified")+n, FALSE ); + info->onAllDesktops = config->readBoolEntry( QString("sticky")+n, FALSE ); + info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); + info->keepAbove = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); + info->keepBelow = config->readBoolEntry( QString("keepBelow")+n, FALSE ); + info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); + info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); + info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); + info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); + info->active = false; + info->fake = true; + } + config->deleteGroup( "FakeSession" ); + } + +void writeRules( KConfig& cfg ) + { + cfg.setGroup( "General" ); + int pos = cfg.readNumEntry( "count" ); + for ( SessionInfo* info = fakeSession.first(); info; info = fakeSession.next() ) + { + if( info->resourceName.isEmpty() && info->resourceClass.isEmpty()) + continue; + ++pos; + cfg.setGroup( QString::number( pos )); + cfg.writeEntry( "description", ( const char* ) ( info->resourceClass + " (KDE3.2)" )); + cfg.writeEntry( "wmclass", ( const char* )( info->resourceName + ' ' + info->resourceClass )); + cfg.writeEntry( "wmclasscomplete", true ); + cfg.writeEntry( "wmclassmatch", 1 ); // 1 == exact match + if( !info->windowRole.isEmpty()) + { + cfg.writeEntry( "windowrole", ( const char* ) info->windowRole ); + cfg.writeEntry( "windowrolematch", 1 ); + } + if( info->windowType == static_cast< NET::WindowType >( -2 )) // undefined + {} // all types + if( info->windowType == NET::Unknown ) + cfg.writeEntry( "types", NET::NormalMask ); + else + cfg.writeEntry( "types", 1 << info->windowType ); + cfg.writeEntry( "position", info->geometry.topLeft()); + cfg.writeEntry( "positionrule", 4 ); // 4 == remember + cfg.writeEntry( "size", info->geometry.size()); + cfg.writeEntry( "sizerule", 4 ); + cfg.writeEntry( "maximizevert", info->maximized & NET::MaxVert ); + cfg.writeEntry( "maximizevertrule", 4 ); + cfg.writeEntry( "maximizehoriz", info->maximized & NET::MaxHoriz ); + cfg.writeEntry( "maximizehorizrule", 4 ); + cfg.writeEntry( "fullscreen", info->fullscreen ); + cfg.writeEntry( "fullscreenrule", 4 ); + cfg.writeEntry( "desktop", info->desktop ); + cfg.writeEntry( "desktoprule", 4 ); + cfg.writeEntry( "minimize", info->minimized ); + cfg.writeEntry( "minimizerule", 4 ); + cfg.writeEntry( "shade", info->shaded ); + cfg.writeEntry( "shaderule", 4 ); + cfg.writeEntry( "above", info->keepAbove ); + cfg.writeEntry( "aboverule", 4 ); + cfg.writeEntry( "below", info->keepBelow ); + cfg.writeEntry( "belowrule", 4 ); + cfg.writeEntry( "skiptaskbar", info->skipTaskbar ); + cfg.writeEntry( "skiptaskbarrule", 4 ); + cfg.writeEntry( "skippager", info->skipPager ); + cfg.writeEntry( "skippagerrule", 4 ); + cfg.writeEntry( "noborder", info->userNoBorder ); + cfg.writeEntry( "noborderrule", 4 ); + } + cfg.setGroup( "General" ); + cfg.writeEntry( "count", pos ); + } + +int main() + { + KInstance inst( "kwin_update_window_settings" ); + KConfig src_cfg( "kwinrc" ); + KConfig dest_cfg( "kwinrulesrc" ); + loadFakeSessionInfo( &src_cfg ); + writeRules( dest_cfg ); + src_cfg.sync(); + dest_cfg.sync(); + DCOPClient client; + client.attach(); + client.send("kwin*", "", "reconfigure()", ""); + } diff --git a/kwin/events.cpp b/kwin/events.cpp new file mode 100644 index 000000000..039c760b4 --- /dev/null +++ b/kwin/events.cpp @@ -0,0 +1,1571 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to handling incoming events. + +*/ + +#include "client.h" +#include "workspace.h" +#include "atoms.h" +#include "tabbox.h" +#include "group.h" +#include "rules.h" + +#include <qwhatsthis.h> +#include <kkeynative.h> +#include <qapplication.h> + +#include <X11/extensions/shape.h> +#include <X11/Xatom.h> +#include <stdlib.h> + +extern Time qt_x_time; +extern Atom qt_window_role; + +namespace KWinInternal +{ + +// **************************************** +// WinInfo +// **************************************** + +WinInfo::WinInfo( Client * c, Display * display, Window window, + Window rwin, const unsigned long pr[], int pr_size ) + : NETWinInfo( display, window, rwin, pr, pr_size, NET::WindowManager ), m_client( c ) + { + } + +void WinInfo::changeDesktop(int desktop) + { + m_client->workspace()->sendClientToDesktop( m_client, desktop, true ); + } + +void WinInfo::changeState( unsigned long state, unsigned long mask ) + { + mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore + mask &= ~NET::Hidden; // clients are not allowed to change this directly + state &= mask; // for safety, clear all other bits + + if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) == 0 ) + m_client->setFullScreen( false, false ); + if ( (mask & NET::Max) == NET::Max ) + m_client->setMaximize( state & NET::MaxVert, state & NET::MaxHoriz ); + else if ( mask & NET::MaxVert ) + m_client->setMaximize( state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal ); + else if ( mask & NET::MaxHoriz ) + m_client->setMaximize( m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz ); + + if ( mask & NET::Shaded ) + m_client->setShade( state & NET::Shaded ? ShadeNormal : ShadeNone ); + if ( mask & NET::KeepAbove) + m_client->setKeepAbove( (state & NET::KeepAbove) != 0 ); + if ( mask & NET::KeepBelow) + m_client->setKeepBelow( (state & NET::KeepBelow) != 0 ); + if( mask & NET::SkipTaskbar ) + m_client->setSkipTaskbar( ( state & NET::SkipTaskbar ) != 0, true ); + if( mask & NET::SkipPager ) + m_client->setSkipPager( ( state & NET::SkipPager ) != 0 ); + if( mask & NET::DemandsAttention ) + m_client->demandAttention(( state & NET::DemandsAttention ) != 0 ); + if( mask & NET::Modal ) + m_client->setModal( ( state & NET::Modal ) != 0 ); + // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() ) + if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 ) + m_client->setFullScreen( true, false ); + } + + +// **************************************** +// RootInfo +// **************************************** + +RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr ) + : NETRootInfo4( dpy, w, name, pr, pr_num, scr ) + { + workspace = ws; + } + +void RootInfo::changeNumberOfDesktops(int n) + { + workspace->setNumberOfDesktops( n ); + } + +void RootInfo::changeCurrentDesktop(int d) + { + workspace->setCurrentDesktop( d ); + } + +void RootInfo::changeActiveWindow( Window w, NET::RequestSource src, Time timestamp, Window active_window ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + { + if( timestamp == CurrentTime ) + timestamp = c->userTime(); + if( src != NET::FromApplication && src != FromTool ) + src = NET::FromTool; + if( src == NET::FromTool ) + workspace->activateClient( c, true ); // force + else // NET::FromApplication + { + Client* c2; + if( workspace->allowClientActivation( c, timestamp )) + workspace->activateClient( c ); + // if activation of the requestor's window would be allowed, allow activation too + else if( active_window != None + && ( c2 = workspace->findClient( WindowMatchPredicate( active_window ))) != NULL + && workspace->allowClientActivation( c2, + timestampCompare( timestamp, c2->userTime() > 0 ? timestamp : c2->userTime()))) + workspace->activateClient( c ); + else + c->demandAttention(); + } + } + } + +void RootInfo::restackWindow( Window w, RequestSource src, Window above, int detail, Time timestamp ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + { + if( timestamp == CurrentTime ) + timestamp = c->userTime(); + if( src != NET::FromApplication && src != FromTool ) + src = NET::FromTool; + c->restackWindow( above, detail, src, timestamp, true ); + } + } + +void RootInfo::gotTakeActivity( Window w, Time timestamp, long flags ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + workspace->handleTakeActivity( c, timestamp, flags ); + } + +void RootInfo::closeWindow(Window w) + { + Client* c = workspace->findClient( WindowMatchPredicate( w )); + if ( c ) + c->closeWindow(); + } + +void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction) + { + Client* c = workspace->findClient( WindowMatchPredicate( w )); + if ( c ) + { + updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp + c->NETMoveResize( x_root, y_root, (Direction)direction); + } + } + +void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height ) + { + Client* c = workspace->findClient( WindowMatchPredicate( w )); + if ( c ) + c->NETMoveResizeWindow( flags, x, y, width, height ); + } + +void RootInfo::gotPing( Window w, Time timestamp ) + { + if( Client* c = workspace->findClient( WindowMatchPredicate( w ))) + c->gotPing( timestamp ); + } + +void RootInfo::changeShowingDesktop( bool showing ) + { + workspace->setShowingDesktop( showing ); + } + +// **************************************** +// Workspace +// **************************************** + +/*! + Handles workspace specific XEvents + */ +bool Workspace::workspaceEvent( XEvent * e ) + { + if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) ) + { + mouse_emulation = FALSE; + XUngrabKeyboard( qt_xdisplay(), qt_x_time ); + } + + if( e->type == PropertyNotify || e->type == ClientMessage ) + { + unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ]; + rootInfo->event( e, dirty, NETRootInfo::PROPERTIES_SIZE ); + if( dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames ) + saveDesktopSettings(); + if( dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout ) + updateDesktopLayout(); + } + + // events that should be handled before Clients can get them + switch (e->type) + { + case ButtonPress: + case ButtonRelease: + was_user_interaction = true; + // fallthrough + case MotionNotify: + if ( tab_grab || control_grab ) + { + tab_box->handleMouseEvent( e ); + return TRUE; + } + break; + case KeyPress: + { + was_user_interaction = true; + KKeyNative keyX( (XEvent*)e ); + uint keyQt = keyX.keyCodeQt(); + kdDebug(125) << "Workspace::keyPress( " << keyX.key().toString() << " )" << endl; + if (movingClient) + { + movingClient->keyPressEvent(keyQt); + return true; + } + if( tab_grab || control_grab ) + { + tabBoxKeyPress( keyX ); + return true; + } + break; + } + case KeyRelease: + was_user_interaction = true; + if( tab_grab || control_grab ) + { + tabBoxKeyRelease( e->xkey ); + return true; + } + break; + }; + + if( Client* c = findClient( WindowMatchPredicate( e->xany.window ))) + { + if( c->windowEvent( e )) + return true; + } + else if( Client* c = findClient( WrapperIdMatchPredicate( e->xany.window ))) + { + if( c->windowEvent( e )) + return true; + } + else if( Client* c = findClient( FrameIdMatchPredicate( e->xany.window ))) + { + if( c->windowEvent( e )) + return true; + } + else + { + Window special = findSpecialEventWindow( e ); + if( special != None ) + if( Client* c = findClient( WindowMatchPredicate( special ))) + { + if( c->windowEvent( e )) + return true; + } + } + if( movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window + && ( e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease )) + { + if( movingClient->windowEvent( e )) + return true; + } + + switch (e->type) + { + case CreateNotify: + if ( e->xcreatewindow.parent == root && + !QWidget::find( e->xcreatewindow.window) && + !e->xcreatewindow.override_redirect ) + { + // see comments for allowClientActivation() + XChangeProperty(qt_xdisplay(), e->xcreatewindow.window, + atoms->kde_net_wm_user_creation_time, XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)&qt_x_time, 1); + } + break; + + case UnmapNotify: + { + // check for system tray windows + if ( removeSystemTrayWin( e->xunmap.window, true ) ) + { + // If the system tray gets destroyed, the system tray + // icons automatically get unmapped, reparented and mapped + // again to the closest non-client ancestor due to + // QXEmbed's SaveSet feature. Unfortunatly with kicker + // this closest ancestor is not the root window, but our + // decoration, so we reparent explicitely back to the root + // window. + XEvent ev; + WId w = e->xunmap.window; + if ( XCheckTypedWindowEvent (qt_xdisplay(), w, + ReparentNotify, &ev) ) + { + if ( ev.xreparent.parent != root ) + { + XReparentWindow( qt_xdisplay(), w, root, 0, 0 ); + addSystemTrayWin( w ); + } + } + return TRUE; + } + + return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt + } + case MapNotify: + + return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt + + case ReparentNotify: + { + //do not confuse Qt with these events. After all, _we_ are the + //window manager who does the reparenting. + return TRUE; + } + case DestroyNotify: + { + if ( removeSystemTrayWin( e->xdestroywindow.window, false ) ) + return TRUE; + return false; + } + case MapRequest: + { + updateXTime(); + + // e->xmaprequest.window is different from e->xany.window + // TODO this shouldn't be necessary now + Client* c = findClient( WindowMatchPredicate( e->xmaprequest.window )); + if ( !c ) + { +// don't check for the parent being the root window, this breaks when some app unmaps +// a window, changes something and immediately maps it back, without giving KWin +// a chance to reparent it back to root +// since KWin can get MapRequest only for root window children and +// children of WindowWrapper (=clients), the check is AFAIK useless anyway +// Note: Now the save-set support in Client::mapRequestEvent() actually requires that +// this code doesn't check the parent to be root. +// if ( e->xmaprequest.parent == root ) { //###TODO store previously destroyed client ids + if ( addSystemTrayWin( e->xmaprequest.window ) ) + return TRUE; + c = createClient( e->xmaprequest.window, false ); + if ( c != NULL && root != qt_xrootwin() ) + { // TODO what is this? + // TODO may use QWidget::create + XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 ); + } + if( c == NULL ) // refused to manage, simply map it (most probably override redirect) + XMapRaised( qt_xdisplay(), e->xmaprequest.window ); + return true; + } + if( c ) + { + c->windowEvent( e ); + updateFocusChains( c, FocusChainUpdate ); + return true; + } + break; + } + case EnterNotify: + { + if ( QWhatsThis::inWhatsThisMode() ) + { + QWidget* w = QWidget::find( e->xcrossing.window ); + if ( w ) + QWhatsThis::leaveWhatsThisMode(); + } + if( electricBorder(e)) + return true; + break; + } + case LeaveNotify: + { + if ( !QWhatsThis::inWhatsThisMode() ) + break; + // TODO is this cliente ever found, given that client events are searched above? + Client* c = findClient( FrameIdMatchPredicate( e->xcrossing.window )); + if ( c && e->xcrossing.detail != NotifyInferior ) + QWhatsThis::leaveWhatsThisMode(); + break; + } + case ConfigureRequest: + { + if ( e->xconfigurerequest.parent == root ) + { + XWindowChanges wc; + wc.border_width = e->xconfigurerequest.border_width; + wc.x = e->xconfigurerequest.x; + wc.y = e->xconfigurerequest.y; + wc.width = e->xconfigurerequest.width; + wc.height = e->xconfigurerequest.height; + wc.sibling = None; + wc.stack_mode = Above; + unsigned int value_mask = e->xconfigurerequest.value_mask + & ( CWX | CWY | CWWidth | CWHeight | CWBorderWidth ); + XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, &wc ); + return true; + } + break; + } + case KeyPress: + if ( mouse_emulation ) + return keyPressMouseEmulation( e->xkey ); + break; + case KeyRelease: + if ( mouse_emulation ) + return FALSE; + break; + case FocusIn: + if( e->xfocus.window == rootWin() && QCString( getenv("KDE_MULTIHEAD")).lower() != "true" + && ( e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot )) + { + updateXTime(); // focusToNull() uses qt_x_time, which is old now (FocusIn has no timestamp) + Window focus; + int revert; + XGetInputFocus( qt_xdisplay(), &focus, &revert ); + if( focus == None || focus == PointerRoot ) + { + //kdWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" << endl; + Client *c = mostRecentlyActivatedClient(); + if( c != NULL ) + requestFocus( c, true ); + else if( activateNextClient( NULL )) + ; // ok, activated + else + focusToNull(); + } + } + // fall through + case FocusOut: + return true; // always eat these, they would tell Qt that KWin is the active app + case ClientMessage: + if( electricBorder( e )) + return true; + break; + default: + break; + } + return FALSE; + } + +// Some events don't have the actual window which caused the event +// as e->xany.window (e.g. ConfigureRequest), but as some other +// field in the XEvent structure. +Window Workspace::findSpecialEventWindow( XEvent* e ) + { + switch( e->type ) + { + case CreateNotify: + return e->xcreatewindow.window; + case DestroyNotify: + return e->xdestroywindow.window; + case UnmapNotify: + return e->xunmap.window; + case MapNotify: + return e->xmap.window; + case MapRequest: + return e->xmaprequest.window; + case ReparentNotify: + return e->xreparent.window; + case ConfigureNotify: + return e->xconfigure.window; + case GravityNotify: + return e->xgravity.window; + case ConfigureRequest: + return e->xconfigurerequest.window; + case CirculateNotify: + return e->xcirculate.window; + case CirculateRequest: + return e->xcirculaterequest.window; + default: + return None; + }; + } + +// **************************************** +// Client +// **************************************** + +/*! + General handler for XEvents concerning the client window + */ +bool Client::windowEvent( XEvent* e ) + { + if( e->xany.window == window()) // avoid doing stuff on frame or wrapper + { + unsigned long dirty[ 2 ]; + info->event( e, dirty, 2 ); // pass through the NET stuff + + if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 ) + fetchName(); + if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName ) != 0 ) + fetchIconicName(); + if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut ) != 0 + || ( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut ) != 0 ) + { + if( isTopMenu()) // the fallback mode of KMenuBar may alter the strut + checkWorkspacePosition(); // restore it + workspace()->updateClientArea(); + } + if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 ) + getIcons(); + // Note there's a difference between userTime() and info->userTime() + // info->userTime() is the value of the property, userTime() also includes + // updates of the time done by KWin (ButtonPress on windowrapper etc.). + if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime ) != 0 ) + { + workspace()->setWasUserInteraction(); + updateUserTime( info->userTime()); + } + if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 ) + startupIdChanged(); + if( dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry ) + { + if( demandAttentionKNotifyTimer != NULL ) + demandAttentionKNotify(); + } + } + +// TODO move all focus handling stuff to separate file? + switch (e->type) + { + case UnmapNotify: + unmapNotifyEvent( &e->xunmap ); + break; + case DestroyNotify: + destroyNotifyEvent( &e->xdestroywindow ); + break; + case MapRequest: + // this one may pass the event to workspace + return mapRequestEvent( &e->xmaprequest ); + case ConfigureRequest: + configureRequestEvent( &e->xconfigurerequest ); + break; + case PropertyNotify: + propertyNotifyEvent( &e->xproperty ); + break; + case KeyPress: + updateUserTime(); + workspace()->setWasUserInteraction(); + break; + case ButtonPress: + updateUserTime(); + workspace()->setWasUserInteraction(); + buttonPressEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state, + e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root ); + break; + case KeyRelease: + // don't update user time on releases + // e.g. if the user presses Alt+F2, the Alt release + // would appear as user input to the currently active window + break; + case ButtonRelease: + // don't update user time on releases + // e.g. if the user presses Alt+F2, the Alt release + // would appear as user input to the currently active window + buttonReleaseEvent( e->xbutton.window, e->xbutton.button, e->xbutton.state, + e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root ); + break; + case MotionNotify: + motionNotifyEvent( e->xmotion.window, e->xmotion.state, + e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root ); + workspace()->updateFocusMousePosition( QPoint( e->xmotion.x_root, e->xmotion.y_root )); + break; + case EnterNotify: + enterNotifyEvent( &e->xcrossing ); + // MotionNotify is guaranteed to be generated only if the mouse + // move start and ends in the window; for cases when it only + // starts or only ends there, Enter/LeaveNotify are generated. + // Fake a MotionEvent in such cases to make handle of mouse + // events simpler (Qt does that too). + motionNotifyEvent( e->xcrossing.window, e->xcrossing.state, + e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root ); + workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root )); + break; + case LeaveNotify: + motionNotifyEvent( e->xcrossing.window, e->xcrossing.state, + e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root ); + leaveNotifyEvent( &e->xcrossing ); + // not here, it'd break following enter notify handling + // workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root )); + break; + case FocusIn: + focusInEvent( &e->xfocus ); + break; + case FocusOut: + focusOutEvent( &e->xfocus ); + break; + case ReparentNotify: + break; + case ClientMessage: + clientMessageEvent( &e->xclient ); + break; + case ColormapChangeMask: + if( e->xany.window == window()) + { + cmap = e->xcolormap.colormap; + if ( isActive() ) + workspace()->updateColormap(); + } + break; + default: + if( e->xany.window == window()) + { + if( e->type == Shape::shapeEvent() ) + { + is_shape = Shape::hasShape( window()); // workaround for #19644 + updateShape(); + } + } + break; + } + return true; // eat all events + } + +/*! + Handles map requests of the client window + */ +bool Client::mapRequestEvent( XMapRequestEvent* e ) + { + if( e->window != window()) + { + // Special support for the save-set feature, which is a bit broken. + // If there's a window from one client embedded in another one, + // e.g. using XEMBED, and the embedder suddenly looses its X connection, + // save-set will reparent the embedded window to its closest ancestor + // that will remains. Unfortunately, with reparenting window managers, + // this is not the root window, but the frame (or in KWin's case, + // it's the wrapper for the client window). In this case, + // the wrapper will get ReparentNotify for a window it won't know, + // which will be ignored, and then it gets MapRequest, as save-set + // always maps. Returning true here means that Workspace::workspaceEvent() + // will handle this MapRequest and manage this window (i.e. act as if + // it was reparented to root window). + if( e->parent == wrapperId()) + return false; + return true; // no messing with frame etc. + } + if( isTopMenu() && workspace()->managingTopMenus()) + return true; // kwin controls these + switch ( mappingState() ) + { + case WithdrawnState: + assert( false ); // WMs are not supposed to manage clients in Withdrawn state, +// manage(); // after initial mapping manage() is called from createClient() + break; + case IconicState: + // also copied in clientMessage() + if( isMinimized()) + unminimize(); + if( isShade()) + setShade( ShadeNone ); + if( !isOnCurrentDesktop()) + { + if( workspace()->allowClientActivation( this )) + workspace()->activateClient( this ); + else + demandAttention(); + } + break; + case NormalState: + // TODO fake MapNotify? + break; + } + return true; + } + +/*! + Handles unmap notify events of the client window + */ +void Client::unmapNotifyEvent( XUnmapEvent* e ) + { + if( e->window != window()) + return; + if( e->event != wrapperId()) + { // most probably event from root window when initially reparenting + bool ignore = true; + if( e->event == workspace()->rootWin() && e->send_event ) + ignore = false; // XWithdrawWindow() + if( ignore ) + return; + } + switch( mappingState()) + { + case IconicState: + releaseWindow(); + return; + case NormalState: + // maybe we will be destroyed soon. Check this first. + XEvent ev; + if( XCheckTypedWindowEvent (qt_xdisplay(), window(), + DestroyNotify, &ev) ) // TODO I don't like this much + { + destroyClient(); // deletes this + return; + } + releaseWindow(); + break; + default: + assert( false ); + } + } + +void Client::destroyNotifyEvent( XDestroyWindowEvent* e ) + { + if( e->window != window()) + return; + destroyClient(); + } + + +bool blockAnimation = FALSE; + +/*! + Handles client messages for the client window +*/ +void Client::clientMessageEvent( XClientMessageEvent* e ) + { + if( e->window != window()) + return; // ignore frame/wrapper + // WM_STATE + if ( e->message_type == atoms->kde_wm_change_state ) + { + if( isTopMenu() && workspace()->managingTopMenus()) + return; // kwin controls these + if( e->data.l[ 1 ] ) + blockAnimation = true; + if( e->data.l[ 0 ] == IconicState ) + minimize(); + else if( e->data.l[ 0 ] == NormalState ) + { // copied from mapRequest() + if( isMinimized()) + unminimize(); + if( isShade()) + setShade( ShadeNone ); + if( !isOnCurrentDesktop()) + { + if( workspace()->allowClientActivation( this )) + workspace()->activateClient( this ); + else + demandAttention(); + } + } + blockAnimation = false; + } + else if ( e->message_type == atoms->wm_change_state) + { + if( isTopMenu() && workspace()->managingTopMenus()) + return; // kwin controls these + if ( e->data.l[0] == IconicState ) + minimize(); + return; + } + } + + +/*! + Handles configure requests of the client window + */ +void Client::configureRequestEvent( XConfigureRequestEvent* e ) + { + if( e->window != window()) + return; // ignore frame/wrapper + if ( isResize() || isMove()) + return; // we have better things to do right now + + if( fullscreen_mode == FullScreenNormal ) // refuse resizing of fullscreen windows + { // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode + sendSyntheticConfigureNotify(); + return; + } + if( isSplash() // no manipulations with splashscreens either + || isTopMenu()) // topmenus neither + { + sendSyntheticConfigureNotify(); + return; + } + + if ( e->value_mask & CWBorderWidth ) + { + // first, get rid of a window border + XWindowChanges wc; + unsigned int value_mask = 0; + + wc.border_width = 0; + value_mask = CWBorderWidth; + XConfigureWindow( qt_xdisplay(), window(), value_mask, & wc ); + } + + if( e->value_mask & ( CWX | CWY | CWHeight | CWWidth )) + configureRequest( e->value_mask, e->x, e->y, e->width, e->height, 0, false ); + + if ( e->value_mask & CWStackMode ) + restackWindow( e->above, e->detail, NET::FromApplication, userTime(), false ); + + // TODO sending a synthetic configure notify always is fine, even in cases where + // the ICCCM doesn't require this - it can be though of as 'the WM decided to move + // the window later'. The client should not cause that many configure request, + // so this should not have any significant impact. With user moving/resizing + // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()). + sendSyntheticConfigureNotify(); + + // SELI TODO accept configure requests for isDesktop windows (because kdesktop + // may get XRANDR resize event before kwin), but check it's still at the bottom? + } + + +/*! + Handles property changes of the client window + */ +void Client::propertyNotifyEvent( XPropertyEvent* e ) + { + if( e->window != window()) + return; // ignore frame/wrapper + switch ( e->atom ) + { + case XA_WM_NORMAL_HINTS: + getWmNormalHints(); + break; + case XA_WM_NAME: + fetchName(); + break; + case XA_WM_ICON_NAME: + fetchIconicName(); + break; + case XA_WM_TRANSIENT_FOR: + readTransient(); + break; + case XA_WM_HINTS: + getWMHints(); + getIcons(); // because KWin::icon() uses WMHints as fallback + break; + default: + if ( e->atom == atoms->wm_protocols ) + getWindowProtocols(); + else if (e->atom == atoms->wm_client_leader ) + getWmClientLeader(); + else if( e->atom == qt_window_role ) + window_role = staticWindowRole( window()); + else if( e->atom == atoms->motif_wm_hints ) + getMotifHints(); + break; + } + } + + +void Client::enterNotifyEvent( XCrossingEvent* e ) + { + if( e->window != frameId()) + return; // care only about entering the whole frame + if( e->mode == NotifyNormal || + ( !options->focusPolicyIsReasonable() && + e->mode == NotifyUngrab ) ) + { + + if (options->shadeHover && isShade()) + { + delete shadeHoverTimer; + shadeHoverTimer = new QTimer( this ); + connect( shadeHoverTimer, SIGNAL( timeout() ), this, SLOT( shadeHover() )); + shadeHoverTimer->start( options->shadeHoverInterval, TRUE ); + } + + if ( options->focusPolicy == Options::ClickToFocus ) + return; + + if ( options->autoRaise && !isDesktop() && + !isDock() && !isTopMenu() && workspace()->focusChangeEnabled() && + workspace()->topClientOnDesktop( workspace()->currentDesktop()) != this ) + { + delete autoRaiseTimer; + autoRaiseTimer = new QTimer( this ); + connect( autoRaiseTimer, SIGNAL( timeout() ), this, SLOT( autoRaise() ) ); + autoRaiseTimer->start( options->autoRaiseInterval, TRUE ); + } + + QPoint currentPos( e->x_root, e->y_root ); + if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() || isTopMenu() ) ) + return; + // for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus + // change came because of window changes (e.g. closing a window) - #92290 + if( options->focusPolicy != Options::FocusFollowsMouse + || currentPos != workspace()->focusMousePosition()) + { + if ( options->delayFocus ) + workspace()->requestDelayFocus( this ); + else + workspace()->requestFocus( this ); + } + return; + } + } + +void Client::leaveNotifyEvent( XCrossingEvent* e ) + { + if( e->window != frameId()) + return; // care only about leaving the whole frame + if ( e->mode == NotifyNormal ) + { + if ( !buttonDown ) + { + mode = PositionCenter; + setCursor( arrowCursor ); + } + bool lostMouse = !rect().contains( QPoint( e->x, e->y ) ); + // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations + // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event + // comes after leaving the rect) - so lets check if the pointer is really outside the window + + // TODO this still sucks if a window appears above this one - it should lose the mouse + // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :( + // (repeat after me 'AARGHL!') + if ( !lostMouse && e->detail != NotifyInferior ) + { + int d1, d2, d3, d4; + unsigned int d5; + Window w, child; + if( XQueryPointer( qt_xdisplay(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5 ) == False + || child == None ) + lostMouse = true; // really lost the mouse + } + if ( lostMouse ) + { + cancelAutoRaise(); + workspace()->cancelDelayFocus(); + cancelShadeHover(); + if ( shade_mode == ShadeHover && !moveResizeMode && !buttonDown ) + setShade( ShadeNormal ); + } + if ( options->focusPolicy == Options::FocusStrictlyUnderMouse ) + if ( isActive() && lostMouse ) + workspace()->requestFocus( 0 ) ; + return; + } + } + +#define XCapL KKeyNative::modXLock() +#define XNumL KKeyNative::modXNumLock() +#define XScrL KKeyNative::modXScrollLock() +void Client::grabButton( int modifier ) + { + unsigned int mods[ 8 ] = + { + 0, XCapL, XNumL, XNumL | XCapL, + XScrL, XScrL | XCapL, + XScrL | XNumL, XScrL | XNumL | XCapL + }; + for( int i = 0; + i < 8; + ++i ) + XGrabButton( qt_xdisplay(), AnyButton, + modifier | mods[ i ], + wrapperId(), FALSE, ButtonPressMask, + GrabModeSync, GrabModeAsync, None, None ); + } + +void Client::ungrabButton( int modifier ) + { + unsigned int mods[ 8 ] = + { + 0, XCapL, XNumL, XNumL | XCapL, + XScrL, XScrL | XCapL, + XScrL | XNumL, XScrL | XNumL | XCapL + }; + for( int i = 0; + i < 8; + ++i ) + XUngrabButton( qt_xdisplay(), AnyButton, + modifier | mods[ i ], wrapperId()); + } +#undef XCapL +#undef XNumL +#undef XScrL + +/* + Releases the passive grab for some modifier combinations when a + window becomes active. This helps broken X programs that + missinterpret LeaveNotify events in grab mode to work properly + (Motif, AWT, Tk, ...) + */ +void Client::updateMouseGrab() + { + if( workspace()->globalShortcutsDisabled()) + { + XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId()); + // keep grab for the simple click without modifiers if needed (see below) + bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this; + if( !( !options->clickRaise || not_obscured )) + grabButton( None ); + return; + } + if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab() + { + // first grab all modifier combinations + XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE, + ButtonPressMask, + GrabModeSync, GrabModeAsync, + None, None ); + // remove the grab for no modifiers only if the window + // is unobscured or if the user doesn't want click raise + // (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is + // the most recently raised window) + bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this; + if( !options->clickRaise || not_obscured ) + ungrabButton( None ); + else + grabButton( None ); + ungrabButton( ShiftMask ); + ungrabButton( ControlMask ); + ungrabButton( ControlMask | ShiftMask ); + } + else + { + XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, wrapperId()); + // simply grab all modifier combinations + XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, wrapperId(), FALSE, + ButtonPressMask, + GrabModeSync, GrabModeAsync, + None, None ); + } + } + +int qtToX11Button( Qt::ButtonState button ) + { + if( button == Qt::LeftButton ) + return Button1; + else if( button == Qt::MidButton ) + return Button2; + else if( button == Qt::RightButton ) + return Button3; + return AnyButton; + } + +int qtToX11State( Qt::ButtonState state ) + { + int ret = 0; + if( state & Qt::LeftButton ) + ret |= Button1Mask; + if( state & Qt::MidButton ) + ret |= Button2Mask; + if( state & Qt::RightButton ) + ret |= Button3Mask; + if( state & Qt::ShiftButton ) + ret |= ShiftMask; + if( state & Qt::ControlButton ) + ret |= ControlMask; + if( state & Qt::AltButton ) + ret |= KKeyNative::modX(KKey::ALT); + if( state & Qt::MetaButton ) + ret |= KKeyNative::modX(KKey::WIN); + return ret; + } + +// Qt propagates mouse events up the widget hierachy, which means events +// for the decoration window cannot be (easily) intercepted as X11 events +bool Client::eventFilter( QObject* o, QEvent* e ) + { + if( decoration == NULL + || o != decoration->widget()) + return false; + if( e->type() == QEvent::MouseButtonPress ) + { + QMouseEvent* ev = static_cast< QMouseEvent* >( e ); + return buttonPressEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()), + ev->x(), ev->y(), ev->globalX(), ev->globalY() ); + } + if( e->type() == QEvent::MouseButtonRelease ) + { + QMouseEvent* ev = static_cast< QMouseEvent* >( e ); + return buttonReleaseEvent( decorationId(), qtToX11Button( ev->button()), qtToX11State( ev->state()), + ev->x(), ev->y(), ev->globalX(), ev->globalY() ); + } + if( e->type() == QEvent::MouseMove ) // FRAME i fake z enter/leave? + { + QMouseEvent* ev = static_cast< QMouseEvent* >( e ); + return motionNotifyEvent( decorationId(), qtToX11State( ev->state()), + ev->x(), ev->y(), ev->globalX(), ev->globalY() ); + } + if( e->type() == QEvent::Wheel ) + { + QWheelEvent* ev = static_cast< QWheelEvent* >( e ); + bool r = buttonPressEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()), + ev->x(), ev->y(), ev->globalX(), ev->globalY() ); + r = r || buttonReleaseEvent( decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State( ev->state()), + ev->x(), ev->y(), ev->globalX(), ev->globalY() ); + return r; + } + if( e->type() == QEvent::Resize ) + { + QResizeEvent* ev = static_cast< QResizeEvent* >( e ); + // Filter out resize events that inform about size different than frame size. + // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync. + // These events only seem to be delayed events from initial resizing before show() was called + // on the decoration widget. + if( ev->size() != size()) + return true; + } + return false; + } + +// return value matters only when filtering events before decoration gets them +bool Client::buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ) + { + if (buttonDown) + { + if( w == wrapperId()) + XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time); + return true; + } + + if( w == wrapperId() || w == frameId() || w == decorationId()) + { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace + updateUserTime(); + workspace()->setWasUserInteraction(); + uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? + KKeyNative::modX(KKey::WIN) : + KKeyNative::modX(KKey::ALT); + bool bModKeyHeld = keyModX != 0 && ( state & KKeyNative::accelModMaskX()) == keyModX; + + if( isSplash() + && button == Button1 && !bModKeyHeld ) + { // hide splashwindow if the user clicks on it + hideClient( true ); + if( w == wrapperId()) + XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time); + return true; + } + + Options::MouseCommand com = Options::MouseNothing; + bool was_action = false; + bool perform_handled = false; + if ( bModKeyHeld ) + { + was_action = true; + switch (button) + { + case Button1: + com = options->commandAll1(); + break; + case Button2: + com = options->commandAll2(); + break; + case Button3: + com = options->commandAll3(); + break; + case Button4: + case Button5: + com = options->operationWindowMouseWheel( button == Button4 ? 120 : -120 ); + break; + } + } + else + { // inactive inner window + if( !isActive() && w == wrapperId()) + { + was_action = true; + perform_handled = true; + switch (button) + { + case Button1: + com = options->commandWindow1(); + break; + case Button2: + com = options->commandWindow2(); + break; + case Button3: + com = options->commandWindow3(); + break; + default: + com = Options::MouseActivateAndPassClick; + } + } + // active inner window + if( isActive() && w == wrapperId() + && options->clickRaise && button < 4 ) // exclude wheel + { + com = Options::MouseActivateRaiseAndPassClick; + was_action = true; + perform_handled = true; + } + } + if( was_action ) + { + bool replay = performMouseCommand( com, QPoint( x_root, y_root), perform_handled ); + + if ( isSpecialWindow()) + replay = TRUE; + + if( w == wrapperId()) // these can come only from a grab + XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, CurrentTime ); //qt_x_time); + return true; + } + } + + if( w == wrapperId()) // these can come only from a grab + { + XAllowEvents(qt_xdisplay(), ReplayPointer, CurrentTime ); //qt_x_time); + return true; + } + if( w == decorationId()) + return false; // don't eat decoration events + if( w == frameId()) + processDecorationButtonPress( button, state, x, y, x_root, y_root ); + return true; + } + + +// this function processes button press events only after decoration decides not to handle them, +// unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them +void Client::processDecorationButtonPress( int button, int /*state*/, int x, int y, int x_root, int y_root ) + { + Options::MouseCommand com = Options::MouseNothing; + bool active = isActive(); + if ( !wantsInput() ) // we cannot be active, use it anyway + active = TRUE; + + if ( button == Button1 ) + com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); + else if ( button == Button2 ) + com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); + else if ( button == Button3 ) + com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); + if( button == Button1 + && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching + && com != Options::MouseMinimize ) // mouse release event + { + mode = mousePosition( QPoint( x, y )); + buttonDown = TRUE; + moveOffset = QPoint( x, y ); + invertedMoveOffset = rect().bottomRight() - moveOffset; + unrestrictedMoveResize = false; + setCursor( mode ); // update to sizeAllCursor if about to move + } + performMouseCommand( com, QPoint( x_root, y_root )); + } + +// called from decoration +void Client::processMousePressEvent( QMouseEvent* e ) + { + if( e->type() != QEvent::MouseButtonPress ) + { + kdWarning() << "processMousePressEvent()" << endl; + return; + } + int button; + switch( e->button()) + { + case LeftButton: + button = Button1; + break; + case MidButton: + button = Button2; + break; + case RightButton: + button = Button3; + break; + default: + return; + } + processDecorationButtonPress( button, e->state(), e->x(), e->y(), e->globalX(), e->globalY()); + } + +// return value matters only when filtering events before decoration gets them +bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int y, int x_root, int y_root ) + { + if( w == decorationId() && !buttonDown) + return false; + if( w == wrapperId()) + { + XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime ); //qt_x_time); + return true; + } + if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow()) + return true; + x = this->x(); // translate from grab window to local coords + y = this->y(); + if ( (state & ( Button1Mask & Button2Mask & Button3Mask )) == 0 ) + { + buttonDown = FALSE; + if ( moveResizeMode ) + { + finishMoveResize( false ); + // mouse position is still relative to old Client position, adjust it + QPoint mousepos( x_root - x, y_root - y ); + mode = mousePosition( mousepos ); + } + setCursor( mode ); + } + return true; + } + +static bool was_motion = false; +static Time next_motion_time = CurrentTime; +// Check whole incoming X queue for MotionNotify events +// checking whole queue is done by always returning False in the predicate. +// If there are more MotionNotify events in the queue, all until the last +// one may be safely discarded (if a ButtonRelease event comes, a MotionNotify +// will be faked from it, so there's no need to check other events). +// This helps avoiding being overloaded by being flooded from many events +// from the XServer. +static Bool motion_predicate( Display*, XEvent* ev, XPointer ) +{ + if( ev->type == MotionNotify ) + { + was_motion = true; + next_motion_time = ev->xmotion.time; // for setting time + } + return False; +} + +static bool waitingMotionEvent() + { +// The queue doesn't need to be checked until the X timestamp +// of processes events reaches the timestamp of the last suitable +// MotionNotify event in the queue. + if( next_motion_time != CurrentTime + && timestampCompare( qt_x_time, next_motion_time ) < 0 ) + return true; + was_motion = false; + XSync( qt_xdisplay(), False ); // this helps to discard more MotionNotify events + XEvent dummy; + XCheckIfEvent( qt_xdisplay(), &dummy, motion_predicate, NULL ); + return was_motion; + } + +// return value matters only when filtering events before decoration gets them +bool Client::motionNotifyEvent( Window w, int /*state*/, int x, int y, int x_root, int y_root ) + { + if( w != frameId() && w != decorationId() && w != moveResizeGrabWindow()) + return true; // care only about the whole frame + if ( !buttonDown ) + { + Position newmode = mousePosition( QPoint( x, y )); + if( newmode != mode ) + setCursor( newmode ); + mode = newmode; + // reset the timestamp for the optimization, otherwise with long passivity + // the option in waitingMotionEvent() may be always true + next_motion_time = CurrentTime; + return false; + } + if( w == moveResizeGrabWindow()) + { + x = this->x(); // translate from grab window to local coords + y = this->y(); + } + if( !waitingMotionEvent()) + handleMoveResize( x, y, x_root, y_root ); + return true; + } + +void Client::focusInEvent( XFocusInEvent* e ) + { + if( e->window != window()) + return; // only window gets focus + if ( e->mode == NotifyUngrab ) + return; // we don't care + if ( e->detail == NotifyPointer ) + return; // we don't care + if( !isShown( false ) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile -> + return; // activateNextClient() already transferred focus elsewhere + // check if this client is in should_get_focus list or if activation is allowed + bool activate = workspace()->allowClientActivation( this, -1U, true ); + workspace()->gotFocusIn( this ); // remove from should_get_focus list + if( activate ) + setActive( TRUE ); + else + { + workspace()->restoreFocus(); + demandAttention(); + } + } + +// When a client loses focus, FocusOut events are usually immediatelly +// followed by FocusIn events for another client that gains the focus +// (unless the focus goes to another screen, or to the nofocus widget). +// Without this check, the former focused client would have to be +// deactivated, and after that, the new one would be activated, with +// a short time when there would be no active client. This can cause +// flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred +// from it to its transient, the fullscreen would be kept in the Active layer +// at the beginning and at the end, but not in the middle, when the active +// client would be temporarily none (see Client::belongToLayer() ). +// Therefore, the events queue is checked, whether it contains the matching +// FocusIn event, and if yes, deactivation of the previous client will +// be skipped, as activation of the new one will automatically deactivate +// previously active client. +static bool follows_focusin = false; +static bool follows_focusin_failed = false; +static Bool predicate_follows_focusin( Display*, XEvent* e, XPointer arg ) + { + if( follows_focusin || follows_focusin_failed ) + return False; + Client* c = ( Client* ) arg; + if( e->type == FocusIn && c->workspace()->findClient( WindowMatchPredicate( e->xfocus.window ))) + { // found FocusIn + follows_focusin = true; + return False; + } + // events that may be in the queue before the FocusIn event that's being + // searched for + if( e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify ) + return False; + follows_focusin_failed = true; // a different event - stop search + return False; + } + +static bool check_follows_focusin( Client* c ) + { + follows_focusin = follows_focusin_failed = false; + XEvent dummy; + // XCheckIfEvent() is used to make the search non-blocking, the predicate + // always returns False, so nothing is removed from the events queue. + // XPeekIfEvent() would block. + XCheckIfEvent( qt_xdisplay(), &dummy, predicate_follows_focusin, (XPointer)c ); + return follows_focusin; + } + + +void Client::focusOutEvent( XFocusOutEvent* e ) + { + if( e->window != window()) + return; // only window gets focus + if ( e->mode == NotifyGrab ) + return; // we don't care + if ( isShade() ) + return; // here neither + if ( e->detail != NotifyNonlinear + && e->detail != NotifyNonlinearVirtual ) + // SELI check all this + return; // hack for motif apps like netscape + if ( QApplication::activePopupWidget() ) + return; + if( !check_follows_focusin( this )) + setActive( FALSE ); + } + +// performs _NET_WM_MOVERESIZE +void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction ) + { + if( direction == NET::Move ) + performMouseCommand( Options::MouseMove, QPoint( x_root, y_root )); + else if( moveResizeMode && direction == NET::MoveResizeCancel ) + { + finishMoveResize( true ); + buttonDown = FALSE; + setCursor( mode ); + } + else if( direction >= NET::TopLeft && direction <= NET::Left ) + { + static const Position convert[] = + { + PositionTopLeft, + PositionTop, + PositionTopRight, + PositionRight, + PositionBottomRight, + PositionBottom, + PositionBottomLeft, + PositionLeft + }; + if(!isResizable() || isShade()) + return; + if( moveResizeMode ) + finishMoveResize( false ); + buttonDown = TRUE; + moveOffset = QPoint( x_root - x(), y_root - y()); // map from global + invertedMoveOffset = rect().bottomRight() - moveOffset; + unrestrictedMoveResize = false; + mode = convert[ direction ]; + setCursor( mode ); + if( !startMoveResize()) + { + buttonDown = false; + setCursor( mode ); + } + } + else if( direction == NET::KeyboardMove ) + { // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm + QCursor::setPos( geometry().center() ); + performMouseCommand( Options::MouseUnrestrictedMove, geometry().center()); + } + else if( direction == NET::KeyboardSize ) + { // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm + QCursor::setPos( geometry().bottomRight()); + performMouseCommand( Options::MouseUnrestrictedResize, geometry().bottomRight()); + } + } + +void Client::keyPressEvent( uint key_code ) + { + updateUserTime(); + if ( !isMove() && !isResize() ) + return; + bool is_control = key_code & Qt::CTRL; + bool is_alt = key_code & Qt::ALT; + key_code = key_code & 0xffff; + int delta = is_control?1:is_alt?32:8; + QPoint pos = QCursor::pos(); + switch ( key_code ) + { + case Key_Left: + pos.rx() -= delta; + break; + case Key_Right: + pos.rx() += delta; + break; + case Key_Up: + pos.ry() -= delta; + break; + case Key_Down: + pos.ry() += delta; + break; + case Key_Space: + case Key_Return: + case Key_Enter: + finishMoveResize( false ); + buttonDown = FALSE; + setCursor( mode ); + break; + case Key_Escape: + finishMoveResize( true ); + buttonDown = FALSE; + setCursor( mode ); + break; + default: + return; + } + QCursor::setPos( pos ); + } + +// **************************************** +// Group +// **************************************** + +bool Group::groupEvent( XEvent* e ) + { + unsigned long dirty[ 2 ]; + leader_info->event( e, dirty, 2 ); // pass through the NET stuff + if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMIcon) != 0 ) + getIcons(); + if(( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId ) != 0 ) + startupIdChanged(); + return false; + } + + +} // namespace diff --git a/kwin/eventsrc b/kwin/eventsrc new file mode 100644 index 000000000..44d6342d0 --- /dev/null +++ b/kwin/eventsrc @@ -0,0 +1,5918 @@ +[!Global!] +IconName=kwin +Comment=The KDE Window Manager +Comment[af]=Die KDE Venster Bestuurder +Comment[ar]=مسير KDE للنواÙØ° +Comment[az]=KDE PÉ™ncÉ™rÉ™ Ä°darÉ™cisi +Comment[be]=Кіраўнік вокнаў KDE +Comment[bg]=Мениджър на прозорци +Comment[bn]=কে.ডি.ই উইণà§à¦¡à§‹ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦° +Comment[br]=Merour prenester KDE +Comment[bs]=KDE upravitelj prozorima +Comment[ca]=El gestor de finestres KDE +Comment[cs]=Správce oken KDE +Comment[csb]=Menedżer òknów KDE +Comment[cy]=Y Trefnydd Ffenestri KDE +Comment[da]=KDE VindueshÃ¥ndtering +Comment[de]=KDE-Fenstermanager +Comment[el]=Ο διαχειÏιστής παÏαθÏÏων του KDE +Comment[eo]=KDE-fenestroadministrilo +Comment[es]=El gestor de ventanas de KDE +Comment[et]=KDE aknahaldur +Comment[eu]=KDEren leiho kudeatzailea +Comment[fa]=مدیر پنجره KDE +Comment[fi]=KDE:n ikkunaohjelma +Comment[fr]=Le gestionnaire de fenêtres de KDE +Comment[fy]=De KDE-windowmanager +Comment[ga]=Bainisteoir Fuinneoga KDE +Comment[gl]=O Xestor de Fiestras de KDE +Comment[he]=×ž× ×”×œ ×”×—×œ×•× ×•×ª של KDE +Comment[hi]=केडीई विंडो पà¥à¤°à¤¬à¤‚धक +Comment[hr]=KDE upravitelj prozorima +Comment[hu]=KDE ablakkezelÅ‘ +Comment[id]=Manajer Jendela KDE +Comment[is]=Gluggastjóri KDE +Comment[it]=Il window manager di KDE +Comment[ja]=KDE ウィンドウマãƒãƒ¼ã‚¸ãƒ£ +Comment[ka]=KDE ფáƒáƒœáƒ¯áƒ ის მენეჯერი +Comment[kk]=KDE терезе менеджері +Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច KDE +Comment[ko]=KDE ì°½ ê´€ë¦¬ìž +Comment[lo]=ລະບົບຈັດàºàº²àº™àº«àº™à»‰àº²àº•à»ˆàº²àº‡àº‚àºàº‡ KDE +Comment[lt]=KDE langų tvarkyklÄ— +Comment[lv]=KDE Logu Menedžeris +Comment[mk]=Менаџерот на прозорци на KDE +Comment[mn]=КДÐ-Цонхны удирдагч +Comment[ms]=Pengurus Tetingkap KDE +Comment[mt]=Window manager tal-KDE +Comment[nb]=KDE Vindusbehandler +Comment[nds]=De Finsterpleger vun KDE +Comment[ne]=KDE सञà¥à¤à¥à¤¯à¤¾à¤² पà¥à¤°à¤¬à¤¨à¥à¤§à¤• +Comment[nl]=De KDE-windowmanager +Comment[nn]=KDE Vindaugshandterar +Comment[nso]=Molaodi wa Window ya KDE +Comment[oc]=Lo gestionari de finestres KDE +Comment[pa]=KDE à¨à¨°à©‹à¨–ਾ ਮੈਨੇਜਰ +Comment[pl]=Menedżer okien KDE +Comment[pt]=Gestor de janelas do KDE +Comment[pt_BR]=Gerenciador de Janelas do KDE +Comment[ro]=Managerul de ferestre KDE +Comment[ru]=Оконный менеджер +Comment[rw]=Mugenga w'Idirishya KDE +Comment[se]=KDE lásegieÄ‘ahalli +Comment[sk]=Správca okien KDE +Comment[sl]=Okenski upravitelj KDE +Comment[sr]=KDE менаџер прозора +Comment[sr@Latn]=KDE menadžer prozora +Comment[sv]=KDE:s fönsterhanterare +Comment[ta]=KDE சாளர மேலாளர௠+Comment[tg]=Мудири тирезаи KDE +Comment[th]=ระบบจัดà¸à¸²à¸£à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¸‚à¸à¸‡ KDE +Comment[tr]=KDE Pencere Yöneticisi +Comment[tt]=KDE Täräzä Ä°däräçe +Comment[uk]=Менеджер вікон KDE +Comment[uz]=KDE oyna boshqaruvchi +Comment[uz@cyrillic]=KDE ойна бошқарувчи +Comment[ven]=Mulanguli wa windo ya KDE +Comment[vi]=Trình quản là Cá»a sổ của KDE +Comment[wa]=Li manaedjeu di purneas di KDE +Comment[xh]=Umphathi we Window ye KDE +Comment[zh_CN]=KDE 窗å£ç®¡ç†å™¨ +Comment[zh_TW]=KDE 視窗管ç†ç¨‹å¼ +Comment[zu]=Imenenja yama-Window ye-KDE + +[desktop1] +Name=Change to Desktop 1 +Name[af]=Verander na Werkskerm 1 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 1 +Name[az]=1. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 1 +Name[bg]=Превключване на работен плот 1 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১-ঠযাও +Name[br]=Gwintañ da vurev 1 +Name[bs]=Premjesti se na radnu povrÅ¡inu 1 +Name[ca]=Canvi a l'escriptori 1 +Name[cs]=PÅ™epnutà na plochu 1 +Name[csb]=Zmieni na pùlt 1 +Name[cy]=Newid i Penbwrdd 1 +Name[da]=Skift til desktop 1 +Name[de]=Wechsel zu Arbeitsfläche 1 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 1 +Name[eo]=Al tabulo 1 +Name[es]=Cambiar al escritorio 1 +Name[et]=Liikumine 1. töölauale +Name[eu]=Aldatu 1. mahaigainera +Name[fa]=تغییر به رومیزی Û± +Name[fi]=Vaihda työpöytään 1 +Name[fr]=Aller au bureau 1 +Name[fy]=Gean nei buroblêd 1 +Name[ga]=Téigh go Deasc 1 +Name[gl]=Ir ao Escritório 1 +Name[he]=מעבר לשולחן עבודה 1 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 1 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 1 +Name[hu]=Váltás az 1. asztalra +Name[id]=Ganti ke Desktop 1 +Name[is]=Birta skjáborð 1 +Name[it]=Va al desktop 1 +Name[ja]=デスクトップ 1 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 1-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=1-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១ +Name[ko]=ë°ìŠ¤í¬í†± 1ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 1 +Name[lt]=Pereiti į darbastalį 1 +Name[lv]=NomainÄ«t uz Darbvirsmu 1 +Name[mk]=Кон површина 1 +Name[mn]=Ðжлын тавцан 1 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 1 +Name[mt]=Mur f' Desktop 1 +Name[nb]=Bytt til skrivebord 1 +Name[nds]=Na Schriefdisch 1 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 1 +Name[nn]=Byt til skrivebord 1 +Name[nso]=Fetogela go Desktop 1 +Name[oc]=Canvia au burèu 1 +Name[pa]=ਵੇਹੜੇ 1 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 1 +Name[pt]=Mudar para o Ecrã 1 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 1 +Name[ro]=Trece în ecranul 1 +Name[ru]=Перейти на рабочий Ñтол 1 +Name[rw]=Hindura ku Biro 1 +Name[se]=Mana vuosttáš Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 1 +Name[sl]=Preklopi na namizje 1 +Name[sr]=Промени на радну површину 1 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 1 +Name[ss]=Tjintjela ku desktop 1 +Name[sv]=Byt till skrivbord 1 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 1கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 1కౠమారండి +Name[tg]=Гузариш ба мизи кории 1 +Name[th]=สลับไปยังพื้นที่หน้าจภ1 +Name[tr]=1. Masaüstüne Git +Name[tt]=1. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 1 +Name[uz]=Ish stoli 1ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 1га ўтиш +Name[ven]=U shandukela kha desikithopo yau thoma +Name[vi]=Äổi vá» Mà n hình ná»n 1 +Name[wa]=Potchî sol prumî scribanne +Name[xh]=Tshintshela kwi Desktop 1 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 1 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢ä¸€ +Name[zu]=Shintshela kwi-Desktop 1 +Comment=Virtual desktop one is selected +Comment[af]=Virtuele Werkskerm een is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 1 هو المØدد +Comment[az]=1. Masa üstü seçildi +Comment[be]=Выбраны першы віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 1 +Comment[bn]=পà§à¦°à¦¥à¦® à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Unan a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Jedan je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual u +Comment[cs]=Je vybrána virtuálnà plocha 1 +Comment[csb]=Wëbróny je pierszi pùlt +Comment[da]=Virtuel desktop én er valgt +Comment[de]=Virtuelle Arbeitsfläche 1 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 1 +Comment[eo]=Tabulo unua estas elektita +Comment[es]=Seleccionado el escritorio virtual Uno +Comment[et]=Esimene virtuaalne töölaud on valitud +Comment[eu]=Lehen mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی یک انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä yksi on valittu +Comment[fr]=Le bureau virtuel 1 est sélectionné +Comment[fy]=Firtueel buroblêd ien is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a haon +Comment[gl]=Escolle-se o escritório virtual número Un +Comment[he]=שולחן העבודה הווירטו×לי ×חד × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª à¤à¤• चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 1 +Comment[hu]=Az 1. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð eitt er virkt +Comment[it]=Il desktop virtuale uno viene selezionato. +Comment[ja]=仮想デスクトップ 1 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირპირველი სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Бірінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 1ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas pirmas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 1. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 1 +Comment[ms]=Desktop maya satu dipilih +Comment[mt]=Desktop Virtwali Wieħed huwa Magħżul +Comment[nb]=Virtuelt skrivebord én er valgt +Comment[nds]=De virtuelle Schriefdisch Een is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad een is geselecteerd +Comment[nn]=Virtuelt skrivebord éin vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਇੱਕ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano pierwszy pulpit +Comment[pt]=O ecrã virtual um é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 1 está selecionada +Comment[ro]=Ecranul virtual 1 este selectat +Comment[ru]=Выбран первый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara rimwe byatoranyijwe +Comment[se]=Vuosttáš virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 1 +Comment[sl]=Izbrano je prvo navidezno namizje +Comment[sr]=Изабрана је прва виртуелна радна површина +Comment[sr@Latn]=Izabrana je prva virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord ett är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ ஒனà¯à®±à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=ఒకటొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่ทำหน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่หนึ่งถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü bir seçili +Comment[tt]=Berençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 1 +Comment[uz]=Birinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Биринчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 1 đã được chá»n +Comment[wa]=Li prumî forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 1 +Comment[zh_TW]=虛擬桌é¢ä¸€è¢«é¸å– +default_sound=KDE_Desktop_1.ogg +default_presentation=0 + +[desktop2] +Name=Change to Desktop 2 +Name[af]=Verander na Werkskerm 2 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 2 +Name[az]=2. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 2 +Name[bg]=Превключване на работен плот 2 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ২-ঠযাও +Name[br]=Gwintañ da vurev 2 +Name[bs]=Premjesti se na radnu povrÅ¡inu 2 +Name[ca]=Canvi a l'escriptori 2 +Name[cs]=PÅ™epnutà na plochu 2 +Name[csb]=Zmieni na pùlt 2 +Name[cy]=Newid i Penbwrdd 2 +Name[da]=Skift til desktop 2 +Name[de]=Wechsel zu Arbeitsfläche 2 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 2 +Name[eo]=Al tabulo 2 +Name[es]=Cambiar al escritorio 2 +Name[et]=Liikumine 2. töölauale +Name[eu]=Aldatu 2. mahaigainera +Name[fa]=تغییر به رومیزی Û² +Name[fi]=Vaihda työpöytään 2 +Name[fr]=Aller au bureau 2 +Name[fy]=Gean nei buroblêd 2 +Name[ga]=Téigh go Deasc 2 +Name[gl]=Ir ao Escritório 2 +Name[he]=מעבר לשולחן עבודה 2 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 2 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 2 +Name[hu]=Váltás a 2. asztalra +Name[id]=Ganti ke Desktop 2 +Name[is]=Birta skjáborð 2 +Name[it]=Va al desktop 2 +Name[ja]=デスクトップ 2 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 2-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=2-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ២ +Name[ko]=ë°ìŠ¤í¬í†± 2ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີທຳງານ 2 +Name[lt]=Pereiti į darbastalį 2 +Name[lv]=NomainÄ«t uz Darbvirsmu 2 +Name[mk]=Кон површина 2 +Name[mn]=Ðжлын тавцан 2 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 2 +Name[mt]=Mur f' Desktop 2 +Name[nb]=Bytt til skrivebord 2 +Name[nds]=Na Schriefdisch 2 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª २ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 2 +Name[nn]=Byt til skrivebord 2 +Name[nso]=Fetogela go Desktop 2 +Name[oc]=Canvia au burèu 2 +Name[pa]=ਵੇਹੜੇ 2 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 2 +Name[pt]=Mudar para o Ecrã 2 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 2 +Name[ro]=Trece în ecranul 2 +Name[ru]=Перейти на рабочий Ñтол 2 +Name[rw]=Hindura ku Biro 2 +Name[se]=Mana nuppi Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 2 +Name[sl]=Preklopi na namizje 2 +Name[sr]=Промени на радну површину 2 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 2 +Name[ss]=Tjintjela ku desktop 2 +Name[sv]=Byt till skrivbord 2 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 2கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 2కౠమారండి +Name[tg]=Гузариш ба мизи кории 2 +Name[th]=สลับไปยังพื้นที่หน้าจภ2 +Name[tr]=2. Masaüstüne Git +Name[tt]=2. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 2 +Name[uz]=Ish stoli 2ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 2га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhuvhili +Name[vi]=Äổi vá» Mà n hình ná»n 2 +Name[wa]=Potchî sol deujhinme sicribanne +Name[xh]=Tshintshela kwi Desktop 2 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 2 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢äºŒ +Name[zu]=Shintshela kwi-Desktop 2 +Comment=Virtual desktop two is selected +Comment[af]=Virtuele Werkskerm twee is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 2 هو المØدد +Comment[az]=2. Masa üstü seçildi +Comment[be]=Выбраны другі віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 2 +Comment[bn]=দà§à¦¬à¦¿à¦¤à§€à§Ÿ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Daou a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Dva je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual dos +Comment[cs]=Je vybrána virtuálnà plocha 2 +Comment[csb]=Wëbróny je drëdżi pùlt +Comment[da]=Virtuel desktop to er valgt +Comment[de]=Virtuelle Arbeitsfläche 2 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 2 +Comment[eo]=Tabulo dua estas elektita +Comment[es]=Seleccionado el escritorio virtual Dos +Comment[et]=Teine virtuaalne töölaud on valitud +Comment[eu]=Bigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی دو انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kaksi on valittu +Comment[fr]=Le bureau virtuel 2 est sélectionné +Comment[fy]=Firtueel buroblêd twa is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a dó +Comment[gl]=Escolle-se o escritório virtual número Dous +Comment[he]=שולחן העבודה הווירטו×לי ×©×ª×™×™× × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª दो चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 2 +Comment[hu]=A 2. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð tvö er virkt +Comment[it]=Il desktop virtuale due viene selezionato. +Comment[ja]=仮想デスクトップ 2 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეáƒáƒ ე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Екінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ២ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 2ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas antras menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 2. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 2 +Comment[ms]=Desktop maya dua dipilih +Comment[mt]=Desktop Virtwali Tnejn huwa Magħżul +Comment[nb]=Virtuelt skrivebord to er valgt +Comment[nds]=De virtuelle Schriefdisch Twee is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª २ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad twee is geselecteerd +Comment[nn]=Virtuelt skrivebord to vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਦੋ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano drugi pulpit +Comment[pt]=O ecrã virtual dois é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 2 está selecionada +Comment[ro]=Ecranul virtual 2 este selectat +Comment[ru]=Выбран второй виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara kabiri byatoranyijwe +Comment[se]=Nubbi virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 2 +Comment[sl]=Izbrano je drugo navidezno namizje +Comment[sr]=Изабрана је друга виртуелна радна површина +Comment[sr@Latn]=Izabrana je druga virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tvÃ¥ är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ இரணà¯à®Ÿà¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=రెండొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สà¸à¸‡à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü iki seçili +Comment[tt]=Ä°kençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 2 +Comment[uz]=Ikkinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Иккинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 2 đã được chá»n +Comment[wa]=Li deujhinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 2 +Comment[zh_TW]=虛擬桌é¢äºŒè¢«é¸å– +default_sound=KDE_Desktop_2.ogg +default_presentation=0 + +[desktop3] +Name=Change to Desktop 3 +Name[af]=Verander na Werkskerm 3 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 3 +Name[az]=3. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 3 +Name[bg]=Превключване на работен плот 3 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৩-ঠযাও +Name[br]=Gwintañ da vurev 3 +Name[bs]=Premjesti se na radnu povrÅ¡inu 3 +Name[ca]=Canvi a l'escriptori 3 +Name[cs]=PÅ™epnutà na plochu 3 +Name[csb]=Zmieni na pùlt 3 +Name[cy]=Newid i Penbwrdd 3 +Name[da]=Skift til desktop 3 +Name[de]=Wechsel Arbeitsfläche 3 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 3 +Name[eo]=Al tabulo 3 +Name[es]=Cambiar al escritorio 3 +Name[et]=Liikumine 3. töölauale +Name[eu]=Aldatu 3. mahaigainera +Name[fa]=تغییر به رومیزی Û³ +Name[fi]=Vaihda työpöytään 3 +Name[fr]=Aller au bureau 3 +Name[fy]=Gean nei buroblêd 3 +Name[ga]=Téigh go Deasc 3 +Name[gl]=Ir ao Escritório 3 +Name[he]=מעבר לשולחן עבודה 3 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 3 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 3 +Name[hu]=Váltás a 3. asztalra +Name[id]=Ganti ke Desktop 3 +Name[is]=Birta skjáborð 3 +Name[it]=Va al desktop 3 +Name[ja]=デスクトップ 3 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 3-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=3-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៣ +Name[ko]=ë°ìŠ¤í¬í†± 3으로 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 3 +Name[lt]=Pereiti į darbastalį 3 +Name[lv]=NomainÄ«t uz Darbvirsmu 3 +Name[mk]=Кон површина 3 +Name[mn]=Ðжлын тавцан 3 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 3 +Name[mt]=Mur f' Desktop 3 +Name[nb]=Bytt til skrivebord 3 +Name[nds]=Na Schriefdisch 3 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ३ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 3 +Name[nn]=Byt til skrivebord 3 +Name[nso]=Fetogela go Desktop 3 +Name[oc]=Canvia au burèu 3 +Name[pa]=ਵੇਹੜੇ 3 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 3 +Name[pt]=Mudar para o Ecrã 3 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 3 +Name[ro]=Trece în ecranul 3 +Name[ru]=Перейти на рабочий Ñтол 3 +Name[rw]=Hindura ku Biro 3 +Name[se]=Mana goalmmát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 3 +Name[sl]=Preklopi na namizje 3 +Name[sr]=Промени на радну површину 3 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 3 +Name[ss]=Tjintjela ku desktop 3 +Name[sv]=Byt till skrivbord 3 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 3கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 3కౠమారండి +Name[tg]=Гузариш ба мизи кории 3 +Name[th]=สลับไปยังพื้นที่หน้าจภ3 +Name[tr]=3. Masaüstüne Git +Name[tt]=3. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 3 +Name[uz]=Ish stoli 3ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 3га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhuraru +Name[vi]=Äổi vá» Mà n hình ná»n 3 +Name[wa]=Potchî sol troejhinme sicribanne +Name[xh]=Tshintshela kwi Desktop 3 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 3 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢ä¸‰ +Name[zu]=Shintshela kwi-Desktop 3 +Comment=Virtual desktop three is selected +Comment[af]=Virtuele Werkskerm drie is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم3 هو المØدد +Comment[az]=3. Masa üstü seçildi +Comment[be]=Выбраны Ñ‚Ñ€Ñці віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 3 +Comment[bn]=তৃতীয় à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Tri a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Tri je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual tres +Comment[cs]=Je vybrána virtuálnà plocha 3 +Comment[csb]=Wëbróny je trzecy pùlt +Comment[da]=Virtuel desktop tre er valgt +Comment[de]=Virtuelle Arbeitsfläche 3 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 2 +Comment[eo]=Tabulo tria estas elektita +Comment[es]=Seleccionado el escritorio virtual Tres +Comment[et]=Kolmas virtuaalne töölaud on valitud +Comment[eu]=Hirugarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی سه انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kolme on valittu +Comment[fr]=Le bureau virtuel 3 est sélectionné +Comment[fy]=Firtueel buroblêd trije is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a trà +Comment[gl]=Escolle-se o escritório virtual número Tres +Comment[he]=שולחן העבודה הווירטו×לי שלוש × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª तीन चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 3 +Comment[hu]=A 3. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð þrjú er virkt +Comment[it]=Il desktop virtuale tre viene selezionato. +Comment[ja]=仮想デスクトップ 3 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმესáƒáƒ›áƒ” სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Үшінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៣ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 3ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas treÄias menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 3. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 3 +Comment[ms]=Desktop maya tiga dipilih +Comment[mt]=Desktop Virtwali Tlieta huwa Magħżul +Comment[nb]=Virtuelt skrivebord tre er valgt +Comment[nds]=De virtuelle Schriefdisch Dree is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ३ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad drie is geselecteerd +Comment[nn]=Virtuelt skrivebord tre vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਤਿੰਨ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano trzeci pulpit +Comment[pt]=O ecrã virtual três é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 3 está selecionada +Comment[ro]=Ecranul virtual 3 este selectat +Comment[ru]=Выбран третий виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara gatatu byatoranyijwe +Comment[se]=Goalmmát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 3 +Comment[sl]=Izbrano je tretje navidezno namizje +Comment[sr]=Изабрана је трећа виртуелна радна површина +Comment[sr@Latn]=Izabrana je treća virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tre är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ மூனà¯à®±à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=à°®à±à°¡à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สามถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü üç seçili +Comment[tt]=Öçençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 3 +Comment[uz]=Uchinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Учинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 3 đã được chá»n +Comment[wa]=Li troejhinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 3 +Comment[zh_TW]=虛擬桌é¢ä¸‰è¢«é¸å– +default_sound=KDE_Desktop_3.ogg +default_presentation=0 + +[desktop4] +Name=Change to Desktop 4 +Name[af]=Verander na Werkskerm 4 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 4 +Name[az]=4. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 4 +Name[bg]=Превключване на работен плот 4 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৪-ঠযাও +Name[br]=Gwintañ da vurev 4 +Name[bs]=Premjesti se na radnu povrÅ¡inu 4 +Name[ca]=Canvi a l'escriptori 4 +Name[cs]=PÅ™epnutà na plochu 4 +Name[csb]=Zmieni na pùlt 4 +Name[cy]=Newid i Penbwrdd 4 +Name[da]=Skift til desktop 4 +Name[de]=Wechsel zu Arbeitsfläche 4 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 4 +Name[eo]=Al tabulo 4 +Name[es]=Cambiar al escritorio 4 +Name[et]=Liikumine 4. töölauale +Name[eu]=Aldatu 4. mahaigainera +Name[fa]=تغییر به رومیزی Û´ +Name[fi]=Vaihda työpöytään 4 +Name[fr]=Aller au bureau 4 +Name[fy]=Gean nei buroblêd 4 +Name[ga]=Téigh go Deasc 4 +Name[gl]=Ir ao Escritório 4 +Name[he]=מעבר לשולחן עבודה 4 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 4 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 4 +Name[hu]=Váltás a 4. asztalra +Name[id]=Ganti ke Desktop 4 +Name[is]=Birta skjáborð 4 +Name[it]=Va al desktop 4 +Name[ja]=デスクトップ 4 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 4-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=4-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៤ +Name[ko]=ë°ìŠ¤í¬í†± 4ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 4 +Name[lt]=Pereiti į darbastalį 4 +Name[lv]=NomainÄ«t uz Darbvirsmu 4 +Name[mk]=Кон површина 4 +Name[mn]=Ðжлын тавцан 41 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 4 +Name[mt]=Mur f' Desktop 4 +Name[nb]=Bytt til skrivebord 4 +Name[nds]=Na Schriefdisch 4 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ४ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 4 +Name[nn]=Byt til skrivebord 4 +Name[nso]=Fetogela go Desktop 4 +Name[oc]=Canvia au burèu 4 +Name[pa]=ਵੇਹੜੇ 4 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 4 +Name[pt]=Mudar para o Ecrã 4 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 4 +Name[ro]=Trece în ecranul 4 +Name[ru]=Перейти на рабочий Ñтол 4 +Name[rw]=Hindura ku Biro 4 +Name[se]=Mana njeallját Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 4 +Name[sl]=Preklopi na namizje 4 +Name[sr]=Промени на радну површину 4 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 4 +Name[ss]=Tjintjela ku desktop 4 +Name[sv]=Byt till skrivbord 4 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 4கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 4కౠమారండి +Name[tg]=Гузариш ба мизи кории 4 +Name[th]=สลับไปยังพื้นที่หน้าจภ4 +Name[tr]=4. Masaüstüne Git +Name[tt]=4. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 4 +Name[uz]=Ish stoli 4ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 4га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhuna +Name[vi]=Äổi vá» Mà n hình ná»n 4 +Name[wa]=Potchî sol cwatrinme sicribanne +Name[xh]=Tshintshela kwi Desktop 4 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 4 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å›› +Name[zu]=Shintshela kwi-Desktop 4 +Comment=Virtual desktop four is selected +Comment[af]=Virtuele Werkskerm vier is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 4 هو المØدد +Comment[az]=4. Masa üstü seçildi +Comment[be]=Выбраны чацвёрты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 4 +Comment[bn]=চতà§à¦°à§à¦¥ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Pevar a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina ÄŒetiri je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual quatre +Comment[cs]=Je vybrána virtuálnà plocha 4 +Comment[csb]=Wëbróny je czwiôrti pùlt +Comment[da]=Virtuel desktop fire er valgt +Comment[de]=Virtuelle Arbeitsfläche 4 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 4 +Comment[eo]=Tabulo kvara estas elektita +Comment[es]=Seleccionado el escritorio virtual Cuatro +Comment[et]=Neljas virtuaalne töölaud on valitud +Comment[eu]=Laugarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی چهار انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä neljä on valittu +Comment[fr]=Le bureau virtuel 4 est sélectionné +Comment[fy]=Firtueel buroblêd fjouwer is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a ceathair +Comment[gl]=Escolle-se o escritório virtual número Catro +Comment[he]=שולחן העבודה הווירטו×לי ×רבע × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª चार चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 4 +Comment[hu]=A 4. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð fjögur er virkt +Comment[it]=Il desktop virtuale quattro viene selezionato. +Comment[ja]=仮想デスクトップ 4 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეáƒáƒ—ხე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Төртінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៤ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 4ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas ketvirtas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 4. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 4 +Comment[ms]=Desktop maya empat dipilih +Comment[mt]=Desktop Virtwali Erbgħa huwa Magħżul +Comment[nb]=Virtuelt skrivebord fire er valgt +Comment[nds]=De virtuelle Schriefdisch Veer is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ४ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad vier is geselecteerd +Comment[nn]=Virtuelt skrivebord fire vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਚਾਰ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano czwarty pulpit +Comment[pt]=O ecrã virtual quatro é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 4 está selecionada +Comment[ro]=Ecranul virtual 4 este selectat +Comment[ru]=Выбран четвёртый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara kane byatoranyijwe +Comment[se]=Njeallját virtuella Äállinbeavde lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 4 +Comment[sl]=Izbrano je Äetrto navidezno namizje +Comment[sr]=Изабрана је четврта виртуелна радна површина +Comment[sr@Latn]=Izabrana je Äetvrta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord fyra är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ நானà¯à®•à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=నాలà±à°—ొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸ªà¸µà¹ˆà¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü dört seçili +Comment[tt]=Dürtençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 4 +Comment[uz]=ToÊ»rtinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Тўртинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 4 đã được chá»n +Comment[wa]=Li cwatrinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 4 +Comment[zh_TW]=虛擬桌é¢å››è¢«é¸å– +default_sound=KDE_Desktop_4.ogg +default_presentation=0 + +[desktop5] +Name=Change to Desktop 5 +Name[af]=Verander na Werkskerm 5 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 5 +Name[az]=5. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 5 +Name[bg]=Превключване на работен плот 5 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৫-ঠযাও +Name[br]=Gwintañ da vurev 5 +Name[bs]=Premjesti se na radnu povrÅ¡inu 5 +Name[ca]=Canvi a l'escriptori 5 +Name[cs]=PÅ™epnutà na plochu 5 +Name[csb]=Zmieni na pùlt 5 +Name[cy]=Newid i Penbwrdd 5 +Name[da]=Skift til desktop 5 +Name[de]=Wechsel zu Arbeitsfläche 5 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 5 +Name[eo]=Al tabulo 5 +Name[es]=Cambiar al escritorio 5 +Name[et]=Liikumine 5. töölauale +Name[eu]=Aldatu 5. mahaigainera +Name[fa]=تغییر به رومیزی Ûµ +Name[fi]=Vaihda työpöytään 5 +Name[fr]=Aller au bureau 5 +Name[fy]=Gean nei buroblêd 5 +Name[ga]=Téigh go Deasc 5 +Name[gl]=Ir ao Escritório 5 +Name[he]=מעבר לשולחן עבודה 5 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 5 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 5 +Name[hu]=Váltás az 5. asztalra +Name[id]=Ganti ke Desktop 5 +Name[is]=Birta skjáborð 5 +Name[it]=Va al desktop 5 +Name[ja]=デスクトップ 5 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 5-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=5-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៥ +Name[ko]=ë°ìŠ¤í¬í†± 5ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àºˆàº—ີ່ທຳງານ 5 +Name[lt]=Pereiti į darbastalį 5 +Name[lv]=NomainÄ«t uz Darbvirsmu 5 +Name[mk]=Кон површина 5 +Name[mn]=Ðжлын тавцан 5 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 5 +Name[mt]=Mur f' Desktop 5 +Name[nb]=Bytt til skrivebord 5 +Name[nds]=Na Schriefdisch 5 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ५ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 5 +Name[nn]=Byt til skrivebord 5 +Name[nso]=Fetogela go Desktop 5 +Name[oc]=Canvia au burèu 5 +Name[pa]=ਵੇਹੜੇ 5 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 5 +Name[pt]=Mudar para o Ecrã 5 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 5 +Name[ro]=Trece în ecranul 5 +Name[ru]=Перейти на рабочий Ñтол 5 +Name[rw]=Hindura ku Biro 5 +Name[se]=Mana viđát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 5 +Name[sl]=Preklopi na namizje 5 +Name[sr]=Промени на радну површину 5 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 5 +Name[ss]=Tjintjela ku desktop 5 +Name[sv]=Byt till skrivbord 5 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 5கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 5కౠమారండి +Name[tg]=Гузариш ба мизи кории 5 +Name[th]=สลับไปยังพื้นที่หน้าจภ5 +Name[tr]=5. Masaüstüne Git +Name[tt]=5. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 5 +Name[uz]=Ish stoli 5ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 5га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhutanu +Name[vi]=Äổi vá» Mà n hình ná»n 5 +Name[wa]=Potchî sol cénkinme sicribanne +Name[xh]=Tshintshela kwi Desktop 5 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 5 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢äº” +Name[zu]=Shintshela kwi-Desktop 5 +Comment=Virtual desktop five is selected +Comment[af]=Virtuele Werkskerm vyf is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 5 هو المØدد +Comment[az]=5. Masa üstü seçildi +Comment[be]=Выбраны пÑÑ‚Ñ‹ віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 5 +Comment[bn]=পঞà§à¦šà¦® à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Pemp a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Pet je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual cinc +Comment[cs]=Je vybrána virtuálnà plocha 5 +Comment[csb]=Wëbróny je piÄ…ti pùlt +Comment[da]=Virtuel desktop fem er valgt +Comment[de]=Virtuelle Arbeitsfläche 5 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 5 +Comment[eo]=Tabulo kvina estas elektita +Comment[es]=Seleccionado el escritorio virtual Cinco +Comment[et]=Viies virtuaalne töölaud on valitud +Comment[eu]=Bostgarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی پنج انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä viisi on valittu +Comment[fr]=Le bureau virtuel 5 est sélectionné +Comment[fy]=Firtueel buroblêd viif is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a cúig +Comment[gl]=Escolle-se o escritório virtual número Cinco +Comment[he]=שולחן העבודה הווירטו×לי חמש × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª पांच चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 5 +Comment[hu]=Az 5. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð fimm er virkt +Comment[it]=Il desktop virtuale cinque viene selezionato. +Comment[ja]=仮想デスクトップ 5 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეხუთე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=БеÑінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៥ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 5ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas penktas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 5. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 5 +Comment[ms]=Desktop maya lima dipilih +Comment[mt]=Desktop Virtwali Ħamsa huwa Magħżul +Comment[nb]=Virtuelt skrivebord fem er valgt +Comment[nds]=De virtuelle Schriefdisch Fief is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ५ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad vijf is geselecteerd +Comment[nn]=Virtuelt skrivebord fem vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਪੰਜ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano piÄ…ty pulpit +Comment[pt]=O ecrã virtual cinco é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 5 está selecionada +Comment[ro]=Ecranul virtual 5 este selectat +Comment[ru]=Выбран пÑтый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara gatanu byatoranyijwe +Comment[se]=Viđát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 5 +Comment[sl]=Izbrano je peto navidezno namizje +Comment[sr]=Изабрана је пета виртуелна радна површина +Comment[sr@Latn]=Izabrana je peta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord fem är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ à®à®¨à¯à®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=à°à°¦à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่ห้าถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü beÅŸ seçili +Comment[tt]=BiÅŸençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 5 +Comment[uz]=Beshinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Бешинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 5 đã được chá»n +Comment[wa]=Li cénkinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 5 +Comment[zh_TW]=虛擬桌é¢äº”被é¸å– +default_sound=KDE_Desktop_5.ogg +default_presentation=0 + +[desktop6] +Name=Change to Desktop 6 +Name[af]=Verander na Werkskerm 6 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 6 +Name[az]=6. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 6 +Name[bg]=Превключване на работен плот 6 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৬-ঠযাও +Name[br]=Gwintañ da vurev 6 +Name[bs]=Premjesti se na radnu povrÅ¡inu 6 +Name[ca]=Canvi a l'escriptori 6 +Name[cs]=PÅ™epnutà na plochu 6 +Name[csb]=Zmieni na pùlt 6 +Name[cy]=Newid i Penbwrdd 6 +Name[da]=Skift til desktop 6 +Name[de]=Wechsel zu Arbeitsfläche 6 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 6 +Name[eo]=Al tabulo 6 +Name[es]=Cambiar al escritorio 6 +Name[et]=Liikumine 6. töölauale +Name[eu]=Aldatu 6. mahaigainera +Name[fa]=تغییر به رومیزی Û¶ +Name[fi]=Vaihda työpöytään 6 +Name[fr]=Aller au bureau 6 +Name[fy]=Gean nei buroblêd 6 +Name[ga]=Téigh go Deasc 6 +Name[gl]=Ir ao Escritório 6 +Name[he]=מעבר לשולחן עבודה 6 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 6 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 6 +Name[hu]=Váltás a 6. asztalra +Name[id]=Ganti ke Desktop 6 +Name[is]=Birta skjáborð 6 +Name[it]=Va al desktop 6 +Name[ja]=デスクトップ 6 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 6-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=6-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៦ +Name[ko]=ë°ìŠ¤í¬í†± 6으로 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 6 +Name[lt]=Pereiti į darbastalį 6 +Name[lv]=NomainÄ«t uz Darbvirsmu 6 +Name[mk]=Кон површина 6 +Name[mn]=Ðжлын тавцан 6 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 6 +Name[mt]=Mur f' Desktop 6 +Name[nb]=Bytt til skrivebord 6 +Name[nds]=Na Schriefdisch 6 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ६ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 6 +Name[nn]=Byt til skrivebord 6 +Name[nso]=Fetogela go Desktop 6 +Name[oc]=Canvia au burèu 6 +Name[pa]=ਵੇਹੜੇ 6 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 6 +Name[pt]=Mudar para o Ecrã 6 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 6 +Name[ro]=Trece în ecranul 6 +Name[ru]=Перейти на рабочий Ñтол 6 +Name[rw]=Hindura ku Biro 6 +Name[se]=Mana guđát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 6 +Name[sl]=Preklopi na namizje 6 +Name[sr]=Промени на радну површину 6 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 6 +Name[ss]=Tjintjela ku desktop 6 +Name[sv]=Byt till skrivbord 6 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 6கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 6కౠమారండి +Name[tg]=Гузариш ба мизи кории 6 +Name[th]=สลับไปยังพื้นที่หน้าจภ6 +Name[tr]=6. Masaüstüne Git +Name[tt]=6. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 6 +Name[uz]=Ish stoli 6ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 6га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhurathi +Name[vi]=Äổi vá» Mà n hình ná»n 6 +Name[wa]=Potchî sol shijhinme sicribanne +Name[xh]=Tshintshela kwi Desktop 6 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 6 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å… +Name[zu]=Shintshela kwi-Desktop 6 +Comment=Virtual desktop six is selected +Comment[af]=Virtuele Werkskerm ses is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 6 هو المØدد +Comment[az]=6. Masa üstü seçildi +Comment[be]=Выбраны шоÑÑ‚Ñ‹ віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 6 +Comment[bn]=ষষà§à¦ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel C'hwec'h a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Å est je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual sis +Comment[cs]=Je vybrána virtuálnà plocha 6 +Comment[csb]=Wëbróny je szósti pùlt +Comment[da]=Virtuel desktop seks er valgt +Comment[de]=Virtuelle Arbeitsfläche 6 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 6 +Comment[eo]=Tabulo sesa estas elektita +Comment[es]=Seleccionado el escritorio virtual Seis +Comment[et]=Kuues virtuaalne töölaud on valitud +Comment[eu]=Seigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی شش انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kuusi on valittu +Comment[fr]=Le bureau virtuel 6 est sélectionné +Comment[fy]=Firtueel buroblêd seis is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a sé +Comment[gl]=Escolle-se o escritório virtual número Seis +Comment[he]=שולחן העבודה הווירטו×לי שש × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª छः चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 6 +Comment[hu]=A 6. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð sex er virkt +Comment[it]=Il desktop virtuale sei viene selezionato. +Comment[ja]=仮想デスクトップ 6 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეექვსე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Ðлтыншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៦ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 6ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas Å¡eÅ¡tas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 6. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 6 +Comment[ms]=Desktop maya enam dipilih +Comment[mt]=Desktop Virtwali Sitta huwa Magħżul +Comment[nb]=Virtuelt skrivebord seks er valgt +Comment[nds]=De virtuelle Schriefdisch Söss is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ६ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad zes is geselecteerd +Comment[nn]=Virtuelt skrivebord seks vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਛੇ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano szósty pulpit +Comment[pt]=O ecrã virtual seis é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 6 está selecionada +Comment[ro]=Ecranul virtual 6 este selectat +Comment[ru]=Выбран шеÑтой виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara gatandatu byatoranyijwe +Comment[se]=Guđát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 6 +Comment[sl]=Izbrano je Å¡esto navidezno namizje +Comment[sr]=Изабрана је шеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je Å¡esta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord sex är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ ஆற௠தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=ఆరొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่หà¸à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü altı seçili +Comment[tt]=Altınçı xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 6 +Comment[uz]=Oltinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Олтинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 6 đã được chá»n +Comment[wa]=Li shijhinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 6 +Comment[zh_TW]=虛擬桌é¢å…被é¸å– +default_sound=KDE_Desktop_6.ogg +default_presentation=0 + +[desktop7] +Name=Change to Desktop 7 +Name[af]=Verander na Werkskerm 7 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 7 +Name[az]=7. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 7 +Name[bg]=Превключване на работен плот 5 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª à§-ঠযাও +Name[br]=Gwintañ da vurev 7 +Name[bs]=Premjesti se na radnu povrÅ¡inu 7 +Name[ca]=Canvi a l'escriptori 7 +Name[cs]=PÅ™epnutà na plochu 7 +Name[csb]=Zmieni na pùlt 7 +Name[cy]=Newid i Penbwrdd 7 +Name[da]=Skift til desktop 7 +Name[de]=Wechsel zu Arbeitsfläche 7 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 7 +Name[eo]=Al tabulo 7 +Name[es]=Cambiar al escritorio 7 +Name[et]=Liikumine 7. töölauale +Name[eu]=Aldatu 7. mahaigainera +Name[fa]=تغییر به رومیزی Û· +Name[fi]=Vaihda työpöytään 7 +Name[fr]=Aller au bureau 7 +Name[fy]=Gean nei buroblêd 7 +Name[ga]=Téigh go Deasc 7 +Name[gl]=Ir ao Escritório 7 +Name[he]=מעבר לשולחן עבודה 7 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 7 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 7 +Name[hu]=Váltás a 7. asztalra +Name[id]=Ganti ke Desktop 7 +Name[is]=Birta skjáborð 7 +Name[it]=Va al desktop 7 +Name[ja]=デスクトップ 7 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 7-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=7-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៧ +Name[ko]=ë°ìŠ¤í¬í†± 7ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 7 +Name[lt]=Pereiti į darbastalį 7 +Name[lv]=NomainÄ«t uz Darbvirsmu 7 +Name[mk]=Кон површина 7 +Name[mn]=Ðжлын тавцан 7 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 7 +Name[mt]=Mur f' Desktop 7 +Name[nb]=Bytt til skrivebord 7 +Name[nds]=Na Schriefdisch 7 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ॠमा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 7 +Name[nn]=Byt til skrivebord 7 +Name[nso]=Fetogela go Desktop 7 +Name[oc]=Canvia au burèu 7 +Name[pa]=ਵੇਹੜੇ 7 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 7 +Name[pt]=Mudar para o Ecrã 7 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 7 +Name[ro]=Trece în ecranul 7 +Name[ru]=Перейти на рабочий Ñтол 7 +Name[rw]=Hindura ku Biro 7 +Name[se]=Mana ÄihÄÄet Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 7 +Name[sl]=Preklopi na namizje 7 +Name[sr]=Промени на радну површину 7 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 7 +Name[ss]=Tjintjela ku desktop 7 +Name[sv]=Byt till skrivbord 7 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 7கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 7కౠమారండి +Name[tg]=Гузариш ба мизи кории 7 +Name[th]=สลับไปยังพื้นที่หน้าจภ7 +Name[tr]=7. Masaüstüne Git +Name[tt]=7. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 7 +Name[uz]=Ish stoli 7ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 7га ўтиш +Name[ven]=U shandukele kha desikithopo ya vhusumbe +Name[vi]=Äổi vá» Mà n hình ná»n 7 +Name[wa]=Potchî sol setinme sicribanne +Name[xh]=Tshintshela kwi Desktop 7 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 7 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢ä¸ƒ +Name[zu]=Shintshela kwi-Desktop 7 +Comment=Virtual desktop seven is selected +Comment[af]=Virtuele Werkskerm sewe is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 7 هو المØدد +Comment[az]=7. Masa üstü seçildi +Comment[be]=Выбраны Ñёмы віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 7 +Comment[bn]=সপà§à¦¤à¦® à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Seizh a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Sedam je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual set +Comment[cs]=Je vybrána virtuálnà plocha 7 +Comment[csb]=Wëbróny je sódmy pùlt +Comment[da]=Virtuel desktop syv er valgt +Comment[de]=Virtuelle Arbeitsfläche 7 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 7 +Comment[eo]=Tabulo sepa estas elektita +Comment[es]=Seleccionado el escritorio virtual Siete +Comment[et]=Seitsmes virtuaalne töölaud on valitud +Comment[eu]=Zazpigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی Ù‡Ùت انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä seitsemän on valittu +Comment[fr]=Le bureau virtuel 7 est sélectionné +Comment[fy]=Firtueel buroblêd sân is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a seacht +Comment[gl]=Escolle-se o escritório virtual número Sete +Comment[he]=שולחן העבודה הווירטו×לי שבע × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª सात चà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 7 +Comment[hu]=A 7. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð sjö er virkt +Comment[it]=Il desktop virtuale sette viene selezionato. +Comment[ja]=仮想デスクトップ 7 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეშვიდე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Жетінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៧ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 7ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas septintas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 7. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 7 +Comment[ms]=Desktop maya tujuh dipilih +Comment[mt]=Desktop Virtwali Sebgħa huwa Magħżul +Comment[nb]=Virtuelt skrivebord syv er valgt +Comment[nds]=De virtuelle Schriefdisch Söben is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ॠचयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad zeven is geselecteerd +Comment[nn]=Virtuelt skrivebord sju vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਸੱਤ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano siódmy pulpit +Comment[pt]=O ecrã virtual sete é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 7 está selecionada +Comment[ro]=Ecranul virtual 7 este selectat +Comment[ru]=Выбран Ñедьмой виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara karindwi byatoranyijwe +Comment[se]=ÄŒihÄÄet virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 7 +Comment[sl]=Izbrano je sedmo navidezno namizje +Comment[sr]=Изабрана је Ñедма виртуелна радна површина +Comment[sr@Latn]=Izabrana je sedma virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord sju är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ à®à®´à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=à°à°¡à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่เจ็ดถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü yedi seçili +Comment[tt]=Cidençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 7 +Comment[uz]=Yettinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Еттинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 7 đã được chá»n +Comment[wa]=Li setinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 7 +Comment[zh_TW]=虛擬桌é¢ä¸ƒè¢«é¸å– +default_sound=KDE_Desktop_7.ogg +default_presentation=0 + +[desktop8] +Name=Change to Desktop 8 +Name[af]=Verander na Werkskerm 8 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 8 +Name[az]=8. Masa ÃœstünÉ™ Get +Name[be]=ПерайÑці на працоўны Ñтол 8 +Name[bg]=Превключване на работен плот 7 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৮-ঠযাও +Name[br]=Gwintañ da vurev 8 +Name[bs]=Premjesti se na radnu povrÅ¡inu 8 +Name[ca]=Canvi a l'escriptori 8 +Name[cs]=PÅ™epnutà na plochu 8 +Name[csb]=Zmieni na pùlt 8 +Name[cy]=Newid i Penbwrdd 8 +Name[da]=Skift til desktop 8 +Name[de]=Wechsel zu Arbeitsfläche 8 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 8 +Name[eo]=Al tabulo 8 +Name[es]=Cambiar al escritorio 8 +Name[et]=Liikumine 8. töölauale +Name[eu]=Aldatu 8. mahaigainera +Name[fa]=تغییر به رومیزی Û¸ +Name[fi]=Vaihda työpöytään 8 +Name[fr]=Aller au bureau 8 +Name[fy]=Gean nei buroblêd 8 +Name[ga]=Téigh go Deasc 8 +Name[gl]=Ir ao Escritório 8 +Name[he]=מעבר לשולחן עבודה 8 +Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª 8 पर जाà¤à¤ +Name[hr]=Prebaci se na radnu povrÅ¡inu 8 +Name[hu]=Váltás a 8. asztalra +Name[id]=Ganti ke Desktop 8 +Name[is]=Birta skjáborð 8 +Name[it]=Va al desktop 8 +Name[ja]=デスクトップ 8 ã«ç§»å‹• +Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ 8-ზე გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ +Name[kk]=8-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៨ +Name[ko]=ë°ìŠ¤í¬í†± 8ë¡œ 바꾸기 +Name[lo]=ສະລັບໄປàºàº±àº‡àºžàº·à»‰àº™àº—ີ່ທຳງານ 8 +Name[lt]=Pereiti į darbastalį 8 +Name[lv]=NomainÄ«t uz Darbvirsmu 8 +Name[mk]=Кон површина 8 +Name[mn]=Ðжлын тавцан 8 Ñ€Ò¯Ò¯ оч +Name[ms]=Ubah ke Desktop 8 +Name[mt]=Mur f' Desktop 8 +Name[nb]=Bytt til skrivebord 8 +Name[nds]=Na Schriefdisch 8 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ८ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 8 +Name[nn]=Byt til skrivebord 8 +Name[nso]=Fetogela go Desktop 8 +Name[oc]=Canvia au burèu 8 +Name[pa]=ਵੇਹੜੇ 8 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 8 +Name[pt]=Mudar para o Ecrã 8 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 8 +Name[ro]=Trece în ecranul 8 +Name[ru]=Перейти на рабочий Ñтол 8 +Name[rw]=Hindura ku Biro 8 +Name[se]=Mana gávccát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 8 +Name[sl]=Preklopi na namizje 8 +Name[sr]=Промени на радну површину 8 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 8 +Name[ss]=Tjintjela ku desktop 8 +Name[sv]=Byt till skrivbord 8 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 8கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 8కౠమారండి +Name[tg]=Гузариш ба мизи кории 8 +Name[th]=สลับไปยังพื้นที่หน้าจภ8 +Name[tr]=8. Masaüstüne Git +Name[tt]=8. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 8 +Name[uz]=Ish stoli 8ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 8га ўтиш +Name[ven]=U shandukela kha desikithopo ya vhumalo +Name[vi]=Äổi vá» Mà n hình ná»n 8 +Name[wa]=Potchî sol ûtinme sicribanne +Name[xh]=Tshintshela kwi Desktop 8 +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 8 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å…« +Name[zu]=Shintshela kwi-Desktop 8 +Comment=Virtual desktop eight is selected +Comment[af]=Virtuele Werkskerm agt is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 8 هو المØدد +Comment[az]=8. Masa üstü seçildi +Comment[be]=Выбраны воÑьмы віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 8 +Comment[bn]=অষà§à¦Ÿà¦® à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Eizh a zo diuzet +Comment[bs]=Virtualna radna povrÅ¡ina Osam je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual vuit +Comment[cs]=Je vybrána virtuálnà plocha 8 +Comment[csb]=Wëbróny je ósmy pùlt +Comment[da]=Virtuel desktop otte er valgt +Comment[de]=Virtuelle Arbeitsfläche 8 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 8 +Comment[eo]=Tabulo oka estas elektita +Comment[es]=Seleccionado el escritorio virtual Ocho +Comment[et]=Kaheksas virtuaalne töölaud on valitud +Comment[eu]=Zortzigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی هشت انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kahdeksan on valittu +Comment[fr]=Le bureau virtuel 8 est sélectionné +Comment[fy]=Firtueel buroblêd acht is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a hocht +Comment[gl]=Escolle-se o escritório virtual número Oito +Comment[he]=שולחן העבודה הווירטו×לי ×©×ž×•× ×” × ×‘×—×¨ +Comment[hi]=आà¤à¤¾à¤¸à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª आठचà¥à¤¨à¤¾ गया है +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 8 +Comment[hu]=A 8. virtuális asztal kiválasztva +Comment[is]=Sýndarskjáborð átta er virkt +Comment[it]=Il desktop virtuale otto viene selezionato. +Comment[ja]=仮想デスクトップ 8 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმერვე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Сегізінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៨ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 8ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas aÅ¡tuntas menamas darbastalis +Comment[lv]=VirtuÄlÄ Darbvirsma nr. 8. ir izvÄ“lÄ“ta +Comment[mk]=Избрана е виртуелната површина бр. 8 +Comment[ms]=Desktop maya lapan dipilih +Comment[mt]=Desktop Virtwali Tmienja huwa Magħżul +Comment[nb]=Virtuelt skrivebord Ã¥tte er valgt +Comment[nds]=De virtuelle Schriefdisch Acht is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ८ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad acht is geselecteerd +Comment[nn]=Virtuelt skrivebord Ã¥tte vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਅੱਠਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano ósmy pulpit +Comment[pt]=O ecrã virtual oito é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 8 está selecionada +Comment[ro]=Ecranul virtual 8 este selectat +Comment[ru]=Выбран воÑьмой виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara umunani byatoranyijwe +Comment[se]=Gávccát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 8 +Comment[sl]=Izbrano je osmo navidezno namizje +Comment[sr]=Изабрана је оÑма виртуелна радна површина +Comment[sr@Latn]=Izabrana je osma virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord Ã¥tta är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ எடà¯à®Ÿà¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=ఎనిమిదొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่à¹à¸›à¸”ถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü sekiz seçili +Comment[tt]=Sigezençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 8 +Comment[uz]=Sakkizinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Саккизинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 8 đã được chá»n +Comment[wa]=Li ûtinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 8 +Comment[zh_TW]=虛擬桌é¢å…«è¢«é¸å– +default_sound=KDE_Desktop_8.ogg +default_presentation=0 + +[desktop9] +Name=Change to Desktop 9 +Name[af]=Verander na Werkskerm 9 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 9 +Name[be]=ПерайÑці на працоўны Ñтол 9 +Name[bg]=Превключване на работен плот 9 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ৯-ঠযাও +Name[br]=Gwintañ da vurev 9 +Name[bs]=Premjesti se na radnu povrÅ¡inu 9 +Name[ca]=Canvi a l'escriptori 9 +Name[cs]=PÅ™epnutà na plochu 9 +Name[csb]=Zmieni na pùlt 9 +Name[da]=Skift til desktop 9 +Name[de]=Wechsel zu Arbeitsfläche 9 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 9 +Name[eo]=Al tabulo 9 +Name[es]=Cambiar al escritorio 9 +Name[et]=Liikumine 9. töölauale +Name[eu]=Aldatu 9. mahaigainera +Name[fa]=تغییر به رومیزی Û¹ +Name[fi]=Vaihda työpöytään 9 +Name[fr]=Aller au bureau 9 +Name[fy]=Gean nei buroblêd 9 +Name[ga]=Téigh go Deasc 9 +Name[gl]=Ir ao Escritório 9 +Name[he]=מעבר לשולחן עבודה 9 +Name[hr]=Prebaci se na radnu povrÅ¡inu 9 +Name[hu]=Váltás a 9. asztalra +Name[id]=Ganti ke Desktop 9 +Name[is]=Birta skjáborð 9 +Name[it]=Va al desktop 9 +Name[ja]=デスクトップ 9 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 9 +Name[kk]=9-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ៩ +Name[ko]=ë°ìŠ¤í¬í†± 9ë¡œ 바꾸기 +Name[lt]=Pereiti į 9 darbastalį +Name[mk]=Кон површина 9 +Name[ms]=Ubah ke Desktop 9 +Name[nb]=Bytt til skrivebord 9 +Name[nds]=Na Schriefdisch 9 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ९ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 9 +Name[nn]=Byt til skrivebord 9 +Name[pa]=ਵੇਹੜੇ 9 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 9 +Name[pt]=Mudar para o Ecrã 9 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 9 +Name[ro]=Trece în ecranul 9 +Name[ru]=Перейти на рабочий Ñтол 9 +Name[rw]=Hindura ku Biro 9 +Name[se]=Mana ovccát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 9 +Name[sl]=Preklopi na namizje 9 +Name[sr]=Промени на радну површину 9 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 9 +Name[sv]=Byt till skrivbord 9 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 9கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 9కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ9 +Name[tr]=9. Masaüstüne Git +Name[tt]=9. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 9 +Name[uz]=Ish stoli 9ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 9га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 9 +Name[wa]=Potchî sol nouvinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 9 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢ä¹ +Comment=Virtual desktop nine is selected +Comment[af]=Virtuele Werkskerm nege is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 9 هو المØدد +Comment[be]=Выбраны дзевÑÑ‚Ñ‹ віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 9 +Comment[bn]=নবম à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Nav a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Devet je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual nou +Comment[cs]=Je vybrána virtuálnà plocha 9 +Comment[csb]=Wëbróny je dzewiÄ…ti pùlt +Comment[da]=Virtuel desktop ni er valgt +Comment[de]=Virtuelle Arbeitsfläche 9 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 9 +Comment[eo]=Tabulo naÅa estas elektita +Comment[es]=Seleccionado el escritorio virtual Nueve +Comment[et]=Ãœheksas virtuaalne töölaud on valitud +Comment[eu]=Bederatzigarren mahaigain birtuala hautatua dago. +Comment[fa]=رومیزی مجازی نه انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä yhdeksän on valittu +Comment[fr]=Le bureau virtuel 9 est sélectionné +Comment[fy]=Firtueel buroblêd njoggen is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a naoi +Comment[gl]=Selecciona-se o escritório virtual número nove +Comment[he]=שולחן העבודה הווירטו×לי תשע × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 9 +Comment[hu]=A 9. asztal kiválasztva +Comment[is]=Sýndarskjáborð nÃu er virkt +Comment[it]=Il desktop virtuale nove viene selezionato. +Comment[ja]=仮想デスクトップ 9 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეცხრე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Тоғызыншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ៩ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 9ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas devintas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 9 +Comment[ms]=Desktop maya sembilan dipilih +Comment[nb]=Virtuelt skrivebord ni er valgt +Comment[nds]=De virtuelle Schriefdisch Negen is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ९ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad negen is geselecteerd +Comment[nn]=Virtuelt skrivebord ni vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਨੌਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano dziewiÄ…ty pulpit +Comment[pt]=O ecrã virtual nove é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 9 está selecionada +Comment[ro]=Ecranul virtual 9 este selectat +Comment[ru]=Выбран девÑтый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara icyenda byatoranyijwe +Comment[se]=Ovccát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 9 +Comment[sl]=Izbrano je deveto navidezno namizje +Comment[sr]=Изабрана је девета виртуелна радна површина +Comment[sr@Latn]=Izabrana je deveta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord nio är valt +Comment[te]=తొమà±à°®à°¿à°¦à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่เà¸à¹‰à¸²à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü dokuz seçili +Comment[tt]=Tuğızınçı xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 9 +Comment[uz]=ToÊ»qqizinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Тўққизинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 9 đã được chá»n +Comment[wa]=Li nouvinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 9 +Comment[zh_TW]=虛擬桌é¢ä¹è¢«é¸å– +default_presentation=0 + +[desktop10] +Name=Change to Desktop 10 +Name[af]=Verander na Werkskerm 10 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 10 +Name[be]=ПерайÑці на працоўны Ñтол 10 +Name[bg]=Превключване на работен плот 10 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১০-ঠযাও +Name[br]=Gwintañ da vurev 10 +Name[bs]=Premjesti se na radnu povrÅ¡inu 10 +Name[ca]=Canvi a l'escriptori 10 +Name[cs]=PÅ™epnutà na plochu 10 +Name[csb]=Zmieni na pùlt 10 +Name[da]=Skift til desktop 10 +Name[de]=Wechsel zu Arbeitsfläche 10 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 10 +Name[eo]=Al tabulo 10 +Name[es]=Cambiar al escritorio 10 +Name[et]=Liikumine 10. töölauale +Name[eu]=Aldatu 10. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û° +Name[fi]=Vaihda työpöytään 10 +Name[fr]=Aller au bureau 10 +Name[fy]=Gean nei buroblêd 10 +Name[ga]=Téigh go Deasc 10 +Name[gl]=Ir ao Escritório 10 +Name[he]=מעבר לשולחן עבודה 10 +Name[hr]=Prebaci se na radnu povrÅ¡inu 10 +Name[hu]=Váltás a 10. asztalra +Name[id]=Ganti ke Desktop 10 +Name[is]=Birta skjáborð 10 +Name[it]=Va al desktop 10 +Name[ja]=デスクトップ 10 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 10 +Name[kk]=10-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១០+Name[ko]=ë°ìŠ¤í¬í†± 10으로 바꾸기 +Name[lt]=Pereiti į 10 darbastalį +Name[mk]=Кон површина 10 +Name[ms]=Ubah ke Desktop 10 +Name[nb]=Bytt til skrivebord 10 +Name[nds]=Na Schriefdisch 10 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १० मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 10 +Name[nn]=Byt til skrivebord 10 +Name[pa]=ਵੇਹੜੇ 10 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 10 +Name[pt]=Mudar para o Ecrã 10 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 10 +Name[ro]=Trece în ecranul 10 +Name[ru]=Перейти на рабочий Ñтол 10 +Name[rw]=Hindura ku Biro 10 +Name[se]=Mana logát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 10 +Name[sl]=Preklopi na namizje 10 +Name[sr]=Промени на радну површину 10 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 10 +Name[sv]=Byt till skrivbord 10 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 10கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 10కౠమారండి +Name[th]=สลับไปยังพื้นที่ทำหน้าจภ10 +Name[tr]=10. Masaüstüne Git +Name[tt]=10. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 10 +Name[uz]=Ish stoli 10ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 10га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 10 +Name[wa]=Potchî sol dijhinme scribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 10 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å +Comment=Virtual desktop ten is selected +Comment[af]=Virtuele Werkskerm tien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 10 هو المØدد +Comment[be]=Выбраны дзеÑÑÑ‚Ñ‹ віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 10 +Comment[bn]=দশম à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Dek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Deset je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual deu +Comment[cs]=Je vybrána virtuálnà plocha 10 +Comment[csb]=Wëbróny je dzesÄ…ti pùlt +Comment[da]=Virtuel desktop ti er valgt +Comment[de]=Virtuelle Arbeitsfläche 10 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 10 +Comment[eo]=Tabulo deka estas elektita +Comment[es]=Seleccionado el escritorio virtual Diez +Comment[et]=Kümnes virtuaalne töölaud on valitud +Comment[eu]=Hamargarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی ده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kymmenen on valittu +Comment[fr]=Le bureau virtuel 10 est sélectionné +Comment[fy]=Firtueel buroblêd tsien is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a deich +Comment[gl]=Escolle-se o escritório virtual número dez +Comment[he]=שולחן העבודה הווירטו×לי עשר × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 10 +Comment[hu]=A 10. asztal kiválasztva +Comment[is]=Sýndarskjáborð tÃu er virkt +Comment[it]=Il desktop virtuale dieci viene selezionato. +Comment[ja]=仮想デスクトップ 10 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეáƒáƒ—ე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Оныншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១០ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 10ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas deÅ¡imtas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 10 +Comment[ms]=Desktop maya sepuluh dipilih +Comment[nb]=Virtuelt skrivebord ti er valgt +Comment[nds]=De virtuelle Schriefdisch Teihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १० चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad tien is geselecteerd +Comment[nn]=Virtuelt skrivebord ti vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਦਸ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano dziesiÄ…ty pulpit +Comment[pt]=O ecrã virtual dez é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 10 está selecionada +Comment[ro]=Ecranul virtual 10 este selectat +Comment[ru]=Выбран деÑÑтый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara icumi byatoranyijwe +Comment[se]=Logát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 10 +Comment[sl]=Izbrano je deseto navidezno namizje +Comment[sr]=Изабрана је деÑета виртуелна радна површина +Comment[sr@Latn]=Izabrana je deseta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tio är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதà¯à®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบดถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on seçili +Comment[tt]=Unınçı xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 10 +Comment[uz]=OÊ»ninchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўнинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 10 đã được chá»n +Comment[wa]=Li dijhinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 10 +Comment[zh_TW]=虛擬桌é¢å被é¸å– +default_presentation=0 + +[desktop11] +Name=Change to Desktop 11 +Name[af]=Verander na Werkskerm 11 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 11 +Name[be]=ПерайÑці на працоўны Ñтол 11 +Name[bg]=Превключване на работен плот 11 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১১-য় যাও +Name[br]=Gwintañ da vurev 11 +Name[bs]=Premjesti se na radnu povrÅ¡inu 11 +Name[ca]=Canvi a l'escriptori 11 +Name[cs]=PÅ™epnutà na plochu 11 +Name[csb]=Zmieni na pùlt 11 +Name[da]=Skift til desktop 11 +Name[de]=Wechsel zu Arbeitsfläche 11 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 11 +Name[eo]=Al tabulo 11 +Name[es]=Cambiar al escritorio 11 +Name[et]=Liikumine 11. töölauale +Name[eu]=Aldatu 11. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û± +Name[fi]=Vaihda työpöytään 11 +Name[fr]=Aller au bureau 11 +Name[fy]=Gean nei buroblêd 11 +Name[ga]=Téigh go Deasc 11 +Name[gl]=Ir ao Escritório 11 +Name[he]=מעבר לשולחן עבודה 11 +Name[hr]=Prebaci se na radnu povrÅ¡inu 11 +Name[hu]=Váltás a 11. asztalra +Name[id]=Ganti ke Desktop 12 +Name[is]=Birta skjáborð 11 +Name[it]=Va al desktop 11 +Name[ja]=デスクトップ 11 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 11 +Name[kk]=11-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១១ +Name[ko]=ë°ìŠ¤í¬í†± 11ë¡œ 바꾸기 +Name[lt]=Pereiti į 11 darbastalį +Name[mk]=Кон површина 11 +Name[ms]=Ubah ke Desktop 11 +Name[nb]=Bytt til skrivebord 11 +Name[nds]=Na Schriefdisch 11 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª ११ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 11 +Name[nn]=Byt til skrivebord 11 +Name[pa]=ਵੇਹੜੇ 11 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 11 +Name[pt]=Mudar para o Ecrã 11 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 11 +Name[ro]=Trece în ecranul 11 +Name[ru]=Перейти на рабочий Ñтол 11 +Name[rw]=Hindura ku Biro 11 +Name[se]=Mana oktanuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 11 +Name[sl]=Preklopi na namizje 11 +Name[sr]=Промени на радну површину 11 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 11 +Name[sv]=Byt till skrivbord 11 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 11கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 11కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ11 +Name[tr]=11. Masaüstüne Git +Name[tt]=11. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 11 +Name[uz]=Ish stoli 11ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 11га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 11 +Name[wa]=Potchî sol onzinme scribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 11 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å一 +Comment=Virtual desktop eleven is selected +Comment[af]=Virtuele Werkskerm elf is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 11 هو المØدد +Comment[be]=Выбраны адзінаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 11 +Comment[bn]=à¦à¦•à¦¾à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel Unnek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Jedanaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual onze +Comment[cs]=Je vybrána virtuálnà plocha 11 +Comment[csb]=Wëbróny je jednôsti pùlt +Comment[da]=Virtuel desktop elleve er valgt +Comment[de]=Virtuelle Arbeitsfläche 11 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 11 +Comment[eo]=Tabulo dek unua estas elektita +Comment[es]=Seleccionado el escritorio virtual Once +Comment[et]=Ãœheteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamaikagarren. mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی یازده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä yksitoista on valittu +Comment[fr]=Le bureau virtuel 11 est sélectionné +Comment[fy]=Firtueel buroblêd alve is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a haon déag +Comment[gl]=Escolle-se o escritório virtual número once +Comment[he]=שולחן העבודה הווירטו×לי ×חד־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 11 +Comment[hu]=A 11. asztal kiválasztva +Comment[is]=Sýndarskjáborð ellefu er virkt +Comment[it]=Il desktop virtuale undici viene selezionato. +Comment[ja]=仮想デスクトップ 11 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთერთმეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он бірінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១១ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 11ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas vienuoliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 11 +Comment[ms]=Desktop maya sebelas dipilih +Comment[nb]=Virtuelt skrivebord elleve er valgt +Comment[nds]=De virtuelle Schriefdisch Ölven is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª ११ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad elf is geselecteerd +Comment[nn]=Virtuelt skrivebord elleve vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਗਿਆਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano jedenasty pulpit +Comment[pt]=O ecrã virtual onze é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 11 está selecionada +Comment[ro]=Ecranul virtual 11 este selectat +Comment[ru]=Выбран одиннадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na rimwe byatoranyijwe +Comment[se]=Oktanuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 11 +Comment[sl]=Izbrano je enajsto navidezno namizje +Comment[sr]=Изабрана је једанаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je jedanaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord elva är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினொனà¯à®±à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదకొండొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบเà¸à¹‡à¸”ดถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on bir seçili +Comment[tt]=Unberençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 11 +Comment[uz]=OÊ»n birinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн биринчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 11 đã được chá»n +Comment[wa]=L' onzinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 11 +Comment[zh_TW]=虛擬桌é¢å一被é¸å– +default_presentation=0 + +[desktop12] +Name=Change to Desktop 12 +Name[af]=Verander na Werkskerm 12 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ رقم 12 +Name[be]=ПерайÑці на працоўны Ñтол 12 +Name[bg]=Превключване на работен плот 12 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১২-য় যাওও +Name[br]=Gwintañ da vurev 12 +Name[bs]=Premjesti se na radnu povrÅ¡inu 12 +Name[ca]=Canvi a l'escriptori 12 +Name[cs]=PÅ™epnutà na plochu 12 +Name[csb]=Zmieni na pùlt 12 +Name[da]=Skift til desktop 12 +Name[de]=Wechsel zu Arbeitsfläche 12 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 12 +Name[eo]=Al tabulo 12 +Name[es]=Cambiar al escritorio 12 +Name[et]=Liikumine 12. töölauale +Name[eu]=Aldatu 12. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û² +Name[fi]=Vaihda työpöytään 12 +Name[fr]=Aller au bureau 12 +Name[fy]=Gean nei buroblêd 12 +Name[ga]=Téigh go Deasc 12 +Name[gl]=Ir ao Escritório 12 +Name[he]=1מעבר לשולחן עבודה 2 +Name[hr]=Prebaci se na radnu povrÅ¡inu 12 +Name[hu]=Váltás a 12. asztalra +Name[id]=Ganti ke Desktop 12 +Name[is]=Birta skjáborð 12 +Name[it]=Va al desktop 12 +Name[ja]=デスクトップ 12 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 12 +Name[kk]=12-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១២ +Name[ko]=ë°ìŠ¤í¬í†± 12ë¡œ 바꾸기 +Name[lt]=Pereiti į 12 darbastalį +Name[mk]=Кон површина 12 +Name[ms]=Ubah ke Desktop 12 +Name[nb]=Bytt til skrivebord 12 +Name[nds]=Na Schriefdisch 12 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १२ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 12 +Name[nn]=Byt til skrivebord 12 +Name[pa]=ਵੇਹੜੇ 12 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 12 +Name[pt]=Mudar para o Ecrã 12 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 12 +Name[ro]=Trece în ecranul 12 +Name[ru]=Перейти на рабочий Ñтол 12 +Name[rw]=Hindura ku Biro 12 +Name[se]=Mana guoktenuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 12 +Name[sl]=Preklopi na namizje 12 +Name[sr]=Промени на радну површину 12 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 12 +Name[sv]=Byt till skrivbord 12 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 12கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 12కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ12 +Name[tr]=12. Masaüstüne Git +Name[tt]=12. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 12 +Name[uz]=Ish stoli 12ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 12га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 12 +Name[wa]=Potchî sol dozinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 12 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å二 +Comment=Virtual desktop twelve is selected +Comment[af]=Virtuele Werkskerm twaalf is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 12 هو المØدد +Comment[be]=Выбраны дванаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 12 +Comment[bn]=দà§à¦¬à¦¾à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel daouzek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Dvanaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual dotze +Comment[cs]=Je vybrána virtuálnà plocha 12 +Comment[csb]=Wëbróny je dwanôsti pùlt +Comment[da]=Virtuel desktop tolv er valgt +Comment[de]=Virtuelle Arbeitsfläche 12 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 12 +Comment[eo]=Tabulo dek dua estas elektita +Comment[es]=Seleccionado el escritorio virtual Doce +Comment[et]=Kaheteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamabigarren mahaigain birtuala hautatua dago. +Comment[fa]=رومیزی مجازی دوازده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kaksitoista on valittu +Comment[fr]=Le bureau virtuel 12 est sélectionné +Comment[fy]=Firtueel buroblêd tolve is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a dó dhéag +Comment[gl]=Escolle-se o escritório virtual número doze +Comment[he]=שולחן העבודה הווירטו×לי שתיי×־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 12 +Comment[hu]=A 12. asztal kiválasztva +Comment[is]=Sýndarskjáborð tólf er virkt +Comment[it]=Il desktop virtuale dodici viene selezionato. +Comment[ja]=仮想デスクトップ 12 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთáƒáƒ მეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он екінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១២ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 12ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas dvyliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 12 +Comment[ms]=Desktop maya dua belas dipilih +Comment[nb]=Virtuelt skrivebord tolv er valgt +Comment[nds]=De virtuelle Schriefdisch Twölf is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १२ चयन गरिà¤à¤•à¥‹ छ +Comment[nn]=Virtuelt skrivebord tolv vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਬਾਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano dwunasty pulpit +Comment[pt]=O ecrã virtual doze é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 12 está selecionada +Comment[ro]=Ecranul virtual 12 este selectat +Comment[ru]=Выбран двенадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na kabiri byatoranyijwe +Comment[se]=Guoktenuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 12 +Comment[sl]=Izbrano je dvanajsto navidezno namizje +Comment[sr]=Изабрана је дванаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je dvanaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tolv är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பனà¯à®©à®¿à®°à¯†à®£à¯à®Ÿà¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పనà±à°¨à±†à°‚డొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบสà¸à¸‡à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on iki seçili +Comment[tt]=Unikençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 12 +Comment[uz]=OÊ»n ikkinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн иккинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 12 đã được chá»n +Comment[wa]=Li dozinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 12 +Comment[zh_TW]=虛擬桌é¢å二被é¸å– +default_presentation=0 + +[desktop13] +Name=Change to Desktop 13 +Name[af]=Verander na Werkskerm 13 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 13 +Name[be]=ПерайÑці на працоўны Ñтол 13 +Name[bg]=Превключване на работен плот 13 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৩-য় যাও +Name[br]=Gwintañ da vurev 13 +Name[bs]=Premjesti se na radnu povrÅ¡inu 13 +Name[ca]=Canvi a l'escriptori 13 +Name[cs]=PÅ™epnutà na plochu 13 +Name[csb]=Zmieni na pùlt 13 +Name[da]=Skift til desktop 13 +Name[de]=Wechsel Arbeitsfläche 13 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 13 +Name[eo]=Al tabulo 13 +Name[es]=Cambiar al escritorio 13 +Name[et]=Liikumine 13. töölauale +Name[eu]=Aldatu 13. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û³ +Name[fi]=Vaihda työpöytään 13 +Name[fr]=Aller au bureau 13 +Name[fy]=Gean nei buroblêd 13 +Name[ga]=Téigh go Deasc 13 +Name[gl]=Ir ao Escritório 13 +Name[he]=מעבר לשולחן עבודה 13 +Name[hr]=Prebaci se na radnu povrÅ¡inu 13 +Name[hu]=Váltás a 13. asztalra +Name[id]=Ganti ke Desktop 13 +Name[is]=Birta skjáborð 13 +Name[it]=Va al desktop 13 +Name[ja]=デスクトップ 13 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 13 +Name[kk]=13-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៣ +Name[ko]=ë°ìŠ¤í¬í†± 13으로 바꾸기 +Name[lt]=Pereiti į 13 darbastalį +Name[mk]=Кон површина 13 +Name[ms]=Ubah ke Desktop 13 +Name[nb]=Bytt til skrivebord 13 +Name[nds]=Na Schriefdisch 13 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १३ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 13 +Name[nn]=Byt til skrivebord 13 +Name[pa]=ਵੇਹੜੇ 13 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 13 +Name[pt]=Mudar para o Ecrã 13 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 13 +Name[ro]=Trece în ecranul 13 +Name[ru]=Перейти на рабочий Ñтол 13 +Name[rw]=Hindura ku Biro 13 +Name[se]=Mana golbmanuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 13 +Name[sl]=Preklopi na namizje 13 +Name[sr]=Промени на радну површину 13 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 13 +Name[sv]=Byt till skrivbord 13 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 13கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 13కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ13 +Name[tr]=13. Masaüstüne Git +Name[tt]=13. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 13 +Name[uz]=Ish stoli 13ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 13га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 13 +Name[wa]=Potchî sol trazinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 13 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å三 +Comment=Virtual desktop thirteen is selected +Comment[af]=Virtuele Werkskerm dertien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 13 هو المØدد +Comment[be]=Выбраны трынаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 13 +Comment[bn]=তà§à¦°à§Ÿà§‹à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel trizek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Trinaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual tretze +Comment[cs]=Je vybrána virtuálnà plocha 13 +Comment[csb]=Wëbróny je trzënôsti pùlt +Comment[da]=Virtuel desktop tretten er valgt +Comment[de]=Virtuelle Arbeitsfläche 13 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 13 +Comment[eo]=Tabulo dek tria estas elektita +Comment[es]=Seleccionado el escritorio virtual Trece +Comment[et]=Kolmeteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamahirugarren mahaigain birtuala hautatua dago. +Comment[fa]=رومیزی مجازی سیزده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kolmetoista on valittu +Comment[fr]=Le bureau virtuel 13 est sélectionné +Comment[fy]=Firtueel buroblêd trettjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a trà déag +Comment[gl]=Escolle-se o escritório virtual número Trece +Comment[he]=שולחן העבודה הווירטו×לי שלוש־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 13 +Comment[hu]=A 13. asztal kiválasztva +Comment[is]=Sýndarskjáborð þrettán er virkt +Comment[it]=Il desktop virtuale tredici viene selezionato. +Comment[ja]=仮想デスクトップ 13 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეცáƒáƒ›áƒ”ტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он үшінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៣ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 13ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas tryliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 13 +Comment[ms]=Desktop maya tiga belas dipilih +Comment[nb]=Virtuelt skrivebord tretten er valgt +Comment[nds]=De virtuelle Schriefdisch Dörteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १३ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad dertien is geselecteerd +Comment[nn]=Virtuelt skrivebord tretten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਤੇਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano trzynasty pulpit +Comment[pt]=O ecrã virtual treze é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 13 está selecionada +Comment[ro]=Ecranul virtual 13 este selectat +Comment[ru]=Выбран тринадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na gatatu byatoranyijwe +Comment[se]=Golbmanuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 13 +Comment[sl]=Izbrano je trinajsto navidezno namizje +Comment[sr]=Изабрана је тринаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je trinaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tretton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினà¯à®®à¯‚னà¯à®±à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదమà±à°¡à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸ªà¸´à¸šà¸ªà¸²à¸¡à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on üç seçili +Comment[tt]=Unöçençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 13 +Comment[uz]=OÊ»n uchinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн учинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 13 đã được chá»n +Comment[wa]=Li trazinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 13 +Comment[zh_TW]=虛擬桌é¢å三被é¸å– +default_presentation=0 + +[desktop14] +Name=Change to Desktop 14 +Name[af]=Verander na Werkskerm 14 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 14 +Name[be]=ПерайÑці на працоўны Ñтол 14 +Name[bg]=Превключване на работен плот 14 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৪-য় যাও +Name[br]=Gwintañ da vurev 14 +Name[bs]=Premjesti se na radnu povrÅ¡inu 14 +Name[ca]=Canvi a l'escriptori 14 +Name[cs]=PÅ™epnutà na plochu 14 +Name[csb]=Zmieni na pùlt 14 +Name[da]=Skift til desktop 14 +Name[de]=Wechsel zu Arbeitsfläche 14 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 14 +Name[eo]=Al tabulo 14 +Name[es]=Cambiar al escritorio 14 +Name[et]=Liikumine 14. töölauale +Name[eu]=Aldatu 14. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û´ +Name[fi]=Vaihda työpöytään 14 +Name[fr]=Aller au bureau 14 +Name[fy]=Gean nei buroblêd 14 +Name[ga]=Téigh go Deasc 14 +Name[gl]=Ir ao Escritório 14 +Name[he]=1מעבר לשולחן עבודה 4 +Name[hr]=Prebaci se na radnu povrÅ¡inu 14 +Name[hu]=Váltás a 14. asztalra +Name[id]=Ganti ke Desktop 14 +Name[is]=Birta skjáborð 14 +Name[it]=Va al desktop 14 +Name[ja]=デスクトップ 14 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 14 +Name[kk]=14-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៤ +Name[ko]=ë°ìŠ¤í¬í†± 14ë¡œ 바꾸기 +Name[lt]=Pereiti į 14 darbastalį +Name[mk]=Кон површина 14 +Name[ms]=Ubah ke Desktop 14 +Name[nb]=Bytt til skrivebord 14 +Name[nds]=Na Schriefdisch 14 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १४ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 14 +Name[nn]=Byt til skrivebord 14 +Name[pa]=ਵੇਹੜੇ 14 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 14 +Name[pt]=Mudar para o Ecrã 14 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 14 +Name[ro]=Trece în ecranul 14 +Name[ru]=Перейти на рабочий Ñтол 14 +Name[rw]=Hindura ku Biro 14 +Name[se]=Mana njealljenuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 14 +Name[sl]=Preklopi na namizje 14 +Name[sr]=Промени на радну површину 14 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 14 +Name[sv]=Byt till skrivbord 14 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 14கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 14కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ14 +Name[tr]=14. Masaüstüne Git +Name[tt]=14. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 14 +Name[uz]=Ish stoli 14ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 14га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 14 +Name[wa]=Potchî sol catoirzinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 14 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢åå›› +Comment=Virtual desktop fourteen is selected +Comment[af]=Virtuele Werkskerm veertien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 14 هو المØدد +Comment[be]=Выбраны чатырнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 14 +Comment[bn]=চতà§à¦°à§à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel pevarezk a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina ÄŒetrnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual catorze +Comment[cs]=Je vybrána virtuálnà plocha 14 +Comment[csb]=Wëbróny je sztërnôsti pùlt +Comment[da]=Virtuel desktop fjorten er valgt +Comment[de]=Virtuelle Arbeitsfläche 14 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 14 +Comment[eo]=Tabulo dek kvara estas elektita +Comment[es]=Seleccionado el escritorio virtual Catorce +Comment[et]=Neljateistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamalaugarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی چهارده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä neljätoista on valittu +Comment[fr]=Le bureau virtuel 14 est sélectionné +Comment[fy]=Firtueel buroblêd fjirtjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a ceathair déag +Comment[gl]=Escolle-se o escritório virtual número catorce +Comment[he]=שולחן העבודה הווירטו×לי ×רבע־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 14 +Comment[hu]=A 14. asztal kiválasztva +Comment[is]=Sýndarskjáborð fjórtán er virkt +Comment[it]=Il desktop virtuale quattordici viene selezionato. +Comment[ja]=仮想デスクトップ 14 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთáƒáƒ—ხმეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он төртінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៤ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 14ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas keturioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 14 +Comment[ms]=Desktop maya empat belas dipilih +Comment[nb]=Virtuelt skrivebord fjorten er valgt +Comment[nds]=De virtuelle Schriefdisch Veerteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १४ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad veertien is geselecteerd +Comment[nn]=Virtuelt skrivebord fjorten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਚੌਦਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano czternasty pulpit +Comment[pt]=O ecrã virtual catorze é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 14 está selecionada +Comment[ro]=Ecranul virtual 14 este selectat +Comment[ru]=Выбран четырнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na kane byatoranyijwe +Comment[se]=Njealljánuppelogát virtuella Äállinbeavde lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 14 +Comment[sl]=Izbrano je Å¡tirinajsto navidezno namizje +Comment[sr]=Изабрана је четрнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je Äetrnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord fjorton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினானà¯à®•à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదà±à°¨à°¾à°²à±à°—ొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบสี่ถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on dört seçili +Comment[tt]=Undürtençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 14 +Comment[uz]=OÊ»n toÊ»rtinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн тўртинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 14 đã được chá»n +Comment[wa]=Li catoirzinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 14 +Comment[zh_TW]=虛擬桌é¢å四被é¸å– +default_presentation=0 + +[desktop15] +Name=Change to Desktop 15 +Name[af]=Verander na Werkskerm 15 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 15 +Name[be]=ПерайÑці на працоўны Ñтол 15 +Name[bg]=Превключване на работен плот 15 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৫-য় যাও +Name[br]=Gwintañ da vurev 15 +Name[bs]=Premjesti se na radnu povrÅ¡inu 15 +Name[ca]=Canvi a l'escriptori 15 +Name[cs]=PÅ™epnutà na plochu 15 +Name[csb]=Zmieni na pùlt 15 +Name[da]=Skift til desktop 15 +Name[de]=Wechsel zu Arbeitsfläche 15 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 15 +Name[eo]=Al tabulo 15 +Name[es]=Cambiar al escritorio 15 +Name[et]=Liikumine 15. töölauale +Name[eu]=Aldatu 15. mahaigainera +Name[fa]=تغییر به رومیزی Û±Ûµ +Name[fi]=Vaihda työpöytään 15 +Name[fr]=Aller au bureau 15 +Name[fy]=Gean nei buroblêd 15 +Name[ga]=Téigh go Deasc 15 +Name[gl]=Ir ao Escritório 15 +Name[he]=מעבר לשולחן עבודה 15 +Name[hr]=Prebaci se na radnu povrÅ¡inu 15 +Name[hu]=Váltás a 15. asztalra +Name[id]=Ganti ke Desktop 15 +Name[is]=Birta skjáborð 15 +Name[it]=Va al desktop 15 +Name[ja]=デスクトップ 15 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 15 +Name[kk]=15-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៥ +Name[ko]=ë°ìŠ¤í¬í†± 15ë¡œ 바꾸기 +Name[lt]=Pereiti į 15 darbastalį +Name[mk]=Кон површина 15 +Name[ms]=Ubah ke Desktop 15 +Name[nb]=Bytt til skrivebord 15 +Name[nds]=Na Schriefdisch 15 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १५ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 15 +Name[nn]=Byt til skrivebord 1 +Name[pa]=ਵੇਹੜੇ 15 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 15 +Name[pt]=Mudar para o Ecrã 15 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 15 +Name[ro]=Trece în ecranul 15 +Name[ru]=Перейти на рабочий Ñтол 15 +Name[rw]=Hindura ku Biro 15 +Name[se]=Mana vihttanuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 15 +Name[sl]=Preklopi na namizje 15 +Name[sr]=Промени на радну површину 15 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 15 +Name[sv]=Byt till skrivbord 15 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 15கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 15కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ15 +Name[tr]=15. Masaüstüne Git +Name[tt]=15. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 15 +Name[uz]=Ish stoli 15ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 15га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 15 +Name[wa]=Potchî sol cwénzinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 15 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å五 +Comment=Virtual desktop fifteen is selected +Comment[af]=Virtuele Werkskerm vyftien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 15 هو المØدد +Comment[be]=Выбраны пÑтнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 15 +Comment[bn]=পঞà§à¦šà¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel pempzek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Petnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual quinze +Comment[cs]=Je vybrána virtuálnà plocha 15 +Comment[csb]=Wëbróny je piãtnôsti pùlt +Comment[da]=Virtuel desktop femten er valgt +Comment[de]=Virtuelle Arbeitsfläche 15 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 15 +Comment[eo]=Tabulo dek kvina estas elektita +Comment[es]=Seleccionado el escritorio virtual Quince +Comment[et]=Viieteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamabostgarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی پانزده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä viisitoista on valittu +Comment[fr]=Le bureau virtuel 15 est sélectionné +Comment[fy]=Firtueel buroblêd fyftjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a cúig déag +Comment[gl]=Escolle-se o escritório virtual número quince +Comment[he]=שולחן העבודה הווירטו×לי חמש־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 15 +Comment[hu]=A 15. asztal kiválasztva +Comment[is]=Sýndarskjáborð fimmtán er virkt +Comment[it]=Il desktop virtuale quindici viene selezionato. +Comment[ja]=仮想デスクトップ 15 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთხუთმეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он беÑінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៥ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 15ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas penkioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 15 +Comment[ms]=Desktop maya lima belas dipilih +Comment[nb]=Virtuelt skrivebord femten er valgt +Comment[nds]=De virtuelle Schriefdisch Foffteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १५ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad vijftien is geselecteerd +Comment[nn]=Virtuelt skrivebord femten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਪੰਦਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano piÄ™tnasty pulpit +Comment[pt]=O ecrã virtual quinze é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 15 está selecionada +Comment[ro]=Ecranul virtual 15 este selectat +Comment[ru]=Выбран пÑтнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na gatatu byatoranyijwe +Comment[se]=Vihttanuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 15 +Comment[sl]=Izbrano je petnajsto navidezno namizje +Comment[sr]=Изабрана је петнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je petnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord femton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினைநà¯à®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదిహెనొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบห้าถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on beÅŸ seçili +Comment[tt]=UnbiÅŸençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 15 +Comment[uz]=OÊ»n beshinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн бешинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 15 đã được chá»n +Comment[wa]=Li cwénzinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 15 +Comment[zh_TW]=虛擬桌é¢å五被é¸å– +default_presentation=0 + +[desktop16] +Name=Change to Desktop 16 +Name[af]=Verander na Werkskerm 16 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 16 +Name[be]=ПерайÑці на працоўны Ñтол 16 +Name[bg]=Превключване на работен плот 16 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৬-য় যাও +Name[br]=Gwintañ da vurev 16 +Name[bs]=Premjesti se na radnu povrÅ¡inu 16 +Name[ca]=Canvi a l'escriptori 16 +Name[cs]=PÅ™epnutà na plochu 16 +Name[csb]=Zmieni na pùlt 16 +Name[da]=Skift til desktop 16 +Name[de]=Wechsel zu Arbeitsfläche 16 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 16 +Name[eo]=Al tabulo 16 +Name[es]=Cambiar al escritorio 16 +Name[et]=Liikumine 16. töölauale +Name[eu]=Aldatu 16. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û¶ +Name[fi]=Vaihda työpöytään 16 +Name[fr]=Aller au bureau 16 +Name[fy]=Gean nei buroblêd 16 +Name[ga]=Téigh go Deasc 16 +Name[gl]=Ir ao Escritório 16 +Name[he]=מעבר לשולחן עבודה 16 +Name[hr]=Prebaci se na radnu povrÅ¡inu 16 +Name[hu]=Váltás a 16. asztalra +Name[id]=Ganti ke Desktop 16 +Name[is]=Birta skjáborð 16 +Name[it]=Va al desktop 16 +Name[ja]=デスクトップ 16 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 16 +Name[kk]=16-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៦ +Name[ko]=ë°ìŠ¤í¬í†± 16으로 바꾸기 +Name[lt]=Pereiti į 16 darbastalį +Name[mk]=Кон површина 16 +Name[ms]=Ubah ke Desktop 16 +Name[nb]=Bytt til skrivebord 16 +Name[nds]=Na Schriefdisch 16 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १६ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 16 +Name[nn]=Byt til skrivebord 16 +Name[pa]=ਵੇਹੜੇ 16 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 16 +Name[pt]=Mudar para o Ecrã 16 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 16 +Name[ro]=Trece în ecranul 16 +Name[ru]=Перейти на рабочий Ñтол 16 +Name[rw]=Hindura ku Biro 16 +Name[se]=Mana guhttanuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 16 +Name[sl]=Preklopi na namizje 16 +Name[sr]=Промени на радну површину 16 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 16 +Name[sv]=Byt till skrivbord 16 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 116கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 16కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ16 +Name[tr]=16. Masaüstüne Git +Name[tt]=16. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 16 +Name[uz]=Ish stoli 16ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 16га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 16 +Name[wa]=Potchî sol sazinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 16 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢åå… +Comment=Virtual desktop sixteen is selected +Comment[af]=Virtuele Werkskerm sestien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 16 هو المØدد +Comment[be]=Выбраны шаÑнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 16 +Comment[bn]=ষোড়শ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel c'hwezek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Å esnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual setze +Comment[cs]=Je vybrána virtuálnà plocha 16 +Comment[csb]=Wëbróny je szesnôsti pùlt +Comment[da]=Virtuel desktop seksten er valgt +Comment[de]=Virtuelle Arbeitsfläche 16 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 16 +Comment[eo]=Tabulo dek sesa estas elektita +Comment[es]=Seleccionado el escritorio virtual Dieciseis +Comment[et]=Kuueteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamaseigarrenmahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی شانزده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kuusitoista on valittu +Comment[fr]=Le bureau virtuel 16 est sélectionné +Comment[fy]=Firtueel buroblêd sechstjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a sé déag +Comment[gl]=Escolle-se o escritório virtual número dezaseis +Comment[he]=שולחן העבודה הווירטו×לי שש־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 16 +Comment[hu]=A 16. asztal kiválasztva +Comment[is]=Sýndarskjáborð sextán er virkt +Comment[it]=Il desktop virtuale sedici viene selezionato. +Comment[ja]=仮想デスクトップ 16 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთექვსმეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он алтыншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៦ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 16ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas Å¡eÅ¡ioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 17 +Comment[ms]=Desktop maya enam belas dipilih +Comment[nb]=Virtuelt skrivebord seksten er valgt +Comment[nds]=De virtuelle Schriefdisch Sössteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १६ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad zestien is geselecteerd +Comment[nn]=Virtuelt skrivebord seksten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਸੋਲਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano szesnasty pulpit +Comment[pt]=O ecrã virtual dezasseis é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 16 está selecionada +Comment[ro]=Ecranul virtual 17 este selectat +Comment[ru]=Выбран шеÑтнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na gatandatu byatoranyijwe +Comment[se]=Guhttanuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 16 +Comment[sl]=Izbrano je Å¡estnajsto navidezno namizje +Comment[sr]=Изабрана је шеÑнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je Å¡esnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord sexton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினாற௠தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదహారొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบหà¸à¸–ูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on altı seçili +Comment[tt]=Unaltınçı xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 16 +Comment[uz]=OÊ»n oltinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн олтинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 16 đã được chá»n +Comment[wa]=Li sazinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 16 +Comment[zh_TW]=虛擬桌é¢åå…被é¸å– +default_presentation=0 + +[desktop17] +Name=Change to Desktop 17 +Name[af]=Verander na Werkskerm 17 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 17 +Name[be]=ПерайÑці на працоўны Ñтол 17 +Name[bg]=Превключване на работен плот 17 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১à§-য় যাও +Name[br]=Gwintañ da vurev 17 +Name[bs]=Premjesti se na radnu povrÅ¡inu 17 +Name[ca]=Canvi a l'escriptori 17 +Name[cs]=PÅ™epnutà na plochu 17 +Name[csb]=Zmieni na pùlt 17 +Name[da]=Skift til desktop 17 +Name[de]=Wechsel zu Arbeitsfläche 17 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 17 +Name[eo]=Al tabulo 17 +Name[es]=Cambiar al escritorio 17 +Name[et]=Liikumine 17. töölauale +Name[eu]=Aldatu 17. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û· +Name[fi]=Vaihda työpöytään 17 +Name[fr]=Aller au bureau 17 +Name[fy]=Gean nei buroblêd 17 +Name[ga]=Téigh go Deasc 17 +Name[gl]=Ir ao Escritório 17 +Name[he]=1מעבר לשולחן עבודה 7 +Name[hr]=Prebaci se na radnu povrÅ¡inu 17 +Name[hu]=Váltás a 17. asztalra +Name[id]=Ganti ke Desktop 17 +Name[is]=Birta skjáborð 17 +Name[it]=Va al desktop 17 +Name[ja]=デスクトップ 17 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 17 +Name[kk]=17-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៧ +Name[ko]=ë°ìŠ¤í¬í†± 17으로 바꾸기 +Name[lt]=Pereiti į 17 darbastalį +Name[mk]=Кон површина 17 +Name[ms]=Ubah ke Desktop 17 +Name[nb]=Bytt til skrivebord 17 +Name[nds]=Na Schriefdisch 17 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १ॠमा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 17 +Name[nn]=Byt til skrivebord 17 +Name[pa]=ਵੇਹੜੇ 17 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 17 +Name[pt]=Mudar para o Ecrã 17 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 17 +Name[ro]=Trece în ecranul 17 +Name[ru]=Перейти на рабочий Ñтол 17 +Name[rw]=Hindura ku Biro 17 +Name[se]=Mana Äiežanuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 17 +Name[sl]=Preklopi na namizje 17 +Name[sr]=Промени на радну површину 17 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 17 +Name[sv]=Byt till skrivbord 17 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 17கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 17కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ17 +Name[tr]=17. Masaüstüne Git +Name[tt]=17. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 17 +Name[uz]=Ish stoli 17ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 17га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 17 +Name[wa]=Potchî sol di-setinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 17 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢å七 +Comment=Virtual desktop seventeen is selected +Comment[af]=Virtuele Werkskerm sewentien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 17 هو المØدد +Comment[be]=Выбраны ÑÑмнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 17 +Comment[bn]=সপà§à¦¤à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel seitek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Sedamnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual disset +Comment[cs]=Je vybrána virtuálnà plocha 17 +Comment[csb]=Wëbróny je sétmenôsti pùlt +Comment[da]=Virtuel desktop sytten er valgt +Comment[de]=Virtuelle Arbeitsfläche 17 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 17 +Comment[eo]=Tabulo dek sepa estas elektita +Comment[es]=Seleccionado el escritorio virtual Diecisiete +Comment[et]=Seitsmeteistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hamazazpigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی Ù‡Ùده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä seitsemäntoista on valittu +Comment[fr]=Le bureau virtuel 17 est sélectionné +Comment[fy]=Firtueel buroblêd santjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a seacht déag +Comment[gl]=Escolle-se o escritório virtual número dezasete +Comment[he]=שולחן העבודה הווירטו×לי שבע־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 17 +Comment[hu]=A 17. asztal kiválasztva +Comment[is]=Sýndarskjáborð sautján er virkt +Comment[it]=Il desktop virtuale diciassette viene selezionato. +Comment[ja]=仮想デスクトップ 17 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეჩვიდმეტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он жетінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៧ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 17ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas septynioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 17 +Comment[ms]=Desktop maya tujuh belas dipilih +Comment[nb]=Virtuelt skrivebord sytten er valgt +Comment[nds]=De virtuelle Schriefdisch Söventeihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १ॠचयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad zeventien is geselecteerd +Comment[nn]=Virtuelt skrivebord sytten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਸਤਾਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano siedemnasty pulpit +Comment[pt]=O ecrã virtual dezassete é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 17 está selecionada +Comment[ro]=Ecranul virtual 17 este selectat +Comment[ru]=Выбран Ñемнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi na karindwi byatoranyijwe +Comment[se]=ÄŒiežanuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 17 +Comment[sl]=Izbrano je sedemnajsto navidezno namizje +Comment[sr]=Изабрана је ÑедамнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je sedamnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord sjutton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினேழ௠தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదిహెడొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบเจ็ดถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on yedi seçili +Comment[tt]=Uncidençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 17 +Comment[uz]=OÊ»n yettinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн еттинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 17 đã được chá»n +Comment[wa]=Li di-setinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 17 +Comment[zh_TW]=虛擬桌é¢å七被é¸å– +default_presentation=0 + +[desktop18] +Name=Change to Desktop 18 +Name[af]=Verander na Werkskerm 18 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 18 +Name[be]=ПерайÑці на працоўны Ñтол 18 +Name[bg]=Превключване на работен плот 18 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৮-য় যাও +Name[br]=Gwintañ da vurev 18 +Name[bs]=Premjesti se na radnu povrÅ¡inu 18 +Name[ca]=Canvi a l'escriptori 18 +Name[cs]=PÅ™epnutà na plochu 18 +Name[csb]=Zmieni na pùlt 18 +Name[da]=Skift til desktop 18 +Name[de]=Wechsel zu Arbeitsfläche 18 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 18 +Name[eo]=Al tabulo 18 +Name[es]=Cambiar al escritorio 18 +Name[et]=Liikumine 18. töölauale +Name[eu]=Aldatu 18. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û¸ +Name[fi]=Vaihda työpöytään 18 +Name[fr]=Aller au bureau 18 +Name[fy]=Gean nei buroblêd 18 +Name[ga]=Téigh go Deasc 18 +Name[gl]=Ir ao Escritório 18 +Name[he]=מעבר לשולחן עבודה 18 +Name[hr]=Prebaci se na radnu povrÅ¡inu 18 +Name[hu]=Váltás a 18. asztalra +Name[id]=Ganti ke Desktop 18 +Name[is]=Birta skjáborð 18 +Name[it]=Va al desktop 18 +Name[ja]=デスクトップ 18 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 18 +Name[kk]=18-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž» ១៨ +Name[ko]=ë°ìŠ¤í¬í†± 18ë¡œ 바꾸기 +Name[lt]=Pereiti į 18 darbastalį +Name[mk]=Кон површина 18 +Name[ms]=Ubah ke Desktop 18 +Name[nb]=Bytt til skrivebord 18 +Name[nds]=Na Schriefdisch 18 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १८ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar buraublad 18 +Name[nn]=Byt til skrivebord 18 +Name[pa]=ਵੇਹੜੇ 18 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 18 +Name[pt]=Mudar para o Ecrã 18 +Name[pt_BR]=Mudar para o Ãrea de Trabalho 18 +Name[ro]=Trece în ecranul 18 +Name[ru]=Перейти на рабочий Ñтол 18 +Name[rw]=Hindura ku Biro 18 +Name[se]=Mana gávccenuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 18 +Name[sl]=Preklopi na namizje 18 +Name[sr]=Промени на радну површину 18 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 18 +Name[sv]=Byt till skrivbord 18 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 18கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 18కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ18 +Name[tr]=18. Masaüstüne Git +Name[tt]=18. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 18 +Name[uz]=Ish stoli 18ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 18га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 18 +Name[wa]=Potchî sol dijh-ûtinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 18 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢åå…« +Comment=Virtual desktop eighteen is selected +Comment[af]=Virtuele Werkskerm agtien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 18 هو المØدد +Comment[be]=Выбраны ваÑемнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 18 +Comment[bn]=অষà§à¦Ÿà¦¾à¦¦à¦¶ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel triwec'h a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Osamnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual divuit +Comment[cs]=Je vybrána virtuálnà plocha 18 +Comment[csb]=Wëbróny je òsmenôsti pùlt +Comment[da]=Virtuel desktop atten er valgt +Comment[de]=Virtuelle Arbeitsfläche 18 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 18 +Comment[eo]=Tabulo dek oka estas elektita +Comment[es]=Seleccionado el escritorio virtual Dieciocho +Comment[et]=Kaheksateiskümnes virtuaalne töölaud on valitud +Comment[eu]=Hamazortzigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی هجده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kahdeksantoista on valittu +Comment[fr]=Le bureau virtuel 18 est sélectionné +Comment[fy]=Firtueel buroblêd achtjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a hocht déag +Comment[gl]=Escolle-se o escritório virtual número dezaoito +Comment[he]=שולחן העבודה הווירטו×לי ×©×ž×•× ×”Ö¾×¢×©×¨×” × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 18 +Comment[hu]=A 18. asztal kiválasztva +Comment[is]=Sýndarskjáborð átján er virkt +Comment[it]=Il desktop virtuale diciotto viene selezionato. +Comment[ja]=仮想デスクトップ 18 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეთვრáƒáƒ›áƒ”ტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он Ñегізінші виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៨ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 18ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas aÅ¡tuonioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 18 +Comment[ms]=Desktop maya lapan belas dipilih +Comment[nb]=Virtuelt skrivebord atten er valgt +Comment[nds]=De virtuelle Schriefdisch Achtteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १८ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad achttien is geselecteerd +Comment[nn]=Virtuelt skrivebord atten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਅਠਾਰਾਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano osiemnasty pulpit +Comment[pt]=O ecrã virtual dezoito é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 18 está selecionada +Comment[ro]=Ecranul virtual 18 este selectat +Comment[ru]=Выбран воÑемнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi n'umunani byatoranyijwe +Comment[se]=Gávccenuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 18 +Comment[sl]=Izbrano je osemnajsto navidezno namizje +Comment[sr]=Изабрана је оÑамнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je osamnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord arton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதினெடà¯à®Ÿà¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పదà±à°¦à±†à°¨à°¿à°®à°¿à°¦à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบà¹à¸›à¸”ถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on sekiz seçili +Comment[tt]=Unsigezençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 18 +Comment[uz]=OÊ»n sakkizinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн Ñаккизинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 18 đã được chá»n +Comment[wa]=Li dijh-ûtinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 18 +Comment[zh_TW]=虛擬桌é¢å八被é¸å– +default_presentation=0 + +[desktop19] +Name=Change to Desktop 19 +Name[af]=Verander na Werkskerm 19 +Name[ar]=غيير الى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 19 +Name[be]=ПерайÑці на працоўны Ñтол 19 +Name[bg]=Превключване на работен плот 19 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ১৯-ঠযাও +Name[br]=Gwintañ da vurev 19 +Name[bs]=Premjesti se na radnu povrÅ¡inu 19 +Name[ca]=Canvi a l'escriptori 19 +Name[cs]=PÅ™epnutà na plochu 19 +Name[csb]=Zmieni na pùlt 19 +Name[da]=Skift til desktop 19 +Name[de]=Wechsel zu Arbeitsfläche 19 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 19 +Name[eo]=Al tabulo 19 +Name[es]=Cambiar al escritorio 19 +Name[et]=Liikumine 19. töölauale +Name[eu]=Aldatu 19. mahaigainera +Name[fa]=تغییر به رومیزی Û±Û¹ +Name[fi]=Vaihda työpöytään 19 +Name[fr]=Aller au bureau 19 +Name[fy]=Gean nei buroblêd 19 +Name[ga]=Téigh go Deasc 19 +Name[gl]=Ir ao Escritório 19 +Name[he]=מעבר לשולחן עבודה 19 +Name[hr]=Prebaci se na radnu povrÅ¡inu 19 +Name[hu]=Váltás az 19. asztalra +Name[id]=Ganti ke Desktop 19 +Name[is]=Birta skjáborð 19 +Name[it]=Va al desktop 19 +Name[ja]=デスクトップ 19 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 19 +Name[kk]=19-Ò¯Ñтелге ауыÑу +Name[km]=ប្ដូរ​ទៅ​ផ្ទៃážáž»Â ១៩ +Name[ko]=ë°ìŠ¤í¬í†± 19ë¡œ 바꾸기 +Name[lt]=Pereiti į 19 darbastalį +Name[mk]=Кон површина 19 +Name[ms]=Ubah ke Desktop 19 +Name[nb]=Bytt til skrivebord 19 +Name[nds]=Na Schriefdisch 19 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª १९ मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 19 +Name[nn]=Byt til skrivebord 19 +Name[pa]=ਵੇਹੜੇ 19 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 19 +Name[pt]=Mudar para o Ecrã 19 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 19 +Name[ro]=Trece în ecranul 19 +Name[ru]=Перейти на рабочий Ñтол 19 +Name[rw]=Hindura ku Biro 19 +Name[se]=Mana ovccinuppelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 19 +Name[sl]=Preklopi na namizje 19 +Name[sr]=Промени на радну површину 19 +Name[sr@Latn]=Promeni na radnu povrÅ¡inu 19 +Name[sv]=Byt till skrivbord 19 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 19கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 19కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ19 +Name[tr]=19. Masaüstüne Git +Name[tt]=19. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 19 +Name[uz]=Ish stoli 19ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 19га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 19 +Name[wa]=Potchî sol dijh-nouvinme scribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 19 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢åä¹ +Comment=Virtual desktop nineteen is selected +Comment[af]=Virtuele Werkskerm negentien is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 19 هو المØدد +Comment[be]=Выбраны дзевÑтнаццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 19 +Comment[bn]=উনবিংশ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel naontek a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Devetnaest je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual dinou +Comment[cs]=Je vybrána virtuálnà plocha 19 +Comment[csb]=Wëbróny je dzéwiãtnôsti pùlt +Comment[da]=Virtuel desktop nitten er valgt +Comment[de]=Virtuelle Arbeitsfläche 19 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 19 +Comment[eo]=Tabulo dek naÅa estas elektita +Comment[es]=Seleccionado el escritorio virtual Diecinueve +Comment[et]=Ãœheksateistkümnes virtuaalne töölaud on valitud +Comment[eu]=Hemeretzigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی نوزده انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä yhdeksäntoista on valittu +Comment[fr]=Le bureau virtuel 19 est sélectionné +Comment[fy]=Firtueel buroblêd njoggentjin is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil a naoi déag +Comment[gl]=Escolle-se o escritório virtual número dezanove +Comment[he]=שולחן העבודה הווירטו×לי תשע־עשרה × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 19 +Comment[hu]=A 19. asztal kiválasztva +Comment[is]=Sýndarskjáborð nÃtján er virkt +Comment[it]=Il desktop virtuale diciannove viene selezionato. +Comment[ja]=仮想デスクトップ 19 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეცხრáƒáƒ›áƒ”ტე ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=Он тоғызыншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ១៩ ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 19ê°€ ì„ íƒë¨ +Comment[lt]=Pasirinktas devynioliktas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 19 +Comment[ms]=Desktop maya sembilan belas dipilih +Comment[nb]=Virtuelt skrivebord nitten er valgt +Comment[nds]=De virtuelle Schriefdisch Negenteihn is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª १९ चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad negentien is geselecteerd +Comment[nn]=Virtuelt skrivebord nitten vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਉਨੀਂ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano dziewiÄ™tnasty pulpit +Comment[pt]=O ecrã virtual dezanove é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 19 está selecionada +Comment[ro]=Ecranul virtual 19 este selectat +Comment[ru]=Выбран девÑтнадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara cumi n'icyenda byatoranyijwe +Comment[se]=Ovccinuppelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 19 +Comment[sl]=Izbrano je devetnajsto navidezno namizje +Comment[sr]=Изабрана је деветнаеÑта виртуелна радна површина +Comment[sr@Latn]=Izabrana je devetnaesta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord nitton är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ பதà¯à®¤à¯Šà®©à¯à®ªà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=పంతొమà±à°®à°¿à°¦à±Šà°µ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่สิบเà¸à¹‰à¸²à¸”ถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü on dokuz seçili +Comment[tt]=Untuğızınçı xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 19 +Comment[uz]=OÊ»n toÊ»qqizinchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Ўн тўққизинчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 19 đã được chá»n +Comment[wa]=Li dijh-nouvinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 19 +Comment[zh_TW]=虛擬桌é¢åä¹è¢«é¸å– +default_presentation=0 + +[desktop20] +Name=Change to Desktop 20 +Name[af]=Verander na Werkskerm 20 +Name[ar]=غيير إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ 20 +Name[be]=ПерайÑці на працоўны Ñтол 20 +Name[bg]=Превключване на работен плот 20 +Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ২০-তে যাও +Name[br]=Gwintañ da vurev 20 +Name[bs]=Premjesti se na radnu povrÅ¡inu 20 +Name[ca]=Canvi a l'escriptori 20 +Name[cs]=PÅ™epnutà na plochu 20 +Name[csb]=Zmieni na pùlt 20 +Name[da]=Skift til desktop 20 +Name[de]=Wechsel zu Arbeitsfläche 20 +Name[el]=Αλλαγή στην επιφάνεια εÏγασίας 20 +Name[eo]=Al tabulo 20 +Name[es]=Cambiar al escritorio 20 +Name[et]=Liikumine 20. töölauale +Name[eu]=Aldatu 20. mahaigainera +Name[fa]=تغییر به رومیزی Û²Û° +Name[fi]=Vaihda työpöytään 20 +Name[fr]=Aller au bureau 20 +Name[fy]=Gean nei buroblêd 20 +Name[ga]=Téigh go Deasc 20 +Name[gl]=Ir ao Escritório 20 +Name[he]=מעבר לשולחן עבודה 20 +Name[hr]=Prebaci se na radnu povrÅ¡inu 20 +Name[hu]=Váltás a 20. asztalra +Name[id]=Ganti ke Desktop 20 +Name[is]=Birta skjáborð 20 +Name[it]=Va al desktop 20 +Name[ja]=デスクトップ 20 ã«ç§»å‹• +Name[ka]=გáƒáƒ“áƒáƒ¡áƒ•áƒšáƒ სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე 20 +Name[kk]=20-Ò¯Ñтелге ауыÑу +Name[km]=ផ្លាស់ប្ážáž¼ážšâ€‹áž‘ៅ​ផ្ទៃážáž» ២០+Name[ko]=ë°ìŠ¤í¬í†± 20으로 바꾸기 +Name[lt]=Pereiti į 20 darbastalį +Name[mk]=Кон површина 20 +Name[ms]=Ubah ke Desktop 20 +Name[nb]=Bytt til skrivebord 20 +Name[nds]=Na Schriefdisch 20 wesseln +Name[ne]=डेसà¥à¤•à¤Ÿà¤ª २० मा परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Ga naar bureaublad 20 +Name[nn]=Byt til skrivebord 20 +Name[pa]=ਵੇਹੜੇ 20 ਲਈ ਤਬਦੀਲ +Name[pl]=ZmieÅ„ na pulpit 20 +Name[pt]=Mudar para o Ecrã 20 +Name[pt_BR]=Mudar para a Ãrea de Trabalho 20 +Name[ro]=Trece în ecranul 20 +Name[ru]=Перейти на рабочий Ñтол 20 +Name[rw]=Hindura ku Biro 20 +Name[se]=Mana guoktelogát Äállinbeavdái +Name[sk]=ZmeniÅ¥ na pracovnú plochu 20 +Name[sl]=Preklopi na namizje 20 +Name[sr]=Прелаз на радну површину 20 +Name[sr@Latn]=Prelaz na radnu povrÅ¡inu 20 +Name[sv]=Byt till skrivbord 20 +Name[ta]=மேலà¯à®®à¯‡à®šà¯ˆ 20கà¯à®•à¯ மாறà¯à®±à¯ +Name[te]=à°°à°‚à°—à°¸à±à°¥à°²à°‚ 20కౠమారండి +Name[th]=สลับไปยังพื้นที่หน้าจภ20 +Name[tr]=20. Masaüstüne Git +Name[tt]=20. Östälgä Küç +Name[uk]=Перейти на Ñтільницю 20 +Name[uz]=Ish stoli 20ga oÊ»tish +Name[uz@cyrillic]=Иш Ñтоли 20га ўтиш +Name[vi]=Äổi vá» Mà n hình ná»n 20 +Name[wa]=Potchî sol vintinme sicribanne +Name[zh_CN]=æ›´æ”¹åˆ°æ¡Œé¢ 20 +Name[zh_TW]=切æ›åˆ°æ¡Œé¢äºŒå +Comment=Virtual desktop twenty is selected +Comment[af]=Virtuele Werkskerm twintig is gekies +Comment[ar]=Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي رقم 20 هو المØدد +Comment[be]=Выбраны дваццаты віртуальны працоўны Ñтол +Comment[bg]=Превключване на работен плот 20 +Comment[bn]=বিংশ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ª নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয়েছে +Comment[br]=Burev galloudel warn-ugent a zo dibabet +Comment[bs]=Virtualna radna povrÅ¡ina Dvadeset je izabrana +Comment[ca]=S'ha seleccionat l'escriptori virtual vint +Comment[cs]=Je vybrána virtuálnà plocha 20 +Comment[csb]=Wëbróny je dwadzesti pùlt +Comment[da]=Virtuel desktop tyve er valgt +Comment[de]=Virtuelle Arbeitsfläche 20 ausgewählt +Comment[el]=Έχει επιλεγεί η εικονική επιφάνεια εÏγασίας 20 +Comment[eo]=Tabulo du deka estas elektita +Comment[es]=Seleccionado el escritorio virtual Veinte +Comment[et]=Kahekümnes virtuaalne töölaud on valitud +Comment[eu]=Hogeigarren mahaigain birtuala hautatua dago +Comment[fa]=رومیزی مجازی بیست انتخاب می‌شود +Comment[fi]=Virtuaalityöpöytä kaksikymmentä on valittu +Comment[fr]=Le bureau virtuel 20 est sélectionné +Comment[fy]=Firtueel buroblêd twintich is selektearre +Comment[ga]=RoghnaÃodh deasc fhÃorúil fiche +Comment[gl]=Escolle-se o escritório virtual número vinte +Comment[he]=שולחן העבודה הווירטו×לי ×¢×©×¨×™× × ×‘×—×¨ +Comment[hr]=Odabrana je virtualna radna povrÅ¡ina 20 +Comment[hu]=A 20. asztal kiválasztva +Comment[is]=Sýndarskjáborð tuttugu er virkt +Comment[it]=Il desktop virtuale venti viene selezionato. +Comment[ja]=仮想デスクトップ 20 ãŒé¸æŠžã•ã‚Œã¾ã—㟠+Comment[ka]=áƒáƒ ჩეულირმეáƒáƒªáƒ” ვირტულური სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ +Comment[kk]=ЖиырмаÑыншы виртуалды Ò¯Ñтел таңдауда +Comment[km]=ផ្ទៃážáž»áž“ិម្មិហ២០ážáŸ’រូវ​បាន​ជ្រើស +Comment[ko]=ê°€ìƒ ë°ìŠ¤í¬í†± 20ì´ ì„ íƒë¨ +Comment[lt]=Pasirinktas dvideÅ¡imtas menamas darbastalis +Comment[mk]=Избрана е виртуелната површина бр. 20 +Comment[ms]=Desktop maya dua puluh dipilih +Comment[nb]=Virtuelt skrivebord tjue er valgt +Comment[nds]=De virtuelle Schriefdisch Twintig is utsöcht +Comment[ne]=अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ª २० चयन गरिà¤à¤•à¥‹ छ +Comment[nl]=Virtueel bureaublad 20 is geselecteerd +Comment[nn]=Virtuelt skrivebord tjue vert valt +Comment[pa]=ਫ਼ਰਜ਼ੀ ਵੇਹੜਾ ਵੀਹ ਚà©à¨£à¨¿à¨† +Comment[pl]=Wybrano dwudziesty pulpit +Comment[pt]=O ecrã virtual vinte é seleccionado +Comment[pt_BR]=Ãrea de trabalho virtual 20 está selecionada +Comment[ro]=Ecranul virtual 20 este selectat +Comment[ru]=Выбран двадцатый виртуальный рабочий Ñтол +Comment[rw]=Ibiro bitagaragara makumyabiri byatoranyijwe +Comment[se]=Guoktelogát virtuella Äállinbeavdi lea válljejuvvon +Comment[sk]=Je zvolená virtuálna plocha 20 +Comment[sl]=Izbrano je dvajseto navidezno namizje +Comment[sr]=Изабрана је двадеÑета виртуелна радна површина +Comment[sr@Latn]=Izabrana je dvadeseta virtuelna radna povrÅ¡ina +Comment[sv]=Virtuellt skrivbord tjugo är valt +Comment[ta]=மெயà¯à®¨à®¿à®•à®°à¯ மேலà¯à®®à¯‡à®šà¯ˆ இரà¯à®ªà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=ఇరవైయొవ మిధà±à°¯à°¾ à°°à°‚à°—à°¸à±à°¥à°²à°‚ à°Žà°‚à°šà±à°•à±Šà°¬à°¡à°¿à°¨à°¦à°¿ +Comment[th]=พื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่ยี่สิบถูà¸à¹€à¸¥à¸·à¸à¸ +Comment[tr]=Sanal masaüstü yirmi seçili +Comment[tt]=Yegermençe xıyalıy östäl saylandı +Comment[uk]=Вибрано віртуальну Ñтільницю 20 +Comment[uz]=Yigirmanchi virtual ish stoli tanlangan +Comment[uz@cyrillic]=Йигирманчи виртуал иш Ñтоли танланган +Comment[vi]=Mà n hình ná»n ảo số 20 đã được chá»n +Comment[wa]=Li vintinme forveyou scribanne a stî tchoezi +Comment[zh_CN]=é€‰æ‹©äº†è™šæ‹Ÿæ¡Œé¢ 20 +Comment[zh_TW]=虛擬桌é¢äºŒå被é¸å– +default_presentation=0 + +[activate] +Name=Activate Window +Name[af]=Aktiveer Venster +Name[ar]=نشّط الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ni FÉ™allaÅŸdır +Name[be]=Ðктывізаваць акно +Name[bg]=Ðктивиране на прозорец +Name[bn]=উইণà§à¦¡à§‹ সকà§à¦°à¦¿à§Ÿ করো +Name[br]=Dihuniñ ar prenestr +Name[bs]=Aktiviraj prozor +Name[ca]=Activa finestra +Name[cs]=Aktivace okna +Name[csb]=Aktiwacëjô òkna +Name[cy]=Gweithredu Ffenestr +Name[da]=Aktivér vindue +Name[de]=Fenster aktivieren +Name[el]=ΕνεÏγοποίηση παÏαθÏÏου +Name[eo]=Aktivigi fenestron +Name[es]=Activar ventana +Name[et]=Akna aktiveerimine +Name[eu]=Aktibatu leihoa +Name[fa]=Ùعال کردن پنجره +Name[fi]=Aktivoi ikkuna +Name[fr]=Activer une fenêtre +Name[fy]=Finster aktivearje +Name[ga]=GnÃomhachtaigh Fuinneog +Name[gl]=Activar Fiestra +Name[he]=הפעלת חלון +Name[hi]= विंडो सकà¥à¤°à¤¿à¤¯ करें +Name[hr]=Aktiviraj prozor +Name[hu]=Ablak aktiválása +Name[id]=Mengaktifkan Jendela +Name[is]=Virkja glugga +Name[it]=Attiva finestra +Name[ja]=ウィンドウãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã« +Name[ka]=ფáƒáƒœáƒ¯áƒ ის გáƒáƒáƒ¥áƒ¢áƒ˜áƒ£áƒ ებრ+Name[kk]=Терезені белÑендету +Name[km]=ធ្វើ​ឲ្យ​បង្អួច​សកម្ម +Name[ko]=ì°½ í™œì„±í™”ë¨ +Name[lo]=ຮງàºàº«àº™à»‰àº²àº•à»ˆàº²àº‡àº—ຳງານ +Name[lt]=Suaktyvinti langÄ… +Name[lv]=AktivizÄ“t Logu +Name[mk]=Ðктивирај прозорец +Name[mn]=Цонх идÑвхижүүлÑÑ… +Name[ms]=Aktifkan Tetingkap +Name[mt]=Attiva Window +Name[nb]=Velg vindu +Name[nds]=Finster aktiv setten +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सकà¥à¤°à¤¿à¤¯ बनाउनà¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Venster activeren +Name[nn]=Aktiver vindauge +Name[nso]=Berekisa Window +Name[oc]=Activa finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਸਰਗਰਮ +Name[pl]=Aktywacja okna +Name[pt]=Activar a Janela +Name[pt_BR]=Ativar Janela +Name[ro]=Activează fereastra +Name[ru]=Сделать окно активным +Name[rw]=Gukoresha Idirishya +Name[se]=Aktivere láse +Name[sk]=AktÃvne okno +Name[sl]=Aktiviraj okno +Name[sr]=Прозор Ñе активира +Name[sr@Latn]=Prozor se aktivira +Name[ss]=Vuselela liwindi +Name[sv]=Aktivera fönster +Name[ta]=சாளரதà¯à®¤à¯ˆ செயறà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Фаъол намудани тиреза +Name[th]=เรียà¸à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¸—ำงาน +Name[tr]=Pencereyi EtkinleÅŸtir +Name[tt]=Täräzä Eşçän Ä°tü +Name[uk]=Ðктивізувати вікно +Name[uz]=Oynani aktivlashtirish +Name[uz@cyrillic]=Ойнани активлаштириш +Name[ven]=Lugisani dzi windo +Name[vi]=Cá»a sổ Hoạt Ä‘á»™ng +Name[wa]=Dispierter li purnea +Name[xh]=Yenza i window isebenze +Name[zh_CN]=æ¿€æ´»çª—å£ +Name[zh_TW]=作用ä¸è¦–窗 +Name[zu]=Nyakazisa I-window +Comment=Another window is activated +Comment[af]='n Ander venster is geaktiveer +Comment[ar]=يوجد ناÙذة آخرى منشطة +Comment[az]=BaÅŸqa pÉ™ncÉ™rÉ™ fÉ™allaÅŸdırdı +Comment[be]=Ðктывізаванае іншае акно +Comment[bg]=Ðктивиран е друг прозорец +Comment[bn]=অনà§à¦¯ à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ সকà§à¦°à¦¿à§Ÿ করা হয়েছে +Comment[br]=Ur prenestr all a zo dihunet +Comment[bs]=Drugi prozor je aktiviran +Comment[ca]=S'ha activat una altra finestra +Comment[cs]=Je aktivováno jiné okno +Comment[csb]=Aktiwòwanié jinszegò òkna +Comment[da]=Et andet vindue er aktiveret +Comment[de]=Ein anderes Fenster ist aktiv. +Comment[el]=ΕνεÏγοποιήθηκε Îνα άλλο παÏάθυÏο +Comment[eo]=Alia fenestro aktiviÄis +Comment[es]=Otra ventana está activada +Comment[et]=Teine aken on aktiveeritud +Comment[eu]=Beste leiho bat aktibatua dago +Comment[fa]=پنجرۀ دیگری Ùعال می‌شود +Comment[fi]=Toinen ikkuna aktivoitu +Comment[fr]=Une autre fenêtre est activée +Comment[fy]=In oar finster is aktivearre +Comment[gl]=Outra fiestra está activa +Comment[he]=חלון ×חר מופעל +Comment[hi]=अनà¥à¤¯ विंडो सकà¥à¤°à¤¿à¤¯ है +Comment[hr]=Aktiviran je drugi prozor +Comment[hu]=Egy másik ablak lett aktiválva +Comment[is]=Annar gluggi verður virkur +Comment[it]=Attivazione di un'altra finestra +Comment[ja]=ä»–ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ãªã‚Šã¾ã—㟠+Comment[ka]=სხვრფáƒáƒœáƒ¯áƒáƒ áƒáƒ áƒáƒ¥áƒ¢áƒ˜áƒ£áƒ ი +Comment[kk]=БаÑқа терезе белÑендетілді +Comment[km]=បង្អួច​មួយ​ទៀហážáŸ’រូវ​បាន​ធ្វើ​ឲ្យ​សកម្ម +Comment[ko]=ë˜ ë‹¤ë¥¸ ì°½ì´ í™œì„±í™”ë¨ +Comment[lt]=Suaktyvintas kitas langas +Comment[lv]=Cits Logs jau ir aktivizÄ“ts +Comment[mk]=Ðктивиран е друг прозорец +Comment[ms]=Satu lagi tetingkap diaktifkan +Comment[mt]=Window Oħra Ä iet Attivata +Comment[nb]=Et annet vindu er valgt +Comment[nds]=En anner Finster warrt aktiv maakt +Comment[ne]=अरà¥à¤•à¥‹ सञà¥à¤à¥à¤¯à¤¾à¤² सकà¥à¤°à¤¿à¤¯ पारिà¤à¤•à¥‹ छ +Comment[nl]=Een ander venster is geactiveerd +Comment[nn]=Eit anna vindauge vert aktivert +Comment[pa]=ਹੋਰ à¨à¨°à©‹à¨–ਾ ਸਰਗਰਮ ਹੈ +Comment[pl]=Uaktywnienie innego okna +Comment[pt]=Outra janela é activada +Comment[pt_BR]=Outra janela está ativa +Comment[ro]=A fost activată altă fereastră +Comment[ru]=Ðктивировано другое окно +Comment[rw]=Irindi dirishya rirakora +Comment[se]=Eará láse aktiverejuvvo +Comment[sk]=Iné okno je už aktÃvne +Comment[sl]=Aktivno je drugo okno +Comment[sr]=Ðктивиран је други прозор +Comment[sr@Latn]=Aktiviran je drugi prozor +Comment[sv]=Ett annat fönster är aktiverat +Comment[ta]=மறà¯à®±à¯Šà®°à¯ சாளரம௠செயலாகà¯à®•à®ªà¯à®ªà®Ÿà®¤à¯ +Comment[th]=หน้าต่างà¸à¸·à¹ˆà¸™à¸–ูà¸à¹€à¸£à¸µà¸¢à¸à¸—ำงาน +Comment[tr]=BaÅŸka bir pencere etkinleÅŸtirildi +Comment[tt]=BaÅŸqa täräzä terelände +Comment[uk]=Ðктивоване інше вікно +Comment[uz]=Boshqa oyna aktiv boÊ»ldi +Comment[uz@cyrillic]=Бошқа ойна актив бўлди +Comment[vi]=Má»™t cá»a sổ khác đã được kÃch hoạt +Comment[wa]=On ôte purnea a stî metou en alaedje +Comment[zh_CN]=激活了å¦å¤–ä¸€ä¸ªçª—å£ +Comment[zh_TW]=å¦ä¸€å€‹è¦–çª—ä½œç”¨ä¸ +default_presentation=0 + +[new] +Name=New Window +Name[af]=Nuwe Venster +Name[ar]=ناÙذة جديدة +Name[az]=Yeni PÉ™ncÉ™rÉ™ +Name[be]=Ðовае акно +Name[bg]=Ðов прозорец +Name[bn]=নতà§à¦¨ উইণà§à¦¡à§‹ +Name[br]=Prenestr nevez +Name[bs]=Novi prozor +Name[ca]=Finestra nova +Name[cs]=Nové okno +Name[csb]=Nowé òkno +Name[cy]=Ffenestr Newydd +Name[da]=Nyt vindue +Name[de]=Neues Fenster +Name[el]=ÎÎο παÏάθυÏο +Name[eo]=Nova fenestro +Name[es]=Nueva ventana +Name[et]=Uus aken +Name[eu]=Leiho berria +Name[fa]=پنجرۀ جدید +Name[fi]=Uusi ikkuna +Name[fr]=Nouvelle fenêtre +Name[fy]=Nij finster +Name[ga]=Fuinneog Nua +Name[gl]=Nova Fiestra +Name[he]=חלון חדש +Name[hi]=नया विंडो +Name[hr]=Novi prozor +Name[hu]=Új ablak +Name[id]=Jendela Baru +Name[is]=Opna nýjan glugga +Name[it]=Nuova finestra +Name[ja]=æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ +Name[ka]=áƒáƒ®áƒáƒšáƒ˜ ფáƒáƒœáƒ¯áƒáƒ რ+Name[kk]=Жаңа терезе +Name[km]=បង្អួច​ážáŸ’មី +Name[ko]=새 ì°½ +Name[lo]=ສ້າງຫນ້າຕ່າງໃຫມ່ +Name[lt]=Naujas langas +Name[lv]=Jauns Logs +Name[mk]=Ðов прозорец +Name[mn]=Ð¨Ð¸Ð½Ñ Ñ†Ð¾Ð½Ñ… +Name[ms]=Tetingkap Baru +Name[mt]=Window Ä dida +Name[nb]=Nytt vindu +Name[nds]=Nieg Finster +Name[ne]=नयाठसञà¥à¤à¥à¤¯à¤¾à¤² +Name[nl]=Nieuw venster +Name[nn]=Nytt vindauge +Name[nso]=Window ye Ntshwa +Name[oc]=Navera finestra +Name[pa]=ਨਵਾਂ à¨à¨°à©‹à¨–ਾ +Name[pl]=Nowe okno +Name[pt]=Nova Janela +Name[pt_BR]=Nova Janela +Name[ro]=Fereastră nouă +Name[ru]=Ðовое окно +Name[rw]=Idirishya Rishya +Name[se]=OÄ‘Ä‘a láse +Name[sk]=Nové okno +Name[sl]=Novo okno +Name[sr]=Ðови прозор +Name[sr@Latn]=Novi prozor +Name[ss]=Liwindi lelisha +Name[sv]=Nytt fönster +Name[ta]=பà¯à®¤à®¿à®¯ சாளரம௠+Name[te]=కొతà±à°¤ విండొ +Name[tg]=Тирезаи Ðав +Name[th]=สร้างหน้าต่างใหม่ +Name[tr]=Yeni Pencere +Name[tt]=Yana Täräzä +Name[uk]=Ðове вікно +Name[uz]=Yangi oyna +Name[uz@cyrillic]=Янги ойна +Name[ven]=Windo ntswa +Name[vi]=Mở cá»a sổ má»›i +Name[wa]=Novea purnea +Name[xh]=Window Entsha +Name[zh_CN]=æ–°å»ºçª—å£ +Name[zh_TW]=新視窗 +Name[zu]=I-window Entsha +Comment=New window +Comment[af]=Nuwe Venster +Comment[ar]=ناÙذة جديدة +Comment[az]=Yeni pÉ™ncÉ™rÉ™ +Comment[be]=Ðовае акно +Comment[bg]=Ðов прозорец +Comment[bn]=নতà§à¦¨ উইণà§à¦¡à§‹ +Comment[br]=Prenestr nevez +Comment[bs]=Novi prozor +Comment[ca]=Finestra nova +Comment[cs]=Nové okno +Comment[csb]=Nowé òkno +Comment[da]=Nyt vindue +Comment[de]=Neues Fenster +Comment[el]=ÎÎο παÏάθυÏο +Comment[eo]=Nova fenestro +Comment[es]=Nueva ventana +Comment[et]=Uus aken +Comment[eu]=Leiho berria +Comment[fa]=پنجرۀ جدید +Comment[fi]=Uusi ikkuna +Comment[fr]=Nouvelle fenêtre +Comment[fy]=Nij finster +Comment[ga]=Fuinneog nua +Comment[gl]=Nova Fiestra +Comment[he]=חלון חדש +Comment[hi]=नया विंडो +Comment[hr]=Novi prozor +Comment[hu]=Új ablak +Comment[is]=Nýr gluggi +Comment[it]=Nuova finestra +Comment[ja]=æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ +Comment[ka]=áƒáƒ®áƒáƒšáƒ˜ ფáƒáƒœáƒ¯áƒáƒ რ+Comment[kk]=Жаңа терезе +Comment[km]=បង្អួច​ážáŸ’មី +Comment[ko]=새 ì°½ +Comment[lt]=Naujas langas +Comment[lv]=Jauns Logs +Comment[mk]=Ðов прозорец +Comment[ms]=Tetingkap baru +Comment[mt]=Window Ä dida +Comment[nb]=Nytt vindu +Comment[nds]=Nieg Finster +Comment[ne]=नयाठसञà¥à¤à¥à¤¯à¤¾à¤² +Comment[nl]=Nieuw venster +Comment[nn]=Nytt vindauge +Comment[pa]=ਨਵਾਂ à¨à¨°à©‹à¨–ਾ +Comment[pl]=Nowe okno +Comment[pt]=Nova janela +Comment[pt_BR]=Nova janela +Comment[ro]=Fereastră nouă +Comment[ru]=Ðовое окно +Comment[rw]=Idirishya rishya +Comment[se]=OÄ‘Ä‘a láse +Comment[sk]=Nové okno +Comment[sl]=Novo okno +Comment[sr]=Ðови прозор +Comment[sr@Latn]=Novi prozor +Comment[sv]=Nytt fönster +Comment[ta]=பà¯à®¤à¯ சாளரம௠+Comment[te]=కొతà±à°¤ విండొ +Comment[tg]=Тирезаи нав +Comment[th]=สร้างหน้าต่างใหม่ +Comment[tr]=Yeni pencere +Comment[tt]=Yana Täräzä +Comment[uk]=Відкрите нове вікно +Comment[uz]=Yangi oyna +Comment[uz@cyrillic]=Янги ойна +Comment[vi]=Mở cá»a sổ má»›i +Comment[wa]=Novea purnea +Comment[zh_CN]=æ–°å»ºçª—å£ +Comment[zh_TW]=新建視窗 +default_sound=KDE_Window_Open.ogg +default_presentation=0 + +[delete] +Name=Delete Window +Name[af]=Vee Venster Uit +Name[ar]=Ù…ØÙˆ الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ni Sil +Name[be]=Выдаліць акно +Name[bg]=Изтриване на прозорец +Name[bn]=উইণà§à¦¡à§‹ মà§à¦›à§‡ ফেলো +Name[br]=Lemel ur Prenestr +Name[bs]=Brisanje prozora +Name[ca]=Elimina finestra +Name[cs]=ZruÅ¡it okno +Name[csb]=Rëmôj òkno +Name[cy]=Dileu Ffenestr +Name[da]=Slet vindue +Name[de]=Fenster löschen +Name[el]=ΔιαγÏαφή παÏαθÏÏου +Name[eo]=Forigu fenestron +Name[es]=Eliminar ventana +Name[et]=Akna kustutamine +Name[eu]=Ezabatu leihoa +Name[fa]=Øذ٠پنجره +Name[fi]=Lopeta ikkuna +Name[fr]=Supprimer la fenêtre +Name[fy]=Finster wiskje +Name[ga]=Scrios Fuinneog +Name[gl]=Borrar Fiestra +Name[he]=מחיקת חלון +Name[hi]=विंडो मिटाà¤à¤ +Name[hr]=IzbriÅ¡i prozor +Name[hu]=Ablak törlése +Name[id]=Hapus Jendela +Name[is]=Eyða glugga +Name[it]=Elimina finestra +Name[ja]=ウィンドウを削除 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის წáƒáƒ¨áƒšáƒ +Name[kk]=Терезені жою +Name[km]=លុប​បង្អួច +Name[ko]=ì°½ ì‚ì œ +Name[lo]=ລົບຫນ້າຕ່າງ +Name[lt]=PaÅ¡alinti langÄ… +Name[lv]=DzÄ“st Logu +Name[mk]=Избриши прозорец +Name[mn]=Цонх уÑтгах +Name[ms]=Hapuskan Tetingkap +Name[mt]=Neħħi Window +Name[nb]=Slett vindu +Name[nds]=Finster wegdoon +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Venster verwijderen +Name[nn]=Fjern vindauge +Name[nso]=Tlosa Window +Name[oc]=Suprima finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਹਟਾਓ +Name[pl]=UsuniÄ™cie okna +Name[pt]=Apagar a Janela +Name[pt_BR]=Remover Janela +Name[ro]=Distruge fereastra +Name[ru]=Удалить окно +Name[rw]=Gusiba Idirishya +Name[se]=Sihko láse +Name[sk]=ZmazaÅ¥ okno +Name[sl]=ZbriÅ¡i okno +Name[sr]=Прозор Ñе брише +Name[sr@Latn]=Prozor se briÅ¡e +Name[ss]=Bulala liwindi +Name[sv]=Ta bort fönster +Name[ta]=சாளரதà¯à®¤à¯ˆ நீகà¯à®•à¯ +Name[te]=విండొ తిసివెయి +Name[tg]=Ðобуд Ñохтани тиреза +Name[th]=ลบหน้าต่าง +Name[tr]=Pencereyi Sil +Name[tt]=Täräzäne Beter +Name[uk]=Вилучити вікно +Name[uz]=Oynani oÊ»chirish +Name[uz@cyrillic]=Ойнани ўчириш +Name[ven]=Thuthani Windo +Name[vi]=Xoá cá»a sổ +Name[wa]=Disfacer purnea +Name[xh]=Cima i Window +Name[zh_CN]=åˆ é™¤çª—å£ +Name[zh_TW]=刪除視窗 +Name[zu]=Cisha I-window +Comment=Delete window +Comment[af]=Vee Venster Uit +Comment[ar]=Ù…ØÙˆ الناÙذة +Comment[az]=PÉ™ncÉ™rÉ™ni sil +Comment[be]=Выдаліць акно +Comment[bg]=Изтриване на прозорец +Comment[bn]=উইণà§à¦¡à§‹ বনà§à¦§ করো +Comment[br]=Lemel ur prenestr +Comment[bs]=Brisanje prozora +Comment[ca]=Esborra finestra +Comment[cs]=Okna bylo odstranÄ›no +Comment[csb]=Rëmôj òkno +Comment[da]=Slet vindue +Comment[de]=Fenster entfernen +Comment[el]=ΔιαγÏαφή παÏαθÏÏου +Comment[eo]=Forigu fenestron +Comment[es]=Eliminar ventana +Comment[et]=Akna kustutamine +Comment[eu]=Ezabatu leihoa +Comment[fa]=Øذ٠پنجره +Comment[fi]=Lopeta ikkuna +Comment[fr]=Supprimer une fenêtre +Comment[fy]=Finster wiskje +Comment[ga]=Scrios Fuinneog +Comment[gl]=Borrar fiestra +Comment[he]=חלון × ×ž×—×§ +Comment[hi]=विंडो मिटाà¤à¤ +Comment[hr]=Brisanje prozora +Comment[hu]=Ablak törlése +Comment[is]=Eyða glugga +Comment[it]=Eliminazione finestra +Comment[ja]=ウィンドウを削除 +Comment[ka]=ფáƒáƒœáƒ¯áƒ ის წáƒáƒ¨áƒšáƒ +Comment[kk]=Терезені жою +Comment[km]=លុប​បង្អួច +Comment[ko]=ì°½ ì—†ì• ê¸° +Comment[lt]=PaÅ¡alinti langÄ… +Comment[lv]=DzÄ“st Logu +Comment[mk]=Избриши прозорец +Comment[ms]=Hapuskan tetingkap +Comment[mt]=Ħassar Window +Comment[nb]=Slett vindu +Comment[nds]=Finster wegdoon +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Venster verwijderen +Comment[nn]=Lukk vindauge +Comment[pa]=à¨à¨°à©‹à¨–ਾ ਹਟਾਓ +Comment[pl]=UsuÅ„ okno +Comment[pt]=Apagar a janela +Comment[pt_BR]=Remover Janela +Comment[ro]=ÃŽnchide fereastra +Comment[ru]=Удалить окно +Comment[rw]=Gusiba idirishya +Comment[se]=Sihko láse +Comment[sk]=OdstrániÅ¥ okno +Comment[sl]=ZbriÅ¡i okno +Comment[sr]=Прозор Ñе брише +Comment[sr@Latn]=Prozor se briÅ¡e +Comment[sv]=Ta bort fönster +Comment[ta]=சாளரதà¯à®¤à¯ˆ நீகà¯à®•à¯ +Comment[te]=విండొ తిసివెయి +Comment[th]=ลบหน้าต่าง +Comment[tr]=Pencereyi sil +Comment[tt]=Täräzäne beter +Comment[uk]=Вікно вилучено +Comment[uz]=Oynani oÊ»chirish +Comment[uz@cyrillic]=Ойнани ўчириш +Comment[vi]=Xoá cá»a sổ +Comment[wa]=Disfacer purnea +Comment[zh_CN]=åˆ é™¤çª—å£ +Comment[zh_TW]=刪除視窗 +default_presentation=0 +[close] +Name=Window Close +Name[af]=Maak Venster Toe +Name[ar]=إغلاق الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ni BaÄŸla +Name[be]=Закрыць акно +Name[bg]=ЗатварÑне на прозорец +Name[bn]=উইণà§à¦¡à§‹ বনà§à¦§ +Name[br]=Serriñ ar Prenestr +Name[bs]=Zatvaranje prozora +Name[ca]=Tanca finestra +Name[cs]=ZavÅ™enà okna +Name[csb]=Zamknij òkno +Name[cy]=Cau Ffenestr +Name[da]=Luk vindue +Name[de]=Fenster schließen +Name[el]=Κλείσιμο παÏαθÏÏου +Name[eo]=Fenestro fermiÄas +Name[es]=Cerrar ventana +Name[et]=Akna sulgemine +Name[eu]=Itxi leihoa +Name[fa]=بستن پنجره +Name[fi]=Sulje ikkuna +Name[fr]=Fermer une fenêtre +Name[fy]=Finster slute +Name[ga]=Dún Fuinneog +Name[gl]=Pechar Fiestra +Name[he]=סגירת חלון +Name[hi]=विंडो बनà¥à¤¦ करें +Name[hr]=Zatvori prozor +Name[hu]=Ablak bezárása +Name[id]=Tutup Jendela +Name[is]=Glugga lokað +Name[it]=Chiudi finestra +Name[ja]=ウィンドウを閉ã˜ã‚‹ +Name[ka]=ფáƒáƒœáƒ¯áƒ ის დáƒáƒ®áƒ£áƒ ვრ+Name[kk]=Терезені жабу +Name[km]=បង្អួច​បិទ +Name[ko]=ì°½ 닫기 +Name[lo]=ປິດຫນ້າຕ່າງ +Name[lt]=Uždaryti langÄ… +Name[lv]=AizvÄ“rt logu +Name[mk]=Затвори прозорец +Name[mn]=Цонх хаах +Name[ms]=Tetingkap Tutup +Name[mt]=Agħlaq Window +Name[nb]=Lukk vindu +Name[nds]=Finster tomaken +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² बनà¥à¤¦ +Name[nl]=Venster sluiten +Name[nn]=Lukk vindauge +Name[nso]=Tswalelo ya Window +Name[oc]=Tanca finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਬੰਦ +Name[pl]=ZamkniÄ™cie okna +Name[pt]=Fechar a Janela +Name[pt_BR]=Fechar Janela +Name[ro]=ÃŽnchide fereastra +Name[ru]=Закрыть окно +Name[rw]=Idirishya Gufunga +Name[se]=Láse giddana +Name[sk]=ZatvoriÅ¥ okno +Name[sl]=Zapri okno +Name[sr]=Прозор Ñе затвара +Name[sr@Latn]=Prozor se zatvara +Name[ss]=Vala liwindi +Name[sv]=Fönster stängs +Name[ta]=சாளரதà¯à®¤à¯ˆ மூட௠+Name[te]=విండొ à°®à±à°¸à°¿à°µà±†à°¯à°¿ +Name[tg]=Пӯшидани тиреза +Name[th]=ปิดหน้าต่าง +Name[tr]=Pencereyi Kapat +Name[tt]=Täräzäne Yap +Name[uk]=Закрити вікно +Name[uz]=Oynani yopish +Name[uz@cyrillic]=Ойнани ёпиш +Name[ven]=Valani Windo +Name[vi]=Cá»a sổ đóng +Name[wa]=Cloyaedje di purnea +Name[xh]=Window Iyavala +Name[zh_CN]=窗å£å…³é— +Name[zh_TW]=關閉視窗 +Name[zu]=Ukuvaleka Kwe-window +Comment=A window closes +Comment[af]='n Venster het toegemaak +Comment[ar]=ناÙذة تغلق +Comment[az]=PÉ™ncÉ™rÉ™ baÄŸlanır +Comment[be]=Ðкно закрываецца +Comment[bg]=ЗатварÑне на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ বনà§à¦§ করা হয়েছে +Comment[br]=Sarret eo ur prenestr +Comment[bs]=Prozor se zatvara +Comment[ca]=Es tanca una finestra +Comment[cs]=Okno bylo zavÅ™eno +Comment[csb]=Ã’kno sã zamëkô +Comment[da]=Et vindue lukker +Comment[de]=Fenster wird geschlossen +Comment[el]=Ένα παÏάθυÏο κλείνει +Comment[eo]=Fenestro fermiÄas +Comment[es]=Se cierra una ventana +Comment[et]=Aken sulgub +Comment[eu]=Leihoa itxi egiten da +Comment[fa]=پنجره را می‌بندد +Comment[fi]=Ikkuna sulkeutuu +Comment[fr]=Une fenêtre se ferme +Comment[fy]=In finster slút +Comment[ga]=Dúntar fuinneog +Comment[gl]=Pecha-se unha fiestra +Comment[he]=חלון × ×¡×’×¨ +Comment[hi]=à¤à¤• विंडो बनà¥à¤¦ हà¥à¤† +Comment[hr]=Prozor se zatvara +Comment[hu]=Ablak bezáródása +Comment[is]=Gluggi lokast +Comment[it]=Una finestra si chiude +Comment[ja]=ウィンドウãŒé–‰ã˜ã¾ã™ +Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რიხურებრ+Comment[kk]=Терезені жабу +Comment[km]=បង្អួច​មួយ​បិទ +Comment[ko]=ì°½ì´ ë‹«íž˜ +Comment[lt]=Langas uždaromas +Comment[lv]=Logs Aizveras +Comment[mk]=Прозорецот Ñе затвора +Comment[ms]=Tetingkap tutup +Comment[mt]=Window Tingħalaq +Comment[nb]=Et vindu lukkes +Comment[nds]=En Finster warrt tomaakt +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² बनà¥à¤¦ +Comment[nl]=Een venster sluit +Comment[nn]=Eit vindauge vert lukka +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਬੰਦ +Comment[pl]=Okno siÄ™ zamyka +Comment[pt]=Uma janela é fechada +Comment[pt_BR]=Uma janela fecha +Comment[ro]=A fost închisă o fereastră +Comment[ru]=Окно закрываетÑÑ +Comment[rw]=Idirishya rifunga +Comment[se]=Láse giddejuvvo +Comment[sk]=Okno sa uzavrie +Comment[sl]=Zapre se okno +Comment[sr]=Прозор Ñе затвара +Comment[sr@Latn]=Prozor se zatvara +Comment[sv]=Ett fönster stängs +Comment[ta]=சாளரம௠மூடà¯à®•à®¿à®±à®¤à¯ +Comment[te]=విండొ à°®à±à°¸à°¿à°µà±†à°¯à°¿à°¬à°¡à±à°¤à±à°‚ది +Comment[th]=หน้าต่างถูà¸à¸›à¸´à¸” +Comment[tr]=Bir pencere kapatıldı +Comment[tt]=Täräzäne yabu +Comment[uk]=Вікно закриваєтьÑÑ +Comment[uz]=Oyna yopilmoqda +Comment[uz@cyrillic]=Ойна ёпилмоқда +Comment[vi]=Má»™t cá»a sổ đóng +Comment[wa]=On purnea est cloyou +Comment[zh_CN]=窗å£å…³é— +Comment[zh_TW]=關閉一個視窗 +default_sound=KDE_Window_Close.ogg +default_presentation=0 + +[shadeup] +Name=Window Shade Up +Name[af]=Venster Skadu Aan +Name[ar]=إزالة إنسدال الناÙذة +Name[az]=PÉ™ncÉ™rÉ™nin Yuxarı Burulması +Name[be]=Згарнуць акно Ñž загаловак +Name[bg]=Свиване на прозорец нагоре +Name[bn]=উইণà§à¦¡à§‹ উপরে গà§à¦Ÿà¦¿à§Ÿà§‡ নাও +Name[br]=Rollañ ar prenestr +Name[bs]=Podizanje prozora +Name[ca]=Plega la finestra +Name[cs]=Zarolovánà okna +Name[csb]=Ã’dkrëwanié òkna +Name[cy]=Rholio'r Ffenestr i Fyny +Name[da]=Skyg vindue op +Name[de]=Fensterheber nach oben +Name[el]=ΤÏλιγμα πάνω παÏαθÏÏου +Name[eo]=Fenestro supren volviÄas +Name[es]=Enrrollar ventana +Name[et]=Akna varjamine +Name[eu]=Bildu leihoa +Name[fa]=سایۀ رو به بالای پنجره +Name[fi]=Rullaa ikkuna ylös +Name[fr]=Enrouler une fenêtre +Name[fy]=Finster oprôlje +Name[gl]=Enrolar Fiestra +Name[he]=גלילת חלון למעלה +Name[hi]=विंडो शेड अप +Name[hr]=Zamotaj prozor +Name[hu]=Ablak felgördÃtése +Name[id]=Jendela berangsur ke atas +Name[is]=Glugga rúllað upp +Name[it]=Arrotola finestra +Name[ja]=ウィンドウã®ã‚·ã‚§ãƒ¼ãƒ‰ +Name[ka]=ფáƒáƒœáƒ¯áƒ ის áƒáƒ™áƒ”ცვრ+Name[kk]=Ðйдарына түю +Name[km]=បង្អួច​លិប​ចូល +Name[ko]=ì°½ì´ ë§ì•„ ì˜¬ë ¤ì§ +Name[lo]=ພັບເàºàº±àºšàº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Rodyti pilnai +Name[lv]=AizÄ“nots logs +Name[mk]=ЗаÑенчи нагоре +Name[mn]=Цонхны ÑүүдÑÑ€ дÑÑш нь +Name[mt]=Cekken Window f'Linja +Name[nb]=Rull opp vinduet +Name[nds]=Finster inrullen +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सेड अप +Name[nl]=Venster oprollen +Name[nn]=Rull opp vindauge +Name[nso]=Ntshofatso ya Window +Name[oc]=Plega la finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਪਰਛਾਵਾਂ ਕਰੋ +Name[pl]=OdsÅ‚oniÄ™cie okna +Name[pt]=Enrolar a Janela +Name[pt_BR]=Enrolar Janela +Name[ro]=Strînge fereastra +Name[ru]=Свернуть в заголовок +Name[rw]=Idirishya Ubwijime Hejuru +Name[se]=Rulle láse bajás +Name[sk]=ZobraziÅ¥ okno +Name[sl]=Zvij okno +Name[sr]=Прозор Ñе намотава +Name[sr@Latn]=Prozor se namotava +Name[sv]=Fönster rullas upp +Name[ta]=சாளர நிழல௠à®à®±à¯à®±à¯ +Name[tg]=Тиреза ба боло ÑÐ¾Ñ ÑˆÑƒÐ´ +Name[th]=พับเà¸à¹‡à¸šà¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Name[tr]=Pencere Yukarı +Name[tt]=Täräzäne TasmaÄŸa Tör +Name[uk]=Згорнути вікно +Name[ven]=Murunzi wa windo wa ntha +Name[vi]=Cá»a sổ cuốn lên +Name[wa]=Erôlmint do purnea +Name[xh]=Window Yenza umthunzi Phezulu +Name[zh_CN]=窗å£å·èµ· +Name[zh_TW]=收起視窗 +Name[zu]=Umthunzi we-Window Uphezulu +Comment=A window is shaded up +Comment[af]='n Venster se skaduwee is geaktiveer +Comment[ar]=تم إلغاء إنسدال الناÙذة +Comment[az]=PÉ™ncÉ™rÉ™ yuxarı burulub +Comment[be]=Ðкно згорнутае Ñž загаловак +Comment[bg]=Свиване на прозорец нагоре +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ গà§à¦Ÿà¦¾à¦¨à§‹ হয়েছে +Comment[br]=Rollet eo ur prenestr +Comment[bs]=Prozor je podignut (zarolan) +Comment[ca]=Una finestra es plega amunt +Comment[cs]=Okno je zarolováno +Comment[csb]=Ã’kno òstôwô rozwité +Comment[da]=Et vindue skygges op +Comment[de]=Fenster ist eingefahren +Comment[el]=Ένα παÏάθυÏο τυλίχθηκε πάνω +Comment[eo]=Fenestro volviÄis supren +Comment[es]=Se recoge una ventana +Comment[et]=Aken rullitakse kokku +Comment[eu]=Leihoa bildu da +Comment[fa]=پنجره دارای سایۀ رو به بالاست +Comment[fi]=Ikkuna rullataan ylös +Comment[fr]=Une fenêtre est enroulée +Comment[fy]=In finster is oprôle +Comment[gl]=Unha fiestra prega-se +Comment[he]=חלון × ×’×œ×œ למעלה +Comment[hi]=à¤à¤• विंडो में छाया à¤à¤°à¥€ गई +Comment[hr]=Prozor je zamotan +Comment[hu]=Ablak felgördÃtése +Comment[is]=Glugga er rúllað upp +Comment[it]=Una finestra viene arrotolata +Comment[ja]=ウィンドウãŒã‚·ã‚§ãƒ¼ãƒ‰ã•ã‚Œã¾ã—㟠+Comment[kk]=Терезе айдарына түйілді +Comment[km]=បង្អួច​មួយ​ážáŸ’រូវ​បាន​លិប​ចូល +Comment[ko]=ì°½ì´ ë§ì•„ ì˜¬ë ¤ì§ +Comment[lt]=Langas rodomas pilnai +Comment[lv]=Logs ir AizÄ“nots +Comment[mk]=Прозорецот Ñе заÑенчува нагоре +Comment[mt]=Window tingħalaq 'il fuq +Comment[nb]=Et vindu rulles opp +Comment[nds]=En Finster warrt inrullt +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सेड अप गरियो +Comment[nl]=Een venster is opgerold +Comment[nn]=Eit vindauge vert rulla opp +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਦਾ ਪਰਛਾਵਾਂ ਤਬਦੀਲ +Comment[pl]=Okno zostaje rozwiniÄ™te +Comment[pt]=Uma janela é enrolada +Comment[pt_BR]=Uma janela é enrolada (para cima) +Comment[ro]=O fereastră s-a strîns +Comment[ru]=Окно Ñвёрнуто в заголовок +Comment[rw]=Idirishya rihawe ubwijime hejuru +Comment[se]=Láse rullejuvvo bajás +Comment[sk]=Okno je zabalené +Comment[sl]=Okno je zvito +Comment[sr]=Прозор је намотан +Comment[sr@Latn]=Prozor je namotan +Comment[sv]=Ett fönster rullas upp +Comment[ta]=சாளரம௠நிழலிடபà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. +Comment[th]=หน้าต่างถูà¸à¸žà¸±à¸šà¸‚ึ้น +Comment[tr]=Pencere yukarı gizlendi +Comment[tt]=Täräzä tasmaÄŸa törelde +Comment[uk]=Вікно згорнуто +Comment[vi]=Má»™t cá»a sổ đã bị cuốn lên +Comment[wa]=On purnea a stî erôlé +Comment[zh_CN]=窗å£è¢«å·èµ· +Comment[zh_TW]=視窗已收起 +default_sound=KDE_Window_Shade_Up.ogg +default_presentation=0 + +[shadedown] +Name=Window Shade Down +Name[af]=Venster Skadu Af +Name[ar]=إنسدال الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ni AÅŸağı Salınması +Name[be]=Разгарнуць акно з загалоўка +Name[bg]=Свиване на прозорец надолу +Name[bn]=উইণà§à¦¡à§‹ নামাও +Name[br]=Dirollañ ar Prenestr +Name[bs]=SpuÅ¡tanje prozora +Name[ca]=Desplega la finestra +Name[cs]=Vyrolovánà okna +Name[csb]=Zacyganié òkna +Name[cy]=Rholio'r Ffenestr i Lawr +Name[da]=Skyg vindue ned +Name[de]=Fensterheber nach unten +Name[el]=ΤÏλιγμα κάτω παÏαθÏÏου +Name[eo]=Fenestro malsupren volviÄas +Name[es]=Desenrrollar ventana +Name[et]=Akna taasnäitamine +Name[eu]=Zabaldu leihoa +Name[fa]=سایۀ رو به پایین پنجره +Name[fi]=Rullaa ikkuna alas +Name[fr]=Dérouler une fenêtre +Name[fy]=Finster ôfrôlje +Name[gl]=Despregar Fiestra +Name[he]=גלילת חלון למטה +Name[hi]=विंडो शेड डाउन +Name[hr]=Odmotaj prozor +Name[hu]=Ablak legördÃtése +Name[id]=Jendela berangsur ke basah +Name[is]=Glugga rúllað niður +Name[it]=Srotola finestra +Name[ja]=ウィンドウã®ã‚·ã‚§ãƒ¼ãƒ‰è§£é™¤ +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ჩáƒáƒ›áƒáƒ¨áƒšáƒ +Name[kk]=Терезені айдарынан жаю +Name[km]=បង្អួច​លា​ចáŸáž‰ +Name[ko]=ì°½ì´ í’€ì–´ ë‚´ë ¤ì§ +Name[lo]=ເລີàºàºžàº±àºšà»€àºàº±àºšàº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Tik antraÅ¡tÄ—s juosta +Name[lv]=AtÄ“nots Logs +Name[mk]=ЗаÑенчи прозорец надолу +Name[mn]=Цонхны ÑүүдÑÑ€ доош нь +Name[mt]=Kabbar Window Minn Linja +Name[nb]=Rull ned vinduet +Name[nds]=Finster utrullen +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सेड डाउन +Name[nl]=Venster afrollen +Name[nn]=Rull ned vindauge +Name[nso]=Tagafatso ya Window +Name[oc]=Desplega la finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਪਰਛਾਵਾਂ ਹਟਾਓ +Name[pl]=ZasÅ‚oniÄ™cie okna +Name[pt]=Desenrolar a Janela +Name[pt_BR]=Desenrolar Janela +Name[ro]=Derulează fereastra +Name[ru]=Развернуть из заголовка +Name[rw]=Idirishya Ubwijime Hasi +Name[se]=Rulle láse vulos +Name[sk]=SchovaÅ¥ okno +Name[sl]=Razvij okno +Name[sr]=Прозор Ñе одмотава +Name[sr@Latn]=Prozor se odmotava +Name[sv]=Fönster rullas ner +Name[ta]=சாளர நிழல௠இறகà¯à®•à¯ +Name[tg]=Тиреза ба поён ÑÐ¾Ñ ÑˆÑƒÐ´ +Name[th]=ยà¸à¹€à¸¥à¸´à¸à¸žà¸±à¸šà¹€à¸à¹‡à¸šà¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Name[tr]=Pencere AÅŸağı +Name[tt]=Täräzäne Tasmadan Torğız +Name[uk]=Розгорнути вікно +Name[ven]=Murunzi wa windo wa fhasi +Name[vi]=Cá»a sổ cuốn xuống +Name[wa]=Disrôlmint des purneas +Name[xh]=Window Yenza umthunzi Ezantsi +Name[zh_CN]=窗å£å±•å¼€ +Name[zh_TW]=展開視窗 +Name[zu]=Umthunzi we-Window Uphansi +Comment=A window is shaded down +Comment[af]='n Venster se skaduwee is gedeaktiveer +Comment[ar]=تم إنسدال الناÙذة +Comment[az]=PÉ™ncÉ™rÉ™ aÅŸağı salınıb +Comment[be]=Ðкно разгорнутае з загалоўка +Comment[bg]=Свиване на прозорец нагоре +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ নামানো হয়েছে +Comment[br]=Dirollet eo ur prenestr +Comment[bs]=Prozor je spuÅ¡ten (odrolan) +Comment[ca]=Una finestra es plega avall +Comment[cs]=Okno je vyrolováno +Comment[csb]=Ã’kno òstôwô zacygniãté +Comment[da]=Et vindue skygges ned +Comment[de]=Fenster ist ausgefahren +Comment[el]=Ένα παÏάθυÏο τυλίχθηκε κάτω +Comment[eo]=Fenestro volviÄis malsupren +Comment[es]=Se extiende una ventana +Comment[et]=Aken rullitakse lahti +Comment[eu]=Leihoa zabaldu da +Comment[fa]=پنجره دارای سایۀ رو به پایین است +Comment[fi]=Ikkuna rullataan alas +Comment[fr]=Une fenêtre est déroulée +Comment[fy]=In finster is ôfrôle +Comment[gl]=Unha fiestra desprega-se +Comment[he]=חלון × ×’×œ×œ למטה +Comment[hi]=à¤à¤• विंडो से छाया हटाई गई +Comment[hr]=Prozor je odmotan +Comment[hu]=Ablak legördÃtése +Comment[is]=Glugga er rúllað niður +Comment[it]=Una finestra viene srotolata +Comment[ja]=ウィンドウã®ã‚·ã‚§ãƒ¼ãƒ‰ãŒè§£é™¤ã•ã‚Œã¾ã—㟠+Comment[kk]=Терезе айдарынан жайылды +Comment[km]=បង្អួច​មួយ​ážáŸ’រូវ​បាន​លា​ចáŸáž‰ +Comment[ko]=ì°½ì´ í’€ì–´ ë‚´ë ¤ì§ +Comment[lt]=Rodoma tik lango antraÅ¡tÄ—s juosta +Comment[lv]=Logs ir AtÄ“nots +Comment[mk]=Прозорецот е заÑенчен надолу +Comment[mt]=Window tingħalaq 'l isfel +Comment[nb]=Et vindu rulles ned +Comment[nds]=En Finster warrt wedder utrullt +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सेड डाउन गरियो +Comment[nl]=Een venster is afgerold +Comment[nn]=Eit vindauge vert rulla ned +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਦਾ ਪਰਛਾਵਾਂ ਹਟਾਓ +Comment[pl]=Okno zostaje zwiniÄ™te +Comment[pt]=Uma janela é desenrolada +Comment[pt_BR]=Uma janela é desenrolada +Comment[ro]=O fereastră s-a derulat +Comment[ru]=Окно развёрнуто из заголовка +Comment[rw]=Idirishya rihawe ubwijime hasi +Comment[se]=Láse rullejuvvo vulos +Comment[sk]=Okno je rozbalené +Comment[sl]=Okno je razvito +Comment[sr]=Прозор је одмотан +Comment[sr@Latn]=Prozor je odmotan +Comment[sv]=Ett fönster rullas ner +Comment[ta]=சாளரமொனà¯à®±à¯ நிழலிடபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[th]=หน้าต่างถูà¸à¸„ลี่ลง +Comment[tr]=Pencere aÅŸağı gizlendi +Comment[tt]=Täräzä tasmadan torğızıldı +Comment[uk]=Вікно розгорнуто +Comment[vi]=Má»™t cá»a sổ đã bị cuốn xuống +Comment[wa]=On purnea a stî disrôlé +Comment[zh_CN]=窗å£è¢«å±•å¼€ +Comment[zh_TW]=視窗已展開 +default_sound=KDE_Window_Shade_Down.ogg +default_presentation=0 + +[minimize] +Name=Window Minimize +Name[af]=Minimeer Venster +Name[ar]=تصغير الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ KiçildilmÉ™si +Name[be]=Ðайменшыць акно +Name[bg]=Минимизиране на прозорец +Name[bn]=উইণà§à¦¡à§‹ মিনিমাইজ +Name[br]=Kilbleg ar prenestr +Name[bs]=Minimiziranje prozora +Name[ca]=Minimitza finestra +Name[cs]=Minimalizace okna +Name[csb]=Minimalizacëjô òkna +Name[cy]=Lleihau Ffenestr +Name[da]=Minimér vindue +Name[de]=Fenster minimieren +Name[el]=Ελαχιστοποίηση παÏαθÏÏου +Name[en_GB]=Window Minimise +Name[eo]=Fenestro minimumiÄas +Name[es]=Minimizar ventana +Name[et]=Akna minimeerimine +Name[eu]=Minimizatu leihoa +Name[fa]=کمینه‌سازی پنجره +Name[fi]=Pienennä ikkuna +Name[fr]=Réduire une fenêtre +Name[fy]=Minimalisearje finster +Name[ga]=Ãoslaghdaigh Fuinneog +Name[gl]=Fiestra Minimizada +Name[he]=מזעור חלון +Name[hi]=विंडो नà¥à¤¯à¥‚नतम +Name[hr]=Minimiziraj prozor +Name[hu]=Ablak minimalizálása +Name[is]=Glugga lágmarkað +Name[it]=Minimizza finestra +Name[ja]=ウィンドウ最å°åŒ– +Name[ka]=ფáƒáƒœáƒ¯áƒ ის მინიმიზებრ+Name[kk]=Терезені түю +Name[km]=បង្អួច​បង្រួម​អប្បបរមា +Name[ko]=ì°½ì´ ìµœì†Œí™”ë¨ +Name[lt]=Sumažinti langÄ… +Name[lv]=Minimizet logu +Name[mk]=Спушти прозорец +Name[mn]=Цонх жижигÑгÑÑ… +Name[mt]=Imminimizza Window +Name[nb]=Minimer vindu +Name[nds]=Finster minimeert +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² नà¥à¤¯à¥‚न पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Venster minimaliseren +Name[nn]=Minimer vindauge +Name[pa]=à¨à¨°à©‹à¨–ਾ ਨਿਊਨਤਮ +Name[pl]=Minimalizacja okna +Name[pt]=Minimizar a Janela +Name[pt_BR]=Minimizar Janela +Name[ro]=Minimizează fereastra +Name[ru]=ÐœÐ¸Ð½Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¾ÐºÐ½Ð° +Name[rw]=Idirishya KugiraGito +Name[se]=Minimere láse +Name[sk]=MinimalizovaÅ¥ okno +Name[sl]=PomanjÅ¡aj okno +Name[sr]=Минимизација прозора +Name[sr@Latn]=Minimizacija prozora +Name[sv]=Fönster minimeras +Name[ta]=சாளரதà¯à®¤à¯ˆà®ªà¯ சிறிதாகà¯à®•à¯ +Name[te]=విండొ à°šà°¿à°¨à±à°¨à°¦à°¿ చెయి +Name[tg]=Тирезаи бешина +Name[th]=ย่à¸à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¹€à¸¥à¹‡à¸à¸ªà¸¸à¸” +Name[tr]=Pencereyi Simge Haline Getir +Name[tt]=Täräzäne Törep quy +Name[uk]=Мінімізувати вікно +Name[uz]=Oynani yigÊ»ish +Name[uz@cyrillic]=Ойнани йиғиш +Name[vi]=Thu gá»n Cá»a sổ +Name[wa]=Purnea metou Ã¥ pus ptit +Name[zh_CN]=窗å£æœ€å°åŒ– +Name[zh_TW]=最å°åŒ–視窗 +Comment=A window is minimized +Comment[af]='n Venster is geminimeer +Comment[ar]=تمّ تصغير ناÙذة +Comment[az]=PÉ™ncÉ™rÉ™ kiçildilib +Comment[be]=Ðкно найменшана +Comment[bg]=Минимизиране на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ মিনিমাইজ করা হয়েছে +Comment[br]=Kilbleget eo ur prenestr +Comment[bs]=Prozor je minimiziran +Comment[ca]=Es minimitza una finestra +Comment[cs]=Okno je minimalizováno +Comment[csb]=Ã’kno je minimalizowóné +Comment[da]=Et vindue minimeres +Comment[de]=Fenster ist minimiert +Comment[el]=Ένα παÏάθυÏο ελαχιστοποιήθηκε +Comment[en_GB]=A window is minimised +Comment[eo]=Fenestro minimumiÄas +Comment[es]=Se minimiza una ventana +Comment[et]=Aken on minimeeritud +Comment[eu]=Leihoa minimizatu egiten da +Comment[fa]=پنجره کمینه می‌شود +Comment[fi]=Ikkuna on pienennetty +Comment[fr]=Une fenêtre est réduite +Comment[fy]=In finster is minimalisearre +Comment[gl]=Minimizou-se unha fiestra +Comment[he]=חלון ממוזער +Comment[hi]=à¤à¤• विंडो नà¥à¤¯à¥‚नतम हà¥à¤† +Comment[hr]=Prozor je minimiziran +Comment[hu]=Ablak minimalizálása +Comment[is]=Gluggi er lágmarkaður +Comment[it]=Una finestra viene minimizzata +Comment[ja]=ウィンドウãŒæœ€å°åŒ–ã•ã‚Œã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რმინიმიზირებულირ+Comment[kk]=Терезе түйілді +Comment[km]=បង្អួច​មួយ​ážáŸ’រូវ​បាន​បង្រួម​អប្បបរមា +Comment[ko]=ì°½ì´ ìµœì†Œí™”ë¨ +Comment[lt]=Langas sumažintas +Comment[lv]=Logs ir MinimizÄ“ts +Comment[mk]=Прозорецот Ñе Ñпушта +Comment[mt]=Window Ä¡iet minimizzata +Comment[nb]=Et vindu minimeres +Comment[nds]=En Finster warrt minimeert +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² नà¥à¤¯à¥‚न पारिà¤à¤•à¥‹ छ +Comment[nl]=Een venster is geminimaliseerd +Comment[nn]=Eit vindauge vert minimert +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਨਿਊਨਤਮ +Comment[pl]=Okno jest minimalizowane +Comment[pt]=Uma janela é minimizada +Comment[pt_BR]=Uma janela é minimizada +Comment[ro]=O fereastră a fost minimizată +Comment[ru]=Окно минимизировано +Comment[rw]=Idirishya rigizwe ritoya +Comment[se]=Láse minimerejuvvo +Comment[sk]=Okno je minimalizované +Comment[sl]=Okno je pomanjÅ¡ano +Comment[sr]=Прозор је минимизован +Comment[sr@Latn]=Prozor je minimizovan +Comment[sv]=Ett fönster minimeras +Comment[ta]=சாளரம௠சிறிதாகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯ +Comment[te]=విండొ à°šà°¿à°¨à±à°¨à°¦à°¿ చెయబడà±à°¤à±à°‚ది +Comment[th]=หน้าต่างถูà¸à¸¢à¹ˆà¸à¹€à¸¥à¹‡à¸à¸ªà¸¸à¸” +Comment[tr]=Bir pencere simge haline getirildi +Comment[tt]=Täräzä törep quyıldı +Comment[uk]=Вікно мінімізовано +Comment[uz]=Oyna yigÊ»ilgan +Comment[uz@cyrillic]=Ойна йиғилган +Comment[vi]=Má»™t cá»a sổ đã được thu gá»n +Comment[wa]=On purnea a stî metou Ã¥ pus ptit +Comment[zh_CN]=窗å£è¢«æœ€å°åŒ– +Comment[zh_TW]=視窗被最å°åŒ– +default_sound=KDE_Window_Iconify.ogg +default_presentation=0 + +[unminimize] +Name=Window Unminimize +Name[af]=Venster nie meer geminimeer +Name[ar]=عدم تصغير الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ Geri BöyüdülmÉ™si +Name[be]=Разнайменшыць акно +Name[bg]=ВъзÑтановÑване на минимизиран прозорец +Name[bn]=উইণà§à¦¡à§‹ আনমিনিমাইজ +Name[bs]=Deminimiziranje prozora +Name[ca]=Desminimitza finestra +Name[cs]=Obnovenà okna po minimalizaci +Name[csb]=Doprowadzenié nazôd z minimalizacëji òkna +Name[cy]=Dad-leihau Ffenestr +Name[da]=Afminimér vindue +Name[de]=Minimieren rückgängig machen +Name[el]=Αποελαχιστοποίηση παÏαθÏÏου +Name[en_GB]=Window Unminimise +Name[eo]=Fenestro neminimumiÄas +Name[es]=Deminimizar ventana +Name[et]=Akna suuruse taastamine +Name[eu]=Desminimizatu leihoa +Name[fa]=کمینه نکردن پنجره +Name[fi]=Suurenna ikkuna takaisin +Name[fr]=Restaurer une fenêtre +Name[fy]=Minimalisearjen fan finster ûngedien meitsje +Name[gl]=Fiestra Non Minimizada +Name[he]=ביטול מזעור חלון +Name[hi]=विंडो नà¥à¤¯à¥‚नतम नहीं +Name[hr]=Vrati minimiziran prozor +Name[hu]=Minimalizált ablak visszaállÃtása +Name[is]=Gluggi ekki lágmarkað +Name[it]=Deminimizza finestra +Name[ja]=ウィンドウ最å°åŒ–解除 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ”ბის áƒáƒ¦áƒ“გენრ+Name[kk]=Терезені жаю +Name[km]=បង្អួច​មិន​បង្រួម​អប្បបរមា +Name[ko]=ì°½ 최소화 취소 +Name[lt]=Panaikinti lango sumažinimÄ… +Name[lv]=Loga AtminimizÄ“Å¡ana +Name[mk]=Врати прозорец +Name[mn]=Цонхны томÑголт авах +Name[mt]=Irrestawra window minimizzata +Name[nb]=Gjenopprett vindu +Name[nds]=Finster wedder wiesen +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² नà¥à¤¯à¥‚न नपारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Minimalisatie van venster ongedaan maken +Name[nn]=Gjenopprett vindauge +Name[pa]=à¨à¨°à©‹à¨–ਾ ਨਾ-ਨਿਊਨਤਮ +Name[pl]=Powrót ze stanu minimalizacji okna +Name[pt]=Desminimizar a Janela +Name[pt_BR]=Desminimizar Janela +Name[ro]=Reface fereastra +Name[ru]=ВоÑÑтановление размеров окна +Name[rw]=Idirishya KutagiraGito +Name[se]=Máhcat láse +Name[sk]=ZruÅ¡iÅ¥ minimalizáciu okna +Name[sl]=Od-pomanjÅ¡aj okno +Name[sr]=Прозор Ñе обнавља поÑле минимизације +Name[sr@Latn]=Prozor se obnavlja posle minimizacije +Name[sv]=Fönsterminimering tas bort +Name[ta]=சாளர சிறிதாகà¯à®•à®¾à®¤à¯‡ +Name[te]=విండొ à°šà°¿à°¨à±à°¨à°¦à°¿ చెయకౠ+Name[tg]=Тирезаи то бешина +Name[th]=ยà¸à¹€à¸¥à¸´à¸à¸¢à¹ˆà¸à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¹€à¸¥à¹‡à¸à¸ªà¸¸à¸” +Name[tr]=Pencereyi Simge Halinden Çıkar +Name[tt]=Täräzäne Torğız +Name[uk]=Демінімізувати вікно +Name[vi]=Mở lại Cá»a sổ +Name[wa]=Purnea rimetou a s' grandeu di dvant +Name[zh_CN]=窗å£å–消最å°åŒ– +Name[zh_TW]=å–消最å°åŒ–視窗 +Comment=A Window is restored +Comment[af]='n Venster is herstel +Comment[ar]=ناÙذة استرجعت +Comment[az]=PÉ™ncÉ™rÉ™ köhnÉ™ böyüklüyünÉ™ gÉ™tirilib +Comment[be]=Ðкно адноўлена Ñž памеры +Comment[bg]=ВъзÑтановÑване на минимизиран прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ রিসà§à¦Ÿà§‹à¦° করা হয়েছে +Comment[br]=Assavet eo ur prenestr +Comment[bs]=Prozor je prikazan +Comment[ca]=Es restaura una finestra +Comment[cs]=Okno je obnoveno +Comment[csb]=Ã’kno je doprowadzoné nazôd +Comment[da]=Et vindue genetableres +Comment[de]=Fenster ist wiederhergestellt +Comment[el]=Ένα παÏάθυÏο αποκαταστάθηκε +Comment[eo]=Fenestro reaperas +Comment[es]=Se restaura una ventana +Comment[et]=Aken on taastatud +Comment[eu]=Leihoa leheneratu egiten da +Comment[fa]=پنجره بازگردانده می‌شود +Comment[fi]=Ikkuna on palautettu +Comment[fr]=Une fenêtre en icône est restaurée +Comment[fy]=In finster is hersteld +Comment[gl]=Restaurou-se unha fiestra +Comment[he]=חלון משוחזר +Comment[hi]=à¤à¤• विंडो पà¥à¤¨à¤°à¥à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ हà¥à¤† +Comment[hr]=Prozor je obnovljen +Comment[hu]=Ablak visszaállÃtása +Comment[is]=Gluggi er færður úr táknmynd +Comment[it]=Una finestra viene ripristinata +Comment[ja]=ウィンドウãŒå¾©å…ƒã•ã‚Œã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ áƒáƒ¦áƒ“გენილირ+Comment[kk]=Терезе жайылды +Comment[km]=បង្អួច​មួយ​ážáŸ’រូវ​បាន​ស្ដារ​ឡើង​វិញ +Comment[ko]=ì°½ì´ ë³µì›ë¨ +Comment[lt]=Langas atstatytas +Comment[lv]=Logs ir Atjaunots +Comment[mk]=Прозорецот Ñе враќа +Comment[mt]=Window tiÄ¡i Restawrata +Comment[nb]=Et vindu gjenopprettes +Comment[nds]=En Finster warrt wedderherstellt +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² पूरà¥à¤µà¤¾à¤µà¤¸à¥à¤¥à¤¾à¤®à¤¾ लà¥à¤¯à¤¾à¤‡à¤à¤•à¥‹ छ +Comment[nl]=Een venster is hersteld +Comment[nn]=Eit vindauge vert gjenoppretta +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਮà©à©œ-ਪà©à¨°à¨¾à¨ªà¨¤ +Comment[pl]=Okno jest przywrócone +Comment[pt]=Uma janela é restaurada +Comment[pt_BR]=Uma Janela é restaurada +Comment[ro]=O fereastră a fost restaurată +Comment[ru]=Размер окна воÑÑтановлен +Comment[rw]=Idirishya ryashubijwe ingano yaryo +Comment[se]=Láse huksejuvvo fas +Comment[sk]=Okno je obnovené +Comment[sl]=Okno je povrnjeno +Comment[sr]=Прозор је обновљен +Comment[sr@Latn]=Prozor je obnovljen +Comment[sv]=Ett fönster Ã¥terställs +Comment[ta]=சாளரமொனà¯à®±à¯ மீளமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[te]=విండొనౠపూరà±à°µ à°¸à±à°¥à°¿à°¤à°¿ à°•à°¿ తెసà±à°¤à±à°‚ది +Comment[th]=หน้าต่างถูà¸à¹€à¸£à¸µà¸¢à¸à¸„ืนขนาด +Comment[tr]=Bir pencere eski boyutuna getirildi +Comment[tt]=Täräzä torğızıldı +Comment[uk]=Вікно відновлено +Comment[uz]=Oyna tiklangan +Comment[uz@cyrillic]=Ойна тикланган +Comment[vi]=Má»™t cá»a sổ đã được mở lại +Comment[wa]=On purnea est rfé +Comment[zh_CN]=窗å£è¢«æ¢å¤ +Comment[zh_TW]=視窗æ¢å¾©å¤§å° +default_sound=KDE_Window_DeIconify.ogg +default_presentation=0 + +[maximize] +Name=Window Maximize +Name[af]=Maksimeer Venster +Name[ar]=تكبير الناÙذة +Name[az]=PÉ™ncÉ™rÉ™nin BöyüdülmÉ™si +Name[be]=Ðайбольшыць акно +Name[bg]=МакÑимизиране на прозорец +Name[bn]=উইণà§à¦¡à§‹ মà§à¦¯à¦¾à¦•à§à¦¸à¦¿à¦®à¦¾à¦‡à¦œ +Name[br]=Astenn ar Prenestr +Name[bs]=Maksimiziranje prozora +Name[ca]=Maximitza finestra +Name[cs]=Maximalizace okna +Name[csb]=Maksymalizacëjô òkna +Name[cy]=Ehangu Ffenestr +Name[da]=Maksimér vindue +Name[de]=Fenster maximieren +Name[el]=Μεγιστοποίηση παÏαθÏÏου +Name[en_GB]=Window Maximise +Name[eo]=Fenestro maksimumiÄas +Name[es]=Maximizar ventana +Name[et]=Akna maksimeerimine +Name[eu]=Maximizatu leihoa +Name[fa]=بیشینه‌سازی پنجره +Name[fi]=Suurenna ikkuna +Name[fr]=Maximiser une fenêtre +Name[fy]=finster maksimalisearje +Name[ga]=Uasmhéadaigh Fuinneog +Name[gl]=Maximiza-se unha Fiestra +Name[he]=הגדלת חלון +Name[hi]=विंडो अधिकतम +Name[hr]=Maksimiziraj prozor +Name[hu]=Ablak maximalizálása +Name[id]=Maximize Jendela +Name[is]=Gluggi hámarkaður +Name[it]=Massimizza finestra +Name[ja]=ウィンドウ最大化 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის მáƒáƒ®áƒ¡áƒ˜áƒ›áƒ˜áƒ–ებრ+Name[kk]=Терезені кең жаю +Name[km]=បង្អួច​ពង្រីក​អážáž·áž”រមា +Name[ko]=ì°½ 최대화 +Name[lo]=ຂະຫàºàº²àºàº«àº™à»‰àº²àº•à»ˆàº²àº‡à»ƒàº«àºà»ˆàºªàº¸àº” +Name[lt]=IÅ¡didinti langÄ… +Name[lv]=MaksimizÄ“t logu +Name[mk]=Рашири прозорец +Name[mn]=Цонх томÑгох +Name[mt]=Immassimizza Window +Name[nb]=Maksimer vindu +Name[nds]=Finster maximeren +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² ठूलो पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Venster maximaliseren +Name[nn]=Maksimer vindauge +Name[nso]=Koketso ya Window +Name[oc]=Maximitza finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਅਧਿਕਤਮ +Name[pl]=Maksymalizacja okna +Name[pt]=Maximizar a Janela +Name[pt_BR]=Maximizar Janela +Name[ro]=Maximizează fereastra +Name[ru]=РаÑпахнуть окно +Name[rw]=Idirishya KugiraKinini +Name[se]=Maksimere láse +Name[sk]=MaximalizovaÅ¥ okno +Name[sl]=Razpni okno +Name[sr]=Прозор Ñе макÑимизује +Name[sr@Latn]=Prozor se maksimizuje +Name[ss]=Khulisa liwindi +Name[sv]=Fönster maximeras +Name[ta]=சாளரதà¯à®¤à¯ˆà®ªà¯ பெரிதாகà¯à®•à¯ +Name[te]=విండొ పెదà±à°¦à°¦à°¿ చెయి +Name[tg]=Тирезаи бешина +Name[th]=ขยายหน้าต่างเต็มจภ+Name[tr]=Pencereyi Kapla +Name[tt]=Täräzäne Cäyep quy +Name[uk]=МакÑимізувати вікно +Name[uz]=Oynani yoyish +Name[uz@cyrillic]=Ойнани ёйиш +Name[ven]=U hudza Windo +Name[vi]=Mở to Cá»a sổ +Name[wa]=Purnea metou Ã¥ pus grand +Name[xh]=Window Yenza nkulu +Name[zh_CN]=窗å£æœ€å¤§åŒ– +Name[zh_TW]=最大化視窗 +Name[zu]=Khulisa I-window +Comment=A window is maximized +Comment[af]='n Venster is gemaksimeer +Comment[ar]=ناÙذة كبّرت +Comment[az]=PÉ™ncÉ™rÉ™ böyüdülüb +Comment[be]=Ðкно найбольшана +Comment[bg]=МакÑимизиране на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ মà§à¦¯à¦¾à¦•à§à¦¸à¦¿à¦®à¦¾à¦‡à¦œ করা হয়েছে +Comment[br]=Astennet eo ur prenestr +Comment[bs]=Prozor je maksimiziran +Comment[ca]=Es maximitza una finestra +Comment[cs]=Okno je maximalizováno +Comment[csb]=Ã’kno je maksymilizowóné +Comment[da]=Et vindue maksimeres +Comment[de]=Fenster ist maximiert +Comment[el]=Ένα παÏάθυÏο μεγιστοποιήθηκε +Comment[en_GB]=A window is maximised +Comment[eo]=Fenestro maksimumiÄas +Comment[es]=Se maximiza una ventana +Comment[et]=Aken on maksimeeritud +Comment[eu]=Leihoa maximizatu egiten da +Comment[fa]=پنجره بیشینه می‌شود +Comment[fi]=Ikkuna on suurennettu +Comment[fr]=Une fenêtre est maximisée +Comment[fy]=In finster is maksimalisearre +Comment[gl]=Maximizou-se unha fiestra +Comment[he]=חלון מוגדל +Comment[hi]=à¤à¤• विंडो अधिकतम हà¥à¤† +Comment[hr]=Prozor je maksimiziran +Comment[hu]=Ablak maximalizálása +Comment[is]=Gluggi er hámarkaður +Comment[it]=Una finestra viene massimizzata +Comment[ja]=ウィンドウãŒæœ€å¤§åŒ–ã•ã‚Œã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რმáƒáƒ¥áƒ¡áƒ˜áƒ›áƒ˜áƒ–ირებულირ+Comment[kk]=Терезе барынша кеңейтілді +Comment[km]=បង្អួច​មួយ​ážáŸ’រូវ​បាន​ពង្រីក​អážáž·áž”រមា +Comment[ko]=ì°½ì´ ìµœëŒ€í™”ë¨ +Comment[lt]=Langas iÅ¡didintas +Comment[lv]=Logs ir MaksimizÄ“ts +Comment[mk]=Прозорецот Ñе раширува +Comment[mt]=Window tiÄ¡i Mkabbra +Comment[nb]=Et vindu maksimeres +Comment[nds]=En Finster warrt maximeert +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² ठूलो पारिà¤à¤•à¥‹ छ +Comment[nl]=Een venster is gemaximaliseerd +Comment[nn]=Eit vindauge vert maksimert +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਅਧਿਕਤਮ +Comment[pl]=Okno jest maksymalizowane +Comment[pt]=Uma janela é maximizada +Comment[pt_BR]=Uma janela é maximizada +Comment[ro]=O fereastră a fost maximizată +Comment[ru]=Окно раÑпахнуто на веÑÑŒ Ñкран +Comment[rw]=Idirishya rigizwe riri +Comment[se]=Láse maksimerejuvvo +Comment[sk]=Okno je maximalizované +Comment[sl]=Okno je razpeto +Comment[sr]=Прозор је макÑимизован +Comment[sr@Latn]=Prozor je maksimizovan +Comment[sv]=Ett fönster maximeras +Comment[ta]=சாளரம௠பெரிதாகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯ +Comment[te]=విండొ పెదà±à°¦à°¦à°¿ చెయబడà±à°¤à±à°‚ది +Comment[th]=หน้าต่างถูà¸à¸‚ยายเต็มจภ+Comment[tr]=Bir pencere büyütüldü +Comment[tt]=Täräzä cäyelep quyıldı +Comment[uk]=Вікно макÑимізовано +Comment[uz]=Oyna yoyilgan +Comment[uz@cyrillic]=Ойна ёйилган +Comment[vi]=Má»™t cá»a sổ đã được mở to +Comment[wa]=On purnea a stî metou Ã¥ pus grand +Comment[zh_CN]=窗å£è¢«æœ€å¤§åŒ– +Comment[zh_TW]=視窗被最大化 +default_sound=KDE_Window_UnHide.ogg +default_presentation=0 + +[unmaximize] +Name=Window Unmaximize +Name[af]=Venster nie meer gemaksimeer +Name[ar]=إزالة تكبير ناÙذة +Name[az]=PÉ™ncÉ™rÉ™nin Geri KiçildilmÉ™si +Name[be]=Разнайбольшыць акно +Name[bg]=ВъзÑтановÑване на макÑимизиран прозорец +Name[bn]=উইণà§à¦¡à§‹ আনমà§à¦¯à¦¾à¦•à§à¦¸à¦¿à¦®à¦¾à¦‡à¦œ +Name[br]=Krennañ ar Prenestr +Name[bs]=Demaksimiziranje prozora +Name[ca]=Desmaximitza finestra +Name[cs]=Obnovenà okna po maximalizaci +Name[csb]=Doprowadzenié nazôd z masymilizacëji òkna +Name[cy]=Dad_ehangu Ffenestr +Name[da]=Afmaksimér vindue +Name[de]=Fenster auf vorige Größe +Name[el]=Απομεγιστοποίηση παÏαθÏÏου +Name[en_GB]=Window Unmaximise +Name[eo]=Fenestro nemaksimumiÄas +Name[es]=Demaximizar ventana +Name[et]=Akna suuruse taastamine +Name[eu]=Desmaximizatu leihoa +Name[fa]=بیشینه نکردن پنجره +Name[fi]=Poista ikkunan suurennus +Name[fr]=Restaurer une fenêtre maximisée +Name[fy]=Maksimalisaasje fan finster ûngedien meitsje +Name[gl]=Desmaximiza-se unha Fiestra +Name[he]=שיחזור הגדלת חלון +Name[hi]=विंडो अधिकतम नहीं +Name[hr]=Vrati maksimiziran prozor +Name[hu]=Maximalizált ablak visszaállÃtása +Name[id]=UnMaximize Jendela +Name[is]=Gluggi úr hámarki +Name[it]=Demassimizza finestra +Name[ja]=ウィンドウ最大化解除 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ áƒáƒ¦áƒ“გენილირ+Name[kk]=Терезені тарылттыру +Name[km]=បង្អួច​មិន​ពង្រីក​អážáž·áž”រមា +Name[ko]=ì°½ 최대화 취소 +Name[lo]=àºàº»àºà»€àº¥àºµàºàº‚ະຫàºàº²àºàº«àº™à»‰àº²àº•à»ˆàº²àº‡à»ƒàº«àºà»ˆàºªàº¸àº” +Name[lt]=Sumažinti langÄ… +Name[lv]=Loga AtmaksimizÄ“Å¡ana +Name[mk]=Одрашири прозорец +Name[mn]=Цонхны томÑголт авах +Name[mt]=ÄŠekken Window mill-Massimu +Name[nb]=Gjenopprett vindu +Name[nds]=Finster op ole Grött +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² नबढाउनà¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Maximalisatie van venster ongedaan maken +Name[nn]=Gjenopprett vindauge +Name[nso]=Phokotso ya Window +Name[oc]=Demaximitza finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਨਾ-ਅਧਿਕਤਮ +Name[pl]=Powrót ze stanu maksymalizacji okna +Name[pt]=Reduzir a Janela +Name[pt_BR]=Desmaximizar Janela +Name[ro]=Reface fereastra +Name[ru]=ВоÑÑтановить размер окна +Name[rw]=Idirishya KutagiraKinini +Name[se]=Máhcat láse +Name[sk]=ZmenÅ¡iÅ¥ okno +Name[sl]=Od-razpni okno +Name[sr]=Прозор Ñе обнавља поÑле макÑимизовања +Name[sr@Latn]=Prozor se obnavlja posle maksimizovanja +Name[sv]=Fönstermaximering tas bort +Name[ta]=சாளரதà¯à®¤à¯ˆ பெரிதாகà¯à®•à®¾à®¤à¯‡ +Name[te]=విండొ పెదà±à°¦à°¦à°¿ చెయకౠ+Name[tg]=Тирезаи то бешина +Name[th]=ยà¸à¹€à¸¥à¸´à¸à¸‚ยายหน้าต่างใหà¸à¹ˆà¸ªà¸¸à¸” +Name[tr]=Pencereyi Küçült +Name[tt]=Täräzä Ãœlçämen Torğız +Name[uk]=ДемакÑимізувати вікно +Name[ven]=U fhungudza Windo +Name[vi]=Nhá» lại Cá»a sổ +Name[wa]=Purnea rimetou a s' grandeu di dvant +Name[xh]=Window Sukwenza ubukhulu +Name[zh_CN]=窗å£å–消最大化 +Name[zh_TW]=å–消最大化視窗 +Name[zu]=Nciphisa i-Window +Comment=A window loses maximization +Comment[af]='n Venster het sy maksimisering verloor +Comment[ar]=ناÙذة Ùقدت التكبير +Comment[az]=PÉ™ncÉ™rÉ™ geri kiçildilir +Comment[be]=Ðкно адноўлена Ñž памеры +Comment[bg]=ВъзÑтановÑване на макÑимизиран прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ মà§à¦¯à¦¾à¦•à§à¦¸à¦¿à¦®à¦¾à¦‡à¦œà§‡à¦¶à¦¨ হারিয়েছে +Comment[br]=Koll a ra ur prenestr e astenn +Comment[bs]=Prozor gubi maksimizaciju +Comment[ca]=Una finestra perd la maximització +Comment[cs]=Okno ztratilo maximalizaci +Comment[csb]=Ã’kno nie je ju zmaksymilizowóné +Comment[da]=Et vindue afmaksimeres +Comment[de]=Fenster unter Maximalgröße gebracht +Comment[el]=Ένα παÏάθυÏο χάνει τη μεγιστοποίησή του +Comment[en_GB]=A window loses maximisation +Comment[eo]=Fenestro nemaksimumiÄis +Comment[es]=Una ventana pierde su maximización +Comment[et]=Aken kaotab maksimaalse suuruse +Comment[eu]=Leihoak galdu egiten du maximizazioa +Comment[fa]=بیشینه‌سازی پنجره از بین می‌رود +Comment[fi]=Ikkuna menettää suurennuksen +Comment[fr]=Une fenêtre maximisée est restaurée +Comment[fy]=In finster ferliest maksimalisaasje +Comment[gl]=Unha fiestra perdeu a maximización +Comment[he]=מבוטלת הגדלת חלון +Comment[hi]=à¤à¤• विंडो ने अधिकतम खोया +Comment[hr]=Prozor gubi maksimiziranost +Comment[hu]=Maximalizált ablak visszaállÃtása +Comment[is]=Gluggi hættir að vera hámarkaður +Comment[it]=Una finestra massimizzata viene ripristinata +Comment[ja]=ウィンドウã®æœ€å¤§åŒ–ãŒè§£é™¤ã•ã‚Œã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რáƒáƒ¦áƒáƒ áƒáƒ მáƒáƒ¥áƒ¡áƒ˜áƒ›áƒ˜áƒ–ირებული +Comment[kk]=Терезе кеңейтуден қайтты +Comment[km]=បង្អួច​មួយ​បាážáŸ‹áž”ង់​ការ​ពង្រីក​អážáž·áž”រមា +Comment[ko]=ì°½ 최대화가 ì·¨ì†Œë¨ +Comment[lt]=Langas prarado iÅ¡didinimÄ… +Comment[lv]=Logs ZaudÄ“jis MaksimizÄciju +Comment[mk]=Прозорецот го губи раширувањето +Comment[mt]=Window ma Tibqax Imkabbra +Comment[nb]=Et vindu mister maksimeringa +Comment[nds]=En Finster is nich mehr maximeert +Comment[ne]=ठूलो पारिà¤à¤•à¥‹ सञà¥à¤à¥à¤¯à¤¾à¤² हराउछ +Comment[nl]=Een venster verliest maximalisatie +Comment[nn]=Eit vindauge mister maksimering +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਢਿੱਲਾ ਅਧਿਕਤਮ +Comment[pl]=Okno przestaje być zmaksymalizowane +Comment[pt]=Uma janela deixa de estar maximizada +Comment[pt_BR]=Uma janela perde a maximização +Comment[ro]=O fereastră a pierdut maximizarea +Comment[ru]=Окно более не раÑпахнуто +Comment[rw]=Idirishya ribuze ukugirwa riri +Comment[se]=Láse ii Å¡at maksimerejuvvon +Comment[sk]=Okno nie je maximalizované +Comment[sl]=Okno ni veÄ razpeto +Comment[sr]=Прозор више није макÑимизован +Comment[sr@Latn]=Prozor viÅ¡e nije maksimizovan +Comment[sv]=Ett fönster förlorar maximering +Comment[ta]=சாளரம௠பெரிதாவதை இழநà¯à®¤à®¤à¯ +Comment[te]=విండొనౠపెదà±à°¦ à°¸à±à°¥à°¿à°¤à°¿à°¨à°¿ కొలà±à°ªà±Šà°¤à±à°‚ది +Comment[th]=หน้าต่างสูà¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¸‚ยายเต็มจภ+Comment[tr]=Bir pencere büyümesini kaybetti +Comment[tt]=Cäyelgän täräzä ülçämen torğızu +Comment[uk]=Вікно втрачає макÑимізацію +Comment[vi]=Má»™t cá»a sổ không mở to nữa +Comment[wa]=On purnea a piede si grandeu +Comment[zh_CN]=窗å£ä¸å†æœ€å¤§åŒ– +Comment[zh_TW]=視窗å–消最大化 +default_sound=KDE_Window_Hide.ogg +default_presentation=0 + +[on_all_desktops] +Name=Window On All Desktops +Name[af]=Venster op alle werkskerms +Name[ar]=الناÙذة على كل Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØ§ØªØ¨ +Name[be]=Ðкно на ÑžÑÑ–Ñ… працоўных Ñталах +Name[bg]=Прозорец на вÑички работни плотове +Name[bn]=উইণà§à¦¡à§‹ সব ডেসà§à¦•à¦Ÿà¦ªà§‡ +Name[br]=Ar prenestr war an holl vurevoù +Name[bs]=Prozor na svim desktopima +Name[ca]=Finestra a tots els escriptoris +Name[cs]=Okno na vÅ¡ech plochách +Name[csb]=Ã’kno na wszëtczich pùltach +Name[cy]=Ffenestr ar Bob Un Penbwrdd +Name[da]=Vindue pÃ¥ alle desktoppe +Name[de]=Fenster auf allen Arbeitsflächen +Name[el]=ΠαÏάθυÏο σε όλες τις επιφάνειες εÏγασίας +Name[eo]=Fenestro sur ĉiuj tabuloj +Name[es]=Ventana en todos los escritorios +Name[et]=Aken kõigil töölaudadel +Name[eu]=Leihoa mahaigain guztietan +Name[fa]=پنجره روی همۀ رومیزیها +Name[fi]=Ikkuna kaikilla työpöydillä +Name[fr]=Fenêtre visible sur tous les bureaux +Name[fy]=Finster op alle buroblêden +Name[gl]=Fiestra en Todos os Escritórios +Name[he]=הצגת חלון על כל ×©×•×œ×—× ×•×ª העבודה +Name[hi]=सà¤à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª पर विंडो +Name[hr]=Prozor na svim radnim povrÅ¡inama +Name[hu]=Ablak az összes munkaasztalra +Name[is]=Glugga á öll skjáborð +Name[it]=Finestra su tutti i desktop +Name[ja]=ウィンドウãŒã™ã¹ã¦ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã§å¯è¦–ã« +Name[ka]=ფáƒáƒœáƒ¯áƒáƒ რყველრსáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ერ+Name[kk]=Бүкіл Ò¯Ñтелдердегі терезе +Name[km]=បង្អួច​លើ​ផ្ទៃážáž»â€‹áž‘ាំងអស់ +Name[ko]=ì°½ì´ ëª¨ë“ ë°ìŠ¤í¬í†±ì— ë³´ìž„ +Name[lt]=Langas matomas visuose darbastaliuose +Name[lv]=Logs uz visÄm darbvirsmÄm +Name[mk]=Прозорецот на Ñите површини +Name[mn]=Цонхыг бүх ажлын тавцан дÑÑÑ€ +Name[mt]=Window fuq desktops kollha +Name[nb]=Vindu pÃ¥ alle skrivebord +Name[nds]=Finster op all Schriefdischen +Name[ne]=सबै डेसà¥à¤•à¤Ÿà¤ªà¤¹à¤°à¥‚मा सञà¥à¤à¥à¤¯à¤¾à¤² +Name[nl]=Venster op alle bureaubladen +Name[nn]=Vindauge pÃ¥ alle skrivebord +Name[pa]=à¨à¨°à©‹à¨–ਾ ਸਠਵੇਹੜਿਆਂ 'ਤੇ +Name[pl]=Pokazywanie okna na wszystkich pulpitach +Name[pt]=Janela em Todos os Ecrãs +Name[pt_BR]=Janela em Todas as Ãreas de Trabalho +Name[ro]=Fereastră pe toate ecranele +Name[ru]=Окно на вÑех рабочих Ñтолах +Name[rw]=Idirishya Ku Biro Byose +Name[se]=Láse buot Äállinbevddiin +Name[sk]=Okno na vÅ¡etky plochy +Name[sl]=Okno na vsa namizja +Name[sr]=Прозор на Ñвим радним површинама +Name[sr@Latn]=Prozor na svim radnim povrÅ¡inama +Name[sv]=Fönster pÃ¥ alla skrivbord +Name[ta]=எலà¯à®²à®¾ மேலà¯à®®à¯‡à®šà¯ˆà®•à®³à®¿à®²à¯à®®à¯ சாளரம௠+Name[te]=à°…à°¨à±à°¨à°¿ à°°à°‚à°—à°¸à±à°¥à°²à°¾à°² మిద విండొ +Name[tg]=Тиреза ба тамоми мизи корӣ +Name[th]=หน้าต่างบนทุà¸à¸žà¸·à¹‰à¸™à¸—ี่หน้าจภ+Name[tr]=Pencere Tüm Masaüstlerinde +Name[tt]=Täräzäne Bar Öställärgä +Name[uk]=Вікно на вÑÑ– Ñтільниці +Name[uz]=Oyna hamma ish stollariga +Name[uz@cyrillic]=Ойна ҳамма иш Ñтолларига +Name[vi]=Cá»a sổ trên Tất cả các Mà n hình ná»n +Name[wa]=Purnea hÃ¥yné so tos les scribannes +Name[zh_CN]=窗å£åœ¨å…¨éƒ¨æ¡Œé¢ä¸Š +Name[zh_TW]=è¦–çª—åœ¨æ‰€æœ‰æ¡Œé¢ +Comment=A window is made visible on all desktops +Comment[af]='n Venster is sigbaar op al die werkskerms +Comment[ar]=تمّ إظهار ناÙذة على كل Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØ§ØªØ¨ +Comment[be]=Ðкно робіцца бачным на ÑžÑÑ–Ñ… працоўных Ñталах +Comment[bg]=Прозорец на вÑички работни плотове +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ à¦à¦–ন থেকে সব ডেসà§à¦•à¦Ÿà¦ªà§‡ দেখা যাবে +Comment[bs]=Prozor je vidljiv na svim radnim povrÅ¡inama +Comment[ca]=Una finestra es fa visible a tots els escriptoris +Comment[cs]=Okno je viditelné na vÅ¡ech plochách +Comment[csb]=Ã’kno bãdze je widzec na wszëtczich pùltach +Comment[da]=Et vindue gøres synligt pÃ¥ alle desktoppe +Comment[de]=Fenster wird auf allen Arbeitsflächen angezeigt +Comment[el]=Ένα παÏάθυÏο γίνεται οÏατό σε όλες τις επιφάνειες εÏγασίας +Comment[eo]=Fenestro videbliÄis sur ĉiuj tabuloj +Comment[es]=Una ventana se hace visible en todos los escritorios +Comment[et]=Aken on muudetud nähtavaks kõigil töölaudadel +Comment[eu]=Leihoa mahaigain guztietan ikusgai bihurtzen da +Comment[fa]=پنجره روی همۀ رومیزیها قابل رؤیت است +Comment[fi]=Ikkuna näytetään kaikilla työpöydillä +Comment[fr]=Une fenêtre est maintenant visible sur tous les bureaux +Comment[fy]=In finster is sichtber makke op alle buroblêden +Comment[gl]=Unha fiestra fai-se visÃbel en todos os escritórios +Comment[he]=חלון × ×¢×©×” מוצג בכל ×©×•×œ×—× ×•×ª העבודה +Comment[hi]=à¤à¤• विंडो सà¤à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª पर दृषà¥à¤Ÿà¤¿à¤—ोचर हà¥à¤† +Comment[hr]=Prozor je vidljiv na svim radnim povrÅ¡inama +Comment[hu]=Egy ablak megjelent az összes munkaasztalon +Comment[is]=Gluggi er látinn sjást á öllum skjáborðum +Comment[it]=Una finestra viene resa visibile su tutti i desktop +Comment[ja]=ウィンドウãŒã™ã¹ã¦ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã§å¯è¦–ã«ãªã‚Šã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რყველრსáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე ჩáƒáƒœáƒ¡ +Comment[kk]=Терезе бүкіл Ò¯Ñтелдерде көрінетін болды +Comment[km]=បង្អួច​ážáŸ’រូវ​បាន​ធ្វើ​ឲ្យ​មើលឃើញ លើ​ផ្ទៃážáž»â€‹áž‘ាំងអស់ +Comment[lt]=Langas matomas visuose darbastaliuose +Comment[lv]=Logs ir redzams uz visÄm darbvirsmÄm +Comment[mk]=Прозорецот Ñе прави видлив на Ñите површини +Comment[mt]=Window twaħħlet fuq id-desktops kollha +Comment[nb]=Et vindu gjøres synlig pÃ¥ alle skrivebordene +Comment[nds]=En Finster warrt op all Schriefdischen wiest +Comment[ne]=सबै डेसà¥à¤•à¤Ÿà¤ªà¤¹à¤°à¥‚मा सञà¥à¤à¥à¤¯à¤¾à¤² देखिने बनाइà¤à¤•à¥‹ छ +Comment[nl]=Een venster is zichtbaar gemaakt op alle bureaubladen +Comment[nn]=Eit vindauge vert gjort synleg pÃ¥ alle skriveborda +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਸਠਵੇਹੜਿਆਂ ਵਿੱਚ ਦਿੱਸੇ +Comment[pl]=Okno bedzie widoczne na wszystkich pulpitach +Comment[pt]=Uma janela passa a aparecer em todos os ecrãs +Comment[pt_BR]=Uma janela ficou visÃvel em todas as áreas de trabalho +Comment[ro]=O fereastră a fost făcută vizibilă pe toate ecranele +Comment[ru]=Окно Ñделано видимым на вÑех рабочих Ñтолах +Comment[rw]=Idirishya ryagaragajwe ku biro byose +Comment[se]=Láse darvvihuvvo buot Äállinbevddiide +Comment[sk]=Okno bude viditeľné na vÅ¡etkých plochách +Comment[sl]=Okno je vidno na vseh namizjih +Comment[sr]=Прозор је учињен видљивим на Ñвим радним површинама +Comment[sr@Latn]=Prozor je uÄinjen vidljivim na svim radnim povrÅ¡inama +Comment[sv]=Ett fönster görs synligt pÃ¥ alla skrivbord +Comment[ta]=சாளரதà¯à®¤à¯ˆ அனைதà¯à®¤à¯ மேலà¯à®®à¯‡à®šà¯ˆà®¯à®¿à®²à¯à®®à¯ பாரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯ +Comment[te]=à°…à°¨à±à°¨à°¿ à°°à°‚à°—à°¸à±à°¥à°²à°¾à°² మిద విండొ నౠచూపబడà±à°¨à± +Comment[th]=หน้าต่างถูà¸à¸—ำให้เห็นบนทุà¸à¹† พื้นที่หน้าจภ+Comment[tr]=Bir pencere tüm masaüstlerinde görünür hale getirildi +Comment[tt]=Täräzäne bar öställärdä dä kürenerlek itü +Comment[uk]=Вікно зроблено видимим на вÑÑ–Ñ… ÑтільницÑÑ… +Comment[uz]=Oyna hamma ish stollarida koÊ»rinadigan qilindi +Comment[uz@cyrillic]=Ойна ҳамма иш Ñтолларида кўринадиган қилинди +Comment[vi]=Má»™t cá»a sổ được hiện ra trên tất cả các mà n hình ná»n +Comment[wa]=On purnea a stî hÃ¥yné so tos les scribannes +Comment[zh_CN]=窗å£å‡ºçŽ°åœ¨å…¨éƒ¨æ¡Œé¢ä¸Š +Comment[zh_TW]=視窗å¯åœ¨æ‰€æœ‰æ¡Œé¢è¢«çœ‹åˆ° +default_sound=KDE_Window_Sticky.ogg +default_presentation=0 + +[not_on_all_desktops] +Name=Window Not On All Desktops +Name[af]=Venster nie op alle werkskerms +Name[ar]=الناÙذة ليست على كل Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØ§ØªØ¨ +Name[be]=Ðкно не на ÑžÑÑ–Ñ… працоўных Ñталах +Name[bg]=Прозорец Ñамо на един работен плот +Name[bn]=উইণà§à¦¡à§‹ সব ডেসà§à¦•à¦Ÿà¦ªà§‡ নয় +Name[br]=N'eo ket war an holl vurevoù ar prenestr +Name[bs]=Prozor nije na svim desktopima +Name[ca]=Finestra no a tots els escriptoris +Name[cs]=Okno nenà na vÅ¡ech plochách +Name[csb]=Felënk òkno na wszëtczich pùltach +Name[cy]=Ffenestr Dim ar Bob Un Penbwrdd +Name[da]=Vindue ikke pÃ¥ alle desktoppe +Name[de]=Fenster nicht auf allen Arbeitsflächen +Name[el]=ΠαÏάθυÏο όχι σε όλες τις επιφάνειες εÏγασίας +Name[eo]=Fenestro ne sur ĉiuj tabuloj +Name[es]=Ventana no en todos los escritorios +Name[et]=Aken ei ole kõigil töölaudadel +Name[eu]=Leihoa mahaigain guztietan ez +Name[fa]=پنجره روی همۀ رومیزیها نیست +Name[fi]=Ikkuna vain yhdellä työpöydällä +Name[fr]=Fenêtre visible sur un seul bureau +Name[fy]=Finster net op alle buroblêden +Name[gl]=Fiestra Non en Todos os Escritórios +Name[he]=הצגת חלון ×œ× ×¢×œ כל ×©×•×œ×—× ×•×ª העבודה +Name[hi]=सà¤à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª पर विंडो नहीं +Name[hr]=Prozor nije na svim radnim povrÅ¡inama +Name[hu]=Ablak nem az összes munkaasztalra +Name[is]=Gluggi ekki á öllum skjáborðum +Name[it]=Finestra non su tutti i desktop +Name[ja]=ウィンドウãŒä¸€éƒ¨ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã§ä¸å¯è¦–ã« +Name[ka]=ფáƒáƒœáƒ¯áƒáƒ რáƒáƒ áƒáƒ ყველრსáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე +Name[kk]=Бүкіл Ò¯Ñтелдердегі терезе ÐµÐ¼ÐµÑ +Name[km]=បង្អួច​មិន​នៅ​លើ​ផ្ទៃážáž»â€‹áž‘ាំងអស់ +Name[ko]=ì°½ì´ ëª¨ë“ ë°ìŠ¤í¬í†±ì— ë³´ìž„ +Name[lt]=Langas matomas ne visuose darbastaliuose +Name[lv]=Logs ne uz visÄm darbvirsmÄm +Name[mk]=Прозорецот не на Ñите површини +Name[mn]=Цонхыг бүх ажлын тавцан дÑÑÑ€ биш +Name[mt]=Window mhux fuq desktops kollha +Name[nb]=Vindu ikke pÃ¥ alle skrivebord +Name[nds]=Finster nich op all Schriefdischen +Name[ne]=सबै डेसà¥à¤•à¤Ÿà¤ªà¤¹à¤°à¥‚मा सञà¥à¤à¥à¤¯à¤¾à¤² छैन +Name[nl]=Venster niet op alle bureaubladen +Name[nn]=Vindauge ikkje pÃ¥ alle skrivebord +Name[pa]=à¨à¨°à©‹à¨–ਾ ਸਠਵੇਹੜਿਆਂ 'ਤੇ ਨਹੀਂ +Name[pl]=UsuniÄ™cie pokazywania okna na wszystkich pulpitach +Name[pt]=Janela Não Em Todos os Ecrãs +Name[pt_BR]=Janela Não Em Todas Ãreas de Trabalho +Name[ro]=Fereastra nu este pe toate ecranele +Name[ru]=Окно не на вÑех рабочих Ñтолах +Name[rw]=Idirishya Atari Ku Biro Byose +Name[se]=Láse ii buot Äállinbevddiin +Name[sk]=Okno na jednu plochu +Name[sl]=Okno ne na vsa namizja +Name[sr]=Прозор није на Ñвим радним површинама +Name[sr@Latn]=Prozor nije na svim radnim povrÅ¡inama +Name[sv]=Fönster inte pÃ¥ alla skrivbord +Name[ta]=எலà¯à®²à®¾ மேலà¯à®®à¯‡à®šà¯ˆà®•à®³à®¿à®²à¯à®®à¯ சாளரம௠இலà¯à®²à¯ˆ +Name[te]=à°…à°¨à±à°¨à°¿ à°°à°‚à°—à°¸à±à°¥à°²à°¾à°² మిద విండొ వదà±à°¦à± +Name[tg]=Тиреза на ба тамоми мизи корӣ +Name[th]=หน้าต่างไม่à¸à¸¢à¸¹à¹ˆà¸šà¸™à¸—ุà¸à¸žà¸·à¹‰à¸™à¸—ี่ทำงาน +Name[tr]=Pencere Tüm Masaüstlerinde DeÄŸil +Name[tt]=Täräzäne Bar Öställärgä tügel +Name[uk]=Вікно не на вÑÑ– Ñтільниці +Name[uz]=Oyna hamma ish stollarida emas +Name[uz@cyrillic]=Ойна ҳамма иш Ñтолларида ÑÐ¼Ð°Ñ +Name[vi]=Cá»a sổ không trên Tất cả các Mà n hình ná»n +Name[wa]=Purnea nén hÃ¥yné so tos les scribannes +Name[zh_CN]=窗å£ä¸åœ¨å…¨éƒ¨æ¡Œé¢ä¸Š +Name[zh_TW]=視窗ä¸åœ¨æ‰€æœ‰æ¡Œé¢ +Comment=A Window is no longer visible on all desktops +Comment[af]='n Venster is nie meer op al die werkskerms sigbaar nie +Comment[ar]=تمّ إلغاء ظهور ناÙذة على كل Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØ§ØªØ¨ +Comment[be]=Ðкно пераÑтае быць бачным на ÑžÑÑ–Ñ… працоўных Ñталах +Comment[bg]=Прозорец Ñамо на един работен плот +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ আর সব ডেসà§à¦•à¦Ÿà¦ªà§‡ দেখা যাচà§à¦›à§‡ না +Comment[bs]=Prozor viÅ¡e nije vidljiv na svim radnim povrÅ¡inama +Comment[ca]=Una finestra deixa de ser visible a tots els escriptoris +Comment[cs]=Okno již nenà viditelné na vÅ¡ech plochách +Comment[csb]=Ã’kno nie bãdze je widzec na wszëtczich pùltach +Comment[da]=Et vindue er ikke længere synligt pÃ¥ alle desktoppe +Comment[de]=Fenster wird nicht mehr auf allen Arbeitsflächen angezeigt +Comment[el]=Ένα παÏάθυÏο δεν είναι πλÎον οÏατό σε όλες τις επιφάνειες εÏγασίας +Comment[eo]=Fenestro nevidebliÄis sur ĉiuj labortabloj +Comment[es]=Una ventana ya no es visible en todos los escritorios +Comment[et]=Aken ei ole enam nähtav kõigil töölaudadel +Comment[eu]=Leihoa ez da gehiago ikusiko leiho guztietan +Comment[fa]=پنجره روی همۀ رومیزیها قابل رؤیت نیست +Comment[fi]=Ikkuna ei ole enää näkyvillä kaikilla työpöydillä +Comment[fr]=Une fenêtre n'est maintenant plus visible sur tous les bureaux +Comment[fy]=In finster is net langer sichtber op alle buroblêden +Comment[gl]=Unha fiestra xa non é visÃbel en todos os escritórios +Comment[he]=חלון כבר ×œ× ×ž×•×¦×’ בכל ×©×•×œ×—× ×•×ª העבודה +Comment[hi]=à¤à¤• विंडो सà¤à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª पर अब दृषà¥à¤Ÿà¤¿à¤—ोचर नहीं है +Comment[hr]=Prozor viÅ¡e nije vidljiv na svim radnim povrÅ¡inama +Comment[hu]=Egy ablak nem jelenik meg többé az összes munkaasztalon +Comment[is]=Gluggi er ekki lengur sýnilegur á öllum skjáborðum +Comment[it]=Una finestra non è più visibile su tutti i desktop +Comment[ja]=ウィンドウãŒä¸€éƒ¨ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã§ä¸å¯è¦–ã«ãªã‚Šã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ რáƒáƒ ჩáƒáƒœáƒ¡ ყველრსáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒáƒ–ე +Comment[kk]=Терезе бүкіл Ò¯Ñтелдерде көрінетін ÐµÐ¼ÐµÑ Ð±Ð¾Ð»Ð´Ñ‹ +Comment[km]=មើល​មិន​ឃើញ​បង្អួច​ទៀážâ€‹áž¡áž¾áž™ លើ​ផ្ទៃážáž»â€‹áž‘ាំងអស់ +Comment[lt]=Langas nebematomas visuose darbastaliuose +Comment[lv]=Logs vairs nav redzams uz visÄm darba virsmÄm +Comment[mk]=Прозорецот не е повеќе видлив на Ñите површини +Comment[mt]=Window m'għadhiex imwaħħla fuq id-desktops kollha +Comment[nb]=Et vindu er ikke lenger synlig pÃ¥ alle skrivebordene +Comment[nds]=En Finster warrt nich mehr op all Schriefdischen wiest +Comment[ne]=सबै डेसà¥à¤•à¤Ÿà¤ªà¤¹à¤°à¥‚मा लामो समय समà¥à¤® सञà¥à¤à¥à¤¯à¤¾à¤² देखिदैन +Comment[nl]=Een venster is niet langer zichtbaar op alle bureaubladen +Comment[nn]=Eit vindauge er ikkje lenger synleg pÃ¥ alle skriveborda +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਸਠਵੇਹੜਿਆਂ ਵਿੱਚ ਨਾ ਦਿੱਸੇ +Comment[pl]=Okno nie jest już widoczne na wszystkich pulpitach +Comment[pt]=Uma janela deixa de aparecer em todos os ecrãs +Comment[pt_BR]=Uma Janela não está mais visÃvel em todas as áreas de trabalho +Comment[ro]=O fereastră nu mai este vizibilă pe toate ecranele +Comment[ru]=Окно более не ÑвлÑетÑÑ Ð²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼ на вÑех рабочих Ñтолах +Comment[rw]=Idirishya ntirikigaragara ku biro byose +Comment[se]=Láse ii Å¡at oidno buot Äállinbevddiin +Comment[sk]=Okno už nebude viditeľné na vÅ¡etkých plochách +Comment[sl]=Okno ni veÄ vidno na vseh namizjih +Comment[sr]=Прозор више није видљив на Ñвим радним површинама +Comment[sr@Latn]=Prozor viÅ¡e nije vidljiv na svim radnim povrÅ¡inama +Comment[sv]=Ett fönster blir inte längre synligt pÃ¥ alla skrivbord +Comment[ta]=அனைதà¯à®¤à¯ மேலà¯à®®à¯‡à®šà¯ˆà®¯à®¿à®²à¯à®®à¯ சாளரம௠தெரியாத௠+Comment[te]=à°…à°¨à±à°¨à°¿ à°°à°‚à°—à°¸à±à°¥à°²à°¾à°² మిద విండొ నౠచూపించబడదౠ+Comment[th]=หน้าต่างไม่ได้ถูà¸à¸—ำให้เห็นบนทุà¸à¸žà¸·à¹‰à¸™à¸—ี่หน้าจà¸à¸à¸µà¸à¸•à¹ˆà¸à¹„ป +Comment[tr]=Bir pencere tüm masaüstlerinde gizli hale getirildi +Comment[tt]=Täräzäne bar öställärdä dä kürenerlek itmäw +Comment[uk]=Вікно більше не Ñ” видимим на вÑÑ–Ñ… ÑтільницÑÑ… +Comment[uz]=Oyna hamma ish stollarida koÊ»rinmaydigan qilindi +Comment[uz@cyrillic]=Ойна ҳамма иш Ñтолларида кўринмайдиган қилинди +Comment[vi]=Má»™t cá»a sổ không còn được nhìn thấy trên tất cả các mà n hình ná»n +Comment[wa]=On purnea n' est pus hÃ¥yné so tos les scribannes +Comment[zh_CN]=窗å£ä¸å†å‡ºçŽ°åœ¨å…¨éƒ¨æ¡Œé¢ä¸Š +Comment[zh_TW]=視窗在所有桌é¢ä¸å†å¯è¢«çœ‹åˆ° +default_presentation=1 +default_sound=KDE_Window_UnSticky.ogg + +[transnew] +Name=New Dialog +Name[af]=Nuwe Dialoog +Name[ar]=Øوار جديد +Name[az]=Yeni PÉ™ncÉ™rÉ™ +Name[be]=Ðовы дыÑлог +Name[bg]=Ðов диалогов прозорец +Name[bn]=নতà§à¦¨ ডায়ালগ +Name[br]=Kendiviz nevez +Name[bs]=Novi dijalog +Name[ca]=Nou dià leg +Name[cs]=Nové dialogové okno +Name[csb]=Nowé dialogòwé òkno +Name[cy]=Ymgom Newydd +Name[da]=Ny dialog +Name[de]=Neuer Dialog +Name[el]=ÎÎος διάλογος +Name[en_GB]=New Dialogue +Name[eo]=Nova dialogo +Name[es]=Nuevo diálogo +Name[et]=Uus dialoog +Name[eu]=Elkarrizketa berria +Name[fa]=Ù…Øاورۀ جدید +Name[fi]=Uusi ikkuna +Name[fr]=Nouvelle boîte de dialogue +Name[fy]=Nij dialooch +Name[ga]=Dialóg Nua +Name[gl]=Novo Diálogo +Name[he]=דו־שיח חדש +Name[hi]=नया संवाद +Name[hr]=Novi dijalog +Name[hu]=Új párbeszédablak +Name[id]=Dialog Baru +Name[is]=Opna nýjan glugga +Name[it]=Nuova finestra di dialogo +Name[ja]=æ–°è¦ãƒ€ã‚¤ã‚¢ãƒã‚° +Name[ka]=áƒáƒ®áƒáƒšáƒ˜ დიáƒáƒšáƒáƒ’ი +Name[kk]=Жаңа диалог +Name[km]=ប្រអប់​ážáŸ’មី +Name[ko]=새 대화 ìƒìž +Name[lo]=ສ້າງàºà»ˆàºàº‡à»‚ຕ້ຕàºàºšà»ƒàº«àº¡à»ˆ +Name[lt]=Naujas dialogas +Name[lv]=Jauns Dialogs +Name[mk]=Ðов дијалог +Name[mn]=Ð¨Ð¸Ð½Ñ Ð´Ð¸Ð°Ð»Ð¾Ð³ +Name[mt]=Djalogu Ä did +Name[nb]=Nytt dialogvindu +Name[nds]=Nieg Dialoog +Name[ne]=नयाठसंवाद +Name[nl]=Nieuw dialoog +Name[nn]=Ny dialogboks +Name[nso]=Poledisano ye Ntshwa +Name[pa]=ਨਵਾਂ ਵਾਰਤਾਲਾਪ +Name[pl]=Nowe okno dialogowe +Name[pt]=Nova Janela +Name[pt_BR]=Novo Diálogo +Name[ro]=Dialog nou +Name[ru]=Ðовый диалог +Name[rw]=Ikiganiro Gishya +Name[se]=OÄ‘Ä‘a láseÅ¡ +Name[sk]=Nový dialóg +Name[sl]=Novo pogovorno okno +Name[sr]=Ðови дијалог +Name[sr@Latn]=Novi dijalog +Name[ss]=Inkhulumo-mphendvulwano lensha +Name[sv]=Ny dialog +Name[ta]=பà¯à®¤à®¿à®¯ உரையாடல௠+Name[te]=కొతà±à°¤ డైలాగౠ+Name[tg]=Гуфтугӯи нав +Name[th]=สร้างà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¹ƒà¸«à¸¡à¹ˆ +Name[tr]=Yeni Pencere +Name[tt]=Yaña Dialog +Name[uk]=Ðове вікно діалогу +Name[uz]=Yangi dialog +Name[uz@cyrillic]=Янги диалог +Name[ven]=Nyambedzano ntswa +Name[vi]=Mở há»™p thoại má»›i +Name[wa]=Novea purnea di kesse +Name[xh]=Incoko yababini Entsha +Name[zh_CN]=新对è¯æ¡† +Name[zh_TW]=æ–°å°è©±ç›’ +Name[zu]=Ingxoxo Entsha +Comment=Transient window (a dialog) appears +Comment[af]=Oorgang Venster ('n dialoog) verskyn +Comment[ar]=تظهر ناÙذة ( Øوار) مءقت +Comment[be]=З'ÑўлÑецца дыÑлог +Comment[bg]=Ðов диалогов прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ সাময়িক উইণà§à¦¡à§‹ (ডায়ালগ) আবিরà§à¦à§‚ত হয়েছে +Comment[bs]=Prolazni prozor (dijalog) se pojavljuje +Comment[ca]=Apareix una finestra transitòria (un dià leg) +Comment[cs]=Objevilo se dialogové okno +Comment[csb]=Pòkôże sã doczasné dialogòwé òkno +Comment[da]=Midlertidigt vindue (en dialog) kommer til syne +Comment[de]=Transientes Fenster (Dialog) wird angezeigt +Comment[el]=Εμφάνιση Î¼ÎµÏ„Î±Î²Î±Ï„Î¹ÎºÎ¿Ï Ï€Î±ÏαθÏÏου (διαλόγου) +Comment[en_GB]=Transient window (a dialogue) appears +Comment[eo]=Provizora fenestro (dialogo) aperas +Comment[es]=Aparece una ventana transitoria (un diálogo) +Comment[et]=Ajutine aken (dialoog) ilmub +Comment[eu]=Leiho elkarrizketa agertzen da +Comment[fa]=پنجرۀ گذرا )یک Ù…Øاوره( ظاهر می‌شود +Comment[fi]=Lyhytaikainen ikkuna (dialogi) ilmestyy +Comment[fr]=Une boîte de dialogue apparaît +Comment[fy]=In dialooch ferskynt +Comment[gl]=Aparece unha fiestra temporal (un diálogo) +Comment[he]=מופיע חלון ×רעי (דו־שיח) +Comment[hi]=टà¥à¤°à¤¾à¤‚जिà¤à¤‚ट विंडो (à¤à¤• संवाद) पà¥à¤°à¤•à¤Ÿ हà¥à¤† +Comment[hr]=Prolazni prozor (dijalog) se pojavio +Comment[hu]=Párbeszédablak megjelenése +Comment[is]=Fyrirspurnargluggi birtist +Comment[it]=Appare una finestra di dialogo +Comment[ja]=ダイアãƒã‚°ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒè¡¨ç¤ºã‚Œã¾ã—㟠+Comment[kk]=Диалог терезеÑін шығару +Comment[km]=បង្អួរ​បណ្ដោះ​អាសន្ន (ប្រអប់​មួយ) ážáŸ’រូវ​បាន​លáŸáž…ឡើង +Comment[ko]=ìž„ì‹œ ì°½(대화ìƒìž)ì´ ë‚˜íƒ€ë‚¨ +Comment[lt]=Atsiranda laikinas langas (dialogas) +Comment[lv]=ParÄdÄs ĪslaicÄ«gs Logs (dialogs) +Comment[mk]=Се појавува преоден прозорец (дијалог) +Comment[mt]=Tidher window tranżjenti (djalogu) +Comment[nb]=Midlertidig dialogvindu vises +Comment[nds]=En kortwielig Finster (Dialoog) dukt op +Comment[ne]=असà¥à¤¥à¤¾à¤¯à¥€ सञà¥à¤à¥à¤¯à¤¾à¤² (à¤à¤• संवाद) देखिनà¥à¤› +Comment[nl]=Een dialoog verschijnt +Comment[nn]=Mellombels vindauge (ein dialog) vert opna +Comment[pa]=ਗੱਲਬਾਤ à¨à¨°à©‹à¨–ਾ(ਵਾਰਤਾਲਾਪ) ਉਪਲੱਬਧ +Comment[pl]=Pojawia siÄ™ tymczaowe okno dialogowe +Comment[pt]=Uma janela transitória (uma janela de diálogo) aparece +Comment[pt_BR]=Aparece uma janela transiente (um diálogo) +Comment[ro]=A apărut o fereastră de dialog +Comment[ru]=ВыводитÑÑ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ðµ окно (диалог) +Comment[rw]=Idirishya(ikiganiro) nzibacyuho riragaye +Comment[se]=GaskaboddosaÅ¡ láse (láseÅ¡) rahpojuvvo +Comment[sk]=Objavilo sa doÄasné dialógové okno +Comment[sl]=Odprlo se je prehodno (pogovorno) okno +Comment[sr]=Пролазни прозор (дијалог) Ñе појављује +Comment[sr@Latn]=Prolazni prozor (dijalog) se pojavljuje +Comment[sv]=Ett tillfälligt fönster (en dialogruta) dyker upp +Comment[ta]=தறà¯à®•à®¾à®²à®¿à®• சாளரம௠(ஒர௠உரையாடலà¯) தோனà¯à®±à¯à®•à®¿à®±à®¤à¯ +Comment[th]=หน้าต่างชั่วคราว (à¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸š) ปราà¸à¸Ž +Comment[tr]=Bir iletiÅŸim kutusu belirdi +Comment[tt]=Waqıtlı täräzä çığaru +Comment[uk]=З'ÑвлÑєтьÑÑ Ñ‚Ð¸Ð¼Ñ‡Ð°Ñове вікно (діалог) +Comment[uz]=Muloqat oynasi paydo boÊ»ldi +Comment[uz@cyrillic]=Мулоқат ойнаÑи пайдо бўлди +Comment[vi]=Xuất hiện cá»a sổ (má»™t há»™p thoại) tạm thá»i +Comment[wa]=Ene divize aparete +Comment[zh_CN]=出现了临时窗å£(对è¯æ¡†) +Comment[zh_TW]=暫態視窗(å°è©±)å‡ºç¾ +default_sound=KDE_Dialog_Appear.ogg +default_presentation=0 + +[transdelete] +Name=Delete Dialog +Name[af]=Vee Dialoog Uit +Name[ar]=Ù…ØÙˆ الØوار +Name[az]=Dialoqu Sil +Name[be]=Выдаліць дыÑлог +Name[bg]=ЗатварÑне на диалогов прозорец +Name[bn]=ডায়ালগ মà§à¦›à§‡ ফেল +Name[br]=Lemel ar gendiviz +Name[bs]=BriÅ¡i dijalog +Name[ca]=Esborra el dià leg +Name[cs]=ZruÅ¡enà dialogového okna +Name[csb]=Rëmniãce dialogòwegò òkna +Name[cy]=Dileu Ymgom +Name[da]=Slet dialog +Name[de]=Dialog löschen +Name[el]=ΔιαγÏαφή διαλόγου +Name[en_GB]=Delete Dialogue +Name[eo]=Forigo de dialogo +Name[es]=Eliminar diálogo +Name[et]=Dialoogi kustutamine +Name[eu]=Ezabatu elkarrizketa +Name[fa]=ØØ°Ù Ù…Øاوره +Name[fi]=Lopeta ikkuna +Name[fr]=Fermeture de la boîte de dialogue +Name[fy]=Dialooch wisse +Name[ga]=Scrios Dialóg +Name[gl]=Pechar Diálogo +Name[he]=מחיקת דו־שיח +Name[hi]=संवाद मिटाà¤à¤ +Name[hr]=IzbriÅ¡i dijalog +Name[hu]=Törlési párbeszédablak +Name[id]=Hapus Dialog +Name[is]=Eyða glugga +Name[it]=Elimina finestra di dialogo +Name[ja]=ダイアãƒã‚°ã‚’削除 +Name[ka]=დიáƒáƒšáƒáƒ’ის წáƒáƒ¨áƒšáƒ +Name[kk]=Диалогты өшіру +Name[km]=លុប​ប្រអប់ +Name[ko]=대화 ìƒìž ì‚ì œ +Name[lo]=ລົບàºà»ˆàºàº‡à»‚ຕ້ຕàºàºš +Name[lt]=PaÅ¡alinti dialogÄ… +Name[lv]=DzÄ“st Dialogu +Name[mk]=Избриши дијалог +Name[mn]=Диалог уÑтгах +Name[mt]=Neħħi Dialog +Name[nb]=Fjern dialogvindu +Name[nds]=Dialoog wegdoon +Name[ne]=संवाद मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Dialoog verwijderen +Name[nn]=Fjern dialogboks +Name[nso]=Tlosa Poledisano +Name[pa]=ਵਾਰਤਾਲਾਪ ਹਟਾਓ +Name[pl]=UsuniÄ™cie okna dialogowego +Name[pt]=Apagar a Janela +Name[pt_BR]=Remover Diálogo +Name[ro]=ÃŽnchide dialog +Name[ru]=Удалить диалог +Name[rw]=Gusiba Ikiganiro +Name[se]=Sihko láseÅ¡ +Name[sk]=ZmazaÅ¥ dialóg +Name[sl]=ZbriÅ¡i pogovorno okno +Name[sr]=Дијалог Ñе брише +Name[sr@Latn]=Dijalog se briÅ¡e +Name[ss]=Bulala inkhulumo-mphendvulwano +Name[sv]=Ta bort dialog +Name[ta]=உரையாடலை நீகà¯à®•à¯ +Name[te]=డైలాగౠతిసివెయి +Name[tg]=Ðобуд Ñохтани гуфтугӯ +Name[th]=ลบà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸š +Name[tr]=Pencereyi Sil +Name[tt]=Beter Dialognı +Name[uk]=Вилучити вікно діалогу +Name[uz]=Dialogni oÊ»chirish +Name[uz@cyrillic]=Диалогни ўчириш +Name[ven]=Thuthani nyambedzano +Name[vi]=Xoá Há»™p thoại +Name[wa]=Disfacer l' purnea di kesse +Name[xh]=Cima Incoko yababini +Name[zh_CN]=åˆ é™¤å¯¹è¯æ¡† +Name[zh_TW]=刪除å°è©±ç›’ +Name[zu]=Cisha Ingxoxo +Comment=Transient window (a dialog) is removed +Comment[af]=Oorgang Venster ('n dialoog) is verwyder +Comment[ar]=يتم Øذ٠ناÙذة ( Øوار ) موقتة +Comment[be]=ДыÑлог выдалÑецца +Comment[bg]=ЗатварÑне на диалогов прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ সাময়িক উইণà§à¦¡à§‹ (ডায়ালগ) সরিয়ে ফেলা হয়েছে +Comment[bs]=Prolazni prozor (dijalog) nestaje +Comment[ca]=S'elimina una finestra transitòria (un dià leg) +Comment[cs]=Dialogové okno bylo zavÅ™eno +Comment[csb]=Przepôdô doczasné dialogòwé òkno +Comment[da]=Midlertidigt vindue (en dialog) fjernes +Comment[de]=Transientes Fenster (Dialog) wird entfernt +Comment[el]=Μεταβατικό παÏάθυÏο (Îνας διάλογος) αφαιÏείται +Comment[en_GB]=Transient window (a dialogue) is removed +Comment[eo]=Provizora fenestro (dialogo) malaperas +Comment[es]=Desaparece una ventana transitoria (un diálogo) +Comment[et]=Ajutine aken (dialoog) on eemaldatud +Comment[eu]=Leiho elkarrizketa kentzen da +Comment[fa]=پنجرۀ گذرا )یک Ù…Øاوره( Øذ٠می‌شود +Comment[fi]=Lyhytaikainen ikkuna (dialogi) poistetaan +Comment[fr]=Une boîte de dialogue disparaît +Comment[fy]=In dialooch is slúten +Comment[gl]=ElimÃna-se unha fiestra temporal (un diálogo) +Comment[he]=מוסר חלון ×רעי (דו־שיח) +Comment[hi]=टà¥à¤°à¤¾à¤‚जिà¤à¤‚ट विंडो (à¤à¤• संवाद) हटाया गया +Comment[hr]=Prolazni prozor (dijalog) je uklonjen +Comment[hu]=Párbeszédablak eltávolÃtása +Comment[is]=Fyrirspurnarglugga er eytt +Comment[it]=Viene rimossa una finestra di dialogo +Comment[ja]=ダイアãƒã‚°ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒé–‰ã˜ã‚‰ã‚Œã¾ã—㟠+Comment[kk]=Диалог терезеÑÑ– жойылды +Comment[km]=បង្អួរ​បណ្ដោះ​អាសន្ន (ប្រអប់​មួយ) ážáŸ’រូវ​បាន​យកចáŸáž‰ +Comment[ko]=ìž„ì‹œ ì°½(대화ìƒìž)ì´ ì‚ì œë¨ +Comment[lt]=Laikinas langas (dialogas) yra paÅ¡alintas +Comment[lv]=ĪslaicÄ«gais Logs (dialogs) ir aizvÄkts +Comment[mk]=ОтÑтранет е преодниот прозорец (дијалог) +Comment[mt]=Titneħħa window tranżjenti (djalogu) +Comment[nb]=Midlertidig dialogvindu fjernes +Comment[nds]=En kortwielig Finster (Dialoog) warrt tomaakt +Comment[ne]=असà¥à¤¥à¤¾à¤¯à¥€ सञà¥à¤à¥à¤¯à¤¾à¤² (à¤à¤• संवाद) हटाइà¤à¤•à¥‹ छ +Comment[nl]=Een dialoog is gesloten +Comment[nn]=Mellombels vindauge (ein dialog) vert lukka +Comment[pa]=ਗੱਲਬਾਤ à¨à¨°à©‹à¨–ਾ(ਵਾਰਤਾਲਾਪ) ਹਟਾਇਆ ਗਿਆ +Comment[pl]=Znika tymczasowe okno dialogowe +Comment[pt]=Uma janela transitória (uma janela de diálogo) desaparece +Comment[pt_BR]=Uma janela transiente (um diálogo) é removida +Comment[ro]=A dispărut o fereastră de dialog +Comment[ru]=Временное окно (диалог) удалено +Comment[rw]=Idirishya(ikiganiro) nzibacyuho rikuweho +Comment[se]=GaskaboddosaÅ¡ láse (láseÅ¡) giddejuvvo +Comment[sk]=DoÄasné dialógové okno je odstránené +Comment[sl]=Zaprlo se je prehodno (pogovorno) okno +Comment[sr]=Пролазни прозор (дијалог) је уклоњен +Comment[sr@Latn]=Prolazni prozor (dijalog) je uklonjen +Comment[sv]=Ett tillfälligt fönster (en dialogruta) försvinner +Comment[ta]=தறà¯à®•à®¾à®²à®¿à®• சாளரமà¯(உரையாடல௠பெடà¯à®Ÿà®¿) நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[th]=หน้าต่างชั่วคราว (à¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸š) ถูà¸à¸¥à¸šà¸—ิ้ง +Comment[tr]=Bir iletiÅŸim kutusu kaldırıldı +Comment[tt]=Waqıtlı täräzä beterelde +Comment[uk]=Зникає тимчаÑове вікно (діалог) +Comment[uz]=Muloqat oynasi yopildi +Comment[uz@cyrillic]=Мулоқат ойнаÑи ёпилди +Comment[vi]=Cá»a sổ tạm thá»i (má»™t há»™p thoại) đã được bá» +Comment[wa]=Ene divize est oistêye +Comment[zh_CN]=åˆ é™¤äº†ä¸´æ—¶çª—å£(对è¯æ¡†) +Comment[zh_TW]=暫態視窗(å°è©±)移除 +default_sound=KDE_Dialog_Disappear.ogg +default_presentation=0 + +[movestart] +Name=Window Move Start +Name[af]=Venster Beweeg Begin +Name[ar]=بداية تØريك الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ Daşıma BaÅŸlanğıcı +Name[be]=Пачаць рух акна +Name[bg]=Ðачало на премеÑтване на прозорец +Name[bn]=উইণà§à¦¡à§‹ সরানো শà§à¦°à§ +Name[br]=Kregiñ da zilec'hiañ ar prenestr +Name[bs]=PoÄetak pomjeranja prozora +Name[ca]=Inici de moviment de la finestra +Name[cs]=ZaÄátek pÅ™esunu okna +Name[csb]=Sztart przesëwaniô òkna +Name[cy]=Cychwyn Symud y Ffenestr +Name[da]=Vindue flyt begynd +Name[de]=Fenster verschieben: Start +Name[el]=ΑÏχή μετακίνησης παÏαθÏÏου +Name[eo]=Komenco de fenestromovo +Name[es]=Comenzar a mover la ventana +Name[et]=Akna liigutamise algus +Name[eu]=Leihoaren mugimenduaren hasiera +Name[fa]=آغاز Øرکت پنجره +Name[fi]=Ikkunan siirto alkaa +Name[fr]=Début de déplacement de fenêtre +Name[fy]=Begjinne mei finsterbeweging +Name[gl]=InÃcio dun Movemento de Fiestra +Name[he]=התחלת הזזת חלון +Name[hi]=विंडो खिसकाना चालू +Name[hr]=PoÄetak micanja prozora +Name[hu]=Ablakmozgatás kezdete +Name[id]=Pindah Start Jendela +Name[is]=Færsla glugga hefst +Name[it]=Inizio spostamento finestra +Name[ja]=ウィンドウ移動開始 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის მáƒáƒ«áƒ áƒáƒáƒ‘რდáƒáƒ˜áƒ¬áƒ§áƒ +Name[kk]=Терезе жылжытуын баÑтау +Name[km]=ចាប់ផ្ដើម​ផ្លាស់ទី​បង្អួច +Name[ko]=ì°½ ì´ë™ ì‹œìž‘ë¨ +Name[lo]=ເລີ່ມàºà»‰àº²àºàº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Lango perkÄ—limo pradžia +Name[lv]=Loga PÄrvietoÅ¡ana SÄkta +Name[mk]=Почеток на премеÑтување на прозорец +Name[mn]=Цонх шилжүүлÑÑ…: ÐхлÑл +Name[mt]=Ibda' Mexxi Window +Name[nb]=Start vindusflytting +Name[nds]=Finsterschuven hett anfangt +Name[ne]=सारà¥à¤¨à¥‡ सञà¥à¤à¥à¤¯à¤¾à¤² सà¥à¤°à¥à¤†à¤¤ +Name[nl]=Starten met vensterbeweging +Name[nn]=Start vindaugsflytting +Name[nso]=Thomiso ya Tshutiso ya Window +Name[oc]=Inici de moviment de finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ à¨à¨§à¨°-ਓਧਰ ਸ਼à©à¨°à©‚ +Name[pl]=Start przesuwania okna +Name[pt]=Janela Começa a Mover-se +Name[pt_BR]=Começar a mover janela +Name[ro]=ÃŽnceput mutare fereastră +Name[ru]=Ðачало Ð¿ÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¾ÐºÐ½Ð° +Name[rw]=Itangira ry'Iyimura ry'Idirishya +Name[se]=Lásesirdima álgu +Name[sk]=ZaÄaÅ¥ presun okna +Name[sl]=Premakni zaÄetek okna +Name[sr]=Почетак премештања прозора +Name[sr@Latn]=PoÄetak premeÅ¡tanja prozora +Name[sv]=Fönsterflytt börjar +Name[ta]=சாளர நகரà¯à®¤à¯à®¤à®²à¯ ஆரமà¯à®ªà®®à¯ +Name[te]=విండొల కదలిక మొదలౠ+Name[tg]=Тирезаҳо шурӯъ ба ҳаракат кард +Name[th]=เริ่มย้ายหน้าต่าง +Name[tr]=Windows Taşı BaÅŸlangıç +Name[tt]=Täräzä Küçerü BaÅŸlaw +Name[uk]=Почати рухати вікно +Name[uz]=Oynani koÊ»chirishni boshlash +Name[uz@cyrillic]=Ойнани кўчиришни бошлаш +Name[ven]=U thoma u tshimbila ha windo +Name[vi]=Bắt đầu Di chuyển Cá»a sổ +Name[wa]=Li purnea cmince a bodjî +Name[xh]=Isiqalo Sentshukumo ye Window +Name[zh_CN]=窗å£ç§»åŠ¨å¼€å§‹ +Name[zh_TW]=開始移動視窗 +Name[zu]=Ukuqala komnyakazo we-Window +Comment=A window has begun moving +Comment[af]='n Venster het begin beweeg +Comment[ar]=بدأت ناÙذة ÙÙŠ التØرك +Comment[be]=Ðкно пачынае рухацца +Comment[bg]=Ðачало на премеÑтване на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ সরানো শà§à¦°à§ হয়েছে +Comment[bs]=Prozor se poÄeo pomjerati +Comment[ca]=Una finestra ha començat a moure's +Comment[cs]=Okno zapoÄalo pÅ™esun +Comment[csb]=Ã’kno zaczënô bëc przesëwóné +Comment[da]=Et vindue er begyndt at flyttes +Comment[de]=Fenster wird verschoben +Comment[el]=Ένα παÏάθυÏο άÏχισε να μετακινείται +Comment[eo]=Fenestro komencis movadon +Comment[es]=Comienza a moverse una ventana +Comment[et]=Aken on hakanud liikuma +Comment[eu]=Leihoa mugitzen hasi da +Comment[fa]=پنجره شروع به Øرکت می‌کند +Comment[fi]=Ikkuna aloittaa siirtymisen +Comment[fr]=Début de déplacement de fenêtre +Comment[fy]=In finster begon mei bewegen +Comment[gl]=Unha Fiestra Comezou a se Mover +Comment[he]=חלון החל לזוז +Comment[hi]=à¤à¤• विंडो खिसकना चालू हà¥à¤† +Comment[hr]=Prozor je zapoÄeo pomicanje +Comment[hu]=Ablakmozgatás kezdete +Comment[is]=Gluggi hefur byrjað að færast +Comment[it]=Inizia lo spostamento di una finestra +Comment[ja]=ウィンドウã®ç§»å‹•ã‚’開始ã—ã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ áƒáƒ› დáƒáƒ˜áƒ¬áƒ§áƒ მáƒáƒ«áƒ áƒáƒáƒ‘რ+Comment[kk]=Терезені жылжытуы баÑталды +Comment[km]=បង្អួច​មួយ​បាន​ចាប់ផ្ដើម​ផ្លាស់ទី +Comment[ko]=ì°½ ì´ë™ì´ ì‹œìž‘ë¨ +Comment[lt]=Langas pradÄ—jo judÄ—ti +Comment[lv]=Logs ir SÄcis PÄrvietoties +Comment[mk]=Прозорецот почнува да Ñе премеÑтува +Comment[mt]=Window bdiet titmexxa +Comment[nb]=Vindusflytting starter +Comment[nds]=Dat Verschuven vun'n Finster hett anfungen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ सारà¥à¤¨à¥‡ कà¥à¤°à¤® सà¥à¤°à¥à¤†à¤¤ गरेको छ +Comment[nl]=Een venster begon met bewegen +Comment[nn]=Vindaugsflytting startar +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ਾ ਹਿੱਲਣਾ ਸ਼à©à¨°à©‚ +Comment[pl]=Okno zaczyna być przesuwane +Comment[pt]=Uma janela começou a movimentar-se +Comment[pt_BR]=Uma janela começou a ser movida +Comment[ro]=O fereastră a început să se miÈ™te +Comment[ru]=Окно начало перемещатьÑÑ +Comment[rw]=Idirishya ryatangiye kwimuka +Comment[se]=Láse lea lihkadiÅ¡goahtán +Comment[sk]=ZaÄal sa presun okna +Comment[sl]=Okno se je priÄelo premikati +Comment[sr]=Прозор је почео Ñа премештањем +Comment[sr@Latn]=Prozor je poÄeo sa premeÅ¡tanjem +Comment[sv]=Ett fönster har börjat flyttas +Comment[ta]=சாளரம௠நகர ஆரமà¯à®ªà®¿à®¤à¯à®¤à®¤à¯ +Comment[te]=విండొ కదలటం మొదలà±à°ªà±†à°Ÿà±à°Ÿà°¿à°‚ది +Comment[th]=หน้าต่างà¸à¸³à¸¥à¸±à¸‡à¹€à¸£à¸´à¹ˆà¸¡à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢ +Comment[tr]=Bir pencere hareket etmeye baÅŸladı +Comment[tt]=Täräzä küçerelä baÅŸladı +Comment[uk]=Початок переÑÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–ÐºÐ½Ð° +Comment[uz]=Oyna koÊ»chib boshladi +Comment[uz@cyrillic]=Ойна кўчиб бошлади +Comment[vi]=Má»™t cá»a sổ đã bắt đầu được di chuyển +Comment[wa]=On purnea a cmincî a bodjî +Comment[zh_CN]=窗å£å¼€å§‹ç§»åŠ¨ +Comment[zh_TW]=視窗開始移動 +default_presentation=0 + +[moveend] +Name=Window Move End +Name[af]=Venster Beweeg Klaar +Name[ar]=نهاية تØريك الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ Daşıma BitiÅŸi +Name[be]=Скончыць рух акна +Name[bg]=Край на премеÑтване на прозорец +Name[bn]=উইণà§à¦¡à§‹ সরানো শেষ +Name[br]=Echuiñ da zilec'hiañ ar prenestr +Name[bs]=Kraj pomjeranja prozora +Name[ca]=Fi de moviment de la finestra +Name[cs]=Konec pÅ™esunu okna +Name[csb]=Kùńc przesëwaniô òkna +Name[cy]=Gorffen Symud y Ffenestr +Name[da]=Vindue flyt slut +Name[de]=Fenster verschieben: Ende +Name[el]=ΤÎλος μετακίνησης παÏαθÏÏου +Name[eo]=Fino de fenestromovo +Name[es]=Terminar de mover la ventana +Name[et]=Akna liigutamise lõpp +Name[eu]=Leihoaren mugimenduaren bukaera +Name[fa]=پایان Øرکت پنجره +Name[fi]=Ikkunan siirto loppuu +Name[fr]=Fin de déplacement de fenêtre +Name[fy]=Stopje mei finsterbeweging +Name[gl]=Remate dun Movemento de Fiestra +Name[he]=×¡×™×•× ×”×–×–×ª חלון +Name[hi]=विंडो खिसकाना ख़तà¥à¤® +Name[hr]=ZavrÅ¡etak micanja prozora +Name[hu]=Ablakmozgatás vége +Name[id]=Pindah End Jendela +Name[is]=Færslu glugga lýkur +Name[it]=Fine spostamento finestra +Name[ja]=ウィンドウ移動終了 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის მáƒáƒ«áƒ áƒáƒáƒ‘ის დáƒáƒ¡áƒáƒ¡áƒ ული +Name[kk]=Терезе жылжытуы аÑқталды +Name[km]=ឈប់​ផ្លាស់ទី​បង្អួច +Name[ko]=ì°½ ì´ë™ì´ ëë‚¬ìŒ +Name[lo]=ສີ້ນສຸດàºàº²àº™àºà»‰àº²àºàº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Lango perkÄ—limo pabaiga +Name[lv]=Loga PÄrvietoÅ¡ana Beigta +Name[mk]=Крај на премеÑтување на прозорец +Name[mn]=Цонх шилжүүлÑÑ…: ТөгÑгөл +Name[mt]=Lest Mexxi Window +Name[nb]=Slutt vindusflytting +Name[nds]=Finsterschuven is an't Enn +Name[ne]=सारà¥à¤¨à¥‡ सञà¥à¤à¥à¤¯à¤¾à¤² अनà¥à¤¤à¥à¤¯ +Name[nl]=Stoppen met vensterbeweging +Name[nn]=Slutt vindaugsflytting +Name[nso]=Mafelelo a Tshutiso ya Window +Name[oc]=Fin de moviment de finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ à¨à¨§à¨°-ਓਧਰ ਸਮਾਪਤ +Name[pl]=Koniec przesuwania okna +Name[pt]=Janela Pára de Mover-se +Name[pt_BR]=Acabar de mover janela +Name[ro]=SfîrÈ™it mutare fereastră +Name[ru]=Конец Ð¿ÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¾ÐºÐ½Ð° +Name[rw]=Impera y'Iyimura ry'Idirishya +Name[se]=Lásesirdima loahppa +Name[sk]=DokonÄiÅ¥ presun okna +Name[sl]=Premakni konec okna +Name[sr]=Крај премештања прозора +Name[sr@Latn]=Kraj premeÅ¡tanja prozora +Name[sv]=Fönsterflytt slutar +Name[ta]=சாளர நகரà¯à®¤à¯à®¤à®²à¯ à®®à¯à®Ÿà®¿à®µà¯ +Name[te]=విండొల కదలిక ఆపౠ+Name[tg]=Ҳаракати тиреза тамом шуд +Name[th]=สิ้นสุดà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Name[tr]=Pencere Taşı BitiÅŸ +Name[tt]=Täräzä Küçerü Tuqtatu +Name[uk]=Закінчити рухати вікно +Name[uz]=Oynani koÊ»chirishni tugatish +Name[uz@cyrillic]=Ойнани кўчиришни тугатиш +Name[ven]=U fhela hau tshimbila ha windo +Name[vi]=Kết thúc Di chuyển Cá»a sổ +Name[wa]=Li purnea s' djoke di bodjî +Name[xh]=Isiphelo Sentshukumo ye Window +Name[zh_CN]=窗å£ç§»åŠ¨ç»“æŸ +Name[zh_TW]=完æˆç§»å‹•è¦–窗 +Name[zu]=Ukuphela komnyakazo we-Window +Comment=A window has completed its moving +Comment[af]='n Venster het sy beweging voltooi +Comment[ar]=إنتهت ناÙذة من التØرك +Comment[be]=Ðкно завÑршае рух +Comment[bg]=Край на премеÑтване на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ সরানো শেষ হয়েছে +Comment[bs]=Prozor je zavrÅ¡io micanje +Comment[ca]=Una finestra ha acabat de moure's +Comment[cs]=Okno dokonÄilo pÅ™esun +Comment[csb]=Zakùńczono przesëwanié òkna +Comment[da]=Et vindue er færdigt med at flytte +Comment[de]=Fensterverschiebung abgeschlossen +Comment[el]=Ένα παÏάθυÏο ολοκλήÏωσε τη μετακίνησή του +Comment[eo]=Fenestro finis la movadon +Comment[es]=Una ventana ha terminado de moverse +Comment[et]=Aken on lõpetanud liikumise +Comment[eu]=Leihoaren mugimendua bukatu da +Comment[fa]=پنجره Øرکتش را کامل می‌کند +Comment[fi]=Ikkunan siirto valmis +Comment[fr]=Fin de déplacement de fenêtre +Comment[fy]=In finster is klear mei bewegen +Comment[gl]=Unha Fiestra rematou o seu movemento +Comment[he]=חלון ×”×©×œ×™× ×ת הזזתו +Comment[hi]=à¤à¤• विंडो ने खिसकना पूरà¥à¤£ किया +Comment[hr]=Prozor je zavrÅ¡io pomicanje +Comment[hu]=Ablakmozgatás vége +Comment[is]=Gluggi er kominn á áfangastað +Comment[it]=Finisce lo spostamento di una finestra +Comment[ja]=ウィンドウã®ç§»å‹•ã‚’終了ã—ã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒ ის მáƒáƒ«áƒ áƒáƒáƒ‘რსრულდებრ+Comment[kk]=Терезені жылжытуы аÑқталды +Comment[km]=បង្អួច​មួយ​បាន​បញ្ចប់​ការ​ផ្លាស់ទី​របស់​វា +Comment[ko]=ì°½ ì´ë™ì´ ëë‚¬ìŒ +Comment[lt]=Lango perkÄ—limas baigtas +Comment[lv]=Logs ir Pabeidzis PÄrvietoÅ¡anos +Comment[mk]=Прозорецот го заврши Ñвоето премеÑтување +Comment[mt]=Window waqfet titmexxa +Comment[nb]=Vindusflytting slutter +Comment[nds]=Dat Verschuven vun'n Finster is fardig +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ सारà¥à¤¨à¥‡ कà¥à¤°à¤® समापà¥à¤¤ गरेको छ +Comment[nl]=Een venster is klaar met bewegen +Comment[nn]=Vindaugsflytting ferdig +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਦਾ ਗਤੀਵਿਧੀ ਸਮਾਪਤ +Comment[pl]=Okno zakoÅ„czyÅ‚o przesuwanie +Comment[pt]=Uma janela acabou de movimentar-se +Comment[pt_BR]=Uma janela acabou de ser movida +Comment[ro]=O fereastră È™i-a terminat miÈ™carea +Comment[ru]=Перемещение окна завершилоÑÑŒ +Comment[rw]=Idirishya ryarangije iyimura ryaryo +Comment[se]=Láse lea geargan lihkadeames +Comment[sk]=Presun okna je ukonÄený +Comment[sl]=Okno se je prenehalo premikati +Comment[sr]=Прозор је завршио премештање +Comment[sr@Latn]=Prozor je zavrÅ¡io premeÅ¡tanje +Comment[sv]=Ett fönster har flyttats klart +Comment[ta]=நகரà¯à®¤à¯à®¤à¯à®µà®¤à®©à¯ மூலம௠சாளரம௠மà¯à®´à¯à®®à¯ˆà®¯à®Ÿà¯ˆà®¨à¯à®¤à®¤à¯. +Comment[te]=విండొ కదలటం అయిపొయింది +Comment[th]=หน้าต่างถูà¸à¸¢à¹‰à¸²à¸¢à¹€à¸ªà¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§ +Comment[tr]=Bir pencere hareketini tamamladı +Comment[tt]=Täräzä küçerü tuqtaldı +Comment[uk]=Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÑÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–ÐºÐ½Ð° +Comment[uz]=Oyna koÊ»chishni tugatdi +Comment[uz@cyrillic]=Ойна кўчишни тугатди +Comment[vi]=Má»™t cá»a sổ đã được di chuyển xong +Comment[wa]=On purnea a fini d' bodjî +Comment[zh_CN]=窗å£å®Œæˆç§»åŠ¨ +Comment[zh_TW]=視窗完æˆç§»å‹• +default_presentation=0 + +[resizestart] +Name=Window Resize Start +Name[af]=Venster Hervergroot Begin +Name[ar]=بداية تغيير قياس الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ Böyüklüyü DÉ™yiÅŸdirmÉ™ BaÅŸlanğıcı +Name[be]=Пачаць змены памеру акна +Name[bg]=Ðачало на промÑна на размера на прозорец +Name[bn]=উইণà§à¦¡à§‹ মাপ বদল শà§à¦°à§ +Name[br]=Kregiñ da adventañ ar prenestr +Name[bs]=PoÄetak promjene veliÄine prozora +Name[ca]=Inici del canvi de mida de la finestra +Name[cs]=ZaÄátek zmÄ›ny velikosti okna +Name[csb]=ZôczÄ…tk zmianë miarë òkna +Name[cy]=Cychwyn Newid Maint y Ffenestr +Name[da]=Vindue ændr størrelse begynd +Name[de]=Fenstergröße verstellen: Start +Name[el]=ΑÏχή αλλαγής μεγÎθους παÏαθÏÏου +Name[eo]=Komenco de fenestro-regrandigo +Name[es]=Comenzar a redimensionar la ventana +Name[et]=Akna suuruse muutmise algus +Name[eu]=Leihoaren tamaina aldaketaren hasiera +Name[fa]=آغاز تغییر اندازۀ پنجره +Name[fi]=Ikkunan koonmuutos alkaa +Name[fr]=Début de redimensionnement de fenêtre +Name[fy]=Begjin mei it finster fan grutte te wizigjen +Name[gl]=InÃcio dun Redimensionamento de Fiestra +Name[he]=התחלת ×©×™× ×•×™ גודל חלון +Name[hi]=विंडो नया-आकार चालू +Name[hr]=PoÄetak promjene veliÄine +Name[hu]=Ablakátméretezés kezdete +Name[id]=Resize Start Jendela +Name[is]=Stærðarbreyting glugga hefst +Name[it]=Inizio ridimensionamento finestra +Name[ja]=ウィンドウリサイズ開始 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ იზვლბრ+Name[kk]=Терезе өлшемін өзгертуін баÑтау +Name[km]=ចាប់ផ្ដើម​ប្ដូច​ទំហំ​បង្អួច +Name[ko]=ì°½ í¬ê¸° ì¡°ì ˆ ì‹œìž‘ë¨ +Name[lo]=ເລີ່ມປັບຂະຫນາດຫນ້າຕ່າງ +Name[lt]=Lango dydžio keitimo pradžia +Name[lv]=SÄkas Loga IzmÄ“ra Maiņa +Name[mk]=Почеток на промена на големината на прозорец +Name[mn]=Цонхны Ñ…ÑмжÑÑ Ó©Ó©Ñ€Ñ‡Ð¸Ð»Ó©Ñ…: ÐхлÑл +Name[mt]=Ibda daqqas it-tieqa +Name[nb]=Start endring av vindusstørrelse +Name[nds]=Grött-Ännern hett anfangt +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² रिसाइज सà¥à¤°à¥à¤†à¤¤ +Name[nl]=Starten met venster van grootte wijzigen +Name[nn]=Start endring av vindaugsstorleik +Name[nso]=Thomiso ya Popoleswa ya Window +Name[oc]=Inici de canvia de talha de finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਮà©à©œ-ਆਕਾਰ ਸ਼à©à¨°à©‚ +Name[pl]=PoczÄ…tek zmiany rozmiaru okna +Name[pt]=Janela Começa a Mudar de Tamanho +Name[pt_BR]=Iniciar redimensionamento da janela +Name[ro]=ÃŽnceput redimensionare fereastră +Name[ru]=МенÑетÑÑ Ñ€Ð°Ð·Ð¼ÐµÑ€ окна +Name[rw]=Itangira ry'Ihindurangano ry'Idirishya +Name[se]=Lásesturrodaga rievdadeami álgu +Name[sk]=ZmeniÅ¥ veľkosÅ¥ okna +Name[sl]=ZaÄetek raztezanja okna +Name[sr]=Почетак мењања величине прозора +Name[sr@Latn]=PoÄetak menjanja veliÄine prozora +Name[sv]=Storleksändring av fönster börjar +Name[ta]=சாளர அளவ௠மாறà¯à®± ஆரமà¯à®ªà®®à¯ +Name[tg]=Таъғири андозаи тиреза шурӯъ шуд +Name[th]=เริ่มปรับขนาดหน้าต่าง +Name[tr]=Pencere Boyutlandır BaÅŸlat +Name[tt]=Täräzä Ãœlçämen Ãœzgärtü BaÅŸlaw +Name[uk]=Почати змінювати розмір вікна +Name[uz]=Oynaning oÊ»lchamini oÊ»zgartirish boshlandi +Name[uz@cyrillic]=Ойнанинг ўлчамини ўзгартириш бошланди +Name[ven]=U thoma ha uita saizi hafhu ha windo +Name[vi]=Bắt đầu Thay đổi kÃch cỡ Cá»a sổ +Name[wa]=Li purnea cmince a candjî di grandeu +Name[xh]=Isiqalo Soniko kwakhona sobungakanani se Window +Name[zh_CN]=窗å£ç¼©æ”¾å¼€å§‹ +Name[zh_TW]=é–‹å§‹æ”¹è®Šè¦–çª—å¤§å° +Name[zu]=Ukuqala Kokushintsha usayizi we-Window +Comment=A window has begun resizing +Comment[af]='n Venster het begin hervergroot +Comment[ar]=بدأت الناÙذة ÙÙŠ تغيير القياس +Comment[be]=Ðкно пачынае змÑнÑць памер +Comment[bg]=Ðачало на промÑна на размера на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹à¦° মাপ বদলানো শà§à¦°à§ হয়েছে +Comment[bs]=Prozor je poÄeo mijenjati veliÄinu +Comment[ca]=Una finestra ha començat a canviar de mida +Comment[cs]=Okno zapoÄalo zmÄ›nu velikosti +Comment[csb]=Ã’kno naczãło zmieniwac miarã +Comment[da]=Et vindue er begyndt at ændre størrelse +Comment[de]=Größenveränderung des Fensters wird gestartet +Comment[el]=Ένα παÏάθυÏο άÏχισε να αλλάζει μÎγεθος +Comment[eo]=Vindozo komencis regrandigon +Comment[es]=Comienza a redimensionarse una ventana +Comment[et]=Akna suurus on hakanud muutuma +Comment[eu]=Leihoaren tamaina aldatzen hasi da +Comment[fa]=تغییر اندازۀ پنجره شروع ‌شده است +Comment[fi]=Ikkunan koonmuutos alkaa +Comment[fr]=Début de redimensionnement de fenêtre +Comment[fy]=In finster is begon mei it wizigjen fan grutte +Comment[gl]=Unha Fiestra Comezou a se Redimensionar +Comment[he]=חלון החל ×œ×©× ×•×ª ×ת גודלו +Comment[hi]=à¤à¤• विंडो में नया-आकार बनाना चालू हà¥à¤† +Comment[hr]=Prozor je zapoÄeo promjenu veliÄine +Comment[hu]=Ablakátméretezés kezdete +Comment[is]=Stærð glugga er byrjuð að breytast +Comment[it]=Inizia il ridimensionamento di una finestra +Comment[ja]=ウィンドウã®ãƒªã‚µã‚¤ã‚ºã‚’開始ã—ã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒáƒ áƒáƒ› დáƒáƒ˜áƒ¬áƒ§áƒ ზáƒáƒ›áƒ˜áƒ¡ შეცვლრ+Comment[kk]=Терезе өлшемін өзгертуі баÑталды +Comment[km]=បង្អួច​មួយ​បាន​ចាប់ផ្ដើម​ប្ដូរ​ទំហំ +Comment[ko]=ì°½ í¬ê¸° ì¡°ì ˆì´ ì‹œìž‘ë¨ +Comment[lt]=Lango dydis pradÄ—tas keisti +Comment[lv]=Loga IzmÄ“ra Maiņa SÄkusies +Comment[mk]=Прозорецот започна да ја менува големината +Comment[mt]=Window bdiet tiddaqqas +Comment[nb]=Endring av vindusstørrelsen starter +Comment[nds]=Dat Ännern vun de Grött hett anfungen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ रिसाइज गरà¥à¤¨à¥‡ कà¥à¤°à¤® सà¥à¤°à¥ गरेको छ +Comment[nl]=Een venster begon met het wijzigen van grootte +Comment[nn]=Endring av vindaugsstorleiken startar +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਦੀ ਆਕਾਰ ਤਬਦੀਲੀ ਸ਼à©à¨°à©‚ +Comment[pl]=Okno zaczęło zmianiać rozmiar +Comment[pt]=Uma janela começou a mudar de tamanho +Comment[pt_BR]=Uma janela começou a ser redimensionada +Comment[ro]=O fereastră È™i-a început redimensionarea +Comment[ru]=Окно начало изменÑÑ‚ÑŒ размер +Comment[rw]=Idirishya ryatangiye ihindurangero +Comment[se]=Láse lea rievdadiÅ¡goahtán sturrodaga +Comment[sk]=ZaÄala zmena veľkosti okna +Comment[sl]=Spreminjanje velikosti okna se je zaÄelo +Comment[sr]=Прозор је почео мењање величине +Comment[sr@Latn]=Prozor je poÄeo menjanje veliÄine +Comment[sv]=Storleksändring av ett fönster har pÃ¥börjats +Comment[ta]=சாளரதà¯à®¤à®¿à®©à¯ அளவ௠மாறதà¯à®¤à¯à®µà®™à¯à®•à¯à®•à®¿à®±à®¤à¯ +Comment[th]=หน้าต่างเริ่มปรับขนาด +Comment[tr]=Bir pencere yeniden boyutlandırılmaya baÅŸladı +Comment[tt]=Täräzä ülçämen üzgärtü baÅŸlandı +Comment[uk]=Початок зміни розміру вікна +Comment[uz]=Oynaning oÊ»lchami oÊ»zgarib boshladi +Comment[uz@cyrillic]=Ойнанинг ўлчами ўзгариб бошлади +Comment[vi]=Má»™t cá»a sổ đã được bắt đầu thay đổi kÃch cỡ +Comment[wa]=On purnea a cmincî a candjî s' grandeu +Comment[zh_CN]=窗å£å¼€å§‹ç¼©æ”¾ +Comment[zh_TW]=è¦–çª—é–‹å§‹æ”¹è®Šå¤§å° +default_presentation=0 + +[resizeend] +Name=Window Resize End +Name[af]=Venster Hervergroot Klaar +Name[ar]=نهاية تغيير قياس الناÙذة +Name[az]=PÉ™ncÉ™rÉ™ Böyüklüyü DÉ™yiÅŸdirmÉ™ BitiÅŸi +Name[be]=Скончыць змены памеру акна +Name[bg]=Край на промÑна на размера на прозорец +Name[bn]=উইণà§à¦¡à§‹ মাপ বদল শেষ +Name[br]=Echuiñ da adventañ ar prenestr +Name[bs]=Kraj promjene veliÄine prozora +Name[ca]=Fi del canvi de mida de la finestra +Name[cs]=Konec zmÄ›ny velikosti okna +Name[csb]=Kùńc zmianë miarë òkna +Name[cy]=Gorffen Newid Maint y Ffenestr +Name[da]=Vindue ændr størrelse slut +Name[de]=Fenstergröße verstellen: Ende +Name[el]=ΤÎλος αλλαγής μεγÎθους παÏαθÏÏου +Name[eo]=Fino de fenestro-regrandigo +Name[es]=Terminar de redimensionar la ventana +Name[et]=Akna suuruse muutmise lõpp +Name[eu]=Leihoaren tamaina aldaketaren bukaera +Name[fa]=پایان تغییر اندازۀ پنجره +Name[fi]=Ikkunan koonmuutos loppuu +Name[fr]=Fin de redimensionnement de fenêtre +Name[fy]=Finster fan grutte wizigjen einigje +Name[gl]=Remate dun Redimensionamento de Fiestra +Name[he]=×¡×™×•× ×©×™× ×•×™ גודל חלון +Name[hi]=विंडो नया-आकार बनà¥à¤¦ +Name[hr]=ZavrÅ¡etak promjene veliÄine +Name[hu]=Ablakátméretezés vége +Name[id]=Resize End Jendela +Name[is]=Stærðarbreyting glugga lýkur +Name[it]=Fine ridimensionamento finestra +Name[ja]=ウィンドウリサイズ終了 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ შეიცვáƒáƒšáƒ +Name[kk]=Терезе өлшемін өзгертуін аÑқтау +Name[km]=ឈប់​ប្ដូរ​ទំហំ​បង្អួច +Name[ko]=ì°½ 닫기가 ëë‚¬ìŒ +Name[lo]=ສີ້ນສຸດàºàº²àº™àº›àº±àºšàº‚ະຫນາດຫນ້າຕ່າງ +Name[lt]=Lango dydžio keitimo pabaiga +Name[lv]=Loga IzmÄ“ra Maiņa Beidzas +Name[mk]=Крај на промена на големината на прозорец +Name[mn]=Цонхны Ñ…ÑмжÑÑ Ó©Ó©Ñ€Ñ‡Ð¸Ð»Ó©Ñ…: ТөгÑгөл +Name[mt]=Lesti mid-daqqis tat-tieqa +Name[nb]=Slutt endring av vindusstørrelse +Name[nds]=Grött-Ännern is an't Enn +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² रिसाइज अनà¥à¤¤à¥à¤¯ +Name[nl]=Stoppen met venster van grootte wijzigen +Name[nn]=Slutt endring av vindaugsstorleik +Name[nso]=Mafelelo a Popoleswa ya Window +Name[oc]=Fin de canvi de talha de finestra +Name[pa]=à¨à¨°à©‹à¨–ਾ ਮà©à©œ ਆਕਾਰ ਸਮਾਪਤ +Name[pl]=Koniec zmiany rozmiaru okna +Name[pt]=Janela Acaba de Mudar de Tamanho +Name[pt_BR]=Parar redimensionamento da janela +Name[ro]=SfîrÈ™it redimensionare fereastră +Name[ru]=Размер окна изменилÑÑ +Name[rw]=Ihindurangero ry'Idirishya Ryarangiye +Name[se]=Lásesturrodaga rievdadeami loahppa +Name[sk]=DokonÄiÅ¥ zmenu veľkosti okna +Name[sl]=Konec raztezanja okna +Name[sr]=Крај мењања величине прозора +Name[sr@Latn]=Kraj menjanja veliÄine prozora +Name[sv]=Storleksändring av fönster slutar +Name[ta]=சாளர அளவ௠மாறà¯à®± à®®à¯à®Ÿà®¿à®µà¯ +Name[tg]=Таъғири андозаи тиреза тамом шуд +Name[th]=สิ้นสุดà¸à¸²à¸£à¸›à¸£à¸±à¸šà¸‚นาดหน้าต่าง +Name[tr]=Pencer Boyutlandır Bitir +Name[tt]=Täräzä Ãœlçämen Ãœzgärtü Tuqtatu +Name[uk]=Закінчити змінювати розмір вікна +Name[uz]=Oynaning oÊ»lchamini oÊ»zgartirish tugadi +Name[uz@cyrillic]=Ойнанинг ўлчамини ўзгартириш тугади +Name[ven]=U fhela ha uita saizi hafhu ha windo +Name[vi]=Kết thúc Thay đổi kÃch cỡ Cá»a sổ +Name[wa]=Li purnea s' djoke di candjî di grandeu +Name[xh]=Isiphelo Sobungakanani kwakhona se Window +Name[zh_CN]=窗å£ç¼©æ”¾ç»“æŸ +Name[zh_TW]=完æˆæ”¹è®Šè¦–çª—å¤§å° +Name[zu]=Ukuphela ngokushintsha usayizi we-Window +Comment=A window has finished resizing +Comment[af]='n Venster se hervergrooting is voltooi +Comment[ar]=إنتهت ناÙذة تغيير القياس +Comment[be]=Ðкно завÑршае змены памеру +Comment[bg]=Край на промÑна на размера на прозорец +Comment[bn]=à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹à¦° মাপ বদলানো শেষ হয়েছে +Comment[bs]=Prozor je gotov s mijenjanjem veliÄine +Comment[ca]=Una finestra ha acabat de canviar de mida +Comment[cs]=Okno dokonÄilo zmÄ›nu velikosti +Comment[csb]=Ã’kno skùńczëło zmieniwac miarã +Comment[da]=Et vindue er færdigt med at ændre størrelse +Comment[de]=Größenveränderung des Fensters abgeschlossen +Comment[el]=Ένα παÏάθυÏο ολοκλήÏωσε την αλλαγή μεγÎθους +Comment[eo]=Vindozo finis regrandigon +Comment[es]=Termina de redimensionarse una ventana +Comment[et]=Aken on suuruse muutmise lõpetanud +Comment[eu]=Leihoaren tamaina aldaketa bukatu da +Comment[fa]=تغییر اندازۀ پنجره به اتمام رسیده است +Comment[fi]=Ikkunan koonmuutos loppuu +Comment[fr]=Fin de redimensionnement de fenêtre +Comment[fy]=In finster is ree mei it wizigjen fan grutte +Comment[gl]=Unha Fiestra Rematou o Redimensionamento +Comment[he]=חלון ×”×©×œ×™× ×ת ×©×™× ×•×™ גודלו +Comment[hi]=à¤à¤• विंडो ने नया-आकार पूरà¥à¤£ किया +Comment[hr]=Prozor je zavrÅ¡io s promjenom veliÄine +Comment[hu]=Ablakátméretezés vége +Comment[is]=Stærð glugga hefur breyst +Comment[it]=Finisce il ridimensionamento di una finestra +Comment[ja]=ウィンドウã®ãƒªã‚µã‚¤ã‚ºãŒçµ‚了ã—ã¾ã—㟠+Comment[ka]=ფáƒáƒœáƒ¯áƒ ის ზáƒáƒ›áƒ˜áƒ¡ ცვლილებრდáƒáƒ¡áƒ ულდრ+Comment[kk]=Терезе өлшемін өзгертуі аÑқталды +Comment[km]=បង្អួច​មួយ​បាន​បញ្ចប់​ការ​ប្ដូរ​ទំហំ +Comment[ko]=ì°½ í¬ê¸° ì¡°ì ˆì´ ëë‚¬ìŒ +Comment[lt]=Lango dydžio keitimas baigtas +Comment[lv]=Loga IzmÄ“ra Maiņa Beigusies +Comment[mk]=Прозорецот заврши Ñо менувањето на големината +Comment[mt]=Window spiÄ‹Ä‹at tiddaqqas +Comment[nb]=Endring av vindusstørrelsen slutter +Comment[nds]=Dat Ännern vun de Grött is fardig +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ रिसाइज गरà¥à¤¨à¥‡ कà¥à¤°à¤® अनà¥à¤¤à¥à¤¯ गरेको छ +Comment[nl]=Een venster is klaar met het wijzigen van grootte +Comment[nn]=Endring av vindaugsstorleiken ferdig +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਦੀ ਆਕਾਰ ਤਬਦੀਲੀ ਸਮਾਪਤ +Comment[pl]=Okno skoÅ„czyÅ‚o zmieniać rozmiar +Comment[pt]=Uma janela acabou de mudar de tamanho +Comment[pt_BR]=Uma janela foi redimensionada +Comment[ro]=O fereastră È™i-a terminat redimensionarea +Comment[ru]=Изменение размеров окна завершено +Comment[rw]=Idirishya ryarangije ihindurangero +Comment[se]=Láse lea geargan sturrodaga rievdadeames +Comment[sk]=Zmena veľkosti okna je ukonÄená +Comment[sl]=Spreminjanje velikosti okna je konÄano +Comment[sr]=Прозор је завршио промену величине +Comment[sr@Latn]=Prozor je zavrÅ¡io promenu veliÄine +Comment[sv]=Storleksändring av ett fönster har avslutats +Comment[ta]=சாளரதà¯à®¤à®¿à®©à¯ அளவ௠மாறà¯à®±à¯à®¤à®²à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[th]=หน้าต่างปรับขนาดเสร็จà¹à¸¥à¹‰à¸§ +Comment[tr]=Bir pencerenin yeniden boyutlandırma iÅŸlemi bitti +Comment[tt]=Täräzä ülçämen üzgärtü tuqtadı +Comment[uk]=Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð¸ розміру вікна +Comment[uz]=Oynaning oÊ»lchami oÊ»zgarib boÊ»ldi +Comment[uz@cyrillic]=Ойнанинг ўлчами ўзгариб бўлди +Comment[vi]=Má»™t cá»a sổ thay đổi kÃch cỡ xong +Comment[wa]=On purnea a fini d' candjî s' grandeu +Comment[zh_CN]=窗å£å®Œæˆç¼©æ”¾ +Comment[zh_TW]=視窗完æˆæ”¹è®Šå¤§å° +default_presentation=0 + +[demandsattentioncurrent] +Name=Window On Current Desktop Demands Attention +Name[af]='n Venseter op die huidige werkskerm het aandag nodig +Name[ar]=الناÙذة على Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ تتطلب الإنتباه +Name[be]=Ðкно на актыўным працоўным Ñтале прыцÑгвае ўвагу +Name[bg]=Прозорец на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚ÐµÐ½ плот изиÑква внимание +Name[bn]=বরà§à¦¤à¦®à¦¾à¦¨ ডেসà§à¦•à¦Ÿà¦ªà§‡ উইণà§à¦¡à§‹ আপনার দৃষà§à¦Ÿà¦¿ আকরà§à¦·à¦£ করার চেষà§à¦Ÿà¦¾ করছে +Name[bs]=Prozor na trenutnom desktopu zahtijeva pažnju +Name[ca]=Una finestra de l'escriptori actual demana atenció +Name[cs]=Okno na aktuálnà ploÅ¡e vyžaduje pozornost +Name[csb]=Ã’kno na biéżnym pùlce domôgô sã bôczënkù +Name[da]=Vindue pÃ¥ aktuel desktop kræver opmærksomhed +Name[de]=Fenster auf aktueller Arbeitsfläche erfordert Aufmerksamkeit +Name[el]=ΠαÏάθυÏο στην Ï„ÏÎχουσα επιφάνεια εÏγασίας απαιτεί την Ï€Ïοσοχή σας +Name[eo]=Fenestro sur Aktuala Tabulo Petas Atenton +Name[es]=La ventana del escritorio demanda atención +Name[et]=Aken aktiivsel töölaual nõuab tähelepanu +Name[eu]=Uneko mahaigaineko leihoak jaramon egitea eskatzen du. +Name[fa]=پنجره روی رومیزی جاری نیاز به توجه دارد +Name[fi]=Ikkuna nykyisellä työpöydällä kaipaa huomiota +Name[fr]=La fenêtre sur le bureau courant demande votre attention +Name[fy]=Finster op aktive buroblêd freget om oandacht +Name[gl]=Unha fiestra deste Escritório Requere Atención +Name[he]=חלון בשולחן עבודה × ×•×›×—×™ דורש תשומת לב +Name[hr]=Prozor na trenutnoj radnoj povrÅ¡ini zahtijeva pažnju +Name[hu]=Egy ablak az aktuális asztalon beavatkozást igényel +Name[is]=Gluggi á núverandi skjáborði krefst athygli +Name[it]=Finestra sul desktop attivo richiede l'attenzione +Name[ja]=ç¾åœ¨ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—上ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒæ³¨æ„を促ã—ã¦ã„ã¾ã™ +Name[ka]=მიმდინáƒáƒ ე სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ˜áƒ¡ ფáƒáƒœáƒ¯áƒáƒ რყურáƒáƒ“ღებáƒáƒ¡ მáƒáƒ˜áƒ—ხáƒáƒ•áƒ¡ +Name[kk]=ҚолданыÑтағы Ò¯Ñтелдегі терезе назар аударуын Ñұрайды +Name[km]=បង្អួច​លើ​ផ្ទៃážáž»â€‹áž”ច្ចុប្បន្ន ទាមទារ​ការ​ប្រុងប្រយáŸážáŸ’áž“ +Name[ko]=현재 ë°ìŠ¤í¬í†±ì˜ ì°½ì´ ì‘ë‹µì„ ê¸°ë‹¤ë¦¼ +Name[lt]=Langas dabartiniame darbastalyje reikalauja dÄ—mesio +Name[mk]=Прозорец на тековната површина бара внимание +Name[nb]=Vinduer pÃ¥ dette skrivebordet pÃ¥kaller oppmerksomheten +Name[nds]=Finster op den aktuellen Schriefdischen bruukt Acht +Name[ne]=हालको डेसà¥à¤•à¤Ÿà¤ªà¤®à¤¾ सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ साबधानी अनà¥à¤°à¥‹à¤§ गरà¥à¤› +Name[nl]=Venster op huidig bureaublad vraagt om aandacht +Name[nn]=Vindauge pÃ¥ dette skrivebordet pÃ¥kallar merksemd +Name[pa]=ਮੌਜੂਦਾ ਵੇਹੜੇ ਵਿਚਲੇ à¨à¨°à©‹à¨–ੇ ਨੂੰ ਧਿਆਨ ਦੀ ਲੋੜ +Name[pl]=Okno na bieżącym pulpicie domaga siÄ™ uwagi +Name[pt]=Janela no Ecrã Actual que Necessita de Atenção +Name[pt_BR]=Janela na Ãrea de Trabalho Atual Demanda Atenção +Name[ro]=Fereastră pe ecranul curent necesită atenÈ›ie +Name[ru]=Произошло Ñобытие в окне на текущем рабочем Ñтоле +Name[rw]=Idirishya Ku Biro Bigezweho Risaba Ubwitonzi +Name[se]=Láse dán Äállinbeavddis U +Name[sk]=Okno na aktuálnu pracovnú plochu +Name[sl]=Okno na trenutnem namizju potrebuje pozornost +Name[sr]=Прозор на текућој радној површини захтева пажњу +Name[sr@Latn]=Prozor na tekućoj radnoj povrÅ¡ini zahteva pažnju +Name[sv]=Fönster pÃ¥ nuvarande skrivbord kräver uppmärksamhet +Name[th]=หน้าต่างบนพื้นที่หน้าจà¸à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸„วามสนใจ +Name[tr]=Geçerli Masaüstündeki Pencere Ä°lgi Bekliyor +Name[tt]=Bu Östäldäge Täräzä Ä°ÄŸtibar Birü Sorà +Name[uk]=Вікно на поточній Ñтільниці потребує уваги +Name[vi]=Cá»a sổ trên Mà n hình ná»n Hiện tại Cần được Quan tâm +Name[wa]=On purnea hÃ¥yné so l' sicribannes do moumint dimande voste atincion +Name[zh_CN]=当å‰æ¡Œé¢ä¸Šçš„窗å£è¯·æ±‚æ³¨æ„ +Name[zh_TW]=視窗在目å‰æ¡Œé¢è¦æ±‚焦點 +Comment=A window on the current virtual desktop demands attention +Comment[af]='n Venseter op die huidige virtuele werkskerm het aandag nodig +Comment[ar]=ناÙذة على Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ الوهمي الØالي تتطلّب الإنتباه +Comment[be]=Ðкно на бÑгучым віртуальным працоўным Ñтале прыцÑгвае ўвагу +Comment[bg]=Прозорец на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚ÐµÐ½ плот изиÑква внимание +Comment[bn]=বরà§à¦¤à¦®à¦¾à¦¨ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ªà§‡ à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ আপনার দৃষà§à¦Ÿà¦¿ আকরà§à¦·à¦£ করার চেষà§à¦Ÿà¦¾ করছে +Comment[bs]=Prozor na trenutnoj radnoj povrÅ¡ini zahtijeva pažnju +Comment[ca]=Una finestra de l'escriptori virtual actual demana atenció +Comment[cs]=Okno na aktuálnà virtuálnà ploÅ¡e vyžaduje vaÅ¡i pozornost +Comment[csb]=Ã’kno na biéżnym pùlce domôgô sã bôczëniô na se +Comment[da]=Et vindue pÃ¥ den nuværende desktop kræver opmærksomhed +Comment[de]=Ein Fenster auf der aktuellen virtuellen Arbeitsfläche erfordert Ihre Aufmerksamkeit +Comment[el]=Ένα παÏάθυÏο στην Ï„ÏÎχουσα επιφάνεια εÏγασίας απαιτεί την Ï€Ïοσοχή σας +Comment[eo]=Fenestro sur aktuala virtuala tabulo petas atenton +Comment[es]=Una ventana del escritorio requiere atención +Comment[et]=Aken aktiivsel virtuaalsel töölaual nõuab tähelepanu +Comment[eu]=Uneko mahaigain birtualeko leiho batek jaramon egitea eskatzen du +Comment[fa]=پنجره‌ای روی رومیزی مجازی جاری، نیاز به توجه دارد +Comment[fi]=Ikkuna nykyisellä virtuaalityöpöydällä kaipaa huomiota +Comment[fr]=Une fenêtre du bureau virtuel actuel demande votre attention +Comment[fy]=In finster op it aktive buroblêd freget om oandacht +Comment[gl]=Unha fiestra neste escritório virtual requere atención +Comment[he]=חלון בשולחן עבודה × ×•×›×—×™ דורש תשומת לב +Comment[hr]=Prozor na trenutnoj virtualnoj radnoj povrÅ¡ini zahtijeva pažnju +Comment[hu]=Egy ablak az aktuális asztalon beavatkozást igényel +Comment[is]=Gluggi á núverandi sýndarskjáborði krefst athygli +Comment[it]=Una finestra sul desktop virtuale corrente sta richiedendo l'attenzione +Comment[ja]=ç¾åœ¨ã®ä»®æƒ³ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ä¸Šã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒæ³¨æ„を促ã—ã¦ã„ã¾ã™ +Comment[ka]=მიმდინáƒáƒ ე ვირტუáƒáƒšáƒ£áƒ ი სáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ˜áƒ¡ ფáƒáƒœáƒ¯áƒáƒ რყურáƒáƒ“ღებáƒáƒ¡ მáƒáƒ˜áƒ—ხáƒáƒ•áƒ¡ +Comment[kk]=ҚолданыÑтағы виртуалды Ò¯Ñтелдегі терезе назар аударуын Ñұрайды +Comment[km]= បង្អួច​លើ​ផ្ទៃážáž»â€‹áž“ិម្មិážâ€‹áž”ច្ចុប្បន្ន​ទាមទារការប្រុង​ប្រយáŸážáŸ’áž“ +Comment[ko]=현재 ê°€ìƒ ë°ìŠ¤í¬í†±ì˜ ì°½ì´ ì‘ë‹µì„ ê¸°ë‹¤ë¦¼ +Comment[lt]=Langas dabartiniame menamame darbastalyje reikalauja dÄ—mesio +Comment[mk]=Ðекој од прозорците на тековната површина бара внимание +Comment[nb]=Et vindu pÃ¥ dette skrivebordet pÃ¥kaller oppmerksomheten +Comment[nds]=En Finster op den aktuellen virtuellen Schriefdischen bruukt Acht +Comment[ne]=हालको अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ªà¤®à¤¾ सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ साबधानी अनà¥à¤°à¥‹à¤§ गरà¥à¤› +Comment[nl]=Een venster op het huidige virtuele bureaublad vraagt om aandacht +Comment[nn]=Eit vindauge pÃ¥ dette virtuelle skrivebordet pÃ¥kallar merksemd. +Comment[pa]=ਮੌਜੂਦਾ ਫ਼ਰਜ਼ੀ ਵੇਹੜੇ ਵਿਚਲੇ à¨à¨°à©‹à¨–ੇ ਨੂੰ ਧਿਆਨ ਦੀ ਲੋੜ ਹੈ +Comment[pl]=Okno na bieżącym pulpicie domaga siÄ™ zwrócenia na nie uwagi +Comment[pt]=Uma janela no ecrã actual pediu a atenção do utilizador +Comment[pt_BR]=Uma janela na área de trabalho virtual atual demanda atenção +Comment[ro]=O fereastră în ecranul virtual curent necesită atenÈ›ie +Comment[ru]=Произошло Ñобытие, требующее вашего вниманиÑ, в окне на текущем рабочем Ñтоле +Comment[rw]=Idirishya ku biro bigezweho bitaboneka bidakora bisaba ubwitonzi +Comment[sk]=Okno na aktuálnu pracovnú plochu +Comment[sl]=Okno na trenutnem navideznem namizju potrebuje pozornost +Comment[sr]=Прозор на текућој виртуелној радној површини захтева пажњу +Comment[sr@Latn]=Prozor na tekućoj virtuelnoj radnoj povrÅ¡ini zahteva pažnju +Comment[sv]=Ett fönster pÃ¥ det nuvarande virtuella skrivbordet kräver uppmärksamhet +Comment[th]=หน้าต่างบนพื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸à¸¢à¸¹à¹ˆà¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸„วามสนใจ +Comment[tt]=Biredäge östäldä bulÄŸan täräzä iÄŸtibar birüne sorà +Comment[uk]=Вікно на поточній віртуальній Ñтільниці потребує вашої уваги +Comment[vi]=Má»™t cá»a sổ trên mà n hình ná»n hiện tại Ä‘ang cần được bạn quan tâm đến +Comment[wa]=On purnea hÃ¥yné so l' forveyou scribannes do moumint dimande voste atincion +Comment[zh_CN]=当å‰è™šæ‹Ÿæ¡Œé¢ä¸Šçš„窗å£è¯·æ±‚æ³¨æ„ +Comment[zh_TW]=一個在目å‰è™›æ“¬æ¡Œé¢ä¸Šçš„視窗è¦æ±‚焦點 +default_presentation=64 + +[demandsattentionother] +Name=Window On Other Desktop Demands Attention +Name[af]='n Venseter op 'n ander werkskerm het aandag nodig +Name[ar]=ناÙذة على Ø³Ø·Ø Ù…ÙƒØªØ¨ آخر تتطلّب الإنتباه +Name[be]=Ðкно на ÑžÑÑ–Ñ… працоўных Ñталах прыцÑгвае ўвагу +Name[bg]=Прозорец от друг работен плот изиÑква внимание +Name[bn]=অনà§à¦¯ ডেসà§à¦•à¦Ÿà¦ªà§‡ উইণà§à¦¡à§‹ আপনার দৃষà§à¦Ÿà¦¿ আকরà§à¦·à¦£ করার চেষà§à¦Ÿà¦¾ করছে +Name[bs]=Prozor na drugom desktopu zahtijeva pažnju +Name[ca]=Una finestra d'un altre escriptori demana atenció +Name[cs]=Okno na jiné ploÅ¡e vyžaduje pozornost +Name[csb]=Ã’kno na jinszëm pùlce domôgô sã bôczënkù +Name[da]=Vindue pÃ¥ anden desktop kræver opmærksomhed +Name[de]=Fenster auf anderer Arbeitsfläche erfordert Aufmerksamkeit +Name[el]=ΠαÏάθυÏο σε επιφάνεια εÏγασίας διαφοÏετική από την Ï„ÏÎχουσα απαιτεί την Ï€Ïοσοχή σας +Name[eo]=Fenestro sur alia tabulo petas atenton +Name[es]=Ventana de otro escritorio demanda atención +Name[et]=Aken teisel töölaual nõuab tähelepanu +Name[eu]=Beste mahaigaineko leihoak jaramon egitea eskatzen du +Name[fa]=پنجره روی رومیزیهای دیگر نیاز به توجه دارد +Name[fi]=Ikkuna toisella työpöydällä kaipaa huomiota +Name[fr]=Une fenêtre sur un autre bureau demande votre attention +Name[fy]=Finster op oar buroblêd freget om oandacht +Name[gl]=Unha Fiestra en Outro Escritório Requere Atención +Name[he]=חלון בשולחן עבודה ×חר דורש תשומת לב +Name[hr]=Prozor na drugoj radnoj povrÅ¡ini zahtijeva pažnju +Name[hu]=Egy ablak beavatkozást igényel (másik asztalon) +Name[is]=Gluggi á öðru skjáborði krefst athygli +Name[it]=Finestra su altro desktop richiede l'attenzione +Name[ja]=ä»–ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—上ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒæ³¨æ„を促ã—ã¦ã„ã¾ã™ +Name[ka]=სხვრსáƒáƒ›áƒ£áƒ¨áƒáƒ დáƒáƒ¤áƒ˜áƒ¡ ფáƒáƒœáƒ¯áƒáƒ რყურáƒáƒ“ღებáƒáƒ¡ მáƒáƒ˜áƒ—ხáƒáƒ•áƒ¡ +Name[kk]=БаÑқа Ò¯Ñтелдегі терезе назар аударуын Ñұрайды +Name[km]=បង្អួច​លើ​ផ្ទៃážáž»â€‹áž•áŸ’សáŸáž„ ទាមទារ​ការ​ប្រុង​ប្រយáŸážáŸ’áž“ +Name[ko]=다른 ë°ìŠ¤í¬í†±ì˜ ì°½ì´ ì‘ë‹µì„ ê¸°ë‹¤ë¦¼ +Name[lt]=Langas kitame darbastalyje reikalauja dÄ—mesio +Name[mk]=Прозорец на друга површина бара внимание +Name[nb]=Vindu pÃ¥ andre skrivebord pÃ¥kaller oppmerksomheten +Name[nds]=Finster op en anner Schriefdisch bruukt Acht +Name[ne]=अनà¥à¤¯ डेसà¥à¤•à¤Ÿà¤ªà¤®à¤¾ सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ साबधानी अनà¥à¤°à¥‹à¤§ गरà¥à¤› +Name[nl]=Venster op ander bureaublad vraagt om aandacht +Name[nn]=Vindauge pÃ¥ anna skrivebord pÃ¥kallar merksemd +Name[pa]=ਹੋਰ ਵੇਹੜੇ ਵਿਚਲੇ à¨à¨°à©‹à¨–ੇ ਨੂੰ ਧਿਆਨ ਦੀ ਲੋੜ +Name[pl]=Okno na innym pulpicie domaga siÄ™ uwagi +Name[pt]=Janela noutro Ecrã que Necessita de Atenção +Name[pt_BR]=Janela em Outra Ãrea de Trabalho Demanda Atenção +Name[ro]=O fereastră pe un alt ecran necesită atenÈ›ie +Name[ru]=Произошло Ñобытие в окне на другом рабочем Ñтоле +Name[rw]=Idirishya Ku Biro Bindi Risaba Ubwitonzi +Name[sk]=Okno na inú pracovnú plochu +Name[sl]=Okno na drugem namizju potrebuje pozornost +Name[sr]=Прозор на другој радној површини захтева пажњу +Name[sr@Latn]=Prozor na drugoj radnoj povrÅ¡ini zahteva pažnju +Name[sv]=Fönster pÃ¥ annat skrivbord kräver uppmärksamhet +Name[th]=หน้าต่างบนพื้นที่หน้าจà¸à¸à¸·à¹ˆà¸™à¹† ต้à¸à¸‡à¸à¸²à¸£à¸„วามสนใจ +Name[tr]=DiÄŸer Masaüstündeki Pencere Ä°lgi Bekliyor +Name[tt]=BaÅŸqa Östäldäge Täräzä Ä°ÄŸtibar Birü Sorà +Name[uk]=Вікно на іншій Ñтільниці потребує уваги +Name[vi]=Cá»a sổ trên Mà n hình ná»n Khác Cần được Quan tâm +Name[wa]=On purnea hÃ¥yné so-z on ôte sicribannes dimande voste atincion +Name[zh_CN]=其它桌é¢ä¸Šçš„窗å£è¯·æ±‚æ³¨æ„ +Name[zh_TW]=在其他桌é¢çš„視窗è¦æ±‚焦點 +Comment=A window on an inactive virtual desktop demands attention +Comment[af]='n Venster op 'n onaktiewe virtuele werkskerm het aandag nodig +Comment[ar]=ناÙذة على Ø³Ø·Ø Ù…ÙƒØªØ¨ وهمي غير نشط تتطلّب الإنتباه +Comment[be]=Ðкно на ÑžÑÑ–Ñ… віртуальных працоўных Ñталах прыцÑгвае ўвагу +Comment[bg]=Прозорец от друг работен плот изиÑква внимание +Comment[bn]=অনà§à¦¯ à¦à¦•à¦Ÿà¦¿ à¦à¦¾à¦°à§à¦šà§à§Ÿà¦¾à¦² ডেসà§à¦•à¦Ÿà¦ªà§‡ à¦à¦•à¦Ÿà¦¿ উইণà§à¦¡à§‹ আপনার দৃষà§à¦Ÿà¦¿ আকরà§à¦·à¦£ করার চেষà§à¦Ÿà¦¾ করছে +Comment[bs]=Prozor na drugom neaktivnom desktopu zahtijeva pažnju +Comment[ca]=Una finestra d'un escriptori virtual inactiu demana atenció +Comment[cs]=Okno na neaktivnà virtuálnà ploÅ¡e vyžaduje vaÅ¡i pozornost +Comment[csb]=Ã’kno na jinszëm pùlce domôgô sã bôczëniô na se +Comment[da]=Vindue pÃ¥ anden desktop kræver opmærksomhed +Comment[de]=Ein Fenster auf einer virtuellen Arbeitsfläche erfordert Ihre Aufmerksamkeit +Comment[el]=Ένα παÏάθυÏο σε μη ενεÏγή επιφάνεια εÏγασίας απαιτεί την Ï€Ïοσοχή σας +Comment[eo]=Fenestro sur neaktiva virtual tabulo petas atenton +Comment[es]=Una ventana de otro escritorio requiere atención +Comment[et]=Aken mitteaktiivsel virtuaalsel töölaual nõuab tähelepanu +Comment[eu]=Mahaigain birtual inaktiboko leihoa jaramon egitea eskatzen du +Comment[fa]=یک پنجره روی رومیزی مجازی Ùعال نیاز به توجه دارد +Comment[fi]=Ikkuna ei-aktiivisella virtuaalisella työpöydällä kaipaa huomiota +Comment[fr]=Une fenêtre sur un bureau virtuel inactif demande l'attention +Comment[fy]=In finster op in ynaktyf firtueel buroblêd freget om oandacht +Comment[gl]=Unha fiestra nun escritório virtual inactivo requere atención +Comment[he]=חלון בשולחן עבודה ×חר דורש תשומת לב +Comment[hr]=Prozor na neaktivnoj virtualnoj radnoj povrÅ¡ini zahtijeva pažnju +Comment[hu]=Egy ablak beavatkozást igényel egy másik asztalon +Comment[is]=Gluggi á óvirku sýndarskjáborði krefst athygli +Comment[it]=Una finestra un desktop virtuale diverso da quello corrente sta richiedendo l'attenzione +Comment[ja]=éžã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªä»®æƒ³ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—上ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒæ³¨æ„を促ã—ã¦ã„ã¾ã™ +Comment[kk]=БаÑқа виртуалды Ò¯Ñтелдегі терезе назар аударуын Ñұрайды +Comment[km]=បង្អួច​នៅ​លើ​ផ្ទៃážáž»â€‹áž“ិម្មិážâ€‹áž¢ážŸáž€áž˜áŸ’ម ទាមទារ​ការ​ប្រុង​ប្រយáŸážáŸ’áž“ +Comment[ko]=비활성 ê°€ìƒ ë°ìŠ¤í¬í†±ì˜ ì°½ì´ ì‘ë‹µì„ ê¸°ë‹¤ë¦¼ +Comment[lt]=Langas neaktyviame virtualiame darbastalyje reikalauja dÄ—mesio +Comment[mk]=Ðекој прозорец на неактивна виртуелна површина бара внимание +Comment[nb]=Et vindu pÃ¥ et annet skrivebord pÃ¥kaller oppmerksomheten +Comment[nds]=En Finster op en inaktiven virtuellen Schriefdischen bruukt Acht +Comment[ne]=निसà¥à¤•à¥à¤°à¤¿à¤¯ अवासà¥à¤¤à¤µà¤¿à¤• डेसà¥à¤•à¤Ÿà¤ªà¤®à¤¾ सञà¥à¤à¥à¤¯à¤¾à¤²à¤²à¥‡ साबधानी अनà¥à¤°à¥‹à¤§ गरà¥à¤› +Comment[nl]=Een venster op een inactief virtueel bureaublad vraagt om aandacht +Comment[nn]=Eit vindauge pÃ¥ eit anna virtuelt skrivebord pÃ¥kallar merksemd. +Comment[pa]=ਨਾ-ਸਰਗਰਮ ਵੇਹੜੇ ਵਿਚਲੇ à¨à¨°à©‹à¨–ੇ ਨੂੰ ਧਿਆਨ ਦੀ ਲੋੜ ਹੈ +Comment[pl]=Okno na innym, nieaktywnym pulpicie domaga siÄ™ zwrócenia na nie uwagi +Comment[pt]=Uma janela num ecrã inactivo pediu a atenção do utilizador +Comment[pt_BR]=Uma janela em uma área de trabalho inativa demanda atenção +Comment[ro]=O fereastră pe un ecran virtual inactiv necesită atenÈ›ie +Comment[ru]=Произошло Ñобытие, требующее вашего вниманиÑ, в окне на другом рабочем Ñтоле +Comment[rw]=Idirishya ku biro bitaboneka bidakora bisaba ubwitonzi +Comment[sk]=Okno na neaktÃvnu virtuálnu pracovnú plochu +Comment[sl]=Okno na nedejavnem navideznem namizju potrebuje pozornost +Comment[sr]=Прозор на неактивној виртуелној радној површини захтева пажњу +Comment[sr@Latn]=Prozor na neaktivnoj virtuelnoj radnoj povrÅ¡ini zahteva pažnju +Comment[sv]=Ett fönster pÃ¥ ett inaktivt skrivbord kräver uppmärksamhet +Comment[th]=หน้าต่างบนพื้นที่หน้าจà¸à¹€à¸ªà¸¡à¸·à¸à¸™à¸—ี่ไม่ได้à¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ต้à¸à¸‡à¸à¸²à¸£à¸„วามสนใจ +Comment[tt]=BaÅŸqa östäldä bulÄŸan täräzä iÄŸtibar birüne sorà +Comment[uk]=Вікно на неактивній віртуальній Ñтільниці потребує уваги +Comment[vi]=Má»™t cá»a sổ trên mà n hình ná»n khác Ä‘ang cần được bạn quan tâm đến +Comment[wa]=On purnea hÃ¥yné so-z on forveyou scribannes nén en alaedje dimande voste atincion +Comment[zh_CN]=éžæ¿€æ´»çš„虚拟桌é¢ä¸Šçš„窗å£è¯·æ±‚æ³¨æ„ +Comment[zh_TW]=一個在éžä½œç”¨ä¸è™›æ“¬æ¡Œé¢ä¸Šçš„視窗è¦æ±‚焦點 +default_presentation=64 diff --git a/kwin/geometry.cpp b/kwin/geometry.cpp new file mode 100644 index 000000000..7c64eadcf --- /dev/null +++ b/kwin/geometry.cpp @@ -0,0 +1,2589 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to geometry, i.e. workspace size, + window positions and window sizes. + +*/ + +#include "client.h" +#include "workspace.h" + +#include <kapplication.h> +#include <kglobal.h> +#include <qpainter.h> +#include <kwin.h> + +#include "placement.h" +#include "notifications.h" +#include "geometrytip.h" +#include "rules.h" + +extern Time qt_x_time; + +namespace KWinInternal +{ + +//******************************************** +// Workspace +//******************************************** + +/*! + Resizes the workspace after an XRANDR screen size change + */ +void Workspace::desktopResized() + { + QRect geom = QApplication::desktop()->geometry(); + NETSize desktop_geometry; + desktop_geometry.width = geom.width(); + desktop_geometry.height = geom.height(); + rootInfo->setDesktopGeometry( -1, desktop_geometry ); + + updateClientArea(); + checkElectricBorders( true ); + } + +/*! + Updates the current client areas according to the current clients. + + If the area changes or force is true, the new areas are propagated to the world. + + The client area is the area that is available for clients (that + which is not taken by windows like panels, the top-of-screen menu + etc). + + \sa clientArea() + */ + +void Workspace::updateClientArea( bool force ) + { + QDesktopWidget *desktopwidget = KApplication::desktop(); + int nscreens = desktopwidget -> numScreens (); +// kdDebug () << "screens: " << nscreens << endl; + QRect* new_wareas = new QRect[ numberOfDesktops() + 1 ]; + QRect** new_sareas = new QRect*[ numberOfDesktops() + 1]; + QRect* screens = new QRect [ nscreens ]; + QRect desktopArea = desktopwidget -> geometry (); + for( int iS = 0; + iS < nscreens; + iS ++ ) + { + screens [iS] = desktopwidget -> screenGeometry (iS); + } + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + { + new_wareas[ i ] = desktopArea; + new_sareas[ i ] = new QRect [ nscreens ]; + for( int iS = 0; + iS < nscreens; + iS ++ ) + new_sareas[ i ][ iS ] = screens[ iS ]; + } + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + if( !(*it)->hasStrut()) + continue; + QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea ); + if( (*it)->isOnAllDesktops()) + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + { + new_wareas[ i ] = new_wareas[ i ].intersect( r ); + for( int iS = 0; + iS < nscreens; + iS ++ ) + new_sareas[ i ][ iS ] = + new_sareas[ i ][ iS ].intersect( + (*it)->adjustedClientArea( desktopArea, screens[ iS ] ) + ); + } + else + { + new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersect( r ); + for( int iS = 0; + iS < nscreens; + iS ++ ) + { +// kdDebug () << "adjusting new_sarea: " << screens[ iS ] << endl; + new_sareas[ (*it)->desktop() ][ iS ] = + new_sareas[ (*it)->desktop() ][ iS ].intersect( + (*it)->adjustedClientArea( desktopArea, screens[ iS ] ) + ); + } + } + } +#if 0 + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + { + for( int iS = 0; + iS < nscreens; + iS ++ ) + kdDebug () << "new_sarea: " << new_sareas[ i ][ iS ] << endl; + } +#endif + // TODO topmenu update for screenarea changes? + if( topmenu_space != NULL ) + { + QRect topmenu_area = desktopArea; + topmenu_area.setTop( topMenuHeight()); + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + new_wareas[ i ] = new_wareas[ i ].intersect( topmenu_area ); + } + + bool changed = force; + + if (! screenarea) + changed = true; + + for( int i = 1; + !changed && i <= numberOfDesktops(); + ++i ) + { + if( workarea[ i ] != new_wareas[ i ] ) + changed = true; + for( int iS = 0; + iS < nscreens; + iS ++ ) + if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ]) + changed = true; + } + + if ( changed ) + { + delete[] workarea; + workarea = new_wareas; + new_wareas = NULL; + delete[] screenarea; + screenarea = new_sareas; + new_sareas = NULL; + NETRect r; + for( int i = 1; i <= numberOfDesktops(); i++) + { + r.pos.x = workarea[ i ].x(); + r.pos.y = workarea[ i ].y(); + r.size.width = workarea[ i ].width(); + r.size.height = workarea[ i ].height(); + rootInfo->setWorkArea( i, r ); + } + + updateTopMenuGeometry(); + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + (*it)->checkWorkspacePosition(); + for( ClientList::ConstIterator it = desktops.begin(); + it != desktops.end(); + ++it) + (*it)->checkWorkspacePosition(); + } + delete[] screens; + delete[] new_sareas; + delete[] new_wareas; + } + +void Workspace::updateClientArea() + { + updateClientArea( false ); + } + + +/*! + returns the area available for clients. This is the desktop + geometry minus windows on the dock. Placement algorithms should + refer to this rather than geometry(). + + \sa geometry() + */ +QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const + { + if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 ) + desktop = currentDesktop(); + QDesktopWidget *desktopwidget = KApplication::desktop(); + int screen = desktopwidget->isVirtualDesktop() ? desktopwidget->screenNumber( p ) : desktopwidget->primaryScreen(); + if( screen < 0 ) + screen = desktopwidget->primaryScreen(); + QRect sarea = screenarea // may be NULL during KWin initialization + ? screenarea[ desktop ][ screen ] + : desktopwidget->screenGeometry( screen ); + QRect warea = workarea[ desktop ].isNull() + ? QApplication::desktop()->geometry() + : workarea[ desktop ]; + switch (opt) + { + case MaximizeArea: + if (options->xineramaMaximizeEnabled) + return sarea; + else + return warea; + case MaximizeFullArea: + if (options->xineramaMaximizeEnabled) + return desktopwidget->screenGeometry( screen ); + else + return desktopwidget->geometry(); + case FullScreenArea: + if (options->xineramaFullscreenEnabled) + return desktopwidget->screenGeometry( screen ); + else + return desktopwidget->geometry(); + case PlacementArea: + if (options->xineramaPlacementEnabled) + return sarea; + else + return warea; + case MovementArea: + if (options->xineramaMovementEnabled) + return desktopwidget->screenGeometry( screen ); + else + return desktopwidget->geometry(); + case WorkArea: + return warea; + case FullArea: + return desktopwidget->geometry(); + case ScreenArea: + return desktopwidget->screenGeometry( screen ); + } + assert( false ); + return QRect(); + } + +QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const + { + return clientArea( opt, c->geometry().center(), c->desktop()); + } + +/*! + Client \a c is moved around to position \a pos. This gives the + workspace the opportunity to interveniate and to implement + snap-to-windows functionality. + */ +QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) + { + //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone + //CT adapted for kwin on 25Nov1999 + //aleXXX 02Nov2000 added second snapping mode + if (options->windowSnapZone || options->borderSnapZone ) + { + const bool sOWO=options->snapOnlyWhenOverlapping; + const QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop()); + const int xmin = maxRect.left(); + const int xmax = maxRect.right()+1; //desk size + const int ymin = maxRect.top(); + const int ymax = maxRect.bottom()+1; + + const int cx(pos.x()); + const int cy(pos.y()); + const int cw(c->width()); + const int ch(c->height()); + const int rx(cx+cw); + const int ry(cy+ch); //these don't change + + int nx(cx), ny(cy); //buffers + int deltaX(xmax); + int deltaY(ymax); //minimum distance to other clients + + int lx, ly, lrx, lry; //coords and size for the comparison client, l + + // border snap + int snap = options->borderSnapZone; //snap trigger + if (snap) + { + if ((sOWO?(cx<xmin):true) && (QABS(xmin-cx)<snap)) + { + deltaX = xmin-cx; + nx = xmin; + } + if ((sOWO?(rx>xmax):true) && (QABS(rx-xmax)<snap) && (QABS(xmax-rx) < deltaX)) + { + deltaX = rx-xmax; + nx = xmax - cw; + } + + if ((sOWO?(cy<ymin):true) && (QABS(ymin-cy)<snap)) + { + deltaY = ymin-cy; + ny = ymin; + } + if ((sOWO?(ry>ymax):true) && (QABS(ry-ymax)<snap) && (QABS(ymax-ry) < deltaY)) + { + deltaY =ry-ymax; + ny = ymax - ch; + } + } + + // windows snap + snap = options->windowSnapZone; + if (snap) + { + QValueList<Client *>::ConstIterator l; + for (l = clients.begin();l != clients.end();++l ) + { + if ((*l)->isOnDesktop(currentDesktop()) && + !(*l)->isMinimized() + && (*l) != c ) + { + lx = (*l)->x(); + ly = (*l)->y(); + lrx = lx + (*l)->width(); + lry = ly + (*l)->height(); + + if ( (( cy <= lry ) && ( cy >= ly )) || + (( ry >= ly ) && ( ry <= lry )) || + (( cy <= ly ) && ( ry >= lry )) ) + { + if ((sOWO?(cx<lrx):true) && (QABS(lrx-cx)<snap) && ( QABS(lrx -cx) < deltaX) ) + { + deltaX = QABS( lrx - cx ); + nx = lrx; + } + if ((sOWO?(rx>lx):true) && (QABS(rx-lx)<snap) && ( QABS( rx - lx )<deltaX) ) + { + deltaX = QABS(rx - lx); + nx = lx - cw; + } + } + + if ( (( cx <= lrx ) && ( cx >= lx )) || + (( rx >= lx ) && ( rx <= lrx )) || + (( cx <= lx ) && ( rx >= lrx )) ) + { + if ((sOWO?(cy<lry):true) && (QABS(lry-cy)<snap) && (QABS( lry -cy ) < deltaY)) + { + deltaY = QABS( lry - cy ); + ny = lry; + } + //if ( (QABS( ry-ly ) < snap) && (QABS( ry - ly ) < deltaY )) + if ((sOWO?(ry>ly):true) && (QABS(ry-ly)<snap) && (QABS( ry - ly ) < deltaY )) + { + deltaY = QABS( ry - ly ); + ny = ly - ch; + } + } + } + } + } + pos = QPoint(nx, ny); + } + return pos; + } + +QRect Workspace::adjustClientSize( Client* c, QRect moveResizeGeom, int mode ) + { + //adapted from adjustClientPosition on 29May2004 + //this function is called when resizing a window and will modify + //the new dimensions to snap to other windows/borders if appropriate + if ( options->windowSnapZone || options->borderSnapZone ) + { + const bool sOWO=options->snapOnlyWhenOverlapping; + + const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop()); + const int xmin = maxRect.left(); + const int xmax = maxRect.right(); //desk size + const int ymin = maxRect.top(); + const int ymax = maxRect.bottom(); + + const int cx(moveResizeGeom.left()); + const int cy(moveResizeGeom.top()); + const int rx(moveResizeGeom.right()); + const int ry(moveResizeGeom.bottom()); + + int newcx(cx), newcy(cy); //buffers + int newrx(rx), newry(ry); + int deltaX(xmax); + int deltaY(ymax); //minimum distance to other clients + + int lx, ly, lrx, lry; //coords and size for the comparison client, l + + // border snap + int snap = options->borderSnapZone; //snap trigger + if (snap) + { + deltaX = int(snap); + deltaY = int(snap); + +#define SNAP_BORDER_TOP \ + if ((sOWO?(newcy<ymin):true) && (QABS(ymin-newcy)<deltaY)) \ + { \ + deltaY = QABS(ymin-newcy); \ + newcy = ymin; \ + } + +#define SNAP_BORDER_BOTTOM \ + if ((sOWO?(newry>ymax):true) && (QABS(ymax-newry)<deltaY)) \ + { \ + deltaY = QABS(ymax-newcy); \ + newry = ymax; \ + } + +#define SNAP_BORDER_LEFT \ + if ((sOWO?(newcx<xmin):true) && (QABS(xmin-newcx)<deltaX)) \ + { \ + deltaX = QABS(xmin-newcx); \ + newcx = xmin; \ + } + +#define SNAP_BORDER_RIGHT \ + if ((sOWO?(newrx>xmax):true) && (QABS(xmax-newrx)<deltaX)) \ + { \ + deltaX = QABS(xmax-newrx); \ + newrx = xmax; \ + } + switch ( mode ) + { + case PositionBottomRight: + SNAP_BORDER_BOTTOM + SNAP_BORDER_RIGHT + break; + case PositionRight: + SNAP_BORDER_RIGHT + break; + case PositionBottom: + SNAP_BORDER_BOTTOM + break; + case PositionTopLeft: + SNAP_BORDER_TOP + SNAP_BORDER_LEFT + break; + case PositionLeft: + SNAP_BORDER_LEFT + break; + case PositionTop: + SNAP_BORDER_TOP + break; + case PositionTopRight: + SNAP_BORDER_TOP + SNAP_BORDER_RIGHT + break; + case PositionBottomLeft: + SNAP_BORDER_BOTTOM + SNAP_BORDER_LEFT + break; + default: + assert( false ); + break; + } + + + } + + // windows snap + snap = options->windowSnapZone; + if (snap) + { + deltaX = int(snap); + deltaY = int(snap); + QValueList<Client *>::ConstIterator l; + for (l = clients.begin();l != clients.end();++l ) + { + if ((*l)->isOnDesktop(currentDesktop()) && + !(*l)->isMinimized() + && (*l) != c ) + { + lx = (*l)->x()-1; + ly = (*l)->y()-1; + lrx =(*l)->x() + (*l)->width(); + lry =(*l)->y() + (*l)->height(); + +#define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy >= ly )) || \ + (( newry >= ly ) && ( newry <= lry )) || \ + (( newcy <= ly ) && ( newry >= lry )) ) + +#define WITHIN_WIDTH ( (( cx <= lrx ) && ( cx >= lx )) || \ + (( rx >= lx ) && ( rx <= lrx )) || \ + (( cx <= lx ) && ( rx >= lrx )) ) + +#define SNAP_WINDOW_TOP if ( (sOWO?(newcy<lry):true) \ + && WITHIN_WIDTH \ + && (QABS( lry - newcy ) < deltaY) ) { \ + deltaY = QABS( lry - newcy ); \ + newcy=lry; \ + } + +#define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \ + && WITHIN_WIDTH \ + && (QABS( ly - newry ) < deltaY) ) { \ + deltaY = QABS( ly - newry ); \ + newry=ly; \ + } + +#define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \ + && WITHIN_HEIGHT \ + && (QABS( lrx - newcx ) < deltaX)) { \ + deltaX = QABS( lrx - newcx ); \ + newcx=lrx; \ + } + +#define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \ + && WITHIN_HEIGHT \ + && (QABS( lx - newrx ) < deltaX)) \ + { \ + deltaX = QABS( lx - newrx ); \ + newrx=lx; \ + } + + switch ( mode ) + { + case PositionBottomRight: + SNAP_WINDOW_BOTTOM + SNAP_WINDOW_RIGHT + break; + case PositionRight: + SNAP_WINDOW_RIGHT + break; + case PositionBottom: + SNAP_WINDOW_BOTTOM + break; + case PositionTopLeft: + SNAP_WINDOW_TOP + SNAP_WINDOW_LEFT + break; + case PositionLeft: + SNAP_WINDOW_LEFT + break; + case PositionTop: + SNAP_WINDOW_TOP + break; + case PositionTopRight: + SNAP_WINDOW_TOP + SNAP_WINDOW_RIGHT + break; + case PositionBottomLeft: + SNAP_WINDOW_BOTTOM + SNAP_WINDOW_LEFT + break; + default: + assert( false ); + break; + } + } + } + } + moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry)); + } + return moveResizeGeom; + } + +/*! + Marks the client as being moved around by the user. + */ +void Workspace::setClientIsMoving( Client *c ) + { + Q_ASSERT(!c || !movingClient); // Catch attempts to move a second + // window while still moving the first one. + movingClient = c; + if (movingClient) + ++block_focus; + else + --block_focus; + } + +/*! + Cascades all clients on the current desktop + */ +void Workspace::cascadeDesktop() + { +// TODO XINERAMA this probably is not right for xinerama + Q_ASSERT( block_stacking_updates == 0 ); + ClientList::ConstIterator it(stackingOrder().begin()); + initPositioning->reinitCascading( currentDesktop()); + QRect area = clientArea( PlacementArea, QPoint( 0, 0 ), currentDesktop()); + for (; it != stackingOrder().end(); ++it) + { + if((!(*it)->isOnDesktop(currentDesktop())) || + ((*it)->isMinimized()) || + ((*it)->isOnAllDesktops()) || + (!(*it)->isMovable()) ) + continue; + initPositioning->placeCascaded(*it, area); + } + } + +/*! + Unclutters the current desktop by smart-placing all clients + again. + */ +void Workspace::unclutterDesktop() + { + ClientList::Iterator it(clients.fromLast()); + for (; it != clients.end(); --it) + { + if((!(*it)->isOnDesktop(currentDesktop())) || + ((*it)->isMinimized()) || + ((*it)->isOnAllDesktops()) || + (!(*it)->isMovable()) ) + continue; + initPositioning->placeSmart(*it, QRect()); + } + } + + +void Workspace::updateTopMenuGeometry( Client* c ) + { + if( !managingTopMenus()) + return; + if( c != NULL ) + { + XEvent ev; + ev.xclient.display = qt_xdisplay(); + ev.xclient.type = ClientMessage; + ev.xclient.window = c->window(); + static Atom msg_type_atom = XInternAtom( qt_xdisplay(), "_KDE_TOPMENU_MINSIZE", False ); + ev.xclient.message_type = msg_type_atom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = qt_x_time; + ev.xclient.data.l[1] = topmenu_space->width(); + ev.xclient.data.l[2] = topmenu_space->height(); + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + XSendEvent( qt_xdisplay(), c->window(), False, NoEventMask, &ev ); + KWin::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know + c->checkWorkspacePosition(); + return; + } + // c == NULL - update all, including topmenu_space + QRect area; + area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ? + area.setHeight( topMenuHeight()); + topmenu_space->setGeometry( area ); + for( ClientList::ConstIterator it = topmenus.begin(); + it != topmenus.end(); + ++it ) + updateTopMenuGeometry( *it ); + } + +//******************************************** +// Client +//******************************************** + + +void Client::keepInArea( QRect area, bool partial ) + { + if( partial ) + { + // increase the area so that can have only 100 pixels in the area + area.setLeft( QMIN( area.left() - width() + 100, area.left())); + area.setTop( QMIN( area.top() - height() + 100, area.top())); + area.setRight( QMAX( area.right() + width() - 100, area.right())); + area.setBottom( QMAX( area.bottom() + height() - 100, area.bottom())); + } + if ( geometry().right() > area.right() && width() < area.width() ) + move( area.right() - width(), y() ); + if ( geometry().bottom() > area.bottom() && height() < area.height() ) + move( x(), area.bottom() - height() ); + if( !area.contains( geometry().topLeft() )) + { + int tx = x(); + int ty = y(); + if ( tx < area.x() ) + tx = area.x(); + if ( ty < area.y() ) + ty = area.y(); + move( tx, ty ); + } + } + +/*! + Returns \a area with the client's strut taken into account. + + Used from Workspace in updateClientArea. + */ +// TODO move to Workspace? + +QRect Client::adjustedClientArea( const QRect &desktopArea, const QRect& area ) const + { + QRect r = area; + // topmenu area is reserved in updateClientArea() + if( isTopMenu()) + return r; + NETExtendedStrut str = strut(); + QRect stareaL = QRect( + 0, + str . left_start, + str . left_width, + str . left_end - str . left_start + 1 ); + QRect stareaR = QRect ( + desktopArea . right () - str . right_width + 1, + str . right_start, + str . right_width, + str . right_end - str . right_start + 1 ); + QRect stareaT = QRect ( + str . top_start, + 0, + str . top_end - str . top_start + 1, + str . top_width); + QRect stareaB = QRect ( + str . bottom_start, + desktopArea . bottom () - str . bottom_width + 1, + str . bottom_end - str . bottom_start + 1, + str . bottom_width); + + NETExtendedStrut ext = info->extendedStrut(); + if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 + && ( str.left_width != 0 || str.right_width != 0 || str.top_width != 0 || str.bottom_width != 0 )) { + + // hack, might cause problems... this tries to guess the start/end of a + // non-extended strut; only works on windows that have exact same + // geometry as their strut (ie, if the geometry fits the width + // exactly, we will adjust length of strut to match the geometry as well; + // otherwise we use the full-edge strut) + + if (stareaT.top() == geometry().top() && stareaT.bottom() == geometry().bottom()) { + stareaT.setLeft(geometry().left()); + stareaT.setRight(geometry().right()); +// kdDebug () << "Trimming top-strut to geometry() to: " << stareaT << endl; + } + if (stareaB.top() == geometry().top() && stareaB.bottom() == geometry().bottom()) { + stareaB.setLeft(geometry().left()); + stareaB.setRight(geometry().right()); +// kdDebug () << "Trimming bottom-strut to geometry(): " << stareaB << endl; + } + if (stareaL.left() == geometry().left() && stareaL.right() == geometry().right()) { + stareaL.setTop(geometry().top()); + stareaL.setBottom(geometry().bottom()); +// kdDebug () << "Trimming left-strut to geometry(): " << stareaL << endl; + } + if (stareaR.left() == geometry().left() && stareaR.right() == geometry().right()) { + stareaR.setTop(geometry().top()); + stareaR.setBottom(geometry().bottom()); +// kdDebug () << "Trimming right-strut to geometry(): " << stareaR << endl; + } + } + + QRect screenarea = workspace()->clientArea( ScreenArea, this ); + // HACK: workarea handling is not xinerama aware, so if this strut + // reserves place at a xinerama edge that's inside the virtual screen, + // ignore the strut for workspace setting. + if( area == kapp->desktop()->geometry()) + { + if( stareaL.left() < screenarea.left()) + stareaL = QRect(); + if( stareaR.right() > screenarea.right()) + stareaR = QRect(); + if( stareaT.top() < screenarea.top()) + stareaT = QRect(); + if( stareaB.bottom() < screenarea.bottom()) + stareaB = QRect(); + } + // Handle struts at xinerama edges that are inside the virtual screen. + // They're given in virtual screen coordinates, make them affect only + // their xinerama screen. + stareaL.setLeft( KMAX( stareaL.left(), screenarea.left())); + stareaR.setRight( KMIN( stareaR.right(), screenarea.right())); + stareaT.setTop( KMAX( stareaT.top(), screenarea.top())); + stareaB.setBottom( KMIN( stareaB.bottom(), screenarea.bottom())); + + if (stareaL . intersects (area)) { +// kdDebug () << "Moving left of: " << r << " to " << stareaL.right() + 1 << endl; + r . setLeft( stareaL . right() + 1 ); + } + if (stareaR . intersects (area)) { +// kdDebug () << "Moving right of: " << r << " to " << stareaR.left() - 1 << endl; + r . setRight( stareaR . left() - 1 ); + } + if (stareaT . intersects (area)) { +// kdDebug () << "Moving top of: " << r << " to " << stareaT.bottom() + 1 << endl; + r . setTop( stareaT . bottom() + 1 ); + } + if (stareaB . intersects (area)) { +// kdDebug () << "Moving bottom of: " << r << " to " << stareaB.top() - 1 << endl; + r . setBottom( stareaB . top() - 1 ); + } + return r; + } + +NETExtendedStrut Client::strut() const + { + NETExtendedStrut ext = info->extendedStrut(); + NETStrut str = info->strut(); + if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 + && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 )) + { + // build extended from simple + if( str.left != 0 ) + { + ext.left_width = str.left; + ext.left_start = 0; + ext.left_end = XDisplayHeight( qt_xdisplay(), DefaultScreen( qt_xdisplay())); + } + if( str.right != 0 ) + { + ext.right_width = str.right; + ext.right_start = 0; + ext.right_end = XDisplayHeight( qt_xdisplay(), DefaultScreen( qt_xdisplay())); + } + if( str.top != 0 ) + { + ext.top_width = str.top; + ext.top_start = 0; + ext.top_end = XDisplayWidth( qt_xdisplay(), DefaultScreen( qt_xdisplay())); + } + if( str.bottom != 0 ) + { + ext.bottom_width = str.bottom; + ext.bottom_start = 0; + ext.bottom_end = XDisplayWidth( qt_xdisplay(), DefaultScreen( qt_xdisplay())); + } + } + return ext; + } + +bool Client::hasStrut() const + { + NETExtendedStrut ext = strut(); + if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 ) + return false; + return true; + } + + +// updates differences to workarea edges for all directions +void Client::updateWorkareaDiffs() + { + QRect area = workspace()->clientArea( WorkArea, this ); + QRect geom = geometry(); + workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right()); + workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom()); + } + +// If the client was inside workarea in the x direction, and if it was close to the left/right +// edge, return the distance from the left/right edge (negative for left, positive for right) +// INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'. +// In order to recognize 'at the left workarea edge' from 'at the right workarea edge' +// (i.e. negative vs positive zero), the distances are one larger in absolute value than they +// really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy +// to rewrite it just to make it nicer. If this will ever get touched again, perhaps then. +// the y direction is done the same, just the values will be rotated: top->left, bottom->right +int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right ) + { + int left_diff = left - a_left; + int right_diff = a_right - right; + if( left_diff < 0 || right_diff < 0 ) + return INT_MIN; + else // fully inside workarea in this direction direction + { + // max distance from edge where it's still considered to be close and is kept at that distance + int max_diff = ( a_right - a_left ) / 10; + if( left_diff < right_diff ) + return left_diff < max_diff ? -left_diff - 1 : INT_MAX; + else if( left_diff > right_diff ) + return right_diff < max_diff ? right_diff + 1 : INT_MAX; + return INT_MAX; // not close to workarea edge + } + } + +void Client::checkWorkspacePosition() + { + if( isDesktop()) + { + QRect area = workspace()->clientArea( FullArea, this ); + if( geometry() != area ) + setGeometry( area ); + return; + } + if( maximizeMode() != MaximizeRestore ) + // TODO update geom_restore? + changeMaximize( false, false, true ); // adjust size + + if( isFullScreen()) + { + QRect area = workspace()->clientArea( FullScreenArea, this ); + if( geometry() != area ) + setGeometry( area ); + return; + } + if( isDock()) + return; + if( isTopMenu()) + { + if( workspace()->managingTopMenus()) + { + QRect area; + ClientList mainclients = mainClients(); + if( mainclients.count() == 1 ) + area = workspace()->clientArea( MaximizeFullArea, mainclients.first()); + else + area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop()); + area.setHeight( workspace()->topMenuHeight()); +// kdDebug() << "TOPMENU size adjust: " << area << ":" << this << endl; + setGeometry( area ); + } + return; + } + + if( !isShade()) // TODO + { + int old_diff_x = workarea_diff_x; + int old_diff_y = workarea_diff_y; + updateWorkareaDiffs(); + + // this can be true only if this window was mapped before KWin + // was started - in such case, don't adjust position to workarea, + // because the window already had its position, and if a window + // with a strut altering the workarea would be managed in initialization + // after this one, this window would be moved + if( workspace()->initializing()) + return; + + QRect area = workspace()->clientArea( WorkArea, this ); + QRect new_geom = geometry(); + QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 ); + QRect tmp_area_x( area.left(), 0, area.width(), 0 ); + checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x ); + // the x<->y swapping + QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 ); + QRect tmp_area_y( area.top(), 0, area.height(), 0 ); + checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y ); + new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width()); + QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size())); + if( final_geom != new_geom ) // size increments, or size restrictions + { // adjusted size differing matters only for right and bottom edge + if( old_diff_x != INT_MAX && old_diff_x > 0 ) + final_geom.moveRight( area.right() - ( old_diff_x - 1 )); + if( old_diff_y != INT_MAX && old_diff_y > 0 ) + final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 )); + } + if( final_geom != geometry() ) + setGeometry( final_geom ); + // updateWorkareaDiffs(); done already by setGeometry() + } + } + +// Try to be smart about keeping the clients visible. +// If the client was fully inside the workspace before, try to keep +// it still inside the workarea, possibly moving it or making it smaller if possible, +// and try to keep the distance from the nearest workarea edge. +// On the other hand, it it was partially moved outside of the workspace in some direction, +// don't do anything with that direction if it's still at least partially visible. If it's +// not visible anymore at all, make sure it's visible at least partially +// again (not fully, as that could(?) be potentionally annoying) by +// moving it slightly inside the workarea (those '+ 5'). +// Again, this is done for the x direction, y direction will be done by x<->y swapping +void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ) + { + if( old_diff != INT_MIN ) // was inside workarea + { + if( old_diff == INT_MAX ) // was in workarea, but far from edge + { + if( new_diff == INT_MIN ) // is not anymore fully in workarea + { + rect.setLeft( area.left()); + rect.setRight( area.right()); + } + return; + } + if( isMovable()) + { + if( old_diff < 0 ) // was in left third, keep distance from left edge + rect.moveLeft( area.left() + ( -old_diff - 1 )); + else // old_diff > 0 // was in right third, keep distance from right edge + rect.moveRight( area.right() - ( old_diff - 1 )); + } + else if( isResizable()) + { + if( old_diff < 0 ) + rect.setLeft( area.left() + ( -old_diff - 1 ) ); + else // old_diff > 0 + rect.setRight( area.right() - ( old_diff - 1 )); + } + if( rect.width() > area.width() && isResizable()) + rect.setWidth( area.width()); + if( isMovable()) + { + if( rect.left() < area.left()) + rect.moveLeft( area.left()); + else if( rect.right() > area.right()) + rect.moveRight( area.right()); + } + } + if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 ) + { // not visible (almost) at all - try to make it at least partially visible + if( isMovable()) + { + if( rect.left() < area.left() + 5 ) + rect.moveRight( area.left() + 5 ); + if( rect.right() > area.right() - 5 ) + rect.moveLeft( area.right() - 5 ); + } + } + } + +/*! + Adjust the frame size \a frame according to he window's size hints. + */ +QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const + { + // first, get the window size for the given frame size s + + QSize wsize( frame.width() - ( border_left + border_right ), + frame.height() - ( border_top + border_bottom )); + if( wsize.isEmpty()) + wsize = QSize( 1, 1 ); + + return sizeForClientSize( wsize, mode, false ); + } + +// this helper returns proper size even if the window is shaded +// see also the comment in Client::setGeometry() +QSize Client::adjustedSize() const + { + return sizeForClientSize( clientSize()); + } + +/*! + Calculate the appropriate frame size for the given client size \a + wsize. + + \a wsize is adapted according to the window's size hints (minimum, + maximum and incremental size changes). + + */ +QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode, bool noframe ) const + { + int w = wsize.width(); + int h = wsize.height(); + if( w < 1 || h < 1 ) + { + kdWarning() << "sizeForClientSize() with empty size!" << endl; + kdWarning() << kdBacktrace() << endl; + } + if (w<1) w = 1; + if (h<1) h = 1; + + // basesize, minsize, maxsize, paspect and resizeinc have all values defined, + // even if they're not set in flags - see getWmNormalHints() + QSize min_size = minSize(); + QSize max_size = maxSize(); + if( decoration != NULL ) + { + QSize decominsize = decoration->minimumSize(); + QSize border_size( border_left + border_right, border_top + border_bottom ); + if( border_size.width() > decominsize.width()) // just in case + decominsize.setWidth( border_size.width()); + if( border_size.height() > decominsize.height()) + decominsize.setHeight( border_size.height()); + if( decominsize.width() > min_size.width()) + min_size.setWidth( decominsize.width()); + if( decominsize.height() > min_size.height()) + min_size.setHeight( decominsize.height()); + } + w = QMIN( max_size.width(), w ); + h = QMIN( max_size.height(), h ); + w = QMAX( min_size.width(), w ); + h = QMAX( min_size.height(), h ); + + int w1 = w; + int h1 = h; + int width_inc = xSizeHint.width_inc; + int height_inc = xSizeHint.height_inc; + int basew_inc = xSizeHint.min_width; // see getWmNormalHints() + int baseh_inc = xSizeHint.min_height; + w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc; + h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc; +// code for aspect ratios based on code from FVWM + /* + * The math looks like this: + * + * minAspectX dwidth maxAspectX + * ---------- <= ------- <= ---------- + * minAspectY dheight maxAspectY + * + * If that is multiplied out, then the width and height are + * invalid in the following situations: + * + * minAspectX * dheight > minAspectY * dwidth + * maxAspectX * dheight < maxAspectY * dwidth + * + */ + if( xSizeHint.flags & PAspect ) + { + double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT + double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise + double max_aspect_w = xSizeHint.max_aspect.x; + double max_aspect_h = xSizeHint.max_aspect.y; + // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments, + // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time, + // and I have no idea how it works, let's hope nobody relies on that. + w -= xSizeHint.base_width; + h -= xSizeHint.base_height; + int max_width = max_size.width() - xSizeHint.base_width; + int min_width = min_size.width() - xSizeHint.base_width; + int max_height = max_size.height() - xSizeHint.base_height; + int min_height = min_size.height() - xSizeHint.base_height; +#define ASPECT_CHECK_GROW_W \ + if( min_aspect_w * h > min_aspect_h * w ) \ + { \ + int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \ + if( w + delta <= max_width ) \ + w += delta; \ + } +#define ASPECT_CHECK_SHRINK_H_GROW_W \ + if( min_aspect_w * h > min_aspect_h * w ) \ + { \ + int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \ + if( h - delta >= min_height ) \ + h -= delta; \ + else \ + { \ + int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \ + if( w + delta <= max_width ) \ + w += delta; \ + } \ + } +#define ASPECT_CHECK_GROW_H \ + if( max_aspect_w * h < max_aspect_h * w ) \ + { \ + int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \ + if( h + delta <= max_height ) \ + h += delta; \ + } +#define ASPECT_CHECK_SHRINK_W_GROW_H \ + if( max_aspect_w * h < max_aspect_h * w ) \ + { \ + int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \ + if( w - delta >= min_width ) \ + w -= delta; \ + else \ + { \ + int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \ + if( h + delta <= max_height ) \ + h += delta; \ + } \ + } + switch( mode ) + { + case SizemodeAny: +#if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width, + // so that changing aspect ratio to a different value and back keeps the same size (#87298) + { + ASPECT_CHECK_SHRINK_H_GROW_W + ASPECT_CHECK_SHRINK_W_GROW_H + ASPECT_CHECK_GROW_H + ASPECT_CHECK_GROW_W + break; + } +#endif + case SizemodeFixedW: + { + // the checks are order so that attempts to modify height are first + ASPECT_CHECK_GROW_H + ASPECT_CHECK_SHRINK_H_GROW_W + ASPECT_CHECK_SHRINK_W_GROW_H + ASPECT_CHECK_GROW_W + break; + } + case SizemodeFixedH: + { + ASPECT_CHECK_GROW_W + ASPECT_CHECK_SHRINK_W_GROW_H + ASPECT_CHECK_SHRINK_H_GROW_W + ASPECT_CHECK_GROW_H + break; + } + case SizemodeMax: + { + // first checks that try to shrink + ASPECT_CHECK_SHRINK_H_GROW_W + ASPECT_CHECK_SHRINK_W_GROW_H + ASPECT_CHECK_GROW_W + ASPECT_CHECK_GROW_H + break; + } + } +#undef ASPECT_CHECK_SHRINK_H_GROW_W +#undef ASPECT_CHECK_SHRINK_W_GROW_H +#undef ASPECT_CHECK_GROW_W +#undef ASPECT_CHECK_GROW_H + w += xSizeHint.base_width; + h += xSizeHint.base_height; + } + if( !rules()->checkStrictGeometry( false )) + { + // disobey increments and aspect when maximized + if( maximizeMode() & MaximizeHorizontal ) + w = w1; + if( maximizeMode() & MaximizeVertical ) + h = h1; + } + + if( !noframe ) + { + w += border_left + border_right; + h += border_top + border_bottom; + } + return rules()->checkSize( QSize( w, h )); + } + +/*! + Gets the client's normal WM hints and reconfigures itself respectively. + */ +void Client::getWmNormalHints() + { + long msize; + if (XGetWMNormalHints(qt_xdisplay(), window(), &xSizeHint, &msize) == 0 ) + xSizeHint.flags = 0; + // set defined values for the fields, even if they're not in flags + + if( ! ( xSizeHint.flags & PMinSize )) + xSizeHint.min_width = xSizeHint.min_height = 0; + if( xSizeHint.flags & PBaseSize ) + { + // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3 + // The other way around PMinSize is not a complete fallback for PBaseSize, + // so that's not handled here. + if( ! ( xSizeHint.flags & PMinSize )) + { + xSizeHint.min_width = xSizeHint.base_width; + xSizeHint.min_height = xSizeHint.base_height; + } + } + else + xSizeHint.base_width = xSizeHint.base_height = 0; + if( ! ( xSizeHint.flags & PMaxSize )) + xSizeHint.max_width = xSizeHint.max_height = INT_MAX; + else + { + xSizeHint.max_width = QMAX( xSizeHint.max_width, 1 ); + xSizeHint.max_height = QMAX( xSizeHint.max_height, 1 ); + } + if( xSizeHint.flags & PResizeInc ) + { + xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 ); + xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 ); + } + else + { + xSizeHint.width_inc = 1; + xSizeHint.height_inc = 1; + } + if( xSizeHint.flags & PAspect ) + { // no dividing by zero + xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 ); + xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 ); + } + else + { + xSizeHint.min_aspect.x = 1; + xSizeHint.min_aspect.y = INT_MAX; + xSizeHint.max_aspect.x = INT_MAX; + xSizeHint.max_aspect.y = 1; + } + if( ! ( xSizeHint.flags & PWinGravity )) + xSizeHint.win_gravity = NorthWestGravity; + if( isManaged()) + { // update to match restrictions + QSize new_size = adjustedSize(); + if( new_size != size() && !isFullScreen()) + { + QRect orig_geometry = geometry(); + resizeWithChecks( new_size ); + if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen()) + { + // try to keep the window in its xinerama screen if possible, + // if that fails at least keep it visible somewhere + QRect area = workspace()->clientArea( MovementArea, this ); + if( area.contains( orig_geometry )) + keepInArea( area ); + area = workspace()->clientArea( WorkArea, this ); + if( area.contains( orig_geometry )) + keepInArea( area ); + } + } + } + updateAllowedActions(); // affects isResizeable() + } + +QSize Client::minSize() const + { + return rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height )); + } + +QSize Client::maxSize() const + { + return rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height )); + } + +/*! + Auxiliary function to inform the client about the current window + configuration. + + */ +void Client::sendSyntheticConfigureNotify() + { + XConfigureEvent c; + c.type = ConfigureNotify; + c.send_event = True; + c.event = window(); + c.window = window(); + c.x = x() + clientPos().x(); + c.y = y() + clientPos().y(); + c.width = clientSize().width(); + c.height = clientSize().height(); + c.border_width = 0; + c.above = None; + c.override_redirect = 0; + XSendEvent( qt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c ); + } + +const QPoint Client::calculateGravitation( bool invert, int gravity ) const + { + int dx, dy; + dx = dy = 0; + + if( gravity == 0 ) // default (nonsense) value for the argument + gravity = xSizeHint.win_gravity; + +// dx, dy specify how the client window moves to make space for the frame + switch (gravity) + { + case NorthWestGravity: // move down right + default: + dx = border_left; + dy = border_top; + break; + case NorthGravity: // move right + dx = 0; + dy = border_top; + break; + case NorthEastGravity: // move down left + dx = -border_right; + dy = border_top; + break; + case WestGravity: // move right + dx = border_left; + dy = 0; + break; + case CenterGravity: + break; // will be handled specially + case StaticGravity: // don't move + dx = 0; + dy = 0; + break; + case EastGravity: // move left + dx = -border_right; + dy = 0; + break; + case SouthWestGravity: // move up right + dx = border_left ; + dy = -border_bottom; + break; + case SouthGravity: // move up + dx = 0; + dy = -border_bottom; + break; + case SouthEastGravity: // move up left + dx = -border_right; + dy = -border_bottom; + break; + } + if( gravity != CenterGravity ) + { // translate from client movement to frame movement + dx -= border_left; + dy -= border_top; + } + else + { // center of the frame will be at the same position client center without frame would be + dx = - ( border_left + border_right ) / 2; + dy = - ( border_top + border_bottom ) / 2; + } + if( !invert ) + return QPoint( x() + dx, y() + dy ); + else + return QPoint( x() - dx, y() - dy ); + } + +void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool ) + { + if( gravity == 0 ) // default (nonsense) value for the argument + gravity = xSizeHint.win_gravity; + if( value_mask & ( CWX | CWY )) + { + QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation + if ( value_mask & CWX ) + new_pos.setX( rx ); + if ( value_mask & CWY ) + new_pos.setY( ry ); + + // clever(?) workaround for applications like xv that want to set + // the location to the current location but miscalculate the + // frame size due to kwin being a double-reparenting window + // manager + if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y() + && gravity == NorthWestGravity && !from_tool ) + { + new_pos.setX( x()); + new_pos.setY( y()); + } + + int nw = clientSize().width(); + int nh = clientSize().height(); + if ( value_mask & CWWidth ) + nw = rw; + if ( value_mask & CWHeight ) + nh = rh; + QSize ns = sizeForClientSize( QSize( nw, nh ) ); + new_pos = rules()->checkPosition( new_pos ); + + // TODO what to do with maximized windows? + if ( maximizeMode() != MaximizeFull + || ns != size()) + { + QRect orig_geometry = geometry(); + GeometryUpdatesPostponer blocker( this ); + move( new_pos ); + plainResize( ns ); + setGeometry( QRect( calculateGravitation( false, gravity ), size())); + updateFullScreenHack( QRect( new_pos, QSize( nw, nh ))); + QRect area = workspace()->clientArea( WorkArea, this ); + if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen() + && area.contains( orig_geometry )) + keepInArea( area ); + + // this is part of the kicker-xinerama-hack... it should be + // safe to remove when kicker gets proper ExtendedStrut support; + // see Workspace::updateClientArea() and + // Client::adjustedClientArea() + if (hasStrut ()) + workspace() -> updateClientArea (); + } + } + + if ( value_mask & (CWWidth | CWHeight ) + && ! ( value_mask & ( CWX | CWY )) ) // pure resize + { + int nw = clientSize().width(); + int nh = clientSize().height(); + if ( value_mask & CWWidth ) + nw = rw; + if ( value_mask & CWHeight ) + nh = rh; + QSize ns = sizeForClientSize( QSize( nw, nh ) ); + + if( ns != size()) // don't restore if some app sets its own size again + { + QRect orig_geometry = geometry(); + GeometryUpdatesPostponer blocker( this ); + int save_gravity = xSizeHint.win_gravity; + xSizeHint.win_gravity = gravity; + resizeWithChecks( ns ); + xSizeHint.win_gravity = save_gravity; + updateFullScreenHack( QRect( calculateGravitation( true, xSizeHint.win_gravity ), QSize( nw, nh ))); + if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()) + { + // try to keep the window in its xinerama screen if possible, + // if that fails at least keep it visible somewhere + QRect area = workspace()->clientArea( MovementArea, this ); + if( area.contains( orig_geometry )) + keepInArea( area ); + area = workspace()->clientArea( WorkArea, this ); + if( area.contains( orig_geometry )) + keepInArea( area ); + } + } + } + // No need to send synthetic configure notify event here, either it's sent together + // with geometry change, or there's no need to send it. + // Handling of the real ConfigureRequest event forces sending it, as there it's necessary. + } + +void Client::resizeWithChecks( int w, int h, ForceGeometry_t force ) + { + if( shade_geometry_change ) + assert( false ); + else if( isShade()) + { + if( h == border_top + border_bottom ) + { + kdWarning() << "Shaded geometry passed for size:" << endl; + kdWarning() << kdBacktrace() << endl; + } + } + int newx = x(); + int newy = y(); + QRect area = workspace()->clientArea( WorkArea, this ); + // don't allow growing larger than workarea + if( w > area.width()) + w = area.width(); + if( h > area.height()) + h = area.height(); + QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size + w = tmp.width(); + h = tmp.height(); + switch( xSizeHint.win_gravity ) + { + case NorthWestGravity: // top left corner doesn't move + default: + break; + case NorthGravity: // middle of top border doesn't move + newx = ( newx + width() / 2 ) - ( w / 2 ); + break; + case NorthEastGravity: // top right corner doesn't move + newx = newx + width() - w; + break; + case WestGravity: // middle of left border doesn't move + newy = ( newy + height() / 2 ) - ( h / 2 ); + break; + case CenterGravity: // middle point doesn't move + newx = ( newx + width() / 2 ) - ( w / 2 ); + newy = ( newy + height() / 2 ) - ( h / 2 ); + break; + case StaticGravity: // top left corner of _client_ window doesn't move + // since decoration doesn't change, equal to NorthWestGravity + break; + case EastGravity: // // middle of right border doesn't move + newx = newx + width() - w; + newy = ( newy + height() / 2 ) - ( h / 2 ); + break; + case SouthWestGravity: // bottom left corner doesn't move + newy = newy + height() - h; + break; + case SouthGravity: // middle of bottom border doesn't move + newx = ( newx + width() / 2 ) - ( w / 2 ); + newy = newy + height() - h; + break; + case SouthEastGravity: // bottom right corner doesn't move + newx = newx + width() - w; + newy = newy + height() - h; + break; + } + // if it would be moved outside of workarea, keep it inside, + // see also Client::computeWorkareaDiff() + if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit + { + if( newx < area.left()) + newx = area.left(); + if( newx + w > area.right() + 1 ) + newx = area.right() + 1 - w; + assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above + } + if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit + { + if( newy < area.top()) + newy = area.top(); + if( newy + h > area.bottom() + 1 ) + newy = area.bottom() + 1 - h; + assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above + } + setGeometry( newx, newy, w, h, force ); + } + +// _NET_MOVERESIZE_WINDOW +void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height ) + { + int gravity = flags & 0xff; + int value_mask = 0; + if( flags & ( 1 << 8 )) + value_mask |= CWX; + if( flags & ( 1 << 9 )) + value_mask |= CWY; + if( flags & ( 1 << 10 )) + value_mask |= CWWidth; + if( flags & ( 1 << 11 )) + value_mask |= CWHeight; + configureRequest( value_mask, x, y, width, height, gravity, true ); + } + +/*! + Returns whether the window is moveable or has a fixed + position. + */ +bool Client::isMovable() const + { + if( !motif_may_move || isFullScreen()) + return false; + if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :) + return false; + if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() ) + return false; + if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position + return false; + return true; + } + +/*! + Returns whether the window is resizable or has a fixed size. + */ +bool Client::isResizable() const + { + if( !motif_may_resize || isFullScreen()) + return false; + if( isSpecialWindow() ) + return false; + if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() ) + return false; + if( rules()->checkSize( QSize()).isValid()) // forced size + return false; + + QSize min = minSize(); + QSize max = maxSize(); + return min.width() < max.width() || min.height() < max.height(); + } + +/* + Returns whether the window is maximizable or not + */ +bool Client::isMaximizable() const + { + { // isMovable() and isResizable() may be false for maximized windows + // with moving/resizing maximized windows disabled + TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore ); + if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ? + return false; + } + if ( maximizeMode() != MaximizeRestore ) + return TRUE; + QSize max = maxSize(); +#if 0 + if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X + return false; +#else + // apparently there are enough apps which specify some arbitrary value + // for their maximum size just for the fun of it + QSize areasize = workspace()->clientArea( MaximizeArea, this ).size(); + if( max.width() < areasize.width() || max.height() < areasize.height()) + return false; +#endif + return true; + } + + +/*! + Reimplemented to inform the client about the new window position. + */ +void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) + { + // this code is also duplicated in Client::plainResize() + // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry, + // simply because there are too many places dealing with geometry. Those places + // ignore shaded state and use normal geometry, which they usually should get + // from adjustedSize(). Such geometry comes here, and if the window is shaded, + // the geometry is used only for client_size, since that one is not used when + // shading. Then the frame geometry is adjusted for the shaded geometry. + // This gets more complicated in the case the code does only something like + // setGeometry( geometry()) - geometry() will return the shaded frame geometry. + // Such code is wrong and should be changed to handle the case when the window is shaded, + // for example using Client::clientSize(). + if( shade_geometry_change ) + ; // nothing + else if( isShade()) + { + if( h == border_top + border_bottom ) + { + kdDebug() << "Shaded geometry passed for size:" << endl; + kdDebug() << kdBacktrace() << endl; + } + else + { + client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); + h = border_top + border_bottom; + } + } + else + { + client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); + } + if( force == NormalGeometrySet && frame_geometry == QRect( x, y, w, h )) + return; + frame_geometry = QRect( x, y, w, h ); + updateWorkareaDiffs(); + if( postpone_geometry_updates != 0 ) + { + pending_geometry_update = true; + return; + } + resizeDecoration( QSize( w, h )); + XMoveResizeWindow( qt_xdisplay(), frameId(), x, y, w, h ); +// resizeDecoration( QSize( w, h )); + if( !isShade()) + { + QSize cs = clientSize(); + XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(), + cs.width(), cs.height()); + XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height()); + } + updateShape(); + // SELI TODO won't this be too expensive? + updateWorkareaDiffs(); + sendSyntheticConfigureNotify(); + updateWindowRules(); + checkMaximizeGeometry(); + } + +void Client::plainResize( int w, int h, ForceGeometry_t force ) + { + // this code is also duplicated in Client::setGeometry(), and it's also commented there + if( shade_geometry_change ) + ; // nothing + else if( isShade()) + { + if( h == border_top + border_bottom ) + { + kdDebug() << "Shaded geometry passed for size:" << endl; + kdDebug() << kdBacktrace() << endl; + } + else + { + client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); + h = border_top + border_bottom; + } + } + else + { + client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); + } + if( QSize( w, h ) != rules()->checkSize( QSize( w, h ))) + { + kdDebug() << "forced size fail:" << QSize( w,h ) << ":" << rules()->checkSize( QSize( w, h )) << endl; + kdDebug() << kdBacktrace() << endl; + } + if( force == NormalGeometrySet && frame_geometry.size() == QSize( w, h )) + return; + frame_geometry.setSize( QSize( w, h )); + updateWorkareaDiffs(); + if( postpone_geometry_updates != 0 ) + { + pending_geometry_update = true; + return; + } + resizeDecoration( QSize( w, h )); + XResizeWindow( qt_xdisplay(), frameId(), w, h ); +// resizeDecoration( QSize( w, h )); + if( !isShade()) + { + QSize cs = clientSize(); + XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(), + cs.width(), cs.height()); + XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height()); + } + updateShape(); + updateWorkareaDiffs(); + sendSyntheticConfigureNotify(); + updateWindowRules(); + checkMaximizeGeometry(); + } + +/*! + Reimplemented to inform the client about the new window position. + */ +void Client::move( int x, int y, ForceGeometry_t force ) + { + if( force == NormalGeometrySet && frame_geometry.topLeft() == QPoint( x, y )) + return; + frame_geometry.moveTopLeft( QPoint( x, y )); + updateWorkareaDiffs(); + if( postpone_geometry_updates != 0 ) + { + pending_geometry_update = true; + return; + } + XMoveWindow( qt_xdisplay(), frameId(), x, y ); + sendSyntheticConfigureNotify(); + updateWindowRules(); + checkMaximizeGeometry(); + } + + +void Client::postponeGeometryUpdates( bool postpone ) + { + if( postpone ) + { + if( postpone_geometry_updates == 0 ) + pending_geometry_update = false; + ++postpone_geometry_updates; + } + else + { + if( --postpone_geometry_updates == 0 ) + { + if( pending_geometry_update ) + { + if( isShade()) + setGeometry( QRect( pos(), adjustedSize()), ForceGeometrySet ); + else + setGeometry( geometry(), ForceGeometrySet ); + pending_geometry_update = false; + } + } + } + } + +void Client::maximize( MaximizeMode m ) + { + setMaximize( m & MaximizeVertical, m & MaximizeHorizontal ); + } + +/*! + Sets the maximization according to \a vertically and \a horizontally + */ +void Client::setMaximize( bool vertically, bool horizontally ) + { // changeMaximize() flips the state, so change from set->flip + changeMaximize( + max_mode & MaximizeVertical ? !vertically : vertically, + max_mode & MaximizeHorizontal ? !horizontally : horizontally, + false ); + } + +void Client::changeMaximize( bool vertical, bool horizontal, bool adjust ) + { + if( !isMaximizable()) + return; + + MaximizeMode old_mode = max_mode; + // 'adjust == true' means to update the size only, e.g. after changing workspace size + if( !adjust ) + { + if( vertical ) + max_mode = MaximizeMode( max_mode ^ MaximizeVertical ); + if( horizontal ) + max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal ); + } + + max_mode = rules()->checkMaximize( max_mode ); + if( !adjust && max_mode == old_mode ) + return; + + GeometryUpdatesPostponer blocker( this ); + + // maximing one way and unmaximizing the other way shouldn't happen + Q_ASSERT( !( vertical && horizontal ) + || ((( max_mode & MaximizeVertical ) != 0 ) == (( max_mode & MaximizeHorizontal ) != 0 ))); + + QRect clientArea = workspace()->clientArea( MaximizeArea, this ); + + // save sizes for restoring, if maximalizing + if( !adjust && !( y() == clientArea.top() && height() == clientArea.height())) + { + geom_restore.setTop( y()); + geom_restore.setHeight( height()); + } + if( !adjust && !( x() == clientArea.left() && width() == clientArea.width())) + { + geom_restore.setLeft( x()); + geom_restore.setWidth( width()); + } + + if( !adjust ) + { + if(( vertical && !(old_mode & MaximizeVertical )) + || ( horizontal && !( old_mode & MaximizeHorizontal ))) + Notify::raise( Notify::Maximize ); + else + Notify::raise( Notify::UnMaximize ); + } + + if( decoration != NULL ) // decorations may turn off some borders when maximized + decoration->borders( border_left, border_right, border_top, border_bottom ); + + // restore partial maximizations + if ( old_mode==MaximizeFull && max_mode==MaximizeRestore ) + { + if ( maximizeModeRestore()==MaximizeVertical ) + { + max_mode = MaximizeVertical; + maxmode_restore = MaximizeRestore; + } + if ( maximizeModeRestore()==MaximizeHorizontal ) + { + max_mode = MaximizeHorizontal; + maxmode_restore = MaximizeRestore; + } + } + + switch (max_mode) + { + + case MaximizeVertical: + { + if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull + { + if( geom_restore.width() == 0 ) + { // needs placement + plainResize( adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )); + workspace()->placeSmart( this, clientArea ); + } + else + setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()), + adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet); + } + else + setGeometry( QRect(QPoint(x(), clientArea.top()), + adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet); + info->setState( NET::MaxVert, NET::Max ); + break; + } + + case MaximizeHorizontal: + { + if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull + { + if( geom_restore.height() == 0 ) + { // needs placement + plainResize( adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )); + workspace()->placeSmart( this, clientArea ); + } + else + setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()), + adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), ForceGeometrySet); + } + else + setGeometry( QRect( QPoint(clientArea.left(), y()), + adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )), ForceGeometrySet); + info->setState( NET::MaxHoriz, NET::Max ); + break; + } + + case MaximizeRestore: + { + QRect restore = geometry(); + // when only partially maximized, geom_restore may not have the other dimension remembered + if( old_mode & MaximizeVertical ) + { + restore.setTop( geom_restore.top()); + restore.setBottom( geom_restore.bottom()); + } + if( old_mode & MaximizeHorizontal ) + { + restore.setLeft( geom_restore.left()); + restore.setRight( geom_restore.right()); + } + if( !restore.isValid()) + { + QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 ); + if( geom_restore.width() > 0 ) + s.setWidth( geom_restore.width()); + if( geom_restore.height() > 0 ) + s.setHeight( geom_restore.height()); + plainResize( adjustedSize( s )); + workspace()->placeSmart( this, clientArea ); + restore = geometry(); + if( geom_restore.width() > 0 ) + restore.moveLeft( geom_restore.x()); + if( geom_restore.height() > 0 ) + restore.moveTop( geom_restore.y()); + } + setGeometry( restore, ForceGeometrySet ); + info->setState( 0, NET::Max ); + break; + } + + case MaximizeFull: + { + if( !adjust ) + { + if( old_mode & MaximizeVertical ) + maxmode_restore = MaximizeVertical; + if( old_mode & MaximizeHorizontal ) + maxmode_restore = MaximizeHorizontal; + } + QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax ); + QRect r = QRect(clientArea.topLeft(), adjSize); + setGeometry( r, ForceGeometrySet ); + info->setState( NET::Max, NET::Max ); + break; + } + default: + break; + } + + updateAllowedActions(); + if( decoration != NULL ) + decoration->maximizeChange(); + updateWindowRules(); + } + +void Client::resetMaximize() + { + if( max_mode == MaximizeRestore ) + return; + max_mode = MaximizeRestore; + Notify::raise( Notify::UnMaximize ); + info->setState( 0, NET::Max ); + updateAllowedActions(); + if( decoration != NULL ) + decoration->borders( border_left, border_right, border_top, border_bottom ); + if( isShade()) + setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet ); + else + setGeometry( geometry(), ForceGeometrySet ); + if( decoration != NULL ) + decoration->maximizeChange(); + } + +void Client::checkMaximizeGeometry() + { + // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called + // when after the condition is no longer true + if( isShade()) + return; + if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows + return; + // Just in case. + static int recursion_protection = 0; + if( recursion_protection > 3 ) + { + kdWarning( 1212 ) << "Check maximize overflow - you loose!" << endl; + kdWarning( 1212 ) << kdBacktrace() << endl; + return; + } + ++recursion_protection; + QRect max_area = workspace()->clientArea( MaximizeArea, this ); + if( geometry() == max_area ) + { + if( max_mode != MaximizeFull ) + maximize( MaximizeFull ); + } + else if( x() == max_area.left() && width() == max_area.width()) + { + if( max_mode != MaximizeHorizontal ) + maximize( MaximizeHorizontal ); + } + else if( y() == max_area.top() && height() == max_area.height()) + { + if( max_mode != MaximizeVertical ) + maximize( MaximizeVertical ); + } + else if( max_mode != MaximizeRestore ) + { + resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry() + } + --recursion_protection; + } + +bool Client::isFullScreenable( bool fullscreen_hack ) const + { + if( !rules()->checkFullScreen( true )) + return false; + if( fullscreen_hack ) + return isNormalWindow(); + if( rules()->checkStrictGeometry( false )) + { + // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements + QRect fsarea = workspace()->clientArea( FullScreenArea, this ); + if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size()) + return false; + } + // don't check size constrains - some apps request fullscreen despite requesting fixed size + return !isSpecialWindow(); // also better disallow only weird types to go fullscreen + } + +bool Client::userCanSetFullScreen() const + { + if( fullscreen_mode == FullScreenHack ) + return false; + if( !isFullScreenable( false )) + return false; + // isMaximizable() returns false if fullscreen + TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone ); + return isNormalWindow() && isMaximizable(); + } + +void Client::setFullScreen( bool set, bool user ) + { + if( !isFullScreen() && !set ) + return; + if( fullscreen_mode == FullScreenHack ) + return; + if( user && !userCanSetFullScreen()) + return; + set = rules()->checkFullScreen( set ); + setShade( ShadeNone ); + bool was_fs = isFullScreen(); + if( !was_fs ) + geom_fs_restore = geometry(); + fullscreen_mode = set ? FullScreenNormal : FullScreenNone; + if( was_fs == isFullScreen()) + return; + StackingUpdatesBlocker blocker1( workspace()); + GeometryUpdatesPostponer blocker2( this ); + workspace()->updateClientLayer( this ); // active fullscreens get different layer + info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen ); + updateDecoration( false, false ); + if( isFullScreen()) + setGeometry( workspace()->clientArea( FullScreenArea, this )); + else + { + if( !geom_fs_restore.isNull()) + setGeometry( QRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size()))); + // TODO isShaded() ? + else + { // does this ever happen? + setGeometry( workspace()->clientArea( MaximizeArea, this )); + } + } + updateWindowRules(); + } + +int Client::checkFullScreenHack( const QRect& geom ) const + { + // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack + if( noBorder() && !isUserNoBorder() && isFullScreenable( true )) + { + if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size()) + return 2; // full area fullscreen hack + if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size()) + return 1; // xinerama-aware fullscreen hack + } + return 0; + } + +void Client::updateFullScreenHack( const QRect& geom ) + { + int type = checkFullScreenHack( geom ); + if( fullscreen_mode == FullScreenNone && type != 0 ) + { + fullscreen_mode = FullScreenHack; + updateDecoration( false, false ); + QRect geom; + if( rules()->checkStrictGeometry( false )) + { + geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area + ? workspace()->clientArea( FullArea, geom.center(), desktop()) + : workspace()->clientArea( ScreenArea, geom.center(), desktop()); + } + else + geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop()); + setGeometry( geom ); + } + else if( fullscreen_mode == FullScreenHack && type == 0 ) + { + fullscreen_mode = FullScreenNone; + updateDecoration( false, false ); + // whoever called this must setup correct geometry + } + StackingUpdatesBlocker blocker( workspace()); + workspace()->updateClientLayer( this ); // active fullscreens get different layer + } + +static QRect* visible_bound = 0; +static GeometryTip* geometryTip = 0; + +void Client::drawbound( const QRect& geom ) + { + assert( visible_bound == NULL ); + visible_bound = new QRect( geom ); + doDrawbound( *visible_bound, false ); + } + +void Client::clearbound() + { + if( visible_bound == NULL ) + return; + doDrawbound( *visible_bound, true ); + delete visible_bound; + visible_bound = 0; + } + +void Client::doDrawbound( const QRect& geom, bool clear ) + { + if( decoration != NULL && decoration->drawbound( geom, clear )) + return; // done by decoration + QPainter p ( workspace()->desktopWidget() ); + p.setPen( QPen( Qt::white, 5 ) ); + p.setRasterOp( Qt::XorROP ); + // the line is 5 pixel thick, so compensate for the extra two pixels + // on outside (#88657) + QRect g = geom; + if( g.width() > 5 ) + { + g.setLeft( g.left() + 2 ); + g.setRight( g.right() - 2 ); + } + if( g.height() > 5 ) + { + g.setTop( g.top() + 2 ); + g.setBottom( g.bottom() - 2 ); + } + p.drawRect( g ); + } + +void Client::positionGeometryTip() + { + assert( isMove() || isResize()); + // Position and Size display + if (options->showGeometryTip()) + { + if( !geometryTip ) + { // save under is not necessary with opaque, and seem to make things slower + bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque ) + || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ); + geometryTip = new GeometryTip( &xSizeHint, save_under ); + } + QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself + wgeom.setWidth( wgeom.width() - ( width() - clientSize().width())); + wgeom.setHeight( wgeom.height() - ( height() - clientSize().height())); + if( isShade()) + wgeom.setHeight( 0 ); + geometryTip->setGeometry( wgeom ); + if( !geometryTip->isVisible()) + { + geometryTip->show(); + geometryTip->raise(); + } + } + } + +class EatAllPaintEvents + : public QObject + { + protected: + virtual bool eventFilter( QObject* o, QEvent* e ) + { return e->type() == QEvent::Paint && o != geometryTip; } + }; + +static EatAllPaintEvents* eater = 0; + +bool Client::startMoveResize() + { + assert( !moveResizeMode ); + assert( QWidget::keyboardGrabber() == NULL ); + assert( QWidget::mouseGrabber() == NULL ); + if( QApplication::activePopupWidget() != NULL ) + return false; // popups have grab + bool has_grab = false; + // This reportedly improves smoothness of the moveresize operation, + // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* + // (http://lists.kde.org/?t=107302193400001&r=1&w=2) + XSetWindowAttributes attrs; + QRect r = workspace()->clientArea( FullArea, this ); + move_resize_grab_window = XCreateWindow( qt_xdisplay(), workspace()->rootWin(), r.x(), r.y(), + r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs ); + XMapRaised( qt_xdisplay(), move_resize_grab_window ); + if( XGrabPointer( qt_xdisplay(), move_resize_grab_window, False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, + GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), qt_x_time ) == Success ) + has_grab = true; + if( XGrabKeyboard( qt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, qt_x_time ) == Success ) + has_grab = true; + if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize + { + XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); + move_resize_grab_window = None; + return false; + } + if ( maximizeMode() != MaximizeRestore ) + resetMaximize(); + moveResizeMode = true; + workspace()->setClientIsMoving(this); + initialMoveResizeGeom = moveResizeGeom = geometry(); + checkUnrestrictedMoveResize(); + // rule out non opaque windows from useless translucency settings, maybe resizes? + if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove)) + setShadowSize(0); + if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque){ + savedOpacity_ = opacity_; + setOpacity(options->translucentMovingWindows, options->movingWindowOpacity); + } + if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque ) + || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) ) + { + grabXServer(); + kapp->sendPostedEvents(); + // we have server grab -> nothing should cause paint events + // unfortunately, that's not completely true, Qt may generate + // paint events on some widgets due to FocusIn(?) + // eat them, otherwise XOR painting will be broken (#58054) + // paint events for the geometrytip need to be allowed, though + eater = new EatAllPaintEvents; +// not needed anymore? kapp->installEventFilter( eater ); + } + Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart ); + return true; + } + +void Client::finishMoveResize( bool cancel ) + { + leaveMoveResize(); + if( cancel ) + setGeometry( initialMoveResizeGeom ); + else + setGeometry( moveResizeGeom ); + checkMaximizeGeometry(); +// FRAME update(); + Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); + } + +void Client::leaveMoveResize() + { + // rule out non opaque windows from useless translucency settings, maybe resizes? + if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque) + setOpacity(true, savedOpacity_); + if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove)) + updateShadowSize(); + clearbound(); + if (geometryTip) + { + geometryTip->hide(); + delete geometryTip; + geometryTip = NULL; + } + if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque ) + || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) ) + ungrabXServer(); + XUngrabKeyboard( qt_xdisplay(), qt_x_time ); + XUngrabPointer( qt_xdisplay(), qt_x_time ); + XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); + move_resize_grab_window = None; + workspace()->setClientIsMoving(0); + if( move_faked_activity ) + workspace()->unfakeActivity( this ); + move_faked_activity = false; + moveResizeMode = false; + delete eater; + eater = 0; + } + +// This function checks if it actually makes sense to perform a restricted move/resize. +// If e.g. the titlebar is already outside of the workarea, there's no point in performing +// a restricted move resize, because then e.g. resize would also move the window (#74555). +// NOTE: Most of it is duplicated from handleMoveResize(). +void Client::checkUnrestrictedMoveResize() + { + if( unrestrictedMoveResize ) + return; + QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop()); + int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; + // restricted move/resize - keep at least part of the titlebar always visible + // how much must remain visible when moved away in that direction + left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); + right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); + // width/height change with opaque resizing, use the initial ones + titlebar_marge = initialMoveResizeGeom.height(); + top_marge = border_bottom; + bottom_marge = border_top; + if( isResize()) + { + if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) + unrestrictedMoveResize = true; + if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) + unrestrictedMoveResize = true; + if( moveResizeGeom.right() < desktopArea.left() + left_marge ) + unrestrictedMoveResize = true; + if( moveResizeGeom.left() > desktopArea.right() - right_marge ) + unrestrictedMoveResize = true; + if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out + unrestrictedMoveResize = true; + } + if( isMove()) + { + if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out + unrestrictedMoveResize = true; + // no need to check top_marge, titlebar_marge already handles it + if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) + unrestrictedMoveResize = true; + if( moveResizeGeom.right() < desktopArea.left() + left_marge ) + unrestrictedMoveResize = true; + if( moveResizeGeom.left() > desktopArea.right() - right_marge ) + unrestrictedMoveResize = true; + } + } + +void Client::handleMoveResize( int x, int y, int x_root, int y_root ) + { + if(( mode == PositionCenter && !isMovable()) + || ( mode != PositionCenter && ( isShade() || !isResizable()))) + return; + + if ( !moveResizeMode ) + { + QPoint p( QPoint( x, y ) - moveOffset ); + if (p.manhattanLength() >= 6) + { + if( !startMoveResize()) + { + buttonDown = false; + setCursor( mode ); + return; + } + } + else + return; + } + + // ShadeHover or ShadeActive, ShadeNormal was already avoided above + if ( mode != PositionCenter && shade_mode != ShadeNone ) + setShade( ShadeNone ); + + QPoint globalPos( x_root, y_root ); + // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done, + // the bottomleft corner should be at is at (topleft.x(), bottomright().y()) + QPoint topleft = globalPos - moveOffset; + QPoint bottomright = globalPos + invertedMoveOffset; + QRect previousMoveResizeGeom = moveResizeGeom; + + // TODO move whole group when moving its leader or when the leader is not mapped? + + // compute bounds + // NOTE: This is duped in checkUnrestrictedMoveResize(). + QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop()); + int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; + if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely + left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5; + else // restricted move/resize - keep at least part of the titlebar always visible + { + // how much must remain visible when moved away in that direction + left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); + right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); + // width/height change with opaque resizing, use the initial ones + titlebar_marge = initialMoveResizeGeom.height(); + top_marge = border_bottom; + bottom_marge = border_top; + } + + bool update = false; + if( isResize()) + { + // first resize (without checking constrains), then snap, then check bounds, then check constrains + QRect orig = initialMoveResizeGeom; + Sizemode sizemode = SizemodeAny; + switch ( mode ) + { + case PositionTopLeft: + moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; + break; + case PositionBottomRight: + moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; + break; + case PositionBottomLeft: + moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; + break; + case PositionTopRight: + moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; + break; + case PositionTop: + moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ; + sizemode = SizemodeFixedH; // try not to affect height + break; + case PositionBottom: + moveResizeGeom = QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ; + sizemode = SizemodeFixedH; + break; + case PositionLeft: + moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ; + sizemode = SizemodeFixedW; + break; + case PositionRight: + moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ; + sizemode = SizemodeFixedW; + break; + case PositionCenter: + default: + assert( false ); + break; + } + + // adjust new size to snap to other windows/borders + moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode ); + + // NOTE: This is duped in checkUnrestrictedMoveResize(). + if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) + moveResizeGeom.setBottom( desktopArea.top() + top_marge ); + if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) + moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge ); + if( moveResizeGeom.right() < desktopArea.left() + left_marge ) + moveResizeGeom.setRight( desktopArea.left() + left_marge ); + if( moveResizeGeom.left() > desktopArea.right() - right_marge ) + moveResizeGeom.setLeft(desktopArea.right() - right_marge ); + if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out + moveResizeGeom.setTop( desktopArea.top()); + + QSize size = adjustedSize( moveResizeGeom.size(), sizemode ); + // the new topleft and bottomright corners (after checking size constrains), if they'll be needed + topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 ); + bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 ); + orig = moveResizeGeom; + switch ( mode ) + { // these 4 corners ones are copied from above + case PositionTopLeft: + moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; + break; + case PositionBottomRight: + moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; + break; + case PositionBottomLeft: + moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; + break; + case PositionTopRight: + moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; + break; + // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change. + // Therefore grow to the right/bottom if needed. + // TODO it should probably obey gravity rather than always using right/bottom ? + case PositionTop: + moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; + break; + case PositionBottom: + moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; + break; + case PositionLeft: + moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y())); + break; + case PositionRight: + moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; + break; + case PositionCenter: + default: + assert( false ); + break; + } + if( moveResizeGeom.size() != previousMoveResizeGeom.size()) + update = true; + } + else if( isMove()) + { + assert( mode == PositionCenter ); + // first move, then snap, then check bounds + moveResizeGeom.moveTopLeft( topleft ); + moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) ); + // NOTE: This is duped in checkUnrestrictedMoveResize(). + if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out + moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 ); + // no need to check top_marge, titlebar_marge already handles it + if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) + moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge ); + if( moveResizeGeom.right() < desktopArea.left() + left_marge ) + moveResizeGeom.moveRight( desktopArea.left() + left_marge ); + if( moveResizeGeom.left() > desktopArea.right() - right_marge ) + moveResizeGeom.moveLeft(desktopArea.right() - right_marge ); + if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) + update = true; + } + else + assert( false ); + + if( update ) + { + if( rules()->checkMoveResizeMode + ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) + { + setGeometry( moveResizeGeom ); + positionGeometryTip(); + } + else if( rules()->checkMoveResizeMode + ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent ) + { + clearbound(); // it's necessary to move the geometry tip when there's no outline + positionGeometryTip(); // shown, otherwise it would cause repaint problems in case + drawbound( moveResizeGeom ); // they overlap; the paint event will come after this, + } // so the geometry tip will be painted above the outline + } + if ( isMove() ) + workspace()->clientMoved(globalPos, qt_x_time); + } + + +} // namespace diff --git a/kwin/geometrytip.cpp b/kwin/geometrytip.cpp new file mode 100644 index 000000000..c8a046950 --- /dev/null +++ b/kwin/geometrytip.cpp @@ -0,0 +1,63 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2003, Karol Szwed <kszwed@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "geometrytip.h" + +namespace KWinInternal +{ + +GeometryTip::GeometryTip( const XSizeHints* xSizeHints, bool save_under ): + QLabel(NULL, "kwingeometry" ) + { + setMargin(1); + setIndent(0); + setLineWidth(1); + setFrameStyle( QFrame::Raised | QFrame::StyledPanel ); + setAlignment( AlignCenter | AlignTop ); + sizeHints = xSizeHints; + if( save_under ) + { + XSetWindowAttributes attr; + attr.save_under = True; // use saveunder if possible to avoid weird effects in transparent mode + XChangeWindowAttributes( qt_xdisplay(), winId(), CWSaveUnder, &attr ); + } + } + +GeometryTip::~GeometryTip() + { + } + +void GeometryTip::setGeometry( const QRect& geom ) + { + int w = geom.width(); + int h = geom.height(); + + if (sizeHints) + { + if (sizeHints->flags & PResizeInc) + { + w = ( w - sizeHints->base_width ) / sizeHints->width_inc; + h = ( h - sizeHints->base_height ) / sizeHints->height_inc; + } + } + + h = QMAX( h, 0 ); // in case of isShade() and PBaseSize + QString pos; + pos.sprintf( "%+d,%+d<br>(<b>%d x %d</b>)", + geom.x(), geom.y(), w, h ); + setText( pos ); + adjustSize(); + move( geom.x() + ((geom.width() - width()) / 2), + geom.y() + ((geom.height() - height()) / 2) ); + } + +} // namespace + +#include "geometrytip.moc" diff --git a/kwin/geometrytip.h b/kwin/geometrytip.h new file mode 100644 index 000000000..6fdd2599d --- /dev/null +++ b/kwin/geometrytip.h @@ -0,0 +1,34 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2003, Karol Szwed <kszwed@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_GEOMETRY_TIP_H +#define KWIN_GEOMETRY_TIP_H + +#include <qlabel.h> +#include "client.h" + +namespace KWinInternal +{ + +class GeometryTip: public QLabel + { + Q_OBJECT + public: + GeometryTip( const XSizeHints* xSizeHints, bool save_under ); + ~GeometryTip(); + void setGeometry( const QRect& geom ); + + private: + const XSizeHints* sizeHints; + }; + +} // namespace + +#endif diff --git a/kwin/group.cpp b/kwin/group.cpp new file mode 100644 index 000000000..405e32927 --- /dev/null +++ b/kwin/group.cpp @@ -0,0 +1,1118 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to window grouping. + +*/ + +//#define QT_CLEAN_NAMESPACE + +#include "group.h" + +#include "workspace.h" +#include "client.h" + +#include <assert.h> +#include <kstartupinfo.h> + + +/* + TODO + Rename as many uses of 'transient' as possible (hasTransient->hasSubwindow,etc.), + or I'll get it backwards in half of the cases again. +*/ + +namespace KWinInternal +{ + +/* + Consistency checks for window relations. Since transients are determinated + using Client::transiency_list and main windows are determined using Client::transientFor() + or the group for group transients, these have to match both ways. +*/ +//#define ENABLE_TRANSIENCY_CHECK + +#ifdef NDEBUG +#undef ENABLE_TRANSIENCY_CHECK +#endif + +#ifdef ENABLE_TRANSIENCY_CHECK +static bool transiencyCheckNonExistent = false; + +bool performTransiencyCheck() + { + bool ret = true; + ClientList clients = Workspace::self()->clients; + for( ClientList::ConstIterator it1 = clients.begin(); + it1 != clients.end(); + ++it1 ) + { + if( (*it1)->deleting ) + continue; + if( (*it1)->in_group == NULL ) + { + kdDebug() << "TC: " << *it1 << " in not in a group" << endl; + ret = false; + } + else if( !(*it1)->in_group->members().contains( *it1 )) + { + kdDebug() << "TC: " << *it1 << " has a group " << (*it1)->in_group << " but group does not contain it" << endl; + ret = false; + } + if( !(*it1)->isTransient()) + { + if( !(*it1)->mainClients().isEmpty()) + { + kdDebug() << "TC: " << *it1 << " is not transient, has main clients:" << (*it1)->mainClients() << endl; + ret = false; + } + } + else + { + ClientList mains = (*it1)->mainClients(); + for( ClientList::ConstIterator it2 = mains.begin(); + it2 != mains.end(); + ++it2 ) + { + if( transiencyCheckNonExistent + && !Workspace::self()->clients.contains( *it2 ) + && !Workspace::self()->desktops.contains( *it2 )) + { + kdDebug() << "TC:" << *it1 << " has non-existent main client " << endl; + kdDebug() << "TC2:" << *it2 << endl; // this may crash + ret = false; + continue; + } + if( !(*it2)->transients_list.contains( *it1 )) + { + kdDebug() << "TC:" << *it1 << " has main client " << *it2 << " but main client does not have it as a transient" << endl; + ret = false; + } + } + } + ClientList trans = (*it1)->transients_list; + for( ClientList::ConstIterator it2 = trans.begin(); + it2 != trans.end(); + ++it2 ) + { + if( transiencyCheckNonExistent + && !Workspace::self()->clients.contains( *it2 ) + && !Workspace::self()->desktops.contains( *it2 )) + { + kdDebug() << "TC:" << *it1 << " has non-existent transient " << endl; + kdDebug() << "TC2:" << *it2 << endl; // this may crash + ret = false; + continue; + } + if( !(*it2)->mainClients().contains( *it1 )) + { + kdDebug() << "TC:" << *it1 << " has transient " << *it2 << " but transient does not have it as a main client" << endl; + ret = false; + } + } + } + GroupList groups = Workspace::self()->groups; + for( GroupList::ConstIterator it1 = groups.begin(); + it1 != groups.end(); + ++it1 ) + { + ClientList members = (*it1)->members(); + for( ClientList::ConstIterator it2 = members.begin(); + it2 != members.end(); + ++it2 ) + { + if( (*it2)->in_group != *it1 ) + { + kdDebug() << "TC: Group " << *it1 << " contains client " << *it2 << " but client is not in that group" << endl; + ret = false; + } + } + } + return ret; + } + +static QString transiencyCheckStartBt; +static const Client* transiencyCheckClient; +static int transiencyCheck = 0; + +static void startTransiencyCheck( const QString& bt, const Client* c, bool ne ) + { + if( ++transiencyCheck == 1 ) + { + transiencyCheckStartBt = bt; + transiencyCheckClient = c; + } + if( ne ) + transiencyCheckNonExistent = true; + } +static void checkTransiency() + { + if( --transiencyCheck == 0 ) + { + if( !performTransiencyCheck()) + { + kdDebug() << "BT:" << transiencyCheckStartBt << endl; + kdDebug() << "CLIENT:" << transiencyCheckClient << endl; + assert( false ); + } + transiencyCheckNonExistent = false; + } + } +class TransiencyChecker + { + public: + TransiencyChecker( const QString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); } + ~TransiencyChecker() { checkTransiency(); } + }; + +void checkNonExistentClients() + { + startTransiencyCheck( kdBacktrace(), NULL, true ); + checkTransiency(); + } + +#define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c ) + +#else + +#define TRANSIENCY_CHECK( c ) + +void checkNonExistentClients() + { + } + +#endif + +//******************************************** +// Group +//******************************************** + +Group::Group( Window leader_P, Workspace* workspace_P ) + : leader_client( NULL ), + leader_wid( leader_P ), + _workspace( workspace_P ), + leader_info( NULL ), + user_time( -1U ), + refcount( 0 ) + { + if( leader_P != None ) + { + leader_client = workspace_P->findClient( WindowMatchPredicate( leader_P )); + unsigned long properties[ 2 ] = { 0, NET::WM2StartupId }; + leader_info = new NETWinInfo( qt_xdisplay(), leader_P, workspace()->rootWin(), + properties, 2 ); + } + workspace()->addGroup( this, Allowed ); + } + +Group::~Group() + { + delete leader_info; + } + +QPixmap Group::icon() const + { + if( leader_client != NULL ) + return leader_client->icon(); + else if( leader_wid != None ) + { + QPixmap ic; + Client::readIcons( leader_wid, &ic, NULL ); + return ic; + } + return QPixmap(); + } + +QPixmap Group::miniIcon() const + { + if( leader_client != NULL ) + return leader_client->miniIcon(); + else if( leader_wid != None ) + { + QPixmap ic; + Client::readIcons( leader_wid, NULL, &ic ); + return ic; + } + return QPixmap(); + } + +void Group::addMember( Client* member_P ) + { + TRANSIENCY_CHECK( member_P ); + _members.append( member_P ); +// kdDebug() << "GROUPADD:" << this << ":" << member_P << endl; +// kdDebug() << kdBacktrace() << endl; + } + +void Group::removeMember( Client* member_P ) + { + TRANSIENCY_CHECK( member_P ); +// kdDebug() << "GROUPREMOVE:" << this << ":" << member_P << endl; +// kdDebug() << kdBacktrace() << endl; + Q_ASSERT( _members.contains( member_P )); + _members.remove( member_P ); +// there are cases when automatic deleting of groups must be delayed, +// e.g. when removing a member and doing some operation on the possibly +// other members of the group (which would be however deleted already +// if there were no other members) + if( refcount == 0 && _members.isEmpty()) + { + workspace()->removeGroup( this, Allowed ); + delete this; + } + } + +void Group::ref() + { + ++refcount; + } + +void Group::deref() + { + if( --refcount == 0 && _members.isEmpty()) + { + workspace()->removeGroup( this, Allowed ); + delete this; + } + } + +void Group::gotLeader( Client* leader_P ) + { + assert( leader_P->window() == leader_wid ); + leader_client = leader_P; + } + +void Group::lostLeader() + { + assert( !_members.contains( leader_client )); + leader_client = NULL; + if( _members.isEmpty()) + { + workspace()->removeGroup( this, Allowed ); + delete this; + } + } + +void Group::getIcons() + { + // TODO - also needs adding the flag to NETWinInfo + } + +//*************************************** +// Workspace +//*************************************** + +Group* Workspace::findGroup( Window leader ) const + { + assert( leader != None ); + for( GroupList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + if( (*it)->leader() == leader ) + return *it; + return NULL; + } + +// Client is group transient, but has no group set. Try to find +// group with windows with the same client leader. +Group* Workspace::findClientLeaderGroup( const Client* c ) const + { + TRANSIENCY_CHECK( c ); + Group* ret = NULL; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + { + if( *it == c ) + continue; + if( (*it)->wmClientLeader() == c->wmClientLeader()) + { + if( ret == NULL || ret == (*it)->group()) + ret = (*it)->group(); + else + { + // There are already two groups with the same client leader. + // This most probably means the app uses group transients without + // setting group for its windows. Merging the two groups is a bad + // hack, but there's no really good solution for this case. + ClientList old_group = (*it)->group()->members(); + // old_group autodeletes when being empty + for( unsigned int pos = 0; + pos < old_group.count(); + ++pos ) + { + Client* tmp = old_group[ pos ]; + if( tmp != c ) + tmp->changeClientLeaderGroup( ret ); + } + } + } + } + return ret; + } + +void Workspace::updateMinimizedOfTransients( Client* c ) + { + // if mainwindow is minimized or shaded, minimize transients too + if ( c->isMinimized() || c->isShade() ) + { + for( ClientList::ConstIterator it = c->transients().begin(); + it != c->transients().end(); + ++it ) + { + if( !(*it)->isMinimized() + && !(*it)->isTopMenu() ) // topmenus are not minimized, they're hidden + { + (*it)->minimize( true ); // avoid animation + updateMinimizedOfTransients( (*it) ); + } + } + } + else + { // else unmiminize the transients + for( ClientList::ConstIterator it = c->transients().begin(); + it != c->transients().end(); + ++it ) + { + if( (*it)->isMinimized() + && !(*it)->isTopMenu()) + { + (*it)->unminimize( true ); // avoid animation + updateMinimizedOfTransients( (*it) ); + } + } + } + } + + +/*! + Sets the client \a c's transient windows' on_all_desktops property to \a on_all_desktops. + */ +void Workspace::updateOnAllDesktopsOfTransients( Client* c ) + { + for( ClientList::ConstIterator it = c->transients().begin(); + it != c->transients().end(); + ++it) + { + if( (*it)->isOnAllDesktops() != c->isOnAllDesktops()) + (*it)->setOnAllDesktops( c->isOnAllDesktops()); + } + } + +// A new window has been mapped. Check if it's not a mainwindow for some already existing transient window. +void Workspace::checkTransients( Window w ) + { + TRANSIENCY_CHECK( NULL ); + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + (*it)->checkTransient( w ); + } + + + +//**************************************** +// Client +//**************************************** + +// hacks for broken apps here +// all resource classes are forced to be lowercase +bool Client::resourceMatch( const Client* c1, const Client* c2 ) + { + // xv has "xv" as resource name, and different strings starting with "XV" as resource class + if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" ) + return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv"; + // Mozilla has "Mozilla" as resource name, and different strings as resource class + if( c1->resourceName() == "mozilla" ) + return c2->resourceName() == "mozilla"; + return c1->resourceClass() == c2->resourceClass(); + } + +bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack ) + { + bool same_app = false; + + // tests that definitely mean they belong together + if( c1 == c2 ) + same_app = true; + else if( c1->isTransient() && c2->hasTransient( c1, true )) + same_app = true; // c1 has c2 as mainwindow + else if( c2->isTransient() && c1->hasTransient( c2, true )) + same_app = true; // c2 has c1 as mainwindow + else if( c1->group() == c2->group()) + same_app = true; // same group + else if( c1->wmClientLeader() == c2->wmClientLeader() + && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), + && c2->wmClientLeader() != c2->window()) // don't use in this test then + same_app = true; // same client leader + + // tests that mean they most probably don't belong together + else if( c1->pid() != c2->pid() + || c1->wmClientMachine( false ) != c2->wmClientMachine( false )) + ; // different processes + else if( c1->wmClientLeader() != c2->wmClientLeader() + && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), + && c2->wmClientLeader() != c2->window()) // don't use in this test then + ; // different client leader + else if( !resourceMatch( c1, c2 )) + ; // different apps + else if( !sameAppWindowRoleMatch( c1, c2, active_hack )) + ; // "different" apps + else if( c1->pid() == 0 || c2->pid() == 0 ) + ; // old apps that don't have _NET_WM_PID, consider them different + // if they weren't found to match above + else + same_app = true; // looks like it's the same app + + return same_app; + } + +// Non-transient windows with window role containing '#' are always +// considered belonging to different applications (unless +// the window role is exactly the same). KMainWindow sets +// window role this way by default, and different KMainWindow +// usually "are" different application from user's point of view. +// This help with no-focus-stealing for e.g. konqy reusing. +// On the other hand, if one of the windows is active, they are +// considered belonging to the same application. This is for +// the cases when opening new mainwindow directly from the application, +// e.g. 'Open New Window' in konqy ( active_hack == true ). +bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ) + { + if( c1->isTransient()) + { + while( c1->transientFor() != NULL ) + c1 = c1->transientFor(); + if( c1->groupTransient()) + return c1->group() == c2->group(); +#if 0 + // if a group transient is in its own group, it didn't possibly have a group, + // and therefore should be considered belonging to the same app like + // all other windows from the same app + || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; +#endif + } + if( c2->isTransient()) + { + while( c2->transientFor() != NULL ) + c2 = c2->transientFor(); + if( c2->groupTransient()) + return c1->group() == c2->group(); +#if 0 + || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; +#endif + } + int pos1 = c1->windowRole().find( '#' ); + int pos2 = c2->windowRole().find( '#' ); + if(( pos1 >= 0 && pos2 >= 0 ) + || + // hacks here + // Mozilla has resourceName() and resourceClass() swapped + c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" ) + { + if( !active_hack ) // without the active hack for focus stealing prevention, + return c1 == c2; // different mainwindows are always different apps + if( !c1->isActive() && !c2->isActive()) + return c1 == c2; + else + return true; + } + return true; + } + +/* + + Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3 + + WM_TRANSIENT_FOR is basically means "this is my mainwindow". + For NET::Unknown windows, transient windows are considered to be NET::Dialog + windows, for compatibility with non-NETWM clients. KWin may adjust the value + of this property in some cases (window pointing to itself or creating a loop, + keeping NET::Splash windows above other windows from the same app, etc.). + + Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after + possibly being adjusted by KWin. Client::transient_for points to the Client + this Client is transient for, or is NULL. If Client::transient_for_id is + poiting to the root window, the window is considered to be transient + for the whole window group, as suggested in NETWM 7.3. + + In the case of group transient window, Client::transient_for is NULL, + and Client::groupTransient() returns true. Such window is treated as + if it were transient for every window in its window group that has been + mapped _before_ it (or, to be exact, was added to the same group before it). + Otherwise two group transients can create loops, which can lead very very + nasty things (bug #67914 and all its dupes). + + Client::original_transient_for_id is the value of the property, which + may be different if Client::transient_for_id if e.g. forcing NET::Splash + to be kept on top of its window group, or when the mainwindow is not mapped + yet, in which case the window is temporarily made group transient, + and when the mainwindow is mapped, transiency is re-evaluated. + + This can get a bit complicated with with e.g. two Konqueror windows created + by the same process. They should ideally appear like two independent applications + to the user. This should be accomplished by all windows in the same process + having the same window group (needs to be changed in Qt at the moment), and + using non-group transients poiting to their relevant mainwindow for toolwindows + etc. KWin should handle both group and non-group transient dialogs well. + + In other words: + - non-transient windows : isTransient() == false + - normal transients : transientFor() != NULL + - group transients : groupTransient() == true + + - list of mainwindows : mainClients() (call once and loop over the result) + - list of transients : transients() + - every window in the group : group()->members() +*/ + +void Client::readTransient() + { + TRANSIENCY_CHECK( this ); + Window new_transient_for_id; + if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id )) + { + original_transient_for_id = new_transient_for_id; + new_transient_for_id = verifyTransientFor( new_transient_for_id, true ); + } + else + { + original_transient_for_id = None; + new_transient_for_id = verifyTransientFor( None, false ); + } + setTransient( new_transient_for_id ); + } + +void Client::setTransient( Window new_transient_for_id ) + { + TRANSIENCY_CHECK( this ); + if( new_transient_for_id != transient_for_id ) + { + removeFromMainClients(); + transient_for = NULL; + transient_for_id = new_transient_for_id; + if( transient_for_id != None && !groupTransient()) + { + transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id )); + assert( transient_for != NULL ); // verifyTransient() had to check this + transient_for->addTransient( this ); + } // checkGroup() will check 'check_active_modal' + checkGroup( NULL, true ); // force, because transiency has changed + if( isTopMenu()) + workspace()->updateCurrentTopMenu(); + workspace()->updateClientLayer( this ); + } + } + +void Client::removeFromMainClients() + { + TRANSIENCY_CHECK( this ); + if( transientFor() != NULL ) + transientFor()->removeTransient( this ); + if( groupTransient()) + { + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + (*it)->removeTransient( this ); + } + } + +// *sigh* this transiency handling is madness :( +// This one is called when destroying/releasing a window. +// It makes sure this client is removed from all grouping +// related lists. +void Client::cleanGrouping() + { + TRANSIENCY_CHECK( this ); +// kdDebug() << "CLEANGROUPING:" << this << endl; +// for( ClientList::ConstIterator it = group()->members().begin(); +// it != group()->members().end(); +// ++it ) +// kdDebug() << "CL:" << *it << endl; +// ClientList mains; +// mains = mainClients(); +// for( ClientList::ConstIterator it = mains.begin(); +// it != mains.end(); +// ++it ) +// kdDebug() << "MN:" << *it << endl; + removeFromMainClients(); +// kdDebug() << "CLEANGROUPING2:" << this << endl; +// for( ClientList::ConstIterator it = group()->members().begin(); +// it != group()->members().end(); +// ++it ) +// kdDebug() << "CL2:" << *it << endl; +// mains = mainClients(); +// for( ClientList::ConstIterator it = mains.begin(); +// it != mains.end(); +// ++it ) +// kdDebug() << "MN2:" << *it << endl; + for( ClientList::ConstIterator it = transients_list.begin(); + it != transients_list.end(); + ) + { + if( (*it)->transientFor() == this ) + { + ClientList::ConstIterator it2 = it++; + removeTransient( *it2 ); + } + else + ++it; + } +// kdDebug() << "CLEANGROUPING3:" << this << endl; +// for( ClientList::ConstIterator it = group()->members().begin(); +// it != group()->members().end(); +// ++it ) +// kdDebug() << "CL3:" << *it << endl; +// mains = mainClients(); +// for( ClientList::ConstIterator it = mains.begin(); +// it != mains.end(); +// ++it ) +// kdDebug() << "MN3:" << *it << endl; + // HACK + // removeFromMainClients() did remove 'this' from transient + // lists of all group members, but then made windows that + // were transient for 'this' group transient, which again + // added 'this' to those transient lists :( + ClientList group_members = group()->members(); + group()->removeMember( this ); + in_group = NULL; + for( ClientList::ConstIterator it = group_members.begin(); + it != group_members.end(); + ++it ) + (*it)->removeTransient( this ); +// kdDebug() << "CLEANGROUPING4:" << this << endl; +// for( ClientList::ConstIterator it = group_members.begin(); +// it != group_members.end(); +// ++it ) +// kdDebug() << "CL4:" << *it << endl; + } + +// Make sure that no group transient is considered transient +// for a window that is (directly or indirectly) transient for it +// (including another group transients). +// Non-group transients not causing loops are checked in verifyTransientFor(). +void Client::checkGroupTransients() + { + TRANSIENCY_CHECK( this ); + for( ClientList::ConstIterator it1 = group()->members().begin(); + it1 != group()->members().end(); + ++it1 ) + { + if( !(*it1)->groupTransient()) // check all group transients in the group + continue; // TODO optimize to check only the changed ones? + for( ClientList::ConstIterator it2 = group()->members().begin(); + it2 != group()->members().end(); + ++it2 ) // group transients can be transient only for others in the group, + { // so don't make them transient for the ones that are transient for it + if( *it1 == *it2 ) + continue; + for( Client* cl = (*it2)->transientFor(); + cl != NULL; + cl = cl->transientFor()) + { + if( cl == *it1 ) + { // don't use removeTransient(), that would modify *it2 too + (*it2)->transients_list.remove( *it1 ); + continue; + } + } + // if *it1 and *it2 are both group transients, and are transient for each other, + // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later, + // and should be therefore on top of *it1 + // TODO This could possibly be optimized, it also requires hasTransient() to check for loops. + if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true )) + (*it2)->transients_list.remove( *it1 ); + // if there are already windows W1 and W2, W2 being transient for W1, and group transient W3 + // is added, make it transient only for W2, not for W1, because it's already indirectly + // transient for it - the indirect transiency actually shouldn't break anything, + // but it can lead to exponentially expensive operations (#95231) + // TODO this is pretty slow as well + for( ClientList::ConstIterator it3 = group()->members().begin(); + it3 != group()->members().end(); + ++it3 ) + { + if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 ) + continue; + if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false )) + { + if( (*it2)->hasTransient( *it3, true )) + (*it2)->transients_list.remove( *it1 ); + if( (*it3)->hasTransient( *it2, true )) + (*it3)->transients_list.remove( *it1 ); + } + } + } + } + } + +/*! + Check that the window is not transient for itself, and similar nonsense. + */ +Window Client::verifyTransientFor( Window new_transient_for, bool defined ) + { + Window new_property_value = new_transient_for; + // make sure splashscreens are shown above all their app's windows, even though + // they're in Normal layer + if( isSplash() && new_transient_for == None ) + new_transient_for = workspace()->rootWin(); + if( new_transient_for == None ) + if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window + new_property_value = new_transient_for = workspace()->rootWin(); + else + return None; + if( new_transient_for == window()) // pointing to self + { // also fix the property itself + kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl; + new_property_value = new_transient_for = workspace()->rootWin(); + } +// The transient_for window may be embedded in another application, +// so kwin cannot see it. Try to find the managed client for the +// window and fix the transient_for property if possible. + WId before_search = new_transient_for; + while( new_transient_for != None + && new_transient_for != workspace()->rootWin() + && !workspace()->findClient( WindowMatchPredicate( new_transient_for ))) + { + Window root_return, parent_return; + Window* wins = NULL; + unsigned int nwins; + int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins); + if ( wins ) + XFree((void *) wins); + if ( r == 0) + break; + new_transient_for = parent_return; + } + if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for ))) + { + if( new_transient_for != before_search ) + { + kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window " + << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl; + new_property_value = new_transient_for; // also fix the property + } + } + else + new_transient_for = before_search; // nice try +// loop detection +// group transients cannot cause loops, because they're considered transient only for non-transient +// windows in the group + int count = 20; + Window loop_pos = new_transient_for; + while( loop_pos != None && loop_pos != workspace()->rootWin()) + { + Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos )); + if( pos == NULL ) + break; + loop_pos = pos->transient_for_id; + if( --count == 0 || pos == this ) + { + kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl; + new_transient_for = workspace()->rootWin(); + } + } + if( new_transient_for != workspace()->rootWin() + && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL ) + { // it's transient for a specific window, but that window is not mapped + new_transient_for = workspace()->rootWin(); + } + if( new_property_value != original_transient_for_id ) + XSetTransientForHint( qt_xdisplay(), window(), new_property_value ); + return new_transient_for; + } + +void Client::addTransient( Client* cl ) + { + TRANSIENCY_CHECK( this ); + assert( !transients_list.contains( cl )); +// assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients() + assert( cl != this ); + transients_list.append( cl ); + if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal()) + check_active_modal = true; +// kdDebug() << "ADDTRANS:" << this << ":" << cl << endl; +// kdDebug() << kdBacktrace() << endl; +// for( ClientList::ConstIterator it = transients_list.begin(); +// it != transients_list.end(); +// ++it ) +// kdDebug() << "AT:" << (*it) << endl; + } + +void Client::removeTransient( Client* cl ) + { + TRANSIENCY_CHECK( this ); +// kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl; +// kdDebug() << kdBacktrace() << endl; + transients_list.remove( cl ); + // cl is transient for this, but this is going away + // make cl group transient + if( cl->transientFor() == this ) + { + cl->transient_for_id = None; + cl->transient_for = NULL; // SELI +// SELI cl->setTransient( workspace()->rootWin()); + cl->setTransient( None ); + } + } + +// A new window has been mapped. Check if it's not a mainwindow for this already existing window. +void Client::checkTransient( Window w ) + { + TRANSIENCY_CHECK( this ); + if( original_transient_for_id != w ) + return; + w = verifyTransientFor( w, true ); + setTransient( w ); + } + +// returns true if cl is the transient_for window for this client, +// or recursively the transient_for window +bool Client::hasTransient( const Client* cl, bool indirect ) const + { + // checkGroupTransients() uses this to break loops, so hasTransient() must detect them + ConstClientList set; + return hasTransientInternal( cl, indirect, set ); + } + +bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const + { + if( cl->transientFor() != NULL ) + { + if( cl->transientFor() == this ) + return true; + if( !indirect ) + return false; + if( set.contains( cl )) + return false; + set.append( cl ); + return hasTransientInternal( cl->transientFor(), indirect, set ); + } + if( !cl->isTransient()) + return false; + if( group() != cl->group()) + return false; + // cl is group transient, search from top + if( transients().contains( const_cast< Client* >( cl ))) + return true; + if( !indirect ) + return false; + if( set.contains( this )) + return false; + set.append( this ); + for( ClientList::ConstIterator it = transients().begin(); + it != transients().end(); + ++it ) + if( (*it)->hasTransientInternal( cl, indirect, set )) + return true; + return false; + } + +ClientList Client::mainClients() const + { + if( !isTransient()) + return ClientList(); + if( transientFor() != NULL ) + return ClientList() << const_cast< Client* >( transientFor()); + ClientList result; + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + if((*it)->hasTransient( this, false )) + result.append( *it ); + return result; + } + +Client* Client::findModal() + { + for( ClientList::ConstIterator it = transients().begin(); + it != transients().end(); + ++it ) + if( Client* ret = (*it)->findModal()) + return ret; + if( isModal()) + return this; + return NULL; + } + +// Client::window_group only holds the contents of the hint, +// but it should be used only to find the group, not for anything else +// Argument is only when some specific group needs to be set. +void Client::checkGroup( Group* set_group, bool force ) + { + TRANSIENCY_CHECK( this ); + Group* old_group = in_group; + if( old_group != NULL ) + old_group->ref(); // turn off automatic deleting + if( set_group != NULL ) + { + if( set_group != in_group ) + { + if( in_group != NULL ) + in_group->removeMember( this ); + in_group = set_group; + in_group->addMember( this ); + } + } + else if( window_group != None ) + { + Group* new_group = workspace()->findGroup( window_group ); + if( transientFor() != NULL && transientFor()->group() != new_group ) + { // move the window to the right group (e.g. a dialog provided + // by different app, but transient for this one, so make it part of that group) + new_group = transientFor()->group(); + } + if( new_group == NULL ) // doesn't exist yet + new_group = new Group( window_group, workspace()); + if( new_group != in_group ) + { + if( in_group != NULL ) + in_group->removeMember( this ); + in_group = new_group; + in_group->addMember( this ); + } + } + else + { + if( transientFor() != NULL ) + { // doesn't have window group set, but is transient for something + // so make it part of that group + Group* new_group = transientFor()->group(); + if( new_group != in_group ) + { + if( in_group != NULL ) + in_group->removeMember( this ); + in_group = transientFor()->group(); + in_group->addMember( this ); + } + } + else if( groupTransient()) + { // group transient which actually doesn't have a group :( + // try creating group with other windows with the same client leader + Group* new_group = workspace()->findClientLeaderGroup( this ); + if( new_group == NULL ) + new_group = new Group( None, workspace()); + if( new_group != in_group ) + { + if( in_group != NULL ) + in_group->removeMember( this ); + in_group = new_group; + in_group->addMember( this ); + } + } + else // Not transient without a group, put it in its client leader group. + { // This might be stupid if grouping was used for e.g. taskbar grouping + // or minimizing together the whole group, but as long as its used + // only for dialogs it's better to keep windows from one app in one group. + Group* new_group = workspace()->findClientLeaderGroup( this ); + if( in_group != NULL && in_group != new_group ) + { + in_group->removeMember( this ); + in_group = NULL; + } + if( new_group == NULL ) + new_group = new Group( None, workspace() ); + if( in_group != new_group ) + { + in_group = new_group; + in_group->addMember( this ); + } + } + } + if( in_group != old_group || force ) + { + for( ClientList::Iterator it = transients_list.begin(); + it != transients_list.end(); + ) + { // group transients in the old group are no longer transient for it + if( (*it)->groupTransient() && (*it)->group() != group()) + it = transients_list.remove( it ); + else + ++it; + } + if( groupTransient()) + { + // no longer transient for ones in the old group + if( old_group != NULL ) + { + for( ClientList::ConstIterator it = old_group->members().begin(); + it != old_group->members().end(); + ++it ) + (*it)->removeTransient( this ); + } + // and make transient for all in the new group + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + { + if( *it == this ) + break; // this means the window is only transient for windows mapped before it + (*it)->addTransient( this ); + } + } + // group transient splashscreens should be transient even for windows + // in group mapped later + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + { + if( !(*it)->isSplash()) + continue; + if( !(*it)->groupTransient()) + continue; + if( *it == this || hasTransient( *it, true )) // TODO indirect? + continue; + addTransient( *it ); + } + } + if( old_group != NULL ) + old_group->deref(); // can be now deleted if empty + checkGroupTransients(); + checkActiveModal(); + workspace()->updateClientLayer( this ); + } + +// used by Workspace::findClientLeaderGroup() +void Client::changeClientLeaderGroup( Group* gr ) + { + // transientFor() != NULL are in the group of their mainwindow, so keep them there + if( transientFor() != NULL ) + return; + // also don't change the group for window which have group set + if( window_group ) + return; + checkGroup( gr ); // change group + } + +bool Client::check_active_modal = false; + +void Client::checkActiveModal() + { + // if the active window got new modal transient, activate it. + // cannot be done in AddTransient(), because there may temporarily + // exist loops, breaking findModal + Client* check_modal = workspace()->mostRecentlyActivatedClient(); + if( check_modal != NULL && check_modal->check_active_modal ) + { + Client* new_modal = check_modal->findModal(); + if( new_modal != NULL && new_modal != check_modal ) + { + if( !new_modal->isManaged()) + return; // postpone check until end of manage() + workspace()->activateClient( new_modal ); + } + check_modal->check_active_modal = false; + } + } + +} // namespace diff --git a/kwin/group.h b/kwin/group.h new file mode 100644 index 000000000..21ebb8d17 --- /dev/null +++ b/kwin/group.h @@ -0,0 +1,90 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_GROUP_H +#define KWIN_GROUP_H + +#include "utils.h" +#include <X11/X.h> +#include <netwm.h> + +namespace KWinInternal +{ + +class Client; +class Workspace; + +class Group + { + public: + Group( Window leader, Workspace* workspace ); + ~Group(); + Window leader() const; + const Client* leaderClient() const; + Client* leaderClient(); + const ClientList& members() const; + QPixmap icon() const; + QPixmap miniIcon() const; + void addMember( Client* member ); + void removeMember( Client* member ); + void gotLeader( Client* leader ); + void lostLeader(); + Workspace* workspace(); + bool groupEvent( XEvent* e ); + void updateUserTime( Time time = CurrentTime ); + Time userTime() const; + void ref(); + void deref(); + private: + void getIcons(); + void startupIdChanged(); + ClientList _members; + Client* leader_client; + Window leader_wid; + Workspace* _workspace; + NETWinInfo* leader_info; + Time user_time; + int refcount; + }; + +inline Window Group::leader() const + { + return leader_wid; + } + +inline const Client* Group::leaderClient() const + { + return leader_client; + } + +inline Client* Group::leaderClient() + { + return leader_client; + } + +inline const ClientList& Group::members() const + { + return _members; + } + +inline Workspace* Group::workspace() + { + return _workspace; + } + +inline Time Group::userTime() const + { + return user_time; + } + +} // namespace + +#endif diff --git a/kwin/kcmkwin/Makefile.am b/kwin/kcmkwin/Makefile.am new file mode 100644 index 000000000..ff552e928 --- /dev/null +++ b/kwin/kcmkwin/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = kwinoptions kwindecoration kwinrules diff --git a/kwin/kcmkwin/kwindecoration/Makefile.am b/kwin/kcmkwin/kwindecoration/Makefile.am new file mode 100644 index 000000000..4d908001f --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kcm_kwindecoration.la + +kcm_kwindecoration_la_SOURCES = kwindecoration.cpp buttons.cpp kwindecorationIface.skel preview.cpp +noinst_HEADERS = kwindecoration.h kwindecorationIface.h buttons.h preview.h + +kcm_kwindecoration_la_LDFLAGS = \ + -module -avoid-version $(all_libraries) -no-undefined + +kcm_kwindecoration_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la + +METASOURCES = AUTO + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kcmkwindecoration.pot + +xdg_apps_DATA = kwindecoration.desktop diff --git a/kwin/kcmkwin/kwindecoration/buttons.cpp b/kwin/kcmkwin/kwindecoration/buttons.cpp new file mode 100644 index 000000000..888e092e7 --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/buttons.cpp @@ -0,0 +1,883 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2004, Sandro Giessl <sandro@giessl.com> + Copyright (c) 2001 + Karol Szwed <gallium@kde.org> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <qheader.h> +#include <qpainter.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qstyle.h> + +#include <kdebug.h> + +#include <kdialog.h> +#include <klocale.h> +#include <kglobalsettings.h> + +#include <kdecorationfactory.h> + +#include "buttons.h" +#include "pixmaps.h" + + +#define BUTTONDRAGMIMETYPE "application/x-kde_kwindecoration_buttons" +ButtonDrag::ButtonDrag( Button btn, QWidget* parent, const char* name) + : QStoredDrag( BUTTONDRAGMIMETYPE, parent, name) +{ + QByteArray data; + QDataStream stream(data, IO_WriteOnly); + stream << btn.name; + stream << btn.icon; + stream << btn.type.unicode(); + stream << (int) btn.duplicate; + stream << (int) btn.supported; + setEncodedData( data ); +} + + +bool ButtonDrag::canDecode( QDropEvent* e ) +{ + return e->provides( BUTTONDRAGMIMETYPE ); +} + +bool ButtonDrag::decode( QDropEvent* e, Button& btn ) +{ + QByteArray data = e->data( BUTTONDRAGMIMETYPE ); + if ( data.size() ) + { + e->accept(); + QDataStream stream(data, IO_ReadOnly); + stream >> btn.name; + stream >> btn.icon; + ushort type; + stream >> type; + btn.type = QChar(type); + int duplicate; + stream >> duplicate; + btn.duplicate = duplicate; + int supported; + stream >> supported; + btn.supported = supported; + return TRUE; + } + return FALSE; +} + + +Button::Button() +{ +} + +Button::Button(const QString& n, const QBitmap& i, QChar t, bool d, bool s) + : name(n), + icon(i), + type(t), + duplicate(d), + supported(s) +{ +} + +Button::~Button() +{ +} + +// helper function to deal with the Button's bitmaps more easily... +QPixmap bitmapPixmap(const QBitmap& bm, const QColor& color) +{ + QPixmap pm(bm.size() ); + pm.setMask(bm); + QPainter p(&pm); + p.setPen(color); + p.drawPixmap(0,0,bm); + p.end(); + return pm; +} + + +ButtonSource::ButtonSource(QWidget *parent, const char* name) + : KListView(parent, name) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + setResizeMode(QListView::AllColumns); + setDragEnabled(true); + setAcceptDrops(true); + setDropVisualizer(false); + setSorting(-1); + header()->setClickEnabled(false); + header()->hide(); + + addColumn(i18n("Buttons") ); +} + +ButtonSource::~ButtonSource() +{ +} + +QSize ButtonSource::sizeHint() const +{ + // make the sizeHint height a bit smaller than the one of QListView... + + if ( cachedSizeHint().isValid() ) + return cachedSizeHint(); + + constPolish(); + + QSize s( header()->sizeHint() ); + + if ( verticalScrollBar()->isVisible() ) + s.setWidth( s.width() + style().pixelMetric(QStyle::PM_ScrollBarExtent) ); + s += QSize(frameWidth()*2,frameWidth()*2); + + // size hint: 4 lines of text... + s.setHeight( s.height() + fontMetrics().lineSpacing()*3 ); + + setCachedSizeHint( s ); + + return s; +} + +void ButtonSource::hideAllButtons() +{ + QListViewItemIterator it(this); + while (it.current() ) { + it.current()->setVisible(false); + ++it; + } +} + +void ButtonSource::showAllButtons() +{ + QListViewItemIterator it(this); + while (it.current() ) { + it.current()->setVisible(true); + ++it; + } +} + +void ButtonSource::showButton( QChar btn ) +{ + QListViewItemIterator it(this); + while (it.current() ) { + ButtonSourceItem *item = dynamic_cast<ButtonSourceItem*>(it.current() ); + if (item && item->button().type == btn) { + it.current()->setVisible(true); + return; + } + ++it; + } +} + +void ButtonSource::hideButton( QChar btn ) +{ + QListViewItemIterator it(this); + while (it.current() ) { + ButtonSourceItem *item = dynamic_cast<ButtonSourceItem*>(it.current() ); + if (item && item->button().type == btn && !item->button().duplicate) { + it.current()->setVisible(false); + return; + } + ++it; + } +} + +bool ButtonSource::acceptDrag(QDropEvent* e) const +{ + return acceptDrops() && ButtonDrag::canDecode(e); +} + +QDragObject *ButtonSource::dragObject() +{ + ButtonSourceItem *i = dynamic_cast<ButtonSourceItem*>(selectedItem() ); + + if (i) { + ButtonDrag *bd = new ButtonDrag(i->button(), viewport(), "button_drag"); + bd->setPixmap(bitmapPixmap(i->button().icon, colorGroup().foreground() )); + return bd; + } + + return 0; +} + +ButtonDropSiteItem::ButtonDropSiteItem(const Button& btn) + : m_button(btn) +{ +} + +ButtonDropSiteItem::~ButtonDropSiteItem() +{ +} + +Button ButtonDropSiteItem::button() +{ + return m_button; +} + +int ButtonDropSiteItem::width() +{ +// return m_button.icon.width(); + return 20; +} + +int ButtonDropSiteItem::height() +{ +// return m_button.icon.height(); + return 20; +} + +void ButtonDropSiteItem::draw(QPainter *p, const QColorGroup& cg, QRect r) +{ +// p->fillRect(r, cg.base() ); + if (m_button.supported) + p->setPen(cg.foreground() ); + else + p->setPen(cg.mid() ); + QBitmap &i = m_button.icon; + p->drawPixmap(r.left()+(r.width()-i.width())/2, r.top()+(r.height()-i.height())/2, i); +} + + +ButtonDropSite::ButtonDropSite( QWidget* parent, const char* name ) + : QFrame( parent, name ), + m_selected(0) +{ + setAcceptDrops( TRUE ); + setFrameShape( WinPanel ); + setFrameShadow( Raised ); + setMinimumHeight( 26 ); + setMaximumHeight( 26 ); + setMinimumWidth( 250 ); // Ensure buttons will fit +} + +ButtonDropSite::~ButtonDropSite() +{ + clearLeft(); + clearRight(); +} + +void ButtonDropSite::clearLeft() +{ + while (!buttonsLeft.isEmpty() ) { + ButtonDropSiteItem *item = buttonsLeft.first(); + if (removeButton(item) ) { + emit buttonRemoved(item->button().type); + delete item; + } + } +} + +void ButtonDropSite::clearRight() +{ + while (!buttonsRight.isEmpty() ) { + ButtonDropSiteItem *item = buttonsRight.first(); + if (removeButton(item) ) { + emit buttonRemoved(item->button().type); + delete item; + } + } +} + +void ButtonDropSite::dragMoveEvent( QDragMoveEvent* e ) +{ + QPoint p = e->pos(); + if (leftDropArea().contains(p) || rightDropArea().contains(p) || buttonAt(p) ) { + e->accept(); + + // 2 pixel wide drop visualizer... + QRect r = contentsRect(); + int x = -1; + if (leftDropArea().contains(p) ) { + x = leftDropArea().left(); + } else if (rightDropArea().contains(p) ) { + x = rightDropArea().right()+1; + } else { + ButtonDropSiteItem *item = buttonAt(p); + if (item) { + if (p.x() < item->rect.left()+item->rect.width()/2 ) { + x = item->rect.left(); + } else { + x = item->rect.right()+1; + } + } + } + if (x != -1) { + QRect tmpRect(x, r.y(), 2, r.height() ); + if (tmpRect != m_oldDropVisualizer) { + cleanDropVisualizer(); + m_oldDropVisualizer = tmpRect; + update(tmpRect); + } + } + + } else { + e->ignore(); + + cleanDropVisualizer(); + } +} + +void ButtonDropSite::cleanDropVisualizer() +{ + if (m_oldDropVisualizer.isValid()) + { + QRect rect = m_oldDropVisualizer; + m_oldDropVisualizer = QRect(); // rect is invalid + update(rect); + } +} + +void ButtonDropSite::dragEnterEvent( QDragEnterEvent* e ) +{ + if ( ButtonDrag::canDecode( e ) ) + e->accept(); +} + +void ButtonDropSite::dragLeaveEvent( QDragLeaveEvent* /* e */ ) +{ + cleanDropVisualizer(); +} + +void ButtonDropSite::dropEvent( QDropEvent* e ) +{ + cleanDropVisualizer(); + + QPoint p = e->pos(); + + // collect information where to insert the dropped button + ButtonList *buttonList = 0; + ButtonList::iterator buttonPosition; + + if (leftDropArea().contains(p) ) { + buttonList = &buttonsLeft; + buttonPosition = buttonsLeft.end(); + } else if (rightDropArea().contains(p) ) { + buttonList = &buttonsRight; + buttonPosition = buttonsRight.begin(); + } else { + ButtonDropSiteItem *aboveItem = buttonAt(p); + if (!aboveItem) + return; // invalid drop. hasn't occured _over_ a button (or left/right dropArea), return... + + ButtonList::iterator it; + if (!getItemIterator(aboveItem, buttonList, it) ) { + // didn't find the aboveItem. unlikely to happen since buttonAt() already seems to have found + // something valid. anyway... + return; + } + + // got the list and the aboveItem position. now determine if the item should be inserted + // before aboveItem or after aboveItem. + QRect aboveItemRect = aboveItem->rect; + if (!aboveItemRect.isValid() ) + return; + + if (p.x() < aboveItemRect.left()+aboveItemRect.width()/2 ) { + // insert before the item + buttonPosition = it; + } else { + if (it != buttonList->end() ) + buttonPosition = ++it; + else + buttonPosition = it; // already at the end(), can't increment the iterator! + } + } + + // know where to insert the button. now see if we can use an existing item (drag within the widget = move) + // orneed to create a new one + ButtonDropSiteItem *buttonItem = 0; + if (e->source() == this && m_selected) { + ButtonList *oldList = 0; + ButtonList::iterator oldPos; + if (getItemIterator(m_selected, oldList, oldPos) ) { + if (oldPos == buttonPosition) + return; // button didn't change its position during the drag... + + oldList->remove(oldPos); + buttonItem = m_selected; + } else { + return; // m_selected not found, return... + } + } else { + // create new button from the drop object... + Button btn; + if (ButtonDrag::decode(e, btn) ) { + buttonItem = new ButtonDropSiteItem(btn); + } else { + return; // something has gone wrong while we were trying to decode the drop event + } + } + + // now the item can actually be inserted into the list! :) + (*buttonList).insert(buttonPosition, buttonItem); + emit buttonAdded(buttonItem->button().type); + emit changed(); + recalcItemGeometry(); + update(); +} + +bool ButtonDropSite::getItemIterator(ButtonDropSiteItem *item, ButtonList* &list, ButtonList::iterator &iterator) +{ + if (!item) + return false; + + ButtonList::iterator it = buttonsLeft.find(item); // try the left list first... + if (it == buttonsLeft.end() ) { + it = buttonsRight.find(item); // try the right list... + if (it == buttonsRight.end() ) { + return false; // item hasn't been found in one of the list, return... + } else { + list = &buttonsRight; + iterator = it; + } + } else { + list = &buttonsLeft; + iterator = it; + } + + return true; +} + +QRect ButtonDropSite::leftDropArea() +{ + // return a 10 pixel drop area... + QRect r = contentsRect(); + + int leftButtonsWidth = calcButtonListWidth(buttonsLeft); + return QRect(r.left()+leftButtonsWidth, r.top(), 10, r.height() ); +} + +QRect ButtonDropSite::rightDropArea() +{ + // return a 10 pixel drop area... + QRect r = contentsRect(); + + int rightButtonsWidth = calcButtonListWidth(buttonsRight); + return QRect(r.right()-rightButtonsWidth-10, r.top(), 10, r.height() ); +} + +void ButtonDropSite::mousePressEvent( QMouseEvent* e ) +{ + // TODO: only start the real drag after some drag distance + m_selected = buttonAt(e->pos() ); + if (m_selected) { + ButtonDrag *bd = new ButtonDrag(m_selected->button(), this); + bd->setPixmap(bitmapPixmap(m_selected->button().icon, colorGroup().foreground() ) ); + bd->dragMove(); + } +} + +void ButtonDropSite::resizeEvent(QResizeEvent*) +{ + recalcItemGeometry(); +} + +void ButtonDropSite::recalcItemGeometry() +{ + QRect r = contentsRect(); + + // update the geometry of the items in the left button list + int offset = r.left(); + for (ButtonList::const_iterator it = buttonsLeft.begin(); it != buttonsLeft.end(); ++it) { + int w = (*it)->width(); + (*it)->rect = QRect(offset, r.top(), w, (*it)->height() ); + offset += w; + } + + // the right button list... + offset = r.right() - calcButtonListWidth(buttonsRight); + for (ButtonList::const_iterator it = buttonsRight.begin(); it != buttonsRight.end(); ++it) { + int w = (*it)->width(); + (*it)->rect = QRect(offset, r.top(), w, (*it)->height() ); + offset += w; + } +} + +ButtonDropSiteItem *ButtonDropSite::buttonAt(QPoint p) { + // try to find the item in the left button list + for (ButtonList::const_iterator it = buttonsLeft.begin(); it != buttonsLeft.end(); ++it) { + if ( (*it)->rect.contains(p) ) { + return *it; + } + } + + // try to find the item in the right button list + for (ButtonList::const_iterator it = buttonsRight.begin(); it != buttonsRight.end(); ++it) { + if ( (*it)->rect.contains(p) ) { + return *it; + } + } + + return 0; +} + +bool ButtonDropSite::removeButton(ButtonDropSiteItem *item) { + if (!item) + return false; + + // try to remove the item from the left button list + if (buttonsLeft.remove(item) >= 1) { + return true; + } + + // try to remove the item from the right button list + if (buttonsRight.remove(item) >= 1) { + return true; + } + + return false; +} + +int ButtonDropSite::calcButtonListWidth(const ButtonList& btns) +{ + int w = 0; + for (ButtonList::const_iterator it = btns.begin(); it != btns.end(); ++it) { + w += (*it)->width(); + } + + return w; +} + +bool ButtonDropSite::removeSelectedButton() +{ + bool succ = removeButton(m_selected); + if (succ) { + emit buttonRemoved(m_selected->button().type); + emit changed(); + delete m_selected; + m_selected = 0; + recalcItemGeometry(); + update(); // repaint... + } + + return succ; +} + +void ButtonDropSite::drawButtonList(QPainter *p, const ButtonList& btns, int offset) +{ + for (ButtonList::const_iterator it = btns.begin(); it != btns.end(); ++it) { + QRect itemRect = (*it)->rect; + if (itemRect.isValid() ) { + (*it)->draw(p, colorGroup(), itemRect); + } + offset += (*it)->width(); + } +} + +void ButtonDropSite::drawContents( QPainter* p ) +{ + int leftoffset = calcButtonListWidth( buttonsLeft ); + int rightoffset = calcButtonListWidth( buttonsRight ); + int offset = 3; + + QRect r = contentsRect(); + + // Shrink by 1 + r.moveBy(1 + leftoffset, 1); + r.setWidth( r.width() - 2 - leftoffset - rightoffset ); + r.setHeight( r.height() - 2 ); + + drawButtonList( p, buttonsLeft, offset ); + + QColor c1( 0x0A, 0x5F, 0x89 ); // KDE 2 titlebar default colour + p->fillRect( r, c1 ); + p->setPen( Qt::white ); + p->setFont( QFont( KGlobalSettings::generalFont().family(), 12, QFont::Bold) ); + p->drawText( r, AlignLeft | AlignVCenter, i18n("KDE") ); + + offset = geometry().width() - 3 - rightoffset; + drawButtonList( p, buttonsRight, offset ); + + if (m_oldDropVisualizer.isValid() ) + { + p->fillRect(m_oldDropVisualizer, Dense4Pattern); + } +} + +ButtonSourceItem::ButtonSourceItem(QListView * parent, const Button& btn) + : QListViewItem(parent), + m_button(btn), + m_dirty(true) +{ + setButton(btn); +} + +ButtonSourceItem::~ButtonSourceItem() +{ +} + +void ButtonSourceItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) +{ + // we need the color group cg, so to the work here, not in setButton... + if (m_dirty) { + if (m_button.supported) { + setPixmap(0, bitmapPixmap(m_button.icon, cg.foreground() ) ); + } else { + setPixmap(0, bitmapPixmap(m_button.icon, cg.mid() ) ); + } + m_dirty = false; + } + + if (m_button.supported) { + QListViewItem::paintCell(p,cg,column,width,align); + } else { + // grey out unsupported buttons + QColorGroup cg2 = cg; + cg2.setColor(QColorGroup::Text, cg.mid() ); + QListViewItem::paintCell(p,cg2,column,width,align); + } +} + +void ButtonSourceItem::setButton(const Button& btn) +{ + m_button = btn; + m_dirty = true; // update the pixmap when in paintCell()... + if (btn.supported) { + setText(0, btn.name); + } else { + setText(0, i18n("%1 (unavailable)").arg(btn.name) ); + } +} + +Button ButtonSourceItem::button() const +{ + return m_button; +} + + +ButtonPositionWidget::ButtonPositionWidget(QWidget *parent, const char* name) + : QWidget(parent,name), + m_factory(0) +{ + QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() ); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + QLabel* label = new QLabel( this ); + m_dropSite = new ButtonDropSite( this ); + label->setAlignment( int( QLabel::WordBreak ) ); + label->setText( i18n( "To add or remove titlebar buttons, simply <i>drag</i> items " + "between the available item list and the titlebar preview. Similarly, " + "drag items within the titlebar preview to re-position them.") ); + m_buttonSource = new ButtonSource(this, "button_source"); + + layout->addWidget(label); + layout->addWidget(m_dropSite); + layout->addWidget(m_buttonSource); + + connect( m_dropSite, SIGNAL(buttonAdded(QChar)), m_buttonSource, SLOT(hideButton(QChar)) ); + connect( m_dropSite, SIGNAL(buttonRemoved(QChar)), m_buttonSource, SLOT(showButton(QChar)) ); + connect( m_buttonSource, SIGNAL(dropped(QDropEvent*, QListViewItem*)), m_dropSite, SLOT(removeSelectedButton()) ); + + connect( m_dropSite, SIGNAL(changed()), SIGNAL(changed()) ); + + // insert all possible buttons into the source (backwards to keep the preferred order...) + bool dummy; + new ButtonSourceItem(m_buttonSource, getButton('R', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('L', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('B', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('F', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('X', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('A', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('I', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('H', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('S', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('M', dummy) ); + new ButtonSourceItem(m_buttonSource, getButton('_', dummy) ); +} + +ButtonPositionWidget::~ButtonPositionWidget() +{ +} + +void ButtonPositionWidget::setDecorationFactory(KDecorationFactory *factory) +{ + if (!factory) + return; + + m_factory = factory; + + // get the list of supported buttons + if (m_factory->supports(KDecorationDefines::AbilityAnnounceButtons) ) { + QString supportedButtons; + + if (m_factory->supports(KDecorationDefines::AbilityButtonMenu) ) + supportedButtons.append('M'); + if (m_factory->supports(KDecorationDefines::AbilityButtonOnAllDesktops) ) + supportedButtons.append('S'); + if (m_factory->supports(KDecorationDefines::AbilityButtonSpacer) ) + supportedButtons.append('_'); + if (m_factory->supports(KDecorationDefines::AbilityButtonHelp) ) + supportedButtons.append('H'); + if (m_factory->supports(KDecorationDefines::AbilityButtonMinimize) ) + supportedButtons.append('I'); + if (m_factory->supports(KDecorationDefines::AbilityButtonMaximize) ) + supportedButtons.append('A'); + if (m_factory->supports(KDecorationDefines::AbilityButtonClose) ) + supportedButtons.append('X'); + if (m_factory->supports(KDecorationDefines::AbilityButtonAboveOthers) ) + supportedButtons.append('F'); + if (m_factory->supports(KDecorationDefines::AbilityButtonBelowOthers) ) + supportedButtons.append('B'); + if (m_factory->supports(KDecorationDefines::AbilityButtonShade) ) + supportedButtons.append('L'); + if (m_factory->supports(KDecorationDefines::AbilityButtonResize) ) + supportedButtons.append('R'); + + m_supportedButtons = supportedButtons; + } else { + // enable only buttons available before AbilityButton* introduction + m_supportedButtons = "MSHIAX_"; + } + + // update the button lists... + // 1. set status on the source items... + QListViewItemIterator it(m_buttonSource); + while (it.current() ) { + ButtonSourceItem *i = dynamic_cast<ButtonSourceItem*>(it.current() ); + if (i) { + Button b = i->button(); + b.supported = m_supportedButtons.contains(b.type); + i->setButton(b); + } + ++it; + } + // 2. rebuild the drop site items... + setButtonsLeft(buttonsLeft() ); + setButtonsRight(buttonsRight() ); +} + +Button ButtonPositionWidget::getButton(QChar type, bool& success) { + success = true; + + if (type == 'R') { + QBitmap bmp(resize_width, resize_height, resize_bits, true); + bmp.setMask(bmp); + return Button(i18n("Resize"), bmp, 'R', false, m_supportedButtons.contains('R') ); + } else if (type == 'L') { + QBitmap bmp(shade_width, shade_height, shade_bits, true); + bmp.setMask(bmp); + return Button(i18n("Shade"), bmp, 'L', false, m_supportedButtons.contains('L') ); + } else if (type == 'B') { + QBitmap bmp(keepbelowothers_width, keepbelowothers_height, keepbelowothers_bits, true); + bmp.setMask(bmp); + return Button(i18n("Keep Below Others"), bmp, 'B', false, m_supportedButtons.contains('B') ); + } else if (type == 'F') { + QBitmap bmp(keepaboveothers_width, keepaboveothers_height, keepaboveothers_bits, true); + bmp.setMask(bmp); + return Button(i18n("Keep Above Others"), bmp, 'F', false, m_supportedButtons.contains('F') ); + } else if (type == 'X') { + QBitmap bmp(close_width, close_height, close_bits, true); + bmp.setMask(bmp); + return Button(i18n("Close"), bmp, 'X', false, m_supportedButtons.contains('X') ); + } else if (type == 'A') { + QBitmap bmp(maximize_width, maximize_height, maximize_bits, true); + bmp.setMask(bmp); + return Button(i18n("Maximize"), bmp, 'A', false, m_supportedButtons.contains('A') ); + } else if (type == 'I') { + QBitmap bmp(minimize_width, minimize_height, minimize_bits, true); + bmp.setMask(bmp); + return Button(i18n("Minimize"), bmp, 'I', false, m_supportedButtons.contains('I') ); + } else if (type == 'H') { + QBitmap bmp(help_width, help_height, help_bits, true); + bmp.setMask(bmp); + return Button(i18n("Help"), bmp, 'H', false, m_supportedButtons.contains('H') ); + } else if (type == 'S') { + QBitmap bmp(onalldesktops_width, onalldesktops_height, onalldesktops_bits, true); + bmp.setMask(bmp); + return Button(i18n("On All Desktops"), bmp, 'S', false, m_supportedButtons.contains('S') ); + } else if (type == 'M') { + QBitmap bmp(menu_width, menu_height, menu_bits, true); + bmp.setMask(bmp); + return Button(i18n("Menu"), bmp, 'M', false, m_supportedButtons.contains('M') ); + } else if (type == '_') { + QBitmap bmp(spacer_width, spacer_height, spacer_bits, true); + bmp.setMask(bmp); + return Button(i18n("--- spacer ---"), bmp, '_', true, m_supportedButtons.contains('_') ); + } else { + success = false; + return Button(); + } +} + +QString ButtonPositionWidget::buttonsLeft() const +{ + ButtonList btns = m_dropSite->buttonsLeft; + QString btnString = ""; + for (ButtonList::const_iterator it = btns.begin(); it != btns.end(); ++it) { + btnString.append( (*it)->button().type ); + } + return btnString; +} + +QString ButtonPositionWidget::buttonsRight() const +{ + ButtonList btns = m_dropSite->buttonsRight; + QString btnString = ""; + for (ButtonList::const_iterator it = btns.begin(); it != btns.end(); ++it) { + btnString.append( (*it)->button().type ); + } + return btnString; +} + +void ButtonPositionWidget::setButtonsLeft(const QString &buttons) +{ + // to keep the button lists consistent, first remove all left buttons, then add buttons again... + m_dropSite->clearLeft(); + + for (uint i = 0; i < buttons.length(); ++i) { + bool succ = false; + Button btn = getButton(buttons[i], succ); + if (succ) { + m_dropSite->buttonsLeft.append(new ButtonDropSiteItem(btn) ); + m_buttonSource->hideButton(btn.type); + } + } + m_dropSite->recalcItemGeometry(); + m_dropSite->update(); +} + +void ButtonPositionWidget::setButtonsRight(const QString &buttons) +{ + // to keep the button lists consistent, first remove all left buttons, then add buttons again... + m_dropSite->clearRight(); + + for (uint i = 0; i < buttons.length(); ++i) { + bool succ = false; + Button btn = getButton(buttons[i], succ); + if (succ) { + m_dropSite->buttonsRight.append(new ButtonDropSiteItem(btn) ); + m_buttonSource->hideButton(btn.type); + } + } + m_dropSite->recalcItemGeometry(); + m_dropSite->update(); +} + +#include "buttons.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/kcmkwin/kwindecoration/buttons.h b/kwin/kcmkwin/kwindecoration/buttons.h new file mode 100644 index 000000000..a3db6266e --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/buttons.h @@ -0,0 +1,227 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2004, Sandro Giessl <sandro@giessl.com> + Copyright (c) 2001 + Karol Szwed <gallium@kde.org> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef __BUTTONS_H_ +#define __BUTTONS_H_ + +#include <qbitmap.h> +#include <qevent.h> +#include <qdragobject.h> +#include <qlistbox.h> + +#include <klistview.h> + +class KDecorationFactory; + +/** + * This class holds the button data. + */ +class Button +{ + public: + Button(); + Button(const QString& name, const QBitmap& icon, QChar type, bool duplicate, bool supported); + virtual ~Button(); + + QString name; + QBitmap icon; + QChar type; + bool duplicate; + bool supported; +}; + +class ButtonDrag : public QStoredDrag +{ + public: + ButtonDrag( Button btn, QWidget* parent, const char* name=0 ); + ~ButtonDrag() {}; + + static bool canDecode( QDropEvent* e ); + static bool decode( QDropEvent* e, Button& btn ); +}; + +/** + * This is plugged into ButtonDropSite + */ +class ButtonDropSiteItem +{ + public: + ButtonDropSiteItem(const Button& btn); + ~ButtonDropSiteItem(); + + Button button(); + + QRect rect; + int width(); + int height(); + + void draw(QPainter *p, const QColorGroup& cg, QRect rect); + + private: + Button m_button; +}; + +/** + * This is plugged into ButtonSource + */ +class ButtonSourceItem : public QListViewItem +{ + public: + ButtonSourceItem(QListView * parent, const Button& btn); + virtual ~ButtonSourceItem(); + + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align); + + void setButton(const Button& btn); + Button button() const; + private: + Button m_button; + bool m_dirty; +}; + +/** + * Implements the button drag source list view + */ +class ButtonSource : public KListView +{ + Q_OBJECT + + public: + ButtonSource(QWidget *parent = 0, const char* name = 0); + virtual ~ButtonSource(); + + QSize sizeHint() const; + + void hideAllButtons(); + void showAllButtons(); + + public slots: + void hideButton(QChar btn); + void showButton(QChar btn); + + protected: + bool acceptDrag(QDropEvent* e) const; + virtual QDragObject *dragObject(); +}; + +typedef QValueList<ButtonDropSiteItem*> ButtonList; + +/** + * This class renders and handles the demo titlebar dropsite + */ +class ButtonDropSite: public QFrame +{ + Q_OBJECT + + public: + ButtonDropSite( QWidget* parent=0, const char* name=0 ); + ~ButtonDropSite(); + + // Allow external classes access our buttons - ensure buttons are + // not duplicated however. + ButtonList buttonsLeft; + ButtonList buttonsRight; + void clearLeft(); + void clearRight(); + + signals: + void buttonAdded(QChar btn); + void buttonRemoved(QChar btn); + void changed(); + + public slots: + bool removeSelectedButton(); ///< This slot is called after we drop on the item listbox... + void recalcItemGeometry(); ///< Call this whenever the item list changes... updates the items' rect property + + protected: + void resizeEvent(QResizeEvent*); + void dragEnterEvent( QDragEnterEvent* e ); + void dragMoveEvent( QDragMoveEvent* e ); + void dragLeaveEvent( QDragLeaveEvent* e ); + void dropEvent( QDropEvent* e ); + void mousePressEvent( QMouseEvent* e ); ///< Starts dragging a button... + + void drawContents( QPainter* p ); + ButtonDropSiteItem *buttonAt(QPoint p); + bool removeButton(ButtonDropSiteItem *item); + int calcButtonListWidth(const ButtonList& buttons); ///< Computes the total space the buttons will take in the titlebar + void drawButtonList(QPainter *p, const ButtonList& buttons, int offset); + + QRect leftDropArea(); + QRect rightDropArea(); + + private: + /** + * Try to find the item. If found, set its list and iterator and return true, else return false + */ + bool getItemIterator(ButtonDropSiteItem *item, ButtonList* &list, ButtonList::iterator &iterator); + + void cleanDropVisualizer(); + QRect m_oldDropVisualizer; + + ButtonDropSiteItem *m_selected; +}; + +class ButtonPositionWidget : public QWidget +{ + Q_OBJECT + + public: + ButtonPositionWidget(QWidget *parent = 0, const char* name = 0); + ~ButtonPositionWidget(); + + /** + * set the factory, so the class e.g. knows which buttons are supported by the client + */ + void setDecorationFactory(KDecorationFactory *factory); + + QString buttonsLeft() const; + QString buttonsRight() const; + void setButtonsLeft(const QString &buttons); + void setButtonsRight(const QString &buttons); + + signals: + void changed(); + + private: + void clearButtonList(const ButtonList& btns); + Button getButton(QChar type, bool& success); + + ButtonDropSite* m_dropSite; + ButtonSource *m_buttonSource; + + KDecorationFactory *m_factory; + QString m_supportedButtons; +}; + + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/kcmkwin/kwindecoration/kwindecoration.cpp b/kwin/kcmkwin/kwindecoration/kwindecoration.cpp new file mode 100644 index 000000000..7435781bc --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/kwindecoration.cpp @@ -0,0 +1,613 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2001 + Karol Szwed <gallium@kde.org> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <assert.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qgroupbox.h> +#include <qcheckbox.h> +#include <qtabwidget.h> +#include <qvbox.h> +#include <qlabel.h> +#include <qfile.h> +#include <qslider.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <kdebug.h> +#include <kdesktopfile.h> +#include <kstandarddirs.h> +#include <kglobal.h> +#include <klocale.h> +#include <kdialog.h> +#include <kgenericfactory.h> +#include <kaboutdata.h> +#include <dcopclient.h> + +#include "kwindecoration.h" +#include "preview.h" +#include <kdecoration_plugins_p.h> +#include <kdecorationfactory.h> + +// KCModule plugin interface +// ========================= +typedef KGenericFactory<KWinDecorationModule, QWidget> KWinDecoFactory; +K_EXPORT_COMPONENT_FACTORY( kcm_kwindecoration, KWinDecoFactory("kcmkwindecoration") ) + +KWinDecorationModule::KWinDecorationModule(QWidget* parent, const char* name, const QStringList &) + : DCOPObject("KWinClientDecoration"), + KCModule(KWinDecoFactory::instance(), parent, name), + kwinConfig("kwinrc"), + pluginObject(0) +{ + kwinConfig.setGroup("Style"); + plugins = new KDecorationPreviewPlugins( &kwinConfig ); + + QVBoxLayout* layout = new QVBoxLayout(this, 0, KDialog::spacingHint()); + +// Save this for later... +// cbUseMiniWindows = new QCheckBox( i18n( "Render mini &titlebars for all windows"), checkGroup ); +// QWhatsThis::add( cbUseMiniWindows, i18n( "Note that this option is not available on all styles yet!" ) ); + + tabWidget = new QTabWidget( this ); + layout->addWidget( tabWidget ); + + // Page 1 (General Options) + QWidget *pluginPage = new QWidget( tabWidget ); + + QVBoxLayout* pluginLayout = new QVBoxLayout(pluginPage, KDialog::marginHint(), KDialog::spacingHint()); + + // decoration chooser + decorationList = new KComboBox( pluginPage ); + QString whatsThis = i18n("Select the window decoration. This is the look and feel of both " + "the window borders and the window handle."); + QWhatsThis::add(decorationList, whatsThis); + pluginLayout->addWidget(decorationList); + + QGroupBox *pluginSettingsGrp = new QGroupBox( i18n("Decoration Options"), pluginPage ); + pluginSettingsGrp->setColumnLayout( 0, Vertical ); + pluginSettingsGrp->setFlat( true ); + pluginSettingsGrp->layout()->setMargin( 0 ); + pluginSettingsGrp->layout()->setSpacing( KDialog::spacingHint() ); + pluginLayout->addWidget( pluginSettingsGrp ); + + pluginLayout->addStretch(); + + // Border size chooser + lBorder = new QLabel (i18n("B&order size:"), pluginSettingsGrp); + cBorder = new QComboBox(pluginSettingsGrp); + lBorder->setBuddy(cBorder); + QWhatsThis::add( cBorder, i18n( "Use this combobox to change the border size of the decoration." )); + lBorder->hide(); + cBorder->hide(); + QHBoxLayout *borderSizeLayout = new QHBoxLayout(pluginSettingsGrp->layout() ); + borderSizeLayout->addWidget(lBorder); + borderSizeLayout->addWidget(cBorder); + borderSizeLayout->addStretch(); + + pluginConfigWidget = new QVBox(pluginSettingsGrp); + pluginSettingsGrp->layout()->add( pluginConfigWidget ); + + // Page 2 (Button Selector) + QWidget* buttonPage = new QWidget( tabWidget ); + QVBoxLayout* buttonLayout = new QVBoxLayout(buttonPage, KDialog::marginHint(), KDialog::spacingHint()); + + cbShowToolTips = new QCheckBox( + i18n("&Show window button tooltips"), buttonPage ); + QWhatsThis::add( cbShowToolTips, + i18n( "Enabling this checkbox will show window button tooltips. " + "If this checkbox is off, no window button tooltips will be shown.")); + + cbUseCustomButtonPositions = new QCheckBox( + i18n("Use custom titlebar button &positions"), buttonPage ); + QWhatsThis::add( cbUseCustomButtonPositions, + i18n( "The appropriate settings can be found in the \"Buttons\" Tab; " + "please note that this option is not available on all styles yet." ) ); + + buttonLayout->addWidget( cbShowToolTips ); + buttonLayout->addWidget( cbUseCustomButtonPositions ); + + // Add nifty dnd button modification widgets + buttonPositionWidget = new ButtonPositionWidget(buttonPage, "button_position_widget"); + buttonPositionWidget->setDecorationFactory(plugins->factory() ); + QHBoxLayout* buttonControlLayout = new QHBoxLayout(buttonLayout); + buttonControlLayout->addSpacing(20); + buttonControlLayout->addWidget(buttonPositionWidget); +// buttonLayout->addStretch(); + + // preview + QVBoxLayout* previewLayout = new QVBoxLayout(layout, KDialog::spacingHint() ); + previewLayout->setMargin( KDialog::marginHint() ); + + preview = new KDecorationPreview( this ); + previewLayout->addWidget(preview); + + preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + // Load all installed decorations into memory + // Set up the decoration lists and other UI settings + findDecorations(); + createDecorationList(); + readConfig( &kwinConfig ); + resetPlugin( &kwinConfig ); + + tabWidget->insertTab( pluginPage, i18n("&Window Decoration") ); + tabWidget->insertTab( buttonPage, i18n("&Buttons") ); + + connect( buttonPositionWidget, SIGNAL(changed()), this, SLOT(slotButtonsChanged()) ); // update preview etc. + connect( buttonPositionWidget, SIGNAL(changed()), this, SLOT(slotSelectionChanged()) ); // emit changed()... + connect( decorationList, SIGNAL(activated(const QString&)), SLOT(slotSelectionChanged()) ); + connect( decorationList, SIGNAL(activated(const QString&)), + SLOT(slotChangeDecoration(const QString&)) ); + connect( cbUseCustomButtonPositions, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); + connect(cbUseCustomButtonPositions, SIGNAL(toggled(bool)), buttonPositionWidget, SLOT(setEnabled(bool))); + connect(cbUseCustomButtonPositions, SIGNAL(toggled(bool)), this, SLOT(slotButtonsChanged()) ); + connect( cbShowToolTips, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); + connect( cBorder, SIGNAL( activated( int )), SLOT( slotBorderChanged( int ))); +// connect( cbUseMiniWindows, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); + + // Allow kwin dcop signal to update our selection list + connectDCOPSignal("kwin", 0, "dcopResetAllClients()", "dcopUpdateClientList()", false); + + KAboutData *about = + new KAboutData(I18N_NOOP("kcmkwindecoration"), + I18N_NOOP("Window Decoration Control Module"), + 0, 0, KAboutData::License_GPL, + I18N_NOOP("(c) 2001 Karol Szwed")); + about->addAuthor("Karol Szwed", 0, "gallium@kde.org"); + setAboutData(about); +} + + +KWinDecorationModule::~KWinDecorationModule() +{ + delete preview; // needs to be destroyed before plugins + delete plugins; +} + + +// Find all theme desktop files in all 'data' dirs owned by kwin. +// And insert these into a DecorationInfo structure +void KWinDecorationModule::findDecorations() +{ + QStringList dirList = KGlobal::dirs()->findDirs("data", "kwin"); + QStringList::ConstIterator it; + + for (it = dirList.begin(); it != dirList.end(); it++) + { + QDir d(*it); + if (d.exists()) + for (QFileInfoListIterator it(*d.entryInfoList()); it.current(); ++it) + { + QString filename(it.current()->absFilePath()); + if (KDesktopFile::isDesktopFile(filename)) + { + KDesktopFile desktopFile(filename); + QString libName = desktopFile.readEntry("X-KDE-Library"); + + if (!libName.isEmpty() && libName.startsWith( "kwin3_" )) + { + DecorationInfo di; + di.name = desktopFile.readName(); + di.libraryName = libName; + decorations.append( di ); + } + } + } + } +} + + +// Fills the decorationList with a list of available kwin decorations +void KWinDecorationModule::createDecorationList() +{ + QValueList<DecorationInfo>::ConstIterator it; + + // Sync with kwin hardcoded KDE2 style which has no desktop item + QStringList decorationNames; + decorationNames.append( i18n("KDE 2") ); + for (it = decorations.begin(); it != decorations.end(); ++it) + { + decorationNames.append((*it).name); + } + decorationNames.sort(); + decorationList->insertStringList(decorationNames); +} + + +// Reset the decoration plugin to what the user just selected +void KWinDecorationModule::slotChangeDecoration( const QString & text) +{ + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + // Let the user see config options for the currently selected decoration + resetPlugin( &kwinConfig, text ); +} + + +// This is the selection handler setting +void KWinDecorationModule::slotSelectionChanged() +{ + emit KCModule::changed(true); +} + +static const char* const border_names[ KDecorationDefines::BordersCount ] = + { + I18N_NOOP( "Tiny" ), + I18N_NOOP( "Normal" ), + I18N_NOOP( "Large" ), + I18N_NOOP( "Very Large" ), + I18N_NOOP( "Huge" ), + I18N_NOOP( "Very Huge" ), + I18N_NOOP( "Oversized" ) + }; + +int KWinDecorationModule::borderSizeToIndex( BorderSize size, QValueList< BorderSize > sizes ) +{ + int pos = 0; + for( QValueList< BorderSize >::ConstIterator it = sizes.begin(); + it != sizes.end(); + ++it, ++pos ) + if( size <= *it ) + break; + return pos; +} + +KDecorationDefines::BorderSize KWinDecorationModule::indexToBorderSize( int index, + QValueList< BorderSize > sizes ) +{ + QValueList< BorderSize >::ConstIterator it = sizes.begin(); + for(; + it != sizes.end(); + ++it, --index ) + if( index == 0 ) + break; + return *it; +} + +void KWinDecorationModule::slotBorderChanged( int size ) +{ + if( lBorder->isHidden()) + return; + emit KCModule::changed( true ); + QValueList< BorderSize > sizes; + if( plugins->factory() != NULL ) + sizes = plugins->factory()->borderSizes(); + assert( sizes.count() >= 2 ); + border_size = indexToBorderSize( size, sizes ); + + // update preview + preview->setTempBorderSize(plugins, border_size); +} + +void KWinDecorationModule::slotButtonsChanged() +{ + // update preview + preview->setTempButtons(plugins, cbUseCustomButtonPositions->isChecked(), buttonPositionWidget->buttonsLeft(), buttonPositionWidget->buttonsRight() ); +} + +QString KWinDecorationModule::decorationName( QString& libName ) +{ + QString decoName; + + QValueList<DecorationInfo>::Iterator it; + for( it = decorations.begin(); it != decorations.end(); ++it ) + if ( (*it).libraryName == libName ) + { + decoName = (*it).name; + break; + } + + return decoName; +} + + +QString KWinDecorationModule::decorationLibName( const QString& name ) +{ + QString libName; + + // Find the corresponding library name to that of + // the current plugin name + QValueList<DecorationInfo>::Iterator it; + for( it = decorations.begin(); it != decorations.end(); ++it ) + if ( (*it).name == name ) + { + libName = (*it).libraryName; + break; + } + + if (libName.isEmpty()) + libName = "kwin_default"; // KDE 2 + + return libName; +} + + +// Loads/unloads and inserts the decoration config plugin into the +// pluginConfigWidget, allowing for dynamic configuration of decorations +void KWinDecorationModule::resetPlugin( KConfig* conf, const QString& currentDecoName ) +{ + // Config names are "kwin_icewm_config" + // for "kwin3_icewm" kwin client + + QString oldName = styleToConfigLib( oldLibraryName ); + + QString currentName; + if (!currentDecoName.isEmpty()) + currentName = decorationLibName( currentDecoName ); // Use what the user selected + else + currentName = currentLibraryName; // Use what was read from readConfig() + + if( plugins->loadPlugin( currentName ) + && preview->recreateDecoration( plugins )) + preview->enablePreview(); + else + preview->disablePreview(); + plugins->destroyPreviousPlugin(); + + checkSupportedBorderSizes(); + + // inform buttonPositionWidget about the new factory... + buttonPositionWidget->setDecorationFactory(plugins->factory() ); + + currentName = styleToConfigLib( currentName ); + + // Delete old plugin widget if it exists + delete pluginObject; + pluginObject = 0; + + // Use klibloader for library manipulation + KLibLoader* loader = KLibLoader::self(); + + // Free the old library if possible + if (!oldLibraryName.isNull()) + loader->unloadLibrary( QFile::encodeName(oldName) ); + + KLibrary* library = loader->library( QFile::encodeName(currentName) ); + if (library != NULL) + { + void* alloc_ptr = library->symbol("allocate_config"); + + if (alloc_ptr != NULL) + { + allocatePlugin = (QObject* (*)(KConfig* conf, QWidget* parent))alloc_ptr; + pluginObject = (QObject*)(allocatePlugin( conf, pluginConfigWidget )); + + // connect required signals and slots together... + connect( pluginObject, SIGNAL(changed()), this, SLOT(slotSelectionChanged()) ); + connect( this, SIGNAL(pluginLoad(KConfig*)), pluginObject, SLOT(load(KConfig*)) ); + connect( this, SIGNAL(pluginSave(KConfig*)), pluginObject, SLOT(save(KConfig*)) ); + connect( this, SIGNAL(pluginDefaults()), pluginObject, SLOT(defaults()) ); + pluginConfigWidget->show(); + return; + } + } + + pluginConfigWidget->hide(); +} + + +// Reads the kwin config settings, and sets all UI controls to those settings +// Updating the config plugin if required +void KWinDecorationModule::readConfig( KConfig* conf ) +{ + // General tab + // ============ + cbShowToolTips->setChecked( conf->readBoolEntry("ShowToolTips", true )); +// cbUseMiniWindows->setChecked( conf->readBoolEntry("MiniWindowBorders", false)); + + // Find the corresponding decoration name to that of + // the current plugin library name + + oldLibraryName = currentLibraryName; + currentLibraryName = conf->readEntry("PluginLib", + ((QPixmap::defaultDepth() > 8) ? "kwin_plastik" : "kwin_quartz")); + QString decoName = decorationName( currentLibraryName ); + + // If we are using the "default" kde client, use the "default" entry. + if (decoName.isEmpty()) + decoName = i18n("KDE 2"); + + int numDecos = decorationList->count(); + for (int i = 0; i < numDecos; ++i) + { + if (decorationList->text(i) == decoName) + { + decorationList->setCurrentItem(i); + break; + } + } + + // Buttons tab + // ============ + bool customPositions = conf->readBoolEntry("CustomButtonPositions", false); + cbUseCustomButtonPositions->setChecked( customPositions ); + buttonPositionWidget->setEnabled( customPositions ); + // Menu and onAllDesktops buttons are default on LHS + buttonPositionWidget->setButtonsLeft( conf->readEntry("ButtonsOnLeft", "MS") ); + // Help, Minimize, Maximize and Close are default on RHS + buttonPositionWidget->setButtonsRight( conf->readEntry("ButtonsOnRight", "HIAX") ); + + int bsize = conf->readNumEntry( "BorderSize", BorderNormal ); + if( bsize >= BorderTiny && bsize < BordersCount ) + border_size = static_cast< BorderSize >( bsize ); + else + border_size = BorderNormal; + checkSupportedBorderSizes(); + + emit KCModule::changed(false); +} + + +// Writes the selected user configuration to the kwin config file +void KWinDecorationModule::writeConfig( KConfig* conf ) +{ + QString name = decorationList->currentText(); + QString libName = decorationLibName( name ); + + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + // General settings + conf->writeEntry("PluginLib", libName); + conf->writeEntry("CustomButtonPositions", cbUseCustomButtonPositions->isChecked()); + conf->writeEntry("ShowToolTips", cbShowToolTips->isChecked()); +// conf->writeEntry("MiniWindowBorders", cbUseMiniWindows->isChecked()); + + // Button settings + conf->writeEntry("ButtonsOnLeft", buttonPositionWidget->buttonsLeft() ); + conf->writeEntry("ButtonsOnRight", buttonPositionWidget->buttonsRight() ); + conf->writeEntry("BorderSize", border_size ); + + oldLibraryName = currentLibraryName; + currentLibraryName = libName; + + // We saved, so tell kcmodule that there have been no new user changes made. + emit KCModule::changed(false); +} + + +void KWinDecorationModule::dcopUpdateClientList() +{ + // Changes the current active ListBox item, and + // Loads a new plugin configuration tab if required. + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + readConfig( &kwinConfig ); + resetPlugin( &kwinConfig ); +} + + +// Virutal functions required by KCModule +void KWinDecorationModule::load() +{ + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + // Reset by re-reading the config + readConfig( &kwinConfig ); + resetPlugin( &kwinConfig ); +} + + +void KWinDecorationModule::save() +{ + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + writeConfig( &kwinConfig ); + emit pluginSave( &kwinConfig ); + + kwinConfig.sync(); + resetKWin(); + // resetPlugin() will get called via the above DCOP function +} + + +void KWinDecorationModule::defaults() +{ + // Set the KDE defaults + cbUseCustomButtonPositions->setChecked( false ); + buttonPositionWidget->setEnabled( false ); + cbShowToolTips->setChecked( true ); +// cbUseMiniWindows->setChecked( false); +// Don't set default for now +// decorationList->setSelected( +// decorationList->findItem( i18n("KDE 2") ), true ); // KDE classic client + + buttonPositionWidget->setButtonsLeft("MS"); + buttonPositionWidget->setButtonsRight("HIAX"); + + border_size = BorderNormal; + checkSupportedBorderSizes(); + + // Set plugin defaults + emit pluginDefaults(); +} + +void KWinDecorationModule::checkSupportedBorderSizes() +{ + QValueList< BorderSize > sizes; + if( plugins->factory() != NULL ) + sizes = plugins->factory()->borderSizes(); + if( sizes.count() < 2 ) { + lBorder->hide(); + cBorder->hide(); + } else { + cBorder->clear(); + for (QValueList<BorderSize>::const_iterator it = sizes.begin(); it != sizes.end(); ++it) { + BorderSize size = *it; + cBorder->insertItem(i18n(border_names[size]), borderSizeToIndex(size,sizes) ); + } + int pos = borderSizeToIndex( border_size, sizes ); + lBorder->show(); + cBorder->show(); + cBorder->setCurrentItem(pos); + slotBorderChanged( pos ); + } +} + +QString KWinDecorationModule::styleToConfigLib( QString& styleLib ) +{ + if( styleLib.startsWith( "kwin3_" )) + return "kwin_" + styleLib.mid( 6 ) + "_config"; + else + return styleLib + "_config"; +} + +QString KWinDecorationModule::quickHelp() const +{ + return i18n( "<h1>Window Manager Decoration</h1>" + "<p>This module allows you to choose the window border decorations, " + "as well as titlebar button positions and custom decoration options.</p>" + "To choose a theme for your window decoration click on its name and apply your choice by clicking the \"Apply\" button below." + " If you do not want to apply your choice you can click the \"Reset\" button to discard your changes." + "<p>You can configure each theme in the \"Configure [...]\" tab. There are different options specific for each theme.</p>" + "<p>In \"General Options (if available)\" you can activate the \"Buttons\" tab by checking the \"Use custom titlebar button positions\" box." + " In the \"Buttons\" tab you can change the positions of the buttons to your liking.</p>" ); +} + + +void KWinDecorationModule::resetKWin() +{ + bool ok = kapp->dcopClient()->send("kwin*", "KWinInterface", + "reconfigure()", QByteArray()); + if (!ok) + kdDebug() << "kcmkwindecoration: Could not reconfigure kwin" << endl; +} + +#include "kwindecoration.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; + diff --git a/kwin/kcmkwin/kwindecoration/kwindecoration.desktop b/kwin/kcmkwin/kwindecoration/kwindecoration.desktop new file mode 100644 index 000000000..3c5f12e6f --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/kwindecoration.desktop @@ -0,0 +1,231 @@ +[Desktop Entry] +Exec=kcmshell kwindecoration +Icon=kcmkwm +Type=Application +DocPath=kcontrol/kwindecoration/index.html + +X-KDE-ModuleType=Library +X-KDE-Library=kwindecoration +X-KDE-FactoryName=kwindecoration + +Name=Window Decorations +Name[af]=Venster Versierings +Name[ar]=زخرÙØ© الناÙذة +Name[be]=ДÑкарацыі вокнаў +Name[bg]=Ð”ÐµÐºÐ¾Ñ€Ð°Ñ†Ð¸Ñ +Name[bn]=উইণà§à¦¡à§‹ সাজসজà§à¦œà¦¾ +Name[br]=Kinkladur ar prenester +Name[bs]=Ukrasi prozora +Name[ca]=Decoració de les finestres +Name[cs]=Dekorace oken +Name[csb]=Dekòracëje òkna +Name[cy]=Addurniadau Ffenestr +Name[da]=Vinduesdekorationer +Name[de]=Fensterdekorationen +Name[el]=Διακοσμήσεις παÏαθÏÏων +Name[eo]=Fenestro-ornamaĵo +Name[es]=Decoración de ventanas +Name[et]=Akna dekoratsioonid +Name[eu]=Leihoaren dekorazioak +Name[fa]=تزئینات پنجره +Name[fi]=Ikkunoiden kehykset +Name[fr]=Décoration des fenêtres +Name[fy]=Finsterdekoraasjes +Name[gl]=Decoracións das Fiestras +Name[he]=קישוטי ×—×œ×•× ×•×ª +Name[hi]=विंडो सजावट +Name[hr]=Ukrasi prozora +Name[hu]=Ablakkeret-stÃlus +Name[id]=Dekorasi Jendela +Name[is]=Gluggaskreytingar +Name[it]=Decorazioni finestra +Name[ja]=ウィンドウ装飾 +Name[ka]=ფáƒáƒœáƒ¯áƒ ის დეკáƒáƒ áƒáƒªáƒ˜áƒ +Name[kk]=Терезе безендіруі +Name[km]=ការ​ážáž»áž”ážáŸ‚ង​បង្អួច +Name[ko]=ì°½ 종료 ë„구 +Name[lo]=àºàº²àº™àº•àº»àºà»àº•à»ˆàº‡àº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Langų iÅ¡vaizda +Name[lv]=Loga DekorÄcijas +Name[mk]=Декорации на прозорците +Name[mn]=Цонхны заÑал +Name[mt]=Dekorazzjoni tal-Windows +Name[nb]=Vinduspynt +Name[nds]=Finsterdekoratschoon +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सजावट +Name[nl]=Vensterdecoraties +Name[nn]=Vindaugsdekorasjonar +Name[nso]=Dikgabiso tsa Window +Name[pa]=à¨à¨°à©‹à¨–ਾ ਸਜਾਵਟ +Name[pl]=Dekoracje okna +Name[pt]=Decorações das Janelas +Name[pt_BR]=Decorações da Janela +Name[ro]=Decorări +Name[ru]=Декорации окон +Name[rw]=Imitako y'Idirishya +Name[se]=Lásehearvvat +Name[sk]=Dekorácie okien +Name[sl]=Okraski oken +Name[sr]=Декорација прозора +Name[sr@Latn]=Dekoracija prozora +Name[ss]=Kuhlotjiswa kweliwindi +Name[sv]=Fönsterdekoration +Name[ta]=சாளர அலஙà¯à®•à®°à®¿à®ªà¯à®ªà¯à®•à®³à¯ +Name[tg]=ДекоратÑиÑҳои тиреза +Name[th]=ตà¸à¹à¸•à¹ˆà¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Name[tr]=Pencere Dekorasyonları +Name[tt]=Täräzä Bizäge +Name[uk]=ÐžÐ±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÐ¾Ð½ +Name[uz]=Oynaning bezaklari +Name[uz@cyrillic]=Ойнанинг безаклари +Name[ven]=U khavhisedza ha windo +Name[vi]=Trang trà Cá»a sổ +Name[wa]=GÃ¥liotaedjes des purneas +Name[xh]=Izihombiso zeWindow +Name[zh_CN]=窗å£è£…饰 +Name[zh_TW]=視窗è£é£¾ +Name[zu]=Imihlobiso ye-window + +Comment=Configure the look and feel of window titles +Comment[af]=Stel die uitdrukking en gevoek van venster titels op +Comment[ar]=إعداد شكل Ùˆ ملمس عنوان الناÙذة +Comment[be]=ÐаÑтаўленні вонкавага выглÑду загалоўкаў вокнаў +Comment[bg]=ÐаÑтройване Ð²ÑŠÐ½ÑˆÐ½Ð¸Ñ Ð²Ð¸Ð´ на прозорците +Comment[bn]=উইণà§à¦¡à§‹ শিরোনামের চেহারা কনফিগার করà§à¦¨ +Comment[br]=Kefluniañ neuz ha feson titloù ar prenester +Comment[bs]=Ovdje možete podesiti izgled i ponaÅ¡anje naslova prozora +Comment[ca]=Configura l'aspecte i efecte dels tÃtols de la finestra +Comment[cs]=Nastavenà vzhledu a dekoracà oken +Comment[csb]=Kònfigùracëjô wëzdrzatkù ë ùchòwaniô titlowi lëstwë òknów +Comment[cy]=Ffurfweddu golwg a theimlad teitlau ffenestri +Comment[da]=Indstil udseendet af vinduestitler +Comment[de]=Das Erscheinungsbild von Fenstertiteln festlegen +Comment[el]=Ρυθμίστε την εμφάνιση και την αίσθηση των τίτλων παÏαθÏÏου +Comment[eo]=Agordu la fenestrajn titolojn +Comment[es]=Configuración del aspecto y comportamiento de los tÃtulos de las ventanas +Comment[et]=Akna tiitliribade välimuse ja tunnetuse seadistamine +Comment[eu]=Konfiguratu leihoaren izenburuen itxura +Comment[fa]=پیکربندی ظاهر Ùˆ اØساس عنوان پنجره‌ها +Comment[fi]=Muokkaa ikkunoiden kehysten ulkonäköä +Comment[fr]=Configuration de l'apparence du titre des fenêtres +Comment[fy]=Hjir kinne jo it uterlik en gedrach fan finstertitels ynstelle +Comment[gl]=Configurar a apariencia dos tÃtulos das fiestras +Comment[he]=×©×™× ×•×™ הגדרות המר××” והתחושה של כותרות ×—×œ×•× ×•×ª +Comment[hi]=विंडो शीरà¥à¤·à¤•à¥‹à¤‚ के रूप आकार को कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje izgleda naslova prozora +Comment[hu]=Az ablakok cÃmsorának megjelenési beállÃtásai +Comment[is]=Stilla viðmót gluggatitla +Comment[it]=Configura l'aspetto e il comportamento dei titoli delle finestre +Comment[ja]=ウィンドウã®ã‚¿ã‚¤ãƒˆãƒ«ãƒãƒ¼ã®å¤–観をè¨å®š +Comment[ka]=ფáƒáƒœáƒ¯áƒ ის სáƒáƒ—áƒáƒ£áƒ ის იერსáƒáƒ®áƒ˜áƒ¡ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезе айдарының безендіруін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​រូបរាង​របស់​ចំណងជើង​បង្អួច +Comment[ko]=ì°½ ì œëª© í‘œì‹œì¤„ì˜ ëª¨ìŠµê³¼ ëŠë‚Œ ì„¤ì • +Comment[lo]=ປັດà»àº•à»ˆàº‡àº¥àº±àºàºªàº°àº™àº°à»àº¥àº°àº„ວາມຮູ້ສືàºà»ƒàº™àºàº²àº™à»ƒàºŠà»‰àº‡àº²àº™àº‚àºàº‡àºšàº²àº§à»€àºŠàºµ Konqueror +Comment[lt]=KonfigÅ«ruoti langų antraÅ¡Äių iÅ¡vaizdÄ… ir elgsenÄ… +Comment[lv]=KonfigurÄ“ loga virsrakstu izskatu un izturÄ“Å¡anos +Comment[mk]=Конфигурирајте го изгледот и чувÑтвото на наÑловите на прозорците +Comment[mn]=Цонхны толгойн харагдалтыг тохируулах +Comment[mt]=Ikkonfigura d-dehra u l-użu tat-titli tal-windows +Comment[nb]=Her kan du sette opp hvordan nettleseren Konqueror skal virke og se ut +Comment[nds]=Dat Utsehn vun de Finstertiteln instellen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² शीरà¥à¤·à¤•à¤¹à¤°à¥‚को हेराइ र बà¥à¤à¤¾à¤‡ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u het uiterlijk en gedrag van venstertitels instellen +Comment[nn]=Set opp utsjÃ¥naden pÃ¥ vindaugstitlar +Comment[nso]=Beakanya pogego le maikutlo a maina a window +Comment[pa]=à¨à¨°à©‹à¨–ਾ ਸਿਰਲੇਖਾਂ ਦੇ ਰੰਗ-ਰੂਪ ਦੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja wyglÄ…du i zachowania belek tytuÅ‚owych okien +Comment[pt]=Configuração da aparência e comportamento dos tÃtulos das janelas +Comment[pt_BR]=Configura a aparência dos tÃtulos de janelas +Comment[ro]=Configurează aspectul titlului ferestrelor +Comment[ru]=ÐаÑтройка внешнего вида заголовков окон +Comment[rw]=Kuboneza imboneko n'ukumva kw'imitwe y'idirishya +Comment[se]=Heivet lásenamahusaid fárdda +Comment[sk]=Nastavenie vzhľadu titulkov okien +Comment[sl]=Nastavite videz in delovanje naslovnih vrstic okna. +Comment[sr]=Подешавање изгледа и оÑећаја наÑловних линија прозора +Comment[sr@Latn]=PodeÅ¡avanje izgleda i osećaja naslovnih linija prozora +Comment[sv]=Anpassa namnlisternas utseende och känsla +Comment[ta]=சாளரம௠மறà¯à®±à¯à®®à¯ தலைபà¯à®ªà®¿à®©à¯ காடà¯à®šà®¿à®µà®•à¯ˆà®¯à¯ˆ மாறà¯à®±à¯ +Comment[tg]=Танзими намо ва ҳиÑи унвони тиреза +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸¥à¸±à¸à¸©à¸“ะรูปà¹à¸šà¸šà¹à¸¥à¸°à¸ªà¸±à¸¡à¸œà¸±à¸ªà¸ªà¸¶à¸à¸‚à¸à¸‡à¹à¸–บหัวเรื่à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=Pencere baÅŸlıklarını görünümlerini yapılandır +Comment[tt]=Täräzä baÅŸlığınıñ küreneÅŸen caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð³Ð»Ñду та поведінки заголовків вікон +Comment[uz]=Oyna sarlavhasining tashqi koÊ»rinishini moslash +Comment[uz@cyrillic]=Ойна ÑарлавҳаÑининг ташқи кўринишини моÑлаш +Comment[ven]=Dzudzanyani mbonalelo na zwipfi zwa buronza ya inithanete ino pfi Konqueror +Comment[vi]=Cấu hình cảm nháºn cho tên cá»a sổ +Comment[wa]=Apontyî li rivnance eyet l' dujhance des tites des purneas +Comment[xh]=Qwalasela inkangeleko nemvakalelo yezihloko zeWindow +Comment[zh_CN]=é…置窗å£æ ‡é¢˜çš„观感 +Comment[zh_TW]=è¨å®šè¦–窗標題列的外觀與感覺 +Comment[zu]=Hlanganisela ukubona kanye nokuzwa kwezihloko zama-window + +Keywords=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration +Keywords[ar]=kwin,ناÙذة,مسيير,الØاÙØ©,الشكل,سمة,مظهر,ملمس,تصميم,زر,معامل,مدبر,kwm,زخرÙات +Keywords[az]=kwin,pÉ™ncÉ™rÉ™,idarəçi,kÉ™nar,tÉ™rz,örtü,görünüş,toxuma,yer,düymÉ™,applet,kÉ™nar,kwm,dekorasiya,bÉ™zÉ™k +Keywords[be]=Ðкно,Кіраўнік,ÐœÑжа,Стыль,ТÑма,Вонкавы выглÑд,Кнопкі,Ðпрацоўшчык,Край,ДÑкарацыÑ,kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration +Keywords[bg]=прозорец, декорациÑ, заглавие, бутони, меню, kwin, window, manager, border, style, theme, look, feel, layout, button, handle, edge, kwm, decoration +Keywords[ca]=kwin,finestra,gestor,vora,estil,tema,aspecte,comportament,disposició,botó,nansa,marges,kwm,decoració +Keywords[cs]=kwin,okno,správce,okraj,styl,motiv,vzhled,rozvrženÃ,tlaÄÃtko,úchytka,hrana,kwm,dekorace +Keywords[csb]=kwin,òkno,menedżer,zberk,sztél,téma,wëzdrzatk,ùchòwanié,ùstôw,knÄ…pa,ùchwët,raÅ„t,kwm,dekòracëjô +Keywords[cy]=kwin,ffenestr,trefnydd,ymyl,arddull,thema,golwg,teimlad,haenlun,botwm,carn,kwm,addurniad +Keywords[da]=kwin,vindue,hÃ¥ndtering,kant,stil,tema,udseende,fornemmelse,layout,knap,hÃ¥ndtag,kant,kwm,dekoration +Keywords[de]=KWin,Kwm,Fenster,Manager,Rahmen,Design,Stile,Themes,Optik,Erscheinungsbild,Layout,Knöpfe,Ränder,Dekorationen +Keywords[el]=kwin,παÏάθυÏο,διαχειÏιστής,πεÏίγÏαμμα,στυλ,θÎμα,εμφάνιση,αίσθηση,διάταξη,κουμπί,χειÏισμός,άκÏο,kwm,διακόσμηση +Keywords[eo]=kwin,fenestro,administrilo,rando,stilo,etoso,aspekto,konduto,aranÄo,butono,eÄo,kwm,ornamo +Keywords[es]=kwin,ventana,gestor,borde,estilo,tema,aspecto,comportamiento,disposición,botón,asa,esquina,kwm,decoración +Keywords[et]=kwin,aken,haldur,piire,stiil,teema,välimus,kasutamine,nupud,serv,kwm,dekoratsioon +Keywords[eu]=kwin,leihoa,kudeatzailea,ertza,estiloa,gaia,itxura,antolaketa,botoia, maneiatzailea,ertzea,kwm,dekorazioa +Keywords[fa]=kwinØŒ پنجره، مدیر، لبه، سبک، چهره، ظاهر، اØساس، طرØ‌بندی، دکمه، گرداندن، لبه، kwmØŒ تزئین +Keywords[fi]=kwin,ikkuna,ikkunaohjelma,ikkunoinnin hallintaohjelma,tausta,tyyli,teema,ulkonäkö,tuntuma,ulkoasu,painike,kahva,kulma,kwm,kehys +Keywords[fr]=kwin,fenêtre,gestionnaire,bordure,style,thème,apparence,ergonomie,disposition,bouton,poignée,bord,kwm,décoration +Keywords[fy]=kwin,window,manager,rand,stijl,theme,tema,look,uiterlijk,gedrag,feel,layout,opmaak,button,knoppen,handle,rand,kwm,decoratie,windowmanager,venster,vensterbeheer,finster,râne,kader,styltema,uterlik,gedrach,finsterbehear +Keywords[ga]=kwin,fuinneog,bainisteoir,imlÃne,stÃl,téama,leagan amach,cnaipe,hanla,ciumhais,kwm,maisiúchán +Keywords[gl]=kwin,fiestra,xestor,beira,estilo,tema,apariencia,formato,botón,xestión,esquina,kwm,decoración +Keywords[he]=×ž× ×”×œ ×—×œ×•× ×•×ª,×—×œ×•× ×•×ª,×ž× ×”×œ,גבול,מסגרת,×¡×’× ×•×Ÿ,ערכה,ערכת × ×•×©×,מר××”,תחושה,פריסה,תצוגה,כפתור,ידית,קצה,קישוט, kwin,window,manager,border,style,theme,look,feel,layout,button,handle, edge,kwm,decoration +Keywords[hi]=के-विन,विंडो,पà¥à¤°à¤¬à¤‚धक,बारà¥à¤¡à¤°,शैली,पà¥à¤°à¤¸à¤‚ग,रूप,अनà¥à¤à¤µ,ले-आउट,बटन,हैंडल,किनारा,केडबलà¥à¤¯à¥‚à¤à¤®,सजावट +Keywords[hr]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,prozor,upravljanje,obrub,stil,tema,izgled,raspored,gumb,rukovanje,rub,ukras +Keywords[hu]=KWin,ablak,kezelÅ‘,szegély,stÃlus,téma,kinézet,megjelenés,elrendezés,nyomógomb,fogantyú,perem,kwm,ablakstÃlus +Keywords[is]=kwin,gluggi,gluggastjóri,gluggar,kantar,rammi,skreyting,þema,stÃll,útlit,takki,kwm,skraut +Keywords[it]=kwin,finestra,window manager,bordo,stile,tema,aspetto,pulsante,maniglia,bordo,kwm,decorazione +Keywords[ja]=kwin,ウィンドウ,マãƒãƒ¼ã‚¸ãƒ£,æž ,スタイル,テーマ,ルック,外観,レイアウト,ボタン,ãƒãƒ³ãƒ‰ãƒ«,エッジ,kwm,装飾 +Keywords[km]=kwin,បង្អួច,កម្មវិធី​គ្រប់គ្រង,ស៊ុម,រចនាបáŸáž‘្ម,ស្បែក,មុážáž„ារ,ប្លង់,ប៊ូážáž»áž„,ការ​ប្រើ,គែម,kwm,ការ​ážáž»áž”ážáŸ‚áž„ +Keywords[lt]=kwin,window,manager,border,style,theme,look,feel,layout,buttons,handle,edge,kwm,decoration,langas,tvarkyklÄ—,rÄ—melis,stilius,tema,žiÅ«rÄ—ti,jausti,iÅ¡dÄ—stymas,mygtukai,kraÅ¡tas,dekoracija +Keywords[lv]=kwin, logs, menedžeris, rÄmis, stils, tÄ“ma, skats, gars, izkÄrtojums, poga, rokturis, stÅ«ris, kwm, dekorÄcija +Keywords[mk]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,прозорец,менаџер,граница,Ñтил,тема,изглед,чувÑтво,раÑпоред,копче,рачка,раб,декорација +Keywords[mn]=KWin,Kwm,Цонх,Manager,ХүрÑÑ,Design,Ð¥ÑлбÑÑ€,Загвар, Optik,Харагдалт,Layout,Товч,Өнцөг,ЗаÑал +Keywords[mt]=kwin, window, manager, border, bordura, stil, tema, apparenza, style, theme, look, feel, layout, tqassim, użu, button, handle, edge, kwm, decoration +Keywords[nb]=kwin,vindu,vindusstyring,styrer,ramme,stil,tema,utseende,layout,knapp,kant,kwm,pynt,dekorasjon +Keywords[nds]=kwin,Finster,Finsterpleger,manager,Rahmen,Stil,Muster,look,feel,layout,Knoop,Greep,Rand,kwm,Dekoratschoon +Keywords[ne]=के विन,सञà¥à¤à¥à¤¯à¤¾à¤², पà¥à¤°à¤¬à¤¨à¥à¤§à¤•, किनारा, शैली, विषयवसà¥à¤¤à¥, हेराइ, बà¥à¤à¤¾à¤‡, सजावट, बटन, हà¥à¤¯à¤¾à¤¨à¥à¤¡à¤², छेउ,kwm, सजावट +Keywords[nl]=kwin,window,manager,rand,stijl,theme,thema,look,uiterlijk,gedrag,feel, layout,opmaak,button,knoppen,handle,rand,kwm,decoratie,windowmanager,venster,vensterbeheer +Keywords[nn]=kwin,vindauge,kant,bord,stil,tema,utsjÃ¥nad,bunad,knapp,handtak,kwm,dekorasjon +Keywords[nso]=kwin,window,molaodi,mollwane,mokgwa,molaetsa,tebelego,maikutlo,peakanyo,setobetswa,moswaro,nthla,kwm,kgabiso +Keywords[pa]=kwin,handle,edge,kwm,decoration,à¨à¨°à©‹à¨–ਾ,ਮੈਨੇਜਰ,ਹਾਸ਼ੀਆ,ਸ਼ੈਲੀ,ਸਰੂਪ,ਦਿੱਖ,ਖਾਕਾ,ਬਟਨ,ਹੈਂਡਲ,ਸਜਾਵਟ +Keywords[pl]=kwin,okno,menedżer,brzeg,styl,motyw,wyglÄ…d,zachowanie,ukÅ‚ad,przycisk,uchwyt,krawÄ™dź,kwm,dekoracja +Keywords[pt]=kwin,janela,gestor,contorno,estilo,tema,aparência,comportamento,visual,botão,pega,extremo,kwm,decoração +Keywords[pt_BR]=kwin,janela,gerenciador,borda,estilo,tema,aparência,aparência,botão, gerenciador,borda,kwm,decoração +Keywords[ro]=kwin,fereastră,manager,margine,stil,tematică,aspect,comportament,format,buton,kwm,decorare +Keywords[rw]=kwin,idirishya,muyobozi,impera,imisusire,insanganyamatsiko,imboneko,kumva,imigaragarire,buto,ikirindi,impera,kwm,ugutaaka +Keywords[se]=kwin,láse,gieÄ‘ahalli,ravda,stiila,fáddá,fárda,dovdu,hápmi,boallu,geavja,ravda,kwm,hearva +Keywords[sk]=kwin,okno,správa,okraj,Å¡týl,téma,vzhľad,rozloženie,tlaÄidlo,hrana,kwm,dekorácia,oblasÅ¥ +Keywords[sl]=kwin,okno,upravitelj,rob,meja,slog,stil,tema,pogled,obÄutek,gumb,roÄaj,rob,kwm,okrasek +Keywords[sr]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,прозор,менаџер,оквир,Ñтил,тема,изглед,дугме,хватаљка,декорација +Keywords[sr@Latn]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,prozor,menadžer,okvir,stil,tema,izgled,dugme,hvataljka,dekoracija +Keywords[sv]=kwin,fönster,hanterare,kant,stil,tema,utseende,känsla,layout,knapp,hantera,kant,kwm,dekoration +Keywords[ta]=kwin,சாளரமà¯,மேலாளரà¯,விளிமà¯à®ªà¯,பாணி,தலைபà¯à®ªà¯,பாரà¯à®µà¯ˆ,உணரà¯à®¤à®²à¯,உரà¯à®µà®°à¯ˆ,விசை,கையாளà¯,à®®à¯à®©à¯ˆ,kwm,அலஙà¯à®•à®°à®¿à®ªà¯à®ªà¯ +Keywords[th]=kwin,หน้าต่าง,ตัวจัดà¸à¸²à¸£,à¸à¸£à¸à¸š,ลัà¸à¸©à¸“ะ,ชุดตà¸à¹à¸•à¹ˆà¸‡,มà¸à¸‡à¹€à¸«à¹‡à¸™,รู้สึà¸,à¸à¸²à¸£à¸ˆà¸±à¸”วาง,ปุ่ม,ที่จับ,ขà¸à¸š,kwm,à¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡ +Keywords[tr]=kwin,pencere,yönetici,kenar,stil,tema,görünüş,doku,yerleÅŸim,düğme,tutamaç,kenar,kwm,dekorasyon +Keywords[uk]=kwin,вікно,менеджер,границÑ,Ñтиль,тема,виглÑд,поведінка,розклад,кнопка,handle,край,kwm,Ð¾Ð±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð½Ñ +Keywords[uz]=kwin,kwm,bezak,oyna,boshqaruvchi,usul,tashqi koÊ»rinish +Keywords[uz@cyrillic]=kwin,kwm,безак,ойна,бошқарувчи,уÑул,ташқи кўриниш +Keywords[ven]=kwin,windo,mulanguli,mukanoni,tshitaela,thero,sedza,upfa,vhuvha,bathene,fara,mafhedziselo,kwn,u khavhisedza +Keywords[vi]=kwin,cá»a sổ,quản lý,bá»,kiểu,sắc thái,ngoại hình,cảm nháºn,sắp xếp,nút,Ä‘iá»u khiển,cạnh,kwm,trang trà +Keywords[wa]=kwin,kpurnea,purnea,manaedjeu,boird,stîle,tinme,rivnance,layout;loukance,boton,apougnî,costé,kwm,gÃ¥liotaedje +Keywords[xh]=kwin,window,umphathi,umda,uhlobo,umxholo wokuxoxwa,jonga,yiva,beka,iqhosha,umqheba,umda,kwm,uhombiso +Keywords[zh_CN]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,窗å£,管ç†å™¨,边框,æ ·å¼,主题,观感,布局,按钮,处ç†,边缘,装饰 +Keywords[zh_TW]=kwin,window,manager,border,style,theme,look,feel,layout,button,handle,edge,kwm,decoration,視窗,管ç†å“¡,邊框,é¢¨æ ¼,佈景主題,外觀,感覺,佈局,按鈕,é‚Šç·£,è£é£¾ +Keywords[zu]=kwin,i-window,imenenja,umngcele,isitayela,bona,izwa, isendlalelo,inkinobho,isibambo,unqenqema,kwm,umhlobiso +Categories=Qt;KDE;X-KDE-settings-looknfeel; diff --git a/kwin/kcmkwin/kwindecoration/kwindecoration.h b/kwin/kcmkwin/kwindecoration/kwindecoration.h new file mode 100644 index 000000000..eea091ebc --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/kwindecoration.h @@ -0,0 +1,135 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2001 + Karol Szwed <gallium@kde.org> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef KWINDECORATION_H +#define KWINDECORATION_H + +#include <kcmodule.h> +#include <dcopobject.h> +#include <buttons.h> +#include <kconfig.h> +#include <klibloader.h> + +#include <kdecoration.h> + +#include "kwindecorationIface.h" + +class KComboBox; +class QCheckBox; +class QLabel; +class QTabWidget; +class QVBox; +class QSlider; + +class KDecorationPlugins; +class KDecorationPreview; + +// Stores themeName and its corresponding library Name +struct DecorationInfo +{ + QString name; + QString libraryName; +}; + + +class KWinDecorationModule : public KCModule, virtual public KWinDecorationIface, public KDecorationDefines +{ + Q_OBJECT + + public: + KWinDecorationModule(QWidget* parent, const char* name, const QStringList &); + ~KWinDecorationModule(); + + virtual void load(); + virtual void save(); + virtual void defaults(); + + QString quickHelp() const; + + virtual void dcopUpdateClientList(); + + signals: + void pluginLoad( KConfig* conf ); + void pluginSave( KConfig* conf ); + void pluginDefaults(); + + protected slots: + // Allows us to turn "save" on + void slotSelectionChanged(); + void slotChangeDecoration( const QString & ); + void slotBorderChanged( int ); + void slotButtonsChanged(); + + private: + void readConfig( KConfig* conf ); + void writeConfig( KConfig* conf ); + void findDecorations(); + void createDecorationList(); + void updateSelection(); + QString decorationLibName( const QString& name ); + QString decorationName ( QString& libName ); + static QString styleToConfigLib( QString& styleLib ); + void resetPlugin( KConfig* conf, const QString& currentDecoName = QString::null ); + void resetKWin(); + void checkSupportedBorderSizes(); + static int borderSizeToIndex( BorderSize size, QValueList< BorderSize > sizes ); + static BorderSize indexToBorderSize( int index, QValueList< BorderSize > sizes ); + + QTabWidget* tabWidget; + + // Page 1 + KComboBox* decorationList; + QValueList<DecorationInfo> decorations; + + KDecorationPreview* preview; + KDecorationPlugins* plugins; + KConfig kwinConfig; + + QCheckBox* cbUseCustomButtonPositions; + // QCheckBox* cbUseMiniWindows; + QCheckBox* cbShowToolTips; + QLabel* lBorder; + QComboBox* cBorder; + BorderSize border_size; + + QObject* pluginObject; + QWidget* pluginConfigWidget; + QString currentLibraryName; + QString oldLibraryName; + QObject* (*allocatePlugin)( KConfig* conf, QWidget* parent ); + + // Page 2 + ButtonPositionWidget *buttonPositionWidget; + QVBox* buttonPage; +}; + + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/kcmkwin/kwindecoration/kwindecorationIface.h b/kwin/kcmkwin/kwindecoration/kwindecorationIface.h new file mode 100644 index 000000000..f45be6b7b --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/kwindecorationIface.h @@ -0,0 +1,44 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2001 + Karol Szwed (gallium) <karlmail@usa.net> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef __KWINDECORATIONIFACE_H +#define __KWINDECORATIONIFACE_H + +#include <dcopobject.h> + +class KWinDecorationIface: virtual public DCOPObject +{ + K_DCOP + public: + + k_dcop: + virtual void dcopUpdateClientList()=0; +}; + +#endif diff --git a/kwin/kcmkwin/kwindecoration/pixmaps.h b/kwin/kcmkwin/kwindecoration/pixmaps.h new file mode 100644 index 000000000..76f60b3e9 --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/pixmaps.h @@ -0,0 +1,110 @@ +/* + This is the new kwindecoration kcontrol module + + Copyright (c) 2004, Sandro Giessl <sandro@giessl.com> + Copyright (c) 2001 + Karol Szwed <gallium@kde.org> + http://gallium.n3.net/ + + Supports new kwin configuration plugins, and titlebar button position + modification via dnd interface. + + Based on original "kwintheme" (Window Borders) + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +// Button icon bitmap data which is hopefully generic enough to be recognized by everyone. + +// close.xbm: +#define close_width 12 +#define close_height 12 +static unsigned char close_bits[] = { + 0x00, 0x00, 0x06, 0x06, 0x0e, 0x07, 0x9c, 0x03, 0xf8, 0x01, 0xf0, 0x00, + 0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, 0x0e, 0x07, 0x06, 0x06, 0x00, 0x00 }; + +// help.xbm: +#define help_width 12 +#define help_height 12 +static unsigned char help_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xc0, 0x01, + 0xe0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00 }; + +// keepaboveothers.xbm: +#define keepaboveothers_width 12 +#define keepaboveothers_height 12 +static unsigned char keepaboveothers_bits[] = { + 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xfe, 0x07, + 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// keepbelowothers.xbm: +#define keepbelowothers_width 12 +#define keepbelowothers_height 12 +static unsigned char keepbelowothers_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, + 0xfe, 0x07, 0x60, 0x00, 0xf8, 0x01, 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00 }; + +// maximize.xbm: +#define maximize_width 12 +#define maximize_height 12 +static unsigned char maximize_bits[] = { + 0x00, 0x00, 0xfe, 0x07, 0xfe, 0x07, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, + 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0xfe, 0x07, 0x00, 0x00 }; + +// menu.xbm: +#define menu_width 12 +#define menu_height 12 +static unsigned char menu_bits[] = { + 0x00, 0x00, 0xfc, 0x03, 0xf4, 0x02, 0x04, 0x02, 0xf4, 0x02, 0x04, 0x02, + 0xf4, 0x02, 0x04, 0x02, 0xf4, 0x02, 0x04, 0x02, 0xfc, 0x03, 0x00, 0x00 }; + +// minimize.xbm: +#define minimize_width 12 +#define minimize_height 12 +static unsigned char minimize_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, 0xfe, 0x07, 0x00, 0x00 }; + +// onalldesktops.xbm: +#define onalldesktops_width 12 +#define onalldesktops_height 12 +static unsigned char onalldesktops_bits[] = { + 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xfe, 0x07, + 0xfe, 0x07, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00 }; + +// resize.xbm: +#define resize_width 12 +#define resize_height 12 +static unsigned char resize_bits[] = { + 0x00, 0x00, 0xfe, 0x07, 0x42, 0x04, 0x42, 0x04, 0x42, 0x04, 0x42, 0x04, + 0x7e, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0xfe, 0x07, 0x00, 0x00 }; + +// shade.xbm: +#define shade_width 12 +#define shade_height 12 +static unsigned char shade_bits[] = { + 0x00, 0x00, 0xfe, 0x07, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// spacer.xbm: +#define spacer_width 12 +#define spacer_height 12 +static unsigned char spacer_bits[] = { + 0x00, 0x00, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x54, 0x03, + 0xac, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x00, 0x00 }; + +// vim: ts=4 diff --git a/kwin/kcmkwin/kwindecoration/preview.cpp b/kwin/kcmkwin/kwindecoration/preview.cpp new file mode 100644 index 000000000..8c88a3604 --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/preview.cpp @@ -0,0 +1,507 @@ +/* + * + * Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "preview.h" + +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> +#include <kglobal.h> +#include <qlabel.h> +#include <qstyle.h> +#include <kiconloader.h> + +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> + +#include <kdecorationfactory.h> +#include <kdecoration_plugins_p.h> + +// FRAME the preview doesn't update to reflect the changes done in the kcm + +KDecorationPreview::KDecorationPreview( QWidget* parent, const char* name ) + : QWidget( parent, name ) + { + options = new KDecorationPreviewOptions; + + bridge[Active] = new KDecorationPreviewBridge( this, true ); + bridge[Inactive] = new KDecorationPreviewBridge( this, false ); + + deco[Active] = deco[Inactive] = NULL; + + no_preview = new QLabel( i18n( "No preview available.\n" + "Most probably there\n" + "was a problem loading the plugin." ), this ); + + no_preview->setAlignment( AlignCenter ); + + setMinimumSize( 100, 100 ); + no_preview->resize( size()); + } + +KDecorationPreview::~KDecorationPreview() + { + for ( int i = 0; i < NumWindows; i++ ) + { + delete deco[i]; + delete bridge[i]; + } + delete options; + } + +bool KDecorationPreview::recreateDecoration( KDecorationPlugins* plugins ) + { + for ( int i = 0; i < NumWindows; i++ ) + { + delete deco[i]; // deletes also window + deco[i] = plugins->createDecoration( bridge[i] ); + deco[i]->init(); + } + + if( deco[Active] == NULL || deco[Inactive] == NULL ) + { + return false; + } + + positionPreviews(); + deco[Inactive]->widget()->show(); + deco[Active]->widget()->show(); + + return true; + } + +void KDecorationPreview::enablePreview() + { + no_preview->hide(); + } + +void KDecorationPreview::disablePreview() + { + delete deco[Active]; + delete deco[Inactive]; + deco[Active] = deco[Inactive] = NULL; + no_preview->show(); + } + +void KDecorationPreview::resizeEvent( QResizeEvent* e ) + { + QWidget::resizeEvent( e ); + positionPreviews(); + } + +void KDecorationPreview::positionPreviews() + { + int titleBarHeight, leftBorder, rightBorder, xoffset, + dummy1, dummy2, dummy3; + QRect geometry; + QSize size; + + no_preview->resize( this->size() ); + + if ( !deco[Active] || !deco[Inactive] ) + return; + + // don't have more than one reference to the same dummy variable in one borders() call. + deco[Active]->borders( dummy1, dummy2, titleBarHeight, dummy3 ); + deco[Inactive]->borders( leftBorder, rightBorder, dummy1, dummy2 ); + + titleBarHeight = kMin( int( titleBarHeight * .9 ), 30 ); + xoffset = kMin( kMax( 10, QApplication::reverseLayout() + ? leftBorder : rightBorder ), 30 ); + + // Resize the active window + size = QSize( width() - xoffset, height() - titleBarHeight ) + .expandedTo( deco[Active]->minimumSize() ); + geometry = QRect( QPoint( 0, titleBarHeight ), size ); + deco[Active]->widget()->setGeometry( QStyle::visualRect( geometry, this ) ); + + // Resize the inactive window + size = QSize( width() - xoffset, height() - titleBarHeight ) + .expandedTo( deco[Inactive]->minimumSize() ); + geometry = QRect( QPoint( xoffset, 0 ), size ); + deco[Inactive]->widget()->setGeometry( QStyle::visualRect( geometry, this ) ); + } + +void KDecorationPreview::setPreviewMask( const QRegion& reg, int mode, bool active ) + { + QWidget *widget = active ? deco[Active]->widget() : deco[Inactive]->widget(); + + // FRAME duped from client.cpp + if( mode == Unsorted ) + { + XShapeCombineRegion( qt_xdisplay(), widget->winId(), ShapeBounding, 0, 0, + reg.handle(), ShapeSet ); + } + else + { + QMemArray< QRect > rects = reg.rects(); + XRectangle* xrects = new XRectangle[ rects.count() ]; + for( unsigned int i = 0; + i < rects.count(); + ++i ) + { + xrects[ i ].x = rects[ i ].x(); + xrects[ i ].y = rects[ i ].y(); + xrects[ i ].width = rects[ i ].width(); + xrects[ i ].height = rects[ i ].height(); + } + XShapeCombineRectangles( qt_xdisplay(), widget->winId(), ShapeBounding, 0, 0, + xrects, rects.count(), ShapeSet, mode ); + delete[] xrects; + } + if( active ) + mask = reg; // keep shape of the active window for unobscuredRegion() + } + +QRect KDecorationPreview::windowGeometry( bool active ) const + { + QWidget *widget = active ? deco[Active]->widget() : deco[Inactive]->widget(); + return widget->geometry(); + } + +void KDecorationPreview::setTempBorderSize(KDecorationPlugins* plugin, KDecorationDefines::BorderSize size) + { + options->setCustomBorderSize(size); + if (plugin->factory()->reset(KDecorationDefines::SettingBorder) ) + { + // can't handle the change, recreate decorations then + recreateDecoration(plugin); + } + else + { + // handles the update, only update position... + positionPreviews(); + } + } + +void KDecorationPreview::setTempButtons(KDecorationPlugins* plugin, bool customEnabled, const QString &left, const QString &right) + { + options->setCustomTitleButtonsEnabled(customEnabled); + options->setCustomTitleButtons(left, right); + if (plugin->factory()->reset(KDecorationDefines::SettingButtons) ) + { + // can't handle the change, recreate decorations then + recreateDecoration(plugin); + } + else + { + // handles the update, only update position... + positionPreviews(); + } + } + +QRegion KDecorationPreview::unobscuredRegion( bool active, const QRegion& r ) const + { + if( active ) // this one is not obscured + return r; + else + { + // copied from KWin core's code + QRegion ret = r; + QRegion r2 = mask; + if( r2.isEmpty()) + r2 = QRegion( windowGeometry( true )); + r2.translate( windowGeometry( true ).x() - windowGeometry( false ).x(), + windowGeometry( true ).y() - windowGeometry( false ).y()); + ret -= r2; + return ret; + } + } + +KDecorationPreviewBridge::KDecorationPreviewBridge( KDecorationPreview* p, bool a ) + : preview( p ), active( a ) + { + } + +QWidget* KDecorationPreviewBridge::initialParentWidget() const + { + return preview; + } + +Qt::WFlags KDecorationPreviewBridge::initialWFlags() const + { + return 0; + } + +bool KDecorationPreviewBridge::isActive() const + { + return active; + } + +bool KDecorationPreviewBridge::isCloseable() const + { + return true; + } + +bool KDecorationPreviewBridge::isMaximizable() const + { + return true; + } + +KDecoration::MaximizeMode KDecorationPreviewBridge::maximizeMode() const + { + return KDecoration::MaximizeRestore; + } + +bool KDecorationPreviewBridge::isMinimizable() const + { + return true; + } + +bool KDecorationPreviewBridge::providesContextHelp() const + { + return true; + } + +int KDecorationPreviewBridge::desktop() const + { + return 1; + } + +bool KDecorationPreviewBridge::isModal() const + { + return false; + } + +bool KDecorationPreviewBridge::isShadeable() const + { + return true; + } + +bool KDecorationPreviewBridge::isShade() const + { + return false; + } + +bool KDecorationPreviewBridge::isSetShade() const + { + return false; + } + +bool KDecorationPreviewBridge::keepAbove() const + { + return false; + } + +bool KDecorationPreviewBridge::keepBelow() const + { + return false; + } + +bool KDecorationPreviewBridge::isMovable() const + { + return true; + } + +bool KDecorationPreviewBridge::isResizable() const + { + return true; + } + +NET::WindowType KDecorationPreviewBridge::windowType( unsigned long ) const + { + return NET::Normal; + } + +QIconSet KDecorationPreviewBridge::icon() const + { + return QIconSet( KGlobal::iconLoader()->loadIcon( "xapp", KIcon::NoGroup, 16 ), + KGlobal::iconLoader()->loadIcon( "xapp", KIcon::NoGroup, 32 )); + } + +QString KDecorationPreviewBridge::caption() const + { + return active ? i18n( "Active Window" ) : i18n( "Inactive Window" ); + } + +void KDecorationPreviewBridge::processMousePressEvent( QMouseEvent* ) + { + } + +void KDecorationPreviewBridge::showWindowMenu( const QRect &) + { + } + +void KDecorationPreviewBridge::showWindowMenu( QPoint ) + { + } + +void KDecorationPreviewBridge::performWindowOperation( WindowOperation ) + { + } + +void KDecorationPreviewBridge::setMask( const QRegion& reg, int mode ) + { + preview->setPreviewMask( reg, mode, active ); + } + +bool KDecorationPreviewBridge::isPreview() const + { + return true; + } + +QRect KDecorationPreviewBridge::geometry() const + { + return preview->windowGeometry( active ); + } + +QRect KDecorationPreviewBridge::iconGeometry() const + { + return QRect(); + } + +QRegion KDecorationPreviewBridge::unobscuredRegion( const QRegion& r ) const + { + return preview->unobscuredRegion( active, r ); + } + +QWidget* KDecorationPreviewBridge::workspaceWidget() const + { + return preview; + } + +WId KDecorationPreviewBridge::windowId() const + { + return 0; // no decorated window + } + +void KDecorationPreviewBridge::closeWindow() + { + } + +void KDecorationPreviewBridge::maximize( MaximizeMode ) + { + } + +void KDecorationPreviewBridge::minimize() + { + } + +void KDecorationPreviewBridge::showContextHelp() + { + } + +void KDecorationPreviewBridge::setDesktop( int ) + { + } + +void KDecorationPreviewBridge::titlebarDblClickOperation() + { + } + +void KDecorationPreviewBridge::titlebarMouseWheelOperation( int ) + { + } + +void KDecorationPreviewBridge::setShade( bool ) + { + } + +void KDecorationPreviewBridge::setKeepAbove( bool ) + { + } + +void KDecorationPreviewBridge::setKeepBelow( bool ) + { + } + +int KDecorationPreviewBridge::currentDesktop() const + { + return 1; + } + +void KDecorationPreviewBridge::helperShowHide( bool ) + { + } + +void KDecorationPreviewBridge::grabXServer( bool ) + { + } + +KDecorationPreviewOptions::KDecorationPreviewOptions() + { + customBorderSize = BordersCount; // invalid + customButtonsChanged = false; // invalid + customButtons = true; + customTitleButtonsLeft = QString::null; // invalid + customTitleButtonsRight = QString::null; // invalid + + d = new KDecorationOptionsPrivate; + d->defaultKWinSettings(); + updateSettings(); + } + +KDecorationPreviewOptions::~KDecorationPreviewOptions() + { + delete d; + } + +unsigned long KDecorationPreviewOptions::updateSettings() + { + KConfig cfg( "kwinrc", true ); + unsigned long changed = 0; + changed |= d->updateKWinSettings( &cfg ); + + // set custom border size/buttons + if (customBorderSize != BordersCount) + d->border_size = customBorderSize; + if (customButtonsChanged) + d->custom_button_positions = customButtons; + if (customButtons) { + if (!customTitleButtonsLeft.isNull() ) + d->title_buttons_left = customTitleButtonsLeft; + if (!customTitleButtonsRight.isNull() ) + d->title_buttons_right = customTitleButtonsRight; + } else { + d->title_buttons_left = "MS"; + d->title_buttons_right = "HIAX"; + } + + return changed; + } + +void KDecorationPreviewOptions::setCustomBorderSize(BorderSize size) + { + customBorderSize = size; + + updateSettings(); + } + +void KDecorationPreviewOptions::setCustomTitleButtonsEnabled(bool enabled) +{ + customButtonsChanged = true; + customButtons = enabled; + + updateSettings(); +} + +void KDecorationPreviewOptions::setCustomTitleButtons(const QString &left, const QString &right) + { + customTitleButtonsLeft = left; + customTitleButtonsRight = right; + + updateSettings(); + } + +bool KDecorationPreviewPlugins::provides( Requirement ) + { + return false; + } + +#include "preview.moc" diff --git a/kwin/kcmkwin/kwindecoration/preview.h b/kwin/kcmkwin/kwindecoration/preview.h new file mode 100644 index 000000000..1c1943486 --- /dev/null +++ b/kwin/kcmkwin/kwindecoration/preview.h @@ -0,0 +1,150 @@ +/* + * + * Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KWINDECORATION_PREVIEW_H +#define KWINDECORATION_PREVIEW_H + +#include <qwidget.h> +#include <kdecoration_p.h> +#include <kdecoration_plugins_p.h> + +class QLabel; + +class KDecorationPreviewBridge; +class KDecorationPreviewOptions; + +class KDecorationPreview + : public QWidget + { + Q_OBJECT + public: + // Note: Windows can't be added or removed without making changes to + // the code, since parts of it assume there's just an active + // and an inactive window. + enum Windows { Inactive = 0, Active, NumWindows }; + + KDecorationPreview( QWidget* parent = NULL, const char* name = NULL ); + virtual ~KDecorationPreview(); + bool recreateDecoration( KDecorationPlugins* plugin ); + void enablePreview(); + void disablePreview(); + void setPreviewMask( const QRegion&, int, bool ); + QRegion unobscuredRegion( bool, const QRegion& ) const; + QRect windowGeometry( bool ) const; + void setTempBorderSize(KDecorationPlugins* plugin, KDecorationDefines::BorderSize size); + void setTempButtons(KDecorationPlugins* plugin, bool customEnabled, const QString &left, const QString &right); + protected: + virtual void resizeEvent( QResizeEvent* ); + private: + void positionPreviews(); + KDecorationPreviewOptions* options; + KDecorationPreviewBridge* bridge[NumWindows]; + KDecoration* deco[NumWindows]; + QLabel* no_preview; + QRegion mask; + }; + +class KDecorationPreviewBridge + : public KDecorationBridge + { + public: + KDecorationPreviewBridge( KDecorationPreview* preview, bool active ); + virtual bool isActive() const; + virtual bool isCloseable() const; + virtual bool isMaximizable() const; + virtual MaximizeMode maximizeMode() const; + virtual bool isMinimizable() const; + virtual bool providesContextHelp() const; + virtual int desktop() const; + virtual bool isModal() const; + virtual bool isShadeable() const; + virtual bool isShade() const; + virtual bool isSetShade() const; + virtual bool keepAbove() const; + virtual bool keepBelow() const; + virtual bool isMovable() const; + virtual bool isResizable() const; + virtual NET::WindowType windowType( unsigned long supported_types ) const; + virtual QIconSet icon() const; + virtual QString caption() const; + virtual void processMousePressEvent( QMouseEvent* ); + virtual void showWindowMenu( const QRect &); + virtual void showWindowMenu( QPoint ); + virtual void performWindowOperation( WindowOperation ); + virtual void setMask( const QRegion&, int ); + virtual bool isPreview() const; + virtual QRect geometry() const; + virtual QRect iconGeometry() const; + virtual QRegion unobscuredRegion( const QRegion& r ) const; + virtual QWidget* workspaceWidget() const; + virtual WId windowId() const; + virtual void closeWindow(); + virtual void maximize( MaximizeMode mode ); + virtual void minimize(); + virtual void showContextHelp(); + virtual void setDesktop( int desktop ); + virtual void titlebarDblClickOperation(); + virtual void titlebarMouseWheelOperation( int delta ); + virtual void setShade( bool set ); + virtual void setKeepAbove( bool ); + virtual void setKeepBelow( bool ); + virtual int currentDesktop() const; + virtual QWidget* initialParentWidget() const; + virtual Qt::WFlags initialWFlags() const; + virtual void helperShowHide( bool show ); + virtual void grabXServer( bool grab ); + private: + KDecorationPreview* preview; + bool active; + }; + +class KDecorationPreviewOptions + : public KDecorationOptions + { + public: + KDecorationPreviewOptions(); + virtual ~KDecorationPreviewOptions(); + virtual unsigned long updateSettings(); + + void setCustomBorderSize(BorderSize size); + void setCustomTitleButtonsEnabled(bool enabled); + void setCustomTitleButtons(const QString &left, const QString &right); + + private: + BorderSize customBorderSize; + bool customButtonsChanged; + bool customButtons; + QString customTitleButtonsLeft; + QString customTitleButtonsRight; + }; + +class KDecorationPreviewPlugins + : public KDecorationPlugins + { + public: + KDecorationPreviewPlugins( KConfig* cfg ); + virtual bool provides( Requirement ); + }; + +inline KDecorationPreviewPlugins::KDecorationPreviewPlugins( KConfig* cfg ) + : KDecorationPlugins( cfg ) + { + } + +#endif diff --git a/kwin/kcmkwin/kwinoptions/AUTHORS b/kwin/kcmkwin/kwinoptions/AUTHORS new file mode 100644 index 000000000..0615c59db --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/AUTHORS @@ -0,0 +1,12 @@ +Please use http://bugs.kde.org to report bugs. +The following authors may have retired by the time you read this :-) + +KWM Configuration Module: + + Pat Dowler (dowler@pt1B1106.FSH.UVic.CA) + + Bernd Wuebben <wuebben@kde.org> + +Conversion to kcontrol applet: + + Matthias Hoelzer (hoelzer@physik.uni-wuerzburg.de) diff --git a/kwin/kcmkwin/kwinoptions/ChangeLog b/kwin/kcmkwin/kwinoptions/ChangeLog new file mode 100644 index 000000000..0b923864c --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/ChangeLog @@ -0,0 +1,51 @@ +1999-03-06 Mario Weilguni <mweilguni@kde.org> + + * changes for Qt 2.0 + +1998-11-29 Alex Zepeda <garbanzo@hooked.net> + + * pics/Makefile.am, pics/mini/Makefile.am: Install icons from their + "proper" subdirectories. + +1998-11-20 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * advanced.[cpp,h]: fixed bugs. Mostly a disgusting one: + no lists saving for the special options (Decor, Focus a.o.) + +1998-11-09 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * advanced.[cpp,h] : new tab for some of the last of the + kwm's options which remained out of the GUI config: + CtrlTab, TraverseAll, AltTabeMode, Button3Grab and + the filter lists for decorations, focus, stickyness, + session management ignore ( I kinda disklike the solution + I got for the latest) + +1998-11-06 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * titlebar.[cpp,h] : added title alignment config + +1998-10-23 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * titlebar.cpp: completed what Matthias started (took out + useless checks) + * widows.cpp: make autoRaise toggling clearer + +1998-10-22 Matthias Ettrich <ettrich@kde.org> + + * titlebar.cpp: less options on titlebar doubleclick + +1998-10-21 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * desktop.[cpp,h]: now with consistent layout use + resizeEvent() deleted + +1998-10-19 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * windows.[cpp,h]: now with consistent layout use + resizeEvent() deleted + +1998-10-18 Cristian Tibirna <ctibirna@gch.ulaval.ca> + + * titlebar.[cpp,h]: fixed the (in)activetitleebar pixmap selection + 1998-10-21 (still buggy, don't quite understand why) diff --git a/kwin/kcmkwin/kwinoptions/Makefile.am b/kwin/kcmkwin/kwinoptions/Makefile.am new file mode 100644 index 000000000..b6abdf0e7 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/Makefile.am @@ -0,0 +1,18 @@ +METASOURCES = AUTO +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kcm_kwinoptions.la + +kcm_kwinoptions_la_SOURCES = windows.cpp mouse.cpp main.cpp +kcm_kwinoptions_la_LDFLAGS = -module -avoid-version $(all_libraries) -no-undefined +kcm_kwinoptions_la_LIBADD = $(LIB_KDEUI) + +noinst_HEADERS = windows.h mouse.h + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kcmkwm.pot + +kwin_kcmkwin_options_data_DATA = kwinactions.desktop kwinadvanced.desktop kwinfocus.desktop kwinmoving.desktop kwintranslucency.desktop +kwin_kcmkwin_options_datadir = $(kde_appsdir)/.hidden + +xdg_apps_DATA = kwinoptions.desktop diff --git a/kwin/kcmkwin/kwinoptions/kwinactions.desktop b/kwin/kcmkwin/kwinoptions/kwinactions.desktop new file mode 100644 index 000000000..61b58b06d --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwinactions.desktop @@ -0,0 +1,233 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinactions +DocPath=kcontrol/windowmanagement/index.html#action-actions + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwinactions + +Name=Actions +Name[af]=Aksies +Name[ar]=الأÙعال +Name[be]=ДзеÑнні +Name[bg]=ДейÑÑ‚Ð²Ð¸Ñ +Name[bn]=কাজ +Name[br]=Oberoù +Name[bs]=Akcije +Name[ca]=Accions +Name[cs]=ÄŒinnosti +Name[csb]=Dzejaniô +Name[cy]=Gweithredoedd +Name[da]=Handlinger +Name[de]=Aktionen +Name[el]=ΕνÎÏγειες +Name[eo]=Agoj +Name[es]=Acciones +Name[et]=Tegevused +Name[eu]=Ekintzak +Name[fa]=کنشها +Name[fi]=Toiminnot +Name[fo]=Tiltøk +Name[fy]=Aksjes +Name[ga]=GnÃomhartha +Name[gl]=Accións +Name[he]=פעולות +Name[hi]=कà¥à¤°à¤¿à¤¯à¤¾à¤à¤ +Name[hr]=Aktivnosti +Name[hu]=Műveletek +Name[is]=Aðgerðir +Name[it]=Azioni +Name[ja]=動作 +Name[ka]=ქცევრ+Name[kk]=Ðмалдар +Name[km]=អំពើ +Name[ko]=ë™ìž‘ +Name[lo]=à»àºàºšàºžàºµà»€àº„ຊັ້ນ +Name[lt]=Veiksmai +Name[lv]=DarbÄ«bas +Name[mk]=Ðкции +Name[mn]=Үйлдлүүл +Name[ms]=Tindakan +Name[mt]=Azzjonijiet +Name[nb]=Handlinger +Name[nds]=Akschonen +Name[ne]=कारà¥à¤¯ +Name[nl]=Acties +Name[nn]=Handlingar +Name[nso]=Dikgato +Name[pa]=ਕਾਰਵਾਈਆਂ +Name[pl]=CzynnoÅ›ci +Name[pt]=Acções +Name[pt_BR]=Ações +Name[ro]=AcÈ›iuni +Name[ru]=ДейÑÑ‚Ð²Ð¸Ñ +Name[rw]=Ibikorwa +Name[se]=Doaimmat +Name[sk]=Akcie +Name[sl]=Dejanja +Name[sr]=Ðкције +Name[sr@Latn]=Akcije +Name[ss]=Tento +Name[sv]=Ã…tgärder +Name[ta]=செயலà¯à®•à®³à¯ +Name[te]=à°šà°°à±à°¯à°²à± +Name[tg]=Ðмалиёт +Name[th]=à¸à¸²à¸£à¸à¸£à¸°à¸—ำ +Name[tr]=Eylemler +Name[tt]=Ğämällär +Name[uk]=Дії +Name[uz]=Amallar +Name[uz@cyrillic]=Ðмаллар +Name[ven]=Nyito +Name[vi]=Gõ phÃm +Name[wa]=Accions +Name[xh]=Iintshukumo +Name[zh_CN]=动作 +Name[zh_TW]=動作 +Name[zu]=Iminyakazo + +Comment=Configure keyboard and mouse settings +Comment[af]=Konfigureer sleutelbord en muis instellings +Comment[ar]=إعداد لوØØ© المÙØ§ØªÙŠØ Ùˆ الÙأرة +Comment[be]=ÐаÑтаўленні клавіÑтуры Ñ– мышы +Comment[bg]=ÐаÑтройване на дейÑтвиÑта Ñ ÐºÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÑ€Ð°Ñ‚Ð° и мишката +Comment[bn]=কীবোরà§à¦¡ à¦à¦¬à¦‚ মাউস সেটিংস কনফিগার করà§à¦¨ +Comment[br]=Kefluniañ ar stokellaoueg hag al logodenn +Comment[bs]=Podesite postavke tastature i miÅ¡a +Comment[ca]=Aquà podeu configurar l'arranjament del teclat i del ratolà +Comment[cs]=Nastavenà klávesnice a myÅ¡i +Comment[csb]=Kònfigùracëjô nastôwù klawiaturë ë mëszë +Comment[cy]=Ffurfweddu gosodiadau bysellfwrdd a llygoden +Comment[da]=Indstil tastatur og museopsætning +Comment[de]=Einstellungen für Tastatur und Maus festlegen +Comment[el]=Ρυθμίστε το πληκτÏολόγιο και το ποντίκι +Comment[eo]=Agordu la klavaron kaj la muson +Comment[es]=Configuración del teclado y del ratón +Comment[et]=Klaviatuuri ja hiire seadistamine +Comment[eu]=Konfiguratu teklatua eta saguaren ezarpenak +Comment[fa]=پیکربندی تنظیمات صÙØÙ‡ کلید Ùˆ موشی +Comment[fi]=Muokkaa näppäimistön ja hiiren asetuksia +Comment[fr]=Configuration des réglages du clavier et de la souris +Comment[fy]=Hjir kinne jo de ynstellings fan toetseboerd en mûs ynstelle +Comment[ga]=Cumraigh socruithe an mhéarchláir agus na luiche +Comment[gl]=Configurar as opcións do teclado e o rato +Comment[he]=×©×™× ×•×™ הגדרות המקלדת והעכבר +Comment[hi]=कà¥à¤‚जीपट तथा माउस विनà¥à¤¯à¤¾à¤¸ कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje postavki tipkovnice i miÅ¡a +Comment[hu]=A billentyűzet- és egérkezelés beállÃtásai +Comment[is]=Stilla lyklaborð og mús +Comment[it]=Configura le impostazioni della tastiera e del mouse +Comment[ja]=ã‚ーボードã¨ãƒžã‚¦ã‚¹ã®è¨å®š +Comment[ka]=კლáƒáƒ•áƒ˜áƒáƒ¢áƒ£áƒ ის დრთáƒáƒ’უნáƒáƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრების კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Перенетақта мен тышқанның параметрлерін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​ការ​កំណážáŸ‹â€‹áž€áŸ’ដារចុច និង​កណ្ដុរ +Comment[ko]=키보드와 마우스 ì„¤ì • +Comment[lo]=ປັບà»àº•à»ˆàº‡àºàº²àº™àº•àº±à»‰àº‡àº„່າລະບົບດັດສະນີ +Comment[lt]=KonfigÅ«ruoti klaviatÅ«ros ir pelÄ—s nustatymus +Comment[lv]=Å eit JÅ«s varat konfigurÄ“t tastatÅ«ras un peles parametrus +Comment[mk]=Конфигурирајте ги поÑтавувањата на таÑтатурата и глушецот +Comment[mn]=Гар ба Хулгана тохируулах +Comment[mt]=Ikkonfigura t-tastiera u l-maws +Comment[nb]=Innstillinger for tastatur og mus +Comment[nds]=Tastatuur un Muus instellen +Comment[ne]=कà¥à¤žà¥à¤œà¥€à¤ªà¤¾à¤Ÿà¥€ र माउस सेटिङ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u de instellingen van toetsenbord en muis instellen +Comment[nn]=Innstillingar for tastatur og mus +Comment[nso]=Beakanya dipeakanyo tsa legotlo le keyboard +Comment[pa]=ਕੀਬੋਰਡ ਅਤੇ ਮਾਊਸ ਸੈਟਿੰਗ ਦੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja ustawieÅ„ klawiatury i myszy +Comment[pt]=Configuração das opções do teclado e do rato +Comment[pt_BR]=Configura as preferências de teclado e mouse +Comment[ro]=Configurează setările de tastatură È™i mouse +Comment[ru]=ÐаÑтройка параметров клавиатуры и мыши +Comment[rw]= Kuboneza mwandikisho n'amagenamiterere imbeba +Comment[se]=Heivet boallobeavddi ja sáhpána +Comment[sk]=Nastavenie klávesnice a myÅ¡i +Comment[sl]=Nastavitve tipkovnice in miÅ¡ke +Comment[sr]=Подешавање поÑтавки таÑтатуре и миша +Comment[sr@Latn]=PodeÅ¡avanje postavki tastature i miÅ¡a +Comment[sv]=Anpassa inställningar för tangentbord och mus +Comment[ta]=விசைபà¯à®ªà®²à®•à¯ˆà®¯à¯ˆà®¯à¯à®®à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à¯ˆà®¯à¯à®®à¯ வடிவமை +Comment[tg]=Танзимоти забонак ва муш +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸„่าà¹à¸›à¹‰à¸™à¸žà¸´à¸¡à¸žà¹Œà¹à¸¥à¸°à¹€à¸¡à¹‰à¸²à¸ªà¹Œ +Comment[tr]=Klavye ve fare ayarlarını buradan yapabilirsiniz +Comment[tt]=Töylek belän küse eÅŸläwen caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ð°Ð²Ñ–Ð°Ñ‚ÑƒÑ€Ð¸ та мишки +Comment[uz]=Sichqoncha va tugmatagni moslash +Comment[uz@cyrillic]=Сичқонча ва тугматагни моÑлаш +Comment[ven]=Dzudzanyani mavhekanyele a mausu na khiibodo +Comment[vi]=Cấu hình bà n phÃm và chuá»™t +Comment[wa]=Apontiaedjes del sori eyet del taprece +Comment[xh]=Qwalasela izicwangciso zebhodi yezitshixho neze mouse +Comment[zh_CN]=é…ç½®é”®ç›˜å’Œé¼ æ ‡è®¾ç½® +Comment[zh_TW]=è¨å®šéµç›¤èˆ‡æ»‘é¼ çš„è¨å®šå€¼ +Comment[zu]=Hlanganisela izilungiselelo zebhodi yokhiye kanye neze-mouse + +Keywords=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize +Keywords[ar]=ظلل,كبر كبّر,صغر صغّر,Ø®Ùض,عمليات,قائمة,تغيير القياس,تظليل,تكبير,تصغير,الى الأسÙÙ„,قائمة العمليات,شريط العنوان,تØجيم +Keywords[be]=Згарнуць,Ðайбольшыць,Ðайменшыць,ЗмÑніць памер,Меню аперацыÑÑž,Меню дзеÑннÑÑž,Загаловак,shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize +Keywords[bg]=Ñгъване, макÑимизиране, минимизиране, заглавие, промÑна, размер, shade, maximise, maximize, minimize, minimise, lower, operations menu, titlebar, resize +Keywords[ca]=plegar,maximitzar,minimitzar,passar al davant,operaciones del menú, barra del tÃtol,redimensionar +Keywords[cs]=zarolovánÃ,maximalizace,minimalizace,nabÃdka ÄinnostÃ,titulek,zmÄ›na velikosti +Keywords[csb]=ceniô,maksymalizacëjô,minimalizacëjô,niżi,menu dzejaniów,titlowô lëstew,zmiana miarë +Keywords[cy]=arliwio,ehangu,lleihau,iselhau,dewislen gweithrediadau, bar y teitl, ail-feintio +Keywords[da]=skyg,maksimér,minimér,sænk,operationsmenu,titellinje,ændr størrelse +Keywords[de]=Fensterheber,Maximieren,Minimieren,Nach oben/unten,Titelleiste,Größe ändern +Keywords[el]=Ï„Ïλιγμα,μεγιστοποίηση,μεγιστοποίηση,ελαχιστοποίηση,ελαχιστοποίηση,κατÎβασμα,Î¼ÎµÎ½Î¿Ï Ï‡ÎµÎ¹Ïισμών,μπάÏα τίτλου,αλλαγή μεγÎθους +Keywords[eo]=maksimigi,minimumigi,menuo,titolo,grandecÅanÄi,grandigi +Keywords[es]=recoger,maximizar,minimizar,pasar al frente,pasar atrás,operaciones,menñu barra de tÃtulo,redimensionarmenu,titlebar,resize +Keywords[et]=varjamine,maksimeerimine,minimeerimine,toimingute menüü,langetamine,tõstmine,tiitliriba,suuruse muutmine +Keywords[eu]=bildu,maximizatu,minimizatu,atzera eraman,eragiketak, menua,titulu-barra,tamaina aldatu +Keywords[fa]=سایه، بیشینه‌ساز‌ی، بیشینه‌ساز‌ی، کمینه‌ساز‌ی، کمینه‌ساز‌ی، پایین‌تر، گزینگان عملیات، میله عنوان، تغییر اندازه +Keywords[fi]=varjosta,suurenna,pienennä,laske,toimintovalikko,otsikkopalkki,vaihda kokoa +Keywords[fr]=fondu,maximiser,enrouler,réduire,abaisser, menu des opérations,barre de titre,redimensionner +Keywords[fy]=shade,oprollen,maximaliseren,minimaliseren,naar achtergrond,lower,menu,titelbalk,grootte wijzigen,oprôlje,maksimalisearje,minimalisearje,nei eftergrûn,grutte +Keywords[ga]=scáthaigh,uasmhéadaigh,Ãosmhéadaigh,Ãoslaghdaigh,laghdaigh,roghchlár oibrÃochta,barra teidil,athraigh méid +Keywords[gl]=sombra,maximizar,maximizar,minimizar,baixar,menú de operacións,barra de tÃtulo,redimensionar +Keywords[he]=גלילה,הגדלה,מזעור,שליחה לרקע,תפריט פעולות,שורת כותרת,×©×™× ×•×™ גודל,shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize +Keywords[hi]=शेड,अधिकतम,बड़ा,नà¥à¤¯à¥‚नतम,छोटा,नीचे,ऑपरेशनà¥à¤¸ मेनà¥à¤¯à¥‚,शीरà¥à¤·à¤•-पटà¥à¤Ÿà¥€,नयाआकार +Keywords[hr]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,sjena,maksimiziranje,minimiziranje,postupci izbornika,naslov,naslovna traka,promjena veliÄine +Keywords[hu]=felgördÃtés,maximalizálás,maximalizált,minimalizálás,minimalizált,hátraengedés,műveletek menü,cÃmsor,átméretezés +Keywords[is]=rúlla upp,skyggja,hámarka,lágmarka,sökkva,aðgerðavalmynd, titilrönd,breyta stærð +Keywords[it]=arrotola,massimizza,ingrandisci,minimizza,alza,abbassa,menu operazioni, barra del titolo,titolo,ridimensiona,sposta +Keywords[ja]=シェード,最大化,最å°åŒ–,後é¢ã¸,æ“作メニュー,タイトルãƒãƒ¼,サイズ変更,リサイズ +Keywords[km]=ស្រមោល,ពង្រីក​អážáž·áž”រមា,បង្រួម​អប្បបរមា,បន្ទាប់​ចុះ​ក្រោម,ម៉ឺនុយ​ប្រážáž·áž”ážáŸ’ážáž·áž€áž¶ážš,របារ​ចំណងជើង,ប្ដូរ​ទំហំ +Keywords[lt]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize, iÅ¡didinti,sumažinti,paslÄ—pti, operacijų meniu,antraÅ¡tÄ—s juosta,pakeisti dydį +Keywords[lv]=Ä“not,maksimizÄ“t,maksimizÄ“,minimizet,minimizÄ“,zemÄks,darbÄ«bas izvÄ“lne,virsraksta josla,mainÄ«t izmÄ“ru +Keywords[mk]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,заÑени,рашири,Ñпушти,мени Ñо операции,наÑловна лента,промени големина +Keywords[mn]=Цонхны ÑүүдÑÑ€,ТомÑгох,ЖижигÑгÑÑ…,ДÑÑшÑÑ/доошоо, Толгойн Ñамбар,Ð¥ÑмжÑÑ Ó©Ó©Ñ€Ñ‡Ð¸Ð»Ó©Ñ…, ҮйлдÑл цÑÑ +Keywords[mt]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,massimizza,minimma,baxxi,Ä‹ekken,kabbar,daqqas +Keywords[nb]=rulle opp,maksimere,minimere,endre størrelse,operasjoner,meny,tittel,skalere,vindu, tittellinje +Keywords[nds]=inrullen,maximeren,minimeren,utrullen,Akschonenmenü,Titelbalken,Grött ännern +Keywords[ne]=छायाà¤, बढाउनà¥, बढाउनà¥, घटाउनà¥, घटाउनà¥,à¤à¤¨à¥à¤¦à¤¾ तल, सञà¥à¤šà¤¾à¤²à¤¨ मेनà¥, शीरà¥à¤·à¤•à¤ªà¤Ÿà¥à¤Ÿà¥€, रिसाइज +Keywords[nl]= shade,oprollen,maximaliseren,minimaliseren,naar achtergrond,lower,menu,tibelbalk,grootte wijzigen +Keywords[nn]=rull opp,maksimer,minimer,senk,hev,operasjonsmeny,tittellinje,endra storleik,vindauge +Keywords[nso]=ntshofatsa,oketsa,oketsa,fokotsa,fokotsa,isa fase,menu wa diphethagatso,bar ya leina,popoleswa +Keywords[pa]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,ਨਿਊਨਤਮ,ਅਧਿਕਤਮ,ਕਾਰਵਾਈ ਮੇਨੂ,ਮà©à©œ-ਅਕਾਰ +Keywords[pl]=cieÅ„,maksymalizacja,minimalizacja,niżej,menu dziaÅ‚aÅ„,belka tutuÅ‚owa, zmiana rozmiaru +Keywords[pt]=enrolar,maximizar,minimizar,baixar,menu de operações,barra de tÃtulo,dimensionar +Keywords[pt_BR]=esconder,maximizar,minimizar,menor,menu de operações,barra de operações,redimensionar +Keywords[ro]=derulare,maximizare,minimizare,meniul de operaÈ›ii,bara de titlu,redimensionare +Keywords[rw]=igicucu,kugira kinini,kugira gito,kugira gito,ntoya,ibikubiyemo by'ibikorwa,umwanyabikoresho,ihindurangero +Keywords[se]=rulle bajáš,maksimere,minimere,vuolit,dáhpáhusat,fállu,fálut,namahusholga,namahus,skálere +Keywords[sk]=zabalenie,maximalizácia,minimalizácia,presun dozadu,presun dopredu,operácie menu,titulok,zmena veľkosti +Keywords[sl]=zvij,raztegni,poveÄaj,pomanjÅ¡aj,razpni,menu operacij,naslovna vrstica,velikost +Keywords[sr]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,намотавање,макÑимизуј,минимизуј,ниже,мени операција,наÑловна линија,промена величине +Keywords[sr@Latn]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,namotavanje,maksimizuj,minimizuj,niže,meni operacija,naslovna linija,promena veliÄine +Keywords[sv]=rulla upp,maximera,minimera,sänk,Ã¥tgärdsmeny,namnlist,ändra storlek +Keywords[ta]=நிழலà¯, பெரிதாகà¯à®•à¯,பெரிதாகà¯à®•à¯,சிறிதாகà¯à®•à¯, சிறிதாகà¯à®•à¯, கà¯à®±à¯ˆà®¨à¯à®¤,செயலà¯à®•à®³à¯ படà¯à®Ÿà®¿,தலைபà¯à®ªà¯ படà¯à®Ÿà®¿, அளவை மாறà¯à®±à¯ +Keywords[th]=พับเà¸à¹‡à¸š,ขยายเต็มจà¸,ย่à¸à¹€à¸¥à¹‡à¸à¸ªà¸¸à¸”,à¸à¸¢à¸¹à¹ˆà¸”้านล่าง,เมนูปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£,à¹à¸–บหัวเรื่à¸à¸‡,ปรับขนาด +Keywords[tr]=topla,küçült,büyüt,indir,iÅŸlemler,araç çubuÄŸu +Keywords[tt]=shade,maximise,maximize,minimize,minimise,lower,operations saylaq,titlebar,resize +Keywords[uk]=згорнути,мінімізувати,макÑимізувати,опуÑтити, меню операцій,Ñмужка титулу,зміна розміру +Keywords[uz]=soyalash,yoyish,yigÊ»ish,orqaga,amallar menyusi,sarlavha,resize +Keywords[uz@cyrillic]=ÑоÑлаш,ёйиш,йиғиш,орқага,амаллар менюÑи,Ñарлавҳа,resize +Keywords[ven]=kovhekana,engedza,engedza,fhungudza,fhungudza,fhasi,mashumele a menu, bara ya thoho,uitulula saizi +Keywords[vi]=cuốn,phóng to,phóng to,thu nhá»,thu nhá»,hạ thấp,các thao tác trên thá»±c Ä‘Æ¡n,tên thanh,thay đổi cỡ +Keywords[wa]=ombe,ombion,Ã¥ pus grand,, pus ptit,djivêyes des operÃ¥cions,bÃ¥r di tite,candjî li grandeu +Keywords[xh]=umthunzi,yandisa,yandisa,ncipisa nciphisa,yehlisa,menu zemisebenzi,iba yewonga,yenza ubungakanani kwakhona +Keywords[zh_CN]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,å·èµ·,最大化,最å°åŒ–,é™ä½Ž,æ“作èœå•,æ ‡é¢˜æ ,缩放 +Keywords[zh_TW]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,收起,最大化,最å°åŒ–,é™ä¸‹,æ“作é¸å–®,標題列,é‡è¨å¤§å° +Keywords[zu]=umthunzi,khulisa,khulisa,nciphisa,nciphisa,okungezansi, imenu yemisebenzi,ibha yesihloko,shintsha usayizi diff --git a/kwin/kcmkwin/kwinoptions/kwinadvanced.desktop b/kwin/kcmkwin/kwinoptions/kwinadvanced.desktop new file mode 100644 index 000000000..e8b6b52e8 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwinadvanced.desktop @@ -0,0 +1,232 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinadvanced +DocPath=kcontrol/windowmanagement/index.html#action-advanced + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwinadvanced + +Name=Advanced +Name[af]=Gevorderde +Name[ar]=متقدم +Name[be]=ÐдмыÑÐ»Ð¾Ð²Ñ‹Ñ +Name[bg]=Допълнителни +Name[bn]=অগà§à¦°à¦¸à¦° +Name[br]=Barek +Name[bs]=Napredno +Name[ca]=Avançat +Name[cs]=PokroÄilé +Name[csb]=Awansowóné +Name[cy]=Uwch +Name[da]=Avanceret +Name[de]=Erweitert +Name[el]=Î ÏοχωÏημÎνα +Name[eo]=Pliaj +Name[es]=Avanzado +Name[et]=Muu +Name[eu]=Aurreratua +Name[fa]=پیشرÙته +Name[fi]=Lisäasetukset +Name[fo]=Tilkomið +Name[fr]=Avancé +Name[fy]=Avansearre +Name[ga]=Casta +Name[gl]=Avanzado +Name[he]=×ž×ª×§×“× +Name[hi]=विसà¥à¤¤à¥ƒà¤¤ +Name[hr]=Napredno +Name[hu]=Speciális +Name[is]=Ãtarlegt +Name[it]=Avanzate +Name[ja]=詳細 +Name[ka]=დáƒáƒ›áƒáƒ¢áƒ”ბით +Name[kk]=ҚоÑымша +Name[km]=កម្រិážâ€‹ážáŸ’ពស់ +Name[ko]=ê³ ê¸‰ +Name[lo]=ັàºàº£àº±à»ˆàº‡ +Name[lt]=SudÄ—tingiau +Name[lv]=PaplaÅ¡inÄts +Name[mk]=Ðапредни +Name[mn]=ӨргөтгөÑөн +Name[ms]=Lanjutan +Name[mt]=Avvanzat +Name[nb]=Avansert +Name[nds]=Verwiedert +Name[ne]=उनà¥à¤¨à¤¤ +Name[nl]=Geavanceerd +Name[nn]=Avansert +Name[nso]=Beetswepele +Name[pa]=ਤਕਨੀਕੀ +Name[pl]=Zaawansowane +Name[pt]=Avançado +Name[pt_BR]=Avançado +Name[ro]=Avansat +Name[ru]=Дополнительно +Name[rw]=Bihanitse +Name[se]=Viiddiduvvon +Name[sk]=PokroÄilé +Name[sl]=Napredno +Name[sr]=Ðапредно +Name[sr@Latn]=Napredno +Name[ss]=Sembili +Name[sv]=Avancerat +Name[ta]=உயரà¯à®¨à®¿à®²à¯ˆ +Name[te]=ఆధà±à°¨à°¿à°• +Name[tg]=Пешрафта +Name[th]=เพิ่มเติม +Name[tr]=GeliÅŸmiÅŸ +Name[tt]=Kiñäytep +Name[uk]=Додатково +Name[uz]=QoÊ»shimcha +Name[uz@cyrillic]=Қўшимча +Name[ven]=Zwa khwinesa +Name[vi]=Nâng cao +Name[wa]=Sipepieus +Name[xh]=Ebhekisa phambili +Name[zh_CN]=高级 +Name[zh_TW]=進階 +Name[zu]=Okwezinga eliphezulu + +Comment=Configure advanced window management features +Comment[af]=Konfigureer gevorderde venster bestuuring funksies +Comment[ar]=تهيئة ميزات النواÙØ° المتقدمة +Comment[be]=ÐаÑтаўленне адмыÑловых здольнаÑцÑÑž ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ Ð²Ð¾ÐºÐ½Ð°Ð¼Ñ– +Comment[bg]=ÐаÑтройване допълнителните параметри на прозорците +Comment[bn]=উইণà§à¦¡à§‹ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦®à§‡à¦¨à§à¦Ÿà§‡à¦° কà§à¦·à§à¦¦à§à¦°à¦¾à¦¤à¦¿à¦•à§à¦·à§à¦¦à§à¦° বৈশিষà§à¦Ÿà§à¦¯ কনফিগার করà§à¦¨ +Comment[bs]=Podesite napredne mogućnosti upravljanja prozorima +Comment[ca]=Aquà podeu configurar les caracterÃstiques avançades de la gestió de finestres +Comment[cs]=Nastavenà pokroÄilých vlastnostà správce oken +Comment[csb]=Kònfigùracëjô awansowónëch òptacëji sprôwianiô òknama +Comment[cy]=Ffurfweddu Nodweddion Uwch Trefnu Ffenestri +Comment[da]=Indstil avancerede vindueshÃ¥ndteringsegenskaber +Comment[de]=Erweiterte Fensterverwaltung einrichten +Comment[el]=Ρυθμίστε Ï€ÏοχωÏημÎνα χαÏακτηÏιστικά της διαχείÏισης παÏαθÏÏων +Comment[eo]=Agordu la pli bonajn fenestroadministrilecojn +Comment[es]=Configuración de las propiedades avanzadas del gestor de ventanas +Comment[et]=Aknahalduri seadistamine +Comment[eu]=Konfiguratu leiho kudeaketaren ezaugarri aurreratuak +Comment[fa]=پیکربندی ویژگیهای مدیریت پنجرۀ پیشرÙته +Comment[fi]=Muokkaa ikkunoinnin ominaisuuksien lisäasetuksia +Comment[fr]=Configuration des fonctionnalités de gestion avancée des fenêtres +Comment[fy]=Hjir kinne jo de avansearre finsterbehearmooglikheden ynstelle +Comment[ga]=Cumraigh ardghnéithe bhainisteoireacht na bhfuinneog +Comment[gl]=Configurar as caracterÃsticas avanzadas da xestión de fiestras +Comment[he]=×©×™× ×•×™ הגדרות מתקדמות של × ×™×”×•×œ ×—×œ×•× ×•×ª +Comment[hi]=विसà¥à¤¤à¥ƒà¤¤ विंडो पà¥à¤°à¤¬à¤‚धन विशेषता कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje naprednih svojstava upravitelja prozora +Comment[hu]=Az ablakkezelés speciális beállÃtásai +Comment[is]=Stilla Ãtarlegri hegðan setustjóra +Comment[it]=Configura le funzioni avanzate del gestore delle finestre +Comment[ja]=ウィンドウ管ç†æ©Ÿèƒ½ã®è©³ç´°è¨å®š +Comment[ka]=ფáƒáƒœáƒ¯áƒ ების მენეჯერის დáƒáƒ›áƒáƒ¢áƒ”ბითი ფუნქციების კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезе баÑқаруының қоÑымша параметрлерін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​លក្ážážŽáŸˆâ€‹áž–ិសáŸážŸâ€‹áž€áž¶ážšâ€‹áž‚្រប់គ្រង​បង្អួច​កម្រិážâ€‹ážáŸ’ពស់ +Comment[ko]=ê³ ê¸‰ ì°½ ê´€ë¦¬ìž ê¸°ëŠ¥ ì„¤ì •í•˜ê¸° +Comment[lo]=ທ່ານສາມາດປັບà»àº•à»ˆàº‡àºàº²àº™àº—ຳງານຂàºàº‡à»€àº„ື່àºàº‡àº¡àº·àºˆàº±àº”àºàº²àº™à»€àºŠàºªàºŠàº»àº™à»„ດ້ທີ່ນີ້ +Comment[lt]=KonfigÅ«ruoti sudÄ—tingesnes langų tvarkymo savybes +Comment[lv]=Å eit JÅ«s varat konfigurÄ“t logu pÄrvaldÄ«Å¡anas iespÄ“jas +Comment[mk]=Конфигурирајте ги напредните можноÑти за менаџмент на прозорците +Comment[mn]=ӨргөтгөÑөн цонхны удирдлага тохируулах +Comment[mt]=Setings avvanzati tal-immaniÄ¡Ä¡jar tal-windows +Comment[nb]=Tilpass avansert vindusbehandling +Comment[nds]=Verwiederte Finsterinstellen +Comment[ne]=उनà¥à¤¨à¤¤ सञà¥à¤à¥à¤¯à¤¾à¤² वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ आकृति कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u de geavanceerde vensterbeheermogelijkheden instellen +Comment[nn]=Set opp avanserte vindaugshandsamarfunksjonar +Comment[nso]=Beakanya dika tseo di beetswego pele tsa taolo ya window +Comment[pa]=ਤਕਨੀਕੀ à¨à¨°à©‹à¨–ਾ ਪਰਬੰਧਨ ਫੀਚਰ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja zaawansowanych opcji zarzÄ…dzania oknami +Comment[pt]=Configuração das funcionalidades de gestão de janelas avançadas +Comment[pt_BR]=Configura as caracterÃsticas avançadas de gerenciamento de janela +Comment[ro]=Configurează caracteristicile de management al ferestrelor +Comment[ru]=ÐаÑтройка дополнительных функций менеджера окон +Comment[rw]=Kuboneza ibijyanye n'ubuyobozi buhanitse bw'idirishya +Comment[se]=Heivet viiddiduvvon lásegieÄ‘ahanfunkÅ¡uvnnaid +Comment[sk]=Nastavenie pokroÄilých možnostà správy okien +Comment[sl]=Nastavitve dodatnih možnosti upravljanja oken +Comment[sr]=Подешавање напредних могућноÑти управљања прозорима +Comment[sr@Latn]=PodeÅ¡avanje naprednih mogućnosti upravljanja prozorima +Comment[sv]=Anpassa avancerade fönsterhanteringsfunktioner +Comment[ta]=மேமà¯à®ªà®Ÿà¯à®Ÿ சாளர மேலாளரின௠தனà¯à®®à¯ˆà®•à®³à¯ˆ வடிவமை +Comment[tg]=Танзими мудириÑти пешрафтаи тирезаҳо +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸„ุณสมบัติเพิ่มเติมขà¸à¸‡à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=GeliÅŸmiÅŸ pencere yönetim özelliklerini yapılandır +Comment[tt]=Täräzä idäräçeneñ östämä çaraların caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¸Ñ… можливоÑтей ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–ÐºÐ½Ð°Ð¼Ð¸ +Comment[uz]=Oyna boshqaruvning koÊ»shimcha imkoniyatlarini moslash +Comment[uz@cyrillic]=Ойна бошқарувнинг кўшимча имкониÑтларини моÑлаш +Comment[ven]=Dzudzanyani zwitenwa zwa vhulanguli ha khwinesa +Comment[vi]=Cấu hình các tÃnh năng quản lý cá»a sổ nâng cao +Comment[wa]=Apontiaedje des spepieusès fonccionÃ¥lités do manaedjaedje des purneas +Comment[xh]=Qwalasela imisebenzi esemqoka ebhekisa phambili yophatho lwe window +Comment[zh_CN]=é…置高级窗å£ç®¡ç†ç‰¹æ€§ +Comment[zh_TW]=è¨å®šé€²éšŽè¦–窗管ç†åŠŸèƒ½ +Comment[zu]=Hlanganisela amalungu okuphathwa kwe-window yezinga eliphezulu + +Keywords=shading,border,hover,active borders +Keywords[af]=shading,border,hover,active grense +Keywords[ar]=shading,border,hover,active borders,تظليل,ØاÙØ©,الØوا٠النشطة +Keywords[be]=Згортванне,ÐœÑжа,ÐÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ Ð¼ÐµÐ¶Ñ‹,ÐÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ ÐºÑ€Ð°Ñ–,shading,border,hover,active borders +Keywords[bg]=рамка, ÑÑнка, фокуÑ, активиране, shading, border, hover, active borders +Keywords[ca]=plegat,vora,desplegat automà tic,vores actives +Keywords[cs]=zarolovánÃ,vyrolovánÃ,okraj,pÅ™ejezd myÅ¡Ã,aktivnà okraje +Keywords[csb]=cenienié,graÅ„ce,przesëwanié nad,aktiwné graÅ„ce +Keywords[cy]=arliwio,ymyl,hofran,ymylon weithgar +Keywords[da]=skygning,kant,svæv,aktive kanter +Keywords[de]=Umrandung,Aktive Ränder,Schweben,Fensterheber +Keywords[el]=Ï„Ïλιγμα,πεÏίγÏαμμα,hover,ενεÏγά ÏŒÏια +Keywords[eo]=bordo,ombro,Åvebo +Keywords[es]=recogiendo,borde,ratón sobre,bordes activos +Keywords[et]=varjamine,piire,hover,aktiivsed piirded +Keywords[eu]=biltzen,ertza,gainean,ertz aktiboak +Keywords[fa]=سایه‌زنی، لبه، پلکیدن، لبه‌های Ùعال +Keywords[fi]=varjostus,kehys,kellunta,aktiiviset kehykset +Keywords[fr]=bordures,cacher,couvrir,bordures actives +Keywords[fy]=oprollen,oprôlje,afrollen,ôfrôlje,hoover,actieve randen,aktive rânen,randen magnetisch,magnetische randen +Keywords[ga]=scáthú,imlÃne,foluain,imlÃnte beo +Keywords[gl]=sombra,beira,beiras activas,flotar +Keywords[he]=גלילה,מסגרת,גבול,ריחוף,גבולות פעילי×,shading,border,hover,active borders +Keywords[hi]=शेडिंग,किनारा,हावर,सकà¥à¤°à¤¿à¤¯ किनारे +Keywords[hr]=shading,border,hover,active borders,osjenÄavanje,obrubi,lebdjeće,aktivni obrubi +Keywords[hu]=árnyékolás,szegély,lebegés,aktÃv szegélyek +Keywords[is]=rúlla upp,skyggja,rammi,svif,hover,virkir rammar +Keywords[it]=arrotola,bordi,passaggio del mouse,bordi attivi +Keywords[ja]=畳む,境界,復元,アクティブ境界 +Keywords[km]=ស្រមោល,ស៊ុម,សំកាំង,ស៊ុម​សកម្ម +Keywords[lt]=shading,border,hover,active borders, tik antraÅ¡tÄ—s juosta, rÄ—melis, aktyvÅ«s rÄ—meliai +Keywords[lv]=Ä“noÅ¡ana,mala,hover,aktÄ«vÄs malas +Keywords[mk]=shading,border,hover,active borders,заÑенчување,граница,лебди,активни граници +Keywords[mn]=СүүдÑÑ€,ИдÑвхитÑй хүрÑÑ,ÐрьÑ,ХүрÑÑ +Keywords[mt]=shading,border,hover,active borders,bordura +Keywords[nb]=rull opp,sveve over,ramme,aktive rammer,kant +Keywords[nds]=Inrullen,Utrullen,Rahmen,hover,aktiven Rahmen +Keywords[ne]=छायाà¤, किनारा, होà¤à¤°, सकà¥à¤°à¤¿à¤¯ किनारा +Keywords[nl]=oprollen,afrollen,rand,hoover,actieve randen,randen magnetisch,magnetische randen +Keywords[nn]=rull opp,ramme,vindaugsramme,vindauge,mus over,aktive kantar +Keywords[nso]=ntshofatsa,mollwane,hover,mellwane yeo e berekago +Keywords[pl]=cieniowanie,granice,przesuwanie ponad,aktywne granice +Keywords[pt]=enrolamento,contorno,passar por cima,contornos activos +Keywords[pt_BR]=ocultamento,borda,pairar,bordas ativas +Keywords[ro]=strîngere,margine,margini active +Keywords[rw]=ubwijime,urubibi,kureremba,imbibi zikora +Keywords[se]=shading,border,hover,active borders,aktiivalaÅ¡ láse,láse,aktiivalaÅ¡ ravddat +Keywords[sk]=zabalenie,okraj,presun,aktÃvne okraje +Keywords[sl]=senÄenje,rob,meja,lebdenje,aktivni robovi +Keywords[sr]=намотавање,ивица,лебдење,активне ивице +Keywords[sr@Latn]=namotavanje,ivica,lebdenje,aktivne ivice +Keywords[sv]=skuggning,kant,sväva,aktiva kanter +Keywords[ta]=நிழலிடà¯à®¤à®²à¯,விளிமà¯à®ªà¯,ஹோவரà¯, செயலில௠உளà¯à®³ விளிமà¯à®ªà¯à®•à®³à¯ +Keywords[th]=พับเà¸à¹‡à¸š,à¸à¸£à¸à¸š,à¸à¸¢à¸¹à¹ˆà¹€à¸«à¸™à¸·à¸,à¸à¸£à¸à¸šà¸—ี่ทำงาน +Keywords[tr]=toplama,sınır,etkin kenarlıklar +Keywords[uk]=згортаннÑ,рамка,проходженнÑ,активні рамки +Keywords[uz]=soyalash,chegara,aktiv chegaralar,hover +Keywords[uz@cyrillic]=ÑоÑлаш,чегара,актив чегаралар,hover +Keywords[ven]=murunzi,mukano,hover,mikano i tshilaho +Keywords[vi]=che phủ,bá»,lượn,bá» hoạt Ä‘á»™ng +Keywords[wa]=ombe,ombraedje,boird,costé,hover,ovrants boirds +Keywords[xh]=ulwenziwo lwesithuba,umda,ixhonyiwe,imida esebenzayo +Keywords[zh_CN]=shading,border,hover,active borders,å·èµ·,边框,悬åœ,激活边框 +Keywords[zh_TW]=shading,border,hover,active borders,收起,邊框,åœç•™,作用ä¸é‚Šæ¡† +Keywords[zu]=ukusitha,umngcele,didizela,imingcele enyakazayo diff --git a/kwin/kcmkwin/kwinoptions/kwinfocus.desktop b/kwin/kcmkwin/kwinoptions/kwinfocus.desktop new file mode 100644 index 000000000..115463654 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwinfocus.desktop @@ -0,0 +1,222 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinfocus +DocPath=kcontrol/windowmanagement/index.html#action-focus + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwinfocus + +Name=Focus +Name[af]=Fokus +Name[ar]=التركيز +Name[be]=Ð¤Ð¾ÐºÑƒÑ +Name[bg]=Ð¤Ð¾ÐºÑƒÑ +Name[bn]=ফোকাস +Name[br]=Fokus +Name[bs]=Fokus +Name[cs]=Zaměřenà +Name[csb]=Zrëszanié +Name[cy]=Canolbwynt +Name[da]=Fokus +Name[de]=Aktivierung +Name[el]=Εστίαση +Name[eo]=Fokuso +Name[es]=Foco +Name[et]=Fookus +Name[eu]=Fokua +Name[fa]=کانون +Name[fi]=Kohdistus +Name[ga]=Fócas +Name[gl]=Foco +Name[he]=התמקדות +Name[hi]=फ़ोकस +Name[hr]=Fokus +Name[hu]=Fókuszálás +Name[id]=Fokus +Name[is]=Virkni +Name[ja]=フォーカス +Name[ka]=ფáƒáƒ™áƒ£áƒ¡áƒ˜ +Name[kk]=Ðазар +Name[km]=ផ្ដោážâ€‹áž¢áž¶ážšáž˜áŸ’មណ០+Name[ko]=ì´ˆì +Name[lo]=ຫີນ +Name[lt]=Fokusas +Name[lv]=Fokuss +Name[mk]=ФокуÑирање +Name[mn]=ИдÑвхижүүлÑÑ… +Name[ms]=Fokus +Name[nb]=Fokus +Name[nds]=Fokus +Name[ne]=फोकस +Name[nn]=Fokus +Name[nso]=Tebeledisiso +Name[pa]=ਕੇਂਦਰ +Name[pl]=Ognisko +Name[pt]=Foco +Name[pt_BR]=Foco +Name[ru]=Ð¤Ð¾ÐºÑƒÑ +Name[rw]=Igaragaza rikeye +Name[se]=Fohkus +Name[sk]=Fokus +Name[sl]=Fokus +Name[sr]=Ð¤Ð¾ÐºÑƒÑ +Name[sr@Latn]=Fokus +Name[sv]=Fokus +Name[ta]=à®®à¯à®©à¯ˆà®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[te]=ఫొకసౠ+Name[tg]=Марказ +Name[th]=โฟà¸à¸±à¸ª +Name[tr]=Odaklama +Name[tt]=EÅŸ Ãœzäge +Name[uk]=Ð¤Ð¾ÐºÑƒÑ +Name[uz]=Fokus +Name[uz@cyrillic]=Ð¤Ð¾ÐºÑƒÑ +Name[ven]=U thetshelesa +Name[vi]=Chá»n +Name[zh_CN]=焦点 +Name[zh_TW]=焦點 +Name[zu]=Ukunaka + +Comment=Configure the window focus policy +Comment[af]=Konfigureer die venster fokus beleid +Comment[ar]=قم بإعداد سياسة التركيز على النواÙØ° +Comment[be]=ÐаÑтаўленні выкарыÑÑ‚Ð°Ð½Ð½Ñ Ñ„Ð¾ÐºÑƒÑу +Comment[bg]=ÐаÑтройване активирането на прозорците +Comment[bn]=উইণà§à¦¡à§‹ ফোকাস পলিসি কনফিগার করà§à¦¨ +Comment[br]=Kefluniañ politikerez fokus ar prenestr +Comment[bs]=Podesite politiku fokusa prozora +Comment[ca]=Aquà podeu configurar la polÃtica del focus +Comment[cs]=Nastavenà způsobu zaměřovánà oken +Comment[csb]=Kònfigùracëjô zrëszaniô òknów +Comment[cy]=Ffurfweddu'r polisi canolbyntio ffenestri +Comment[da]=Indstil vinduers fokuspolitik +Comment[de]=Fensteraktivierung einrichten +Comment[el]=Ρυθμίστε τον Ï„Ïόπο εστίασης των παÏαθÏÏων +Comment[eo]=Agordu la fenestran fokusdirektivon +Comment[es]=Configuración de la polÃtica de enfocado de ventanas +Comment[et]=Akendele fookuse andmise reeglid +Comment[eu]=Konfiguratu leihoen fokuaren portaera +Comment[fa]=پیکربندی خط مشی کانون پنجره +Comment[fi]=Ikkunoiden kohdistustapa +Comment[fr]=Configuration de la politique de focus des fenêtres +Comment[fy]=Hjir kinne jo it fokusbeliid fan finsters ynstelle +Comment[ga]=Cumraigh an polasaà um fhócas fuinneoige +Comment[gl]=Configurar polÃtica de outorgación do foco ás fiestras +Comment[he]=קביעת ×ž×“×™× ×™×•×ª ההתמקדות של ×—×œ×•× ×•×ª +Comment[hi]=विंडो फ़ोकस पॉलिसी कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje pravila za fokusiranje prozora +Comment[hu]=Az ablakok fókuszálási jellemzÅ‘inek beállÃtásai +Comment[is]=Stilla virkni stefnu +Comment[it]=Configura la politica del focus delle finestre +Comment[ja]=ウィンドウã®ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ãƒãƒªã‚·ãƒ¼ã®è¨å®š +Comment[kk]=Терезедегі назар тартібін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​គោលការណáŸâ€‹áž•áŸ’ដោážâ€‹áž¢áž¶ážšáž˜áŸ’មណáŸâ€‹ážšáž”ស់​បង្អួច +Comment[ko]=ì°½ ì´ˆì ì •ì±… ì„¤ì •í•˜ê¸° +Comment[lo]=ປັàºàºàºµàº™àºªàºàº”ສ່àºàº‡àºŠàºµàºžàºµàºàº¹ +Comment[lt]=KonfigÅ«ruoti langų fokusavimo taisykles +Comment[lv]=KonfigurÄ“ logu fokusÄ“Å¡anÄs politiku +Comment[mk]=Конфигурирајте ја политиката на фокуÑирање на прозорците +Comment[mn]=Цонх идÑвхижүүлÑлт тохируулах +Comment[mt]=Ikkonfigura kif jieħdu l-focus il-windows +Comment[nb]=Tilpass praksisen for vindusfokus +Comment[nds]=Regeln för den Finsterfokus instellen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² फोकस नीति कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u het vensterfocusbeleid instellen +Comment[nn]=Set opp praksisen for vindaugsfokus +Comment[nso]=Beakanya molao wa tebeledisiso ya window +Comment[pa]=à¨à¨°à©‹à¨–ਾ ਕੇਂਦਰ ਨੀਤੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja przenoszenia ogniska +Comment[pt]=Configuração da polÃtica de foco (primeiro plano) das janelas +Comment[pt_BR]=Configura a polÃtica de foco de janela +Comment[ro]=Configurează politica de focus a ferestrelor +Comment[ru]=ÐаÑтройка политики фокуÑÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾ÐºÐ¾Ð½ +Comment[rw]=Kuboneza politiki y'igaragazarikeye ry'idirishya +Comment[se]=Heivet lásefohkusa doaibmanvuogi +Comment[sk]=Nastavenie politiky aktivácie okien +Comment[sl]=Nastavitve ravnanja s fokusom okna +Comment[sr]=Подешавање начина фокуÑирања прозора +Comment[sr@Latn]=PodeÅ¡avanje naÄina fokusiranja prozora +Comment[sv]=Anpassa policy för fönsterfokus +Comment[ta]=சாளர தà¯à®µà®•à¯à®• à®®à¯à®±à¯ˆà®¯à¯ˆ வடிவமை +Comment[tg]=Танзими ÑиёÑати маркази тиреза +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸£à¸¹à¸›à¹à¸šà¸šà¸à¸²à¸£à¹„ด้รับโฟà¸à¸±à¸ªà¸‚à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=Pencere odaklama yöntemini yapılandır +Comment[tt]=Täräzä terelätü tärtiben caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²ÐµÐ´Ñ–Ð½ÐºÑƒ фокуÑу вікна +Comment[uz]=Oynani fokuslash qoidasini moslash +Comment[uz@cyrillic]=Ойнани фокуÑлаш қоидаÑини моÑлаш +Comment[ven]=Dzudzanyani wa muano wa window +Comment[vi]=Cấu hình cách chá»n cá»a sổ +Comment[wa]=Aponteye li dujhance di focus di purnea +Comment[xh]=Qwalasela inkqubo yobekiso lwe window +Comment[zh_CN]=é…置窗å£ç„¦ç‚¹ç–ç•¥ +Comment[zh_TW]=è¨å®šè¦–çª—ç„¦é»žæ”¿ç– +Comment[zu]=Hlanganisela umthetho wokunaka kwe-window + +Keywords=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop +Keywords[af]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all werkskerm +Keywords[be]=ФокуÑ,РамÑшчÑнне,КлавіÑтура,УÑе Ð¿Ñ€Ð°Ñ†Ð¾ÑžÐ½Ñ‹Ñ Ñталы,focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop +Keywords[bg]=фокуÑ, активиране, автоматично, щракане, focus, placement, auto raise, raise, click raise, keyboard, CDE, alt-tab, all desktop +Keywords[ca]=focus,emplaçament,elevar automà ticament,elevar,elevar en clicar,teclat,CDE,alt-tab,tot l'escriptori +Keywords[cs]=zaměřenÃ,umÃsÅ¥ovánÃ,automatické zdviženÃ,klávesnice,CDE,Alt-Tab,plochy +Keywords[csb]=focus, ùmieszczanié, aùtomatné przenoszenié,pòdnoszenié,pòdnoszenié przë klëkniãcô,klawiatura,CDE,alt-tab,wszëtczé biurka,wszëtczé pùltë +Keywords[cy]=canolbwynt,lleoliad,hunan-godi,codi,codi ar ?l clic,allweddell,CDE,alt-tab,penbwrdd cyfan +Keywords[da]=fokus,placering,autohæv,hæv,klikhæv,tastatur,CDE,alt-tab,alle desktoppe +Keywords[de]=Fokus,Aktivierung,Platzierung,Autom. Aktivierung,Nach oben/unten, Klick,Tastatur,CDE +Keywords[el]=εστίαση,τοποθÎτηση,αυτόματη ανÏψωση,ανÏψωση,ανÏψωση με πάτημα,πληκτÏολόγιο,CDE,alt-tab,all desktop +Keywords[eo]=fokuso,lokado,aÅto-altigo,altigo,kliki,kilk altigo,klavaro,CDE,alt-tab,ĉiuj tabuloj +Keywords[es]=foco,ubicación,auto psara al frente,pasar al frente,pulsar,teclado,CDE,alt-tab,todo el escritorio +Keywords[et]=fookus,asetus,klikk,klõps,klaviatuur,CDE,alt-tab +Keywords[eu]=fokua,kokapena,auto aurrera ekarri,aurrera ekarri,klik eta aurrera eraman,teklatua,CDE,alt-tab,mahaigain guztiak +Keywords[fa]=کانون، جای‌دهی، بالا بردن خودکار، بالا بردن، بالا بردن Ùشار، صÙØÙ‡ کلید، CDEØŒ دگرساز-جهش، همۀ رومیزیها +Keywords[fi]=fokus,sijoitus,automaattinosto,napsautusnosto,näppäimistö,CDE +Keywords[fr]=focus,placement,auto élévation,élévation,clic clavier,CDE,alt-tab,tous les bureaux +Keywords[fy]=focus,fokus,finster,aandacht,oandacht,plaatsing,pleatsing,voorgrond,foargrûn,achtergrond,eftergrûn,klikken,toetsenbord,toetseboerd,CDE,alt-tab,alle bureaubladen,alle buroblêden +Keywords[ga]=fócas,láithriú,uathardaigh,ardaigh,gliogardaigh,méarchlár,CDE,alt-tab,gach deasc +Keywords[gl]=foco,emprazamento,auto subir,subir,premer para subir,teclado,CDE,alt-tab,todo o escritório +Keywords[he]=התמקדות,מיקו×,הב××” ×וטומטית לחזית,הב××” לחזית,לחיצה להב××” לחזית,מקלדת,כל ×©×•×œ×—× ×•×ª העבודה, focus,placement,auto raise,raise, click raise,keyboard,CDE,alt-tab,all desktop +Keywords[hi]=फ़ोकस, सà¥à¤¥à¤¿à¤¤à¤¿,सà¥à¤µà¤¤à¤ƒ ऊपर उठाà¤à¤,ऊपर उठाà¤à¤,कà¥à¤²à¤¿à¤• ऊपर उठाà¤à¤,कà¥à¤‚जीपट,सीडीई,ऑलà¥à¤Ÿ-टेब,सà¤à¥€ डेसà¥à¤•à¤Ÿà¥‰à¤ª +Keywords[hr]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,fokus,smjeÅ¡taj,automatska pojava,pojava,pojava klikanjem,tipkovnica,sve radne povrÅ¡ine +Keywords[hu]=fókusz,elhelyezés,automatikus elÅ‘rehozás,elÅ‘rehozás,elÅ‘rehozás kattintásra,billentyűzet,CDE,Alt+Tab,minden munkaasztal +Keywords[id]=fokus,penempatan,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop +Keywords[is]=fókus,staðsetning,fleyta upp sjálfkrafa, fleyta upp,smellur fleytir upp,lyklaborð,CDE,alt-tab,öll skjáborð +Keywords[it]=focus,piazzamento,alza automaticamente,alza,clic per alzare,tastiera, CDE,alt-tab,tutti i desktop +Keywords[ja]=フォーカス,é…ç½®,自動的ã«å‰é¢ã«,å‰é¢ã«,クリックã—ã¦å‰é¢ã«,ã‚ーボード,CDE,alt-tab,ã™ã¹ã¦ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ— +Keywords[km]=ផ្ážáŸ„ážâ€‹áž¢áž¶ážšáž˜áŸ’មណáŸ,ការ​ដាក់,លើក​ឡើង​ស្វáŸáž™áž”្រវážáŸ’ážáž·,លើកឡើង,ចុច លើកឡើង,ក្ដារចុច,CDE,ជំនួស-ážáŸáž”,ផ្ទៃážáž»â€‹áž‘ាំងអស់ +Keywords[lt]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop, fokusas,perkÄ—limas į pirmÄ… planÄ…, pirmas planas, klaviatÅ«ra, CDE,visi darbastaliai +Keywords[lv]=fokuss,novietojums,automÄtiska pacelÅ¡anÄs,pacelÅ¡anÄs,klikÅ¡Ä·araise,tastatÅ«ra,CDE,alt-tab,visas darbavirsmas +Keywords[mk]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,фокуÑ,позиција,авто подигање,подигање,клик подигање,Ñите деÑктопи +Keywords[mn]=Хараа,ИдÑвхижүүлÑл,Зайчлал,Ðвто.ИдÑвхижүүлÑлт, ДÑÑшÑÑ/доошоо,Товшилт,Гар,КДE +Keywords[mt]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,tella',tastiera +Keywords[nb]=fokus,plassering,autohev,hev,klikk og hev,tastatur,CDE,bytt vindu,alle skrivebord +Keywords[nds]=aktiveren,Fokus,Platzeren,automaatsch fokusseren,aktiveren,klick fokusseren,Tastatuur,CDE,ALT-TAB,all Schriefdischen +Keywords[ne]=फोकस, सà¥à¤¥à¤¾à¤ªà¤¨à¤¾, सà¥à¤µà¤¤: बृदà¥à¤§à¤¿, बृदà¥à¤§à¤¿, बृदà¥à¤§à¤¿ कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥, कà¥à¤žà¥à¤œà¥€à¤ªà¤¾à¤Ÿà¥€,CDE,अलà¥à¤Ÿ-टà¥à¤¯à¤¾à¤¬, सबै डेसà¥à¤•à¤Ÿà¤ª +Keywords[nl]=focus,aandacht,plaatsing,voorgrond,achtergrond,klikken,toetsenbord,CDE,alt-tab,alle bureaubladen +Keywords[nn]=fokus,plassering,autohev,hev,klikk og hev,tastatur,CDE,byt vindauge,alle skrivebord +Keywords[nso]=tebeledisiso,peo,kgodiso ya go itirisa,kgodiso,tobetsa kgodiso,keyboard,CDE,alt-tab,desktop kamoka +Keywords[pl]=ognisko,umiejscawianie,automatyczne podnoszenie,podnoszenie, podniesienie przy klikniÄ™ciu,klawiatura,CDE,alt-tab,wszystkie biurka,wszystkie pulpity +Keywords[pt]=foco,colocação,auto-elevar,elevar,elevar com 'click',teclado,CDE,alt-tab,todos os ecrãs +Keywords[pt_BR]=foco,posição,auto elevar,elevar,teclado,CDE,alt-tab,todas as áreas de trabalho +Keywords[ro]=focus,plasare,ridicare automată,clic,tastatură,CDE,alt-tab,toate ecranele +Keywords[rw]=igaragazarikeye,ishyira mu mwanya,byizamura,kuzamura,Kanda kuzamura,Mwandikisho,CDE,Alt - tab,Ibiro byose +Keywords[se]=fohkus,auto lokten,lokten,coahkkal ja lokte,boallobeavdi,CDE,molssu lássii,buot Äállinbeavddi +Keywords[sk]=fokus,umiestnenie,automatický presun,dopredu,dopredu na kliknutie,klávesnica,CDE,alt-tab,vÅ¡etky plochy +Keywords[sl]=fokus,postavitev,dvigni,tipkovnica,CDE,alt-tab,vsa namizja +Keywords[sr]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,фокуÑ,Ñмештање,аутоматÑко подизање,подизање,таÑтатура,alt-tab, Ñви радна површина +Keywords[sr@Latn]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,fokus,smeÅ¡tanje,automatsko podizanje,podizanje,tastatura,alt-tab, svi radna povrÅ¡ina +Keywords[sv]=fokus,placering,autohöjning,höjning,höjning med klick,tangentbord,CDE,alt-tab,alla skrivbord +Keywords[ta]=à®’à®°à¯à®®à¯à®•à®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®¤à®²à¯,இடமà¯, தானாக மேலெழà¯à®®à¯à®ªà¯, மேலேழà¯à®®à¯à®ªà¯à®¤à®²à¯, மேல௠கà¯à®³à®¿à®•à¯à®šà¯†à®¯à¯,விசைபà¯à®ªà®²à®•à¯ˆ,CDE,alt-tab,எலà¯à®²à®¾ மேலà¯à®®à¯‡à®šà¯ˆ +Keywords[th]=โฟà¸à¸±à¸ª,ตำà¹à¸«à¸™à¹ˆà¸‡,à¸à¸¢à¸¹à¹ˆà¸”้านบนà¸à¸±à¸•à¹‚นมัติ,à¸à¸¢à¸¹à¹ˆà¸”้านบน,คลิ้à¸à¹€à¸žà¸·à¹ˆà¸à¸à¸¢à¸¹à¹ˆà¸”้านบน,à¹à¸›à¹‰à¸™à¸žà¸´à¸¡à¸žà¹Œ,CDE,alt-tab,ทุà¸à¸žà¸·à¹‰à¸™à¸—ี่ทำงาน +Keywords[tr]=odaklama,yerleÅŸim,masaüstü,yükselt,tıkla,klavye,CDE,yerleÅŸim +Keywords[uk]=фокуÑ,розташуваннÑ,автопідніманнÑ,піднÑти,клавіатура,CDE,alt-tab,вÑÑ ÑÑ‚Ñ–Ð»ÑŒÐ½Ð¸Ñ†Ñ +Keywords[uz]=avto-oldinga,oldinga,bosib oldinga,CDE,alt-tab,hamma ish stollari,fokuslash,tugmatag,joylashish +Keywords[uz@cyrillic]=авто-олдинга,олдинга,боÑиб олдинга,CDE,alt-tab,ҳамма иш Ñтоллари,фокуÑлаш,тугматаг,жойлашиш +Keywords[ven]=thetshelesa,u vhetshela,u vusa,vusa,putedzani u vusa,khiibodo,CDE,alt-tab,desktop dzothe +Keywords[vi]=chá»n,di chuyển,tá»± nâng lên,nâng lên,nhấn nâng lên,bà n phÃm,CDE,alt-tab,tất cả mà n hình ná»n +Keywords[wa]=focus,plaece,rimonte tot seu,rimonter,rimonte s' on clitche,taprece,CDE,ALT-TAB,tos les scribannes +Keywords[xh]=ucaco,ubuyiselo,phakamisa ngokuzenzekelayo,phakamisa,nqakraza phakamisa, ibhodi yezitshixho,CDE,alt-tab,zonke ii desktop +Keywords[zh_CN]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,焦点,放置,自动å‡èµ·,å‡èµ·,å•å‡»å‡èµ·,键盘,å…¨éƒ¨æ¡Œé¢ +Keywords[zh_TW]=focus,placement,auto raise,raise,click raise,keyboard,CDE,alt-tab,all desktop,焦點,佈置,自動抬å‡,抬å‡,單擊抬å‡,éµç›¤,å…¨éƒ¨æ¡Œé¢ +Keywords[zu]=ukunaka,ukubeka,ukukhuphula okuzenzekelayo,ukukhuphula, ukukhuphula ngokucofa,ibhodi yokhiye,CDE,alt-tab,wonke ama-desktop diff --git a/kwin/kcmkwin/kwinoptions/kwinmoving.desktop b/kwin/kcmkwin/kwinoptions/kwinmoving.desktop new file mode 100644 index 000000000..912418e0e --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwinmoving.desktop @@ -0,0 +1,229 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinmoving +DocPath=kcontrol/windowmanagement/index.html#action-moving + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwinmoving + +Name=Moving +Name[af]=Beweeg +Name[ar]=تØريك +Name[be]=ПерамÑшчÑнне +Name[bg]=ПремеÑтване +Name[bn]=সরানো হচà§à¦›à§‡ +Name[br]=O tilec'hiañ +Name[bs]=Pomjeranje +Name[ca]=Moviment +Name[cs]=PÅ™esouvánà +Name[csb]=Przesëwanié +Name[cy]=Symud +Name[da]=Flytter +Name[de]=Verschieben +Name[el]=Μετακίνηση +Name[eo]=Movanta +Name[es]=Moviendo +Name[et]=Liigutamine +Name[eu]=Mugitzen +Name[fa]=Øرکت +Name[fi]=Siirtäminen +Name[fo]=Flytir +Name[fr]=Déplacement +Name[fy]=Ferpleatsing +Name[ga]=Bogadh +Name[gl]=Movemento +Name[he]=×”×–×–×” +Name[hi]=खिसक रहा है +Name[hr]=Pomicanje +Name[hu]=Mozgatás +Name[id]=Pindah +Name[is]=Færa +Name[it]=Spostamento +Name[ja]=移動 +Name[ka]=გáƒáƒ“áƒáƒáƒ“გილებრ+Name[kk]=Жылжыту +Name[km]=ការ​ផ្លាស់ទី +Name[ko]=ì´ë™ +Name[lo]=ລàºàºàºàº´àº™ +Name[lt]=PerkÄ—limas +Name[lv]=PÄrvietoÅ¡ana +Name[mk]=Движење +Name[mn]=ШилжүүлÑÑ… +Name[mt]=Tmexxija +Name[nb]=Flytting +Name[nds]=Verschuven +Name[ne]=सारà¥à¤¦à¤¾ +Name[nl]=Verplaatsing +Name[nn]=Flytting +Name[nso]=Sutha +Name[pa]=à¨à¨§à¨°-ਓਧਰ +Name[pl]=Przesuwanie +Name[pt]=Mover +Name[pt_BR]=Movendo +Name[ro]=Mutare +Name[ru]=Перемещение +Name[rw]=Kwimura +Name[se]=Lihkadeamen +Name[sk]=Presun +Name[sl]=Premikanje +Name[sr]=Померање +Name[sr@Latn]=Pomeranje +Name[sv]=Förflyttning +Name[ta]=நகரà¯à®•à®¿à®±à®¤à¯ +Name[te]=కదలిక +Name[tg]=Ҳаракат +Name[th]=à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢ +Name[tr]=Taşıma +Name[tt]=Küçerü +Name[uz]=KoÊ»chirish +Name[uz@cyrillic]=Кўчириш +Name[ven]=U tshimbila +Name[vi]=Di chuyển +Name[wa]=Bodjî +Name[xh]=Iyahamba +Name[zh_CN]=移动 +Name[zh_TW]=移動 +Name[zu]=Iyanyakaza + +Comment=Configure the way that windows are moved +Comment[af]=Konfigureer die weg wat vensters word verskuif +Comment[ar]=تعري٠الطريقة التي تتØرك Ùيها النواÙØ° +Comment[be]=ÐаÑтаўленні перамÑшчÑÐ½Ð½Ñ Ð²Ð¾ÐºÐ½Ð°Ñž +Comment[bg]=ÐаÑтройване премеÑтването на прозорците +Comment[bn]=উইণà§à¦¡à§‹ কিà¦à¦¾à¦¬à§‡ সরানো হবে তা কনফিগার করà§à¦¨ +Comment[br]=Kefluniañ an daoare e vez fiñvalet ar prenester +Comment[bs]=Podesite naÄin pomjeranja prozora +Comment[ca]=Aquà podeu configurar els valors per quan es moguin les finestres +Comment[cs]=Nastavenà způsobu pÅ™esouvánà oken +Comment[csb]=Kònfigùracëjô sztélu przesëwaniô òknów +Comment[cy]=Ffurfweddu'r ffordd y symudir ffenestri +Comment[da]=Indstil den mÃ¥de vinduer flyttes pÃ¥ +Comment[de]=Festlegen, wie Fenster verschoben werden +Comment[el]=Ρυθμίστε τον Ï„Ïόπο με τον οποίο μετακινοÏνται τα παÏάθυÏα +Comment[eo]=Agordu la manieron, per kiu oni movas la fenestrojn +Comment[es]=Configuración del modo de movimiento de las ventanas +Comment[et]=Akende liigutamise viisi seadistamine +Comment[eu]=Konfiguratu leihoak mugitzeko era +Comment[fa]=پیکربندی روشی Ú©Ù‡ پنجره‌ها Øرکت می‌کنند +Comment[fi]=Ikkunoiden siirtäminen +Comment[fr]=Configuration de la manière dont les fenêtres sont déplacées +Comment[fy]=Hjir kinne jo de wize wêrop finsters har ferpleatse ynstelle +Comment[ga]=Cumraigh conas a aistrÃtear fuinneoga +Comment[gl]=Aqui pode configurar o xeito en que se moven as fiestras +Comment[he]=×©×™× ×•×™ הדרך שבה ×—×œ×•× ×•×ª ×ž×•×–×–×™× +Comment[hi]=कॉनà¥à¤«à¤¼à¤¿à¤—र करें कि विंडो किस तरह खिसके +Comment[hr]=Konfiguriranje naÄina pomicanja prozora +Comment[hu]=Az ablakmozgatás beállÃtásai +Comment[is]=Stilla hvernig gluggar eru hreyfðir +Comment[it]=Configura il modo in cui vengono spostate le finestre +Comment[ja]=ウィンドウã®ç§»å‹•ã®ä»•æ–¹ã®è¨å®š +Comment[ka]=ფáƒáƒœáƒ¯áƒ ების გáƒáƒ“áƒáƒáƒ“გილების კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезені жылжыту Ñ‚Ó™Ñілін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​របៀប​ដែល​បង្អួច​ážáŸ’រូវ​បាន​ផ្លាស់ទី +Comment[ko]=ì°½ì´ ì›€ì§ì´ëŠ” 방법 ì„¤ì •í•˜ê¸° +Comment[lo]=ປັບà»àº•à»ˆàº‡àºàº²àº™àº•àº±à»‰àº‡àº„່າລະບົບà»àº„ຊທີ່ນີ่້ +Comment[lt]=KonfigÅ«ruoti langų perkÄ—limÄ… +Comment[lv]=Å eit JÅ«s varat konfigurÄ“t kÄ tiek pÄrvietoti logi +Comment[mk]=Конфигурирајте го начинот на кој Ñе движат прозорците +Comment[mn]=Цонхыг Ñааж шилжүүлÑхийг тогтоох +Comment[mt]=Ikkonfigura kif tmexxi l-windows +Comment[nb]=Velg hvordan vinduer skal flyttes +Comment[nds]=Instellen, woans de Finstern beweegt warrt +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤¹à¤°à¥‚ सारà¥à¤¨à¥‡ तरिका कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u de wijze waarop vensters worden verplaatst instellen +Comment[nn]=Vel korleis vindauga skal flyttast +Comment[nso]=Beakanya ka mokgwa wo window yeo e suthiswago kagona +Comment[pa]=à¨à¨°à©‹à¨–ੇ ਦੇ ਹਿੱਲਣ ਦੇ ਢੰਗ ਦੀ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja stylu przesuwania okien +Comment[pt]=Configuração da forma como as janelas são movidas +Comment[pt_BR]=Configura o modo como as janelas são movidas +Comment[ro]=Configurează modul în care sînt mutate ferestrele +Comment[ru]=ÐаÑтройка ÑпоÑоба Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¾ÐºÐ¾Ð½ +Comment[rw]=Kuboneza uburyo amadirishya yimurwa +Comment[se]=Heivet mo láset lihkaduvvot +Comment[sk]=Nastavenie presunu okien +Comment[sl]=Nastavitve naÄinov premikanja okna +Comment[sr]=Подешавање начина на који Ñе прозори померају +Comment[sr@Latn]=PodeÅ¡avanje naÄina na koji se prozori pomeraju +Comment[sv]=Anpassa hur fönster flyttas +Comment[ta]=சாளரம௠நகரà¯à®°à¯à®®à¯ விததà¯à®¤à¯ˆ அமை +Comment[tg]=Танзими навъи ҳаракати тирезаҳо +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸¥à¸±à¸à¸©à¸“ะวิธีà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=Pencerelerin taşınması yöntemini yapılandır +Comment[tt]=Täräzä küçerü ısulın caylaw +Comment[uk]=Тут можна налаштувати поведінку при переÑуванні вікон +Comment[uz]=Oynalarni koÊ»chirish yoÊ»lini moslash +Comment[uz@cyrillic]=Ойналарни кўчириш йўлини моÑлаш +Comment[ven]=Dzudzanyani ndila ine windoow ya tshimbidzhwa zwone +Comment[vi]=Cấu hình cách di chuyển cá»a sổ +Comment[wa]=Apontyî l' manire di bodjî des purneas +Comment[xh]=Qwalasela iindlela ii windows ezshukunyiswa ngayo +Comment[zh_CN]=é…置窗å£ç§»åŠ¨çš„æ–¹å¼ +Comment[zh_TW]=è¨å®šè¦–çª—ç§»å‹•çš„æ–¹å¼ +Comment[zu]=Hlanganisela indlela ama-window anyakaziswa ngayo + +Keywords=moving,smart,cascade,maximize,maximise,snap zone,snap,border +Keywords[be]=ПерамÑшчÑнне,КаÑкадам,Ðайбольшыць,Ðайменшыць,ÐœÑжа,moving,smart,cascade,maximize,maximise,snap zone,snap,border +Keywords[bg]=премеÑтване, прозорци, прозорец, промÑна, размер, размера, moving, smart, cascade, maximize, maximise, snap zone, snap, border +Keywords[ca]=moviment,astut,cascada,maximitza,zona d'atracció,atracció,vora +Keywords[cs]=pÅ™esun,chytré,kaskáda,maximalizace,zóna,okraje +Keywords[csb]=przesëwanié,kaskadowò,masymilizacëjô,òbéńda chwëtaniô,chwëtanié,graÅ„ca +Keywords[cy]=symud,clyfar,rhaedru,maint llawn,cylchfa gosod,snap,ymyl +Keywords[da]=flytter,smart,stabl,maksimér,snapzone,snap,kant +Keywords[de]=Verschieben,Gestaffelt,Maximieren,Minimieren,Einrastzone,Ränder +Keywords[el]=μετακίνηση,Îξυπνη,διαδοχικός,μεγιστοποίηση,μεγιστοποίηση,ζώνη αÏπαγής,αÏπαγή,πεÏίγÏαμμα +Keywords[eo]=movanta,maksimigi,bordo +Keywords[es]=moviendo,inteligente,cascada,maximizar,zona de adhesión,adhesión,adherir,borde +Keywords[et]=liigutamine,tark,kaskaad,maksimeerimine,haaramise tsoon,haaramine,piire +Keywords[eu]=mugitzen,fina,kaskadan,maximizatu,itsasketa eremua, itsasketa,ertza +Keywords[fa]=Øرکت، هوشمند، آبشاری، بیشینه‌ساز‌ی، بیشینه‌ساز‌ی، منطقۀ پرش، پرش، لبه +Keywords[fi]=liikkuva,älykäs,sarjoita,suurenna,kehys +Keywords[fr]=déplacement,cascade,maximiser,minimiser,coller,zone d'attraction,bordure +Keywords[fy]=verplaatsing,ferpleatse,intelligen,yntelligint,tûk,plaatsing,pleatsing,trapsgewijs,trepfoarm,maximaliseren,maksimalisearje,randen magnetisch,magnetisch veld,rand,râne,magnetische randen +Keywords[gl]=movemento,pequeno,fervenza,maximizar,maximizar,zona de suxección,suexección,beira +Keywords[he]=×”×–×–×”,×—×›×,מדורג,הגדלה,×זור הצמדה,הצמדה,גבול, moving,smart,cascade,maximize,maximise,snap zone,snap,border +Keywords[hi]=खिसकता,चतà¥à¤°,कासà¥à¤•à¥‡à¤¡,अधिकतम,नà¥à¤¯à¥‚नतम,सà¥à¤¨à¥ˆà¤ª ज़ोन,सà¥à¤¨à¥‡à¤ª,किनारा +Keywords[hr]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,pomicanje,pametno,kaskadno,maksimiziranje,snap zona,snap,obrub +Keywords[hu]=mozgatás,intelligens,lépcsÅ‘zetes,maximalizálás,illeszkedési zóna,illeszkedés,szegély +Keywords[id]=pindah,smart,cascade,maksimum,minimum,snap zone,snap,border +Keywords[is]=færi,snjallt,stafla,hámarka,gripsvæði,grip,rammi,jaðar +Keywords[it]=spostamento,intelligente,cascata,massimizza,zona magnetica,bordi,bordi magnetici +Keywords[ja]=移動,スマート,カスケード,最大化,スナップゾーン,スナップ境界 +Keywords[km]=ការ​ផ្លាស់ទី,ឆ្លាáž,ល្បាក់,ពង្រីក​អប្បបរមា,ážáŸ†áž”ន់​ážáŸ’ទាស់,ážáŸ’ទាស់,ស៊ុម +Keywords[lt]=moving,smart,cascade,maximize,maximise,snap zone,snap,border, perkÄ—limas,iÅ¡moningas,kaskada,iÅ¡didinti,rÄ—melis,riba +Keywords[lv]=pÄrvietoÅ¡ana,gudra,kaskÄde,maksimizÄ“,maksimizÄ“Å¡ana,pielipsanas zona,pielipt,mala +Keywords[mk]=moving,smart,cascade,maximize,maximise,snap zone,snap,border, движење,паметно,каÑкадно,рашири,Ñпушти,граница +Keywords[mn]=ШилжүүлÑÑ…,Ухаалаг,Зайчилах,ТомÑгох,ЖижигÑгÑÑ…,Талбай,ХүрÑÑ +Keywords[mt]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,tmexxija,kaskada,massimizza,faqqa',bordura +Keywords[nb]=flytting,smart,overlapping,maksimer,kantlÃ¥s,vindu +Keywords[nds]=bewegen,verschuven,plietsch,maximeren,minimeren,Andockrebeet,andocken,Rahmen +Keywords[ne]=सारà¥à¤¨à¥‡, छरितो, कà¥à¤¯à¤¾à¤¸à¤•à¥‡à¤¡, बढाउनà¥, बढाउनà¥, सà¥à¤¨à¥à¤¯à¤¾à¤ª जोन, सà¥à¤¨à¥à¤¯à¤¾à¤ª, किनारा +Keywords[nl]=verplaatsing,intelligent,plaatsing,trapsgewijs,maximaliseren,randen magnetisch,magnetisch veld,rand,magnetische randen +Keywords[nn]=flytting,smart,overlapping,maksimer,kantlÃ¥s,vindauge +Keywords[nso]=sutha,botsana,phororo,oketsa,oketsa,lefelo la thwantsho ya menwana, thwantsho ya menwana,mollwane +Keywords[pa]=moving,smart,cascade,maximize,maximise,snap zone,snap,ਹਾਸ਼ੀਆ +Keywords[pl]=przesuwanie,sprytne,kaskadowo,maksymalizacja,obszar chwytania,chwytanie, granica +Keywords[pt]=mover,inteligente,cascata,maximizar,zona de atracção,atracção,contorno +Keywords[pt_BR]=movendo,pequeno,cascata,maximizar,zona rápida,zona de encaixe,borda +Keywords[ro]=mutare,isteÈ›,cascadă,maximizare,margine +Keywords[rw]=kwimura,cyiza,urukurikirane,kugira kinini ,gukata agace,gukata, impera +Keywords[sk]=presun,inteligentný,kaskádovaÅ¥,maximalizácia,minimalizácia,priÅ¥ahovanie,zóna priÅ¥ahovania,okraje +Keywords[sl]=premikanje,pametno,kaskadno,poveÄaj,raztegni,meja,mreža,pripni +Keywords[sr]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,померање,паметно,каÑкадно,макÑимизуј,snap zone,snap,ивица +Keywords[sr@Latn]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,pomeranje,pametno,kaskadno,maksimizuj,snap zone,snap,ivica +Keywords[sv]=förflyttning,smart,kaskad,maximera,bindningszon,bind,kant +Keywords[ta]=நகரà¯à®®à¯,பà¯à®¤à¯à®¤à®¿à®šà®¾à®²à®¿,மேலà¯à®•à¯à®•à¯à®®à¯‡à®²à¯, பெரிதாகà¯à®•à¯,பெரிதாகà¯à®•à¯,நிழற௠படமெடà¯à®ªà¯à®ªà¯ பகà¯à®¤à®¿,நிழறà¯à®ªà®Ÿà®®à¯,விளிமà¯à®ªà¯ +Keywords[th]=à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢,ฉลาด,จัดเป็นขั้นๆ,ขยายเต็มจà¸,ขยายเต็มหน้าจà¸,snap zone,snap,border +Keywords[tr]=taşıma,taşınma,akıllı,kenar +Keywords[uk]=переÑуваннÑ,кмітливий,каÑкад,макÑимізувати,зона захопленнÑ,захопленнÑ,рамка +Keywords[uz]=snap zone,snap,border,koÊ»chirish,ixcham,oynalarni tartiblash,yoyish +Keywords[uz@cyrillic]=snap zone,snap,border,кўчириш,ихчам,ойналарни тартиблаш,ёйиш +Keywords[ven]=tshimbila,zwavhudi,cascade,engedza,engedza,fhethu ha snap,snap,mukano+- +Keywords[vi]=di chuyển,thông minh,tầng,phóng to,phóng to,vùng thả,thả,bá» +Keywords[wa]=bodjî,malén,a pertinne,, pus grand,nap zone,snap,bÃ¥oird +Keywords[xh]=iyashukuma,iyabukeka,cascade,yenza nkulu,yenza nkulu,yandisa,snap zone,snap,umda +Keywords[zh_CN]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,移动,智能,层å ,最大化,最å°åŒ–,å¸é™„区域,边框 +Keywords[zh_TW]=moving,smart,cascade,maximize,maximise,snap zone,snap,border,移動,è°æ˜Ž,最大化,邊框 +Keywords[zu]=iyanyakaza,okukhaliphile,landelanisa,khulisa,khulisa,indawo yokunamathisela,namathisela,umngcele diff --git a/kwin/kcmkwin/kwinoptions/kwinoptions.desktop b/kwin/kcmkwin/kwinoptions/kwinoptions.desktop new file mode 100644 index 000000000..23a222197 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwinoptions.desktop @@ -0,0 +1,225 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinoptions +DocPath=kcontrol/windowmanagement/index.html + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwinoptions + +Name=Window Behavior +Name[af]=Venster Gedrag +Name[ar]=سلوك النواÙØ° +Name[az]=PÉ™ncÉ™rÉ™ Davranışı +Name[be]=Паводзіны вокнаў +Name[bg]=Прозорци +Name[bn]=উইণà§à¦¡à§‹ আচরণ +Name[br]=Emzalc'h ar prenester +Name[bs]=PonaÅ¡anje prozora +Name[ca]=Comportament de les finestres +Name[cs]=Chovánà oken +Name[csb]=Ùchòwanié òkna +Name[cy]=Ymddygiad Ffenestri +Name[da]=Vinduesopførsel +Name[de]=Fenstereigenschaften +Name[el]=ΣυμπεÏιφοÏά παÏαθÏÏων +Name[en_GB]=Window Behaviour +Name[eo]=Fenestrokonduto +Name[es]=Comportamiento de la ventana +Name[et]=Akende käitumine +Name[eu]=Leihoen portaera +Name[fa]=رÙتار پنجره +Name[fi]=Ikkunoiden käyttäytyminen +Name[fr]=Comportement des fenêtres +Name[fy]=Finstergedrach +Name[ga]=Oibriú na bhFuinneoga +Name[gl]=Comportamento das Fiestras +Name[he]=פעולת ×—×œ×•× ×•×ª +Name[hi]=विंडो बरà¥à¤¤à¤¾à¤µ +Name[hr]=PonaÅ¡anje prozora +Name[hu]=Ablakműveletek +Name[is]=Hegðun glugga +Name[it]=Comportamento delle finestre +Name[ja]=ウィンドウã®æŒ™å‹• +Name[ka]=ფáƒáƒœáƒ¯áƒ ის ქცევრ+Name[kk]=Терезе қаÑиеттері +Name[km]=ឥរិយាបážâ€‹áž”ង្អួច +Name[ko]=ì°½ ë™ìž‘ +Name[lo]=ພິດຕິàºàº³àº‚àºàº‡àº«àº™à»‰àº²àº•à»ˆàº²àº‡ +Name[lt]=Langų elgsena +Name[lv]=Loga izturÄ“Å¡anÄs +Name[mk]=ОднеÑување на прозорци +Name[mn]=Цонхны байдал +Name[mt]=ImÄ¡ieba tal-Window +Name[nb]=Vindusoppførsel +Name[nds]=Finsterbedregen +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² वà¥à¤¯à¤µà¤¹à¤¾à¤° +Name[nl]=Venstergedrag +Name[nn]=VindaugsÃ¥tferd +Name[nso]=Mekgwa ya Window +Name[oc]=Comportament de las finestras +Name[pa]=à¨à¨°à©‹à¨–ਾ ਵਿਵਹਾਰ +Name[pl]=Okna +Name[pt]=Comportamento das Janelas +Name[pt_BR]=Comportamento da Janela +Name[ro]=Comportament fereastră +Name[ru]=Поведение окна +Name[rw]=Imyitwarire y'Idirishya +Name[se]=Láseláhtten +Name[sk]=Správanie okien +Name[sl]=ObnaÅ¡anje oken +Name[sr]=Понашање прозора +Name[sr@Latn]=PonaÅ¡anje prozora +Name[ss]=Kutiphatsa kweliwindi +Name[sv]=Fönsterbeteende +Name[ta]=சாளர நடதà¯à®¤à¯ˆ +Name[tg]=Рафтори тиреза +Name[th]=พฤติà¸à¸£à¸£à¸¡à¸‚à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Name[tr]=Pencere Davranışı +Name[tt]=Täräzä Çağılışı +Name[uk]=Поведінка вікон +Name[uz]=Oynaning xususiyatlari +Name[uz@cyrillic]=Ойнанинг хуÑуÑиÑтлари +Name[ven]=Maitele a windo +Name[vi]=Ứng xá» của Cá»a sổ +Name[wa]=Dujhance des purneas +Name[xh]=Ukuziphatha kwe Window +Name[zh_CN]=窗å£è¡Œä¸º +Name[zh_TW]=視窗行為 +Name[zu]=Ukuziphatha kwe-Window + +Comment=Configure the window behavior +Comment[af]=Stel die venster gedrag op +Comment[ar]=إعداد سلوك الناÙذة +Comment[be]=ÐаÑтаўленні паводзінаў вокнаў +Comment[bg]=ÐаÑтройване поведението на прозорците +Comment[bn]=উইণà§à¦¡à§‹à¦Ÿà¦¿à¦° আচরণ কনফিগার করà§à¦¨ +Comment[br]=Kefluniañ emzalc'h ar prenester +Comment[bs]=Podesite ponaÅ¡anje prozora +Comment[ca]=Configura el comportament de finestra +Comment[cs]=Nastavenà chovánà oken +Comment[csb]=Kònfigùracëjô ùchòwaniô òkna +Comment[cy]=Ffurfweddu ymddygiad y ffenestr +Comment[da]=Indstil vinduets opførsel +Comment[de]=Fensterverhalten festlegen +Comment[el]=ΡÏθμιση της συμπεÏιφοÏάς των παÏαθÏÏων +Comment[en_GB]=Configure the window behaviour +Comment[eo]=Agordas la konduton de la fenestro +Comment[es]=Configuración del comportamiento de la ventana +Comment[et]=Akende käitumise seadistamine +Comment[eu]=Konfiguratu arakatzailearen portaera +Comment[fa]=پیکربندی رÙتار پنجره +Comment[fi]=Ikkunoiden asetukset +Comment[fr]=Configuration du comportement des fenêtres +Comment[fy]=Stel it finstergedrach yn +Comment[ga]=Cumraigh oibriú na bhfuinneog +Comment[gl]=Configurar o comportamento das fiestras +Comment[he]=×©×™× ×•×™ הגדרות פעולת הדפדפן +Comment[hi]=विंडो वà¥à¤¯à¤µà¤¹à¤¾à¤° कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje ponaÅ¡anja preglednika +Comment[hu]=Az ablakok működési jellemzÅ‘inek beállÃtása +Comment[is]=Stilla hegðan glugga +Comment[it]=Configura il comportamento della finestra +Comment[ja]=ウィンドウã®æŒ™å‹•ã‚’è¨å®š +Comment[ka]=ბრáƒáƒ£áƒ–ერის ქცევის კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезе қаÑиеттерін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​ឥរិយាបážâ€‹áž”ង្អួច +Comment[ko]=ì°½ ë™ìž‘ ì„¤ì • +Comment[lt]=KonfigÅ«ruoti lango elgsenÄ… +Comment[lv]=KonfigurÄ“ loga izturÄ“Å¡anos +Comment[mk]=Конфигурирајте го однеÑувањето на прозорците +Comment[mt]=Ikkonfigura l-imÄ¡ieba tal-windows +Comment[nb]=Tilpass vinduets oppførsel +Comment[nds]=Dat Finsterbedregen instellen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² वà¥à¤¯à¤µà¤¹à¤¾à¤° कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Stel het venstergedrag in +Comment[nn]=Oppsett av nettlesarÃ¥tferda +Comment[pa]=à¨à¨°à©‹à¨–ਾ ਵਿਵਹਾਰ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja zachowania okna +Comment[pt]=Configurar o comportamento das janelas +Comment[pt_BR]=Configura o comportamento das janelas +Comment[ro]=Configurează comportamentul ferestrei +Comment[ru]=ÐаÑтройка Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð° +Comment[rw]=Kuboneza imyitwarire y'idirishya +Comment[se]=Heivet láseláhttema +Comment[sk]=Nastavenie správania okna +Comment[sl]=Nastavite obnaÅ¡anje brskalnika +Comment[sr]=Подешавање понашања прозора +Comment[sr@Latn]=PodeÅ¡avanje ponaÅ¡anja prozora +Comment[sv]=Anpassa fönsterbeteende +Comment[ta]=º¡ÇÃò¾¢ý Àñ¨À ¯ûǨà +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸žà¸¤à¸•à¸´à¸à¸£à¸¡à¸‚à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=Pencere davranışlarını yapılandır +Comment[tt]=Täräzä çağılışın caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²ÐµÐ´Ñ–Ð½ÐºÐ¸ вікна +Comment[uz]=Oynaning xususiyatlarini moslash +Comment[uz@cyrillic]=Ойнанинг хуÑуÑиÑтларини моÑлаш +Comment[vi]=Cấu hình cách cá»a sổ ứng xá» +Comment[wa]=Apontyî l' dujhance des purneas +Comment[zh_CN]=é…置窗å£è¡Œä¸º +Comment[zh_TW]=è¨å®šè¦–窗行為 + +Keywords=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick +Keywords[ar]=تركيز ركز,موضع مكان,سلوك الناÙذة,تØريك,رÙع, رÙع تلقائي,نواÙØ°,إطار,شريط العنوان,نقر ثنائي +Keywords[be]=ФокуÑ,РазмÑшчÑнне,Паводзіны акна,ÐнімацыÑ,Вокны,Ðкно,ФрÑйм,Загаловак,Двайны націÑк мышы,focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick +Keywords[bg]=фокуÑ, поведение, прозорец, активиране, заглавие, focus, placement, window behaviour, animation, raise, auto raise, windows, frame, titlebar, doubleclick +Keywords[bs]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,fokus,smjeÅ¡taj,ponaÅ¡anje prozora,animacija,podigni,automatski podigni,prozori,okvir,naslovna trak,dvoklik,dvostruki klik +Keywords[ca]=focus,emplaçament,comportament de la finestra,animació,eleva,eleva automà ticament,finestres,marc,barra del tÃtol,doble clic +Keywords[cs]=ZaměřenÃ,UmÃstÄ›nà oken,Chovánà oken,Animace,Okna,RámeÄek,Titulek,Dvojklik +Keywords[csb]=zrëszanié,aktiwòwanié,ùkłôdanié òknów,ùchòwanié òknów,animacëjô,aùtomatné aktiwòwanié,òkna,òkno,rama,tilowô lëstew,dëbeltné klëkniãce +Keywords[cy]=canolbwynt,lleoliad,ymddygiad ffenestr,bywluniad,codi,hunan-godi,ffenestri,ffrâm,bar-teitl,clic dwbl,dwbl-glic +Keywords[da]=fokus,placering,vinduesopførsel,animering,Hæv,autohæv,vinduer,ramme,titellinje,dobbeltklik +Keywords[de]=Aktivierung,Platzierung,Fensterverhalten,Animation,Nach vorn/hinten, Fenster,Rahmen,Umrandung,Titelleiste,Doppelklick +Keywords[el]=εστίαση,τοποθÎτηση,συμπεÏιφοÏά παÏαθÏÏων,εφΠκίνησης,ανÏψωση,αυτόματη ανÏψωση,παÏάθυÏα,πλαίσιο,μπάÏα τίτλου,διπλό κλικ +Keywords[en_GB]=focus,placement,window behaviour,animation,raise,auto raise,windows,frame,titlebar,doubleclick +Keywords[eo]=fokuso,lokigo,fenestrokonduto,spektaklo,malfonigo,aÅtomalfonigo,fenestro,kadro,titollistelo,duklako +Keywords[es]=foco,ubicación,posición,ventana,comportamiento,animación,frente,pasar al frente,ventanas,marco,barra de tÃtulo,doble pulsación +Keywords[et]=fookus,asetus,akende käitumine,animatsioon,tõstmine,automaatne tõstmine,aknad,raam,tiitliriba,topeltklikk,topeltklõps +Keywords[eu]=fokua,kokalekua,leihoaren portaera,animazioa,aurrera ekarri,auto aurrera ekarri,leihoak,markoa,titulu-barra,klik bikoitza +Keywords[fa]=کانون، جای‌دهی، رÙتار پنجره، پویانمایی، بالا بردن، بالا بردن خودکار، پنجره‌ها، قاب، میله عنوان، دو بار Ùشار +Keywords[fi]=fokus,sijoittaminen,ikkunan toiminta,animaatio,nosto,automaattinosto,ikkunat,kehys,otsikkorivi,tuplanapsautus,kaksoisnapsautus +Keywords[fr]=focus,gestion du focus,fenêtre,placement des fenêtres,comportement des fenêtres,animation,fenêtres,barre de titre,double clic,souris,boutons de la souris,dessus,dessous,raise,auto raise +Keywords[fy]=focusbeleid,vensterplaatsing,finsterferpleatsing,venstergedrag,finstergedrach,focus,window,finster,plaatsing,pleatsing,plaatsingbeleid,pleatsingbelied,animatie,animaasje,voorgrond,foargrûn,vensters,finsters,frame,kader,dubbelklikken,dûbelklikke,fokusbeliid +Keywords[ga]=fócas,láithriú,oibriú na bhfuinneog,beochan,ardaigh,uathardaigh,fuinneoga,fráma,ceannteideal,déchliceáil +Keywords[gl]=foco,emprazamento,comportamento da fiestra,animación,subir,auto subir,fiestras,marco,barra de tÃtulo,dobre presión +Keywords[he]=התמקדות,מיקו×,פעולת ×—×œ×•× ×•×ª,×× ×™×ž×¦×™×”,×”× ×¤×©×”,הב××” לחזית,הב××” ×וטומטית לחזית,×—×œ×•× ×•×ª,גבול,מסגרת,שורת כותרת,לחיצה כפולה, focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick +Keywords[hi]=फोकस,पà¥à¤²à¥‡à¤¸à¤®à¥‡à¤‚ट,विंडो वà¥à¤¯à¤µà¤¹à¤¾à¤°,à¤à¤¨à¤¿à¤®à¥‡à¤¶à¤¨,ऊपर उठाà¤à¤,सà¥à¤µà¤¤à¤ƒ ऊपर उठाà¤à¤,विंडोज़,फà¥à¤°à¥‡à¤®,शीरà¥à¤·à¤•-पटà¥à¤Ÿà¥€,डबल-कà¥à¤²à¤¿à¤• +Keywords[hr]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,fokus,smjeÅ¡taj,ponaÅ¡anje prozora,animacija,pojava,podizanje,automatska pojava,prozori,okvir,naslovna traka,dvostruko klikanje +Keywords[hu]=fókusz,elhelyezés,ablakelhelyezés,animáció,felemelés,automatikus felemelés,ablakok,keret,cÃmsor,dupla kattintás +Keywords[is]=virkni glugga,staðsetning,hegðun,högun glugga,hækka,hækka sjálfkrafa,gluggar,titilslá,titilrönd,tvÃsmella +Keywords[it]=focus,piazzamento,comportamento finestre,animazione,alza,alza automaticamente,finestre,cornice,barra del titolo,doppio clic +Keywords[ja]=フォーカス,é…ç½®,ウィンドウã®æŒ™å‹•,アニメーション,å‰é¢ã¸,自動的ã«å‰é¢ã¸,ウィンドウ,フレーム,タイトルãƒãƒ¼,ダブルクリック +Keywords[km]=ផ្ដោážâ€‹áž¢áž¶ážšáž˜áŸ’មណáŸ,ការ​ដាក់,ឥរិយាបážâ€‹áž”ង្អួច,ចលនា,លើកឡើង,លើក​ឡើង​ស្វáŸáž™áž”្រវážáŸ’ážáž·,បង្អួច,ស៊ុម,របារ​ចំណងជើង,ចុច​ទ្វáŸážŠáž„ +Keywords[lt]=focus,placement,window behaviour,animation,raise,auto raise,windows,frame,titlebar,doubleclick,lango iÅ¡dÄ—stymas,elgesys,langai,rÄ—melis,lango antraÅ¡tÄ— +Keywords[lv]=fokuss,novietojums,loga izturÄ“Å¡anÄs,animÄcija,celt,auto celt,logi,kadrs,virsraksta josla,dubultklikÅ¡Ä·is +Keywords[mk]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,фокуÑ,позиционирање,однеÑување на прозорците, анимација,подигање,авто подигање,прозорци,рамка,наÑловна лента,двоен клик +Keywords[mt]=focus,placement,window behaviour,animation,raise,auto raise,windows,frame,titlebar,doubleclick +Keywords[nb]=fokus,plassering,vindusoppførsel,animasjon,hev,autohev,vinduer,ramme,tittelliste,dobbeltklikk +Keywords[nds]=Fokus,Platzeren,Finsterbedregen,Animatschoon,fokusseren,aktiveren,automaatsch fokusseren,Finster,Rahmen,Titelbalken,Dubbelklick +Keywords[ne]=फोकस, सà¥à¤¥à¤¾à¤ªà¤¨à¤¾, सञà¥à¤à¥à¤¯à¤¾à¤² वà¥à¤¯à¤µà¤¹à¤¾à¤°, à¤à¤¨à¤¿à¤®à¥‡à¤¸à¤¨, बृदà¥à¤§à¤¿, सà¥à¤µà¤¤: बृदà¥à¤§à¤¿, सञà¥à¤à¥à¤¯à¤¾à¤², फà¥à¤°à¥‡à¤®, शीरà¥à¤·à¤•à¤ªà¤Ÿà¥à¤Ÿà¥€, डबल कà¥à¤²à¤¿à¤• +Keywords[nl]=focusbeleid,vensterplaatsing,venstergedrag,focus,window,plaatsing,plaatsingbeleid,animatie,voorgrond,vensters,frame,kader,dubbelklikken +Keywords[nn]=fokus,plassering,vindaugsplassering,vindaugsÃ¥tferd,animasjon,hev,automatisk heving,vindauge,ramme,tittellinje,dobbeltklikk +Keywords[pa]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,à¨à¨°à©‹à¨–ਾ ਵਿਹਾਰ,ਸਜੀਵਤਾ,à¨à¨°à©‹à¨–ੇ +Keywords[pl]=uaktywnianie,ukÅ‚adanie okien,zachowanie okien,animacja,automatyczne uaktywnienie,okno,okna,ramka,pasek tytuÅ‚owy,podwójne klikniÄ™cie +Keywords[pt]=foco,posicionamento,comportamento das janelas,animação,elevar,auto-elevar,janelas,contorno,barra de tÃtulo,duplo-carregar +Keywords[pt_BR]=foco,posicionamento,comportamento das janelas,animação,elevar,auto-elevar,janelas, quadro,barra de tÃtulo,clique duplo +Keywords[ro]=focus,plasare,comportament fereastră,animaÈ›ie,ridicare,automat,ferestre,cadru,bară de titlu,dublu clic +Keywords[rw]=igaragazarikeye, ugushyira mu mwanya,imyitwarire y'idirishya,ibyinisha,byizamura,amadirishya,ikadiri,umwanyabikoresho,gukandakabiri +Keywords[se]=fohkus,sajádat,láseláhtten,animaÅ¡uvdna,lokte,autolokte,láset,rámma,namahusholga,duppalcoahkkal +Keywords[sk]=fokus,umiestnenie,správanie okien,animácia,zdvihnutie,automatické zdvihnutie,titulok,dvojité kliknutie,rámec,okná +Keywords[sl]=fokus,postavitev,obnaÅ¡anje okna,animacija,dvigni,okna,okvir,naslovna vrstica,dvojni klik +Keywords[sr]=focus,placement,window behaviour,animation,raise,ауто raise,windows,frame,titlebar,doubleclick,фокуÑ,понашање прозора,анимација,прозори,оквир,наÑловна линија,двоклик +Keywords[sr@Latn]=focus,placement,window behaviour,animation,raise,auto raise,windows,frame,titlebar,doubleclick,fokus,ponaÅ¡anje prozora,animacija,prozori,okvir,naslovna linija,dvoklik +Keywords[sv]=fokus,placering,fönsterbeteende,animering,höj, autohöj,fönster,kant,namnlist,dubbelklick +Keywords[ta]=பாரà¯à®µà¯ˆ,இடமà¯,சாளரபணà¯à®ªà¯,உயிரà¯à®šà®¿à®¤à¯à®¤à®¿à®°à®®à¯,மேலெழà¯à®ªà¯à®ªà¯, தானாக மேலெழà¯à®ªà¯à®ªà¯,சாளரஙà¯à®•à®³à¯,சடà¯à®Ÿà®®à¯,தலைபà¯à®ªà¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿,இரணà¯à®Ÿà¯ சொடà¯à®•à¯à®•à¯ +Keywords[th]=โฟà¸à¸±à¸ª,à¸à¸²à¸£à¸§à¸²à¸‡à¸•à¸³à¹à¸«à¸™à¹ˆà¸‡,พฤติà¸à¸£à¸£à¸¡à¸‚à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡,à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™,ยà¸à¸‚ึ้นไว้ด้านบน,ยà¸à¸‚ึ้นไว้ด้านบนโดยà¸à¸±à¸•à¹‚นมัติ,หน้าต่าง,à¸à¸£à¸à¸š,à¹à¸–บหัวเรื่à¸à¸‡,ดับเบิลคลิภ+Keywords[tr]=odak,odak davranış biçimi,pencere yerleÅŸimi,pencere davranışı,YerleÅŸim davranış biçimi,animasyon,kaldır,otomatik kaldır,çerçeve,çift tıkla +Keywords[uk]=фокуÑ,розташовуваннÑ,поведінка вікна,анімаціÑ,піднÑти,піднімати автоматично,вікна,рамка,заголовок,подвійне ÐºÐ»Ð°Ñ†Ð°Ð½Ð½Ñ +Keywords[uz]=fokuslash,joylashish,oynaning xususiyatlari,animatsiya,oldinga,avto-oldinga,oynalar,freym,sarlavha,ikki marta bosish +Keywords[uz@cyrillic]=фокуÑлаш,жойлашиш,ойнанинг хуÑуÑиÑтлари,анимациÑ,олдинга,авто-олдинга,ойналар,фрейм,Ñарлавҳа,икки марта боÑиш +Keywords[vi]=chá»n,dịch chuyển,ứng xá» cá»a sổ,hoạt hình,nâng lên,tá»± nâng lên,cá»a sổ,khung,thanh tiêu Ä‘á»,nhấn đúp +Keywords[wa]=focus,plaece,dujhance des purneas,animÃ¥cion,rimonter,rimonte tot seu,purneas,cÃ¥de,bÃ¥r di tite,clitchî deus côps,clitchî deus feyes +Keywords[zh_CN]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,焦点,放置,窗å£è¡Œä¸º,动画,å‡èµ·,自动å‡èµ·,窗å£,框架,æ ‡é¢˜æ ,åŒå‡» +Keywords[zh_TW]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,焦點,放置,視窗行為,å‹•ç•«,抬å‡,自動抬å‡,視窗,框架,標題列,雙擊 +Categories=Qt;KDE;X-KDE-settings-desktop; diff --git a/kwin/kcmkwin/kwinoptions/kwintranslucency.desktop b/kwin/kcmkwin/kwinoptions/kwintranslucency.desktop new file mode 100644 index 000000000..e45e5928a --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/kwintranslucency.desktop @@ -0,0 +1,195 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwintranslucency +DocPath=kcontrol/windowmanagement/index.html#action-translucency + +X-KDE-ModuleType=Library +X-KDE-Library=kwinoptions +X-KDE-FactoryName=kwintranslucency + +Name=Translucency +Name[af]=Deursigtigheid +Name[ar]=الشÙاÙية +Name[be]=ПразрыÑтаÑць +Name[bg]=ПолупрозрачноÑÑ‚ +Name[bn]=সমসà§à¦¬à¦šà§à¦›à¦¤à¦¾ +Name[br]=Treuzwel +Name[bs]=Prozirnost +Name[ca]=Translucidesa +Name[cs]=Průhlednost +Name[csb]=Przezérnota +Name[da]=Gennemsigtighed +Name[de]=Transparenz +Name[el]=Hμιδιαφάνεια +Name[eo]=Travidebleco +Name[es]=Translúcido +Name[et]=Läbipaistvus +Name[eu]=Gardentasuna +Name[fa]=نیمۀ Ø´ÙاÙÛŒ +Name[fi]=Läpikuultavuus +Name[fr]=Transparence +Name[fy]=Trochsichtichheid +Name[ga]=Tréshoilseacht +Name[gl]=Translucidez +Name[he]=שקיפות +Name[hr]=Prozirnost +Name[hu]=ÃttetszÅ‘ség +Name[id]=Tembus Pandang +Name[is]=Gegnsæi +Name[it]=Trasparenza +Name[ja]=é€éŽæ€§ +Name[ka]=ნáƒáƒ®áƒ”ვრáƒáƒ“გáƒáƒ›áƒáƒ˜áƒ ვáƒáƒšáƒ”áƒáƒ‘რ+Name[kk]=Мөлдірлігі +Name[km]=ភាព​ážáŸ’លា +Name[ko]=반투명 +Name[lt]=Permatomumas +Name[mk]=ПровидноÑÑ‚ +Name[nb]=Gjennomsiktighet +Name[nds]=Dörschienen +Name[ne]=सà¥à¤µà¤šà¥à¤›à¤¤à¤¾ +Name[nl]=Transparantie +Name[nn]=Gjennomsiktig +Name[pa]=ਪਾਰਦਰਸ਼ੀ +Name[pl]=Przezroczystość +Name[pt]=Transparência +Name[pt_BR]=Translúcido +Name[ro]=Translucență +Name[ru]=ПолупрозрачноÑÑ‚ÑŒ +Name[rw]=Ukujyakwijima +Name[se]=ÄŒaÄ‘aÄuovgi +Name[sk]=PriesvitnosÅ¥ +Name[sl]=Prosojnost +Name[sr]=ПрозирноÑÑ‚ +Name[sr@Latn]=Prozirnost +Name[sv]=Genomskinlighet +Name[ta]=ஒளிகசிவ௠+Name[th]=ความโปร่งà¹à¸ªà¸‡ +Name[tr]=Åžeffaflık +Name[tt]=Ãœtäkürenü +Name[uk]=ПрозоріÑÑ‚ÑŒ +Name[uz]=Shaffoflik +Name[uz@cyrillic]=Шаффофлик +Name[vi]=Äục & Trong +Name[wa]=Translucidaedje +Name[zh_CN]=é€æ˜Žåº¦ +Name[zh_TW]=é€æ˜Žåº¦ + +Comment=Configure window translucency and shadow management +Comment[af]=Stel die venster deursigtigheid en skaduwee op +Comment[ar]=إعداد تدبيرات Ø´ÙاÙية Ùˆ تظليل الناÙذة +Comment[be]=ÐаÑтаўленні празрыÑтаÑці вокнаў Ñ– кіраванне ценем +Comment[bg]=ÐаÑтройване на полупрозрачноÑтта и Ñенките на прозорците +Comment[bn]=উইণà§à¦¡à§‹à¦° সমসà§à¦¬à¦šà§à¦›à¦¤à¦¾ à¦à¦¬à¦‚ ছায়া কনফিগার করà§à¦¨ +Comment[bs]=Podesite prozirnost prozora i podesite sjenke +Comment[ca]=Configura la translucidesa de finestres i gestiona l'ombra +Comment[cs]=Nastavenà průhlednosti a stÃnovánà oken +Comment[csb]=Kònfigùracëjô przezérnotë ë ceniów òknów +Comment[da]=Indstil vinduets gennemsigtighed og skyggehÃ¥ndtering +Comment[de]=Einrichtung der Fenstertransparenz und Schatten +Comment[el]=ΡÏθμιση ημιδιαφάνειας παÏαθÏÏου και διαχείÏισης σκίασης +Comment[eo]=Agordu administradon de fenestro-travidebleco kaj ombreco. +Comment[es]=Configurar la opacidad de la ventana y gestión de sombra +Comment[et]=Akende läbipaistvuse ja varjude seadistamine +Comment[eu]=Konfiguratu leiho gardentasunak eta itzalen kudeaketa +Comment[fa]=پیکربندی مدیریت نیمۀ Ø´ÙاÙÛŒ Ùˆ سایۀ پنجره +Comment[fi]=Muokkaa ikkunoinnin läpikuultavuus ja varjostus +Comment[fr]=Configuration de la transparence et de l'ombre des fenêtres +Comment[fy]=Finstertrochsichtichheid- en -skaadbehear ynstelle +Comment[ga]=Cumraigh tréshoilseacht na fuinneoige agus bainisteoireacht scátha +Comment[gl]=Configurar a xestión de sombras e translucidez das fiestras +Comment[he]=תצורה של שקיפות ×”×—×œ×•× ×•×ª ×•× ×™×”×•×œ ההצללות +Comment[hr]=Konfiguriranje prozirnosti prozora i upravljanje sjenÄanjem +Comment[hu]=Az ablakkezelés áttetszÅ‘ségi és árnyékolási beállÃtásai +Comment[is]=Stilla gegnsæi glugga og sýsla með skugga +Comment[it]=Configura la trasparenza e le ombre delle finestre +Comment[ja]=ウィンドウã®é€éŽæ€§ã¨å½±ã®è¨å®š +Comment[ka]=ფáƒáƒœáƒ¯áƒ ების გáƒáƒ›áƒáƒ˜áƒ ვáƒáƒšáƒáƒ‘ის დრჩრდილების კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезенің мөлдірлігін және көлеңкеÑін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​ការ​គ្រប់គ្រង​ភាព​ážáŸ’លា និង​ស្រមោល​របស់​បង្អួច +Comment[lt]=KonfigÅ«ruoti langų permatomumÄ… ir Å¡eÅ¡Ä—lius +Comment[mk]=Конфигурирајте ги провидноÑта на прозорците и менаџментот на Ñенки +Comment[nb]=Tilpass behandling av vindusgjennomsiktighet og -skygge +Comment[nds]=Finsterdörschienen un -Schadden instellen +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² सà¥à¤µà¤šà¥à¤›à¤¤à¤¾ र छायाठवà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Venstertransparantie- en -schaduwbeheer instellen +Comment[nn]=Oppsett av gjennomsiktige vindauge og vindaugsskugge +Comment[pa]=à¨à¨°à©‹à¨–ਾ ਪਾਰਦਰਸਤਾ ਅਤੇ ਛਾਂ ਪਰਬੰਧਨ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja przezroczystoÅ›ci i cieni okien +Comment[pt]=Configuração da transparência e sombras de janelas +Comment[pt_BR]=Configura o gerenciamento de sombra e transparência da janela +Comment[ro]=Configurează translucenÈ›a È™i umbra ferestrelor +Comment[ru]=ÐаÑтройка полупрозрачноÑти и теней окна +Comment[rw]=Kuboneza ubuyobozi bw'ukujyakubonerana n'igicucu by'idirishya +Comment[se]=Heivet lášeÄaÄ‘aÄuovgivuoÄ‘a ja suoivvanasgieÄ‘aheami +Comment[sk]=Nastavenie priesvitnosti okien a správcu tieňovania +Comment[sl]=Nastavitve prosojnosti oken in upravljanje s sencami +Comment[sr]=Подешавање прозирноÑти прозора и управљање Ñенкама +Comment[sr@Latn]=PodeÅ¡avanje prozirnosti prozora i upravljanje senkama +Comment[sv]=Anpassa fönstergenomskinlighet och skugghantering +Comment[ta]=சாளர ஒளிகசிவ௠மறà¯à®±à¯à®®à¯ நிழல௠மேலாணà¯à®®à¯ˆà®¯à¯ˆ வடிவமை +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸„ุณสมบัติความโปร่งà¹à¸ªà¸‡à¹à¸¥à¸°à¹€à¸‡à¸²à¸‚à¸à¸‡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡ +Comment[tr]=Pencere ÅŸeffaflığını ve gölge yönetimini yapılandır +Comment[tt]=Täräzä ütäkürenüe belän külägäsen caylaw +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€Ð¾ÑÑ‚Ñ– вікон та ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ñ‚Ñ–Ð½ÐµÐ½Ð½Ñм +Comment[uz]=Oynaning shaffofligini va soyasini moslash +Comment[uz@cyrillic]=Ойнанинг шаффофлигини ва ÑоÑÑини моÑлаш +Comment[vi]=Cấu hình Ä‘á»™ đục và trong cùng vá»›i bóng râm của cá»a sổ +Comment[wa]=Apontiaedje des fonccionÃ¥lités translucides et d' ombe do manaedjaedje des purneas +Comment[zh_CN]=é…置窗å£é€æ˜Žåº¦å’Œé˜´å½±ç®¡ç† +Comment[zh_TW]=è¨å®šè¦–窗é€æ˜Žåº¦èˆ‡é™°å½±ç®¡ç†åŠŸèƒ½ + +Keywords=translucency,transparence,shadows +Keywords[ar]=الشÙاÙية,Ø´ÙاÙ,أظلال +Keywords[be]=ПразрыÑтаÑць,Цені,translucency,transparence,shadows +Keywords[bg]=полупрозрачноÑÑ‚, прозрачноÑÑ‚, ÑÑнка, Ñенки, translucency, transparence, shadows +Keywords[bs]=translucency,transparence,shadows,providnost,sjene,sjenke,sjenka +Keywords[ca]=translucidesa,transparència,ombres +Keywords[cs]=průhlednost,průsvitnost,stÃn +Keywords[csb]=przezérnota,półprzezérnota,przeswiécëna,ceniô,cenie +Keywords[da]=gennemsigtighed,transparens,skygge +Keywords[el]=ημιδιαφάνεια,διαφάνεια,σκιÎÏ‚ +Keywords[eo]=Travidebleco, ombroj +Keywords[es]=translucido,transparente,sombras,opacidad +Keywords[et]=läbipaistvus,läbipaistev,vari +Keywords[eu]=gardena,gardentasunak,itzalak +Keywords[fa]=نیمۀ Ø´ÙاÙی، Ø´ÙاÙیت، سایه‌ها +Keywords[fi]=läpikuultavuus, läpinäkyvyys, varjot +Keywords[fr]=translucidité,translucide,transparence,transparent,ombre +Keywords[fy]=translucency,trochsichtich,ransparantie,schaduwen,skaden +Keywords[ga]=tréshoilseacht,gléine,scáthanna +Keywords[gl]=translucéncia,transparéncia,sombras +Keywords[he]=translucency,transparence,shadows,הצללה,צל,צללי×,שקוף,שקיפות +Keywords[hr]=translucency,transparence,shadows,prozirnost,sjene,providnost +Keywords[hu]=áttetszÅ‘ség,áttetszÅ‘,árnyékok +Keywords[id]=tembus pandang,transparan, bayangan +Keywords[it]=trasparenza,ombre +Keywords[ja]=é€éŽæ€§,é€æ˜Ž,å½± +Keywords[km]=ភាព​ážáŸ’លា,ážáŸ’លា,ស្រមោល +Keywords[lt]=translucency,transparence,shadows,permatomumas,Å¡eÅ¡Ä—liai +Keywords[mk]=translucency,transparence,shadows,провидноÑÑ‚,проѕирноÑÑ‚,Ñенки +Keywords[nb]=gjennomsiktighet,skygge +Keywords[nds]=Transparenz,Schadden,Dörschienen +Keywords[ne]=सà¥à¤µà¤šà¥à¤›à¤¤à¤¾,पारदरà¥à¤¶à¥€à¤¤à¤¾,छायाठ+Keywords[nl]=translucency,transparantie,schaduwen +Keywords[nn]=gjennomsiktig,skugge +Keywords[pa]=translucency,transparence,ਛਾਂ +Keywords[pl]=przezroczystość,półprzezroczystość,przeÅ›witywanie,cieÅ„,cienie +Keywords[pt]=transparência,sombras +Keywords[pt_BR]=translúcido,transparência,sombras +Keywords[ro]=translucență,transparență,umbre +Keywords[rw]=ukujyakwijima,ukubonerana,ibicucu +Keywords[sl]=prosojnost,prozornost,sence +Keywords[sr]=translucency,transparence,shadows,провидноÑÑ‚,Ñенке +Keywords[sr@Latn]=translucency,transparence,shadows,providnost,senke +Keywords[sv]=genomskinlighet,skuggor +Keywords[ta]=ஒளிகசிவà¯,ஊடகதà¯à®¤à®©à¯à®®à¯ˆ,நிழலà¯à®•à®³à¯ +Keywords[th]=ความโปร่งà¹à¸ªà¸‡,ความโปร่งใส,เงา +Keywords[tr]=ÅŸeffaflık,ÅŸeffaf,gölge,opak,opasite +Keywords[uk]=translucency,transparence,shadows,прозоріÑÑ‚ÑŒ,напівпрозоріÑÑ‚ÑŒ,тіні +Keywords[uz]=shaffof,soya +Keywords[uz@cyrillic]=шаффоф,ÑÐ¾Ñ +Keywords[vi]=mỠđục,trong suốt,bóng râm +Keywords[wa]=translucency,transparince,translucide,translucidaedje +Keywords[zh_CN]=é€æ˜Žåº¦,阴影,translucency,transparence,shadows +Keywords[zh_TW]=translucency,transparence,shadows,é€æ˜Ž,é™°å½± diff --git a/kwin/kcmkwin/kwinoptions/main.cpp b/kwin/kcmkwin/kwinoptions/main.cpp new file mode 100644 index 000000000..fbc70e4c8 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/main.cpp @@ -0,0 +1,262 @@ +/* + * + * Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <qlayout.h> + +#include <dcopclient.h> + +#include <kapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kgenericfactory.h> +#include <kaboutdata.h> +#include <kdialog.h> + +#include "mouse.h" +#include "windows.h" + +#include "main.h" + +extern "C" +{ + KDE_EXPORT KCModule *create_kwinfocus(QWidget *parent, const char *name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + KConfig *c = new KConfig("kwinrc", false, true); + return new KFocusConfig(true, c, parent, name); + } + + KDE_EXPORT KCModule *create_kwinactions(QWidget *parent, const char *name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + return new KActionsOptions( parent, name); + } + + KDE_EXPORT KCModule *create_kwinmoving(QWidget *parent, const char *name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + KConfig *c = new KConfig("kwinrc", false, true); + return new KMovingConfig(true, c, parent, name); + } + + KDE_EXPORT KCModule *create_kwinadvanced(QWidget *parent, const char *name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + KConfig *c = new KConfig("kwinrc", false, true); + return new KAdvancedConfig(true, c, parent, name); + } + + KDE_EXPORT KCModule *create_kwintranslucency(QWidget *parent, const char *name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + KConfig *c = new KConfig("kwinrc", false, true); + return new KTranslucencyConfig(true, c, parent, name); + } + + KDE_EXPORT KCModule *create_kwinoptions ( QWidget *parent, const char* name) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue("kcmkwm"); + return new KWinOptions( parent, name); + } +} + +KWinOptions::KWinOptions(QWidget *parent, const char *name) + : KCModule(parent, name) +{ + mConfig = new KConfig("kwinrc", false, true); + + QVBoxLayout *layout = new QVBoxLayout(this); + tab = new QTabWidget(this); + layout->addWidget(tab); + + mFocus = new KFocusConfig(false, mConfig, this, "KWin Focus Config"); + mFocus->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mFocus, i18n("&Focus")); + connect(mFocus, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mTitleBarActions = new KTitleBarActionsConfig(false, mConfig, this, "KWin TitleBar Actions"); + mTitleBarActions->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mTitleBarActions, i18n("&Titlebar Actions")); + connect(mTitleBarActions, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mWindowActions = new KWindowActionsConfig(false, mConfig, this, "KWin Window Actions"); + mWindowActions->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mWindowActions, i18n("Window Actio&ns")); + connect(mWindowActions, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mMoving = new KMovingConfig(false, mConfig, this, "KWin Moving"); + mMoving->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mMoving, i18n("&Moving")); + connect(mMoving, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mAdvanced = new KAdvancedConfig(false, mConfig, this, "KWin Advanced"); + mAdvanced->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mAdvanced, i18n("Ad&vanced")); + connect(mAdvanced, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mTranslucency = new KTranslucencyConfig(false, mConfig, this, "KWin Translucency"); + mTranslucency->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mTranslucency, i18n("&Translucency")); + connect(mTranslucency, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + KAboutData *about = + new KAboutData(I18N_NOOP("kcmkwinoptions"), I18N_NOOP("Window Behavior Configuration Module"), + 0, 0, KAboutData::License_GPL, + I18N_NOOP("(c) 1997 - 2002 KWin and KControl Authors")); + + about->addAuthor("Matthias Ettrich",0,"ettrich@kde.org"); + about->addAuthor("Waldo Bastian",0,"bastian@kde.org"); + about->addAuthor("Cristian Tibirna",0,"tibirna@kde.org"); + about->addAuthor("Matthias Kalle Dalheimer",0,"kalle@kde.org"); + about->addAuthor("Daniel Molkentin",0,"molkentin@kde.org"); + about->addAuthor("Wynn Wilkes",0,"wynnw@caldera.com"); + about->addAuthor("Pat Dowler",0,"dowler@pt1B1106.FSH.UVic.CA"); + about->addAuthor("Bernd Wuebben",0,"wuebben@kde.org"); + about->addAuthor("Matthias Hoelzer-Kluepfel",0,"hoelzer@kde.org"); + setAboutData(about); +} + +KWinOptions::~KWinOptions() +{ + delete mConfig; +} + +void KWinOptions::load() +{ + mConfig->reparseConfiguration(); + mFocus->load(); + mTitleBarActions->load(); + mWindowActions->load(); + mMoving->load(); + mAdvanced->load(); + mTranslucency->load(); + emit KCModule::changed( false ); +} + + +void KWinOptions::save() +{ + mFocus->save(); + mTitleBarActions->save(); + mWindowActions->save(); + mMoving->save(); + mAdvanced->save(); + mTranslucency->save(); + + emit KCModule::changed( false ); + // Send signal to kwin + mConfig->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); +} + + +void KWinOptions::defaults() +{ + mFocus->defaults(); + mTitleBarActions->defaults(); + mWindowActions->defaults(); + mMoving->defaults(); + mAdvanced->defaults(); + mTranslucency->defaults(); +} + +QString KWinOptions::quickHelp() const +{ + return i18n("<h1>Window Behavior</h1> Here you can customize the way windows behave when being" + " moved, resized or clicked on. You can also specify a focus policy as well as a placement" + " policy for new windows." + " <p>Please note that this configuration will not take effect if you do not use" + " KWin as your window manager. If you do use a different window manager, please refer to its documentation" + " for how to customize window behavior."); +} + +void KWinOptions::moduleChanged(bool state) +{ + emit KCModule::changed(state); +} + + +KActionsOptions::KActionsOptions(QWidget *parent, const char *name) + : KCModule(parent, name) +{ + mConfig = new KConfig("kwinrc", false, true); + + QVBoxLayout *layout = new QVBoxLayout(this); + tab = new QTabWidget(this); + layout->addWidget(tab); + + mTitleBarActions = new KTitleBarActionsConfig(false, mConfig, this, "KWin TitleBar Actions"); + mTitleBarActions->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mTitleBarActions, i18n("&Titlebar Actions")); + connect(mTitleBarActions, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); + + mWindowActions = new KWindowActionsConfig(false, mConfig, this, "KWin Window Actions"); + mWindowActions->layout()->setMargin( KDialog::marginHint() ); + tab->addTab(mWindowActions, i18n("Window Actio&ns")); + connect(mWindowActions, SIGNAL(changed(bool)), this, SLOT(moduleChanged(bool))); +} + +KActionsOptions::~KActionsOptions() +{ + delete mConfig; +} + +void KActionsOptions::load() +{ + mTitleBarActions->load(); + mWindowActions->load(); + emit KCModule::changed( false ); +} + + +void KActionsOptions::save() +{ + mTitleBarActions->save(); + mWindowActions->save(); + + emit KCModule::changed( false ); + // Send signal to kwin + mConfig->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); +} + + +void KActionsOptions::defaults() +{ + mTitleBarActions->defaults(); + mWindowActions->defaults(); +} + +void KActionsOptions::moduleChanged(bool state) +{ + emit KCModule::changed(state); +} + +#include "main.moc" diff --git a/kwin/kcmkwin/kwinoptions/main.h b/kwin/kcmkwin/kwinoptions/main.h new file mode 100644 index 000000000..e8591c13f --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/main.h @@ -0,0 +1,100 @@ +/* + * main.h + * + * Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include <qtabwidget.h> +#include <kcmodule.h> + +class KConfig; +class KFocusConfig; +class KTitleBarActionsConfig; +class KWindowActionsConfig; +class KAdvancedConfig; +class KTranslucencyConfig; + +class KWinOptions : public KCModule +{ + Q_OBJECT + +public: + + KWinOptions(QWidget *parent, const char *name); + virtual ~KWinOptions(); + + void load(); + void save(); + void defaults(); + QString quickHelp() const; + + +protected slots: + + void moduleChanged(bool state); + + +private: + + QTabWidget *tab; + + KFocusConfig *mFocus; + KTitleBarActionsConfig *mTitleBarActions; + KWindowActionsConfig *mWindowActions; + KMovingConfig *mMoving; + KAdvancedConfig *mAdvanced; + KTranslucencyConfig *mTranslucency; + + KConfig *mConfig; +}; + +class KActionsOptions : public KCModule +{ + Q_OBJECT + +public: + + KActionsOptions(QWidget *parent, const char *name); + virtual ~KActionsOptions(); + + void load(); + void save(); + void defaults(); + +protected slots: + + void moduleChanged(bool state); + + +private: + + QTabWidget *tab; + + KTitleBarActionsConfig *mTitleBarActions; + KWindowActionsConfig *mWindowActions; + + KConfig *mConfig; +}; + +#endif diff --git a/kwin/kcmkwin/kwinoptions/mouse.cpp b/kwin/kcmkwin/kwinoptions/mouse.cpp new file mode 100644 index 000000000..f220bbfa9 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/mouse.cpp @@ -0,0 +1,856 @@ +/* + * + * Copyright (c) 1998 Matthias Ettrich <ettrich@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <qlabel.h> +#include <qcombobox.h> +#include <qwhatsthis.h> +#include <qlayout.h> +#include <qvgroupbox.h> +#include <qgrid.h> +#include <qsizepolicy.h> +#include <qbitmap.h> +#include <qhgroupbox.h> +#include <qtooltip.h> + +#include <dcopclient.h> +#include <klocale.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdialog.h> +#include <kglobalsettings.h> +#include <kseparator.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <stdlib.h> + +#include "mouse.h" +#include "mouse.moc" + + +namespace { + +char const * const cnf_Max[] = { + "MaximizeButtonLeftClickCommand", + "MaximizeButtonMiddleClickCommand", + "MaximizeButtonRightClickCommand", +}; + +char const * const tbl_Max[] = { + "Maximize", + "Maximize (vertical only)", + "Maximize (horizontal only)", + "" }; + +QPixmap maxButtonPixmaps[3]; + +void createMaxButtonPixmaps() +{ + char const * maxButtonXpms[][3 + 13] = { + {0, 0, 0, + "...............", + ".......#.......", + "......###......", + ".....#####.....", + "..#....#....#..", + ".##....#....##.", + "###############", + ".##....#....##.", + "..#....#....#..", + ".....#####.....", + "......###......", + ".......#.......", + "..............."}, + {0, 0, 0, + "...............", + ".......#.......", + "......###......", + ".....#####.....", + ".......#.......", + ".......#.......", + ".......#.......", + ".......#.......", + ".......#.......", + ".....#####.....", + "......###......", + ".......#.......", + "..............."}, + {0, 0, 0, + "...............", + "...............", + "...............", + "...............", + "..#.........#..", + ".##.........##.", + "###############", + ".##.........##.", + "..#.........#..", + "...............", + "...............", + "...............", + "..............."}, + }; + + QString baseColor(". c " + KGlobalSettings::baseColor().name()); + QString textColor("# c " + KGlobalSettings::textColor().name()); + for (int t = 0; t < 3; ++t) + { + maxButtonXpms[t][0] = "15 13 2 1"; + maxButtonXpms[t][1] = baseColor.ascii(); + maxButtonXpms[t][2] = textColor.ascii(); + maxButtonPixmaps[t] = QPixmap(maxButtonXpms[t]); + maxButtonPixmaps[t].setMask(maxButtonPixmaps[t].createHeuristicMask()); + } +} + +} // namespace + +void KTitleBarActionsConfig::paletteChanged() +{ + createMaxButtonPixmaps(); + for (int b = 0; b < 3; ++b) + for (int t = 0; t < 3; ++t) + coMax[b]->changeItem(maxButtonPixmaps[t], t); + +} + +KTitleBarActionsConfig::KTitleBarActionsConfig (bool _standAlone, KConfig *_config, QWidget * parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + QString strWin1, strWin2, strWin3, strAllKey, strAll1, strAll2, strAll3; + QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint()); + QGrid *grid; + QGroupBox *box; + QLabel *label; + QString strMouseButton1, strMouseButton3, strMouseWheel; + QString txtButton1, txtButton3, txtButton4; + QStringList items; + bool leftHandedMouse = ( KGlobalSettings::mouseSettings().handed == KGlobalSettings::KMouseSettings::LeftHanded); + +/** Titlebar doubleclick ************/ + + QHBoxLayout *hlayout = new QHBoxLayout(layout); + + label = new QLabel(i18n("&Titlebar double-click:"), this); + hlayout->addWidget(label); + QWhatsThis::add( label, i18n("Here you can customize mouse click behavior when double clicking on the" + " titlebar of a window.") ); + + QComboBox* combo = new QComboBox(this); + combo->insertItem(i18n("Maximize")); + combo->insertItem(i18n("Maximize (vertical only)")); + combo->insertItem(i18n("Maximize (horizontal only)")); + combo->insertItem(i18n("Minimize")); + combo->insertItem(i18n("Shade")); + combo->insertItem(i18n("Lower")); + combo->insertItem(i18n("On All Desktops")); + combo->insertItem(i18n("Nothing")); + combo->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + hlayout->addWidget(combo); + coTiDbl = combo; + QWhatsThis::add(combo, i18n("Behavior on <em>double</em> click into the titlebar.")); + + label->setBuddy(combo); + +/** Mouse Wheel Events **************/ + QHBoxLayout *hlayoutW = new QHBoxLayout(layout); + strMouseWheel = i18n("Titlebar wheel event:"); + label = new QLabel(strMouseWheel, this); + hlayoutW->addWidget(label); + txtButton4 = i18n("Handle mouse wheel events"); + QWhatsThis::add( label, txtButton4); + + // Titlebar and frame mouse Wheel + QComboBox* comboW = new QComboBox(this); + comboW->insertItem(i18n("Raise/Lower")); + comboW->insertItem(i18n("Shade/Unshade")); + comboW->insertItem(i18n("Maximize/Restore")); + comboW->insertItem(i18n("Keep Above/Below")); + comboW->insertItem(i18n("Move to Previous/Next Desktop")); + comboW->insertItem(i18n("Change Opacity")); + comboW->insertItem(i18n("Nothing")); + comboW->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); + connect(comboW, SIGNAL(activated(int)), SLOT(changed())); + hlayoutW->addWidget(comboW); + coTiAct4 = comboW; + QWhatsThis::add(comboW, txtButton4); + label->setBuddy(comboW); + +/** Titlebar and frame **************/ + + box = new QVGroupBox( i18n("Titlebar && Frame"), this, "Titlebar and Frame"); + box->layout()->setMargin(KDialog::marginHint()); + box->layout()->setSpacing(KDialog::spacingHint()); + layout->addWidget(box); + QWhatsThis::add( box, i18n("Here you can customize mouse click behavior when clicking on the" + " titlebar or the frame of a window.") ); + + grid = new QGrid(4, Qt::Vertical, box); + + + new QLabel(grid); // dummy + + strMouseButton1 = i18n("Left button:"); + txtButton1 = i18n("In this row you can customize left click behavior when clicking into" + " the titlebar or the frame."); + + strMouseButton3 = i18n("Right button:"); + txtButton3 = i18n("In this row you can customize right click behavior when clicking into" + " the titlebar or the frame." ); + + if ( leftHandedMouse ) + { + qSwap(strMouseButton1, strMouseButton3); + qSwap(txtButton1, txtButton3); + } + + label = new QLabel(strMouseButton1, grid); + QWhatsThis::add( label, txtButton1); + + label = new QLabel(i18n("Middle button:"), grid); + QWhatsThis::add( label, i18n("In this row you can customize middle click behavior when clicking into" + " the titlebar or the frame.") ); + + label = new QLabel(strMouseButton3, grid); + QWhatsThis::add( label, txtButton3); + + + label = new QLabel(i18n("Active"), grid); + label->setAlignment(AlignCenter); + QWhatsThis::add( label, i18n("In this column you can customize mouse clicks into the titlebar" + " or the frame of an active window.") ); + + // Titlebar and frame, active, mouse button 1 + combo = new QComboBox(grid); + combo->insertItem(i18n("Raise")); + combo->insertItem(i18n("Lower")); + combo->insertItem(i18n("Operations Menu")); + combo->insertItem(i18n("Toggle Raise & Lower")); + combo->insertItem(i18n("Nothing")); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiAct1 = combo; + + txtButton1 = i18n("Behavior on <em>left</em> click into the titlebar or frame of an " + "<em>active</em> window."); + + txtButton3 = i18n("Behavior on <em>right</em> click into the titlebar or frame of an " + "<em>active</em> window."); + + // Be nice to left handed users + if ( leftHandedMouse ) qSwap(txtButton1, txtButton3); + + QWhatsThis::add(combo, txtButton1); + + // Titlebar and frame, active, mouse button 2 + + items << i18n("Raise") + << i18n("Lower") + << i18n("Operations Menu") + << i18n("Toggle Raise & Lower") + << i18n("Nothing") + << i18n("Shade"); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiAct2 = combo; + QWhatsThis::add(combo, i18n("Behavior on <em>middle</em> click into the titlebar or frame of an <em>active</em> window.")); + + // Titlebar and frame, active, mouse button 3 + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiAct3 = combo; + QWhatsThis::add(combo, txtButton3 ); + + txtButton1 = i18n("Behavior on <em>left</em> click into the titlebar or frame of an " + "<em>inactive</em> window."); + + txtButton3 = i18n("Behavior on <em>right</em> click into the titlebar or frame of an " + "<em>inactive</em> window."); + + // Be nice to left handed users + if ( leftHandedMouse ) qSwap(txtButton1, txtButton3); + + label = new QLabel(i18n("Inactive"), grid); + label->setAlignment(AlignCenter); + QWhatsThis::add( label, i18n("In this column you can customize mouse clicks into the titlebar" + " or the frame of an inactive window.") ); + + items.clear(); + items << i18n("Activate & Raise") + << i18n("Activate & Lower") + << i18n("Activate") + << i18n("Shade") + << i18n("Operations Menu") + << i18n("Raise") + << i18n("Lower") + << i18n("Nothing"); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiInAct1 = combo; + QWhatsThis::add(combo, txtButton1); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiInAct2 = combo; + QWhatsThis::add(combo, i18n("Behavior on <em>middle</em> click into the titlebar or frame of an <em>inactive</em> window.")); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coTiInAct3 = combo; + QWhatsThis::add(combo, txtButton3); + +/** Maximize Button ******************/ + + box = new QHGroupBox(i18n("Maximize Button"), this, "Maximize Button"); + box->layout()->setMargin(KDialog::marginHint()); + box->layout()->setSpacing(KDialog::spacingHint()); + layout->addWidget(box); + QWhatsThis::add( box, + i18n("Here you can customize behavior when clicking on the maximize button.") ); + + QString strMouseButton[] = { + i18n("Left button:"), + i18n("Middle button:"), + i18n("Right button:")}; + + QString txtButton[] = { + i18n("Behavior on <em>left</em> click onto the maximize button." ), + i18n("Behavior on <em>middle</em> click onto the maximize button." ), + i18n("Behavior on <em>right</em> click onto the maximize button." )}; + + if ( leftHandedMouse ) // Be nice to lefties + { + qSwap(strMouseButton[0], strMouseButton[2]); + qSwap(txtButton[0], txtButton[2]); + } + + createMaxButtonPixmaps(); + for (int b = 0; b < 3; ++b) + { + if (b != 0) new QWidget(box); // Spacer + + QLabel * label = new QLabel(strMouseButton[b], box); + QWhatsThis::add( label, txtButton[b] ); + label ->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum )); + + coMax[b] = new ToolTipComboBox(box, tbl_Max); + for (int t = 0; t < 3; ++t) coMax[b]->insertItem(maxButtonPixmaps[t]); + connect(coMax[b], SIGNAL(activated(int)), SLOT(changed())); + connect(coMax[b], SIGNAL(activated(int)), coMax[b], SLOT(changed())); + QWhatsThis::add( coMax[b], txtButton[b] ); + coMax[b]->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum )); + } + + connect(kapp, SIGNAL(kdisplayPaletteChanged()), SLOT(paletteChanged())); + + layout->addStretch(); + + load(); +} + +KTitleBarActionsConfig::~KTitleBarActionsConfig() +{ + if (standAlone) + delete config; +} + +// do NOT change the texts below, they are written to config file +// and are not shown in the GUI +// they have to match the order of items in GUI elements though +const char* const tbl_TiDbl[] = { + "Maximize", + "Maximize (vertical only)", + "Maximize (horizontal only)", + "Minimize", + "Shade", + "Lower", + "OnAllDesktops", + "Nothing", + "" }; + +const char* const tbl_TiAc[] = { + "Raise", + "Lower", + "Operations menu", + "Toggle raise and lower", + "Nothing", + "Shade", + "" }; + +const char* const tbl_TiInAc[] = { + "Activate and raise", + "Activate and lower", + "Activate", + "Shade", + "Operations menu", + "Raise", + "Lower", + "Nothing", + "" }; + +const char* const tbl_Win[] = { + "Activate, raise and pass click", + "Activate and pass click", + "Activate", + "Activate and raise", + "" }; + +const char* const tbl_AllKey[] = { + "Meta", + "Alt", + "" }; + +const char* const tbl_All[] = { + "Move", + "Activate, raise and move", + "Toggle raise and lower", + "Resize", + "Raise", + "Lower", + "Minimize", + "Nothing", + "" }; + +const char* tbl_TiWAc[] = { + "Raise/Lower", + "Shade/Unshade", + "Maximize/Restore", + "Above/Below", + "Previous/Next Desktop", + "Change Opacity", + "Nothing", + "" }; + +const char* tbl_AllW[] = { + "Raise/Lower", + "Shade/Unshade", + "Maximize/Restore", + "Above/Below", + "Previous/Next Desktop", + "Change Opacity", + "Nothing", + "" }; + +static const char* tbl_num_lookup( const char* const arr[], int pos ) +{ + for( int i = 0; + arr[ i ][ 0 ] != '\0' && pos >= 0; + ++i ) + { + if( pos == 0 ) + return arr[ i ]; + --pos; + } + abort(); // should never happen this way +} + +static int tbl_txt_lookup( const char* const arr[], const char* txt ) +{ + int pos = 0; + for( int i = 0; + arr[ i ][ 0 ] != '\0'; + ++i ) + { + if( qstricmp( txt, arr[ i ] ) == 0 ) + return pos; + ++pos; + } + return 0; +} + +void KTitleBarActionsConfig::setComboText( QComboBox* combo, const char*txt ) +{ + if( combo == coTiDbl ) + combo->setCurrentItem( tbl_txt_lookup( tbl_TiDbl, txt )); + else if( combo == coTiAct1 || combo == coTiAct2 || combo == coTiAct3 ) + combo->setCurrentItem( tbl_txt_lookup( tbl_TiAc, txt )); + else if( combo == coTiInAct1 || combo == coTiInAct2 || combo == coTiInAct3 ) + combo->setCurrentItem( tbl_txt_lookup( tbl_TiInAc, txt )); + else if( combo == coTiAct4 ) + combo->setCurrentItem( tbl_txt_lookup( tbl_TiWAc, txt )); + else if( combo == coMax[0] || combo == coMax[1] || combo == coMax[2] ) + { + combo->setCurrentItem( tbl_txt_lookup( tbl_Max, txt )); + static_cast<ToolTipComboBox *>(combo)->changed(); + } + else + abort(); +} + +const char* KTitleBarActionsConfig::functionTiDbl( int i ) +{ + return tbl_num_lookup( tbl_TiDbl, i ); +} + +const char* KTitleBarActionsConfig::functionTiAc( int i ) +{ + return tbl_num_lookup( tbl_TiAc, i ); +} + +const char* KTitleBarActionsConfig::functionTiInAc( int i ) +{ + return tbl_num_lookup( tbl_TiInAc, i ); +} + +const char* KTitleBarActionsConfig::functionTiWAc(int i) +{ + return tbl_num_lookup( tbl_TiWAc, i ); +} + +const char* KTitleBarActionsConfig::functionMax( int i ) +{ + return tbl_num_lookup( tbl_Max, i ); +} + +void KTitleBarActionsConfig::load() +{ + config->setGroup("Windows"); + setComboText(coTiDbl, config->readEntry("TitlebarDoubleClickCommand","Shade").ascii()); + for (int t = 0; t < 3; ++t) + setComboText(coMax[t],config->readEntry(cnf_Max[t], tbl_Max[t]).ascii()); + + config->setGroup( "MouseBindings"); + setComboText(coTiAct1,config->readEntry("CommandActiveTitlebar1","Raise").ascii()); + setComboText(coTiAct2,config->readEntry("CommandActiveTitlebar2","Lower").ascii()); + setComboText(coTiAct3,config->readEntry("CommandActiveTitlebar3","Operations menu").ascii()); + setComboText(coTiAct4,config->readEntry("CommandTitlebarWheel","Nothing").ascii()); + setComboText(coTiInAct1,config->readEntry("CommandInactiveTitlebar1","Activate and raise").ascii()); + setComboText(coTiInAct2,config->readEntry("CommandInactiveTitlebar2","Activate and lower").ascii()); + setComboText(coTiInAct3,config->readEntry("CommandInactiveTitlebar3","Operations menu").ascii()); +} + +void KTitleBarActionsConfig::save() +{ + config->setGroup("Windows"); + config->writeEntry("TitlebarDoubleClickCommand", functionTiDbl( coTiDbl->currentItem() ) ); + for (int t = 0; t < 3; ++t) + config->writeEntry(cnf_Max[t], functionMax(coMax[t]->currentItem())); + + config->setGroup("MouseBindings"); + config->writeEntry("CommandActiveTitlebar1", functionTiAc(coTiAct1->currentItem())); + config->writeEntry("CommandActiveTitlebar2", functionTiAc(coTiAct2->currentItem())); + config->writeEntry("CommandActiveTitlebar3", functionTiAc(coTiAct3->currentItem())); + config->writeEntry("CommandInactiveTitlebar1", functionTiInAc(coTiInAct1->currentItem())); + config->writeEntry("CommandTitlebarWheel", functionTiWAc(coTiAct4->currentItem())); + config->writeEntry("CommandInactiveTitlebar2", functionTiInAc(coTiInAct2->currentItem())); + config->writeEntry("CommandInactiveTitlebar3", functionTiInAc(coTiInAct3->currentItem())); + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } +} + +void KTitleBarActionsConfig::defaults() +{ + setComboText(coTiDbl, "Shade"); + setComboText(coTiAct1,"Raise"); + setComboText(coTiAct2,"Lower"); + setComboText(coTiAct3,"Operations menu"); + setComboText(coTiAct4,"Nothing"); + setComboText(coTiInAct1,"Activate and raise"); + setComboText(coTiInAct2,"Activate and lower"); + setComboText(coTiInAct3,"Operations menu"); + for (int t = 0; t < 3; ++t) + setComboText(coMax[t], tbl_Max[t]); +} + + +KWindowActionsConfig::KWindowActionsConfig (bool _standAlone, KConfig *_config, QWidget * parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + QString strWin1, strWin2, strWin3, strAllKey, strAll1, strAll2, strAll3, strAllW; + QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint()); + QGrid *grid; + QGroupBox *box; + QLabel *label; + QString strMouseButton1, strMouseButton3; + QString txtButton1, txtButton3; + QStringList items; + bool leftHandedMouse = ( KGlobalSettings::mouseSettings().handed == KGlobalSettings::KMouseSettings::LeftHanded); + +/** Inactive inner window ******************/ + + box = new QVGroupBox(i18n("Inactive Inner Window"), this, "Inactive Inner Window"); + box->layout()->setMargin(KDialog::marginHint()); + box->layout()->setSpacing(KDialog::spacingHint()); + layout->addWidget(box); + QWhatsThis::add( box, i18n("Here you can customize mouse click behavior when clicking on an inactive" + " inner window ('inner' means: not titlebar, not frame).") ); + + grid = new QGrid(3, Qt::Vertical, box); + + strMouseButton1 = i18n("Left button:"); + txtButton1 = i18n("In this row you can customize left click behavior when clicking into" + " the titlebar or the frame."); + + strMouseButton3 = i18n("Right button:"); + txtButton3 = i18n("In this row you can customize right click behavior when clicking into" + " the titlebar or the frame." ); + + if ( leftHandedMouse ) + { + qSwap(strMouseButton1, strMouseButton3); + qSwap(txtButton1, txtButton3); + } + + strWin1 = i18n("In this row you can customize left click behavior when clicking into" + " an inactive inner window ('inner' means: not titlebar, not frame)."); + + strWin3 = i18n("In this row you can customize right click behavior when clicking into" + " an inactive inner window ('inner' means: not titlebar, not frame)."); + + // Be nice to lefties + if ( leftHandedMouse ) qSwap(strWin1, strWin3); + + label = new QLabel(strMouseButton1, grid); + QWhatsThis::add( label, strWin1 ); + + label = new QLabel(i18n("Middle button:"), grid); + strWin2 = i18n("In this row you can customize middle click behavior when clicking into" + " an inactive inner window ('inner' means: not titlebar, not frame)."); + QWhatsThis::add( label, strWin2 ); + + label = new QLabel(strMouseButton3, grid); + QWhatsThis::add( label, strWin3 ); + + items.clear(); + items << i18n("Activate, Raise & Pass Click") + << i18n("Activate & Pass Click") + << i18n("Activate") + << i18n("Activate & Raise"); + + QComboBox* combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coWin1 = combo; + QWhatsThis::add( combo, strWin1 ); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coWin2 = combo; + QWhatsThis::add( combo, strWin2 ); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coWin3 = combo; + QWhatsThis::add( combo, strWin3 ); + + +/** Inner window, titlebar and frame **************/ + + box = new QVGroupBox(i18n("Inner Window, Titlebar && Frame"), this, "Inner Window, Titlebar and Frame"); + box->layout()->setMargin(KDialog::marginHint()); + box->layout()->setSpacing(KDialog::spacingHint()); + layout->addWidget(box); + QWhatsThis::add( box, i18n("Here you can customize KDE's behavior when clicking somewhere into" + " a window while pressing a modifier key.")); + + grid = new QGrid(5, Qt::Vertical, box); + + // Labels + label = new QLabel(i18n("Modifier key:"), grid); + + strAllKey = i18n("Here you select whether holding the Meta key or Alt key " + "will allow you to perform the following actions."); + QWhatsThis::add( label, strAllKey ); + + + strMouseButton1 = i18n("Modifier key + left button:"); + strAll1 = i18n("In this row you can customize left click behavior when clicking into" + " the titlebar or the frame."); + + strMouseButton3 = i18n("Modifier key + right button:"); + strAll3 = i18n("In this row you can customize right click behavior when clicking into" + " the titlebar or the frame." ); + + if ( leftHandedMouse ) + { + qSwap(strMouseButton1, strMouseButton3); + qSwap(strAll1, strAll3); + } + + label = new QLabel(strMouseButton1, grid); + QWhatsThis::add( label, strAll1); + + label = new QLabel(i18n("Modifier key + middle button:"), grid); + strAll2 = i18n("Here you can customize KDE's behavior when middle clicking into a window" + " while pressing the modifier key."); + QWhatsThis::add( label, strAll2 ); + + label = new QLabel(strMouseButton3, grid); + QWhatsThis::add( label, strAll3); + + label = new QLabel(i18n("Modifier key + mouse wheel:"), grid); + strAllW = i18n("Here you can customize KDE's behavior when scrolling with the mouse wheel" + " in a window while pressing the modifier key."); + QWhatsThis::add( label, strAllW); + + // Combo's + combo = new QComboBox(grid); + combo->insertItem(i18n("Meta")); + combo->insertItem(i18n("Alt")); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coAllKey = combo; + QWhatsThis::add( combo, strAllKey ); + + items.clear(); + items << i18n("Move") + << i18n("Activate, Raise and Move") + << i18n("Toggle Raise & Lower") + << i18n("Resize") + << i18n("Raise") + << i18n("Lower") + << i18n("Minimize") + << i18n("Nothing"); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coAll1 = combo; + QWhatsThis::add( combo, strAll1 ); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coAll2 = combo; + QWhatsThis::add( combo, strAll2 ); + + combo = new QComboBox(grid); + combo->insertStringList(items); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coAll3 = combo; + QWhatsThis::add( combo, strAll3 ); + + combo = new QComboBox(grid); + combo->insertItem(i18n("Raise/Lower")); + combo->insertItem(i18n("Shade/Unshade")); + combo->insertItem(i18n("Maximize/Restore")); + combo->insertItem(i18n("Keep Above/Below")); + combo->insertItem(i18n("Move to Previous/Next Desktop")); + combo->insertItem(i18n("Change Opacity")); + combo->insertItem(i18n("Nothing")); + connect(combo, SIGNAL(activated(int)), SLOT(changed())); + coAllW = combo; + QWhatsThis::add( combo, strAllW ); + + layout->addStretch(); + + load(); +} + +KWindowActionsConfig::~KWindowActionsConfig() +{ + if (standAlone) + delete config; +} + +void KWindowActionsConfig::setComboText( QComboBox* combo, const char*txt ) +{ + if( combo == coWin1 || combo == coWin2 || combo == coWin3 ) + combo->setCurrentItem( tbl_txt_lookup( tbl_Win, txt )); + else if( combo == coAllKey ) + combo->setCurrentItem( tbl_txt_lookup( tbl_AllKey, txt )); + else if( combo == coAll1 || combo == coAll2 || combo == coAll3 ) + combo->setCurrentItem( tbl_txt_lookup( tbl_All, txt )); + else if( combo == coAllW ) + combo->setCurrentItem( tbl_txt_lookup( tbl_AllW, txt )); + else + abort(); +} + +const char* KWindowActionsConfig::functionWin( int i ) +{ + return tbl_num_lookup( tbl_Win, i ); +} + +const char* KWindowActionsConfig::functionAllKey( int i ) +{ + return tbl_num_lookup( tbl_AllKey, i ); +} + +const char* KWindowActionsConfig::functionAll( int i ) +{ + return tbl_num_lookup( tbl_All, i ); +} + +const char* KWindowActionsConfig::functionAllW(int i) +{ + return tbl_num_lookup( tbl_AllW, i ); +} + +void KWindowActionsConfig::load() +{ + config->setGroup( "MouseBindings"); + setComboText(coWin1,config->readEntry("CommandWindow1","Activate, raise and pass click").ascii()); + setComboText(coWin2,config->readEntry("CommandWindow2","Activate and pass click").ascii()); + setComboText(coWin3,config->readEntry("CommandWindow3","Activate and pass click").ascii()); + setComboText(coAllKey,config->readEntry("CommandAllKey","Alt").ascii()); + setComboText(coAll1,config->readEntry("CommandAll1","Move").ascii()); + setComboText(coAll2,config->readEntry("CommandAll2","Toggle raise and lower").ascii()); + setComboText(coAll3,config->readEntry("CommandAll3","Resize").ascii()); + setComboText(coAllW,config->readEntry("CommandAllWheel","Nothing").ascii()); +} + +void KWindowActionsConfig::save() +{ + config->setGroup("MouseBindings"); + config->writeEntry("CommandWindow1", functionWin(coWin1->currentItem())); + config->writeEntry("CommandWindow2", functionWin(coWin2->currentItem())); + config->writeEntry("CommandWindow3", functionWin(coWin3->currentItem())); + config->writeEntry("CommandAllKey", functionAllKey(coAllKey->currentItem())); + config->writeEntry("CommandAll1", functionAll(coAll1->currentItem())); + config->writeEntry("CommandAll2", functionAll(coAll2->currentItem())); + config->writeEntry("CommandAll3", functionAll(coAll3->currentItem())); + config->writeEntry("CommandAllWheel", functionAllW(coAllW->currentItem())); + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } +} + +void KWindowActionsConfig::defaults() +{ + setComboText(coWin1,"Activate, raise and pass click"); + setComboText(coWin2,"Activate and pass click"); + setComboText(coWin3,"Activate and pass click"); + setComboText(coAllKey,"Alt"); + setComboText (coAll1,"Move"); + setComboText(coAll2,"Toggle raise and lower"); + setComboText(coAll3,"Resize"); + setComboText(coAllW,"Nothing"); +} diff --git a/kwin/kcmkwin/kwinoptions/mouse.h b/kwin/kcmkwin/kwinoptions/mouse.h new file mode 100644 index 000000000..24b2d4030 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/mouse.h @@ -0,0 +1,137 @@ +/* + * mouse.h + * + * Copyright (c) 1998 Matthias Ettrich <ettrich@kde.org> + * + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KKWMMOUSECONFIG_H__ +#define __KKWMMOUSECONFIG_H__ + +class KConfig; + +#include <qwidget.h> +#include <kcmodule.h> +#include <qcombobox.h> +#include <qtooltip.h> + + +class ToolTipComboBox: public QComboBox +{ + Q_OBJECT + +public: + ToolTipComboBox(QWidget * owner, char const * const * toolTips_) + : QComboBox(owner) + , toolTips(toolTips_) {} + +public slots: + void changed() {QToolTip::add( this, i18n(toolTips[currentItem()]) );} + +protected: + char const * const * toolTips; +}; + + + +class KTitleBarActionsConfig : public KCModule +{ + Q_OBJECT + +public: + + KTitleBarActionsConfig( bool _standAlone, KConfig *_config, QWidget *parent=0, const char* name=0 ); + ~KTitleBarActionsConfig( ); + + void load(); + void save(); + void defaults(); + +public slots: + void changed() { emit KCModule::changed(true); } + +private: + QComboBox* coTiDbl; + + QComboBox* coTiAct1; + QComboBox* coTiAct2; + QComboBox* coTiAct3; + QComboBox* coTiAct4; + QComboBox* coTiInAct1; + QComboBox* coTiInAct2; + QComboBox* coTiInAct3; + + ToolTipComboBox * coMax[3]; + + KConfig *config; + bool standAlone; + + const char* functionTiDbl(int); + const char* functionTiAc(int); + const char* functionTiWAc(int); + const char* functionTiInAc(int); + const char* functionMax(int); + + void setComboText(QComboBox* combo, const char* text); + const char* fixup( const char* s ); + +private slots: + void paletteChanged(); + +}; + +class KWindowActionsConfig : public KCModule +{ + Q_OBJECT + +public: + + KWindowActionsConfig( bool _standAlone, KConfig *_config, QWidget *parent=0, const char* name=0 ); + ~KWindowActionsConfig( ); + + void load(); + void save(); + void defaults(); + +public slots: + void changed() { emit KCModule::changed(true); } + +private: + QComboBox* coWin1; + QComboBox* coWin2; + QComboBox* coWin3; + + QComboBox* coAllKey; + QComboBox* coAll1; + QComboBox* coAll2; + QComboBox* coAll3; + QComboBox* coAllW; + + KConfig *config; + bool standAlone; + + const char* functionWin(int); + const char* functionAllKey(int); + const char* functionAll(int); + const char* functionAllW(int); + + void setComboText(QComboBox* combo, const char* text); + const char* fixup( const char* s ); +}; + +#endif + diff --git a/kwin/kcmkwin/kwinoptions/windows.cpp b/kwin/kcmkwin/kwinoptions/windows.cpp new file mode 100644 index 000000000..db682b316 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/windows.cpp @@ -0,0 +1,1627 @@ +/* + * windows.cpp + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * Copyright (c) 2001 Waldo Bastian bastian@kde.org + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include <config.h> + +#include <qdir.h> +#include <qlayout.h> +#include <qslider.h> +#include <qwhatsthis.h> +#include <qvbuttongroup.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <kmessagebox.h> + +#include <kactivelabel.h> +#include <klocale.h> +#include <kcolorbutton.h> +#include <kconfig.h> +#include <knuminput.h> +#include <kapplication.h> +#include <kdialog.h> +#include <dcopclient.h> +#include <kglobal.h> +#include <kprocess.h> +#include <qtabwidget.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "windows.h" + + +// kwin config keywords +#define KWIN_FOCUS "FocusPolicy" +#define KWIN_PLACEMENT "Placement" +#define KWIN_MOVE "MoveMode" +#define KWIN_MINIMIZE_ANIM "AnimateMinimize" +#define KWIN_MINIMIZE_ANIM_SPEED "AnimateMinimizeSpeed" +#define KWIN_RESIZE_OPAQUE "ResizeMode" +#define KWIN_GEOMETRY "GeometryTip" +#define KWIN_AUTORAISE_INTERVAL "AutoRaiseInterval" +#define KWIN_AUTORAISE "AutoRaise" +#define KWIN_DELAYFOCUS_INTERVAL "DelayFocusInterval" +#define KWIN_DELAYFOCUS "DelayFocus" +#define KWIN_CLICKRAISE "ClickRaise" +#define KWIN_ANIMSHADE "AnimateShade" +#define KWIN_MOVE_RESIZE_MAXIMIZED "MoveResizeMaximizedWindows" +#define KWIN_ALTTABMODE "AltTabStyle" +#define KWIN_TRAVERSE_ALL "TraverseAll" +#define KWIN_SHOW_POPUP "ShowPopup" +#define KWIN_ROLL_OVER_DESKTOPS "RollOverDesktops" +#define KWIN_SHADEHOVER "ShadeHover" +#define KWIN_SHADEHOVER_INTERVAL "ShadeHoverInterval" +#define KWIN_FOCUS_STEALING "FocusStealingPreventionLevel" +#define KWIN_HIDE_UTILITY "HideUtilityWindowsForInactive" + +// kwm config keywords +#define KWM_ELECTRIC_BORDER "ElectricBorders" +#define KWM_ELECTRIC_BORDER_DELAY "ElectricBorderDelay" + +//CT 15mar 98 - magics +#define KWM_BRDR_SNAP_ZONE "BorderSnapZone" +#define KWM_BRDR_SNAP_ZONE_DEFAULT 10 +#define KWM_WNDW_SNAP_ZONE "WindowSnapZone" +#define KWM_WNDW_SNAP_ZONE_DEFAULT 10 + +#define MAX_BRDR_SNAP 100 +#define MAX_WNDW_SNAP 100 +#define MAX_EDGE_RES 1000 + + +KFocusConfig::~KFocusConfig () +{ + if (standAlone) + delete config; +} + +// removed the LCD display over the slider - this is not good GUI design :) RNolden 051701 +KFocusConfig::KFocusConfig (bool _standAlone, KConfig *_config, QWidget * parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + QString wtstr; + QBoxLayout *lay = new QVBoxLayout (this, 0, KDialog::spacingHint()); + + //iTLabel = new QLabel(i18n(" Allowed overlap:\n" + // "(% of desktop space)"), + // plcBox); + //iTLabel->setAlignment(AlignTop|AlignHCenter); + //pLay->addWidget(iTLabel,1,1); + + //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); + //pLay->addWidget(interactiveTrigger,1,2); + + //pLay->addRowSpacing(2,KDialog::spacingHint()); + + //lay->addWidget(plcBox); + + // focus policy + fcsBox = new QButtonGroup(i18n("Focus"),this); + fcsBox->setColumnLayout( 0, Qt::Horizontal ); + + QBoxLayout *fLay = new QVBoxLayout(fcsBox->layout(), + KDialog::spacingHint()); + + QBoxLayout *cLay = new QHBoxLayout(fLay); + QLabel *fLabel = new QLabel(i18n("&Policy:"), fcsBox); + cLay->addWidget(fLabel, 0); + focusCombo = new QComboBox(false, fcsBox); + focusCombo->insertItem(i18n("Click to Focus"), CLICK_TO_FOCUS); + focusCombo->insertItem(i18n("Focus Follows Mouse"), FOCUS_FOLLOWS_MOUSE); + focusCombo->insertItem(i18n("Focus Under Mouse"), FOCUS_UNDER_MOUSE); + focusCombo->insertItem(i18n("Focus Strictly Under Mouse"), FOCUS_STRICTLY_UNDER_MOUSE); + cLay->addWidget(focusCombo,1 ,Qt::AlignLeft); + fLabel->setBuddy(focusCombo); + + // FIXME, when more policies have been added to KWin + wtstr = i18n("The focus policy is used to determine the active window, i.e." + " the window you can work in. <ul>" + " <li><em>Click to focus:</em> A window becomes active when you click into it." + " This is the behavior you might know from other operating systems.</li>" + " <li><em>Focus follows mouse:</em> Moving the mouse pointer actively on to a" + " normal window activates it. New windows will receive the focus," + " without you having to point the mouse at them explicitly." + " Very practical if you are using the mouse a lot.</li>" + " <li><em>Focus under mouse:</em> The window that happens to be under the" + " mouse pointer is active. If the mouse points nowhere, the last window" + " that was under the mouse has focus." + " New windows will not automatically receive the focus.</li>" + " <li><em>Focus strictly under mouse:</em> Only the window under the mouse pointer is" + " active. If the mouse points nowhere, nothing has focus." + " </ul>" + "Note that 'Focus under mouse' and 'Focus strictly under mouse' prevent certain" + " features such as the Alt+Tab walk through windows dialog in the KDE mode" + " from working properly." + ); + QWhatsThis::add( focusCombo, wtstr); + QWhatsThis::add(fLabel, wtstr); + + connect(focusCombo, SIGNAL(activated(int)), this, SLOT(setAutoRaiseEnabled()) ); + + // autoraise delay + autoRaiseOn = new QCheckBox(i18n("Auto &raise"), fcsBox); + fLay->addWidget(autoRaiseOn); + connect(autoRaiseOn,SIGNAL(toggled(bool)), this, SLOT(autoRaiseOnTog(bool))); + + autoRaise = new KIntNumInput(500, fcsBox); + autoRaise->setLabel(i18n("Dela&y:"), Qt::AlignVCenter|Qt::AlignLeft); + autoRaise->setRange(0, 3000, 100, true); + autoRaise->setSteps(100,100); + autoRaise->setSuffix(i18n(" msec")); + fLay->addWidget(autoRaise); + + connect(focusCombo, SIGNAL(activated(int)), this, SLOT(setDelayFocusEnabled()) ); + + delayFocusOn = new QCheckBox(i18n("Delay focus"), fcsBox); + fLay->addWidget(delayFocusOn); + connect(delayFocusOn,SIGNAL(toggled(bool)), this, SLOT(delayFocusOnTog(bool))); + + delayFocus = new KIntNumInput(500, fcsBox); + delayFocus->setLabel(i18n("Dela&y:"), Qt::AlignVCenter|Qt::AlignLeft); + delayFocus->setRange(0, 3000, 100, true); + delayFocus->setSteps(100,100); + delayFocus->setSuffix(i18n(" msec")); + fLay->addWidget(delayFocus); + + clickRaiseOn = new QCheckBox(i18n("C&lick raise active window"), fcsBox); + connect(clickRaiseOn,SIGNAL(toggled(bool)), this, SLOT(clickRaiseOnTog(bool))); + fLay->addWidget(clickRaiseOn); + +// fLay->addColSpacing(0,QMAX(autoRaiseOn->sizeHint().width(), +// clickRaiseOn->sizeHint().width()) + 15); + + QWhatsThis::add( autoRaiseOn, i18n("When this option is enabled, a window in the background will automatically" + " come to the front when the mouse pointer has been over it for some time.") ); + wtstr = i18n("This is the delay after which the window that the mouse pointer is over will automatically" + " come to the front."); + QWhatsThis::add( autoRaise, wtstr ); + + QWhatsThis::add( clickRaiseOn, i18n("When this option is enabled, the active window will be brought to the" + " front when you click somewhere into the window contents. To change" + " it for inactive windows, you need to change the settings" + " in the Actions tab.") ); + + QWhatsThis::add( delayFocusOn, i18n("When this option is enabled, there will be a delay after which the" + " window the mouse pointer is over will become active (receive focus).") ); + QWhatsThis::add( delayFocus, i18n("This is the delay after which the window the mouse pointer is over" + " will automatically receive focus.") ); + + lay->addWidget(fcsBox); + + kbdBox = new QButtonGroup(i18n("Navigation"), this); + kbdBox->setColumnLayout( 0, Qt::Horizontal ); + QVBoxLayout *kLay = new QVBoxLayout(kbdBox->layout(), KDialog::spacingHint()); + + altTabPopup = new QCheckBox( i18n("Show window list while switching windows"), kbdBox ); + kLay->addWidget( altTabPopup ); + + wtstr = i18n("Hold down the Alt key and press the Tab key repeatedly to walk" + " through the windows on the current desktop (the Alt+Tab" + " combination can be reconfigured).\n\n" + "If this checkbox is checked" + " a popup widget is shown, displaying the icons of all windows to" + " walk through and the title of the currently selected one.\n\n" + "Otherwise, the focus is passed to a new window each time Tab" + " is pressed, with no popup widget. In addition, the previously" + " activated window will be sent to the back in this mode."); + QWhatsThis::add( altTabPopup, wtstr ); + connect(focusCombo, SIGNAL(activated(int)), this, SLOT(updateAltTabMode())); + + traverseAll = new QCheckBox( i18n( "&Traverse windows on all desktops" ), kbdBox ); + kLay->addWidget( traverseAll ); + + wtstr = i18n( "Leave this option disabled if you want to limit walking through" + " windows to the current desktop." ); + QWhatsThis::add( traverseAll, wtstr ); + + rollOverDesktops = new QCheckBox( i18n("Desktop navi&gation wraps around"), kbdBox ); + kLay->addWidget(rollOverDesktops); + + wtstr = i18n( "Enable this option if you want keyboard or active desktop border navigation beyond" + " the edge of a desktop to take you to the opposite edge of the new desktop." ); + QWhatsThis::add( rollOverDesktops, wtstr ); + + showPopupinfo = new QCheckBox( i18n("Popup desktop name on desktop &switch"), kbdBox ); + kLay->addWidget(showPopupinfo); + + wtstr = i18n( "Enable this option if you wish to see the current desktop" + " name popup whenever the current desktop is changed." ); + QWhatsThis::add( showPopupinfo, wtstr ); + + lay->addWidget(kbdBox); + + lay->addStretch(); + + // Any changes goes to slotChanged() + connect(focusCombo, SIGNAL(activated(int)), SLOT(changed())); + connect(fcsBox, SIGNAL(clicked(int)), SLOT(changed())); + connect(autoRaise, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(delayFocus, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(altTabPopup, SIGNAL(clicked()), SLOT(changed())); + connect(traverseAll, SIGNAL(clicked()), SLOT(changed())); + connect(rollOverDesktops, SIGNAL(clicked()), SLOT(changed())); + connect(showPopupinfo, SIGNAL(clicked()), SLOT(changed())); + + load(); +} + + +int KFocusConfig::getFocus() +{ + return focusCombo->currentItem(); +} + +void KFocusConfig::setFocus(int foc) +{ + focusCombo->setCurrentItem(foc); + + // this will disable/hide the auto raise delay widget if focus==click + setAutoRaiseEnabled(); + updateAltTabMode(); +} + +void KFocusConfig::updateAltTabMode() +{ + // not KDE-style Alt+Tab with unreasonable focus policies + altTabPopup->setEnabled( focusCombo->currentItem() == 0 || focusCombo->currentItem() == 1 ); +} + +void KFocusConfig::setAutoRaiseInterval(int tb) +{ + autoRaise->setValue(tb); +} + +void KFocusConfig::setDelayFocusInterval(int tb) +{ + delayFocus->setValue(tb); +} + +int KFocusConfig::getAutoRaiseInterval() +{ + return autoRaise->value(); +} + +int KFocusConfig::getDelayFocusInterval() +{ + return delayFocus->value(); +} + +void KFocusConfig::setAutoRaise(bool on) +{ + autoRaiseOn->setChecked(on); +} + +void KFocusConfig::setDelayFocus(bool on) +{ + delayFocusOn->setChecked(on); +} + +void KFocusConfig::setClickRaise(bool on) +{ + clickRaiseOn->setChecked(on); +} + +void KFocusConfig::setAutoRaiseEnabled() +{ + // the auto raise related widgets are: autoRaise + if ( focusCombo->currentItem() != CLICK_TO_FOCUS ) + { + autoRaiseOn->setEnabled(true); + autoRaiseOnTog(autoRaiseOn->isChecked()); + } + else + { + autoRaiseOn->setEnabled(false); + autoRaiseOnTog(false); + } +} + +void KFocusConfig::setDelayFocusEnabled() +{ + // the delayed focus related widgets are: delayFocus + if ( focusCombo->currentItem() != CLICK_TO_FOCUS ) + { + delayFocusOn->setEnabled(true); + delayFocusOnTog(delayFocusOn->isChecked()); + } + else + { + delayFocusOn->setEnabled(false); + delayFocusOnTog(false); + } +} + +void KFocusConfig::autoRaiseOnTog(bool a) { + autoRaise->setEnabled(a); + clickRaiseOn->setEnabled( !a ); +} + +void KFocusConfig::delayFocusOnTog(bool a) { + delayFocus->setEnabled(a); +} + +void KFocusConfig::clickRaiseOnTog(bool ) { +} + +void KFocusConfig::setAltTabMode(bool a) { + altTabPopup->setChecked(a); +} + +void KFocusConfig::setTraverseAll(bool a) { + traverseAll->setChecked(a); +} + +void KFocusConfig::setRollOverDesktops(bool a) { + rollOverDesktops->setChecked(a); +} + +void KFocusConfig::setShowPopupinfo(bool a) { + showPopupinfo->setChecked(a); +} + +void KFocusConfig::load( void ) +{ + QString key; + + config->setGroup( "Windows" ); + + key = config->readEntry(KWIN_FOCUS); + if( key == "ClickToFocus") + setFocus(CLICK_TO_FOCUS); + else if( key == "FocusFollowsMouse") + setFocus(FOCUS_FOLLOWS_MOUSE); + else if(key == "FocusUnderMouse") + setFocus(FOCUS_UNDER_MOUSE); + else if(key == "FocusStrictlyUnderMouse") + setFocus(FOCUS_STRICTLY_UNDER_MOUSE); + + int k = config->readNumEntry(KWIN_AUTORAISE_INTERVAL,750); + setAutoRaiseInterval(k); + + k = config->readNumEntry(KWIN_DELAYFOCUS_INTERVAL,750); + setDelayFocusInterval(k); + + key = config->readEntry(KWIN_AUTORAISE); + setAutoRaise(key == "on"); + key = config->readEntry(KWIN_DELAYFOCUS); + setDelayFocus(key == "on"); + key = config->readEntry(KWIN_CLICKRAISE); + setClickRaise(key != "off"); + setAutoRaiseEnabled(); // this will disable/hide the auto raise delay widget if focus==click + setDelayFocusEnabled(); + + key = config->readEntry(KWIN_ALTTABMODE, "KDE"); + setAltTabMode(key == "KDE"); + + setRollOverDesktops( config->readBoolEntry(KWIN_ROLL_OVER_DESKTOPS, true )); + + config->setGroup( "PopupInfo" ); + setShowPopupinfo( config->readBoolEntry(KWIN_SHOW_POPUP, false )); + + config->setGroup( "TabBox" ); + setTraverseAll( config->readBoolEntry(KWIN_TRAVERSE_ALL, false )); + + config->setGroup("Desktops"); + emit KCModule::changed(false); +} + +void KFocusConfig::save( void ) +{ + int v; + + config->setGroup( "Windows" ); + + v = getFocus(); + if (v == CLICK_TO_FOCUS) + config->writeEntry(KWIN_FOCUS,"ClickToFocus"); + else if (v == FOCUS_UNDER_MOUSE) + config->writeEntry(KWIN_FOCUS,"FocusUnderMouse"); + else if (v == FOCUS_STRICTLY_UNDER_MOUSE) + config->writeEntry(KWIN_FOCUS,"FocusStrictlyUnderMouse"); + else + config->writeEntry(KWIN_FOCUS,"FocusFollowsMouse"); + + v = getAutoRaiseInterval(); + if (v <0) v = 0; + config->writeEntry(KWIN_AUTORAISE_INTERVAL,v); + + v = getDelayFocusInterval(); + if (v <0) v = 0; + config->writeEntry(KWIN_DELAYFOCUS_INTERVAL,v); + + if (autoRaiseOn->isChecked()) + config->writeEntry(KWIN_AUTORAISE, "on"); + else + config->writeEntry(KWIN_AUTORAISE, "off"); + + if (delayFocusOn->isChecked()) + config->writeEntry(KWIN_DELAYFOCUS, "on"); + else + config->writeEntry(KWIN_DELAYFOCUS, "off"); + + if (clickRaiseOn->isChecked()) + config->writeEntry(KWIN_CLICKRAISE, "on"); + else + config->writeEntry(KWIN_CLICKRAISE, "off"); + + if (altTabPopup->isChecked()) + config->writeEntry(KWIN_ALTTABMODE, "KDE"); + else + config->writeEntry(KWIN_ALTTABMODE, "CDE"); + + config->writeEntry( KWIN_ROLL_OVER_DESKTOPS, rollOverDesktops->isChecked()); + + config->setGroup( "PopupInfo" ); + config->writeEntry( KWIN_SHOW_POPUP, showPopupinfo->isChecked()); + + config->setGroup( "TabBox" ); + config->writeEntry( KWIN_TRAVERSE_ALL , traverseAll->isChecked()); + + config->setGroup("Desktops"); + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } + emit KCModule::changed(false); +} + +void KFocusConfig::defaults() +{ + setAutoRaiseInterval(0); + setDelayFocusInterval(0); + setFocus(CLICK_TO_FOCUS); + setAutoRaise(false); + setDelayFocus(false); + setClickRaise(true); + setAltTabMode(true); + setTraverseAll( false ); + setRollOverDesktops(true); + setShowPopupinfo(false); + emit KCModule::changed(true); +} + +KAdvancedConfig::~KAdvancedConfig () +{ + if (standAlone) + delete config; +} + +KAdvancedConfig::KAdvancedConfig (bool _standAlone, KConfig *_config, QWidget *parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + QString wtstr; + QBoxLayout *lay = new QVBoxLayout (this, 0, KDialog::spacingHint()); + + //iTLabel = new QLabel(i18n(" Allowed overlap:\n" + // "(% of desktop space)"), + // plcBox); + //iTLabel->setAlignment(AlignTop|AlignHCenter); + //pLay->addWidget(iTLabel,1,1); + + //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); + //pLay->addWidget(interactiveTrigger,1,2); + + //pLay->addRowSpacing(2,KDialog::spacingHint()); + + //lay->addWidget(plcBox); + + shBox = new QVButtonGroup(i18n("Shading"), this); + + animateShade = new QCheckBox(i18n("Anima&te"), shBox); + QWhatsThis::add(animateShade, i18n("Animate the action of reducing the window to its titlebar (shading)" + " as well as the expansion of a shaded window") ); + + shadeHoverOn = new QCheckBox(i18n("&Enable hover"), shBox); + + connect(shadeHoverOn, SIGNAL(toggled(bool)), this, SLOT(shadeHoverChanged(bool))); + + shadeHover = new KIntNumInput(500, shBox); + shadeHover->setLabel(i18n("Dela&y:"), Qt::AlignVCenter|Qt::AlignLeft); + shadeHover->setRange(0, 3000, 100, true); + shadeHover->setSteps(100, 100); + shadeHover->setSuffix(i18n(" msec")); + + QWhatsThis::add(shadeHoverOn, i18n("If Shade Hover is enabled, a shaded window will un-shade automatically " + "when the mouse pointer has been over the title bar for some time.")); + + wtstr = i18n("Sets the time in milliseconds before the window unshades " + "when the mouse pointer goes over the shaded window."); + QWhatsThis::add(shadeHover, wtstr); + + lay->addWidget(shBox); + + // Any changes goes to slotChanged() + connect(animateShade, SIGNAL(toggled(bool)), SLOT(changed())); + connect(shadeHoverOn, SIGNAL(toggled(bool)), SLOT(changed())); + connect(shadeHover, SIGNAL(valueChanged(int)), SLOT(changed())); + + electricBox = new QVButtonGroup(i18n("Active Desktop Borders"), this); + electricBox->setMargin(15); + + QWhatsThis::add( electricBox, i18n("If this option is enabled, moving the mouse to a screen border" + " will change your desktop. This is e.g. useful if you want to drag windows from one desktop" + " to the other.") ); + active_disable = new QRadioButton(i18n("D&isabled"), electricBox); + active_move = new QRadioButton(i18n("Only &when moving windows"), electricBox); + active_always = new QRadioButton(i18n("A&lways enabled"), electricBox); + + delays = new KIntNumInput(10, electricBox); + delays->setRange(0, MAX_EDGE_RES, 50, true); + delays->setSuffix(i18n(" msec")); + delays->setLabel(i18n("Desktop &switch delay:")); + QWhatsThis::add( delays, i18n("Here you can set a delay for switching desktops using the active" + " borders feature. Desktops will be switched after the mouse has been pushed against a screen border" + " for the specified number of milliseconds.") ); + + connect( electricBox, SIGNAL(clicked(int)), this, SLOT(setEBorders())); + + // Any changes goes to slotChanged() + connect(electricBox, SIGNAL(clicked(int)), SLOT(changed())); + connect(delays, SIGNAL(valueChanged(int)), SLOT(changed())); + + lay->addWidget(electricBox); + + QHBoxLayout* focusStealingLayout = new QHBoxLayout( lay,KDialog::spacingHint()); + QLabel* focusStealingLabel = new QLabel( i18n( "Focus stealing prevention level:" ), this ); + focusStealing = new QComboBox( this ); + focusStealing->insertItem( i18n( "Focus Stealing Prevention Level", "None" )); + focusStealing->insertItem( i18n( "Focus Stealing Prevention Level", "Low" )); + focusStealing->insertItem( i18n( "Focus Stealing Prevention Level", "Normal" )); + focusStealing->insertItem( i18n( "Focus Stealing Prevention Level", "High" )); + focusStealing->insertItem( i18n( "Focus Stealing Prevention Level", "Extreme" )); + focusStealingLabel->setBuddy( focusStealing ); + focusStealingLayout->addWidget( focusStealingLabel ); + focusStealingLayout->addWidget( focusStealing, AlignLeft ); + wtstr = i18n( "<p>This option specifies how much KWin will try to prevent unwanted focus stealing " + "caused by unexpected activation of new windows. (Note: This feature does not " + "work with the Focus Under Mouse or Focus Strictly Under Mouse focus policies.)" + "<ul>" + "<li><em>None:</em> Prevention is turned off " + "and new windows always become activated.</li>" + "<li><em>Low:</em> Prevention is enabled; when some window does not have support " + "for the underlying mechanism and KWin cannot reliably decide whether to " + "activate the window or not, it will be activated. This setting may have both " + "worse and better results than normal level, depending on the applications.</li>" + "<li><em>Normal:</em> Prevention is enabled.</li>" + "<li><em>High:</em> New windows get activated only if no window is currently active " + "or if they belong to the currently active application. This setting is probably " + "not really usable when not using mouse focus policy.</li>" + "<li><em>Extreme:</em> All windows must be explicitly activated by the user.</li>" + "</ul></p>" + "<p>Windows that are prevented from stealing focus are marked as demanding attention, " + "which by default means their taskbar entry will be highlighted. This can be changed " + "in the Notifications control module.</p>" ); + QWhatsThis::add( focusStealing, wtstr ); + QWhatsThis::add( focusStealingLabel, wtstr ); + connect(focusStealing, SIGNAL(activated(int)), SLOT(changed())); + + hideUtilityWindowsForInactive = new QCheckBox( i18n( "Hide utility windows for inactive applications" ), this ); + QWhatsThis::add( hideUtilityWindowsForInactive, + i18n( "When turned on, utility windows (tool windows, torn-off menus,...) of inactive applications will be" + " hidden and will be shown only when the application becomes active. Note that applications" + " have to mark the windows with the proper window type for this feature to work." )); + connect(hideUtilityWindowsForInactive, SIGNAL(toggled(bool)), SLOT(changed())); + lay->addWidget( hideUtilityWindowsForInactive ); + + lay->addStretch(); + load(); + +} + +void KAdvancedConfig::setShadeHover(bool on) { + shadeHoverOn->setChecked(on); + shadeHover->setEnabled(on); +} + +void KAdvancedConfig::setShadeHoverInterval(int k) { + shadeHover->setValue(k); +} + +int KAdvancedConfig::getShadeHoverInterval() { + + return shadeHover->value(); +} + +void KAdvancedConfig::shadeHoverChanged(bool a) { + shadeHover->setEnabled(a); +} + +void KAdvancedConfig::setAnimateShade(bool a) { + animateShade->setChecked(a); +} + +void KAdvancedConfig::setFocusStealing(int l) { + l = KMAX( 0, KMIN( 4, l )); + focusStealing->setCurrentItem(l); +} + +void KAdvancedConfig::setHideUtilityWindowsForInactive(bool s) { + hideUtilityWindowsForInactive->setChecked( s ); +} + +void KAdvancedConfig::load( void ) +{ + config->setGroup( "Windows" ); + + setAnimateShade(config->readBoolEntry(KWIN_ANIMSHADE, true)); + setShadeHover(config->readBoolEntry(KWIN_SHADEHOVER, false)); + setShadeHoverInterval(config->readNumEntry(KWIN_SHADEHOVER_INTERVAL, 250)); + + setElectricBorders(config->readNumEntry(KWM_ELECTRIC_BORDER, 0)); + setElectricBorderDelay(config->readNumEntry(KWM_ELECTRIC_BORDER_DELAY, 150)); + +// setFocusStealing( config->readNumEntry(KWIN_FOCUS_STEALING, 2 )); + // TODO default to low for now + setFocusStealing( config->readNumEntry(KWIN_FOCUS_STEALING, 1 )); + setHideUtilityWindowsForInactive( config->readBoolEntry( KWIN_HIDE_UTILITY, true )); + + emit KCModule::changed(false); +} + +void KAdvancedConfig::save( void ) +{ + int v; + + config->setGroup( "Windows" ); + config->writeEntry(KWIN_ANIMSHADE, animateShade->isChecked()); + if (shadeHoverOn->isChecked()) + config->writeEntry(KWIN_SHADEHOVER, "on"); + else + config->writeEntry(KWIN_SHADEHOVER, "off"); + + v = getShadeHoverInterval(); + if (v<0) v = 0; + config->writeEntry(KWIN_SHADEHOVER_INTERVAL, v); + + config->writeEntry(KWM_ELECTRIC_BORDER, getElectricBorders()); + config->writeEntry(KWM_ELECTRIC_BORDER_DELAY,getElectricBorderDelay()); + + config->writeEntry(KWIN_FOCUS_STEALING, focusStealing->currentItem()); + config->writeEntry(KWIN_HIDE_UTILITY, hideUtilityWindowsForInactive->isChecked()); + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } + emit KCModule::changed(false); +} + +void KAdvancedConfig::defaults() +{ + setAnimateShade(true); + setShadeHover(false); + setShadeHoverInterval(250); + setElectricBorders(0); + setElectricBorderDelay(150); +// setFocusStealing(2); + // TODO default to low for now + setFocusStealing(1); + setHideUtilityWindowsForInactive( true ); + emit KCModule::changed(true); +} + +void KAdvancedConfig::setEBorders() +{ + delays->setEnabled(!active_disable->isChecked()); +} + +int KAdvancedConfig::getElectricBorders() +{ + if (active_move->isChecked()) + return 1; + if (active_always->isChecked()) + return 2; + return 0; +} + +int KAdvancedConfig::getElectricBorderDelay() +{ + return delays->value(); +} + +void KAdvancedConfig::setElectricBorders(int i){ + switch(i) + { + case 1: active_move->setChecked(true); break; + case 2: active_always->setChecked(true); break; + default: active_disable->setChecked(true); break; + } + setEBorders(); +} + +void KAdvancedConfig::setElectricBorderDelay(int delay) +{ + delays->setValue(delay); +} + + +KMovingConfig::~KMovingConfig () +{ + if (standAlone) + delete config; +} + +KMovingConfig::KMovingConfig (bool _standAlone, KConfig *_config, QWidget *parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + QString wtstr; + QBoxLayout *lay = new QVBoxLayout (this, 0, KDialog::spacingHint()); + + windowsBox = new QButtonGroup(i18n("Windows"), this); + windowsBox->setColumnLayout( 0, Qt::Horizontal ); + + QBoxLayout *wLay = new QVBoxLayout (windowsBox->layout(), KDialog::spacingHint()); + + QBoxLayout *bLay = new QVBoxLayout; + wLay->addLayout(bLay); + + opaque = new QCheckBox(i18n("Di&splay content in moving windows"), windowsBox); + bLay->addWidget(opaque); + QWhatsThis::add( opaque, i18n("Enable this option if you want a window's content to be fully shown" + " while moving it, instead of just showing a window 'skeleton'. The result may not be satisfying" + " on slow machines without graphic acceleration.") ); + + resizeOpaqueOn = new QCheckBox(i18n("Display content in &resizing windows"), windowsBox); + bLay->addWidget(resizeOpaqueOn); + QWhatsThis::add( resizeOpaqueOn, i18n("Enable this option if you want a window's content to be shown" + " while resizing it, instead of just showing a window 'skeleton'. The result may not be satisfying" + " on slow machines.") ); + + geometryTipOn = new QCheckBox(i18n("Display window &geometry when moving or resizing"), windowsBox); + bLay->addWidget(geometryTipOn); + QWhatsThis::add(geometryTipOn, i18n("Enable this option if you want a window's geometry to be displayed" + " while it is being moved or resized. The window position relative" + " to the top-left corner of the screen is displayed together with" + " its size.")); + + QGridLayout *rLay = new QGridLayout(2,3); + bLay->addLayout(rLay); + rLay->setColStretch(0,0); + rLay->setColStretch(1,1); + + minimizeAnimOn = new QCheckBox(i18n("Animate minimi&ze and restore"), + windowsBox); + QWhatsThis::add( minimizeAnimOn, i18n("Enable this option if you want an animation shown when" + " windows are minimized or restored." ) ); + rLay->addWidget(minimizeAnimOn,0,0); + + minimizeAnimSlider = new QSlider(0,10,10,0,QSlider::Horizontal, windowsBox); + minimizeAnimSlider->setSteps(1, 1); + // QSlider::Below clashes with a X11/X.h #define + #undef Below + minimizeAnimSlider->setTickmarks(QSlider::Below); + rLay->addMultiCellWidget(minimizeAnimSlider,0,0,1,2); + + connect(minimizeAnimOn, SIGNAL(toggled(bool)), this, SLOT(setMinimizeAnim(bool))); + connect(minimizeAnimSlider, SIGNAL(valueChanged(int)), this, SLOT(setMinimizeAnimSpeed(int))); + + minimizeAnimSlowLabel= new QLabel(i18n("Slow"),windowsBox); + minimizeAnimSlowLabel->setAlignment(Qt::AlignTop|Qt::AlignLeft); + rLay->addWidget(minimizeAnimSlowLabel,1,1); + + minimizeAnimFastLabel= new QLabel(i18n("Fast"),windowsBox); + minimizeAnimFastLabel->setAlignment(Qt::AlignTop|Qt::AlignRight); + rLay->addWidget(minimizeAnimFastLabel,1,2); + + wtstr = i18n("Here you can set the speed of the animation shown when windows are" + " minimized and restored. "); + QWhatsThis::add( minimizeAnimSlider, wtstr ); + QWhatsThis::add( minimizeAnimSlowLabel, wtstr ); + QWhatsThis::add( minimizeAnimFastLabel, wtstr ); + + moveResizeMaximized = new QCheckBox( i18n("Allow moving and resizing o&f maximized windows"), windowsBox); + bLay->addWidget(moveResizeMaximized); + QWhatsThis::add(moveResizeMaximized, i18n("When enabled, this feature activates the border of maximized windows" + " and allows you to move or resize them," + " just like for normal windows")); + + QBoxLayout *vLay = new QHBoxLayout(bLay); + + QLabel *plcLabel = new QLabel(i18n("&Placement:"),windowsBox); + + placementCombo = new QComboBox(false, windowsBox); + placementCombo->insertItem(i18n("Smart"), SMART_PLACEMENT); + placementCombo->insertItem(i18n("Maximizing"), MAXIMIZING_PLACEMENT); + placementCombo->insertItem(i18n("Cascade"), CASCADE_PLACEMENT); + placementCombo->insertItem(i18n("Random"), RANDOM_PLACEMENT); + placementCombo->insertItem(i18n("Centered"), CENTERED_PLACEMENT); + placementCombo->insertItem(i18n("Zero-Cornered"), ZEROCORNERED_PLACEMENT); + // CT: disabling is needed as long as functionality misses in kwin + //placementCombo->insertItem(i18n("Interactive"), INTERACTIVE_PLACEMENT); + //placementCombo->insertItem(i18n("Manual"), MANUAL_PLACEMENT); + placementCombo->setCurrentItem(SMART_PLACEMENT); + + // FIXME, when more policies have been added to KWin + wtstr = i18n("The placement policy determines where a new window" + " will appear on the desktop." + " <ul>" + " <li><em>Smart</em> will try to achieve a minimum overlap of windows</li>" + " <li><em>Maximizing</em> will try to maximize every window to fill the whole screen." + " It might be useful to selectively affect placement of some windows using" + " the window-specific settings.</li>" + " <li><em>Cascade</em> will cascade the windows</li>" + " <li><em>Random</em> will use a random position</li>" + " <li><em>Centered</em> will place the window centered</li>" + " <li><em>Zero-Cornered</em> will place the window in the top-left corner</li>" + "</ul>") ; + + QWhatsThis::add( plcLabel, wtstr); + QWhatsThis::add( placementCombo, wtstr); + + plcLabel->setBuddy(placementCombo); + vLay->addWidget(plcLabel, 0); + vLay->addWidget(placementCombo, 1, Qt::AlignLeft); + + bLay->addSpacing(10); + + lay->addWidget(windowsBox); + + //iTLabel = new QLabel(i18n(" Allowed overlap:\n" + // "(% of desktop space)"), + // plcBox); + //iTLabel->setAlignment(AlignTop|AlignHCenter); + //pLay->addWidget(iTLabel,1,1); + + //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); + //pLay->addWidget(interactiveTrigger,1,2); + + //pLay->addRowSpacing(2,KDialog::spacingHint()); + + //lay->addWidget(plcBox); + + + //CT 15mar98 - add EdgeResistance, BorderAttractor, WindowsAttractor config + MagicBox = new QVButtonGroup(i18n("Snap Zones"), this); + MagicBox->setMargin(15); + + BrdrSnap = new KIntNumInput(10, MagicBox); + BrdrSnap->setSpecialValueText( i18n("none") ); + BrdrSnap->setRange( 0, MAX_BRDR_SNAP); + BrdrSnap->setLabel(i18n("&Border snap zone:")); + BrdrSnap->setSteps(1,10); + QWhatsThis::add( BrdrSnap, i18n("Here you can set the snap zone for screen borders, i.e." + " the 'strength' of the magnetic field which will make windows snap to the border when" + " moved near it.") ); + + WndwSnap = new KIntNumInput(10, MagicBox); + WndwSnap->setSpecialValueText( i18n("none") ); + WndwSnap->setRange( 0, MAX_WNDW_SNAP); + WndwSnap->setLabel(i18n("&Window snap zone:")); + BrdrSnap->setSteps(1,10); + QWhatsThis::add( WndwSnap, i18n("Here you can set the snap zone for windows, i.e." + " the 'strength' of the magnetic field which will make windows snap to each other when" + " they're moved near another window.") ); + + OverlapSnap=new QCheckBox(i18n("Snap windows onl&y when overlapping"),MagicBox); + QWhatsThis::add( OverlapSnap, i18n("Here you can set that windows will be only" + " snapped if you try to overlap them, i.e. they will not be snapped if the windows" + " comes only near another window or border.") ); + + lay->addWidget(MagicBox); + lay->addStretch(); + + load(); + + // Any changes goes to slotChanged() + connect( opaque, SIGNAL(clicked()), SLOT(changed())); + connect( resizeOpaqueOn, SIGNAL(clicked()), SLOT(changed())); + connect( geometryTipOn, SIGNAL(clicked()), SLOT(changed())); + connect( minimizeAnimOn, SIGNAL(clicked() ), SLOT(changed())); + connect( minimizeAnimSlider, SIGNAL(valueChanged(int)), SLOT(changed())); + connect( moveResizeMaximized, SIGNAL(toggled(bool)), SLOT(changed())); + connect( placementCombo, SIGNAL(activated(int)), SLOT(changed())); + connect( BrdrSnap, SIGNAL(valueChanged(int)), SLOT(changed())); + connect( BrdrSnap, SIGNAL(valueChanged(int)), SLOT(slotBrdrSnapChanged(int))); + connect( WndwSnap, SIGNAL(valueChanged(int)), SLOT(changed())); + connect( WndwSnap, SIGNAL(valueChanged(int)), SLOT(slotWndwSnapChanged(int))); + connect( OverlapSnap, SIGNAL(clicked()), SLOT(changed())); + + // To get suffix to BrdrSnap and WndwSnap inputs with default values. + slotBrdrSnapChanged(BrdrSnap->value()); + slotWndwSnapChanged(WndwSnap->value()); +} + +int KMovingConfig::getMove() +{ + return (opaque->isChecked())? OPAQUE : TRANSPARENT; +} + +void KMovingConfig::setMove(int trans) +{ + opaque->setChecked(trans == OPAQUE); +} + +void KMovingConfig::setGeometryTip(bool showGeometryTip) +{ + geometryTipOn->setChecked(showGeometryTip); +} + +bool KMovingConfig::getGeometryTip() +{ + return geometryTipOn->isChecked(); +} + +// placement policy --- CT 31jan98 --- +int KMovingConfig::getPlacement() +{ + return placementCombo->currentItem(); +} + +void KMovingConfig::setPlacement(int plac) +{ + placementCombo->setCurrentItem(plac); +} + +bool KMovingConfig::getMinimizeAnim() +{ + return minimizeAnimOn->isChecked(); +} + +int KMovingConfig::getMinimizeAnimSpeed() +{ + return minimizeAnimSlider->value(); +} + +void KMovingConfig::setMinimizeAnim(bool anim) +{ + minimizeAnimOn->setChecked( anim ); + minimizeAnimSlider->setEnabled( anim ); + minimizeAnimSlowLabel->setEnabled( anim ); + minimizeAnimFastLabel->setEnabled( anim ); +} + +void KMovingConfig::setMinimizeAnimSpeed(int speed) +{ + minimizeAnimSlider->setValue(speed); +} + +int KMovingConfig::getResizeOpaque() +{ + return (resizeOpaqueOn->isChecked())? RESIZE_OPAQUE : RESIZE_TRANSPARENT; +} + +void KMovingConfig::setResizeOpaque(int opaque) +{ + resizeOpaqueOn->setChecked(opaque == RESIZE_OPAQUE); +} + +void KMovingConfig::setMoveResizeMaximized(bool a) { + moveResizeMaximized->setChecked(a); +} + +void KMovingConfig::slotBrdrSnapChanged(int value) { + BrdrSnap->setSuffix(i18n(" pixel", " pixels", value)); +} + +void KMovingConfig::slotWndwSnapChanged(int value) { + WndwSnap->setSuffix(i18n(" pixel", " pixels", value)); +} + +void KMovingConfig::load( void ) +{ + QString key; + + config->setGroup( "Windows" ); + + key = config->readEntry(KWIN_MOVE, "Opaque"); + if( key == "Transparent") + setMove(TRANSPARENT); + else if( key == "Opaque") + setMove(OPAQUE); + + //CT 17Jun1998 - variable animation speed from 0 (none!!) to 10 (max) + bool anim = config->readBoolEntry(KWIN_MINIMIZE_ANIM, true ); + int animSpeed = config->readNumEntry(KWIN_MINIMIZE_ANIM_SPEED, 5); + if( animSpeed < 1 ) animSpeed = 0; + if( animSpeed > 10 ) animSpeed = 10; + setMinimizeAnim( anim ); + setMinimizeAnimSpeed( animSpeed ); + + // DF: please keep the default consistent with kwin (options.cpp line 145) + key = config->readEntry(KWIN_RESIZE_OPAQUE, "Opaque"); + if( key == "Opaque") + setResizeOpaque(RESIZE_OPAQUE); + else if ( key == "Transparent") + setResizeOpaque(RESIZE_TRANSPARENT); + + //KS 10Jan2003 - Geometry Tip during window move/resize + bool showGeomTip = config->readBoolEntry(KWIN_GEOMETRY, false); + setGeometryTip( showGeomTip ); + + // placement policy --- CT 19jan98 --- + key = config->readEntry(KWIN_PLACEMENT); + //CT 13mar98 interactive placement +// if( key.left(11) == "interactive") { +// setPlacement(INTERACTIVE_PLACEMENT); +// int comma_pos = key.find(','); +// if (comma_pos < 0) +// interactiveTrigger->setValue(0); +// else +// interactiveTrigger->setValue (key.right(key.length() +// - comma_pos).toUInt(0)); +// iTLabel->setEnabled(true); +// interactiveTrigger->show(); +// } +// else { +// interactiveTrigger->setValue(0); +// iTLabel->setEnabled(false); +// interactiveTrigger->hide(); + if( key == "Random") + setPlacement(RANDOM_PLACEMENT); + else if( key == "Cascade") + setPlacement(CASCADE_PLACEMENT); //CT 31jan98 + //CT 31mar98 manual placement + else if( key == "manual") + setPlacement(MANUAL_PLACEMENT); + else if( key == "Centered") + setPlacement(CENTERED_PLACEMENT); + else if( key == "ZeroCornered") + setPlacement(ZEROCORNERED_PLACEMENT); + else if( key == "Maximizing") + setPlacement(MAXIMIZING_PLACEMENT); + else + setPlacement(SMART_PLACEMENT); +// } + + setMoveResizeMaximized(config->readBoolEntry(KWIN_MOVE_RESIZE_MAXIMIZED, false)); + + int v; + + v = config->readNumEntry(KWM_BRDR_SNAP_ZONE, KWM_BRDR_SNAP_ZONE_DEFAULT); + if (v > MAX_BRDR_SNAP) setBorderSnapZone(MAX_BRDR_SNAP); + else if (v < 0) setBorderSnapZone (0); + else setBorderSnapZone(v); + + v = config->readNumEntry(KWM_WNDW_SNAP_ZONE, KWM_WNDW_SNAP_ZONE_DEFAULT); + if (v > MAX_WNDW_SNAP) setWindowSnapZone(MAX_WNDW_SNAP); + else if (v < 0) setWindowSnapZone (0); + else setWindowSnapZone(v); + + OverlapSnap->setChecked(config->readBoolEntry("SnapOnlyWhenOverlapping",false)); + emit KCModule::changed(false); +} + +void KMovingConfig::save( void ) +{ + int v; + + config->setGroup( "Windows" ); + + v = getMove(); + if (v == TRANSPARENT) + config->writeEntry(KWIN_MOVE,"Transparent"); + else + config->writeEntry(KWIN_MOVE,"Opaque"); + + config->writeEntry(KWIN_GEOMETRY, getGeometryTip()); + + // placement policy --- CT 31jan98 --- + v =getPlacement(); + if (v == RANDOM_PLACEMENT) + config->writeEntry(KWIN_PLACEMENT, "Random"); + else if (v == CASCADE_PLACEMENT) + config->writeEntry(KWIN_PLACEMENT, "Cascade"); + else if (v == CENTERED_PLACEMENT) + config->writeEntry(KWIN_PLACEMENT, "Centered"); + else if (v == ZEROCORNERED_PLACEMENT) + config->writeEntry(KWIN_PLACEMENT, "ZeroCornered"); + else if (v == MAXIMIZING_PLACEMENT) + config->writeEntry(KWIN_PLACEMENT, "Maximizing"); +//CT 13mar98 manual and interactive placement +// else if (v == MANUAL_PLACEMENT) +// config->writeEntry(KWIN_PLACEMENT, "Manual"); +// else if (v == INTERACTIVE_PLACEMENT) { +// QString tmpstr = QString("Interactive,%1").arg(interactiveTrigger->value()); +// config->writeEntry(KWIN_PLACEMENT, tmpstr); +// } + else + config->writeEntry(KWIN_PLACEMENT, "Smart"); + + config->writeEntry(KWIN_MINIMIZE_ANIM, getMinimizeAnim()); + config->writeEntry(KWIN_MINIMIZE_ANIM_SPEED, getMinimizeAnimSpeed()); + + v = getResizeOpaque(); + if (v == RESIZE_OPAQUE) + config->writeEntry(KWIN_RESIZE_OPAQUE, "Opaque"); + else + config->writeEntry(KWIN_RESIZE_OPAQUE, "Transparent"); + + config->writeEntry(KWIN_MOVE_RESIZE_MAXIMIZED, moveResizeMaximized->isChecked()); + + + config->writeEntry(KWM_BRDR_SNAP_ZONE,getBorderSnapZone()); + config->writeEntry(KWM_WNDW_SNAP_ZONE,getWindowSnapZone()); + config->writeEntry("SnapOnlyWhenOverlapping",OverlapSnap->isChecked()); + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } + emit KCModule::changed(false); +} + +void KMovingConfig::defaults() +{ + setMove(OPAQUE); + setResizeOpaque(RESIZE_TRANSPARENT); + setGeometryTip(false); + setPlacement(SMART_PLACEMENT); + setMoveResizeMaximized(false); + + //copied from kcontrol/konq/kwindesktop, aleXXX + setWindowSnapZone(KWM_WNDW_SNAP_ZONE_DEFAULT); + setBorderSnapZone(KWM_BRDR_SNAP_ZONE_DEFAULT); + OverlapSnap->setChecked(false); + + setMinimizeAnim( true ); + setMinimizeAnimSpeed( 5 ); + emit KCModule::changed(true); +} + +int KMovingConfig::getBorderSnapZone() { + return BrdrSnap->value(); +} + +void KMovingConfig::setBorderSnapZone(int pxls) { + BrdrSnap->setValue(pxls); +} + +int KMovingConfig::getWindowSnapZone() { + return WndwSnap->value(); +} + +void KMovingConfig::setWindowSnapZone(int pxls) { + WndwSnap->setValue(pxls); +} + +KTranslucencyConfig::~KTranslucencyConfig () +{ + if (standAlone) + delete config; + if (kompmgr) + kompmgr->detach(); +} + +KTranslucencyConfig::KTranslucencyConfig (bool _standAlone, KConfig *_config, QWidget *parent, const char *) + : KCModule(parent, "kcmkwm"), config(_config), standAlone(_standAlone) +{ + kompmgr = 0L; + resetKompmgr_ = FALSE; + QVBoxLayout *lay = new QVBoxLayout (this); + kompmgrAvailable_ = kompmgrAvailable(); + if (!kompmgrAvailable_){ + KActiveLabel *label = new KActiveLabel(i18n("<qt><b>It seems that alpha channel support is not available.</b><br><br>" + "Please make sure you have " + "<a href=\"http://www.freedesktop.org/\">Xorg ≥ 6.8</a>," + " and installed the kompmgr that came with kwin.<br>" + "Also, make sure you have the following entries in your XConfig (e.g. /etc/X11/xorg.conf):<br><br>" + "<i>Section \"Extensions\"<br>" + "Option \"Composite\" \"Enable\"<br>" + "EndSection</i><br><br>" + "And if your GPU provides hardware-accelerated Xrender support (mainly nVidia cards):<br><br>" + "<i>Option \"RenderAccel\" \"true\"</i><br>" + "In <i>Section \"Device\"</i></qt>"), this); + lay->addWidget(label); + } + else + { + QTabWidget *tabW = new QTabWidget(this); + QWidget *tGroup = new QWidget(tabW); + QVBoxLayout *vLay = new QVBoxLayout (tGroup,KDialog::marginHint(), KDialog::spacingHint()); + vLay->addSpacing(11); // to get the proper gb top offset + + onlyDecoTranslucent = new QCheckBox(i18n("Apply translucency only to decoration"),tGroup); + vLay->addWidget(onlyDecoTranslucent); + + vLay->addSpacing(11); + + QGridLayout *gLay = new QGridLayout(vLay,4,2,KDialog::spacingHint()); + gLay->setColStretch(1,1); + + activeWindowTransparency = new QCheckBox(i18n("Active windows:"),tGroup); + gLay->addWidget(activeWindowTransparency,0,0); + activeWindowOpacity = new KIntNumInput(100, tGroup); + activeWindowOpacity->setRange(0,100); + activeWindowOpacity->setSuffix("%"); + gLay->addWidget(activeWindowOpacity,0,1); + + inactiveWindowTransparency = new QCheckBox(i18n("Inactive windows:"),tGroup); + gLay->addWidget(inactiveWindowTransparency,1,0); + inactiveWindowOpacity = new KIntNumInput(100, tGroup); + inactiveWindowOpacity->setRange(0,100); + inactiveWindowOpacity->setSuffix("%"); + gLay->addWidget(inactiveWindowOpacity,1,1); + + movingWindowTransparency = new QCheckBox(i18n("Moving windows:"),tGroup); + gLay->addWidget(movingWindowTransparency,2,0); + movingWindowOpacity = new KIntNumInput(100, tGroup); + movingWindowOpacity->setRange(0,100); + movingWindowOpacity->setSuffix("%"); + gLay->addWidget(movingWindowOpacity,2,1); + + dockWindowTransparency = new QCheckBox(i18n("Dock windows:"),tGroup); + gLay->addWidget(dockWindowTransparency,3,0); + dockWindowOpacity = new KIntNumInput(100, tGroup); + dockWindowOpacity->setRange(0,100); + dockWindowOpacity->setSuffix("%"); + gLay->addWidget(dockWindowOpacity,3,1); + + vLay->addSpacing(11); + + keepAboveAsActive = new QCheckBox(i18n("Treat 'keep above' windows as active ones"),tGroup); + vLay->addWidget(keepAboveAsActive); + + disableARGB = new QCheckBox(i18n("Disable ARGB windows (ignores window alpha maps, fixes gtk1 apps)"),tGroup); + vLay->addWidget(disableARGB); + + vLay->addStretch(); + tabW->addTab(tGroup, i18n("Opacity")); + + QWidget *sGroup = new QWidget(tabW); +// sGroup->setCheckable(TRUE); + QVBoxLayout *vLay2 = new QVBoxLayout (sGroup,11,6); + vLay2->addSpacing(11); // to get the proper gb top offset + useShadows = new QCheckBox(i18n("Use shadows"),sGroup); + vLay2->addWidget(useShadows); + + vLay2->addSpacing(11); + + QGridLayout *gLay2 = new QGridLayout(vLay2,6,2); + gLay2->setColStretch(1,1); + + QLabel *label1 = new QLabel(i18n("Active window size:"),sGroup); + gLay2->addWidget(label1,0,0); + activeWindowShadowSize = new KIntNumInput(12,sGroup); + activeWindowShadowSize->setRange(0,32); +// activeWindowShadowSize->setSuffix("px"); + gLay2->addWidget(activeWindowShadowSize,0,1); + + QLabel *label2 = new QLabel(i18n("Inactive window size:"),sGroup); + gLay2->addWidget(label2,1,0); + inactiveWindowShadowSize = new KIntNumInput(6,sGroup); + inactiveWindowShadowSize->setRange(0,32); +// inactiveWindowShadowSize->setSuffix("px"); + gLay2->addWidget(inactiveWindowShadowSize,1,1); + + QLabel *label3 = new QLabel(i18n("Dock window size:"),sGroup); + gLay2->addWidget(label3,2,0); + dockWindowShadowSize = new KIntNumInput(6,sGroup); + dockWindowShadowSize->setRange(0,32); +// dockWindowShadowSize->setSuffix("px"); + gLay2->addWidget(dockWindowShadowSize,2,1); + + QLabel *label4 = new QLabel(i18n("Vertical offset:"),sGroup); + gLay2->addWidget(label4,3,0); + shadowTopOffset = new KIntNumInput(80,sGroup); + shadowTopOffset->setSuffix("%"); + shadowTopOffset->setRange(-200,200); + gLay2->addWidget(shadowTopOffset,3,1); + + QLabel *label5 = new QLabel(i18n("Horizontal offset:"),sGroup); + gLay2->addWidget(label5,4,0); + shadowLeftOffset = new KIntNumInput(0,sGroup); + shadowLeftOffset->setSuffix("%"); + shadowLeftOffset->setRange(-200,200); + gLay2->addWidget(shadowLeftOffset,4,1); + + QLabel *label6 = new QLabel(i18n("Shadow color:"),sGroup); + gLay2->addWidget(label6,5,0); + shadowColor = new KColorButton(Qt::black,sGroup); + gLay2->addWidget(shadowColor,5,1); + gLay2->setColStretch(1,1); + vLay2->addSpacing(11); + removeShadowsOnMove = new QCheckBox(i18n("Remove shadows on move"),sGroup); + vLay2->addWidget(removeShadowsOnMove); + removeShadowsOnResize = new QCheckBox(i18n("Remove shadows on resize"),sGroup); + vLay2->addWidget(removeShadowsOnResize); + vLay2->addStretch(); + tabW->addTab(sGroup, i18n("Shadows")); + + QWidget *eGroup = new QWidget(this); + QVBoxLayout *vLay3 = new QVBoxLayout (eGroup,11,6); + + fadeInWindows = new QCheckBox(i18n("Fade-in windows (including popups)"),eGroup); + fadeOnOpacityChange = new QCheckBox(i18n("Fade between opacity changes"),eGroup); + fadeInSpeed = new KIntNumInput(100, eGroup); + fadeInSpeed->setRange(1,100); + fadeInSpeed->setLabel(i18n("Fade-in speed:")); + fadeOutSpeed = new KIntNumInput(100, eGroup); + fadeOutSpeed->setRange(1,100); + fadeOutSpeed->setLabel(i18n("Fade-out speed:")); + vLay3->addWidget(fadeInWindows); + vLay3->addWidget(fadeOnOpacityChange); + vLay3->addWidget(fadeInSpeed); + vLay3->addWidget(fadeOutSpeed); + vLay3->addStretch(); + + tabW->addTab(eGroup, i18n("Effects")); + + useTranslucency = new QCheckBox(i18n("Use translucency/shadows"),this); + lay->addWidget(useTranslucency); + lay->addWidget(tabW); + + connect(useTranslucency, SIGNAL(toggled(bool)), tabW, SLOT(setEnabled(bool))); + + connect(activeWindowTransparency, SIGNAL(toggled(bool)), activeWindowOpacity, SLOT(setEnabled(bool))); + connect(inactiveWindowTransparency, SIGNAL(toggled(bool)), inactiveWindowOpacity, SLOT(setEnabled(bool))); + connect(movingWindowTransparency, SIGNAL(toggled(bool)), movingWindowOpacity, SLOT(setEnabled(bool))); + connect(dockWindowTransparency, SIGNAL(toggled(bool)), dockWindowOpacity, SLOT(setEnabled(bool))); + + connect(useTranslucency, SIGNAL(toggled(bool)), SLOT(changed())); + connect(onlyDecoTranslucent, SIGNAL(toggled(bool)), SLOT(changed())); + connect(activeWindowTransparency, SIGNAL(toggled(bool)), SLOT(changed())); + connect(inactiveWindowTransparency, SIGNAL(toggled(bool)), SLOT(changed())); + connect(movingWindowTransparency, SIGNAL(toggled(bool)), SLOT(changed())); + connect(dockWindowTransparency, SIGNAL(toggled(bool)), SLOT(changed())); + connect(keepAboveAsActive, SIGNAL(toggled(bool)), SLOT(changed())); + connect(disableARGB, SIGNAL(toggled(bool)), SLOT(changed())); + connect(useShadows, SIGNAL(toggled(bool)), SLOT(changed())); + connect(removeShadowsOnResize, SIGNAL(toggled(bool)), SLOT(changed())); + connect(removeShadowsOnMove, SIGNAL(toggled(bool)), SLOT(changed())); + + connect(activeWindowOpacity, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(inactiveWindowOpacity, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(movingWindowOpacity, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(dockWindowOpacity, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(dockWindowShadowSize, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(activeWindowShadowSize, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(inactiveWindowShadowSize, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(shadowTopOffset, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(shadowLeftOffset, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(shadowColor, SIGNAL(changed(const QColor&)), SLOT(changed())); + connect(fadeInWindows, SIGNAL(toggled(bool)), SLOT(changed())); + connect(fadeOnOpacityChange, SIGNAL(toggled(bool)), SLOT(changed())); + connect(fadeInSpeed, SIGNAL(valueChanged(int)), SLOT(changed())); + connect(fadeOutSpeed, SIGNAL(valueChanged(int)), SLOT(changed())); + + connect(useShadows, SIGNAL(toggled(bool)), dockWindowShadowSize, SLOT(setEnabled(bool))); + connect(useShadows, SIGNAL(toggled(bool)), activeWindowShadowSize, SLOT(setEnabled(bool))); + connect(useShadows, SIGNAL(toggled(bool)), inactiveWindowShadowSize, SLOT(setEnabled(bool))); + connect(useShadows, SIGNAL(toggled(bool)), shadowTopOffset, SLOT(setEnabled(bool))); + connect(useShadows, SIGNAL(toggled(bool)), shadowLeftOffset, SLOT(setEnabled(bool))); + connect(useShadows, SIGNAL(toggled(bool)), shadowColor, SLOT(setEnabled(bool))); + + load(); + + tabW->setEnabled(useTranslucency->isChecked()); + + connect(useTranslucency, SIGNAL(toggled(bool)), this, SLOT(showWarning(bool))); + + // handle kompmgr restarts if necessary + connect(useTranslucency, SIGNAL(toggled(bool)), SLOT(resetKompmgr())); + connect(disableARGB, SIGNAL(toggled(bool)), SLOT(resetKompmgr())); + connect(useShadows, SIGNAL(toggled(bool)), SLOT(resetKompmgr())); + connect(inactiveWindowShadowSize, SIGNAL(valueChanged(int)), SLOT(resetKompmgr())); + connect(shadowTopOffset, SIGNAL(valueChanged(int)), SLOT(resetKompmgr())); + connect(shadowLeftOffset, SIGNAL(valueChanged(int)), SLOT(resetKompmgr())); + connect(shadowColor, SIGNAL(changed(const QColor&)), SLOT(resetKompmgr())); + connect(fadeInWindows, SIGNAL(toggled(bool)), SLOT(resetKompmgr())); + connect(fadeOnOpacityChange, SIGNAL(toggled(bool)), SLOT(resetKompmgr())); + connect(fadeInSpeed, SIGNAL(valueChanged(int)), SLOT(resetKompmgr())); + connect(fadeOutSpeed, SIGNAL(valueChanged(int)), SLOT(resetKompmgr())); + + } +} + +void KTranslucencyConfig::resetKompmgr() +{ + resetKompmgr_ = TRUE; +} +void KTranslucencyConfig::load( void ) +{ + + if (!kompmgrAvailable_) + return; + config->setGroup( "Notification Messages" ); + useTranslucency->setChecked(config->readBoolEntry("UseTranslucency",false)); + + config->setGroup( "Translucency" ); + activeWindowTransparency->setChecked(config->readBoolEntry("TranslucentActiveWindows",false)); + inactiveWindowTransparency->setChecked(config->readBoolEntry("TranslucentInactiveWindows",true)); + movingWindowTransparency->setChecked(config->readBoolEntry("TranslucentMovingWindows",false)); + removeShadowsOnMove->setChecked(config->readBoolEntry("RemoveShadowsOnMove",false)); + removeShadowsOnResize->setChecked(config->readBoolEntry("RemoveShadowsOnResize",false)); + dockWindowTransparency->setChecked(config->readBoolEntry("TranslucentDocks",true)); + keepAboveAsActive->setChecked(config->readBoolEntry("TreatKeepAboveAsActive",true)); + onlyDecoTranslucent->setChecked(config->readBoolEntry("OnlyDecoTranslucent",false)); + + activeWindowOpacity->setValue(config->readNumEntry("ActiveWindowOpacity",100)); + inactiveWindowOpacity->setValue(config->readNumEntry("InactiveWindowOpacity",75)); + movingWindowOpacity->setValue(config->readNumEntry("MovingWindowOpacity",25)); + dockWindowOpacity->setValue(config->readNumEntry("DockOpacity",80)); + + int ass, iss, dss; + dss = config->readNumEntry("DockShadowSize", 33); + ass = config->readNumEntry("ActiveWindowShadowSize", 133); + iss = config->readNumEntry("InactiveWindowShadowSize", 67); + + activeWindowOpacity->setEnabled(activeWindowTransparency->isChecked()); + inactiveWindowOpacity->setEnabled(inactiveWindowTransparency->isChecked()); + movingWindowOpacity->setEnabled(movingWindowTransparency->isChecked()); + dockWindowOpacity->setEnabled(dockWindowTransparency->isChecked()); + + KConfig conf_(QDir::homeDirPath() + "/.xcompmgrrc"); + conf_.setGroup("xcompmgr"); + + disableARGB->setChecked(conf_.readBoolEntry("DisableARGB",FALSE)); + + useShadows->setChecked(conf_.readEntry("Compmode","CompClientShadows").compare("CompClientShadows") == 0); + shadowTopOffset->setValue(-1*(conf_.readNumEntry("ShadowOffsetY",-80))); + shadowLeftOffset->setValue(-1*(conf_.readNumEntry("ShadowOffsetX",0))); + + int ss = conf_.readNumEntry("ShadowRadius",6); + dockWindowShadowSize->setValue((int)(dss*ss/100.0)); + activeWindowShadowSize->setValue((int)(ass*ss/100.0)); + inactiveWindowShadowSize->setValue((int)(iss*ss/100.0)); + + QString hex = conf_.readEntry("ShadowColor","#000000"); + uint r, g, b; + r = g = b = 256; + + if (sscanf(hex.latin1(), "0x%02x%02x%02x", &r, &g, &b)!=3 || r > 255 || g > 255 || b > 255) + shadowColor->setColor(Qt::black); + else + shadowColor->setColor(QColor(r,g,b)); + + fadeInWindows->setChecked(conf_.readBoolEntry("FadeWindows",TRUE)); + fadeOnOpacityChange->setChecked(conf_.readBoolEntry("FadeTrans",FALSE)); + fadeInSpeed->setValue((int)(conf_.readDoubleNumEntry("FadeInStep",0.020)*1000.0)); + fadeOutSpeed->setValue((int)(conf_.readDoubleNumEntry("FadeOutStep",0.070)*1000.0)); + + emit KCModule::changed(false); +} + +void KTranslucencyConfig::save( void ) +{ + if (!kompmgrAvailable_) + return; + config->setGroup( "Notification Messages" ); + config->writeEntry("UseTranslucency",useTranslucency->isChecked()); + + config->setGroup( "Translucency" ); + config->writeEntry("TranslucentActiveWindows",activeWindowTransparency->isChecked()); + config->writeEntry("TranslucentInactiveWindows",inactiveWindowTransparency->isChecked()); + config->writeEntry("TranslucentMovingWindows",movingWindowTransparency->isChecked()); + config->writeEntry("TranslucentDocks",dockWindowTransparency->isChecked()); + config->writeEntry("TreatKeepAboveAsActive",keepAboveAsActive->isChecked()); + config->writeEntry("ActiveWindowOpacity",activeWindowOpacity->value()); + config->writeEntry("InactiveWindowOpacity",inactiveWindowOpacity->value()); + config->writeEntry("MovingWindowOpacity",movingWindowOpacity->value()); + config->writeEntry("DockOpacity",dockWindowOpacity->value()); + // for simplification: + // xcompmgr supports a general shadow radius and additionally lets external apps set a multiplicator for each window + // (speed reasons, so the shadow matrix hasn't to be recreated for every window) + // we set inactive windows to 100%, the radius to the inactive window value and adjust the multiplicators for docks and active windows + // this way the user can set the three values without caring about the radius/multiplicator stuff + // additionally we find a value between big and small values to have a more smooth appereance + config->writeEntry("DockShadowSize",(int)(200.0 * dockWindowShadowSize->value() / (activeWindowShadowSize->value() + inactiveWindowShadowSize->value()))); + config->writeEntry("ActiveWindowShadowSize",(int)(200.0 * activeWindowShadowSize->value() / (activeWindowShadowSize->value() + inactiveWindowShadowSize->value()))); + config->writeEntry("InactiveWindowShadowSize",(int)(200.0 * inactiveWindowShadowSize->value() / (activeWindowShadowSize->value() + inactiveWindowShadowSize->value()))); + + config->writeEntry("RemoveShadowsOnMove",removeShadowsOnMove->isChecked()); + config->writeEntry("RemoveShadowsOnResize",removeShadowsOnResize->isChecked()); + config->writeEntry("OnlyDecoTranslucent", onlyDecoTranslucent->isChecked()); + config->writeEntry("ResetKompmgr",resetKompmgr_); + + KConfig *conf_ = new KConfig(QDir::homeDirPath() + "/.xcompmgrrc"); + conf_->setGroup("xcompmgr"); + + conf_->writeEntry("Compmode",useShadows->isChecked()?"CompClientShadows":""); + conf_->writeEntry("DisableARGB",disableARGB->isChecked()); + conf_->writeEntry("ShadowOffsetY",-1*shadowTopOffset->value()); + conf_->writeEntry("ShadowOffsetX",-1*shadowLeftOffset->value()); + + + int r, g, b; + shadowColor->color().rgb( &r, &g, &b ); + QString hex; + hex.sprintf("0x%02X%02X%02X", r,g,b); + conf_->writeEntry("ShadowColor",hex); + conf_->writeEntry("ShadowRadius",(activeWindowShadowSize->value() + inactiveWindowShadowSize->value()) / 2); + conf_->writeEntry("FadeWindows",fadeInWindows->isChecked()); + conf_->writeEntry("FadeTrans",fadeOnOpacityChange->isChecked()); + conf_->writeEntry("FadeInStep",fadeInSpeed->value()/1000.0); + conf_->writeEntry("FadeOutStep",fadeOutSpeed->value()/1000.0); + + delete conf_; + + if (standAlone) + { + config->sync(); + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } + emit KCModule::changed(false); +} + +void KTranslucencyConfig::defaults() +{ + if (!kompmgrAvailable_) + return; + useTranslucency->setChecked(false); + onlyDecoTranslucent->setChecked(false); + activeWindowTransparency->setChecked(false); + inactiveWindowTransparency->setChecked(true); + movingWindowTransparency->setChecked(false); + dockWindowTransparency->setChecked(true); + keepAboveAsActive->setChecked(true); + disableARGB->setChecked(false); + + activeWindowOpacity->setValue(100); + inactiveWindowOpacity->setValue(75); + movingWindowOpacity->setValue(25); + dockWindowOpacity->setValue(80); + + dockWindowShadowSize->setValue(6); + activeWindowShadowSize->setValue(12); + inactiveWindowShadowSize->setValue(6); + shadowTopOffset->setValue(80); + shadowLeftOffset->setValue(0); + + activeWindowOpacity->setEnabled(false); + inactiveWindowOpacity->setEnabled(true); + movingWindowOpacity->setEnabled(false); + dockWindowOpacity->setEnabled(true); + useShadows->setChecked(TRUE); + removeShadowsOnMove->setChecked(FALSE); + removeShadowsOnResize->setChecked(FALSE); + shadowColor->setColor(Qt::black); + fadeInWindows->setChecked(TRUE); + fadeOnOpacityChange->setChecked(FALSE); + fadeInSpeed->setValue(70); + fadeOutSpeed->setValue(20); + emit KCModule::changed(true); +} + + +bool KTranslucencyConfig::kompmgrAvailable() +{ + bool ret; + KProcess proc; + proc << "kompmgr" << "-v"; + ret = proc.start(KProcess::DontCare, KProcess::AllOutput); + proc.detach(); + return ret; +} + +void KTranslucencyConfig::showWarning(bool alphaActivated) +{ + if (alphaActivated) + KMessageBox::information(this, i18n("<qt>Translucency support is new and may cause problems<br> including crashes (sometimes the translucency engine, seldom even X).</qt>"), i18n("Warning")); +} + +#include "windows.moc" diff --git a/kwin/kcmkwin/kwinoptions/windows.h b/kwin/kcmkwin/kwinoptions/windows.h new file mode 100644 index 000000000..60a4d69f0 --- /dev/null +++ b/kwin/kcmkwin/kwinoptions/windows.h @@ -0,0 +1,289 @@ +/* + * windows.h + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * Copyright (c) 2001 Waldo Bastian bastian@kde.org + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KWINDOWCONFIG_H__ +#define __KWINDOWCONFIG_H__ + +#include <qwidget.h> +#include <kcmodule.h> +#include <config.h> + +class QRadioButton; +class QCheckBox; +class QPushButton; +class QComboBox; +class QGroupBox; +class QLabel; +class QSlider; +class QButtonGroup; +class QSpinBox; +class QVButtonGroup; + +class KColorButton; +class KIntNumInput; + +#define TRANSPARENT 0 +#define OPAQUE 1 + +#define CLICK_TO_FOCUS 0 +#define FOCUS_FOLLOW_MOUSE 1 + +#define TITLEBAR_PLAIN 0 +#define TITLEBAR_SHADED 1 + +#define RESIZE_TRANSPARENT 0 +#define RESIZE_OPAQUE 1 + +#define SMART_PLACEMENT 0 +#define MAXIMIZING_PLACEMENT 1 +#define CASCADE_PLACEMENT 2 +#define RANDOM_PLACEMENT 3 +#define CENTERED_PLACEMENT 4 +#define ZEROCORNERED_PLACEMENT 5 +#define INTERACTIVE_PLACEMENT 6 +#define MANUAL_PLACEMENT 7 + +#define CLICK_TO_FOCUS 0 +#define FOCUS_FOLLOWS_MOUSE 1 +#define FOCUS_UNDER_MOUSE 2 +#define FOCUS_STRICTLY_UNDER_MOUSE 3 + +class QSpinBox; + +class KFocusConfig : public KCModule +{ + Q_OBJECT +public: + KFocusConfig( bool _standAlone, KConfig *_config, QWidget *parent=0, const char* name=0 ); + ~KFocusConfig(); + + void load(); + void save(); + void defaults(); + +private slots: + void setDelayFocusEnabled(); + void setAutoRaiseEnabled(); + void autoRaiseOnTog(bool);//CT 23Oct1998 + void delayFocusOnTog(bool); + void clickRaiseOnTog(bool); + void updateAltTabMode(); + void changed() { emit KCModule::changed(true); } + + +private: + + int getFocus( void ); + int getAutoRaiseInterval( void ); + int getDelayFocusInterval( void ); + + void setFocus(int); + void setAutoRaiseInterval(int); + void setAutoRaise(bool); + void setDelayFocusInterval(int); + void setDelayFocus(bool); + void setClickRaise(bool); + void setAltTabMode(bool); + void setTraverseAll(bool); + void setRollOverDesktops(bool); + void setShowPopupinfo(bool); + + QButtonGroup *fcsBox; + QComboBox *focusCombo; + QCheckBox *autoRaiseOn; + QCheckBox *delayFocusOn; + QCheckBox *clickRaiseOn; + KIntNumInput *autoRaise; + KIntNumInput *delayFocus; + + QButtonGroup *kbdBox; + QCheckBox *altTabPopup; + QCheckBox *traverseAll; + QCheckBox *rollOverDesktops; + QCheckBox *showPopupinfo; + + KConfig *config; + bool standAlone; +}; + +class KMovingConfig : public KCModule +{ + Q_OBJECT +public: + KMovingConfig( bool _standAlone, KConfig *config, QWidget *parent=0, const char* name=0 ); + ~KMovingConfig(); + + void load(); + void save(); + void defaults(); + +private slots: + void setMinimizeAnim( bool ); + void setMinimizeAnimSpeed( int ); + void changed() { emit KCModule::changed(true); } + void slotBrdrSnapChanged( int ); + void slotWndwSnapChanged( int ); + +private: + int getMove( void ); + bool getMinimizeAnim( void ); + int getMinimizeAnimSpeed( void ); + int getResizeOpaque ( void ); + bool getGeometryTip( void ); //KS + int getPlacement( void ); //CT + + void setMove(int); + void setResizeOpaque(int); + void setGeometryTip(bool); //KS + void setPlacement(int); //CT + void setMoveResizeMaximized(bool); + + QButtonGroup *windowsBox; + QCheckBox *opaque; + QCheckBox *resizeOpaqueOn; + QCheckBox *geometryTipOn; + QCheckBox* minimizeAnimOn; + QSlider *minimizeAnimSlider; + QLabel *minimizeAnimSlowLabel, *minimizeAnimFastLabel; + QCheckBox *moveResizeMaximized; + + QComboBox *placementCombo; + + KConfig *config; + bool standAlone; + + int getBorderSnapZone(); + void setBorderSnapZone( int ); + int getWindowSnapZone(); + void setWindowSnapZone( int ); + + QVButtonGroup *MagicBox; + KIntNumInput *BrdrSnap, *WndwSnap; + QCheckBox *OverlapSnap; + +}; + +class KAdvancedConfig : public KCModule +{ + Q_OBJECT +public: + KAdvancedConfig( bool _standAlone, KConfig *config, QWidget *parent=0, const char* name=0 ); + ~KAdvancedConfig(); + + void load(); + void save(); + void defaults(); + +private slots: + void shadeHoverChanged(bool); + + //copied from kcontrol/konq/kwindesktop, aleXXX + void setEBorders(); + + void changed() { emit KCModule::changed(true); } + +private: + + int getShadeHoverInterval (void ); + void setAnimateShade(bool); + void setShadeHover(bool); + void setShadeHoverInterval(int); + + QCheckBox *animateShade; + QButtonGroup *shBox; + QCheckBox *shadeHoverOn; + KIntNumInput *shadeHover; + + KConfig *config; + bool standAlone; + + int getElectricBorders( void ); + int getElectricBorderDelay(); + void setElectricBorders( int ); + void setElectricBorderDelay( int ); + + QVButtonGroup *electricBox; + QRadioButton *active_disable; + QRadioButton *active_move; + QRadioButton *active_always; + KIntNumInput *delays; + + void setFocusStealing( int ); + void setHideUtilityWindowsForInactive( bool ); + + QComboBox* focusStealing; + QCheckBox* hideUtilityWindowsForInactive; +}; + +class KProcess; +class KTranslucencyConfig : public KCModule +{ + Q_OBJECT +public: + KTranslucencyConfig( bool _standAlone, KConfig *config, QWidget *parent=0, const char* name=0 ); + ~KTranslucencyConfig(); + + void load(); + void save(); + void defaults(); + +private: + QCheckBox *useTranslucency; + QCheckBox *activeWindowTransparency; + QCheckBox *inactiveWindowTransparency; + QCheckBox *movingWindowTransparency; + QCheckBox *dockWindowTransparency; + QCheckBox *keepAboveAsActive; + QCheckBox *disableARGB; + QCheckBox *fadeInWindows; + QCheckBox *fadeOnOpacityChange; + QCheckBox *useShadows; + QCheckBox *removeShadowsOnResize; + QCheckBox *removeShadowsOnMove; + QGroupBox *sGroup; + QCheckBox *onlyDecoTranslucent; +// QPushButton *xcompmgrButton; + KIntNumInput *activeWindowOpacity; + KIntNumInput *inactiveWindowOpacity; + KIntNumInput *movingWindowOpacity; + KIntNumInput *dockWindowOpacity; + KIntNumInput *dockWindowShadowSize; + KIntNumInput *activeWindowShadowSize; + KIntNumInput *inactiveWindowShadowSize; + KIntNumInput *shadowTopOffset; + KIntNumInput *shadowLeftOffset; + KIntNumInput *fadeInSpeed; + KIntNumInput *fadeOutSpeed; + KColorButton *shadowColor; + KConfig *config; + bool standAlone; + bool alphaActivated; + bool resetKompmgr_; + bool kompmgrAvailable(); + bool kompmgrAvailable_; + KProcess *kompmgr; + +private slots: + void resetKompmgr(); + void showWarning(bool alphaActivated); + +}; +#endif diff --git a/kwin/kcmkwin/kwinrules/Makefile.am b/kwin/kcmkwin/kwinrules/Makefile.am new file mode 100644 index 000000000..18aef9ccd --- /dev/null +++ b/kwin/kcmkwin/kwinrules/Makefile.am @@ -0,0 +1,29 @@ +METASOURCES = AUTO +AM_CPPFLAGS = $(all_includes) -DKCMRULES +INCLUDES = -I$(top_srcdir)/kwin/lib + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kwin_rules_dialog.la +kde_module_LTLIBRARIES = kcm_kwinrules.la +noinst_LTLIBRARIES = libkwinrules.la + +libkwinrules_la_SOURCES = ruleswidget.cpp ruleslist.cpp kwinsrc.cpp detectwidget.cpp \ + ruleswidgetbase.ui ruleslistbase.ui detectwidgetbase.ui editshortcutbase.ui +libkwinrules_la_LDFLAGS = $(all_libraries) -no-undefined +libkwinrules_la_LIBADD = $(LIB_KDEUI) + +kcm_kwinrules_la_SOURCES = kcm.cpp +kcm_kwinrules_la_LDFLAGS = -module -avoid-version $(all_libraries) -no-undefined +kcm_kwinrules_la_LIBADD = libkwinrules.la +kcm_kwinrules_la_COMPILE_FIRST = ruleslistbase.h ruleswidgetbase.h editshortcutbase.h + +kwin_rules_dialog_la_SOURCES = main.cpp +kwin_rules_dialog_la_LDFLAGS = -module -avoid-version $(all_libraries) -no-undefined +kwin_rules_dialog_la_LIBADD = libkwinrules.la +kwin_rules_dialog_la_COMPILE_FIRST = ruleslistbase.h ruleswidgetbase.h editshortcutbase.h + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kcmkwinrules.pot + +xdg_apps_DATA = kwinrules.desktop diff --git a/kwin/kcmkwin/kwinrules/detectwidget.cpp b/kwin/kcmkwin/kwinrules/detectwidget.cpp new file mode 100644 index 000000000..1a8fa5af5 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/detectwidget.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "detectwidget.h" + +#include <kapplication.h> +#include <klocale.h> +#include <kdebug.h> +#include <kwin.h> +#include <qlabel.h> +#include <qradiobutton.h> +#include <qcheckbox.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <fixx11h.h> + +namespace KWinInternal +{ + +DetectWidget::DetectWidget( QWidget* parent, const char* name ) +: DetectWidgetBase( parent, name ) + { + } + +DetectDialog::DetectDialog( QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, "", Ok | Cancel ) +, grabber( NULL ) + { + widget = new DetectWidget( this ); + setMainWidget( widget ); + } + +void DetectDialog::detect( WId window ) + { + if( window == 0 ) + selectWindow(); + else + readWindow( window ); + } + +void DetectDialog::readWindow( WId w ) + { + if( w == 0 ) + { + emit detectionDone( false ); + return; + } + info = KWin::windowInfo( w, -1U, -1U ); // read everything + if( !info.valid()) + { + emit detectionDone( false ); + return; + } + wmclass_class = info.windowClassClass(); + wmclass_name = info.windowClassName(); + role = info.windowRole(); + type = info.windowType( NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask ); + title = info.name(); + extrarole = ""; // TODO + machine = info.clientMachine(); + executeDialog(); + } + +void DetectDialog::executeDialog() + { + static const char* const types[] = + { + I18N_NOOP( "Normal Window" ), + I18N_NOOP( "Desktop" ), + I18N_NOOP( "Dock (panel)" ), + I18N_NOOP( "Toolbar" ), + I18N_NOOP( "Torn-Off Menu" ), + I18N_NOOP( "Dialog Window" ), + I18N_NOOP( "Override Type" ), + I18N_NOOP( "Standalone Menubar" ), + I18N_NOOP( "Utility Window" ), + I18N_NOOP( "Splash Screen" ) + }; + widget->class_label->setText( wmclass_class + " (" + wmclass_name + ' ' + wmclass_class + ")" ); + widget->role_label->setText( role ); + widget->use_role->setEnabled( !role.isEmpty()); + if( widget->use_role->isEnabled()) + widget->use_role->setChecked( true ); + else + widget->use_whole_class->setChecked( true ); + if( type == NET::Unknown ) + widget->type_label->setText( i18n( "Unknown - will be treated as Normal Window" )); + else + widget->type_label->setText( i18n( types[ type ] )); + widget->title_label->setText( title ); + widget->extrarole_label->setText( extrarole ); + widget->machine_label->setText( machine ); + emit detectionDone( exec() == QDialog::Accepted ); + } + +QCString DetectDialog::selectedClass() const + { + if( widget->use_class->isChecked() || widget->use_role->isChecked()) + return wmclass_class; + return wmclass_name + ' ' + wmclass_class; + } + +bool DetectDialog::selectedWholeClass() const + { + return widget->use_whole_class->isChecked(); + } + +QCString DetectDialog::selectedRole() const + { + if( widget->use_role->isChecked()) + return role; + return ""; + } + +QString DetectDialog::selectedTitle() const + { + return title; + } + +Rules::StringMatch DetectDialog::titleMatch() const + { + return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch; + } + +bool DetectDialog::selectedWholeApp() const + { + return widget->use_class->isChecked(); + } + +NET::WindowType DetectDialog::selectedType() const + { + return type; + } + +QCString DetectDialog::selectedMachine() const + { + return machine; + } + +void DetectDialog::selectWindow() + { + // use a dialog, so that all user input is blocked + // use WX11BypassWM and moving away so that it's not actually visible + // grab only mouse, so that keyboard can be used e.g. for switching windows + grabber = new QDialog( NULL, NULL, true, WX11BypassWM ); + grabber->move( -1000, -1000 ); + grabber->show(); + grabber->grabMouse( crossCursor ); + grabber->installEventFilter( this ); + } + +bool DetectDialog::eventFilter( QObject* o, QEvent* e ) + { + if( o != grabber ) + return false; + if( e->type() != QEvent::MouseButtonRelease ) + return false; + delete grabber; + grabber = NULL; + if( static_cast< QMouseEvent* >( e )->button() != LeftButton ) + { + emit detectionDone( false ); + return true; + } + readWindow( findWindow()); + return true; + } + +WId DetectDialog::findWindow() + { + Window root; + Window child; + uint mask; + int rootX, rootY, x, y; + Window parent = qt_xrootwin(); + Atom wm_state = XInternAtom( qt_xdisplay(), "WM_STATE", False ); + for( int i = 0; + i < 10; + ++i ) + { + XQueryPointer( qt_xdisplay(), parent, &root, &child, + &rootX, &rootY, &x, &y, &mask ); + if( child == None ) + return 0; + Atom type; + int format; + unsigned long nitems, after; + unsigned char* prop; + if( XGetWindowProperty( qt_xdisplay(), child, wm_state, 0, 0, False, AnyPropertyType, + &type, &format, &nitems, &after, &prop ) == Success ) + { + if( prop != NULL ) + XFree( prop ); + if( type != None ) + return child; + } + parent = child; + } + return 0; + } + +} // namespace + +#include "detectwidget.moc" diff --git a/kwin/kcmkwin/kwinrules/detectwidget.h b/kwin/kcmkwin/kwinrules/detectwidget.h new file mode 100644 index 000000000..593f4e056 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/detectwidget.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __DETECTWIDGET_H__ +#define __DETECTWIDGET_H__ + +#include "detectwidgetbase.h" + +#include <kdialogbase.h> +#include <kwin.h> + +#include "../../rules.h" + +namespace KWinInternal +{ + +class DetectWidget + : public DetectWidgetBase + { + Q_OBJECT + public: + DetectWidget( QWidget* parent = NULL, const char* name = NULL ); + }; + +class DetectDialog + : public KDialogBase + { + Q_OBJECT + public: + DetectDialog( QWidget* parent = NULL, const char* name = NULL ); + void detect( WId window ); + QCString selectedClass() const; + bool selectedWholeClass() const; + QCString selectedRole() const; + bool selectedWholeApp() const; + NET::WindowType selectedType() const; + QString selectedTitle() const; + Rules::StringMatch titleMatch() const; + QCString selectedMachine() const; + const KWin::WindowInfo& windowInfo() const; + signals: + void detectionDone( bool ); + protected: + virtual bool eventFilter( QObject* o, QEvent* e ); + private: + void selectWindow(); + void readWindow( WId window ); + void executeDialog(); + WId findWindow(); + QCString wmclass_class; + QCString wmclass_name; + QCString role; + NET::WindowType type; + QString title; + QCString extrarole; + QCString machine; + DetectWidget* widget; + QDialog* grabber; + KWin::WindowInfo info; + }; + +inline +const KWin::WindowInfo& DetectDialog::windowInfo() const + { + return info; + } + +} // namespace + +#endif diff --git a/kwin/kcmkwin/kwinrules/detectwidgetbase.ui b/kwin/kcmkwin/kwinrules/detectwidgetbase.ui new file mode 100644 index 000000000..31cae3ef3 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/detectwidgetbase.ui @@ -0,0 +1,218 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KWinInternal::DetectWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Form3</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>523</width> + <height>325</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <spacer row="9" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel11</cstring> + </property> + <property name="text"> + <string>Extra role:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Class:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Role:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="1"> + <property name="name"> + <cstring>type_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Title:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>class_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>role_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="1"> + <property name="name"> + <cstring>title_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="7" column="1"> + <property name="name"> + <cstring>machine_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Type:</string> + </property> + </widget> + <widget class="QLabel" row="6" column="1"> + <property name="name"> + <cstring>extrarole_label</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="7" column="0"> + <property name="name"> + <cstring>textLabel13</cstring> + </property> + <property name="text"> + <string>Machine:</string> + </property> + </widget> + <widget class="Line" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel9</cstring> + </property> + <property name="text"> + <string>Information About Selected Window</string> + </property> + </widget> + <widget class="QButtonGroup" row="8" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>use_class</cstring> + </property> + <property name="text"> + <string>Use window &class (whole application)</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>For selecting all windows belonging to a specific application, selecting only window class should usually work.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>use_role</cstring> + </property> + <property name="text"> + <string>Use window class and window &role (specific window)</string> + </property> + <property name="whatsThis" stdset="0"> + <string>For selecting a specific window in an application, both window class and window role should be selected. Window class will determine the application, and window role the specific window in the application; many applications do not provide useful window roles though.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>use_whole_class</cstring> + </property> + <property name="text"> + <string>Use &whole window class (specific window)</string> + </property> + <property name="whatsThis" stdset="0"> + <string>With some (non-KDE) applications whole window class can be sufficient for selecting a specific window in an application, as they set whole window class to contain both application and window role.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>match_title</cstring> + </property> + <property name="text"> + <string>Match also window &title</string> + </property> + </widget> + </vbox> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kwin/kcmkwin/kwinrules/editshortcutbase.ui b/kwin/kcmkwin/kwinrules/editshortcutbase.ui new file mode 100644 index 000000000..3813ae589 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/editshortcutbase.ui @@ -0,0 +1,164 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>EditShortcutBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>EditShortcutBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>587</width> + <height>402</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>A single shortcut can be easily assigned or cleared using the two buttons. Only shortcuts with modifiers can be used.<p> +It is possible to have several possible shortcuts, and the first available shortcut will be used. The shortcuts are specified using space-separated shortcut sets. One set is specified as <i>base</i>+(<i>list</i>), where base are modifiers and list is a list of keys.<br> +For example "<b>Shift+Alt+(123) Shift+Ctrl+(ABC)</b>" will first try <b>Shift+Alt+1</b>, then others with <b>Shift+Ctrl+C</b> as the last one.</string> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>shortcut</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>pushButton1</cstring> + </property> + <property name="text"> + <string>&Single Shortcut</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>pushButton2</cstring> + </property> + <property name="text"> + <string>C&lear</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line2</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>pushButton1</sender> + <signal>clicked()</signal> + <receiver>EditShortcutBase</receiver> + <slot>editShortcut()</slot> + </connection> + <connection> + <sender>pushButton2</sender> + <signal>clicked()</signal> + <receiver>EditShortcutBase</receiver> + <slot>clearShortcut()</slot> + </connection> +</connections> +<slots> + <slot access="protected" specifier="pure virtual">editShortcut()</slot> + <slot access="protected" specifier="pure virtual">clearShortcut()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kwin/kcmkwin/kwinrules/kcm.cpp b/kwin/kcmkwin/kwinrules/kcm.cpp new file mode 100644 index 000000000..b40cfa8b2 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/kcm.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcm.h" + +#include <kglobal.h> +#include <qlayout.h> +#include <klocale.h> +#include <kapplication.h> +#include <dcopclient.h> +#include <kaboutdata.h> + +#include "ruleslist.h" + +extern "C" + KDE_EXPORT KCModule *create_kwinrules( QWidget *parent, const char *name ) + { + //CT there's need for decision: kwm or kwin? + KGlobal::locale()->insertCatalogue( "kcmkwinrules" ); + return new KWinInternal::KCMRules( parent, name ); + } + +namespace KWinInternal +{ + +KCMRules::KCMRules( QWidget *parent, const char *name ) +: KCModule( parent, name ) +, config( "kwinrulesrc" ) + { + QVBoxLayout *layout = new QVBoxLayout( this ); + widget = new KCMRulesList( this ); + layout->addWidget( widget ); + connect( widget, SIGNAL( changed( bool )), SLOT( moduleChanged( bool ))); + KAboutData *about = new KAboutData(I18N_NOOP( "kcmkwinrules" ), + I18N_NOOP( "Window-Specific Settings Configuration Module" ), + 0, 0, KAboutData::License_GPL, I18N_NOOP( "(c) 2004 KWin and KControl Authors" )); + about->addAuthor("Lubos Lunak",0,"l.lunak@kde.org"); + setAboutData(about); + } + +void KCMRules::load() + { + config.reparseConfiguration(); + widget->load(); + emit KCModule::changed( false ); + } + +void KCMRules::save() + { + widget->save(); + emit KCModule::changed( false ); + // Send signal to kwin + config.sync(); + if( !kapp->dcopClient()->isAttached()) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + } + +void KCMRules::defaults() + { + widget->defaults(); + } + +QString KCMRules::quickHelp() const + { + return i18n("<h1>Window-specific Settings</h1> Here you can customize window settings specifically only" + " for some windows." + " <p>Please note that this configuration will not take effect if you do not use" + " KWin as your window manager. If you do use a different window manager, please refer to its documentation" + " for how to customize window behavior."); + } + +void KCMRules::moduleChanged( bool state ) + { + emit KCModule::changed( state ); + } + +} + +// i18n freeze :-/ +#if 0 +I18N_NOOP("Remember settings separately for every window") +I18N_NOOP("Show internal settings for remembering") +I18N_NOOP("Internal setting for remembering") +#endif + + +#include "kcm.moc" diff --git a/kwin/kcmkwin/kwinrules/kcm.h b/kwin/kcmkwin/kwinrules/kcm.h new file mode 100644 index 000000000..47fba75d2 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/kcm.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __KCM_H__ +#define __KCM_H__ + +#include <kcmodule.h> +#include <kconfig.h> + +class KConfig; +class KAboutData; + +namespace KWinInternal +{ + +class KCMRulesList; + +class KCMRules + : public KCModule + { + Q_OBJECT + public: + KCMRules( QWidget *parent, const char *name ); + virtual void load(); + virtual void save(); + virtual void defaults(); + virtual QString quickHelp() const; + protected slots: + void moduleChanged( bool state ); + private: + KCMRulesList* widget; + KConfig config; + }; + +} // namespace + +#endif diff --git a/kwin/kcmkwin/kwinrules/kwinrules.desktop b/kwin/kcmkwin/kwinrules/kwinrules.desktop new file mode 100644 index 000000000..245994a8a --- /dev/null +++ b/kwin/kcmkwin/kwinrules/kwinrules.desktop @@ -0,0 +1,209 @@ +[Desktop Entry] +Icon=kcmkwm +Type=Application +Exec=kcmshell kwinrules +DocPath=kcontrol/windowmanagement/index.html + +X-KDE-ModuleType=Library +X-KDE-Library=kwinrules +X-KDE-FactoryName=kwinrules + +Name=Window-Specific Settings +Name[af]=Venser spesifike Instellings +Name[ar]=تعيينات خاصة بالنواÙØ° +Name[be]=ÐаÑтаўленні Ð´Ð»Ñ Ð°Ñобных вокнаў +Name[bg]=Специфични прозорци +Name[bn]=উইণà§à¦¡à§‹-পà§à¦°à¦¤à¦¿ সেটিংস +Name[bs]=Postavke specifiÄne za ovaj prozor +Name[ca]=Arranjament especÃfic de finestra +Name[cs]=Specifická nastavenà oken +Name[csb]=Nastôw specyficzny dlô òkna +Name[cy]=Gosodiadau sy'n Benodol i'r Ffenestr +Name[da]=Vinduesspecifik opsætning +Name[de]=Fensterspezifische Einstellungen +Name[el]=Ρυθμίσεις σχετικÎÏ‚ με παÏάθυÏα +Name[eo]=Apartafenestraj Agordoj +Name[es]=Preferencias especÃficas de la ventana +Name[et]=Akende seadistused +Name[eu]=Leihoen ezarpen espezifikoak +Name[fa]=تنظیمات مشخص پنجره +Name[fi]=Ikkunakohtaiset asetukset +Name[fr]=Paramètres spécifiques à la fenêtre +Name[fy]=Finsterspesifike ynstellings +Name[gl]=Opcións especÃficas da fiestra +Name[he]=הגדרות ×—×œ×•× ×•×ª ספציפיות +Name[hi]=विशिषà¥à¤Ÿ-विंडो विनà¥à¤¯à¤¾à¤¸ +Name[hr]=Postavke za prozor +Name[hu]=Egyedi ablakbeállÃtások +Name[is]=Stillingar einstakra glugga +Name[it]=Impostazioni specifiche della finestra +Name[ja]=ウィンドウ固有ã®è¨å®š +Name[ka]=ფáƒáƒœáƒ¯áƒ ის გáƒáƒœáƒ¡áƒáƒ™áƒ£áƒ—რებული პáƒáƒ áƒáƒ›áƒ”ტრები +Name[kk]=Терезенің ерекше параметрлері +Name[km]=ការ​កំណážáŸ‹â€‹ážŸáž˜áŸ’រាប់​បង្អួច +Name[ko]=ì°½ ì§€ì • +Name[lt]=Nuo lango priklausantys nustatymai +Name[lv]=Specifiski logu parametri +Name[mk]=ПоÑтавувања по прозорец +Name[nb]=Innstillinger for vinduer +Name[nds]=Instellen för enkelte Finstern +Name[ne]=सञà¥à¤à¥à¤¯à¤¾à¤² निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ सेटिङ +Name[nl]=Vensterspecifieke instellingen +Name[nn]=Instillingar for vindauge +Name[pa]=à¨à¨°à©‹à¨–ਾ-ਖਾਸ ਸਥਾਪਨ +Name[pl]=Ustawienia danego okna +Name[pt]=Opções EspecÃficas da Janela +Name[pt_BR]=Configurações EspecÃficas da Janela +Name[ro]=Setări specifice fereastră +Name[ru]=ОÑобые параметры окна +Name[rw]=Amagenamiterere y'Idirishya-Ryihariye +Name[se]=Láseheivehusat +Name[sk]=Å pecifické nastavenia okna +Name[sl]=Nastavitve glede na okno +Name[sr]=ПоÑебне поÑтавке за прозоре +Name[sr@Latn]=Posebne postavke za prozore +Name[sv]=Fönsterspecifika inställningar +Name[ta]=சாளர-கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®®à¯ அமைபà¯à®ªà¯à®•à®³à¯ +Name[th]=ตั้งค่าเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¹‚ดยเฉพาะ +Name[tr]=Pencereye Özel Seçenekler +Name[tt]=Täräzägä-Bäyle Caylawlar +Name[uk]=Параметри Ð´Ð»Ñ Ð¾ÐºÑ€ÐµÐ¼Ð¸Ñ… вікон +Name[uz]=Oynaga oid moslamalar +Name[uz@cyrillic]=Ойнага оид моÑламалар +Name[vi]=Thiết láºp Danh riêng cho Cá»a sổ +Name[wa]=Apontiaedjes specifikes a-z on purnea +Name[zh_CN]=特定窗å£çš„设置 +Name[zh_TW]=特定視窗è¨å®š + +Comment=Configure settings specifically for a window +Comment[af]=Stel spesifieke venster instellings op +Comment[ar]=إعداد التعيينات الخاصة بناÙذة +Comment[be]=ÐаÑтаўленні Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð°Ð³Ð° вакна аÑабіÑта +Comment[bg]=ÐаÑтройване на Ñпецифични оÑобеноÑти за отделните прозорци +Comment[bn]=শà§à¦§à§à¦®à¦¾à¦¤à§à¦° à¦à¦‡ বিশেষ উইণà§à¦¡à§‹-টির সেটিংস কনফিগার করà§à¦¨ +Comment[bs]=Podesite postavke specifiÄne za ovaj prozor +Comment[ca]=Configura l'arranjament especÃficament per a una finestra +Comment[cs]=Nastavenà specifická pro okno +Comment[csb]=Kònfigùracëjô nastôwów specyficznëch dlô wëbrónegò òkna +Comment[cy]=Ffurfweddu gosodiadau yn benodol ar gyfer ffenestr +Comment[da]=Indstil opsætning specifikt for et vindue +Comment[de]=Einstellungen für einzelne Fenster vornehmen +Comment[el]=Ρυθμίσεις σχετικÎÏ‚ ειδικά με Ένα ΠαÏάθυÏο +Comment[eo]=Agordi nur por tiu fenestro +Comment[es]=Configurar parámetros especÃficos de una ventana +Comment[et]=Spetsiaalselt akendega seotud seadistused +Comment[eu]=Konfiguratu leiho jakin baten ezarpenak +Comment[fa]=پیکربندی تنظیمات برای پنجره به طور ‌ویژه +Comment[fi]=Muokkaa ikkunakohtaisia asetuksia +Comment[fr]=Configuration de paramètres spécifiques à une fenêtre +Comment[fy]=Hjir kinne jo ynstellings kieze spesifyk foar ien finster +Comment[ga]=Cumraigh na socruithe le haghaidh fuinneoige ar leith +Comment[gl]=Configuración das opcións especÃficas para unha fiestra +Comment[he]=קבע הגדרות לחלון ×ž×¡×•×™× +Comment[hi]=किसी खास विंडो के लिठविनà¥à¤¯à¤¾à¤¸ कॉनà¥à¤«à¤¼à¤¿à¤—र करें +Comment[hr]=Konfiguriranje odreÄ‘enih postavki za prozor +Comment[hu]=Egy adott ablak beállÃtásai +Comment[is]=Stillingar sem varða tiltekinn glugga +Comment[it]=Configura le impostazioni specifiche di una finestra +Comment[ja]=特定ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«å›ºæœ‰ã®è¨å®šã‚’è¡Œã„ã¾ã™ +Comment[ka]=ფáƒáƒœáƒ¯áƒ ის გáƒáƒœáƒ¡áƒáƒ™áƒ£áƒ—რებული პáƒáƒ áƒáƒ›áƒ”ტრების კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ +Comment[kk]=Терезенің ерекше параметрлерін баптау +Comment[km]=កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​ការ​កំណážáŸ‹â€‹áž–ិសáŸážŸâ€‹ážŸáž˜áŸ’រាប់​បង្អួច​មួយ +Comment[ko]=ì°½ë§ˆë‹¤ì˜ ê°œë³„ ì„¤ì • +Comment[lt]=ÄŒia galite konfigÅ«ruoti konkretaus lango nustatymus +Comment[lv]=KonfigurÄ“ parametrus priekÅ¡ speciÄla loga +Comment[mk]=Конфигурирајте ги поÑтавувањата за поодделни прозорци +Comment[mt]=Ikkonfigura setings li jistgħu itejbu l-veloÄ‹ità ta' KDE +Comment[nb]=Innstillinger som er knyttet til et spesielt vindu +Comment[nds]=Instellen för enkelte Finstern +Comment[ne]=सञà¥à¤à¥à¤¯à¤¾à¤²à¤•à¤¾ लागि निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ तरिकाले सेटिङ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Hier kunt u instellingen kiezen specifiek voor één venster +Comment[nn]=Innstillingar som er knytte til eitt særskilt vindauge +Comment[pa]=ਇੱਕ à¨à¨°à©‹à¨–ੇ ਲਈ ਖਾਸ ਸਥਾਪਨ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja ustawieÅ„ wybranego okna +Comment[pt]=Configura as opções especificamente para uma janela +Comment[pt_BR]=Configurações especÃficas para uma janela +Comment[ro]=Configurează setări specifice anumitor ferestre +Comment[ru]=ÐаÑтройка оÑобых параметров окна +Comment[rw]=Kuboneza Amagenamiterere mu buryo bwihariye bw'idirishya +Comment[se]=Heivehusat erenoamáš láse várás +Comment[sk]=Nastavenie pre jednotlivé okná +Comment[sl]=Nastavite možnosti glede na posamezno okno +Comment[sr]=Подешавање поÑтавки поÑебно за прозор +Comment[sr@Latn]=PodeÅ¡avanje postavki posebno za prozor +Comment[sv]=Anpassa inställningar specifikt för ett fönster +Comment[ta]=KDE செயலà¯à®¤à®¿à®±à®©à¯ˆ அதிகபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ அமைபà¯à®ªà¯à®•à®³à¯ˆ அமை +Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¸„่าต่างๆ ที่เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡à¹‚ดยเฉพาะ +Comment[tr]=Bir pencere için özel olan seçenekleri yapılandır +Comment[tt]=Täräzä öçen ayırım caylawlar +Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² Ð´Ð»Ñ Ð¾ÐºÑ€ÐµÐ¼Ð¾Ð³Ð¾ типу вікон +Comment[uz]=Oynaga oid boÊ»lgan moslamalarni moslash +Comment[uz@cyrillic]=Ойнага оид бўлган моÑламаларни моÑлаш +Comment[vi]=Thiết láºp cà i đặt dà nh riêng cho cá»a sổ +Comment[wa]=Apontiaedjes specifikes po-z on purnea +Comment[zh_CN]=é…置一个窗å£çš„设置 +Comment[zh_TW]=è¨å®šç‰¹å®šè¦–窗è¨å®šå€¼ + +Keywords=size,position,state,window behavior,windows,specific,workarounds,remember,rules +Keywords[ar]=القياس,الموضع,الØالة,سلوك الناÙذة,نواÙØ°,خاص,تدكر,قواعد,معاملات +Keywords[be]=Памер,ПазіцыÑ,Стан,Паводзіны акна,Вокны,Ðкно,СпецыфічныÑ,Запомніць,Правілы,size,position,state,window behavior,windows,specific,workarounds,remember,rules +Keywords[bg]=размер, позициÑ, ÑÑŠÑтоÑние, прозорец, поведение,правила, запомнÑне,size,position,state,window behavior,windows,specific,workarounds,remember,rules +Keywords[bs]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,veliÄina,položaj,ponaÅ¡anje prozora,prozori,specifiÄno,pravila +Keywords[ca]=mida,posició,estat,comportament de finestra,finestres,especÃfic,reparacions,recorda,regles +Keywords[cs]=velikost,pozice,umÃstÄ›nÃ,stav,chovánà oken,pravidla +Keywords[csb]=miara,pòłożenié,stón,ùchòwanié òknół,òkna,spamiãtanié nastôwów,zapamiãtanié nastôwów,regle +Keywords[cy]=maint,lleoliad,cyflwr,ffenestr,ymddygiad,ffenestri,penodol,ffyrdd osgoi,cofio,rheolau +Keywords[da]=størrelse,position,tilstand,vinduesopførsel,vinduer,specifik, omgÃ¥else,husk,regler +Keywords[de]=Größe,Position,Status,Fensterverhalten,Fenster,Regeln +Keywords[el]=μÎγεθος,θÎση,κατάσταση,συμπεÏιφοÏά παÏαθÏÏου,παÏάθυÏα,ειδικÎÏ‚,λÏσεις,αποθήκευση,κανόνων +Keywords[en_GB]=size,position,state,window behaviour,windows,specific,workarounds,remember,rules +Keywords[eo]=grandeco,pozicio,stato,fenestra konduto,fenestroj,reguloj +Keywords[es]=tamaño,posición,estado,comportamiento de la ventana,ventanas,especÃfico,soluciones,recordar,reglas +Keywords[et]=suurus,asend,olek,akna käitumine,aknad,spetsiaalne,kohandamine,reeglid +Keywords[eu]=neurria,kokapena,egoera,leihoaren portaera,leihoak,espezifikoa,konponbideak,gogoratu,arauak +Keywords[fa]=اندازه، موقعیت، وضعیت، رÙتار پنجره، پنجره‌ها، مشخص، workaroundsØŒ یادآوری، قواعد +Keywords[fi]=koko,sijainti,tila,ikkunan käyttäytyminen,ikkunat,ikkunakohtainen,säännöt +Keywords[fr]=taille,position,état,comportement de fenêtre,fenêtres,spécifique,astuces,souvenir,règles +Keywords[fy]=grootte,grutte,positie,posysje,venstergedrag,finstergedrach,vensters,finsters,specifiek,spesifyk,alternatieven,alternativen,onthouden,ûnthâlde,regels +Keywords[ga]=méid,ionad,staid,oibriú na bhfuinneog,fuinneoga,sainiúil,réitigh seiftithe,meabhraigh,rialacha +Keywords[gl]=tamaño,posición,estado,comportamento da fiestra,fiestras,especÃfico,lembrar,reglas +Keywords[he]=גודל, מיקו×, מצב, חלון, ×”×ª× ×”×’×•×ª, ×—×œ×•× ×•×ª, ×ישית, זכור, חוקי×, size,position,state,window behavior,windows,specific,workarounds,remember,rules +Keywords[hi]=आकार,सà¥à¤¥à¤¾à¤¨,सà¥à¤¥à¤¿à¤¤à¤¿,विंडो वà¥à¤¯à¤µà¤¹à¤¾à¤°,विंडोज़,विशिषà¥à¤Ÿ,कारà¥à¤¯-विकलà¥à¤ª,याददाशà¥à¤¤,नियम +Keywords[hr]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,veliÄina,položaj,smjeÅ¡taj,stanje,ponaÅ¡anje prozora,prozori,rjeÅ¡enja,zapamti,upamti,pravila +Keywords[hu]=méret,pozÃció,állapot,ablakműködés,ablakok,specifikus,megjegyzés,szabályok +Keywords[id]=ukuran,posisi,status,perilaku jendela,jendela, spesifik,solusi,remember,aturan +Keywords[it]=dimensione,posizione,stato,comportamento finestra,finestre,specifico,ricorda,regole +Keywords[ja]=サイズ,ä½ç½®,状態,ウィンドウã®æŒ™å‹•,windows,特定,ワークアラウンド,記憶,ルール +Keywords[km]=ទំហំ,ទីážáž¶áŸ†áž„,សភាព,ឥរិយាបážâ€‹áž”ង្អួច បង្អួចs ជាក់លាក់c ចងចាំr ច្បាប់s +Keywords[lt]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,dydis,pozicija,bÅ«sena,lango eelgesys,langai,atsiminti,taisyklÄ—s +Keywords[lv]=izmÄ“rs,novietojums,statuss,loga izturÄ“Å¡anÄs,logi,specifisks,apjÄjiens,atcerÄ“ties,noteikumi +Keywords[mk]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,големина,позиција,ÑоÑтојба,однеÑување на прозорец,Ñпецифично,заобиколни +Keywords[mt]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,daqs +Keywords[nb]=størrelse,posisjon,status,vindusoppførsel,vinduer,spesifikk,jukseløsninger,husk,regler +Keywords[nds]=Grött,Positschoon,Status,Finsterbedregen,Finstern,enkel,enkelte,Regeln,behollen,workaround +Keywords[ne]=साइज, सà¥à¤¥à¤¿à¤¤à¤¿, अवसà¥à¤¥à¤¾, सञà¥à¤à¥à¤¯à¤¾à¤² वà¥à¤¯à¤µà¤¹à¤¾à¤°, सञà¥à¤à¥à¤¯à¤¾à¤²à¤¹à¤°à¥‚, निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ, कारà¥à¤¯à¤•à¥à¤·à¥‡à¤¤à¥à¤°, याद गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥, नियम +Keywords[nl]=grootte,positie,venstergedrag,vensters,specifiek,alternatieven,onthouden,regels +Keywords[nn]=storleik,plassering,tilstand,vindaugsÃ¥tferd,vindauge,spesifikk,jukseløysingar,hugs,reglar +Keywords[pa]=ਅਕਾਰ,ਸਥਿਤੀ,à¨à¨°à©‹à¨–ਾ ਵਿਵਹਾਰ,à¨à¨°à©‹à¨–ੇ,ਖਾਸ,ਯਾਦ ਰੱਖੋ,ਨਿਯਮ +Keywords[pl]=rozmiar,poÅ‚ożenie,stan,zachowanie okien,okna,zapamiÄ™tywanie ustawieÅ„,reguÅ‚y +Keywords[pt]=tamanho,posição,estado,comportamento da janela,janelas,especÃfico,recordar,regras +Keywords[pt_BR]=tamanho,posição, estado, comportamento da janela, janelas, especÃfica, elementos, lembrar, regras +Keywords[ro]=mărime,poziÈ›ie,stare,comportament fereastră,ferestre,specific,reguli +Keywords[rw]=Ingano,Ibirindiro,leta, imyitwarire y'idirishya,amadirishya,byihariye, amakorahafi,kwibuka,amategeko +Keywords[se]=sturrodat,sajádat,dilli,láseláhtten,láset,erenoamáš,fillenÄovdosat,muitte,njuolggadusat +Keywords[sk]=veľkosÅ¥,pozÃcia,stav,správanie okna,okná,Å¡pecifické,jedno okno,opravy,pravidlá +Keywords[sl]=velikost,položaj,stanje,obnaÅ¡anje okna,okna,posebno,obvoz,zapomni,pravila +Keywords[sr]=величина,позиција,Ñтање,понашање прозора,прозори,запамти,правила +Keywords[sr@Latn]=veliÄina,pozicija,stanje,ponaÅ¡anje prozora,prozori,zapamti,pravila +Keywords[sv]=storlek,position,tillstÃ¥nd,fönsterbeteende,fönster,specifik,komma runt,komma ihÃ¥g,regler +Keywords[ta]=அளவà¯,நிலை,மாநிலமà¯,சாளர நடதà¯à®¤à¯ˆ,சாளரஙà¯à®•à®³à¯,கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ,பணிவடà¯à®Ÿà®¾à®°à®™à¯à®•à®³à¯,விதிகள௠+Keywords[th]=ขนาด,ตำà¹à¸«à¸™à¹ˆà¸‡,สถานะ,พฤติà¸à¸£à¸£à¸¡à¸«à¸™à¹‰à¸²à¸•à¹ˆà¸²à¸‡,หน้าต่าง,โดยเฉพาะ,หารà¸à¸šà¹†,จำ,à¸à¸Ž +Keywords[tr]=boyut,yer,pozisyon,durum,pencere davranışı,pencereler,özel,çalışma alanı,hatırla,kurallar +Keywords[uk]=розмір,позиціÑ,Ñтан,поведінка вікна,вікна,окремі,підігнати,запам'Ñтати,правила +Keywords[uz]=oÊ»lcham,joy,holat,oynaning xususiyatlari,oynalar,maxsus,eslab qolish,qoidalar +Keywords[uz@cyrillic]=ўлчам,жой,ҳолат,ойнанинг хуÑуÑиÑтлари,ойналар,махÑуÑ,ÑÑлаб қолиш,қоидалар +Keywords[vi]=kÃch cỡ,vị trÃ,trạng thái,ứng xá» cá»a sổ,cá»a sổ,dà nh riêng,cách chữa chạy,nhá»›,quy tắc +Keywords[wa]=grandeu,plaece,estat,estance,dujhance des purneas,purneas,specifike,workarounds,remember,rîles,sovni,sovnance +Keywords[zh_CN]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,大å°,ä½ç½®,状æ€,窗å£è¡Œä¸º,窗å£,特定,工作区,è®°ä½,规则 +Keywords[zh_TW]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,大å°,ä½ç½®,狀態,視窗行為,視窗,指定,記憶,è¦å‰‡ +Categories=Qt;KDE;X-KDE-settings-desktop; diff --git a/kwin/kcmkwin/kwinrules/kwinsrc.cpp b/kwin/kcmkwin/kwinrules/kwinsrc.cpp new file mode 100644 index 000000000..38f48e003 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/kwinsrc.cpp @@ -0,0 +1,8 @@ +// Include some code from kwin core in order to avoid +// double implementation. + +#include "ruleslist.h" +#include "../../rules.cpp" +#include "../../placement.cpp" +#include "../../options.cpp" +#include "../../utils.cpp" diff --git a/kwin/kcmkwin/kwinrules/main.cpp b/kwin/kcmkwin/kwinrules/main.cpp new file mode 100644 index 000000000..9657e3649 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/main.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <dcopclient.h> +#include <kconfig.h> +#include <klocale.h> +#include <kwin.h> + +#include <X11/Xlib.h> +#include <fixx11h.h> + +#include "ruleswidget.h" +#include "../../rules.h" + +namespace KWinInternal +{ + +static void loadRules( QValueList< Rules* >& rules ) + { + KConfig cfg( "kwinrulesrc", true ); + cfg.setGroup( "General" ); + int count = cfg.readNumEntry( "count" ); + for( int i = 1; + i <= count; + ++i ) + { + cfg.setGroup( QString::number( i )); + Rules* rule = new Rules( cfg ); + rules.append( rule ); + } + } + +static void saveRules( const QValueList< Rules* >& rules ) + { + KConfig cfg( "kwinrulesrc" ); + QStringList groups = cfg.groupList(); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + cfg.deleteGroup( *it ); + cfg.setGroup( "General" ); + cfg.writeEntry( "count", rules.count()); + int i = 1; + for( QValueList< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + { + cfg.setGroup( QString::number( i )); + (*it)->write( cfg ); + ++i; + } + } + +static Rules* findRule( const QValueList< Rules* >& rules, Window wid, bool whole_app ) + { + KWin::WindowInfo info = KWin::windowInfo( wid, + NET::WMName | NET::WMWindowType, + NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2ClientMachine ); + if( !info.valid()) // shouldn't really happen + return NULL; + QCString wmclass_class = info.windowClassClass().lower(); + QCString wmclass_name = info.windowClassName().lower(); + QCString role = info.windowRole().lower(); + NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask ); + QString title = info.name(); +// QCString extrarole = ""; // TODO + QCString machine = info.clientMachine().lower(); + Rules* best_match = NULL; + int match_quality = 0; + for( QValueList< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + { + // try to find an exact match, i.e. not a generic rule + Rules* rule = *it; + int quality = 0; + bool generic = true; + if( rule->wmclassmatch != Rules::ExactMatch ) + continue; // too generic + if( !rule->matchWMClass( wmclass_class, wmclass_name )) + continue; + // from now on, it matches the app - now try to match for a specific window + if( rule->wmclasscomplete ) + { + quality += 1; + generic = false; // this can be considered specific enough (old X apps) + } + if( !whole_app ) + { + if( rule->windowrolematch != Rules::UnimportantMatch ) + { + quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1; + generic = false; + } + if( rule->titlematch != Rules::UnimportantMatch ) + { + quality += rule->titlematch == Rules::ExactMatch ? 3 : 1; + generic = false; + } + if( rule->types != NET::AllTypesMask ) + { + int bits = 0; + for( unsigned int bit = 1; + bit < 1U << 31; + bit <<= 1 ) + if( rule->types & bit ) + ++bits; + if( bits == 1 ) + quality += 2; + } + if( generic ) // ignore generic rules, use only the ones that are for this window + continue; + } + else + { + if( rule->types == NET::AllTypesMask ) + quality += 2; + } + if( !rule->matchType( type ) + || !rule->matchRole( role ) + || !rule->matchTitle( title ) + || !rule->matchClientMachine( machine )) + continue; + if( quality > match_quality ) + { + best_match = rule; + match_quality = quality; + } + } + if( best_match != NULL ) + return best_match; + Rules* ret = new Rules; + if( whole_app ) + { + ret->description = i18n( "Application settings for %1" ).arg( wmclass_class ); + // TODO maybe exclude some types? If yes, then also exclude them above + // when searching. + ret->types = NET::AllTypesMask; + ret->titlematch = Rules::UnimportantMatch; + ret->clientmachine = machine; // set, but make unimportant + ret->clientmachinematch = Rules::UnimportantMatch; + ret->extrarolematch = Rules::UnimportantMatch; + ret->windowrolematch = Rules::UnimportantMatch; + if( wmclass_name == wmclass_class ) + { + ret->wmclasscomplete = false; + ret->wmclass = wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + else + { + // WM_CLASS components differ - perhaps the app got -name argument + ret->wmclasscomplete = true; + ret->wmclass = wmclass_name + ' ' + wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + return ret; + } + ret->description = i18n( "Window settings for %1" ).arg( wmclass_class ); + if( type == NET::Unknown ) + ret->types = NET::NormalMask; + else + ret->types = 1 << type; // convert type to its mask + ret->title = title; // set, but make unimportant + ret->titlematch = Rules::UnimportantMatch; + ret->clientmachine = machine; // set, but make unimportant + ret->clientmachinematch = Rules::UnimportantMatch; +// ret->extrarole = extra; TODO + ret->extrarolematch = Rules::UnimportantMatch; + if( !role.isEmpty() + && role != "unknown" && role != "unnamed" ) // Qt sets this if not specified + { + ret->windowrole = role; + ret->windowrolematch = Rules::ExactMatch; + if( wmclass_name == wmclass_class ) + { + ret->wmclasscomplete = false; + ret->wmclass = wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + else + { + // WM_CLASS components differ - perhaps the app got -name argument + ret->wmclasscomplete = true; + ret->wmclass = wmclass_name + ' ' + wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + } + else // no role set + { + if( wmclass_name != wmclass_class ) + { + ret->wmclasscomplete = true; + ret->wmclass = wmclass_name + ' ' + wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + else + { + // This is a window that has no role set, and both components of WM_CLASS + // match (possibly only differing in case), which most likely means either + // the application doesn't give a damn about distinguishing its various + // windows, or it's an app that uses role for that, but this window + // lacks it for some reason. Use non-complete WM_CLASS matching, also + // include window title in the matching, and pray it causes many more positive + // matches than negative matches. + ret->titlematch = Rules::ExactMatch; + ret->wmclasscomplete = false; + ret->wmclass = wmclass_class; + ret->wmclassmatch = Rules::ExactMatch; + } + } + return ret; + } + +static int edit( Window wid, bool whole_app ) + { + QValueList< Rules* > rules; + loadRules( rules ); + Rules* orig_rule = findRule( rules, wid, whole_app ); + RulesDialog dlg; + // dlg.edit() creates new Rules instance if edited + Rules* edited_rule = dlg.edit( orig_rule, wid, true ); + if( edited_rule == NULL || edited_rule->isEmpty()) + { + rules.remove( orig_rule ); + delete orig_rule; + if( orig_rule != edited_rule ) + delete edited_rule; + } + else if( edited_rule != orig_rule ) + { + QValueList< Rules* >::Iterator pos = rules.find( orig_rule ); + if( pos != rules.end()) + *pos = edited_rule; + else + rules.prepend( edited_rule ); + delete orig_rule; + } + saveRules( rules ); + if( !kapp->dcopClient()->isAttached()) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("kwin*", "", "reconfigure()", ""); + return 0; + } + +} // namespace + +static const KCmdLineOptions options[] = + { + // no need for I18N_NOOP(), this is not supposed to be used directly + { "wid <wid>", "WId of the window for special window settings.", 0 }, + { "whole-app", "Whether the settings should affect all windows of the application.", 0 }, + KCmdLineLastOption + }; + +extern "C" +KDE_EXPORT int kdemain( int argc, char* argv[] ) + { + KLocale::setMainCatalogue( "kcmkwinrules" ); + KCmdLineArgs::init( argc, argv, "kwin_rules_dialog", I18N_NOOP( "KWin" ), + I18N_NOOP( "KWin helper utility" ), "1.0" ); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + bool id_ok = false; + Window id = args->getOption( "wid" ).toULong( &id_ok ); + bool whole_app = args->isSet( "whole-app" ); + args->clear(); + if( !id_ok || id == None ) + { + KCmdLineArgs::usage( i18n( "This helper utility is not supposed to be called directly." )); + return 1; + } + return KWinInternal::edit( id, whole_app ); + } diff --git a/kwin/kcmkwin/kwinrules/ruleslist.cpp b/kwin/kcmkwin/kwinrules/ruleslist.cpp new file mode 100644 index 000000000..f4704089f --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleslist.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ruleslist.h" + +#include <klistbox.h> +#include <kpushbutton.h> +#include <assert.h> +#include <kdebug.h> +#include <kconfig.h> + +#include "ruleswidget.h" + +namespace KWinInternal +{ + +KCMRulesList::KCMRulesList( QWidget* parent, const char* name ) +: KCMRulesListBase( parent, name ) + { + // connect both current/selected, so that current==selected (stupid QListBox :( ) + connect( rules_listbox, SIGNAL( currentChanged( QListBoxItem* )), + SLOT( activeChanged( QListBoxItem*))); + connect( rules_listbox, SIGNAL( selectionChanged( QListBoxItem* )), + SLOT( activeChanged( QListBoxItem*))); + connect( new_button, SIGNAL( clicked()), + SLOT( newClicked())); + connect( modify_button, SIGNAL( clicked()), + SLOT( modifyClicked())); + connect( delete_button, SIGNAL( clicked()), + SLOT( deleteClicked())); + connect( moveup_button, SIGNAL( clicked()), + SLOT( moveupClicked())); + connect( movedown_button, SIGNAL( clicked()), + SLOT( movedownClicked())); + connect( rules_listbox, SIGNAL( doubleClicked ( QListBoxItem * ) ), + SLOT( modifyClicked())); + load(); + } + +KCMRulesList::~KCMRulesList() + { + for( QValueVector< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ++it ) + delete *it; + rules.clear(); + } + +void KCMRulesList::activeChanged( QListBoxItem* item ) + { + if( item != NULL ) + rules_listbox->setSelected( item, true ); // make current==selected + modify_button->setEnabled( item != NULL ); + delete_button->setEnabled( item != NULL ); + moveup_button->setEnabled( item != NULL && item->prev() != NULL ); + movedown_button->setEnabled( item != NULL && item->next() != NULL ); + } + +void KCMRulesList::newClicked() + { + RulesDialog dlg; + Rules* rule = dlg.edit( NULL, 0, false ); + if( rule == NULL ) + return; + int pos = rules_listbox->currentItem() + 1; + rules_listbox->insertItem( rule->description, pos ); + rules_listbox->setSelected( pos, true ); + rules.insert( rules.begin() + pos, rule ); + emit changed( true ); + } + +void KCMRulesList::modifyClicked() + { + int pos = rules_listbox->currentItem(); + if ( pos == -1 ) + return; + RulesDialog dlg; + Rules* rule = dlg.edit( rules[ pos ], 0, false ); + if( rule == rules[ pos ] ) + return; + delete rules[ pos ]; + rules[ pos ] = rule; + rules_listbox->changeItem( rule->description, pos ); + emit changed( true ); + } + +void KCMRulesList::deleteClicked() + { + int pos = rules_listbox->currentItem(); + assert( pos != -1 ); + rules_listbox->removeItem( pos ); + rules.erase( rules.begin() + pos ); + emit changed( true ); + } + +void KCMRulesList::moveupClicked() + { + int pos = rules_listbox->currentItem(); + assert( pos != -1 ); + if( pos > 0 ) + { + QString txt = rules_listbox->text( pos ); + rules_listbox->removeItem( pos ); + rules_listbox->insertItem( txt, pos - 1 ); + rules_listbox->setSelected( pos - 1, true ); + Rules* rule = rules[ pos ]; + rules[ pos ] = rules[ pos - 1 ]; + rules[ pos - 1 ] = rule; + } + emit changed( true ); + } + +void KCMRulesList::movedownClicked() + { + int pos = rules_listbox->currentItem(); + assert( pos != -1 ); + if( pos < int( rules_listbox->count()) - 1 ) + { + QString txt = rules_listbox->text( pos ); + rules_listbox->removeItem( pos ); + rules_listbox->insertItem( txt, pos + 1 ); + rules_listbox->setSelected( pos + 1, true ); + Rules* rule = rules[ pos ]; + rules[ pos ] = rules[ pos + 1 ]; + rules[ pos + 1 ] = rule; + } + emit changed( true ); + } + +void KCMRulesList::load() + { + rules_listbox->clear(); + for( QValueVector< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ++it ) + delete *it; + rules.clear(); + KConfig cfg( "kwinrulesrc", true ); + cfg.setGroup( "General" ); + int count = cfg.readNumEntry( "count" ); + rules.reserve( count ); + for( int i = 1; + i <= count; + ++i ) + { + cfg.setGroup( QString::number( i )); + Rules* rule = new Rules( cfg ); + rules.append( rule ); + rules_listbox->insertItem( rule->description ); + } + if( rules.count() > 0 ) + rules_listbox->setSelected( 0, true ); + else + activeChanged( NULL ); + } + +void KCMRulesList::save() + { + KConfig cfg( "kwinrulesrc" ); + QStringList groups = cfg.groupList(); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + cfg.deleteGroup( *it ); + cfg.setGroup( "General" ); + cfg.writeEntry( "count", rules.count()); + int i = 1; + for( QValueVector< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + { + cfg.setGroup( QString::number( i )); + (*it)->write( cfg ); + ++i; + } + } + +void KCMRulesList::defaults() + { + load(); + } + +} // namespace + +#include "ruleslist.moc" diff --git a/kwin/kcmkwin/kwinrules/ruleslist.h b/kwin/kcmkwin/kwinrules/ruleslist.h new file mode 100644 index 000000000..b192d2f4e --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleslist.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __RULESLIST_H__ +#define __RULESLIST_H__ + +#include "ruleslistbase.h" + +#include <qvaluevector.h> + +#include "../../rules.h" + +class QListBoxItem; + +namespace KWinInternal +{ + +class KCMRulesList + : public KCMRulesListBase + { + Q_OBJECT + public: + KCMRulesList( QWidget* parent = NULL, const char* name = NULL ); + virtual ~KCMRulesList(); + void load(); + void save(); + void defaults(); + signals: + void changed( bool ); + private slots: + void newClicked(); + void modifyClicked(); + void deleteClicked(); + void moveupClicked(); + void movedownClicked(); + void activeChanged( QListBoxItem* ); + private: + QValueVector< Rules* > rules; + }; + +} // namespace + +#endif diff --git a/kwin/kcmkwin/kwinrules/ruleslistbase.ui b/kwin/kcmkwin/kwinrules/ruleslistbase.ui new file mode 100644 index 000000000..c2abdaa9d --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleslistbase.ui @@ -0,0 +1,91 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KWinInternal::KCMRulesListBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KListBox" row="0" column="0" rowspan="6" colspan="1"> + <property name="name"> + <cstring>rules_listbox</cstring> + </property> + </widget> + <widget class="KPushButton" row="0" column="1"> + <property name="name"> + <cstring>new_button</cstring> + </property> + <property name="text"> + <string>&New...</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="1"> + <property name="name"> + <cstring>modify_button</cstring> + </property> + <property name="text"> + <string>&Modify...</string> + </property> + </widget> + <widget class="KPushButton" row="2" column="1"> + <property name="name"> + <cstring>delete_button</cstring> + </property> + <property name="text"> + <string>Delete</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KPushButton" row="3" column="1"> + <property name="name"> + <cstring>moveup_button</cstring> + </property> + <property name="text"> + <string>Move &Up</string> + </property> + </widget> + <widget class="KPushButton" row="4" column="1"> + <property name="name"> + <cstring>movedown_button</cstring> + </property> + <property name="text"> + <string>Move &Down</string> + </property> + </widget> + <spacer row="5" column="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>294</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kwin/kcmkwin/kwinrules/ruleswidget.cpp b/kwin/kcmkwin/kwinrules/ruleswidget.cpp new file mode 100644 index 000000000..7a8afe292 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleswidget.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ruleswidget.h" + +#include <klineedit.h> +#include <krestrictedline.h> +#include <kcombobox.h> +#include <qcheckbox.h> +#include <kpushbutton.h> +#include <qlabel.h> +#include <kwinmodule.h> +#include <klocale.h> +#include <qregexp.h> +#include <qwhatsthis.h> +#include <assert.h> +#include <kmessagebox.h> +#include <qtabwidget.h> +#include <qtimer.h> + +#include "../../rules.h" + +#include "detectwidget.h" + +namespace KWinInternal +{ + +#define SETUP( var, type ) \ + connect( enable_##var, SIGNAL( toggled( bool )), rule_##var, SLOT( setEnabled( bool ))); \ + connect( enable_##var, SIGNAL( toggled( bool )), this, SLOT( updateEnable##var())); \ + connect( rule_##var, SIGNAL( activated( int )), this, SLOT( updateEnable##var())); \ + QWhatsThis::add( enable_##var, enableDesc ); \ + QWhatsThis::add( rule_##var, type##RuleDesc ); + +RulesWidget::RulesWidget( QWidget* parent, const char* name ) +: RulesWidgetBase( parent, name ) +, detect_dlg( NULL ) + { + QString enableDesc = + i18n( "Enable this checkbox to alter this window property for the specified window(s)." ); + QString setRuleDesc = + i18n( "Specify how the window property should be affected:<ul>" + "<li><em>Do Not Affect:</em> The window property will not be affected and therefore" + " the default handling for it will be used. Specifying this will block more generic" + " window settings from taking effect.</li>" + "<li><em>Apply Initially:</em> The window property will be only set to the given value" + " after the window is created. No further changes will be affected.</li>" + "<li><em>Remember:</em> The value of the window property will be remembered and every time" + " time the window is created, the last remembered value will be applied.</li>" + "<li><em>Force:</em> The window property will be always forced to the given value.</li>" + "<li><em>Apply Now:</em> The window property will be set to the given value immediately" + " and will not be affected later (this action will be deleted afterwards).</li>" + "<li><em>Force temporarily:</em> The window property will be forced to the given value" + " until it is hidden (this action will be deleted after the window is hidden).</li>" + "</ul>" ); + QString forceRuleDesc = + i18n( "Specify how the window property should be affected:<ul>" + "<li><em>Do Not Affect:</em> The window property will not be affected and therefore" + " the default handling for it will be used. Specifying this will block more generic" + " window settings from taking effect.</li>" + "<li><em>Force:</em> The window property will be always forced to the given value.</li>" + "<li><em>Force temporarily:</em> The window property will be forced to the given value" + " until it is hidden (this action will be deleted after the window is hidden).</li>" + "</ul>" ); + // window tabs have enable signals done in designer + // geometry tab + SETUP( position, set ); + SETUP( size, set ); + SETUP( desktop, set ); + SETUP( maximizehoriz, set ); + SETUP( maximizevert, set ); + SETUP( minimize, set ); + SETUP( shade, set ); + SETUP( fullscreen, set ); + SETUP( placement, force ); + // preferences tab + SETUP( above, set ); + SETUP( below, set ); + SETUP( noborder, set ); + SETUP( skiptaskbar, set ); + SETUP( skippager, set ); + SETUP( acceptfocus, force ); + SETUP( closeable, force ); + SETUP( opacityactive, force ); + SETUP( opacityinactive, force ); + SETUP( shortcut, force ); + // workarounds tab + SETUP( fsplevel, force ); + SETUP( moveresizemode, force ); + SETUP( type, force ); + SETUP( ignoreposition, force ); + SETUP( minsize, force ); + SETUP( maxsize, force ); + SETUP( strictgeometry, force ); + SETUP( disableglobalshortcuts, force ); + KWinModule module; + int i; + for( i = 1; + i <= module.numberOfDesktops(); + ++i ) + desktop->insertItem( QString::number( i ).rightJustify( 2 ) + ":" + module.desktopName( i )); + desktop->insertItem( i18n( "All Desktops" )); + } + +#undef SETUP + +#define UPDATE_ENABLE_SLOT( var ) \ +void RulesWidget::updateEnable##var() \ + { \ + /* leave the label readable label_##var->setEnabled( enable_##var->isChecked() && rule_##var->currentItem() != 0 );*/ \ + var->setEnabled( enable_##var->isChecked() && rule_##var->currentItem() != 0 ); \ + } + +// geometry tab +UPDATE_ENABLE_SLOT( position ) +UPDATE_ENABLE_SLOT( size ) +UPDATE_ENABLE_SLOT( desktop ) +UPDATE_ENABLE_SLOT( maximizehoriz ) +UPDATE_ENABLE_SLOT( maximizevert ) +UPDATE_ENABLE_SLOT( minimize ) +UPDATE_ENABLE_SLOT( shade ) +UPDATE_ENABLE_SLOT( fullscreen ) +UPDATE_ENABLE_SLOT( placement ) +// preferences tab +UPDATE_ENABLE_SLOT( above ) +UPDATE_ENABLE_SLOT( below ) +UPDATE_ENABLE_SLOT( noborder ) +UPDATE_ENABLE_SLOT( skiptaskbar ) +UPDATE_ENABLE_SLOT( skippager ) +UPDATE_ENABLE_SLOT( acceptfocus ) +UPDATE_ENABLE_SLOT( closeable ) +UPDATE_ENABLE_SLOT( opacityactive ) +UPDATE_ENABLE_SLOT( opacityinactive ) +void RulesWidget::updateEnableshortcut() + { + shortcut->setEnabled( enable_shortcut->isChecked() && rule_shortcut->currentItem() != 0 ); + shortcut_edit->setEnabled( enable_shortcut->isChecked() && rule_shortcut->currentItem() != 0 ); + } +// workarounds tab +UPDATE_ENABLE_SLOT( fsplevel ) +UPDATE_ENABLE_SLOT( moveresizemode ) +UPDATE_ENABLE_SLOT( type ) +UPDATE_ENABLE_SLOT( ignoreposition ) +UPDATE_ENABLE_SLOT( minsize ) +UPDATE_ENABLE_SLOT( maxsize ) +UPDATE_ENABLE_SLOT( strictgeometry ) +UPDATE_ENABLE_SLOT( disableglobalshortcuts ) + +#undef UPDATE_ENABLE_SLOT + +static const int set_rule_to_combo[] = + { + 0, // Unused + 0, // Don't Affect + 3, // Force + 1, // Apply + 2, // Remember + 4, // ApplyNow + 5 // ForceTemporarily + }; + +static const Rules::SetRule combo_to_set_rule[] = + { + ( Rules::SetRule )Rules::DontAffect, + ( Rules::SetRule )Rules::Apply, + ( Rules::SetRule )Rules::Remember, + ( Rules::SetRule )Rules::Force, + ( Rules::SetRule )Rules::ApplyNow, + ( Rules::SetRule )Rules::ForceTemporarily + }; + +static const int force_rule_to_combo[] = + { + 0, // Unused + 0, // Don't Affect + 1, // Force + 0, // Apply + 0, // Remember + 0, // ApplyNow + 2 // ForceTemporarily + }; + +static const Rules::ForceRule combo_to_force_rule[] = + { + ( Rules::ForceRule )Rules::DontAffect, + ( Rules::ForceRule )Rules::Force, + ( Rules::ForceRule )Rules::ForceTemporarily + }; + +static QString positionToStr( const QPoint& p ) + { + if( p == invalidPoint ) + return QString::null; + return QString::number( p.x()) + "," + QString::number( p.y()); + } + +static QPoint strToPosition( const QString& str ) + { // two numbers, with + or -, separated by any of , x X : + QRegExp reg( "\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*" ); + if( !reg.exactMatch( str )) + return invalidPoint; + return QPoint( reg.cap( 1 ).toInt(), reg.cap( 2 ).toInt()); + } + +static QString sizeToStr( const QSize& s ) + { + if( !s.isValid()) + return QString::null; + return QString::number( s.width()) + "," + QString::number( s.height()); + } + +static QSize strToSize( const QString& str ) + { // two numbers, with + or -, separated by any of , x X : + QRegExp reg( "\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*" ); + if( !reg.exactMatch( str )) + return QSize(); + return QSize( reg.cap( 1 ).toInt(), reg.cap( 2 ).toInt()); + } + +//used for opacity settings +static QString intToStr( const int& s ) + { + if( s < 1 || s > 100 ) + return QString::null; + return QString::number(s); + } + +static int strToInt( const QString& str ) + { + int tmp = str.toInt(); + if( tmp < 1 || tmp > 100 ) + return 100; + return tmp; + } + +int RulesWidget::desktopToCombo( int d ) const + { + if( d >= 1 && d < desktop->count()) + return d - 1; + return desktop->count() - 1; // on all desktops + } + +int RulesWidget::comboToDesktop( int val ) const + { + if( val == desktop->count() - 1 ) + return NET::OnAllDesktops; + return val + 1; + } + +static int placementToCombo( Placement::Policy placement ) + { + static const int conv[] = + { + 1, // NoPlacement + 0, // Default + 0, // Unknown + 6, // Random + 2, // Smart + 4, // Cascade + 5, // Centered + 7, // ZeroCornered + 8, // UnderMouse + 9, // OnMainWindow + 3 // Maximizing + }; + return conv[ placement ]; + } + +static Placement::Policy comboToPlacement( int val ) + { + static const Placement::Policy conv[] = + { + Placement::Default, + Placement::NoPlacement, + Placement::Smart, + Placement::Maximizing, + Placement::Cascade, + Placement::Centered, + Placement::Random, + Placement::ZeroCornered, + Placement::UnderMouse, + Placement::OnMainWindow + // no Placement::Unknown + }; + return conv[ val ]; + } + +static int moveresizeToCombo( Options::MoveResizeMode mode ) + { + return mode == Options::Opaque ? 0 : 1; + } + +static Options::MoveResizeMode comboToMoveResize( int val ) + { + return val == 0 ? Options::Opaque : Options::Transparent; + } + +static int typeToCombo( NET::WindowType type ) + { + if( type < NET::Normal || type > NET::Splash ) + return 0; // Normal + static const int conv[] = + { + 0, // Normal + 7, // Desktop + 3, // Dock + 4, // Toolbar + 5, // Menu + 1, // Dialog + 8, // Override + 9, // TopMenu + 2, // Utility + 6 // Splash + }; + return conv[ type ]; + } + +static NET::WindowType comboToType( int val ) + { + static const NET::WindowType conv[] = + { + NET::Normal, + NET::Dialog, + NET::Utility, + NET::Dock, + NET::Toolbar, + NET::Menu, + NET::Splash, + NET::Desktop, + NET::Override, + NET::TopMenu + }; + return conv[ val ]; + } + +#define GENERIC_RULE( var, func, Type, type, uimethod, uimethod0 ) \ + if( rules->var##rule == Rules::Unused##Type##Rule ) \ + { \ + enable_##var->setChecked( false ); \ + rule_##var->setCurrentItem( 0 ); \ + var->uimethod0; \ + updateEnable##var(); \ + } \ + else \ + { \ + enable_##var->setChecked( true ); \ + rule_##var->setCurrentItem( type##_rule_to_combo[ rules->var##rule ] ); \ + var->uimethod( func( rules->var )); \ + updateEnable##var(); \ + } + +#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setChecked, setChecked( false )) +#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setText, setText( "" )) +#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setCurrentItem, setCurrentItem( 0 )) +#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setChecked, setChecked( false )) +#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setText, setText( "" )) +#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setCurrentItem, setCurrentItem( 0 )) + +void RulesWidget::setRules( Rules* rules ) + { + Rules tmp; + if( rules == NULL ) + rules = &tmp; // empty + description->setText( rules->description ); + wmclass->setText( rules->wmclass ); + whole_wmclass->setChecked( rules->wmclasscomplete ); + wmclass_match->setCurrentItem( rules->wmclassmatch ); + wmclassMatchChanged(); + role->setText( rules->windowrole ); + role_match->setCurrentItem( rules->windowrolematch ); + roleMatchChanged(); + types->setSelected( 0, rules->types & NET::NormalMask ); + types->setSelected( 1, rules->types & NET::DialogMask ); + types->setSelected( 2, rules->types & NET::UtilityMask ); + types->setSelected( 3, rules->types & NET::DockMask ); + types->setSelected( 4, rules->types & NET::ToolbarMask ); + types->setSelected( 5, rules->types & NET::MenuMask ); + types->setSelected( 6, rules->types & NET::SplashMask ); + types->setSelected( 7, rules->types & NET::DesktopMask ); + types->setSelected( 8, rules->types & NET::OverrideMask ); + types->setSelected( 9, rules->types & NET::TopMenuMask ); + title->setText( rules->title ); + title_match->setCurrentItem( rules->titlematch ); + titleMatchChanged(); + extra->setText( rules->extrarole ); + extra_match->setCurrentItem( rules->extrarolematch ); + extraMatchChanged(); + machine->setText( rules->clientmachine ); + machine_match->setCurrentItem( rules->clientmachinematch ); + machineMatchChanged(); + LINEEDIT_SET_RULE( position, positionToStr ); + LINEEDIT_SET_RULE( size, sizeToStr ); + COMBOBOX_SET_RULE( desktop, desktopToCombo ); + CHECKBOX_SET_RULE( maximizehoriz, ); + CHECKBOX_SET_RULE( maximizevert, ); + CHECKBOX_SET_RULE( minimize, ); + CHECKBOX_SET_RULE( shade, ); + CHECKBOX_SET_RULE( fullscreen, ); + COMBOBOX_FORCE_RULE( placement, placementToCombo ); + CHECKBOX_SET_RULE( above, ); + CHECKBOX_SET_RULE( below, ); + CHECKBOX_SET_RULE( noborder, ); + CHECKBOX_SET_RULE( skiptaskbar, ); + CHECKBOX_SET_RULE( skippager, ); + CHECKBOX_FORCE_RULE( acceptfocus, ); + CHECKBOX_FORCE_RULE( closeable, ); + LINEEDIT_FORCE_RULE( opacityactive, intToStr ); + LINEEDIT_FORCE_RULE( opacityinactive, intToStr ); + LINEEDIT_SET_RULE( shortcut, ); + COMBOBOX_FORCE_RULE( fsplevel, ); + COMBOBOX_FORCE_RULE( moveresizemode, moveresizeToCombo ); + COMBOBOX_FORCE_RULE( type, typeToCombo ); + CHECKBOX_FORCE_RULE( ignoreposition, ); + LINEEDIT_FORCE_RULE( minsize, sizeToStr ); + LINEEDIT_FORCE_RULE( maxsize, sizeToStr ); + CHECKBOX_FORCE_RULE( strictgeometry, ); + CHECKBOX_FORCE_RULE( disableglobalshortcuts, ); + } + +#undef GENERIC_RULE +#undef CHECKBOX_SET_RULE +#undef LINEEDIT_SET_RULE +#undef COMBOBOX_SET_RULE +#undef CHECKBOX_FORCE_RULE +#undef LINEEDIT_FORCE_RULE +#undef COMBOBOX_FORCE_RULE + +#define GENERIC_RULE( var, func, Type, type, uimethod ) \ + if( enable_##var->isChecked()) \ + { \ + rules->var##rule = combo_to_##type##_rule[ rule_##var->currentItem() ]; \ + rules->var = func( var->uimethod()); \ + } \ + else \ + rules->var##rule = Rules::Unused##Type##Rule; + +#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, isChecked ) +#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, text ) +#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, currentItem ) +#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, isChecked ) +#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, text ) +#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, currentItem ) + +Rules* RulesWidget::rules() const + { + Rules* rules = new Rules(); + rules->description = description->text(); + rules->wmclass = wmclass->text().utf8(); + rules->wmclasscomplete = whole_wmclass->isChecked(); + rules->wmclassmatch = static_cast< Rules::StringMatch >( wmclass_match->currentItem()); + rules->windowrole = role->text().utf8(); + rules->windowrolematch = static_cast< Rules::StringMatch >( role_match->currentItem()); + rules->types = 0; + bool all_types = true; + for( unsigned int i = 0; + i < types->count(); + ++i ) + if( !types->isSelected( i )) + all_types = false; + if( all_types ) // if all types are selected, use AllTypesMask (for future expansion) + rules->types = NET::AllTypesMask; + else + { + rules->types |= types->isSelected( 0 ) ? NET::NormalMask : 0; + rules->types |= types->isSelected( 1 ) ? NET::DialogMask : 0; + rules->types |= types->isSelected( 2 ) ? NET::UtilityMask : 0; + rules->types |= types->isSelected( 3 ) ? NET::DockMask : 0; + rules->types |= types->isSelected( 4 ) ? NET::ToolbarMask : 0; + rules->types |= types->isSelected( 5 ) ? NET::MenuMask : 0; + rules->types |= types->isSelected( 6 ) ? NET::SplashMask : 0; + rules->types |= types->isSelected( 7 ) ? NET::DesktopMask : 0; + rules->types |= types->isSelected( 8 ) ? NET::OverrideMask : 0; + rules->types |= types->isSelected( 9 ) ? NET::TopMenuMask : 0; + } + rules->title = title->text(); + rules->titlematch = static_cast< Rules::StringMatch >( title_match->currentItem()); + rules->extrarole = extra->text().utf8(); + rules->extrarolematch = static_cast< Rules::StringMatch >( extra_match->currentItem()); + rules->clientmachine = machine->text().utf8(); + rules->clientmachinematch = static_cast< Rules::StringMatch >( machine_match->currentItem()); + LINEEDIT_SET_RULE( position, strToPosition ); + LINEEDIT_SET_RULE( size, strToSize ); + COMBOBOX_SET_RULE( desktop, comboToDesktop ); + CHECKBOX_SET_RULE( maximizehoriz, ); + CHECKBOX_SET_RULE( maximizevert, ); + CHECKBOX_SET_RULE( minimize, ); + CHECKBOX_SET_RULE( shade, ); + CHECKBOX_SET_RULE( fullscreen, ); + COMBOBOX_FORCE_RULE( placement, comboToPlacement ); + CHECKBOX_SET_RULE( above, ); + CHECKBOX_SET_RULE( below, ); + CHECKBOX_SET_RULE( noborder, ); + CHECKBOX_SET_RULE( skiptaskbar, ); + CHECKBOX_SET_RULE( skippager, ); + CHECKBOX_FORCE_RULE( acceptfocus, ); + CHECKBOX_FORCE_RULE( closeable, ); + LINEEDIT_FORCE_RULE( opacityactive, strToInt ); + LINEEDIT_FORCE_RULE( opacityinactive, strToInt ); + LINEEDIT_SET_RULE( shortcut, ); + COMBOBOX_FORCE_RULE( fsplevel, ); + COMBOBOX_FORCE_RULE( moveresizemode, comboToMoveResize ); + COMBOBOX_FORCE_RULE( type, comboToType ); + CHECKBOX_FORCE_RULE( ignoreposition, ); + LINEEDIT_FORCE_RULE( minsize, strToSize ); + LINEEDIT_FORCE_RULE( maxsize, strToSize ); + CHECKBOX_FORCE_RULE( strictgeometry, ); + CHECKBOX_FORCE_RULE( disableglobalshortcuts, ); + return rules; + } + +#undef GENERIC_RULE +#undef CHECKBOX_SET_RULE +#undef LINEEDIT_SET_RULE +#undef COMBOBOX_SET_RULE +#undef CHECKBOX_FORCE_RULE +#undef LINEEDIT_FORCE_RULE +#undef COMBOBOX_FORCE_RULE + +#define STRING_MATCH_COMBO( type ) \ +void RulesWidget::type##MatchChanged() \ + { \ + edit_reg_##type->setEnabled( type##_match->currentItem() == Rules::RegExpMatch ); \ + type->setEnabled( type##_match->currentItem() != Rules::UnimportantMatch ); \ + } + +STRING_MATCH_COMBO( wmclass ) +STRING_MATCH_COMBO( role ) +STRING_MATCH_COMBO( title ) +STRING_MATCH_COMBO( extra ) +STRING_MATCH_COMBO( machine ) + +#undef STRING_MATCH_COMBO + +void RulesWidget::detectClicked() + { + assert( detect_dlg == NULL ); + detect_dlg = new DetectDialog; + connect( detect_dlg, SIGNAL( detectionDone( bool )), this, SLOT( detected( bool ))); + detect_dlg->detect( 0 ); + } + +void RulesWidget::detected( bool ok ) + { + if( ok ) + { + wmclass->setText( detect_dlg->selectedClass()); + wmclass_match->setCurrentItem( Rules::ExactMatch ); + wmclassMatchChanged(); // grrr + whole_wmclass->setChecked( detect_dlg->selectedWholeClass()); + role->setText( detect_dlg->selectedRole()); + role_match->setCurrentItem( detect_dlg->selectedRole().isEmpty() + ? Rules::UnimportantMatch : Rules::ExactMatch ); + roleMatchChanged(); + if( detect_dlg->selectedWholeApp()) + { + for( unsigned int i = 0; + i < types->count(); + ++i ) + types->setSelected( i, true ); + } + else + { + NET::WindowType type = detect_dlg->selectedType(); + for( unsigned int i = 0; + i < types->count(); + ++i ) + types->setSelected( i, false ); + types->setSelected( typeToCombo( type ), true ); + } + title->setText( detect_dlg->selectedTitle()); + title_match->setCurrentItem( detect_dlg->titleMatch()); + titleMatchChanged(); + machine->setText( detect_dlg->selectedMachine()); + machine_match->setCurrentItem( Rules::UnimportantMatch ); + machineMatchChanged(); + // prefill values from to window to settings which already set + const KWin::WindowInfo& info = detect_dlg->windowInfo(); + prefillUnusedValues( info ); + } + delete detect_dlg; + detect_dlg = NULL; + detect_dlg_ok = ok; + } + +#define GENERIC_PREFILL( var, func, info, uimethod ) \ + if( !enable_##var->isChecked()) \ + { \ + var->uimethod( func( info )); \ + } + +#define CHECKBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setChecked ) +#define LINEEDIT_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setText ) +#define COMBOBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setCurrentItem ) + +void RulesWidget::prefillUnusedValues( const KWin::WindowInfo& info ) + { + LINEEDIT_PREFILL( position, positionToStr, info.frameGeometry().topLeft() ); + LINEEDIT_PREFILL( size, sizeToStr, info.frameGeometry().size() ); + COMBOBOX_PREFILL( desktop, desktopToCombo, info.desktop() ); + CHECKBOX_PREFILL( maximizehoriz,, info.state() & NET::MaxHoriz ); + CHECKBOX_PREFILL( maximizevert,, info.state() & NET::MaxVert ); + CHECKBOX_PREFILL( minimize,, info.isMinimized() ); + CHECKBOX_PREFILL( shade,, info.state() & NET::Shaded ); + CHECKBOX_PREFILL( fullscreen,, info.state() & NET::FullScreen ); + //COMBOBOX_PREFILL( placement, placementToCombo ); + CHECKBOX_PREFILL( above,, info.state() & NET::KeepAbove ); + CHECKBOX_PREFILL( below,, info.state() & NET::KeepBelow ); + // noborder is only internal KWin information, so let's guess + CHECKBOX_PREFILL( noborder,, info.frameGeometry() == info.geometry() ); + CHECKBOX_PREFILL( skiptaskbar,, info.state() & NET::SkipTaskbar ); + CHECKBOX_PREFILL( skippager,, info.state() & NET::SkipPager ); + //CHECKBOX_PREFILL( acceptfocus, ); + //CHECKBOX_PREFILL( closeable, ); + LINEEDIT_PREFILL( opacityactive, intToStr, 100 /*get the actual opacity somehow*/); + LINEEDIT_PREFILL( opacityinactive, intToStr, 100 /*get the actual opacity somehow*/); + //LINEEDIT_PREFILL( shortcut, ); + //COMBOBOX_PREFILL( fsplevel, ); + //COMBOBOX_PREFILL( moveresizemode, moveresizeToCombo ); + COMBOBOX_PREFILL( type, typeToCombo, info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) ); + //CHECKBOX_PREFILL( ignoreposition, ); + LINEEDIT_PREFILL( minsize, sizeToStr, info.frameGeometry().size() ); + LINEEDIT_PREFILL( maxsize, sizeToStr, info.frameGeometry().size() ); + //CHECKBOX_PREFILL( strictgeometry, ); + //CHECKBOX_PREFILL( disableglobalshortcuts, ); + } + +#undef GENERIC_PREFILL +#undef CHECKBOX_PREFILL +#undef LINEEDIT_PREFILL +#undef COMBOBOX_PREFILL + +bool RulesWidget::finalCheck() + { + if( description->text().isEmpty()) + { + if( !wmclass->text().isEmpty()) + description->setText( i18n( "Settings for %1" ).arg( wmclass->text())); + else + description->setText( i18n( "Unnamed entry" )); + } + bool all_types = true; + for( unsigned int i = 0; + i < types->count(); + ++i ) + if( !types->isSelected( i )) + all_types = false; + if( wmclass_match->currentItem() == Rules::UnimportantMatch && all_types ) + { + if( KMessageBox::warningContinueCancel( topLevelWidget(), + i18n( "You have specified the window class as unimportant.\n" + "This means the settings will possibly apply to windows from all applications. " + "If you really want to create a generic setting, it is recommended you at least " + "limit the window types to avoid special window types." )) != KMessageBox::Continue ) + return false; + } + return true; + } + +void RulesWidget::prepareWindowSpecific( WId window ) + { + tabs->setCurrentPage( 2 ); // geometry tab, skip tabs for window identification + KWin::WindowInfo info( window, -1U, -1U ); // read everything + prefillUnusedValues( info ); + } + +void RulesWidget::shortcutEditClicked() + { + EditShortcutDialog dlg( topLevelWidget()); + dlg.setShortcut( shortcut->text()); + if( dlg.exec() == QDialog::Accepted ) + shortcut->setText( dlg.shortcut()); + } + +RulesDialog::RulesDialog( QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "Edit Window-Specific Settings" ), Ok | Cancel ) + { + widget = new RulesWidget( this ); + setMainWidget( widget ); + } + +// window is set only for Alt+F3/Window-specific settings, because the dialog +// is then related to one specific window +Rules* RulesDialog::edit( Rules* r, WId window, bool show_hints ) + { + rules = r; + widget->setRules( rules ); + if( window != 0 ) + widget->prepareWindowSpecific( window ); + if( show_hints ) + QTimer::singleShot( 0, this, SLOT( displayHints())); + exec(); + return rules; + } + +void RulesDialog::displayHints() + { + QString str = "<qt><p>"; + str += i18n( "This configuration dialog allows altering settings only for the selected window" + " or application. Find the setting you want to affect, enable the setting using the checkbox," + " select in what way the setting should be affected and to which value." ); +#if 0 // maybe later + str += "</p><p>" + i18n( "Consult the documentation for more details." ); +#endif + str += "</p></qt>"; + KMessageBox::information( this, str, QString::null, "displayhints" ); + } + +void RulesDialog::accept() + { + if( !widget->finalCheck()) + return; + rules = widget->rules(); + KDialogBase::accept(); + } + +EditShortcut::EditShortcut( QWidget* parent, const char* name ) +: EditShortcutBase( parent, name ) + { + } + +void EditShortcut::editShortcut() + { + ShortcutDialog dlg( KShortcut( shortcut->text()), topLevelWidget()); + if( dlg.exec() == QDialog::Accepted ) + shortcut->setText( dlg.shortcut().toString()); + } + +void EditShortcut::clearShortcut() + { + shortcut->setText( "" ); + } + +EditShortcutDialog::EditShortcutDialog( QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "Edit Shortcut" ), Ok | Cancel ) + { + widget = new EditShortcut( this ); + setMainWidget( widget ); + } + +void EditShortcutDialog::setShortcut( const QString& cut ) + { + widget->shortcut->setText( cut ); + } + +QString EditShortcutDialog::shortcut() const + { + return widget->shortcut->text(); + } + +ShortcutDialog::ShortcutDialog( const KShortcut& cut, QWidget* parent, const char* name ) + : KShortcutDialog( cut, false /*TODO???*/, parent, name ) + { + } + +void ShortcutDialog::accept() + { + for( int i = 0; + ; + ++i ) + { + KKeySequence seq = shortcut().seq( i ); + if( seq.isNull()) + break; + if( seq.key( 0 ) == Key_Escape ) + { + reject(); + return; + } + if( seq.key( 0 ) == Key_Space ) + { // clear + setShortcut( KShortcut()); + KShortcutDialog::accept(); + return; + } + if( seq.key( 0 ).modFlags() == 0 ) + { // no shortcuts without modifiers + KShortcut cut = shortcut(); + cut.setSeq( i, KKeySequence()); + setShortcut( cut ); + return; + } + } + KShortcutDialog::accept(); + } + +} // namespace + +#include "ruleswidget.moc" diff --git a/kwin/kcmkwin/kwinrules/ruleswidget.h b/kwin/kcmkwin/kwinrules/ruleswidget.h new file mode 100644 index 000000000..94af87ef4 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleswidget.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __RULESWIDGET_H__ +#define __RULESWIDGET_H__ + +#include <kdialogbase.h> +#include <kwin.h> +#include <kshortcutdialog.h> + +#include "ruleswidgetbase.h" +#include "editshortcutbase.h" + +namespace KWinInternal +{ + +class Rules; +class DetectDialog; + +class RulesWidget + : public RulesWidgetBase + { + Q_OBJECT + public: + RulesWidget( QWidget* parent = NULL, const char* name = NULL ); + void setRules( Rules* r ); + Rules* rules() const; + bool finalCheck(); + void prepareWindowSpecific( WId window ); + signals: + void changed( bool state ); + protected slots: + virtual void detectClicked(); + virtual void wmclassMatchChanged(); + virtual void roleMatchChanged(); + virtual void titleMatchChanged(); + virtual void extraMatchChanged(); + virtual void machineMatchChanged(); + virtual void shortcutEditClicked(); + private slots: + // geometry tab + void updateEnableposition(); + void updateEnablesize(); + void updateEnabledesktop(); + void updateEnablemaximizehoriz(); + void updateEnablemaximizevert(); + void updateEnableminimize(); + void updateEnableshade(); + void updateEnablefullscreen(); + void updateEnableplacement(); + // preferences tab + void updateEnableabove(); + void updateEnablebelow(); + void updateEnablenoborder(); + void updateEnableskiptaskbar(); + void updateEnableskippager(); + void updateEnableacceptfocus(); + void updateEnablecloseable(); + void updateEnableopacityactive(); + void updateEnableopacityinactive(); + // workarounds tab + void updateEnablefsplevel(); + void updateEnablemoveresizemode(); + void updateEnabletype(); + void updateEnableignoreposition(); + void updateEnableminsize(); + void updateEnablemaxsize(); + void updateEnablestrictgeometry(); + void updateEnableshortcut(); + void updateEnabledisableglobalshortcuts(); + // internal + void detected( bool ); + private: + int desktopToCombo( int d ) const; + int comboToDesktop( int val ) const; + void prefillUnusedValues( const KWin::WindowInfo& info ); + DetectDialog* detect_dlg; + bool detect_dlg_ok; + }; + +class RulesDialog + : public KDialogBase + { + Q_OBJECT + public: + RulesDialog( QWidget* parent = NULL, const char* name = NULL ); + Rules* edit( Rules* r, WId window, bool show_hints ); + protected: + virtual void accept(); + private slots: + void displayHints(); + private: + RulesWidget* widget; + Rules* rules; + }; + +class EditShortcut + : public EditShortcutBase + { + Q_OBJECT + public: + EditShortcut( QWidget* parent = NULL, const char* name = NULL ); + protected: + void editShortcut(); + void clearShortcut(); + }; + +class EditShortcutDialog + : public KDialogBase + { + Q_OBJECT + public: + EditShortcutDialog( QWidget* parent = NULL, const char* name = NULL ); + void setShortcut( const QString& cut ); + QString shortcut() const; + private: + EditShortcut* widget; + }; + +// slightly duped from utils.cpp +class ShortcutDialog + : public KShortcutDialog + { + Q_OBJECT + public: + ShortcutDialog( const KShortcut& cut, QWidget* parent = NULL, const char* name = NULL ); + virtual void accept(); + }; + +} // namespace + +#endif diff --git a/kwin/kcmkwin/kwinrules/ruleswidgetbase.ui b/kwin/kcmkwin/kwinrules/ruleswidgetbase.ui new file mode 100644 index 000000000..01c1b9918 --- /dev/null +++ b/kwin/kcmkwin/kwinrules/ruleswidgetbase.ui @@ -0,0 +1,2597 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KWinInternal::RulesWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Form2</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>630</width> + <height>503</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabs</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Window</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>De&scription:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>description</cstring> + </property> + </widget> + <widget class="KLineEdit" row="1" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>description</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Window &class (application type):</string> + </property> + <property name="buddy" stdset="0"> + <cstring>wmclass</cstring> + </property> + </widget> + <widget class="QLabel" row="5" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Window &role:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>role</cstring> + </property> + </widget> + <widget class="KLineEdit" row="6" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>role</cstring> + </property> + </widget> + <spacer row="9" column="1"> + <property name="name"> + <cstring>spacer29</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="7" column="0"> + <item> + <property name="text"> + <string>Unimportant</string> + </property> + </item> + <item> + <property name="text"> + <string>Exact Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Substring Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Regular Expression</string> + </property> + </item> + <property name="name"> + <cstring>role_match</cstring> + </property> + </widget> + <spacer row="7" column="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>212</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="7" column="2"> + <property name="name"> + <cstring>edit_reg_role</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit</string> + </property> + </widget> + <spacer row="7" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>211</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="4" column="0"> + <item> + <property name="text"> + <string>Unimportant</string> + </property> + </item> + <item> + <property name="text"> + <string>Exact Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Substring Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Regular Expression</string> + </property> + </item> + <property name="name"> + <cstring>wmclass_match</cstring> + </property> + </widget> + <spacer row="4" column="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>212</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="4" column="2"> + <property name="name"> + <cstring>edit_reg_wmclass</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer row="4" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>211</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KLineEdit" row="3" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>wmclass</cstring> + </property> + </widget> + <widget class="QCheckBox" row="3" column="4"> + <property name="name"> + <cstring>whole_wmclass</cstring> + </property> + <property name="text"> + <string>Match w&hole window class</string> + </property> + </widget> + <widget class="QGroupBox" row="8" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Detect Window Properties</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer27</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>270</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>detect1</cstring> + </property> + <property name="text"> + <string>&Detect</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer28</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>269</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Window &Extra</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Window &types:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>types</cstring> + </property> + </widget> + <widget class="KListBox" row="1" column="0" rowspan="1" colspan="4"> + <item> + <property name="text"> + <string>Normal Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Dialog Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Utility Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Dock (panel)</string> + </property> + </item> + <item> + <property name="text"> + <string>Toolbar</string> + </property> + </item> + <item> + <property name="text"> + <string>Torn-Off Menu</string> + </property> + </item> + <item> + <property name="text"> + <string>Splash Screen</string> + </property> + </item> + <item> + <property name="text"> + <string>Desktop</string> + </property> + </item> + <item> + <property name="text"> + <string>Override Type</string> + </property> + </item> + <item> + <property name="text"> + <string>Standalone Menubar</string> + </property> + </item> + <property name="name"> + <cstring>types</cstring> + </property> + <property name="selectionMode"> + <enum>Multi</enum> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Window t&itle:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>title</cstring> + </property> + </widget> + <widget class="KLineEdit" row="3" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>title</cstring> + </property> + </widget> + <widget class="QLabel" row="5" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Extra role:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>extra</cstring> + </property> + </widget> + <widget class="KLineEdit" row="6" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>extra</cstring> + </property> + </widget> + <widget class="QLabel" row="8" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>&Machine (hostname):</string> + </property> + <property name="buddy" stdset="0"> + <cstring>machine</cstring> + </property> + </widget> + <widget class="KLineEdit" row="9" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>machine</cstring> + </property> + </widget> + <widget class="KComboBox" row="4" column="0"> + <item> + <property name="text"> + <string>Unimportant</string> + </property> + </item> + <item> + <property name="text"> + <string>Exact Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Substring Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Regular Expression</string> + </property> + </item> + <property name="name"> + <cstring>title_match</cstring> + </property> + </widget> + <spacer row="4" column="1"> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="4" column="2"> + <property name="name"> + <cstring>edit_reg_title</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer row="4" column="3"> + <property name="name"> + <cstring>spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="7" column="0"> + <item> + <property name="text"> + <string>Unimportant</string> + </property> + </item> + <item> + <property name="text"> + <string>Exact Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Substring Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Regular Expression</string> + </property> + </item> + <property name="name"> + <cstring>extra_match</cstring> + </property> + </widget> + <spacer row="7" column="1"> + <property name="name"> + <cstring>spacer2_2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="7" column="2"> + <property name="name"> + <cstring>edit_reg_extra</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer row="7" column="3"> + <property name="name"> + <cstring>spacer5_2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="10" column="0"> + <item> + <property name="text"> + <string>Unimportant</string> + </property> + </item> + <item> + <property name="text"> + <string>Exact Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Substring Match</string> + </property> + </item> + <item> + <property name="text"> + <string>Regular Expression</string> + </property> + </item> + <property name="name"> + <cstring>machine_match</cstring> + </property> + </widget> + <spacer row="10" column="1"> + <property name="name"> + <cstring>spacer2_2_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="10" column="2"> + <property name="name"> + <cstring>edit_reg_machine</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer row="10" column="3"> + <property name="name"> + <cstring>spacer5_2_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>199</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>&Geometry</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_size</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KRestrictedLine" row="1" column="2"> + <property name="name"> + <cstring>size</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789-+,xX:</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>enable_size</cstring> + </property> + <property name="text"> + <string>&Size</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>enable_position</cstring> + </property> + <property name="text"> + <string>&Position</string> + </property> + </widget> + <widget class="KRestrictedLine" row="0" column="2"> + <property name="name"> + <cstring>position</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789-+,xX:</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_position</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="3" column="2"> + <property name="name"> + <cstring>maximizevert</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_maximizehoriz</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="4" column="2"> + <property name="name"> + <cstring>fullscreen</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>enable_maximizehoriz</cstring> + </property> + <property name="text"> + <string>Maximized &horizontally</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="2"> + <property name="name"> + <cstring>maximizehoriz</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="4" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_fullscreen</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0"> + <property name="name"> + <cstring>enable_fullscreen</cstring> + </property> + <property name="text"> + <string>&Fullscreen</string> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_maximizevert</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>enable_maximizevert</cstring> + </property> + <property name="text"> + <string>Maximized &vertically</string> + </property> + </widget> + <widget class="KComboBox" row="5" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_desktop</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="5" column="2"> + <property name="name"> + <cstring>desktop</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="5" column="0"> + <property name="name"> + <cstring>enable_desktop</cstring> + </property> + <property name="text"> + <string>&Desktop</string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="0"> + <property name="name"> + <cstring>enable_shade</cstring> + </property> + <property name="text"> + <string>Sh&aded</string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="2"> + <property name="name"> + <cstring>shade</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="6" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_minimize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="6" column="2"> + <property name="name"> + <cstring>minimize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="6" column="0"> + <property name="name"> + <cstring>enable_minimize</cstring> + </property> + <property name="text"> + <string>M&inimized</string> + </property> + </widget> + <widget class="KComboBox" row="8" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_placement</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="8" column="2"> + <item> + <property name="text"> + <string>Default</string> + </property> + </item> + <item> + <property name="text"> + <string>No Placement</string> + </property> + </item> + <item> + <property name="text"> + <string>Smart</string> + </property> + </item> + <item> + <property name="text"> + <string>Maximizing</string> + </property> + </item> + <item> + <property name="text"> + <string>Cascade</string> + </property> + </item> + <item> + <property name="text"> + <string>Centered</string> + </property> + </item> + <item> + <property name="text"> + <string>Random</string> + </property> + </item> + <item> + <property name="text"> + <string>Top-Left Corner</string> + </property> + </item> + <item> + <property name="text"> + <string>Under Mouse</string> + </property> + </item> + <item> + <property name="text"> + <string>On Main Window</string> + </property> + </item> + <property name="name"> + <cstring>placement</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="8" column="0"> + <property name="name"> + <cstring>enable_placement</cstring> + </property> + <property name="text"> + <string>P&lacement</string> + </property> + </widget> + <spacer row="9" column="1"> + <property name="name"> + <cstring>spacer31</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="7" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_shade</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>&Preferences</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>enable_above</cstring> + </property> + <property name="text"> + <string>Keep &above</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>enable_below</cstring> + </property> + <property name="text"> + <string>Keep &below</string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0"> + <property name="name"> + <cstring>enable_skippager</cstring> + </property> + <property name="text"> + <string>Skip pa&ger</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>enable_skiptaskbar</cstring> + </property> + <property name="text"> + <string>Skip &taskbar</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>enable_noborder</cstring> + </property> + <property name="text"> + <string>&No border</string> + </property> + </widget> + <widget class="QCheckBox" row="5" column="0"> + <property name="name"> + <cstring>enable_acceptfocus</cstring> + </property> + <property name="text"> + <string>Accept &focus</string> + </property> + </widget> + <widget class="QCheckBox" row="6" column="0"> + <property name="name"> + <cstring>enable_closeable</cstring> + </property> + <property name="text"> + <string>&Closeable</string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="0"> + <property name="name"> + <cstring>enable_opacityactive</cstring> + </property> + <property name="text"> + <string>A&ctive opacity in %</string> + </property> + </widget> + <widget class="KComboBox" row="7" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_opacityactive</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KRestrictedLine" row="7" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>opacityactive</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789</string> + </property> + </widget> + <spacer row="7" column="4" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer24</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>171</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="6" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="5" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="4" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="3" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="2" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="1" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="0" column="3" rowspan="1" colspan="3"> + <property name="name"> + <cstring>spacer36</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>290</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="0" column="2"> + <property name="name"> + <cstring>above</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="2"> + <property name="name"> + <cstring>below</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="2"> + <property name="name"> + <cstring>noborder</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="2"> + <property name="name"> + <cstring>skiptaskbar</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="2"> + <property name="name"> + <cstring>skippager</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="5" column="2"> + <property name="name"> + <cstring>acceptfocus</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="6" column="2"> + <property name="name"> + <cstring>closeable</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="6" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_closeable</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="5" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_acceptfocus</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="4" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_skippager</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_skiptaskbar</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_noborder</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_below</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_above</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <spacer row="10" column="1"> + <property name="name"> + <cstring>spacer33</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>80</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="8" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_opacityinactive</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KRestrictedLine" row="8" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>opacityinactive</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789</string> + </property> + </widget> + <spacer row="8" column="4" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer25</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>181</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="8" column="0"> + <property name="name"> + <cstring>enable_opacityinactive</cstring> + </property> + <property name="text"> + <string>I&nactive opacity in %</string> + </property> + </widget> + <widget class="QCheckBox" row="9" column="0"> + <property name="name"> + <cstring>enable_shortcut</cstring> + </property> + <property name="text"> + <string>Shortcut</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="9" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Initially</string> + </property> + </item> + <item> + <property name="text"> + <string>Remember</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Apply Now</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_shortcut</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QPushButton" row="9" column="5"> + <property name="name"> + <cstring>shortcut_edit</cstring> + </property> + <property name="text"> + <string>Edit...</string> + </property> + </widget> + <widget class="KRestrictedLine" row="9" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>shortcut</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>W&orkarounds</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>enable_fsplevel</cstring> + </property> + <property name="text"> + <string>&Focus stealing prevention</string> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_type</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="2" column="3"> + <item> + <property name="text"> + <string>Normal Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Dialog Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Utility Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Dock (panel)</string> + </property> + </item> + <item> + <property name="text"> + <string>Toolbar</string> + </property> + </item> + <item> + <property name="text"> + <string>Torn-Off Menu</string> + </property> + </item> + <item> + <property name="text"> + <string>Splash Screen</string> + </property> + </item> + <item> + <property name="text"> + <string>Desktop</string> + </property> + </item> + <item> + <property name="text"> + <string>Override Type</string> + </property> + </item> + <item> + <property name="text"> + <string>Standalone Menubar</string> + </property> + </item> + <property name="name"> + <cstring>type</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="1" column="3"> + <item> + <property name="text"> + <string>Opaque</string> + </property> + </item> + <item> + <property name="text"> + <string>Transparent</string> + </property> + </item> + <property name="name"> + <cstring>moveresizemode</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>enable_type</cstring> + </property> + <property name="text"> + <string>Window &type</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>enable_moveresizemode</cstring> + </property> + <property name="text"> + <string>&Moving/resizing</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_fsplevel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_moveresizemode</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KComboBox" row="0" column="3"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Low</string> + </property> + </item> + <item> + <property name="text"> + <string>Normal</string> + </property> + </item> + <item> + <property name="text"> + <string>High</string> + </property> + </item> + <item> + <property name="text"> + <string>Extreme</string> + </property> + </item> + <property name="name"> + <cstring>fsplevel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="KRestrictedLine" row="5" column="3"> + <property name="name"> + <cstring>maxsize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789-+,xX:</string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0"> + <property name="name"> + <cstring>enable_minsize</cstring> + </property> + <property name="text"> + <string>M&inimum size</string> + </property> + </widget> + <widget class="KComboBox" row="4" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_minsize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="5" column="0"> + <property name="name"> + <cstring>enable_maxsize</cstring> + </property> + <property name="text"> + <string>M&aximum size</string> + </property> + </widget> + <widget class="KRestrictedLine" row="4" column="3"> + <property name="name"> + <cstring>minsize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="validChars"> + <string>0123456789-+,xX:</string> + </property> + </widget> + <widget class="KComboBox" row="5" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_maxsize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>enable_ignoreposition</cstring> + </property> + <property name="text"> + <string>Ignore requested &geometry</string> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_ignoreposition</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="3" column="3"> + <property name="name"> + <cstring>ignoreposition</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer row="8" column="2"> + <property name="name"> + <cstring>spacer35</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>160</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="6" column="0"> + <property name="name"> + <cstring>enable_strictgeometry</cstring> + </property> + <property name="text"> + <string>Strictly obey geometry</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="6" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_strictgeometry</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="6" column="3"> + <property name="name"> + <cstring>strictgeometry</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="3"> + <property name="name"> + <cstring>disableglobalshortcuts</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="0"> + <property name="name"> + <cstring>enable_disableglobalshortcuts</cstring> + </property> + <property name="text"> + <string>Block global shortcuts</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="KComboBox" row="7" column="1"> + <item> + <property name="text"> + <string>Do Not Affect</string> + </property> + </item> + <item> + <property name="text"> + <string>Force</string> + </property> + </item> + <item> + <property name="text"> + <string>Force Temporarily</string> + </property> + </item> + <property name="name"> + <cstring>rule_disableglobalshortcuts</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>detect1</sender> + <signal>clicked()</signal> + <receiver>Form2</receiver> + <slot>detectClicked()</slot> + </connection> + <connection> + <sender>wmclass_match</sender> + <signal>activated(int)</signal> + <receiver>Form2</receiver> + <slot>wmclassMatchChanged()</slot> + </connection> + <connection> + <sender>role_match</sender> + <signal>activated(int)</signal> + <receiver>Form2</receiver> + <slot>roleMatchChanged()</slot> + </connection> + <connection> + <sender>title_match</sender> + <signal>activated(int)</signal> + <receiver>Form2</receiver> + <slot>titleMatchChanged()</slot> + </connection> + <connection> + <sender>extra_match</sender> + <signal>activated(int)</signal> + <receiver>Form2</receiver> + <slot>extraMatchChanged()</slot> + </connection> + <connection> + <sender>machine_match</sender> + <signal>activated(int)</signal> + <receiver>Form2</receiver> + <slot>machineMatchChanged()</slot> + </connection> + <connection> + <sender>shortcut_edit</sender> + <signal>clicked()</signal> + <receiver>Form2</receiver> + <slot>shortcutEditClicked()</slot> + </connection> +</connections> +<tabstops> + <tabstop>tabs</tabstop> + <tabstop>description</tabstop> + <tabstop>wmclass</tabstop> + <tabstop>whole_wmclass</tabstop> + <tabstop>wmclass_match</tabstop> + <tabstop>edit_reg_wmclass</tabstop> + <tabstop>role</tabstop> + <tabstop>role_match</tabstop> + <tabstop>edit_reg_role</tabstop> + <tabstop>detect1</tabstop> + <tabstop>types</tabstop> + <tabstop>title</tabstop> + <tabstop>title_match</tabstop> + <tabstop>edit_reg_title</tabstop> + <tabstop>extra</tabstop> + <tabstop>extra_match</tabstop> + <tabstop>edit_reg_extra</tabstop> + <tabstop>machine</tabstop> + <tabstop>machine_match</tabstop> + <tabstop>edit_reg_machine</tabstop> + <tabstop>enable_position</tabstop> + <tabstop>rule_position</tabstop> + <tabstop>position</tabstop> + <tabstop>enable_size</tabstop> + <tabstop>rule_size</tabstop> + <tabstop>size</tabstop> + <tabstop>enable_maximizehoriz</tabstop> + <tabstop>rule_maximizehoriz</tabstop> + <tabstop>maximizehoriz</tabstop> + <tabstop>enable_maximizevert</tabstop> + <tabstop>rule_maximizevert</tabstop> + <tabstop>maximizevert</tabstop> + <tabstop>enable_fullscreen</tabstop> + <tabstop>rule_fullscreen</tabstop> + <tabstop>fullscreen</tabstop> + <tabstop>enable_desktop</tabstop> + <tabstop>rule_desktop</tabstop> + <tabstop>desktop</tabstop> + <tabstop>enable_minimize</tabstop> + <tabstop>rule_minimize</tabstop> + <tabstop>minimize</tabstop> + <tabstop>enable_shade</tabstop> + <tabstop>rule_shade</tabstop> + <tabstop>shade</tabstop> + <tabstop>enable_placement</tabstop> + <tabstop>rule_placement</tabstop> + <tabstop>placement</tabstop> + <tabstop>enable_above</tabstop> + <tabstop>rule_above</tabstop> + <tabstop>above</tabstop> + <tabstop>enable_below</tabstop> + <tabstop>rule_below</tabstop> + <tabstop>below</tabstop> + <tabstop>enable_noborder</tabstop> + <tabstop>rule_noborder</tabstop> + <tabstop>noborder</tabstop> + <tabstop>enable_skiptaskbar</tabstop> + <tabstop>rule_skiptaskbar</tabstop> + <tabstop>skiptaskbar</tabstop> + <tabstop>enable_skippager</tabstop> + <tabstop>rule_skippager</tabstop> + <tabstop>skippager</tabstop> + <tabstop>enable_acceptfocus</tabstop> + <tabstop>rule_acceptfocus</tabstop> + <tabstop>acceptfocus</tabstop> + <tabstop>enable_closeable</tabstop> + <tabstop>rule_closeable</tabstop> + <tabstop>closeable</tabstop> + <tabstop>enable_opacityactive</tabstop> + <tabstop>rule_opacityactive</tabstop> + <tabstop>opacityactive</tabstop> + <tabstop>enable_opacityinactive</tabstop> + <tabstop>rule_opacityinactive</tabstop> + <tabstop>opacityinactive</tabstop> + <tabstop>enable_shortcut</tabstop> + <tabstop>rule_shortcut</tabstop> + <tabstop>shortcut</tabstop> + <tabstop>shortcut_edit</tabstop> + <tabstop>enable_fsplevel</tabstop> + <tabstop>rule_fsplevel</tabstop> + <tabstop>fsplevel</tabstop> + <tabstop>enable_moveresizemode</tabstop> + <tabstop>rule_moveresizemode</tabstop> + <tabstop>moveresizemode</tabstop> + <tabstop>enable_type</tabstop> + <tabstop>rule_type</tabstop> + <tabstop>type</tabstop> + <tabstop>enable_ignoreposition</tabstop> + <tabstop>rule_ignoreposition</tabstop> + <tabstop>ignoreposition</tabstop> + <tabstop>enable_minsize</tabstop> + <tabstop>rule_minsize</tabstop> + <tabstop>minsize</tabstop> + <tabstop>enable_maxsize</tabstop> + <tabstop>rule_maxsize</tabstop> + <tabstop>maxsize</tabstop> + <tabstop>enable_strictgeometry</tabstop> + <tabstop>rule_strictgeometry</tabstop> + <tabstop>strictgeometry</tabstop> + <tabstop>enable_disableglobalshortcuts</tabstop> + <tabstop>rule_disableglobalshortcuts</tabstop> + <tabstop>disableglobalshortcuts</tabstop> +</tabstops> +<slots> + <slot access="protected" specifier="pure virtual">detectClicked()</slot> + <slot access="protected" specifier="pure virtual">wmclassMatchChanged()</slot> + <slot access="protected" specifier="pure virtual">roleMatchChanged()</slot> + <slot access="protected" specifier="pure virtual">titleMatchChanged()</slot> + <slot access="protected" specifier="pure virtual">extraMatchChanged()</slot> + <slot access="protected" specifier="pure virtual">machineMatchChanged()</slot> + <slot access="protected" specifier="pure virtual">shortcutEditClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klistbox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>krestrictedline.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/kwin/killer/Makefile.am b/kwin/killer/Makefile.am new file mode 100644 index 000000000..acbf5e77d --- /dev/null +++ b/kwin/killer/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = $(all_includes) + +bin_PROGRAMS = kwin_killer_helper + +kwin_killer_helper_SOURCES = killer.cpp +kwin_killer_helper_LDADD = $(LIB_KDEUI) +kwin_killer_helper_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +METASOURCES = AUTO diff --git a/kwin/killer/killer.cpp b/kwin/killer/killer.cpp new file mode 100644 index 000000000..eed473cf5 --- /dev/null +++ b/kwin/killer/killer.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** + + Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <klocale.h> +#include <unistd.h> +#include <kwin.h> +#include <X11/Xlib.h> + +static const KCmdLineOptions options[] = + { + // no need for I18N_NOOP(), this is not supposed to be used directly + { "pid <pid>", "PID of the application to terminate.", 0 }, + { "hostname <hostname>", "Hostname on which the application is running.", 0 }, + { "windowname <caption>", "Caption of the window to be terminated.", 0 }, + { "applicationname <name>", "Name of the application to be terminated.", 0 }, + { "wid <id>", "ID of resource belonging to the application.", 0 }, + { "timestamp <time>", "Time of user action causing killing.", 0 }, + KCmdLineLastOption + }; + +int main( int argc, char* argv[] ) + { + KLocale::setMainCatalogue( "kwin" ); // the messages are in kwin's .po file + KCmdLineArgs::init( argc, argv, "kwin_killer_helper", I18N_NOOP( "KWin" ), + I18N_NOOP( "KWin helper utility" ), "1.0" ); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + QCString hostname = args->getOption( "hostname" ); + bool pid_ok = false; + pid_t pid = args->getOption( "pid" ).toULong( &pid_ok ); + QString caption = QString::fromUtf8( args->getOption( "windowname" )); + QString appname = QString::fromLatin1( args->getOption( "applicationname" )); + bool id_ok = false; + Window id = args->getOption( "wid" ).toULong( &id_ok ); + bool time_ok = false; + Time timestamp =args->getOption( "timestamp" ).toULong( &time_ok ); + args->clear(); + if( !pid_ok || pid == 0 || !id_ok || id == None || !time_ok || timestamp == CurrentTime + || hostname.isEmpty() || caption.isEmpty() || appname.isEmpty()) + { + KCmdLineArgs::usage( i18n( "This helper utility is not supposed to be called directly." )); + return 1; + } + QString question = i18n( + "<qt>Window with title \"<b>%2</b>\" is not responding. " + "This window belongs to application <b>%1</b> (PID=%3, hostname=%4).<p>" + "Do you wish to terminate this application? (All unsaved data in this application will be lost.)</qt>" ) + .arg( appname ).arg( caption ).arg( pid ).arg( hostname ); + app.updateUserTimestamp( timestamp ); + if( KMessageBox::warningYesNoWId( id, question, QString::null, i18n("Terminate"), i18n("Keep Running") ) == KMessageBox::Yes ) + { + if( hostname != "localhost" ) + { + KProcess proc; + proc << "xon" << hostname << "kill" << pid; + proc.start( KProcess::DontCare ); + } + else + ::kill( pid, SIGKILL ); + XKillClient( qt_xdisplay(), id ); + } + } diff --git a/kwin/killwindow.cpp b/kwin/killwindow.cpp new file mode 100644 index 000000000..6d0152349 --- /dev/null +++ b/kwin/killwindow.cpp @@ -0,0 +1,112 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#ifndef QT_CLEAN_NAMESPACE +//#define QT_CLEAN_NAMESPACE +//#endif +#include "killwindow.h" +#include <qcursor.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +#include <X11/cursorfont.h> + +namespace KWinInternal +{ + +KillWindow::KillWindow( Workspace* ws ) + : workspace( ws ) + { + } + +KillWindow::~KillWindow() + { + } + +void KillWindow::start() + { + static Cursor kill_cursor = 0; + if (!kill_cursor) + kill_cursor = XCreateFontCursor(qt_xdisplay(), XC_pirate); + + if (XGrabPointer(qt_xdisplay(), qt_xrootwin(), False, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | + EnterWindowMask | LeaveWindowMask, + GrabModeAsync, GrabModeAsync, None, + kill_cursor, CurrentTime) == GrabSuccess) + { + XGrabKeyboard(qt_xdisplay(), qt_xrootwin(), False, + GrabModeAsync, GrabModeAsync, CurrentTime); + + XEvent ev; + int return_pressed = 0; + int escape_pressed = 0; + int button_released = 0; + + grabXServer(); + + while (!return_pressed && !escape_pressed && !button_released) + { + XMaskEvent(qt_xdisplay(), KeyPressMask | ButtonPressMask | + ButtonReleaseMask | PointerMotionMask, &ev); + + if (ev.type == KeyPress) + { + int kc = XKeycodeToKeysym(qt_xdisplay(), ev.xkey.keycode, 0); + int mx = 0; + int my = 0; + return_pressed = (kc == XK_Return) || (kc == XK_space); + escape_pressed = (kc == XK_Escape); + if (kc == XK_Left) mx = -10; + if (kc == XK_Right) mx = 10; + if (kc == XK_Up) my = -10; + if (kc == XK_Down) my = 10; + if (ev.xkey.state & ControlMask) + { + mx /= 10; + my /= 10; + } + QCursor::setPos(QCursor::pos()+QPoint(mx, my)); + } + + if (ev.type == ButtonRelease) + { + button_released = (ev.xbutton.button == Button1); + if ( ev.xbutton.button == Button3 ) + { + escape_pressed = TRUE; + break; + } + if( ev.xbutton.button == Button1 || ev.xbutton.button == Button2 ) + workspace->killWindowId(ev.xbutton.subwindow); + } + continue; + } + if (return_pressed) + { + Window root, child; + int dummy1, dummy2, dummy3, dummy4; + unsigned int dummy5; + if( XQueryPointer( qt_xdisplay(), qt_xrootwin(), &root, &child, + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5 ) == true + && child != None ) + workspace->killWindowId( child ); + } + + ungrabXServer(); + + XUngrabKeyboard(qt_xdisplay(), CurrentTime); + XUngrabPointer(qt_xdisplay(), CurrentTime); + } + } + +} // namespace diff --git a/kwin/killwindow.h b/kwin/killwindow.h new file mode 100644 index 000000000..3d94c7291 --- /dev/null +++ b/kwin/killwindow.h @@ -0,0 +1,35 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_KILLWINDOW_H +#define KWIN_KILLWINDOW_H + +#include "workspace.h" + +namespace KWinInternal +{ + +class KillWindow + { + public: + + KillWindow( Workspace* ws ); + ~KillWindow(); + + void start(); + + private: + Workspace* workspace; + }; + +} // namespace + +#endif diff --git a/kwin/kompmgr/Makefile.am b/kwin/kompmgr/Makefile.am new file mode 100644 index 000000000..6cc8ce434 --- /dev/null +++ b/kwin/kompmgr/Makefile.am @@ -0,0 +1,6 @@ +bin_PROGRAMS = kompmgr + +kompmgr_SOURCES = kompmgr.c +kompmgr_LDADD = $(X_LDFLAGS) $(X_RPATH) -lXcomposite -lXdamage -lXfixes $(LIB_XRENDER) $(LIB_X11) -lXext -lm + +INCLUDES = $(all_includes) diff --git a/kwin/kompmgr/configure.in.in b/kwin/kompmgr/configure.in.in new file mode 100644 index 000000000..bbabdd73c --- /dev/null +++ b/kwin/kompmgr/configure.in.in @@ -0,0 +1,8 @@ +if test "x$with_composite" != "xno"; then + KDE_CHECK_HEADERS(X11/extensions/Xdamage.h,,compile_kompmgr=no,[#include <X11/Xlib.h>]) + KDE_CHECK_HEADERS(X11/extensions/Xcomposite.h,,compile_kompmgr=no,[#include <X11/Xlib.h>]) + KDE_CHECK_HEADERS(X11/extensions/Xrender.h,,compile_kompmgr=no,[#include <X11/Xlib.h>]) +else + compile_kompmgr=no +fi +AM_CONDITIONAL(include_kompmgr, test x"$compile_kompmgr" != x"no" ) diff --git a/kwin/kompmgr/kompmgr.c b/kwin/kompmgr/kompmgr.c new file mode 100644 index 000000000..a91d0a3d6 --- /dev/null +++ b/kwin/kompmgr/kompmgr.c @@ -0,0 +1,3005 @@ +/* + * $Id$ + * + * Copyright © 2003 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + + +/* Modified by Matthew Hawn. I don't know what to say here so follow what it + says above. Not that I can really do anything about it +*/ + +/* Modified by Dan Doel*/ + +/* +Version 2.x of xcompmgr, kompmgr changes by Thomas Lübking and Heiko Przybyl +check baghira.sf.net for more infos +*/ +#define _VERSION_ 2.02 +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/shape.h> + +#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 +#define HAS_NAME_WINDOW_PIXMAP 1 +#endif + +#define CAN_DO_USABLE 0 + +#define _TOPHEIGHT_(x) ((x >> 24) & 0xff) +#define _RIGHTWIDTH_(x) ((x >> 16) & 0xff) +#define _BOTTOMHEIGHT_(x) ((x >> 8) & 0xff) +#define _LEFTWIDTH_(x) (x & 0xff) + +typedef struct _ignore { + struct _ignore *next; + unsigned long sequence; +} ignore; + +typedef struct _win { + struct _win *next; + Window id; +#if HAS_NAME_WINDOW_PIXMAP + Pixmap pixmap; +#endif + XWindowAttributes a; +#if CAN_DO_USABLE + Bool usable; /* mapped and all damaged at one point */ + XRectangle damage_bounds; /* bounds of damage */ +#endif + Bool isInFade; + int mode; + int damaged; + Damage damage; + Picture picture; + Picture alphaPict; + Picture shadowPict; + XserverRegion borderSize; + XserverRegion decoRegion; + XserverRegion contentRegion; + XserverRegion extents; + unsigned int preShadeOpacity; + Picture shadow; + /*Picture alpha;*/ + int shadow_dx; + int shadow_dy; + int shadow_width; + int shadow_height; + unsigned int opacity; + unsigned int shadowSize; + Atom windowType; + unsigned long damage_sequence; /* sequence when damage was created */ + Bool shapable; /* this will allow window managers to exclude windows if just the deco is shaped*/ + unsigned int decoHash; + Picture dimPicture; + + /* for drawing translucent windows */ + XserverRegion borderClip; + struct _win *prev_trans; +} win; + +typedef struct _conv { + int size; + double *data; +} conv; + +typedef struct _fade { + struct _fade *next; + win *w; + double cur; + double finish; + double step; + void (*callback) (Display *dpy, win *w, Bool gone); + Display *dpy; + unsigned int decoHash; + Bool gone; +} fade; + +win *list; +fade *fades; +Display *dpy; +char *display = 0; +int scr; +Window root; +Picture rootPicture; +Picture rootBuffer; +Picture blackPicture; +Picture transBlackPicture; +Picture rootTile; +XserverRegion allDamage; +Bool clipChanged; +#if HAS_NAME_WINDOW_PIXMAP +Bool hasNamePixmap; +#endif +int root_height, root_width; +ignore *ignore_head, **ignore_tail = &ignore_head; +int xfixes_event, xfixes_error; +int damage_event, damage_error; +int composite_event, composite_error; +int render_event, render_error; +Bool synchronize; +int composite_opcode; +Bool disable_argb = False; + +int shapeEvent; + +/* find these once and be done with it */ +Atom opacityAtom; +Atom shadowAtom; +Atom shadeAtom; +Atom shapableAtom; +Atom decoHashAtom; +Atom dimAtom; +Atom deskChangeAtom; +Atom winTypeAtom; +Atom winDesktopAtom; +Atom winDockAtom; +Atom winToolbarAtom; +Atom winMenuAtom; +Atom winUtilAtom; +Atom winSplashAtom; +Atom winDialogAtom; +Atom winNormalAtom; + +/* opacity property name; sometime soon I'll write up an EWMH spec for it */ +#define OPACITY_PROP "_KDE_WM_WINDOW_OPACITY" +#define SHADOW_PROP "_KDE_WM_WINDOW_SHADOW" +#define SHADE_PROP "_KDE_WM_WINDOW_SHADE" +#define SHAPABLE_PROP "_KDE_WM_WINDOW_SHAPABLE" +#define DECOHASH_PROP "_KDE_WM_WINDOW_DECOHASH" +#define DIM_PROP "_KDE_WM_WINDOW_DIM" +#define DESKCHANGE_PROP "_KDE_WM_DESKTOP_CHANGE" + +#define TRANSLUCENT 0xe0000000 +#define OPAQUE 0xffffffff + +conv *gaussianMap; + +#define WINDOW_SOLID 0 +#define WINDOW_TRANS 1 +#define WINDOW_ARGB 2 + +#define TRANS_OPACITY 0.75 + +#define DEBUG_REPAINT 0 +#define DEBUG_EVENTS 0 +#define MONITOR_REPAINT 0 + +#define SHADOWS 1 +#define SHARP_SHADOW 0 + +typedef enum _compMode { + CompSimple, /* looks like a regular X server */ + CompServerShadows, /* use window alpha for shadow; sharp, but precise */ + CompClientShadows /* use window extents for shadow, blurred */ +} CompMode; + +static void +determine_mode(Display *dpy, win *w); + +static double +get_opacity_percent(Display *dpy, win *w, double def); + +static XserverRegion +win_extents (Display *dpy, win *w); + +CompMode compMode = CompSimple; + +int shadowRadius = 12; +int shadowOffsetX = 0; +int shadowOffsetY = 0; +double shadowOpacity = .75; +XRenderColor shadowColor; + +double fade_in_step = 0.028; +double fade_out_step = 0.03; +int fade_delta = 10; +int fade_time = 0; +Bool fadeWindows = False; +Bool excludeDockShadows = False; +Bool fadeTrans = False; + +Bool autoRedirect = False; + +/* For shadow precomputation */ +int Gsize = -1; +unsigned char *shadowCorner = NULL; +unsigned char *shadowTop = NULL; + +XRenderPictFormat* sXRenderFindVisualFormat(Display *dpy, _Xconst Visual *visual) +{ + XRenderPictFormat* format = XRenderFindVisualFormat(dpy,visual); + if (format) + return format; + else + return XRenderFindStandardFormat (dpy, PictStandardRGB24); +} + +int +get_time_in_milliseconds () +{ + struct timeval tv; + + gettimeofday (&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +fade * +find_fade (win *w) +{ + fade *f; + + for (f = fades; f; f = f->next) + { + if (f->w == w) + return f; + } + return 0; +} + +void dequeue_fade (Display *dpy, fade *f) +{ + fade **prev; + f->w->isInFade = False; + f->w->decoHash = f->decoHash; + + for (prev = &fades; *prev; prev = &(*prev)->next) + if (*prev == f) + { + *prev = f->next; + if (f->callback) + { + (*f->callback) (dpy, f->w, f->gone); + } + free (f); + break; + } +} + +void +cleanup_fade (Display *dpy, win *w) +{ + fade *f = find_fade (w); + if (f) + dequeue_fade (dpy, f); +} + +void +enqueue_fade (Display *dpy, fade *f) +{ + f->w->isInFade = True; + if (!fades) + fade_time = get_time_in_milliseconds () + fade_delta; + f->next = fades; + fades = f; +} + +static void unmap_callback (Display *dpy, win *w, Bool gone); + +static void +set_fade (Display *dpy, win *w, double start, double finish, double step, + void (*callback) (Display *dpy, win *w, Bool gone), + Bool gone, Bool exec_callback, Bool override, Bool wholeWin) +{ + fade *f; + + f = find_fade (w); + if (!f) + { + if (start == finish) + return; + f = malloc (sizeof (fade)); + f->next = 0; + f->w = w; + f->decoHash = w->decoHash; + f->cur = start; + enqueue_fade (dpy, f); + } + else if(!override) + return; + else + { + if (exec_callback && f->callback) + (*f->callback)(dpy, f->w, f->gone); + } + if (finish < 0) + finish = 0; + if (finish > 1) + finish = 1; + f->finish = finish; + if (f->cur < finish) + f->step = step; + else if (f->cur > finish) + f->step = -step; + f->gone = gone && (exec_callback || f->callback != unmap_callback); + f->callback = callback; + w->opacity = f->cur * OPAQUE; + if (wholeWin) + w->decoHash = 0; +#if 0 + printf ("set_fade start %g step %g\n", f->cur, f->step); +#endif + determine_mode (dpy, w); + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + if( w->extents != None ) + XFixesDestroyRegion( dpy, w->extents ); + w->extents = win_extents (dpy, w); + } +} + + int +fade_timeout (void) +{ + int now; + int delta; + if (!fades) + return -1; + now = get_time_in_milliseconds(); + delta = fade_time - now; + if (delta < 0) + delta = 0; + /* printf ("timeout %d\n", delta); */ + return delta; +} + + void +run_fades (Display *dpy) +{ + int now = get_time_in_milliseconds(); + fade *f, *next; + int steps; + Bool need_dequeue; + +#if 0 + printf ("run fades\n"); +#endif + if (fade_time - now > 0) + return; + steps = 1 + (now - fade_time) / fade_delta; + for (next = fades; (f = next); ) + { + win *w = f->w; + next = f->next; + f->cur += f->step * steps; + if (f->cur >= 1) + f->cur = 1; + else if (f->cur < 0) + f->cur = 0; +#if 0 + printf ("opacity now %g -> %g\n", f->cur, f->finish); +#endif + w->opacity = f->cur * OPAQUE; + need_dequeue = False; + if (f->step > 0) + { + if (f->cur >= f->finish) + { + w->opacity = f->finish*OPAQUE; + need_dequeue = True; + } + } + else + { + if (f->cur <= f->finish) + { + w->opacity = f->finish*OPAQUE; + need_dequeue = True; + } + } + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + if( w->extents != None ) + XFixesDestroyRegion( dpy, w->extents ); + w->extents = win_extents(dpy, w); + } + determine_mode (dpy, w); + /* Must do this last as it might destroy f->w in callbacks */ + if (need_dequeue) + dequeue_fade (dpy, f); + } + fade_time = now + fade_delta; +} + +#define SHADOW_OFFSET_X ((-shadowRadius * 7 / 5) - shadowOffsetX * shadowRadius / 100) * w->shadowSize +#define SHADOW_OFFSET_Y ((-shadowRadius * 7 / 5) - shadowOffsetY * shadowRadius / 100) * w->shadowSize +/*#define SHADOW_OFFSET_X (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetX * shadowRadius / 10000 +#define SHADOW_OFFSET_Y (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetY * shadowRadius / 10000*/ + + static double +gaussian (double r, double x, double y) +{ + return ((1 / (sqrt (2 * M_PI * r))) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + + + static conv * +make_gaussian_map (Display *dpy, double r) +{ + conv *c; + int size = ((int) ceil ((r * 3)) + 1) & ~1; + int center = size / 2; + int x, y; + double t; + double g; + + c = malloc (sizeof (conv) + size * size * sizeof (double)); + c->size = size; + c->data = (double *) (c + 1); + t = 0.0; + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + g = gaussian (r, (double) (x - center), (double) (y - center)); + t += g; + c->data[y * size + x] = g; + } + /* printf ("gaussian total %f\n", t); */ + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + c->data[y*size + x] /= t; + } + return c; +} + +/* + * A picture will help + * + * -center 0 width width+center + * -center +-----+-------------------+-----+ + * | | | | + * | | | | + * 0 +-----+-------------------+-----+ + * | | | | + * | | | | + * | | | | + * height +-----+-------------------+-----+ + * | | | | + * height+ | | | | + * center +-----+-------------------+-----+ + */ + + static unsigned char +sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) +{ + int fx, fy; + double *g_data; + double *g_line = map->data; + int g_size = map->size; + int center = g_size / 2; + int fx_start, fx_end; + int fy_start, fy_end; + double v; + + /* + * Compute set of filter values which are "in range", + * that's the set with: + * 0 <= x + (fx-center) && x + (fx-center) < width && + * 0 <= y + (fy-center) && y + (fy-center) < height + * + * 0 <= x + (fx - center) x + fx - center < width + * center - x <= fx fx < width + center - x + */ + + fx_start = center - x; + if (fx_start < 0) + fx_start = 0; + fx_end = width + center - x; + if (fx_end > g_size) + fx_end = g_size; + + fy_start = center - y; + if (fy_start < 0) + fy_start = 0; + fy_end = height + center - y; + if (fy_end > g_size) + fy_end = g_size; + + g_line = g_line + fy_start * g_size + fx_start; + + v = 0; + for (fy = fy_start; fy < fy_end; fy++) + { + g_data = g_line; + g_line += g_size; + + for (fx = fx_start; fx < fx_end; fx++) + v += *g_data++; + } + if (v > 1) + v = 1; + + return ((unsigned char) (v * opacity * 255.0)); +} + +/* precompute shadow corners and sides to save time for large windows */ + static void +presum_gaussian (conv *map) +{ + int center = map->size/2; + int opacity, x, y; + + Gsize = map->size; + + if (shadowCorner) + free ((void *)shadowCorner); + if (shadowTop) + free ((void *)shadowTop); + + shadowCorner = (unsigned char *)(malloc ((Gsize + 1) * (Gsize + 1) * 26)); + shadowTop = (unsigned char *)(malloc ((Gsize + 1) * 26)); + + for (x = 0; x <= Gsize; x++) + { + shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2); + for(opacity = 0; opacity < 25; opacity++) + shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25; + for(y = 0; y <= x; y++) + { + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2); + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + for(opacity = 0; opacity < 25; opacity++) + shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25; + } + } +} + + static XImage * +make_shadow (Display *dpy, double opacity, int width, int height) +{ + XImage *ximage; + unsigned char *data; + int gsize = gaussianMap->size; + int ylimit, xlimit; + int swidth = width + gsize; + int sheight = height + gsize; + int center = gsize / 2; + int x, y; + unsigned char d; + int x_diff; + int opacity_int = (int)(opacity * 25); + data = malloc (swidth * sheight * sizeof (unsigned char)); + if (!data) + return 0; + ximage = XCreateImage (dpy, + DefaultVisual(dpy, DefaultScreen(dpy)), + 8, + ZPixmap, + 0, + (char *) data, + swidth, sheight, 8, swidth * sizeof (unsigned char)); + if (!ximage) + { + free (data); + return 0; + } + /* + * Build the gaussian in sections + */ + + /* + * center (fill the complete data array) + */ + if (Gsize > 0) + d = shadowTop[opacity_int * (Gsize + 1) + Gsize]; + else + d = sum_gaussian (gaussianMap, opacity, center, center, width, height); + memset(data, d, sheight * swidth); + + /* + * corners + */ + ylimit = gsize; + if (ylimit > sheight / 2) + ylimit = (sheight + 1) / 2; + xlimit = gsize; + if (xlimit > swidth / 2) + xlimit = (swidth + 1) / 2; + + for (y = 0; y < ylimit; y++) + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize && ylimit == Gsize) + d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height); + data[y * swidth + x] = d; + data[(sheight - y - 1) * swidth + x] = d; + data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + + /* + * top/bottom + */ + x_diff = swidth - (gsize * 2); + if (x_diff > 0 && ylimit > 0) + { + for (y = 0; y < ylimit; y++) + { + if (ylimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + y]; + else + d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height); + memset (&data[y * swidth + gsize], d, x_diff); + memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff); + } + } + + /* + * sides + */ + + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height); + for (y = gsize; y < sheight - gsize; y++) + { + data[y * swidth + x] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + } + + return ximage; +} + + static Picture +shadow_picture (Display *dpy, double opacity, Picture alpha_pict, int width, int height, int *wp, int *hp) +{ + XImage *shadowImage; + Pixmap shadowPixmap; + Pixmap finalPixmap; + Picture shadowPicture; + Picture finalPicture; + GC gc; + + shadowImage = make_shadow (dpy, opacity, width, height); + if (!shadowImage) + return None; + shadowPixmap = XCreatePixmap (dpy, root, + shadowImage->width, + shadowImage->height, + 8); + if (!shadowPixmap) + { + XDestroyImage (shadowImage); + return None; + } + + shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, + XRenderFindStandardFormat (dpy, PictStandardA8), + 0, 0); + if (!shadowPicture) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return None; + } + + gc = XCreateGC (dpy, shadowPixmap, 0, 0); + if (!gc) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + XRenderFreePicture (dpy, shadowPicture); + return None; + } + + XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, + shadowImage->width, + shadowImage->height); + *wp = shadowImage->width; + *hp = shadowImage->height; + XFreeGC (dpy, gc); + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return shadowPicture; +} + + Picture +solid_picture (Display *dpy, Bool argb, double a, double r, double g, double b) +{ + Pixmap pixmap; + Picture picture; + XRenderPictureAttributes pa; + XRenderColor c; + + pixmap = XCreatePixmap (dpy, root, 1, 1, argb ? 32 : 8); + if (!pixmap) + return None; + + pa.repeat = True; + picture = XRenderCreatePicture (dpy, pixmap, + XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8), + CPRepeat, + &pa); + if (!picture) + { + XFreePixmap (dpy, pixmap); + return None; + } + + c.alpha = a * 0xffff; + c.red = r * 0xffff; + c.green = g * 0xffff; + c.blue = b * 0xffff; + XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); + XFreePixmap (dpy, pixmap); + return picture; +} + + void +discard_ignore (Display *dpy, unsigned long sequence) +{ + while (ignore_head) + { + if ((long) (sequence - ignore_head->sequence) > 0) + { + ignore *next = ignore_head->next; + free (ignore_head); + ignore_head = next; + if (!ignore_head) + ignore_tail = &ignore_head; + } + else + break; + } +} + + void +set_ignore (Display *dpy, unsigned long sequence) +{ + ignore *i = malloc (sizeof (ignore)); + if (!i) + return; + i->sequence = sequence; + i->next = 0; + *ignore_tail = i; + ignore_tail = &i->next; +} + + int +should_ignore (Display *dpy, unsigned long sequence) +{ + discard_ignore (dpy, sequence); + return ignore_head && ignore_head->sequence == sequence; +} + + static win * +find_win (Display *dpy, Window id) +{ + win *w; + + for (w = list; w; w = w->next) + if (w->id == id) + return w; + return 0; +} + +static char *backgroundProps[] = { + "_XROOTPMAP_ID", + "_XSETROOT_ID", + 0, +}; + + static Picture +root_tile (Display *dpy) +{ + Picture picture; + Atom actual_type; + Pixmap pixmap; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop; + Bool fill; + XRenderPictureAttributes pa; + int p; + + pixmap = None; + for (p = 0; backgroundProps[p]; p++) + { + if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False), + 0, 4, False, AnyPropertyType, + &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && + actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1) + { + pixmap = *(long*)prop; + XFree (prop); + fill = False; + break; + } + } + if (!pixmap) + { + pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr)); + fill = True; + } + pa.repeat = True; + picture = XRenderCreatePicture (dpy, pixmap, + sXRenderFindVisualFormat (dpy, + DefaultVisual (dpy, scr)), + CPRepeat, &pa); + if (fill) + { + XRenderColor c; + + c.red = c.green = c.blue = 0x8080; + c.alpha = 0xffff; + XRenderFillRectangle (dpy, PictOpSrc, picture, &c, + 0, 0, 1, 1); + } + return picture; +} + + static void +paint_root (Display *dpy) +{ + if (!rootTile) + rootTile = root_tile (dpy); + + XRenderComposite (dpy, PictOpSrc, + rootTile, None, rootBuffer, + 0, 0, 0, 0, 0, 0, root_width, root_height); +} + + static XserverRegion +win_extents (Display *dpy, win *w) +{ + XRectangle r; + + r.x = w->a.x; + r.y = w->a.y; + r.width = w->a.width + w->a.border_width * 2; + r.height = w->a.height + w->a.border_width * 2; + if (compMode != CompSimple && w->shadowSize > 0 && !(w->windowType == winDockAtom && excludeDockShadows)) + { + if (compMode == CompServerShadows || w->mode != WINDOW_ARGB) + { + XRectangle sr; + + if (compMode == CompServerShadows) + { + w->shadow_dx = 2; + w->shadow_dy = 7; + w->shadow_width = w->a.width; + w->shadow_height = w->a.height; + } + else + { + w->shadow_dx = SHADOW_OFFSET_X; + w->shadow_dx = w->shadow_dx / 100; + w->shadow_dy = SHADOW_OFFSET_Y; + w->shadow_dy = w->shadow_dy / 100; + if (!w->shadow) + { + double opacity = shadowOpacity; + if (w->shadowSize > 100) + opacity = opacity/(w->shadowSize*0.015); + if (w->mode == WINDOW_TRANS) + opacity = opacity * ((double)w->opacity)/((double)OPAQUE); + w->shadow = shadow_picture (dpy, opacity, w->alphaPict, + w->a.width + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)) , + w->a.height + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)), + &w->shadow_width, &w->shadow_height); + /*int kill; + w->alpha = shadow_picture (dpy, 0.9, w->alphaPict, + w->a.width + w->a.border_width * 2, + w->a.height + w->a.border_width * 2, + &kill, &kill);*/ + } + } + sr.x = w->a.x + w->shadow_dx; + sr.y = w->a.y + w->shadow_dy; + sr.width = w->shadow_width; + sr.height = w->shadow_height; + if (sr.x < r.x) + { + r.width = (r.x + r.width) - sr.x; + r.x = sr.x; + } + if (sr.y < r.y) + { + r.height = (r.y + r.height) - sr.y; + r.y = sr.y; + } + if (sr.x + sr.width > r.x + r.width) + r.width = sr.x + sr.width - r.x; + if (sr.y + sr.height > r.y + r.height) + r.height = sr.y + sr.height - r.y; + } + } + return XFixesCreateRegion (dpy, &r, 1); +} + + static XserverRegion +border_size (Display *dpy, win *w) +{ + XserverRegion border; + /* + * if window doesn't exist anymore, this will generate an error + * as well as not generate a region. Perhaps a better XFixes + * architecture would be to have a request that copies instead + * of creates, that way you'd just end up with an empty region + * instead of an invalid XID. + */ + set_ignore (dpy, NextRequest (dpy)); + border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding); + /* translate this */ + set_ignore (dpy, NextRequest (dpy)); + XFixesTranslateRegion (dpy, border, + w->a.x + w->a.border_width, + w->a.y + w->a.border_width); + return border; +} + + static XserverRegion +deco_region (Display *dpy, win *w) +{ + XserverRegion title; + XRectangle r; /*titlebounding rect*/ + /* + * if window doesn't exist anymore, this will generate an error + * as well as not generate a region. Perhaps a better XFixes + * architecture would be to have a request that copies instead + * of creates, that way you'd just end up with an empty region + * instead of an invalid XID. + */ + r.x = w->a.x - w->a.border_width + _LEFTWIDTH_(w->decoHash); + r.y = w->a.y - w->a.border_width + _TOPHEIGHT_(w->decoHash); + r.width = w->a.width + w->a.border_width * 2 - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash); + r.height = w->a.height + w->a.border_width - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash); + set_ignore (dpy, NextRequest (dpy)); + title = XFixesCreateRegion (dpy, &r, 1); + if (!w->borderSize) + w->borderSize = border_size (dpy, w); + set_ignore (dpy, NextRequest (dpy)); + XFixesSubtractRegion(dpy, title, w->borderSize, title); + return title; +} + + static XserverRegion +content_region (Display *dpy, win *w) +{ + XserverRegion content; + XRectangle r; /*contentbounding rect*/ + /* + * if window doesn't exist anymore, this will generate an error + * as well as not generate a region. Perhaps a better XFixes + * architecture would be to have a request that copies instead + * of creates, that way you'd just end up with an empty region + * instead of an invalid XID. + */ + r.x = w->a.x - w->a.border_width + _LEFTWIDTH_(w->decoHash); + r.y = w->a.y - w->a.border_width + _TOPHEIGHT_(w->decoHash); + r.width = w->a.width + w->a.border_width * 2 - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash); + r.height = w->a.height + w->a.border_width - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash); + set_ignore (dpy, NextRequest (dpy)); + content = XFixesCreateRegion (dpy, &r, 1); + if (!w->borderSize) + w->borderSize = border_size (dpy, w); + set_ignore (dpy, NextRequest (dpy)); + XFixesIntersectRegion(dpy, content, w->borderSize, content); + return content; +} + + static void +paint_all (Display *dpy, XserverRegion region) +{ + win *w; + win *t = 0; + + if (!region) + { + XRectangle r; + r.x = 0; + r.y = 0; + r.width = root_width; + r.height = root_height; + region = XFixesCreateRegion (dpy, &r, 1); + } +#if MONITOR_REPAINT + rootBuffer = rootPicture; +#else + if (!rootBuffer) + { + Pixmap rootPixmap = XCreatePixmap (dpy, root, root_width, root_height, + DefaultDepth (dpy, scr)); + rootBuffer = XRenderCreatePicture (dpy, rootPixmap, + sXRenderFindVisualFormat (dpy, + DefaultVisual (dpy, scr)), + 0, 0); + XFreePixmap (dpy, rootPixmap); + } +#endif + XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region); +#if MONITOR_REPAINT + XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture, + 0, 0, 0, 0, 0, 0, root_width, root_height); +#endif +#if DEBUG_REPAINT + printf ("paint:"); +#endif + for (w = list; w; w = w->next) + { +#if CAN_DO_USABLE + if (!w->usable) + continue; +#endif + /* never painted, ignore it */ + if (!w->damaged) + continue; + + /* skip invisible windows */ + if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= root_width || w->a.y >= root_height) + continue; + + if (!w->picture) + { + XRenderPictureAttributes pa; + XRenderPictFormat *format; + Drawable draw = w->id; + +#if HAS_NAME_WINDOW_PIXMAP + if (hasNamePixmap && !w->pixmap) + w->pixmap = XCompositeNameWindowPixmap (dpy, w->id); + if (w->pixmap) + draw = w->pixmap; +#endif + format = sXRenderFindVisualFormat (dpy, w->a.visual); + pa.subwindow_mode = IncludeInferiors; + w->picture = XRenderCreatePicture (dpy, draw, + format, + CPSubwindowMode, + &pa); + } +#if DEBUG_REPAINT + printf (" 0x%x", w->id); +#endif + if (clipChanged) + { + if (w->borderSize) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->borderSize); + w->borderSize = None; + } + if (w->decoRegion) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->decoRegion); + w->decoRegion = None; + } + if (w->contentRegion) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->contentRegion); + w->contentRegion = None; + } + if (w->extents) + { + XFixesDestroyRegion (dpy, w->extents); + w->extents = None; + } + if (w->borderClip) + { + XFixesDestroyRegion (dpy, w->borderClip); + w->borderClip = None; + } + } + if (!w->borderSize) + w->borderSize = border_size (dpy, w); + if (!w->extents) + w->extents = win_extents (dpy, w); + if ((w->mode == WINDOW_SOLID) || ((w->mode == WINDOW_TRANS) && w->decoHash)) + { + int x, y, wid, hei; +#if HAS_NAME_WINDOW_PIXMAP + x = w->a.x; + y = w->a.y; + wid = w->a.width + w->a.border_width * 2; + hei = w->a.height + w->a.border_width * 2; +#else + x = w->a.x + w->a.border_width; + y = w->a.y + w->a.border_width; + wid = w->a.width; + hei = w->a.height; +#endif + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); + set_ignore (dpy, NextRequest (dpy)); + /*XFixesSubtractRegion (dpy, region, region, w->borderSize); + set_ignore (dpy, NextRequest (dpy));*/ + if (w->mode == WINDOW_SOLID) + { + XFixesSubtractRegion (dpy, region, region, w->borderSize); + set_ignore (dpy, NextRequest (dpy)); + XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer, + 0, 0, 0, 0, x, y, wid, hei); + if (w->dimPicture) + XRenderComposite (dpy, PictOpOver, w->dimPicture, None, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); + } + else + { + if (!w->contentRegion) + w->contentRegion = content_region (dpy, w); + XFixesSubtractRegion (dpy, region, region, w->contentRegion); + set_ignore (dpy, NextRequest (dpy)); + /*solid part*/ + XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer, + _LEFTWIDTH_(w->decoHash), _TOPHEIGHT_(w->decoHash), 0, 0, + x + _LEFTWIDTH_(w->decoHash), + y + _TOPHEIGHT_(w->decoHash), + wid - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash), + hei - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash)); + } + } + if (!w->borderClip) + { + w->borderClip = XFixesCreateRegion (dpy, 0, 0); + XFixesCopyRegion (dpy, w->borderClip, region); + } + w->prev_trans = t; + t = w; + } +#if DEBUG_REPAINT + printf ("\n"); + fflush (stdout); +#endif + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); + paint_root (dpy); + for (w = t; w; w = w->prev_trans) + { + /*skip desktop */ + if (w->windowType == winDesktopAtom) + continue; + + if (w->shadowSize > 0){ + switch (compMode) { + case CompSimple: + break; + case CompServerShadows: + /* dont' bother drawing shadows on desktop windows */ + if (w->windowType == winDesktopAtom) + break; + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); + set_ignore (dpy, NextRequest (dpy)); + if (w->opacity != OPAQUE && !w->shadowPict) + w->shadowPict = solid_picture (dpy, True, + (double) w->opacity / OPAQUE * 0.3, + 0, 0, 0); + XRenderComposite (dpy, PictOpOver, + w->shadowPict ? w->shadowPict : transBlackPicture, + w->picture, rootBuffer, + 0, 0, 0, 0, + w->a.x + w->shadow_dx, + w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height); + break; + case CompClientShadows: + if (w->shadow && w->windowType != winDesktopAtom) + { + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); + XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer, + 0, 0, 0, 0, + w->a.x + w->shadow_dx, + w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height); + } + break; + } + } + if (w->opacity != OPAQUE && !w->alphaPict) + w->alphaPict = solid_picture (dpy, False, + (double) w->opacity / OPAQUE, shadowColor.red, shadowColor.green, shadowColor.blue); + if (w->mode == WINDOW_TRANS) + { + int x, y, wid, hei; + XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize); + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); +#if HAS_NAME_WINDOW_PIXMAP + x = w->a.x; + y = w->a.y; + wid = w->a.width + w->a.border_width * 2; + hei = w->a.height + w->a.border_width * 2; +#else + x = w->a.x + w->a.border_width; + y = w->a.y + w->a.border_width; + wid = w->a.width; + hei = w->a.height; +#endif + set_ignore (dpy, NextRequest (dpy)); + if (!w->decoHash) + { + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + 0, 0, 0, 0, x, y, wid, hei); + } + else + { + /*trans part*/ + /* PICTURE ;) + |-----------------------------| + | top | + |-----------------------------| + |l | | r| + |e | | i| + |f | | g| + |t | | h| + |--------------------------| t| + | bottom | | + |--------------------------|--|*/ + /*top*/ + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + 0, 0, 0, 0, x, y, wid, _TOPHEIGHT_(w->decoHash)); + /*right*/ + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + wid - _RIGHTWIDTH_(w->decoHash), _TOPHEIGHT_(w->decoHash), + 0, 0, + x + wid - _RIGHTWIDTH_(w->decoHash), + y + _TOPHEIGHT_(w->decoHash), _RIGHTWIDTH_(w->decoHash), + hei - _TOPHEIGHT_(w->decoHash)); + /*bottom*/ + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + 0, hei - _BOTTOMHEIGHT_(w->decoHash), 0, 0, + x, y + hei - _BOTTOMHEIGHT_(w->decoHash), + wid - _RIGHTWIDTH_(w->decoHash), _BOTTOMHEIGHT_(w->decoHash)); + /*left*/ + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + 0, _TOPHEIGHT_(w->decoHash), 0, 0, + x, y + _TOPHEIGHT_(w->decoHash), + _LEFTWIDTH_(w->decoHash), hei - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash)); + } + } + else if (w->mode == WINDOW_ARGB) + { + int x, y, wid, hei; + XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize); + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); +#if HAS_NAME_WINDOW_PIXMAP + x = w->a.x; + y = w->a.y; + wid = w->a.width + w->a.border_width * 2; + hei = w->a.height + w->a.border_width * 2; +#else + x = w->a.x + w->a.border_width; + y = w->a.y + w->a.border_width; + wid = w->a.width; + hei = w->a.height; +#endif + set_ignore (dpy, NextRequest (dpy)); + XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, + 0, 0, 0, 0, + x, y, wid, hei); + } + XFixesDestroyRegion (dpy, w->borderClip); + w->borderClip = None; + } + XFixesDestroyRegion (dpy, region); + if (rootBuffer != rootPicture) + { +#if 0 + XTransform t; + t.matrix[0][0] = XDoubleToFixed (3.0 /*/ scale*/); + t.matrix[0][1] = 0.0; + t.matrix[0][2] = 0.0; + + t.matrix[1][0] = 0.0; + t.matrix[1][1] = XDoubleToFixed (1.0 /*/ scale*/); + t.matrix[1][2] = 0.0; + + t.matrix[2][0] = 0.0; + t.matrix[2][1] = 0.0; + t.matrix[2][2] = XDoubleToFixed (1.0); + + XRenderSetPictureTransform (dpy, rootBuffer, &t); +#endif + XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None); + XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture, + 0, 0, 0, 0, 0, 0, root_width, root_height); + } +} + + static void +add_damage (Display *dpy, XserverRegion damage) +{ + if (allDamage) + { + XFixesUnionRegion (dpy, allDamage, allDamage, damage); + XFixesDestroyRegion (dpy, damage); + } + else + allDamage = damage; +} + + static void +repair_win (Display *dpy, win *w) +{ + XserverRegion parts; + + if (!w->damaged) + { + parts = win_extents (dpy, w); + set_ignore (dpy, NextRequest (dpy)); + XDamageSubtract (dpy, w->damage, None, None); + } + else + { + XserverRegion o; + parts = XFixesCreateRegion (dpy, 0, 0); + set_ignore (dpy, NextRequest (dpy)); + XDamageSubtract (dpy, w->damage, None, parts); + XFixesTranslateRegion (dpy, parts, + w->a.x + w->a.border_width, + w->a.y + w->a.border_width); + if (compMode == CompServerShadows) + { + o = XFixesCreateRegion (dpy, 0, 0); + XFixesCopyRegion (dpy, o, parts); + XFixesTranslateRegion (dpy, o, w->shadow_dx, w->shadow_dy); + XFixesUnionRegion (dpy, parts, parts, o); + XFixesDestroyRegion (dpy, o); + } + } + add_damage (dpy, parts); + w->damaged = 1; +} + +static unsigned int +get_opacity_prop(Display *dpy, win *w, unsigned int def); + + static void +map_win (Display *dpy, Window id, unsigned long sequence, Bool fade) +{ + win *w = find_win (dpy, id); + Drawable back; + + if (!w) + return; + w->a.map_state = IsViewable; + + /* This needs to be here or else we lose transparency messages */ + XSelectInput (dpy, id, PropertyChangeMask); + +#if CAN_DO_USABLE + w->damage_bounds.x = w->damage_bounds.y = 0; + w->damage_bounds.width = w->damage_bounds.height = 0; +#endif + w->damaged = 0; + + if (fade && fadeWindows) + set_fade (dpy, w, 0, get_opacity_prop(dpy, w, OPAQUE)*1.0/OPAQUE, fade_in_step, 0, False, True, True, True); +} + + static void +finish_unmap_win (Display *dpy, win *w) +{ + w->damaged = 0; +#if CAN_DO_USABLE + w->usable = False; +#endif + if (w->extents != None) + { + add_damage (dpy, w->extents); /* destroys region */ + w->extents = None; + } + +#if HAS_NAME_WINDOW_PIXMAP + if (w->pixmap) + { + XFreePixmap (dpy, w->pixmap); + w->pixmap = None; + } +#endif + + if (w->picture) + { + set_ignore (dpy, NextRequest (dpy)); + XRenderFreePicture (dpy, w->picture); + w->picture = None; + } + + /* don't care about properties anymore */ + set_ignore (dpy, NextRequest (dpy)); + XSelectInput(dpy, w->id, 0); + + if (w->borderSize) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->borderSize); + w->borderSize = None; + } + + if (w->decoRegion) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->decoRegion); + w->decoRegion = None; + } + + if (w->contentRegion) + { + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, w->contentRegion); + w->contentRegion = None; + } + + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + } + if (w->borderClip) + { + XFixesDestroyRegion (dpy, w->borderClip); + w->borderClip = None; + } + + clipChanged = True; +} + +#if HAS_NAME_WINDOW_PIXMAP + static void +unmap_callback (Display *dpy, win *w, Bool gone) +{ + finish_unmap_win (dpy, w); +} +#endif + + static void +unmap_win (Display *dpy, Window id, Bool fade) +{ + win *w = find_win (dpy, id); + if (!w) + return; + w->a.map_state = IsUnmapped; +#if HAS_NAME_WINDOW_PIXMAP + if (w->pixmap && fade && fadeWindows) + set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, unmap_callback, False, False, True, True); + else +#endif + finish_unmap_win (dpy, w); +} + +/* Get the opacity prop from window + not found: default + otherwise the value + */ + static unsigned int +get_opacity_prop(Display *dpy, win *w, unsigned int def) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data; + int result = XGetWindowProperty(dpy, w->id, opacityAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL && format == 32 ) + { + unsigned int i; + i = *(long*)data; + XFree( (void *) data); + return i; + } + return def; +} + + static unsigned int +get_shadow_prop(Display *dpy, win *w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, w->id, shadowAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL && format == 32 ) + { + unsigned int i; + i = *(long*)data; + XFree( (void *) data); + /*i added this for security reaons but limiting a value to 200% is somewhat indiscriminate + if (i > 200) + return 200; + else*/ + return i; + } + return 100; +} + + static unsigned int +get_shade_prop(Display *dpy, win *w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, w->id, shadeAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL && format == 32 ) + { + unsigned int i; + i = *(long*)data; + XFree( (void *) data); + return i; + } + return 0; +} + + static Bool +get_shapable_prop(Display *dpy, win *w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, w->id, shapableAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL && format == 32 ) + { + unsigned int i; + i = *(long*)data; + XFree( (void *) data); + return i==1; + } + return True; /*in general, the window should be shapable*/ +} + +static unsigned int +get_decoHash_prop(Display *dpy, win *w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, w->id, decoHashAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL && format == 32 ) + { + unsigned int i; + i = *(long*)data; + XFree( (void *) data); + return i; + } + return 0; /*no titlebar*/ +} + +static unsigned int +get_dim_prop(Display *dpy, win *w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, w->id, dimAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL) + { + unsigned int i; + memcpy (&i, data, sizeof (unsigned int)); + XFree( (void *) data); + if (i == 0) i = 1; + return i; + } + return OPAQUE; /*in general, the window is not dimmed*/ +} + +static unsigned int +get_deskchange_prop(Display *dpy, Window id) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data = NULL; + int result = XGetWindowProperty(dpy, id, deskChangeAtom, 0L, 1L, False, + XA_CARDINAL, &actual, &format, + &n, &left, &data); + if (result == Success && data != NULL) + { + unsigned int i; + memcpy (&i, data, sizeof (unsigned int)); + XFree( (void *) data); + if (i < 3) + return i; + } + return 0; /*no valid change state*/ +} + +/* Get the opacity property from the window in a percent format + not found: default +otherwise: the value +*/ + static double +get_opacity_percent(Display *dpy, win *w, double def) +{ + if (w && w->isInFade) + { + fade *f = find_fade(w); + return f->finish; + } + else + { + unsigned int opacity = get_opacity_prop (dpy, w, (unsigned int)(OPAQUE*def)); + return opacity*1.0/OPAQUE; + } +} +#if 0 +static void +damage_shape(Display *dpy, win *w, XRectangle *shape_damage) +{ + set_ignore (dpy, NextRequest (dpy)); + XserverRegion region = XFixesCreateRegion (dpy, shape_damage, 1); + set_ignore (dpy, NextRequest (dpy)); + XserverRegion tmpRegion; + add_damage(dpy, region); + win *i; + XRectangle *rect; + int n; + for (i = w; i; i = i->next) + { + XFixesIntersectRegion (dpy, tmpRegion, region, w->extents); + rect = XFixesFetchRegion (dpy, region, &n); + free(rect); + printf("%d\n",n); + if (n != 1) + { + w->damage = True; + XFixesSubtractRegion (dpy, region, region, w->extents); + } + else + break; + } + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, tmpRegion); + set_ignore (dpy, NextRequest (dpy)); + XFixesDestroyRegion (dpy, region); +} +#endif + +/* determine mode for window all in one place. + Future might check for menu flag and other cool things + */ + + static Atom +get_wintype_prop(Display * dpy, Window w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data; + int result = XGetWindowProperty (dpy, w, winTypeAtom, 0L, 1L, False, + XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && data != None && format == 32 ) + { + Atom a; + a = *(long*)data; + XFree ( (void *) data); + return a; + } + return winNormalAtom; +} + + static void +determine_mode(Display *dpy, win *w) +{ + int mode; + XRenderPictFormat *format; + unsigned int default_opacity; + + /* if trans prop == -1 fall back on previous tests*/ + + if (w->alphaPict) + { + XRenderFreePicture (dpy, w->alphaPict); + w->alphaPict = None; + } + if (w->shadowPict) + { + XRenderFreePicture (dpy, w->shadowPict); + w->shadowPict = None; + } + + if (w->a.class == InputOnly) + { + format = 0; + } + else + { + format = XRenderFindVisualFormat (dpy, w->a.visual); + } + + if (!disable_argb && format && format->type == PictTypeDirect && format->direct.alphaMask) + { + mode = WINDOW_ARGB; + } + else if (w->opacity != OPAQUE) + { + mode = WINDOW_TRANS; + } + else + { + mode = WINDOW_SOLID; + } + w->mode = mode; + if (w->extents) + { + XserverRegion damage; + damage = XFixesCreateRegion (dpy, 0, 0); + XFixesCopyRegion (dpy, damage, w->extents); + add_damage (dpy, damage); + } +} + + static Atom +determine_wintype (Display *dpy, Window w) +{ + Window root_return, parent_return; + Window *children = NULL; + unsigned int nchildren, i; + Atom type; + + type = get_wintype_prop (dpy, w); + if (type != winNormalAtom) + return type; + + if (!XQueryTree (dpy, w, &root_return, &parent_return, &children, + &nchildren)) + { + /* XQueryTree failed. */ + if (children) + XFree ((void *)children); + return winNormalAtom; + } + + for (i = 0;i < nchildren;i++) + { + type = determine_wintype (dpy, children[i]); + if (type != winNormalAtom) + return type; + } + + if (children) + XFree ((void *)children); + + return winNormalAtom; +} + + static void +add_win (Display *dpy, Window id, Window prev) +{ + win *new = malloc (sizeof (win)); + win **p; + unsigned int tmp; + + if (!new) + return; + if (prev) + { + for (p = &list; *p; p = &(*p)->next) + if ((*p)->id == prev) + break; + } + else + p = &list; + new->id = id; + set_ignore (dpy, NextRequest (dpy)); + if (!XGetWindowAttributes (dpy, id, &new->a)) + { + free (new); + return; + } + new->damaged = 0; +#if CAN_DO_USABLE + new->usable = False; +#endif +#if HAS_NAME_WINDOW_PIXMAP + new->pixmap = None; +#endif + new->picture = None; + if (new->a.class == InputOnly) + { + new->damage_sequence = 0; + new->damage = None; + } + else + { + new->damage_sequence = NextRequest (dpy); + new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty); + } + new->isInFade = False; + new->alphaPict = None; + new->shadowPict = None; + new->borderSize = None; + new->extents = None; + new->shadow = None; + new->shadow_dx = 0; + new->shadow_dy = 0; + new->shadow_width = 0; + new->shadow_height = 0; + new->opacity = OPAQUE; + new->shadowSize = 100; + new->decoHash = 0; + + new->borderClip = None; + new->prev_trans = 0; + + XShapeSelectInput( dpy, id, ShapeNotifyMask ); + + /* moved mode setting to one place */ + new->opacity = get_opacity_prop (dpy, new, OPAQUE); + new->shadowSize = get_shadow_prop (dpy, new); + new->shapable = get_shapable_prop(dpy, new); + new->decoHash = get_decoHash_prop(dpy, new); + tmp = get_dim_prop(dpy, new); + new->dimPicture = (tmp < OPAQUE) ? solid_picture (dpy, True, (double)tmp/OPAQUE, 0.1, 0.1, 0.1) : None; + new->windowType = determine_wintype (dpy, new->id); + determine_mode (dpy, new); + + new->next = *p; + *p = new; + if (new->a.map_state == IsViewable) + map_win (dpy, id, new->damage_sequence - 1, True); +} + + void +restack_win (Display *dpy, win *w, Window new_above) +{ + Window old_above; + + if (w->next) + old_above = w->next->id; + else + old_above = None; + if (old_above != new_above) + { + win **prev; + + /* unhook */ + for (prev = &list; *prev; prev = &(*prev)->next) + if ((*prev) == w) + break; + *prev = w->next; + + /* rehook */ + for (prev = &list; *prev; prev = &(*prev)->next) + { + if ((*prev)->id == new_above) + break; + } + w->next = *prev; + *prev = w; + } +} + + static void +configure_win (Display *dpy, XConfigureEvent *ce) +{ + win *w = find_win (dpy, ce->window); + Window above; + XserverRegion damage = None; + + if (!w) + { + if (ce->window == root) + { + if (rootBuffer) + { + XRenderFreePicture (dpy, rootBuffer); + rootBuffer = None; + } + root_width = ce->width; + root_height = ce->height; + } + return; + } +#if CAN_DO_USABLE + if (w->usable) +#endif + { + damage = XFixesCreateRegion (dpy, 0, 0); + if (w->extents != None) + XFixesCopyRegion (dpy, damage, w->extents); + } + w->a.x = ce->x; + w->a.y = ce->y; + if (w->a.width != ce->width || w->a.height != ce->height) + { +#if HAS_NAME_WINDOW_PIXMAP + if (w->pixmap) + { + XFreePixmap (dpy, w->pixmap); + w->pixmap = None; + if (w->picture) + { + XRenderFreePicture (dpy, w->picture); + w->picture = None; + } + } +#endif + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + } + } + w->a.width = ce->width; + w->a.height = ce->height; + w->a.border_width = ce->border_width; + w->a.override_redirect = ce->override_redirect; + restack_win (dpy, w, ce->above); + if (damage) + { + XserverRegion extents = win_extents (dpy, w); + XFixesUnionRegion (dpy, damage, damage, extents); + XFixesDestroyRegion (dpy, extents); + add_damage (dpy, damage); + } + clipChanged = True; +} + + static void +circulate_win (Display *dpy, XCirculateEvent *ce) +{ + win *w = find_win (dpy, ce->window); + Window new_above; + + if (!w) + return; + + if (ce->place == PlaceOnTop) + new_above = list->id; + else + new_above = None; + restack_win (dpy, w, new_above); + clipChanged = True; +} + + static void +finish_destroy_win (Display *dpy, Window id, Bool gone) +{ + win **prev, *w; + + for (prev = &list; (w = *prev); prev = &w->next) + if (w->id == id) + { + if (!gone) + finish_unmap_win (dpy, w); + *prev = w->next; + if (w->picture) + { + set_ignore (dpy, NextRequest (dpy)); + XRenderFreePicture (dpy, w->picture); + w->picture = None; + } + if (w->alphaPict) + { + XRenderFreePicture (dpy, w->alphaPict); + w->alphaPict = None; + } + if (w->shadowPict) + { + XRenderFreePicture (dpy, w->shadowPict); + w->shadowPict = None; + } + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + } + if (w->damage != None) + { + set_ignore (dpy, NextRequest (dpy)); + XDamageDestroy (dpy, w->damage); + w->damage = None; + } + cleanup_fade (dpy, w); + free (w); + break; + } +} + +#if HAS_NAME_WINDOW_PIXMAP + static void +destroy_callback (Display *dpy, win *w, Bool gone) +{ + finish_destroy_win (dpy, w->id, gone); +} +#endif + + static void +destroy_win (Display *dpy, Window id, Bool gone, Bool fade) +{ + win *w = find_win (dpy, id); +#if HAS_NAME_WINDOW_PIXMAP + if (w && w->pixmap && fade && fadeWindows) + set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, destroy_callback, gone, False, True, True); + else +#endif + { + finish_destroy_win (dpy, id, gone); + } +} + +/* + static void + dump_win (win *w) + { + printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id, + w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width); + } + + + static void + dump_wins (void) + { + win *w; + + printf ("windows:\n"); + for (w = list; w; w = w->next) + dump_win (w); + } + */ + + static void +damage_win (Display *dpy, XDamageNotifyEvent *de) +{ + win *w = find_win (dpy, de->drawable); + + if (!w) + return; +#if CAN_DO_USABLE + if (!w->usable) + { + if (w->damage_bounds.width == 0 || w->damage_bounds.height == 0) + { + w->damage_bounds = de->area; + } + else + { + if (de->area.x < w->damage_bounds.x) + { + w->damage_bounds.width += (w->damage_bounds.x - de->area.x); + w->damage_bounds.x = de->area.x; + } + if (de->area.y < w->damage_bounds.y) + { + w->damage_bounds.height += (w->damage_bounds.y - de->area.y); + w->damage_bounds.y = de->area.y; + } + if (de->area.x + de->area.width > w->damage_bounds.x + w->damage_bounds.width) + w->damage_bounds.width = de->area.x + de->area.width - w->damage_bounds.x; + if (de->area.y + de->area.height > w->damage_bounds.y + w->damage_bounds.height) + w->damage_bounds.height = de->area.y + de->area.height - w->damage_bounds.y; + } +#if 0 + printf ("unusable damage %d, %d: %d x %d bounds %d, %d: %d x %d\n", + de->area.x, + de->area.y, + de->area.width, + de->area.height, + w->damage_bounds.x, + w->damage_bounds.y, + w->damage_bounds.width, + w->damage_bounds.height); +#endif + if (w->damage_bounds.x <= 0 && + w->damage_bounds.y <= 0 && + w->a.width <= w->damage_bounds.x + w->damage_bounds.width && + w->a.height <= w->damage_bounds.y + w->damage_bounds.height) + { + clipChanged = True; + if (fadeWindows) + set_fade (dpy, w, 0, get_opacity_percent (dpy, w, 1.0), fade_in_step, 0, False, True, True, False); + w->usable = True; + } + } + if (w->usable) +#endif + repair_win (dpy, w); +} + + static int +error (Display *dpy, XErrorEvent *ev) +{ + int o; + char *name = 0; + + if (should_ignore (dpy, ev->serial)) + return 0; + + if (ev->request_code == composite_opcode && + ev->minor_code == X_CompositeRedirectSubwindows) + { + fprintf (stderr, "Another composite manager is already running\n"); + exit (1); + } + + o = ev->error_code - xfixes_error; + switch (o) { + case BadRegion: name = "BadRegion"; break; + default: break; + } + o = ev->error_code - damage_error; + switch (o) { + case BadDamage: name = "BadDamage"; break; + default: break; + } + o = ev->error_code - render_error; + switch (o) { + case BadPictFormat: name ="BadPictFormat"; break; + case BadPicture: name ="BadPicture"; break; + case BadPictOp: name ="BadPictOp"; break; + case BadGlyphSet: name ="BadGlyphSet"; break; + case BadGlyph: name ="BadGlyph"; break; + default: break; + } + +#ifndef NDEBUG + fprintf (stderr,"error %d request %d minor %d serial %d\n", + ev->error_code, ev->request_code, ev->minor_code, ev->serial); +#endif + + /* abort (); this is just annoying to most people */ + return 0; +} + + static void +expose_root (Display *dpy, Window root, XRectangle *rects, int nrects) +{ + XserverRegion region = XFixesCreateRegion (dpy, rects, nrects); + + add_damage (dpy, region); +} + + + static int +ev_serial (XEvent *ev) +{ + if ((ev->type & 0x7f) != KeymapNotify) + return ev->xany.serial; + return NextRequest (ev->xany.display); +} + + + static char * +ev_name (XEvent *ev) +{ + static char buf[128]; + switch (ev->type & 0x7f) { + case Expose: + return "Expose"; + case MapNotify: + return "Map"; + case UnmapNotify: + return "Unmap"; + case ReparentNotify: + return "Reparent"; + case CirculateNotify: + return "Circulate"; + default: + if (ev->type == damage_event + XDamageNotify) + return "Damage"; + sprintf (buf, "Event %d", ev->type); + return buf; + } +} + + static Window +ev_window (XEvent *ev) +{ + switch (ev->type) { + case Expose: + return ev->xexpose.window; + case MapNotify: + return ev->xmap.window; + case UnmapNotify: + return ev->xunmap.window; + case ReparentNotify: + return ev->xreparent.window; + case CirculateNotify: + return ev->xcirculate.window; + default: + if (ev->type == damage_event + XDamageNotify){ + fprintf(stderr, "%d", ev->type); + return ((XDamageNotifyEvent *) ev)->drawable; + } + return 0; + } +} + +void +setShadowColor(char *value){ /*format nach #xxxxxx (html) ändern?*/ + unsigned int tmp; + char **res = NULL; + tmp = strtoul(value, res, 16); + if( !value || strlen(value) < 6 || strlen(value) > 8 || (*(value+1) == 'x' && strlen(value) < 8) || res != NULL ){ + shadowColor.red = 0; + shadowColor.green = 0; + shadowColor.blue = 0; + printf("wrong hexadecimal (use 0xXXXXXX or XXXXXX)! defaulting to black...\n"); + return; + } + shadowColor.blue = tmp&0xff; + tmp >>= 8; + shadowColor.green = tmp&0xff; + tmp >>= 8; + shadowColor.red = tmp&0xff; +} + +typedef enum _option{ + Display_=0, + Compmode, + ExcludeDockShadows, + FadeWindows, + FadeTrans, + AutoRedirect, + Synchronize, + ShadowColor, + ShadowRadius, + ShadowOpacity, + ShadowOffsetX, + ShadowOffsetY, + FadeOutStep, + FadeInStep, + FadeDelta, + DisableARGB, + NUMBEROFOPTIONS +} Option; + +const char * +options[NUMBEROFOPTIONS] = { + "Display", /*0*/ + "Compmode", /*1*/ + "ExcludeDockShadows", /*2*/ + "FadeWindows", /*3*/ + "FadeTrans", /*4*/ + "AutoRedirect", /*5*/ + "Synchronize", /*6*/ + "ShadowColor", /*7*/ + "ShadowRadius", /*8*/ + "ShadowOpacity", /*9*/ + "ShadowOffsetX", /*10*/ + "ShadowOffsetY", /*11*/ + "FadeOutStep", /*12*/ + "FadeInStep", /*13*/ + "FadeDelta", /*14*/ + "DisableARGB", /*15*/ + /*put your thingy in here...*/ +}; + +void +setValue(Option option, char *value ){ + switch(option){ /*please keep that upside-down, because this way adding a new option is easier (all in one view)*/ + + case FadeDelta: + fade_delta = atoi(value); + if (fade_delta < 1) + fade_delta = 10; + break; + case FadeInStep: + fade_in_step = atof(value); + if (fade_in_step <= 0) + fade_in_step = 0.01; + break; + case FadeOutStep: + fade_out_step = atof(value); + if (fade_out_step <= 0) + fade_out_step = 0.01; + break; + case ShadowOffsetY: + shadowOffsetY = atoi(value); + break; + case ShadowOffsetX: + shadowOffsetX = atoi(value); + break; + case ShadowOpacity: + shadowOpacity = atof(value); + break; + case ShadowRadius: + shadowRadius = atoi(value); + break; + case ShadowColor: + setShadowColor(value); + break; + case Synchronize: + synchronize = ( strcasecmp(value, "true") == 0 ); + break; + case AutoRedirect: + autoRedirect = ( strcasecmp(value, "true") == 0 ); + break; + case FadeTrans: + fadeTrans = ( strcasecmp(value, "true") == 0 ); + break; + case FadeWindows: + fadeWindows = ( strcasecmp(value, "true") == 0 ); + break; + case ExcludeDockShadows: + excludeDockShadows = ( strcasecmp(value, "true") == 0 ); + break; + case Compmode: + if( strcasecmp(value, "CompClientShadows") == 0 ){ + compMode = CompClientShadows; + } + else if( strcasecmp(value, "CompServerShadows") == 0 ){ + compMode = CompServerShadows; + } + else{ + compMode = CompSimple; /*default*/ + } + break; + case Display_: + break; + display = strdup(value); + break; + case DisableARGB: + disable_argb = ( strcasecmp(value, "true") == 0 ); + break; + default: + break; + } +} + +int +setParameter(char *line){ + char *name = strtok(line, "="); + char *value = line+strlen(name)+1; + Option i; + for(i=Display_; i < NUMBEROFOPTIONS; i++){ + if( strcasecmp(name, *(options+i) ) == 0 ){ + setValue(i, value); + name = value = NULL; + return 1; + } + } + printf("ignored unknown option: <%s>\n", name); + name = value = NULL; + return 0; +} + +void +loadConfig(char *filename){ + FILE *file = NULL; + char line[ 1024 ]; + size_t length = 0; + Bool wasNull = False; + Bool section = False; + + if( filename == NULL ){ + const char *home = getenv("HOME"); + const char *configfile = "/.xcompmgrrc"; + int n = strlen(home)+strlen(configfile)+1; + filename = (char*)malloc(n*sizeof(char)); + memset(filename,0,n); + wasNull = True; + + strcat(filename, home); + strcat(filename, configfile); + } + + printf("trying '%s' as configfile\n\n", filename); + + if( (file = fopen(filename, "r")) == NULL ){ + printf("failed to open config file. does it exist?\n"); + if( wasNull ){ + free(filename); + filename = NULL; + } + return; + } + + /*find section*/ + while( !section && fgets(line, 1023, file) != NULL ){ + if( strcmp(line, "[xcompmgr]\n") == 0 ) + section = True; + } + /*read and set values*/ + while( section && fgets(line, 1023, file) != NULL ){ + int ret = strlen( line ); + if( ret > 1 ){ + if( *line == '[' )/*found new section - maybe check for '\n'?*/ + break; + *(line+ret-1) = '\0'; + setParameter(line); + } + } + printf("\nfinished parsing the config file\n"); + fclose(file); + if( wasNull ){ + free(filename); + filename = NULL; + } +} + + void +usage (char *program) +{ + fprintf (stderr, "%s v1.0\n", program); + fprintf (stderr, "usage: %s [options]\n", program); + fprintf (stderr, "Options\n"); + fprintf (stderr, " -d display\n Specifies which display should be managed.\n"); + fprintf (stderr, " -r radius\n Specifies the blur radius for client-side shadows. (default 12)\n"); + fprintf (stderr, " -o opacity\n Specifies the translucency for client-side shadows. (default .75)\n"); + fprintf (stderr, " -l left-offset\n Specifies the left offset for client-side shadows. (default -15)\n"); + fprintf (stderr, " -t top-offset\n Specifies the top offset for clinet-side shadows. (default -15)\n"); + fprintf (stderr, " -I fade-in-step\n Specifies the opacity change between steps while fading in. (default 0.028)\n"); + fprintf (stderr, " -O fade-out-step\n Specifies the opacity change between steps while fading out. (default 0.03)\n"); + fprintf (stderr, " -D fade-delta-time\n Specifies the time between steps in a fade in milliseconds. (default 10)\n"); + fprintf (stderr, " -a\n Use automatic server-side compositing. Faster, but no special effects.\n"); + fprintf (stderr, " -c\n Draw client-side shadows with fuzzy edges.\n"); + fprintf (stderr, " -C\n Avoid drawing shadows on dock/panel windows.\n"); + fprintf (stderr, " -f\n Fade windows in/out when opening/closing.\n"); + fprintf (stderr, " -F\n Fade windows during opacity changes.\n"); + fprintf (stderr, " -n\n Normal client-side compositing with transparency support\n"); + fprintf (stderr, " -s\n Draw server-side shadows with sharp edges.\n"); + fprintf (stderr, " -S\n Enable synchronous operation (for debugging).\n"); + fprintf (stderr, " -x [0x]XXXXXX\n Choose Custom Color in hex format\n"); + fprintf (stderr, " -v\n Print version Number and exit\n"); + fprintf (stderr, " -h\n Print this help\n"); + exit (1); +} + + static void +give_me_a_name(void) +{ + Window w; + + w = XCreateSimpleWindow (dpy, RootWindow(dpy, 0), 0, 0, 1, 1, 0, None, + None); + Xutf8SetWMProperties(dpy, w, "kcompmgr", "kcompmgr", NULL, 0, NULL, NULL, + NULL); +} + + int +main (int argc, char **argv) +{ + XEvent ev; + Window root_return, parent_return; + Window *children; + Pixmap transPixmap; + Pixmap blackPixmap; + unsigned int nchildren; + int i; + XRenderPictureAttributes pa; + XRenderColor c; + XRectangle *expose_rects = 0; + int size_expose = 0; + int n_expose = 0; + struct pollfd ufd; + int n; + int last_update; + int now; + int p; + int composite_major, composite_minor; + + int o; + char **res = NULL; + + shadowColor.red = 0; + shadowColor.green = 0; + shadowColor.blue = 0; + + loadConfig(NULL); /*we do that before cmdline-parsing, so config-values can be overridden*/ + /*used for shadow colors*/ + while ((o = getopt (argc, argv, "D:I:O:d:r:o:l:t:scnfFCaSx:vh")) != -1) + { + switch (o) { + case 'd': + display = optarg; + break; + case 'D': + fade_delta = atoi (optarg); + if (fade_delta < 1) + fade_delta = 10; + break; + case 'I': + fade_in_step = atof (optarg); + if (fade_in_step <= 0) + fade_in_step = 0.01; + break; + case 'O': + fade_out_step = atof (optarg); + if (fade_out_step <= 0) + fade_out_step = 0.01; + break; + case 's': + compMode = CompServerShadows; + break; + case 'c': + compMode = CompClientShadows; + break; + case 'C': + excludeDockShadows = True; + break; + case 'n': + compMode = CompSimple; + break; + case 'f': + fadeWindows = True; + break; + case 'F': + fadeTrans = True; + break; + case 'a': + autoRedirect = True; + break; + case 'S': + synchronize = True; + break; + case 'r': + shadowRadius = atoi (optarg); + break; + case 'o': + shadowOpacity = atof (optarg); + break; + case 'l': + shadowOffsetX = atoi (optarg); + break; + case 't': + shadowOffsetY = atoi (optarg); + break; + case 'x': + if( compMode != CompClientShadows ){ + fprintf(stderr, "sorry, but we need ClientShadows (-c) for coloring to work properly!\ndefaulting to black...\n"); + break; + } + setShadowColor(optarg); + break; + case 'v': fprintf (stderr, "%s v%-3.2f\n", argv[0], _VERSION_); exit (0); + case 'h': + default: + usage (argv[0]); + break; + } + } + + dpy = XOpenDisplay (display); + if (!dpy) + { + fprintf (stderr, "Can't open display\n"); + exit (1); + } + XSetErrorHandler (error); + if (synchronize) + XSynchronize (dpy, 1); + scr = DefaultScreen (dpy); + root = RootWindow (dpy, scr); + + if (!XRenderQueryExtension (dpy, &render_event, &render_error)) + { + fprintf (stderr, "No render extension\n"); + exit (1); + } + if (!XQueryExtension (dpy, COMPOSITE_NAME, &composite_opcode, + &composite_event, &composite_error)) + { + fprintf (stderr, "No composite extension\n"); + exit (1); + } + XCompositeQueryVersion (dpy, &composite_major, &composite_minor); +#if HAS_NAME_WINDOW_PIXMAP + if (composite_major > 0 || composite_minor >= 2) + hasNamePixmap = True; +#endif + + if (!XDamageQueryExtension (dpy, &damage_event, &damage_error)) + { + fprintf (stderr, "No damage extension\n"); + exit (1); + } + if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error)) + { + fprintf (stderr, "No XFixes extension\n"); + exit (1); + } + + fprintf(stderr, "Started\n"); + + give_me_a_name(); + + /* get atoms */ + shadowAtom = XInternAtom (dpy, SHADOW_PROP, False); + opacityAtom = XInternAtom (dpy, OPACITY_PROP, False); + shadeAtom = XInternAtom (dpy, SHADE_PROP, False); + shapableAtom = XInternAtom (dpy, SHAPABLE_PROP, False); + decoHashAtom = XInternAtom (dpy, DECOHASH_PROP, False); + dimAtom = XInternAtom (dpy, DIM_PROP, False); + deskChangeAtom = XInternAtom (dpy, DESKCHANGE_PROP, False); + winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", False); + winDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + winDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + winToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); + winMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", False); + winUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); + winSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); + winDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + winNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); + + pa.subwindow_mode = IncludeInferiors; + + if (compMode == CompClientShadows) + { + gaussianMap = make_gaussian_map(dpy, shadowRadius); + presum_gaussian (gaussianMap); + } + + root_width = DisplayWidth (dpy, scr); + root_height = DisplayHeight (dpy, scr); + + rootPicture = XRenderCreatePicture (dpy, root, + sXRenderFindVisualFormat (dpy, + DefaultVisual (dpy, scr)), + CPSubwindowMode, + &pa); + blackPicture = solid_picture (dpy, True, 1, (double)(shadowColor.red)/0xff, (double)(shadowColor.green)/0xff, (double)(shadowColor.blue)/0xff); + if (compMode == CompServerShadows) + transBlackPicture = solid_picture (dpy, True, 0.3, 0, 0, 0); + allDamage = None; + clipChanged = True; + XGrabServer (dpy); + if (autoRedirect) + XCompositeRedirectSubwindows (dpy, root, CompositeRedirectAutomatic); + else + { + int dummy; + XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual); + XSelectInput (dpy, root, + SubstructureNotifyMask| + ExposureMask| + StructureNotifyMask| + PropertyChangeMask | + VisibilityChangeMask); + + /*shaping stuff*/ + XShapeQueryExtension(dpy, &shapeEvent, &dummy); + + XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren); + for (i = 0; i < nchildren; i++) + add_win (dpy, children[i], i ? children[i-1] : None); + XFree (children); + } + XUngrabServer (dpy); + ufd.fd = ConnectionNumber (dpy); + ufd.events = POLLIN; + if (!autoRedirect) + paint_all (dpy, None); + for (;;) + { + /* dump_wins (); */ + do { + if (autoRedirect) + XFlush (dpy); + if (!QLength (dpy)) + { + if (poll (&ufd, 1, fade_timeout()) == 0) + { + run_fades (dpy); + break; + } + } + + XNextEvent (dpy, &ev); + if ((ev.type & 0x7f) != KeymapNotify) + discard_ignore (dpy, ev.xany.serial); +#if DEBUG_EVENTS + printf ("event %10.10s serial 0x%08x window 0x%08x\n", + ev_name(&ev), ev_serial (&ev), ev_window (&ev)); +#endif + if (!autoRedirect) switch (ev.type) { + case CreateNotify: + add_win (dpy, ev.xcreatewindow.window, 0); + break; + case ConfigureNotify: + configure_win (dpy, &ev.xconfigure); + break; + case DestroyNotify: + destroy_win (dpy, ev.xdestroywindow.window, True, True); + break; + case MapNotify: + map_win (dpy, ev.xmap.window, ev.xmap.serial, True); + break; + case UnmapNotify: + unmap_win (dpy, ev.xunmap.window, True); + break; + case ReparentNotify: + if (ev.xreparent.parent == root) + add_win (dpy, ev.xreparent.window, 0); + else + destroy_win (dpy, ev.xreparent.window, False, True); + break; + case CirculateNotify: + circulate_win (dpy, &ev.xcirculate); + break; + case Expose: + if (ev.xexpose.window == root) + { + int more = ev.xexpose.count + 1; + if (n_expose == size_expose) + { + if (expose_rects) + { + expose_rects = realloc (expose_rects, + (size_expose + more) * + sizeof (XRectangle)); + size_expose += more; + } + else + { + expose_rects = malloc (more * sizeof (XRectangle)); + size_expose = more; + } + } + expose_rects[n_expose].x = ev.xexpose.x; + expose_rects[n_expose].y = ev.xexpose.y; + expose_rects[n_expose].width = ev.xexpose.width; + expose_rects[n_expose].height = ev.xexpose.height; + n_expose++; + if (ev.xexpose.count == 0) + { + expose_root (dpy, root, expose_rects, n_expose); + n_expose = 0; + } + } + break; + case PropertyNotify: + for (p = 0; backgroundProps[p]; p++) + { + if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False)) + { + if (rootTile) + { + XClearArea (dpy, root, 0, 0, 0, 0, True); + XRenderFreePicture (dpy, rootTile); + rootTile = None; + break; + } + } + } + /* Window set shade? */ + if (ev.xproperty.atom == shadeAtom) + { + win * w = find_win(dpy, ev.xproperty.window); + if (w){ + unsigned int tmp = get_shade_prop(dpy, w); + if (tmp) + { + if (tmp == 1) + { + w->preShadeOpacity = w->opacity; + w->opacity = w->opacity-1; /*assuming that no human being will ever be able to shade an invisable window ;) */ + determine_mode(dpy, w); + } + else if (tmp == 2) + { + w->opacity = w->preShadeOpacity; + determine_mode(dpy, w); + } + } + break; + } + } + else if (ev.xproperty.atom == shapableAtom) + { + win * w = find_win(dpy, ev.xproperty.window); + if (w) + { + w->shapable = get_shapable_prop(dpy, w); + /* printf("%u is %s shapable\n",w->id,w->shapable?"":"not");*/ + } + else + printf("arrrg, window not found\n"); + } + else if (ev.xproperty.atom == decoHashAtom) + { + win * w = find_win(dpy, ev.xproperty.window); + if (w) + { + w->decoHash = get_decoHash_prop(dpy, w); + } + else + printf("arrrg, window not found\n"); + } + else if (ev.xproperty.atom == dimAtom) + { + win * w = find_win(dpy, ev.xproperty.window); + if (w) + { + unsigned int tmp = get_dim_prop(dpy, w); + if (w->dimPicture) + { + XRenderFreePicture (dpy, w->dimPicture); + w->dimPicture = None; + } + if (tmp < OPAQUE) + w->dimPicture = solid_picture (dpy, True, (double)tmp/OPAQUE, 0.1, 0.1, 0.1); + } + else + printf("arrrg, window not found\n"); + } + /* check if Trans or Shadow property was changed */ + else if (ev.xproperty.atom == opacityAtom || ev.xproperty.atom == shadowAtom) + { + /* reset mode and redraw window */ + win * w = find_win(dpy, ev.xproperty.window); + if (w) + { + unsigned int tmp; + unsigned int oldShadowSize = w->shadowSize; + if (ev.xproperty.atom == opacityAtom) + { + tmp = get_opacity_prop(dpy, w, OPAQUE); + /*THis will most probably happen if window is in fade - resulting in that the fade process isn't updated or broken -> we may have a wrong opacity in the future*/ + /*if (tmp == w->opacity) + break;*/ /*skip if opacity does not change*/ + if (fadeTrans) + { + static double start, finish, step; + start = w->opacity*1.0/OPAQUE; + finish = (tmp*1.0)/OPAQUE; + + if ( start > finish ) + step = fade_out_step; + else + step = fade_in_step; + + set_fade (dpy, w, start, finish, step, 0, False, True, True, False); + break; + } + else + w->opacity = tmp; + } + else + { + tmp = get_shadow_prop(dpy, w); + if (tmp == w->shadowSize) + break; /*skip if shadow does not change*/ + w->shadowSize = tmp; +/* if (w->isInFade) + break;*/ + } + if (w->shadow) + { + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + } + if (oldShadowSize < w->shadowSize) /* this is important to catch size changes on cleanup with determine_mode*/ + { + if( w->extents != None ) + XFixesDestroyRegion( dpy, w->extents ); + w->extents = win_extents (dpy, w); + determine_mode(dpy, w); + } + else + { + determine_mode(dpy, w); + if( w->extents != None ) + XFixesDestroyRegion( dpy, w->extents ); + w->extents = win_extents (dpy, w); + } + } + } + else if (ev.xproperty.atom == deskChangeAtom) + { + /*just set global variable*/ + unsigned int tmp = get_deskchange_prop(dpy, ev.xproperty.window); + printf("desk change, state:%d\n",tmp); + } + break; + default: + if (ev.type == damage_event + XDamageNotify) + { + /* printf("damaging win: %u\n",ev.xany.window);*/ + damage_win (dpy, (XDamageNotifyEvent *) &ev); + } + else if (ev.type == shapeEvent) + { + win * w = find_win(dpy, ev.xany.window); +#if 1 + if (w && w->shapable) +#endif +#if 0 + if (w) +#endif + { +#if 0 + XRectangle rect; + rect.x = ((XShapeEvent*)&ev)->x; + rect.y = ((XShapeEvent*)&ev)->y; + rect.width = ((XShapeEvent*)&ev)->width; + rect.height = ((XShapeEvent*)&ev)->height; + damage_shape(dpy, w, &rect); +#endif +#if 0 + if (w->shadowSize != 0) + { + w->shadowSize = 0; + XRenderFreePicture (dpy, w->shadow); + w->shadow = None; + determine_mode(dpy, w); + if( w->extents != None ) + XFixesDestroyRegion( dpy, w->extents ); + w->extents = win_extents (dpy, w); + } +#endif + /*this is hardly efficient, but a current workaraound + shaping support isn't that good so far (e.g. we lack shaped shadows) + IDEA: use XRender to scale/shift a copy of the window and then blurr it*/ +#if 1 + if (w->picture) + { + clipChanged = True; + repair_win (dpy, w); + } +#endif + } + } + break; + } + } while (QLength (dpy)); + if (allDamage && !autoRedirect) + { + /*static int paint;*/ + paint_all (dpy, allDamage); + /*paint++;*/ + XSync (dpy, False); + allDamage = None; + clipChanged = False; + } + } +} diff --git a/kwin/kwin.kcfg b/kwin/kwin.kcfg new file mode 100644 index 000000000..9865d296c --- /dev/null +++ b/kwin/kwin.kcfg @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kwinrc" /> + <group name="Desktops" > + <entry key="Name_1" type="String" /> + <entry key="Name_2" type="String" /> + <entry key="Name_3" type="String" /> + <entry key="Name_4" type="String" /> + <entry key="Number" type="Int" /> + </group> + <group name="FakeSession" > + <entry key="count" type="Int" /> + </group> + <group name="MouseBindings" > + <entry key="CommandActiveTitlebar2" type="String" /> + <entry key="CommandActiveTitlebar3" type="String" /> + <entry key="CommandActiveTitlebar1" type="String" /> + <entry key="CommandInactiveTitlebar1" type="String" /> + <entry key="CommandInactiveTitlebar2" type="String" /> + <entry key="CommandInactiveTitlebar3" type="String" /> + </group> + <group name="Style" > + <entry key="ButtonsOnLeft" type="String" /> + <entry key="ButtonsOnRight" type="String" /> + <entry key="PluginLib" type="String" /> + <entry key="BorderSize" type="Int" /> + <entry key="CustomButtonPositions" type="Bool" /> + <entry key="ShowToolTips" type="Bool" /> + </group> + <group name="Windows" > + <entry key="AltTabStyle" type="String" /> + <entry key="FocusPolicy" type="String" /> + <entry key="MoveMode" type="String" /> + <entry key="ResizeMode" type="String" /> + <entry key="TitlebarDoubleClickCommand" type="String" /> + <entry key="AnimateMinimize" type="Bool" /> + <entry key="AnimateShade" type="Bool" /> + <entry key="IgnoreFocusStealingClasses" type="StringList" /> + <entry key="MoveResizeMaximizedWindows" type="Bool" /> + <entry key="ShadeHover" type="Bool" /> + <entry key="GeometryTip" type="Bool" /> + <entry key="RollOverDesktops" type="Bool" /> + <entry key="FocusStealingPreventionLevel" type="Int" /> + <entry key="AnimateMinimizeSpeed" type="Int" /> + <entry key="Placement" type="String" /> + <entry key="AutoRaise" type="Bool" /> + <entry key="AutoRaiseInterval" type="Int" /> + <entry key="DelayFocus" type="Bool" /> + <entry key="DelayFocusInterval" type="Int" /> + <entry key="ShadeHoverInterval" type="Int" /> + <entry key="ClickRaise" type="Bool" /> + <entry key="BorderSnapZone" type="Int" /> + <entry key="WindowSnapZone" type="Int" /> + <entry key="SnapOnlyWhenOverlapping" type="Bool" /> + <entry key="ElectricBorders" type="Int" /> + <entry key="ElectricBorderDelay" type="Int" /> + <entry key="IgnorePositionClasses" type="StringList" /> + <entry key="KillPingTimeout" type="Int" /> + <entry key="ShowDesktopIsMinimizeAll" type="Bool" /> + </group> + + <group name="WM" > + <entry key="frame" type="Color" /> + <entry key="handle" type="Color" /> + <entry key="activeBackground" type="Color" /> + <entry key="activeBlend" type="Color" /> + <entry key="activeFont" type="Font" /> + <entry key="activeFontSmall" type="Font" /> + <entry key="activeForeground" type="Color" /> + <entry key="activeTitleBtnBg" type="Color" /> + <entry key="inactiveBackground" type="Color" /> + <entry key="inactiveBlend" type="Color" /> + <entry key="inactiveFont" type="Color" /> + <entry key="inactiveFontSmall" type="Color" /> + <entry key="inactiveForeground" type="Color" /> + <entry key="inactiveFrame" type="Color" /> + <entry key="inactiveHandle" type="Color" /> + <entry key="inactiveTitleBtnBg" type="Color" /> + </group> +</kcfg> diff --git a/kwin/kwinbindings.cpp b/kwin/kwinbindings.cpp new file mode 100644 index 000000000..1fd8c572f --- /dev/null +++ b/kwin/kwinbindings.cpp @@ -0,0 +1,178 @@ +#ifndef NOSLOTS +# define DEF2( name, descr, key3, key4, fnSlot ) \ + keys->insert( name, i18n(descr), QString::null, key3, key4, this, SLOT(fnSlot) ) +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4, this, SLOT(fnSlot) ) +#else +# define DEF2( name, descr, key3, key4, fnSlot ) \ + keys->insert( name, i18n(descr), QString::null, key3, key4 ) +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4 ) +#endif + +#define WIN KKey::QtWIN + +// some shortcuts have Tarzan-speech like names, they need extra normal human descriptions with DEF2() +// the others can use DEF() + + keys->insert( "Program:kwin", i18n("System") ); + + keys->insert( "Group:Navigation", i18n("Navigation") ); + DEF( I18N_NOOP("Walk Through Windows"), ALT+Qt::Key_Tab, ALT+Qt::Key_Tab, slotWalkThroughWindows() ); + DEF( I18N_NOOP("Walk Through Windows (Reverse)"), ALT+SHIFT+Qt::Key_Tab, ALT+SHIFT+Qt::Key_Tab, slotWalkBackThroughWindows() ); + DEF( I18N_NOOP("Walk Through Desktops"), 0, WIN+Qt::Key_Tab, slotWalkThroughDesktops() ); + DEF( I18N_NOOP("Walk Through Desktops (Reverse)"), 0, WIN+SHIFT+Qt::Key_Tab, slotWalkBackThroughDesktops() ); + DEF( I18N_NOOP("Walk Through Desktop List"), 0, 0, slotWalkThroughDesktopList() ); + DEF( I18N_NOOP("Walk Through Desktop List (Reverse)"), 0, 0, slotWalkBackThroughDesktopList() ); + + keys->insert( "Group:Windows", i18n("Windows") ); + DEF( I18N_NOOP("Window Operations Menu"), ALT+Qt::Key_F3, ALT+Qt::Key_Menu, slotWindowOperations() ); + DEF2( "Window Close", I18N_NOOP("Close Window"), + ALT+Qt::Key_F4, "Alt+Escape;Alt+F4", slotWindowClose() ); + DEF2( "Window Maximize", I18N_NOOP("Maximize Window"), + 0, WIN+Qt::Key_Plus, slotWindowMaximize() ); + DEF2( "Window Maximize Vertical", I18N_NOOP("Maximize Window Vertically"), + 0, WIN+Qt::Key_Bar, slotWindowMaximizeVertical() ); + DEF2( "Window Maximize Horizontal", I18N_NOOP("Maximize Window Horizontally"), + 0, WIN+Qt::Key_Equal, slotWindowMaximizeHorizontal() ); + DEF2( "Window Minimize", I18N_NOOP("Minimize Window"), + 0, WIN+Qt::Key_Minus, slotWindowMinimize() ); + DEF2( "Window Shade", I18N_NOOP("Shade Window"), + 0, WIN+Qt::Key_Underscore, slotWindowShade() ); + DEF2( "Window Move", I18N_NOOP("Move Window"), + 0, 0, slotWindowMove() ); + DEF2( "Window Resize", I18N_NOOP("Resize Window"), + 0, 0, slotWindowResize() ); + DEF2( "Window Raise", I18N_NOOP("Raise Window"), + 0, 0, slotWindowRaise() ); + DEF2( "Window Lower", I18N_NOOP("Lower Window"), + 0, 0, slotWindowLower() ); + DEF( I18N_NOOP("Toggle Window Raise/Lower"), 0, 0, slotWindowRaiseOrLower() ); + DEF2( "Window Fullscreen", I18N_NOOP("Make Window Fullscreen"), + 0, 0, slotWindowFullScreen() ); + DEF2( "Window No Border", I18N_NOOP("Hide Window Border"), + 0, 0, slotWindowNoBorder() ); + DEF2( "Window Above Other Windows", I18N_NOOP("Keep Window Above Others"), + 0, 0, slotWindowAbove() ); + DEF2( "Window Below Other Windows", I18N_NOOP("Keep Window Below Others"), + 0, 0, slotWindowBelow() ); + DEF( I18N_NOOP("Activate Window Demanding Attention"), CTRL+ALT+Qt::Key_A, 0, slotActivateAttentionWindow()); + DEF( I18N_NOOP("Setup Window Shortcut"), 0, 0, slotSetupWindowShortcut()); + DEF2( "Window Pack Right", I18N_NOOP("Pack Window to the Right"), + 0, 0, slotWindowPackRight() ); + DEF2( "Window Pack Left", I18N_NOOP("Pack Window to the Left"), + 0, 0, slotWindowPackLeft() ); + DEF2( "Window Pack Up", I18N_NOOP("Pack Window Up"), + 0, 0, slotWindowPackUp() ); + DEF2( "Window Pack Down", I18N_NOOP("Pack Window Down"), + 0, 0, slotWindowPackDown() ); + DEF2( "Window Grow Horizontal", I18N_NOOP("Pack Grow Window Horizontally"), + 0, 0, slotWindowGrowHorizontal() ); + DEF2( "Window Grow Vertical", I18N_NOOP("Pack Grow Window Vertically"), + 0, 0, slotWindowGrowVertical() ); + DEF2( "Window Shrink Horizontal", I18N_NOOP("Pack Shrink Window Horizontally"), + 0, 0, slotWindowShrinkHorizontal() ); + DEF2( "Window Shrink Vertical", I18N_NOOP("Pack Shrink Window Vertically"), + 0, 0, slotWindowShrinkVertical() ); + + keys->insert( "Group:Window Desktop", i18n("Window & Desktop") ); + DEF2( "Window On All Desktops", I18N_NOOP("Keep Window on All Desktops"), + 0, 0, slotWindowOnAllDesktops() ); + DEF( I18N_NOOP("Window to Desktop 1"), 0, WIN+ALT+Qt::Key_F1, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 2"), 0, WIN+ALT+Qt::Key_F2, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 3"), 0, WIN+ALT+Qt::Key_F3, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 4"), 0, WIN+ALT+Qt::Key_F4, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 5"), 0, WIN+ALT+Qt::Key_F5, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 6"), 0, WIN+ALT+Qt::Key_F6, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 7"), 0, WIN+ALT+Qt::Key_F7, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 8"), 0, WIN+ALT+Qt::Key_F8, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 9"), 0, WIN+ALT+Qt::Key_F9, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 10"), 0, WIN+ALT+Qt::Key_F10, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 11"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 12"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 13"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 14"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 15"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 16"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 17"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 18"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 19"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Desktop 20"), 0, 0, slotWindowToDesktop(int) ); + DEF( I18N_NOOP("Window to Next Desktop"), 0, 0, slotWindowToNextDesktop() ); + DEF( I18N_NOOP("Window to Previous Desktop"), 0, 0, slotWindowToPreviousDesktop() ); + DEF( I18N_NOOP("Window One Desktop to the Right"), 0, 0, slotWindowToDesktopRight() ); + DEF( I18N_NOOP("Window One Desktop to the Left"), 0, 0, slotWindowToDesktopLeft() ); + DEF( I18N_NOOP("Window One Desktop Up"), 0, 0, slotWindowToDesktopUp() ); + DEF( I18N_NOOP("Window One Desktop Down"), 0, 0, slotWindowToDesktopDown() ); + + keys->insert( "Group:Desktop Switching", i18n("Desktop Switching") ); + DEF( I18N_NOOP("Switch to Desktop 1"), CTRL+Qt::Key_F1, WIN+Qt::Key_F1, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 2"), CTRL+Qt::Key_F2, WIN+Qt::Key_F2, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 3"), CTRL+Qt::Key_F3, WIN+Qt::Key_F3, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 4"), CTRL+Qt::Key_F4, WIN+Qt::Key_F4, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 5"), CTRL+Qt::Key_F5, WIN+Qt::Key_F5, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 6"), CTRL+Qt::Key_F6, WIN+Qt::Key_F6, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 7"), CTRL+Qt::Key_F7, WIN+Qt::Key_F7, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 8"), CTRL+Qt::Key_F8, WIN+Qt::Key_F8, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 9"), CTRL+Qt::Key_F9, WIN+Qt::Key_F9, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 10"), CTRL+Qt::Key_F10, WIN+Qt::Key_F10, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 11"), CTRL+Qt::Key_F11, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 12"), CTRL+Qt::Key_F12, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 13"), CTRL+SHIFT+Qt::Key_F1, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 14"), CTRL+SHIFT+Qt::Key_F2, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 15"), CTRL+SHIFT+Qt::Key_F3, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 16"), CTRL+SHIFT+Qt::Key_F4, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 17"), CTRL+SHIFT+Qt::Key_F5, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 18"), CTRL+SHIFT+Qt::Key_F6, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 19"), CTRL+SHIFT+Qt::Key_F7, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Desktop 20"), CTRL+SHIFT+Qt::Key_F8, 0, slotSwitchToDesktop(int) ); + DEF( I18N_NOOP("Switch to Next Desktop"), 0, 0, slotSwitchDesktopNext() ); + DEF( I18N_NOOP("Switch to Previous Desktop"), 0, 0, slotSwitchDesktopPrevious() ); + DEF( I18N_NOOP("Switch One Desktop to the Right"), 0, 0, slotSwitchDesktopRight() ); + DEF( I18N_NOOP("Switch One Desktop to the Left"), 0, 0, slotSwitchDesktopLeft() ); + DEF( I18N_NOOP("Switch One Desktop Up"), 0, 0, slotSwitchDesktopUp() ); + DEF( I18N_NOOP("Switch One Desktop Down"), 0, 0, slotSwitchDesktopDown() ); + + keys->insert( "Group:Miscellaneous", i18n("Miscellaneous") ); + DEF( I18N_NOOP("Mouse Emulation"), ALT+Qt::Key_F12, 0, slotMouseEmulation() ); + DEF( I18N_NOOP("Kill Window"), ALT+CTRL+Qt::Key_Escape, WIN+CTRL+Qt::Key_Delete, slotKillWindow() ); + DEF( I18N_NOOP("Window Screenshot"), ALT+Qt::Key_Print, ALT+Qt::Key_Print, slotGrabWindow() ); + DEF( I18N_NOOP("Desktop Screenshot"), CTRL+Qt::Key_Print, WIN+Qt::Key_Print, slotGrabDesktop() ); +#ifdef IN_KWIN + { + KGlobalAccel* keys = disable_shortcuts_keys; +#endif + DEF( I18N_NOOP("Block Global Shortcuts"), 0, 0, slotDisableGlobalShortcuts()); +#ifdef IN_KWIN + } +#endif + +/*This belongs in taskbar rather than here, so it'll have to wait until after 2.2 is done. + -- ellis +DEF( I18N_NOOP("Switch to Window 1", WIN+Qt::Key_1")); +DEF( I18N_NOOP("Switch to Window 2", WIN+Qt::Key_2")); +DEF( I18N_NOOP("Switch to Window 3", WIN+Qt::Key_3")); +DEF( I18N_NOOP("Switch to Window 4", WIN+Qt::Key_4")); +DEF( I18N_NOOP("Switch to Window 5", WIN+Qt::Key_5")); +DEF( I18N_NOOP("Switch to Window 6", WIN+Qt::Key_6")); +DEF( I18N_NOOP("Switch to Window 7", WIN+Qt::Key_7")); +DEF( I18N_NOOP("Switch to Window 8", WIN+Qt::Key_8")); +DEF( I18N_NOOP("Switch to Window 9", WIN+Qt::Key_9")); + +#ifdef WITH_LABELS +DEF( I18N_NOOP("Window & Taskbar"Group:Window Desktop", 0); +#endif +DEF( I18N_NOOP("Window to Taskbar Position 1", WIN+Qt::Key_Alt+1")); +DEF( I18N_NOOP("Window to Taskbar Position 2", WIN+Qt::Key_Alt+2")); +DEF( I18N_NOOP("Window to Taskbar Position 3", WIN+Qt::Key_Alt+3")); +DEF( I18N_NOOP("Window to Taskbar Position 4", WIN+Qt::Key_Alt+4")); +DEF( I18N_NOOP("Window to Taskbar Position 5", WIN+Qt::Key_Alt+5")); +DEF( I18N_NOOP("Window to Taskbar Position 6", WIN+Qt::Key_Alt+6")); +DEF( I18N_NOOP("Window to Taskbar Position 7", WIN+Qt::Key_Alt+7")); +DEF( I18N_NOOP("Window to Taskbar Position 8", WIN+Qt::Key_Alt+8")); +DEF( I18N_NOOP("Window to Taskbar Position 9", WIN+Qt::Key_Alt+9")); +*/ + +#undef DEF +#undef DEF2 +#undef WIN diff --git a/kwin/layers.cpp b/kwin/layers.cpp new file mode 100644 index 000000000..28085cf27 --- /dev/null +++ b/kwin/layers.cpp @@ -0,0 +1,773 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +// SELI zmenit doc + +/* + + This file contains things relevant to stacking order and layers. + + Design: + + Normal unconstrained stacking order, as requested by the user (by clicking + on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order. + That list shouldn't be used at all, except for building + Workspace::stacking_order. The building is done + in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should + be used to get the stacking order, because it also checks the stacking order + is up to date. + All clients are also stored in Workspace::clients (except for isDesktop() clients, + as those are very special, and are stored in Workspace::desktops), in the order + the clients were created. + + Every window has one layer assigned in which it is. There are 6 layers, + from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer + and ActiveLayer (see also NETWM sect.7.10.). The layer a window is in depends + on the window type, and on other things like whether the window is active. + + NET::Splash clients belong to the Normal layer. NET::TopMenu clients + belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow + are in the Normal layer in order to keep the 'allow window to cover + the panel' Kicker setting to work as intended (this may look like a slight + spec violation, but a) I have no better idea, b) the spec allows adjusting + the stacking order if the WM thinks it's a good idea . We put all + NET::KeepAbove above all Docks too, even though the spec suggests putting + them in the same layer. + + Most transients are in the same layer as their mainwindow, + see Workspace::constrainedStackingOrder(), they may also be in higher layers, but + they should never be below their mainwindow. + + When some client attribute changes (above/below flag, transiency...), + Workspace::updateClientLayer() should be called in order to make + sure it's moved to the appropriate layer ClientList if needed. + + Currently the things that affect client in which layer a client + belongs: KeepAbove/Keep Below flags, window type, fullscreen + state and whether the client is active, mainclient (transiency). + + Make sure updateStackingOrder() is called in order to make + Workspace::stackingOrder() up to date and propagated to the world. + Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker + helper class) it's possible to temporarily disable updates + and the stacking order will be updated once after it's allowed again. + +*/ + +#include <assert.h> + +#include <kdebug.h> + +#include "utils.h" +#include "client.h" +#include "workspace.h" +#include "tabbox.h" +#include "group.h" +#include "rules.h" + +extern Time qt_x_time; + +namespace KWinInternal +{ + +//******************************* +// Workspace +//******************************* + +void Workspace::updateClientLayer( Client* c ) + { + if( c == NULL ) + return; + if( c->layer() == c->belongsToLayer()) + return; + StackingUpdatesBlocker blocker( this ); + c->invalidateLayer(); // invalidate, will be updated when doing restacking + for( ClientList::ConstIterator it = c->transients().begin(); + it != c->transients().end(); + ++it ) + updateClientLayer( *it ); + } + +void Workspace::updateStackingOrder( bool propagate_new_clients ) + { + if( block_stacking_updates > 0 ) + { + blocked_propagating_new_clients = blocked_propagating_new_clients || propagate_new_clients; + return; + } + ClientList new_stacking_order = constrainedStackingOrder(); + bool changed = ( new_stacking_order != stacking_order ); + stacking_order = new_stacking_order; +#if 0 + kdDebug() << "stacking:" << changed << endl; + if( changed || propagate_new_clients ) + { + for( ClientList::ConstIterator it = stacking_order.begin(); + it != stacking_order.end(); + ++it ) + kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl; + } +#endif + if( changed || propagate_new_clients ) + { + propagateClients( propagate_new_clients ); + if( active_client ) + active_client->updateMouseGrab(); + } + } + +/*! + Propagates the managed clients to the world. + Called ONLY from updateStackingOrder(). + */ +void Workspace::propagateClients( bool propagate_new_clients ) + { + Window *cl; // MW we should not assume WId and Window to be compatible + // when passig pointers around. + + // restack the windows according to the stacking order + Window* new_stack = new Window[ stacking_order.count() + 2 ]; + int pos = 0; + // Stack all windows under the support window. The support window is + // not used for anything (besides the NETWM property), and it's not shown, + // but it was lowered after kwin startup. Stacking all clients below + // it ensures that no client will be ever shown above override-redirect + // windows (e.g. popups). + new_stack[ pos++ ] = supportWindow->winId(); + int topmenu_space_pos = 1; // not 0, that's supportWindow !!! + for( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); + --it ) + { + new_stack[ pos++ ] = (*it)->frameId(); + if( (*it)->belongsToLayer() >= DockLayer ) + topmenu_space_pos = pos; + } + if( topmenu_space != NULL ) + { // make sure the topmenu space is below all topmenus, fullscreens, etc. + for( int i = pos; + i > topmenu_space_pos; + --i ) + new_stack[ i ] = new_stack[ i - 1 ]; + new_stack[ topmenu_space_pos ] = topmenu_space->winId(); + ++pos; + } + // TODO isn't it too inefficient to restart always all clients? + // TODO don't restack not visible windows? + assert( new_stack[ 0 ] = supportWindow->winId()); + XRestackWindows(qt_xdisplay(), new_stack, pos); + delete [] new_stack; + + if ( propagate_new_clients ) + { + cl = new Window[ desktops.count() + clients.count()]; + pos = 0; + // TODO this is still not completely in the map order + for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it ) + cl[pos++] = (*it)->window(); + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it ) + cl[pos++] = (*it)->window(); + rootInfo->setClientList( cl, pos ); + delete [] cl; + } + + cl = new Window[ stacking_order.count()]; + pos = 0; + for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) + cl[pos++] = (*it)->window(); + rootInfo->setClientListStacking( cl, pos ); + delete [] cl; + } + + +/*! + Returns topmost visible client. Windows on the dock, the desktop + or of any other special kind are excluded. Also if the window + doesn't accept focus it's excluded. + */ +// TODO misleading name for this method +Client* Workspace::topClientOnDesktop( int desktop, bool unconstrained, bool only_normal ) const + { +// TODO Q_ASSERT( block_stacking_updates == 0 ); + ClientList::ConstIterator begin, end; + if( !unconstrained ) + { + begin = stacking_order.fromLast(); + end = stacking_order.end(); + } + else + { + begin = unconstrained_stacking_order.fromLast(); + end = unconstrained_stacking_order.end(); + } + for( ClientList::ConstIterator it = begin; + it != end; + --it ) + { + if( (*it)->isOnDesktop( desktop ) && (*it)->isShown( false )) + { + if( !only_normal ) + return *it; + if( (*it)->wantsTabFocus() && !(*it)->isSpecialWindow()) + return *it; + } + } + return 0; + } + +Client* Workspace::findDesktop( bool topmost, int desktop ) const + { +// TODO Q_ASSERT( block_stacking_updates == 0 ); + if( topmost ) + { + for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) + { + if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop() + && (*it)->isShown( true )) + return *it; + } + } + else // bottom-most + { + for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) + { + if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop() + && (*it)->isShown( true )) + return *it; + } + } + return NULL; + } + +void Workspace::raiseOrLowerClient( Client *c) + { + if (!c) return; + Client* topmost = NULL; +// TODO Q_ASSERT( block_stacking_updates == 0 ); + if ( most_recently_raised && stacking_order.contains( most_recently_raised ) && + most_recently_raised->isShown( true ) && c->isOnCurrentDesktop()) + topmost = most_recently_raised; + else + topmost = topClientOnDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop()); + + if( c == topmost) + lowerClient(c); + else + raiseClient(c); + } + + +void Workspace::lowerClient( Client* c ) + { + if ( !c ) + return; + if( c->isTopMenu()) + return; + + c->cancelAutoRaise(); + + StackingUpdatesBlocker blocker( this ); + + unconstrained_stacking_order.remove( c ); + unconstrained_stacking_order.prepend( c ); + if( c->isTransient()) + { + // lower also mainclients, in their reversed stacking order + ClientList mainclients = ensureStackingOrder( c->mainClients()); + for( ClientList::ConstIterator it = mainclients.fromLast(); + it != mainclients.end(); + ++it ) + lowerClient( *it ); + } + + if ( c == most_recently_raised ) + most_recently_raised = 0; + } + +void Workspace::lowerClientWithinApplication( Client* c ) + { + if ( !c ) + return; + if( c->isTopMenu()) + return; + + c->cancelAutoRaise(); + + StackingUpdatesBlocker blocker( this ); + + unconstrained_stacking_order.remove( c ); + bool lowered = false; + // first try to put it below the bottom-most window of the application + for( ClientList::Iterator it = unconstrained_stacking_order.begin(); + it != unconstrained_stacking_order.end(); + ++it ) + if( Client::belongToSameApplication( *it, c )) + { + unconstrained_stacking_order.insert( it, c ); + lowered = true; + break; + } + if( !lowered ) + unconstrained_stacking_order.prepend( c ); + // ignore mainwindows + } + +void Workspace::raiseClient( Client* c ) + { + if ( !c ) + return; + if( c->isTopMenu()) + return; + + c->cancelAutoRaise(); + + StackingUpdatesBlocker blocker( this ); + + if( c->isTransient()) + { + ClientList mainclients = ensureStackingOrder( c->mainClients()); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + raiseClient( *it ); + } + + unconstrained_stacking_order.remove( c ); + unconstrained_stacking_order.append( c ); + + if( !c->isSpecialWindow()) + { + most_recently_raised = c; + pending_take_activity = NULL; + } + } + +void Workspace::raiseClientWithinApplication( Client* c ) + { + if ( !c ) + return; + if( c->isTopMenu()) + return; + + c->cancelAutoRaise(); + + StackingUpdatesBlocker blocker( this ); + // ignore mainwindows + + // first try to put it above the top-most window of the application + for( ClientList::Iterator it = unconstrained_stacking_order.fromLast(); + it != unconstrained_stacking_order.end(); + --it ) + { + if( *it == c ) // don't lower it just because it asked to be raised + return; + if( Client::belongToSameApplication( *it, c )) + { + unconstrained_stacking_order.remove( c ); + ++it; // insert after the found one + unconstrained_stacking_order.insert( it, c ); + return; + } + } + } + +void Workspace::raiseClientRequest( Client* c, NET::RequestSource src, Time timestamp ) + { + if( src == NET::FromTool || allowFullClientRaising( c, timestamp )) + raiseClient( c ); + else + { + raiseClientWithinApplication( c ); + c->demandAttention(); + } + } + +void Workspace::lowerClientRequest( Client* c, NET::RequestSource src, Time /*timestamp*/ ) + { + // If the client has support for all this focus stealing prevention stuff, + // do only lowering within the application, as that's the more logical + // variant of lowering when application requests it. + // No demanding of attention here of course. + if( src == NET::FromTool || !c->hasUserTimeSupport()) + lowerClient( c ); + else + lowerClientWithinApplication( c ); + } + +void Workspace::restackClientUnderActive( Client* c ) + { + if( c->isTopMenu()) + return; + if( !active_client || active_client == c ) + { + raiseClient( c ); + return; + } + + assert( unconstrained_stacking_order.contains( active_client )); + if( Client::belongToSameApplication( active_client, c )) + { // put it below the active window if it's the same app + unconstrained_stacking_order.remove( c ); + unconstrained_stacking_order.insert( unconstrained_stacking_order.find( active_client ), c ); + } + else + { // put in the stacking order below _all_ windows belonging to the active application + for( ClientList::Iterator it = unconstrained_stacking_order.begin(); + it != unconstrained_stacking_order.end(); + ++it ) + { // TODO ignore topmenus? + if( Client::belongToSameApplication( active_client, *it )) + { + if( *it != c ) + { + unconstrained_stacking_order.remove( c ); + unconstrained_stacking_order.insert( it, c ); + } + break; + } + } + } + assert( unconstrained_stacking_order.contains( c )); + for( int desktop = 1; + desktop <= numberOfDesktops(); + ++desktop ) + { // do for every virtual desktop to handle the case of onalldesktop windows + if( c->wantsTabFocus() && c->isOnDesktop( desktop ) && focus_chain[ desktop ].contains( active_client )) + { + if( Client::belongToSameApplication( active_client, c )) + { // put it after the active window if it's the same app + focus_chain[ desktop ].remove( c ); + focus_chain[ desktop ].insert( focus_chain[ desktop ].find( active_client ), c ); + } + else + { // put it in focus_chain[currentDesktop()] after all windows belonging to the active applicationa + focus_chain[ desktop ].remove( c ); + for( ClientList::Iterator it = focus_chain[ desktop ].fromLast(); + it != focus_chain[ desktop ].end(); + --it ) + { + if( Client::belongToSameApplication( active_client, *it )) + { + focus_chain[ desktop ].insert( it, c ); + break; + } + } + } + } + } + // the same for global_focus_chain + if( c->wantsTabFocus() && global_focus_chain.contains( active_client )) + { + if( Client::belongToSameApplication( active_client, c )) + { + global_focus_chain.remove( c ); + global_focus_chain.insert( global_focus_chain.find( active_client ), c ); + } + else + { + global_focus_chain.remove( c ); + for( ClientList::Iterator it = global_focus_chain.fromLast(); + it != global_focus_chain.end(); + --it ) + { + if( Client::belongToSameApplication( active_client, *it )) + { + global_focus_chain.insert( it, c ); + break; + } + } + } + } + updateStackingOrder(); + } + +void Workspace::circulateDesktopApplications() + { + if ( desktops.count() > 1 ) + { + bool change_active = activeClient()->isDesktop(); + raiseClient( findDesktop( false, currentDesktop())); + if( change_active ) // if the previously topmost Desktop was active, activate this new one + activateClient( findDesktop( true, currentDesktop())); + } + // if there's no active client, make desktop the active one + if( desktops.count() > 0 && activeClient() == NULL && should_get_focus.count() == 0 ) + activateClient( findDesktop( true, currentDesktop())); + } + + +/*! + Returns a stacking order based upon \a list that fulfills certain contained. + */ +ClientList Workspace::constrainedStackingOrder() + { + ClientList layer[ NumLayers ]; + +#if 0 + kdDebug() << "stacking1:" << endl; +#endif + // build the order from layers + QMap< Group*, Layer > minimum_layer; + for( ClientList::ConstIterator it = unconstrained_stacking_order.begin(); + it != unconstrained_stacking_order.end(); + ++it ) + { + Layer l = (*it)->layer(); + // If a window is raised above some other window in the same window group + // which is in the ActiveLayer (i.e. it's fulscreened), make sure it stays + // above that window (see #95731). + if( minimum_layer.contains( (*it)->group()) + && minimum_layer[ (*it)->group() ] == ActiveLayer + && ( l == NormalLayer || l == AboveLayer )) + { + l = minimum_layer[ (*it)->group() ]; + } + minimum_layer[ (*it)->group() ] = l; + layer[ l ].append( *it ); + } + ClientList stacking; + for( Layer lay = FirstLayer; + lay < NumLayers; + ++lay ) + stacking += layer[ lay ]; +#if 0 + kdDebug() << "stacking2:" << endl; + for( ClientList::ConstIterator it = stacking.begin(); + it != stacking.end(); + ++it ) + kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl; +#endif + // now keep transients above their mainwindows + // TODO this could(?) use some optimization + for( ClientList::Iterator it = stacking.fromLast(); + it != stacking.end(); + ) + { + if( !(*it)->isTransient()) + { + --it; + continue; + } + ClientList::Iterator it2 = stacking.end(); + if( (*it)->groupTransient()) + { + if( (*it)->group()->members().count() > 0 ) + { // find topmost client this one is transient for + for( it2 = stacking.fromLast(); + it2 != stacking.end(); + --it2 ) + { + if( *it2 == *it ) + { + it2 = stacking.end(); // don't reorder + break; + } + if( (*it2)->hasTransient( *it, true ) && keepTransientAbove( *it2, *it )) + break; + } + } // else it2 remains pointing at stacking.end() + } + else + { + for( it2 = stacking.fromLast(); + it2 != stacking.end(); + --it2 ) + { + if( *it2 == *it ) + { + it2 = stacking.end(); // don't reorder + break; + } + if( *it2 == (*it)->transientFor() && keepTransientAbove( *it2, *it )) + break; + } + } +// kdDebug() << "STACK:" << (*it) << ":" << ( it2 == stacking.end() ? ((Client*)0) : (*it2)) << endl; + if( it2 == stacking.end()) + { + --it; + continue; + } + Client* current = *it; + ClientList::Iterator remove_it = it; + --it; + stacking.remove( remove_it ); + if( !current->transients().isEmpty()) // this one now can be possibly above its transients, + it = it2; // so go again higher in the stack order and possibly move those transients again + ++it2; // insert after the mainwindow, it's ok if it2 is now stacking.end() + stacking.insert( it2, current ); + } +#if 0 + kdDebug() << "stacking3:" << endl; + for( ClientList::ConstIterator it = stacking.begin(); + it != stacking.end(); + ++it ) + kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl; + kdDebug() << "\n\n" << endl; +#endif + return stacking; + } + +void Workspace::blockStackingUpdates( bool block ) + { + if( block ) + { + if( block_stacking_updates == 0 ) + blocked_propagating_new_clients = false; + ++block_stacking_updates; + } + else // !block + if( --block_stacking_updates == 0 ) + updateStackingOrder( blocked_propagating_new_clients ); + } + +// Ensure list is in stacking order +ClientList Workspace::ensureStackingOrder( const ClientList& list ) const + { +// TODO Q_ASSERT( block_stacking_updates == 0 ); + if( list.count() < 2 ) + return list; + // TODO is this worth optimizing? + ClientList result = list; + for( ClientList::ConstIterator it = stacking_order.begin(); + it != stacking_order.end(); + ++it ) + if( result.remove( *it ) != 0 ) + result.append( *it ); + return result; + } + +// check whether a transient should be actually kept above its mainwindow +// there may be some special cases where this rule shouldn't be enfored +bool Workspace::keepTransientAbove( const Client* mainwindow, const Client* transient ) + { + // When topmenu's mainwindow becomes active, topmenu is raised and shown. + // They also belong to the Dock layer. This makes them to be very high. + // Therefore don't keep group transients above them, otherwise this would move + // group transients way too high. + if( mainwindow->isTopMenu() && transient->groupTransient()) + return false; + // #93832 - don't keep splashscreens above dialogs + if( transient->isSplash() && mainwindow->isDialog()) + return false; + // This is rather a hack for #76026. Don't keep non-modal dialogs above + // the mainwindow, but only if they're group transient (since only such dialogs + // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker) + // needs to be found. + if( transient->isDialog() && !transient->isModal() && transient->groupTransient()) + return false; + // #63223 - don't keep transients above docks, because the dock is kept high, + // and e.g. dialogs for them would be too high too + if( mainwindow->isDock()) + return false; + return true; + } + +//******************************* +// Client +//******************************* + +void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSource src, Time timestamp, bool send_event ) + { + switch ( detail ) + { + case Above: + case TopIf: + workspace()->raiseClientRequest( this, src, timestamp ); + break; + case Below: + case BottomIf: + workspace()->lowerClientRequest( this, src, timestamp ); + break; + case Opposite: + default: + break; + } + if( send_event ) + sendSyntheticConfigureNotify(); + } + +void Client::setKeepAbove( bool b ) + { + b = rules()->checkKeepAbove( b ); + if( b && !rules()->checkKeepBelow( false )) + setKeepBelow( false ); + if ( b == keepAbove()) + { // force hint change if different + if( bool( info->state() & NET::KeepAbove ) != keepAbove()) + info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove ); + return; + } + keep_above = b; + info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove ); + if( decoration != NULL ) + decoration->emitKeepAboveChanged( keepAbove()); + workspace()->updateClientLayer( this ); + updateWindowRules(); + } + +void Client::setKeepBelow( bool b ) + { + b = rules()->checkKeepBelow( b ); + if( b && !rules()->checkKeepAbove( false )) + setKeepAbove( false ); + if ( b == keepBelow()) + { // force hint change if different + if( bool( info->state() & NET::KeepBelow ) != keepBelow()) + info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow ); + return; + } + keep_below = b; + info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow ); + if( decoration != NULL ) + decoration->emitKeepBelowChanged( keepBelow()); + workspace()->updateClientLayer( this ); + updateWindowRules(); + } + +Layer Client::layer() const + { + if( in_layer == UnknownLayer ) + const_cast< Client* >( this )->in_layer = belongsToLayer(); + return in_layer; + } + +Layer Client::belongsToLayer() const + { + if( isDesktop()) + return DesktopLayer; + if( isSplash()) // no damn annoying splashscreens + return NormalLayer; // getting in the way of everything else + if( isDock() && keepBelow()) + // slight hack for the 'allow window to cover panel' Kicker setting + // don't move keepbelow docks below normal window, but only to the same + // layer, so that both may be raised to cover the other + return NormalLayer; + if( keepBelow()) + return BelowLayer; + if( isDock() && !keepBelow()) + return DockLayer; + if( isTopMenu()) + return DockLayer; + // only raise fullscreen above docks if it's the topmost window in unconstrained stacking order, + // i.e. the window set to be topmost by the user (also includes transients of the fullscreen window) + const Client* ac = workspace()->mostRecentlyActivatedClient(); // instead of activeClient() - avoids flicker + const Client* top = workspace()->topClientOnDesktop( desktop(), true, false ); + if( isFullScreen() && ac != NULL && top != NULL + && ( ac == this || this->group() == ac->group()) + && ( top == this || this->group() == top->group())) + return ActiveLayer; + if( keepAbove()) + return AboveLayer; + return NormalLayer; + } + +} // namespace diff --git a/kwin/lib/Makefile.am b/kwin/lib/Makefile.am new file mode 100644 index 000000000..bb584e25a --- /dev/null +++ b/kwin/lib/Makefile.am @@ -0,0 +1,20 @@ +# FRAME libkwin??? +lib_LTLIBRARIES = libkdecorations.la + +libkdecorations_la_SOURCES = kdecoration.cpp kdecoration_p.cpp kdecoration_plugins_p.cpp \ + kdecorationfactory.cpp kcommondecoration.cpp +libkdecorations_la_LIBADD = $(LIB_KDECORE) +libkdecorations_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined + +# FRAME +include_HEADERS = kdecoration.h kdecoration_p.h kdecoration_plugins_p.h \ + kdecorationfactory.h kcommondecoration.h + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +include ../../admin/Doxyfile.am + +messages: rc.cpp + $(XGETTEXT) `find . -name \*.cpp` -o $(podir)/kwin_lib.pot + -rm rc.cpp diff --git a/kwin/lib/kcommondecoration.cpp b/kwin/lib/kcommondecoration.cpp new file mode 100644 index 000000000..58a95b599 --- /dev/null +++ b/kwin/lib/kcommondecoration.cpp @@ -0,0 +1,966 @@ +/* + This file is part of the KDE project. + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + */ + +#include <qapplication.h> +#include <qcursor.h> +#include <qdatetime.h> +#include <qlabel.h> +#include <qtooltip.h> +#include <qwidget.h> + +#include <kdebug.h> + +#include <kapplication.h> +#include <kdecorationfactory.h> +#include <klocale.h> + +#include "kcommondecoration.h" +#include "kcommondecoration.moc" + +KCommonDecoration::KCommonDecoration(KDecorationBridge* bridge, KDecorationFactory* factory) + : KDecoration (bridge, factory), + m_previewWidget(0), + btnHideMinWidth(200), + btnHideLastWidth(0), + closing(false) +{ + // sizeof(...) is calculated at compile time + memset(m_button, 0, sizeof(KCommonDecorationButton *) * NumButtons); +} + +KCommonDecoration::~KCommonDecoration() +{ + for (int n=0; n<NumButtons; n++) { + if (m_button[n]) delete m_button[n]; + } + delete m_previewWidget; +} + +bool KCommonDecoration::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return false; + + case DB_ButtonHide: + return true; + } + + return false; +} + +int KCommonDecoration::layoutMetric(LayoutMetric lm, bool, const KCommonDecorationButton *) const +{ + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + case LM_TitleEdgeTop: + case LM_TitleEdgeBottom: + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 5; + + + case LM_ButtonWidth: + case LM_ButtonHeight: + case LM_TitleHeight: + return 20; + + case LM_ButtonSpacing: + return 5; + + case LM_ButtonMarginTop: + return 0; + + case LM_ExplicitButtonSpacer: + return 5; + + default: + return 0; + } +} + +void KCommonDecoration::init() +{ + createMainWidget(WNoAutoErase); + + // for flicker-free redraws + widget()->setBackgroundMode(NoBackground); + + widget()->installEventFilter( this ); + + resetLayout(); + + connect(this, SIGNAL(keepAboveChanged(bool) ), SLOT(keepAboveChange(bool) ) ); + connect(this, SIGNAL(keepBelowChanged(bool) ), SLOT(keepBelowChange(bool) ) ); + + updateCaption(); +} + +void KCommonDecoration::reset( unsigned long changed ) +{ + if (changed & SettingButtons) { + resetLayout(); + widget()->update(); + } +} + +QRegion KCommonDecoration::cornerShape(WindowCorner) +{ + return QRegion(); +} + +void KCommonDecoration::updateCaption() +{ + // This should be reimplemented in decorations for better efficiency + widget()->update(); +} + +void KCommonDecoration::borders( int& left, int& right, int& top, int& bottom ) const +{ + left = layoutMetric(LM_BorderLeft); + right = layoutMetric(LM_BorderRight); + bottom = layoutMetric(LM_BorderBottom); + top = layoutMetric(LM_TitleHeight) + + layoutMetric(LM_TitleEdgeTop) + + layoutMetric(LM_TitleEdgeBottom); + + updateLayout(); // TODO!! don't call everytime we are in ::borders +} + +void KCommonDecoration::updateLayout() const +{ + QRect r = widget()->rect(); + int r_x, r_y, r_x2, r_y2; + r.coords(&r_x, &r_y, &r_x2, &r_y2); + + // layout preview widget + if (m_previewWidget) { + const int borderLeft = layoutMetric(LM_BorderLeft); + const int borderRight = layoutMetric(LM_BorderRight); + const int borderBottom = layoutMetric(LM_BorderBottom); + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + + int left = r_x+borderLeft; + int top = r_y+titleEdgeTop+titleHeight+titleEdgeBottom; + int width = r_x2-borderRight-left+1; + int height = r_y2-borderBottom-top+1; + m_previewWidget->setGeometry(left, top, width, height); + moveWidget(left,top, m_previewWidget); + resizeWidget(width, height, m_previewWidget); + } + + // resize buttons... + for (int n=0; n<NumButtons; n++) { + if (m_button[n]) { + QSize newSize = QSize(layoutMetric(LM_ButtonWidth, true, m_button[n]), + layoutMetric(LM_ButtonHeight, true, m_button[n]) ); + if (newSize != m_button[n]->size() ) + m_button[n]->setSize(newSize); + } + } + + // layout buttons + int y = r_y + layoutMetric(LM_TitleEdgeTop) + layoutMetric(LM_ButtonMarginTop); + if (m_buttonsLeft.count() > 0) { + const int buttonSpacing = layoutMetric(LM_ButtonSpacing); + int x = r_x + layoutMetric(LM_TitleEdgeLeft); + for (ButtonContainer::const_iterator it = m_buttonsLeft.begin(); it != m_buttonsLeft.end(); ++it) { + bool elementLayouted = false; + if (*it) { + if (!(*it)->isHidden() ) { + moveWidget(x,y, *it); + x += layoutMetric(LM_ButtonWidth, true, ::qt_cast<KCommonDecorationButton*>(*it) ); + elementLayouted = true; + } + } else { + x+= layoutMetric(LM_ExplicitButtonSpacer); + elementLayouted = true; + } + if (elementLayouted && it != m_buttonsLeft.end() ) + x += buttonSpacing; + } + } + + if (m_buttonsRight.count() > 0) { + const int titleEdgeRightLeft = r_x2-layoutMetric(LM_TitleEdgeRight)+1; + + const int buttonSpacing = layoutMetric(LM_ButtonSpacing); + int x = titleEdgeRightLeft - buttonContainerWidth(m_buttonsRight); + for (ButtonContainer::const_iterator it = m_buttonsRight.begin(); it != m_buttonsRight.end(); ++it) { + bool elementLayouted = false; + if (*it) { + if (!(*it)->isHidden() ) { + moveWidget(x,y, *it); + x += layoutMetric(LM_ButtonWidth, true, ::qt_cast<KCommonDecorationButton*>(*it) );; + elementLayouted = true; + } + } else { + x += layoutMetric(LM_ExplicitButtonSpacer); + elementLayouted = true; + } + if (elementLayouted && it != m_buttonsRight.end() ) + x += buttonSpacing; + } + } +} + +void KCommonDecoration::updateButtons() const +{ + for (int n=0; n<NumButtons; n++) + if (m_button[n]) m_button[n]->update(); +} + +void KCommonDecoration::resetButtons() const +{ + for (int n=0; n<NumButtons; n++) + if (m_button[n]) m_button[n]->reset(KCommonDecorationButton::ManualReset); +} + +void KCommonDecoration::resetLayout() +{ + for (int n=0; n<NumButtons; n++) { + if (m_button[n]) { + delete m_button[n]; + m_button[n] = 0; + } + } + m_buttonsLeft.clear(); + m_buttonsRight.clear(); + + delete m_previewWidget; + m_previewWidget = 0; + + // shown instead of the window contents in decoration previews + if(isPreview() ) { + m_previewWidget = new QLabel(i18n("%1 is the name of window decoration style", "<center><b>%1 preview</b></center>").arg(visibleName() ), widget()); + m_previewWidget->show(); + } + + addButtons(m_buttonsLeft, + options()->customButtonPositions() ? options()->titleButtonsLeft() : defaultButtonsLeft(), + true); + addButtons(m_buttonsRight, + options()->customButtonPositions() ? options()->titleButtonsRight() : defaultButtonsRight(), + false); + + updateLayout(); + + const int minTitleBarWidth = 35; + btnHideMinWidth = buttonContainerWidth(m_buttonsLeft,true) + buttonContainerWidth(m_buttonsRight,true) + + layoutMetric(LM_TitleEdgeLeft,false) + layoutMetric(LM_TitleEdgeRight,false) + + layoutMetric(LM_TitleBorderLeft,false) + layoutMetric(LM_TitleBorderRight,false) + + minTitleBarWidth; + btnHideLastWidth = 0; +} + +int KCommonDecoration::buttonsLeftWidth() const +{ + return buttonContainerWidth(m_buttonsLeft); +} + +int KCommonDecoration::buttonsRightWidth() const +{ + return buttonContainerWidth(m_buttonsRight); +} + +int KCommonDecoration::buttonContainerWidth(const ButtonContainer &btnContainer, bool countHidden) const +{ + int explicitSpacer = layoutMetric(LM_ExplicitButtonSpacer); + + int shownElementsCount = 0; + + int w = 0; + for (ButtonContainer::const_iterator it = btnContainer.begin(); it != btnContainer.end(); ++it) { + if (*it) { + if (countHidden || !(*it)->isHidden() ) { + w += (*it)->width(); + ++shownElementsCount; + } + } else { + w += explicitSpacer; + ++shownElementsCount; + } + } + w += layoutMetric(LM_ButtonSpacing)*(shownElementsCount-1); + + return w; +} + +void KCommonDecoration::addButtons(ButtonContainer &btnContainer, const QString& s, bool isLeft) +{ + if (s.length() > 0) { + for (unsigned n=0; n < s.length(); n++) { + KCommonDecorationButton *btn = 0; + switch (s[n]) { + case 'M': // Menu button + if (!m_button[MenuButton]){ + btn = createButton(MenuButton); + if (!btn) break; + btn->setTipText(i18n("Menu") ); + btn->setRealizeButtons(LeftButton|RightButton); + connect(btn, SIGNAL(pressed()), SLOT(menuButtonPressed())); + connect(btn, SIGNAL(released()), this, SLOT(menuButtonReleased())); + + m_button[MenuButton] = btn; + } + break; + case 'S': // OnAllDesktops button + if (!m_button[OnAllDesktopsButton]){ + btn = createButton(OnAllDesktopsButton); + if (!btn) break; + const bool oad = isOnAllDesktops(); + btn->setTipText(oad?i18n("Not on all desktops"):i18n("On all desktops") ); + btn->setToggleButton(true); + btn->setOn( oad ); + connect(btn, SIGNAL(clicked()), SLOT(toggleOnAllDesktops())); + + m_button[OnAllDesktopsButton] = btn; + } + break; + case 'H': // Help button + if ((!m_button[HelpButton]) && providesContextHelp()){ + btn = createButton(HelpButton); + if (!btn) break; + btn->setTipText(i18n("Help") ); + connect(btn, SIGNAL(clicked()), SLOT(showContextHelp())); + + m_button[HelpButton] = btn; + } + break; + case 'I': // Minimize button + if ((!m_button[MinButton]) && isMinimizable()){ + btn = createButton(MinButton); + if (!btn) break; + btn->setTipText(i18n("Minimize") ); + connect(btn, SIGNAL(clicked()), SLOT(minimize())); + + m_button[MinButton] = btn; + } + break; + case 'A': // Maximize button + if ((!m_button[MaxButton]) && isMaximizable()){ + btn = createButton(MaxButton); + if (!btn) break; + btn->setRealizeButtons(LeftButton|MidButton|RightButton); + const bool max = maximizeMode()==MaximizeFull; + btn->setTipText(max?i18n("Restore"):i18n("Maximize") ); + btn->setToggleButton(true); + btn->setOn( max ); + connect(btn, SIGNAL(clicked()), SLOT(slotMaximize())); + + m_button[MaxButton] = btn; + } + break; + case 'X': // Close button + if ((!m_button[CloseButton]) && isCloseable()){ + btn = createButton(CloseButton); + if (!btn) break; + btn->setTipText(i18n("Close") ); + connect(btn, SIGNAL(clicked()), SLOT(closeWindow())); + + m_button[CloseButton] = btn; + } + break; + case 'F': // AboveButton button + if (!m_button[AboveButton]){ + btn = createButton(AboveButton); + if (!btn) break; + bool above = keepAbove(); + btn->setTipText(above?i18n("Do not keep above others"):i18n("Keep above others") ); + btn->setToggleButton(true); + btn->setOn( above ); + connect(btn, SIGNAL(clicked()), SLOT(slotKeepAbove())); + + m_button[AboveButton] = btn; + } + break; + case 'B': // BelowButton button + if (!m_button[BelowButton]){ + btn = createButton(BelowButton); + if (!btn) break; + bool below = keepBelow(); + btn->setTipText(below?i18n("Do not keep below others"):i18n("Keep below others") ); + btn->setToggleButton(true); + btn->setOn( below ); + connect(btn, SIGNAL(clicked()), SLOT(slotKeepBelow())); + + m_button[BelowButton] = btn; + } + break; + case 'L': // Shade button + if ((!m_button[ShadeButton]) && isShadeable()){ + btn = createButton(ShadeButton); + if (!btn) break; + bool shaded = isSetShade(); + btn->setTipText(shaded?i18n("Unshade"):i18n("Shade") ); + btn->setToggleButton(true); + btn->setOn( shaded ); + connect(btn, SIGNAL(clicked()), SLOT(slotShade())); + + m_button[ShadeButton] = btn; + } + break; + case '_': // Spacer item + btnContainer.append(0); + } + + + if (btn) { + btn->setLeft(isLeft); + btn->setSize(QSize(layoutMetric(LM_ButtonWidth, true, btn),layoutMetric(LM_ButtonHeight, true, btn)) ); + btn->show(); + btnContainer.append(btn); + } + + } + } +} + +void KCommonDecoration::calcHiddenButtons() +{ + if (width() == btnHideLastWidth) + return; + + btnHideLastWidth = width(); + + //Hide buttons in the following order: + KCommonDecorationButton* btnArray[] = { m_button[HelpButton], m_button[ShadeButton], m_button[BelowButton], + m_button[AboveButton], m_button[OnAllDesktopsButton], m_button[MaxButton], + m_button[MinButton], m_button[MenuButton], m_button[CloseButton] }; + const int buttonsCount = sizeof( btnArray ) / sizeof( btnArray[ 0 ] ); + + int current_width = width(); + int count = 0; + + // Hide buttons + while (current_width < btnHideMinWidth && count < buttonsCount) + { + if (btnArray[count] ) { + current_width += btnArray[count]->width(); + if (btnArray[count]->isVisible() ) + btnArray[count]->hide(); + } + count++; + } + // Show the rest of the buttons... + for(int i = count; i < buttonsCount; i++) + { + if (btnArray[i] ) { + + if (! btnArray[i]->isHidden() ) + break; // all buttons shown... + + btnArray[i]->show(); + } + } +} + +void KCommonDecoration::show() +{ + if (decorationBehaviour(DB_ButtonHide) ) + calcHiddenButtons(); + widget()->show(); +} + +void KCommonDecoration::resize( const QSize& s ) +{ + widget()->resize( s ); +} + +QSize KCommonDecoration::minimumSize() const +{ + const int minWidth = QMAX(layoutMetric(LM_TitleEdgeLeft), layoutMetric(LM_BorderLeft)) + +QMAX(layoutMetric(LM_TitleEdgeRight), layoutMetric(LM_BorderRight)) + +layoutMetric(LM_TitleBorderLeft)+layoutMetric(LM_TitleBorderRight); + return QSize(minWidth, + layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight) + +layoutMetric(LM_TitleEdgeBottom) + +layoutMetric(LM_BorderBottom) ); +} + +void KCommonDecoration::maximizeChange() +{ + if( m_button[MaxButton] ) { + m_button[MaxButton]->setOn( maximizeMode()==MaximizeFull); + m_button[MaxButton]->setTipText( (maximizeMode()!=MaximizeFull) ? + i18n("Maximize") + : i18n("Restore")); + m_button[MaxButton]->reset(KCommonDecorationButton::StateChange); + } + updateWindowShape(); + widget()->update(); +} + +void KCommonDecoration::desktopChange() +{ + if ( m_button[OnAllDesktopsButton] ) { + m_button[OnAllDesktopsButton]->setOn( isOnAllDesktops() ); + m_button[OnAllDesktopsButton]->setTipText( isOnAllDesktops() ? + i18n("Not on all desktops") + : i18n("On all desktops")); + m_button[OnAllDesktopsButton]->reset(KCommonDecorationButton::StateChange); + } +} + +void KCommonDecoration::shadeChange() +{ + if ( m_button[ShadeButton] ) { + bool shaded = isSetShade(); + m_button[ShadeButton]->setOn( shaded ); + m_button[ShadeButton]->setTipText( shaded ? + i18n("Unshade") + : i18n("Shade")); + m_button[ShadeButton]->reset(KCommonDecorationButton::StateChange); + } +} + +void KCommonDecoration::iconChange() +{ + if (m_button[MenuButton]) + { + m_button[MenuButton]->update(); + m_button[MenuButton]->reset(KCommonDecorationButton::IconChange); + } +} + +void KCommonDecoration::activeChange() +{ + updateButtons(); + widget()->update(); // do something similar to updateCaption here +} + +void KCommonDecoration::captionChange() +{ + updateCaption(); +} + +void KCommonDecoration::keepAboveChange(bool above) +{ + if (m_button[AboveButton]) + { + m_button[AboveButton]->setOn(above); + m_button[AboveButton]->setTipText( above?i18n("Do not keep above others"):i18n("Keep above others") ); + m_button[AboveButton]->reset(KCommonDecorationButton::StateChange); + } + + if (m_button[BelowButton] && m_button[BelowButton]->isOn()) + { + m_button[BelowButton]->setOn(false); + m_button[BelowButton]->setTipText( i18n("Keep below others") ); + m_button[BelowButton]->reset(KCommonDecorationButton::StateChange); + } +} + +void KCommonDecoration::keepBelowChange(bool below) +{ + if (m_button[BelowButton]) + { + m_button[BelowButton]->setOn(below); + m_button[BelowButton]->setTipText( below?i18n("Do not keep below others"):i18n("Keep below others") ); + m_button[BelowButton]->reset(KCommonDecorationButton::StateChange); + } + + if (m_button[AboveButton] && m_button[AboveButton]->isOn()) + { + m_button[AboveButton]->setOn(false); + m_button[AboveButton]->setTipText( i18n("Keep above others") ); + m_button[AboveButton]->reset(KCommonDecorationButton::StateChange); + } +} + +void KCommonDecoration::slotMaximize() +{ + if (m_button[MaxButton]) + { + maximize(m_button[MaxButton]->lastMousePress() ); + } +} + +void KCommonDecoration::slotShade() +{ + setShade( !isSetShade() ); +} + +void KCommonDecoration::slotKeepAbove() +{ + setKeepAbove(!keepAbove() ); +} + +void KCommonDecoration::slotKeepBelow() +{ + setKeepBelow(!keepBelow() ); +} + +void KCommonDecoration::menuButtonPressed() +{ + static QTime* t = NULL; + static KCommonDecoration* lastClient = NULL; + if (t == NULL) + t = new QTime; + bool dbl = (lastClient==this && t->elapsed() <= QApplication::doubleClickInterval()); + lastClient = this; + t->start(); + if (!dbl || !decorationBehaviour(DB_MenuClose) ) { + QRect menuRect = m_button[MenuButton]->rect(); + QPoint menutop = m_button[MenuButton]->mapToGlobal(menuRect.topLeft()); + QPoint menubottom = m_button[MenuButton]->mapToGlobal(menuRect.bottomRight())+QPoint(0,2); + KDecorationFactory* f = factory(); + showWindowMenu(QRect(menutop, menubottom)); + if( !f->exists( this )) // 'this' was deleted + return; + m_button[MenuButton]->setDown(false); + } + else + closing = true; +} + +void KCommonDecoration::menuButtonReleased() +{ + if(closing) + closeWindow(); +} + +void KCommonDecoration::resizeEvent(QResizeEvent */*e*/) +{ + if (decorationBehaviour(DB_ButtonHide) ) + calcHiddenButtons(); + + updateLayout(); + + updateWindowShape(); + // FIXME: don't update() here! this would result in two paintEvent()s + // because there is already "something" else triggering the repaint... +// widget()->update(); +} + +void KCommonDecoration::moveWidget(int x, int y, QWidget *widget) const +{ + QPoint p = widget->pos(); + int oldX = p.y(); + int oldY = p.x(); + + if (x!=oldX || y!=oldY) + widget->move(x,y); +} + +void KCommonDecoration::resizeWidget(int w, int h, QWidget *widget) const +{ + QSize s = widget->size(); + int oldW = s.width(); + int oldH = s.height(); + + if (w!=oldW || h!=oldH) + widget->resize(w,h); +} + +void KCommonDecoration::mouseDoubleClickEvent(QMouseEvent *e) +{ + if( e->button() != LeftButton ) + return; + + int tb = layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeBottom); + // when shaded, react on double clicks everywhere to make it easier to unshade. otherwise + // react only on double clicks in the title bar region... + if (isSetShade() || e->pos().y() <= tb ) + titlebarDblClickOperation(); +} + +void KCommonDecoration::wheelEvent(QWheelEvent *e) +{ + int tb = layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeBottom); + if (isSetShade() || e->pos().y() <= tb ) + titlebarMouseWheelOperation( e->delta()); +} + +KCommonDecoration::Position KCommonDecoration::mousePosition(const QPoint &point) const +{ + const int corner = 18+3*layoutMetric(LM_BorderBottom, false)/2; + Position pos = PositionCenter; + + QRect r = widget()->rect(); + int r_x, r_y, r_x2, r_y2; + r.coords(&r_x, &r_y, &r_x2, &r_y2); + int p_x = point.x(); + int p_y = point.y(); + const int borderLeft = layoutMetric(LM_BorderLeft); +// const int borderRight = layoutMetric(LM_BorderRight); + const int borderBottom = layoutMetric(LM_BorderBottom); + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + + const int borderBottomTop = r_y2-borderBottom+1; + const int borderLeftRight = r_x+borderLeft-1; +// const int borderRightLeft = r_x2-borderRight+1; + const int titleEdgeLeftRight = r_x+titleEdgeLeft-1; + const int titleEdgeRightLeft = r_x2-titleEdgeRight+1; + const int titleEdgeBottomBottom = r_y+titleEdgeTop+titleHeight+titleEdgeBottom-1; + const int titleEdgeTopBottom = r_y+titleEdgeTop-1; + + if (p_y <= titleEdgeTopBottom) { + if (p_x <= r_x+corner) + pos = PositionTopLeft; + else if (p_x >= r_x2-corner) + pos = PositionTopRight; + else + pos = PositionTop; + } else if (p_y <= titleEdgeBottomBottom) { + if (p_x <= titleEdgeLeftRight) + pos = PositionTopLeft; + else if (p_x >= titleEdgeRightLeft) + pos = PositionTopRight; + else + pos = PositionCenter; // title bar + } else if (p_y < borderBottomTop) { + if (p_y < r_y2-corner) { + if (p_x <= borderLeftRight) + pos = PositionLeft; + else + pos = PositionRight; + } else { + if (p_x <= borderLeftRight) + pos = PositionBottomLeft; + else + pos = PositionBottomRight; + } + } else if(p_y >= borderBottomTop) { + if (p_x <= r_x+corner) + pos = PositionBottomLeft; + else if (p_x >= r_x2-corner) + pos = PositionBottomRight; + else + pos = PositionBottom; + } + + return pos; +} + +void KCommonDecoration::updateWindowShape() +{ + // don't mask the widget... + if (!decorationBehaviour(DB_WindowMask) ) + return; + + int w = widget()->width(); + int h = widget()->height(); + + bool tl=true,tr=true,bl=true,br=true; // is there a transparent rounded corner in top-left? etc + + QDesktopWidget *desktop=KApplication::desktop(); + // no transparent rounded corners if this window corner lines up with a screen corner + for(int screen=0; screen < desktop->numScreens(); ++screen) + { + QRect fullscreen(desktop->screenGeometry(screen)); + QRect window = geometry(); + + if(window.topLeft() == fullscreen.topLeft() ) tl = false; + if(window.topRight() == fullscreen.topRight() ) tr = false; + if(window.bottomLeft() == fullscreen.bottomLeft() ) bl = false; + if(window.bottomRight()== fullscreen.bottomRight() ) br = false; + } + + QRegion mask(0, 0, w, h); + + // Remove top-left corner. + if(tl) + { + mask -= cornerShape(WC_TopLeft); + } + // Remove top-right corner. + if(tr) + { + mask -= cornerShape(WC_TopRight); + } + // Remove top-left corner. + if(bl) + { + mask -= cornerShape(WC_BottomLeft); + } + // Remove top-right corner. + if(br) + { + mask -= cornerShape(WC_BottomRight); + } + + setMask( mask ); +} + +bool KCommonDecoration::eventFilter( QObject* o, QEvent* e ) +{ + if( o != widget()) + return false; + switch( e->type()) + { + case QEvent::Resize: + resizeEvent(static_cast<QResizeEvent*>(e) ); + return true; + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e )); + return true; + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e )); + return true; + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e )); + return true; + case QEvent::Wheel: + wheelEvent( static_cast< QWheelEvent* >( e )); + return true; + default: + return false; + } +} + +const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask; + +bool KCommonDecoration::isToolWindow() const +{ + NET::WindowType type = windowType( SUPPORTED_WINDOW_TYPES_MASK ); + return ((type==NET::Toolbar)||(type==NET::Utility)||(type==NET::Menu)); +} + +QRect KCommonDecoration::titleRect() const +{ + int r_x, r_y, r_x2, r_y2; + widget()->rect().coords(&r_x, &r_y, &r_x2, &r_y2); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleBorderLeft = layoutMetric(LM_TitleBorderLeft); + const int titleBorderRight = layoutMetric(LM_TitleBorderRight); + const int ttlHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottomBottom = r_y+titleEdgeTop+ttlHeight+titleEdgeBottom-1; + return QRect(r_x+titleEdgeLeft+buttonsLeftWidth()+titleBorderLeft, r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-titleBorderRight-(r_x+titleEdgeLeft+buttonsLeftWidth()+titleBorderLeft), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); +} + + +KCommonDecorationButton::KCommonDecorationButton(ButtonType type, KCommonDecoration *parent, const char *name) + : QButton(parent->widget(), name), + m_decoration(parent), + m_type(type), + m_realizeButtons(LeftButton), + m_lastMouse(NoButton), + m_isLeft(true) +{ + setCursor(ArrowCursor); +} + +KCommonDecorationButton::~KCommonDecorationButton() +{ +} + +KCommonDecoration *KCommonDecorationButton::decoration() const +{ + return m_decoration; +} + +ButtonType KCommonDecorationButton::type() const +{ + return m_type; +} + +bool KCommonDecorationButton::isLeft() const +{ + return m_isLeft; +} + +void KCommonDecorationButton::setLeft(bool left) +{ + m_isLeft = left; +} + +void KCommonDecorationButton::setRealizeButtons(int btns) +{ + m_realizeButtons = btns; +} + +void KCommonDecorationButton::setSize(const QSize &s) +{ + if (!m_size.isValid() || s != size() ) { + m_size = s; + + setFixedSize(m_size); + reset(SizeChange); + } +} + +QSize KCommonDecorationButton::sizeHint() const +{ + return m_size; +} + +void KCommonDecorationButton::setTipText(const QString &tip) { + QToolTip::remove(this ); + QToolTip::add(this, tip ); +} + +void KCommonDecorationButton::setToggleButton(bool toggle) +{ + QButton::setToggleButton(toggle); + reset(ToggleChange); +} + +void KCommonDecorationButton::setOn(bool on) +{ + if (on != isOn() ) { + QButton::setOn(on); + reset(StateChange); + } +} + +void KCommonDecorationButton::mousePressEvent(QMouseEvent* e) +{ + m_lastMouse = e->button(); + // pass on event after changing button to LeftButton + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button()&m_realizeButtons)?LeftButton:NoButton, e->state()); + + QButton::mousePressEvent(&me); +} + +void KCommonDecorationButton::mouseReleaseEvent(QMouseEvent* e) +{ + m_lastMouse = e->button(); + // pass on event after changing button to LeftButton + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button()&m_realizeButtons)?LeftButton:NoButton, e->state()); + + QButton::mouseReleaseEvent(&me); +} diff --git a/kwin/lib/kcommondecoration.h b/kwin/lib/kcommondecoration.h new file mode 100644 index 000000000..9495cf148 --- /dev/null +++ b/kwin/lib/kcommondecoration.h @@ -0,0 +1,367 @@ +/* + This file is part of the KDE project. + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + */ + +#ifndef KCOMMONDECORATION_H +#define KCOMMONDECORATION_H + +#include <qbutton.h> +#include <qvaluevector.h> + +#include "kdecoration.h" + +class KDecorationBridge; +class KDecorationFactory; + +enum ButtonType { + HelpButton=0, + MaxButton, + MinButton, + CloseButton, + MenuButton, + OnAllDesktopsButton, + AboveButton, + BelowButton, + ShadeButton, + NumButtons +}; + +class KCommonDecorationButton; + +class KCommonDecorationButtonPrivate; +class KCommonDecorationPrivate; + +/** + * This class eases development of decorations by implementing parts of KDecoration + * which are error prone and common for most decorations. + * It takes care of the window layout, button/action handling, and window mask creation. + */ +class KWIN_EXPORT KCommonDecoration : public KDecoration +{ + Q_OBJECT + + public: + KCommonDecoration(KDecorationBridge* bridge, KDecorationFactory* factory); + virtual ~KCommonDecoration(); + + /** + * Used to calculate the decoration layout. The basic layout looks like this: + * + * Window: + * _______________________________________________________________ + * | LM_TitleEdgeTop | + * |_______________________________________________________________| + * | LM_TitleEdgeLeft | [title] | LM_TitleEdgeRight | + * |__________________|________________________|___________________| + * | LM_TitleEdgeBottom | + * |_______________________________________________________________| + * | | | | + * | | | | + * | | | | + * |LM_BorderLeft LM_BorderRight| + * |_|___________________________________________________________|_| + * | LM_BorderBottom | + * |_______________________________________________________________| + * + * Title: + * ___________________________________________________________________________________ + * | LM_ButtonMarginTop | | LM_ButtonMarginTop | + * |________________________________| |_________________________________| + * | [Buttons] | LM_TitleBorderLeft | LM_TitleHeight | LM_TitleBorderRight | [Buttons] | + * |___________|____________________|________________|_____________________|___________| + * + * Buttons: + * _____________________________________________________________________________________________ + * | button | spacing | button | spacing | explicit spacer | spacing | ... | spacing | button | + * |________|_________|________|_________|_________________|_________|________|_________|________| + * + * @see layoutMetric() + */ + enum LayoutMetric + { + LM_BorderLeft, + LM_BorderRight, + LM_BorderBottom, + LM_TitleHeight, + LM_TitleBorderLeft, + LM_TitleBorderRight, + LM_TitleEdgeLeft, + LM_TitleEdgeRight, + LM_TitleEdgeTop, + LM_TitleEdgeBottom, + LM_ButtonWidth, + LM_ButtonHeight, + LM_ButtonSpacing, + LM_ExplicitButtonSpacer, + LM_ButtonMarginTop + }; + + enum DecorationBehaviour + { + DB_MenuClose, ///< Close window on double clicking the menu + DB_WindowMask, ///< Set a mask on the window + DB_ButtonHide ///< Hide buttons when there is not enough space in the titlebar + }; + + enum WindowCorner + { + WC_TopLeft, + WC_TopRight, + WC_BottomLeft, + WC_BottomRight + }; + + /** + * The name of the decoration used in the decoration preview. + */ + virtual QString visibleName() const = 0; + /** + * The default title button order on the left. + * @see KDecoration::titleButtonsLeft() + * @see KDecoration::titleButtonsRight() + */ + virtual QString defaultButtonsLeft() const = 0; + /** + * The default title button order on the left. + * @see KDecoration::titleButtonsLeft() + * @see KDecoration::titleButtonsRight() + */ + virtual QString defaultButtonsRight() const = 0; + + /** + * This controls whether some specific behaviour should be enabled or not. + * @see DecorationBehaviour + */ + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + + /** + * This controls the layout of the decoration in various ways. It is + * possible to have a different layout for different window states. + * @param lm The layout element. + * @param respectWindowState Whether window states should be taken into account or a "default" state should be assumed. + * @param button For LM_ButtonWidth and LM_ButtonHeight, the button. + */ + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton *button = 0) const; + + /** + * Create a new title bar button. KCommonDecoration takes care of memory management. + * @return a pointer to the button, or 0 if the button should not be created. + */ + virtual KCommonDecorationButton *createButton(ButtonType type) = 0; + + /** + * @return the mask for the specific window corner. + */ + virtual QRegion cornerShape(WindowCorner corner); + + /** + * This updates the window mask using the information provided by + * cornerShape(). Edges which are aligned to screen corners are not + * shaped for better usability (remember to paint these areas in paintEvent(), too). + * You normally don't want/need to reimplement updateWindowShape(). + * @see cornerShape() + */ + virtual void updateWindowShape(); + + /** + * Draw the window decoration. + */ + virtual void paintEvent(QPaintEvent *e) = 0; + + /** + * This is used to update the painting of the title bar after the caption has been changed. + * Reimplement for a more efficient implementation (default calls update() on the whole decoration). + */ + virtual void updateCaption(); + + int buttonsLeftWidth() const; + int buttonsRightWidth() const; + + /** + * TODO: remove? + */ + void updateLayout() const; + /** + * Makes sure all buttons are repainted. + */ + void updateButtons() const; + /** + * Manually call reset() on each button. + */ + void resetButtons() const; + + /** + * Convenience method. + * @returns true if the window type is NET::Toolbar, NET::Utility, or NET::Menu + */ + bool isToolWindow() const; + /** + * Convenience method. + * @returns the title rect. + */ + QRect titleRect() const; + + public: + /** + * Handles widget and layout creation, call the base implementation when subclassing this member. + */ + virtual void init(); + /** + * Handles SettingButtons, call the base implementation when subclassing this member. + */ + virtual void reset( unsigned long changed ); + virtual void borders( int& left, int& right, int& top, int& bottom ) const; + virtual void show(); + virtual void resize(const QSize& s); + virtual QSize minimumSize() const; + virtual void maximizeChange(); + virtual void desktopChange(); + virtual void shadeChange(); + virtual void iconChange(); + virtual void activeChange(); + virtual void captionChange(); + public slots: + void keepAboveChange(bool above); + void keepBelowChange(bool below); + void slotMaximize(); + void slotShade(); + void slotKeepAbove(); + void slotKeepBelow(); + void menuButtonPressed(); + void menuButtonReleased(); + public: + virtual Position mousePosition(const QPoint &point) const; + + virtual bool eventFilter( QObject* o, QEvent* e ); + virtual void resizeEvent(QResizeEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void wheelEvent(QWheelEvent *e); + + private: + void resetLayout(); + + void moveWidget(int x, int y, QWidget *widget) const; + void resizeWidget(int w, int h, QWidget *widget) const; + + typedef QValueVector <KCommonDecorationButton*> ButtonContainer; ///< If the entry is 0, it's a spacer. + int buttonContainerWidth(const ButtonContainer &btnContainer, bool countHidden = false) const; + void addButtons(ButtonContainer &btnContainer, const QString& buttons, bool isLeft); + + KCommonDecorationButton *m_button[NumButtons]; + + ButtonContainer m_buttonsLeft; + ButtonContainer m_buttonsRight; + + QWidget *m_previewWidget; + + // button hiding for small windows + void calcHiddenButtons(); + int btnHideMinWidth; + int btnHideLastWidth; + + bool closing; // for menu doubleclick closing... + + KCommonDecorationPrivate *d; +}; + +/** + * Title bar buttons of KCommonDecoration need to inherit this class. + */ +class KWIN_EXPORT KCommonDecorationButton : public QButton +{ + friend class KCommonDecoration; + + public: + KCommonDecorationButton(ButtonType type, KCommonDecoration *parent, const char *name); + virtual ~KCommonDecorationButton(); + + /** + * These flags specify what has changed, e.g. the reason for a reset(). + */ + enum + { + ManualReset = 1 << 0, ///< The button might want to do a full reset for some reason... + SizeChange = 1 << 1, ///< The button size changed @see setSize() + ToggleChange = 1 << 2, ///< The button toggle state has changed @see setToggleButton() + StateChange = 1 << 3, ///< The button has been set pressed or not... @see setOn() + IconChange = 1 << 4, ///< The window icon has been changed + DecorationReset = 1 << 5 ///< E.g. when decoration colors have changed + }; + /** + * Initialize the button after size change etc. + */ + virtual void reset(unsigned long changed) = 0; + /** + * @returns the KCommonDecoration the button belongs to. + */ + KCommonDecoration *decoration() const; + /** + * @returns the button type. + * @see ButtonType + */ + ButtonType type() const; + + /** + * Whether the button is left of the titlebar or not. + */ + bool isLeft() const; + + /** + * Set which mouse buttons the button should honor. Used e.g. to prevent accidental right mouse clicks. + */ + void setRealizeButtons(int btns); + /** + * Set the button size. + */ + void setSize(const QSize &s); + /** + * Set/update the button's tool tip + */ + void setTipText(const QString &tip); + /** + * The mouse button that has been clicked last time. + */ + ButtonState lastMousePress() const { return m_lastMouse; } + + QSize sizeHint() const; + + protected: + void setToggleButton(bool toggle); + void setOn(bool on); + void setLeft(bool left); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + private: + KCommonDecoration *m_decoration; + ButtonType m_type; + int m_realizeButtons; + QSize m_size; + ButtonState m_lastMouse; + + bool m_isLeft; + + KCommonDecorationButtonPrivate *d; +}; + +#endif // KCOMMONDECORATION_H diff --git a/kwin/lib/kdecoration.cpp b/kwin/lib/kdecoration.cpp new file mode 100644 index 000000000..c7b3d6929 --- /dev/null +++ b/kwin/lib/kdecoration.cpp @@ -0,0 +1,444 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#include "kdecoration.h" + +#include <kdebug.h> +#include <qapplication.h> +#include <kglobal.h> +#include <assert.h> +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <X11/Xlib.h> +#include <fixx11h.h> +#endif + +#include "kdecoration_p.h" +#include "kdecorationfactory.h" + +KDecorationOptions* KDecoration::options_; + +KDecoration::KDecoration( KDecorationBridge* bridge, KDecorationFactory* factory ) + : bridge_( bridge ), + w_( NULL ), + factory_( factory ) + { + factory->addDecoration( this ); + } + +KDecoration::~KDecoration() + { + factory()->removeDecoration( this ); + delete w_; + } + +const KDecorationOptions* KDecoration::options() + { + return options_; + } + +void KDecoration::createMainWidget( WFlags flags ) + { + // FRAME check flags? + setMainWidget( new QWidget( initialParentWidget(), "decoration widget", initialWFlags() | flags )); + } + +void KDecoration::setMainWidget( QWidget* w ) + { + assert( w_ == NULL ); + w_ = w; + w->setMouseTracking( true ); + widget()->resize( geometry().size()); + } + +QWidget* KDecoration::initialParentWidget() const + { + return bridge_->initialParentWidget(); + } + +Qt::WFlags KDecoration::initialWFlags() const + { + return bridge_->initialWFlags(); + } + +bool KDecoration::isActive() const + { + return bridge_->isActive(); + } + +bool KDecoration::isCloseable() const + { + return bridge_->isCloseable(); + } + +bool KDecoration::isMaximizable() const + { + return bridge_->isMaximizable(); + } + +KDecoration::MaximizeMode KDecoration::maximizeMode() const + { + return bridge_->maximizeMode(); + } + +bool KDecoration::isMinimizable() const + { + return bridge_->isMinimizable(); + } + +bool KDecoration::providesContextHelp() const + { + return bridge_->providesContextHelp(); + } + +int KDecoration::desktop() const + { + return bridge_->desktop(); + } + +bool KDecoration::isModal() const + { + return bridge_->isModal(); + } + +bool KDecoration::isShadeable() const + { + return bridge_->isShadeable(); + } + +bool KDecoration::isShade() const + { + return bridge_->isShade(); + } + +bool KDecoration::isSetShade() const + { + return bridge_->isSetShade(); + } + +bool KDecoration::keepAbove() const + { + return bridge_->keepAbove(); + } + +bool KDecoration::keepBelow() const + { + return bridge_->keepBelow(); + } + +bool KDecoration::isMovable() const + { + return bridge_->isMovable(); + } + +bool KDecoration::isResizable() const + { + return bridge_->isResizable(); + } + +NET::WindowType KDecoration::windowType( unsigned long supported_types ) const + { // this one is also duplicated in KDecorationFactory + return bridge_->windowType( supported_types ); + } + +QIconSet KDecoration::icon() const + { + return bridge_->icon(); + } + +QString KDecoration::caption() const + { + return bridge_->caption(); + } + +void KDecoration::processMousePressEvent( QMouseEvent* e ) + { + return bridge_->processMousePressEvent( e ); + } + +void KDecoration::showWindowMenu( const QRect &pos ) + { + bridge_->showWindowMenu( pos ); + } + +void KDecoration::showWindowMenu( QPoint pos ) + { + bridge_->showWindowMenu( pos ); + } + +void KDecoration::performWindowOperation( WindowOperation op ) + { + bridge_->performWindowOperation( op ); + } + +void KDecoration::setMask( const QRegion& reg, int mode ) + { + bridge_->setMask( reg, mode ); + } + +void KDecoration::clearMask() + { + bridge_->setMask( QRegion(), 0 ); + } + +bool KDecoration::isPreview() const + { + return bridge_->isPreview(); + } + +QRect KDecoration::geometry() const + { + return bridge_->geometry(); + } + +QRect KDecoration::iconGeometry() const + { + return bridge_->iconGeometry(); + } + +QRegion KDecoration::unobscuredRegion( const QRegion& r ) const + { + return bridge_->unobscuredRegion( r ); + } + +QWidget* KDecoration::workspaceWidget() const + { + return bridge_->workspaceWidget(); + } + +WId KDecoration::windowId() const + { + return bridge_->windowId(); + } + +void KDecoration::closeWindow() + { + bridge_->closeWindow(); + } + +void KDecoration::maximize( ButtonState button ) + { + performWindowOperation( options()->operationMaxButtonClick( button )); + } + +void KDecoration::maximize( MaximizeMode mode ) + { + bridge_->maximize( mode ); + } + +void KDecoration::minimize() + { + bridge_->minimize(); + } + +void KDecoration::showContextHelp() + { + bridge_->showContextHelp(); + } + +void KDecoration::setDesktop( int desktop ) + { + bridge_->setDesktop( desktop ); + } + +void KDecoration::toggleOnAllDesktops() + { + if( isOnAllDesktops()) + setDesktop( bridge_->currentDesktop()); + else + setDesktop( NET::OnAllDesktops ); + } + +void KDecoration::titlebarDblClickOperation() + { + bridge_->titlebarDblClickOperation(); + } + +void KDecoration::titlebarMouseWheelOperation( int delta ) + { + bridge_->titlebarMouseWheelOperation( delta ); + } + +void KDecoration::setShade( bool set ) + { + bridge_->setShade( set ); + } + +void KDecoration::setKeepAbove( bool set ) + { + bridge_->setKeepAbove( set ); + } + +void KDecoration::setKeepBelow( bool set ) + { + bridge_->setKeepBelow( set ); + } + +bool KDecoration::drawbound( const QRect&, bool ) + { + return false; + } + +bool KDecoration::animateMinimize( bool ) + { + return false; + } + +bool KDecoration::windowDocked( Position ) + { + return false; + } + +void KDecoration::helperShowHide( bool show ) + { + bridge_->helperShowHide( show ); + } + +void KDecoration::reset( unsigned long ) + { + } + +void KDecoration::grabXServer() + { + bridge_->grabXServer( true ); + } + +void KDecoration::ungrabXServer() + { + bridge_->grabXServer( false ); + } + +KDecoration::Position KDecoration::mousePosition( const QPoint& p ) const +{ + const int range = 16; + int bleft, bright, btop, bbottom; + borders( bleft, bright, btop, bbottom ); + btop = KMIN( btop, 4 ); // otherwise whole titlebar would have resize cursor + + Position m = PositionCenter; + + if ( ( p.x() > bleft && p.x() < widget()->width() - bright ) + && ( p.y() > btop && p.y() < widget()->height() - bbottom ) ) + return PositionCenter; + + if ( p.y() <= KMAX( range, btop ) && p.x() <= KMAX( range, bleft )) + m = PositionTopLeft; + else if ( p.y() >= widget()->height()- KMAX( range, bbottom ) + && p.x() >= widget()->width()- KMAX( range, bright )) + m = PositionBottomRight; + else if ( p.y() >= widget()->height()- KMAX( range, bbottom ) && p.x() <= KMAX( range, bleft )) + m = PositionBottomLeft; + else if ( p.y() <= KMAX( range, btop ) && p.x() >= widget()->width()- KMAX( range, bright )) + m = PositionTopRight; + else if ( p.y() <= btop ) + m = PositionTop; + else if ( p.y() >= widget()->height()-bbottom ) + m = PositionBottom; + else if ( p.x() <= bleft ) + m = PositionLeft; + else if ( p.x() >= widget()->width()-bright ) + m = PositionRight; + else + m = PositionCenter; + return m; +} + +KDecorationOptions::KDecorationOptions() + { + assert( KDecoration::options_ == NULL ); + KDecoration::options_ = this; + } + +KDecorationOptions::~KDecorationOptions() + { + assert( KDecoration::options_ == this ); + KDecoration::options_ = NULL; + } + +const QColor& KDecorationOptions::color(ColorType type, bool active) const +{ + return(d->colors[type + (active ? 0 : NUM_COLORS)]); +} + +const QFont& KDecorationOptions::font(bool active, bool small) const +{ + if ( small ) + return(active ? d->activeFontSmall : d->inactiveFontSmall); + else + return(active ? d->activeFont : d->inactiveFont); +} + +const QColorGroup& KDecorationOptions::colorGroup(ColorType type, bool active) const +{ + int idx = type + (active ? 0 : NUM_COLORS); + if(d->cg[idx]) + return(*d->cg[idx]); + d->cg[idx] = new QColorGroup(Qt::black, d->colors[idx], d->colors[idx].light(150), + d->colors[idx].dark(), d->colors[idx].dark(120), + Qt::black, QApplication::palette().active(). + base()); + return(*d->cg[idx]); +} + +bool KDecorationOptions::customButtonPositions() const +{ + return d->custom_button_positions; +} + +QString KDecorationOptions::titleButtonsLeft() const +{ + return d->title_buttons_left; +} + +QString KDecorationOptions::titleButtonsRight() const +{ + return d->title_buttons_right; +} + +bool KDecorationOptions::showTooltips() const +{ + return d->show_tooltips; +} + +KDecorationOptions::BorderSize KDecorationOptions::preferredBorderSize( KDecorationFactory* factory ) const +{ + assert( factory != NULL ); + if( d->cached_border_size == BordersCount ) // invalid + d->cached_border_size = d->findPreferredBorderSize( d->border_size, + factory->borderSizes()); + return d->cached_border_size; +} + +bool KDecorationOptions::moveResizeMaximizedWindows() const +{ + return d->move_resize_maximized_windows; +} + +KDecorationDefines::WindowOperation KDecorationOptions::operationMaxButtonClick( Qt::ButtonState button ) const + { + return button == Qt::RightButton? d->OpMaxButtonRightClick : + button == Qt::MidButton? d->OpMaxButtonMiddleClick : + d->OpMaxButtonLeftClick; + } + +#include "kdecoration.moc" diff --git a/kwin/lib/kdecoration.h b/kwin/lib/kdecoration.h new file mode 100644 index 000000000..2fd1fc451 --- /dev/null +++ b/kwin/lib/kdecoration.h @@ -0,0 +1,887 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#ifndef KDECORATION_H +#define KDECORATION_H + +#include <qcolor.h> +#include <qfont.h> +#include <qobject.h> +#include <qiconset.h> +#include <netwm_def.h> +#include <kdeversion.h> + +class KDecorationOptionsPrivate; +class KDecorationBridge; +class KDecorationPrivate; +class KDecorationFactory; + +#define KWIN_EXPORT KDE_EXPORT + +/** + * This class provides a namespace for all decoration related classes. + * All shared types are defined here. + * @since 3.2 + */ +class KWIN_EXPORT KDecorationDefines +{ +public: + /** + * These values represent positions inside an area + */ + enum Position + { // without prefix, they'd conflict with Qt::TopLeft etc. :( + PositionCenter = 0x00, + PositionLeft = 0x01, + PositionRight = 0x02, + PositionTop = 0x04, + PositionBottom = 0x08, + PositionTopLeft = PositionLeft | PositionTop, + PositionTopRight = PositionRight | PositionTop, + PositionBottomLeft = PositionLeft | PositionBottom, + PositionBottomRight = PositionRight | PositionBottom + }; + /** + * Maximize mode. These values specify how a window is maximized. + */ + // these values are written to session files, don't change the order + enum MaximizeMode + { + MaximizeRestore = 0, ///< The window is not maximized in any direction. + MaximizeVertical = 1, ///< The window is maximized vertically. + MaximizeHorizontal = 2, ///< The window is maximized horizontally. + /// Equal to @p MaximizeVertical | @p MaximizeHorizontal + MaximizeFull = MaximizeVertical | MaximizeHorizontal + }; + + enum WindowOperation + { + MaximizeOp = 5000, + RestoreOp, + MinimizeOp, + MoveOp, + UnrestrictedMoveOp, + ResizeOp, + UnrestrictedResizeOp, + CloseOp, + OnAllDesktopsOp, + ShadeOp, + KeepAboveOp, + KeepBelowOp, + OperationsOp, + WindowRulesOp, + ToggleStoreSettingsOp = WindowRulesOp, ///< @obsolete + HMaximizeOp, + VMaximizeOp, + LowerOp, + FullScreenOp, + NoBorderOp, + NoOp, + SetupWindowShortcutOp, + ApplicationRulesOp ///< @since 3.5 + }; + /** + * Basic color types that should be recognized by all decoration styles. + * Decorations are not required to implement all the colors, but for the ones that + * are implemented the color setting for them should be obeyed. + */ + enum ColorType + { + ColorTitleBar, ///< The color for the titlebar + ColorTitleBlend, ///< The blend color for the titlebar + ColorFont, ///< The titlebar text color + ColorButtonBg, ///< The color to use for the titlebar buttons + ColorFrame, ///< The color for the window frame (border) + ColorHandle, ///< The color for the resize handle + NUM_COLORS + }; + + /** + * These flags specify which settings changed when rereading settings. + * Each setting in class KDecorationOptions specifies its matching flag. + */ + enum + { + SettingDecoration = 1 << 0, ///< The decoration was changed + SettingColors = 1 << 1, ///< The color palette was changed + SettingFont = 1 << 2, ///< The titlebar font was changed + SettingButtons = 1 << 3, ///< The button layout was changed + SettingTooltips = 1 << 4, ///< The tooltip setting was changed + SettingBorder = 1 << 5 ///< The border size setting was changed + }; + + /** + * Border size. KDecorationOptions::preferredBorderSize() returns + * one of these values. + */ + enum BorderSize + { + BorderTiny, ///< Minimal borders + BorderNormal, ///< Standard size borders, the default setting + BorderLarge, ///< Larger borders + BorderVeryLarge, ///< Very large borders + BorderHuge, ///< Huge borders + BorderVeryHuge, ///< Very huge borders + BorderOversized, ///< Oversized borders + BordersCount ///< @internal + }; + + /** + * Used to find out which features the decoration supports. + * @see KDecorationFactory::supports() + */ + enum Ability + { + AbilityAnnounceButtons = 0, ///< decoration supports AbilityButton* values (always use) + AbilityButtonMenu = 1000, ///< decoration supports the menu button + AbilityButtonOnAllDesktops = 1001, ///< decoration supports the on all desktops button + AbilityButtonSpacer = 1002, ///< decoration supports inserting spacers between buttons + AbilityButtonHelp = 1003, ///< decoration supports what's this help button + AbilityButtonMinimize = 1004, ///< decoration supports a minimize button + AbilityButtonMaximize = 1005, ///< decoration supports a maximize button + AbilityButtonClose = 1006, ///< decoration supports a close button + AbilityButtonAboveOthers = 1007, ///< decoration supports an above button + AbilityButtonBelowOthers = 1008, ///< decoration supports a below button + AbilityButtonShade = 1009, ///< decoration supports a shade button + AbilityButtonResize = 1010, ///< decoration supports a resize button + ABILITY_DUMMY = 10000000 + }; + + enum Requirement { REQUIREMENT_DUMMY = 1000000 }; +}; + +class KDecorationProvides + : public KDecorationDefines + { + public: + virtual bool provides( Requirement req ) = 0; + }; + +/** + * This class holds various configuration settings for the decoration. + * It is accessible from the decorations either as KDecoration::options() + * or KDecorationFactory::options(). + * @since 3.2 + */ +class KWIN_EXPORT KDecorationOptions : public KDecorationDefines + { +public: + KDecorationOptions(); + virtual ~KDecorationOptions(); + /** + * Returns the color that should be used for the given part of the decoration. + * The changed flags for this setting is SettingColors. + * + * @param type The requested color type. + * @param active Whether the color should be for active or inactive windows. + */ + const QColor& color(ColorType type, bool active=true) const; + /** + * Returns a colorgroup using the given decoration color as the background. + * The changed flags for this setting is SettingColors. + * + * @param type The requested color type. + * @param active Whether to return the color for active or inactive windows. + */ + const QColorGroup& colorGroup(ColorType type, bool active=true) const; + /** + * Returns the active or inactive decoration font. + * The changed flags for this setting is SettingFont. + * + * @param active Whether to return the color for active or inactive windows. + * @param small If @a true, returns a font that's suitable for tool windows. + */ + const QFont& font(bool active=true, bool small = false) const; + /** + * Returns @a true if the style should use custom button positions + * The changed flags for this setting is SettingButtons. + * + * @see titleButtonsLeft + * @see titleButtonsRight + */ + bool customButtonPositions() const; + /** + * If customButtonPositions() returns true, titleButtonsLeft + * returns which buttons should be on the left side of the titlebar from left + * to right. Characters in the returned string have this meaning : + * @li 'M' menu button + * @li 'S' on_all_desktops button + * @li 'H' quickhelp button + * @li 'I' minimize ( iconify ) button + * @li 'A' maximize button + * @li 'X' close button + * @li 'F' keep_above_others button + * @li 'B' keep_below_others button + * @li 'L' shade button + * @li 'R' resize button + * @li '_' spacer + * + * The default ( which is also returned if customButtonPositions returns false ) + * is "MS". + * Unknown buttons in the returned string must be ignored. + * The changed flags for this setting is SettingButtons. + */ + QString titleButtonsLeft() const; + /** + * If customButtonPositions() returns true, titleButtonsRight + * returns which buttons should be on the right side of the titlebar from left + * to right. Characters in the return string have the same meaning like + * in titleButtonsLeft(). + * + * The default ( which is also returned if customButtonPositions returns false ) + * is "HIAX". + * Unknown buttons in the returned string must be ignored. + * The changed flags for this setting is SettingButtons. + */ + QString titleButtonsRight() const; + + /** + * @returns true if the style should use tooltips for window buttons + * The changed flags for this setting is SettingTooltips. + */ + bool showTooltips() const; + + /** + * The preferred border size selected by the user, e.g. for accessibility + * reasons, or when using high resolution displays. It's up to the decoration + * to decide which borders or if any borders at all will obey this setting. + * It is guaranteed that the returned value will be one of those + * returned by KDecorationFactory::borderSizes(), so if that one hasn't been + * reimplemented, BorderNormal is always returned. + * The changed flags for this setting is SettingBorder. + * @param factory the decoration factory used + */ + BorderSize preferredBorderSize( KDecorationFactory* factory ) const; + + /* + * When this functions returns false, moving and resizing of maximized windows + * is not allowed, and therefore the decoration is allowed to turn off (some of) + * its borders. + * The changed flags for this setting is SettingButtons. + */ + bool moveResizeMaximizedWindows() const; + + /** + * @internal + */ + WindowOperation operationMaxButtonClick( Qt::ButtonState button ) const; + + /** + * @internal + */ + virtual unsigned long updateSettings() = 0; // returns SettingXYZ mask + +protected: + /** + * @internal + */ + KDecorationOptionsPrivate* d; + }; + + +/** + * This is the base class for a decoration object. It provides functions + * that give various information about the decorated window, and also + * provides pure virtual functions for controlling the decoration that + * every decoration should implement. + * @since 3.2 + */ +class KWIN_EXPORT KDecoration + : public QObject, public KDecorationDefines + { + Q_OBJECT + public: + /** + * Constructs a KDecoration object. Both the arguments are passed from + * KDecorationFactory. Note that the initialization code of the decoration + * should be done in the init() method. + */ + KDecoration( KDecorationBridge* bridge, KDecorationFactory* factory ); + /** + * Destroys the KDecoration. + */ + virtual ~KDecoration(); + + // requests from decoration + + /** + * Returns the KDecorationOptions object, which is used to access + * configuration settings for the decoration. + */ + static const KDecorationOptions* options(); + /** + * Returns @a true if the decorated window is currently active. + */ + bool isActive() const; + /** + * Returns @a true if the decoration window can be closed by the user. + */ + bool isCloseable() const; + /** + * Returns @a true if the decorated window can be maximized. + */ + bool isMaximizable() const; + /** + * Returns the current maximization mode of the decorated window. + * Note that only fully maximized windows should be treated + * as "maximized" (e.g. if the maximize button has only two states). + */ + MaximizeMode maximizeMode() const; + /** + * Returns @a true if the decorated window can be minimized by the user. + */ + bool isMinimizable() const; + /** + * Return @a true if the decorated window can show context help + * (i.e. the decoration should provide the context help button). + */ + bool providesContextHelp() const; + /** + * Returns the number of the virtual desktop the decorated window + * is currently on (including NET::OnAllDesktops for being on all + * desktops). + */ + int desktop() const; + /** + * Convenience function that returns @a true if the window is on all + * virtual desktops. + */ + bool isOnAllDesktops() const; // convenience + /** + * Returns @a true if the decoration window is modal (usually a modal dialog). + */ + bool isModal() const; + /** + * Returns @a true if the decorated window can be shaded. + */ + bool isShadeable() const; + /** + * Returns @a true if the decorated window is currently shaded. + * If the window is e.g. hover unshaded, it's not considered to be shaded. + * This function should not be used for the shade titlebar button, use + * @ref isSetShade() instead. + * + * @see isSetShade + */ + bool isShade() const; + /** + * Returns @a true if the decorated window was set to be shaded. This function + * returns also true if the window is e.g. hover unshaded, so it doesn't + * always correspond to the actual window state. + * + * @see isShade + */ + bool isSetShade() const; + /** + * Returns @a true if the decorated window should be kept above other windows. + */ + bool keepAbove() const; + /** + * Returns @a true if the decorated window should be kept below other windows. + */ + bool keepBelow() const; + /** + * Returns @a true if the decorated window can be moved by the user. + */ + bool isMovable() const; + /** + * Returns @a true if the decorated window can be resized by the user. + */ + bool isResizable() const; + /** + * This function returns the window type of the decorated window. + * The argument to this function is a mask of all window types + * the decoration knows about (as the list of valid window types + * is extended over time, and fallback types are specified in order + * to support older code). For a description of all window types, + * see the definition of the NET::WindowType type. Note that + * some window types never have decorated windows. + * + * An example of usage: + * @code + * const unsigned long supported_types = NET::NormalMask | NET::DesktopMask + * | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask + * | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; + * + * NET::WindowType type = windowType( supported_types ); + * + * if( type == NET::Utility || type == NET::Menu || type == NET::Toolbar ) + * // ... use smaller decorations for tool window types + * else + * // ... use normal decorations + * @endcode + */ + NET::WindowType windowType( unsigned long supported_types ) const; + /** + * Returns an icon set with the decorated window's icon. + */ + QIconSet icon() const; + /** + * Returns the decorated window's caption that should be shown in the titlebar. + */ + QString caption() const; + /** + * This function invokes the window operations menu. + * \param pos specifies the place on the screen where the menu should + * show up. The menu pops up at the bottom-left corner of the specified + * rectangle, unless there is no space, in which case the menu is + * displayed above the rectangle. + * + * \note Decorations that enable a double-click operation for the menu + * button must ensure to call \a showWindowMenu() with the \a pos + * rectangle set to the menu button geometry. + * IMPORTANT: As a result of this function, the decoration object that + * called it may be destroyed after the function returns. This means + * that the decoration object must either return immediately after + * calling showWindowMenu(), or it must use + * KDecorationFactory::exists() to check it's still valid. For example, + * the code handling clicks on the menu button should look similarly + * like this: + * + * \code + * KDecorationFactory* f = factory(); // needs to be saved before + * showWindowMenu( button[MenuButton]->mapToGlobal( menuPoint )); + * if( !f->exists( this )) // destroyed, return immediately + * return; + * button[MenuButton]->setDown(false); + * \endcode + */ + void showWindowMenu( const QRect &pos ); + + /** + * Overloaded version of the above. + */ + void showWindowMenu( QPoint pos ); + /** + * This function performs the given window operation. This function may destroy + * the current decoration object, just like showWindowMenu(). + */ + void performWindowOperation( WindowOperation op ); + /** + * If the decoration is non-rectangular, this function needs to be called + * to set the shape of the decoration. + * + * @param reg The shape of the decoration. + * @param mode The X11 values Unsorted, YSorted, YXSorted and YXBanded that specify + * the sorting of the rectangles, default value is Unsorted. + */ + void setMask( const QRegion& reg, int mode = 0 ); + /** + * This convenience function resets the shape mask. + */ + void clearMask(); // convenience + /** + * If this function returns @a true, the decorated window is used as a preview + * e.g. in the configuration module. In such case, the decoration can e.g. + * show some information in the window area. + */ + bool isPreview() const; + /** + * Returns the geometry of the decoration. + */ + QRect geometry() const; + /** + * Returns the icon geometry for the window, i.e. the geometry of the taskbar + * entry. This is used mainly for window minimize animations. Note that + * the geometry may be null. + */ + QRect iconGeometry() const; + /** + * Returns the intersection of the given region with the region left + * unobscured by the windows stacked above the current one. You can use + * this function to, for example, try to keep the titlebar visible if + * there is a hole available. The region returned is in the coordinate + * space of the decoration. + * @param r The region you want to check for holes + */ + QRegion unobscuredRegion( const QRegion& r ) const; + /** + * Returns the main workspace widget. The main purpose of this function is to + * allow painting the minimize animation or the transparent move bound on it. + */ + QWidget* workspaceWidget() const; + /** + * Returns the handle of the window that is being decorated. It is possible + * the returned value will be 0. + * IMPORTANT: This function is meant for special purposes, and it + * usually should not be used. The main purpose is finding out additional + * information about the window's state. Also note that different kinds + * of windows are decorated: Toplevel windows managed by the window manager, + * test window in the window manager decoration module, and possibly also + * other cases. + * Careless abuse of this function will usually sooner or later lead + * to problems. + * @since 3.4 + */ + WId windowId() const; + /** + * Convenience function that returns the width of the decoration. + */ + int width() const; // convenience + /** + * Convenience function that returns the height of the decoration. + */ + int height() const; // convenience + /** + * This function is the default handler for mouse events. All mouse events + * that are not handled by the decoration itself should be passed to it + * in order to make work operations like window resizing by dragging borders etc. + */ + void processMousePressEvent( QMouseEvent* e ); + + // requests to decoration + + /** + * This function is called immediately after the decoration object is created. + * Due to some technical reasons, initialization should be done here + * instead of in the constructor. + */ + virtual void init() = 0; // called once right after created + + /** + * This function should return mouse cursor position in the decoration. + * Positions at the edge will result in window resizing with mouse button + * pressed, center position will result in moving. + */ + virtual Position mousePosition( const QPoint& p ) const = 0; + + /** + * This function should return the distance from each window side to the inner + * window. The sizes may depend on the state of the decorated window, such as + * whether it's shaded. Decorations often turn off their bottom border when the + * window is shaded, and turn off their left/right/bottom borders when + * the window is maximized and moving and resizing of maximized windows is disabled. + * This function mustn't do any repaints or resizes. Also, if the sizes returned + * by this function don't match the real values, this may result in drawing errors + * or other problems. + * + * @see KDecorationOptions::moveResizeMaximizedWindows() + */ + // mustn't do any repaints, resizes or anything like that + virtual void borders( int& left, int& right, int& top, int& bottom ) const = 0; + /** + * This method is called by kwin when the style should resize the decoration window. + * The usual implementation is to resize the main widget of the decoration to the + * given size. + * + * @param s Specifies the new size of the decoration window. + */ + virtual void resize( const QSize& s ) = 0; + /** + * This function should return the minimum required size for the decoration. + * Note that the returned size shouldn't be too large, because it will be + * used to keep the decorated window at least as large. + */ + virtual QSize minimumSize() const = 0; + /** + * This function is called whenever the window either becomes or stops being active. + * Use isActive() to find out the current state. + */ + virtual void activeChange() = 0; + /** + * This function is called whenever the caption changes. Use caption() to get it. + */ + virtual void captionChange() = 0; + /** + * This function is called whenever the window icon changes. Use icon() to get it. + */ + virtual void iconChange() = 0; + /** + * This function is called whenever the maximalization state of the window changes. + * Use maximizeMode() to get the current state. + */ + virtual void maximizeChange() = 0; + /** + * This function is called whenever the desktop for the window changes. Use + * desktop() or isOnAllDesktops() to find out the current desktop + * on which the window is. + */ + virtual void desktopChange() = 0; + /** + * This function is called whenever the window is shaded or unshaded. Use + * isShade() to get the current state. + */ + virtual void shadeChange() = 0; +#if KDE_IS_VERSION( 3, 90, 0 ) +#warning Redo all the XYZChange() virtuals as signals. +#endif + signals: + /** + * This signal is emitted whenever the window's keep-above state changes. + * @since 3.3 + */ + void keepAboveChanged( bool ); + /** + * This signal is emitted whenever the window's keep-below state changes. + * @since 3.3 + */ + void keepBelowChanged( bool ); + public: + /** + * This function may be reimplemented to provide custom bound drawing + * for transparent moving or resizing of the window. + * @a False should be returned if the default implementation should be used. + * Note that if you e.g. paint the outline using a 5 pixels wide line, + * you should compensate for the 2 pixels that would make the window + * look larger. + * + * @param geom The geometry at this the bound should be drawn + * @param clear @a true if the bound should be cleared + * + * @see workspaceWidget() and geometry(). + */ + virtual bool drawbound( const QRect& geom, bool clear ); + /** + * This function may be reimplemented to provide custom minimize/restore animations + * The reimplementation is allowed to perform X server grabs if necessary + * (only using the functions provided by this API, no direct Xlib calls), but no + * futher event processing is allowed (i.e. no kapp->processEvents()). + * @a False should be returned if the default implementation should be used. + * Note that you should not use this function to force disabling of the animation. + * + * @see workspaceWidget(), geometry() and helperShowHide(). + */ + virtual bool animateMinimize( bool minimize ); + /** + * @internal Reserved. + */ + // TODO position will need also values for top+left+bottom etc. docking ? + virtual bool windowDocked( Position side ); + /** + * This function is called to reset the decoration on settings changes. + * It is usually invoked by calling KDecorationFactory::resetDecorations(). + * + * @param changed Specifies which settings were changed, given by the SettingXXX masks + */ + virtual void reset( unsigned long changed ); + + // special + + /** + * This should be the first function called in init() to specify + * the main widget of the decoration. The widget should be created + * with parent specified by initialParentWidget() and flags + * specified by initialWFlags(). + */ + void setMainWidget( QWidget* ); + /** + * Convenience functions that creates and sets a main widget as necessary. + * In such case, it's usually needed to install an event filter + * on the main widget to receive important events on it. + * + * @param flags Additional widget flags for the main widget. Note that only + * flags that affect widget drawing are allowed. Window type flags + * like WX11BypassWM or WStyle_NoBorder are forbidden. + */ + void createMainWidget( WFlags flags = 0 ); + /** + * The parent widget that should be used for the main widget. + */ + QWidget* initialParentWidget() const; + /** + * The flags that should be used when creating the main widget. + * It is possible to add more flags when creating the main widget, but only flags + * that affect widget drawing are allowed. Window type flags like WX11BypassWM + * or WStyle_NoBorder are forbidden. + */ + WFlags initialWFlags() const; + /** + * This function is only allowed to be called once from animateMinimize(). + * It can be used if the window should be shown or hidden at a specific + * time during the animation. It is forbidden to use this function + * for other purposes. + */ + void helperShowHide( bool show ); + /** + * Returns the main widget for the decoration. + */ + QWidget* widget(); + /** + * Returns the main widget for the decoration. + */ + const QWidget* widget() const; + /** + * Returns the factory that created this decoration. + */ + KDecorationFactory* factory() const; + /** + * Performs X server grab. It is safe to call it several times in a row. + */ + void grabXServer(); + /** + * Ungrabs X server (if the number of ungrab attempts matches the number of grab attempts). + */ + void ungrabXServer(); + public slots: + // requests from decoration + + /** + * This function can be called by the decoration to request + * closing of the decorated window. Note that closing the window + * also involves destroying the decoration. + * IMPORTANT: This function may destroy the current decoration object, + * just like showWindowMenu(). + */ + void closeWindow(); + /* + * Changes the maximize mode of the decorated window. This function should + * be preferred to the other maximize() overload for reacting on clicks + * on the maximize titlebar button. + * NOTE: This function is new in KDE3.3. In order to support also KDE3.2, + * it is recommended to use code like this: + * \code + * ButtonState button = ... ; + * #if KDE_IS_VERSION( 3, 3, 0 ) + * maximize( button ); + * #else + * if( button == MidButton ) + * maximize( maximizeMode() ^ MaximizeVertical ); + * else if( button == RightButton ) + * maximize( maximizeMode() ^ MaximizeHorizontal ); + * else + * maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull ); + * #endif + * \endcode + * @since 3.3 + */ +#if KDE_IS_VERSION( 3, 90, 0 ) +#warning Update the docs. +#endif + void maximize( ButtonState button ); + /** + * Set the maximize mode of the decorated window. + * @param mode The maximization mode to be set. + */ + void maximize( MaximizeMode mode ); + /** + * Minimize the decorated window. + */ + void minimize(); + /** + * Start showing context help in the window (i.e. the mouse will enter + * the what's this mode). + */ + void showContextHelp(); + /** + * Moves the window to the given desktop. Use NET::OnAllDesktops for making + * the window appear on all desktops. + */ + void setDesktop( int desktop ); + /** + * This function toggles the on-all-desktops state of the decorated window. + */ + void toggleOnAllDesktops(); // convenience + /** + * This function performs the operation configured as titlebar double click + * operation. + */ + void titlebarDblClickOperation(); + /** + * This function performs the operation configured as titlebar wheel mouse + * operation. + * @param delta the mouse wheel delta + * @since 3.5 + */ + void titlebarMouseWheelOperation( int delta ); + /** + * Shades or unshades the decorated window. + * @param set Whether the window should be shaded + */ + void setShade( bool set ); + /** + * Sets or reset keeping this window above others. + * @param set Whether to keep the window above others + */ + void setKeepAbove( bool set ); + /** + * Sets or reset keeping this window below others. + * @param set Whether to keep the window below others + */ + void setKeepBelow( bool set ); + /** + * @internal + */ + void emitKeepAboveChanged( bool above ) { emit keepAboveChanged( above ); } + /** + * @internal + */ + void emitKeepBelowChanged( bool below ) { emit keepBelowChanged( below ); } + private: + KDecorationBridge* bridge_; + QWidget* w_; + KDecorationFactory* factory_; + friend class KDecorationOptions; // for options_ + static KDecorationOptions* options_; + KDecorationPrivate* d; + }; + +inline +KDecorationDefines::MaximizeMode operator^( KDecorationDefines::MaximizeMode m1, KDecorationDefines::MaximizeMode m2 ) + { + return KDecorationDefines::MaximizeMode( int(m1) ^ int(m2) ); + } + +inline +KDecorationDefines::MaximizeMode operator&( KDecorationDefines::MaximizeMode m1, KDecorationDefines::MaximizeMode m2 ) + { + return KDecorationDefines::MaximizeMode( int(m1) & int(m2) ); + } + +inline +KDecorationDefines::MaximizeMode operator|( KDecorationDefines::MaximizeMode m1, KDecorationDefines::MaximizeMode m2 ) + { + return KDecorationDefines::MaximizeMode( int(m1) | int(m2) ); + } + +inline QWidget* KDecoration::widget() + { + return w_; + } + +inline const QWidget* KDecoration::widget() const + { + return w_; + } + +inline KDecorationFactory* KDecoration::factory() const + { + return factory_; + } + +inline bool KDecoration::isOnAllDesktops() const + { + return desktop() == NET::OnAllDesktops; + } + +inline int KDecoration::width() const + { + return geometry().width(); + } + +inline int KDecoration::height() const + { + return geometry().height(); + } + +#endif diff --git a/kwin/lib/kdecoration_p.cpp b/kwin/lib/kdecoration_p.cpp new file mode 100644 index 000000000..0117e4ec4 --- /dev/null +++ b/kwin/lib/kdecoration_p.cpp @@ -0,0 +1,235 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#include "kdecoration_p.h" + +#include <kconfig.h> +#include <kglobalsettings.h> +#include <qpalette.h> +#include <qapplication.h> +#include <assert.h> + +KDecorationOptionsPrivate::KDecorationOptionsPrivate() + { + for(int i=0; i < NUM_COLORS*2; ++i) + cg[i] = NULL; + } + +KDecorationOptionsPrivate::~KDecorationOptionsPrivate() + { + int i; + for(i=0; i < NUM_COLORS*2; ++i) + { + if(cg[i]) + { + delete cg[i]; + cg[i] = NULL; + } + } + } + +void KDecorationOptionsPrivate::defaultKWinSettings() + { + title_buttons_left = "MS"; + title_buttons_right = "HIAX"; + custom_button_positions = false; + show_tooltips = true; + border_size = BorderNormal; + cached_border_size = BordersCount; // invalid + move_resize_maximized_windows = true; + OpMaxButtonRightClick = MaximizeOp; + OpMaxButtonMiddleClick = VMaximizeOp; + OpMaxButtonLeftClick = HMaximizeOp; + } + +unsigned long KDecorationOptionsPrivate::updateKWinSettings( KConfig* config ) + { + unsigned long changed = 0; + QString old_group = config->group(); + config->setGroup( "WM" ); + +// SettingColors + QColor old_colors[NUM_COLORS*2]; + for( int i = 0; + i < NUM_COLORS*2; + ++i ) + old_colors[ i ] = colors[ i ]; + + QPalette pal = QApplication::palette(); + // normal colors + colors[ColorFrame] = pal.active().background(); + colors[ColorFrame] = config->readColorEntry("frame", &colors[ColorFrame]); + colors[ColorHandle] = colors[ColorFrame]; + colors[ColorHandle] = config->readColorEntry("handle", &colors[ColorHandle]); + + // full button configuration (background, blend, and foreground + if(QPixmap::defaultDepth() > 8) + colors[ColorButtonBg] = colors[ColorFrame].light(130); + else + colors[ColorButtonBg] = colors[ColorFrame]; + colors[ColorButtonBg] = config->readColorEntry("activeTitleBtnBg", + &colors[ColorFrame]); + colors[ColorTitleBar] = pal.active().highlight(); + colors[ColorTitleBar] = config->readColorEntry("activeBackground", + &colors[ColorTitleBar]); + if(QPixmap::defaultDepth() > 8) + colors[ColorTitleBlend] = colors[ ColorTitleBar ].dark(110); + else + colors[ColorTitleBlend] = colors[ ColorTitleBar ]; + colors[ColorTitleBlend] = config->readColorEntry("activeBlend", + &colors[ColorTitleBlend]); + + colors[ColorFont] = pal.active().highlightedText(); + colors[ColorFont] = config->readColorEntry("activeForeground", &colors[ColorFont]); + + // inactive + colors[ColorFrame+NUM_COLORS] = config->readColorEntry("inactiveFrame", + &colors[ColorFrame]); + colors[ColorTitleBar+NUM_COLORS] = colors[ColorFrame]; + colors[ColorTitleBar+NUM_COLORS] = config-> + readColorEntry("inactiveBackground", &colors[ColorTitleBar+NUM_COLORS]); + + if(QPixmap::defaultDepth() > 8) + colors[ColorTitleBlend+NUM_COLORS] = colors[ ColorTitleBar+NUM_COLORS ].dark(110); + else + colors[ColorTitleBlend+NUM_COLORS] = colors[ ColorTitleBar+NUM_COLORS ]; + colors[ColorTitleBlend+NUM_COLORS] = + config->readColorEntry("inactiveBlend", &colors[ColorTitleBlend+NUM_COLORS]); + + // full button configuration + if(QPixmap::defaultDepth() > 8) + colors[ColorButtonBg+NUM_COLORS] = colors[ColorFrame+NUM_COLORS].light(130); + else + colors[ColorButtonBg+NUM_COLORS] = colors[ColorFrame+NUM_COLORS]; + colors[ColorButtonBg+NUM_COLORS] = + config->readColorEntry("inactiveTitleBtnBg", + &colors[ColorButtonBg]); + + colors[ColorHandle+NUM_COLORS] = + config->readColorEntry("inactiveHandle", &colors[ColorHandle]); + + colors[ColorFont+NUM_COLORS] = colors[ColorFrame].dark(); + colors[ColorFont+NUM_COLORS] = config->readColorEntry("inactiveForeground", + &colors[ColorFont+NUM_COLORS]); + + for( int i = 0; + i < NUM_COLORS*2; + ++i ) + if( old_colors[ i ] != colors[ i ] ) + changed |= SettingColors; + +// SettingFont + QFont old_activeFont = activeFont; + QFont old_inactiveFont = inactiveFont; + QFont old_activeFontSmall = activeFontSmall; + QFont old_inactiveFontSmall = inactiveFontSmall; + + QFont activeFontGuess = KGlobalSettings::windowTitleFont(); + + activeFont = config->readFontEntry("activeFont", &activeFontGuess); + inactiveFont = config->readFontEntry("inactiveFont", &activeFont); + + activeFontSmall = activeFont; + activeFontSmall.setPointSize(activeFont.pointSize() - 2); + activeFontSmall = config->readFontEntry("activeFontSmall", &activeFontSmall); + inactiveFontSmall = config->readFontEntry("inactiveFontSmall", &activeFontSmall); + + if( old_activeFont != activeFont + || old_inactiveFont != inactiveFont + || old_activeFontSmall != activeFontSmall + || old_inactiveFontSmall != inactiveFontSmall ) + changed |= SettingFont; + + config->setGroup( "Style" ); +// SettingsButtons + QString old_title_buttons_left = title_buttons_left; + QString old_title_buttons_right = title_buttons_right; + bool old_custom_button_positions = custom_button_positions; + custom_button_positions = config->readBoolEntry("CustomButtonPositions", false); + if (custom_button_positions) + { + title_buttons_left = config->readEntry("ButtonsOnLeft", "MS"); + title_buttons_right = config->readEntry("ButtonsOnRight", "HIAX"); + } + else + { + title_buttons_left = "MS"; + title_buttons_right = "HIAX"; + } + if( old_custom_button_positions != custom_button_positions + || ( custom_button_positions && + ( old_title_buttons_left != title_buttons_left + || old_title_buttons_right != title_buttons_right ))) + changed |= SettingButtons; + +// SettingTooltips + bool old_show_tooltips = show_tooltips; + show_tooltips = config->readBoolEntry("ShowToolTips", true); + if( old_show_tooltips != show_tooltips ) + changed |= SettingTooltips; + +// SettingBorder + + BorderSize old_border_size = border_size; + int border_size_num = config->readNumEntry( "BorderSize", BorderNormal ); + if( border_size_num >= 0 && border_size_num < BordersCount ) + border_size = static_cast< BorderSize >( border_size_num ); + else + border_size = BorderNormal; + if( old_border_size != border_size ) + changed |= SettingBorder; + cached_border_size = BordersCount; // invalid + + config->setGroup( "Windows" ); + bool old_move_resize_maximized_windows = move_resize_maximized_windows; + move_resize_maximized_windows = config->readBoolEntry( "MoveResizeMaximizedWindows", false ); + if( old_move_resize_maximized_windows != move_resize_maximized_windows ) + changed |= SettingBorder; + +// destroy cached values + int i; + for(i=0; i < NUM_COLORS*2; ++i) + { + if(cg[i]) + { + delete cg[i]; + cg[i] = NULL; + } + } + + config->setGroup( old_group ); + + return changed; + } + +KDecorationDefines::BorderSize KDecorationOptionsPrivate::findPreferredBorderSize( BorderSize size, + QValueList< BorderSize > sizes ) const + { + for( QValueList< BorderSize >::ConstIterator it = sizes.begin(); + it != sizes.end(); + ++it ) + if( size <= *it ) // size is either a supported size, or *it is the closest larger supported + return *it; + return sizes.last(); // size is larger than all supported ones, return largest + } diff --git a/kwin/lib/kdecoration_p.h b/kwin/lib/kdecoration_p.h new file mode 100644 index 000000000..8d0e5e15a --- /dev/null +++ b/kwin/lib/kdecoration_p.h @@ -0,0 +1,111 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#ifndef KDECORATION_P_H +#define KDECORATION_P_H + +// +// This header file is internal. I mean it. +// + +#include "kdecoration.h" +#include <qwidget.h> +#include <qvaluelist.h> + +class KConfig; + +class KWIN_EXPORT KDecorationOptionsPrivate : public KDecorationDefines + { + public: + KDecorationOptionsPrivate(); + virtual ~KDecorationOptionsPrivate(); + void defaultKWinSettings(); // shared implementation + unsigned long updateKWinSettings( KConfig* ); // shared implementation + BorderSize findPreferredBorderSize( BorderSize size, QValueList< BorderSize > ) const; // shared implementation + + QColor colors[NUM_COLORS*2]; + QColorGroup *cg[NUM_COLORS*2]; + QFont activeFont, inactiveFont, activeFontSmall, inactiveFontSmall; + QString title_buttons_left; + QString title_buttons_right; + bool custom_button_positions; + bool show_tooltips; + BorderSize border_size, cached_border_size; + bool move_resize_maximized_windows; + WindowOperation OpMaxButtonRightClick; + WindowOperation OpMaxButtonMiddleClick; + WindowOperation OpMaxButtonLeftClick; + }; + +class KDecorationBridge : public KDecorationDefines + { + public: + virtual bool isActive() const = 0; + virtual bool isCloseable() const = 0; + virtual bool isMaximizable() const = 0; + virtual MaximizeMode maximizeMode() const = 0; + virtual bool isMinimizable() const = 0; + virtual bool providesContextHelp() const = 0; + virtual int desktop() const = 0; + virtual bool isModal() const = 0; + virtual bool isShadeable() const = 0; + virtual bool isShade() const = 0; + virtual bool isSetShade() const = 0; + virtual bool keepAbove() const = 0; + virtual bool keepBelow() const = 0; + virtual bool isMovable() const = 0; + virtual bool isResizable() const = 0; + virtual NET::WindowType windowType( unsigned long supported_types ) const = 0; + virtual QIconSet icon() const = 0; + virtual QString caption() const = 0; + virtual void processMousePressEvent( QMouseEvent* ) = 0; + virtual void showWindowMenu( const QRect &) = 0; + virtual void showWindowMenu( QPoint ) = 0; + virtual void performWindowOperation( WindowOperation ) = 0; + virtual void setMask( const QRegion&, int ) = 0; + virtual bool isPreview() const = 0; + virtual QRect geometry() const = 0; + virtual QRect iconGeometry() const = 0; + virtual QRegion unobscuredRegion( const QRegion& r ) const = 0; + virtual QWidget* workspaceWidget() const = 0; + virtual WId windowId() const = 0; + virtual void closeWindow() = 0; + virtual void maximize( MaximizeMode mode ) = 0; + virtual void minimize() = 0; + virtual void showContextHelp() = 0; + virtual void setDesktop( int desktop ) = 0; + virtual void titlebarDblClickOperation() = 0; + virtual void titlebarMouseWheelOperation( int delta ) = 0; + virtual void setShade( bool set ) = 0; + virtual void setKeepAbove( bool ) = 0; + virtual void setKeepBelow( bool ) = 0; + // not part of public API + virtual int currentDesktop() const = 0; + virtual QWidget* initialParentWidget() const = 0; + virtual Qt::WFlags initialWFlags() const = 0; + virtual void helperShowHide( bool ) = 0; + virtual void grabXServer( bool grab ) = 0; + }; + +#endif diff --git a/kwin/lib/kdecoration_plugins_p.cpp b/kwin/lib/kdecoration_plugins_p.cpp new file mode 100644 index 000000000..85d496105 --- /dev/null +++ b/kwin/lib/kdecoration_plugins_p.cpp @@ -0,0 +1,199 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 1999, 2000 Daniel M. Duley <mosfet@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#include "kdecoration_plugins_p.h" + +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <klibloader.h> +#include <assert.h> + +#include <qdir.h> +#include <qfile.h> + +#include "kdecorationfactory.h" + +KDecorationPlugins::KDecorationPlugins( KConfig* cfg ) + : create_ptr( NULL ), + library( NULL ), + fact( NULL ), + old_library( NULL ), + old_fact( NULL ), + pluginStr( "kwin3_undefined " ), + config( cfg ) + { + } + +KDecorationPlugins::~KDecorationPlugins() + { + if(library) + { + assert( fact != NULL ); + delete fact; + library->unload(); + } + if(old_library) + { + assert( old_fact != NULL ); + delete old_fact; + old_library->unload(); + } + } + +bool KDecorationPlugins::reset( unsigned long changed ) + { + QString oldPlugin = pluginStr; + config->reparseConfiguration(); + bool ret = false; + if(( !loadPlugin( "" ) && library ) // "" = read the one in cfg file + || oldPlugin == pluginStr ) + { // no new plugin loaded, reset the old one + assert( fact != NULL ); + ret = fact->reset( changed ); + } + return ret || oldPlugin != pluginStr; + } + +KDecorationFactory* KDecorationPlugins::factory() + { + return fact; + } + +// convenience +KDecoration* KDecorationPlugins::createDecoration( KDecorationBridge* bridge ) + { + if( fact != NULL ) + return fact->createDecoration( bridge ); + return NULL; + } + +// returns true if plugin was loaded successfully +bool KDecorationPlugins::loadPlugin( QString nameStr ) + { + if( nameStr.isEmpty()) + { + KConfigGroupSaver saver( config, "Style" ); + nameStr = config->readEntry("PluginLib", defaultPlugin ); + } + // make sure people can switch between HEAD and kwin_iii branch + if( nameStr.startsWith( "kwin_" )) + nameStr = "kwin3_" + nameStr.mid( 5 ); + + KLibrary *oldLibrary = library; + KDecorationFactory* oldFactory = fact; + + QString path = KLibLoader::findLibrary(QFile::encodeName(nameStr)); + + // If the plugin was not found, try to find the default + if (path.isEmpty()) + { + nameStr = defaultPlugin; + path = KLibLoader::findLibrary(QFile::encodeName(nameStr)); + } + + // If no library was found, exit kwin with an error message + if (path.isEmpty()) + { + error( i18n("No window decoration plugin library was found." )); + return false; + } + + // Check if this library is not already loaded. + if(pluginStr == nameStr) + return true; + + // Try loading the requested plugin + library = KLibLoader::self()->library(QFile::encodeName(path)); + + // If that fails, fall back to the default plugin + if (!library) + { + kdDebug() << " could not load library, try default plugin again" << endl; + nameStr = defaultPlugin; + if ( pluginStr == nameStr ) + return true; + path = KLibLoader::findLibrary(QFile::encodeName(nameStr)); + if (!path.isEmpty()) + library = KLibLoader::self()->library(QFile::encodeName(path)); + } + + if (!library) + { + error( i18n("The default decoration plugin is corrupt " + "and could not be loaded." )); + return false; + } + + create_ptr = NULL; + if( library->hasSymbol("create_factory")) + { + void* create_func = library->symbol("create_factory"); + if(create_func) + create_ptr = (KDecorationFactory* (*)())create_func; + } + if(!create_ptr) + { + error( i18n( "The library %1 is not a KWin plugin." ).arg( path )); + library->unload(); + return false; + } + fact = create_ptr(); + fact->checkRequirements( this ); // let it check what is supported + + pluginStr = nameStr; + + // For clients in kdeartwork + QString catalogue = nameStr; + catalogue.replace( "kwin3_", "kwin_" ); + KGlobal::locale()->insertCatalogue( catalogue ); + // For KCommonDecoration based clients + KGlobal::locale()->insertCatalogue( "kwin_lib" ); + // For clients in kdebase + KGlobal::locale()->insertCatalogue( "kwin_clients" ); + // For clients in kdeartwork + KGlobal::locale()->insertCatalogue( "kwin_art_clients" ); + + old_library = oldLibrary; // save for delayed destroying + old_fact = oldFactory; + + return true; +} + +void KDecorationPlugins::destroyPreviousPlugin() +{ + // Destroy the old plugin + if(old_library) + { + delete old_fact; + old_fact = NULL; + old_library->unload(); + old_library = NULL; + } +} + +void KDecorationPlugins::error( const QString& ) + { + } diff --git a/kwin/lib/kdecoration_plugins_p.h b/kwin/lib/kdecoration_plugins_p.h new file mode 100644 index 000000000..b82f7f914 --- /dev/null +++ b/kwin/lib/kdecoration_plugins_p.h @@ -0,0 +1,77 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 1999, 2000 Daniel M. Duley <mosfet@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#ifndef KDECORATION_PLUGINS_H +#define KDECORATION_PLUGINS_H + +// +// This header file is internal. I mean it. +// + +#include <qcstring.h> +#include <qstring.h> +#include <qwidget.h> + +#include "kdecoration.h" + +class KLibrary; +class KConfig; +class KDecoration; +class KDecorationBridge; +class KDecorationFactory; + +class KWIN_EXPORT KDecorationPlugins + : public KDecorationProvides + { + public: + KDecorationPlugins( KConfig* cfg ); + virtual ~KDecorationPlugins(); + bool loadPlugin( QString name ); + void destroyPreviousPlugin(); + KDecorationFactory* factory(); + KDecoration* createDecoration( KDecorationBridge* ); + QString currentPlugin() { return pluginStr; } + bool reset( unsigned long changed ); // returns true if decorations need to be recreated + protected: + virtual void error( const QString& error_msg ); + QCString defaultPlugin; // FRAME normalne protected? + private: + KDecorationFactory* (*create_ptr)(); + KLibrary *library; + KDecorationFactory* fact; + KLibrary *old_library; + KDecorationFactory* old_fact; + QString pluginStr; + KConfig* config; + }; + +/* + + Plugins API: + KDecorationFactory* create_factory(); - called once after loading + +*/ + +#endif diff --git a/kwin/lib/kdecorationfactory.cpp b/kwin/lib/kdecorationfactory.cpp new file mode 100644 index 000000000..74508501b --- /dev/null +++ b/kwin/lib/kdecorationfactory.cpp @@ -0,0 +1,85 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#include "kdecorationfactory.h" + +#include <assert.h> + +#include "kdecoration_p.h" + +KDecorationFactory::KDecorationFactory() + { + } + +KDecorationFactory::~KDecorationFactory() + { + assert( _decorations.count() == 0 ); + } + +bool KDecorationFactory::reset( unsigned long ) + { + return false; + } + +bool KDecorationFactory::supports( Ability ) + { + return false; + } + +void KDecorationFactory::checkRequirements( KDecorationProvides* ) + { + } + +QValueList< KDecorationDefines::BorderSize > KDecorationFactory::borderSizes() const + { + return QValueList< BorderSize >() << BorderNormal; + } + +bool KDecorationFactory::exists( const KDecoration* deco ) const + { + return _decorations.contains( const_cast< KDecoration* >( deco )); + } + +void KDecorationFactory::addDecoration( KDecoration* deco ) + { + _decorations.append( deco ); + } + +void KDecorationFactory::removeDecoration( KDecoration* deco ) + { + _decorations.remove( deco ); + } + +void KDecorationFactory::resetDecorations( unsigned long changed ) + { + for( QValueList< KDecoration* >::ConstIterator it = _decorations.begin(); + it != _decorations.end(); + ++it ) + (*it)->reset( changed ); + } + +NET::WindowType KDecorationFactory::windowType( unsigned long supported_types, KDecorationBridge* bridge ) const + { + return bridge->windowType( supported_types ); + } diff --git a/kwin/lib/kdecorationfactory.h b/kwin/lib/kdecorationfactory.h new file mode 100644 index 000000000..08e733c8b --- /dev/null +++ b/kwin/lib/kdecorationfactory.h @@ -0,0 +1,120 @@ +/***************************************************************** +This file is part of the KDE project. + +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +******************************************************************/ + +#ifndef KDECORATIONFACTORY_H +#define KDECORATIONFACTORY_H + +#include "kdecoration.h" + +class KDecoration; +class KDecorationBridge; +class KDecorationFactoryPrivate; + +class KWIN_EXPORT KDecorationFactory + : public KDecorationDefines + { + public: + /** + * Constructor. Called after loading the decoration plugin. All global + * initialization of the plugin should be done in the factory constructor. + */ + KDecorationFactory(); + /** + * Destructor. Called before unloading the decoration plugin. All global + * cleanup of the plugin should be done in the factory destructor. + */ + virtual ~KDecorationFactory(); + /** + * This function must be reimplemented to create decoration objects. + * The argument should be passed to the KDecoration constructor, the second + * KDecoration argument should be this factory object. + */ + virtual KDecoration* createDecoration( KDecorationBridge* bridge ) = 0; + /** + * This function is called when the configuration settings changed. + * The argument specifies what has changed, using the SettingXXX masks. + * It should be determined whether the decorations need to be completely + * remade, in which case true should be returned, or whether only e.g. + * a repaint will be sufficient, in which case false should be returned, + * and resetDecorations() can be called to reset all decoration objects. + * Note that true should be returned only when really necessary. + */ + virtual bool reset( unsigned long changed ); // returns true if the decoration needs to be recreated + + /** + * Reimplement this function if your decoration supports more border sizes than + * the default one (BorderNormal). The returned list must contain all supported + * sizes, ordered from the smallest to the largest one. By default, only + * BorderNormal is returned. + */ + virtual QValueList< BorderSize > borderSizes() const; + + virtual bool supports( Ability ability ); + + virtual void checkRequirements( KDecorationProvides* provides ); + /** + * Returns the KDecorationOptions object, which is used to access + * configuration settings for the decoration. + */ + const KDecorationOptions* options(); // convenience + /** + * Returns true if the given decoration object still exists. This is necessary + * e.g. when calling KDecoration::showWindowMenu(), which may cause the decoration + * to be destroyed. Note that this function is reliable only if called immediately + * after such actions. + */ + bool exists( const KDecoration* deco ) const; + /** + * @internal + */ + void addDecoration( KDecoration* ); + /** + * @internal + */ + void removeDecoration( KDecoration* ); + protected: + /** + * Convenience function that calls KDecoration::reset() for all decoration + * objects. + */ + void resetDecorations( unsigned long changed ); // convenience + /** + * This function has the same functionality like KDecoration::windowType(). + * It can be used in createDecoration() to return different KDecoration + * inherited classes depending on the window type, as at that time + * KDecoration::windowType() is not available yet. The additional argument + * is the one passed to createDecoration(). + */ + NET::WindowType windowType( unsigned long supported_types, KDecorationBridge* bridge ) const; + private: + QValueList< KDecoration* > _decorations; + KDecorationFactoryPrivate* d; + }; + +inline const KDecorationOptions* KDecorationFactory::options() + { + return KDecoration::options(); + } + +#endif diff --git a/kwin/main.cpp b/kwin/main.cpp new file mode 100644 index 000000000..82410d6a4 --- /dev/null +++ b/kwin/main.cpp @@ -0,0 +1,301 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#define QT_CLEAN_NAMESPACE +#include <kconfig.h> + +#include "main.h" + +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> +#include <stdlib.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <dcopclient.h> +#include <dcopref.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> + +#include "atoms.h" +#include "options.h" +#include "sm.h" + +#define INT8 _X11INT8 +#define INT32 _X11INT32 +#include <X11/Xproto.h> +#undef INT8 +#undef INT32 + +extern Time qt_x_time; + +namespace KWinInternal +{ + +Options* options; + +Atoms* atoms; + +int screen_number = -1; + +static bool initting = FALSE; + +static +int x11ErrorHandler(Display *d, XErrorEvent *e) + { + char msg[80], req[80], number[80]; + bool ignore_badwindow = TRUE; //maybe temporary + + if (initting && + ( + e->request_code == X_ChangeWindowAttributes + || e->request_code == X_GrabKey + ) + && (e->error_code == BadAccess)) + { + fputs(i18n("kwin: it looks like there's already a window manager running. kwin not started.\n").local8Bit(), stderr); + exit(1); + } + + if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor)) + return 0; + + XGetErrorText(d, e->error_code, msg, sizeof(msg)); + sprintf(number, "%d", e->request_code); + XGetErrorDatabaseText(d, "XRequest", number, "<unknown>", req, sizeof(req)); + + fprintf(stderr, "kwin: %s(0x%lx): %s\n", req, e->resourceid, msg); + + if (initting) + { + fputs(i18n("kwin: failure during initialization; aborting").local8Bit(), stderr); + exit(1); + } + return 0; + } + +Application::Application( ) +: KApplication( ), owner( screen_number ) + { + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + if (!config()->isImmutable() && args->isSet("lock")) + { + config()->setReadOnly(true); + config()->reparseConfiguration(); + } + + if (screen_number == -1) + screen_number = DefaultScreen(qt_xdisplay()); + + if( !owner.claim( args->isSet( "replace" ), true )) + { + fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").local8Bit(), stderr); + ::exit(1); + } + connect( &owner, SIGNAL( lostOwnership()), SLOT( lostSelection())); + + // if there was already kwin running, it saved its configuration after loosing the selection -> reread + config()->reparseConfiguration(); + + initting = TRUE; // startup.... + + // install X11 error handler + XSetErrorHandler( x11ErrorHandler ); + + // check whether another windowmanager is running + XSelectInput(qt_xdisplay(), qt_xrootwin(), SubstructureRedirectMask ); + syncX(); // trigger error now + + options = new Options; + atoms = new Atoms; + + // create workspace. + (void) new Workspace( isSessionRestored() ); + + syncX(); // trigger possible errors, there's still a chance to abort + + DCOPRef ref( "kded", "kded" ); + ref.send( "unloadModule", QCString( "kdetrayproxy" )); + + initting = FALSE; // startup done, we are up and running now. + + dcopClient()->send( "ksplash", "", "upAndRunning(QString)", QString("wm started")); + XEvent e; + e.xclient.type = ClientMessage; + e.xclient.message_type = XInternAtom( qt_xdisplay(), "_KDE_SPLASH_PROGRESS", False ); + e.xclient.display = qt_xdisplay(); + e.xclient.window = qt_xrootwin(); + e.xclient.format = 8; + strcpy( e.xclient.data.b, "wm started" ); + XSendEvent( qt_xdisplay(), qt_xrootwin(), False, SubstructureNotifyMask, &e ); + } + +Application::~Application() + { + delete Workspace::self(); + if( owner.ownerWindow() != None ) // if there was no --replace (no new WM) + { + XSetInputFocus( qt_xdisplay(), PointerRoot, RevertToPointerRoot, qt_x_time ); + DCOPRef ref( "kded", "kded" ); + if( !ref.send( "loadModule", QCString( "kdetrayproxy" ))) + kdWarning( 176 ) << "Loading of kdetrayproxy failed." << endl; + } + delete options; + } + +void Application::lostSelection() + { + delete Workspace::self(); + // remove windowmanager privileges + XSelectInput(qt_xdisplay(), qt_xrootwin(), PropertyChangeMask ); + DCOPRef ref( "kded", "kded" ); + if( !ref.send( "loadModule", QCString( "kdetrayproxy" ))) + kdWarning( 176 ) << "Loading of kdetrayproxy failed." << endl; + quit(); + } + +bool Application::x11EventFilter( XEvent *e ) + { + if ( Workspace::self()->workspaceEvent( e ) ) + return TRUE; + return KApplication::x11EventFilter( e ); + } + +static void sighandler(int) + { + QApplication::exit(); + } + + +} // namespace + +static const char version[] = "3.0"; +static const char description[] = I18N_NOOP( "KDE window manager" ); + +static KCmdLineOptions args[] = + { + { "lock", I18N_NOOP("Disable configuration options"), 0 }, + { "replace", I18N_NOOP("Replace already-running ICCCM2.0-compliant window manager"), 0 }, + KCmdLineLastOption + }; + +extern "C" +KDE_EXPORT int kdemain( int argc, char * argv[] ) + { + bool restored = false; + for (int arg = 1; arg < argc; arg++) + { + if (! qstrcmp(argv[arg], "-session")) + { + restored = true; + break; + } + } + + if (! restored) + { + // we only do the multihead fork if we are not restored by the session + // manager, since the session manager will register multiple kwins, + // one for each screen... + QCString multiHead = getenv("KDE_MULTIHEAD"); + if (multiHead.lower() == "true") + { + + Display* dpy = XOpenDisplay( NULL ); + if ( !dpy ) + { + fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n", + argv[0], XDisplayName(NULL ) ); + exit (1); + } + + int number_of_screens = ScreenCount( dpy ); + KWinInternal::screen_number = DefaultScreen( dpy ); + int pos; // temporarily needed to reconstruct DISPLAY var if multi-head + QCString display_name = XDisplayString( dpy ); + XCloseDisplay( dpy ); + dpy = 0; + + if ((pos = display_name.findRev('.')) != -1 ) + display_name.remove(pos,10); // 10 is enough to be sure we removed ".s" + + QCString envir; + if (number_of_screens != 1) + { + for (int i = 0; i < number_of_screens; i++ ) + { + // if execution doesn't pass by here, then kwin + // acts exactly as previously + if ( i != KWinInternal::screen_number && fork() == 0 ) + { + KWinInternal::screen_number = i; + // break here because we are the child process, we don't + // want to fork() anymore + break; + } + } + // in the next statement, display_name shouldn't contain a screen + // number. If it had it, it was removed at the "pos" check + envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWinInternal::screen_number); + + if (putenv( strdup(envir.data())) ) + { + fprintf(stderr, + "%s: WARNING: unable to set DISPLAY environment variable\n", + argv[0]); + perror("putenv()"); + } + } + } + } + + KGlobal::locale()->setMainCatalogue("kwin"); + + KAboutData aboutData( "kwin", I18N_NOOP("KWin"), + version, description, KAboutData::License_GPL, + I18N_NOOP("(c) 1999-2005, The KDE Developers")); + aboutData.addAuthor("Matthias Ettrich",0, "ettrich@kde.org"); + aboutData.addAuthor("Cristian Tibirna",0, "tibirna@kde.org"); + aboutData.addAuthor("Daniel M. Duley",0, "mosfet@kde.org"); + aboutData.addAuthor("LuboÅ¡ Luňák", I18N_NOOP( "Maintainer" ), "l.lunak@kde.org"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( args ); + + if (signal(SIGTERM, KWinInternal::sighandler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + if (signal(SIGINT, KWinInternal::sighandler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGHUP, KWinInternal::sighandler) == SIG_IGN) + signal(SIGHUP, SIG_IGN); + + KApplication::disableAutoDcopRegistration(); + KWinInternal::Application a; + KWinInternal::SessionManaged weAreIndeed; + KWinInternal::SessionSaveDoneHelper helper; + + fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, 1); + + QCString appname; + if (KWinInternal::screen_number == 0) + appname = "kwin"; + else + appname.sprintf("kwin-screen-%d", KWinInternal::screen_number); + + DCOPClient* client = a.dcopClient(); + client->registerAs( appname.data(), false); + client->setDefaultObject( "KWinInterface" ); + + return a.exec(); + } + +#include "main.moc" diff --git a/kwin/main.h b/kwin/main.h new file mode 100644 index 000000000..5ac38a61a --- /dev/null +++ b/kwin/main.h @@ -0,0 +1,40 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef MAIN_H +#define MAIN_H + +#include <kapplication.h> +#include "workspace.h" +#include "utils.h" + +namespace KWinInternal +{ + +class Application : public KApplication + { + Q_OBJECT + public: + Application(); + ~Application(); + + protected: + bool x11EventFilter( XEvent * ); + private slots: + void lostSelection(); + + private: + KWinSelectionOwner owner; + }; + +} // namespace + +#endif diff --git a/kwin/manage.cpp b/kwin/manage.cpp new file mode 100644 index 000000000..35dcc88ba --- /dev/null +++ b/kwin/manage.cpp @@ -0,0 +1,596 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to handling incoming events. + +*/ + +#include "client.h" + +#include <kstartupinfo.h> +#include <kglobal.h> +#include <X11/extensions/shape.h> + +#include "notifications.h" +#include "rules.h" +#include "group.h" + +extern Time qt_x_time; + +namespace KWinInternal +{ + +/*! + Manages the clients. This means handling the very first maprequest: + reparenting, initial geometry, initial state, placement, etc. + Returns false if KWin is not going to manage this window. + */ +bool Client::manage( Window w, bool isMapped ) + { + XWindowAttributes attr; + if( !XGetWindowAttributes(qt_xdisplay(), w, &attr)) + return false; + + grabXServer(); + + // from this place on, manage() mustn't return false + postpone_geometry_updates = 1; + pending_geometry_update = true; // force update when finishing with geometry changes + + embedClient( w, attr ); + + // SELI order all these things in some sane manner + + bool init_minimize = false; + XWMHints * hints = XGetWMHints(qt_xdisplay(), w ); + if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState) + init_minimize = true; + if (hints) + XFree(hints); + if( isMapped ) + init_minimize = false; // if it's already mapped, ignore hint + + unsigned long properties[ 2 ]; + properties[ WinInfo::PROTOCOLS ] = + NET::WMDesktop | + NET::WMState | + NET::WMWindowType | + NET::WMStrut | + NET::WMName | + NET::WMIconGeometry | + NET::WMIcon | + NET::WMPid | + NET::WMIconName | + 0; + properties[ WinInfo::PROTOCOLS2 ] = + NET::WM2UserTime | + NET::WM2StartupId | + NET::WM2ExtendedStrut | + 0; + + info = new WinInfo( this, qt_xdisplay(), client, qt_xrootwin(), properties, 2 ); + + cmap = attr.colormap; + + XClassHint classHint; + if ( XGetClassHint( qt_xdisplay(), client, &classHint ) ) + { + // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class + // force lowercase, so that workarounds listing resource classes still work + resource_name = QCString( classHint.res_name ).lower(); + resource_class = QCString( classHint.res_class ).lower(); + XFree( classHint.res_name ); + XFree( classHint.res_class ); + } + ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO change to rules + + window_role = staticWindowRole( w ); + getWmClientLeader(); + getWmClientMachine(); + // first only read the caption text, so that setupWindowRules() can use it for matching, + // and only then really set the caption using setCaption(), which checks for duplicates etc. + // and also relies on rules already existing + cap_normal = readName(); + setupWindowRules( false ); + setCaption( cap_normal, true ); + + detectNoBorder(); + detectShapable(); + fetchIconicName(); + getWMHints(); // needs to be done before readTransient() because of reading the group + modal = ( info->state() & NET::Modal ) != 0; // needs to be valid before handling groups + readTransient(); + getIcons(); + getWindowProtocols(); + getWmNormalHints(); // get xSizeHint + getMotifHints(); + + // TODO try to obey all state information from info->state() + + original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0; + skip_pager = ( info->state() & NET::SkipPager) != 0; + + KStartupInfoId asn_id; + KStartupInfoData asn_data; + bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); + + workspace()->updateClientLayer( this ); + + SessionInfo* session = workspace()->takeSessionInfo( this ); + + if ( session ) + { + if ( session->minimized ) + init_minimize = true; + if( session->userNoBorder ) + setUserNoBorder( true ); + } + + setShortcut( rules()->checkShortcut( session ? session->shortcut : QString::null, true )); + + init_minimize = rules()->checkMinimize( init_minimize, !isMapped ); + if( rules()->checkNoBorder( false, !isMapped )) + setUserNoBorder( true ); + + checkAndSetInitialRuledOpacity(); + + // initial desktop placement + if ( session ) + { + desk = session->desktop; + if( session->onAllDesktops ) + desk = NET::OnAllDesktops; + } + else + { + // if this window is transient, ensure that it is opened on the + // same window as its parent. this is necessary when an application + // starts up on a different desktop than is currently displayed + if( isTransient()) + { + ClientList mainclients = mainClients(); + bool on_current = false; + Client* maincl = NULL; + // this is slightly duplicated from Placement::placeOnMainWindow() + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + { + if( (*it)->isSpecialWindow()) + continue; // don't consider toolbars etc when placing + maincl = *it; + if( (*it)->isOnCurrentDesktop()) + on_current = true; + } + if( on_current ) + desk = workspace()->currentDesktop(); + else if( maincl != NULL ) + desk = maincl->desktop(); + } + if ( info->desktop() ) + desk = info->desktop(); // window had the initial desktop property, force it + if( desktop() == 0 && asn_valid && asn_data.desktop() != 0 ) + desk = asn_data.desktop(); + } + if ( desk == 0 ) // assume window wants to be visible on the current desktop + desk = workspace()->currentDesktop(); + desk = rules()->checkDesktop( desk, !isMapped ); + if( desk != NET::OnAllDesktops ) // do range check + desk = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desk )); + info->setDesktop( desk ); + workspace()->updateOnAllDesktopsOfTransients( this ); // SELI +// onAllDesktopsChange(); decoration doesn't exist here yet + + QRect geom( attr.x, attr.y, attr.width, attr.height ); + bool placementDone = FALSE; + + if ( session ) + geom = session->geometry; + + QRect area; + bool partial_keep_in_area = isMapped || session; + if( isMapped || session ) + area = workspace()->clientArea( FullArea, geom.center(), desktop()); + else if( options->xineramaPlacementEnabled ) + area = workspace()->clientArea( PlacementArea, QCursor::pos(), desktop()); + else + area = workspace()->clientArea( PlacementArea, geom.center(), desktop()); + + if( int type = checkFullScreenHack( geom )) + { + fullscreen_mode = FullScreenHack; + if( rules()->checkStrictGeometry( false )) + { + geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area + ? workspace()->clientArea( FullArea, geom.center(), desktop()) + : workspace()->clientArea( ScreenArea, geom.center(), desktop()); + } + else + geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop()); + placementDone = true; + } + + if ( isDesktop() ) + { + // desktops are treated slightly special + geom = workspace()->clientArea( FullArea, geom.center(), desktop()); + placementDone = true; + } + + bool usePosition = false; + if ( isMapped || session || placementDone ) + placementDone = true; // use geometry + else if( isTransient() && !isUtility() && !isDialog() && !isSplash()) + usePosition = true; + else if( isTransient() && !hasNETSupport()) + usePosition = true; + else if( isDialog() && hasNETSupport()) + // if the dialog is actually non-NETWM transient window, don't try to apply placement to it, + // it breaks with too many things (xmms, display) + { + if( mainClients().count() >= 1 ) + { +#if 1 + // TODO #78082 - Ok, it seems there are after all some cases when an application has a good + // reason to specify a position for its dialog. Too bad other WMs have never bothered + // with placement for dialogs, so apps always specify positions for their dialogs, + // including such silly positions like always centered on the screen or under mouse. + // Using ignoring requested position in window-specific settings helps, but at least + // for Qt apps this should work better. + usePosition = true; +#else + ; // force using placement policy +#endif + } + else + usePosition = true; + } + else if( isSplash()) + ; // force using placement policy + else + usePosition = true; + if( !rules()->checkIgnoreGeometry( !usePosition )) + { + bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass()))); + + if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) || + (xSizeHint.flags & USPosition) ) + { + placementDone = TRUE; + // disobey xinerama placement option for now (#70943) + area = workspace()->clientArea( PlacementArea, geom.center(), desktop()); + } + } + if( true ) // size is always obeyed for now, only with constraints applied + if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) + { + // keep in mind that we now actually have a size :-) + } + + if (xSizeHint.flags & PMaxSize) + geom.setSize( geom.size().boundedTo( + rules()->checkMaxSize( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) ); + if (xSizeHint.flags & PMinSize) + geom.setSize( geom.size().expandedTo( + rules()->checkMinSize( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) ); + + if( isMovable()) + { + if( geom.x() > area.right() || geom.y() > area.bottom()) + placementDone = FALSE; // weird, do not trust. + } + + if ( placementDone ) + move( geom.x(), geom.y() ); // before gravitating + + updateDecoration( false ); // also gravitates + // TODO is CentralGravity right here, when resizing is done after gravitating? + plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped )); + + QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped ); + if( forced_pos != invalidPoint ) + { + move( forced_pos ); + placementDone = true; + // don't keep inside workarea if the window has specially configured position + partial_keep_in_area = true; + area = workspace()->clientArea( FullArea, geom.center(), desktop()); + } + if( !placementDone ) + { // placement needs to be after setting size + workspace()->place( this, area ); + placementDone = TRUE; + } + + if(( !isSpecialWindow() || isToolbar()) && isMovable()) + keepInArea( area, partial_keep_in_area ); + + XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask ); + is_shape = Shape::hasShape( window()); + updateShape(); + + //CT extra check for stupid jdk 1.3.1. But should make sense in general + // if client has initial state set to Iconic and is transient with a parent + // window that is not Iconic, set init_state to Normal + if( init_minimize && isTransient()) + { + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + if( (*it)->isShown( true )) + init_minimize = false; // SELI even e.g. for NET::Utility? + } + // if a dialog is shown for minimized window, minimize it too + if( !init_minimize && isTransient() && mainClients().count() > 0 ) + { + bool visible_parent = false; + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.begin(); + it != mainclients.end(); + ++it ) + if( (*it)->isShown( true )) + visible_parent = true; + if( !visible_parent ) + { + init_minimize = true; + demandAttention(); + } + } + + if( init_minimize ) + minimize( true ); // no animation + + // SELI this seems to be mainly for kstart and ksystraycmd + // probably should be replaced by something better + bool doNotShow = false; + if ( workspace()->isNotManaged( caption() ) ) + doNotShow = TRUE; + + // other settings from the previous session + if ( session ) + { + // session restored windows are not considered to be new windows WRT rules, + // i.e. obey only forcing rules + setKeepAbove( session->keepAbove ); + setKeepBelow( session->keepBelow ); + setSkipTaskbar( session->skipTaskbar, true ); + setSkipPager( session->skipPager ); + setShade( session->shaded ? ShadeNormal : ShadeNone ); + if( session->maximized != MaximizeRestore ) + { + maximize( (MaximizeMode) session->maximized ); + geom_restore = session->restore; + } + if( session->fullscreen == FullScreenHack ) + ; // nothing, this should be already set again above + else if( session->fullscreen != FullScreenNone ) + { + setFullScreen( true, false ); + geom_fs_restore = session->fsrestore; + } + } + else + { + geom_restore = geometry(); // remember restore geometry + if ( isMaximizable() + && ( width() >= area.width() || height() >= area.height() ) ) + { + // window is too large for the screen, maximize in the + // directions necessary + if ( width() >= area.width() && height() >= area.height() ) + { + maximize( Client::MaximizeFull ); + geom_restore = QRect(); // use placement when unmaximizing + } + else if ( width() >= area.width() ) + { + maximize( Client::MaximizeHorizontal ); + geom_restore = QRect(); // use placement when unmaximizing + geom_restore.setY( y()); // but only for horizontal direction + geom_restore.setHeight( height()); + } + else if ( height() >= area.height() ) + { + maximize( Client::MaximizeVertical ); + geom_restore = QRect(); // use placement when unmaximizing + geom_restore.setX( x()); // but only for vertical direction + geom_restore.setWidth( width()); + } + } + // window may want to be maximized + // done after checking that the window isn't larger than the workarea, so that + // the restore geometry from the checks above takes precedence, and window + // isn't restored larger than the workarea + MaximizeMode maxmode = static_cast< MaximizeMode > + ((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 ) + | (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 )); + MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped ); + // either hints were set to maximize, or is forced to maximize, + // or is forced to non-maximize and hints were set to maximize + if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore ) + maximize( forced_maxmode ); + + // read other initial states + setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped )); + setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped )); + setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped )); + setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true ); + setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped )); + if( info->state() & NET::DemandsAttention ) + demandAttention(); + if( info->state() & NET::Modal ) + setModal( true ); + if( fullscreen_mode != FullScreenHack && isFullScreenable()) + setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false ); + } + + updateAllowedActions( true ); + + // TODO this should avoid flicker, because real restacking is done + // only after manage() finishes, but the window is shown sooner + // - keep it? + XLowerWindow( qt_xdisplay(), frameId()); + + // set initial user time directly + user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session ); + group()->updateUserTime( user_time ); // and do what Client::updateUserTime() does + + if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow + hideClient( true ); // is the active one + + if( isShown( true ) && !doNotShow ) + { + if( isDialog()) + Notify::raise( Notify::TransNew ); + if( isNormalWindow()) + Notify::raise( Notify::New ); + + bool allow; + if( session ) + allow = session->active + && ( !workspace()->wasUserInteraction() + || workspace()->activeClient() == NULL || workspace()->activeClient()->isDesktop()); + else + allow = workspace()->allowClientActivation( this, userTime(), false ); + + // if session saving, force showing new windows (i.e. "save file?" dialogs etc.) + // also force if activation is allowed + if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving())) + workspace()->setCurrentDesktop( desktop()); + + bool belongs_to_desktop = false; + for( ClientList::ConstIterator it = group()->members().begin(); + it != group()->members().end(); + ++it ) + if( (*it)->isDesktop()) + { + belongs_to_desktop = true; + break; + } + if( !belongs_to_desktop && workspace()->showingDesktop()) + workspace()->resetShowingDesktop( options->showDesktopIsMinimizeAll ); + + if( isOnCurrentDesktop() && !isMapped && !allow ) + workspace()->restackClientUnderActive( this ); + else + workspace()->raiseClient( this ); + + updateVisibility(); + + if( !isMapped ) + { + if( allow && isOnCurrentDesktop()) + { + if( !isSpecialWindow()) + if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) + workspace()->requestFocus( this ); + } + else + { + if( !session && !isSpecialWindow()) + demandAttention(); + } + } + } + else if( !doNotShow ) // if( !isShown( true ) && !doNotShow ) + { + updateVisibility(); + } + else // doNotShow + { // SELI HACK !!! + hideClient( true ); + setMappingState( IconicState ); + } + assert( mappingState() != WithdrawnState ); + + if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old + { + user_time = qt_x_time - 1000000; + if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid + user_time = qt_x_time - 1000000 + 10; + } + + updateWorkareaDiffs(); + +// sendSyntheticConfigureNotify(); done when setting mapping state + + delete session; + + ungrabXServer(); + + client_rules.discardTemporary(); + applyWindowRules(); // just in case + workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules + updateWindowRules(); // was blocked while !isManaged() + +// TODO there's a small problem here - isManaged() depends on the mapping state, +// but this client is not yet in Workspace's client list at this point, will +// be only done in addClient() + return true; + } + +// called only from manage() +void Client::embedClient( Window w, const XWindowAttributes &attr ) + { + assert( client == None ); + assert( frame == None ); + assert( wrapper == None ); + client = w; + // we don't want the window to be destroyed when we are destroyed + XAddToSaveSet( qt_xdisplay(), client ); + XSelectInput( qt_xdisplay(), client, NoEventMask ); + XUnmapWindow( qt_xdisplay(), client ); + XWindowChanges wc; // set the border width to 0 + wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window + XConfigureWindow( qt_xdisplay(), client, CWBorderWidth, &wc ); + + XSetWindowAttributes swa; + swa.colormap = attr.colormap; + swa.background_pixmap = None; + swa.border_pixel = 0; + + frame = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + attr.depth, InputOutput, attr.visual, + CWColormap | CWBackPixmap | CWBorderPixel, &swa ); + wrapper = XCreateWindow( qt_xdisplay(), frame, 0, 0, 1, 1, 0, + attr.depth, InputOutput, attr.visual, + CWColormap | CWBackPixmap | CWBorderPixel, &swa ); + + XDefineCursor( qt_xdisplay(), frame, arrowCursor.handle()); + // some apps are stupid and don't define their own cursor - set the arrow one for them + XDefineCursor( qt_xdisplay(), wrapper, arrowCursor.handle()); + XReparentWindow( qt_xdisplay(), client, wrapper, 0, 0 ); + XSelectInput( qt_xdisplay(), frame, + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | + ButtonMotionMask | + PointerMotionMask | + EnterWindowMask | LeaveWindowMask | + FocusChangeMask | + ExposureMask | + PropertyChangeMask | + StructureNotifyMask | SubstructureRedirectMask ); + XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); + XSelectInput( qt_xdisplay(), client, + FocusChangeMask | + PropertyChangeMask | + ColormapChangeMask | + EnterWindowMask | LeaveWindowMask | + KeyPressMask | KeyReleaseMask + ); + updateMouseGrab(); + } + +} // namespace diff --git a/kwin/notifications.cpp b/kwin/notifications.cpp new file mode 100644 index 000000000..a2d72d697 --- /dev/null +++ b/kwin/notifications.cpp @@ -0,0 +1,147 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "notifications.h" +#include <knotifyclient.h> + +#include "client.h" + +namespace KWinInternal +{ + +QString Notify::eventToName( Event e ) + { + QString event; + switch ( e ) + { + case Activate: + event = "activate"; + break; + case Close: + event = "close"; + break; + case Minimize: + event = "minimize"; + break; + case UnMinimize: + event = "unminimize"; + break; + case Maximize: + event = "maximize"; + break; + case UnMaximize: + event = "unmaximize"; + break; + case OnAllDesktops: + event = "on_all_desktops"; + break; + case NotOnAllDesktops: + event = "not_on_all_desktops"; + break; + case New: + event = "new"; + break; + case Delete: + event = "delete"; + break; + case TransNew: + event = "transnew"; + break; + case TransDelete: + event = "transdelete"; + break; + case ShadeUp: + event = "shadeup"; + break; + case ShadeDown: + event = "shadedown"; + break; + case MoveStart: + event = "movestart"; + break; + case MoveEnd: + event = "moveend"; + break; + case ResizeStart: + event = "resizestart"; + break; + case ResizeEnd: + event = "resizeend"; + break; + case DemandAttentionCurrent: + event = "demandsattentioncurrent"; + break; + case DemandAttentionOther: + event = "demandsattentionother"; + break; + default: + if ((e > DesktopChange) && (e <= DesktopChange+20)) + { + event = QString("desktop%1").arg(e-DesktopChange); + } + break; + } + return event; + } + +static bool forgetIt = FALSE; +QValueList< Notify::EventData > Notify::pending_events; + +bool Notify::raise( Event e, const QString& message, Client* c ) + { + if ( forgetIt ) + return false; // no connection was possible, don't try each time + + QString event = eventToName( e ); + if ( !event ) + return false; + +// There may be a deadlock if KNotify event is sent while KWin has X grabbed. +// If KNotify is not running, KLauncher may do X requests (startup notification, whatever) +// that will block it. And KNotifyClient waits for the launch to succeed, which means +// KLauncher waits for X and KWin waits for KLauncher. So postpone events in such case. + if( grabbedXServer()) + { + EventData data; + data.event = event; + data.message = message; + data.window = c ? c->window() : 0; + pending_events.append( data ); + return true; + } + + forgetIt= !KNotifyClient::event( c ? c->window() : 0, event, message ); + return !forgetIt; + } + +void Notify::sendPendingEvents() + { + while( !pending_events.isEmpty()) + { + EventData data = pending_events.first(); + pending_events.pop_front(); + if( !forgetIt ) + forgetIt= !KNotifyClient::event( data.window, data.event, data.message ); + } + } + +bool Notify::makeDemandAttention( Event e ) + { + QString event = eventToName( e ); + if( !event ) + return false; + int rep = KNotifyClient::getPresentation( event ); + if( rep == -1 ) + rep = KNotifyClient::getDefaultPresentation( event ); + return rep != -1 && ( rep & KNotifyClient::Taskbar ); + } + +} // namespace diff --git a/kwin/notifications.h b/kwin/notifications.h new file mode 100644 index 000000000..dd7c79323 --- /dev/null +++ b/kwin/notifications.h @@ -0,0 +1,69 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_NOTIFICATIONS_H +#define KWIN_NOTIFICATIONS_H + +#include <stdlib.h> +#include <qstring.h> +#include <qvaluelist.h> + +namespace KWinInternal +{ + +class Client; + +class Notify + { + public: + + enum Event + { + Activate, + Close, + Minimize, + UnMinimize, + Maximize, + UnMaximize, + OnAllDesktops, + NotOnAllDesktops, + New, + Delete, + TransNew, + TransDelete, + ShadeUp, + ShadeDown, + MoveStart, + MoveEnd, + ResizeStart, + ResizeEnd, + DemandAttentionCurrent, + DemandAttentionOther, + DesktopChange = 100 + }; + + static bool raise( Event, const QString& message = QString::null, Client* c = NULL ); + static bool makeDemandAttention( Event ); + static void sendPendingEvents(); + private: + static QString eventToName( Event ); + struct EventData + { + QString event; + QString message; + long window; + }; + static QValueList< EventData > pending_events; + }; + +} // namespace + +#endif diff --git a/kwin/oldheaders/Makefile.am b/kwin/oldheaders/Makefile.am new file mode 100644 index 000000000..2dd570c5f --- /dev/null +++ b/kwin/oldheaders/Makefile.am @@ -0,0 +1,2 @@ +kwin_kwinincludedir = $(includedir)/kwin +kwin_kwininclude_HEADERS = options.h client.h workspace.h kwinbutton.h diff --git a/kwin/oldheaders/client.h b/kwin/oldheaders/client.h new file mode 100644 index 000000000..89db145b2 --- /dev/null +++ b/kwin/oldheaders/client.h @@ -0,0 +1,4 @@ +#error The KWin decorations API provided by this header file is obsolete. +#error All code using it will have to be ported to the new KDecoration API. + +// See kdebase/kwin/clients/PORTING . diff --git a/kwin/oldheaders/kwinbutton.h b/kwin/oldheaders/kwinbutton.h new file mode 100644 index 000000000..89db145b2 --- /dev/null +++ b/kwin/oldheaders/kwinbutton.h @@ -0,0 +1,4 @@ +#error The KWin decorations API provided by this header file is obsolete. +#error All code using it will have to be ported to the new KDecoration API. + +// See kdebase/kwin/clients/PORTING . diff --git a/kwin/oldheaders/options.h b/kwin/oldheaders/options.h new file mode 100644 index 000000000..89db145b2 --- /dev/null +++ b/kwin/oldheaders/options.h @@ -0,0 +1,4 @@ +#error The KWin decorations API provided by this header file is obsolete. +#error All code using it will have to be ported to the new KDecoration API. + +// See kdebase/kwin/clients/PORTING . diff --git a/kwin/oldheaders/workspace.h b/kwin/oldheaders/workspace.h new file mode 100644 index 000000000..89db145b2 --- /dev/null +++ b/kwin/oldheaders/workspace.h @@ -0,0 +1,4 @@ +#error The KWin decorations API provided by this header file is obsolete. +#error All code using it will have to be ported to the new KDecoration API. + +// See kdebase/kwin/clients/PORTING . diff --git a/kwin/options.cpp b/kwin/options.cpp new file mode 100644 index 000000000..690266fc9 --- /dev/null +++ b/kwin/options.cpp @@ -0,0 +1,331 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "options.h" + +#ifndef KCMRULES + +#include <qpalette.h> +#include <qpixmap.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <qtooltip.h> + +#include "client.h" + +#endif + +namespace KWinInternal +{ + +#ifndef KCMRULES + +Options::Options() + : electric_borders( 0 ), + electric_border_delay(0) + { + d = new KDecorationOptionsPrivate; + d->defaultKWinSettings(); + updateSettings(); + } + +Options::~Options() + { + delete d; + } + +unsigned long Options::updateSettings() + { + KConfig *config = KGlobal::config(); + unsigned long changed = 0; + changed |= d->updateKWinSettings( config ); // read decoration settings + + config->setGroup( "Windows" ); + moveMode = stringToMoveResizeMode( config->readEntry("MoveMode", "Opaque" )); + resizeMode = stringToMoveResizeMode( config->readEntry("ResizeMode", "Opaque" )); + show_geometry_tip = config->readBoolEntry("GeometryTip", false); + tabboxOutline = config->readBoolEntry("TabboxOutline", true); + + QString val; + + val = config->readEntry ("FocusPolicy", "ClickToFocus"); + focusPolicy = ClickToFocus; // what a default :-) + if ( val == "FocusFollowsMouse" ) + focusPolicy = FocusFollowsMouse; + else if ( val == "FocusUnderMouse" ) + focusPolicy = FocusUnderMouse; + else if ( val == "FocusStrictlyUnderMouse" ) + focusPolicy = FocusStrictlyUnderMouse; + + val = config->readEntry ("AltTabStyle", "KDE"); + altTabStyle = KDE; // what a default :-) + if ( val == "CDE" ) + altTabStyle = CDE; + + rollOverDesktops = config->readBoolEntry("RollOverDesktops", TRUE); + +// focusStealingPreventionLevel = config->readNumEntry( "FocusStealingPreventionLevel", 2 ); + // TODO use low level for now + focusStealingPreventionLevel = config->readNumEntry( "FocusStealingPreventionLevel", 1 ); + focusStealingPreventionLevel = KMAX( 0, KMIN( 4, focusStealingPreventionLevel )); + if( !focusPolicyIsReasonable()) // #48786, comments #7 and later + focusStealingPreventionLevel = 0; + + KConfig *gc = new KConfig("kdeglobals", false, false); + gc->setGroup("Windows"); + xineramaEnabled = gc->readBoolEntry ("XineramaEnabled", true ); + xineramaPlacementEnabled = gc->readBoolEntry ("XineramaPlacementEnabled", true); + xineramaMovementEnabled = gc->readBoolEntry ("XineramaMovementEnabled", true); + xineramaMaximizeEnabled = gc->readBoolEntry ("XineramaMaximizeEnabled", true); + xineramaFullscreenEnabled = gc->readBoolEntry ("XineramaFullscreenEnabled", true); + delete gc; + + placement = Placement::policyFromString( config->readEntry("Placement"), true ); + + animateShade = config->readBoolEntry("AnimateShade", TRUE ); + + animateMinimize = config->readBoolEntry("AnimateMinimize", TRUE ); + animateMinimizeSpeed = config->readNumEntry("AnimateMinimizeSpeed", 5 ); + + if( focusPolicy == ClickToFocus ) + { + autoRaise = false; + autoRaiseInterval = 0; + delayFocus = false; + delayFocusInterval = 0; + } + else + { + autoRaise = config->readBoolEntry("AutoRaise", FALSE ); + autoRaiseInterval = config->readNumEntry("AutoRaiseInterval", 0 ); + delayFocus = config->readBoolEntry("DelayFocus", FALSE ); + delayFocusInterval = config->readNumEntry("DelayFocusInterval", 0 ); + } + + shadeHover = config->readBoolEntry("ShadeHover", FALSE ); + shadeHoverInterval = config->readNumEntry("ShadeHoverInterval", 250 ); + + // important: autoRaise implies ClickRaise + clickRaise = autoRaise || config->readBoolEntry("ClickRaise", TRUE ); + + borderSnapZone = config->readNumEntry("BorderSnapZone", 10); + windowSnapZone = config->readNumEntry("WindowSnapZone", 10); + snapOnlyWhenOverlapping=config->readBoolEntry("SnapOnlyWhenOverlapping",FALSE); + electric_borders = config->readNumEntry("ElectricBorders", 0); + electric_border_delay = config->readNumEntry("ElectricBorderDelay", 150); + + OpTitlebarDblClick = windowOperation( config->readEntry("TitlebarDoubleClickCommand", "Shade"), true ); + d->OpMaxButtonLeftClick = windowOperation( config->readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true ); + d->OpMaxButtonMiddleClick = windowOperation( config->readEntry("MaximizeButtonMiddleClickCommand", "Maximize (vertical only)"), true ); + d->OpMaxButtonRightClick = windowOperation( config->readEntry("MaximizeButtonRightClickCommand", "Maximize (horizontal only)"), true ); + + ignorePositionClasses = config->readListEntry("IgnorePositionClasses"); + ignoreFocusStealingClasses = config->readListEntry("IgnoreFocusStealingClasses"); + // Qt3.2 and older had resource class all lowercase, but Qt3.3 has it capitalized + // therefore Client::resourceClass() forces lowercase, force here lowercase as well + for( QStringList::Iterator it = ignorePositionClasses.begin(); + it != ignorePositionClasses.end(); + ++it ) + (*it) = (*it).lower(); + for( QStringList::Iterator it = ignoreFocusStealingClasses.begin(); + it != ignoreFocusStealingClasses.end(); + ++it ) + (*it) = (*it).lower(); + + killPingTimeout = config->readNumEntry( "KillPingTimeout", 5000 ); + hideUtilityWindowsForInactive = config->readBoolEntry( "HideUtilityWindowsForInactive", true ); + showDesktopIsMinimizeAll = config->readBoolEntry( "ShowDesktopIsMinimizeAll", false ); + + // Mouse bindings + config->setGroup( "MouseBindings"); + CmdActiveTitlebar1 = mouseCommand(config->readEntry("CommandActiveTitlebar1","Raise"), true ); + CmdActiveTitlebar2 = mouseCommand(config->readEntry("CommandActiveTitlebar2","Lower"), true ); + CmdActiveTitlebar3 = mouseCommand(config->readEntry("CommandActiveTitlebar3","Operations menu"), true ); + CmdInactiveTitlebar1 = mouseCommand(config->readEntry("CommandInactiveTitlebar1","Activate and raise"), true ); + CmdInactiveTitlebar2 = mouseCommand(config->readEntry("CommandInactiveTitlebar2","Activate and lower"), true ); + CmdInactiveTitlebar3 = mouseCommand(config->readEntry("CommandInactiveTitlebar3","Operations menu"), true ); + CmdTitlebarWheel = mouseWheelCommand(config->readEntry("CommandTitlebarWheel","Nothing")); + CmdWindow1 = mouseCommand(config->readEntry("CommandWindow1","Activate, raise and pass click"), false ); + CmdWindow2 = mouseCommand(config->readEntry("CommandWindow2","Activate and pass click"), false ); + CmdWindow3 = mouseCommand(config->readEntry("CommandWindow3","Activate and pass click"), false ); + CmdAllModKey = (config->readEntry("CommandAllKey","Alt") == "Meta") ? Qt::Key_Meta : Qt::Key_Alt; + CmdAll1 = mouseCommand(config->readEntry("CommandAll1","Move"), false ); + CmdAll2 = mouseCommand(config->readEntry("CommandAll2","Toggle raise and lower"), false ); + CmdAll3 = mouseCommand(config->readEntry("CommandAll3","Resize"), false ); + CmdAllWheel = mouseWheelCommand(config->readEntry("CommandAllWheel","Nothing")); + + //translucency settings + config->setGroup( "Notification Messages" ); + useTranslucency = config->readBoolEntry("UseTranslucency", false); + config->setGroup( "Translucency"); + translucentActiveWindows = config->readBoolEntry("TranslucentActiveWindows", false); + activeWindowOpacity = uint((config->readNumEntry("ActiveWindowOpacity", 100)/100.0)*0xFFFFFFFF); + translucentInactiveWindows = config->readBoolEntry("TranslucentInactiveWindows", false); + inactiveWindowOpacity = uint((config->readNumEntry("InactiveWindowOpacity", 75)/100.0)*0xFFFFFFFF); + translucentMovingWindows = config->readBoolEntry("TranslucentMovingWindows", false); + movingWindowOpacity = uint((config->readNumEntry("MovingWindowOpacity", 50)/100.0)*0xFFFFFFFF); + translucentDocks = config->readBoolEntry("TranslucentDocks", false); + dockOpacity = uint((config->readNumEntry("DockOpacity", 80)/100.0)*0xFFFFFFFF); + keepAboveAsActive = config->readBoolEntry("TreatKeepAboveAsActive", true); + //TODO: remove this variable + useTitleMenuSlider = true; + activeWindowShadowSize = config->readNumEntry("ActiveWindowShadowSize", 200); + inactiveWindowShadowSize = config->readNumEntry("InactiveWindowShadowSize", 100); + dockShadowSize = config->readNumEntry("DockShadowSize", 80); + removeShadowsOnMove = config->readBoolEntry("RemoveShadowsOnMove", true); + removeShadowsOnResize = config->readBoolEntry("RemoveShadowsOnResize", true); + onlyDecoTranslucent = config->readBoolEntry("OnlyDecoTranslucent",false); + resetKompmgr = config->readBoolEntry("ResetKompmgr", false); + if (resetKompmgr) + config->writeEntry("ResetKompmgr",FALSE); + + + + // Read button tooltip animation effect from kdeglobals + // Since we want to allow users to enable window decoration tooltips + // and not kstyle tooltips and vise-versa, we don't read the + // "EffectNoTooltip" setting from kdeglobals. + KConfig globalConfig("kdeglobals"); + globalConfig.setGroup("KDE"); + topmenus = globalConfig.readBoolEntry( "macStyle", false ); + + KConfig kdesktopcfg( "kdesktoprc", true ); + kdesktopcfg.setGroup( "Menubar" ); + desktop_topmenu = kdesktopcfg.readBoolEntry( "ShowMenubar", false ); + if( desktop_topmenu ) + topmenus = true; + + QToolTip::setGloballyEnabled( d->show_tooltips ); + + return changed; + } + + +// restricted should be true for operations that the user may not be able to repeat +// if the window is moved out of the workspace (e.g. if the user moves a window +// by the titlebar, and moves it too high beneath Kicker at the top edge, they +// may not be able to move it back, unless they know about Alt+LMB) +Options::WindowOperation Options::windowOperation(const QString &name, bool restricted ) + { + if (name == "Move") + return restricted ? MoveOp : UnrestrictedMoveOp; + else if (name == "Resize") + return restricted ? ResizeOp : UnrestrictedResizeOp; + else if (name == "Maximize") + return MaximizeOp; + else if (name == "Minimize") + return MinimizeOp; + else if (name == "Close") + return CloseOp; + else if (name == "OnAllDesktops") + return OnAllDesktopsOp; + else if (name == "Shade") + return ShadeOp; + else if (name == "Operations") + return OperationsOp; + else if (name == "Maximize (vertical only)") + return VMaximizeOp; + else if (name == "Maximize (horizontal only)") + return HMaximizeOp; + else if (name == "Lower") + return LowerOp; + return NoOp; + } + +Options::MouseCommand Options::mouseCommand(const QString &name, bool restricted ) + { + QString lowerName = name.lower(); + if (lowerName == "raise") return MouseRaise; + if (lowerName == "lower") return MouseLower; + if (lowerName == "operations menu") return MouseOperationsMenu; + if (lowerName == "toggle raise and lower") return MouseToggleRaiseAndLower; + if (lowerName == "activate and raise") return MouseActivateAndRaise; + if (lowerName == "activate and lower") return MouseActivateAndLower; + if (lowerName == "activate") return MouseActivate; + if (lowerName == "activate, raise and pass click") return MouseActivateRaiseAndPassClick; + if (lowerName == "activate and pass click") return MouseActivateAndPassClick; + if (lowerName == "activate, raise and move") + return restricted ? MouseActivateRaiseAndMove : MouseActivateRaiseAndUnrestrictedMove; + if (lowerName == "move") return restricted ? MouseMove : MouseUnrestrictedMove; + if (lowerName == "resize") return restricted ? MouseResize : MouseUnrestrictedResize; + if (lowerName == "shade") return MouseShade; + if (lowerName == "minimize") return MouseMinimize; + if (lowerName == "nothing") return MouseNothing; + return MouseNothing; + } + +Options::MouseWheelCommand Options::mouseWheelCommand(const QString &name) + { + QString lowerName = name.lower(); + if (lowerName == "raise/lower") return MouseWheelRaiseLower; + if (lowerName == "shade/unshade") return MouseWheelShadeUnshade; + if (lowerName == "maximize/restore") return MouseWheelMaximizeRestore; + if (lowerName == "above/below") return MouseWheelAboveBelow; + if (lowerName == "previous/next desktop") return MouseWheelPreviousNextDesktop; + if (lowerName == "change opacity") return MouseWheelChangeOpacity; + return MouseWheelNothing; + } + +bool Options::showGeometryTip() + { + return show_geometry_tip; + } + +int Options::electricBorders() + { + return electric_borders; + } + +int Options::electricBorderDelay() + { + return electric_border_delay; + } + +bool Options::checkIgnoreFocusStealing( const Client* c ) + { + return ignoreFocusStealingClasses.contains(QString::fromLatin1(c->resourceClass())); + } + +Options::MouseCommand Options::wheelToMouseCommand( MouseWheelCommand com, int delta ) + { + switch( com ) + { + case MouseWheelRaiseLower: + return delta > 0 ? MouseRaise : MouseLower; + case MouseWheelShadeUnshade: + return delta > 0 ? MouseSetShade : MouseUnsetShade; + case MouseWheelMaximizeRestore: + return delta > 0 ? MouseMaximize : MouseRestore; + case MouseWheelAboveBelow: + return delta > 0 ? MouseAbove : MouseBelow; + case MouseWheelPreviousNextDesktop: + return delta > 0 ? MousePreviousDesktop : MouseNextDesktop; + case MouseWheelChangeOpacity: + return delta > 0 ? MouseOpacityMore : MouseOpacityLess; + default: + return MouseNothing; + } + } +#endif + +Options::MoveResizeMode Options::stringToMoveResizeMode( const QString& s ) + { + return s == "Opaque" ? Opaque : Transparent; + } + +const char* Options::moveResizeModeToString( MoveResizeMode mode ) + { + return mode == Opaque ? "Opaque" : "Transparent"; + } + +} // namespace diff --git a/kwin/options.h b/kwin/options.h new file mode 100644 index 000000000..59279fe80 --- /dev/null +++ b/kwin/options.h @@ -0,0 +1,341 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_OPTIONS_H +#define KWIN_OPTIONS_H + +#include <qobject.h> +#include <qfont.h> +#include <qpalette.h> +#include <qstringlist.h> +#include <kdecoration_p.h> + +#include "placement.h" + +namespace KWinInternal +{ + +class Client; + +class Options : public KDecorationOptions + { + public: + + Options(); + ~Options(); + + virtual unsigned long updateSettings(); + + /*! + Different focus policies: + <ul> + + <li>ClickToFocus - Clicking into a window activates it. This is + also the default. + + <li>FocusFollowsMouse - Moving the mouse pointer actively onto a + normal window activates it. For convenience, the desktop and + windows on the dock are excluded. They require clicking. + + <li>FocusUnderMouse - The window that happens to be under the + mouse pointer becomes active. The invariant is: no window can + have focus that is not under the mouse. This also means that + Alt-Tab won't work properly and popup dialogs are usually + unsable with the keyboard. Note that the desktop and windows on + the dock are excluded for convenience. They get focus only when + clicking on it. + + <li>FocusStrictlyUnderMouse - this is even worse than + FocusUnderMouse. Only the window under the mouse pointer is + active. If the mouse points nowhere, nothing has the focus. If + the mouse points onto the desktop, the desktop has focus. The + same holds for windows on the dock. + + Note that FocusUnderMouse and FocusStrictlyUnderMouse are not + particulary useful. They are only provided for old-fashined + die-hard UNIX people ;-) + + </ul> + */ + enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse }; + FocusPolicy focusPolicy; + + + /** + Whether clicking on a window raises it in FocusFollowsMouse + mode or not. + */ + bool clickRaise; + + /** + whether autoraise is enabled FocusFollowsMouse mode or not. + */ + bool autoRaise; + + /** + autoraise interval + */ + int autoRaiseInterval; + + /** + whether delay focus is enabled or not. + */ + bool delayFocus; + + /** + delayed focus interval + */ + int delayFocusInterval; + + /** + Whether shade hover is enabled or not + */ + bool shadeHover; + + /** + shade hover interval + */ + int shadeHoverInterval; + + /** + Different Alt-Tab-Styles: + <ul> + + <li> KDE - the recommended KDE style. Alt-Tab opens a nice icon + box that makes it easy to select the window you want to tab + to. The order automatically adjusts to the most recently used + windows. Note that KDE style does not work with the + FocusUnderMouse and FocusStrictlyUnderMouse focus + policies. Choose ClickToFocus or FocusFollowsMouse instead. + + <li> CDE - the old-fashion CDE style. Alt-Tab cycles between + the windows in static order. The current window gets raised, + the previous window gets lowered. + + </ul> + */ + enum AltTabStyle { KDE, CDE }; + AltTabStyle altTabStyle; + + /** + * Xinerama options + */ + bool xineramaEnabled; + bool xineramaPlacementEnabled; + bool xineramaMovementEnabled; + bool xineramaMaximizeEnabled; + bool xineramaFullscreenEnabled; + + /** + MoveResizeMode, either Tranparent or Opaque. + */ + enum MoveResizeMode { Transparent, Opaque }; + + MoveResizeMode resizeMode; + MoveResizeMode moveMode; + + static MoveResizeMode stringToMoveResizeMode( const QString& s ); + static const char* moveResizeModeToString( MoveResizeMode mode ); + + Placement::Policy placement; + + bool focusPolicyIsReasonable() + { + return focusPolicy == ClickToFocus || focusPolicy == FocusFollowsMouse; + } + + /** + * whether we animate the shading of windows to titlebar or not + */ + bool animateShade; + + /** + * the size of the zone that triggers snapping on desktop borders + */ + int borderSnapZone; + + /** + * the number of animation steps (would this be general?) + */ + int windowSnapZone; + + + /** + * snap only when windows will overlap + */ + bool snapOnlyWhenOverlapping; + + /** + * whether we animate the minimization of windows or not + */ + bool animateMinimize; + + /** + * Animation speed (0 .. 10 ) + */ + int animateMinimizeSpeed; + + bool showDesktopIsMinimizeAll; + + /** + * whether or not we roll over to the other edge when switching desktops past the edge + */ + bool rollOverDesktops; + + // 0 - 4 , see Workspace::allowClientActivation() + int focusStealingPreventionLevel; + + /** + * List of window classes to ignore PPosition size hint + */ + QStringList ignorePositionClasses; + + bool checkIgnoreFocusStealing( const Client* c ); + + WindowOperation operationTitlebarDblClick() { return OpTitlebarDblClick; } + + enum MouseCommand + { + MouseRaise, MouseLower, MouseOperationsMenu, MouseToggleRaiseAndLower, + MouseActivateAndRaise, MouseActivateAndLower, MouseActivate, + MouseActivateRaiseAndPassClick, MouseActivateAndPassClick, + MouseMove, MouseUnrestrictedMove, + MouseActivateRaiseAndMove, MouseActivateRaiseAndUnrestrictedMove, + MouseResize, MouseUnrestrictedResize, + MouseShade, MouseSetShade, MouseUnsetShade, + MouseMaximize, MouseRestore, MouseMinimize, + MouseNextDesktop, MousePreviousDesktop, + MouseAbove, MouseBelow, + MouseOpacityMore, MouseOpacityLess, + MouseNothing + }; + + enum MouseWheelCommand + { + MouseWheelRaiseLower, MouseWheelShadeUnshade, MouseWheelMaximizeRestore, + MouseWheelAboveBelow, MouseWheelPreviousNextDesktop, + MouseWheelChangeOpacity, + MouseWheelNothing + }; + + MouseCommand operationTitlebarMouseWheel( int delta ) + { + return wheelToMouseCommand( CmdTitlebarWheel, delta ); + } + MouseCommand operationWindowMouseWheel( int delta ) + { + return wheelToMouseCommand( CmdAllWheel, delta ); + } + + MouseCommand commandActiveTitlebar1() { return CmdActiveTitlebar1; } + MouseCommand commandActiveTitlebar2() { return CmdActiveTitlebar2; } + MouseCommand commandActiveTitlebar3() { return CmdActiveTitlebar3; } + MouseCommand commandInactiveTitlebar1() { return CmdInactiveTitlebar1; } + MouseCommand commandInactiveTitlebar2() { return CmdInactiveTitlebar2; } + MouseCommand commandInactiveTitlebar3() { return CmdInactiveTitlebar3; } + MouseCommand commandWindow1() { return CmdWindow1; } + MouseCommand commandWindow2() { return CmdWindow2; } + MouseCommand commandWindow3() { return CmdWindow3; } + MouseCommand commandAll1() { return CmdAll1; } + MouseCommand commandAll2() { return CmdAll2; } + MouseCommand commandAll3() { return CmdAll3; } + uint keyCmdAllModKey() { return CmdAllModKey; } + + + static WindowOperation windowOperation(const QString &name, bool restricted ); + static MouseCommand mouseCommand(const QString &name, bool restricted ); + static MouseWheelCommand mouseWheelCommand(const QString &name); + + /** + * @returns true if the Geometry Tip should be shown during a window move/resize. + * @since 3.2 + */ + bool showGeometryTip(); + + enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; + /** + * @returns true if electric borders are enabled. With electric borders + * you can change desktop by moving the mouse pointer towards the edge + * of the screen + */ + int electricBorders(); + + /** + * @returns the activation delay for electric borders in milliseconds. + */ + int electricBorderDelay(); + + bool topMenuEnabled() const { return topmenus; } + bool desktopTopMenu() const { return desktop_topmenu; } + + // timeout before non-responding application will be killed after attempt to close + int killPingTimeout; + + // Whether to hide utility windows for inactive applications. + bool hideUtilityWindowsForInactive; + + //translucency settings + bool useTranslucency; + bool translucentActiveWindows; + uint activeWindowOpacity; + bool translucentInactiveWindows; + uint inactiveWindowOpacity; + bool translucentMovingWindows; + uint movingWindowOpacity; + bool removeShadowsOnResize; + bool removeShadowsOnMove; + bool translucentDocks; + uint dockOpacity; + bool keepAboveAsActive; + bool useTitleMenuSlider; + uint activeWindowShadowSize; + uint inactiveWindowShadowSize; + uint dockShadowSize; + bool onlyDecoTranslucent; + bool resetKompmgr; + bool tabboxOutline; + + private: + WindowOperation OpTitlebarDblClick; + + // mouse bindings + MouseCommand CmdActiveTitlebar1; + MouseCommand CmdActiveTitlebar2; + MouseCommand CmdActiveTitlebar3; + MouseCommand CmdInactiveTitlebar1; + MouseCommand CmdInactiveTitlebar2; + MouseCommand CmdInactiveTitlebar3; + MouseWheelCommand CmdTitlebarWheel; + MouseCommand CmdWindow1; + MouseCommand CmdWindow2; + MouseCommand CmdWindow3; + MouseCommand CmdAll1; + MouseCommand CmdAll2; + MouseCommand CmdAll3; + MouseWheelCommand CmdAllWheel; + uint CmdAllModKey; + + int electric_borders; + int electric_border_delay; + bool show_geometry_tip; + bool topmenus; + bool desktop_topmenu; + // List of window classes for which not to use focus stealing prevention + QStringList ignoreFocusStealingClasses; + + MouseCommand wheelToMouseCommand( MouseWheelCommand com, int delta ); + }; + +extern Options* options; + +} // namespace + +#endif diff --git a/kwin/pics/Makefile.am b/kwin/pics/Makefile.am new file mode 100644 index 000000000..8558a612c --- /dev/null +++ b/kwin/pics/Makefile.am @@ -0,0 +1,9 @@ + +kwin_pics_data_DATA = close.png maximize.png maximizedown.png menu.png \ + iconify.png pindown.png pinup.png unknown.png fog.png \ + fog-grey.png bluesun.png greenie.light.png greenie.dim.png + +kwin_pics_datadir = $(kde_datadir)/kwin/pics + +EXTRA_DIST = $(kwin_pics_data_DATA) + diff --git a/kwin/pics/bluesun.png b/kwin/pics/bluesun.png Binary files differnew file mode 100644 index 000000000..1e7bdeb2b --- /dev/null +++ b/kwin/pics/bluesun.png diff --git a/kwin/pics/close.png b/kwin/pics/close.png Binary files differnew file mode 100644 index 000000000..d8fd4a3bb --- /dev/null +++ b/kwin/pics/close.png diff --git a/kwin/pics/fog-grey.png b/kwin/pics/fog-grey.png Binary files differnew file mode 100644 index 000000000..94f0e66dd --- /dev/null +++ b/kwin/pics/fog-grey.png diff --git a/kwin/pics/fog.png b/kwin/pics/fog.png Binary files differnew file mode 100644 index 000000000..349c49e5e --- /dev/null +++ b/kwin/pics/fog.png diff --git a/kwin/pics/greenie.dim.png b/kwin/pics/greenie.dim.png Binary files differnew file mode 100644 index 000000000..c2dc16837 --- /dev/null +++ b/kwin/pics/greenie.dim.png diff --git a/kwin/pics/greenie.light.png b/kwin/pics/greenie.light.png Binary files differnew file mode 100644 index 000000000..49d46b326 --- /dev/null +++ b/kwin/pics/greenie.light.png diff --git a/kwin/pics/iconify.png b/kwin/pics/iconify.png Binary files differnew file mode 100644 index 000000000..d9bd05501 --- /dev/null +++ b/kwin/pics/iconify.png diff --git a/kwin/pics/maximize.png b/kwin/pics/maximize.png Binary files differnew file mode 100644 index 000000000..07471bf21 --- /dev/null +++ b/kwin/pics/maximize.png diff --git a/kwin/pics/maximizedown.png b/kwin/pics/maximizedown.png Binary files differnew file mode 100644 index 000000000..3ef359d7c --- /dev/null +++ b/kwin/pics/maximizedown.png diff --git a/kwin/pics/menu.png b/kwin/pics/menu.png Binary files differnew file mode 100644 index 000000000..4c8a9fffd --- /dev/null +++ b/kwin/pics/menu.png diff --git a/kwin/pics/pindown.png b/kwin/pics/pindown.png Binary files differnew file mode 100644 index 000000000..20be0650a --- /dev/null +++ b/kwin/pics/pindown.png diff --git a/kwin/pics/pinup.png b/kwin/pics/pinup.png Binary files differnew file mode 100644 index 000000000..e2256b276 --- /dev/null +++ b/kwin/pics/pinup.png diff --git a/kwin/pics/unknown.png b/kwin/pics/unknown.png Binary files differnew file mode 100644 index 000000000..b52168b82 --- /dev/null +++ b/kwin/pics/unknown.png diff --git a/kwin/placement.cpp b/kwin/placement.cpp new file mode 100644 index 000000000..223b95c5b --- /dev/null +++ b/kwin/placement.cpp @@ -0,0 +1,812 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 1997 to 2002 Cristian Tibirna <tibirna@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "placement.h" + +#include <qrect.h> +#include <assert.h> + +#ifndef KCMRULES +#include "workspace.h" +#include "client.h" +#include "options.h" +#include "rules.h" +#endif + +namespace KWinInternal +{ + +#ifndef KCMRULES + +Placement::Placement(Workspace* w) + { + m_WorkspacePtr = w; + + reinitCascading( 0 ); + } + +/*! + Places the client \a c according to the workspace's layout policy + */ +void Placement::place(Client* c, QRect& area ) + { + Policy policy = c->rules()->checkPlacement( Default ); + if( policy != Default ) + { + place( c, area, policy ); + return; + } + + if( c->isUtility()) + placeUtility(c, area, options->placement ); + else if( c->isDialog()) + placeDialog(c, area, options->placement ); + else if( c->isSplash()) + placeOnMainWindow( c, area ); // on mainwindow, if any, otherwise centered + else + place(c, area, options->placement); + } + +void Placement::place(Client* c, QRect& area, Policy policy, Policy nextPlacement ) + { + if( policy == Unknown ) + policy = Default; + if( policy == Default ) + policy = options->placement; + if( policy == NoPlacement ) + return; + else if (policy == Random) + placeAtRandom(c, area, nextPlacement); + else if (policy == Cascade) + placeCascaded(c, area, nextPlacement); + else if (policy == Centered) + placeCentered(c, area, nextPlacement); + else if (policy == ZeroCornered) + placeZeroCornered(c, area, nextPlacement); + else if (policy == UnderMouse) + placeUnderMouse(c, area, nextPlacement); + else if (policy == OnMainWindow) + placeOnMainWindow(c, area, nextPlacement); + else if( policy == Maximizing ) + placeMaximizing(c, area, nextPlacement); + else + placeSmart(c, area, nextPlacement); + } + +/*! + Place the client \a c according to a simply "random" placement algorithm. + */ +void Placement::placeAtRandom(Client* c, const QRect& area, Policy /*next*/ ) + { + const int step = 24; + static int px = step; + static int py = 2 * step; + int tx,ty; + + const QRect maxRect = checkArea( c, area ); + + if (px < maxRect.x()) + px = maxRect.x(); + if (py < maxRect.y()) + py = maxRect.y(); + + px += step; + py += 2*step; + + if (px > maxRect.width()/2) + px = maxRect.x() + step; + if (py > maxRect.height()/2) + py = maxRect.y() + step; + tx = px; + ty = py; + if (tx + c->width() > maxRect.right()) + { + tx = maxRect.right() - c->width(); + if (tx < 0) + tx = 0; + px = maxRect.x(); + } + if (ty + c->height() > maxRect.bottom()) + { + ty = maxRect.bottom() - c->height(); + if (ty < 0) + ty = 0; + py = maxRect.y(); + } + c->move(tx, ty); + } + +/*! + Place the client \a c according to a really smart placement algorithm :-) +*/ +void Placement::placeSmart(Client* c, const QRect& area, Policy /*next*/ ) + { + /* + * SmartPlacement by Cristian Tibirna (tibirna@kde.org) + * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with + * permission) ideas from fvwm, authored by + * Anthony Martin (amartin@engr.csulb.edu). + * Xinerama supported added by Balaji Ramani (balaji@yablibli.com) + * with ideas from xfce. + */ + + const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types + long int overlap, min_overlap = 0; + int x_optimal, y_optimal; + int possible; + int desktop = c->desktop() == 0 || c->isOnAllDesktops() ? m_WorkspacePtr->currentDesktop() : c->desktop(); + + int cxl, cxr, cyt, cyb; //temp coords + int xl, xr, yt, yb; //temp coords + int basket; //temp holder + + // get the maximum allowed windows space + const QRect maxRect = checkArea( c, area ); + int x = maxRect.left(), y = maxRect.top(); + x_optimal = x; y_optimal = y; + + //client gabarit + int ch = c->height() - 1; + int cw = c->width() - 1; + + bool first_pass = true; //CT lame flag. Don't like it. What else would do? + + //loop over possible positions + do + { + //test if enough room in x and y directions + if (y + ch > maxRect.bottom() && ch < maxRect.height()) + overlap = h_wrong; // this throws the algorithm to an exit + else if(x + cw > maxRect.right()) + overlap = w_wrong; + else + { + overlap = none; //initialize + + cxl = x; cxr = x + cw; + cyt = y; cyb = y + ch; + ClientList::ConstIterator l; + for(l = m_WorkspacePtr->stackingOrder().begin(); l != m_WorkspacePtr->stackingOrder().end() ; ++l) + { + if((*l)->isOnDesktop(desktop) && + (*l)->isShown( false ) && (*l) != c) + { + + xl = (*l)->x(); yt = (*l)->y(); + xr = xl + (*l)->width(); yb = yt + (*l)->height(); + + //if windows overlap, calc the overall overlapping + if((cxl < xr) && (cxr > xl) && + (cyt < yb) && (cyb > yt)) + { + xl = QMAX(cxl, xl); xr = QMIN(cxr, xr); + yt = QMAX(cyt, yt); yb = QMIN(cyb, yb); + if((*l)->keepAbove()) + overlap += 16 * (xr - xl) * (yb - yt); + else if((*l)->keepBelow() && !(*l)->isDock()) // ignore KeepBelow windows + overlap += 0; // for placement (see Client::belongsToLayer() for Dock) + else + overlap += (xr - xl) * (yb - yt); + } + } + } + } + + //CT first time we get no overlap we stop. + if (overlap == none) + { + x_optimal = x; + y_optimal = y; + break; + } + + if (first_pass) + { + first_pass = false; + min_overlap = overlap; + } + //CT save the best position and the minimum overlap up to now + else if (overlap >= none && overlap < min_overlap) + { + min_overlap = overlap; + x_optimal = x; + y_optimal = y; + } + + // really need to loop? test if there's any overlap + if (overlap > none) + { + + possible = maxRect.right(); + if (possible - cw > x) possible -= cw; + + // compare to the position of each client on the same desk + ClientList::ConstIterator l; + for(l = m_WorkspacePtr->stackingOrder().begin(); l != m_WorkspacePtr->stackingOrder().end() ; ++l) + { + + if ((*l)->isOnDesktop(desktop) && + (*l)->isShown( false ) && (*l) != c) + { + + xl = (*l)->x(); yt = (*l)->y(); + xr = xl + (*l)->width(); yb = yt + (*l)->height(); + + // if not enough room above or under the current tested client + // determine the first non-overlapped x position + if((y < yb) && (yt < ch + y)) + { + + if((xr > x) && (possible > xr)) possible = xr; + + basket = xl - cw; + if((basket > x) && (possible > basket)) possible = basket; + } + } + } + x = possible; + } + + // ... else ==> not enough x dimension (overlap was wrong on horizontal) + else if (overlap == w_wrong) + { + x = maxRect.left(); + possible = maxRect.bottom(); + + if (possible - ch > y) possible -= ch; + + //test the position of each window on the desk + ClientList::ConstIterator l; + for(l = m_WorkspacePtr->stackingOrder().begin(); l != m_WorkspacePtr->stackingOrder().end() ; ++l) + { + if((*l)->isOnDesktop(desktop) && + (*l) != c && c->isShown( false )) + { + + xl = (*l)->x(); yt = (*l)->y(); + xr = xl + (*l)->width(); yb = yt + (*l)->height(); + + // if not enough room to the left or right of the current tested client + // determine the first non-overlapped y position + if((yb > y) && (possible > yb)) possible = yb; + + basket = yt - ch; + if((basket > y) && (possible > basket)) possible = basket; + } + } + y = possible; + } + } + while((overlap != none) && (overlap != h_wrong) && (y < maxRect.bottom())); + + if(ch>= maxRect.height()) + y_optimal=maxRect.top(); + + // place the window + c->move(x_optimal, y_optimal); + + } + +void Placement::reinitCascading( int desktop ) + { // desktop == 0 - reinit all + if( desktop == 0 ) + { + cci.clear(); + for( int i = 0; i < m_WorkspacePtr->numberOfDesktops(); i++) + { + DesktopCascadingInfo inf; + inf.pos = QPoint(-1,-1); + inf.col = 0; + inf.row = 0; + cci.append(inf); + } + } + else + { + cci[desktop - 1].pos = QPoint(-1, -1); + cci[desktop - 1].col = cci[desktop - 1].row = 0; + } + } + +/*! + Place windows in a cascading order, remembering positions for each desktop +*/ +void Placement::placeCascaded (Client* c, QRect& area, Policy nextPlacement) + { + /* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98) + */ + // work coords + int xp, yp; + + //CT how do I get from the 'Client' class the size that NW squarish "handle" + const int delta_x = 24; + const int delta_y = 24; + + const int dn = c->desktop() == 0 || c->isOnAllDesktops() ? (m_WorkspacePtr->currentDesktop() - 1) : (c->desktop() - 1); + + // get the maximum allowed windows space and desk's origin + QRect maxRect = checkArea( c, area ); + + // initialize often used vars: width and height of c; we gain speed + const int ch = c->height(); + const int cw = c->width(); + const int X = maxRect.left(); + const int Y = maxRect.top(); + const int H = maxRect.height(); + const int W = maxRect.width(); + + if( nextPlacement == Unknown ) + nextPlacement = Smart; + + //initialize if needed + if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < X || cci[dn].pos.y() < Y ) + { + cci[dn].pos = QPoint(X, Y); + cci[dn].col = cci[dn].row = 0; + } + + + xp = cci[dn].pos.x(); + yp = cci[dn].pos.y(); + + //here to touch in case people vote for resize on placement + if ((yp + ch) > H) yp = Y; + + if ((xp + cw) > W) + if (!yp) + { + place(c,area,nextPlacement); + return; + } + else xp = X; + + //if this isn't the first window + if (cci[dn].pos.x() != X && cci[dn].pos.y() != Y) + { + /* The following statements cause an internal compiler error with + * egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine. + * 22-Dec-1999 CS + * + * if (xp != X && yp == Y) xp = delta_x * (++(cci[dn].col)); + * if (yp != Y && xp == X) yp = delta_y * (++(cci[dn].row)); + */ + if (xp != X && yp == Y) + { + ++(cci[dn].col); + xp = delta_x * cci[dn].col; + } + if (yp != Y && xp == X) + { + ++(cci[dn].row); + yp = delta_y * cci[dn].row; + } + + // last resort: if still doesn't fit, smart place it + if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) + { + place(c,area,nextPlacement); + return; + } + } + + // place the window + c->move(QPoint(xp, yp)); + + // new position + cci[dn].pos = QPoint(xp + delta_x, yp + delta_y); + } + +/*! + Place windows centered, on top of all others +*/ +void Placement::placeCentered (Client* c, const QRect& area, Policy /*next*/ ) + { + + // get the maximum allowed windows space and desk's origin + const QRect maxRect = checkArea( c, area ); + + const int xp = maxRect.left() + (maxRect.width() - c->width()) / 2; + const int yp = maxRect.top() + (maxRect.height() - c->height()) / 2; + + // place the window + c->move(QPoint(xp, yp)); + } + +/*! + Place windows in the (0,0) corner, on top of all others +*/ +void Placement::placeZeroCornered(Client* c, const QRect& area, Policy /*next*/ ) + { + // get the maximum allowed windows space and desk's origin + const QRect maxRect = checkArea( c, area ); + + // place the window + c->move(QPoint(maxRect.left(), maxRect.top())); + } + +void Placement::placeUtility(Client* c, QRect& area, Policy /*next*/ ) + { +// TODO kwin should try to place utility windows next to their mainwindow, +// preferably at the right edge, and going down if there are more of them +// if there's not enough place outside the mainwindow, it should prefer +// top-right corner + // use the default placement for now + place( c, area, Default ); + } + + +void Placement::placeDialog(Client* c, QRect& area, Policy nextPlacement ) + { + placeOnMainWindow( c, area, nextPlacement ); + } + +void Placement::placeUnderMouse(Client* c, QRect& area, Policy /*next*/ ) + { + area = checkArea( c, area ); + QRect geom = c->geometry(); + geom.moveCenter( QCursor::pos()); + c->move( geom.topLeft()); + c->keepInArea( area ); // make sure it's kept inside workarea + } + +void Placement::placeOnMainWindow(Client* c, QRect& area, Policy nextPlacement ) + { + if( nextPlacement == Unknown ) + nextPlacement = Centered; + if( nextPlacement == Maximizing ) // maximize if needed + placeMaximizing( c, area, NoPlacement ); + area = checkArea( c, area ); + ClientList mainwindows = c->mainClients(); + Client* place_on = NULL; + Client* place_on2 = NULL; + int mains_count = 0; + for( ClientList::ConstIterator it = mainwindows.begin(); + it != mainwindows.end(); + ++it ) + { + if( (*it)->isSpecialWindow()) + continue; // don't consider toolbars etc when placing + ++mains_count; + place_on2 = *it; + if( (*it)->isOnCurrentDesktop()) + { + if( place_on == NULL ) + place_on = *it; + else + { // two or more on current desktop -> center + // That's the default at least. However, with maximizing placement + // policy as the default, the dialog should be either maximized or + // made as large as its maximum size and then placed centered. + // So the nextPlacement argument allows chaining. In this case, nextPlacement + // is Maximizing and it will call placeCentered(). + place( c, area, Centered ); + return; + } + } + } + if( place_on == NULL ) + { // 'mains_count' is used because it doesn't include ignored mainwindows + if( mains_count != 1 ) + { + place( c, area, Centered ); + return; + } + place_on = place_on2; // use the only window filtered together with 'mains_count' + } + QRect geom = c->geometry(); + geom.moveCenter( place_on->geometry().center()); + c->move( geom.topLeft()); + // get area again, because the mainwindow may be on different xinerama screen + area = checkArea( c, QRect()); + c->keepInArea( area ); // make sure it's kept inside workarea + } + +void Placement::placeMaximizing(Client* c, QRect& area, Policy nextPlacement ) + { + if( nextPlacement == Unknown ) + nextPlacement = Smart; + if( c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) + { + if( m_WorkspacePtr->clientArea( MaximizeArea, c ) == area ) + c->maximize( Client::MaximizeFull ); + else // if the geometry doesn't match default maximize area (xinerama case?), + { // it's probably better to use the given area + c->setGeometry( area ); + } + } + else + { + c->resizeWithChecks( c->maxSize().boundedTo( area.size())); + place( c, area, nextPlacement ); + } + } + +QRect Placement::checkArea( const Client* c, const QRect& area ) + { + if( area.isNull()) + return m_WorkspacePtr->clientArea( PlacementArea, c->geometry().center(), c->desktop()); + return area; + } + +#endif + + +Placement::Policy Placement::policyFromString( const QString& policy, bool no_special ) + { + if( policy == "NoPlacement" ) + return NoPlacement; + else if( policy == "Default" && !no_special ) + return Default; + else if( policy == "Random" ) + return Random; + else if( policy == "Cascade" ) + return Cascade; + else if( policy == "Centered" ) + return Centered; + else if( policy == "ZeroCornered" ) + return ZeroCornered; + else if( policy == "UnderMouse" && !no_special) + return UnderMouse; + else if( policy == "OnMainWindow" && !no_special) + return OnMainWindow; + else if( policy == "Maximizing" ) + return Maximizing; + else + return Smart; + } + +const char* Placement::policyToString( Policy policy ) + { + const char* const policies[] = + { "NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Cascade", "Centered", + "ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing" }; + assert( policy < int( sizeof( policies ) / sizeof( policies[ 0 ] ))); + return policies[ policy ]; + } + + +#ifndef KCMRULES + +// ******************** +// Workspace +// ******************** + +/*! + Moves active window left until in bumps into another window or workarea edge. + */ +void Workspace::slotWindowPackLeft() + { + if( active_client && active_client->isMovable()) + active_client->move( packPositionLeft( active_client, active_client->geometry().left(), true ), + active_client->y()); + } + +void Workspace::slotWindowPackRight() + { + if( active_client && active_client->isMovable()) + active_client->move( + packPositionRight( active_client, active_client->geometry().right(), true ) + - active_client->width() + 1, active_client->y()); + } + +void Workspace::slotWindowPackUp() + { + if( active_client && active_client->isMovable()) + active_client->move( active_client->x(), + packPositionUp( active_client, active_client->geometry().top(), true )); + } + +void Workspace::slotWindowPackDown() + { + if( active_client && active_client->isMovable()) + active_client->move( active_client->x(), + packPositionDown( active_client, active_client->geometry().bottom(), true ) - active_client->height() + 1 ); + } + +void Workspace::slotWindowGrowHorizontal() + { + if( active_client ) + active_client->growHorizontal(); + } + +void Client::growHorizontal() + { + if( !isResizable() || isShade()) + return; + QRect geom = geometry(); + geom.setRight( workspace()->packPositionRight( this, geom.right(), true )); + QSize adjsize = adjustedSize( geom.size(), SizemodeFixedW ); + if( geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.width_inc > 1 ) // take care of size increments + { + int newright = workspace()->packPositionRight( this, geom.right() + xSizeHint.width_inc - 1, true ); + // check that it hasn't grown outside of the area, due to size increments + // TODO this may be wrong? + if( workspace()->clientArea( MovementArea, + QPoint(( x() + newright ) / 2, geometry().center().y()), desktop()).right() >= newright ) + geom.setRight( newright ); + } + geom.setSize( adjustedSize( geom.size(), SizemodeFixedW )); + setGeometry( geom ); + } + +void Workspace::slotWindowShrinkHorizontal() + { + if( active_client ) + active_client->shrinkHorizontal(); + } + +void Client::shrinkHorizontal() + { + if( !isResizable() || isShade()) + return; + QRect geom = geometry(); + geom.setRight( workspace()->packPositionLeft( this, geom.right(), false )); + if( geom.width() <= 1 ) + return; + geom.setSize( adjustedSize( geom.size(), SizemodeFixedW )); + if( geom.width() > 20 ) + setGeometry( geom ); + } + +void Workspace::slotWindowGrowVertical() + { + if( active_client ) + active_client->growVertical(); + } + +void Client::growVertical() + { + if( !isResizable() || isShade()) + return; + QRect geom = geometry(); + geom.setBottom( workspace()->packPositionDown( this, geom.bottom(), true )); + QSize adjsize = adjustedSize( geom.size(), SizemodeFixedH ); + if( geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.height_inc > 1 ) // take care of size increments + { + int newbottom = workspace()->packPositionDown( this, geom.bottom() + xSizeHint.height_inc - 1, true ); + // check that it hasn't grown outside of the area, due to size increments + if( workspace()->clientArea( MovementArea, + QPoint( geometry().center().x(), ( y() + newbottom ) / 2 ), desktop()).bottom() >= newbottom ) + geom.setBottom( newbottom ); + } + geom.setSize( adjustedSize( geom.size(), SizemodeFixedH )); + setGeometry( geom ); + } + + +void Workspace::slotWindowShrinkVertical() + { + if( active_client ) + active_client->shrinkVertical(); + } + +void Client::shrinkVertical() + { + if( !isResizable() || isShade()) + return; + QRect geom = geometry(); + geom.setBottom( workspace()->packPositionUp( this, geom.bottom(), false )); + if( geom.height() <= 1 ) + return; + geom.setSize( adjustedSize( geom.size(), SizemodeFixedH )); + if( geom.height() > 20 ) + setGeometry( geom ); + } + +int Workspace::packPositionLeft( const Client* cl, int oldx, bool left_edge ) const + { + int newx = clientArea( MovementArea, cl ).left(); + if( oldx <= newx ) // try another Xinerama screen + newx = clientArea( MovementArea, + QPoint( cl->geometry().left() - 1, cl->geometry().center().y()), cl->desktop()).left(); + if( oldx <= newx ) + return oldx; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( !(*it)->isShown( false ) || !(*it)->isOnDesktop( active_client->desktop())) + continue; + int x = left_edge ? (*it)->geometry().right() + 1 : (*it)->geometry().left() - 1; + if( x > newx && x < oldx + && !( cl->geometry().top() > (*it)->geometry().bottom() // they overlap in Y direction + || cl->geometry().bottom() < (*it)->geometry().top())) + newx = x; + } + return newx; + } + +int Workspace::packPositionRight( const Client* cl, int oldx, bool right_edge ) const + { + int newx = clientArea( MovementArea, cl ).right(); + if( oldx >= newx ) // try another Xinerama screen + newx = clientArea( MovementArea, + QPoint( cl->geometry().right() + 1, cl->geometry().center().y()), cl->desktop()).right(); + if( oldx >= newx ) + return oldx; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( !(*it)->isShown( false ) || !(*it)->isOnDesktop( cl->desktop())) + continue; + int x = right_edge ? (*it)->geometry().left() - 1 : (*it)->geometry().right() + 1; + if( x < newx && x > oldx + && !( cl->geometry().top() > (*it)->geometry().bottom() + || cl->geometry().bottom() < (*it)->geometry().top())) + newx = x; + } + return newx; + } + +int Workspace::packPositionUp( const Client* cl, int oldy, bool top_edge ) const + { + int newy = clientArea( MovementArea, cl ).top(); + if( oldy <= newy ) // try another Xinerama screen + newy = clientArea( MovementArea, + QPoint( cl->geometry().center().x(), cl->geometry().top() - 1 ), cl->desktop()).top(); + if( oldy <= newy ) + return oldy; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( !(*it)->isShown( false ) || !(*it)->isOnDesktop( cl->desktop())) + continue; + int y = top_edge ? (*it)->geometry().bottom() + 1 : (*it)->geometry().top() - 1; + if( y > newy && y < oldy + && !( cl->geometry().left() > (*it)->geometry().right() // they overlap in X direction + || cl->geometry().right() < (*it)->geometry().left())) + newy = y; + } + return newy; + } + +int Workspace::packPositionDown( const Client* cl, int oldy, bool bottom_edge ) const + { + int newy = clientArea( MovementArea, cl ).bottom(); + if( oldy >= newy ) // try another Xinerama screen + newy = clientArea( MovementArea, + QPoint( cl->geometry().center().x(), cl->geometry().bottom() + 1 ), cl->desktop()).bottom(); + if( oldy >= newy ) + return oldy; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( !(*it)->isShown( false ) || !(*it)->isOnDesktop( cl->desktop())) + continue; + int y = bottom_edge ? (*it)->geometry().top() - 1 : (*it)->geometry().bottom() + 1; + if( y < newy && y > oldy + && !( cl->geometry().left() > (*it)->geometry().right() + || cl->geometry().right() < (*it)->geometry().left())) + newy = y; + } + return newy; + } + +/*! + Asks the internal positioning object to place a client +*/ +void Workspace::place(Client* c, QRect& area) + { + initPositioning->place( c, area ); + } + +void Workspace::placeSmart(Client* c, const QRect& area) + { + initPositioning->placeSmart( c, area ); + } + +#endif + +} // namespace diff --git a/kwin/placement.h b/kwin/placement.h new file mode 100644 index 000000000..49b436d49 --- /dev/null +++ b/kwin/placement.h @@ -0,0 +1,92 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 1997 to 2002 Cristian Tibirna <tibirna@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_PLACEMENT_H +#define KWIN_PLACEMENT_H + +#include <qpoint.h> +#include <qvaluelist.h> + +namespace KWinInternal +{ + +class Workspace; +class Client; + +class Placement + { + public: + + Placement(Workspace* w); + + /** + * Placement policies. How workspace decides the way windows get positioned + * on the screen. The better the policy, the heavier the resource use. + * Normally you don't have to worry. What the WM adds to the startup time + * is nil compared to the creation of the window itself in the memory + */ + enum Policy + { + NoPlacement, // not really a placement + Default, // special, means to use the global default + Unknown, // special, means the function should use its default + Random, + Smart, + Cascade, + Centered, + ZeroCornered, + UnderMouse, // special + OnMainWindow, // special + Maximizing + }; + + void place(Client* c, QRect& area ); + + void placeAtRandom (Client* c, const QRect& area, Policy next = Unknown ); + void placeCascaded (Client* c, QRect& area, Policy next = Unknown ); + void placeSmart (Client* c, const QRect& area, Policy next = Unknown ); + void placeMaximizing (Client* c, QRect& area, Policy next = Unknown ); + void placeCentered (Client* c, const QRect& area, Policy next = Unknown ); + void placeZeroCornered(Client* c, const QRect& area, Policy next = Unknown ); + void placeDialog (Client* c, QRect& area, Policy next = Unknown ); + void placeUtility (Client* c, QRect& area, Policy next = Unknown ); + + void reinitCascading( int desktop ); + + static Policy policyFromString( const QString& policy, bool no_special ); + static const char* policyToString( Policy policy ); + + private: + + void place(Client* c, QRect& area, Policy policy, Policy nextPlacement = Unknown ); + void placeUnderMouse(Client* c, QRect& area, Policy next = Unknown ); + void placeOnMainWindow(Client* c, QRect& area, Policy next = Unknown ); + QRect checkArea( const Client*c, const QRect& area ); + + Placement(); + + //CT needed for cascading+ + struct DesktopCascadingInfo + { + QPoint pos; + int col; + int row; + }; + + QValueList<DesktopCascadingInfo> cci; + + Workspace* m_WorkspacePtr; + }; + +} // namespace + +#endif diff --git a/kwin/plugins.cpp b/kwin/plugins.cpp new file mode 100644 index 000000000..c7a4fcc15 --- /dev/null +++ b/kwin/plugins.cpp @@ -0,0 +1,42 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Daniel M. Duley <mosfet@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "plugins.h" + +#include <kglobal.h> +#include <klocale.h> +#include <stdlib.h> +#include <qpixmap.h> + +namespace KWinInternal +{ + +PluginMgr::PluginMgr() + : KDecorationPlugins( KGlobal::config()) + { + defaultPlugin = (QPixmap::defaultDepth() > 8) ? + "kwin3_plastik" : "kwin3_quartz"; + loadPlugin( "" ); // load the plugin specified in cfg file + } + +void PluginMgr::error( const QString &error_msg ) + { + qWarning( "%s", (i18n("KWin: ") + error_msg + + i18n("\nKWin will now exit...")).local8Bit().data() ); + exit(1); + } + +bool PluginMgr::provides( Requirement ) + { + return false; + } + +} // namespace diff --git a/kwin/plugins.h b/kwin/plugins.h new file mode 100644 index 000000000..7e779098c --- /dev/null +++ b/kwin/plugins.h @@ -0,0 +1,32 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Daniel M. Duley <mosfet@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_PLUGINS_H +#define KWIN_PLUGINS_H + +#include <kdecoration_plugins_p.h> + +namespace KWinInternal +{ + +class PluginMgr + : public KDecorationPlugins + { + public: + PluginMgr(); + virtual bool provides( Requirement ); + protected: + virtual void error( const QString& error_msg ); + }; + +} // namespace + +#endif diff --git a/kwin/popupinfo.cpp b/kwin/popupinfo.cpp new file mode 100644 index 000000000..aef5dbea7 --- /dev/null +++ b/kwin/popupinfo.cpp @@ -0,0 +1,148 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2002 Alexander Kellett <lypanov@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#define QT_CLEAN_NAMESPACE +#include "popupinfo.h" +#include "workspace.h" +#include "client.h" +#include <qpainter.h> +#include <qlabel.h> +#include <qdrawutil.h> +#include <qstyle.h> +#include <kglobal.h> +#include <fixx11h.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qcursor.h> +#include <kstringhandler.h> +#include <kglobalsettings.h> + +// specify externals before namespace + +namespace KWinInternal +{ + +PopupInfo::PopupInfo( const char *name ) + : QWidget( 0, name ) + { + m_infoString = ""; + m_shown = false; + reset(); + reconfigure(); + connect(&m_delayedHideTimer, SIGNAL(timeout()), this, SLOT(hide())); + + QFont f = font(); + f.setBold( TRUE ); + f.setPointSize( 14 ); + setFont( f ); + + } + +PopupInfo::~PopupInfo() + { + } + + +/*! + Resets the popup info + */ +void PopupInfo::reset() + { + QRect r = KGlobalSettings::desktopGeometry(QCursor::pos()); + + int w = fontMetrics().width( m_infoString ) + 30; + + setGeometry( + (r.width()-w)/2 + r.x(), r.height()/2-fontMetrics().height()-10 + r.y(), + w, fontMetrics().height() + 20 ); + } + + +/*! + Paints the popup info + */ +void PopupInfo::paintEvent( QPaintEvent* ) + { + QPainter p( this ); + style().drawPrimitive( QStyle::PE_Panel, &p, QRect( 0, 0, width(), height() ), + colorGroup(), QStyle::Style_Default ); + paintContents(); + } + + +/*! + Paints the contents of the tab popup info box. + Used in paintEvent() and whenever the contents changes. + */ +void PopupInfo::paintContents() + { + QPainter p( this ); + QRect r( 6, 6, width()-12, height()-12 ); + + p.fillRect( r, colorGroup().brush( QColorGroup::Background ) ); + + /* + p.setPen(Qt::white); + p.drawText( r, AlignCenter, m_infoString ); + p.setPen(Qt::black); + r.moveBy( -1, -1 ); + p.drawText( r, AlignCenter, m_infoString ); + r.moveBy( -1, 0 ); + */ + p.drawText( r, AlignCenter, m_infoString ); + } + +void PopupInfo::hide() + { + m_delayedHideTimer.stop(); + QWidget::hide(); + QApplication::syncX(); + XEvent otherEvent; + while (XCheckTypedEvent (qt_xdisplay(), EnterNotify, &otherEvent ) ) + ; + m_shown = false; + } + +void PopupInfo::reconfigure() + { + KConfig * c(KGlobal::config()); + c->setGroup("PopupInfo"); + m_show = c->readBoolEntry("ShowPopup", false ); + m_delayTime = c->readNumEntry("PopupHideDelay", 350 ); + } + +void PopupInfo::showInfo(QString infoString) + { + if (m_show) + { + m_infoString = infoString; + reset(); + if (m_shown) + { + paintContents(); + } + else + { + show(); + raise(); + m_shown = true; + } + m_delayedHideTimer.start(m_delayTime, true); + } + } + +} // namespace + +#include "popupinfo.moc" diff --git a/kwin/popupinfo.h b/kwin/popupinfo.h new file mode 100644 index 000000000..11936fcfb --- /dev/null +++ b/kwin/popupinfo.h @@ -0,0 +1,50 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_POPUPINFO_H +#define KWIN_POPUPINFO_H +#include <qwidget.h> +#include <qtimer.h> +#include <qvaluelist.h> + +namespace KWinInternal +{ + +class Workspace; + +class PopupInfo : public QWidget + { + Q_OBJECT + public: + PopupInfo( const char *name=0 ); + ~PopupInfo(); + + void reset(); + void hide(); + void showInfo(QString infoString); + + void reconfigure(); + + protected: + void paintEvent( QPaintEvent* ); + void paintContents(); + + private: + QTimer m_delayedHideTimer; + int m_delayTime; + bool m_show; + bool m_shown; + QString m_infoString; + }; + +} // namespace + +#endif diff --git a/kwin/rules.cpp b/kwin/rules.cpp new file mode 100644 index 000000000..1aab4466a --- /dev/null +++ b/kwin/rules.cpp @@ -0,0 +1,1077 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "rules.h" + +#include <fixx11h.h> +#include <kconfig.h> +#include <qregexp.h> +#include <ktempfile.h> +#include <ksimpleconfig.h> +#include <qfile.h> + +#ifndef KCMRULES +#include "client.h" +#include "workspace.h" +#endif + +namespace KWinInternal +{ + +Rules::Rules() + : temporary_state( 0 ) + , wmclassmatch( UnimportantMatch ) + , wmclasscomplete( UnimportantMatch ) + , windowrolematch( UnimportantMatch ) + , titlematch( UnimportantMatch ) + , extrarolematch( UnimportantMatch ) + , clientmachinematch( UnimportantMatch ) + , types( NET::AllTypesMask ) + , placementrule( UnusedForceRule ) + , positionrule( UnusedSetRule ) + , sizerule( UnusedSetRule ) + , minsizerule( UnusedForceRule ) + , maxsizerule( UnusedForceRule ) + , opacityactiverule( UnusedForceRule ) + , opacityinactiverule( UnusedForceRule ) + , ignorepositionrule( UnusedForceRule ) + , desktoprule( UnusedSetRule ) + , typerule( UnusedForceRule ) + , maximizevertrule( UnusedSetRule ) + , maximizehorizrule( UnusedSetRule ) + , minimizerule( UnusedSetRule ) + , shaderule( UnusedSetRule ) + , skiptaskbarrule( UnusedSetRule ) + , skippagerrule( UnusedSetRule ) + , aboverule( UnusedSetRule ) + , belowrule( UnusedSetRule ) + , fullscreenrule( UnusedSetRule ) + , noborderrule( UnusedSetRule ) + , fsplevelrule( UnusedForceRule ) + , acceptfocusrule( UnusedForceRule ) + , moveresizemoderule( UnusedForceRule ) + , closeablerule( UnusedForceRule ) + , strictgeometryrule( UnusedForceRule ) + , shortcutrule( UnusedSetRule ) + , disableglobalshortcutsrule( UnusedForceRule ) + { + } + +Rules::Rules( const QString& str, bool temporary ) + : temporary_state( temporary ? 2 : 0 ) + { + KTempFile file; + QFile* f = file.file(); + if( f != NULL ) + { + QCString s = str.utf8(); + f->writeBlock( s.data(), s.length()); + } + file.close(); + KSimpleConfig cfg( file.name()); + readFromCfg( cfg ); + if( description.isEmpty()) + description = "temporary"; + file.unlink(); + } + +#define READ_MATCH_STRING( var, func ) \ + var = cfg.readEntry( #var ) func; \ + var##match = (StringMatch) QMAX( FirstStringMatch, QMIN( LastStringMatch, cfg.readNumEntry( #var "match" ))); + +#define READ_SET_RULE( var, type, func ) \ + var = func ( cfg.read##type##Entry( #var )); \ + var##rule = readSetRule( cfg, #var "rule" ); + +#define READ_SET_RULE_DEF( var, type, func, def ) \ + var = func ( cfg.read##type##Entry( #var, def )); \ + var##rule = readSetRule( cfg, #var "rule" ); + +#define READ_SET_RULE_2( var, type, func, funcarg ) \ + var = func ( cfg.read##type##Entry( #var ), funcarg ); \ + var##rule = readSetRule( cfg, #var "rule" ); + +#define READ_FORCE_RULE( var, type, func ) \ + var = func ( cfg.read##type##Entry( #var )); \ + var##rule = readForceRule( cfg, #var "rule" ); + +#define READ_FORCE_RULE_2( var, type, func, funcarg ) \ + var = func ( cfg.read##type##Entry( #var ), funcarg ); \ + var##rule = readForceRule( cfg, #var "rule" ); + + +Rules::Rules( KConfig& cfg ) + : temporary_state( 0 ) + { + readFromCfg( cfg ); + } + +static int limit0to4( int i ) { return QMAX( 0, QMIN( 4, i )); } + +void Rules::readFromCfg( KConfig& cfg ) + { + description = cfg.readEntry( "Description" ); + if( description.isEmpty()) // capitalized first, lowercase for backwards compatibility + description = cfg.readEntry( "description" ); + READ_MATCH_STRING( wmclass, .lower().latin1() ); + wmclasscomplete = cfg.readBoolEntry( "wmclasscomplete" ); + READ_MATCH_STRING( windowrole, .lower().latin1() ); + READ_MATCH_STRING( title, ); + READ_MATCH_STRING( extrarole, .lower().latin1() ); + READ_MATCH_STRING( clientmachine, .lower().latin1() ); + types = cfg.readUnsignedLongNumEntry( "types", NET::AllTypesMask ); + READ_FORCE_RULE_2( placement,, Placement::policyFromString, false ); + READ_SET_RULE_DEF( position, Point,, &invalidPoint ); + READ_SET_RULE( size, Size, ); + if( size.isEmpty() && sizerule != ( SetRule )Remember) + sizerule = UnusedSetRule; + READ_FORCE_RULE( minsize, Size, ); + if( !minsize.isValid()) + minsize = QSize( 1, 1 ); + READ_FORCE_RULE( maxsize, Size, ); + if( maxsize.isEmpty()) + maxsize = QSize( 32767, 32767 ); + READ_FORCE_RULE( opacityactive, Num, ); + if( opacityactive < 0 || opacityactive > 100 ) + opacityactive = 100; + READ_FORCE_RULE( opacityinactive, Num, ); + if( opacityinactive < 0 || opacityinactive > 100 ) + opacityinactive = 100; + READ_FORCE_RULE( ignoreposition, Bool, ); + READ_SET_RULE( desktop, Num, ); + type = readType( cfg, "type" ); + typerule = type != NET::Unknown ? readForceRule( cfg, "typerule" ) : UnusedForceRule; + READ_SET_RULE( maximizevert, Bool, ); + READ_SET_RULE( maximizehoriz, Bool, ); + READ_SET_RULE( minimize, Bool, ); + READ_SET_RULE( shade, Bool, ); + READ_SET_RULE( skiptaskbar, Bool, ); + READ_SET_RULE( skippager, Bool, ); + READ_SET_RULE( above, Bool, ); + READ_SET_RULE( below, Bool, ); + READ_SET_RULE( fullscreen, Bool, ); + READ_SET_RULE( noborder, Bool, ); + READ_FORCE_RULE( fsplevel, Num, limit0to4 ); // fsp is 0-4 + READ_FORCE_RULE( acceptfocus, Bool, ); + READ_FORCE_RULE( moveresizemode, , Options::stringToMoveResizeMode ); + READ_FORCE_RULE( closeable, Bool, ); + READ_FORCE_RULE( strictgeometry, Bool, ); + READ_SET_RULE( shortcut, , ); + READ_FORCE_RULE( disableglobalshortcuts, Bool, ); + } + +#undef READ_MATCH_STRING +#undef READ_SET_RULE +#undef READ_SET_RULE_2 +#undef READ_FORCE_RULE +#undef READ_FORCE_RULE_2 + +#define WRITE_MATCH_STRING( var, cast, force ) \ + if( !var.isEmpty() || force ) \ + { \ + cfg.writeEntry( #var, cast var ); \ + cfg.writeEntry( #var "match", var##match ); \ + } \ + else \ + { \ + cfg.deleteEntry( #var ); \ + cfg.deleteEntry( #var "match" ); \ + } + +#define WRITE_SET_RULE( var, func ) \ + if( var##rule != UnusedSetRule ) \ + { \ + cfg.writeEntry( #var, func ( var )); \ + cfg.writeEntry( #var "rule", var##rule ); \ + } \ + else \ + { \ + cfg.deleteEntry( #var ); \ + cfg.deleteEntry( #var "rule" ); \ + } + +#define WRITE_FORCE_RULE( var, func ) \ + if( var##rule != UnusedForceRule ) \ + { \ + cfg.writeEntry( #var, func ( var )); \ + cfg.writeEntry( #var "rule", var##rule ); \ + } \ + else \ + { \ + cfg.deleteEntry( #var ); \ + cfg.deleteEntry( #var "rule" ); \ + } + +#define WRITE_WITH_DEFAULT( var, default ) \ + if( var != default ) \ + cfg.writeEntry( #var, var ); \ + else \ + cfg.deleteEntry( #var ); + + +void Rules::write( KConfig& cfg ) const + { + cfg.writeEntry( "Description", description ); + // always write wmclass + WRITE_MATCH_STRING( wmclass, (const char*), true ); + cfg.writeEntry( "wmclasscomplete", wmclasscomplete ); + WRITE_MATCH_STRING( windowrole, (const char*), false ); + WRITE_MATCH_STRING( title,, false ); + WRITE_MATCH_STRING( extrarole, (const char*), false ); + WRITE_MATCH_STRING( clientmachine, (const char*), false ); + WRITE_WITH_DEFAULT( types, NET::AllTypesMask ); + WRITE_FORCE_RULE( placement, Placement::policyToString ); + WRITE_SET_RULE( position, ); + WRITE_SET_RULE( size, ); + WRITE_FORCE_RULE( minsize, ); + WRITE_FORCE_RULE( maxsize, ); + WRITE_FORCE_RULE( opacityactive, ); + WRITE_FORCE_RULE( opacityinactive, ); + WRITE_FORCE_RULE( ignoreposition, ); + WRITE_SET_RULE( desktop, ); + WRITE_FORCE_RULE( type, ); + WRITE_SET_RULE( maximizevert, ); + WRITE_SET_RULE( maximizehoriz, ); + WRITE_SET_RULE( minimize, ); + WRITE_SET_RULE( shade, ); + WRITE_SET_RULE( skiptaskbar, ); + WRITE_SET_RULE( skippager, ); + WRITE_SET_RULE( above, ); + WRITE_SET_RULE( below, ); + WRITE_SET_RULE( fullscreen, ); + WRITE_SET_RULE( noborder, ); + WRITE_FORCE_RULE( fsplevel, ); + WRITE_FORCE_RULE( acceptfocus, ); + WRITE_FORCE_RULE( moveresizemode, Options::moveResizeModeToString ); + WRITE_FORCE_RULE( closeable, ); + WRITE_FORCE_RULE( strictgeometry, ); + WRITE_SET_RULE( shortcut, ); + WRITE_FORCE_RULE( disableglobalshortcuts, ); + } + +#undef WRITE_MATCH_STRING +#undef WRITE_SET_RULE +#undef WRITE_FORCE_RULE +#undef WRITE_WITH_DEFAULT + +// returns true if it doesn't affect anything +bool Rules::isEmpty() const + { + return( placementrule == UnusedForceRule + && positionrule == UnusedSetRule + && sizerule == UnusedSetRule + && minsizerule == UnusedForceRule + && maxsizerule == UnusedForceRule + && opacityactiverule == UnusedForceRule + && opacityinactiverule == UnusedForceRule + && ignorepositionrule == UnusedForceRule + && desktoprule == UnusedSetRule + && typerule == UnusedForceRule + && maximizevertrule == UnusedSetRule + && maximizehorizrule == UnusedSetRule + && minimizerule == UnusedSetRule + && shaderule == UnusedSetRule + && skiptaskbarrule == UnusedSetRule + && skippagerrule == UnusedSetRule + && aboverule == UnusedSetRule + && belowrule == UnusedSetRule + && fullscreenrule == UnusedSetRule + && noborderrule == UnusedSetRule + && fsplevelrule == UnusedForceRule + && acceptfocusrule == UnusedForceRule + && moveresizemoderule == UnusedForceRule + && closeablerule == UnusedForceRule + && strictgeometryrule == UnusedForceRule + && shortcutrule == UnusedSetRule + && disableglobalshortcutsrule == UnusedForceRule ); + } + +Rules::SetRule Rules::readSetRule( KConfig& cfg, const QString& key ) + { + int v = cfg.readNumEntry( key ); + if( v >= DontAffect && v <= ForceTemporarily ) + return static_cast< SetRule >( v ); + return UnusedSetRule; + } + +Rules::ForceRule Rules::readForceRule( KConfig& cfg, const QString& key ) + { + int v = cfg.readNumEntry( key ); + if( v == DontAffect || v == Force || v == ForceTemporarily ) + return static_cast< ForceRule >( v ); + return UnusedForceRule; + } + +NET::WindowType Rules::readType( KConfig& cfg, const QString& key ) + { + int v = cfg.readNumEntry( key ); + if( v >= NET::Normal && v <= NET::Splash ) + return static_cast< NET::WindowType >( v ); + return NET::Unknown; + } + +bool Rules::matchType( NET::WindowType match_type ) const + { + if( types != NET::AllTypesMask ) + { + if( match_type == NET::Unknown ) + match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching + if( !NET::typeMatchesMask( match_type, types )) + return false; + } + return true; + } + +bool Rules::matchWMClass( const QCString& match_class, const QCString& match_name ) const + { + if( wmclassmatch != UnimportantMatch ) + { // TODO optimize? + QCString cwmclass = wmclasscomplete + ? match_name + ' ' + match_class : match_class; + if( wmclassmatch == RegExpMatch && QRegExp( wmclass ).search( cwmclass ) == -1 ) + return false; + if( wmclassmatch == ExactMatch && wmclass != cwmclass ) + return false; + if( wmclassmatch == SubstringMatch && !cwmclass.contains( wmclass )) + return false; + } + return true; + } + +bool Rules::matchRole( const QCString& match_role ) const + { + if( windowrolematch != UnimportantMatch ) + { + if( windowrolematch == RegExpMatch && QRegExp( windowrole ).search( match_role ) == -1 ) + return false; + if( windowrolematch == ExactMatch && windowrole != match_role ) + return false; + if( windowrolematch == SubstringMatch && !match_role.contains( windowrole )) + return false; + } + return true; + } + +bool Rules::matchTitle( const QString& match_title ) const + { + if( titlematch != UnimportantMatch ) + { + if( titlematch == RegExpMatch && QRegExp( title ).search( match_title ) == -1 ) + return false; + if( titlematch == ExactMatch && title != match_title ) + return false; + if( titlematch == SubstringMatch && !match_title.contains( title )) + return false; + } + return true; + } + +bool Rules::matchClientMachine( const QCString& match_machine ) const + { + if( clientmachinematch != UnimportantMatch ) + { + // if it's localhost, check also "localhost" before checking hostname + if( match_machine != "localhost" && isLocalMachine( match_machine ) + && matchClientMachine( "localhost" )) + return true; + if( clientmachinematch == RegExpMatch + && QRegExp( clientmachine ).search( match_machine ) == -1 ) + return false; + if( clientmachinematch == ExactMatch + && clientmachine != match_machine ) + return false; + if( clientmachinematch == SubstringMatch + && !match_machine.contains( clientmachine )) + return false; + } + return true; + } + +#ifndef KCMRULES +bool Rules::match( const Client* c ) const + { + if( !matchType( c->windowType( true ))) + return false; + if( !matchWMClass( c->resourceClass(), c->resourceName())) + return false; + if( !matchRole( c->windowRole())) + return false; + if( !matchTitle( c->caption( false ))) + return false; + // TODO extrarole + if( !matchClientMachine( c->wmClientMachine( false ))) + return false; + return true; + } + +bool Rules::update( Client* c ) + { + // TODO check this setting is for this client ? + bool updated = false; + if( positionrule == ( SetRule )Remember) + { + if( !c->isFullScreen()) + { + QPoint new_pos = position; + // don't use the position in the direction which is maximized + if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) + new_pos.setX( c->pos().x()); + if(( c->maximizeMode() & MaximizeVertical ) == 0 ) + new_pos.setY( c->pos().y()); + updated = updated || position != new_pos; + position = new_pos; + } + } + if( sizerule == ( SetRule )Remember) + { + if( !c->isFullScreen()) + { + QSize new_size = size; + // don't use the position in the direction which is maximized + if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) + new_size.setWidth( c->size().width()); + if(( c->maximizeMode() & MaximizeVertical ) == 0 ) + new_size.setHeight( c->size().height()); + updated = updated || size != new_size; + size = new_size; + } + } + if( desktoprule == ( SetRule )Remember) + { + updated = updated || desktop != c->desktop(); + desktop = c->desktop(); + } + if( maximizevertrule == ( SetRule )Remember) + { + updated = updated || maximizevert != bool( c->maximizeMode() & MaximizeVertical ); + maximizevert = c->maximizeMode() & MaximizeVertical; + } + if( maximizehorizrule == ( SetRule )Remember) + { + updated = updated || maximizehoriz != bool( c->maximizeMode() & MaximizeHorizontal ); + maximizehoriz = c->maximizeMode() & MaximizeHorizontal; + } + if( minimizerule == ( SetRule )Remember) + { + updated = updated || minimize != c->isMinimized(); + minimize = c->isMinimized(); + } + if( shaderule == ( SetRule )Remember) + { + updated = updated || ( shade != ( c->shadeMode() != ShadeNone )); + shade = c->shadeMode() != ShadeNone; + } + if( skiptaskbarrule == ( SetRule )Remember) + { + updated = updated || skiptaskbar != c->skipTaskbar(); + skiptaskbar = c->skipTaskbar(); + } + if( skippagerrule == ( SetRule )Remember) + { + updated = updated || skippager != c->skipPager(); + skippager = c->skipPager(); + } + if( aboverule == ( SetRule )Remember) + { + updated = updated || above != c->keepAbove(); + above = c->keepAbove(); + } + if( belowrule == ( SetRule )Remember) + { + updated = updated || below != c->keepBelow(); + below = c->keepBelow(); + } + if( fullscreenrule == ( SetRule )Remember) + { + updated = updated || fullscreen != c->isFullScreen(); + fullscreen = c->isFullScreen(); + } + if( noborderrule == ( SetRule )Remember) + { + updated = updated || noborder != c->isUserNoBorder(); + noborder = c->isUserNoBorder(); + } + if (opacityactiverule == ( ForceRule )Force) + { + updated = updated || (uint) (opacityactive/100.0*0xffffffff) != c->ruleOpacityActive(); + opacityactive = (uint)(((double)c->ruleOpacityActive())/0xffffffff*100); + } + if (opacityinactiverule == ( ForceRule )Force) + { + updated = updated || (uint) (opacityinactive/100.0*0xffffffff) != c->ruleOpacityInactive(); + opacityinactive = (uint)(((double)c->ruleOpacityInactive())/0xffffffff*100); + } + return updated; + } + +#define APPLY_RULE( var, name, type ) \ +bool Rules::apply##name( type& arg, bool init ) const \ + { \ + if( checkSetRule( var##rule, init )) \ + arg = this->var; \ + return checkSetStop( var##rule ); \ + } + +#define APPLY_FORCE_RULE( var, name, type ) \ +bool Rules::apply##name( type& arg ) const \ + { \ + if( checkForceRule( var##rule )) \ + arg = this->var; \ + return checkForceStop( var##rule ); \ + } + +APPLY_FORCE_RULE( placement, Placement, Placement::Policy ) + +bool Rules::applyGeometry( QRect& rect, bool init ) const + { + QPoint p = rect.topLeft(); + QSize s = rect.size(); + bool ret = false; // no short-circuiting + if( applyPosition( p, init )) + { + rect.moveTopLeft( p ); + ret = true; + } + if( applySize( s, init )) + { + rect.setSize( s ); + ret = true; + } + return ret; + } + +bool Rules::applyPosition( QPoint& pos, bool init ) const + { + if( this->position != invalidPoint && checkSetRule( positionrule, init )) + pos = this->position; + return checkSetStop( positionrule ); + } + +bool Rules::applySize( QSize& s, bool init ) const + { + if( this->size.isValid() && checkSetRule( sizerule, init )) + s = this->size; + return checkSetStop( sizerule ); + } + +APPLY_FORCE_RULE( minsize, MinSize, QSize ) +APPLY_FORCE_RULE( maxsize, MaxSize, QSize ) +APPLY_FORCE_RULE( opacityactive, OpacityActive, int ) +APPLY_FORCE_RULE( opacityinactive, OpacityInactive, int ) +APPLY_FORCE_RULE( ignoreposition, IgnorePosition, bool ) + +// the cfg. entry needs to stay named the say for backwards compatibility +bool Rules::applyIgnoreGeometry( bool& ignore ) const + { + return applyIgnorePosition( ignore ); + } + +APPLY_RULE( desktop, Desktop, int ) +APPLY_FORCE_RULE( type, Type, NET::WindowType ) + +bool Rules::applyMaximizeHoriz( MaximizeMode& mode, bool init ) const + { + if( checkSetRule( maximizehorizrule, init )) + mode = static_cast< MaximizeMode >(( maximizehoriz ? MaximizeHorizontal : 0 ) | ( mode & MaximizeVertical )); + return checkSetStop( maximizehorizrule ); + } + +bool Rules::applyMaximizeVert( MaximizeMode& mode, bool init ) const + { + if( checkSetRule( maximizevertrule, init )) + mode = static_cast< MaximizeMode >(( maximizevert ? MaximizeVertical : 0 ) | ( mode & MaximizeHorizontal )); + return checkSetStop( maximizevertrule ); + } + +APPLY_RULE( minimize, Minimize, bool ) + +bool Rules::applyShade( ShadeMode& sh, bool init ) const + { + if( checkSetRule( shaderule, init )) + { + if( !this->shade ) + sh = ShadeNone; + if( this->shade && sh == ShadeNone ) + sh = ShadeNormal; + } + return checkSetStop( shaderule ); + } + +APPLY_RULE( skiptaskbar, SkipTaskbar, bool ) +APPLY_RULE( skippager, SkipPager, bool ) +APPLY_RULE( above, KeepAbove, bool ) +APPLY_RULE( below, KeepBelow, bool ) +APPLY_RULE( fullscreen, FullScreen, bool ) +APPLY_RULE( noborder, NoBorder, bool ) +APPLY_FORCE_RULE( fsplevel, FSP, int ) +APPLY_FORCE_RULE( acceptfocus, AcceptFocus, bool ) +APPLY_FORCE_RULE( moveresizemode, MoveResizeMode, Options::MoveResizeMode ) +APPLY_FORCE_RULE( closeable, Closeable, bool ) +APPLY_FORCE_RULE( strictgeometry, StrictGeometry, bool ) +APPLY_RULE( shortcut, Shortcut, QString ) +APPLY_FORCE_RULE( disableglobalshortcuts, DisableGlobalShortcuts, bool ) + + +#undef APPLY_RULE +#undef APPLY_FORCE_RULE + +bool Rules::isTemporary() const + { + return temporary_state > 0; + } + +bool Rules::discardTemporary( bool force ) + { + if( temporary_state == 0 ) // not temporary + return false; + if( force || --temporary_state == 0 ) // too old + { + delete this; + return true; + } + return false; + } + +#define DISCARD_USED_SET_RULE( var ) \ + do { \ + if( var##rule == ( SetRule ) ApplyNow || ( withdrawn && var##rule == ( SetRule ) ForceTemporarily )) \ + var##rule = UnusedSetRule; \ + } while( false ) +#define DISCARD_USED_FORCE_RULE( var ) \ + do { \ + if( withdrawn && var##rule == ( ForceRule ) ForceTemporarily ) \ + var##rule = UnusedForceRule; \ + } while( false ) + +void Rules::discardUsed( bool withdrawn ) + { + DISCARD_USED_FORCE_RULE( placement ); + DISCARD_USED_SET_RULE( position ); + DISCARD_USED_SET_RULE( size ); + DISCARD_USED_FORCE_RULE( minsize ); + DISCARD_USED_FORCE_RULE( maxsize ); + DISCARD_USED_FORCE_RULE( opacityactive ); + DISCARD_USED_FORCE_RULE( opacityinactive ); + DISCARD_USED_FORCE_RULE( ignoreposition ); + DISCARD_USED_SET_RULE( desktop ); + DISCARD_USED_FORCE_RULE( type ); + DISCARD_USED_SET_RULE( maximizevert ); + DISCARD_USED_SET_RULE( maximizehoriz ); + DISCARD_USED_SET_RULE( minimize ); + DISCARD_USED_SET_RULE( shade ); + DISCARD_USED_SET_RULE( skiptaskbar ); + DISCARD_USED_SET_RULE( skippager ); + DISCARD_USED_SET_RULE( above ); + DISCARD_USED_SET_RULE( below ); + DISCARD_USED_SET_RULE( fullscreen ); + DISCARD_USED_SET_RULE( noborder ); + DISCARD_USED_FORCE_RULE( fsplevel ); + DISCARD_USED_FORCE_RULE( acceptfocus ); + DISCARD_USED_FORCE_RULE( moveresizemode ); + DISCARD_USED_FORCE_RULE( closeable ); + DISCARD_USED_FORCE_RULE( strictgeometry ); + DISCARD_USED_SET_RULE( shortcut ); + DISCARD_USED_FORCE_RULE( disableglobalshortcuts ); + } +#undef DISCARD_USED_SET_RULE +#undef DISCARD_USED_FORCE_RULE + +#endif + +#ifndef NDEBUG +kdbgstream& operator<<( kdbgstream& stream, const Rules* r ) + { + return stream << "[" << r->description << ":" << r->wmclass << "]" ; + } +#endif + +#ifndef KCMRULES +void WindowRules::discardTemporary() + { + QValueVector< Rules* >::Iterator it2 = rules.begin(); + for( QValueVector< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ) + { + if( (*it)->discardTemporary( true )) + ++it; + else + { + *it2++ = *it++; + } + } + rules.erase( it2, rules.end()); + } + +void WindowRules::update( Client* c ) + { + bool updated = false; + for( QValueVector< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + if( (*it)->update( c )) // no short-circuiting here + updated = true; + if( updated ) + Workspace::self()->rulesUpdated(); + } + +#define CHECK_RULE( rule, type ) \ +type WindowRules::check##rule( type arg, bool init ) const \ + { \ + if( rules.count() == 0 ) \ + return arg; \ + type ret = arg; \ + for( QValueVector< Rules* >::ConstIterator it = rules.begin(); \ + it != rules.end(); \ + ++it ) \ + { \ + if( (*it)->apply##rule( ret, init )) \ + break; \ + } \ + return ret; \ + } + +#define CHECK_FORCE_RULE( rule, type ) \ +type WindowRules::check##rule( type arg ) const \ + { \ + if( rules.count() == 0 ) \ + return arg; \ + type ret = arg; \ + for( QValueVector< Rules* >::ConstIterator it = rules.begin(); \ + it != rules.end(); \ + ++it ) \ + { \ + if( (*it)->apply##rule( ret )) \ + break; \ + } \ + return ret; \ + } + +CHECK_FORCE_RULE( Placement, Placement::Policy ) + +QRect WindowRules::checkGeometry( QRect rect, bool init ) const + { + return QRect( checkPosition( rect.topLeft(), init ), checkSize( rect.size(), init )); + } + +CHECK_RULE( Position, QPoint ) +CHECK_RULE( Size, QSize ) +CHECK_FORCE_RULE( MinSize, QSize ) +CHECK_FORCE_RULE( MaxSize, QSize ) +CHECK_FORCE_RULE( OpacityActive, int ) +CHECK_FORCE_RULE( OpacityInactive, int ) +CHECK_FORCE_RULE( IgnorePosition, bool ) + +bool WindowRules::checkIgnoreGeometry( bool ignore ) const + { + return checkIgnorePosition( ignore ); + } + +CHECK_RULE( Desktop, int ) +CHECK_FORCE_RULE( Type, NET::WindowType ) +CHECK_RULE( MaximizeVert, KDecorationDefines::MaximizeMode ) +CHECK_RULE( MaximizeHoriz, KDecorationDefines::MaximizeMode ) + +KDecorationDefines::MaximizeMode WindowRules::checkMaximize( MaximizeMode mode, bool init ) const + { + bool vert = checkMaximizeVert( mode, init ) & MaximizeVertical; + bool horiz = checkMaximizeHoriz( mode, init ) & MaximizeHorizontal; + return static_cast< MaximizeMode >(( vert ? MaximizeVertical : 0 ) | ( horiz ? MaximizeHorizontal : 0 )); + } + +CHECK_RULE( Minimize, bool ) +CHECK_RULE( Shade, ShadeMode ) +CHECK_RULE( SkipTaskbar, bool ) +CHECK_RULE( SkipPager, bool ) +CHECK_RULE( KeepAbove, bool ) +CHECK_RULE( KeepBelow, bool ) +CHECK_RULE( FullScreen, bool ) +CHECK_RULE( NoBorder, bool ) +CHECK_FORCE_RULE( FSP, int ) +CHECK_FORCE_RULE( AcceptFocus, bool ) +CHECK_FORCE_RULE( MoveResizeMode, Options::MoveResizeMode ) +CHECK_FORCE_RULE( Closeable, bool ) +CHECK_FORCE_RULE( StrictGeometry, bool ) +CHECK_RULE( Shortcut, QString ) +CHECK_FORCE_RULE( DisableGlobalShortcuts, bool ) + +#undef CHECK_RULE +#undef CHECK_FORCE_RULE + +// Client + +void Client::setupWindowRules( bool ignore_temporary ) + { + client_rules = workspace()->findWindowRules( this, ignore_temporary ); + // check only after getting the rules, because there may be a rule forcing window type + if( isTopMenu()) // TODO cannot have restrictions + client_rules = WindowRules(); + } + +// Applies Force, ForceTemporarily and ApplyNow rules +// Used e.g. after the rules have been modified using the kcm. +void Client::applyWindowRules() + { + checkAndSetInitialRuledOpacity(); + // apply force rules + // Placement - does need explicit update, just like some others below + // Geometry : setGeometry() doesn't check rules + QRect orig_geom = QRect( pos(), sizeForClientSize( clientSize())); // handle shading + QRect geom = client_rules.checkGeometry( orig_geom ); + if( geom != orig_geom ) + setGeometry( geom ); + // MinSize, MaxSize handled by Geometry + // IgnorePosition + setDesktop( desktop()); + // Type + maximize( maximizeMode()); + // Minimize : functions don't check, and there are two functions + if( client_rules.checkMinimize( isMinimized())) + minimize(); + else + unminimize(); + setShade( shadeMode()); + setSkipTaskbar( skipTaskbar(), true ); + setSkipPager( skipPager()); + setKeepAbove( keepAbove()); + setKeepBelow( keepBelow()); + setFullScreen( isFullScreen(), true ); + setUserNoBorder( isUserNoBorder()); + // FSP + // AcceptFocus : + if( workspace()->mostRecentlyActivatedClient() == this + && !client_rules.checkAcceptFocus( true )) + workspace()->activateNextClient( this ); + // MoveResizeMode + // Closeable + QSize s = adjustedSize(); + if( s != size()) + resizeWithChecks( s ); + // StrictGeometry + setShortcut( rules()->checkShortcut( shortcut().toString())); + // see also Client::setActive() + if( isActive()) + workspace()->disableGlobalShortcutsForClient( rules()->checkDisableGlobalShortcuts( false )); + } + +void Client::updateWindowRules() + { + if( !isManaged()) // not fully setup yet + return; + if( workspace()->rulesUpdatesDisabled()) + return; + client_rules.update( this ); + } + +void Client::finishWindowRules() + { + updateWindowRules(); + client_rules = WindowRules(); + } + +void Client::checkAndSetInitialRuledOpacity() +//apply kwin-rules for window-translucency upon hitting apply or starting to manage client + { + int tmp; + + //active translucency + tmp = -1; + tmp = rules()->checkOpacityActive(tmp); + if( tmp != -1 ) //rule did apply and returns valid value + { + rule_opacity_active = (uint)((tmp/100.0)*0xffffffff); + } + else + rule_opacity_active = 0; + + //inactive translucency + tmp = -1; + tmp = rules()->checkOpacityInactive(tmp); + if( tmp != -1 ) //rule did apply and returns valid value + { + rule_opacity_inactive = (uint)((tmp/100.0)*0xffffffff); + } + else + rule_opacity_inactive = 0; + + return; + + if( isDock() ) + //workaround for docks, as they don't have active/inactive settings and don't aut, therefore we take only the active one... + { + uint tmp = rule_opacity_active ? rule_opacity_active : options->dockOpacity; + setOpacity(tmp < 0xFFFFFFFF && (rule_opacity_active || options->translucentDocks), tmp); + } + else + updateOpacity(); + } + +// Workspace + +WindowRules Workspace::findWindowRules( const Client* c, bool ignore_temporary ) + { + QValueVector< Rules* > ret; + for( QValueList< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ) + { + if( ignore_temporary && (*it)->isTemporary()) + { + ++it; + continue; + } + if( (*it)->match( c )) + { + Rules* rule = *it; + kdDebug( 1212 ) << "Rule found:" << rule << ":" << c << endl; + if( rule->isTemporary()) + it = rules.remove( it ); + else + ++it; + ret.append( rule ); + continue; + } + ++it; + } + return WindowRules( ret ); + } + +void Workspace::editWindowRules( Client* c, bool whole_app ) + { + writeWindowRules(); + QStringList args; + args << "--wid" << QString::number( c->window()); + if( whole_app ) + args << "--whole-app"; + KApplication::kdeinitExec( "kwin_rules_dialog", args ); + } + +void Workspace::loadWindowRules() + { + while( !rules.isEmpty()) + { + delete rules.front(); + rules.pop_front(); + } + KConfig cfg( "kwinrulesrc", true ); + cfg.setGroup( "General" ); + int count = cfg.readNumEntry( "count" ); + for( int i = 1; + i <= count; + ++i ) + { + cfg.setGroup( QString::number( i )); + Rules* rule = new Rules( cfg ); + rules.append( rule ); + } + } + +void Workspace::writeWindowRules() + { + rulesUpdatedTimer.stop(); + KConfig cfg( "kwinrulesrc" ); + QStringList groups = cfg.groupList(); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + cfg.deleteGroup( *it ); + cfg.setGroup( "General" ); + cfg.writeEntry( "count", rules.count()); + int i = 1; + for( QValueList< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + { + if( (*it)->isTemporary()) + continue; + cfg.setGroup( QString::number( i )); + (*it)->write( cfg ); + ++i; + } + } + +void Workspace::gotTemporaryRulesMessage( const QString& message ) + { + bool was_temporary = false; + for( QValueList< Rules* >::ConstIterator it = rules.begin(); + it != rules.end(); + ++it ) + if( (*it)->isTemporary()) + was_temporary = true; + Rules* rule = new Rules( message, true ); + rules.prepend( rule ); // highest priority first + if( !was_temporary ) + QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); + } + +void Workspace::cleanupTemporaryRules() + { + bool has_temporary = false; + for( QValueList< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ) + { + if( (*it)->discardTemporary( false )) + it = rules.remove( it ); + else + { + if( (*it)->isTemporary()) + has_temporary = true; + ++it; + } + } + if( has_temporary ) + QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); + } + +void Workspace::discardUsedWindowRules( Client* c, bool withdrawn ) + { + bool updated = false; + for( QValueList< Rules* >::Iterator it = rules.begin(); + it != rules.end(); + ) + { + if( c->rules()->contains( *it )) + { + updated = true; + (*it)->discardUsed( withdrawn ); + if( (*it)->isEmpty()) + { + c->removeRule( *it ); + Rules* r = *it; + it = rules.remove( it ); + delete r; + continue; + } + } + ++it; + } + if( updated ) + rulesUpdated(); + } + +void Workspace::rulesUpdated() + { + rulesUpdatedTimer.start( 1000, true ); + } + +void Workspace::disableRulesUpdates( bool disable ) + { + rules_updates_disabled = disable; + if( !disable ) + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + (*it)->updateWindowRules(); + } + +#endif + +} // namespace diff --git a/kwin/rules.h b/kwin/rules.h new file mode 100644 index 000000000..65a8b9963 --- /dev/null +++ b/kwin/rules.h @@ -0,0 +1,311 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_RULES_H +#define KWIN_RULES_H + +#include <qstring.h> +#include <netwm_def.h> +#include <qrect.h> +#include <qvaluevector.h> +#include <kdebug.h> + +#include "placement.h" +#include "lib/kdecoration.h" +#include "options.h" +#include "utils.h" + +class KConfig; + +namespace KWinInternal +{ + +class Client; +class Rules; + +#ifndef KCMRULES // only for kwin core + +class WindowRules + : public KDecorationDefines + { + public: + WindowRules( const QValueVector< Rules* >& rules ); + WindowRules(); + void update( Client* ); + void discardTemporary(); + bool contains( const Rules* rule ) const; + void remove( Rules* rule ); + Placement::Policy checkPlacement( Placement::Policy placement ) const; + QRect checkGeometry( QRect rect, bool init = false ) const; + // use 'invalidPoint' with checkPosition, unlike QSize() and QRect(), QPoint() is a valid point + QPoint checkPosition( QPoint pos, bool init = false ) const; + QSize checkSize( QSize s, bool init = false ) const; + QSize checkMinSize( QSize s ) const; + QSize checkMaxSize( QSize s ) const; + int checkOpacityActive(int s) const; + int checkOpacityInactive(int s) const; + bool checkIgnoreGeometry( bool ignore ) const; + int checkDesktop( int desktop, bool init = false ) const; + NET::WindowType checkType( NET::WindowType type ) const; + MaximizeMode checkMaximize( MaximizeMode mode, bool init = false ) const; + bool checkMinimize( bool minimized, bool init = false ) const; + ShadeMode checkShade( ShadeMode shade, bool init = false ) const; + bool checkSkipTaskbar( bool skip, bool init = false ) const; + bool checkSkipPager( bool skip, bool init = false ) const; + bool checkKeepAbove( bool above, bool init = false ) const; + bool checkKeepBelow( bool below, bool init = false ) const; + bool checkFullScreen( bool fs, bool init = false ) const; + bool checkNoBorder( bool noborder, bool init = false ) const; + int checkFSP( int fsp ) const; + bool checkAcceptFocus( bool focus ) const; + Options::MoveResizeMode checkMoveResizeMode( Options::MoveResizeMode mode ) const; + bool checkCloseable( bool closeable ) const; + bool checkStrictGeometry( bool strict ) const; + QString checkShortcut( QString s, bool init = false ) const; + bool checkDisableGlobalShortcuts( bool disable ) const; + bool checkIgnorePosition( bool ignore ) const; // obsolete + private: + MaximizeMode checkMaximizeVert( MaximizeMode mode, bool init ) const; + MaximizeMode checkMaximizeHoriz( MaximizeMode mode, bool init ) const; + QValueVector< Rules* > rules; + }; +#endif + +class Rules + : public KDecorationDefines + { + public: + Rules(); + Rules( KConfig& ); + Rules( const QString&, bool temporary ); + void write( KConfig& ) const; + bool isEmpty() const; +#ifndef KCMRULES + void discardUsed( bool withdrawn ); + bool match( const Client* c ) const; + bool update( Client* ); + bool isTemporary() const; + bool discardTemporary( bool force ); // removes if temporary and forced or too old + bool applyPlacement( Placement::Policy& placement ) const; + bool applyGeometry( QRect& rect, bool init ) const; + // use 'invalidPoint' with applyPosition, unlike QSize() and QRect(), QPoint() is a valid point + bool applyPosition( QPoint& pos, bool init ) const; + bool applySize( QSize& s, bool init ) const; + bool applyMinSize( QSize& s ) const; + bool applyMaxSize( QSize& s ) const; + bool applyOpacityActive(int& s) const; + bool applyOpacityInactive(int& s) const; + bool applyIgnoreGeometry( bool& ignore ) const; + bool applyDesktop( int& desktop, bool init ) const; + bool applyType( NET::WindowType& type ) const; + bool applyMaximizeVert( MaximizeMode& mode, bool init ) const; + bool applyMaximizeHoriz( MaximizeMode& mode, bool init ) const; + bool applyMinimize( bool& minimized, bool init ) const; + bool applyShade( ShadeMode& shade, bool init ) const; + bool applySkipTaskbar( bool& skip, bool init ) const; + bool applySkipPager( bool& skip, bool init ) const; + bool applyKeepAbove( bool& above, bool init ) const; + bool applyKeepBelow( bool& below, bool init ) const; + bool applyFullScreen( bool& fs, bool init ) const; + bool applyNoBorder( bool& noborder, bool init ) const; + bool applyFSP( int& fsp ) const; + bool applyAcceptFocus( bool& focus ) const; + bool applyMoveResizeMode( Options::MoveResizeMode& mode ) const; + bool applyCloseable( bool& closeable ) const; + bool applyStrictGeometry( bool& strict ) const; + bool applyShortcut( QString& shortcut, bool init ) const; + bool applyDisableGlobalShortcuts( bool& disable ) const; + bool applyIgnorePosition( bool& ignore ) const; // obsolete + private: +#endif + bool matchType( NET::WindowType match_type ) const; + bool matchWMClass( const QCString& match_class, const QCString& match_name ) const; + bool matchRole( const QCString& match_role ) const; + bool matchTitle( const QString& match_title ) const; + bool matchClientMachine( const QCString& match_machine ) const; + // All these values are saved to the cfg file, and are also used in kstart! + enum + { + Unused = 0, + DontAffect, // use the default value + Force, // force the given value + Apply, // apply only after initial mapping + Remember, // like apply, and remember the value when the window is withdrawn + ApplyNow, // apply immediatelly, then forget the setting + ForceTemporarily // apply and force until the window is withdrawn + }; + enum SetRule + { + UnusedSetRule = Unused, + SetRuleDummy = 256 // so that it's at least short int + }; + enum ForceRule + { + UnusedForceRule = Unused, + ForceRuleDummy = 256 // so that it's at least short int + }; + enum StringMatch + { + FirstStringMatch, + UnimportantMatch = FirstStringMatch, + ExactMatch, + SubstringMatch, + RegExpMatch, + LastStringMatch = RegExpMatch + }; + void readFromCfg( KConfig& cfg ); + static SetRule readSetRule( KConfig&, const QString& key ); + static ForceRule readForceRule( KConfig&, const QString& key ); + static NET::WindowType readType( KConfig&, const QString& key ); +#ifndef KCMRULES + static bool checkSetRule( SetRule rule, bool init ); + static bool checkForceRule( ForceRule rule ); + static bool checkSetStop( SetRule rule ); + static bool checkForceStop( ForceRule rule ); +#endif + int temporary_state; // e.g. for kstart + QString description; + QCString wmclass; + StringMatch wmclassmatch; + bool wmclasscomplete; + QCString windowrole; + StringMatch windowrolematch; + QString title; // TODO "caption" ? + StringMatch titlematch; + QCString extrarole; + StringMatch extrarolematch; + QCString clientmachine; + StringMatch clientmachinematch; + unsigned long types; // types for matching + Placement::Policy placement; + ForceRule placementrule; + QPoint position; + SetRule positionrule; + QSize size; + SetRule sizerule; + QSize minsize; + ForceRule minsizerule; + QSize maxsize; + ForceRule maxsizerule; + int opacityactive; + ForceRule opacityactiverule; + int opacityinactive; + ForceRule opacityinactiverule; + bool ignoreposition; + ForceRule ignorepositionrule; + int desktop; + SetRule desktoprule; + NET::WindowType type; // type for setting + ForceRule typerule; + bool maximizevert; + SetRule maximizevertrule; + bool maximizehoriz; + SetRule maximizehorizrule; + bool minimize; + SetRule minimizerule; + bool shade; + SetRule shaderule; + bool skiptaskbar; + SetRule skiptaskbarrule; + bool skippager; + SetRule skippagerrule; + bool above; + SetRule aboverule; + bool below; + SetRule belowrule; + bool fullscreen; + SetRule fullscreenrule; + bool noborder; + SetRule noborderrule; + int fsplevel; + ForceRule fsplevelrule; + bool acceptfocus; + ForceRule acceptfocusrule; + Options::MoveResizeMode moveresizemode; + ForceRule moveresizemoderule; + bool closeable; + ForceRule closeablerule; + bool strictgeometry; + ForceRule strictgeometryrule; + QString shortcut; + SetRule shortcutrule; + bool disableglobalshortcuts; + ForceRule disableglobalshortcutsrule; + friend kdbgstream& operator<<( kdbgstream& stream, const Rules* ); + }; + +#ifndef KCMRULES +inline +bool Rules::checkSetRule( SetRule rule, bool init ) + { + if( rule > ( SetRule )DontAffect) // Unused or DontAffect + { + if( rule == ( SetRule )Force || rule == ( SetRule ) ApplyNow + || rule == ( SetRule ) ForceTemporarily || init ) + return true; + } + return false; + } + +inline +bool Rules::checkForceRule( ForceRule rule ) + { + return rule == ( ForceRule )Force || rule == ( ForceRule ) ForceTemporarily; + } + +inline +bool Rules::checkSetStop( SetRule rule ) + { + return rule != UnusedSetRule; + } + +inline +bool Rules::checkForceStop( ForceRule rule ) + { + return rule != UnusedForceRule; + } + +inline +WindowRules::WindowRules( const QValueVector< Rules* >& r ) + : rules( r ) + { + } + +inline +WindowRules::WindowRules() + { + } + +inline +bool WindowRules::contains( const Rules* rule ) const + { + return qFind( rules.begin(), rules.end(), rule ) != rules.end(); + } + +inline +void WindowRules::remove( Rules* rule ) + { + QValueVector< Rules* >::Iterator pos = qFind( rules.begin(), rules.end(), rule ); + if( pos != rules.end()) + rules.erase( pos ); + } + +#endif + +#ifdef NDEBUG +inline +kndbgstream& operator<<( kndbgstream& stream, const Rules* ) { return stream; } +#else +kdbgstream& operator<<( kdbgstream& stream, const Rules* ); +#endif + +} // namespace + +#endif diff --git a/kwin/sm.cpp b/kwin/sm.cpp new file mode 100644 index 000000000..37b9526c7 --- /dev/null +++ b/kwin/sm.cpp @@ -0,0 +1,444 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "sm.h" + +#include <qsocketnotifier.h> +#include <qsessionmanager.h> +#include <kdebug.h> +#include <unistd.h> +#include <stdlib.h> +#include <pwd.h> +#include <fixx11h.h> +#include <kconfig.h> +#include <kglobal.h> + +#include "workspace.h" +#include "client.h" + +namespace KWinInternal +{ + +bool SessionManaged::saveState( QSessionManager& sm ) + { + // If the session manager is ksmserver, save stacking + // order, active window, active desktop etc. in phase 1, + // as ksmserver assures no interaction will be done + // before the WM finishes phase 1. Saving in phase 2 is + // too late, as possible user interaction may change some things. + // Phase2 is still needed though (ICCCM 5.2) + char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle())); + bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0; + free( sm_vendor ); + if ( !sm.isPhase2() ) + { + Workspace::self()->sessionSaveStarted(); + if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it + Workspace::self()->storeSession( kapp->sessionConfig(), SMSavePhase0 ); + sm.release(); // Qt doesn't automatically release in this case (bug?) + sm.requestPhase2(); + return true; + } + Workspace::self()->storeSession( kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full ); + kapp->sessionConfig()->sync(); + return true; + } + +// I bet this is broken, just like everywhere else in KDE +bool SessionManaged::commitData( QSessionManager& sm ) + { + if ( !sm.isPhase2() ) + Workspace::self()->sessionSaveStarted(); + return true; + } + +// Workspace + +/*! + Stores the current session in the config file + + \sa loadSessionInfo() + */ +void Workspace::storeSession( KConfig* config, SMSavePhase phase ) + { + config->setGroup("Session" ); + int count = 0; + int active_client = -1; + for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) + { + Client* c = (*it); + QCString sessionId = c->sessionId(); + QCString wmCommand = c->wmCommand(); + if ( sessionId.isEmpty() ) + // remember also applications that are not XSMP capable + // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF + if ( wmCommand.isEmpty() ) + continue; + count++; + if( c->isActive()) + active_client = count; + QString n = QString::number(count); + if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) + { + config->writeEntry( QString("sessionId")+n, sessionId.data() ); + config->writeEntry( QString("windowRole")+n, c->windowRole().data() ); + config->writeEntry( QString("wmCommand")+n, wmCommand.data() ); + config->writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).data() ); + config->writeEntry( QString("resourceName")+n, c->resourceName().data() ); + config->writeEntry( QString("resourceClass")+n, c->resourceClass().data() ); + config->writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(TRUE), c->clientSize() ) ); // FRAME + config->writeEntry( QString("restore")+n, c->geometryRestore() ); + config->writeEntry( QString("fsrestore")+n, c->geometryFSRestore() ); + config->writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); + config->writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() ); + config->writeEntry( QString("desktop")+n, c->desktop() ); + // the config entry is called "iconified" for back. comp. reasons + // (kconf_update script for updating session files would be too complicated) + config->writeEntry( QString("iconified")+n, c->isMinimized() ); + // the config entry is called "sticky" for back. comp. reasons + config->writeEntry( QString("sticky")+n, c->isOnAllDesktops() ); + config->writeEntry( QString("shaded")+n, c->isShade() ); + // the config entry is called "staysOnTop" for back. comp. reasons + config->writeEntry( QString("staysOnTop")+n, c->keepAbove() ); + config->writeEntry( QString("keepBelow")+n, c->keepBelow() ); + config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) ); + config->writeEntry( QString("skipPager")+n, c->skipPager() ); + config->writeEntry( QString("userNoBorder")+n, c->isUserNoBorder() ); + config->writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); + config->writeEntry( QString("shortcut")+n, c->shortcut().toStringInternal()); + } + } + // TODO store also stacking order + if( phase == SMSavePhase0 ) + { + // it would be much simpler to save these values to the config file, + // but both Qt and KDE treat phase1 and phase2 separately, + // which results in different sessionkey and different config file :( + session_active_client = active_client; + session_desktop = currentDesktop(); + } + else if( phase == SMSavePhase2 ) + { + config->writeEntry( "count", count ); + config->writeEntry( "active", session_active_client ); + config->writeEntry( "desktop", session_desktop ); + } + else // SMSavePhase2Full + { + config->writeEntry( "count", count ); + config->writeEntry( "active", session_active_client ); + config->writeEntry( "desktop", currentDesktop()); + } + } + + +/*! + Loads the session information from the config file. + + \sa storeSession() + */ +void Workspace::loadSessionInfo() + { + session.clear(); + KConfig* config = kapp->sessionConfig(); + config->setGroup("Session" ); + int count = config->readNumEntry( "count" ); + int active_client = config->readNumEntry( "active" ); + for ( int i = 1; i <= count; i++ ) + { + QString n = QString::number(i); + SessionInfo* info = new SessionInfo; + session.append( info ); + info->sessionId = config->readEntry( QString("sessionId")+n ).latin1(); + info->windowRole = config->readEntry( QString("windowRole")+n ).latin1(); + info->wmCommand = config->readEntry( QString("wmCommand")+n ).latin1(); + info->wmClientMachine = config->readEntry( QString("wmClientMachine")+n ).latin1(); + info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); + info->resourceClass = config->readEntry( QString("resourceClass")+n ).lower().latin1(); + info->geometry = config->readRectEntry( QString("geometry")+n ); + info->restore = config->readRectEntry( QString("restore")+n ); + info->fsrestore = config->readRectEntry( QString("fsrestore")+n ); + info->maximized = config->readNumEntry( QString("maximize")+n, 0 ); + info->fullscreen = config->readNumEntry( QString("fullscreen")+n, 0 ); + info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); + info->minimized = config->readBoolEntry( QString("iconified")+n, FALSE ); + info->onAllDesktops = config->readBoolEntry( QString("sticky")+n, FALSE ); + info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); + info->keepAbove = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); + info->keepBelow = config->readBoolEntry( QString("keepBelow")+n, FALSE ); + info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); + info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); + info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); + info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); + info->shortcut = config->readEntry( QString("shortcut")+n ); + info->active = ( active_client == i ); + } + } + +/*! + Returns a SessionInfo for client \a c. The returned session + info is removed from the storage. It's up to the caller to delete it. + + This function is called when a new window is mapped and must be managed. + We try to find a matching entry in the session. + + May return 0 if there's no session info for the client. + */ +SessionInfo* Workspace::takeSessionInfo( Client* c ) + { + SessionInfo *realInfo = 0; + QCString sessionId = c->sessionId(); + QCString windowRole = c->windowRole(); + QCString wmCommand = c->wmCommand(); + QCString wmClientMachine = c->wmClientMachine( true ); + QCString resourceName = c->resourceName(); + QCString resourceClass = c->resourceClass(); + + // First search ``session'' + if (! sessionId.isEmpty() ) + { + // look for a real session managed client (algorithm suggested by ICCCM) + for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) + if ( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info )) + { + if ( ! windowRole.isEmpty() ) + { + if ( info->windowRole == windowRole ) + realInfo = session.take(); + } + else + { + if ( info->windowRole.isEmpty() && + info->resourceName == resourceName && + info->resourceClass == resourceClass ) + realInfo = session.take(); + } + } + } + else + { + // look for a sessioninfo with matching features. + for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) + if ( info->resourceName == resourceName && + info->resourceClass == resourceClass && + info->wmClientMachine == wmClientMachine && + sessionInfoWindowTypeMatch( c, info )) + if ( wmCommand.isEmpty() || info->wmCommand == wmCommand ) + realInfo = session.take(); + } + + return realInfo; + } + +bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ) + { + if( info->windowType == -2 ) + { // undefined (not really part of NET::WindowType) + return !c->isSpecialWindow(); + } + return info->windowType == c->windowType(); + } + +// maybe needed later +#if 0 +// KMainWindow's without name() given have WM_WINDOW_ROLE in the form +// of <appname>-mainwindow#<number> +// when comparing them for fake session info, it's probably better to check +// them without the trailing number +bool Workspace::windowRoleMatch( const QCString& role1, const QCString& role2 ) + { + if( role1.isEmpty() && role2.isEmpty()) + return true; + int pos1 = role1.find( '#' ); + int pos2 = role2.find( '#' ); + bool ret; + if( pos1 < 0 || pos2 < 0 || pos1 != pos2 ) + ret = role1 == role2; + else + ret = qstrncmp( role1, role2, pos1 ) == 0; + kdDebug() << "WR:" << role1 << ":" << pos1 << ":" << role2 << ":" << pos2 << ":::" << ret << endl; + return ret; + } +#endif + +static const char* const window_type_names[] = + { + "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", + "Override", "TopMenu", "Utility", "Splash" + }; + // change also the two functions below when adding new entries + +const char* Workspace::windowTypeToTxt( NET::WindowType type ) + { + if( type >= NET::Unknown && type <= NET::Splash ) + return window_type_names[ type + 1 ]; // +1 (unknown==-1) + if( type == -2 ) // undefined (not really part of NET::WindowType) + return "Undefined"; + kdFatal() << "Unknown Window Type" << endl; + return NULL; + } + +NET::WindowType Workspace::txtToWindowType( const char* txt ) + { + for( int i = NET::Unknown; + i <= NET::Splash; + ++i ) + if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1 + return static_cast< NET::WindowType >( i ); + return static_cast< NET::WindowType >( -2 ); // undefined + } + + + + +// KWin's focus stealing prevention causes problems with user interaction +// during session save, as it prevents possible dialogs from getting focus. +// Therefore it's temporarily disabled during session saving. Start of +// session saving can be detected in SessionManaged::saveState() above, +// but Qt doesn't have API for saying when session saved finished (either +// successfully, or was cancelled). Therefore, create another connection +// to session manager, that will provide this information. +// Similarly the remember feature of window-specific settings should be disabled +// during KDE shutdown when windows may move e.g. because of Kicker going away +// (struts changing). When session saving starts, it can be cancelled, in which +// case the shutdown_cancelled callback is invoked, or it's a checkpoint that +// is immediatelly followed by save_complete, or finally it's a shutdown that +// is immediatelly followed by die callback. So getting save_yourself with shutdown +// set disables window-specific settings remembering, getting shutdown_cancelled +// re-enables, otherwise KWin will go away after die. +static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool ) + { + SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); + if( conn_P != session->connection()) + return; + if( shutdown ) + Workspace::self()->disableRulesUpdates( true ); + SmcSaveYourselfDone( conn_P, True ); + } + +static void die( SmcConn conn_P, SmPointer ptr ) + { + SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); + if( conn_P != session->connection()) + return; + // session->saveDone(); we will quit anyway + session->close(); + } + +static void save_complete( SmcConn conn_P, SmPointer ptr ) + { + SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); + if( conn_P != session->connection()) + return; + session->saveDone(); + } + +static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr ) + { + SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); + if( conn_P != session->connection()) + return; + Workspace::self()->disableRulesUpdates( false ); // re-enable + // no need to differentiate between successful finish and cancel + session->saveDone(); + } + +void SessionSaveDoneHelper::saveDone() + { + Workspace::self()->sessionSaveDone(); + } + +SessionSaveDoneHelper::SessionSaveDoneHelper() + { + SmcCallbacks calls; + calls.save_yourself.callback = save_yourself; + calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this); + calls.die.callback = die; + calls.die.client_data = reinterpret_cast< SmPointer >(this); + calls.save_complete.callback = save_complete; + calls.save_complete.client_data = reinterpret_cast< SmPointer >(this); + calls.shutdown_cancelled.callback = shutdown_cancelled; + calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this); + char* id = NULL; + char err[ 11 ]; + conn = SmcOpenConnection( NULL, 0, 1, 0, + SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask + | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err ); + if( id != NULL ) + free( id ); + if( conn == NULL ) + return; // no SM + // set the required properties, mostly dummy values + SmPropValue propvalue[ 5 ]; + SmProp props[ 5 ]; + propvalue[ 0 ].length = sizeof( int ); + int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere + propvalue[ 0 ].value = &value0; + props[ 0 ].name = const_cast< char* >( SmRestartStyleHint ); + props[ 0 ].type = const_cast< char* >( SmCARD8 ); + props[ 0 ].num_vals = 1; + props[ 0 ].vals = &propvalue[ 0 ]; + struct passwd* entry = getpwuid( geteuid() ); + propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0; + propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" ); + props[ 1 ].name = const_cast< char* >( SmUserID ); + props[ 1 ].type = const_cast< char* >( SmARRAY8 ); + props[ 1 ].num_vals = 1; + props[ 1 ].vals = &propvalue[ 1 ]; + propvalue[ 2 ].length = 0; + propvalue[ 2 ].value = (SmPointer)( "" ); + props[ 2 ].name = const_cast< char* >( SmRestartCommand ); + props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 ); + props[ 2 ].num_vals = 1; + props[ 2 ].vals = &propvalue[ 2 ]; + propvalue[ 3 ].length = 0; + propvalue[ 3 ].value = qApp->argv()[ 0 ]; + props[ 3 ].name = const_cast< char* >( SmProgram ); + props[ 3 ].type = const_cast< char* >( SmARRAY8 ); + props[ 3 ].num_vals = 1; + props[ 3 ].vals = &propvalue[ 3 ]; + propvalue[ 4 ].length = 0; + propvalue[ 4 ].value = (SmPointer)( "" ); + props[ 4 ].name = const_cast< char* >( SmCloneCommand ); + props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 ); + props[ 4 ].num_vals = 1; + props[ 4 ].vals = &propvalue[ 4 ]; + SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; + SmcSetProperties( conn, 5, p ); + notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )), + QSocketNotifier::Read, this ); + connect( notifier, SIGNAL( activated( int )), SLOT( processData())); + } + +SessionSaveDoneHelper::~SessionSaveDoneHelper() + { + close(); + } + +void SessionSaveDoneHelper::close() + { + if( conn != NULL ) + { + delete notifier; + SmcCloseConnection( conn, 0, NULL ); + } + conn = NULL; + } + +void SessionSaveDoneHelper::processData() + { + if( conn != NULL ) + IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 ); + } + +} // namespace + +#include "sm.moc" diff --git a/kwin/sm.h b/kwin/sm.h new file mode 100644 index 000000000..0a23b7bad --- /dev/null +++ b/kwin/sm.h @@ -0,0 +1,88 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_SM_H +#define KWIN_SM_H + +#include <X11/SM/SMlib.h> +#include <kapplication.h> +#include <netwm_def.h> + +class QSocketNotifier; + +namespace KWinInternal +{ + +struct SessionInfo + { + QCString sessionId; + QCString windowRole; + QCString wmCommand; + QCString wmClientMachine; + QCString resourceName; + QCString resourceClass; + + QRect geometry; + QRect restore; + QRect fsrestore; + int maximized; + int fullscreen; + int desktop; + bool minimized; + bool onAllDesktops; + bool shaded; + bool keepAbove; + bool keepBelow; + bool skipTaskbar; + bool skipPager; + bool userNoBorder; + NET::WindowType windowType; + QString shortcut; + bool active; // means 'was active in the saved session' + }; + + +enum SMSavePhase + { + SMSavePhase0, // saving global state in "phase 0" + SMSavePhase2, // saving window state in phase 2 + SMSavePhase2Full // complete saving in phase2, there was no phase 0 + }; + +class SessionSaveDoneHelper + : public QObject + { + Q_OBJECT + public: + SessionSaveDoneHelper(); + virtual ~SessionSaveDoneHelper(); + SmcConn connection() const { return conn; } + void saveDone(); + void close(); + private slots: + void processData(); + private: + QSocketNotifier* notifier; + SmcConn conn; + }; + + +class SessionManaged + : public KSessionManaged + { + public: + virtual bool saveState( QSessionManager& sm ); + virtual bool commitData( QSessionManager& sm ); + }; + +} // namespace + +#endif diff --git a/kwin/tabbox.cpp b/kwin/tabbox.cpp new file mode 100644 index 000000000..96440e7af --- /dev/null +++ b/kwin/tabbox.cpp @@ -0,0 +1,1338 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#define QT_CLEAN_NAMESPACE +#include "tabbox.h" +#include "workspace.h" +#include "client.h" +#include <qpainter.h> +#include <qlabel.h> +#include <qdrawutil.h> +#include <qstyle.h> +#include <kglobal.h> +#include <fixx11h.h> +#include <kconfig.h> +#include <klocale.h> +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qcursor.h> +#include <kstringhandler.h> +#include <stdarg.h> +#include <kdebug.h> +#include <kglobalaccel.h> +#include <kkeynative.h> +#include <kglobalsettings.h> +#include <kiconeffect.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> + +// specify externals before namespace + +extern Time qt_x_time; + +namespace KWinInternal +{ + +extern QPixmap* kwin_get_menu_pix_hack(); + +TabBox::TabBox( Workspace *ws, const char *name ) + : QFrame( 0, name, Qt::WNoAutoErase ), current_client( NULL ), wspace(ws) + { + setFrameStyle(QFrame::StyledPanel | QFrame::Plain); + setLineWidth(2); + setMargin(2); + + showMiniIcon = false; + + no_tasks = i18n("*** No Windows ***"); + m = DesktopMode; // init variables + reconfigure(); + reset(); + connect(&delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); + + XSetWindowAttributes attr; + attr.override_redirect = 1; + outline_left = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_right = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_top = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_bottom = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + } + +TabBox::~TabBox() + { + XDestroyWindow( qt_xdisplay(), outline_left ); + XDestroyWindow( qt_xdisplay(), outline_right ); + XDestroyWindow( qt_xdisplay(), outline_top ); + XDestroyWindow( qt_xdisplay(), outline_bottom ); + } + + +/*! + Sets the current mode to \a mode, either DesktopListMode or WindowsMode + + \sa mode() + */ +void TabBox::setMode( Mode mode ) + { + m = mode; + } + + +/*! + Create list of clients on specified desktop, starting with client c +*/ +void TabBox::createClientList(ClientList &list, int desktop /*-1 = all*/, Client *c, bool chain) + { + ClientList::size_type idx = 0; + + list.clear(); + + Client* start = c; + + if ( chain ) + c = workspace()->nextFocusChainClient(c); + else + c = workspace()->stackingOrder().first(); + + Client* stop = c; + + while ( c ) + { + if ( ((desktop == -1) || c->isOnDesktop(desktop)) + && c->wantsTabFocus() ) + { + if ( start == c ) + { + list.remove( c ); + list.prepend( c ); + } + else + { // don't add windows that have modal dialogs + Client* modal = c->findModal(); + if( modal == NULL || modal == c ) + list += c; + else if( !list.contains( modal )) + list += modal; + else + { + // nothing + } + } + } + + if ( chain ) + c = workspace()->nextFocusChainClient( c ); + else + { + if ( idx >= (workspace()->stackingOrder().size()-1) ) + c = 0; + else + c = workspace()->stackingOrder()[++idx]; + } + + if ( c == stop ) + break; + } + } + + +/*! + Resets the tab box to display the active client in WindowsMode, or the + current desktop in DesktopListMode + */ +void TabBox::reset() + { + int w, h, cw = 0, wmax = 0; + + QRect r = KGlobalSettings::desktopGeometry(QCursor::pos()); + + // calculate height of 1 line + // fontheight + 1 pixel above + 1 pixel below, or 32x32 icon + 2 pixel above + below + lineHeight = QMAX(fontMetrics().height() + 2, 32 + 4); + + if ( mode() == WindowsMode ) + { + setCurrentClient( workspace()->activeClient()); + + // get all clients to show + createClientList(clients, options_traverse_all ? -1 : workspace()->currentDesktop(), current_client, true); + + // calculate maximum caption width + cw = fontMetrics().width(no_tasks)+20; + for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + cw = fontMetrics().width( (*it)->caption() ); + if ( cw > wmax ) wmax = cw; + } + + // calculate height for the popup + if ( clients.count() == 0 ) // height for the "not tasks" text + { + QFont f = font(); + f.setBold( TRUE ); + f.setPointSize( 14 ); + + h = QFontMetrics(f).height()*4; + } + else + { + showMiniIcon = false; + h = clients.count() * lineHeight; + + if ( h > (r.height()-(2*frameWidth())) ) // if too high, use mini icons + { + showMiniIcon = true; + // fontheight + 1 pixel above + 1 pixel below, or 16x16 icon + 1 pixel above + below + lineHeight = QMAX(fontMetrics().height() + 2, 16 + 2); + + h = clients.count() * lineHeight; + + if ( h > (r.height()-(2*frameWidth())) ) // if still too high, remove some clients + { + // how many clients to remove + int howMany = (h - (r.height()-(2*frameWidth())))/lineHeight; + for (; howMany; howMany--) + clients.remove(clients.last()); + + h = clients.count() * lineHeight; + } + } + } + } + else + { // DesktopListMode + showMiniIcon = false; + desk = workspace()->currentDesktop(); + + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + cw = fontMetrics().width( workspace()->desktopName(i) ); + if ( cw > wmax ) wmax = cw; + } + + // calculate height for the popup (max. 16 desktops always fit in a 800x600 screen) + h = workspace()->numberOfDesktops() * lineHeight; + } + + // height, width for the popup + h += 2 * frameWidth(); + w = 2*frameWidth() + 5*2 + ( showMiniIcon ? 16 : 32 ) + 8 + wmax; // 5*2=margins, ()=icon, 8=space between icon+text + w = kClamp( w, r.width()/3 , r.width() * 4 / 5 ); + + setGeometry( (r.width()-w)/2 + r.x(), + (r.height()-h)/2+ r.y(), + w, h ); + } + + +/*! + Shows the next or previous item, depending on \a next + */ +void TabBox::nextPrev( bool next) + { + if ( mode() == WindowsMode ) + { + Client* firstClient = NULL; + Client* client = current_client; + do + { + if ( next ) + client = workspace()->nextFocusChainClient(client); + else + client = workspace()->previousFocusChainClient(client); + if (!firstClient) + { + // When we see our first client for the second time, + // it's time to stop. + firstClient = client; + } + else if (client == firstClient) + { + // No candidates found. + client = 0; + break; + } + } while ( client && !clients.contains( client )); + setCurrentClient( client ); + } + else if( mode() == DesktopMode ) + { + if ( next ) + desk = workspace()->nextDesktopFocusChain( desk ); + else + desk = workspace()->previousDesktopFocusChain( desk ); + } + else + { // DesktopListMode + if ( next ) + { + desk++; + if ( desk > workspace()->numberOfDesktops() ) + desk = 1; + } + else + { + desk--; + if ( desk < 1 ) + desk = workspace()->numberOfDesktops(); + } + } + + update(); + } + + + +/*! + Returns the currently displayed client ( only works in WindowsMode ). + Returns 0 if no client is displayed. + */ +Client* TabBox::currentClient() + { + if ( mode() != WindowsMode ) + return 0; + if (!workspace()->hasClient( current_client )) + return 0; + return current_client; + } + +void TabBox::setCurrentClient( Client* c ) + { + if( current_client != c ) + { + current_client = c; + updateOutline(); + } + } + +/*! + Returns the currently displayed virtual desktop ( only works in + DesktopListMode ) + Returns -1 if no desktop is displayed. + */ +int TabBox::currentDesktop() + { + if ( mode() == DesktopListMode || mode() == DesktopMode ) + return desk; + else + return -1; + } + + +/*! + Reimplemented to raise the tab box as well + */ +void TabBox::showEvent( QShowEvent* ) + { + updateOutline(); + XRaiseWindow( qt_xdisplay(), outline_left ); + XRaiseWindow( qt_xdisplay(), outline_right ); + XRaiseWindow( qt_xdisplay(), outline_top ); + XRaiseWindow( qt_xdisplay(), outline_bottom ); + raise(); + } + + +/*! + hide the icon box if necessary + */ +void TabBox::hideEvent( QHideEvent* ) + { + XUnmapWindow( qt_xdisplay(), outline_left ); + XUnmapWindow( qt_xdisplay(), outline_right ); + XUnmapWindow( qt_xdisplay(), outline_top ); + XUnmapWindow( qt_xdisplay(), outline_bottom ); + } + +/*! + Paints the tab box + */ +void TabBox::drawContents( QPainter * ) + { + QRect r(contentsRect()); + QPixmap pix(r.size()); // do double buffering to avoid flickers + pix.fill(this, 0, 0); + + QPainter p; + p.begin(&pix, this); + + QPixmap* menu_pix = kwin_get_menu_pix_hack(); + + int iconWidth = showMiniIcon ? 16 : 32; + int x = 0; + int y = 0; + + if ( mode () == WindowsMode ) + { + if ( !currentClient() ) + { + QFont f = font(); + f.setBold( TRUE ); + f.setPointSize( 14 ); + + p.setFont(f); + p.drawText( r, AlignCenter, no_tasks); + } + else + { + for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + if ( workspace()->hasClient( *it ) ) // safety + { + // draw highlight background + if ( (*it) == current_client ) + p.fillRect(x, y, r.width(), lineHeight, colorGroup().highlight()); + + // draw icon + QPixmap icon; + if ( showMiniIcon ) + { + if ( !(*it)->miniIcon().isNull() ) + icon = (*it)->miniIcon(); + } + else + if ( !(*it)->icon().isNull() ) + icon = (*it)->icon(); + else if ( menu_pix ) + icon = *menu_pix; + + if( !icon.isNull()) + { + if( (*it)->isMinimized()) + KIconEffect::semiTransparent( icon ); + p.drawPixmap( x+5, y + (lineHeight - iconWidth)/2, icon ); + } + + // generate text to display + QString s; + + if ( !(*it)->isOnDesktop(workspace()->currentDesktop()) ) + s = workspace()->desktopName((*it)->desktop()) + ": "; + + if ( (*it)->isMinimized() ) + s += QString("(") + (*it)->caption() + ")"; + else + s += (*it)->caption(); + + s = KStringHandler::cPixelSqueeze(s, fontMetrics(), r.width() - 5 - iconWidth - 8); + + // draw text + if ( (*it) == current_client ) + p.setPen(colorGroup().highlightedText()); + else if( (*it)->isMinimized()) + { + QColor c1 = colorGroup().text(); + QColor c2 = colorGroup().background(); + // from kicker's TaskContainer::blendColors() + int r1, g1, b1; + int r2, g2, b2; + + c1.rgb( &r1, &g1, &b1 ); + c2.rgb( &r2, &g2, &b2 ); + + r1 += (int) ( .5 * ( r2 - r1 ) ); + g1 += (int) ( .5 * ( g2 - g1 ) ); + b1 += (int) ( .5 * ( b2 - b1 ) ); + + p.setPen(QColor( r1, g1, b1 )); + } + else + p.setPen(colorGroup().text()); + + p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, + Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, s); + + y += lineHeight; + } + if ( y >= r.height() ) break; + } + } + } + else + { // DesktopMode || DesktopListMode + int iconHeight = iconWidth; + + // get widest desktop name/number + QFont f(font()); + f.setBold(true); + f.setPixelSize(iconHeight - 4); // pixel, not point because I need to know the pixels + QFontMetrics fm(f); + + int wmax = 0; + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + wmax = QMAX(wmax, fontMetrics().width(workspace()->desktopName(i))); + + // calculate max width of desktop-number text + QString num = QString::number(i); + iconWidth = QMAX(iconWidth - 4, fm.boundingRect(num).width()) + 4; + } + + // In DesktopMode, start at the current desktop + // In DesktopListMode, start at desktop #1 + int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + // draw highlight background + if ( iDesktop == desk ) // current desktop + p.fillRect(x, y, r.width(), lineHeight, colorGroup().highlight()); + + p.save(); + + // draw "icon" (here: number of desktop) + p.fillRect(x+5, y+2, iconWidth, iconHeight, colorGroup().base()); + p.setPen(colorGroup().text()); + p.drawRect(x+5, y+2, iconWidth, iconHeight); + + // draw desktop-number + p.setFont(f); + QString num = QString::number(iDesktop); + p.drawText(x+5, y+2, iconWidth, iconHeight, Qt::AlignCenter, num); + + p.restore(); + + // draw desktop name text + if ( iDesktop == desk ) + p.setPen(colorGroup().highlightedText()); + else + p.setPen(colorGroup().text()); + + p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, + Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, + workspace()->desktopName(iDesktop)); + + // show mini icons from that desktop aligned to each other + int x1 = x + 5 + iconWidth + 8 + wmax + 5; + + ClientList list; + createClientList(list, iDesktop, 0, false); + // clients are in reversed stacking order + for (ClientList::ConstIterator it = list.fromLast(); it != list.end(); --it) + { + if ( !(*it)->miniIcon().isNull() ) + { + if ( x1+18 >= x+r.width() ) // only show full icons + break; + + p.drawPixmap( x1, y + (lineHeight - 16)/2, (*it)->miniIcon() ); + x1 += 18; + } + } + + // next desktop + y += lineHeight; + if ( y >= r.height() ) break; + + if( mode() == DesktopMode ) + iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); + else + iDesktop++; + } + } + p.end(); + bitBlt(this, r.x(), r.y(), &pix); + } + +void TabBox::updateOutline() + { + Client* c = currentClient(); + if( !options->tabboxOutline || c == NULL || this->isHidden() || !c->isShown( true ) || !c->isOnCurrentDesktop()) + { + XUnmapWindow( qt_xdisplay(), outline_left ); + XUnmapWindow( qt_xdisplay(), outline_right ); + XUnmapWindow( qt_xdisplay(), outline_top ); + XUnmapWindow( qt_xdisplay(), outline_bottom ); + return; + } + // left/right parts are between top/bottom, they don't reach as far as the corners + XMoveResizeWindow( qt_xdisplay(), outline_left, c->x(), c->y() + 5, 5, c->height() - 10 ); + XMoveResizeWindow( qt_xdisplay(), outline_right, c->x() + c->width() - 5, c->y() + 5, 5, c->height() - 10 ); + XMoveResizeWindow( qt_xdisplay(), outline_top, c->x(), c->y(), c->width(), 5 ); + XMoveResizeWindow( qt_xdisplay(), outline_bottom, c->x(), c->y() + c->height() - 5, c->width(), 5 ); + { + QPixmap pix( 5, c->height() - 10 ); + QPainter p( &pix ); + p.setPen( white ); + p.drawLine( 0, 0, 0, pix.height() - 1 ); + p.drawLine( 4, 0, 4, pix.height() - 1 ); + p.setPen( gray ); + p.drawLine( 1, 0, 1, pix.height() - 1 ); + p.drawLine( 3, 0, 3, pix.height() - 1 ); + p.setPen( black ); + p.drawLine( 2, 0, 2, pix.height() - 1 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_left, pix.handle()); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_right, pix.handle()); + } + { + QPixmap pix( c->width(), 5 ); + QPainter p( &pix ); + p.setPen( white ); + p.drawLine( 0, 0, pix.width() - 1 - 0, 0 ); + p.drawLine( 4, 4, pix.width() - 1 - 4, 4 ); + p.drawLine( 0, 0, 0, 4 ); + p.drawLine( pix.width() - 1 - 0, 0, pix.width() - 1 - 0, 4 ); + p.setPen( gray ); + p.drawLine( 1, 1, pix.width() - 1 - 1, 1 ); + p.drawLine( 3, 3, pix.width() - 1 - 3, 3 ); + p.drawLine( 1, 1, 1, 4 ); + p.drawLine( 3, 3, 3, 4 ); + p.drawLine( pix.width() - 1 - 1, 1, pix.width() - 1 - 1, 4 ); + p.drawLine( pix.width() - 1 - 3, 3, pix.width() - 1 - 3, 4 ); + p.setPen( black ); + p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); + p.drawLine( 2, 2, 2, 4 ); + p.drawLine( pix.width() - 1 - 2, 2, pix.width() - 1 - 2, 4 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_top, pix.handle()); + } + { + QPixmap pix( c->width(), 5 ); + QPainter p( &pix ); + p.setPen( white ); + p.drawLine( 4, 0, pix.width() - 1 - 4, 0 ); + p.drawLine( 0, 4, pix.width() - 1 - 0, 4 ); + p.drawLine( 0, 4, 0, 0 ); + p.drawLine( pix.width() - 1 - 0, 4, pix.width() - 1 - 0, 0 ); + p.setPen( gray ); + p.drawLine( 3, 1, pix.width() - 1 - 3, 1 ); + p.drawLine( 1, 3, pix.width() - 1 - 1, 3 ); + p.drawLine( 3, 1, 3, 0 ); + p.drawLine( 1, 3, 1, 0 ); + p.drawLine( pix.width() - 1 - 3, 1, pix.width() - 1 - 3, 0 ); + p.drawLine( pix.width() - 1 - 1, 3, pix.width() - 1 - 1, 0 ); + p.setPen( black ); + p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); + p.drawLine( 2, 0, 2, 2 ); + p.drawLine( pix.width() - 1 - 2, 0, pix.width() - 1 - 2, 2 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_bottom, pix.handle()); + } + XClearWindow( qt_xdisplay(), outline_left ); + XClearWindow( qt_xdisplay(), outline_right ); + XClearWindow( qt_xdisplay(), outline_top ); + XClearWindow( qt_xdisplay(), outline_bottom ); + XMapWindow( qt_xdisplay(), outline_left ); + XMapWindow( qt_xdisplay(), outline_right ); + XMapWindow( qt_xdisplay(), outline_top ); + XMapWindow( qt_xdisplay(), outline_bottom ); + } + +void TabBox::hide() + { + delayedShowTimer.stop(); + QWidget::hide(); + QApplication::syncX(); + XEvent otherEvent; + while (XCheckTypedEvent (qt_xdisplay(), EnterNotify, &otherEvent ) ) + ; + } + + +void TabBox::reconfigure() + { + KConfig * c(KGlobal::config()); + c->setGroup("TabBox"); + options_traverse_all = c->readBoolEntry("TraverseAll", false ); + } + +/*! + Rikkus: please document! (Matthias) + + Ok, here's the docs :) + + You call delayedShow() instead of show() directly. + + If the 'ShowDelay' setting is false, show() is simply called. + + Otherwise, we start a timer for the delay given in the settings and only + do a show() when it times out. + + This means that you can alt-tab between windows and you don't see the + tab box immediately. Not only does this make alt-tabbing faster, it gives + less 'flicker' to the eyes. You don't need to see the tab box if you're + just quickly switching between 2 or 3 windows. It seems to work quite + nicely. + */ +void TabBox::delayedShow() + { + KConfig * c(KGlobal::config()); + c->setGroup("TabBox"); + bool delay = c->readBoolEntry("ShowDelay", true); + + if (!delay) + { + show(); + return; + } + + int delayTime = c->readNumEntry("DelayTime", 90); + delayedShowTimer.start(delayTime, true); + } + + +void TabBox::handleMouseEvent( XEvent* e ) + { + XAllowEvents( qt_xdisplay(), AsyncPointer, qt_x_time ); + if( e->type != ButtonPress ) + return; + QPoint pos( e->xbutton.x_root, e->xbutton.y_root ); + if( !geometry().contains( pos )) + { + workspace()->closeTabBox(); // click outside closes tab + return; + } + pos.rx() -= x(); // pos is now inside tabbox + pos.ry() -= y(); + int num = (pos.y()-frameWidth()) / lineHeight; + + if( mode() == WindowsMode ) + { + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( workspace()->hasClient( *it ) && (num == 0) ) // safety + { + setCurrentClient( *it ); + break; + } + num--; + } + } + else + { + int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; + for( int i = 1; + i <= workspace()->numberOfDesktops(); + ++i ) + { + if( num == 0 ) + { + desk = iDesktop; + break; + } + num--; + if( mode() == DesktopMode ) + iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); + else + iDesktop++; + } + } + update(); + } + +//******************************* +// Workspace +//******************************* + + +/*! + Handles alt-tab / control-tab + */ + +static +bool areKeySymXsDepressed( bool bAll, const uint keySyms[], int nKeySyms ) + { + char keymap[32]; + + kdDebug(125) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms << endl; + + XQueryKeymap( qt_xdisplay(), keymap ); + + for( int iKeySym = 0; iKeySym < nKeySyms; iKeySym++ ) + { + uint keySymX = keySyms[ iKeySym ]; + uchar keyCodeX = XKeysymToKeycode( qt_xdisplay(), keySymX ); + int i = keyCodeX / 8; + char mask = 1 << (keyCodeX - (i * 8)); + + kdDebug(125) << iKeySym << ": keySymX=0x" << QString::number( keySymX, 16 ) + << " i=" << i << " mask=0x" << QString::number( mask, 16 ) + << " keymap[i]=0x" << QString::number( keymap[i], 16 ) << endl; + + // Abort if bad index value, + if( i < 0 || i >= 32 ) + return false; + + // If ALL keys passed need to be depressed, + if( bAll ) + { + if( (keymap[i] & mask) == 0 ) + return false; + } + else + { + // If we are looking for ANY key press, and this key is depressed, + if( keymap[i] & mask ) + return true; + } + } + + // If we were looking for ANY key press, then none was found, return false, + // If we were looking for ALL key presses, then all were found, return true. + return bAll; + } + +static bool areModKeysDepressed( const KKeySequence& seq ) + { + uint rgKeySyms[10]; + int nKeySyms = 0; + if( seq.isNull()) + return false; + int mod = seq.key(seq.count()-1).modFlags(); + + if ( mod & KKey::SHIFT ) + { + rgKeySyms[nKeySyms++] = XK_Shift_L; + rgKeySyms[nKeySyms++] = XK_Shift_R; + } + if ( mod & KKey::CTRL ) + { + rgKeySyms[nKeySyms++] = XK_Control_L; + rgKeySyms[nKeySyms++] = XK_Control_R; + } + if( mod & KKey::ALT ) + { + rgKeySyms[nKeySyms++] = XK_Alt_L; + rgKeySyms[nKeySyms++] = XK_Alt_R; + } + if( mod & KKey::WIN ) + { + // It would take some code to determine whether the Win key + // is associated with Super or Meta, so check for both. + // See bug #140023 for details. + rgKeySyms[nKeySyms++] = XK_Super_L; + rgKeySyms[nKeySyms++] = XK_Super_R; + rgKeySyms[nKeySyms++] = XK_Meta_L; + rgKeySyms[nKeySyms++] = XK_Meta_R; + } + + return areKeySymXsDepressed( false, rgKeySyms, nKeySyms ); + } + +static bool areModKeysDepressed( const KShortcut& cut ) + { + for( unsigned int i = 0; + i < cut.count(); + ++i ) + { + if( areModKeysDepressed( cut.seq( i ))) + return true; + } + return false; + } + +void Workspace::slotWalkThroughWindows() + { + if ( root != qt_xrootwin() ) + return; + if ( tab_grab || control_grab ) + return; + if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) + { + //XUngrabKeyboard(qt_xdisplay(), qt_x_time); // need that because of accelerator raw mode + // CDE style raise / lower + CDEWalkThroughWindows( true ); + } + else + { + if ( areModKeysDepressed( cutWalkThroughWindows ) ) + { + if ( startKDEWalkThroughWindows() ) + KDEWalkThroughWindows( true ); + } + else + // if the shortcut has no modifiers, don't show the tabbox, + // don't grab, but simply go to the next window + KDEOneStepThroughWindows( true ); + } + } + +void Workspace::slotWalkBackThroughWindows() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) + { + // CDE style raise / lower + CDEWalkThroughWindows( false ); + } + else + { + if ( areModKeysDepressed( cutWalkThroughWindowsReverse ) ) + { + if ( startKDEWalkThroughWindows() ) + KDEWalkThroughWindows( false ); + } + else + { + KDEOneStepThroughWindows( false ); + } + } + } + +void Workspace::slotWalkThroughDesktops() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktops ) ) + { + if ( startWalkThroughDesktops() ) + walkThroughDesktops( true ); + } + else + { + oneStepThroughDesktops( true ); + } + } + +void Workspace::slotWalkBackThroughDesktops() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopsReverse ) ) + { + if ( startWalkThroughDesktops() ) + walkThroughDesktops( false ); + } + else + { + oneStepThroughDesktops( false ); + } + } + +void Workspace::slotWalkThroughDesktopList() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopList ) ) + { + if ( startWalkThroughDesktopList() ) + walkThroughDesktops( true ); + } + else + { + oneStepThroughDesktopList( true ); + } + } + +void Workspace::slotWalkBackThroughDesktopList() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopListReverse ) ) + { + if ( startWalkThroughDesktopList() ) + walkThroughDesktops( false ); + } + else + { + oneStepThroughDesktopList( false ); + } + } + +bool Workspace::startKDEWalkThroughWindows() + { + if( !establishTabBoxGrab()) + return false; + tab_grab = TRUE; + keys->suspend( true ); + disable_shortcuts_keys->suspend( true ); + client_keys->suspend( true ); + tab_box->setMode( TabBox::WindowsMode ); + tab_box->reset(); + return TRUE; + } + +bool Workspace::startWalkThroughDesktops( int mode ) + { + if( !establishTabBoxGrab()) + return false; + control_grab = TRUE; + keys->suspend( true ); + disable_shortcuts_keys->suspend( true ); + client_keys->suspend( true ); + tab_box->setMode( (TabBox::Mode) mode ); + tab_box->reset(); + return TRUE; + } + +bool Workspace::startWalkThroughDesktops() + { + return startWalkThroughDesktops( TabBox::DesktopMode ); + } + +bool Workspace::startWalkThroughDesktopList() + { + return startWalkThroughDesktops( TabBox::DesktopListMode ); + } + +void Workspace::KDEWalkThroughWindows( bool forward ) + { + tab_box->nextPrev( forward ); + tab_box->delayedShow(); + } + +void Workspace::walkThroughDesktops( bool forward ) + { + tab_box->nextPrev( forward ); + tab_box->delayedShow(); + } + +void Workspace::CDEWalkThroughWindows( bool forward ) + { + Client* c = NULL; +// this function find the first suitable client for unreasonable focus +// policies - the topmost one, with some exceptions (can't be keepabove/below, +// otherwise it gets stuck on them) + Q_ASSERT( block_stacking_updates == 0 ); + for( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); + --it ) + { + if ( (*it)->isOnCurrentDesktop() && !(*it)->isSpecialWindow() + && (*it)->isShown( false ) && (*it)->wantsTabFocus() + && !(*it)->keepAbove() && !(*it)->keepBelow()) + { + c = *it; + break; + } + } + Client* nc = c; + bool options_traverse_all; + { + KConfigGroupSaver saver( KGlobal::config(), "TabBox" ); + options_traverse_all = KGlobal::config()->readBoolEntry("TraverseAll", false ); + } + + Client* firstClient = 0; + do + { + nc = forward ? nextStaticClient(nc) : previousStaticClient(nc); + if (!firstClient) + { + // When we see our first client for the second time, + // it's time to stop. + firstClient = nc; + } + else if (nc == firstClient) + { + // No candidates found. + nc = 0; + break; + } + } while (nc && nc != c && + (( !options_traverse_all && !nc->isOnDesktop(currentDesktop())) || + nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() ) ); + if (nc) + { + if (c && c != nc) + lowerClient( c ); + if ( options->focusPolicyIsReasonable() ) + { + activateClient( nc ); + if( nc->isShade() && options->shadeHover ) + nc->setShade( ShadeActivated ); + } + else + { + if( !nc->isOnDesktop( currentDesktop())) + setCurrentDesktop( nc->desktop()); + raiseClient( nc ); + } + } + } + +void Workspace::KDEOneStepThroughWindows( bool forward ) + { + tab_box->setMode( TabBox::WindowsMode ); + tab_box->reset(); + tab_box->nextPrev( forward ); + if( Client* c = tab_box->currentClient() ) + { + activateClient( c ); + if( c->isShade() && options->shadeHover ) + c->setShade( ShadeActivated ); + } + } + +void Workspace::oneStepThroughDesktops( bool forward, int mode ) + { + tab_box->setMode( (TabBox::Mode) mode ); + tab_box->reset(); + tab_box->nextPrev( forward ); + if ( tab_box->currentDesktop() != -1 ) + setCurrentDesktop( tab_box->currentDesktop() ); + } + +void Workspace::oneStepThroughDesktops( bool forward ) + { + oneStepThroughDesktops( forward, TabBox::DesktopMode ); + } + +void Workspace::oneStepThroughDesktopList( bool forward ) + { + oneStepThroughDesktops( forward, TabBox::DesktopListMode ); + } + +/*! + Handles holding alt-tab / control-tab + */ +void Workspace::tabBoxKeyPress( const KKeyNative& keyX ) + { + bool forward = false; + bool backward = false; + + if (tab_grab) + { + forward = cutWalkThroughWindows.contains( keyX ); + backward = cutWalkThroughWindowsReverse.contains( keyX ); + if (forward || backward) + { + kdDebug(125) << "== " << cutWalkThroughWindows.toStringInternal() + << " or " << cutWalkThroughWindowsReverse.toStringInternal() << endl; + KDEWalkThroughWindows( forward ); + } + } + else if (control_grab) + { + forward = cutWalkThroughDesktops.contains( keyX ) || + cutWalkThroughDesktopList.contains( keyX ); + backward = cutWalkThroughDesktopsReverse.contains( keyX ) || + cutWalkThroughDesktopListReverse.contains( keyX ); + if (forward || backward) + walkThroughDesktops(forward); + } + + if (control_grab || tab_grab) + { + uint keyQt = keyX.keyCodeQt(); + if ( ((keyQt & 0xffff) == Qt::Key_Escape) + && !(forward || backward) ) + { // if Escape is part of the shortcut, don't cancel + closeTabBox(); + } + } + } + +void Workspace::closeTabBox() + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + tab_grab = FALSE; + control_grab = FALSE; + } + +/*! + Handles alt-tab / control-tab releasing + */ +void Workspace::tabBoxKeyRelease( const XKeyEvent& ev ) + { + unsigned int mk = ev.state & + (KKeyNative::modX(KKey::SHIFT) | + KKeyNative::modX(KKey::CTRL) | + KKeyNative::modX(KKey::ALT) | + KKeyNative::modX(KKey::WIN)); + // ev.state is state before the key release, so just checking mk being 0 isn't enough + // using XQueryPointer() also doesn't seem to work well, so the check that all + // modifiers are released: only one modifier is active and the currently released + // key is this modifier - if yes, release the grab + int mod_index = -1; + for( int i = ShiftMapIndex; + i <= Mod5MapIndex; + ++i ) + if(( mk & ( 1 << i )) != 0 ) + { + if( mod_index >= 0 ) + return; + mod_index = i; + } + bool release = false; + if( mod_index == -1 ) + release = true; + else + { + XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); + for (int i=0; i<xmk->max_keypermod; i++) + if (xmk->modifiermap[xmk->max_keypermod * mod_index + i] + == ev.keycode) + release = true; + XFreeModifiermap(xmk); + } + if( !release ) + return; + if (tab_grab) + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + tab_grab = false; + if( Client* c = tab_box->currentClient()) + { + activateClient( c ); + if( c->isShade() && options->shadeHover ) + c->setShade( ShadeActivated ); + } + } + if (control_grab) + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + control_grab = False; + if ( tab_box->currentDesktop() != -1 ) + { + setCurrentDesktop( tab_box->currentDesktop() ); + } + } + } + + +int Workspace::nextDesktopFocusChain( int iDesktop ) const + { + int i = desktop_focus_chain.find( iDesktop ); + if( i >= 0 && i+1 < (int)desktop_focus_chain.size() ) + return desktop_focus_chain[i+1]; + else if( desktop_focus_chain.size() > 0 ) + return desktop_focus_chain[ 0 ]; + else + return 1; + } + +int Workspace::previousDesktopFocusChain( int iDesktop ) const + { + int i = desktop_focus_chain.find( iDesktop ); + if( i-1 >= 0 ) + return desktop_focus_chain[i-1]; + else if( desktop_focus_chain.size() > 0 ) + return desktop_focus_chain[desktop_focus_chain.size()-1]; + else + return numberOfDesktops(); + } + +/*! + auxiliary functions to travers all clients according the focus + order. Useful for kwms Alt-tab feature. +*/ +Client* Workspace::nextFocusChainClient( Client* c ) const + { + if ( global_focus_chain.isEmpty() ) + return 0; + ClientList::ConstIterator it = global_focus_chain.find( c ); + if ( it == global_focus_chain.end() ) + return global_focus_chain.last(); + if ( it == global_focus_chain.begin() ) + return global_focus_chain.last(); + --it; + return *it; + } + +/*! + auxiliary functions to travers all clients according the focus + order. Useful for kwms Alt-tab feature. +*/ +Client* Workspace::previousFocusChainClient( Client* c ) const + { + if ( global_focus_chain.isEmpty() ) + return 0; + ClientList::ConstIterator it = global_focus_chain.find( c ); + if ( it == global_focus_chain.end() ) + return global_focus_chain.first(); + ++it; + if ( it == global_focus_chain.end() ) + return global_focus_chain.first(); + return *it; + } + +/*! + auxiliary functions to travers all clients according the static + order. Useful for the CDE-style Alt-tab feature. +*/ +Client* Workspace::nextStaticClient( Client* c ) const + { + if ( !c || clients.isEmpty() ) + return 0; + ClientList::ConstIterator it = clients.find( c ); + if ( it == clients.end() ) + return clients.first(); + ++it; + if ( it == clients.end() ) + return clients.first(); + return *it; + } +/*! + auxiliary functions to travers all clients according the static + order. Useful for the CDE-style Alt-tab feature. +*/ +Client* Workspace::previousStaticClient( Client* c ) const + { + if ( !c || clients.isEmpty() ) + return 0; + ClientList::ConstIterator it = clients.find( c ); + if ( it == clients.end() ) + return clients.last(); + if ( it == clients.begin() ) + return clients.last(); + --it; + return *it; + } + +bool Workspace::establishTabBoxGrab() + { + if( XGrabKeyboard( qt_xdisplay(), root, FALSE, + GrabModeAsync, GrabModeAsync, qt_x_time) != GrabSuccess ) + return false; + // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent + // using Alt+Tab while DND (#44972). However force passive grabs on all windows + // in order to catch MouseRelease events and close the tabbox (#67416). + // All clients already have passive grabs in their wrapper windows, so check only + // the active client, which may not have it. + assert( !forced_global_mouse_grab ); + forced_global_mouse_grab = true; + if( active_client != NULL ) + active_client->updateMouseGrab(); + return true; + } + +void Workspace::removeTabBoxGrab() + { + XUngrabKeyboard(qt_xdisplay(), qt_x_time); + assert( forced_global_mouse_grab ); + forced_global_mouse_grab = false; + if( active_client != NULL ) + active_client->updateMouseGrab(); + } + +} // namespace + +#include "tabbox.moc" diff --git a/kwin/tabbox.h b/kwin/tabbox.h new file mode 100644 index 000000000..b2d8e4bc0 --- /dev/null +++ b/kwin/tabbox.h @@ -0,0 +1,102 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_TABBOX_H +#define KWIN_TABBOX_H + +#include <qframe.h> +#include <qtimer.h> +#include <qvaluelist.h> +#include "utils.h" + +class QLabel; + +namespace KWinInternal +{ + +class Workspace; +class Client; + +class TabBox : public QFrame + { + Q_OBJECT + public: + TabBox( Workspace *ws, const char *name=0 ); + ~TabBox(); + + Client* currentClient(); + void setCurrentClient( Client* c ); + int currentDesktop(); + + // DesktopMode and WindowsMode are based on the order in which the desktop + // or window were viewed. + // DesktopListMode lists them in the order created. + enum Mode { DesktopMode, DesktopListMode, WindowsMode }; + void setMode( Mode mode ); + Mode mode() const; + + void reset(); + void nextPrev( bool next = TRUE); + + void delayedShow(); + void hide(); + + void handleMouseEvent( XEvent* ); + + Workspace* workspace() const; + + void reconfigure(); + + protected: + void showEvent( QShowEvent* ); + void hideEvent( QHideEvent* ); + void drawContents( QPainter * ); + + private: + void createClientList(ClientList &list, int desktop /*-1 = all*/, Client *start, bool chain); + void updateOutline(); + + private: + Client* current_client; + Mode m; + Workspace* wspace; + ClientList clients; + int desk; + int lineHeight; + bool showMiniIcon; + QTimer delayedShowTimer; + QString no_tasks; + bool options_traverse_all; + Window outline_left, outline_right, outline_top, outline_bottom; + }; + + +/*! + Returns the tab box' workspace + */ +inline Workspace* TabBox::workspace() const + { + return wspace; + } + +/*! + Returns the current mode, either DesktopListMode or WindowsMode + + \sa setMode() + */ +inline TabBox::Mode TabBox::mode() const + { + return m; + } + +} // namespace + +#endif diff --git a/kwin/tools/Makefile.am b/kwin/tools/Makefile.am new file mode 100644 index 000000000..da92d969d --- /dev/null +++ b/kwin/tools/Makefile.am @@ -0,0 +1,11 @@ +noinst_PROGRAMS = test_gravity + +SUBDIRS = decobenchmark + +INCLUDES = $(all_includes) + +test_gravity_SOURCES = test_gravity.cpp +test_gravity_LDFLAGS = $(all_libraries) $(KDE_RPATH) +test_gravity_LDADD = $(LIB_X11) + +METASOURCES = AUTO diff --git a/kwin/tools/decobenchmark/Makefile.am b/kwin/tools/decobenchmark/Makefile.am new file mode 100644 index 000000000..306c89971 --- /dev/null +++ b/kwin/tools/decobenchmark/Makefile.am @@ -0,0 +1,9 @@ +noinst_PROGRAMS = decobenchmark + +INCLUDES = $(all_includes) + +decobenchmark_SOURCES = main.cpp preview.cpp +decobenchmark_LDFLAGS = $(all_libraries) +decobenchmark_LDADD = ../../lib/libkdecorations.la + +METASOURCES = AUTO diff --git a/kwin/tools/decobenchmark/main.cpp b/kwin/tools/decobenchmark/main.cpp new file mode 100644 index 000000000..847fef69c --- /dev/null +++ b/kwin/tools/decobenchmark/main.cpp @@ -0,0 +1,138 @@ +/* + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright (c) 2005 Luciano Montanaro <mikelima@cirulla.net> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <qtimer.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <kdecoration_plugins_p.h> +#include <kdecorationfactory.h> + +#include <time.h> +#include <sys/timeb.h> +#include <iostream> + + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcmdlineargs.h> + +#include "preview.h" +#include "main.h" + +static KCmdLineOptions options[] = +{ + { "+decoration", "Decoration library to use, such as kwin3_plastik.", 0 }, + { "+tests", "Which test should be executed ('all', 'repaint', 'caption', 'resize', 'recreation')", 0 }, + { "+repetitions", "Number of test repetitions.", 0 }, + { 0, 0, 0 } +}; + +DecoBenchApplication::DecoBenchApplication(const QString &library, Tests tests, int count) : + m_tests(tests), + m_count(count) +{ + KConfig kwinConfig("kwinrc"); + kwinConfig.setGroup("Style"); + + plugins = new KDecorationPreviewPlugins( &kwinConfig ); + preview = new KDecorationPreview( plugins, 0 ); + + if (plugins->loadPlugin(library) ) + kdDebug() << "Decoration library " << library << " loaded..." << endl; + else + kdError() << "Error loading decoration library " << library << "!" << endl; + + if (preview->recreateDecoration() ) + kdDebug() << "Decoration created..." << endl; + else + kdError() << "Error creating decoration!" << endl; + + preview->show(); +} + +DecoBenchApplication::~DecoBenchApplication() +{ + delete preview; + delete plugins; +} + +void DecoBenchApplication::executeTest() +{ + clock_t stime = clock(); + timeb astart, aend; + ftime(&astart); + + if (m_tests == AllTests || m_tests == RepaintTest) + preview->performRepaintTest(m_count); + if (m_tests == AllTests || m_tests == CaptionTest) + preview->performCaptionTest(m_count); + if (m_tests == AllTests || m_tests == ResizeTest) + preview->performResizeTest(m_count); + if (m_tests == AllTests || m_tests == RecreationTest) + preview->performRecreationTest(m_count); + + clock_t etime = clock(); + ftime(&aend); + + long long time_diff = (aend.time - astart.time)*1000+aend.millitm - astart.millitm; + kdDebug() << "Total:" << (float(time_diff)/1000) << endl; + quit(); +} + +int main(int argc, char** argv) +{ + QString style = "keramik"; + // KApplication app(argc, argv); + KAboutData about("decobenchmark", "DecoBenchmark", "0.1", "kwin decoration performance tester...", KAboutData::License_LGPL, "(C) 2005 Sandro Giessl"); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->count() != 3) + KCmdLineArgs::usage("Wrong number of arguments!"); + + QString library = QString(args->arg(0) ); + QString t = QString(args->arg(1) ); + int count = QString(args->arg(2) ).toInt(); + + Tests test; + if (t == "all") + test = AllTests; + else if (t == "repaint") + test = RepaintTest; + else if (t == "caption") + test = CaptionTest; + else if (t == "resize") + test = ResizeTest; + else if (t == "recreation") + test = RecreationTest; + else + KCmdLineArgs::usage("Specify a valid test!"); + + DecoBenchApplication app(library, test, count); + + QTimer::singleShot(0, &app, SLOT(executeTest())); + app.exec(); +} +#include "main.moc" + +// kate: space-indent off; tab-width 4; diff --git a/kwin/tools/decobenchmark/main.h b/kwin/tools/decobenchmark/main.h new file mode 100644 index 000000000..4073238a1 --- /dev/null +++ b/kwin/tools/decobenchmark/main.h @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright (c) 2005 Luciano Montanaro <mikelima@cirulla.net> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef BENCH_MAIN_H +#define BENCH_MAIN_H + +enum Tests { + AllTests, + RepaintTest, + CaptionTest, + ResizeTest, + RecreationTest +}; + +class DecoBenchApplication : public KApplication +{ + Q_OBJECT +public: + DecoBenchApplication(const QString &library, Tests tests, int count); + ~DecoBenchApplication(); + +public slots: + void executeTest(); + +private: + KDecorationPreview *preview; + KDecorationPlugins *plugins; + Tests m_tests; + int m_count; +}; + +#endif // BENCH_MAIN_H + +// kate: space-indent off; tab-width 4; diff --git a/kwin/tools/decobenchmark/preview.cpp b/kwin/tools/decobenchmark/preview.cpp new file mode 100644 index 000000000..35301c513 --- /dev/null +++ b/kwin/tools/decobenchmark/preview.cpp @@ -0,0 +1,412 @@ +/* + * + * Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "preview.h" + +#include <kdebug.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> +#include <kglobal.h> +#include <qlabel.h> +#include <qstyle.h> +#include <kiconloader.h> + +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> + +#include <kdecorationfactory.h> +#include <kdecoration_plugins_p.h> + +// FRAME the preview doesn't update to reflect the changes done in the kcm + +KDecorationPreview::KDecorationPreview( KDecorationPlugins* plugin, QWidget* parent, const char* name ) + : QWidget( parent, name ), + m_plugin(plugin) +{ + options = new KDecorationPreviewOptions; + + bridge = new KDecorationPreviewBridge( this, true, "Deco Benchmark" ); + + deco = 0; + + setFixedSize( 600, 500 ); + + positionPreviews(); +} + +KDecorationPreview::~KDecorationPreview() +{ + delete deco; + delete bridge; + delete options; +} + +void KDecorationPreview::performRepaintTest(int n) +{ + kdDebug() << "start " << n << " repaints..." << endl; + bridge->setCaption("Deco Benchmark"); + deco->captionChange(); + positionPreviews(0); + for (int i = 0; i < n; ++i) { + deco->widget()->repaint(); + kapp->processEvents(); + } +} + +void KDecorationPreview::performCaptionTest(int n) +{ + kdDebug() << "start " << n << " caption changes..." << endl; + QString caption = "Deco Benchmark %1"; + positionPreviews(0); + for (int i = 0; i < n; ++i) { + bridge->setCaption(caption.arg(i) ); + deco->captionChange(); + deco->widget()->repaint(); + kapp->processEvents(); + } +} + +void KDecorationPreview::performResizeTest(int n) +{ + kdDebug() << "start " << n << " resizes..." << endl; + bridge->setCaption("Deco Benchmark"); + deco->captionChange(); + for (int i = 0; i < n; ++i) { + positionPreviews(i % 200); + kapp->processEvents(); + } +} + +void KDecorationPreview::performRecreationTest(int n) +{ + kdDebug() << "start " << n << " resizes..." << endl; + bridge->setCaption("Deco Benchmark"); + deco->captionChange(); + positionPreviews(0); + for (int i = 0; i < n; ++i) { + recreateDecoration(); + kapp->processEvents(); + } +} + +bool KDecorationPreview::recreateDecoration() +{ + delete deco; + deco = m_plugin->createDecoration(bridge); + deco->init(); + + if (!deco) + return false; + + positionPreviews(); + deco->widget()->show(); + + return true; +} + +void KDecorationPreview::positionPreviews(int shrink) +{ + if ( !deco ) + return; + + QSize size = QSize(width()-2*10-shrink, height()-2*10-shrink)/*.expandedTo(deco->minimumSize()*/; + + QRect geometry(QPoint(10, 10), size); + deco->widget()->setGeometry(geometry); +} + +void KDecorationPreview::setPreviewMask( const QRegion& reg, int mode ) +{ + QWidget *widget = deco->widget(); + + // FRAME duped from client.cpp + if( mode == Unsorted ) + { + XShapeCombineRegion( qt_xdisplay(), widget->winId(), ShapeBounding, 0, 0, + reg.handle(), ShapeSet ); + } + else + { + QMemArray< QRect > rects = reg.rects(); + XRectangle* xrects = new XRectangle[ rects.count() ]; + for( unsigned int i = 0; + i < rects.count(); + ++i ) + { + xrects[ i ].x = rects[ i ].x(); + xrects[ i ].y = rects[ i ].y(); + xrects[ i ].width = rects[ i ].width(); + xrects[ i ].height = rects[ i ].height(); + } + XShapeCombineRectangles( qt_xdisplay(), widget->winId(), ShapeBounding, 0, 0, + xrects, rects.count(), ShapeSet, mode ); + delete[] xrects; + } +} + +QRect KDecorationPreview::windowGeometry( bool active ) const +{ + QWidget *widget = deco->widget(); + return widget->geometry(); +} + +QRegion KDecorationPreview::unobscuredRegion( bool active, const QRegion& r ) const +{ + return r; +} + +KDecorationPreviewBridge::KDecorationPreviewBridge( KDecorationPreview* p, bool a, const QString &c ) + : preview( p ), active( a ), m_caption( c ) +{ +} + +void KDecorationPreviewBridge::setCaption(const QString &c) +{ + m_caption = c; +} + +QWidget* KDecorationPreviewBridge::initialParentWidget() const + { + return preview; + } + +Qt::WFlags KDecorationPreviewBridge::initialWFlags() const + { + return 0; + } + +bool KDecorationPreviewBridge::isActive() const + { + return active; + } + +bool KDecorationPreviewBridge::isCloseable() const + { + return true; + } + +bool KDecorationPreviewBridge::isMaximizable() const + { + return true; + } + +KDecoration::MaximizeMode KDecorationPreviewBridge::maximizeMode() const + { + return KDecoration::MaximizeRestore; + } + +bool KDecorationPreviewBridge::isMinimizable() const + { + return true; + } + +bool KDecorationPreviewBridge::providesContextHelp() const + { + return true; + } + +int KDecorationPreviewBridge::desktop() const + { + return 1; + } + +bool KDecorationPreviewBridge::isModal() const + { + return false; + } + +bool KDecorationPreviewBridge::isShadeable() const + { + return true; + } + +bool KDecorationPreviewBridge::isShade() const + { + return false; + } + +bool KDecorationPreviewBridge::isSetShade() const + { + return false; + } + +bool KDecorationPreviewBridge::keepAbove() const + { + return false; + } + +bool KDecorationPreviewBridge::keepBelow() const + { + return false; + } + +bool KDecorationPreviewBridge::isMovable() const + { + return true; + } + +bool KDecorationPreviewBridge::isResizable() const + { + return true; + } + +NET::WindowType KDecorationPreviewBridge::windowType( unsigned long ) const + { + return NET::Normal; + } + +QIconSet KDecorationPreviewBridge::icon() const + { + return SmallIconSet( "xapp" ); + } + +QString KDecorationPreviewBridge::caption() const +{ + return m_caption; +} + +void KDecorationPreviewBridge::processMousePressEvent( QMouseEvent* ) + { + } + +void KDecorationPreviewBridge::showWindowMenu( const QRect &) + { + } + +void KDecorationPreviewBridge::showWindowMenu( QPoint ) + { + } + +void KDecorationPreviewBridge::performWindowOperation( WindowOperation ) + { + } + +void KDecorationPreviewBridge::setMask( const QRegion& reg, int mode ) + { + preview->setPreviewMask( reg, mode ); + } + +bool KDecorationPreviewBridge::isPreview() const + { + return false; + } + +QRect KDecorationPreviewBridge::geometry() const + { + return preview->windowGeometry( active ); + } + +QRect KDecorationPreviewBridge::iconGeometry() const + { + return QRect(); + } + +QRegion KDecorationPreviewBridge::unobscuredRegion( const QRegion& r ) const + { + return preview->unobscuredRegion( active, r ); + } + +QWidget* KDecorationPreviewBridge::workspaceWidget() const + { + return preview; + } + +WId KDecorationPreviewBridge::windowId() const + { + return 0; // no decorated window + } + +void KDecorationPreviewBridge::closeWindow() + { + } + +void KDecorationPreviewBridge::maximize( MaximizeMode ) + { + } + +void KDecorationPreviewBridge::minimize() + { + } + +void KDecorationPreviewBridge::showContextHelp() + { + } + +void KDecorationPreviewBridge::setDesktop( int ) + { + } + +void KDecorationPreviewBridge::titlebarDblClickOperation() + { + } + +void KDecorationPreviewBridge::setShade( bool ) + { + } + +void KDecorationPreviewBridge::setKeepAbove( bool ) + { + } + +void KDecorationPreviewBridge::setKeepBelow( bool ) + { + } + +int KDecorationPreviewBridge::currentDesktop() const + { + return 1; + } + +void KDecorationPreviewBridge::helperShowHide( bool ) + { + } + +void KDecorationPreviewBridge::grabXServer( bool ) + { + } + +KDecorationPreviewOptions::KDecorationPreviewOptions() + { + d = new KDecorationOptionsPrivate; + d->defaultKWinSettings(); + updateSettings(); + } + +KDecorationPreviewOptions::~KDecorationPreviewOptions() + { + delete d; + } + +unsigned long KDecorationPreviewOptions::updateSettings() +{ + KConfig cfg( "kwinrc", true ); + unsigned long changed = 0; + changed |= d->updateKWinSettings( &cfg ); + + return changed; +} + +bool KDecorationPreviewPlugins::provides( Requirement ) + { + return false; + } + +// #include "preview.moc" diff --git a/kwin/tools/decobenchmark/preview.h b/kwin/tools/decobenchmark/preview.h new file mode 100644 index 000000000..4c82ac0a1 --- /dev/null +++ b/kwin/tools/decobenchmark/preview.h @@ -0,0 +1,137 @@ +/* + * + * Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KWINDECORATION_PREVIEW_H +#define KWINDECORATION_PREVIEW_H + +#include <qwidget.h> +#include <kdecoration_p.h> +#include <kdecoration_plugins_p.h> + +class QLabel; + +class KDecorationPreviewBridge; +class KDecorationPreviewOptions; + +class KDecorationPreview + : public QWidget + { + public: + KDecorationPreview( KDecorationPlugins* plugin, QWidget* parent = NULL, const char* name = NULL ); + virtual ~KDecorationPreview(); + + void performRepaintTest(int n); + void performCaptionTest(int n); + void performResizeTest(int n); + void performRecreationTest(int n); + + bool recreateDecoration(); + void setPreviewMask( const QRegion&, int ); + QRegion unobscuredRegion( bool, const QRegion& ) const; + QRect windowGeometry( bool ) const; + private: + void positionPreviews(int shrink = 0); + KDecorationPreviewOptions* options; + KDecorationPreviewBridge* bridge; + KDecoration* deco; + KDecorationPlugins* m_plugin; + }; + +class KDecorationPreviewBridge + : public KDecorationBridge + { + public: + KDecorationPreviewBridge( KDecorationPreview* preview, bool active, const QString &caption ); + + void setCaption(const QString &caption); + + virtual bool isActive() const; + virtual bool isCloseable() const; + virtual bool isMaximizable() const; + virtual MaximizeMode maximizeMode() const; + virtual bool isMinimizable() const; + virtual bool providesContextHelp() const; + virtual int desktop() const; + virtual bool isModal() const; + virtual bool isShadeable() const; + virtual bool isShade() const; + virtual bool isSetShade() const; + virtual bool keepAbove() const; + virtual bool keepBelow() const; + virtual bool isMovable() const; + virtual bool isResizable() const; + virtual NET::WindowType windowType( unsigned long supported_types ) const; + virtual QIconSet icon() const; + virtual QString caption() const; + virtual void processMousePressEvent( QMouseEvent* ); + virtual void showWindowMenu( const QRect &); + virtual void showWindowMenu( QPoint ); + virtual void performWindowOperation( WindowOperation ); + virtual void setMask( const QRegion&, int ); + virtual bool isPreview() const; + virtual QRect geometry() const; + virtual QRect iconGeometry() const; + virtual QRegion unobscuredRegion( const QRegion& r ) const; + virtual QWidget* workspaceWidget() const; + virtual WId windowId() const; + virtual void closeWindow(); + virtual void maximize( MaximizeMode mode ); + virtual void minimize(); + virtual void showContextHelp(); + virtual void setDesktop( int desktop ); + virtual void titlebarDblClickOperation(); + virtual void setShade( bool set ); + virtual void setKeepAbove( bool ); + virtual void setKeepBelow( bool ); + virtual int currentDesktop() const; + virtual QWidget* initialParentWidget() const; + virtual Qt::WFlags initialWFlags() const; + virtual void helperShowHide( bool show ); + virtual void grabXServer( bool grab ); + private: + KDecorationPreview* preview; + bool active; + QString m_caption; + }; + +class KDecorationPreviewOptions + : public KDecorationOptions + { + public: + KDecorationPreviewOptions(); + virtual ~KDecorationPreviewOptions(); + virtual unsigned long updateSettings(); + + private: + }; + +class KDecorationPreviewPlugins + : public KDecorationPlugins + { + public: + KDecorationPreviewPlugins( KConfig* cfg ); + virtual bool provides( Requirement ); + }; + +inline KDecorationPreviewPlugins::KDecorationPreviewPlugins( KConfig* cfg ) + : KDecorationPlugins( cfg ) + { + } + +#endif diff --git a/kwin/tools/test_gravity.cpp b/kwin/tools/test_gravity.cpp new file mode 100644 index 000000000..618023b5f --- /dev/null +++ b/kwin/tools/test_gravity.cpp @@ -0,0 +1,99 @@ +// tests for window gravity + +#include <iostream> +#include <stdlib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +using namespace std; + +const int gravities[ 10 ] = + { + NorthWestGravity, + NorthGravity, + NorthEastGravity, + WestGravity, + CenterGravity, + EastGravity, + SouthWestGravity, + SouthGravity, + SouthEastGravity, + StaticGravity + }; + +const char* const gravity_names[ 10 ] = + { + "NW", "N", "NE", "W", "C", "E", "SW", "S", "SE", "ST" + }; + +Display* dpy = NULL; + +int get_gravity( const char* name ) + { + for( int i = 0; + i < 10; + ++i ) + if( strcmp( name, gravity_names[ i ] ) == 0 ) + return gravities[ i ]; + cerr << "Wrong gravity name" << endl; + exit( 1 ); + } + +void test( const char* gravity ) + { + XSetWindowAttributes attrs; + XSizeHints hints; + hints.flags = USPosition | PWinGravity; + hints.win_gravity = get_gravity( gravity ); + Window w = XCreateWindow( dpy, DefaultRootWindow( dpy ), 100, 100, 200, 100, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, &attrs ); + XSetWMNormalHints( dpy, w, &hints ); + XSelectInput( dpy, w, StructureNotifyMask | ButtonPressMask ); + XMapWindow( dpy, w ); + for(;;) + { + XEvent ev; + XNextEvent( dpy, &ev ); + if( ev.type == ConfigureNotify ) + { + cout << "CONFIGURENOTIFY:" << ev.xany.send_event << ":" << ev.xconfigure.x << ":" << ev.xconfigure.y + << ":" << ev.xconfigure.width << ":" << ev.xconfigure.height << endl; + Window root, child; + int x, x_local, y, y_local; + unsigned int width, height, border, depth; + XGetGeometry( dpy, w, &root, &x_local, &y_local, &width, &height, &border, &depth ); + XTranslateCoordinates( dpy, w, root, 0, 0, &x, &y, &child ); + cout << "GEOMETRY:" << x << ":" << y << ":" << width << ":" << height << ":(" << x_local << ":" << y_local << ")" << endl; + } + else if( ev.type == ButtonPress ) + { + if( ev.xbutton.button == Button1 ) // move + { + cout << "MOVE" << endl; + XMoveWindow( dpy, w, 100, 100 ); + } + else if( ev.xbutton.button == Button2 ) // resize + { + cout << "RESIZE" << endl; + XResizeWindow( dpy, w, 200, 100 ); + } + else if( ev.xbutton.button == Button3 ) // move and resize + { + cout << "MOVERESIZE" << endl; + XMoveResizeWindow( dpy, w, 100, 100, 200, 100 ); + } + } + } + } + +int main( int argc, char* argv[] ) + { + dpy = XOpenDisplay( NULL ); + if( argc != 2 ) + { + cerr << "specify gravity" << endl; + exit( 1 ); + } + test( argv[ 1 ] ); + XCloseDisplay( dpy ); + } diff --git a/kwin/tools/xreply/Makefile b/kwin/tools/xreply/Makefile new file mode 100644 index 000000000..af06929d5 --- /dev/null +++ b/kwin/tools/xreply/Makefile @@ -0,0 +1,2 @@ +all: + gcc -O2 -Wall -shared -fPIC -o libxreply.so xreply.c diff --git a/kwin/tools/xreply/xreply.c b/kwin/tools/xreply/xreply.c new file mode 100644 index 000000000..ecdf6ebc3 --- /dev/null +++ b/kwin/tools/xreply/xreply.c @@ -0,0 +1,197 @@ +/* + + LD_PRELOAD library that gives statistic on number of roundtrips in an application. + + $XREPLY_BACKTRACE defines whether and how backtraces will be printed for every + roundtrip. If not set, only total number of roundtrips is printed after the process + exits. If set to a number, backtrace for every roundtrip will be printed, and the + backtraces will be as deep as the given number. If set to C<number> (e.g. C10), + the backtraces will be "compressed" - every backtrace will be printed only once + after the process exits, together with number of times it occured. + +*/ + +#define _GNU_SOURCE +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <execinfo.h> +#include <assert.h> +#include <X11/Xlibint.h> + +/* Since these symbols are weak, the apps can provide their own, and therefore + e.g. temporarily suspend counting of roundtrips. At least theoretically, + I haven't really tried it. +*/ +__attribute((weak)) long ___xreply_reply_count = 0; +__attribute((weak)) int ___xreply_reply_enabled = 1; + +#define MAX_BACKTRACES 1024 + +extern long ___xreply_reply_count; +extern int ___xreply_reply_enabled; + +typedef Status (*xreply_ptr_t)(Display*,xReply*,int,Bool); + +static xreply_ptr_t xreply_ptr = NULL; +static int xreply_backtrace_set = 0; +static int xreply_backtrace_type = 0; + +struct xreply_struct + { + char* key; + char* text; + int count; + }; +static struct xreply_struct backtraces[ MAX_BACKTRACES ]; +static int backtraces_size = 0; + +static int xreply_compare( const void* left, const void* right ) + { + int left_count = ((struct xreply_struct*)left)->count; + int right_count = ((struct xreply_struct*)right)->count; + return right_count - left_count; + } + +static void xreply_print(void) + { + char tmp[ 1024 ]; + int fd; + fd = open( "/proc/self/cmdline", O_RDONLY ); + if( fd >= 0 ) + { + read( fd, tmp, 1024 ); + tmp[ 1023 ] = '\0'; + close( fd ); + } + fprintf( stderr, "XREPLY (%d : %s): %ld\n", getpid(), tmp, ___xreply_reply_count ); + if( xreply_backtrace_type < 0 ) + { + int i; + qsort( backtraces, backtraces_size, sizeof( struct xreply_struct ), xreply_compare ); + for( i = 0; + i < backtraces_size; + ++i ) + fprintf( stderr, "%d:%s\n\n", backtraces[ i ].count, backtraces[ i ].text ); + } + } + +static void xreply_backtrace() + { + void* trace[256]; + int n = backtrace(trace, 256); + char** strings = backtrace_symbols (trace, n); + + if( xreply_backtrace_type > 0 ) + { + fprintf( stderr, "%ld [\n", ___xreply_reply_count ); + if( n > xreply_backtrace_type ) + n = xreply_backtrace_type; + int i; + for( i = 0; + i < n; + ++i ) + fprintf( stderr, "%d: %s\n", i, strings[ i ] ); + fprintf( stderr, "]\n" ); + } + else + { + char stack[ 256 * 20 ]; + int pos = 0; + int i; + stack[ 0 ] = '\0'; + if( n > -xreply_backtrace_type ) + n = -xreply_backtrace_type; + for( i = 0; + i < n; + ++i ) + { + const char* start = strrchr( strings[ i ], '[' ); + if( start == NULL ) + assert( !"No [ in address." ); + long addr; + if( sscanf( start + 1, "0x%lx", &addr ) != 1 ) + assert( !"Failed to parse address." ); + if( sizeof( void* ) == 4 ) + { + sprintf( stack + pos, "0x%8lx", addr ); + pos += 10; + } + else if( sizeof( void* ) == 8 ) + { + sprintf( stack + pos, "0x%16lx", addr ); + pos += 18; + } + else + assert( !"Unknown sizeof( void* )." ); + } + for( i = 0; + i < backtraces_size; + ++i ) + if( strcmp( backtraces[ i ].key, stack ) == 0 ) + { + ++backtraces[ i ].count; + break; + } + if( i == backtraces_size ) + { + int stack_text_size = 10; + char* stack_text; + char* stack_text_pos; + for( i = 0; + i < n; + ++i ) + stack_text_size += strlen( strings[ i ] ) + 5; + stack_text = stack_text_pos = malloc( stack_text_size ); + for( i = 0; + i < n; + ++i ) + { + stack_text_pos = stpcpy( stack_text_pos, "\n" ); + stack_text_pos = stpcpy( stack_text_pos, strings[ i ] ); + } + backtraces[ backtraces_size ].key = strdup( stack ); + backtraces[ backtraces_size ].text = stack_text; + backtraces[ backtraces_size ].count = 1; + ++backtraces_size; + if( backtraces_size >= MAX_BACKTRACES ) + assert( !"MAX_BACKTRACES reached." ); + } + } + free (strings); + } + +Status +_XReply (dpy, rep, extra, discard) + register Display *dpy; + register xReply *rep; + int extra; /* number of 32-bit words expected after the reply */ + Bool discard; /* should I discard data following "extra" words? */ + { + if( ___xreply_reply_enabled ) + ++___xreply_reply_count; + if( xreply_backtrace_set == 0 ) + { + if( getenv( "XREPLY_BACKTRACE" ) != NULL ) + { // C<number> - compress backtraces, saved as negative value in xreply_backtrace_type + if( getenv( "XREPLY_BACKTRACE" )[ 0 ] == 'C' ) + xreply_backtrace_type = -atoi( getenv( "XREPLY_BACKTRACE" ) + 1 ); + else // <number> - print the backtrace every time + xreply_backtrace_type = atoi( getenv( "XREPLY_BACKTRACE" )); + } + else + xreply_backtrace_type = 0; + } + if( xreply_backtrace_type != 0 ) + xreply_backtrace(); + if( xreply_ptr == NULL ) + { + xreply_ptr = (xreply_ptr_t)dlsym( RTLD_NEXT, "_XReply" ); + if( xreply_ptr == NULL ) + assert( !"dlsym() failed." ); + atexit( xreply_print ); + } + return xreply_ptr( dpy, rep, extra, discard ); + } diff --git a/kwin/useractions.cpp b/kwin/useractions.cpp new file mode 100644 index 000000000..b722bf1a1 --- /dev/null +++ b/kwin/useractions.cpp @@ -0,0 +1,1119 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file contains things relevant to direct user actions, such as + responses to global keyboard shortcuts, or selecting actions + from the window operations menu. + +*/ + +#include "client.h" +#include "workspace.h" + +#include <fixx11h.h> +#include <qhbox.h> +#include <qpushbutton.h> +#include <qslider.h> +#include <qtooltip.h> +#include <qpopupmenu.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kconfig.h> +#include <kglobalaccel.h> +#include <kapplication.h> +#include <qregexp.h> + +#include "killwindow.h" +#include "tabbox.h" + +namespace KWinInternal +{ + +//**************************************** +// Workspace +//**************************************** + +QPopupMenu* Workspace::clientPopup() + { + if ( !popup ) + { + popup = new QPopupMenu; + popup->setCheckable( TRUE ); + popup->setFont(KGlobalSettings::menuFont()); + connect( popup, SIGNAL( aboutToShow() ), this, SLOT( clientPopupAboutToShow() ) ); + connect( popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); + + advanced_popup = new QPopupMenu( popup ); + advanced_popup->setCheckable( TRUE ); + advanced_popup->setFont(KGlobalSettings::menuFont()); + connect( advanced_popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); + advanced_popup->insertItem( SmallIconSet( "up" ), + i18n("Keep &Above Others")+'\t'+keys->shortcut("Window Above Other Windows").seq(0).toString(), Options::KeepAboveOp ); + advanced_popup->insertItem( SmallIconSet( "down" ), + i18n("Keep &Below Others")+'\t'+keys->shortcut("Window Below Other Windows").seq(0).toString(), Options::KeepBelowOp ); + advanced_popup->insertItem( SmallIconSet( "window_fullscreen" ), + i18n("&Fullscreen")+'\t'+keys->shortcut("Window Fullscreen").seq(0).toString(), Options::FullScreenOp ); + advanced_popup->insertItem( i18n("&No Border")+'\t'+keys->shortcut("Window No Border").seq(0).toString(), Options::NoBorderOp ); + advanced_popup->insertItem( SmallIconSet("key_bindings"), + i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp ); + advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Window Settings..."), Options::WindowRulesOp ); + advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Application Settings..."), Options::ApplicationRulesOp ); + + popup->insertItem(i18n("Ad&vanced"), advanced_popup ); + desk_popup_index = popup->count(); + + if (options->useTranslucency){ + QPopupMenu *trans_popup = new QPopupMenu( popup ); + QVBox *transBox = new QVBox(trans_popup); + transButton = new QPushButton(transBox, "transButton"); + QToolTip::add(transButton, i18n("Reset opacity to default value")); + transSlider = new QSlider(0, 100, 1, 100, Qt::Vertical, transBox, "transSlider"); + QToolTip::add(transSlider, i18n("Slide this to set the window's opacity")); + connect(transButton, SIGNAL(clicked()), SLOT(resetClientOpacity())); + connect(transButton, SIGNAL(clicked()), trans_popup, SLOT(hide())); + connect(transSlider, SIGNAL(valueChanged(int)), SLOT(setTransButtonText(int))); + connect(transSlider, SIGNAL(valueChanged(int)), this, SLOT(setPopupClientOpacity(int))); +// connect(transSlider, SIGNAL(sliderReleased()), trans_popup, SLOT(hide())); + trans_popup->insertItem(transBox); + popup->insertItem(i18n("&Opacity"), trans_popup ); + } + + popup->insertItem( SmallIconSet( "move" ), i18n("&Move")+'\t'+keys->shortcut("Window Move").seq(0).toString(), Options::MoveOp ); + popup->insertItem( i18n("Re&size")+'\t'+keys->shortcut("Window Resize").seq(0).toString(), Options::ResizeOp ); + popup->insertItem( i18n("Mi&nimize")+'\t'+keys->shortcut("Window Minimize").seq(0).toString(), Options::MinimizeOp ); + popup->insertItem( i18n("Ma&ximize")+'\t'+keys->shortcut("Window Maximize").seq(0).toString(), Options::MaximizeOp ); + popup->insertItem( i18n("Sh&ade")+'\t'+keys->shortcut("Window Shade").seq(0).toString(), Options::ShadeOp ); + + popup->insertSeparator(); + + if (!KGlobal::config()->isImmutable() && + !kapp->authorizeControlModules(Workspace::configModules(true)).isEmpty()) + { + popup->insertItem(SmallIconSet( "configure" ), i18n("Configur&e Window Behavior..."), this, SLOT( configureWM() )); + popup->insertSeparator(); + } + + popup->insertItem( SmallIconSet( "fileclose" ), i18n("&Close")+'\t'+keys->shortcut("Window Close").seq(0).toString(), Options::CloseOp ); + } + return popup; + } + +//sets the transparency of the client to given value(given by slider) +void Workspace::setPopupClientOpacity(int value) + { + active_popup_client->setCustomOpacityFlag(true); + value = 100 - value; + value<100?active_popup_client->setOpacity(true, (uint)((value/100.0)*0xffffffff)):active_popup_client->setOpacity(false,0xffffffff); + } + +void Workspace::setTransButtonText(int value) + { + value = 100 - value; + if(value < 0) + transButton->setText("000 %"); + else if (value >= 100 ) + transButton->setText("100 %"); + else if(value < 10) + transButton->setText("00"+QString::number(value)+" %"); + else if(value < 100) + transButton->setText("0"+QString::number(value)+" %"); + } + +void Workspace::resetClientOpacity() + { + active_popup_client->setCustomOpacityFlag(false); + active_popup_client->updateOpacity(); + transSlider->setValue(100-active_popup_client->opacityPercentage()); + setTransButtonText(100-active_popup_client->opacityPercentage()); + } + + +/*! + The client popup menu will become visible soon. + + Adjust the items according to the respective popup client. + */ +void Workspace::clientPopupAboutToShow() + { + if ( !active_popup_client || !popup ) + return; + + if ( numberOfDesktops() == 1 ) + { + delete desk_popup; + desk_popup = 0; + } + else + { + initDesktopPopup(); + } + + popup->setItemEnabled( Options::ResizeOp, active_popup_client->isResizable() ); + popup->setItemEnabled( Options::MoveOp, active_popup_client->isMovable() ); + popup->setItemEnabled( Options::MaximizeOp, active_popup_client->isMaximizable() ); + popup->setItemChecked( Options::MaximizeOp, active_popup_client->maximizeMode() == Client::MaximizeFull ); + // This should be checked also when hover unshaded + popup->setItemChecked( Options::ShadeOp, active_popup_client->shadeMode() != ShadeNone ); + popup->setItemEnabled( Options::ShadeOp, active_popup_client->isShadeable()); + advanced_popup->setItemChecked( Options::KeepAboveOp, active_popup_client->keepAbove() ); + advanced_popup->setItemChecked( Options::KeepBelowOp, active_popup_client->keepBelow() ); + advanced_popup->setItemChecked( Options::FullScreenOp, active_popup_client->isFullScreen() ); + advanced_popup->setItemEnabled( Options::FullScreenOp, active_popup_client->userCanSetFullScreen() ); + advanced_popup->setItemChecked( Options::NoBorderOp, active_popup_client->noBorder() ); + advanced_popup->setItemEnabled( Options::NoBorderOp, active_popup_client->userCanSetNoBorder() ); + popup->setItemEnabled( Options::MinimizeOp, active_popup_client->isMinimizable() ); + popup->setItemEnabled( Options::CloseOp, active_popup_client->isCloseable() ); + if (options->useTranslucency) + { + transSlider->setValue(100-active_popup_client->opacityPercentage()); + setTransButtonText(100-active_popup_client->opacityPercentage()); + } + } + + +void Workspace::initDesktopPopup() + { + if (desk_popup) + return; + + desk_popup = new QPopupMenu( popup ); + desk_popup->setCheckable( TRUE ); + desk_popup->setFont(KGlobalSettings::menuFont()); + connect( desk_popup, SIGNAL( activated(int) ), + this, SLOT( slotSendToDesktop(int) ) ); + connect( desk_popup, SIGNAL( aboutToShow() ), + this, SLOT( desktopPopupAboutToShow() ) ); + + popup->insertItem(i18n("To &Desktop"), desk_popup, -1, desk_popup_index ); + } + +/*! + Adjusts the desktop popup to the current values and the location of + the popup client. + */ +void Workspace::desktopPopupAboutToShow() + { + if ( !desk_popup ) + return; + + desk_popup->clear(); + desk_popup->insertItem( i18n("&All Desktops"), 0 ); + if ( active_popup_client && active_popup_client->isOnAllDesktops() ) + desk_popup->setItemChecked( 0, TRUE ); + desk_popup->insertSeparator( -1 ); + int id; + const int BASE = 10; + for ( int i = 1; i <= numberOfDesktops(); i++ ) + { + QString basic_name("%1 %2"); + if (i<BASE) + { + basic_name.prepend('&'); + } + id = desk_popup->insertItem( + basic_name + .arg(i) + .arg( desktopName(i).replace( '&', "&&" )), + i ); + if ( active_popup_client && + !active_popup_client->isOnAllDesktops() && active_popup_client->desktop() == i ) + desk_popup->setItemChecked( id, TRUE ); + } + } + +void Workspace::closeActivePopup() + { + if( active_popup ) + { + active_popup->close(); + active_popup = NULL; + active_popup_client = NULL; + } + } + +/*! + Create the global accel object \c keys. + */ +void Workspace::initShortcuts() + { + keys = new KGlobalAccel( this ); + // a separate KGlobalAccel is needed for the shortcut for disabling global shortcuts, + // otherwise it would also disable itself + disable_shortcuts_keys = new KGlobalAccel( this ); + disable_shortcuts_keys->disableBlocking( true ); +#define IN_KWIN +#include "kwinbindings.cpp" + readShortcuts(); + } + +void Workspace::readShortcuts() + { + keys->readSettings(); + disable_shortcuts_keys->readSettings(); + + cutWalkThroughDesktops = keys->shortcut("Walk Through Desktops"); + cutWalkThroughDesktopsReverse = keys->shortcut("Walk Through Desktops (Reverse)"); + cutWalkThroughDesktopList = keys->shortcut("Walk Through Desktop List"); + cutWalkThroughDesktopListReverse = keys->shortcut("Walk Through Desktop List (Reverse)"); + cutWalkThroughWindows = keys->shortcut("Walk Through Windows"); + cutWalkThroughWindowsReverse = keys->shortcut("Walk Through Windows (Reverse)"); + + keys->updateConnections(); + disable_shortcuts_keys->updateConnections(); + + delete popup; + popup = NULL; // so that it's recreated next time + desk_popup = NULL; + } + + +void Workspace::setupWindowShortcut( Client* c ) + { + assert( client_keys_dialog == NULL ); + keys->suspend( true ); + disable_shortcuts_keys->suspend( true ); + client_keys->suspend( true ); + client_keys_dialog = new ShortcutDialog( c->shortcut()); + client_keys_client = c; + connect( client_keys_dialog, SIGNAL( dialogDone( bool )), SLOT( setupWindowShortcutDone( bool ))); + QRect r = clientArea( ScreenArea, c ); + QSize size = client_keys_dialog->sizeHint(); + QPoint pos = c->pos() + c->clientPos(); + if( pos.x() + size.width() >= r.right()) + pos.setX( r.right() - size.width()); + if( pos.y() + size.height() >= r.bottom()) + pos.setY( r.bottom() - size.height()); + client_keys_dialog->move( pos ); + client_keys_dialog->show(); + active_popup = client_keys_dialog; + active_popup_client = c; + } + +void Workspace::setupWindowShortcutDone( bool ok ) + { + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + if( ok ) + { + client_keys_client->setShortcut( KShortcut( client_keys_dialog->shortcut()).toString()); + } + closeActivePopup(); + delete client_keys_dialog; + client_keys_dialog = NULL; + client_keys_client = NULL; + } + +void Workspace::clientShortcutUpdated( Client* c ) + { + QString key = QString::number( c->window()); + client_keys->remove( key ); + if( !c->shortcut().isNull()) + { + client_keys->insert( key, key ); + client_keys->setShortcut( key, c->shortcut()); + client_keys->setSlot( key, c, SLOT( shortcutActivated())); + client_keys->setActionEnabled( key, true ); + } + client_keys->updateConnections(); + } + +void Workspace::clientPopupActivated( int id ) + { + WindowOperation op = static_cast< WindowOperation >( id ); + Client* c = active_popup_client ? active_popup_client : active_client; + QString type; + switch( op ) + { + case FullScreenOp: + if( !c->isFullScreen() && c->userCanSetFullScreen()) + type = "fullscreenaltf3"; + break; + case NoBorderOp: + if( !c->noBorder() && c->userCanSetNoBorder()) + type = "noborderaltf3"; + break; + default: + break; + }; + if( !type.isEmpty()) + helperDialog( type, c ); + performWindowOperation( c, op ); + } + + +void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) + { + if ( !c ) + return; + + if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp ) + QCursor::setPos( c->geometry().center() ); + if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp ) + QCursor::setPos( c->geometry().bottomRight()); + switch ( op ) + { + case Options::MoveOp: + c->performMouseCommand( Options::MouseMove, QCursor::pos() ); + break; + case Options::UnrestrictedMoveOp: + c->performMouseCommand( Options::MouseUnrestrictedMove, QCursor::pos() ); + break; + case Options::ResizeOp: + c->performMouseCommand( Options::MouseResize, QCursor::pos() ); + break; + case Options::UnrestrictedResizeOp: + c->performMouseCommand( Options::MouseUnrestrictedResize, QCursor::pos() ); + break; + case Options::CloseOp: + c->closeWindow(); + break; + case Options::MaximizeOp: + c->maximize( c->maximizeMode() == Client::MaximizeFull + ? Client::MaximizeRestore : Client::MaximizeFull ); + break; + case Options::HMaximizeOp: + c->maximize( c->maximizeMode() ^ Client::MaximizeHorizontal ); + break; + case Options::VMaximizeOp: + c->maximize( c->maximizeMode() ^ Client::MaximizeVertical ); + break; + case Options::RestoreOp: + c->maximize( Client::MaximizeRestore ); + break; + case Options::MinimizeOp: + c->minimize(); + break; + case Options::ShadeOp: + c->performMouseCommand( Options::MouseShade, QCursor::pos()); + break; + case Options::OnAllDesktopsOp: + c->setOnAllDesktops( !c->isOnAllDesktops() ); + break; + case Options::FullScreenOp: + c->setFullScreen( !c->isFullScreen(), true ); + break; + case Options::NoBorderOp: + c->setUserNoBorder( !c->isUserNoBorder()); + break; + case Options::KeepAboveOp: + { + StackingUpdatesBlocker blocker( this ); + bool was = c->keepAbove(); + c->setKeepAbove( !c->keepAbove() ); + if( was && !c->keepAbove()) + raiseClient( c ); + break; + } + case Options::KeepBelowOp: + { + StackingUpdatesBlocker blocker( this ); + bool was = c->keepBelow(); + c->setKeepBelow( !c->keepBelow() ); + if( was && !c->keepBelow()) + lowerClient( c ); + break; + } + case Options::OperationsOp: + c->performMouseCommand( Options::MouseShade, QCursor::pos()); + break; + case Options::WindowRulesOp: + editWindowRules( c, false ); + break; + case Options::ApplicationRulesOp: + editWindowRules( c, true ); + break; + case Options::SetupWindowShortcutOp: + setupWindowShortcut( c ); + break; + case Options::LowerOp: + lowerClient(c); + break; + case Options::NoOp: + break; + } + } + +/*! + Performs a mouse command on this client (see options.h) + */ +bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos, bool handled ) + { + bool replay = FALSE; + switch (command) + { + case Options::MouseRaise: + workspace()->raiseClient( this ); + break; + case Options::MouseLower: + workspace()->lowerClient( this ); + break; + case Options::MouseShade : + toggleShade(); + cancelShadeHover(); + break; + case Options::MouseSetShade: + setShade( ShadeNormal ); + cancelShadeHover(); + break; + case Options::MouseUnsetShade: + setShade( ShadeNone ); + cancelShadeHover(); + break; + case Options::MouseOperationsMenu: + if ( isActive() && options->clickRaise ) + autoRaise(); + workspace()->showWindowMenu( globalPos, this ); + break; + case Options::MouseToggleRaiseAndLower: + workspace()->raiseOrLowerClient( this ); + break; + case Options::MouseActivateAndRaise: + replay = isActive(); // for clickraise mode + workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled && replay ); + break; + case Options::MouseActivateAndLower: + workspace()->requestFocus( this ); + workspace()->lowerClient( this ); + break; + case Options::MouseActivate: + replay = isActive(); // for clickraise mode + workspace()->takeActivity( this, ActivityFocus, handled && replay ); + break; + case Options::MouseActivateRaiseAndPassClick: + workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled ); + replay = TRUE; + break; + case Options::MouseActivateAndPassClick: + workspace()->takeActivity( this, ActivityFocus, handled ); + replay = TRUE; + break; + case Options::MouseActivateRaiseAndMove: + case Options::MouseActivateRaiseAndUnrestrictedMove: + workspace()->raiseClient( this ); + workspace()->requestFocus( this ); + if( options->moveMode == Options::Transparent && isMovable()) + move_faked_activity = workspace()->fakeRequestedActivity( this ); + // fallthrough + case Options::MouseMove: + case Options::MouseUnrestrictedMove: + { + if (!isMovable()) + break; + if( moveResizeMode ) + finishMoveResize( false ); + mode = PositionCenter; + buttonDown = TRUE; + moveOffset = QPoint( globalPos.x() - x(), globalPos.y() - y()); // map from global + invertedMoveOffset = rect().bottomRight() - moveOffset; + unrestrictedMoveResize = ( command == Options::MouseActivateRaiseAndUnrestrictedMove + || command == Options::MouseUnrestrictedMove ); + setCursor( mode ); + if( !startMoveResize()) + { + buttonDown = false; + setCursor( mode ); + } + break; + } + case Options::MouseResize: + case Options::MouseUnrestrictedResize: + { + if (!isResizable() || isShade()) + break; + if( moveResizeMode ) + finishMoveResize( false ); + buttonDown = TRUE; + moveOffset = QPoint( globalPos.x() - x(), globalPos.y() - y()); // map from global + int x = moveOffset.x(), y = moveOffset.y(); + bool left = x < width() / 3; + bool right = x >= 2 * width() / 3; + bool top = y < height() / 3; + bool bot = y >= 2 * height() / 3; + if (top) + mode = left ? PositionTopLeft : (right ? PositionTopRight : PositionTop); + else if (bot) + mode = left ? PositionBottomLeft : (right ? PositionBottomRight : PositionBottom); + else + mode = (x < width() / 2) ? PositionLeft : PositionRight; + invertedMoveOffset = rect().bottomRight() - moveOffset; + unrestrictedMoveResize = ( command == Options::MouseUnrestrictedResize ); + setCursor( mode ); + if( !startMoveResize()) + { + buttonDown = false; + setCursor( mode ); + } + break; + } + case Options::MouseMaximize: + maximize( Client::MaximizeFull ); + break; + case Options::MouseRestore: + maximize( Client::MaximizeRestore ); + break; + case Options::MouseMinimize: + minimize(); + break; + case Options::MouseAbove: + { + StackingUpdatesBlocker blocker( workspace()); + if( keepBelow()) + setKeepBelow( false ); + else + setKeepAbove( true ); + break; + } + case Options::MouseBelow: + { + StackingUpdatesBlocker blocker( workspace()); + if( keepAbove()) + setKeepAbove( false ); + else + setKeepBelow( true ); + break; + } + case Options::MousePreviousDesktop: + workspace()->windowToPreviousDesktop( this ); + break; + case Options::MouseNextDesktop: + workspace()->windowToNextDesktop( this ); + break; + case Options::MouseOpacityMore: + if (opacity_ < 0xFFFFFFFF) + { + if (opacity_ < 0xF3333333) + { + setOpacity(TRUE, opacity_ + 0xCCCCCCC); + custom_opacity = true; + } + else + { + setOpacity(FALSE, 0xFFFFFFFF); + custom_opacity = false; + } + } + break; + case Options::MouseOpacityLess: + if (opacity_ > 0) + { + setOpacity(TRUE, (opacity_ > 0xCCCCCCC) ? opacity_ - 0xCCCCCCC : 0); + custom_opacity = true; + } + break; + case Options::MouseNothing: + replay = TRUE; + break; + } + return replay; + } + +// KDE4 remove me +void Workspace::showWindowMenuAt( unsigned long, int, int ) + { + slotWindowOperations(); + } + +void Workspace::slotActivateAttentionWindow() + { + if( attention_chain.count() > 0 ) + activateClient( attention_chain.first()); + } + +void Workspace::slotSwitchDesktopNext() + { + int d = currentDesktop() + 1; + if ( d > numberOfDesktops() ) + { + if ( options->rollOverDesktops ) + { + d = 1; + } + else + { + return; + } + } + setCurrentDesktop(d); + } + +void Workspace::slotSwitchDesktopPrevious() + { + int d = currentDesktop() - 1; + if ( d <= 0 ) + { + if ( options->rollOverDesktops ) + d = numberOfDesktops(); + else + return; + } + setCurrentDesktop(d); + } + +void Workspace::slotSwitchDesktopRight() + { + int desktop = desktopToRight( currentDesktop()); + if( desktop == currentDesktop()) + return; + setCurrentDesktop( desktop ); + } + +void Workspace::slotSwitchDesktopLeft() + { + int desktop = desktopToLeft( currentDesktop()); + if( desktop == currentDesktop()) + return; + setCurrentDesktop( desktop ); + } + +void Workspace::slotSwitchDesktopUp() + { + int desktop = desktopUp( currentDesktop()); + if( desktop == currentDesktop()) + return; + setCurrentDesktop( desktop ); + } + +void Workspace::slotSwitchDesktopDown() + { + int desktop = desktopDown( currentDesktop()); + if( desktop == currentDesktop()) + return; + setCurrentDesktop( desktop ); + } + +void Workspace::slotSwitchToDesktop( int i ) + { + setCurrentDesktop( i ); + } + + +void Workspace::slotWindowToDesktop( int i ) + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( i >= 1 && i <= numberOfDesktops() && c + && !c->isDesktop() + && !c->isDock() + && !c->isTopMenu()) + sendClientToDesktop( c, i, true ); + } + +/*! + Maximizes the popup client + */ +void Workspace::slotWindowMaximize() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + performWindowOperation( c, Options::MaximizeOp ); + } + +/*! + Maximizes the popup client vertically + */ +void Workspace::slotWindowMaximizeVertical() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + performWindowOperation( c, Options::VMaximizeOp ); + } + +/*! + Maximizes the popup client horiozontally + */ +void Workspace::slotWindowMaximizeHorizontal() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + performWindowOperation( c, Options::HMaximizeOp ); + } + + +/*! + Minimizes the popup client + */ +void Workspace::slotWindowMinimize() + { + Client* c = active_popup_client ? active_popup_client : active_client; + performWindowOperation( c, Options::MinimizeOp ); + } + +/*! + Shades/unshades the popup client respectively + */ +void Workspace::slotWindowShade() + { + Client* c = active_popup_client ? active_popup_client : active_client; + performWindowOperation( c, Options::ShadeOp ); + } + +/*! + Raises the popup client + */ +void Workspace::slotWindowRaise() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + raiseClient( c ); + } + +/*! + Lowers the popup client + */ +void Workspace::slotWindowLower() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + lowerClient( c ); + } + +/*! + Does a toggle-raise-and-lower on the popup client; + */ +void Workspace::slotWindowRaiseOrLower() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if ( c ) + raiseOrLowerClient( c ); + } + +void Workspace::slotWindowOnAllDesktops() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + c->setOnAllDesktops( !c->isOnAllDesktops()); + } + +void Workspace::slotWindowFullScreen() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + performWindowOperation( c, Options::FullScreenOp ); + } + +void Workspace::slotWindowNoBorder() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + performWindowOperation( c, Options::NoBorderOp ); + } + +void Workspace::slotWindowAbove() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + performWindowOperation( c, Options::KeepAboveOp ); + } + +void Workspace::slotWindowBelow() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + performWindowOperation( c, Options::KeepBelowOp ); + } +void Workspace::slotSetupWindowShortcut() + { + Client* c = active_popup_client ? active_popup_client : active_client; + if( c ) + performWindowOperation( c, Options::SetupWindowShortcutOp ); + } + +/*! + Move window to next desktop + */ +void Workspace::slotWindowToNextDesktop() + { + windowToNextDesktop( active_popup_client ? active_popup_client : active_client ); + } + +void Workspace::windowToNextDesktop( Client* c ) + { + int d = currentDesktop() + 1; + if ( d > numberOfDesktops() ) + d = 1; + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + +/*! + Move window to previous desktop + */ +void Workspace::slotWindowToPreviousDesktop() + { + windowToPreviousDesktop( active_popup_client ? active_popup_client : active_client ); + } + +void Workspace::windowToPreviousDesktop( Client* c ) + { + int d = currentDesktop() - 1; + if ( d <= 0 ) + d = numberOfDesktops(); + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + +void Workspace::slotWindowToDesktopRight() + { + int d = desktopToRight( currentDesktop()); + if( d == currentDesktop()) + return; + Client* c = active_popup_client ? active_popup_client : active_client; + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + +void Workspace::slotWindowToDesktopLeft() + { + int d = desktopToLeft( currentDesktop()); + if( d == currentDesktop()) + return; + Client* c = active_popup_client ? active_popup_client : active_client; + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + +void Workspace::slotWindowToDesktopUp() + { + int d = desktopUp( currentDesktop()); + if( d == currentDesktop()) + return; + Client* c = active_popup_client ? active_popup_client : active_client; + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + +void Workspace::slotWindowToDesktopDown() + { + int d = desktopDown( currentDesktop()); + if( d == currentDesktop()) + return; + Client* c = active_popup_client ? active_popup_client : active_client; + if (c && !c->isDesktop() + && !c->isDock() && !c->isTopMenu()) + { + setClientIsMoving( c ); + setCurrentDesktop( d ); + setClientIsMoving( NULL ); + } + } + + +/*! + Kill Window feature, similar to xkill + */ +void Workspace::slotKillWindow() + { + KillWindow kill( this ); + kill.start(); + } + +/*! + Sends the popup client to desktop \a desk + + Internal slot for the window operation menu + */ +void Workspace::slotSendToDesktop( int desk ) + { + if ( !active_popup_client ) + return; + if ( desk == 0 ) + { // the 'on_all_desktops' menu entry + active_popup_client->setOnAllDesktops( !active_popup_client->isOnAllDesktops()); + return; + } + + sendClientToDesktop( active_popup_client, desk, false ); + + } + +/*! + Shows the window operations popup menu for the activeClient() + */ +void Workspace::slotWindowOperations() + { + if ( !active_client ) + return; + QPoint pos = active_client->pos() + active_client->clientPos(); + showWindowMenu( pos.x(), pos.y(), active_client ); + } + +void Workspace::showWindowMenu( const QRect &pos, Client* cl ) + { + if (!kapp->authorizeKAction("kwin_rmb")) + return; + if( !cl ) + return; + if( active_popup_client != NULL ) // recursion + return; + if ( cl->isDesktop() + || cl->isDock() + || cl->isTopMenu()) + return; + + active_popup_client = cl; + QPopupMenu* p = clientPopup(); + active_popup = p; + int x = pos.left(); + int y = pos.bottom(); + if (y == pos.top()) + p->exec( QPoint( x, y ) ); + else + { + QRect area = clientArea(ScreenArea, QPoint(x, y), currentDesktop()); + clientPopupAboutToShow(); // needed for sizeHint() to be correct :-/ + int popupHeight = p->sizeHint().height(); + if (y + popupHeight < area.height()) + p->exec( QPoint( x, y ) ); + else + p->exec( QPoint( x, pos.top() - popupHeight ) ); + } + // active popup may be already changed (e.g. the window shortcut dialog) + if( active_popup == p ) + closeActivePopup(); + } + +/*! + Closes the popup client + */ +void Workspace::slotWindowClose() + { + if ( tab_box->isVisible()) + return; + Client* c = active_popup_client ? active_popup_client : active_client; + performWindowOperation( c, Options::CloseOp ); + } + +/*! + Starts keyboard move mode for the popup client + */ +void Workspace::slotWindowMove() + { + Client* c = active_popup_client ? active_popup_client : active_client; + performWindowOperation( c, Options::UnrestrictedMoveOp ); + } + +/*! + Starts keyboard resize mode for the popup client + */ +void Workspace::slotWindowResize() + { + Client* c = active_popup_client ? active_popup_client : active_client; + performWindowOperation( c, Options::UnrestrictedResizeOp ); + } + +void Client::setShortcut( const QString& _cut ) + { + QString cut = rules()->checkShortcut( _cut ); + if( cut.isEmpty()) + return setShortcutInternal( KShortcut()); +// Format: +// base+(abcdef)<space>base+(abcdef) +// E.g. Alt+Ctrl+(ABCDEF) Win+X,Win+(ABCDEF) + if( !cut.contains( '(' ) && !cut.contains( ')' ) && !cut.contains( ' ' )) + { + if( workspace()->shortcutAvailable( KShortcut( cut ), this )) + setShortcutInternal( KShortcut( cut )); + else + setShortcutInternal( KShortcut()); + return; + } + QValueList< KShortcut > keys; + QStringList groups = QStringList::split( ' ', cut ); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + { + QRegExp reg( "(.*\\+)\\((.*)\\)" ); + if( reg.search( *it ) > -1 ) + { + QString base = reg.cap( 1 ); + QString list = reg.cap( 2 ); + for( unsigned int i = 0; + i < list.length(); + ++i ) + { + KShortcut c( base + list[ i ] ); + if( !c.isNull()) + keys.append( c ); + } + } + } + for( QValueList< KShortcut >::ConstIterator it = keys.begin(); + it != keys.end(); + ++it ) + { + if( _shortcut == *it ) // current one is in the list + return; + } + for( QValueList< KShortcut >::ConstIterator it = keys.begin(); + it != keys.end(); + ++it ) + { + if( workspace()->shortcutAvailable( *it, this )) + { + setShortcutInternal( *it ); + return; + } + } + setShortcutInternal( KShortcut()); + } + +void Client::setShortcutInternal( const KShortcut& cut ) + { + if( _shortcut == cut ) + return; + _shortcut = cut; + updateCaption(); + workspace()->clientShortcutUpdated( this ); + } + +bool Workspace::shortcutAvailable( const KShortcut& cut, Client* ignore ) const + { + // TODO check global shortcuts etc. + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + { + if( (*it) != ignore && (*it)->shortcut() == cut ) + return false; + } + return true; + } + +} // namespace diff --git a/kwin/utils.cpp b/kwin/utils.cpp new file mode 100644 index 000000000..b0f77df37 --- /dev/null +++ b/kwin/utils.cpp @@ -0,0 +1,386 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + This file is for (very) small utility functions/classes. + +*/ + +#include "utils.h" + +#include <unistd.h> + +#ifndef KCMRULES + +#include <kxerrorhandler.h> +#include <assert.h> +#include <kdebug.h> + +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> +#include <X11/Xatom.h> + +#include "atoms.h" +#include "notifications.h" + +extern Time qt_x_time; + +#endif + +namespace KWinInternal +{ + +#ifndef KCMRULES + +// used to store the return values of +// XShapeQueryExtension. +// Necessary since shaped window are an extension to X +int Shape::kwin_shape_version = 0; +int Shape::kwin_shape_event = 0; + +// does the window w need a shape combine mask around it? +bool Shape::hasShape( WId w) + { + int xws, yws, xbs, ybs; + unsigned int wws, hws, wbs, hbs; + int boundingShaped = 0, clipShaped = 0; + if (!available()) + return FALSE; + XShapeQueryExtents(qt_xdisplay(), w, + &boundingShaped, &xws, &yws, &wws, &hws, + &clipShaped, &xbs, &ybs, &wbs, &hbs); + return boundingShaped != 0; + } + +int Shape::shapeEvent() + { + return kwin_shape_event; + } + +void Shape::init() + { + kwin_shape_version = 0; + int dummy; + if( !XShapeQueryExtension(qt_xdisplay(), &kwin_shape_event, &dummy)) + return; + int major, minor; + if( !XShapeQueryVersion( qt_xdisplay(), &major, &minor )) + return; + kwin_shape_version = major * 0x10 + minor; + } + +void Motif::readFlags( WId w, bool& noborder, bool& resize, bool& move, + bool& minimize, bool& maximize, bool& close ) + { + Atom type; + int format; + unsigned long length, after; + unsigned char* data; + MwmHints* hints = 0; + if ( XGetWindowProperty( qt_xdisplay(), w, atoms->motif_wm_hints, 0, 5, + FALSE, atoms->motif_wm_hints, &type, &format, + &length, &after, &data ) == Success ) + { + if ( data ) + hints = (MwmHints*) data; + } + noborder = false; + resize = true; + move = true; + minimize = true; + maximize = true; + close = true; + if ( hints ) + { + // To quote from Metacity 'We support those MWM hints deemed non-stupid' + if ( hints->flags & MWM_HINTS_FUNCTIONS ) + { + // if MWM_FUNC_ALL is set, other flags say what to turn _off_ + bool set_value = (( hints->functions & MWM_FUNC_ALL ) == 0 ); + resize = move = minimize = maximize = close = !set_value; + if( hints->functions & MWM_FUNC_RESIZE ) + resize = set_value; + if( hints->functions & MWM_FUNC_MOVE ) + move = set_value; + if( hints->functions & MWM_FUNC_MINIMIZE ) + minimize = set_value; + if( hints->functions & MWM_FUNC_MAXIMIZE ) + maximize = set_value; + if( hints->functions & MWM_FUNC_CLOSE ) + close = set_value; + } + if ( hints->flags & MWM_HINTS_DECORATIONS ) + { + if ( hints->decorations == 0 ) + noborder = true; + } + XFree( data ); + } + } + +//************************************ +// KWinSelectionOwner +//************************************ + +KWinSelectionOwner::KWinSelectionOwner( int screen_P ) + : KSelectionOwner( make_selection_atom( screen_P ), screen_P ) + { + } + +Atom KWinSelectionOwner::make_selection_atom( int screen_P ) + { + if( screen_P < 0 ) + screen_P = DefaultScreen( qt_xdisplay()); + char tmp[ 30 ]; + sprintf( tmp, "WM_S%d", screen_P ); + return XInternAtom( qt_xdisplay(), tmp, False ); + } + +void KWinSelectionOwner::getAtoms() + { + KSelectionOwner::getAtoms(); + if( xa_version == None ) + { + Atom atoms[ 1 ]; + const char* const names[] = + { "VERSION" }; + XInternAtoms( qt_xdisplay(), const_cast< char** >( names ), 1, False, atoms ); + xa_version = atoms[ 0 ]; + } + } + +void KWinSelectionOwner::replyTargets( Atom property_P, Window requestor_P ) + { + KSelectionOwner::replyTargets( property_P, requestor_P ); + Atom atoms[ 1 ] = { xa_version }; + // PropModeAppend ! + XChangeProperty( qt_xdisplay(), requestor_P, property_P, XA_ATOM, 32, PropModeAppend, + reinterpret_cast< unsigned char* >( atoms ), 1 ); + } + +bool KWinSelectionOwner::genericReply( Atom target_P, Atom property_P, Window requestor_P ) + { + if( target_P == xa_version ) + { + long version[] = { 2, 0 }; + XChangeProperty( qt_xdisplay(), requestor_P, property_P, XA_INTEGER, 32, + PropModeReplace, reinterpret_cast< unsigned char* >( &version ), 2 ); + } + else + return KSelectionOwner::genericReply( target_P, property_P, requestor_P ); + return true; + } + +Atom KWinSelectionOwner::xa_version = None; + + +QCString getStringProperty(WId w, Atom prop, char separator) + { + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + QCString result = ""; + KXErrorHandler handler; // ignore errors + status = XGetWindowProperty( qt_xdisplay(), w, prop, 0, 10000, + FALSE, XA_STRING, &type, &format, + &nitems, &extra, &data ); + if ( status == Success) + { + if (data && separator) + { + for (int i=0; i<(int)nitems; i++) + if (!data[i] && i+1<(int)nitems) + data[i] = separator; + } + if (data) + result = (const char*) data; + XFree(data); + } + return result; + } + +static Time next_x_time; +static Bool update_x_time_predicate( Display*, XEvent* event, XPointer ) +{ + if( next_x_time != CurrentTime ) + return False; + // from qapplication_x11.cpp + switch ( event->type ) { + case ButtonPress: + // fallthrough intended + case ButtonRelease: + next_x_time = event->xbutton.time; + break; + case MotionNotify: + next_x_time = event->xmotion.time; + break; + case KeyPress: + // fallthrough intended + case KeyRelease: + next_x_time = event->xkey.time; + break; + case PropertyNotify: + next_x_time = event->xproperty.time; + break; + case EnterNotify: + case LeaveNotify: + next_x_time = event->xcrossing.time; + break; + case SelectionClear: + next_x_time = event->xselectionclear.time; + break; + default: + break; + } + return False; +} + +/* + Updates qt_x_time. This used to simply fetch current timestamp from the server, + but that can cause qt_x_time to be newer than timestamp of events that are + still in our events queue, thus e.g. making XSetInputFocus() caused by such + event to be ignored. Therefore events queue is searched for first + event with timestamp, and extra PropertyNotify is generated in order to make + sure such event is found. +*/ +void updateXTime() + { + static QWidget* w = 0; + if ( !w ) + w = new QWidget; + long data = 1; + XChangeProperty(qt_xdisplay(), w->winId(), atoms->kwin_running, atoms->kwin_running, 32, + PropModeAppend, (unsigned char*) &data, 1); + next_x_time = CurrentTime; + XEvent dummy; + XCheckIfEvent( qt_xdisplay(), &dummy, update_x_time_predicate, NULL ); + if( next_x_time == CurrentTime ) + { + XSync( qt_xdisplay(), False ); + XCheckIfEvent( qt_xdisplay(), &dummy, update_x_time_predicate, NULL ); + } + assert( next_x_time != CurrentTime ); + qt_x_time = next_x_time; + XEvent ev; // remove the PropertyNotify event from the events queue + XWindowEvent( qt_xdisplay(), w->winId(), PropertyChangeMask, &ev ); + } + +static int server_grab_count = 0; + +void grabXServer() + { + if( ++server_grab_count == 1 ) + XGrabServer( qt_xdisplay()); + } + +void ungrabXServer() + { + assert( server_grab_count > 0 ); + if( --server_grab_count == 0 ) + { + XUngrabServer( qt_xdisplay()); + XFlush( qt_xdisplay()); + Notify::sendPendingEvents(); + } + } + +bool grabbedXServer() + { + return server_grab_count > 0; + } + +#endif + +bool isLocalMachine( const QCString& host ) + { +#ifdef HOST_NAME_MAX + char hostnamebuf[HOST_NAME_MAX]; +#else + char hostnamebuf[256]; +#endif + if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) + { + hostnamebuf[sizeof(hostnamebuf)-1] = 0; + if (host == hostnamebuf) + return true; + if( char *dot = strchr(hostnamebuf, '.')) + { + *dot = '\0'; + if( host == hostnamebuf ) + return true; + } + } + return false; + } + +#ifndef KCMRULES +ShortcutDialog::ShortcutDialog( const KShortcut& cut ) + : KShortcutDialog( cut, false /*TODO???*/ ) + { + // make it a popup, so that it has the grab + XSetWindowAttributes attrs; + attrs.override_redirect = True; + XChangeWindowAttributes( qt_xdisplay(), winId(), CWOverrideRedirect, &attrs ); + setWFlags( WType_Popup ); + } + +void ShortcutDialog::accept() + { + for( int i = 0; + ; + ++i ) + { + KKeySequence seq = shortcut().seq( i ); + if( seq.isNull()) + break; + if( seq.key( 0 ) == Key_Escape ) + { + reject(); + return; + } + if( seq.key( 0 ) == Key_Space ) + { // clear + setShortcut( KShortcut()); + KShortcutDialog::accept(); + return; + } + if( seq.key( 0 ).modFlags() == 0 ) + { // no shortcuts without modifiers + KShortcut cut = shortcut(); + cut.setSeq( i, KKeySequence()); + setShortcut( cut ); + return; + } + } + KShortcutDialog::accept(); + } + +// Workaround for Qt bug causing #119142 - wheel event causes only calling +// of hide() but not close(), so dialog closing is not performed. +// Possible recursive calling close->hide->close should be fine, as close() +// has checks against that. +void ShortcutDialog::hide() + { + close(); + return KShortcutDialog::hide(); + } + +#endif + + +} // namespace + +#ifndef KCMRULES +#include "utils.moc" +#endif diff --git a/kwin/utils.h b/kwin/utils.h new file mode 100644 index 000000000..eb762c0c3 --- /dev/null +++ b/kwin/utils.h @@ -0,0 +1,273 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_UTILS_H +#define KWIN_UTILS_H + +#include <qvaluelist.h> +#include <qwidget.h> +#include <kmanagerselection.h> +#include <netwm_def.h> +#include <kshortcutdialog.h> + +namespace KWinInternal +{ + +const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask; + +const long ClientWinMask = KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | + ButtonMotionMask | + PointerMotionMask | // need this, too! + EnterWindowMask | LeaveWindowMask | + FocusChangeMask | + ExposureMask | + StructureNotifyMask | + SubstructureRedirectMask; + +const QPoint invalidPoint( INT_MIN, INT_MIN ); + +class Client; +class Group; +class Options; + +typedef QValueList< Client* > ClientList; +typedef QValueList< const Client* > ConstClientList; + +typedef QValueList< Group* > GroupList; +typedef QValueList< const Group* > ConstGroupList; + +extern Options* options; + +enum Layer + { + UnknownLayer = -1, + FirstLayer = 0, + DesktopLayer = FirstLayer, + BelowLayer, + NormalLayer, + DockLayer, + AboveLayer, + ActiveLayer, // active fullscreen, or active dialog + NumLayers // number of layers, must be last + }; + +// yes, I know this is not 100% like standard operator++ +inline void operator++( Layer& lay ) + { + lay = static_cast< Layer >( lay + 1 ); + } + +// for Client::takeActivity() +enum ActivityFlags + { + ActivityFocus = 1 << 0, // focus the window + ActivityFocusForce = 1 << 1, // focus even if Dock etc. + ActivityRaise = 1 << 2 // raise the window + }; + +// Some KWin classes, mainly Client and Workspace, are very tighly coupled, +// and some of the methods of one class may be called only from speficic places. +// Those methods have additional allowed_t argument. If you pass Allowed +// as an argument to any function, make sure you really know what you're doing. +enum allowed_t { Allowed }; + +// some enums to have more readable code, instead of using bools +enum ForceGeometry_t { NormalGeometrySet, ForceGeometrySet }; + +// Areas, mostly related to Xinerama +enum clientAreaOption + { + PlacementArea, // geometry where a window will be initially placed after being mapped + MovementArea, // ??? window movement snapping area? ignore struts + MaximizeArea, // geometry to which a window will be maximized + MaximizeFullArea, // like MaximizeArea, but ignore struts - used e.g. for topmenu + FullScreenArea, // area for fullscreen windows + // these below don't depend on xinerama settings + WorkArea, // whole workarea (all screens together) + FullArea, // whole area (all screens together), ignore struts + ScreenArea // one whole screen, ignore struts + }; + +enum ShadeMode + { + ShadeNone, // not shaded + ShadeNormal, // normally shaded - isShade() is true only here + ShadeHover, // "shaded", but visible due to hover unshade + ShadeActivated // "shaded", but visible due to alt+tab to the window + }; + +class Shape + { + public: + static bool available() { return kwin_shape_version > 0; } + static int version() { return kwin_shape_version; } // as 16*major+minor, i.e. two hex digits + static bool hasShape( WId w); + static int shapeEvent(); + static void init(); + private: + static int kwin_shape_version; + static int kwin_shape_event; + }; + +// compile with XShape older than 1.0 +#ifndef ShapeInput +const int ShapeInput = 2; +#endif + +class Motif + { + public: + static void readFlags( WId w, bool& noborder, bool& resize, bool& move, + bool& minimize, bool& maximize, bool& close ); + struct MwmHints + { + ulong flags; + ulong functions; + ulong decorations; + long input_mode; + ulong status; + }; + enum { + MWM_HINTS_FUNCTIONS = (1L << 0), + MWM_HINTS_DECORATIONS = (1L << 1), + + MWM_FUNC_ALL = (1L << 0), + MWM_FUNC_RESIZE = (1L << 1), + MWM_FUNC_MOVE = (1L << 2), + MWM_FUNC_MINIMIZE = (1L << 3), + MWM_FUNC_MAXIMIZE = (1L << 4), + MWM_FUNC_CLOSE = (1L << 5) + }; + }; + +class KWinSelectionOwner + : public KSelectionOwner + { + Q_OBJECT + public: + KWinSelectionOwner( int screen ); + protected: + virtual bool genericReply( Atom target, Atom property, Window requestor ); + virtual void replyTargets( Atom property, Window requestor ); + virtual void getAtoms(); + private: + Atom make_selection_atom( int screen ); + static Atom xa_version; + }; + +// Class which saves original value of the variable, assigns the new value +// to it, and in the destructor restores the value. +// Used in Client::isMaximizable() and so on. +// It also casts away contness and generally this looks like a hack. +template< typename T > +class TemporaryAssign + { + public: + TemporaryAssign( const T& var, const T& value ) + : variable( var ), orig( var ) + { + const_cast< T& >( variable ) = value; + } + ~TemporaryAssign() + { + const_cast< T& >( variable ) = orig; + } + private: + const T& variable; + T orig; + }; + +QCString getStringProperty(WId w, Atom prop, char separator=0); +void updateXTime(); +void grabXServer(); +void ungrabXServer(); +bool grabbedXServer(); + +// the docs say it's UrgencyHint, but it's often #defined as XUrgencyHint +#ifndef UrgencyHint +#define UrgencyHint XUrgencyHint +#endif + +// for STL-like algo's +#define KWIN_CHECK_PREDICATE( name, check ) \ +struct name \ + { \ + inline bool operator()( const Client* cl ) { return check; }; \ + } + +#define KWIN_COMPARE_PREDICATE( name, type, check ) \ +struct name \ + { \ + typedef type type_helper; /* in order to work also with type being 'const Client*' etc. */ \ + inline name( const type_helper& compare_value ) : value( compare_value ) {}; \ + inline bool operator()( const Client* cl ) { return check; }; \ + const type_helper& value; \ + } + +#define KWIN_PROCEDURE( name, action ) \ +struct name \ + { \ + inline void operator()( Client* cl ) { action; }; \ + } + +KWIN_CHECK_PREDICATE( TruePredicate, cl == cl /*true, avoid warning about 'cl' */ ); + +template< typename T > +Client* findClientInList( const ClientList& list, T predicate ) + { + for ( ClientList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if ( predicate( const_cast< const Client* >( *it))) + return *it; + } + return NULL; + } + +inline +int timestampCompare( Time time1, Time time2 ) // like strcmp() + { + return NET::timestampCompare( time1, time2 ); + } + +inline +Time timestampDiff( Time time1, Time time2 ) // returns time2 - time1 + { + return NET::timestampDiff( time1, time2 ); + } + +bool isLocalMachine( const QCString& host ); + +void checkNonExistentClients(); + +#ifndef KCMRULES +// Qt dialogs emit no signal when closed :( +class ShortcutDialog + : public KShortcutDialog + { + Q_OBJECT + public: + ShortcutDialog( const KShortcut& cut ); + virtual void accept(); + virtual void hide(); + signals: + void dialogDone( bool ok ); + protected: + virtual void done( int r ) { KShortcutDialog::done( r ); emit dialogDone( r == Accepted ); } + }; +#endif + +} // namespace + +#endif diff --git a/kwin/wm-spec/index.html b/kwin/wm-spec/index.html new file mode 100644 index 000000000..187fe7d72 --- /dev/null +++ b/kwin/wm-spec/index.html @@ -0,0 +1,243 @@ +<HTML +><HEAD +><TITLE +>Extended Window Manager Hints</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="NEXT" +TITLE="Non-ICCCM features" +HREF="x24.html"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="AEN2" +>Extended Window Manager Hints</A +></H1 +><DIV +CLASS="AUTHORGROUP" +><A +NAME="AEN3" +></A +><H3 +CLASS="CORPAUTHOR" +> <A +HREF="http://www.freedesktop.org" +TARGET="_top" +>X Desktop Group</A +> + </H3 +></DIV +><SPAN +CLASS="RELEASEINFO" +>DRAFT version 1.2<BR></SPAN +><SPAN +CLASS="DATE" +>August 19, 2002<BR></SPAN +><HR></DIV +><DIV +CLASS="TOC" +><DL +><DT +><B +>Table of Contents</B +></DT +><DT +>1. <A +HREF="index.html#AEN9" +>Introduction</A +></DT +><DT +>2. <A +HREF="x24.html" +>Non-ICCCM features</A +></DT +><DT +>3. <A +HREF="x107.html" +>Root Window Properties (+Related Messages)</A +></DT +><DT +>4. <A +HREF="x208.html" +>Other Root Window Messages</A +></DT +><DT +>5. <A +HREF="x225.html" +>Application Window Properties</A +></DT +><DT +>6. <A +HREF="x340.html" +>Window Manager Protocols</A +></DT +><DT +>7. <A +HREF="x351.html" +>Implementation notes</A +></DT +><DT +>8. <A +HREF="x479.html" +>References</A +></DT +><DT +>9. <A +HREF="x483.html" +>Copyright</A +></DT +><DT +>10. <A +HREF="x489.html" +>Contributors</A +></DT +><DT +>11. <A +HREF="x512.html" +>Change history</A +></DT +></DL +></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN9" +>1. Introduction</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN11" +>1.1. Version</A +></H2 +><P +>This is DRAFT version 1.2 of the Extended Window Manager Hints (EWMH) spec, updated August 19, 2002. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN14" +>1.2. What is this spec?</A +></H2 +><P +>This spec defines interactions between window managers, applications, +and the utilities that form part of a desktop environment. It builds +on the ICCCM [2], which defines WM (window manager) interactions at a +lower level. The ICCCM does not provide ways to implement many +features that modern desktop users expect. The GNOME and KDE desktop +projects originally developed their own extensions to the ICCCM to +support these features; this spec replaces those custom extensions +with a standardized set of ICCCM additions that any desktop +environment can adopt. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN17" +>1.3. Language used in this specification</A +></H2 +><P +>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", +"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be +interpreted as described in RFC 2119. + </P +><P +>The key words "Window Manager" refer to a window manager which is adopting this +specification. "Pager" refers to desktop utility applications, including +pagers and taskbars. "Application" refers to other clients. "Clients" refers +to Pagers + Applications ie. all X clients, except for the Window Manager. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN21" +>1.4. Prerequisites for adoption of this specification</A +></H2 +><P +>Window Managers and Clients which aim to fulfil this specification MUST adhere +to the ICCCM on which this specification builds. If this specification +explicitly modifies the ICCCM Window Managers and Clients MUST fulfil these +modifications. + </P +></DIV +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +> </TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x24.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +> </TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Non-ICCCM features</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x107.html b/kwin/wm-spec/x107.html new file mode 100644 index 000000000..d165e1fbc --- /dev/null +++ b/kwin/wm-spec/x107.html @@ -0,0 +1,627 @@ +<HTML +><HEAD +><TITLE +>Root Window Properties (+Related Messages)</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Non-ICCCM features" +HREF="x24.html"><LINK +REL="NEXT" +TITLE="Other Root Window Messages" +HREF="x208.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x24.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x208.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN107" +>3. Root Window Properties (+Related Messages)</A +></H1 +><P +>Whenever this spec speaks about <SPAN +CLASS="QUOTE" +>"sending a message to the root +window"</SPAN +>, it is understood that the client is supposed to create +a ClientMessage event with the specified contents and send it by using +a SendEvent request with the following arguments: + <PRE +CLASS="PROGRAMLISTING" +>destination root +propagate False +event-mask (SubstructureNotify|SubstructureRedirect) +event the specified ClientMessage</PRE +> + </P +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN112" +>3.1. _NET_SUPPORTED</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_SUPPORTED, ATOM[]/32</PRE +><P +>This property MUST be set by the Window Manager to indicate which hints it +supports. For example: considering _NET_WM_STATE +both this atom and all supported states e.g. _NET_WM_STATE_MODAL, +_NET_WM_STATE_STICKY, would be listed. This assumes that backwards +incompatible changes will not be made to the hints (without being renamed). + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN116" +>3.2. _NET_CLIENT_LIST</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_CLIENT_LIST, WINDOW[]/32 +_NET_CLIENT_LIST_STACKING, WINDOW[]/32</PRE +><P +>These arrays contain all X Windows managed by the Window Manager. +_NET_CLIENT_LIST has initial mapping order, starting with the oldest window. +_NET_CLIENT_LIST_STACKING has bottom-to-top stacking order. These properties +SHOULD be set and updated by the Window Manager. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN120" +>3.3. _NET_NUMBER_OF_DESKTOPS</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_NUMBER_OF_DESKTOPS, CARDINAL/32</PRE +><P +>This property SHOULD be set and updated by the Window Manager to indicate the +number of virtual desktops. + </P +><P +>A Pager can request change in the desktops number by sending a _NET_NUMBER_OF_DESKTOPS message to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_NUMBER_OF_DESKTOPS + message_type = _NET_NUMBER_OF_DESKTOPS + format = 32 + data.l[0] = new_number_of_desktops</PRE +><P +>The Window Manager is free to honor or reject this request. If request is honored _NET_NUMBER_OF_DESKTOPS MUST be set to the new number of desktops, _NET_VIRTUAL_ROOTS MUST be set to store the new number of desktop virtual root window IDs and _NET_DESKTOP_VIEWPORT and _NET_WORKAREA must also be changed accordingly. The _NET_DESKTOP_NAMES property MAY remain unchanged. + </P +><P +> +If the number of desktops is shrinking and _NET_CURRENT_DESKTOP is out of the new range of available desktops, then this MUST be set to the last available desktop from the new set. If number of desktops is shrinking then clients that are still present on desktops, that are out of the new range, MUST be moved to the very last desktop from the new set. For these _NET_WM_DESKTOP MUST be updated. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN128" +>3.4. _NET_DESKTOP_GEOMETRY</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_GEOMETRY width, height, CARDINAL[2]/32</PRE +><P +>Array of two cardinals that defines the common size of all desktops. +This property SHOULD be set by the Window Manager. + </P +><P +>A Pager can request a change in the desktop geometry by sending a _NET_DESKTOP_GEOMETRY client +message to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_GEOMETRY + message_type = _NET_DESKTOP_GEOMETRY + format = 32 + data.l[0] = new_width + data.l[1] = new_height</PRE +><P +>The Window Manager MAY choose to ignore this message, in which case _NET_DESKTOP_GEOMETRY property will remain unchanged. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN135" +>3.5. _NET_DESKTOP_VIEWPORT</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_VIEWPORT x, y, CARDINAL[][2]/32</PRE +><P +>Array of pairs of cardinals that define the top left corner of each desktops +viewport. For Window Managers that don't support large desktops, this MUST +always be set to (0,0). + </P +><P +>A Pager can request to change the viewport for the current desktop by sending a +_NET_DESKTOP_VIEWPORT client message to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_VIEWPORT + message_type = _NET_DESKTOP_VIEWPORT + format = 32 + data.l[0] = new_vx + data.l[1] = new_vy</PRE +><P +>The Window Manager MAY choose to ignore this message, in which case _NET_DESKTOP_VIEWPORT property will remain unchanged. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN142" +>3.6. _NET_CURRENT_DESKTOP</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_CURRENT_DESKTOP desktop, CARDINAL/32</PRE +><P +>The index of the current desktop. This is always an integer between 0 and +_NET_NUMBER_OF_DESKTOPS - 1. This MUST be set and updated by the Window +Manager If a Pager wants to switch to another virtual desktop, it MUST send +a _NET_CURRENT_DESKTOP client message to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_CURRENT_DESKTOP + message_type = _NET_CURRENT_DESKTOP + format = 32 + data.l[0] = new_index</PRE +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN147" +>3.7. _NET_DESKTOP_NAMES</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_NAMES, UTF8_STRING[]</PRE +><P +>The names of all virtual desktops. This is a list of NULL-terminated strings in UTF-8 [1] encoding. This property MAY be changed by a Pager or the Window Manager at any time. + </P +><P +>Note: The number of names could be different from _NET_NUMBER_OF_DESKTOPS. +If it is less than _NET_NUMBER_OF_DESKTOPS - then the desktops with high +numbers are unnamed. If it is larger than _NET_NUMBER_OF_DESKTOPS, then the +excess names outside of the _NET_NUMBER_OF_DESKTOPS are considered to be +reserved in case number of desktops is increased.</P +><P +>Rationale: The name is not a necessary attribute of a virtual desktop. Thus +the availability or unavailability of names has no impact on virtual desktop +functionality. Since names are set by users and users are likely to preset +names for a fixed number of desktops, it doesn't make sense to shrink or grow +this list when the number of available desktops changes.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN153" +>3.8. _NET_ACTIVE_WINDOW</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_ACTIVE_WINDOW, WINDOW/32</PRE +><P +>The window ID of the currently active window or None if no window has the focus. +This is a read-only property set by the +Window Manager. If a client (for example, a taskbar) wants to activate +another window, it MUST send a _NET_ACTIVE_WINDOW client message to the root +window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_ACTIVE_WINDOW + window = window to activate + message_type = _NET_ACTIVE_WINDOW + format = 32 + data.l[0] = 0 /* may be used later */</PRE +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN158" +>3.9. _NET_WORKAREA</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WORKAREA, x, y, width, height CARDINAL[][4]/32 + </PRE +><P +>This property MUST be set by WM upon calculating the work area for +each desktop. Contains a geometry for each desktop. These geometries are +specified relative to the viewport on each desktop and specify an area that is +completely contained within the viewport. + Work area SHOULD be used by desktop applications to place desktop icons appropriately. + </P +><P +> The Window Manager SHOULD calculate this space by taking the current page minus space occupied by dock and panel windows, as indicated by the <A +HREF="x225.html#NETWMSTRUT" +>_NET_WM_STRUT</A +> property set on client windows. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN164" +>3.10. _NET_SUPPORTING_WM_CHECK</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_SUPPORTING_WM_CHECK, WINDOW/32</PRE +><P +>The Window Manager MUST set this property on the root window to be the ID of a + child window created by the WM, to indicate that a compliant WM is + active. The child window MUST also have the _NET_SUPPORTING_WM_CHECK + property set to the ID of the child window. The child window MUST also + have the _NET_WM_NAME property set to the name of the Window Manager. + </P +><P +>Rationale: The child window is used to distinguish an active Window Manager + from a stale _NET_SUPPORTING_WM_CHECK + property that happens to point to another window. If the + _NET_SUPPORTING_WM_CHECK window on the client window is missing + or not properly set, clients SHOULD assume that no conforming + Window Manager is present. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN169" +>3.11. _NET_VIRTUAL_ROOTS</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_VIRTUAL_ROOTS, WINDOW[]/32</PRE +><P +>To implement virtual desktops, some window managers reparent client windows to +a child of the root window. Window managers using this technique MUST set +this property to a list of IDs for windows that are acting as virtual root +windows. This property allows background setting programs to work with +virtual roots and allows clients to figure out the WM frame windows of their +windows. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN173" +>3.12. _NET_DESKTOP_LAYOUT</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_DESKTOP_LAYOUT, orientation, x, y, starting_corner CARDINAL[4]/32 + #define _NET_WM_ORIENTATION_HORZ 0 + #define _NET_WM_ORIENTATION_VERT 1 + + #define _NET_WM_TOPLEFT 0 + #define _NET_WM_TOPRIGHT 1 + #define _NET_WM_BOTTOMRIGHT 2 + #define _NET_WM_BOTTOMLEFT 3</PRE +><P +> <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>This property is set by a Pager, not by the Window + Manager.</I +></SPAN +> + When setting this property, the Pager must own a manager selection (as + defined in the ICCCM 2.8). The manager selection is called + <TT +CLASS="LITERAL" +>_NET_DESKTOP_LAYOUT_Sn</TT +> where + <TT +CLASS="LITERAL" +>n</TT +> is the screen number. The purpose of + this property is to allow the Window Manager to know the desktop + layout displayed by the Pager. + </P +><P +> <TT +CLASS="LITERAL" +>_NET_DESKTOP_LAYOUT</TT +> describes the layout of virtual + desktops relative to each other. More specifically, it describes the layout + used by the owner of the manager selection. The Window Manager may use + this layout information or may choose to ignore it. + The property contains four values: the Pager orientation, the number of + desktops in the X direction, the number in the Y direction, and the + starting corner of the Pager. + </P +><P +> Note: In order to interoperate with Pagers implementing an earlier + draft of this document, Window Managers should accept a + <TT +CLASS="LITERAL" +>_NET_DESKTOP_LAYOUT</TT +> property of length 3 and + use <TT +CLASS="LITERAL" +>_NET_WM_TOPLEFT</TT +> as the starting corner in this case. + </P +><P +> The virtual desktops are arranged in a rectangle + with X rows and Y columns. If X times Y does not match the total number of + desktops as specified by + <TT +CLASS="LITERAL" +>_NET_NUMBER_OF_DESKTOPS</TT +>, the highest-numbered + workspaces are assumed to be nonexistent. Either X or Y (but not + both) may be specified as 0 in which case its actual value will be + derived from <TT +CLASS="LITERAL" +>_NET_NUMBER_OF_DESKTOPS</TT +>. + </P +><P +> When the orientation is <TT +CLASS="LITERAL" +>_NET_WM_ORIENTATION_HORZ</TT +> + the desktops are layed out in rows, with the first desktop in the + specified starting corner. So a layout with X=4 and Y=3 starting in + the <TT +CLASS="LITERAL" +>_NET_WM_TOPLEFT</TT +> corner looks like this: +<PRE +CLASS="PROGRAMLISTING" +> +--+--+--+--+ + | 0| 1| 2| 3| + +--+--+--+--+ + | 4| 5| 6| 7| + +--+--+--+--+ + | 8| 9|10|11| + +--+--+--+--+</PRE +> +With starting_corner <TT +CLASS="LITERAL" +>_NET_WM_BOTTOMRIGHT</TT +>, it looks like +this: +<PRE +CLASS="PROGRAMLISTING" +> +--+--+--+--+ + |11|10| 9| 8| + +--+--+--+--+ + | 7| 6| 5| 4| + +--+--+--+--+ + | 3| 2| 1| 0| + +--+--+--+--+</PRE +> + + </P +><P +> When the orientation is <TT +CLASS="LITERAL" +>_NET_WM_ORIENTATION_VERT</TT +> + the layout for X=4 and Y=3 starting in the <TT +CLASS="LITERAL" +>_NET_WM_TOPLEFT</TT +> + corner looks like: + +<PRE +CLASS="PROGRAMLISTING" +> +--+--+--+--+ + | 0| 3| 6| 9| + +--+--+--+--+ + | 1| 4| 7|10| + +--+--+--+--+ + | 2| 5| 8|11| + +--+--+--+--+</PRE +> +With starting_corner <TT +CLASS="LITERAL" +>_NET_WM_TOPRIGHT</TT +>, it looks like: + +<PRE +CLASS="PROGRAMLISTING" +> +--+--+--+--+ + | 9| 6| 3| 0| + +--+--+--+--+ + |10| 7| 4| 1| + +--+--+--+--+ + |11| 8| 5| 2| + +--+--+--+--+</PRE +> + </P +><P +> The numbers here are the desktop numbers, as for + <TT +CLASS="LITERAL" +>_NET_CURRENT_DESKTOP</TT +>. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN202" +>3.13. _NET_SHOWING_DESKTOP</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_SHOWING_DESKTOP desktop, CARDINAL/32</PRE +><P +> Some Window Managers have a "showing the desktop" mode in which windows + are hidden, and the desktop background is displayed and focused. If a + Window Manager supports the _NET_SHOWING_DESKTOP hint, it MUST set it + to a value of 1 if the Window Manager is in "showing the desktop" mode, + and a value of zero if the Window Manager is not in this mode. + </P +><P +> If a Pager wants to enter or leave the mode, it MUST + send a _NET_SHOWING_DESKTOP client message to the root window + requesting the change: + <PRE +CLASS="PROGRAMLISTING" +>_NET_SHOWING_DESKTOP + message_type = _NET_SHOWING_DESKTOP + format = 32 + data.l[0] = boolean 0 or 1</PRE +> + The Window Manager may choose to ignore this client message. + </P +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x24.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x208.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Non-ICCCM features</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Other Root Window Messages</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x208.html b/kwin/wm-spec/x208.html new file mode 100644 index 000000000..1097bd923 --- /dev/null +++ b/kwin/wm-spec/x208.html @@ -0,0 +1,225 @@ +<HTML +><HEAD +><TITLE +>Other Root Window Messages</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Root Window Properties (+Related Messages)" +HREF="x107.html"><LINK +REL="NEXT" +TITLE="Application Window Properties" +HREF="x225.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x107.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x225.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN208" +>4. Other Root Window Messages</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN210" +>4.1. _NET_CLOSE_WINDOW</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_CLOSE_WINDOW</PRE +><P +> Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW client + message request to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_CLOSE_WINDOW + window = window to close + message_type = _NET_CLOSE_WINDOW + format = 32 + data.l[0] = 0 /* may be used later */</PRE +><P +>The Window Manager MUST then attempt to close the window specified. + </P +><P +> Rationale: A Window Manager might be more clever than the usual method (send WM_DELETE message if the protocol is selected, XKillClient otherwise). It might introduce a timeout, for example. Instead of duplicating the code, the Window Manager can easily do the job. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN217" +>4.2. _NET_WM_MOVERESIZE</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_MOVERESIZE + window = window to be moved or resized + message_type = _NET_WM_MOVERESIZE + format = 32 + data.l[0] = x_root + data.l[1] = y_root + data.l[2] = direction + data.l[3] = button</PRE +><P +> This message allows an application to initiate window movement or + resizing. This allows the application to define its own move and size + "grips", whilst letting the window manager control the actual operation. + This means that all moves/resizes can happen in a consistent manner as + defined by the WM. + </P +><P +> When sending this message in response to a button press event, button + SHOULD indicate the button which was pressed, x_root and y_root MUST + indicate the position of the button press with respect to the root + window and direction MUST indicate whether this is a move or resize + event, and if it is a resize event, which edges of the window the size + grip applies to. When sending this message in response to a key event, + the direction MUST indicate wether this this is a move or resize event + and the other fields are unused. + </P +><PRE +CLASS="PROGRAMLISTING" +>#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */</PRE +><P +> The Client MUST release all grabs prior to sending such message. + </P +><P +> The Window Manager can use the button field to determine the + events on which it terminates the operation initiated by the + _NET_WM_MOVERESIZE message. Since there is a race condition between + client sending the _NET_WM_MOVERESIZE message and the user releasing + the button, Window Managers are advised to offer some other means to + terminate the operation, e.g. by pressing the ESC key. + </P +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x107.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x225.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Root Window Properties (+Related Messages)</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Application Window Properties</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x225.html b/kwin/wm-spec/x225.html new file mode 100644 index 000000000..7a31fb1b7 --- /dev/null +++ b/kwin/wm-spec/x225.html @@ -0,0 +1,720 @@ +<HTML +><HEAD +><TITLE +>Application Window Properties</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Other Root Window Messages" +HREF="x208.html"><LINK +REL="NEXT" +TITLE="Window Manager Protocols" +HREF="x340.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x208.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x340.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN225" +>5. Application Window Properties</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN227" +>5.1. _NET_WM_NAME</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_NAME, UTF8_STRING</PRE +><P +>The Client SHOULD set this to the title of the window in UTF-8 encoding. If +set, the Window Manager should use this in preference to WM_NAME. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN231" +>5.2. _NET_WM_VISIBLE_NAME</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_VISIBLE_NAME, UTF8_STRING</PRE +><P +>If the Window Manager displays a window name other than _NET_WM_NAME the Window Manager MUST set this to the title displayed in UTF-8 encoding. + </P +><P +>Rationale: For window managers that display a title different from the _NET_WM_NAME or WM_NAME of the window (i.e. xterm <1>, xterm <2>, ... is shown, but _NET_WM_NAME / WM_NAME is still xterm for each window). This property allows taskbars / pagers to display the same title as the window manager. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN236" +>5.3. _NET_WM_ICON_NAME</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_ICON_NAME, UTF8_STRING</PRE +><P +>The Client SHOULD set this to the title of the icon for this window in UTF-8 +encoding. If set, the Window Manager should use this in preference to +WM_ICON_NAME. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN240" +>5.4. _NET_WM_VISIBLE_ICON_NAME</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_VISIBLE_ICON_NAME, UTF8_STRING</PRE +><P +>If the Window Manager displays an icon name other than _NET_WM_ICON_NAME +the Window Manager MUST set this to the title displayed in UTF-8 encoding. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN244" +>5.5. _NET_WM_DESKTOP</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_DESKTOP desktop, CARDINAL/32</PRE +><P +>Cardinal to determine the desktop the window is in (or wants to be) starting +with 0 for the first desktop. A Client MAY choose not to set this property, +in which case the Window Manager SHOULD place as it wishes. 0xFFFFFFFF +indicates that the window SHOULD appear on all desktops/workspaces. + </P +><P +>The Window Manager should honor _NET_WM_DESKTOP whenever a withdrawn window +requests to be mapped. + </P +><P +>The Window Manager should remove the property whenever +a window is withdrawn, but it should leave the property in place when it is +shutting down, e.g. in response to losing ownership of the WM_Sn manager +selection. + </P +><P +>Rationale: Removing the property upon window withdrawal helps legacy +applications which want to reuse withdrawn windows. Not removing the property +upon shutdown allows the next Window Manager to restore windows to their +previous desktops. + </P +><P +>A Client can request a change of desktop for a non-withdrawn window by sending +a _NET_WM_DESKTOP client message to the root window: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_DESKTOP + window = the respective client window + message_type = _NET_WM_DESKTOP + format = 32 + data.l[0] = new_desktop</PRE +><P +> The Window Manager MUST keep this property updated on all windows. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN254" +>5.6. _NET_WM_WINDOW_TYPE</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_WINDOW_TYPE, ATOM[]/32</PRE +><P +>This SHOULD be set by the Client before mapping, to a list of atoms indicating +the functional type of the window. This property SHOULD be used by the window +manager in determining the decoration, stacking position and other behaviour +of the window. The Client SHOULD specify window types in order of preference +(the first being most preferable), but MUST include at least one of the basic +window type atoms from the list below. This is to allow for extension of the +list of types, whilst providing default behaviour for window managers that do +not recognise the extensions. + </P +><P +>Rationale: This hint is intend to replace the MOTIF hints. One of the +objections to the MOTIF hints is that they are a purely visual description of +the window decoration. By describing the function of the window, the window +manager can apply consistent decoration and behaviour to windows of the same +type. Possible examples of behaviour include keeping dock/panels on top or +allowing pinnable menus / toolbars to only be hidden when another window has +focus (NextStep style). + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_WINDOW_TYPE_DESKTOP, ATOM +_NET_WM_WINDOW_TYPE_DOCK, ATOM +_NET_WM_WINDOW_TYPE_TOOLBAR, ATOM +_NET_WM_WINDOW_TYPE_MENU, ATOM +_NET_WM_WINDOW_TYPE_UTILITY, ATOM +_NET_WM_WINDOW_TYPE_SPLASH, ATOM +_NET_WM_WINDOW_TYPE_DIALOG, ATOM +_NET_WM_WINDOW_TYPE_NORMAL, ATOM</PRE +><P +>_NET_WM_WINDOW_TYPE_DESKTOP indicates a desktop feature. This can include a +single window containing desktop icons with the same dimensions as the screen, +allowing the desktop environment to have full control of the desktop, without +the need for proxying root window clicks. + </P +><P +>_NET_WM_WINDOW_TYPE_DOCK indicates a dock or panel feature. Typically a +window manager would keep such windows on top of all other windows. + </P +><P +>_NET_WM_WINDOW_TYPE_TOOLBAR and _NET_WM_WINDOW_TYPE_MENU indicate toolbar and +pinnable menu windows, respectively (i.e. toolbars and menus "torn off" from +the main application). Windows of this type may set the WM_TRANSIENT_FOR +hint indicating the main application window. + </P +><P +>_NET_WM_WINDOW_TYPE_UTILITY indicates a small persistent utility window, such as +a palette or toolbox. It is distinct from type TOOLBAR because it does not +correspond to a toolbar torn off from the main application. It's distinct from +type DIALOG because it isn't a transient dialog, the user will probably keep it +open while they're working. Windows of this type may set the WM_TRANSIENT_FOR +hint indicating the main application window. + </P +><P +>_NET_WM_WINDOW_TYPE_SPLASH indicates that the window is a splash screen +displayed as an application is starting up. + </P +><P +>_NET_WM_WINDOW_TYPE_DIALOG indicates that this is a dialog window. If +_NET_WM_WINDOW_TYPE is not set, then windows with WM_TRANSIENT_FOR set MUST +be taken as this type. + </P +><P +>_NET_WM_WINDOW_TYPE_NORMAL indicates that this is a normal, top-level window. +Windows with neither _NET_WM_WINDOW_TYPE nor WM_TRANSIENT_FOR are set MUST +be taken as this type. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN267" +>5.7. _NET_WM_STATE</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_STATE, ATOM[]</PRE +><P +>A list of hints describing the window state. Atoms present in the list MUST be +considered set, atoms not present in the list MUST be considered not set. The +Window Manager SHOULD honor +_NET_WM_STATE whenever a withdrawn window requests to be mapped. A Client +wishing to change the state of a window MUST send a _NET_WM_STATE client +message to the root window (see below). The Window Manager MUST keep this +property updated to reflect the current state of the window. + </P +><P +>The Window Manager should remove the property whenever +a window is withdrawn, but it should leave the property in place when it is +shutting down, e.g. in response to losing ownership of the WM_Sn manager +selection. + </P +><P +>Rationale: Removing the property upon window withdrawal helps legacy +applications which want to reuse withdrawn windows. Not removing the property +upon shutdown allows the next Window Manager to restore windows to their +previous state. + </P +><P +>Possible atoms are: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_STATE_MODAL, ATOM +_NET_WM_STATE_STICKY, ATOM +_NET_WM_STATE_MAXIMIZED_VERT, ATOM +_NET_WM_STATE_MAXIMIZED_HORZ, ATOM +_NET_WM_STATE_SHADED, ATOM +_NET_WM_STATE_SKIP_TASKBAR, ATOM +_NET_WM_STATE_SKIP_PAGER, ATOM +_NET_WM_STATE_HIDDEN, ATOM +_NET_WM_STATE_FULLSCREEN, ATOM +_NET_WM_STATE_FLOATING, ATOM</PRE +><P +>An implementation MAY add new atoms to this list. Implementations +without extensions MUST ignore any unknown atoms, effectively removing +them from the list. These extension atoms MUST NOT start with the prefix +_NET. + </P +><P +>_NET_WM_STATE_MODAL indicates that this is a modal dialog box. The +WM_TRANSIENT_FOR hint MUST be set to indicate which window the dialog is a +modal for, or set to the root window if the dialog is a modal for its window +group. + </P +><P +>_NET_WM_STATE_STICKY indicates that the Window Manager SHOULD keep the +window's position fixed on the screen, even when the virtual desktop scrolls. + </P +><P +>_NET_WM_STATE_MAXIMIZED_{VERT,HORZ} indicates that the window is +{vertically,horizontally} maximised. + </P +><P +>_NET_WM_STATE_SHADED indicates that the window is shaded. + </P +><P +>_NET_WM_STATE_SKIP_TASKBAR indicates that the window should not be +included on a taskbar. This hint should be requested by the +application, i.e. it indicates that the window by nature is never +in the taskbar. Applications should not set this hint if +_NET_WM_WINDOW_TYPE already conveys the exact nature of the +window. + </P +><P +>_NET_WM_STATE_SKIP_PAGER indicates that the window should not be +included on a Pager. This hint should be requested by the application, +i.e. it indicates that the window by nature is never in the +Pager. Applications should not set this hint if _NET_WM_WINDOW_TYPE +already conveys the exact nature of the window. + </P +><P +>_NET_WM_STATE_HIDDEN should be set by the window manager to indicate +that a window would not be visible on the screen if its +desktop/viewport were active and its coordinates were within the +screen bounds. The canonical example is that minimized windows should +be in the _NET_WM_STATE_HIDDEN state. Pagers and similar applications +should use _NET_WM_STATE_HIDDEN instead of WM_STATE to decide whether +to display a window in miniature representations of the windows on a +desktop. +<A +NAME="AEN283" +HREF="#FTN.AEN283" +>[1]</A +> + </P +><P +>_NET_WM_STATE_FULLSCREEN indicates that the window should fill the entire screen +and have no window decorations. For example, a presentation program would use +this hint. + </P +><P +>_NET_WM_STATE_FLOATING indicates that the window should be on top of other +windows of the same type. Applications should not set this hint +if _NET_WM_WINDOW_TYPE already conveys the exact nature of the window. +Windows in this state would typically appear above other windows of the same +_NET_WM_WINDOW_TYPE. + </P +><P +>To change the state of a mapped window, a Client MUST send a _NET_WM_STATE +client message to the root window (window is the respective window, type +_NET_WM_STATE, format 32, l[0]=<the action, as listed below>, +l[1]=<First property to alter>, l[2]=<Second property to alter>). +This message allows two properties to be changed simultaneously, specifically +to allow both horizontal and vertical maximisation to be altered together. +l[2] MUST be set to zero if only one property is to be changed. l[0], the +action, MUST be one of: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_STATE_REMOVE 0 /* remove/unset property */ +_NET_WM_STATE_ADD 1 /* add/set property */ +_NET_WM_STATE_TOGGLE 2 /* toggle property */</PRE +><P +> See also the implementation notes on <A +HREF="x351.html#URGENCY" +>urgency</A +> and <A +HREF="x351.html#NORESIZE" +>fixed size windows</A +>. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN292" +>5.8. _NET_WM_ALLOWED_ACTIONS</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_ALLOWED_ACTIONS, ATOM[]</PRE +><P +>A list of atoms indicating user operations that the window manager supports for +this window. Atoms present in the list indicate allowed actions, atoms not +present in the list indicate actions that are not supported for this window. +The window manager MUST keep this property updated to reflect the +actions which are currently "active" or "sensitive" for a window. +Taskbars, Pagers, and other tools use _NET_WM_ALLOWED_ACTIONS to +decide which actions should be made available to the user. + </P +><P +>Possible atoms are: + </P +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_ACTION_MOVE, ATOM +_NET_WM_ACTION_RESIZE, ATOM +_NET_WM_ACTION_SHADE, ATOM +_NET_WM_ACTION_STICK, ATOM +_NET_WM_ACTION_MAXIMIZE_HORZ, ATOM +_NET_WM_ACTION_MAXIMIZE_VERT, ATOM +_NET_WM_ACTION_FULLSCREEN, ATOM +_NET_WM_ACTION_CHANGE_DESKTOP, ATOM +_NET_WM_ACTION_CLOSE, ATOM</PRE +><P +>An implementation MAY add new atoms to this list. Implementations +without extensions MUST ignore any unknown atoms, effectively removing +them from the list. These extension atoms MUST NOT start with the prefix +_NET. + </P +><P +>Note that the actions listed here are those that the <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>Window +Manager</I +></SPAN +> will honor for this window. The operations must still be +requested through the normal mechanisms outlined in this specification. For +example, _NET_WM_ACTION_CLOSE does not mean that clients can send a +WM_DELETE_WINDOW message to this window; it means that clients can use a +_NET_CLOSE_WINDOW message to ask the Window Manager to do so. + </P +><P +>Window Managers SHOULD ignore the value of _NET_WM_ALLOWED_ACTIONS when they +initially manage a window. This value may be left over from a previous window +manager with different policies. + </P +><P +>_NET_WM_ACTION_MOVE indicates that the window may be moved around the screen. + </P +><P +>_NET_WM_ACTION_RESIZE indicates that the window may be resized. +(Implementation note: window managers can identify a non-resizable +window because its minimum and maximum size in WM_NORMAL_HINTS will be the same.) + </P +><P +>_NET_WM_ACTION_SHADE indicates that the window may be shaded. + </P +><P +>_NET_WM_ACTION_STICK indicates that the window may have its sticky state +toggled (as for _NET_WM_STATE_STICKY). Note that this state has to do with +viewports, not desktops. + </P +><P +>_NET_WM_ACTION_MAXIMIZE_HORZ indicates that the window may be maximized horizontally. + </P +><P +>_NET_WM_ACTION_MAXIMIZE_VERT indicates that the window may be maximized vertically. + </P +><P +>_NET_WM_ACTION_FULLSCREEN indicates that the window may be brought to + fullscreen mode. + </P +><P +>_NET_WM_ACTION_CHANGE_DESKTOP indicates that the window may be moved between desktops. + </P +><P +>_NET_WM_ACTION_CLOSE indicates that the window may be closed (i.e. a WM_DELETE_WINDOW +message may be sent). + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN311" +>5.9. _NET_WM_STRUT</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_STRUT, left, right, top, bottom, CARDINAL[4]/32</PRE +><P +>This property MUST be set by the Client if the window is to reserve space at +the edge of the screen. The property contains a 4 cardinals specifying the +width of the reserved area at each border of the screen. +The order of the borders is left, right, top, bottom. +The client MAY change this property anytime, therefore the Window Manager MUST +watch out for property notify events. + </P +><P +>The purpose of struts is to reserve space at the borders of the desktop. This +is very useful for a docking area, a taskbar or a panel, for instance. The +window manager should know about this reserved space in order to be able to +preserve the space. Also maximized windows should not cover that reserved +space. + </P +><P +>Rationale: A simple "do not cover" hint is not enough for dealing with e.g. +auto-hide panels. + </P +><P +>Notes: An auto-hide panel SHOULD set the strut to be its minimum, hidden size. +A "corner" panel that does not extend for the full length of a screen border +SHOULD only set one strut. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN318" +>5.10. _NET_WM_ICON_GEOMETRY</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_ICON_GEOMETRY, x, y, width, height, CARDINAL[4]/32</PRE +><P +>This optional property MAY be set by standalone tools like a taskbar or an +iconbox. It specifies the geometry of a possible icon in case the window is iconified. + </P +><P +>Rationale: This makes it possible for a window manager to display a nice +animation like morphing the window into its icon. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN323" +>5.11. _NET_WM_ICON</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_ICON CARDINAL[][2+n]/32</PRE +><P +>This is an array of possible icons for the client. This specification does +not stipulate what size these icons should be, but individual desktop +environments or toolkits may do so. The Window Manager MAY scale any of these +icons to an appropriate size. + </P +><P +>This is an array of 32bit packed CARDINAL ARGB with high byte being A, low +byte being B. First two cardinals are width, height. Data is in rows, left to +right and top to bottom. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN328" +>5.12. _NET_WM_PID</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_PID CARDINAL/32</PRE +><P +>If set, this property MUST contain the process ID of the client owning this +window. This MAY be used by the Window Manager to kill windows which do not +respond to the _NET_WM_PING protocol. + </P +><P +>If _NET_WM_PID is set, the ICCCM-specified property WM_CLIENT_MACHINE +MUST also be set. While the ICCCM only requests that WM_CLIENT_MACHINE is set +<SPAN +CLASS="QUOTE" +>" to a string that forms the name of the machine running the client as +seen from the machine running the server"</SPAN +> conformance to this +specification requires that WM_CLIENT_MACHINE be set to the fully-qualified domain +name of the client's host. + </P +><P +>See also the implementation notes on <A +HREF="x351.html#KILLINGWINDOWS" +>killing hung processes</A +>. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN336" +>5.13. _NET_WM_HANDLED_ICONS</A +></H2 +><PRE +CLASS="PROGRAMLISTING" +>_NET_WM_HANDLED_ICONS</PRE +><P +>This property can be set by clients to indicate that the Window Manager need +not provide icons for iconified windows, for example if the client is a taskbar +and provides buttons for iconified windows. + </P +></DIV +></DIV +><H3 +CLASS="FOOTNOTES" +>Notes</H3 +><TABLE +BORDER="0" +CLASS="FOOTNOTES" +WIDTH="100%" +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +WIDTH="5%" +><A +NAME="FTN.AEN283" +HREF="x225.html#AEN283" +>[1]</A +></TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +WIDTH="95%" +><P +>Implementation note: if an application asks to toggle +_NET_WM_STATE_HIDDEN the window manager should probably just ignore +the request, since _NET_WM_STATE_HIDDEN is a function of some other +aspect of the window such as minimization, rather than an independent +state.</P +></TD +></TR +></TABLE +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x208.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x340.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Other Root Window Messages</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Window Manager Protocols</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x24.html b/kwin/wm-spec/x24.html new file mode 100644 index 000000000..47b406d0b --- /dev/null +++ b/kwin/wm-spec/x24.html @@ -0,0 +1,496 @@ +<HTML +><HEAD +><TITLE +>Non-ICCCM features</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +HREF="index.html"><LINK +REL="NEXT" +TITLE="Root Window Properties (+Related Messages)" +HREF="x107.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="index.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x107.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN24" +>2. Non-ICCCM features</A +></H1 +><P +>There is a number of window management features or behaviours which are +not specified in the ICCCM, but are commonly met in modern Window Managers and Desktop Environments.</P +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN27" +>2.1. Additional States</A +></H2 +><P +>The ICCCM allows Window Managers to implement additional window states, which will +appear to clients as substates of NormalState and IconicState. Two +commonly met examples are Maximized and Shaded. A Window Manager may implement these +as proper substates of NormalState and IconicState, or it may treat them +as independent flags, allowing e.g. a maximized window to be iconified +and to re-appear as maximized upon de-iconification.</P +><DIV +CLASS="SECT3" +><H3 +CLASS="SECT3" +><A +NAME="AEN30" +>2.1.1. Maximization</A +></H3 +><P +>Maximization is a very old feature of Window Managers. There was even a ZoomedState +in early ICCCM drafts. Maximizing a window should give it as much of the +screen area as possible (this may not be the full screen area, but only +a smaller 'workarea', since the Window Manager may have reserved certain areas for other +windows). A Window Manager is expected to remember the geometry of a maximized window +and restore it upon de-maximization. Modern Window Managers typically allow separate +horizontal and vertical maximization.</P +><P +>With the introduction of the Xinerama extension in X11 R6.4, maximization +has become more involved. Xinerama allows a screen to span multiple +monitors in a freely configurable geometry. In such a setting, maximizing +a window would ideally not grow it to fill the whole screen, but only the +monitor it is shown on. There are of course borderline cases for windows +crossing monitor boundaries, and 'real' maximization to the full screen may +sometimes be useful.</P +></DIV +><DIV +CLASS="SECT3" +><H3 +CLASS="SECT3" +><A +NAME="AEN34" +>2.1.2. Shading</A +></H3 +><P +>Some Desktop Environments offer shading (also known as rollup) as an alternative to +iconfication. A shaded window typically shows only the titlebar, the client +window is hidden, thus shading is not useful for windows which are not +decorated with a titlebar.</P +></DIV +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN37" +>2.2. Modality</A +></H2 +><P +>The Window Manager _TRANSIENT_FOR hint of the ICCCM allows clients to specify that a +toplevel window may be closed before the client finishes. A typical example +of a transient window is a dialog. Some dialogs can be open for a long time, +while the user continues to work in the main window. Other dialogs have to be +closed before the user can continue to work in the main window. This property +is called modality. While clients can implement modal windows in an ICCCM +compliant way using the globally active input model, some Window Managers offer support +for handling modality.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="LARGEDESKS" +>2.3. Large Desktops</A +></H2 +><P +>The Window Manager may offer to arrange the managed windows on a desktop that is +larger than the root window. The screen functions as a viewport on this large +desktop. Different policies regarding the positioning of the viewport on the +desktop can be implemented: The Window Manager may only allow to change the viewport +position in increments of the screen size (paging) or it may allow arbitrary +positions (scrolling).</P +><P +>To fulfill the ICCCM principle that clients should behave the same +regardless wether a Window Manager is running or not, Window Managers which +implement large desktops must interpret all client-provided geometries with +respect to the current viewport.</P +><DIV +CLASS="SECT3" +><H3 +CLASS="SECT3" +><A +NAME="LARGEDESKSIMPL" +>2.3.1. Implementation note</A +></H3 +><P +>There are two options for implementing a large desktop: The first is to +keep the managed windows (or, if reparenting, their frames) as children +of the root window. Moving the viewport is achieved by moving all managed +windows in the opposite direction.</P +><P +>The second alternative is to reparent all managed windows to a dedicated +large window (somewhat inappropriately called a 'virtual root'). Moving +the viewport is then achieved by moving the virtual root in the opposite +direction.</P +><P +>Both alternatives are completely ICCCM compliant, although the second one +may be somewhat problematic for clients trying to figure out the Window Manager decorations +around their toplevel windows and for clients trying to draw background +images on the root window.</P +></DIV +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN49" +>2.4. Sticky windows</A +></H2 +><P +>A Window Manager which implements a large desktop typically offers a way for the user +to make certain windows 'stick to the glass', i.e. these windows will stay +at the same position on the screen when the viewport is moved.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN52" +>2.5. Virtual Desktops</A +></H2 +><P +>Most X servers have only a single screen. The Window Manager may virtualize this +resource and offer multiple so-called 'virtual desktops', of which only one +can be shown on the screen at a time. There is some variation among the +features of virtual desktop implementations. There may be a fixed number +of desktops, or new ones may be created dynamically. The size of the desktops +may be fixed or variable. If the desktops are larger than the root window, +their viewports (see <A +HREF="x24.html#LARGEDESKS" +>Section 2.3</A +>) may be independent or forced to be at the same +position.</P +><P +>A Window Manager which implements virtual desktops generally offers a way for the user +to move clients between desktops. Clients may be allowed to occupy more than +one desktop simultaneously.</P +><DIV +CLASS="SECT3" +><H3 +CLASS="SECT3" +><A +NAME="AEN57" +>2.5.1. Implementation note</A +></H3 +><P +>There are at least two options for implementing virtual desktops. +The first is to use multiple virtual roots (see <A +HREF="x24.html#LARGEDESKSIMPL" +>Section 2.3.1</A +>) and change the current +desktop by manipulating the stacking order of the virtual roots. This is +completely ICCCM compliant, but has the issues outlined in <A +HREF="x24.html#LARGEDESKSIMPL" +>Section 2.3.1</A +></P +><P +>The second option is to keep all managed windows as children of the root +window and unmap the frames of those which are not on the current +desktop. Unmapped windows should be placed in IconicState, according to +the ICCCM. Windows which are actually iconified or minimized +should have the _NET_WM_STATE_HIDDEN property set, to +communicate to Pagers that the window should not be represented as +"onscreen."</P +></DIV +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN63" +>2.6. Pagers</A +></H2 +><P +>A pager offers a different UI for window management tasks. It shows a +miniature view of the desktop(s) representing managed windows by small +rectangles and allows the user to initiate various Window Manager actions by manipulating +these representations. Typically offered actions are activation (see <A +HREF="x24.html#ACTIVATION" +>Section 2.8</A +>), +moving, restacking, iconification, maximization and closing. On a large +desktop, the pager may offer a way to move the viewport. On virtual desktops, +the pager may offer ways to move windows between desktops and to change the +current desktop.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN67" +>2.7. Taskbars</A +></H2 +><P +>A taskbar offers another UI for window management tasks. It typically +represents client windows as a list of buttons labelled with the window +titles and possibly icons. Pressing a button initiates a Window Manager action on the +represented window, typical actions being activation and iconification. +In environments with a taskbar, icons are often considered inappropriate, +since the iconified windows are already represented in the taskbar.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="ACTIVATION" +>2.8. Activation</A +></H2 +><P +>In the X world, activating a window means to give it the input focus. +This may not be possible if the window is unmapped, because it is on a +different desktop. Thus, activating a window may involve additional steps +like moving it to the current desktop (or changing to the desktop the window +is on), deiconifying it or raising it.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN73" +>2.9. Animated iconification</A +></H2 +><P +>Some Window Managers display some form of animation when (de-)iconifying a window. +This may be a line drawing connecting the corners of the window with +the corners of the icon or the window may be opaquely moved and resized +on some trajectory joining the window location and the icon location.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN76" +>2.10. Window-in-window MDI</A +></H2 +><P +>Window-in-window MDI is a multiple document interface known from MS +Windows platforms. Programs employing it have a single top-level window +which contains a workspace which contains the subwindows for the open +documents. These subwindows are decorated with Window Manager frames and can be +manipulated within their parent window just like ordinary top-level +windows on the root window.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN79" +>2.11. Layered stacking order</A +></H2 +><P +>Some Window Managers keep the toplevel windows not in a single linear stack, +but subdivide the stack into several layers. There is a lot of variation +among the features of layered stacking order implementations. The number of +layers may or may not be fixed. The layer of a toplevel window may be explicit +and directly modifyable or derived from other properties of the window, e.g. +the <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>type</I +></SPAN +> of the window. The stacking order may or may not +be strict, i.e. not allow the user to raise or lower windows beyond their +layer.</P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN83" +>2.12. Scope of this spec</A +></H2 +><P +>This spec tries to address the following issues:</P +><P +></P +><UL +><LI +><P +>Allow clients to influence their initial state with respect +to maximization, shading, stickyness, desktop, stacking order.</P +></LI +><LI +><P +>Improve the Window Managers ability to vary window +decorations and maintain the stacking order by allowing clients to hint the +Window Manager about the type of their windows.</P +></LI +><LI +><P +>Enable pagers and taskbars to be implemented as separate +clients and allow them to work with any compliant Window Manager.</P +></LI +></UL +><P +>This spec doesn't cover any of the following:</P +><P +></P +><UL +><LI +><P +>Other IPC mechanisms like ICE or Corba.</P +></LI +><LI +><P +>Window Manager configuration.</P +></LI +><LI +><P +>Window Manager documentation.</P +></LI +><LI +><P +>Clients appearing on a proper subset of desktops.</P +></LI +><LI +><P +>Window-in-window MDI.</P +></LI +></UL +><P +>The Window Manager is supposed to be in charge of window management +policy, so that there is consistent behaviour on the user's screen no matter +who wrote the clients.</P +><P +>The spec offers a lot of external control about Window Manager actions. +This is intended mainly to allow pagers, taskbars and similar Window Manager +UIs to be implemented as separate clients. "Ordinary" clients shouldn't use +these except maybe in response to a direct user request (i.e. setting a +config option to start maximized or specifying a -desk n cmdline +argument).</P +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x107.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Root Window Properties (+Related Messages)</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x340.html b/kwin/wm-spec/x340.html new file mode 100644 index 000000000..e72c48cc6 --- /dev/null +++ b/kwin/wm-spec/x340.html @@ -0,0 +1,182 @@ +<HTML +><HEAD +><TITLE +>Window Manager Protocols</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Application Window Properties" +HREF="x225.html"><LINK +REL="NEXT" +TITLE="Implementation notes" +HREF="x351.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x225.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x351.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN340" +>6. Window Manager Protocols</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN342" +>6.1. _NET_WM_PING</A +></H2 +><P +>This protocol allows the Window Manager to determine if the Client is still +processing X events. This can be used by the Window Manager to determine if a +window which fails to close after being sent WM_DELETE_WINDOW has stopped +responding, or has stalled for some other reason, such as waiting for user +confirmation. A Client SHOULD indicate that it is willing to participate in +this protocol by listing _NET_WM_PING in the WM_PROTOCOLS property of the +client window. + </P +><P +>A Window Manager can use this protocol at any time by sending a client message +as follows: + </P +><PRE +CLASS="PROGRAMLISTING" +>type = ClientMessage +window = the respective client window +message_type = WM_PROTOCOLS +format = 32 +data.l[0] = _NET_WM_PING +data.l[1] = timestamp</PRE +><P +>A participating Client receiving this message MUST send it back to the root +window immediately, by setting window = root, and calling XSendEvent. The +Client MUST NOT alter the timestamp, as this can be used by the Window Manager +to uniquely identify the ping. + </P +><P +>The Window Manager MAY kill the Client (using _NET_WM_PID) if it fails to +respond to this protocol within a reasonable time. + </P +><P +>See also the implementation notes on <A +HREF="x351.html#KILLINGWINDOWS" +>killing hung processes</A +>. + </P +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x225.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x351.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Application Window Properties</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Implementation notes</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x351.html b/kwin/wm-spec/x351.html new file mode 100644 index 000000000..882f63fbf --- /dev/null +++ b/kwin/wm-spec/x351.html @@ -0,0 +1,648 @@ +<HTML +><HEAD +><TITLE +>Implementation notes</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Window Manager Protocols" +HREF="x340.html"><LINK +REL="NEXT" +TITLE="References" +HREF="x479.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x340.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x479.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN351" +>7. Implementation notes</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN353" +>7.1. Desktop/workspace model</A +></H2 +><P +>This spec assumes a desktop model that consists of one or more completely +independent desktops which may or may not be larger than the screen area. +When a desktop is larger than the screen it is left to the window manager if +it will implement scrolling or paging. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN356" +>7.2. File Manager desktop</A +></H2 +><P +>This spec suggests implementing the file manager desktop by mapping a +desktop-sized window (no shape) to all desktops, with +_NET_WM_WINDOW_TYPE_DESKTOP. This makes the desktop focusable and greatly +simplifies implementation of the file manager. It is also faster than +managing lots of small shaped windows. The file manager draws the background +on this window. There should be a root property with a window handle for use +in applications that want to draw the background (xearth). + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN359" +>7.3. Implementing enhanced support for application transient windows</A +></H2 +><P +>If the WM_TRANSIENT_FOR property is set to None or Root window, the window +should be treated as a transient for all other windows in the same group. It +has been noted that this is a slight ICCCM violation, but as this behaviour is +pretty standard for many toolkits and window managers, and is extremely +unlikely to break anything, it seems reasonable to document it as standard. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="URGENCY" +>7.4. Urgency</A +></H2 +><P +> Dialog boxes should indicate their urgency level (information or warning) using the urgency bit in the WM_HINTS.flags property, as defined in the ICCCM. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="NORESIZE" +>7.5. Fixed size windows</A +></H2 +><P +> Windows can indicate that they are non-resizable by setting minheight = maxheight and minwidth = maxwidth in the ICCCM WM_NORMAL_HINTS property. The Window Manager MAY decorate such windows differently. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN368" +>7.6. Pagers and Taskbars</A +></H2 +><P +> This specification attempts to make reasonable provisions for WM independent pagers and taskbars. Window Managers that require / desire additional functionality beyond what can be achieved using the mechanisms set out in this specification may choose to implement their own pagers, which communicates with the Window Manager using further, WM-specific hints, or some other means. + </P +><P +> Pagers should decide whether to show a miniature version of a + window using the following guidelines: + <P +></P +><UL +><LI +><P +> If either _NET_WM_STATE_SKIP_PAGER or + _NET_WM_STATE_HIDDEN are set on a window, then the + pager should not show that window. + </P +></LI +><LI +><P +> The pager may choose not to display windows with + certain semantic types; this spec has no + recommendations, but common practice is to avoid + displaying _NET_WM_WINDOW_TYPE_DOCK for example. + </P +></LI +><LI +><P +> If the _NET_WM_STATE_SKIP_PAGER and + _NET_WM_STATE_HIDDEN hints are not present, and the + window manager claims to support _NET_WM_STATE_HIDDEN, + then the window should be shown if it's in either + NormalState or IconicState. + </P +></LI +><LI +><P +> For window managers that do not support + _NET_WM_STATE_HIDDEN, the pager should + not show windows in IconicState. These window + managers are probably using an older version + of this specification. + </P +></LI +></UL +> + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN381" +>7.7. Window Movement</A +></H2 +><P +>Window manager implementors should refer to the ICCCM for definitive +specifications of how to handle MapRequest and ConfigureRequest events. +However, since these aspects of the ICCCM are easily misread, this +document offers the following clarifications: + </P +><P +></P +><UL +><LI +><P +>Window managers MUST honour the win_gravity field of WM_NORMAL_HINTS + for both MapRequest _and_ ConfigureRequest events [1] + </P +></LI +><LI +><P +>Applications are free to change their win_gravity setting at any time + </P +><P +>If application changes its gravity then Window manager should adjust the +reference point, so that client window will not move as the result. +For example if client's gravity was NorthWestGravity and reference point +was +at the top-left corner of the frame window, then after change of gravity to +the SouthEast reference point should be adjusted to point to the +lower-right +corner of the frame. + </P +></LI +><LI +><P +>When generating synthetic ConfigureNotify events, the position given + MUST be the top-left corner of the client window in relation to the + origin of the root window (i.e., ignoring win_gravity) [2] + </P +></LI +><LI +><P +>XMoveWindow(w,x,y) behaviour depends on the window gravity. Upon receiving a +request from client application the Window Manager calculates a new reference +point, based on the client window's own size, border width and gravity. For given client +window dimentions (width, height) and border width (bw), the reference point will be +placed at: + </P +><DIV +CLASS="INFORMALTABLE" +><A +NAME="AEN394" +></A +><P +></P +><TABLE +BORDER="1" +CLASS="CALSTABLE" +><TBODY +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>Gravity:</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>ref_x:</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>ref_y:</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>StaticGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>NorthWestGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x-bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y-bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>NorthGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+(width/2)</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y-bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>NorthEastGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+width+bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y-bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>EastGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+width+bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+(height/2)</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>SouthEastGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+width+bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+height+bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>SouthGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+(width/2)</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+height+bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>SouthWestGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x-bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+height+bw</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>WestGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x-bw</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+(height/2)</TD +></TR +><TR +><TD +ALIGN="LEFT" +VALIGN="TOP" +>CenterGravity</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>x+(width/2)</TD +><TD +ALIGN="LEFT" +VALIGN="TOP" +>y+(height/2)</TD +></TR +></TBODY +></TABLE +><P +></P +></DIV +><P +>The Window manager will use the reference point as calculated above, +until next XMoveWindow request. The Window Manager will place frame decorations +in the following position, based on the window gravity : + </P +><P +>StaticGravity: + </P +><P +>window's left top corner will be placed at (ref_x,ref_y) + </P +><P +>NorthWestGravity: + </P +><P +>window frame's left top corner will be placed at (ref_x,ref_y) + </P +><P +>NorthGravity: + </P +><P +>window frame's top side's center will be placed at (ref_x,ref_y) + </P +><P +>NorthEastGravity: + </P +><P +>window frame's right top corner will be placed at (ref_x,ref_y) + </P +><P +>EastGravity: + </P +><P +>window frame's right side's center will be placed at (ref_x,ref_y) + </P +><P +>SouthWestGravity: + </P +><P +>window frame's left bottom corner will be placed at (ref_x,ref_y) + </P +><P +>SouthGravity: + </P +><P +>window frame's bottom side's center will be placed at (ref_x,ref_y) + </P +><P +>SouthEastGravity: + </P +><P +>window frame's right bottom corner will be placed at (ref_x,ref_y) + </P +><P +>WestGravity: + </P +><P +>window frame's left side's center will be placed at (ref_x,ref_y) + </P +><P +>CenterGravity: + </P +><P +>window frame's center will be placed at (ref_x,ref_y) + </P +></LI +><LI +><P +>Implementation Note for Application developers: + </P +><P +>When client window is resized - its reference point does not move. +So for example if window has SouthEastGravity and it is resized - +the bottom-right corner of its frame will not move but instead +top-left corner will be adjusted by the difference in size. + </P +></LI +><LI +><P +>Implementation Note for WM developers : + </P +><P +>when calculating reference point at the time of initial placement - +initial window's width should be taken into consideration, as if it +was the frame for this window. + </P +></LI +></UL +><P +>[1] ICCCM Version 2.0, §4.1.2.3 and §4.1.5 + </P +><P +>[2] ICCCM Version 2.0, §4.2.3 + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN470" +>7.8. Window-in-Window MDI</A +></H2 +><P +> The authors of this specification acknowledge that there is no standard + method to allow the Window Manager to manage windows that are part of a + Window-in-Window MDI application. Application authors are advised to + use some other form of MDI, or to propose a mechanism to be included in + a future revision of this specification. + </P +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="KILLINGWINDOWS" +>7.9. Killing Hung Processes</A +></H2 +><P +>If processes fail to respond to the _NET_WM_PING protocol _NET_WM_PID may be +used in combination with the ICCCM specified WM_CLIENT_MACHINE to +attempt to kill a process. + </P +><P +>WM_CLIENT_MACHINE must be set to the fully-qualified domain name of the client's +host. This would normally be retrieved using gethostname(2). When gethostname() +is not available on the client's platform implementors may use the value of the +nodename field of struct utsname as returned by uname(2). An example of how to +retrieve a value for WM_CLIENT_MACHINE: + </P +><P +> <PRE +CLASS="PROGRAMLISTING" +>int net_get_hostname (char *buf, size_t maxlen) +{ +#ifdef HAVE_GETHOSTNAME + if (buf == NULL) return 0; + + gethostname (buf, maxlen); + buf [maxlen - 1] = '\0'; + + return strlen(buf); +#else + struct utsname name; + size_t len; + + if (buf == NULL) return 0; + + uname (&name); + len = strlen (name.nodename); + + if (len >= maxlen) len = maxlen - 1; + strncpy (buf, name.nodename, len); + buf[len] = '\0'; + + return len; +#endif +}</PRE +> + </P +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x340.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x479.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Window Manager Protocols</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>References</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x479.html b/kwin/wm-spec/x479.html new file mode 100644 index 000000000..f1ed9a3ac --- /dev/null +++ b/kwin/wm-spec/x479.html @@ -0,0 +1,143 @@ +<HTML +><HEAD +><TITLE +>References</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Implementation notes" +HREF="x351.html"><LINK +REL="NEXT" +TITLE="Copyright" +HREF="x483.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x351.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x483.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN479" +>8. References</A +></H1 +><P +>[1] F. Yergeau,"UTF-8, a transformation format of ISO 10646", RFC 2279 + </P +><P +>[2] David Rosenthal / Stuart W. Marks "Inter-Client Communication Conventions + Manual (Version 2.0)", X Consortium Standard, X Version 11, Release 6.3 + </P +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x351.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x483.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Implementation notes</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Copyright</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x483.html b/kwin/wm-spec/x483.html new file mode 100644 index 000000000..017828269 --- /dev/null +++ b/kwin/wm-spec/x483.html @@ -0,0 +1,166 @@ +<HTML +><HEAD +><TITLE +>Copyright</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="References" +HREF="x479.html"><LINK +REL="NEXT" +TITLE="Contributors" +HREF="x489.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x479.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x489.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN483" +>9. Copyright</A +></H1 +><P +>Copyright (C) 2000, 2001, 2002 See Contributors List + </P +><P +> +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following +conditions: + </P +><P +> +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + </P +><P +>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + </P +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x479.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x489.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>References</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Contributors</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x489.html b/kwin/wm-spec/x489.html new file mode 100644 index 000000000..a0f38bcc4 --- /dev/null +++ b/kwin/wm-spec/x489.html @@ -0,0 +1,178 @@ +<HTML +><HEAD +><TITLE +>Contributors</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Copyright" +HREF="x483.html"><LINK +REL="NEXT" +TITLE="Change history" +HREF="x512.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x483.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +><A +HREF="x512.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN489" +>10. Contributors</A +></H1 +><P +>Sasha Vasko</P +><P +>Bradley T. Hughes</P +><P +>Dominik Vogt</P +><P +>Havoc Pennington</P +><P +>Jeff Raven</P +><P +>Jim Gettys</P +><P +>John Harper</P +><P +>Julian Adams</P +><P +>Matthias Ettrich</P +><P +>Micheal Rogers</P +><P +>Nathan Clemons</P +><P +>Tim Janik</P +><P +>Tomi Ollila</P +><P +>Sam Lantinga</P +><P +>The Rasterman</P +><P +>Paul Warren</P +><P +>Owen Taylor</P +><P +>Marko Macek</P +><P +>Greg Badros</P +><P +>Matthias Clasen</P +><P +>David Rosenthal</P +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x483.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +><A +HREF="x512.html" +ACCESSKEY="N" +>Next</A +></TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Copyright</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +>Change history</TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/wm-spec/x512.html b/kwin/wm-spec/x512.html new file mode 100644 index 000000000..73a6ca4fb --- /dev/null +++ b/kwin/wm-spec/x512.html @@ -0,0 +1,763 @@ +<HTML +><HEAD +><TITLE +>Change history</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.72 +"><LINK +REL="HOME" +HREF="index.html"><LINK +REL="PREVIOUS" +TITLE="Contributors" +HREF="x489.html"></HEAD +><BODY +CLASS="SECT1" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="NAVHEADER" +><TABLE +SUMMARY="Header navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TH +COLSPAN="3" +ALIGN="center" +></TH +></TR +><TR +><TD +WIDTH="10%" +ALIGN="left" +VALIGN="bottom" +><A +HREF="x489.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="80%" +ALIGN="center" +VALIGN="bottom" +></TD +><TD +WIDTH="10%" +ALIGN="right" +VALIGN="bottom" +> </TD +></TR +></TABLE +><HR +ALIGN="LEFT" +WIDTH="100%"></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN512" +>11. Change history</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN514" +>11.1. Changes since 1.1</A +></H2 +><P +></P +><UL +><LI +><P +> Changed WM_CLIENT_NAME(STRING) from suggested to required for _NET_WM_PID. + </P +></LI +><LI +><P +> Specification and sample code for the content of WM_CLIENT_NAME(STRING). + </P +></LI +><LI +><P +> Added _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_UTILITY. + </P +></LI +><LI +><P +> Added _NET_WM_STATE_FULLSCREEN. + </P +></LI +><LI +><P +> Added _NET_WM_ALLOWED_ACTIONS. + </P +></LI +><LI +><P +> Added _NET_WM_STATE_HIDDEN and clarified purpose of + _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR. Changed + section on virtual desktop implementation to suggest ICCCM compliance + regarding IconicState, using _NET_WM_STATE_HIDDEN to avoid confusion. + Added implementation note for pagers on when to display a window. + </P +></LI +><LI +><P +> Added button field and new directions for keyboard-initiated actions + to the _NET_WM_MOVERESIZE message. + </P +></LI +><LI +><P +> Added advice on removing _NET_WM_STATE and _NET_WM_DESKTOP when a window + is withdrawn. + </P +></LI +><LI +><P +> Added _NET_DESKTOP_LAYOUT to allow a Pager to specify inter-desktop geometry. + </P +></LI +><LI +><P +> Added _NET_SHOWING_DESKTOP. + </P +></LI +><LI +><P +> Added _NET_WM_STATE_FLOATING. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN539" +>11.2. Changes since 1.0</A +></H2 +><P +></P +><UL +><LI +><P +>Fix doctype, add author info, update data. + </P +></LI +><LI +><P +>Change specification description wording to be more inclusive, and to reflect the joint nature of the specification. + </P +></LI +><LI +><P +>Fix miscellaneous typographical, grammar and spelling errors. + </P +></LI +><LI +><P +>Clarified _NET_SUPPORTED to include ALL atoms, not just the property names. + </P +></LI +><LI +><P +>Various corrections to use of MUST and SHOULD. + </P +></LI +><LI +><P +>Fix problem in _NET_WM_ICON where 'bytes' should have been 'cardinals' + </P +></LI +><LI +><P +>Replaced ISO-8559-1 characters with entities. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN556" +>11.3. Changes since 1.0pre5</A +></H2 +><P +></P +><UL +><LI +><P +>Change history moved to end. + </P +></LI +><LI +><P +>UTF-8 Reference updated. + </P +></LI +><LI +><P +>Window Gravity information updated. + </P +></LI +><LI +><P +>Copyright Added. + </P +></LI +><LI +><P +>Minor typo corrections. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN569" +>11.4. Changes since 1.0pre4</A +></H2 +><P +></P +><UL +><LI +><P +>Clarified the interpretation of client-provided geometries on large desktops. + </P +></LI +><LI +><P +>Added more explanation for _NET_DESKTOP_NAMES. + </P +></LI +><LI +><P +>Added _NET_WM_ICON_NAME and _NET_WM_VISIBLE_ICON_NAME. + </P +></LI +><LI +><P +>Tried to improve the wording of _NET_WM_STRUT explanation. + </P +></LI +><LI +><P +>Changed _NET_WORKAREA to an array of viewport-relative geometries. + </P +></LI +><LI +><P +>Updated list of <SPAN +CLASS="QUOTE" +>"dependent"</SPAN +> properties for _NET_NUMBER_OF_DESKTOPS +to include _NET_WORKAREA and _NET_DESKTOP_VIEWPORT. + </P +></LI +><LI +><P +>Tidied formatting of all client messages. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN587" +>11.5. Changes since 1.0pre3</A +></H2 +><P +></P +><UL +><LI +><P +>Added information about common non-ICCCM features. + </P +></LI +><LI +><P +>Added explanation of sending messages to the root window. + </P +></LI +><LI +><P +>Removed XA_ prefix from type names. + </P +></LI +><LI +><P +>Clarified that <SPAN +CLASS="QUOTE" +>"mapping order"</SPAN +> refers to initial mapping +and specify the directions of both orders. + </P +></LI +><LI +><P +>Clarified that desktops have a common size specified by _NET_DESKTOP_GEOMETRY. + </P +></LI +><LI +><P +>Rewrote explanation of _NET_DESKTOP_VIEWPORT. + </P +></LI +><LI +><P +>Tidied formatting of _NET_CURRENT_DESKTOP. + </P +></LI +><LI +><P +>Replaced <SPAN +CLASS="QUOTE" +>"window handle"</SPAN +> by <SPAN +CLASS="QUOTE" +>"window ID"</SPAN +>. + </P +></LI +><LI +><P +>Tidied formatting of _NET_WORKAREA. + </P +></LI +><LI +><P +>Rewrote the motivation for _NET_VIRTUAL_ROOTS. + </P +></LI +><LI +><P +>Added advice on Pointer grabs to _NET_WM_MOVERESIZE. + </P +></LI +><LI +><P +>Fixed typos in _NET_WM_STATE. + </P +></LI +><LI +><P +>Added _NET_WM_STATE_SKIP_PAGER. + </P +></LI +><LI +><P +>Tidied formatting of _NET_WM_STRUT. + </P +></LI +><LI +><P +>Tidied formatting of _NET_WM_ICON_GEOMETRY. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN623" +>11.6. Changes since 1.0pre2</A +></H2 +><P +></P +><UL +><LI +><P +>_NET_SET_NUMBER_OF_DESKTOPS -> _NET_NUMBER_OF_DESKTOPS for consistency. + </P +></LI +><LI +><P +>_NET_WM_VISIBLE_NAME_STRING -> _NET_WM_VISIBLE_NAME for consistency. + </P +></LI +><LI +><P +>_NET_WM_STATE: added explanation of permitted extensions. Added explanation of +being set / not set. + </P +></LI +><LI +><P +>Spellchecked, corrected various typos. + </P +></LI +><LI +><P +>UTF8 -> UTF-8 for consistency. + </P +></LI +><LI +><P +>added references to the ICCCM an UTF-8 (incomplete). + </P +></LI +><LI +><P +>added data and event formats where missing. + </P +></LI +><LI +><P +>clarified _NET_SUPPORTING_WM_CHECK. + </P +></LI +><LI +><P +>fixed formatting of _NET_CLOSE_WINDOW message. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN644" +>11.7. Changes since 1.0pre1</A +></H2 +><P +></P +><UL +><LI +><P +>Removed implementation note concerning Gnome's (potential) file manager behaviour. + </P +></LI +><LI +><P +>The Window Movement section of the implementation notes has been revised. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN651" +>11.8. Changes since 1.9f</A +></H2 +><P +></P +><UL +><LI +><P +>Revised revision number for first accepted release 1.9XX -> 1.0preXX. + </P +></LI +><LI +><P +>Prerequisites for adoption of this specification added. + </P +></LI +><LI +><P +>Tidied formatting of _NET_CURRENT_DESKTOP for consistency. + </P +></LI +><LI +><P +>Tidied formatting of _NET_ACTIVE_WINDOW for consistency. Removed doubled text. + </P +></LI +><LI +><P +>Tidied formatting of _NET_WM_DESKTOP for consistency. + </P +></LI +><LI +><P +>Killing Hung Processes implementation note added. _NET_WM_PID and _NET_WM_PING now link to this. + </P +></LI +><LI +><P +>Clarified x_root and y_root meaning for _NET_WM_MOVERESIZE. + </P +></LI +><LI +><P +>Added contributor list. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN670" +>11.9. Changes since 1.9e</A +></H2 +><P +></P +><UL +><LI +><P +>Added _NET_WM_VISIBLE_NAME_STRING + </P +></LI +><LI +><P +>Removed ambiguity from _NET_NUMBER_OF_DESKTOPS and _NET_DESKTOP_NAMES in combination. + </P +></LI +><LI +><P +>Set _NET_WM_MOVERESIZE format to 32 for consistency. + </P +></LI +><LI +><P +>Removed _NET_PROPERTIES. + </P +></LI +><LI +><P +>Removed comment from _NET_WM_MOVERESIZE. + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN683" +>11.10. Changes since 1.9d</A +></H2 +><P +></P +><UL +><LI +><P +>Added _NET_VIRTUAL_ROOTS + </P +></LI +><LI +><P +>Added note about ICCCM compliant window moves. + </P +></LI +><LI +><P +>Added _NET_WM_HANDLED_ICONS + </P +></LI +><LI +><P +>Added _NET_SUPPORTING_WM_CHECK + </P +></LI +><LI +><P +>Removed degrees of activation + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN696" +>11.11. Changes since 1.9c</A +></H2 +><P +></P +><UL +><LI +><P +>Removed packaging of hints into 2 X properties. Jim Gettys points out that the +performance gains of fewer round trips can be better achieved using Xlib +routines. + </P +></LI +><LI +><P +>Clarified that _NET_DESKTOP_VIEWPORT is in pixels + </P +></LI +><LI +><P +>_NET_DESKTOP_VIEWPORT is now an array, one for each desktop, to allow for +different active viewports on different desktops + </P +></LI +><LI +><P +>_NET_WM_STRUT now only applies on desktops on which the client is visible + </P +></LI +><LI +><P +>Introduced RFC 2119 language, and attempted to clarify the roles of the Window +Manager, Pagers and Applications + </P +></LI +><LI +><P +>Added _NET_WM_NAME + </P +></LI +><LI +><P +>_NET_DESKTOP_NAMES now in UTF8 + </P +></LI +><LI +><P +>Desktops now start from 0 + </P +></LI +><LI +><P +>Added _NET_WM_PID + </P +></LI +><LI +><P +>Added _NET_WM_PING protocol + </P +></LI +><LI +><P +>Added _NET_WM_STATE_SKIP_TASKBAR + </P +></LI +></UL +></DIV +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN721" +>11.12. Changes since 1.9b</A +></H2 +><P +></P +><UL +><LI +><P +>Removed _NET_NUMBER_OF_DESKTOPS client message, as it overlaps unnecessarily with _NET_{INSERT/DELETE}_DESKTOP.</P +></LI +><LI +><P +>Replaced _NET_WM_LAYER and _NET_WM_HINTS with _NET_WM_WINDOW_TYPE functional hint.</P +></LI +><LI +><P +>Changed _NET_WM_STATE to a list of atoms, for extensibility.</P +></LI +><LI +><P +>Expanded description of _NET_WORKAREA and _NET_WM_STRUT.</P +></LI +><LI +><P +>Removed _NET_WM_SIZEMOVE_NOTIFY protocol. </P +></LI +><LI +><P +>Added degrees of activation to _NET_ACTIVE_WINDOW client message</P +></LI +><LI +><P +>Added _NET_WM_ICON</P +></LI +><LI +><P +>My comments are in [[ ]]. Comments from Marko's draft are in [[MM: ]]</P +></LI +></UL +></DIV +></DIV +><DIV +CLASS="NAVFOOTER" +><HR +ALIGN="LEFT" +WIDTH="100%"><TABLE +SUMMARY="Footer navigation table" +WIDTH="100%" +BORDER="0" +CELLPADDING="0" +CELLSPACING="0" +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +><A +HREF="x489.html" +ACCESSKEY="P" +>Prev</A +></TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +><A +HREF="index.html" +ACCESSKEY="H" +>Home</A +></TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +> </TD +></TR +><TR +><TD +WIDTH="33%" +ALIGN="left" +VALIGN="top" +>Contributors</TD +><TD +WIDTH="34%" +ALIGN="center" +VALIGN="top" +> </TD +><TD +WIDTH="33%" +ALIGN="right" +VALIGN="top" +> </TD +></TR +></TABLE +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp new file mode 100644 index 000000000..1335a888c --- /dev/null +++ b/kwin/workspace.cpp @@ -0,0 +1,2732 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#define QT_CLEAN_NAMESPACE + +#include "workspace.h" + +#include <kapplication.h> +#include <kstartupinfo.h> +#include <fixx11h.h> +#include <kconfig.h> +#include <kglobal.h> +#include <qpopupmenu.h> +#include <klocale.h> +#include <qregexp.h> +#include <qpainter.h> +#include <qbitmap.h> +#include <qclipboard.h> +#include <kmenubar.h> +#include <kprocess.h> +#include <kglobalaccel.h> +#include <dcopclient.h> +#include <kipc.h> + +#include "plugins.h" +#include "client.h" +#include "popupinfo.h" +#include "tabbox.h" +#include "atoms.h" +#include "placement.h" +#include "notifications.h" +#include "group.h" +#include "rules.h" + +#include <X11/extensions/shape.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +#include <X11/cursorfont.h> + +extern Time qt_x_time; + +namespace KWinInternal +{ + +extern int screen_number; + +Workspace *Workspace::_self = 0; + +KProcess* kompmgr = 0; +KSelectionOwner* kompmgr_selection; + +bool allowKompmgrRestart = TRUE; + +// Rikkus: This class is too complex. It needs splitting further. +// It's a nightmare to understand, especially with so few comments :( + +// Matthias: Feel free to ask me questions about it. Feel free to add +// comments. I disagree that further splittings makes it easier. 2500 +// lines are not too much. It's the task that is complex, not the +// code. +Workspace::Workspace( bool restore ) + : DCOPObject ("KWinInterface"), + QObject (0, "workspace"), + current_desktop (0), + number_of_desktops(0), + active_popup( NULL ), + active_popup_client( NULL ), + desktop_widget (0), + temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), + rules_updates_disabled( false ), + active_client (0), + last_active_client (0), + most_recently_raised (0), + movingClient(0), + pending_take_activity ( NULL ), + delayfocus_client (0), + showing_desktop( false ), + block_showing_desktop( 0 ), + was_user_interaction (false), + session_saving (false), + control_grab (false), + tab_grab (false), + mouse_emulation (false), + block_focus (0), + tab_box (0), + popupinfo (0), + popup (0), + advanced_popup (0), + desk_popup (0), + desk_popup_index (0), + keys (0), + client_keys ( NULL ), + client_keys_dialog ( NULL ), + client_keys_client ( NULL ), + disable_shortcuts_keys ( NULL ), + global_shortcuts_disabled( false ), + global_shortcuts_disabled_for_client( false ), + root (0), + workspaceInit (true), + startup(0), electric_have_borders(false), + electric_current_border(0), + electric_top_border(None), + electric_bottom_border(None), + electric_left_border(None), + electric_right_border(None), + layoutOrientation(Qt::Vertical), + layoutX(-1), + layoutY(2), + workarea(NULL), + screenarea(NULL), + managing_topmenus( false ), + topmenu_selection( NULL ), + topmenu_watcher( NULL ), + topmenu_height( 0 ), + topmenu_space( NULL ), + set_active_client_recursion( 0 ), + block_stacking_updates( 0 ), + forced_global_mouse_grab( false ) + { + _self = this; + mgr = new PluginMgr; + root = qt_xrootwin(); + default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() ); + installed_colormap = default_colormap; + session.setAutoDelete( TRUE ); + + connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )), + this, SLOT( gotTemporaryRulesMessage( const QString& ))); + connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules())); + + updateXTime(); // needed for proper initialization of user_time in Client ctor + + delayFocusTimer = 0; + + electric_time_first = qt_x_time; + electric_time_last = qt_x_time; + + if ( restore ) + loadSessionInfo(); + + loadWindowRules(); + + (void) QApplication::desktop(); // trigger creation of desktop widget + + desktop_widget = + new QWidget( + 0, + "desktop_widget", + Qt::WType_Desktop | Qt::WPaintUnclipped + ); + + kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later + // call this before XSelectInput() on the root window + startup = new KStartupInfo( + KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this ); + + // select windowmanager privileges + XSelectInput(qt_xdisplay(), root, + KeyPressMask | + PropertyChangeMask | + ColormapChangeMask | + SubstructureRedirectMask | + SubstructureNotifyMask | + FocusChangeMask // for NotifyDetailNone + ); + + Shape::init(); + + // compatibility + long data = 1; + + XChangeProperty( + qt_xdisplay(), + qt_xrootwin(), + atoms->kwin_running, + atoms->kwin_running, + 32, + PropModeAppend, + (unsigned char*) &data, + 1 + ); + + client_keys = new KGlobalAccel( this ); + initShortcuts(); + tab_box = new TabBox( this ); + popupinfo = new PopupInfo( ); + + init(); + +#if (QT_VERSION-0 >= 0x030200) // XRANDR support + connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized())); +#endif + + // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object + if (options->useTranslucency) + { + kompmgr = new KProcess; + connect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), SLOT(handleKompmgrOutput(KProcess*, char*, int))); + *kompmgr << "kompmgr"; + startKompmgr(); + } + } + + +void Workspace::init() + { + checkElectricBorders(); + +// not used yet +// topDock = 0L; +// maximizedWindowCounter = 0; + + supportWindow = new QWidget; + XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp + + XSetWindowAttributes attr; + attr.override_redirect = 1; + null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent, + InputOnly, CopyFromParent, CWOverrideRedirect, &attr ); + XMapWindow(qt_xdisplay(), null_focus_window); + + unsigned long protocols[ 5 ] = + { + NET::Supported | + NET::SupportingWMCheck | + NET::ClientList | + NET::ClientListStacking | + NET::DesktopGeometry | + NET::NumberOfDesktops | + NET::CurrentDesktop | + NET::ActiveWindow | + NET::WorkArea | + NET::CloseWindow | + NET::DesktopNames | + NET::KDESystemTrayWindows | + NET::WMName | + NET::WMVisibleName | + NET::WMDesktop | + NET::WMWindowType | + NET::WMState | + NET::WMStrut | + NET::WMIconGeometry | + NET::WMIcon | + NET::WMPid | + NET::WMMoveResize | + NET::WMKDESystemTrayWinFor | + NET::WMFrameExtents | + NET::WMPing + , + NET::NormalMask | + NET::DesktopMask | + NET::DockMask | + NET::ToolbarMask | + NET::MenuMask | + NET::DialogMask | + NET::OverrideMask | + NET::TopMenuMask | + NET::UtilityMask | + NET::SplashMask | + 0 + , + NET::Modal | +// NET::Sticky | // large desktops not supported (and probably never will be) + NET::MaxVert | + NET::MaxHoriz | + NET::Shaded | + NET::SkipTaskbar | + NET::KeepAbove | +// NET::StaysOnTop | the same like KeepAbove + NET::SkipPager | + NET::Hidden | + NET::FullScreen | + NET::KeepBelow | + NET::DemandsAttention | + 0 + , + NET::WM2UserTime | + NET::WM2StartupId | + NET::WM2AllowedActions | + NET::WM2RestackWindow | + NET::WM2MoveResizeWindow | + NET::WM2ExtendedStrut | + NET::WM2KDETemporaryRules | + NET::WM2ShowingDesktop | + NET::WM2DesktopLayout | + 0 + , + NET::ActionMove | + NET::ActionResize | + NET::ActionMinimize | + NET::ActionShade | +// NET::ActionStick | // Sticky state is not supported + NET::ActionMaxVert | + NET::ActionMaxHoriz | + NET::ActionFullScreen | + NET::ActionChangeDesktop | + NET::ActionClose | + 0 + , + }; + + rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin", + protocols, 5, qt_xscreen() ); + + loadDesktopSettings(); + updateDesktopLayout(); + // extra NETRootInfo instance in Client mode is needed to get the values of the properties + NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop ); + int initial_desktop; + if( !kapp->isSessionRestored()) + initial_desktop = client_info.currentDesktop(); + else + { + KConfigGroupSaver saver( kapp->sessionConfig(), "Session" ); + initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 ); + } + if( !setCurrentDesktop( initial_desktop )) + setCurrentDesktop( 1 ); + + // now we know how many desktops we'll, thus, we initialise the positioning object + initPositioning = new Placement(this); + + connect(&reconfigureTimer, SIGNAL(timeout()), this, + SLOT(slotReconfigure())); + connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows())); + + connect(kapp, SIGNAL(appearanceChanged()), this, + SLOT(slotReconfigure())); + connect(kapp, SIGNAL(settingsChanged(int)), this, + SLOT(slotSettingsChanged(int))); + connect(kapp, SIGNAL( kipcMessage( int, int )), this, SLOT( kipcMessage( int, int ))); + + active_client = NULL; + rootInfo->setActiveWindow( None ); + focusToNull(); + if( !kapp->isSessionRestored()) + ++block_focus; // because it will be set below + + char nm[ 100 ]; + sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); + Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False ); + topmenu_selection = new KSelectionOwner( topmenu_atom ); + topmenu_watcher = new KSelectionWatcher( topmenu_atom ); +// TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before + + { // begin updates blocker block + StackingUpdatesBlocker blocker( this ); + + if( options->topMenuEnabled() && topmenu_selection->claim( false )) + setupTopMenuHandling(); // this can call updateStackingOrder() + else + lostTopMenuSelection(); + + unsigned int i, nwins; + Window root_return, parent_return, *wins; + XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); + for (i = 0; i < nwins; i++) + { + XWindowAttributes attr; + XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); + if (attr.override_redirect ) + continue; + if( topmenu_space && topmenu_space->winId() == wins[ i ] ) + continue; + if (attr.map_state != IsUnmapped) + { + if ( addSystemTrayWin( wins[i] ) ) + continue; + Client* c = createClient( wins[i], true ); + if ( c != NULL && root != qt_xrootwin() ) + { // TODO what is this? + // TODO may use QWidget:.create + XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 ); + c->move(0,0); + } + } + } + if ( wins ) + XFree((void *) wins); + // propagate clients, will really happen at the end of the updates blocker block + updateStackingOrder( true ); + + updateClientArea(); + raiseElectricBorders(); + + // NETWM spec says we have to set it to (0,0) if we don't support it + NETPoint* viewports = new NETPoint[ number_of_desktops ]; + rootInfo->setDesktopViewport( number_of_desktops, *viewports ); + delete[] viewports; + QRect geom = QApplication::desktop()->geometry(); + NETSize desktop_geometry; + desktop_geometry.width = geom.width(); + desktop_geometry.height = geom.height(); + rootInfo->setDesktopGeometry( -1, desktop_geometry ); + setShowingDesktop( false ); + + } // end updates blocker block + + Client* new_active_client = NULL; + if( !kapp->isSessionRestored()) + { + --block_focus; + new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow())); + } + if( new_active_client == NULL + && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage() + { + if( new_active_client == NULL ) + new_active_client = topClientOnDesktop( currentDesktop()); + if( new_active_client == NULL && !desktops.isEmpty() ) + new_active_client = findDesktop( true, currentDesktop()); + } + if( new_active_client != NULL ) + activateClient( new_active_client ); + // SELI TODO this won't work with unreasonable focus policies, + // and maybe in rare cases also if the selected client doesn't + // want focus + workspaceInit = false; +// TODO ungrabXServer() + } + +Workspace::~Workspace() + { + if (kompmgr) + delete kompmgr; + blockStackingUpdates( true ); +// TODO grabXServer(); + // use stacking_order, so that kwin --replace keeps stacking order + for( ClientList::ConstIterator it = stacking_order.begin(); + it != stacking_order.end(); + ++it ) + { + // only release the window + (*it)->releaseWindow( true ); + // No removeClient() is called, it does more than just removing. + // However, remove from some lists to e.g. prevent performTransiencyCheck() + // from crashing. + clients.remove( *it ); + desktops.remove( *it ); + } + delete desktop_widget; + delete tab_box; + delete popupinfo; + delete popup; + if ( root == qt_xrootwin() ) + XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running); + + writeWindowRules(); + KGlobal::config()->sync(); + + delete rootInfo; + delete supportWindow; + delete mgr; + delete[] workarea; + delete[] screenarea; + delete startup; + delete initPositioning; + delete topmenu_watcher; + delete topmenu_selection; + delete topmenu_space; + delete client_keys_dialog; + while( !rules.isEmpty()) + { + delete rules.front(); + rules.pop_front(); + } + XDestroyWindow( qt_xdisplay(), null_focus_window ); +// TODO ungrabXServer(); + _self = 0; + } + +Client* Workspace::createClient( Window w, bool is_mapped ) + { + StackingUpdatesBlocker blocker( this ); + Client* c = new Client( this ); + if( !c->manage( w, is_mapped )) + { + Client::deleteClient( c, Allowed ); + return NULL; + } + addClient( c, Allowed ); + return c; + } + +void Workspace::addClient( Client* c, allowed_t ) + { + // waited with trans settings until window figured out if active or not ;) +// qWarning("%s", (const char*)(c->resourceClass())); + c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None); + // first check if the window has it's own opinion of it's translucency ;) + c->getWindowOpacity(); + if (c->isDock()) + { +// if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c; + if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future + { + c->setShadowSize(options->dockShadowSize); + c->setOpacity(options->translucentDocks, options->dockOpacity); + } + } +//------------------------------------------------ + Group* grp = findGroup( c->window()); + if( grp != NULL ) + grp->gotLeader( c ); + + if ( c->isDesktop() ) + { + desktops.append( c ); + if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) + requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active + } + else + { + updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there + clients.append( c ); + } + if( !unconstrained_stacking_order.contains( c )) + unconstrained_stacking_order.append( c ); + if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires + stacking_order.append( c ); // c to be in stacking_order + if( c->isTopMenu()) + addTopMenu( c ); + updateClientArea(); // this cannot be in manage(), because the client got added only now + updateClientLayer( c ); + if( c->isDesktop()) + { + raiseClient( c ); + // if there's no active client, make this desktop the active one + if( activeClient() == NULL && should_get_focus.count() == 0 ) + activateClient( findDesktop( true, currentDesktop())); + } + c->checkActiveModal(); + checkTransients( c->window()); // SELI does this really belong here? + updateStackingOrder( true ); // propagate new client + if( c->isUtility() || c->isMenu() || c->isToolbar()) + updateToolWindows( true ); + checkNonExistentClients(); + } + +/* + Destroys the client \a c + */ +void Workspace::removeClient( Client* c, allowed_t ) + { + if (c == active_popup_client) + closeActivePopup(); + + if( client_keys_client == c ) + setupWindowShortcutDone( false ); + if( !c->shortcut().isNull()) + c->setShortcut( QString::null ); // remove from client_keys + + if( c->isDialog()) + Notify::raise( Notify::TransDelete ); + if( c->isNormalWindow()) + Notify::raise( Notify::Delete ); + + Q_ASSERT( clients.contains( c ) || desktops.contains( c )); + clients.remove( c ); + desktops.remove( c ); + unconstrained_stacking_order.remove( c ); + stacking_order.remove( c ); + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + focus_chain[ i ].remove( c ); + global_focus_chain.remove( c ); + attention_chain.remove( c ); + showing_desktop_clients.remove( c ); + if( c->isTopMenu()) + removeTopMenu( c ); + Group* group = findGroup( c->window()); + if( group != NULL ) + group->lostLeader(); + + if ( c == most_recently_raised ) + most_recently_raised = 0; + should_get_focus.remove( c ); + Q_ASSERT( c != active_client ); + if ( c == last_active_client ) + last_active_client = 0; + if( c == pending_take_activity ) + pending_take_activity = NULL; + if( c == delayfocus_client ) + cancelDelayFocus(); + + updateStackingOrder( true ); + + if (tab_grab) + tab_box->repaint(); + + updateClientArea(); + } + +void Workspace::updateFocusChains( Client* c, FocusChainChange change ) + { + if( !c->wantsTabFocus()) // doesn't want tab focus, remove + { + for( int i=1; + i<= numberOfDesktops(); + ++i ) + focus_chain[i].remove(c); + global_focus_chain.remove( c ); + return; + } + if(c->desktop() == NET::OnAllDesktops) + { //now on all desktops, add it to focus_chains it is not already in + for( int i=1; i<= numberOfDesktops(); i++) + { // making first/last works only on current desktop, don't affect all desktops + if( i == currentDesktop() + && ( change == FocusChainMakeFirst || change == FocusChainMakeLast )) + { + focus_chain[ i ].remove( c ); + if( change == FocusChainMakeFirst ) + focus_chain[ i ].append( c ); + else + focus_chain[ i ].prepend( c ); + } + else if( !focus_chain[ i ].contains( c )) + { // add it after the active one + if( active_client != NULL && active_client != c + && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) + focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); + else + focus_chain[ i ].append( c ); // otherwise add as the first one + } + } + } + else //now only on desktop, remove it anywhere else + { + for( int i=1; i<= numberOfDesktops(); i++) + { + if( i == c->desktop()) + { + if( change == FocusChainMakeFirst ) + { + focus_chain[ i ].remove( c ); + focus_chain[ i ].append( c ); + } + else if( change == FocusChainMakeLast ) + { + focus_chain[ i ].remove( c ); + focus_chain[ i ].prepend( c ); + } + else if( !focus_chain[ i ].contains( c )) + { + if( active_client != NULL && active_client != c + && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) + focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); + else + focus_chain[ i ].append( c ); // otherwise add as the first one + } + } + else + focus_chain[ i ].remove( c ); + } + } + if( change == FocusChainMakeFirst ) + { + global_focus_chain.remove( c ); + global_focus_chain.append( c ); + } + else if( change == FocusChainMakeLast ) + { + global_focus_chain.remove( c ); + global_focus_chain.prepend( c ); + } + else if( !global_focus_chain.contains( c )) + { + if( active_client != NULL && active_client != c + && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client ) + global_focus_chain.insert( global_focus_chain.fromLast(), c ); + else + global_focus_chain.append( c ); // otherwise add as the first one + } + } + +void Workspace::updateCurrentTopMenu() + { + if( !managingTopMenus()) + return; + // toplevel menubar handling + Client* menubar = 0; + bool block_desktop_menubar = false; + if( active_client ) + { + // show the new menu bar first... + Client* menu_client = active_client; + for(;;) + { + if( menu_client->isFullScreen()) + block_desktop_menubar = true; + for( ClientList::ConstIterator it = menu_client->transients().begin(); + it != menu_client->transients().end(); + ++it ) + if( (*it)->isTopMenu()) + { + menubar = *it; + break; + } + if( menubar != NULL || !menu_client->isTransient()) + break; + if( menu_client->isModal() || menu_client->transientFor() == NULL ) + break; // don't use mainwindow's menu if this is modal or group transient + menu_client = menu_client->transientFor(); + } + if( !menubar ) + { // try to find any topmenu from the application (#72113) + for( ClientList::ConstIterator it = active_client->group()->members().begin(); + it != active_client->group()->members().end(); + ++it ) + if( (*it)->isTopMenu()) + { + menubar = *it; + break; + } + } + } + if( !menubar && !block_desktop_menubar && options->desktopTopMenu()) + { + // Find the menubar of the desktop + Client* desktop = findDesktop( true, currentDesktop()); + if( desktop != NULL ) + { + for( ClientList::ConstIterator it = desktop->transients().begin(); + it != desktop->transients().end(); + ++it ) + if( (*it)->isTopMenu()) + { + menubar = *it; + break; + } + } + // TODO to be cleaned app with window grouping + // Without qt-copy patch #0009, the topmenu and desktop are not in the same group, + // thus the topmenu is not transient for it :-/. + if( menubar == NULL ) + { + for( ClientList::ConstIterator it = topmenus.begin(); + it != topmenus.end(); + ++it ) + if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR + { // set pointing to the root window + menubar = *it; // to recognize it here + break; // Also, with the xroot hack in kdesktop, + } // there's no NET::Desktop window to be transient for + } + } + +// kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl; + if ( menubar ) + { + if( active_client && !menubar->isOnDesktop( active_client->desktop())) + menubar->setDesktop( active_client->desktop()); + menubar->hideClient( false ); + topmenu_space->hide(); + // make it appear like it's been raised manually - it's in the Dock layer anyway, + // and not raising it could mess up stacking order of topmenus within one application, + // and thus break raising of mainclients in raiseClient() + unconstrained_stacking_order.remove( menubar ); + unconstrained_stacking_order.append( menubar ); + } + else if( !block_desktop_menubar ) + { // no topmenu active - show the space window, so that there's not empty space + topmenu_space->show(); + } + + // ... then hide the other ones. Avoids flickers. + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + if( (*it)->isTopMenu() && (*it) != menubar ) + (*it)->hideClient( true ); + } + } + + +void Workspace::updateToolWindows( bool also_hide ) + { + // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) + if( !options->hideUtilityWindowsForInactive ) + { + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + (*it)->hideClient( false ); + return; + } + const Group* group = NULL; + const Client* client = active_client; +// Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow +// will be shown; if a group transient is group, all tools in the group will be shown + while( client != NULL ) + { + if( !client->isTransient()) + break; + if( client->groupTransient()) + { + group = client->group(); + break; + } + client = client->transientFor(); + } + // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, + // i.e. if it's not up to date + + // SELI but maybe it should - what if a new client has been added that's not in stacking order yet? + ClientList to_show, to_hide; + for( ClientList::ConstIterator it = stacking_order.begin(); + it != stacking_order.end(); + ++it ) + { + if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) + { + bool show = true; + if( !(*it)->isTransient()) + { + if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible + show = true; + else if( client != NULL && (*it)->group() == client->group()) + show = true; + else + show = false; + } + else + { + if( group != NULL && (*it)->group() == group ) + show = true; + else if( client != NULL && client->hasTransient( (*it), true )) + show = true; + else + show = false; + } + if( !show && also_hide ) + { + const ClientList mainclients = (*it)->mainClients(); + // don't hide utility windows which are standalone(?) or + // have e.g. kicker as mainwindow + if( mainclients.isEmpty()) + show = true; + for( ClientList::ConstIterator it2 = mainclients.begin(); + it2 != mainclients.end(); + ++it2 ) + { + if( (*it2)->isSpecialWindow()) + show = true; + } + if( !show ) + to_hide.append( *it ); + } + if( show ) + to_show.append( *it ); + } + } // first show new ones, then hide + for( ClientList::ConstIterator it = to_show.fromLast(); + it != to_show.end(); + --it ) // from topmost + // TODO since this is in stacking order, the order of taskbar entries changes :( + (*it)->hideClient( false ); + if( also_hide ) + { + for( ClientList::ConstIterator it = to_hide.begin(); + it != to_hide.end(); + ++it ) // from bottommost + (*it)->hideClient( true ); + updateToolWindowsTimer.stop(); + } + else // setActiveClient() is after called with NULL client, quickly followed + { // by setting a new client, which would result in flickering + updateToolWindowsTimer.start( 50, true ); + } + } + +void Workspace::slotUpdateToolWindows() + { + updateToolWindows( true ); + } + +/*! + Updates the current colormap according to the currently active client + */ +void Workspace::updateColormap() + { + Colormap cmap = default_colormap; + if ( activeClient() && activeClient()->colormap() != None ) + cmap = activeClient()->colormap(); + if ( cmap != installed_colormap ) + { + XInstallColormap(qt_xdisplay(), cmap ); + installed_colormap = cmap; + } + } + +void Workspace::reconfigure() + { + reconfigureTimer.start(200, true); + } + + +void Workspace::slotSettingsChanged(int category) + { + kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl; + if( category == (int) KApplication::SETTINGS_SHORTCUTS ) + readShortcuts(); + } + +/*! + Reread settings + */ +KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() ); + +void Workspace::slotReconfigure() + { + kdDebug(1212) << "Workspace::slotReconfigure()" << endl; + reconfigureTimer.stop(); + + KGlobal::config()->reparseConfiguration(); + unsigned long changed = options->updateSettings(); + tab_box->reconfigure(); + popupinfo->reconfigure(); + initPositioning->reinitCascading( 0 ); + readShortcuts(); + forEachClient( CheckIgnoreFocusStealingProcedure()); + updateToolWindows( true ); + + if( mgr->reset( changed )) + { // decorations need to be recreated +#if 0 // This actually seems to make things worse now + QWidget curtain; + curtain.setBackgroundMode( NoBackground ); + curtain.setGeometry( QApplication::desktop()->geometry() ); + curtain.show(); +#endif + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + { + (*it)->updateDecoration( true, true ); + } + mgr->destroyPreviousPlugin(); + } + else + { + forEachClient( CheckBorderSizesProcedure()); + } + + checkElectricBorders(); + + if( options->topMenuEnabled() && !managingTopMenus()) + { + if( topmenu_selection->claim( false )) + setupTopMenuHandling(); + else + lostTopMenuSelection(); + } + else if( !options->topMenuEnabled() && managingTopMenus()) + { + topmenu_selection->release(); + lostTopMenuSelection(); + } + topmenu_height = 0; // invalidate used menu height + if( managingTopMenus()) + { + updateTopMenuGeometry(); + updateCurrentTopMenu(); + } + + loadWindowRules(); + for( ClientList::Iterator it = clients.begin(); + it != clients.end(); + ++it ) + { + (*it)->setupWindowRules( true ); + (*it)->applyWindowRules(); + discardUsedWindowRules( *it, false ); + } + + if (options->resetKompmgr) // need restart + { + bool tmp = options->useTranslucency; + stopKompmgr(); + if (tmp) + QTimer::singleShot( 200, this, SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart + } + } + +void Workspace::loadDesktopSettings() + { + KConfig* c = KGlobal::config(); + QCString groupname; + if (screen_number == 0) + groupname = "Desktops"; + else + groupname.sprintf("Desktops-screen-%d", screen_number); + KConfigGroupSaver saver(c,groupname); + + int n = c->readNumEntry("Number", 4); + number_of_desktops = n; + delete workarea; + workarea = new QRect[ n + 1 ]; + delete screenarea; + screenarea = NULL; + rootInfo->setNumberOfDesktops( number_of_desktops ); + desktop_focus_chain.resize( n ); + // make it +1, so that it can be accessed as [1..numberofdesktops] + focus_chain.resize( n + 1 ); + for(int i = 1; i <= n; i++) + { + QString s = c->readEntry(QString("Name_%1").arg(i), + i18n("Desktop %1").arg(i)); + rootInfo->setDesktopName( i, s.utf8().data() ); + desktop_focus_chain[i-1] = i; + } + } + +void Workspace::saveDesktopSettings() + { + KConfig* c = KGlobal::config(); + QCString groupname; + if (screen_number == 0) + groupname = "Desktops"; + else + groupname.sprintf("Desktops-screen-%d", screen_number); + KConfigGroupSaver saver(c,groupname); + + c->writeEntry("Number", number_of_desktops ); + for(int i = 1; i <= number_of_desktops; i++) + { + QString s = desktopName( i ); + QString defaultvalue = i18n("Desktop %1").arg(i); + if ( s.isEmpty() ) + { + s = defaultvalue; + rootInfo->setDesktopName( i, s.utf8().data() ); + } + + if (s != defaultvalue) + { + c->writeEntry( QString("Name_%1").arg(i), s ); + } + else + { + QString currentvalue = c->readEntry(QString("Name_%1").arg(i)); + if (currentvalue != defaultvalue) + c->writeEntry( QString("Name_%1").arg(i), "" ); + } + } + } + +QStringList Workspace::configModules(bool controlCenter) + { + QStringList args; + args << "kde-kwindecoration.desktop"; + if (controlCenter) + args << "kde-kwinoptions.desktop"; + else if (kapp->authorizeControlModule("kde-kwinoptions.desktop")) + args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwintranslucency"; + return args; + } + +void Workspace::configureWM() + { + KApplication::kdeinitExec( "kcmshell", configModules(false) ); + } + +/*! + avoids managing a window with title \a title + */ +void Workspace::doNotManage( QString title ) + { + doNotManageList.append( title ); + } + +/*! + Hack for java applets + */ +bool Workspace::isNotManaged( const QString& title ) + { + for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) + { + QRegExp r( (*it) ); + if (r.search(title) != -1) + { + doNotManageList.remove( it ); + return TRUE; + } + } + return FALSE; + } + +/*! + Refreshes all the client windows + */ +void Workspace::refresh() + { + QWidget w; + w.setGeometry( QApplication::desktop()->geometry() ); + w.show(); + w.hide(); + QApplication::flushX(); + } + +/*! + During virt. desktop switching, desktop areas covered by windows that are + going to be hidden are first obscured by new windows with no background + ( i.e. transparent ) placed right below the windows. These invisible windows + are removed after the switch is complete. + Reduces desktop ( wallpaper ) repaints during desktop switching +*/ +class ObscuringWindows + { + public: + ~ObscuringWindows(); + void create( Client* c ); + private: + QValueList<Window> obscuring_windows; + static QValueList<Window>* cached; + static unsigned int max_cache_size; + }; + +QValueList<Window>* ObscuringWindows::cached = 0; +unsigned int ObscuringWindows::max_cache_size = 0; + +void ObscuringWindows::create( Client* c ) + { + if( cached == 0 ) + cached = new QValueList<Window>; + Window obs_win; + XWindowChanges chngs; + int mask = CWSibling | CWStackMode; + if( cached->count() > 0 ) + { + cached->remove( obs_win = cached->first()); + chngs.x = c->x(); + chngs.y = c->y(); + chngs.width = c->width(); + chngs.height = c->height(); + mask |= CWX | CWY | CWWidth | CWHeight; + } + else + { + XSetWindowAttributes a; + a.background_pixmap = None; + a.override_redirect = True; + obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(), + c->width(), c->height(), 0, CopyFromParent, InputOutput, + CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a ); + } + chngs.sibling = c->frameId(); + chngs.stack_mode = Below; + XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs ); + XMapWindow( qt_xdisplay(), obs_win ); + obscuring_windows.append( obs_win ); + } + +ObscuringWindows::~ObscuringWindows() + { + max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1; + for( QValueList<Window>::ConstIterator it = obscuring_windows.begin(); + it != obscuring_windows.end(); + ++it ) + { + XUnmapWindow( qt_xdisplay(), *it ); + if( cached->count() < max_cache_size ) + cached->prepend( *it ); + else + XDestroyWindow( qt_xdisplay(), *it ); + } + } + + +/*! + Sets the current desktop to \a new_desktop + + Shows/Hides windows according to the stacking order and finally + propages the new desktop to the world + */ +bool Workspace::setCurrentDesktop( int new_desktop ) + { + if (new_desktop < 1 || new_desktop > number_of_desktops ) + return false; + + closeActivePopup(); + ++block_focus; +// TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date + StackingUpdatesBlocker blocker( this ); + + int old_desktop = current_desktop; + if (new_desktop != current_desktop) + { + ++block_showing_desktop; + /* + optimized Desktop switching: unmapping done from back to front + mapping done from front to back => less exposure events + */ + Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); + + ObscuringWindows obs_wins; + + current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works) + + for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) + if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) + { + if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) + obs_wins.create( *it ); + (*it)->updateVisibility(); + } + + rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing + + if( movingClient && !movingClient->isOnDesktop( new_desktop )) + movingClient->setDesktop( new_desktop ); + + for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) + if ( (*it)->isOnDesktop( new_desktop ) ) + (*it)->updateVisibility(); + + --block_showing_desktop; + if( showingDesktop()) // do this only after desktop change to avoid flicker + resetShowingDesktop( false ); + } + + // restore the focus on this desktop + --block_focus; + Client* c = 0; + + if ( options->focusPolicyIsReasonable()) + { + // Search in focus chain + if ( movingClient != NULL && active_client == movingClient + && focus_chain[currentDesktop()].contains( active_client ) + && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) + { + c = active_client; // the requestFocus below will fail, as the client is already active + } + if ( !c ) + { + for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); + it != focus_chain[currentDesktop()].end(); + --it ) + { + if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) + { + c = *it; + break; + } + } + } + } + + //if "unreasonable focus policy" + // and active_client is on_all_desktops and under mouse (hence == old_active_client), + // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>) + else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) + c = active_client; + + if( c == NULL && !desktops.isEmpty()) + c = findDesktop( true, currentDesktop()); + + if( c != active_client ) + setActiveClient( NULL, Allowed ); + + if ( c ) + requestFocus( c ); + else + focusToNull(); + + updateCurrentTopMenu(); + + // Update focus chain: + // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3, + // Output: chain = { 3, 1, 2, 4 }. +// kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n") +// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() )); + for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- ) + desktop_focus_chain[i] = desktop_focus_chain[i-1]; + desktop_focus_chain[0] = currentDesktop(); + +// QString s = "desktop_focus_chain[] = { "; +// for( uint i = 0; i < desktop_focus_chain.size(); i++ ) +// s += QString::number(desktop_focus_chain[i]) + ", "; +// kdDebug(1212) << s << "}\n"; + + if( old_desktop != 0 ) // not for the very first time + popupinfo->showInfo( desktopName(currentDesktop()) ); + return true; + } + +// called only from DCOP +void Workspace::nextDesktop() + { + int desktop = currentDesktop() + 1; + setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); + } + +// called only from DCOP +void Workspace::previousDesktop() + { + int desktop = currentDesktop() - 1; + setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops()); + } + +int Workspace::desktopToRight( int desktop ) const + { + int x,y; + calcDesktopLayout(x,y); + int dt = desktop-1; + if (layoutOrientation == Qt::Vertical) + { + dt += y; + if ( dt >= numberOfDesktops() ) + { + if ( options->rollOverDesktops ) + dt -= numberOfDesktops(); + else + return desktop; + } + } + else + { + int d = (dt % x) + 1; + if ( d >= x ) + { + if ( options->rollOverDesktops ) + d -= x; + else + return desktop; + } + dt = dt - (dt % x) + d; + } + return dt+1; + } + +int Workspace::desktopToLeft( int desktop ) const + { + int x,y; + calcDesktopLayout(x,y); + int dt = desktop-1; + if (layoutOrientation == Qt::Vertical) + { + dt -= y; + if ( dt < 0 ) + { + if ( options->rollOverDesktops ) + dt += numberOfDesktops(); + else + return desktop; + } + } + else + { + int d = (dt % x) - 1; + if ( d < 0 ) + { + if ( options->rollOverDesktops ) + d += x; + else + return desktop; + } + dt = dt - (dt % x) + d; + } + return dt+1; + } + +int Workspace::desktopUp( int desktop ) const + { + int x,y; + calcDesktopLayout(x,y); + int dt = desktop-1; + if (layoutOrientation == Qt::Horizontal) + { + dt -= x; + if ( dt < 0 ) + { + if ( options->rollOverDesktops ) + dt += numberOfDesktops(); + else + return desktop; + } + } + else + { + int d = (dt % y) - 1; + if ( d < 0 ) + { + if ( options->rollOverDesktops ) + d += y; + else + return desktop; + } + dt = dt - (dt % y) + d; + } + return dt+1; + } + +int Workspace::desktopDown( int desktop ) const + { + int x,y; + calcDesktopLayout(x,y); + int dt = desktop-1; + if (layoutOrientation == Qt::Horizontal) + { + dt += x; + if ( dt >= numberOfDesktops() ) + { + if ( options->rollOverDesktops ) + dt -= numberOfDesktops(); + else + return desktop; + } + } + else + { + int d = (dt % y) + 1; + if ( d >= y ) + { + if ( options->rollOverDesktops ) + d -= y; + else + return desktop; + } + dt = dt - (dt % y) + d; + } + return dt+1; + } + + +/*! + Sets the number of virtual desktops to \a n + */ +void Workspace::setNumberOfDesktops( int n ) + { + if ( n == number_of_desktops ) + return; + int old_number_of_desktops = number_of_desktops; + number_of_desktops = n; + + if( currentDesktop() > numberOfDesktops()) + setCurrentDesktop( numberOfDesktops()); + + // if increasing the number, do the resizing now, + // otherwise after the moving of windows to still existing desktops + if( old_number_of_desktops < number_of_desktops ) + { + rootInfo->setNumberOfDesktops( number_of_desktops ); + NETPoint* viewports = new NETPoint[ number_of_desktops ]; + rootInfo->setDesktopViewport( number_of_desktops, *viewports ); + delete[] viewports; + updateClientArea( true ); + focus_chain.resize( number_of_desktops + 1 ); + } + + // if the number of desktops decreased, move all + // windows that would be hidden to the last visible desktop + if( old_number_of_desktops > number_of_desktops ) + { + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops()) + sendClientToDesktop( *it, numberOfDesktops(), true ); + } + } + if( old_number_of_desktops > number_of_desktops ) + { + rootInfo->setNumberOfDesktops( number_of_desktops ); + NETPoint* viewports = new NETPoint[ number_of_desktops ]; + rootInfo->setDesktopViewport( number_of_desktops, *viewports ); + delete[] viewports; + updateClientArea( true ); + focus_chain.resize( number_of_desktops + 1 ); + } + + saveDesktopSettings(); + + // Resize and reset the desktop focus chain. + desktop_focus_chain.resize( n ); + for( int i = 0; i < (int)desktop_focus_chain.size(); i++ ) + desktop_focus_chain[i] = i+1; + } + +/*! + Sends client \a c to desktop \a desk. + + Takes care of transients as well. + */ +void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) + { + bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); + c->setDesktop( desk ); + if ( c->desktop() != desk ) // no change or desktop forced + return; + desk = c->desktop(); // Client did range checking + + if ( c->isOnDesktop( currentDesktop() ) ) + { + if ( c->wantsTabFocus() && options->focusPolicyIsReasonable() + && !was_on_desktop // for stickyness changes + && !dont_activate ) + requestFocus( c ); + else + restackClientUnderActive( c ); + } + else + { + raiseClient( c ); + } + + ClientList transients_stacking_order = ensureStackingOrder( c->transients()); + for( ClientList::ConstIterator it = transients_stacking_order.begin(); + it != transients_stacking_order.end(); + ++it ) + sendClientToDesktop( *it, desk, dont_activate ); + updateClientArea(); + } + +void Workspace::setDesktopLayout( int, int, int ) + { // DCOP-only, unused + } + +void Workspace::updateDesktopLayout() + { + // rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to + layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal + ? Qt::Horizontal : Qt::Vertical ); + layoutX = rootInfo->desktopLayoutColumnsRows().width(); + layoutY = rootInfo->desktopLayoutColumnsRows().height(); + if( layoutX == 0 && layoutY == 0 ) // not given, set default layout + layoutY = 2; + } + +void Workspace::calcDesktopLayout(int &x, int &y) const + { + x = layoutX; // <= 0 means compute it from the other and total number of desktops + y = layoutY; + if((x <= 0) && (y > 0)) + x = (numberOfDesktops()+y-1) / y; + else if((y <=0) && (x > 0)) + y = (numberOfDesktops()+x-1) / x; + + if(x <=0) + x = 1; + if (y <= 0) + y = 1; + } + +/*! + Check whether \a w is a system tray window. If so, add it to the respective + datastructures and propagate it to the world. + */ +bool Workspace::addSystemTrayWin( WId w ) + { + if ( systemTrayWins.contains( w ) ) + return TRUE; + + NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor ); + WId trayWinFor = ni.kdeSystemTrayWinFor(); + if ( !trayWinFor ) + return FALSE; + systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) ); + XSelectInput( qt_xdisplay(), w, + StructureNotifyMask + ); + XAddToSaveSet( qt_xdisplay(), w ); + propagateSystemTrayWins(); + return TRUE; + } + +/*! + Check whether \a w is a system tray window. If so, remove it from + the respective datastructures and propagate this to the world. + */ +bool Workspace::removeSystemTrayWin( WId w, bool check ) + { + if ( !systemTrayWins.contains( w ) ) + return FALSE; + if( check ) + { + // When getting UnmapNotify, it's not clear if it's the systray + // reparenting the window into itself, or if it's the window + // going away. This is obviously a flaw in the design, and we were + // just lucky it worked for so long. Kicker's systray temporarily + // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while + // embedding it, allowing KWin to figure out. Kicker just mustn't + // crash before removing it again ... *shrug* . + int num_props; + Atom* props = XListProperties( qt_xdisplay(), w, &num_props ); + if( props != NULL ) + { + for( int i = 0; + i < num_props; + ++i ) + if( props[ i ] == atoms->kde_system_tray_embedding ) + { + XFree( props ); + return false; + } + XFree( props ); + } + } + systemTrayWins.remove( w ); + XRemoveFromSaveSet (qt_xdisplay (), w); + propagateSystemTrayWins(); + return TRUE; + } + + +/*! + Propagates the systemTrayWins to the world + */ +void Workspace::propagateSystemTrayWins() + { + Window *cl = new Window[ systemTrayWins.count()]; + + int i = 0; + for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) + { + cl[i++] = (*it).win; + } + + rootInfo->setKDESystemTrayWindows( cl, i ); + delete [] cl; + } + + +void Workspace::killWindowId( Window window_to_kill ) + { + if( window_to_kill == None ) + return; + Window window = window_to_kill; + Client* client = NULL; + for(;;) + { + client = findClient( FrameIdMatchPredicate( window )); + if( client != NULL ) // found the client + break; + Window parent, root; + Window* children; + unsigned int children_count; + XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count ); + if( children != NULL ) + XFree( children ); + if( window == root ) // we didn't find the client, probably an override-redirect window + break; + window = parent; // go up + } + if( client != NULL ) + client->killWindow(); + else + XKillClient( qt_xdisplay(), window_to_kill ); + } + + +void Workspace::sendPingToWindow( Window window, Time timestamp ) + { + rootInfo->sendPing( window, timestamp ); + } + +void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags ) + { + rootInfo->takeActivity( c->window(), timestamp, flags ); + pending_take_activity = c; + } + + +/*! + Takes a screenshot of the current window and puts it in the clipboard. +*/ +void Workspace::slotGrabWindow() + { + if ( active_client ) + { + QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() ); + + //No XShape - no work. + if( Shape::available()) + { + //As the first step, get the mask from XShape. + int count, order; + XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(), + ShapeBounding, &count, &order); + //The ShapeBounding region is the outermost shape of the window; + //ShapeBounding - ShapeClipping is defined to be the border. + //Since the border area is part of the window, we use bounding + // to limit our work region + if (rects) + { + //Create a QRegion from the rectangles describing the bounding mask. + QRegion contents; + for (int pos = 0; pos < count; pos++) + contents += QRegion(rects[pos].x, rects[pos].y, + rects[pos].width, rects[pos].height); + XFree(rects); + + //Create the bounding box. + QRegion bbox(0, 0, snapshot.width(), snapshot.height()); + + //Get the masked away area. + QRegion maskedAway = bbox - contents; + QMemArray<QRect> maskedAwayRects = maskedAway.rects(); + + //Construct a bitmap mask from the rectangles + QBitmap mask( snapshot.width(), snapshot.height()); + QPainter p(&mask); + p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1); + for (uint pos = 0; pos < maskedAwayRects.count(); pos++) + p.fillRect(maskedAwayRects[pos], Qt::color0); + p.end(); + snapshot.setMask(mask); + } + } + + QClipboard *cb = QApplication::clipboard(); + cb->setPixmap( snapshot ); + } + else + slotGrabDesktop(); + } + +/*! + Takes a screenshot of the whole desktop and puts it in the clipboard. +*/ +void Workspace::slotGrabDesktop() + { + QPixmap p = QPixmap::grabWindow( qt_xrootwin() ); + QClipboard *cb = QApplication::clipboard(); + cb->setPixmap( p ); + } + + +/*! + Invokes keyboard mouse emulation + */ +void Workspace::slotMouseEmulation() + { + + if ( mouse_emulation ) + { + XUngrabKeyboard(qt_xdisplay(), qt_x_time); + mouse_emulation = FALSE; + return; + } + + if ( XGrabKeyboard(qt_xdisplay(), + root, FALSE, + GrabModeAsync, GrabModeAsync, + qt_x_time) == GrabSuccess ) + { + mouse_emulation = TRUE; + mouse_emulation_state = 0; + mouse_emulation_window = 0; + } + } + +/*! + Returns the child window under the mouse and activates the + respective client if necessary. + + Auxiliary function for the mouse emulation system. + */ +WId Workspace::getMouseEmulationWindow() + { + Window root; + Window child = qt_xrootwin(); + int root_x, root_y, lx, ly; + uint state; + Window w; + Client * c = 0; + do + { + w = child; + if (!c) + c = findClient( FrameIdMatchPredicate( w )); + XQueryPointer( qt_xdisplay(), w, &root, &child, + &root_x, &root_y, &lx, &ly, &state ); + } while ( child != None && child != w ); + + if ( c && !c->isActive() ) + activateClient( c ); + return (WId) w; + } + +/*! + Sends a faked mouse event to the specified window. Returns the new button state. + */ +unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state ) + { + if ( !w ) + return state; + QWidget* widget = QWidget::find( w ); + if ( (!widget || widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) + { + int x, y; + Window xw; + XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw ); + if ( type == EmuMove ) + { // motion notify events + XEvent e; + e.type = MotionNotify; + e.xmotion.window = w; + e.xmotion.root = qt_xrootwin(); + e.xmotion.subwindow = w; + e.xmotion.time = qt_x_time; + e.xmotion.x = x; + e.xmotion.y = y; + e.xmotion.x_root = pos.x(); + e.xmotion.y_root = pos.y(); + e.xmotion.state = state; + e.xmotion.is_hint = NotifyNormal; + XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, &e ); + } + else + { + XEvent e; + e.type = type == EmuRelease ? ButtonRelease : ButtonPress; + e.xbutton.window = w; + e.xbutton.root = qt_xrootwin(); + e.xbutton.subwindow = w; + e.xbutton.time = qt_x_time; + e.xbutton.x = x; + e.xbutton.y = y; + e.xbutton.x_root = pos.x(); + e.xbutton.y_root = pos.y(); + e.xbutton.state = state; + e.xbutton.button = button; + XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, &e ); + + if ( type == EmuPress ) + { + switch ( button ) + { + case 2: + state |= Button2Mask; + break; + case 3: + state |= Button3Mask; + break; + default: // 1 + state |= Button1Mask; + break; + } + } + else + { + switch ( button ) + { + case 2: + state &= ~Button2Mask; + break; + case 3: + state &= ~Button3Mask; + break; + default: // 1 + state &= ~Button1Mask; + break; + } + } + } + } + return state; + } + +/*! + Handles keypress event during mouse emulation + */ +bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) + { + if ( root != qt_xrootwin() ) + return FALSE; + int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0); + int km = ev.state & (ControlMask | Mod1Mask | ShiftMask); + + bool is_control = km & ControlMask; + bool is_alt = km & Mod1Mask; + bool is_shift = km & ShiftMask; + int delta = is_control?1:is_alt?32:8; + QPoint pos = QCursor::pos(); + + switch ( kc ) + { + case XK_Left: + case XK_KP_Left: + pos.rx() -= delta; + break; + case XK_Right: + case XK_KP_Right: + pos.rx() += delta; + break; + case XK_Up: + case XK_KP_Up: + pos.ry() -= delta; + break; + case XK_Down: + case XK_KP_Down: + pos.ry() += delta; + break; + case XK_F1: + if ( !mouse_emulation_state ) + mouse_emulation_window = getMouseEmulationWindow(); + if ( (mouse_emulation_state & Button1Mask) == 0 ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); + if ( !is_shift ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); + break; + case XK_F2: + if ( !mouse_emulation_state ) + mouse_emulation_window = getMouseEmulationWindow(); + if ( (mouse_emulation_state & Button2Mask) == 0 ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state ); + if ( !is_shift ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); + break; + case XK_F3: + if ( !mouse_emulation_state ) + mouse_emulation_window = getMouseEmulationWindow(); + if ( (mouse_emulation_state & Button3Mask) == 0 ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state ); + if ( !is_shift ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); + break; + case XK_Return: + case XK_space: + case XK_KP_Enter: + case XK_KP_Space: + { + if ( !mouse_emulation_state ) + { + // nothing was pressed, fake a LMB click + mouse_emulation_window = getMouseEmulationWindow(); + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); + } + else + { // release all + if ( mouse_emulation_state & Button1Mask ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); + if ( mouse_emulation_state & Button2Mask ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); + if ( mouse_emulation_state & Button3Mask ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); + } + } + // fall through + case XK_Escape: + XUngrabKeyboard(qt_xdisplay(), qt_x_time); + mouse_emulation = FALSE; + return TRUE; + default: + return FALSE; + } + + QCursor::setPos( pos ); + if ( mouse_emulation_state ) + mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state ); + return TRUE; + + } + +/*! + Returns the workspace's desktop widget. The desktop widget is + sometimes required by clients to draw on it, for example outlines on + moving or resizing. + */ +QWidget* Workspace::desktopWidget() + { + return desktop_widget; + } + +//Delayed focus functions +void Workspace::delayFocus() + { + requestFocus( delayfocus_client ); + cancelDelayFocus(); + } + +void Workspace::requestDelayFocus( Client* c ) + { + delayfocus_client = c; + delete delayFocusTimer; + delayFocusTimer = new QTimer( this ); + connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) ); + delayFocusTimer->start( options->delayFocusInterval, TRUE ); + } + +void Workspace::cancelDelayFocus() + { + delete delayFocusTimer; + delayFocusTimer = 0; + } + +// Electric Borders +//========================================================================// +// Electric Border Window management. Electric borders allow a user +// to change the virtual desktop by moving the mouse pointer to the +// borders. Technically this is done with input only windows. Since +// electric borders can be switched on and off, we have these two +// functions to create and destroy them. +void Workspace::checkElectricBorders( bool force ) + { + if( force ) + destroyBorderWindows(); + + electric_current_border = 0; + + QRect r = QApplication::desktop()->geometry(); + electricTop = r.top(); + electricBottom = r.bottom(); + electricLeft = r.left(); + electricRight = r.right(); + + if (options->electricBorders() == Options::ElectricAlways) + createBorderWindows(); + else + destroyBorderWindows(); + } + +void Workspace::createBorderWindows() + { + if ( electric_have_borders ) + return; + + electric_have_borders = true; + + QRect r = QApplication::desktop()->geometry(); + XSetWindowAttributes attributes; + unsigned long valuemask; + attributes.override_redirect = True; + attributes.event_mask = ( EnterWindowMask | LeaveWindowMask ); + valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); + attributes.cursor = XCreateFontCursor(qt_xdisplay(), + XC_sb_up_arrow); + electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), + 0,0, + r.width(),1, + 0, + CopyFromParent, InputOnly, + CopyFromParent, + valuemask, &attributes); + XMapWindow(qt_xdisplay(), electric_top_border); + + attributes.cursor = XCreateFontCursor(qt_xdisplay(), + XC_sb_down_arrow); + electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), + 0,r.height()-1, + r.width(),1, + 0, + CopyFromParent, InputOnly, + CopyFromParent, + valuemask, &attributes); + XMapWindow(qt_xdisplay(), electric_bottom_border); + + attributes.cursor = XCreateFontCursor(qt_xdisplay(), + XC_sb_left_arrow); + electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), + 0,0, + 1,r.height(), + 0, + CopyFromParent, InputOnly, + CopyFromParent, + valuemask, &attributes); + XMapWindow(qt_xdisplay(), electric_left_border); + + attributes.cursor = XCreateFontCursor(qt_xdisplay(), + XC_sb_right_arrow); + electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), + r.width()-1,0, + 1,r.height(), + 0, + CopyFromParent, InputOnly, + CopyFromParent, + valuemask, &attributes); + XMapWindow(qt_xdisplay(), electric_right_border); + // Set XdndAware on the windows, so that DND enter events are received (#86998) + Atom version = 4; // XDND version + XChangeProperty( qt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM, + 32, PropModeReplace, ( unsigned char* )&version, 1 ); + XChangeProperty( qt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM, + 32, PropModeReplace, ( unsigned char* )&version, 1 ); + XChangeProperty( qt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM, + 32, PropModeReplace, ( unsigned char* )&version, 1 ); + XChangeProperty( qt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM, + 32, PropModeReplace, ( unsigned char* )&version, 1 ); + } + + +// Electric Border Window management. Electric borders allow a user +// to change the virtual desktop by moving the mouse pointer to the +// borders. Technically this is done with input only windows. Since +// electric borders can be switched on and off, we have these two +// functions to create and destroy them. +void Workspace::destroyBorderWindows() + { + if( !electric_have_borders) + return; + + electric_have_borders = false; + + if(electric_top_border) + XDestroyWindow(qt_xdisplay(),electric_top_border); + if(electric_bottom_border) + XDestroyWindow(qt_xdisplay(),electric_bottom_border); + if(electric_left_border) + XDestroyWindow(qt_xdisplay(),electric_left_border); + if(electric_right_border) + XDestroyWindow(qt_xdisplay(),electric_right_border); + + electric_top_border = None; + electric_bottom_border = None; + electric_left_border = None; + electric_right_border = None; + } + +void Workspace::clientMoved(const QPoint &pos, Time now) + { + if (options->electricBorders() == Options::ElectricDisabled) + return; + + if ((pos.x() != electricLeft) && + (pos.x() != electricRight) && + (pos.y() != electricTop) && + (pos.y() != electricBottom)) + return; + + Time treshold_set = options->electricBorderDelay(); // set timeout + Time treshold_reset = 250; // reset timeout + int distance_reset = 30; // Mouse should not move more than this many pixels + + int border = 0; + if (pos.x() == electricLeft) + border = 1; + else if (pos.x() == electricRight) + border = 2; + else if (pos.y() == electricTop) + border = 3; + else if (pos.y() == electricBottom) + border = 4; + + if ((electric_current_border == border) && + (timestampDiff(electric_time_last, now) < treshold_reset) && + ((pos-electric_push_point).manhattanLength() < distance_reset)) + { + electric_time_last = now; + + if (timestampDiff(electric_time_first, now) > treshold_set) + { + electric_current_border = 0; + + QRect r = QApplication::desktop()->geometry(); + int offset; + + int desk_before = currentDesktop(); + switch(border) + { + case 1: + slotSwitchDesktopLeft(); + if (currentDesktop() != desk_before) + { + offset = r.width() / 5; + QCursor::setPos(r.width() - offset, pos.y()); + } + break; + + case 2: + slotSwitchDesktopRight(); + if (currentDesktop() != desk_before) + { + offset = r.width() / 5; + QCursor::setPos(offset, pos.y()); + } + break; + + case 3: + slotSwitchDesktopUp(); + if (currentDesktop() != desk_before) + { + offset = r.height() / 5; + QCursor::setPos(pos.x(), r.height() - offset); + } + break; + + case 4: + slotSwitchDesktopDown(); + if (currentDesktop() != desk_before) + { + offset = r.height() / 5; + QCursor::setPos(pos.x(), offset); + } + break; + } + return; + } + } + else + { + electric_current_border = border; + electric_time_first = now; + electric_time_last = now; + electric_push_point = pos; + } + + int mouse_warp = 1; + + // reset the pointer to find out wether the user is really pushing + switch( border) + { + case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break; + case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break; + case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break; + case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break; + } + } + +// this function is called when the user entered an electric border +// with the mouse. It may switch to another virtual desktop +bool Workspace::electricBorder(XEvent *e) + { + if( !electric_have_borders ) + return false; + if( e->type == EnterNotify ) + { + if( e->xcrossing.window == electric_top_border || + e->xcrossing.window == electric_left_border || + e->xcrossing.window == electric_bottom_border || + e->xcrossing.window == electric_right_border) + // the user entered an electric border + { + clientMoved( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time ); + return true; + } + } + if( e->type == ClientMessage ) + { + if( e->xclient.message_type == atoms->xdnd_position + && ( e->xclient.window == electric_top_border + || e->xclient.window == electric_bottom_border + || e->xclient.window == electric_left_border + || e->xclient.window == electric_right_border )) + { + updateXTime(); + clientMoved( QPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), qt_x_time ); + return true; + } + } + return false; + } + +// electric borders (input only windows) have to be always on the +// top. For that reason kwm calls this function always after some +// windows have been raised. +void Workspace::raiseElectricBorders() + { + + if(electric_have_borders) + { + XRaiseWindow(qt_xdisplay(), electric_top_border); + XRaiseWindow(qt_xdisplay(), electric_left_border); + XRaiseWindow(qt_xdisplay(), electric_bottom_border); + XRaiseWindow(qt_xdisplay(), electric_right_border); + } + } + +void Workspace::addTopMenu( Client* c ) + { + assert( c->isTopMenu()); + assert( !topmenus.contains( c )); + topmenus.append( c ); + if( managingTopMenus()) + { + int minsize = c->minSize().height(); + if( minsize > topMenuHeight()) + { + topmenu_height = minsize; + updateTopMenuGeometry(); + } + updateTopMenuGeometry( c ); + updateCurrentTopMenu(); + } +// kdDebug() << "NEW TOPMENU:" << c << endl; + } + +void Workspace::removeTopMenu( Client* c ) + { +// if( c->isTopMenu()) +// kdDebug() << "REMOVE TOPMENU:" << c << endl; + assert( c->isTopMenu()); + assert( topmenus.contains( c )); + topmenus.remove( c ); + updateCurrentTopMenu(); + // TODO reduce topMenuHeight() if possible? + } + +void Workspace::lostTopMenuSelection() + { +// kdDebug() << "lost TopMenu selection" << endl; + // make sure this signal is always set when not owning the selection + disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); + connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); + if( !managing_topmenus ) + return; + connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); + disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); + managing_topmenus = false; + delete topmenu_space; + topmenu_space = NULL; + updateClientArea(); + for( ClientList::ConstIterator it = topmenus.begin(); + it != topmenus.end(); + ++it ) + (*it)->checkWorkspacePosition(); + } + +void Workspace::lostTopMenuOwner() + { + if( !options->topMenuEnabled()) + return; +// kdDebug() << "TopMenu selection lost owner" << endl; + if( !topmenu_selection->claim( false )) + { +// kdDebug() << "Failed to claim TopMenu selection" << endl; + return; + } +// kdDebug() << "claimed TopMenu selection" << endl; + setupTopMenuHandling(); + } + +void Workspace::setupTopMenuHandling() + { + if( managing_topmenus ) + return; + connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); + disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); + managing_topmenus = true; + topmenu_space = new QWidget; + Window stack[ 2 ]; + stack[ 0 ] = supportWindow->winId(); + stack[ 1 ] = topmenu_space->winId(); + XRestackWindows(qt_xdisplay(), stack, 2); + updateTopMenuGeometry(); + topmenu_space->show(); + updateClientArea(); + updateCurrentTopMenu(); + } + +int Workspace::topMenuHeight() const + { + if( topmenu_height == 0 ) + { // simply create a dummy menubar and use its preffered height as the menu height + KMenuBar tmpmenu; + tmpmenu.insertItem( "dummy" ); + topmenu_height = tmpmenu.sizeHint().height(); + } + return topmenu_height; + } + +KDecoration* Workspace::createDecoration( KDecorationBridge* bridge ) + { + return mgr->createDecoration( bridge ); + } + +QString Workspace::desktopName( int desk ) const + { + return QString::fromUtf8( rootInfo->desktopName( desk ) ); + } + +bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ) + { + return startup->checkStartup( w, id, data ) == KStartupInfo::Match; + } + +/*! + Puts the focus on a dummy window + Just using XSetInputFocus() with None would block keyboard input + */ +void Workspace::focusToNull() + { + XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time ); + } + +void Workspace::helperDialog( const QString& message, const Client* c ) + { + QStringList args; + QString type; + if( message == "noborderaltf3" ) + { + QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) + .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); + args << "--msgbox" << + i18n( "You have selected to show a window without its border.\n" + "Without the border, you will not be able to enable the border " + "again using the mouse: use the window operations menu instead, " + "activated using the %1 keyboard shortcut." ) + .arg( shortcut ); + type = "altf3warning"; + } + else if( message == "fullscreenaltf3" ) + { + QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) + .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); + args << "--msgbox" << + i18n( "You have selected to show a window in fullscreen mode.\n" + "If the application itself does not have an option to turn the fullscreen " + "mode off you will not be able to disable it " + "again using the mouse: use the window operations menu instead, " + "activated using the %1 keyboard shortcut." ) + .arg( shortcut ); + type = "altf3warning"; + } + else + assert( false ); + KProcess proc; + proc << "kdialog" << args; + if( !type.isEmpty()) + { + KConfig cfg( "kwin_dialogsrc" ); + cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox + if( !cfg.readBoolEntry( type, true )) // has don't show again checked + return; // save launching kdialog + proc << "--dontagain" << "kwin_dialogsrc:" + type; + } + if( c != NULL ) + proc << "--embed" << QString::number( c->window()); + proc.start( KProcess::DontCare ); + } + + +// kompmgr stuff + +void Workspace::startKompmgr() +{ + if (!kompmgr || kompmgr->isRunning()) + return; + if (!kompmgr->start(KProcess::OwnGroup, KProcess::Stderr)) + { + options->useTranslucency = FALSE; + KProcess proc; + proc << "kdialog" << "--error" + << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") + << "--title" << "Composite Manager Failure"; + proc.start(KProcess::DontCare); + } + else + { + delete kompmgr_selection; + char selection_name[ 100 ]; + sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( qt_xdisplay())); + kompmgr_selection = new KSelectionOwner( selection_name ); + connect( kompmgr_selection, SIGNAL( lostOwnership()), SLOT( stopKompmgr())); + kompmgr_selection->claim( true ); + connect(kompmgr, SIGNAL(processExited(KProcess*)), SLOT(restartKompmgr())); + options->useTranslucency = TRUE; + allowKompmgrRestart = FALSE; + QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) ); + QByteArray ba; + QDataStream arg(ba, IO_WriteOnly); + arg << ""; + kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba); + } + if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider +} + +void Workspace::stopKompmgr() +{ + if (!kompmgr || !kompmgr->isRunning()) + return; + delete kompmgr_selection; + kompmgr_selection = NULL; + kompmgr->disconnect(this, SLOT(restartKompmgr())); + options->useTranslucency = FALSE; + if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider + kompmgr->kill(); + QByteArray ba; + QDataStream arg(ba, IO_WriteOnly); + arg << ""; + kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba); +} + +bool Workspace::kompmgrIsRunning() +{ + return kompmgr && kompmgr->isRunning(); +} + +void Workspace::unblockKompmgrRestart() +{ + allowKompmgrRestart = TRUE; +} + +void Workspace::restartKompmgr() +// this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); QTimer::singleShot(200, workspace, SLOT(startKompmgr())); +{ + if (!allowKompmgrRestart) // uh-ohh + { + delete kompmgr_selection; + kompmgr_selection = NULL; + options->useTranslucency = FALSE; + KProcess proc; + proc << "kdialog" << "--error" + << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.") + << "--title" << i18n("Composite Manager Failure"); + proc.start(KProcess::DontCare); + return; + } + if (!kompmgr) + return; +// this should be useless, i keep it for maybe future need +// if (!kcompmgr) +// { +// kompmgr = new KProcess; +// kompmgr->clearArguments(); +// *kompmgr << "kompmgr"; +// } +// ------------------- + if (!kompmgr->start(KProcess::NotifyOnExit, KProcess::Stderr)) + { + delete kompmgr_selection; + kompmgr_selection = NULL; + options->useTranslucency = FALSE; + KProcess proc; + proc << "kdialog" << "--error" + << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") + << "--title" << i18n("Composite Manager Failure"); + proc.start(KProcess::DontCare); + } + else + { + allowKompmgrRestart = FALSE; + QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) ); + } +} + +void Workspace::handleKompmgrOutput( KProcess* , char *buffer, int buflen) +{ + QString message; + QString output = QString::fromLocal8Bit( buffer, buflen ); + if (output.contains("Started",false)) + ; // don't do anything, just pass to the connection release + else if (output.contains("Can't open display",false)) + message = i18n("<qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt>"); + else if (output.contains("No render extension",false)) + message = i18n("<qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg ≥ 6.8 from www.freedesktop.org.<br></qt>"); + else if (output.contains("No composite extension",false)) + message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>" + "<i>Section \"Extensions\"<br>" + "Option \"Composite\" \"Enable\"<br>" + "EndSection</i></qt>"); + else if (output.contains("No damage extension",false)) + message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); + else if (output.contains("No XFixes extension",false)) + message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); + else return; //skip others + // kompmgr startup failed or succeeded, release connection + kompmgr->closeStderr(); + disconnect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), this, SLOT(handleKompmgrOutput(KProcess*, char*, int))); + if( !message.isEmpty()) + { + KProcess proc; + proc << "kdialog" << "--error" + << message + << "--title" << i18n("Composite Manager Failure"); + proc.start(KProcess::DontCare); + } +} + + +void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent) +{ + if (opacityPercent > 100) opacityPercent = 100; + for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) + if (winId == (*it)->window()) + { + (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF)); + return; + } +} + +void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent) +{ + //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400% + if (shadowSizePercent > 400) shadowSizePercent = 400; + for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) + if (winId == (*it)->window()) + { + (*it)->setShadowSize(shadowSizePercent); + return; + } +} + +void Workspace::setUnshadowed(unsigned long winId) +{ + for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) + if (winId == (*it)->window()) + { + (*it)->setShadowSize(0); + return; + } +} + +void Workspace::setShowingDesktop( bool showing ) + { + rootInfo->setShowingDesktop( showing ); + showing_desktop = showing; + ++block_showing_desktop; + if( showing_desktop ) + { + showing_desktop_clients.clear(); + ++block_focus; + ClientList cls = stackingOrder(); + // find them first, then minimize, otherwise transients may get minimized with the window + // they're transient for + for( ClientList::ConstIterator it = cls.begin(); + it != cls.end(); + ++it ) + { + if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow()) + showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker + } + for( ClientList::ConstIterator it = showing_desktop_clients.begin(); + it != showing_desktop_clients.end(); + ++it ) + (*it)->minimize(true); + --block_focus; + if( Client* desk = findDesktop( true, currentDesktop())) + requestFocus( desk ); + } + else + { + for( ClientList::ConstIterator it = showing_desktop_clients.begin(); + it != showing_desktop_clients.end(); + ++it ) + (*it)->unminimize(true); + if( showing_desktop_clients.count() > 0 ) + requestFocus( showing_desktop_clients.first()); + showing_desktop_clients.clear(); + } + --block_showing_desktop; + } + +// Following Kicker's behavior: +// Changing a virtual desktop resets the state and shows the windows again. +// Unminimizing a window resets the state but keeps the windows hidden (except +// the one that was unminimized). +// A new window resets the state and shows the windows again, with the new window +// being active. Due to popular demand (#67406) by people who apparently +// don't see a difference between "show desktop" and "minimize all", this is not +// true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing +// a new window resets the state but doesn't show windows. +void Workspace::resetShowingDesktop( bool keep_hidden ) + { + if( block_showing_desktop > 0 ) + return; + rootInfo->setShowingDesktop( false ); + showing_desktop = false; + ++block_showing_desktop; + if( !keep_hidden ) + { + for( ClientList::ConstIterator it = showing_desktop_clients.begin(); + it != showing_desktop_clients.end(); + ++it ) + (*it)->unminimize(true); + } + showing_desktop_clients.clear(); + --block_showing_desktop; + } + +// Activating/deactivating this feature works like this: +// When nothing is active, and the shortcut is pressed, global shortcuts are disabled +// (using global_shortcuts_disabled) +// When a window that has disabling forced is activated, global shortcuts are disabled. +// (using global_shortcuts_disabled_for_client) +// When a shortcut is pressed and global shortcuts are disabled (either by a shortcut +// or for a client), they are enabled again. +void Workspace::slotDisableGlobalShortcuts() + { + if( global_shortcuts_disabled || global_shortcuts_disabled_for_client ) + disableGlobalShortcuts( false ); + else + disableGlobalShortcuts( true ); + } + +static bool pending_dfc = false; + +void Workspace::disableGlobalShortcutsForClient( bool disable ) + { + if( global_shortcuts_disabled_for_client == disable ) + return; + if( !global_shortcuts_disabled ) + { + if( disable ) + pending_dfc = true; + KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); + // kwin will get the kipc message too + } + } + +void Workspace::disableGlobalShortcuts( bool disable ) + { + KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); + // kwin will get the kipc message too + } + +void Workspace::kipcMessage( int id, int data ) + { + if( id != KIPC::BlockShortcuts ) + return; + if( pending_dfc && data ) + { + global_shortcuts_disabled_for_client = true; + pending_dfc = false; + } + else + { + global_shortcuts_disabled = data; + global_shortcuts_disabled_for_client = false; + } + // update also Alt+LMB actions etc. + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + (*it)->updateMouseGrab(); + } + +} // namespace + +#include "workspace.moc" diff --git a/kwin/workspace.h b/kwin/workspace.h new file mode 100644 index 000000000..9ccf889b4 --- /dev/null +++ b/kwin/workspace.h @@ -0,0 +1,836 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_WORKSPACE_H +#define KWIN_WORKSPACE_H + +#include <qtimer.h> +#include <qvaluevector.h> +#include <kshortcut.h> +#include <qcursor.h> +#include <netwm.h> +#include <kxmessages.h> + +#include "KWinInterface.h" +#include "utils.h" +#include "kdecoration.h" +#include "sm.h" + +#include <X11/Xlib.h> + +class QPopupMenu; +class KConfig; +class KGlobalAccel; +class KShortcutDialog; +class KStartupInfo; +class KStartupInfoId; +class KStartupInfoData; +class QSlider; +class QPushButton; +class KProcess; + +namespace KWinInternal +{ + +class Client; +class TabBox; +class PopupInfo; +class RootInfo; +class PluginMgr; +class Placement; +class Rules; +class WindowRules; + +class SystemTrayWindow + { + public: + SystemTrayWindow() + : win(0),winFor(0) + {} + SystemTrayWindow( WId w ) + : win(w),winFor(0) + {} + SystemTrayWindow( WId w, WId wf ) + : win(w),winFor(wf) + {} + + bool operator==( const SystemTrayWindow& other ) + { return win == other.win; } + WId win; + WId winFor; + }; + +typedef QValueList<SystemTrayWindow> SystemTrayWindowList; + +class Workspace : public QObject, public KWinInterface, public KDecorationDefines + { + Q_OBJECT + public: + Workspace( bool restore = FALSE ); + virtual ~Workspace(); + + static Workspace * self() { return _self; } + + bool workspaceEvent( XEvent * ); + + KDecoration* createDecoration( KDecorationBridge* bridge ); + + bool hasClient( const Client * ); + + template< typename T > Client* findClient( T predicate ); + template< typename T1, typename T2 > void forEachClient( T1 procedure, T2 predicate ); + template< typename T > void forEachClient( T procedure ); + + QRect clientArea( clientAreaOption, const QPoint& p, int desktop ) const; + QRect clientArea( clientAreaOption, const Client* c ) const; + + /** + * @internal + */ + void killWindowId( Window window); + + void killWindow() { slotKillWindow(); } + + WId rootWin() const; + + bool initializing() const; + + /** + * Returns the active client, i.e. the client that has the focus (or None + * if no client has the focus) + */ + Client* activeClient() const; + // Client that was activated, but it's not yet really activeClient(), because + // we didn't process yet the matching FocusIn event. Used mostly in focus + // stealing prevention code. + Client* mostRecentlyActivatedClient() const; + + void activateClient( Client*, bool force = FALSE ); + void requestFocus( Client* c, bool force = FALSE ); + void takeActivity( Client* c, int flags, bool handled ); // flags are ActivityFlags + void handleTakeActivity( Client* c, Time timestamp, int flags ); // flags are ActivityFlags + bool allowClientActivation( const Client* c, Time time = -1U, bool focus_in = false ); + void restoreFocus(); + void gotFocusIn( const Client* ); + void setShouldGetFocus( Client* ); + bool fakeRequestedActivity( Client* c ); + void unfakeActivity( Client* c ); + bool activateNextClient( Client* c ); + bool focusChangeEnabled() { return block_focus == 0; } + + void updateColormap(); + + /** + * Indicates that the client c is being moved around by the user. + */ + void setClientIsMoving( Client *c ); + + void place( Client *c, QRect& area ); + void placeSmart( Client* c, const QRect& area ); + + QPoint adjustClientPosition( Client* c, QPoint pos ); + QRect adjustClientSize( Client* c, QRect moveResizeGeom, int mode ); + void raiseClient( Client* c ); + void lowerClient( Client* c ); + void raiseClientRequest( Client* c, NET::RequestSource src, Time timestamp ); + void lowerClientRequest( Client* c, NET::RequestSource src, Time timestamp ); + void restackClientUnderActive( Client* ); + void updateClientLayer( Client* c ); + void raiseOrLowerClient( Client * ); + void reconfigure(); + + void clientHidden( Client* ); + void clientAttentionChanged( Client* c, bool set ); + + void clientMoved(const QPoint &pos, Time time); + + /** + * Returns the current virtual desktop of this workspace + */ + int currentDesktop() const; + /** + * Returns the number of virtual desktops of this workspace + */ + int numberOfDesktops() const; + void setNumberOfDesktops( int n ); + + QWidget* desktopWidget(); + + // for TabBox + Client* nextFocusChainClient(Client*) const; + Client* previousFocusChainClient(Client*) const; + Client* nextStaticClient(Client*) const; + Client* previousStaticClient(Client*) const; + int nextDesktopFocusChain( int iDesktop ) const; + int previousDesktopFocusChain( int iDesktop ) const; + void closeTabBox(); + + /** + * Returns the list of clients sorted in stacking order, with topmost client + * at the last position + */ + const ClientList& stackingOrder() const; + + ClientList ensureStackingOrder( const ClientList& clients ) const; + + Client* topClientOnDesktop( int desktop, bool unconstrained = false, bool only_normal = true ) const; + Client* findDesktop( bool topmost, int desktop ) const; + void sendClientToDesktop( Client* c, int desktop, bool dont_activate ); + void windowToPreviousDesktop( Client* c ); + void windowToNextDesktop( Client* c ); + + // KDE4 remove me - and it's also in the DCOP interface :( + void showWindowMenuAt( unsigned long id, int x, int y ); + + /** + * Shows the menu operations menu for the client and makes it active if + * it's not already. + */ + void showWindowMenu( const QRect &pos, Client* cl ); + /** + * Backwards compatibility. + */ + void showWindowMenu( int x, int y, Client* cl ); + void showWindowMenu( QPoint pos, Client* cl ); + + void updateMinimizedOfTransients( Client* ); + void updateOnAllDesktopsOfTransients( Client* ); + void checkTransients( Window w ); + + void performWindowOperation( Client* c, WindowOperation op ); + + void storeSession( KConfig* config, SMSavePhase phase ); + + SessionInfo* takeSessionInfo( Client* ); + WindowRules findWindowRules( const Client*, bool ); + void rulesUpdated(); + void discardUsedWindowRules( Client* c, bool withdraw ); + void disableRulesUpdates( bool disable ); + bool rulesUpdatesDisabled() const; + + // dcop interface + void cascadeDesktop(); + void unclutterDesktop(); + void doNotManage(QString); + bool setCurrentDesktop( int new_desktop ); + void nextDesktop(); + void previousDesktop(); + void circulateDesktopApplications(); + + QString desktopName( int desk ) const; + virtual void setDesktopLayout(int , int , int ); + void updateDesktopLayout(); + void setShowingDesktop( bool showing ); + void resetShowingDesktop( bool keep_hidden ); + bool showingDesktop() const; + + bool isNotManaged( const QString& title ); // ### setter or getter ? + + void sendPingToWindow( Window w, Time timestamp ); // called from Client::pingWindow() + void sendTakeActivity( Client* c, Time timestamp, long flags ); // called from Client::takeActivity() + + bool kompmgrIsRunning(); + void setOpacity(unsigned long winId, unsigned int opacityPercent); + void setShadowSize(unsigned long winId, unsigned int shadowSizePercent); + void setUnshadowed(unsigned long winId); // redundant, equals setShadowSize(inId, 0) + + // only called from Client::destroyClient() or Client::releaseWindow() + void removeClient( Client*, allowed_t ); + void setActiveClient( Client*, allowed_t ); + Group* findGroup( Window leader ) const; + void addGroup( Group* group, allowed_t ); + void removeGroup( Group* group, allowed_t ); + Group* findClientLeaderGroup( const Client* c ) const; + + bool checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ); + + void focusToNull(); // SELI public? + enum FocusChainChange { FocusChainMakeFirst, FocusChainMakeLast, FocusChainUpdate }; + void updateFocusChains( Client* c, FocusChainChange change ); + + bool forcedGlobalMouseGrab() const; + void clientShortcutUpdated( Client* c ); + bool shortcutAvailable( const KShortcut& cut, Client* ignore = NULL ) const; + bool globalShortcutsDisabled() const; + void disableGlobalShortcuts( bool disable ); + void disableGlobalShortcutsForClient( bool disable ); + + void sessionSaveStarted(); + void sessionSaveDone(); + void setWasUserInteraction(); + bool wasUserInteraction() const; + bool sessionSaving() const; + + bool managingTopMenus() const; + int topMenuHeight() const; + void updateCurrentTopMenu(); + + int packPositionLeft( const Client* cl, int oldx, bool left_edge ) const; + int packPositionRight( const Client* cl, int oldx, bool right_edge ) const; + int packPositionUp( const Client* cl, int oldy, bool top_edge ) const; + int packPositionDown( const Client* cl, int oldy, bool bottom_edge ) const; + + static QStringList configModules(bool controlCenter); + + void cancelDelayFocus(); + void requestDelayFocus( Client* ); + void updateFocusMousePosition( const QPoint& pos ); + QPoint focusMousePosition() const; + + void toggleTopDockShadows(bool on); + + public slots: + void refresh(); + // keybindings + void slotSwitchDesktopNext(); + void slotSwitchDesktopPrevious(); + void slotSwitchDesktopRight(); + void slotSwitchDesktopLeft(); + void slotSwitchDesktopUp(); + void slotSwitchDesktopDown(); + + void slotSwitchToDesktop( int ); + //void slotSwitchToWindow( int ); + void slotWindowToDesktop( int ); + //void slotWindowToListPosition( int ); + + void slotWindowMaximize(); + void slotWindowMaximizeVertical(); + void slotWindowMaximizeHorizontal(); + void slotWindowMinimize(); + void slotWindowShade(); + void slotWindowRaise(); + void slotWindowLower(); + void slotWindowRaiseOrLower(); + void slotActivateAttentionWindow(); + void slotWindowPackLeft(); + void slotWindowPackRight(); + void slotWindowPackUp(); + void slotWindowPackDown(); + void slotWindowGrowHorizontal(); + void slotWindowGrowVertical(); + void slotWindowShrinkHorizontal(); + void slotWindowShrinkVertical(); + + void slotWalkThroughDesktops(); + void slotWalkBackThroughDesktops(); + void slotWalkThroughDesktopList(); + void slotWalkBackThroughDesktopList(); + void slotWalkThroughWindows(); + void slotWalkBackThroughWindows(); + + void slotWindowOperations(); + void slotWindowClose(); + void slotWindowMove(); + void slotWindowResize(); + void slotWindowAbove(); + void slotWindowBelow(); + void slotWindowOnAllDesktops(); + void slotWindowFullScreen(); + void slotWindowNoBorder(); + + void slotWindowToNextDesktop(); + void slotWindowToPreviousDesktop(); + void slotWindowToDesktopRight(); + void slotWindowToDesktopLeft(); + void slotWindowToDesktopUp(); + void slotWindowToDesktopDown(); + + void slotMouseEmulation(); + void slotDisableGlobalShortcuts(); + + void slotSettingsChanged( int category ); + + void slotReconfigure(); + + void slotKillWindow(); + + void slotGrabWindow(); + void slotGrabDesktop(); + + void slotSetupWindowShortcut(); + void setupWindowShortcutDone( bool ); + + void updateClientArea(); + + // kompmgr, also dcop + void startKompmgr(); + + private slots: + void desktopPopupAboutToShow(); + void clientPopupAboutToShow(); + void slotSendToDesktop( int ); + void clientPopupActivated( int ); + void configureWM(); + void desktopResized(); + void slotUpdateToolWindows(); + void lostTopMenuSelection(); + void lostTopMenuOwner(); + void delayFocus(); + void gotTemporaryRulesMessage( const QString& ); + void cleanupTemporaryRules(); + void writeWindowRules(); + void kipcMessage( int id, int data ); + // kompmgr + void setPopupClientOpacity(int v); + void resetClientOpacity(); + void setTransButtonText(int value); + void unblockKompmgrRestart(); + void restartKompmgr(); + void handleKompmgrOutput( KProcess *proc, char *buffer, int buflen); + void stopKompmgr(); + // end + + protected: + bool keyPressMouseEmulation( XKeyEvent& ev ); + + private: + void init(); + void initShortcuts(); + void readShortcuts(); + void initDesktopPopup(); + void setupWindowShortcut( Client* c ); + + bool startKDEWalkThroughWindows(); + bool startWalkThroughDesktops( int mode ); // TabBox::Mode::DesktopMode | DesktopListMode + bool startWalkThroughDesktops(); + bool startWalkThroughDesktopList(); + void KDEWalkThroughWindows( bool forward ); + void CDEWalkThroughWindows( bool forward ); + void walkThroughDesktops( bool forward ); + void KDEOneStepThroughWindows( bool forward ); + void oneStepThroughDesktops( bool forward, int mode ); // TabBox::Mode::DesktopMode | DesktopListMode + void oneStepThroughDesktops( bool forward ); + void oneStepThroughDesktopList( bool forward ); + bool establishTabBoxGrab(); + void removeTabBoxGrab(); + int desktopToRight( int desktop ) const; + int desktopToLeft( int desktop ) const; + int desktopUp( int desktop ) const; + int desktopDown( int desktop ) const; + + void updateStackingOrder( bool propagate_new_clients = false ); + void propagateClients( bool propagate_new_clients ); // called only from updateStackingOrder + ClientList constrainedStackingOrder(); + void raiseClientWithinApplication( Client* c ); + void lowerClientWithinApplication( Client* c ); + bool allowFullClientRaising( const Client* c, Time timestamp ); + bool keepTransientAbove( const Client* mainwindow, const Client* transient ); + void blockStackingUpdates( bool block ); + void addTopMenu( Client* c ); + void removeTopMenu( Client* c ); + void setupTopMenuHandling(); + void updateTopMenuGeometry( Client* c = NULL ); + void updateToolWindows( bool also_hide ); + + // this is the right way to create a new client + Client* createClient( Window w, bool is_mapped ); + void addClient( Client* c, allowed_t ); + + Window findSpecialEventWindow( XEvent* e ); + + void randomPlacement(Client* c); + void smartPlacement(Client* c); + void cascadePlacement(Client* c, bool re_init = false); + + bool addSystemTrayWin( WId w ); + bool removeSystemTrayWin( WId w, bool check ); + void propagateSystemTrayWins(); + SystemTrayWindow findSystemTrayWin( WId w ); + + // desktop names and number of desktops + void loadDesktopSettings(); + void saveDesktopSettings(); + + // mouse emulation + WId getMouseEmulationWindow(); + enum MouseEmulation { EmuPress, EmuRelease, EmuMove }; + unsigned int sendFakedMouseEvent( QPoint pos, WId win, MouseEmulation type, int button, unsigned int state ); // returns the new state + + void tabBoxKeyPress( const KKeyNative& keyX ); + void tabBoxKeyRelease( const XKeyEvent& ev ); + + // electric borders + void checkElectricBorders( bool force = false ); + void createBorderWindows(); + void destroyBorderWindows(); + bool electricBorder(XEvent * e); + void raiseElectricBorders(); + + // ------------------ + + void helperDialog( const QString& message, const Client* c ); + + void calcDesktopLayout(int &x, int &y) const; + + QPopupMenu* clientPopup(); + void closeActivePopup(); + + void updateClientArea( bool force ); + + SystemTrayWindowList systemTrayWins; + + int current_desktop; + int number_of_desktops; + QMemArray<int> desktop_focus_chain; + + QWidget* active_popup; + Client* active_popup_client; + + QWidget* desktop_widget; + + void loadSessionInfo(); + void loadWindowRules(); + void editWindowRules( Client* c, bool whole_app ); + + QPtrList<SessionInfo> session; + QValueList<Rules*> rules; + KXMessages temporaryRulesMessages; + QTimer rulesUpdatedTimer; + bool rules_updates_disabled; + static const char* windowTypeToTxt( NET::WindowType type ); + static NET::WindowType txtToWindowType( const char* txt ); + static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ); + + Client* active_client; + Client* last_active_client; + Client* most_recently_raised; // used _only_ by raiseOrLowerClient() + Client* movingClient; + Client* pending_take_activity; + + // delay(ed) window focus timer and client + QTimer* delayFocusTimer; + Client* delayfocus_client; + QPoint focusMousePos; + + ClientList clients; + ClientList desktops; + + ClientList unconstrained_stacking_order; // topmost last + ClientList stacking_order; // topmost last + QValueVector< ClientList > focus_chain; // currently active last + ClientList global_focus_chain; // this one is only for things like tabbox's MRU + ClientList should_get_focus; // last is most recent + ClientList attention_chain; + + bool showing_desktop; + ClientList showing_desktop_clients; + int block_showing_desktop; + + GroupList groups; + + bool was_user_interaction; + bool session_saving; + int session_active_client; + int session_desktop; + + bool control_grab; + bool tab_grab; + //KKeyNative walkThroughDesktopsKeycode, walkBackThroughDesktopsKeycode; + //KKeyNative walkThroughDesktopListKeycode, walkBackThroughDesktopListKeycode; + //KKeyNative walkThroughWindowsKeycode, walkBackThroughWindowsKeycode; + KShortcut cutWalkThroughDesktops, cutWalkThroughDesktopsReverse; + KShortcut cutWalkThroughDesktopList, cutWalkThroughDesktopListReverse; + KShortcut cutWalkThroughWindows, cutWalkThroughWindowsReverse; + bool mouse_emulation; + unsigned int mouse_emulation_state; + WId mouse_emulation_window; + int block_focus; + + TabBox* tab_box; + PopupInfo* popupinfo; + + QPopupMenu *popup; + QPopupMenu *advanced_popup; + QPopupMenu *desk_popup; + int desk_popup_index; + + KGlobalAccel *keys; + KGlobalAccel *client_keys; + ShortcutDialog* client_keys_dialog; + Client* client_keys_client; + KGlobalAccel *disable_shortcuts_keys; + bool global_shortcuts_disabled; + bool global_shortcuts_disabled_for_client; + + WId root; + + PluginMgr *mgr; + + RootInfo *rootInfo; + QWidget* supportWindow; + + // swallowing + QStringList doNotManageList; + + // colormap handling + Colormap default_colormap; + Colormap installed_colormap; + + // Timer to collect requests for 'reconfigure' + QTimer reconfigureTimer; + + QTimer updateToolWindowsTimer; + + static Workspace *_self; + + bool workspaceInit; + + KStartupInfo* startup; + + bool electric_have_borders; + int electric_current_border; + WId electric_top_border; + WId electric_bottom_border; + WId electric_left_border; + WId electric_right_border; + int electricLeft; + int electricRight; + int electricTop; + int electricBottom; + Time electric_time_first; + Time electric_time_last; + QPoint electric_push_point; + + Qt::Orientation layoutOrientation; + int layoutX; + int layoutY; + + Placement *initPositioning; + + QRect* workarea; // array of workareas for virtual desktops + QRect** screenarea; // array of workareas per xinerama screen for all virtual desktops + + bool managing_topmenus; + KSelectionOwner* topmenu_selection; + KSelectionWatcher* topmenu_watcher; + ClientList topmenus; // doesn't own them + mutable int topmenu_height; + QWidget* topmenu_space; + + int set_active_client_recursion; + int block_stacking_updates; // when >0, stacking updates are temporarily disabled + bool blocked_propagating_new_clients; // propagate also new clients after enabling stacking updates? + Window null_focus_window; + bool forced_global_mouse_grab; + friend class StackingUpdatesBlocker; + + //kompmgr + QSlider *transSlider; + QPushButton *transButton; + // not used yet + /*Client* topDock; + int maximizedWindowCounter; + int topDockShadowSize;*/ + //end + + signals: + void kompmgrStarted(); + void kompmgrStopped(); + + private: + friend bool performTransiencyCheck(); + }; + +// helper for Workspace::blockStackingUpdates() being called in pairs (true/false) +class StackingUpdatesBlocker + { + public: + StackingUpdatesBlocker( Workspace* w ) + : ws( w ) { ws->blockStackingUpdates( true ); } + ~StackingUpdatesBlocker() + { ws->blockStackingUpdates( false ); } + private: + Workspace* ws; + }; + +// NET WM Protocol handler class +class RootInfo : public NETRootInfo4 + { + private: + typedef KWinInternal::Client Client; // because of NET::Client + public: + RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr= -1); + protected: + virtual void changeNumberOfDesktops(int n); + virtual void changeCurrentDesktop(int d); +// virtual void changeActiveWindow(Window w); the extended version is used + virtual void changeActiveWindow(Window w,NET::RequestSource src, Time timestamp, Window active_window); + virtual void closeWindow(Window w); + virtual void moveResize(Window w, int x_root, int y_root, unsigned long direction); + virtual void moveResizeWindow(Window w, int flags, int x, int y, int width, int height ); + virtual void gotPing(Window w, Time timestamp); + virtual void restackWindow(Window w, RequestSource source, Window above, int detail, Time timestamp); + virtual void gotTakeActivity(Window w, Time timestamp, long flags ); + virtual void changeShowingDesktop( bool showing ); + private: + Workspace* workspace; + }; + + +inline WId Workspace::rootWin() const + { + return root; + } + +inline bool Workspace::initializing() const + { + return workspaceInit; + } + +inline Client* Workspace::activeClient() const + { + return active_client; + } + +inline Client* Workspace::mostRecentlyActivatedClient() const + { + return should_get_focus.count() > 0 ? should_get_focus.last() : active_client; + } + +inline int Workspace::currentDesktop() const + { + return current_desktop; + } + +inline int Workspace::numberOfDesktops() const + { + return number_of_desktops; + } + +inline void Workspace::addGroup( Group* group, allowed_t ) + { + groups.append( group ); + } + +inline void Workspace::removeGroup( Group* group, allowed_t ) + { + groups.remove( group ); + } + +inline const ClientList& Workspace::stackingOrder() const + { +// TODO Q_ASSERT( block_stacking_updates == 0 ); + return stacking_order; + } + +inline void Workspace::showWindowMenu(QPoint pos, Client* cl) + { + showWindowMenu(QRect(pos, pos), cl); + } + +inline void Workspace::showWindowMenu(int x, int y, Client* cl) + { + showWindowMenu(QRect(QPoint(x, y), QPoint(x, y)), cl); + } + +inline +void Workspace::setWasUserInteraction() + { + was_user_interaction = true; + } + +inline +bool Workspace::wasUserInteraction() const + { + return was_user_interaction; + } + +inline +bool Workspace::managingTopMenus() const + { + return managing_topmenus; + } + +inline void Workspace::sessionSaveStarted() + { + session_saving = true; + } + +inline void Workspace::sessionSaveDone() + { + session_saving = false; + } + +inline bool Workspace::sessionSaving() const + { + return session_saving; + } + +inline bool Workspace::forcedGlobalMouseGrab() const + { + return forced_global_mouse_grab; + } + +inline bool Workspace::showingDesktop() const + { + return showing_desktop; + } + +inline bool Workspace::globalShortcutsDisabled() const + { + return global_shortcuts_disabled || global_shortcuts_disabled_for_client; + } + +inline +bool Workspace::rulesUpdatesDisabled() const + { + return rules_updates_disabled; + } + +inline +void Workspace::updateFocusMousePosition( const QPoint& pos ) + { + focusMousePos = pos; + } + +inline +QPoint Workspace::focusMousePosition() const + { + return focusMousePos; + } + +template< typename T > +inline Client* Workspace::findClient( T predicate ) + { + if( Client* ret = findClientInList( clients, predicate )) + return ret; + if( Client* ret = findClientInList( desktops, predicate )) + return ret; + return NULL; + } + +template< typename T1, typename T2 > +inline void Workspace::forEachClient( T1 procedure, T2 predicate ) + { + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + if ( predicate( const_cast< const Client* >( *it))) + procedure( *it ); + for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it) + if ( predicate( const_cast< const Client* >( *it))) + procedure( *it ); + } + +template< typename T > +inline void Workspace::forEachClient( T procedure ) + { + return forEachClient( procedure, TruePredicate()); + } + +KWIN_COMPARE_PREDICATE( ClientMatchPredicate, const Client*, cl == value ); +inline bool Workspace::hasClient( const Client* c ) + { + return findClient( ClientMatchPredicate( c )); + } + +} // namespace + +#endif |