diff options
author | Mavridis Philippe <[email protected]> | 2023-02-10 14:57:28 +0200 |
---|---|---|
committer | Mavridis Philippe <[email protected]> | 2023-03-24 15:28:11 +0200 |
commit | e8208c1dfb4dcd17b1168ac2614aa8d5b761f3fd (patch) | |
tree | 26a597b2a9a11e9cab352940be0d687ea6593668 /kxkb/kcmlayout.cpp | |
parent | a67db2d4847d798c01d4fd7584c5bb9297e109e3 (diff) | |
download | tdebase-e8208c1dfb4dcd17b1168ac2614aa8d5b761f3fd.tar.gz tdebase-e8208c1dfb4dcd17b1168ac2614aa8d5b761f3fd.zip |
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 <[email protected]>
Diffstat (limited to 'kxkb/kcmlayout.cpp')
-rw-r--r-- | kxkb/kcmlayout.cpp | 417 |
1 files changed, 352 insertions, 65 deletions
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 <kdebug.h> #include <tdeapplication.h> #include <kiconloader.h> +#include <tdemessagebox.h> #include <dcopref.h> #include <dcopclient.h> @@ -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<char> 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("<qt>The option <b>%1</b> might conflict with " + "other options that you have already enabled.<br>" + "Are you sure that you really want to enable " + "<b>%2</b>?</qt>") + .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<char> 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; } } |