add more spacing
[personal-kdebase.git] / workspace / kwin / kcmkwin / kwinrules / main.cpp
blob31e60b4175ec81266cbba94ff318a2b14b75aace
1 /*
2 * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include <kcmdlineargs.h>
20 #include <kapplication.h>
21 #include <kconfig.h>
22 #include <klocale.h>
23 #include <kwindowsystem.h>
24 #include <QtDBus/QtDBus>
25 #include <X11/Xlib.h>
26 #include <fixx11h.h>
28 #include "ruleswidget.h"
29 #include "../../rules.h"
30 #include <QByteArray>
32 namespace KWin
35 static void loadRules( QList< Rules* >& rules )
37 KConfig _cfg( "kwinrulesrc" );
38 KConfigGroup cfg(&_cfg, "General" );
39 int count = cfg.readEntry( "count",0 );
40 for( int i = 1;
41 i <= count;
42 ++i )
44 cfg = KConfigGroup(&_cfg,QString::number( i ));
45 Rules* rule = new Rules( cfg );
46 rules.append( rule );
50 static void saveRules( const QList< Rules* >& rules )
52 KConfig cfg( "kwinrulesrc" );
53 QStringList groups = cfg.groupList();
54 for( QStringList::ConstIterator it = groups.constBegin();
55 it != groups.constEnd();
56 ++it )
57 cfg.deleteGroup( *it );
58 cfg.group("General").writeEntry( "count", rules.count());
59 int i = 1;
60 for( QList< Rules* >::ConstIterator it = rules.constBegin();
61 it != rules.constEnd();
62 ++it )
64 KConfigGroup cg( &cfg, QString::number( i ));
65 (*it)->write( cg );
66 ++i;
70 static Rules* findRule( const QList< Rules* >& rules, Window wid, bool whole_app )
72 KWindowInfo info = KWindowSystem::windowInfo( wid,
73 NET::WMName | NET::WMWindowType,
74 NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2ClientMachine );
75 if( !info.valid()) // shouldn't really happen
76 return NULL;
77 QByteArray wmclass_class = info.windowClassClass().toLower();
78 QByteArray wmclass_name = info.windowClassName().toLower();
79 QByteArray role = info.windowRole().toLower();
80 NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask | NET::DockMask
81 | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
82 | NET::UtilityMask | NET::SplashMask );
83 QString title = info.name();
84 // QCString extrarole = ""; // TODO
85 QByteArray machine = info.clientMachine().toLower();
86 Rules* best_match = NULL;
87 int match_quality = 0;
88 for( QList< Rules* >::ConstIterator it = rules.constBegin();
89 it != rules.constEnd();
90 ++it )
92 // try to find an exact match, i.e. not a generic rule
93 Rules* rule = *it;
94 int quality = 0;
95 bool generic = true;
96 if( rule->wmclassmatch != Rules::ExactMatch )
97 continue; // too generic
98 if( !rule->matchWMClass( wmclass_class, wmclass_name ))
99 continue;
100 // from now on, it matches the app - now try to match for a specific window
101 if( rule->wmclasscomplete )
103 quality += 1;
104 generic = false; // this can be considered specific enough (old X apps)
106 if( !whole_app )
108 if( rule->windowrolematch != Rules::UnimportantMatch )
110 quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1;
111 generic = false;
113 if( rule->titlematch != Rules::UnimportantMatch )
115 quality += rule->titlematch == Rules::ExactMatch ? 3 : 1;
116 generic = false;
118 if( rule->types != NET::AllTypesMask )
120 int bits = 0;
121 for( unsigned int bit = 1;
122 bit < 1U << 31;
123 bit <<= 1 )
124 if( rule->types & bit )
125 ++bits;
126 if( bits == 1 )
127 quality += 2;
129 if( generic ) // ignore generic rules, use only the ones that are for this window
130 continue;
132 else
134 if( rule->types == NET::AllTypesMask )
135 quality += 2;
137 if( !rule->matchType( type )
138 || !rule->matchRole( role )
139 || !rule->matchTitle( title )
140 || !rule->matchClientMachine( machine ))
141 continue;
142 if( quality > match_quality )
144 best_match = rule;
145 match_quality = quality;
148 if( best_match != NULL )
149 return best_match;
150 Rules* ret = new Rules;
151 if( whole_app )
153 ret->description = i18n( "Application settings for %1", QString::fromLatin1( wmclass_class ));
154 // TODO maybe exclude some types? If yes, then also exclude them above
155 // when searching.
156 ret->types = NET::AllTypesMask;
157 ret->titlematch = Rules::UnimportantMatch;
158 ret->clientmachine = machine; // set, but make unimportant
159 ret->clientmachinematch = Rules::UnimportantMatch;
160 ret->extrarolematch = Rules::UnimportantMatch;
161 ret->windowrolematch = Rules::UnimportantMatch;
162 if( wmclass_name == wmclass_class )
164 ret->wmclasscomplete = false;
165 ret->wmclass = wmclass_class;
166 ret->wmclassmatch = Rules::ExactMatch;
168 else
170 // WM_CLASS components differ - perhaps the app got -name argument
171 ret->wmclasscomplete = true;
172 ret->wmclass = wmclass_name + ' ' + wmclass_class;
173 ret->wmclassmatch = Rules::ExactMatch;
175 return ret;
177 ret->description = i18n( "Window settings for %1", QString::fromLatin1( wmclass_class ));
178 if( type == NET::Unknown )
179 ret->types = NET::NormalMask;
180 else
181 ret->types = 1 << type; // convert type to its mask
182 ret->title = title; // set, but make unimportant
183 ret->titlematch = Rules::UnimportantMatch;
184 ret->clientmachine = machine; // set, but make unimportant
185 ret->clientmachinematch = Rules::UnimportantMatch;
186 // ret->extrarole = extra; TODO
187 ret->extrarolematch = Rules::UnimportantMatch;
188 if( !role.isEmpty()
189 && role != "unknown" && role != "unnamed" ) // Qt sets this if not specified
191 ret->windowrole = role;
192 ret->windowrolematch = Rules::ExactMatch;
193 if( wmclass_name == wmclass_class )
195 ret->wmclasscomplete = false;
196 ret->wmclass = wmclass_class;
197 ret->wmclassmatch = Rules::ExactMatch;
199 else
201 // WM_CLASS components differ - perhaps the app got -name argument
202 ret->wmclasscomplete = true;
203 ret->wmclass = wmclass_name + ' ' + wmclass_class;
204 ret->wmclassmatch = Rules::ExactMatch;
207 else // no role set
209 if( wmclass_name != wmclass_class )
211 ret->wmclasscomplete = true;
212 ret->wmclass = wmclass_name + ' ' + wmclass_class;
213 ret->wmclassmatch = Rules::ExactMatch;
215 else
217 // This is a window that has no role set, and both components of WM_CLASS
218 // match (possibly only differing in case), which most likely means either
219 // the application doesn't give a damn about distinguishing its various
220 // windows, or it's an app that uses role for that, but this window
221 // lacks it for some reason. Use non-complete WM_CLASS matching, also
222 // include window title in the matching, and pray it causes many more positive
223 // matches than negative matches.
224 ret->titlematch = Rules::ExactMatch;
225 ret->wmclasscomplete = false;
226 ret->wmclass = wmclass_class;
227 ret->wmclassmatch = Rules::ExactMatch;
230 return ret;
233 static int edit( Window wid, bool whole_app )
235 QList< Rules* > rules;
236 loadRules( rules );
237 Rules* orig_rule = findRule( rules, wid, whole_app );
238 RulesDialog dlg;
239 // dlg.edit() creates new Rules instance if edited
240 Rules* edited_rule = dlg.edit( orig_rule, wid, true );
241 if( edited_rule == NULL || edited_rule->isEmpty())
243 rules.removeAll( orig_rule );
244 delete orig_rule;
245 if( orig_rule != edited_rule )
246 delete edited_rule;
248 else if( edited_rule != orig_rule )
250 int pos = rules.indexOf( orig_rule );
251 if( pos != -1)
252 rules[ pos ] = edited_rule;
253 else
254 rules.prepend( edited_rule );
255 delete orig_rule;
257 saveRules( rules );
258 // Send signal to all kwin instances
259 QDBusMessage message =
260 QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
261 QDBusConnection::sessionBus().send(message);
262 return 0;
265 } // namespace
267 extern "C"
268 KDE_EXPORT int kdemain( int argc, char* argv[] )
270 KCmdLineArgs::init( argc, argv, "kwin_rules_dialog", "kcmkwinrules", ki18n( "KWin" ), "1.0" ,
271 ki18n( "KWin helper utility" ));
273 KCmdLineOptions options;
274 options.add("wid <wid>", ki18n("WId of the window for special window settings."));
275 options.add("whole-app", ki18n("Whether the settings should affect all windows of the application."));
276 KCmdLineArgs::addCmdLineOptions( options );
277 KApplication app;
278 KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
279 bool id_ok = false;
280 Window id = args->getOption( "wid" ).toULongLong( &id_ok );
281 bool whole_app = args->isSet( "whole-app" );
282 args->clear();
283 if( !id_ok || id == None )
285 KCmdLineArgs::usageError( i18n( "This helper utility is not supposed to be called directly." ));
286 return 1;
288 return KWin::edit( id, whole_app );