From e8208c1dfb4dcd17b1168ac2614aa8d5b761f3fd Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Fri, 10 Feb 2023 14:57:28 +0200 Subject: [PATCH] Kxkb: improve TDE Control Centre module. 1) Add conflicts check for hotkey checkboxes Due to the ability to set multiple keyboard layout switching hotkeys, it is important to inform the user of conflicting options that are not handled properly by the X.org server when set at the same time (e.g. Win+Space and Alt+Space). This change adds a warning that informs the user about the problem and the conflicting options. This warning is shown only when setting multiple hotkeys via the Xkb options tab, which is for the advanced user. Most users will ever need only one hotkey, and the combobox on the first tab should be more than enough. 2) Add "none" item to layout switching options 3) Replace Reset old options checkbox with radio buttons As per discussion, this makes the function of the option more apparent. WhatIs hints have been added for additional clarity. 4) Update hotkey combobox per server options 5) Avoid duplication of options by querying Xkb for already set options. This was a problem in Append Mode in which `setxkbmap` strings would get too long due to setting already set options. This code checks for already set options and omits them from the new `setxkbmap` call. This does not apply to Overwrite Mode. 6) Overwrite previous grp: options when using the combobox See previous commit message about the addition of hotkeys combobox. Signed-off-by: Mavridis Philippe --- kxkb/extension.cpp | 56 ++++- kxkb/extension.h | 1 + kxkb/kcmlayout.cpp | 417 ++++++++++++++++++++++++++----- kxkb/kcmlayout.h | 6 + kxkb/kcmlayoutwidget.ui | 525 +++++++++++++++++++++++++++------------- kxkb/kxkb.cpp | 2 +- kxkb/kxkb.h | 6 +- kxkb/kxkbconfig.cpp | 4 +- kxkb/kxkbconfig.h | 2 +- kxkb/layoutmap.cpp | 4 +- 10 files changed, 778 insertions(+), 245 deletions(-) diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp index 1b6895043..df61e2fa2 100644 --- a/kxkb/extension.cpp +++ b/kxkb/extension.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -89,11 +90,17 @@ bool XKBExtension::setXkbOptions(const XkbOptions options) TDEProcess p; p << exe; - p << "-layout"; - p << options.layouts; - - p << "-variant"; - p << options.variants; + if (!options.layouts.isEmpty()) + { + p << "-layout"; + p << options.layouts; + } + + if (!options.variants.isEmpty()) + { + p << "-variant"; + p << options.variants; + } if (!options.model.isEmpty()) { p << "-model"; @@ -103,17 +110,50 @@ bool XKBExtension::setXkbOptions(const XkbOptions options) if (options.resetOld) { p << "-option"; } + if (!options.options.isEmpty()) { - p << "-option" << options.options; + p << "-option"; + + if (options.resetOld) + { + p << options.options; + } + else + { + // Avoid duplication of options in Append mode + TQStringList srvOptions = TQStringList::split(",", XKBExtension::getServerOptions()); + TQStringList kxkbOptions = TQStringList::split(",", options.options); + TQStringList newOptions; + for (TQStringList::Iterator it = kxkbOptions.begin(); it != kxkbOptions.end(); ++it) + { + TQString option(*it); + if (!srvOptions.contains(option)) + { + newOptions << option; + } + } + p << newOptions.join(","); + } } - kdDebug() << "[kxkb-extension] Command: " << p.args() << endl; + kdDebug() << "[setXkbOptions] Command: " << p.args() << endl; p.start(TDEProcess::Block); return p.normalExit() && (p.exitStatus() == 0); } +TQString XKBExtension::getServerOptions() +{ + XkbRF_VarDefsRec vd; + if (XkbRF_GetNamesProp(tqt_xdisplay(), nullptr, &vd) && vd.options) + { + kdDebug() << "[kxkb-extension] Got server options " << vd.options << endl; + return TQString(vd.options); + } + return TQString::null; +} + bool XKBExtension::setGroup(unsigned int group) { kdDebug() << "[kxkb-extension] Setting group " << group << endl; @@ -136,4 +176,4 @@ void XKBExtension::processXEvent(XEvent *event) { } } -#include "extension.moc" \ No newline at end of file +#include "extension.moc" diff --git a/kxkb/extension.h b/kxkb/extension.h index fa482c222..9a3d2da8e 100644 --- a/kxkb/extension.h +++ b/kxkb/extension.h @@ -16,6 +16,7 @@ public: bool init(); static bool setXkbOptions(const XkbOptions options); + static TQString getServerOptions(); bool setGroup(unsigned int group); unsigned int getGroup() const; void processXEvent(XEvent *ev); diff --git a/kxkb/kcmlayout.cpp b/kxkb/kcmlayout.cpp index baadfd7e0..a2b5d8e12 100644 --- a/kxkb/kcmlayout.cpp +++ b/kxkb/kcmlayout.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -99,7 +100,8 @@ static TQListViewItem* copyLVI(const TQListViewItem* src, TQListView* parent) LayoutConfig::LayoutConfig(TQWidget *parent, const char *name) : TDECModule(parent, name), - m_rules(NULL) + m_rules(NULL), + m_forceGrpOverwrite(false) { TQVBoxLayout *main = new TQVBoxLayout(this, 0, KDialog::spacingHint()); @@ -232,6 +234,7 @@ void LayoutConfig::initUI() { widget->comboHotkey->insertItem(i18n(hkDesc)); } } + widget->comboHotkey->insertItem(i18n("None")); widget->comboHotkey->insertItem(i18n("Other...")); // display KXKB switching options @@ -243,7 +246,7 @@ void LayoutConfig::initUI() { widget->radFlagOnly->setChecked( showFlag && !showLabel ); widget->radLabelOnly->setChecked( !showFlag && showLabel ); - widget->checkResetOld->setChecked(m_kxkbConfig.m_resetOldOptions); + widget->xkbOptsMode->setButton(m_kxkbConfig.m_resetOldOptions ? 0 : 1); widget->grpLabel->setButton( ( m_kxkbConfig.m_useThemeColors ? 0 : 1 ) ); widget->bgColor->setColor( m_kxkbConfig.m_colorBackground ); @@ -280,15 +283,22 @@ void LayoutConfig::initUI() { widget->chkEnable->setChecked( m_kxkbConfig.m_useKxkb ); widget->grpLayouts->setEnabled( m_kxkbConfig.m_useKxkb ); - widget->optionsFrame->setEnabled( m_kxkbConfig.m_useKxkb ); + widget->swOptsFrame->setEnabled( m_kxkbConfig.m_useKxkb ); + widget->indOptsFrame->setEnabled( m_kxkbConfig.m_useKxkb ); // display xkb options TQStringList activeOptions = TQStringList::split(',', m_kxkbConfig.m_options); + bool foundGrp = false; for (TQStringList::ConstIterator it = activeOptions.begin(); it != activeOptions.end(); ++it) { TQString option = *it; TQString optionKey = option.mid(0, option.find(':')); TQString optionName = m_rules->options()[option]; + + if (optionKey == "grp") { + foundGrp = true; + } + OptionListItem *item = m_optionGroups[i18n(optionKey.latin1())]; if (item != NULL) { @@ -304,8 +314,15 @@ void LayoutConfig::initUI() { } } + if (!foundGrp) { + OptionListItem *grpNone = itemForOption("grp:none"); + if (grpNone) { + grpNone->setOn(true); + } + } + updateOptionsCommand(); - updateHotkeyCombo(); + updateHotkeyCombo(true); emit TDECModule::changed( false ); } @@ -315,7 +332,7 @@ void LayoutConfig::save() TQString model = lookupLocalized(m_rules->models(), widget->comboModel->currentText()); m_kxkbConfig.m_model = model; - m_kxkbConfig.m_resetOldOptions = widget->checkResetOld->isChecked(); + m_kxkbConfig.m_resetOldOptions = widget->radXkbOverwrite->isOn(); m_kxkbConfig.m_options = createOptionString(); m_kxkbConfig.m_useThemeColors = widget->radLabelUseTheme->isChecked(); @@ -373,6 +390,35 @@ void LayoutConfig::save() m_kxkbConfig.save(); + // We might need to unset previous hotkey options + if (m_forceGrpOverwrite) + { + // First get all the server's options + TQStringList srvOptions = TQStringList::split(",", XKBExtension::getServerOptions()); + TQStringList newOptions; + + // Then remove all grp: options + for (TQStringList::Iterator it = srvOptions.begin(); it != srvOptions.end(); ++it) + { + TQString opt(*it); + if (!opt.startsWith("grp:")) + { + newOptions << opt; + } + } + + XkbOptions xkbOptions; + xkbOptions.options = newOptions.join(","); + xkbOptions.resetOld = true; + + if (!XKBExtension::setXkbOptions(xkbOptions)) + { + kdWarning() << "[LayoutConfig::save] Could not overwrite previous grp: options!" << endl; + } + + m_forceGrpOverwrite = false; + } + // Get current layout from Kxkb if (!kapp->dcopClient()->isAttached()) kapp->dcopClient()->attach(); @@ -405,6 +451,8 @@ void LayoutConfig::save() } } + updateHotkeyCombo(); + emit TDECModule::changed( false ); } @@ -518,6 +566,7 @@ void LayoutConfig::variantChanged() if( selectedVariant == DEFAULT_VARIANT_NAME ) selectedVariant = ""; selLayout->setText(LAYOUT_COLUMN_VARIANT, selectedVariant); + updateLayoutCommand(); } // helper @@ -598,12 +647,12 @@ TQWidget* LayoutConfig::makeOptionsTab() listView->clear(); connect(listView, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(changed())); - connect(listView, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(updateOptionsCommand())); + connect(listView, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(resolveConflicts(TQListViewItem *))); connect(listView, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(updateHotkeyCombo())); - connect(widget->checkResetOld, TQT_SIGNAL(toggled(bool)), TQT_SLOT(changed())); - connect(widget->checkResetOld, TQT_SIGNAL(toggled(bool)), TQT_SLOT(updateOptionsCommand())); - connect(widget->checkResetOld, TQT_SIGNAL(toggled(bool)), TQT_SLOT(updateHotkeyCombo())); + connect(widget->xkbOptsMode, TQT_SIGNAL(released(int)), TQT_SLOT(changed())); + connect(widget->xkbOptsMode, TQT_SIGNAL(released(int)), TQT_SLOT(updateOptionsCommand())); + connect(widget->xkbOptsMode, TQT_SIGNAL(released(int)), TQT_SLOT(updateHotkeyCombo())); //Create controllers for all options TQDictIterator it(m_rules->options()); @@ -613,13 +662,20 @@ TQWidget* LayoutConfig::makeOptionsTab() if (!it.currentKey().contains(':')) { if( it.currentKey() == "ctrl" || it.currentKey() == "caps" - || it.currentKey() == "altwin" || it.currentKey() == "grp") { + || it.currentKey() == "altwin") { parent = new OptionListItem(listView, i18n( it.current() ), TQCheckListItem::RadioButtonController, it.currentKey()); OptionListItem *item = new OptionListItem(parent, i18n( "None" ), TQCheckListItem::RadioButton, "none"); item->setState(TQCheckListItem::On); } + else if (it.currentKey() == "grp") { + parent = new OptionListItem(listView, i18n(it.current()), + TQCheckListItem::RadioButtonController, it.currentKey()); + parent->setSelectable(false); + OptionListItem *item = new OptionListItem(parent, i18n("None"), + TQCheckListItem::CheckBox, "grp:none"); + } else { parent = new OptionListItem(listView, i18n( it.current() ), TQCheckListItem::CheckBoxController, it.currentKey()); @@ -643,12 +699,13 @@ TQWidget* LayoutConfig::makeOptionsTab() // workaroung for mistake in rules file for xkb options in XFree 4.2.0 TQString text(it.current()); text = text.replace( "Cap$", "Caps." ); - if( parent->type() == TQCheckListItem::RadioButtonController ) + if ( parent->type() == TQCheckListItem::CheckBoxController + || key.startsWith("grp:")) new OptionListItem(parent, i18n(text.utf8()), - TQCheckListItem::RadioButton, key); + TQCheckListItem::CheckBox, key); else new OptionListItem(parent, i18n(text.utf8()), - TQCheckListItem::CheckBox, key); + TQCheckListItem::RadioButton, key); } } } @@ -662,14 +719,19 @@ void LayoutConfig::updateOptionsCommand() { TQString setxkbmap; TQString options = createOptionString(); + bool overwrite = widget->radXkbOverwrite->isOn(); if( !options.isEmpty() ) { setxkbmap = "setxkbmap -option "; //-rules " + m_rule - if( widget->checkResetOld->isChecked() ) + if (overwrite) setxkbmap += "-option "; setxkbmap += options; } + else if (overwrite) { + setxkbmap = "setxkbmap -option"; + } widget->editCmdLineOpt->setText(setxkbmap); + widget->editCmdLineOpt->setDisabled(setxkbmap.isEmpty()); } void LayoutConfig::updateLayoutCommand() @@ -726,38 +788,268 @@ void LayoutConfig::updateLayoutCommand() widget->editDisplayName->setText(selDisplayName); } +void LayoutConfig::checkConflicts(OptionListItem *current, + TQStringList conflicting, + TQStringList &conflicts) +{ + if (!current || conflicting.count() < 2 || + !conflicting.contains(current->optionName())) + { + return; + } + TQStringList::Iterator it; + for (it = conflicting.begin(); it != conflicting.end(); ++it) { + TQString option(*it); + if (option == current->optionName()) { + continue; + } + + OptionListItem *item = itemForOption(option); + if (item && item->isOn()) { + conflicts << item->text(); + } + } +} + +void LayoutConfig::resolveConflicts(TQListViewItem *lvi) { + OptionListItem *current = (OptionListItem*)lvi; + + kdDebug() << "resolveConflicts : " << current->optionName() << endl; + + if (current->optionName().startsWith("grp:")) { + OptionListItem *grpItem = m_optionGroups[i18n("grp")]; + if (grpItem == NULL) { + kdWarning() << "LayoutConfig: cannot find grp item group" << endl; + return; + } + + OptionListItem *noneItem = grpItem->findChildItem("grp:none"); + if (!noneItem) { + kdDebug() << "LayoutConfig: unable to find None item for grp!" << endl; + } + else { + // Option "none" selected, uncheck all other options immediately + if (current->optionName() == "grp:none") { + if (current->isOn()) { + OptionListItem *child = (OptionListItem*)grpItem->firstChild(); + while (child) { + if (child != current) { + child->setOn(false); + } + child = (OptionListItem*)child->nextSibling(); + } + } + else { + current->setOn(true); + } + updateOptionsCommand(); + return; + } + + // If no options are selected then select "none" + bool notNone = false; + OptionListItem *child = (OptionListItem*)grpItem->firstChild(); + while (child) { + if (child->isOn() && child->optionName() != "none") { + notNone = true; + break; + } + child = (OptionListItem*)child->nextSibling(); + } + + noneItem->setOn(!notNone); + } + } + + TQStringList conflicts; + OptionListItem *conflict; + + TQStringList conflicting; + /* Might be incomplete */ + // Space + conflicting << "grp:win_space_toggle" + << "grp:alt_space_toggle" + << "grp:ctrl_space_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Shift + conflicting.clear(); + conflicting << "grp:ctrl_shift_toggle" + << "grp:alt_shift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Control + conflicting.clear(); + conflicting << "grp:ctrl_select" + << "grp:ctrl_alt_toggle" + << "grp:ctrl_shift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Left Ctrl + conflicting.clear(); + conflicting << "grp:lctrl_toggle" + << "grp:lctrl_lshift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Right Ctrl + conflicting.clear(); + conflicting << "grp:rctrl_toggle" + << "grp:rctrl_rshift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Win + conflicting.clear(); + conflicting << "grp:win_space_toggle" + << "grp:win_switch" + << "win_menu_select"; + checkConflicts(current, conflicting, conflicts); + + // Left Alt + conflicting.clear(); + conflicting << "grp:lalt_toggle" + << "grp:lalt_lshift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Right Alt + conflicting.clear(); + conflicting << "grp:ralt_toggle" + << "grp:ralt_rshift_toggle"; + checkConflicts(current, conflicting, conflicts); + + // Caps Lock + conflicting.clear(); + conflicting << "grp:caps_toggle" + << "grp:caps_select" + << "grp:caps_switch" + << "grp:alt_caps_toggle"; + checkConflicts(current, conflicting, conflicts); + + if (conflicts.count()) { + TQString curText = current->text(); + int confirm = KMessageBox::warningYesNoList(this, + i18n("The option %1 might conflict with " + "other options that you have already enabled.
" + "Are you sure that you really want to enable " + "%2?
") + .arg(curText).arg(curText), + conflicts, + i18n("Conflicting options")); + if (confirm == KMessageBox::No) { + current->setOn(false); + } + } + updateOptionsCommand(); +} + // Synchronizes Xkb grp options --> hotkeys combobox void LayoutConfig::updateHotkeyCombo() { + updateHotkeyCombo(false); +} + +void LayoutConfig::updateHotkeyCombo(bool initial) { OptionListItem *grpItem = m_optionGroups[i18n("grp")]; if (grpItem == NULL) { kdWarning() << "LayoutConfig: cannot find grp item group" << endl; return; } - OptionListItem *child = (OptionListItem*)grpItem->firstChild(); - while (child) { - if (child->isOn()) { - bool found = false; - for (int i = 0; i < widget->comboHotkey->count(); ++i) { - if (child->text() == widget->comboHotkey->text(i)) { - widget->comboHotkey->setCurrentItem(i); - found = true; - } + TQStringList hotkeys; + + // Get server options first + if (initial || widget->xkbOptsMode->selectedId() == 1) + { + TQStringList opts = TQStringList::split(",", XKBExtension::getServerOptions()); + for (TQStringList::Iterator it = opts.begin(); it != opts.end(); ++it) + { + TQString option(*it); + + if (!option.startsWith("grp:")) + { + continue; } - if (!found) { - int other = widget->comboHotkey->count() - 1; - widget->comboHotkey->changeItem(i18n("Other (%1)...").arg(child->text()), - other); - widget->comboHotkey->setCurrentItem(other); + + // Get description from existing item + // This spares us the trouble of querying Xkb rules second time + OptionListItem *item = itemForOption(option); + if (!item) + { + kdWarning() << "[updateHotkeyCombo] server has set unknown option: " + << option << endl; + continue; } + + TQString optionName = item->text(); + if (!hotkeys.contains(optionName) && option != "grp:none") + { + hotkeys << optionName; + } + } + } + + OptionListItem *child = (OptionListItem*)grpItem->firstChild(); + while (child) { + TQString optionText = child->text(); + if (child->isOn() && !hotkeys.contains(optionText) && child->optionName() != "grp:none") { + hotkeys << optionText; } child = (OptionListItem*)child->nextSibling(); } + + if (!hotkeys.count()) { + OptionListItem *noneItem = itemForOption("grp:none"); + if (noneItem) + { + hotkeys << noneItem->text(); + } + else + { + kdWarning() << "[updateHotkeyCombo] cannot find grp:none item!" << endl; + hotkeys << widget->comboHotkey->text(0); // fallback + } + } + + int other = widget->comboHotkey->count() - 1; + widget->comboHotkey->changeItem(i18n("Custom..."), other); + if (hotkeys.count() < 2) { + bool found = false; + for (int i = 0; i < widget->comboHotkey->count(); ++i) { + if (hotkeys[0] == widget->comboHotkey->text(i)) { + widget->comboHotkey->setCurrentItem(i); + found = true; + } + } + if (!found) { + widget->comboHotkey->changeItem(i18n("Other (%1)").arg(hotkeys[0]), + other); + widget->comboHotkey->setCurrentItem(other); + } + } + else { + widget->comboHotkey->changeItem(i18n("Multiple (%1)").arg(hotkeys.join("; ")), + other); + widget->comboHotkey->setCurrentItem(other); + } } // Synchronizes hotkeys combobox --> Xkb grp options void LayoutConfig::hotkeyComboChanged() { - TQString hkDesc = widget->comboHotkey->currentText(); + TQStringList hotkeys; + int other = widget->comboHotkey->count() - 1; + + if (widget->comboHotkey->currentItem() != other) { + hotkeys << widget->comboHotkey->currentText(); + } + else { + TQString otherStr = widget->comboHotkey->text(other); + int i1 = otherStr.find("("); + if (i1 != -1) { // custom hotkey(s) set + ++i1; + int i2 = otherStr.findRev(")"); + if (i2 != -1) { + hotkeys = TQStringList::split("; ", otherStr.mid(i1, i2-i1)); + } + } + } OptionListItem *grpItem = m_optionGroups[i18n("grp")]; if (grpItem == NULL) { @@ -767,21 +1059,17 @@ void LayoutConfig::hotkeyComboChanged() { OptionListItem *child = (OptionListItem*)grpItem->firstChild(); while (child) { - child->setOn(child->text() == hkDesc); + child->setOn(hotkeys.contains(child->text())); child = (OptionListItem*)child->nextSibling(); } - // Other... - if (widget->comboHotkey->count() - 1 == widget->comboHotkey->currentItem()) { - OptionListItem *none = grpItem->findChildItem("none"); - if (none) { - none->setOn(true); - } - widget->tabWidget->setCurrentPage(2); - widget->listOptions->setCurrentItem(none); - widget->listOptions->ensureItemVisible(none); + if (widget->comboHotkey->currentItem() == other) { + widget->tabWidget->setCurrentPage(3); + widget->listOptions->ensureItemVisible(grpItem); widget->listOptions->setFocus(); } + + m_forceGrpOverwrite = true; } void LayoutConfig::changed() @@ -831,6 +1119,20 @@ void LayoutConfig::loadRules() //TODO: reset options and xkb options } +OptionListItem* LayoutConfig::itemForOption(TQString option) { + if (!option.contains(':')) { + return nullptr; + } + + TQString optionKey = option.mid(0, option.find(':')); + OptionListItem *item = m_optionGroups[optionKey]; + + if( !item ) { + kdDebug() << "WARNING: skipping empty group for " << option << endl; + return nullptr; + } + return (OptionListItem*)item->findChildItem(option); +} TQString LayoutConfig::createOptionString() { @@ -838,32 +1140,17 @@ TQString LayoutConfig::createOptionString() for (TQDictIterator it(m_rules->options()); it.current(); ++it) { TQString option(it.currentKey()); - - if (option.contains(':')) { - - TQString optionKey = option.mid(0, option.find(':')); - OptionListItem *item = m_optionGroups[optionKey]; - - if( !item ) { - kdDebug() << "WARNING: skipping empty group for " << it.currentKey() - << endl; - continue; - } - - OptionListItem *child = item->findChildItem( option ); - - if ( child ) { - if ( child->state() == TQCheckListItem::On ) { - TQString selectedName = child->optionName(); - if ( !selectedName.isEmpty() && selectedName != "none" ) { - if (!options.isEmpty()) - options.append(','); - options.append(selectedName); - } - } + OptionListItem *child = itemForOption(option); + if (!child) { + continue; + } + if ( child->state() == TQCheckListItem::On ) { + TQString selectedName = child->optionName(); + if ( !selectedName.isEmpty() && selectedName != "none" ) { + if (!options.isEmpty()) + options.append(','); + options.append(selectedName); } - else - kdDebug() << "Empty option button for group " << it.currentKey() << endl; } } return options; @@ -931,7 +1218,7 @@ extern "C" kapp->startServiceByDesktopName("kxkb"); } else { - if (!XKBExtension::setXkbOptions(m_kxkbConfig.getXkbOptions())) { + if (!XKBExtension::setXkbOptions(m_kxkbConfig.getKXkbOptions())) { kdDebug() << "Setting XKB options failed!" << endl; } } diff --git a/kxkb/kcmlayout.h b/kxkb/kcmlayout.h index e16c09bf7..cfb748c10 100644 --- a/kxkb/kcmlayout.h +++ b/kxkb/kcmlayout.h @@ -31,6 +31,7 @@ public: protected: TQString createOptionString(); void updateIndicator(TQListViewItem* selLayout); + OptionListItem* itemForOption(TQString option); protected slots: void moveUp(); @@ -43,8 +44,10 @@ protected slots: void updateLayoutCommand(); void updateOptionsCommand(); void updateHotkeyCombo(); + void updateHotkeyCombo(bool initial); void add(); void remove(); + void resolveConflicts(TQListViewItem *lvi); void changed(); @@ -54,10 +57,13 @@ private: XkbRules *m_rules; KxkbConfig m_kxkbConfig; TQDict m_optionGroups; + bool m_forceGrpOverwrite; TQWidget* makeOptionsTab(); void updateStickyLimit(); static LayoutUnit getLayoutUnitKey(TQListViewItem *sel); + void checkConflicts(OptionListItem *current, TQStringList conflicting, + TQStringList &conflicts); }; diff --git a/kxkb/kcmlayoutwidget.ui b/kxkb/kcmlayoutwidget.ui index a36919b77..aab21c2cf 100644 --- a/kxkb/kcmlayoutwidget.ui +++ b/kxkb/kcmlayoutwidget.ui @@ -116,7 +116,7 @@ - Here you can choose the key combination you want to use to switch to the next layout. This list includes only the most common variants. If you choose "Other...", then you will be redirected to the "Options" tab where you can pick from all the available variants. + Here you can choose the key combination you want to use to switch to the next layout. This list includes only the most common variants. If you choose "Other...", then you will be redirected to the "Options" tab where you can pick from all the available variants. Note that if you have selected Append Mode in the Xkb Options tab this option is not available; you have to use the Xkb Options tab instead. @@ -430,13 +430,166 @@ Switching Options - - - unnamed - - + + - spacer1 + swOptsFrame + + + StyledPanel + + + Raised + + + 0 + + + + + grpSwitching + + + Switching Policy + + + true + + + If you select "Application" or "Window" switching policy, changing the keyboard layout will only affect the current application or window. + + + + unnamed + + + + radioButton1 + + + &Global + + + true + + + + + radioButton1_3 + + + Application + + + + + radioButton1_2 + + + &Window + + + + + + + grpBoxStickySwitching + + + Sticky Switching + + + + unnamed + + + + chkEnableSticky + + + Enable sticky switching + + + If you have more than two layouts and turn this option on, switching with the keyboard shortcut or clicking on the kxkb indicator will only cycle through the last few layouts. You can specify the number of layouts to rotate below. You can still access all layouts by right-clicking on the kxkb indicator. + + + + + spacer2 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + textLabel1_5 + + + false + + + Number of layouts to rotate: + + + spinBox1 + + + + + spinStickyDepth + + + false + + + 10 + + + 2 + + + + 5 + 0 + 0 + 0 + + + + + + + + + + spacer2 Vertical @@ -451,9 +604,19 @@ - + + + + + tab + + + Indicator Options + + + - optionsFrame + indOptsFrame StyledPanel @@ -465,10 +628,7 @@ 0 - - unnamed - - + grpStyle @@ -514,7 +674,7 @@ - + grpLabel @@ -558,6 +718,9 @@ Plain + + 0 + spacer2 @@ -593,7 +756,32 @@ This color will be used as the indicator's background unless the indicator was set to display a flag. + + + 4 + 0 + 0 + 0 + + + + + spacer23 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + spacer2 @@ -626,29 +814,71 @@ fgColor - - This color will be used to draw the language label on the indicator. - + + This color will be used to draw the language label on the indicator. + + + + 4 + 0 + 0 + 0 + + + + + + spacer23 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + spacer22 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + chkBgTransparent + + + Transparent background + + + Check this to remove the indicator's background. Only applicable in "Label only" mode. + - - - chkBgTransparent - - - Transparent background - - - Check this to remove the indicator's background. Only applicable in "Label only" mode. - - separator1 - + labelFontRequester @@ -659,12 +889,12 @@ This is the font which will be used by the layout indicator to draw the label. - + labelFont - + chkLabelShadow @@ -674,8 +904,16 @@ Draw a drop shadow behind the language label. In some cases this option can improve readability. + + + 4 + 0 + 0 + 0 + + - + shColor @@ -685,86 +923,23 @@ false - - - - - - grpSwitching - - - Switching Policy - - - true - - - If you select "Application" or "Window" switching policy, changing the keyboard layout will only affect the current application or window. - - - - unnamed - - - - radioButton1 - - - &Global - - - true - - - - - radioButton1_3 - - - Application - - - - - radioButton1_2 - - - &Window - - - - - - - grpBoxStickySwitching - - - Sticky Switching - - - - unnamed - - - - chkEnableSticky - - - Enable sticky switching - - - If you have more than two layouts and turn this option on, switching with the keyboard shortcut or clicking on the kxkb indicator will only cycle through the last few layouts. You can specify the number of layouts to rotate below. You can still access all layouts by right-clicking on the kxkb indicator. + + + 4 + 0 + 0 + 0 + - - + - spacer2 + spacer23 Horizontal - Fixed + Expanding @@ -773,55 +948,46 @@ - - - textLabel1_5 - - - false - - - Number of layouts to rotate: - - - spinBox1 - - - - - spinStickyDepth - - - false - - - 10 - - - 2 - - - - 7 - 0 - 0 - 0 - - - - + - chkShowSingle + grpMisc - - Show indicator for single layout + + Miscellaneous + + + + chkShowSingle + + + Show indicator for single layout + + + - + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 40 + + + + @@ -848,15 +1014,7 @@ unnamed - - - checkResetOld - - - &Reset old options - - - + textLabel1_3_2 @@ -864,7 +1022,7 @@ Command: - + editCmdLineOpt @@ -872,7 +1030,7 @@ true - + Options @@ -890,6 +1048,41 @@ + + + xkbOptsMode + + + Options Mode + + + Here you can choose how the options you select here will be applied: in addition to, or instead of existing options. + + + + + radXkbOverwrite + + + &Overwrite existing options (recommended) + + + Overwrite any existing Xkb options that might have been previously set by another program or from a script (e.g. via setxkbmap). This is the recommended option. + + + + + radXkbAppend + + + &Append to existing options + + + Append the selected options to any existing Xkb options that might have been previously set by another program or from a script (e.g. via setxkbmap). Only use this if you really need to. + + + + @@ -902,6 +1095,18 @@ grpLayouts setEnabled(bool) + + chkEnable + toggled(bool) + swOptsFrame + setEnabled(bool) + + + chkEnable + toggled(bool) + indOptsFrame + setEnabled(bool) + chkEnableSticky toggled(bool) @@ -926,12 +1131,6 @@ shColor setEnabled(bool) - - chkEnable - toggled(bool) - optionsFrame - setEnabled(bool) - radFlagOnly toggled(bool) diff --git a/kxkb/kxkb.cpp b/kxkb/kxkb.cpp index 532f26b21..0764aff41 100644 --- a/kxkb/kxkb.cpp +++ b/kxkb/kxkb.cpp @@ -99,7 +99,7 @@ int KXKBApp::newInstance() bool KXKBApp::settingsRead() { - XkbOptions options = kxkbConfig.getXkbOptions(); + XkbOptions options = kxkbConfig.getKXkbOptions(); if( !m_extension->setXkbOptions(options) ) { kdDebug() << "Setting XKB options failed!" << endl; } diff --git a/kxkb/kxkb.h b/kxkb/kxkb.h index f7b0721f2..06c45d834 100644 --- a/kxkb/kxkb.h +++ b/kxkb/kxkb.h @@ -58,8 +58,8 @@ public: virtual int newInstance(); - bool setLayout(const LayoutUnit& layoutUnit); - bool setLayout(const uint group); + bool setLayout(const LayoutUnit& layoutUnit); + bool setLayout(const uint group); k_dcop: bool setLayout(const TQString& layoutPair); TQString getCurrentLayout() { return m_currentLayout.toPair(); } @@ -72,7 +72,7 @@ protected slots: void menuActivated(int id); void windowChanged(WId winId); void layoutApply(); - void slotGroupChanged(uint group); + void slotGroupChanged(uint group); void slotSettingsChanged(int category); diff --git a/kxkb/kxkbconfig.cpp b/kxkb/kxkbconfig.cpp index 183709769..b1b428fa9 100644 --- a/kxkb/kxkbconfig.cpp +++ b/kxkb/kxkbconfig.cpp @@ -266,7 +266,7 @@ TQString KxkbConfig::getDefaultDisplayName(const LayoutUnit& layoutUnit, bool si return displayName; } -const XkbOptions KxkbConfig::getXkbOptions() { +const XkbOptions KxkbConfig::getKXkbOptions() { load(LOAD_ALL); XkbOptions options; @@ -281,7 +281,7 @@ const XkbOptions KxkbConfig::getXkbOptions() { options.variants = variants.join(","); options.model = m_model; options.options = m_options; - kdDebug() << "[getXkbOptions] options: " << m_options << endl; + kdDebug() << "[getKXkbOptions] options: " << m_options << endl; options.resetOld = m_resetOldOptions; return options; } diff --git a/kxkb/kxkbconfig.h b/kxkb/kxkbconfig.h index afa028687..140a2b763 100644 --- a/kxkb/kxkbconfig.h +++ b/kxkb/kxkbconfig.h @@ -128,7 +128,7 @@ public: static TQString getDefaultDisplayName(const TQString& code_); static TQString getDefaultDisplayName(const LayoutUnit& layoutUnit, bool single=false); - const XkbOptions getXkbOptions(); + const XkbOptions getKXkbOptions(); private: static const TQMap parseIncludesMap(const TQStringList& pairList); diff --git a/kxkb/layoutmap.cpp b/kxkb/layoutmap.cpp index 7b5136e2a..636911199 100644 --- a/kxkb/layoutmap.cpp +++ b/kxkb/layoutmap.cpp @@ -88,7 +88,7 @@ LayoutState& LayoutMap::getNextLayout() { layoutQueue.enqueue(layoutState); kdDebug() << "map: Next layout: " << layoutQueue.head()->layoutUnit.toPair() - << " for " << m_currentWinId << endl; + << " for " << m_currentWinId << endl; return *layoutQueue.head(); } @@ -96,7 +96,7 @@ LayoutState& LayoutMap::getNextLayout() { void LayoutMap::setCurrentLayout(const LayoutUnit& layoutUnit) { LayoutQueue& layoutQueue = getCurrentLayoutQueue(m_currentWinId); kdDebug() << "map: Storing layout: " << layoutUnit.toPair() - << " for " << m_currentWinId << endl; + << " for " << m_currentWinId << endl; int queueSize = (int)layoutQueue.count(); for(int ii=0; ii