Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,376 @@
include(ECMPoQmTools)
add_library(KF6WidgetsAddons)
add_library(KF6::WidgetsAddons ALIAS KF6WidgetsAddons)
set_target_properties(KF6WidgetsAddons PROPERTIES
VERSION ${KWIDGETSADDONS_VERSION}
SOVERSION ${KWIDGETSADDONS_SOVERSION}
EXPORT_NAME WidgetsAddons
)
ecm_create_qm_loader(KF6WidgetsAddons kwidgetsaddons6_qt)
target_sources(KF6WidgetsAddons PRIVATE
common_helpers.cpp
common_helpers_p.h
fonthelpers_p.h
kacceleratormanager.cpp
kacceleratormanager.h
kacceleratormanager_p.h
kactionmenu.cpp
kactionmenu.h
kactionselector.cpp
kactionselector.h
kadjustingscrollarea.cpp
kadjustingscrollarea.h
kanimatedbutton.cpp
kanimatedbutton.h
kassistantdialog.cpp
kassistantdialog.h
kbusyindicatorwidget.cpp
kbusyindicatorwidget.h
kcapacitybar.cpp
kcapacitybar.h
kcharselect.cpp
kcharselectdata.cpp
kcharselectdata_p.h
kcharselect.h
kcharselect_p.h
kcollapsiblegroupbox.cpp
kcollapsiblegroupbox.h
kcolorbutton.cpp
kcolorbutton.h
kcolorcombo.cpp
kcolorcombo.h
kcolumnresizer.cpp
kcolumnresizer.h
kcontextualhelpbutton.cpp
kcontextualhelpbutton.h
kcursor.cpp
kcursor.h
kcursor_p.h
kdatecombobox.cpp
kdatecombobox.h
kdatepicker.cpp
kdatepicker.h
kdatepicker_p.h
kdatepickerpopup.cpp
kdatepickerpopup.h
kdaterangecontrol.cpp
kdaterangecontrol_p.h
kdatetable.cpp
kdatetable_p.h
kdatetimeedit.cpp
kdatetimeedit.h
kdragwidgetdecorator.cpp
kdragwidgetdecorator.h
kdualaction.cpp
kdualaction.h
kdualaction_p.h
keditlistwidget.cpp
keditlistwidget.h
kfontaction.cpp
kfontaction.h
kfontchooser.cpp
kfontchooserdialog.cpp
kfontchooserdialog.h
kfontchooser.h
kfontrequester.cpp
kfontrequester.h
kfontsizeaction.cpp
kfontsizeaction.h
kguiitem.cpp
kguiitem.h
kled.cpp
kled.h
klineediteventhandler.h
klineediteventhandler.cpp
kmessagebox.cpp
kmessageboxdontaskagaininterface.h
kmessagebox.h
kmessageboxnotifyinterface.h
kmessagebox_p.cpp
kmessagebox_p.h
kmessagedialog.cpp
kmessagedialog.h
kmessagewidget.cpp
kmessagewidget.h
kmimetypechooser.cpp
kmimetypechooser.h
kmimetypeeditor.cpp
kmimetypeeditor.h
kmultitabbar.cpp
kmultitabbar.h
kmultitabbar_p.h
knewpassworddialog.cpp
knewpassworddialog.h
knewpasswordwidget.cpp
knewpasswordwidget.h
kpagedialog.cpp
kpagedialog.h
kpagedialog_p.h
kpagemodel.cpp
kpagemodel.h
kpagemodel_p.h
kpageview.cpp
kpageview.h
kpageview_p.cpp
kpageview_p.h
kpagewidget.cpp
kpagewidget.h
kpagewidgetmodel.cpp
kpagewidgetmodel.h
kpagewidgetmodel_p.h
kpagewidget_p.h
kpassword.h
kpassworddialog.cpp
kpassworddialog.h
kpasswordlineedit.cpp
kpasswordlineedit.h
kpixmapregionselectordialog.cpp
kpixmapregionselectordialog.h
kpixmapregionselectorwidget.cpp
kpixmapregionselectorwidget.h
kpixmapsequence.cpp
kpixmapsequence.h
kpixmapsequenceoverlaypainter.cpp
kpixmapsequenceoverlaypainter.h
kpixmapsequencewidget.cpp
kpixmapsequencewidget.h
kpopupframe.cpp
kpopupframe.h
kratingpainter.cpp
kratingpainter.h
kratingwidget.cpp
kratingwidget.h
krecentfilesmenu.cpp
krecentfilesmenu.h
kruler.cpp
kruler.h
kselectaction.cpp
kselectaction.h
kselectaction_p.h
kselector.cpp
kselector.h
kseparator.cpp
kseparator.h
ksplittercollapserbutton.cpp
ksplittercollapserbutton.h
ksqueezedtextlabel.cpp
ksqueezedtextlabel.h
kstandardguiitem.cpp
kstandardguiitem.h
kstyleextensions.cpp
kstyleextensions.h
ktimecombobox.cpp
ktimecombobox.h
ktitlewidget.cpp
ktitlewidget.h
ktoggleaction.cpp
ktoggleaction.h
ktoggleaction_p.h
ktogglefullscreenaction.cpp
ktogglefullscreenaction.h
ktoolbarlabelaction.cpp
ktoolbarlabelaction.h
ktoolbarpopupaction.cpp
ktoolbarpopupaction.h
ktoolbarspaceraction.cpp
ktoolbarspaceraction.h
ktooltipwidget.cpp
ktooltipwidget.h
ktwofingerswipe.cpp
ktwofingerswipe.h
ktwofingertap.cpp
ktwofingertap.h
kurllabel.cpp # Not good enough quality. Needs to use QUrl instead of QString and should not inherit QLabel, but hold it as a member instead.
kurllabel.h
kviewstatemaintainerbase.cpp
kviewstatemaintainerbase.h
kviewstateserializer.cpp
kviewstateserializer.h
kxyselector.cpp
kxyselector.h
klineediturldropeventfilter.cpp
klineediturldropeventfilter.h
kjobwidgets.cpp
kjobwidgets.h
icons.qrc
)
set(kwidgetsaddons_UI_SRCS)
qt_wrap_ui(kwidgetsaddons_UI_SRCS
kdatetimeedit.ui
knewpassworddialog.ui
knewpasswordwidget.ui
kpassworddialog.ui
kfontchooserwidget.ui
)
target_sources(KF6WidgetsAddons PRIVATE ${kwidgetsaddons_UI_SRCS})
ecm_qt_declare_logging_category(KF6WidgetsAddons
HEADER loggingcategory.h
IDENTIFIER KWidgetsAddonsLog
CATEGORY_NAME kf.kwidgetsaddons
DEFAULT_SEVERITY Warning
DESCRIPTION "KWidgetsAddons"
EXPORT KWIDGETSADDONS
)
ecm_generate_export_header(KF6WidgetsAddons
BASE_NAME KWidgetsAddons
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 6.5
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
target_link_libraries(KF6WidgetsAddons PUBLIC Qt6::Widgets)
target_include_directories(KF6WidgetsAddons INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KWidgetsAddons>")
ecm_generate_headers(KWidgetsAddons_HEADERS
HEADER_NAMES
KAcceleratorManager
KAnimatedButton
KAdjustingScrollArea
KBusyIndicatorWidget
KCharSelect
KCollapsibleGroupBox
KColorButton
KColumnResizer
KContextualHelpButton
KDateComboBox
KDatePicker
KDatePickerPopup
KDateTimeEdit
KDragWidgetDecorator
KDualAction
KFontAction
KFontChooser
KFontChooserDialog
KFontSizeAction
KGuiItem
KLed
KMessageBox
KMessageBoxDontAskAgainInterface
KMultiTabBar,KMultiTabBarButton,KMultiTabBarTab
KNewPasswordWidget
KPopupFrame
KSelectAction
KStandardGuiItem
KTimeComboBox
KUrlLabel
KCapacityBar
KFontRequester
KPasswordDialog
KRuler
KRecentFilesMenu
KSelector,KGradientSelector
KTitleWidget
KXYSelector
KSeparator
KSqueezedTextLabel
KToggleAction
KToggleFullScreenAction
KViewStateSerializer
KViewStateMaintainerBase
KEditListWidget
KCursor
KRatingPainter
KRatingWidget
KActionSelector
KColorCombo
KActionMenu
KToolBarLabelAction
KToolBarPopupAction
KToolBarSpacerAction
KPageDialog
KPageModel
KPageView
KPageWidget
KPageWidgetModel,KPageWidgetItem
KAssistantDialog
KMessageWidget
KMessageDialog
KNewPasswordDialog
KLineEditUrlDropEventFilter
KLineEditEventHandler
KPassword
KPasswordLineEdit
KPixmapSequence
KPixmapSequenceOverlayPainter
KPixmapSequenceWidget
KPixmapRegionSelectorDialog
KPixmapRegionSelectorWidget
KMimeTypeChooser,KMimeTypeChooserDialog
KMimeTypeEditor
KMessageBoxNotifyInterface
KSplitterCollapserButton
KStyleExtensions
KToolTipWidget
KTwoFingerTap
KTwoFingerSwipe
KJobWidgets
REQUIRED_HEADERS KWidgetsAddons_HEADERS
)
install(TARGETS KF6WidgetsAddons EXPORT KF6WidgetsAddonsTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
${KWidgetsAddons_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/kwidgetsaddons_export.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KWidgetsAddons COMPONENT Devel
)
qt6_add_resources(KF6WidgetsAddons "kcharselect-data"
PREFIX "/kf6/kcharselect/"
FILES kcharselect-data
OUTPUT_TARGETS _rcc_target
)
install(TARGETS ${_rcc_target} EXPORT KF6WidgetsAddonsTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
if (BUILD_QCH)
ecm_add_qch(
KF6WidgetsAddons_QCH
NAME KWidgetsAddons
BASE_NAME KF6WidgetsAddons
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${KWidgetsAddons_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
LINK_QCHS
Qt6Core_QCH
Qt6Gui_QCH
Qt6Widgets_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KWIDGETSADDONS_EXPORT
KWIDGETSADDONS_DEPRECATED_EXPORT
KWIDGETSADDONS_DEPRECATED
"KWIDGETSADDONS_DEPRECATED_VERSION(x, y, t)"
"KWIDGETSADDONS_DEPRECATED_VERSION_BELATED(x, y, tx, ty, t)"
"KWIDGETSADDONS_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
"KWIDGETSADDONS_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, tx, ty, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
if(BUILD_DESIGNERPLUGIN)
add_subdirectory(designer)
endif()
ecm_qt_install_logging_categories(
EXPORT KWIDGETSADDONS
FILE kwidgetsaddons.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
@@ -0,0 +1,6 @@
#!/bin/sh
# Extract strings from all source files.
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
# lconvert.
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kwidgetsaddons6_qt.pot
@@ -0,0 +1,106 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2008 Chusslove Illich <caslav.ilic@gmx.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <common_helpers_p.h>
// If pos points to alphanumeric X in "...(X)...", which is preceded or
// followed only by non-alphanumerics, then "(X)" gets removed.
static QString removeReducedCJKAccMark(const QString &label, int pos)
{
if (pos > 0 && pos + 1 < label.length() //
&& label[pos - 1] == QLatin1Char('(') //
&& label[pos + 1] == QLatin1Char(')') //
&& label[pos].isLetterOrNumber()) {
// Check if at start or end, ignoring non-alphanumerics.
int len = label.length();
int p1 = pos - 2;
while (p1 >= 0 && !label[p1].isLetterOrNumber()) {
--p1;
}
++p1;
int p2 = pos + 2;
while (p2 < len && !label[p2].isLetterOrNumber()) {
++p2;
}
--p2;
const QStringView sview{label};
if (p1 == 0) {
return sview.left(pos - 1) + sview.mid(p2 + 1);
} else if (p2 + 1 == len) {
return sview.left(p1) + sview.mid(pos + 2);
}
}
return label;
}
QString removeAcceleratorMarker(const QString &label_)
{
QString label = label_;
int p = 0;
bool accmarkRemoved = false;
while (true) {
p = label.indexOf(QLatin1Char('&'), p);
if (p < 0 || p + 1 == label.length()) {
break;
}
if (label.at(p + 1).isLetterOrNumber()) {
// Valid accelerator.
label.remove(p, 1);
// May have been an accelerator in CJK-style "(&X)"
// at the start or end of text.
label = removeReducedCJKAccMark(label, p);
accmarkRemoved = true;
} else if (label.at(p + 1) == QLatin1Char('&')) {
// Escaped accelerator marker.
label.remove(p, 1);
}
++p;
}
// If no marker was removed, and there are CJK characters in the label,
// also try to remove reduced CJK marker -- something may have removed
// ampersand beforehand.
if (!accmarkRemoved) {
bool hasCJK = false;
for (const QChar c : std::as_const(label)) {
if (c.unicode() >= 0x2e00) { // rough, but should be sufficient
hasCJK = true;
break;
}
}
if (hasCJK) {
p = 0;
while (true) {
p = label.indexOf(QLatin1Char('('), p);
if (p < 0) {
break;
}
label = removeReducedCJKAccMark(label, p + 1);
++p;
}
}
}
return label;
}
QString dateFormatWith4DigitYear(const QLocale &locale, QLocale::FormatType format)
{
// Clearly a workaround for QLocale using "yy" way too often for years
// and so you get no way to distinguish between 1913 and 2013 anymore...
// bummer.
QString res = locale.dateFormat(format);
res.replace(QLatin1String("yy"), QLatin1String("yyyy"));
res.replace(QLatin1String("yyyyyyyy"), QLatin1String("yyyy"));
return res;
}
@@ -0,0 +1,54 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2008 Chusslove Illich <caslav.ilic@gmx.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef COMMON_HELPERS_P_H
#define COMMON_HELPERS_P_H
#include <QLocale>
#include <QString>
// Standalone (pure Qt) functionality needed internally in more than
// one source file on localization.
/**
* @internal
*
* Removes accelerator marker from a UI text label.
*
* Accelerator marker is not always a plain ampersand (&),
* so it is not enough to just remove it by @c QString::remove().
* The label may contain escaped markers ("&&") which must be resolved
* and skipped, as well as CJK-style markers ("Foo (&F)") where
* the whole parenthesis construct should be removed.
* Therefore always use this function to remove accelerator marker
* from UI labels.
*
* @param label UI label which may contain an accelerator marker
* @return label without the accelerator marker
*/
QString removeAcceleratorMarker(const QString &label);
/**
* @internal
*
* Returns the date format used for the locale with the year as a four digit
* number.
*
* If the date format used for the locale already has a 4-digit year, then
* this is the same as locale.dateFormat(format). Otherwise, it's the result
* of QLocale::dateFormat with "yy" replaced with "yyyy".
* In particular when using the short format for date inputs you may want to
* use the short format with 4-digit year because Qt parses current 2-digit year
* values like 22 as 1922.
*
* @param locale The locale that you want the date format for.
* @param format The date format that you want.
* @return the date format string with 4-digit year
*/
QString dateFormatWith4DigitYear(const QLocale &locale, QLocale::FormatType format);
#endif
@@ -0,0 +1,208 @@
include(ECMAddQtDesignerPlugin)
ecm_qtdesignerplugin_widget(KActionSelector
TOOLTIP "A widget for selecting and arranging actions/objects"
GROUP "Views (KF6)"
)
ecm_qtdesignerplugin_widget(KAnimatedButton
TOOLTIP "An extended version of QToolButton which can display an animated icon."
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KBusyIndicatorWidget
TOOLTIP "Busy Indicator (KF6)"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KCapacityBar
TOOLTIP "Capacity Bar (KF6)"
CONSTRUCTOR_ARGS_CODE "(KCapacityBar::DrawTextOutline, parent)"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KCharSelect
TOOLTIP "Character Selection Widget (KF6)"
WHATSTHIS "A widget that allows selecting a character out of a table"
GROUP "Graphics (KF6)"
)
ecm_qtdesignerplugin_widget(KCollapsibleGroupBox
TOOLTIP "A group box which can be expanded and collapsed"
GROUP "Display (KF6)"
CONTAINER
)
ecm_qtdesignerplugin_widget(KColorButton
TOOLTIP "Color Chooser Button (KF6)"
WHATSTHIS "A button that allows selecting a color"
GROUP "Graphics (KF6)"
)
ecm_qtdesignerplugin_widget(KColorCombo
TOOLTIP "Color Combo Box (KF6)"
GROUP "Graphics (KF6)"
)
ecm_qtdesignerplugin_widget(KContextualHelpButton
TOOLTIP "A button for showing helpful texts (KF6)"
WHATSTHIS "An icon-only button for showing contextually relevant help or explanations"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KDatePicker
TOOLTIP "A date selection widget (KF6)"
WHATSTHIS "Provides a widget for calendar date input"
GROUP "Date/Time (KF6)"
)
ecm_qtdesignerplugin_widget(KDateComboBox
TOOLTIP "This widget can be used to display or edit the date. (KF6)"
GROUP "Date/Time (KF6)"
)
ecm_qtdesignerplugin_widget(KDateTimeEdit
TOOLTIP "This widget can be used to display or edit the date and time. (KF6)"
GROUP "Date/Time (KF6)"
)
ecm_qtdesignerplugin_widget(KEditListWidget
TOOLTIP "Fullfeatured edit box with buttons (KF6)"
GROUP "Views (KF6)"
)
ecm_qtdesignerplugin_widget(KFontChooser
ICON pics/kfontcombo.png
TOOLTIP "Font Chooser (KF6)"
WHATSTHIS "A font type, size and style selection widget complete with preview"
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KFontRequester
TOOLTIP "Font Requester (KF6)"
WHATSTHIS "A compact font selection and preview widget"
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KGradientSelector
INCLUDE_FILE kselector.h
TOOLTIP "Gradient Color Selector (KF6)"
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KLed
TOOLTIP "LED Widget (KF6)"
WHATSTHIS "A widget showing a light emitter diode"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KMessageWidget
TOOLTIP "Display an information or error message in an animated bubble."
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KMimeTypeChooser
TOOLTIP "Provides a checkable list of all available MIME types."
CONSTRUCTOR_ARGS_CODE "(QString(), QStringList(), QString(), QStringList(), KMimeTypeChooser::Comments | KMimeTypeChooser::Patterns, parent)"
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KMultiTabBar
TOOLTIP "Multi Tab Bar Widget (KF6)"
WHATSTHIS "A Widget for horizontal and vertical tabs."
CONSTRUCTOR_ARGS_CODE "(KMultiTabBar::Left, parent)"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KNewPasswordWidget
TOOLTIP "A password input widget"
WHATSTHIS "This widget allows the user to enter a new password."
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KPageView
TOOLTIP "Paged View (KF6)"
WHATSTHIS "A base class which can handle multiple pages."
GROUP "Views (KF6)"
)
ecm_qtdesignerplugin_widget(KPageWidget
TOOLTIP "Paged Widget (KF6)"
WHATSTHIS "Page widget with many layouts (faces)."
GROUP "Views (KF6)"
)
ecm_qtdesignerplugin_widget(KPasswordLineEdit
TOOLTIP "A Password LineEdit (KF6)"
WHATSTHIS "A Custom LineEdit which allows to show password"
GROUP "Input (KF6)"
)
ecm_qtdesignerplugin_widget(KPixmapRegionSelectorWidget
TOOLTIP "Pixmap Region Selector (KF6)"
GROUP "Graphics (KF6)"
)
ecm_qtdesignerplugin_widget(KPixmapSequenceWidget
TOOLTIP "Pixmap Sequence Widget (KF6)"
WHATSTHIS "A simple widget showing a fixed size pixmap sequence."
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KPopupFrame
TOOLTIP "Frame with Popup Menu Behavior (KF6)"
CONTAINER
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KRatingWidget
TOOLTIP "Rating value display (KF6)"
WHATSTHIS "Displays a rating as a range of stars or other arbitrary pixmaps."
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KRuler
TOOLTIP "Measuring Ruler Widget (KF6)"
WHATSTHIS "A measuring ruler widget as seen in KWord for page widths and heights"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KSeparator
TOOLTIP "Standard horizontal or vertical separator."
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KSqueezedTextLabel
TOOLTIP "A QLabel that squeezes its text (KF6)"
WHATSTHIS "If the text is too long to fit into the label it is divided into remaining left and right parts which are separated by three dots"
CONSTRUCTOR_ARGS_CODE "(QStringLiteral(\"KSqueezedTextLabel\"), parent)"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KTimeComboBox
TOOLTIP "This widget can be used to display or edit the time. (KF6)"
GROUP "Date/Time (KF6)"
)
ecm_qtdesignerplugin_widget(KTitleWidget
TOOLTIP "Title box with label and icon"
WHATSTHIS "A styled frame to be used in title positions in dialogs and other widgets"
GROUP "Display (KF6)"
)
ecm_qtdesignerplugin_widget(KUrlLabel
TOOLTIP "URL Label (KF6)"
CONSTRUCTOR_ARGS_CODE "(QStringLiteral(\"KUrlLabel\"), QString(), parent)"
GROUP "Display (KF6)"
)
ecm_add_qtdesignerplugin(kwidgetsaddonswidgets
NAME KWidgetsAddonsWidgets
OUTPUT_NAME kwidgetsaddons6widgets
WIDGETS
KActionSelector
KAnimatedButton
KBusyIndicatorWidget
KCapacityBar
KCharSelect
KCollapsibleGroupBox
KColorButton
KColorCombo
KContextualHelpButton
KDateComboBox
KDatePicker
KDateTimeEdit
KEditListWidget
KFontChooser
KFontRequester
KGradientSelector
KLed
KMessageWidget
KMimeTypeChooser
KMultiTabBar
KNewPasswordWidget
KPageView
KPageWidget
KPasswordLineEdit
KPixmapRegionSelectorWidget
KPixmapSequenceWidget
KPopupFrame
KRatingWidget
KRuler
KSeparator
KSqueezedTextLabel
KTimeComboBox
KTitleWidget
KUrlLabel
LINK_LIBRARIES
KF6::WidgetsAddons
INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer"
COMPONENT Devel
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

@@ -0,0 +1,128 @@
/*
SPDX-FileCopyrightText: 2008 Chusslove Illich <caslav.ilic@gmx.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef FONTHELPERS_P_H
#define FONTHELPERS_P_H
// i18n-related helpers for fonts, common to KFont* widgets.
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <map>
#ifdef NEVERDEFINE // never true
// Font names up for translation, listed for extraction.
//: Generic sans serif font presented in font choosers. When selected,
//: the system will choose a real font, mandated by distro settings.
QT_TRANSLATE_NOOP3("FontHelpers", "Sans Serif", "@item Font name");
//: Generic serif font presented in font choosers. When selected,
//: the system will choose a real font, mandated by distro settings.
QT_TRANSLATE_NOOP3("FontHelpers", "Serif", "@item Font name");
//: Generic monospace font presented in font choosers. When selected,
//: the system will choose a real font, mandated by distro settings.
QT_TRANSLATE_NOOP3("FontHelpers", "Monospace", "@item Font name");
#endif
/**
* @internal
*
* Split the compound raw font name into family and foundry.
*
* @param name the raw font name reported by Qt
* @param family the storage for family name
* @param foundry the storage for foundry name
*/
inline void splitFontString(QStringView name, QString *family, QString *foundry = nullptr)
{
int p1 = name.indexOf(QLatin1Char('['));
if (p1 < 0) {
if (family) {
*family = name.trimmed().toString();
}
if (foundry) {
foundry->clear();
}
} else {
int p2 = name.indexOf(QLatin1Char(']'), p1);
p2 = p2 > p1 ? p2 : name.length();
if (family) {
*family = name.left(p1).trimmed().toString();
}
if (foundry) {
*foundry = name.mid(p1 + 1, p2 - p1 - 1).trimmed().toString();
}
}
}
/**
* @internal
*
* Translate the font name for the user.
* Primarily for generic fonts like Serif, Sans-Serif, etc.
*
* @param name the raw font name reported by Qt
* @return translated font name
*/
inline QString translateFontName(QStringView name)
{
QString family;
QString foundry;
splitFontString(name, &family, &foundry);
// Obtain any regular translations for the family and foundry.
QString trFamily = QCoreApplication::translate("FontHelpers", family.toUtf8().constData(), "@item Font name");
QString trFoundry = foundry;
if (!foundry.isEmpty()) {
trFoundry = QCoreApplication::translate("FontHelpers", foundry.toUtf8().constData(), "@item Font foundry");
}
// Assemble full translation.
QString trfont;
if (foundry.isEmpty()) {
// i18n: Filter by which the translators can translate, or otherwise
// operate on the font names not put up for regular translation.
trfont = QCoreApplication::translate("FontHelpers", "%1", "@item Font name").arg(trFamily);
} else {
// i18n: Filter by which the translators can translate, or otherwise
// operate on the font names not put up for regular translation.
trfont = QCoreApplication::translate("FontHelpers", "%1 [%2]", "@item Font name [foundry]").arg(trFamily, trFoundry);
}
return trfont;
}
static bool fontFamilyCompare(const QString &a, const QString &b)
{
return QString::localeAwareCompare(a, b) < 0;
}
using FontFamiliesMap = std::map<QString, QString, decltype(fontFamilyCompare) *>;
/**
* @internal
*
* Compose locale-aware sorted list of translated font names,
* with generic fonts handled in a special way.
* The mapping of translated to raw names can be reported too if required.
*
* @param names raw font names as reported by Qt
* @param trToRawNames storage for mapping of translated to raw names
* @return sorted list of translated font names
*/
inline FontFamiliesMap translateFontNameList(const QStringList &names)
{
FontFamiliesMap trMap(fontFamilyCompare);
for (const QString &fName : names) {
trMap.insert({translateFontName(fName), fName});
}
return trMap;
}
#endif
@@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>icons/visibility.svg</file>
<file>icons/hint.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg3049"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="hint.svg"
inkscape:export-filename="/home/uri/.kde/share/icons/NITRUX-KDE/16x16/actions/view-right-new.png"
inkscape:export-xdpi="30"
inkscape:export-ydpi="30">
<defs
id="defs3051">
<linearGradient
gradientTransform="translate(0,-1.9999941)"
inkscape:collect="always"
xlink:href="#linearGradient4021-3"
id="linearGradient4123"
gradientUnits="userSpaceOnUse"
x1="3"
y1="1045.3622"
x2="15"
y2="1045.3622" />
<linearGradient
id="linearGradient4021-3">
<stop
id="stop4023-0"
offset="0"
style="stop-color:#b3b3b3;stop-opacity:1;" />
<stop
id="stop4025-2"
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1;" />
</linearGradient>
<clipPath
id="clipPath4160"
clipPathUnits="userSpaceOnUse">
<rect
y="1023.3622"
x="7"
height="1"
width="1"
id="rect4162"
style="opacity:1;fill:#f2f2f2;fill-opacity:1;stroke:none;stroke-opacity:1" />
</clipPath>
<clipPath
id="clipPath4160-6"
clipPathUnits="userSpaceOnUse">
<rect
y="1023.3622"
x="7"
height="1"
width="1"
id="rect4162-4"
style="opacity:1;fill:#f2f2f2;fill-opacity:1;stroke:none;stroke-opacity:1" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="45.254834"
inkscape:cx="4.5612296"
inkscape:cy="8.1291941"
inkscape:document-units="px"
inkscape:current-layer="layer1-6"
showgrid="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1016"
inkscape:window-x="-4"
inkscape:window-y="35"
inkscape:window-maximized="1"
inkscape:showpageshadow="false"
borderlayer="true"
inkscape:snap-bbox="true"
inkscape:snap-global="true"
inkscape:object-nodes="true">
<inkscape:grid
type="xygrid"
id="grid4085" />
<sodipodi:guide
position="2.0000044,14.00003"
orientation="12,0"
id="guide4075" />
<sodipodi:guide
position="2.0000044,2.0000296"
orientation="0,12"
id="guide4077" />
<sodipodi:guide
position="14,9"
orientation="-12,0"
id="guide4079" />
<sodipodi:guide
position="14.000004,14.00003"
orientation="0,-12"
id="guide4081" />
</sodipodi:namedview>
<metadata
id="metadata3054">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-421.71429,-531.79074)">
<g
transform="matrix(0.75,0,0,0.74999813,421.46429,-241.22897)"
id="layer1-0"
inkscape:label="Capa 1">
<g
transform="translate(6.2937501e-7,-2.041003e-4)"
id="layer1-6"
inkscape:label="Capa 1">
<path
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none"
d="M 13.314453 2 L 2 13.294922 L 2.7148438 14 L 14 2.6972656 L 13.314453 2 z M 8 3 A 8.999992 9.0000024 0 0 0 0.12304688 7.6679688 C 0.25199189 8.0317035 0.48048562 8.3445563 0.77929688 8.5761719 A 7.999993 8.0000022 0 0 1 8 4 A 3.9999995 4.0000005 0 0 0 4 8 A 3.9999995 4.0000005 0 0 0 4.1054688 8.8945312 L 5 8 A 2.9999995 3.0000003 0 0 1 8 5 L 8.8925781 4.1074219 A 3.9999995 4.0000005 0 0 0 8.3496094 4.0175781 A 7.999993 8.0000022 0 0 1 8.9277344 4.0722656 L 9.8066406 3.1933594 A 8.999992 9.0000024 0 0 0 8 3 z M 13.835938 5.1640625 L 13.121094 5.8789062 A 7.999993 8.0000022 0 0 1 15.220703 8.5761719 C 15.522218 8.3424607 15.752612 8.0261216 15.880859 7.6582031 A 8.999992 9.0000024 0 0 0 13.835938 5.1640625 z M 11.894531 7.1054688 L 11 8 A 2.9999995 3.0000003 0 0 1 8 11 L 7.1074219 11.892578 A 3.9999995 4.0000005 0 0 0 8 12 A 3.9999995 4.0000005 0 0 0 12 8 A 3.9999995 4.0000005 0 0 0 11.894531 7.1054688 z "
transform="matrix(1.3333333,0,0,1.3333367,0.3333327,1030.6957)"
id="rect4170" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg3049"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="visibility.svg"
inkscape:export-filename="/home/uri/.kde/share/icons/NITRUX-KDE/16x16/actions/view-right-new.png"
inkscape:export-xdpi="30"
inkscape:export-ydpi="30">
<defs
id="defs3051">
<linearGradient
gradientTransform="translate(0,-1.9999941)"
inkscape:collect="always"
xlink:href="#linearGradient4021-3"
id="linearGradient4123"
gradientUnits="userSpaceOnUse"
x1="3"
y1="1045.3622"
x2="15"
y2="1045.3622" />
<linearGradient
id="linearGradient4021-3">
<stop
id="stop4023-0"
offset="0"
style="stop-color:#b3b3b3;stop-opacity:1;" />
<stop
id="stop4025-2"
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1;" />
</linearGradient>
<clipPath
id="clipPath4160"
clipPathUnits="userSpaceOnUse">
<rect
y="1023.3622"
x="7"
height="1"
width="1"
id="rect4162"
style="opacity:1;fill:#f2f2f2;fill-opacity:1;stroke:none;stroke-opacity:1" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="45.254834"
inkscape:cx="10.140744"
inkscape:cy="6.8696601"
inkscape:document-units="px"
inkscape:current-layer="layer1-6"
showgrid="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1016"
inkscape:window-x="-4"
inkscape:window-y="35"
inkscape:window-maximized="1"
inkscape:showpageshadow="false"
borderlayer="true"
inkscape:snap-bbox="true"
inkscape:snap-global="true"
inkscape:object-nodes="true">
<inkscape:grid
type="xygrid"
id="grid4085" />
<sodipodi:guide
position="2.0000044,14.00003"
orientation="12,0"
id="guide4075" />
<sodipodi:guide
position="2.0000044,2.0000296"
orientation="0,12"
id="guide4077" />
<sodipodi:guide
position="14,9"
orientation="-12,0"
id="guide4079" />
<sodipodi:guide
position="14.000004,14.00003"
orientation="0,-12"
id="guide4081" />
</sodipodi:namedview>
<metadata
id="metadata3054">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-421.71429,-531.79074)">
<g
transform="matrix(0.75,0,0,0.74999813,421.46429,-241.22897)"
id="layer1-0"
inkscape:label="Capa 1">
<g
transform="translate(6.2937501e-7,-2.041003e-4)"
id="layer1-6"
inkscape:label="Capa 1">
<path
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none"
d="M 8 3 A 8.9999924 9.0000018 0 0 0 0.12304688 7.6679688 C 0.25199191 8.0317035 0.48048562 8.3445563 0.77929688 8.5761719 A 7.9999934 8.0000016 0 0 1 8 4 A 3.9999997 4.0000003 0 0 0 4 8 A 3.9999997 4.0000003 0 0 0 8 12 A 3.9999997 4.0000003 0 0 0 12 8 A 3.9999997 4.0000003 0 0 0 8.3496094 4.0175781 A 7.9999934 8.0000016 0 0 1 15.220703 8.5761719 C 15.522218 8.3424607 15.752612 8.0261216 15.880859 7.6582031 A 8.9999924 9.0000018 0 0 0 8 3 z M 8 5 A 2.9999997 3.0000001 0 0 1 11 8 A 2.9999997 3.0000001 0 0 1 8 11 A 2.9999997 3.0000001 0 0 1 5 8 A 2.9999997 3.0000001 0 0 1 8 5 z M 8 6 A 1.9999999 2.0000002 0 0 0 6 8 A 1.9999999 2.0000002 0 0 0 8 10 A 1.9999999 2.0000002 0 0 0 10 8 A 1.9999999 2.0000002 0 0 0 9.9101562 7.4121094 A 0.99999992 1 0 0 1 9 8 A 0.99999992 1 0 0 1 8 7 A 0.99999992 1 0 0 1 8.5898438 6.0898438 A 1.9999999 2.0000002 0 0 0 8 6 z "
transform="matrix(1.3333333,0,0,1.3333367,0.3333327,1030.6957)"
id="rect4170" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -0,0 +1,869 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2002 Matthias Hölzer-Klüpfel <mhk@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kacceleratormanager.h"
#include "kacceleratormanager_p.h"
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QDockWidget>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QList>
#include <QMainWindow>
#include <QMenuBar>
#include <QMetaProperty>
#include <QObject>
#include <QPushButton>
#include <QRadioButton>
#include <QStackedWidget>
#include <QTabBar>
#include <QTextEdit>
#include <QWidget>
#include "common_helpers_p.h"
#include "loggingcategory.h"
/*********************************************************************
class Item - helper class containing widget information
This class stores information about the widgets the need accelerators,
as well as about their relationship.
*********************************************************************/
bool KAcceleratorManagerPrivate::programmers_mode = false;
QString KAcceleratorManagerPrivate::changed_string;
QString KAcceleratorManagerPrivate::added_string;
QString KAcceleratorManagerPrivate::removed_string;
QMap<QWidget *, int> KAcceleratorManagerPrivate::ignored_widgets;
QStringList KAcceleratorManagerPrivate::standardNames;
void KAcceleratorManagerPrivate::addStandardActionNames(const QStringList &list)
{
standardNames.append(list);
}
bool KAcceleratorManagerPrivate::standardName(const QString &str)
{
return standardNames.contains(str);
}
KAcceleratorManagerPrivate::Item::~Item()
{
if (m_children) {
while (!m_children->isEmpty()) {
delete m_children->takeFirst();
}
delete m_children;
}
}
void KAcceleratorManagerPrivate::Item::addChild(Item *item)
{
if (!m_children) {
m_children = new ItemList;
}
m_children->append(item);
}
void KAcceleratorManagerPrivate::manage(QWidget *widget)
{
if (!widget) {
qCDebug(KWidgetsAddonsLog) << "null pointer given to manage";
return;
}
if (KAcceleratorManagerPrivate::ignored_widgets.contains(widget)) {
return;
}
if (qobject_cast<QMenu *>(widget)) {
// create a popup accel manager that can deal with dynamic menus
KPopupAccelManager::manage(static_cast<QMenu *>(widget));
return;
}
Item *root = new Item;
QString used;
manageWidget(widget, root, used);
calculateAccelerators(root, used);
delete root;
}
void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used)
{
if (!item->m_children) {
return;
}
// collect the contents
KAccelStringList contents;
contents.reserve(item->m_children->size());
for (Item *it : std::as_const(*item->m_children)) {
contents << it->m_content;
}
// find the right accelerators
KAccelManagerAlgorithm::findAccelerators(contents, used);
// write them back into the widgets
int cnt = -1;
for (Item *it : std::as_const(*item->m_children)) {
cnt++;
QDockWidget *dock = qobject_cast<QDockWidget *>(it->m_widget);
if (dock) {
if (checkChange(contents[cnt])) {
dock->setWindowTitle(contents[cnt].accelerated());
}
continue;
}
QTabBar *tabBar = qobject_cast<QTabBar *>(it->m_widget);
if (tabBar) {
if (checkChange(contents[cnt])) {
tabBar->setTabText(it->m_index, contents[cnt].accelerated());
}
continue;
}
QMenuBar *menuBar = qobject_cast<QMenuBar *>(it->m_widget);
if (menuBar) {
if (it->m_index >= 0) {
QAction *maction = menuBar->actions()[it->m_index];
if (maction) {
checkChange(contents[cnt]);
maction->setText(contents[cnt].accelerated());
}
continue;
}
}
// we possibly reserved an accel, but we won't set it as it looks silly
QGroupBox *groupBox = qobject_cast<QGroupBox *>(it->m_widget);
if (groupBox && !groupBox->isCheckable()) {
continue;
}
int tprop = it->m_widget->metaObject()->indexOfProperty("text");
if (tprop != -1) {
if (checkChange(contents[cnt])) {
it->m_widget->setProperty("text", contents[cnt].accelerated());
}
} else {
tprop = it->m_widget->metaObject()->indexOfProperty("title");
if (tprop != -1 && checkChange(contents[cnt])) {
it->m_widget->setProperty("title", contents[cnt].accelerated());
}
}
}
// calculate the accelerators for the children
for (Item *it : std::as_const(*item->m_children)) {
if (it->m_widget && it->m_widget->isVisibleTo(item->m_widget)) {
calculateAccelerators(it, used);
}
}
}
void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item, QString &used)
{
// we are only interested in direct child widgets
const QList<QWidget *> childList = widget->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
for (QWidget *w : childList) {
if (!w->isVisibleTo(widget) || (w->isWindow() && qobject_cast<QMenu *>(w) == nullptr)) {
continue;
}
if (KAcceleratorManagerPrivate::ignored_widgets.contains(w)) {
continue;
}
manageWidget(w, item, used);
}
}
void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item, QString &used)
{
// If the widget has any action whose shortcuts contain keystrokes in the
// form of Alt+X we need to mark X as used, otherwise we may assign it as accelerator
// and there will be a conflict when trying to use it
const QList<QAction *> widgetActions = w->actions();
for (QAction *action : widgetActions) {
const QList<QKeySequence> actionShortcuts = action->shortcuts();
for (const QKeySequence &sequence : actionShortcuts) {
const QString sequenceAsText = sequence.toString(QKeySequence::PortableText);
const QStringList splitSequence = sequenceAsText.split(QStringLiteral(", "));
for (const QString &shortcut : splitSequence) {
if (shortcut.length() == 5 && shortcut.startsWith(QStringLiteral("Alt+"))) {
used.append(shortcut.right(1));
}
}
}
}
// first treat the special cases
QTabBar *tabBar = qobject_cast<QTabBar *>(w);
if (tabBar) {
manageTabBar(tabBar, item);
return;
}
QStackedWidget *wds = qobject_cast<QStackedWidget *>(w);
if (wds) {
QWidgetStackAccelManager::manage(wds);
// return;
}
QDockWidget *dock = qobject_cast<QDockWidget *>(w);
if (dock) {
// QWidgetStackAccelManager::manage( wds );
manageDockWidget(dock, item);
}
QMenu *popupMenu = qobject_cast<QMenu *>(w);
if (popupMenu) {
// create a popup accel manager that can deal with dynamic menus
KPopupAccelManager::manage(popupMenu);
return;
}
QStackedWidget *wdst = qobject_cast<QStackedWidget *>(w);
if (wdst) {
QWidgetStackAccelManager::manage(wdst);
// return;
}
QMenuBar *menuBar = qobject_cast<QMenuBar *>(w);
if (menuBar) {
manageMenuBar(menuBar, item);
return;
}
if (qobject_cast<QComboBox *>(w) || qobject_cast<QLineEdit *>(w) //
|| w->inherits("Q3TextEdit") //
|| qobject_cast<QTextEdit *>(w) //
|| qobject_cast<QAbstractSpinBox *>(w) //
|| w->inherits("KMultiTabBar") //
|| w->inherits("qdesigner_internal::TextPropertyEditor")) {
return;
}
if (w->inherits("KUrlRequester")) {
traverseChildren(w, item, used);
return;
}
// now treat 'ordinary' widgets
QLabel *label = qobject_cast<QLabel *>(w);
if (label) {
if (!label->buddy()) {
return;
} else {
if (label->textFormat() == Qt::RichText //
|| (label->textFormat() == Qt::AutoText && Qt::mightBeRichText(label->text()))) {
return;
}
}
}
if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast<QGroupBox *>(w) || qobject_cast<QRadioButton *>(w)) {
QString content;
QVariant variant;
int tprop = w->metaObject()->indexOfProperty("text");
if (tprop != -1) {
QMetaProperty p = w->metaObject()->property(tprop);
if (p.isValid() && p.isWritable()) {
variant = p.read(w);
} else {
tprop = -1;
}
}
if (tprop == -1) {
tprop = w->metaObject()->indexOfProperty("title");
if (tprop != -1) {
QMetaProperty p = w->metaObject()->property(tprop);
if (p.isValid() && p.isWritable()) {
variant = p.read(w);
}
}
}
if (variant.isValid()) {
content = variant.toString();
}
if (!content.isEmpty()) {
Item *i = new Item;
i->m_widget = w;
// put some more weight on the usual action elements
int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
if (qobject_cast<QPushButton *>(w) || qobject_cast<QCheckBox *>(w) || qobject_cast<QRadioButton *>(w) || qobject_cast<QLabel *>(w)) {
weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
}
// don't put weight on non-checkable group boxes,
// as usually the contents are more important
QGroupBox *groupBox = qobject_cast<QGroupBox *>(w);
if (groupBox) {
if (groupBox->isCheckable()) {
weight = KAccelManagerAlgorithm::CHECKABLE_GROUP_BOX_WEIGHT;
} else {
weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
}
}
i->m_content = KAccelString(content, weight);
item->addChild(i);
}
}
traverseChildren(w, item, used);
}
void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
{
// ignore QTabBar for QDockWidgets, because QDockWidget on its title change
// also updates its tabbar entry, so on the next run of KCheckAccelerators
// this looks like a conflict and triggers a new reset of the shortcuts -> endless loop
QWidget *parentWidget = bar->parentWidget();
if (parentWidget) {
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget);
// TODO: find better hints that this is a QTabBar for QDockWidgets
if (mainWindow) { // && (mainWindow->layout()->indexOf(bar) != -1)) QMainWindowLayout lacks proper support
return;
}
}
for (int i = 0; i < bar->count(); i++) {
QString content = bar->tabText(i);
if (content.isEmpty()) {
continue;
}
Item *it = new Item;
item->addChild(it);
it->m_widget = bar;
it->m_index = i;
it->m_content = KAccelString(content);
}
}
void KAcceleratorManagerPrivate::manageDockWidget(QDockWidget *dock, Item *item)
{
// As of Qt 4.4.3 setting a shortcut to a QDockWidget has no effect,
// because a QDockWidget does not grab it, even while displaying an underscore
// in the title for the given shortcut letter.
// Still it is useful to set the shortcut, because if QDockWidgets are tabbed,
// the tab automatically gets the same text as the QDockWidget title, including the shortcut.
// And for the QTabBar the shortcut does work, it gets grabbed as usual.
// Having the QDockWidget without a shortcut and resetting the tab text with a title including
// the shortcut does not work, the tab text is instantly reverted to the QDockWidget title
// (see also manageTabBar()).
// All in all QDockWidgets and shortcuts are a little broken for now.
QString content = dock->windowTitle();
if (content.isEmpty()) {
return;
}
Item *it = new Item;
item->addChild(it);
it->m_widget = dock;
it->m_content = KAccelString(content, KAccelManagerAlgorithm::STANDARD_ACCEL);
}
void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
{
QAction *maction;
QString s;
for (int i = 0; i < mbar->actions().count(); ++i) {
maction = mbar->actions()[i];
if (!maction) {
continue;
}
// nothing to do for separators
if (maction->isSeparator()) {
continue;
}
s = maction->text();
if (!s.isEmpty()) {
Item *it = new Item;
item->addChild(it);
it->m_content = KAccelString(s,
KAccelManagerAlgorithm::MENU_TITLE_WEIGHT); // menu titles are important, so raise the weight
it->m_widget = mbar;
it->m_index = i;
}
// have a look at the popup as well, if present
if (maction->menu()) {
KPopupAccelManager::manage(maction->menu());
}
}
}
/*********************************************************************
class KAcceleratorManager - main entry point
This class is just here to provide a clean public API...
*********************************************************************/
void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
{
KAcceleratorManagerPrivate::changed_string.clear();
KAcceleratorManagerPrivate::added_string.clear();
KAcceleratorManagerPrivate::removed_string.clear();
KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
KAcceleratorManagerPrivate::manage(widget);
}
void KAcceleratorManager::last_manage(QString &added, QString &changed, QString &removed)
{
added = KAcceleratorManagerPrivate::added_string;
changed = KAcceleratorManagerPrivate::changed_string;
removed = KAcceleratorManagerPrivate::removed_string;
}
/*********************************************************************
class KAccelString - a string with weighted characters
*********************************************************************/
KAccelString::KAccelString(const QString &input, int initialWeight)
: m_pureText(input)
, m_weight()
{
m_orig_accel = m_pureText.indexOf(QLatin1String("(!)&"));
if (m_orig_accel != -1) {
m_pureText.remove(m_orig_accel, 4);
}
m_orig_accel = m_pureText.indexOf(QLatin1String("(&&)"));
if (m_orig_accel != -1) {
m_pureText.replace(m_orig_accel, 4, QStringLiteral("&"));
}
m_origText = m_pureText;
const int tabPos = m_pureText.indexOf(QLatin1Char('\t'));
if (tabPos != -1) {
m_pureText.truncate(tabPos);
}
m_orig_accel = m_accel = stripAccelerator(m_pureText);
if (initialWeight == -1) {
initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
}
calculateWeights(initialWeight);
// dump();
}
QString KAccelString::accelerated() const
{
QString result = m_origText;
if (result.isEmpty()) {
return result;
}
if (KAcceleratorManagerPrivate::programmers_mode) {
if (m_accel != m_orig_accel) {
int oa = m_orig_accel;
if (m_accel >= 0) {
result.insert(m_accel, QLatin1String("(!)&"));
if (m_accel < m_orig_accel) {
oa += 4;
}
}
if (m_orig_accel >= 0) {
result.replace(oa, 1, QStringLiteral("(&&)"));
}
}
} else {
if (m_accel >= 0 && m_orig_accel != m_accel) {
if (m_orig_accel != -1) {
result.remove(m_orig_accel, 1);
}
result.insert(m_accel, QLatin1Char('&'));
}
}
return result;
}
QChar KAccelString::accelerator() const
{
if ((m_accel < 0) || (m_accel > m_pureText.length())) {
return QChar();
}
return m_pureText[m_accel].toLower();
}
void KAccelString::calculateWeights(int initialWeight)
{
m_weight.resize(m_pureText.length());
int pos = 0;
bool start_character = true;
while (pos < m_pureText.length()) {
QChar c = m_pureText[pos];
int weight = initialWeight + 1;
// add special weight to first character
if (pos == 0) {
weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
}
// add weight to word beginnings
if (start_character) {
weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
start_character = false;
}
// add decreasing weight to left characters
if (pos < 50) {
weight += (50 - pos);
}
// try to preserve the wanted accelerators
if (pos == accel()) {
weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
// qCDebug(KWidgetsAddonsLog) << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText);
if (KAcceleratorManagerPrivate::standardName(m_origText)) {
weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
}
}
// skip non typeable characters
if (!c.isLetterOrNumber()) {
weight = 0;
start_character = true;
}
m_weight[pos] = weight;
++pos;
}
}
int KAccelString::stripAccelerator(QString &text)
{
// Note: this code is derived from QAccel::shortcutKey
int p = 0;
while (p >= 0) {
p = text.indexOf(QLatin1Char('&'), p) + 1;
if (p <= 0 || p >= text.length()) {
break;
}
if (text[p] != QLatin1Char('&')) {
QChar c = text[p];
if (c.isPrint()) {
text.remove(p - 1, 1);
return p - 1;
}
}
p++;
}
return -1;
}
int KAccelString::maxWeight(int &index, const QString &used) const
{
int max = 0;
index = -1;
for (int pos = 0; pos < m_pureText.length(); ++pos) {
if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0) {
if (m_weight[pos] > max) {
max = m_weight[pos];
index = pos;
}
}
}
return max;
}
void KAccelString::dump()
{
QString s;
for (int i = 0; i < m_weight.count(); ++i) {
s += QStringLiteral("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
}
qCDebug(KWidgetsAddonsLog) << "s " << s;
}
/*********************************************************************
findAccelerators - the algorithm determining the new accelerators
The algorithm is very crude:
* each character in each widget text is assigned a weight
* the character with the highest weight over all is picked
* that widget is removed from the list
* the weights are recalculated
* the process is repeated until no more accelerators can be found
The algorithm has some advantages:
* it favors 'nice' accelerators (first characters in a word, etc.)
* it is quite fast, O(N²)
* it is easy to understand :-)
The disadvantages:
* it does not try to find as many accelerators as possible
TODO:
* The result is always correct, but not necessarily optimal. Perhaps
it would be a good idea to add another algorithm with higher complexity
that gets used when this one fails, i.e. leaves widgets without
accelerators.
* The weights probably need some tweaking so they make more sense.
*********************************************************************/
void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
{
KAccelStringList accel_strings = result;
// initially remove all accelerators
for (KAccelStringList::Iterator it = result.begin(), total = result.end(); it != total; ++it) {
(*it).setAccel(-1);
}
// pick the highest bids
for (int cnt = 0; cnt < accel_strings.count(); ++cnt) {
int max = 0;
int index = -1;
int accel = -1;
// find maximum weight
for (int i = 0; i < accel_strings.count(); ++i) {
int a;
int m = accel_strings[i].maxWeight(a, used);
if (m > max) {
max = m;
index = i;
accel = a;
}
}
// stop if no more accelerators can be found
if (index < 0) {
return;
}
// insert the accelerator
if (accel >= 0) {
result[index].setAccel(accel);
used.append(result[index].accelerator());
}
// make sure we don't visit this one again
accel_strings[index] = KAccelString();
}
}
/*********************************************************************
class KPopupAccelManager - managing QMenu widgets dynamically
*********************************************************************/
KPopupAccelManager::KPopupAccelManager(QMenu *popup)
: QObject(popup)
, m_popup(popup)
, m_count(-1)
{
aboutToShow(); // do one check and then connect to show
connect(popup, &QMenu::aboutToShow, this, &KPopupAccelManager::aboutToShow);
}
void KPopupAccelManager::aboutToShow()
{
// Note: we try to be smart and avoid recalculating the accelerators
// whenever possible. Unfortunately, there is no way to know if an
// item has been added or removed, so we can not do much more than
// to compare the items each time the menu is shown :-(
if (m_count != m_popup->actions().count()) {
findMenuEntries(m_entries);
calculateAccelerators();
m_count = m_popup->actions().count();
} else {
KAccelStringList entries;
findMenuEntries(entries);
if (entries != m_entries) {
m_entries = entries;
calculateAccelerators();
}
}
}
void KPopupAccelManager::calculateAccelerators()
{
// find the new accelerators
QString used;
KAccelManagerAlgorithm::findAccelerators(m_entries, used);
// change the menu entries
setMenuEntries(m_entries);
}
void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
{
QString s;
list.clear();
// read out the menu entries
const auto menuActions = m_popup->actions();
for (QAction *maction : menuActions) {
if (maction->isSeparator()) {
continue;
}
s = maction->text();
// in full menus, look at entries with global accelerators last
int weight = 50;
if (s.contains(QLatin1Char('\t'))) {
weight = 0;
}
list.append(KAccelString(s, weight));
// have a look at the popup as well, if present
if (maction->menu()) {
KPopupAccelManager::manage(maction->menu());
}
}
}
// Duplicated from qaction.cpp
static QString copy_of_qt_strippedText(QString s)
{
s.remove(QLatin1String("..."));
for (int i = 0; i < s.size(); ++i) {
if (s.at(i) == QLatin1Char('&')) {
s.remove(i, 1);
}
}
return s.trimmed();
}
void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
{
uint cnt = 0;
const auto menuActions = m_popup->actions();
for (QAction *maction : menuActions) {
if (maction->isSeparator()) {
continue;
}
QString iconText = maction->iconText();
const QString oldText = maction->text();
// Check if iconText was generated by Qt. In that case ignore it (no support for CJK accelerators) and set it from the text.
if (iconText == copy_of_qt_strippedText(oldText)) {
iconText = removeAcceleratorMarker(oldText);
// ensure we don't re-add the ... that Qt removes per default
iconText.remove(QLatin1String("..."));
if (iconText != maction->iconText()) {
maction->setIconText(iconText);
}
}
if (KAcceleratorManagerPrivate::checkChange(list[cnt])) {
maction->setText(list[cnt].accelerated());
}
cnt++;
}
}
void KPopupAccelManager::manage(QMenu *popup)
{
// don't add more than one manager to a popup
if (popup->findChild<KPopupAccelManager *>(QString()) == nullptr) {
new KPopupAccelManager(popup);
}
}
void QWidgetStackAccelManager::manage(QStackedWidget *stack)
{
if (stack->findChild<QWidgetStackAccelManager *>(QString()) == nullptr) {
new QWidgetStackAccelManager(stack);
}
}
QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack)
: QObject(stack)
, m_stack(stack)
{
currentChanged(stack->currentIndex()); // do one check and then connect to show
connect(stack, &QStackedWidget::currentChanged, this, &QWidgetStackAccelManager::currentChanged);
}
bool QWidgetStackAccelManager::eventFilter(QObject *watched, QEvent *e)
{
if (e->type() == QEvent::Show && qApp->activeWindow()) {
KAcceleratorManager::manage(qApp->activeWindow());
watched->removeEventFilter(this);
}
return false;
}
void QWidgetStackAccelManager::currentChanged(int child)
{
if (child < 0 || child >= static_cast<QStackedWidget *>(parent())->count()) {
// NOTE: QStackedWidget emits currentChanged(-1) when it is emptied
return;
}
static_cast<QStackedWidget *>(parent())->widget(child)->installEventFilter(this);
}
void KAcceleratorManager::setNoAccel(QWidget *widget)
{
KAcceleratorManagerPrivate::ignored_widgets[widget] = 1;
}
void KAcceleratorManager::addStandardActionNames(const QStringList &names)
{
KAcceleratorManagerPrivate::addStandardActionNames(names);
}
#include "moc_kacceleratormanager_p.cpp"
@@ -0,0 +1,71 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2002 Matthias Hölzer-Klüpfel <mhk@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef K_ACCELERATORMANAGER_H
#define K_ACCELERATORMANAGER_H
#include <kwidgetsaddons_export.h>
#include <QStringList>
class QWidget;
class QString;
/**
* @class KAcceleratorManager kacceleratormanager.h KAcceleratorManager
*
* KDE Accelerator manager.
*
* This class can be used to find a valid and working set of
* accelerators for any widget.
*
* @author Matthias Hölzer-Klüpfel <mhk@kde.org>
*/
class KWIDGETSADDONS_EXPORT KAcceleratorManager
{
public:
/**
* Manages the accelerators of a widget.
*
* Call this function on the top widget of the hierarchy you
* want to manage. It will fix the accelerators of the child widgets so
* there are never duplicate accelerators. It also tries to put
* accelerators on as many widgets as possible.
*
* The algorithm used tries to take the existing accelerators into
* account, as well as the class of each widget. Hopefully, the result
* is close to what you would assign manually.
*
* QMenu's are managed dynamically, so when you add or remove entries,
* the accelerators are reassigned. If you add or remove widgets to your
* toplevel widget, you will have to call manage again to fix the
* accelerators.
*
* @param widget The toplevel widget you want to manage.
* @param programmers_mode if true, KAcceleratorManager adds (&) for removed
* accels and & before added accels
*/
static void manage(QWidget *widget, bool programmers_mode = false);
/** \internal returns the result of the last manage operation. */
static void last_manage(QString &added, QString &changed, QString &removed);
/**
* Use this method for a widget (and its children) you want no accels to be set on.
*/
static void setNoAccel(QWidget *widget);
/**
* Append names to a list of standard action names.
* These strings will be given higher priority when assigning keyboard accelerators.
* @since 5.0
*/
static void addStandardActionNames(const QStringList &names);
};
#endif // K_ACCELERATORMANAGER_H
@@ -0,0 +1,264 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2002 Matthias Hölzer-Klüpfel <mhk@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KACCELERATORMANAGER_PRIVATE_H
#define KACCELERATORMANAGER_PRIVATE_H
#include <QList>
#include <QObject>
#include <QString>
class QStackedWidget;
class QMenu;
class QMenuBar;
class QTabBar;
class QDockWidget;
/**
* A string class handling accelerators.
*
* This class contains a string and knowledge about accelerators.
* It keeps a list weights, telling how valuable each character
* would be as an accelerator.
*
* @author Matthias Hölzer-Klüpfel <mhk@kde.org>
*/
class KAccelString
{
public:
KAccelString()
: m_pureText()
, m_accel(-1)
, m_orig_accel(-1)
{
}
explicit KAccelString(const QString &input, int initalWeight = -1);
void calculateWeights(int initialWeight);
const QString &pure() const
{
return m_pureText;
}
QString accelerated() const;
int accel() const
{
return m_accel;
}
void setAccel(int accel)
{
m_accel = accel;
}
int originalAccel() const
{
return m_orig_accel;
}
QString originalText() const
{
return m_origText;
}
QChar accelerator() const;
int maxWeight(int &index, const QString &used) const;
bool operator==(const KAccelString &c) const
{
return m_pureText == c.m_pureText && m_accel == c.m_accel && m_orig_accel == c.m_orig_accel;
}
private:
int stripAccelerator(QString &input);
void dump();
QString m_pureText;
QString m_origText;
int m_accel;
int m_orig_accel;
QList<int> m_weight;
};
Q_DECLARE_TYPEINFO(KAccelString, Q_RELOCATABLE_TYPE);
typedef QList<KAccelString> KAccelStringList;
/**
* This class encapsulates the algorithm finding the 'best'
* distribution of accelerators in a hierarchy of widgets.
*
* @author Matthias Hölzer-Klüpfel <mhk@kde.org>
*/
class KAccelManagerAlgorithm
{
public:
enum {
// Default control weight
DEFAULT_WEIGHT = 50,
// Additional weight for first character in string
FIRST_CHARACTER_EXTRA_WEIGHT = 50,
// Additional weight for the beginning of a word
WORD_BEGINNING_EXTRA_WEIGHT = 50,
// Additional weight for the dialog buttons (large, we basically never want these reassigned)
DIALOG_BUTTON_EXTRA_WEIGHT = 300,
// Additional weight for a 'wanted' accelerator
WANTED_ACCEL_EXTRA_WEIGHT = 150,
// Default weight for an 'action' widget (ie, pushbuttons)
ACTION_ELEMENT_WEIGHT = 50,
// Default weight for group boxes (lowest priority)
GROUP_BOX_WEIGHT = -2000,
// Default weight for checkable group boxes (low priority)
CHECKABLE_GROUP_BOX_WEIGHT = 20,
// Default weight for menu titles
MENU_TITLE_WEIGHT = 250,
// Additional weight for KDE standard accelerators
STANDARD_ACCEL = 300,
};
static void findAccelerators(KAccelStringList &result, QString &used);
};
/**
* This class manages a popup menu. It will notice if entries have been
* added or changed, and will recalculate the accelerators accordingly.
*
* This is necessary for dynamic menus like for example in kicker.
*
* @author Matthias Hölzer-Klüpfel <mhk@kde.org>
*/
class KPopupAccelManager : public QObject
{
Q_OBJECT
public:
static void manage(QMenu *popup);
protected:
KPopupAccelManager(QMenu *popup);
private Q_SLOTS:
void aboutToShow();
private:
void calculateAccelerators();
void findMenuEntries(KAccelStringList &list);
void setMenuEntries(const KAccelStringList &list);
QMenu *m_popup;
KAccelStringList m_entries;
int m_count;
};
class QWidgetStackAccelManager : public QObject
{
Q_OBJECT
public:
static void manage(QStackedWidget *popup);
protected:
QWidgetStackAccelManager(QStackedWidget *popup);
private Q_SLOTS:
void currentChanged(int child);
bool eventFilter(QObject *watched, QEvent *e) override;
private:
void calculateAccelerators();
QStackedWidget *m_stack;
KAccelStringList m_entries;
};
/*********************************************************************
class KAcceleratorManagerPrivate - internal helper class
This class does all the work to find accelerators for a hierarchy of
widgets.
*********************************************************************/
class KAcceleratorManagerPrivate
{
public:
static void manage(QWidget *widget);
static bool programmers_mode;
static void addStandardActionNames(const QStringList &strList);
static bool standardName(const QString &str);
static bool checkChange(const KAccelString &as)
{
QString t2 = as.accelerated();
QString t1 = as.originalText();
if (t1 != t2) {
if (as.accel() == -1) {
removed_string += QLatin1String("<tr><td>") + t1.toHtmlEscaped() + QLatin1String("</td></tr>");
} else if (as.originalAccel() == -1) {
added_string += QLatin1String("<tr><td>") + t2.toHtmlEscaped() + QLatin1String("</td></tr>");
} else {
changed_string += QLatin1String("<tr><td>") + t1.toHtmlEscaped() + QLatin1String("</td>");
changed_string += QLatin1String("<td>") + t2.toHtmlEscaped() + QLatin1String("</td></tr>");
}
return true;
}
return false;
}
static QString changed_string;
static QString added_string;
static QString removed_string;
static QMap<QWidget *, int> ignored_widgets;
static QStringList standardNames;
private:
class Item;
public:
typedef QList<Item *> ItemList;
private:
static void traverseChildren(QWidget *widget, Item *item, QString &used);
static void manageWidget(QWidget *widget, Item *item, QString &used);
static void manageMenuBar(QMenuBar *mbar, Item *item);
static void manageTabBar(QTabBar *bar, Item *item);
static void manageDockWidget(QDockWidget *dock, Item *item);
static void calculateAccelerators(Item *item, QString &used);
class Item
{
public:
Item()
: m_widget(nullptr)
, m_children(nullptr)
, m_index(-1)
{
}
~Item();
Item(const Item &) = delete;
Item &operator=(const Item &) = delete;
void addChild(Item *item);
QWidget *m_widget;
KAccelString m_content;
ItemList *m_children;
int m_index;
};
};
#endif // KACCELERATORMANAGER_PRIVATE_H
@@ -0,0 +1,127 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kactionmenu.h"
#include <QMenu>
#include <QToolBar>
class KActionMenuPrivate
{
public:
KActionMenuPrivate()
{
}
~KActionMenuPrivate()
{
}
QToolButton::ToolButtonPopupMode m_popupMode = QToolButton::DelayedPopup;
};
KActionMenu::KActionMenu(QObject *parent)
: QWidgetAction(parent)
, d(new KActionMenuPrivate)
{
setMenu(new QMenu);
setProperty("isShortcutConfigurable", false);
}
KActionMenu::KActionMenu(const QString &text, QObject *parent)
: QWidgetAction(parent)
, d(new KActionMenuPrivate)
{
setMenu(new QMenu);
setProperty("isShortcutConfigurable", false);
setText(text);
}
KActionMenu::KActionMenu(const QIcon &icon, const QString &text, QObject *parent)
: QWidgetAction(parent)
, d(new KActionMenuPrivate)
{
setMenu(new QMenu);
setProperty("isShortcutConfigurable", false);
setIcon(icon);
setText(text);
}
KActionMenu::~KActionMenu()
{
delete menu();
}
QWidget *KActionMenu::createWidget(QWidget *_parent)
{
QToolBar *parent = qobject_cast<QToolBar *>(_parent);
if (!parent) {
return QWidgetAction::createWidget(_parent);
}
QToolButton *button = new QToolButton(parent);
button->setAutoRaise(true);
button->setFocusPolicy(Qt::NoFocus);
button->setIconSize(parent->iconSize());
button->setToolButtonStyle(parent->toolButtonStyle());
QObject::connect(parent, &QToolBar::iconSizeChanged, button, &QAbstractButton::setIconSize);
QObject::connect(parent, &QToolBar::toolButtonStyleChanged, button, &QToolButton::setToolButtonStyle);
button->setDefaultAction(this);
button->setPopupMode(popupMode());
QObject::connect(button, &QToolButton::triggered, parent, &QToolBar::actionTriggered);
return button;
}
void KActionMenu::addAction(QAction *action)
{
menu()->addAction(action);
}
QAction *KActionMenu::addSeparator()
{
QAction *separator = new QAction(this);
separator->setSeparator(true);
addAction(separator);
return separator;
}
QAction *KActionMenu::insertSeparator(QAction *before)
{
QAction *separator = new QAction(this);
separator->setSeparator(true);
insertAction(before, separator);
return separator;
}
void KActionMenu::insertAction(QAction *before, QAction *action)
{
menu()->insertAction(before, action);
}
void KActionMenu::removeAction(QAction *action)
{
menu()->removeAction(action);
}
QToolButton::ToolButtonPopupMode KActionMenu::popupMode() const
{
return d->m_popupMode;
}
void KActionMenu::setPopupMode(QToolButton::ToolButtonPopupMode popupMode)
{
d->m_popupMode = popupMode;
}
#include "moc_kactionmenu.cpp"
@@ -0,0 +1,93 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KACTIONMENU_H
#define KACTIONMENU_H
#include <QToolButton>
#include <QWidgetAction>
#include <memory>
#include <kwidgetsaddons_export.h>
class QMenu;
/**
* @class KActionMenu kactionmenu.h KActionMenu
*
* A KActionMenu is an action that provides a sub-menu of other actions.
*
* Plugged in a popupmenu, it will create a submenu.
* Plugged in a toolbar, it will create a button with a popup menu.
*
* This is the action used by the XMLGUI since it holds other actions.
* If you want a submenu for selecting one tool among many (without icons), see KSelectAction.
*/
class KWIDGETSADDONS_EXPORT KActionMenu : public QWidgetAction
{
Q_OBJECT
Q_PROPERTY(QToolButton::ToolButtonPopupMode popupMode READ popupMode WRITE setPopupMode)
public:
explicit KActionMenu(QObject *parent);
KActionMenu(const QString &text, QObject *parent);
KActionMenu(const QIcon &icon, const QString &text, QObject *parent);
~KActionMenu() override;
/**
* Adds @p action to this KActionMenu.
* The KActionMenu does not take ownership of @p action.
*/
void addAction(QAction *action);
QAction *addSeparator();
void insertAction(QAction *before, QAction *action);
QAction *insertSeparator(QAction *before);
void removeAction(QAction *action);
/**
* The currently used popup mode when plugged in a KToolBar.
*
* @see setPopupMode()
*
* @since 5.77
*/
QToolButton::ToolButtonPopupMode popupMode() const;
/**
* Determines the popup mode when plugged in a KToolBar.
*
* Options are:
* - QToolButton::InstantPopup
* Clicking anywhere on the toolbar button opens the popup menu.
* - QToolButton::DelayedPopup (Default)
* Clicking anywhere on the toolbar button triggers the default action.
* Clicking and holding the toolbar button opens the popup menu instead.
* - QToolButton::MenuButtonPopup
* The toolbar button is split in a main button (triggers default action)
* and an arrow button (opens the popup menu).
*
* @see QToolButton
*
* @since 5.77
*/
void setPopupMode(QToolButton::ToolButtonPopupMode popupMode);
QWidget *createWidget(QWidget *parent) override;
private:
std::unique_ptr<class KActionMenuPrivate> const d;
};
#endif
@@ -0,0 +1,566 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kactionselector.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QListWidget>
#include <QToolButton>
#include <QVBoxLayout>
class KActionSelectorPrivate
{
public:
KActionSelectorPrivate(KActionSelector *qq)
: q(qq)
{
}
KActionSelector *const q = nullptr;
QListWidget *availableListWidget = nullptr;
QListWidget *selectedListWidget = nullptr;
QToolButton *btnAdd = nullptr;
QToolButton *btnRemove = nullptr;
QToolButton *btnUp = nullptr;
QToolButton *btnDown = nullptr;
QLabel *lAvailable = nullptr;
QLabel *lSelected = nullptr;
bool moveOnDoubleClick : 1;
bool keyboardEnabled : 1;
bool showUpDownButtons : 1;
QString addIcon, removeIcon, upIcon, downIcon;
KActionSelector::InsertionPolicy availableInsertionPolicy, selectedInsertionPolicy;
/**
Move item @p item to the other listbox
*/
void moveItem(QListWidgetItem *item);
/**
loads the icons for the move buttons.
*/
void loadIcons();
/**
@return the index to insert an item into listbox @p lb,
given InsertionPolicy @p policy.
Note that if policy is Sorted, this will return -1.
Sort the listbox after inserting the item in that case.
*/
int insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy);
/**
@return the index of the first selected item in listbox @p lb.
If no item is selected, it will return -1.
*/
int selectedRowIndex(QListWidget *lb);
void buttonAddClicked();
void buttonRemoveClicked();
void buttonUpClicked();
void buttonDownClicked();
void itemDoubleClicked(QListWidgetItem *item);
void slotCurrentChanged(QListWidgetItem *)
{
q->setButtonsEnabled();
}
};
// BEGIN Constructor/destructor
KActionSelector::KActionSelector(QWidget *parent)
: QWidget(parent)
, d(new KActionSelectorPrivate(this))
{
d->moveOnDoubleClick = true;
d->keyboardEnabled = true;
d->addIcon = QLatin1String(QApplication::isRightToLeft() ? "go-previous" : "go-next");
d->removeIcon = QLatin1String(QApplication::isRightToLeft() ? "go-next" : "go-previous");
d->upIcon = QStringLiteral("go-up");
d->downIcon = QStringLiteral("go-down");
d->availableInsertionPolicy = Sorted;
d->selectedInsertionPolicy = BelowCurrent;
d->showUpDownButtons = true;
QHBoxLayout *lo = new QHBoxLayout(this);
lo->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *loAv = new QVBoxLayout();
lo->addLayout(loAv);
d->lAvailable = new QLabel(tr("&Available:", "@label:listbox"), this);
loAv->addWidget(d->lAvailable);
d->availableListWidget = new QListWidget(this);
loAv->addWidget(d->availableListWidget);
d->lAvailable->setBuddy(d->availableListWidget);
QVBoxLayout *loHBtns = new QVBoxLayout();
lo->addLayout(loHBtns);
loHBtns->addStretch(1);
d->btnAdd = new QToolButton(this);
loHBtns->addWidget(d->btnAdd);
d->btnRemove = new QToolButton(this);
loHBtns->addWidget(d->btnRemove);
loHBtns->addStretch(1);
QVBoxLayout *loS = new QVBoxLayout();
lo->addLayout(loS);
d->lSelected = new QLabel(tr("&Selected:", "@label:listbox"), this);
loS->addWidget(d->lSelected);
d->selectedListWidget = new QListWidget(this);
loS->addWidget(d->selectedListWidget);
d->lSelected->setBuddy(d->selectedListWidget);
QVBoxLayout *loVBtns = new QVBoxLayout();
lo->addLayout(loVBtns);
loVBtns->addStretch(1);
d->btnUp = new QToolButton(this);
d->btnUp->setAutoRepeat(true);
loVBtns->addWidget(d->btnUp);
d->btnDown = new QToolButton(this);
d->btnDown->setAutoRepeat(true);
loVBtns->addWidget(d->btnDown);
loVBtns->addStretch(1);
d->loadIcons();
connect(d->btnAdd, &QToolButton::clicked, this, [this]() {
d->buttonAddClicked();
});
connect(d->btnRemove, &QToolButton::clicked, this, [this]() {
d->buttonRemoveClicked();
});
connect(d->btnUp, &QToolButton::clicked, this, [this]() {
d->buttonUpClicked();
});
connect(d->btnDown, &QToolButton::clicked, this, [this]() {
d->buttonDownClicked();
});
connect(d->availableListWidget, &QListWidget::itemDoubleClicked, this, [this] (QListWidgetItem *item) { d->itemDoubleClicked(item); });
connect(d->selectedListWidget, &QListWidget::itemDoubleClicked, this, [this] (QListWidgetItem *item) { d->itemDoubleClicked(item); });
connect(d->availableListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled);
connect(d->selectedListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled);
d->availableListWidget->installEventFilter(this);
d->selectedListWidget->installEventFilter(this);
setButtonsEnabled();
}
KActionSelector::~KActionSelector() = default;
// END Constructor/destroctor
// BEGIN Public Methods
QListWidget *KActionSelector::availableListWidget() const
{
return d->availableListWidget;
}
QListWidget *KActionSelector::selectedListWidget() const
{
return d->selectedListWidget;
}
void KActionSelector::setButtonIcon(const QString &icon, MoveButton button)
{
switch (button) {
case ButtonAdd:
d->addIcon = icon;
d->btnAdd->setIcon(QIcon::fromTheme(icon));
break;
case ButtonRemove:
d->removeIcon = icon;
d->btnRemove->setIcon(QIcon::fromTheme(icon));
break;
case ButtonUp:
d->upIcon = icon;
d->btnUp->setIcon(QIcon::fromTheme(icon));
break;
case ButtonDown:
d->downIcon = icon;
d->btnDown->setIcon(QIcon::fromTheme(icon));
break;
default:
// qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIcon: DAINBREAD!";
break;
}
}
void KActionSelector::setButtonIconSet(const QIcon &iconset, MoveButton button)
{
switch (button) {
case ButtonAdd:
d->btnAdd->setIcon(iconset);
break;
case ButtonRemove:
d->btnRemove->setIcon(iconset);
break;
case ButtonUp:
d->btnUp->setIcon(iconset);
break;
case ButtonDown:
d->btnDown->setIcon(iconset);
break;
default:
// qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIconSet: DAINBREAD!";
break;
}
}
void KActionSelector::setButtonTooltip(const QString &tip, MoveButton button)
{
switch (button) {
case ButtonAdd:
d->btnAdd->setText(tip);
d->btnAdd->setToolTip(tip);
break;
case ButtonRemove:
d->btnRemove->setText(tip);
d->btnRemove->setToolTip(tip);
break;
case ButtonUp:
d->btnUp->setText(tip);
d->btnUp->setToolTip(tip);
break;
case ButtonDown:
d->btnDown->setText(tip);
d->btnDown->setToolTip(tip);
break;
default:
// qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonToolTip: DAINBREAD!";
break;
}
}
void KActionSelector::setButtonWhatsThis(const QString &text, MoveButton button)
{
switch (button) {
case ButtonAdd:
d->btnAdd->setWhatsThis(text);
break;
case ButtonRemove:
d->btnRemove->setWhatsThis(text);
break;
case ButtonUp:
d->btnUp->setWhatsThis(text);
break;
case ButtonDown:
d->btnDown->setWhatsThis(text);
break;
default:
// qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonWhatsThis: DAINBREAD!";
break;
}
}
// END Public Methods
// BEGIN Properties
bool KActionSelector::moveOnDoubleClick() const
{
return d->moveOnDoubleClick;
}
void KActionSelector::setMoveOnDoubleClick(bool b)
{
d->moveOnDoubleClick = b;
}
bool KActionSelector::keyboardEnabled() const
{
return d->keyboardEnabled;
}
void KActionSelector::setKeyboardEnabled(bool b)
{
d->keyboardEnabled = b;
}
QString KActionSelector::availableLabel() const
{
return d->lAvailable->text();
}
void KActionSelector::setAvailableLabel(const QString &text)
{
d->lAvailable->setText(text);
}
QString KActionSelector::selectedLabel() const
{
return d->lSelected->text();
}
void KActionSelector::setSelectedLabel(const QString &text)
{
d->lSelected->setText(text);
}
KActionSelector::InsertionPolicy KActionSelector::availableInsertionPolicy() const
{
return d->availableInsertionPolicy;
}
void KActionSelector::setAvailableInsertionPolicy(InsertionPolicy p)
{
d->availableInsertionPolicy = p;
}
KActionSelector::InsertionPolicy KActionSelector::selectedInsertionPolicy() const
{
return d->selectedInsertionPolicy;
}
void KActionSelector::setSelectedInsertionPolicy(InsertionPolicy p)
{
d->selectedInsertionPolicy = p;
}
bool KActionSelector::showUpDownButtons() const
{
return d->showUpDownButtons;
}
void KActionSelector::setShowUpDownButtons(bool show)
{
d->showUpDownButtons = show;
if (show) {
d->btnUp->show();
d->btnDown->show();
} else {
d->btnUp->hide();
d->btnDown->hide();
}
}
// END Properties
// BEGIN Public Slots
void KActionSelector::setButtonsEnabled()
{
d->btnAdd->setEnabled(d->selectedRowIndex(d->availableListWidget) > -1);
d->btnRemove->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1);
d->btnUp->setEnabled(d->selectedRowIndex(d->selectedListWidget) > 0);
d->btnDown->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1 //
&& d->selectedRowIndex(d->selectedListWidget) < d->selectedListWidget->count() - 1);
}
// END Public Slots
// BEGIN Protected
void KActionSelector::keyPressEvent(QKeyEvent *e)
{
if (!d->keyboardEnabled) {
return;
}
if ((e->modifiers() & Qt::ControlModifier)) {
switch (e->key()) {
case Qt::Key_Right:
d->buttonAddClicked();
break;
case Qt::Key_Left:
d->buttonRemoveClicked();
break;
case Qt::Key_Up:
d->buttonUpClicked();
break;
case Qt::Key_Down:
d->buttonDownClicked();
break;
default:
e->ignore();
return;
}
}
}
bool KActionSelector::eventFilter(QObject *o, QEvent *e)
{
if (d->keyboardEnabled && e->type() == QEvent::KeyPress) {
if ((((QKeyEvent *)e)->modifiers() & Qt::ControlModifier)) {
switch (((QKeyEvent *)e)->key()) {
case Qt::Key_Right:
d->buttonAddClicked();
break;
case Qt::Key_Left:
d->buttonRemoveClicked();
break;
case Qt::Key_Up:
d->buttonUpClicked();
break;
case Qt::Key_Down:
d->buttonDownClicked();
break;
default:
return QWidget::eventFilter(o, e);
}
return true;
} else if (QListWidget *lb = qobject_cast<QListWidget *>(o)) {
switch (((QKeyEvent *)e)->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
int index = lb->currentRow();
if (index < 0) {
break;
}
d->moveItem(lb->item(index));
return true;
}
}
}
return QWidget::eventFilter(o, e);
}
// END Protected
// BEGIN Private Slots
void KActionSelectorPrivate::buttonAddClicked()
{
// move all selected items from available to selected listbox
const QList<QListWidgetItem *> list = availableListWidget->selectedItems();
for (QListWidgetItem *item : list) {
availableListWidget->takeItem(availableListWidget->row(item));
selectedListWidget->insertItem(insertionIndex(selectedListWidget, selectedInsertionPolicy), item);
selectedListWidget->setCurrentItem(item);
Q_EMIT q->added(item);
}
if (selectedInsertionPolicy == KActionSelector::Sorted) {
selectedListWidget->sortItems();
}
selectedListWidget->setFocus();
}
void KActionSelectorPrivate::buttonRemoveClicked()
{
// move all selected items from selected to available listbox
const QList<QListWidgetItem *> list = selectedListWidget->selectedItems();
for (QListWidgetItem *item : list) {
selectedListWidget->takeItem(selectedListWidget->row(item));
availableListWidget->insertItem(insertionIndex(availableListWidget, availableInsertionPolicy), item);
availableListWidget->setCurrentItem(item);
Q_EMIT q->removed(item);
}
if (availableInsertionPolicy == KActionSelector::Sorted) {
availableListWidget->sortItems();
}
availableListWidget->setFocus();
}
void KActionSelectorPrivate::buttonUpClicked()
{
int c = selectedRowIndex(selectedListWidget);
if (c < 1) {
return;
}
QListWidgetItem *item = selectedListWidget->item(c);
selectedListWidget->takeItem(c);
selectedListWidget->insertItem(c - 1, item);
selectedListWidget->setCurrentItem(item);
Q_EMIT q->movedUp(item);
}
void KActionSelectorPrivate::buttonDownClicked()
{
int c = selectedRowIndex(selectedListWidget);
if (c < 0 || c == selectedListWidget->count() - 1) {
return;
}
QListWidgetItem *item = selectedListWidget->item(c);
selectedListWidget->takeItem(c);
selectedListWidget->insertItem(c + 1, item);
selectedListWidget->setCurrentItem(item);
Q_EMIT q->movedDown(item);
}
void KActionSelectorPrivate::itemDoubleClicked(QListWidgetItem *item)
{
if (moveOnDoubleClick) {
moveItem(item);
}
}
// END Private Slots
// BEGIN Private Methods
void KActionSelectorPrivate::loadIcons()
{
btnAdd->setIcon(QIcon::fromTheme(addIcon));
btnRemove->setIcon(QIcon::fromTheme(removeIcon));
btnUp->setIcon(QIcon::fromTheme(upIcon));
btnDown->setIcon(QIcon::fromTheme(downIcon));
}
void KActionSelectorPrivate::moveItem(QListWidgetItem *item)
{
QListWidget *lbFrom = item->listWidget();
QListWidget *lbTo;
if (lbFrom == availableListWidget) {
lbTo = selectedListWidget;
} else if (lbFrom == selectedListWidget) {
lbTo = availableListWidget;
} else { //?! somewhat unlikely...
return;
}
KActionSelector::InsertionPolicy p = (lbTo == availableListWidget) ? availableInsertionPolicy : selectedInsertionPolicy;
lbFrom->takeItem(lbFrom->row(item));
lbTo->insertItem(insertionIndex(lbTo, p), item);
lbTo->setFocus();
lbTo->setCurrentItem(item);
if (p == KActionSelector::Sorted) {
lbTo->sortItems();
}
if (lbTo == selectedListWidget) {
Q_EMIT q->added(item);
} else {
Q_EMIT q->removed(item);
}
}
int KActionSelectorPrivate::insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy)
{
int index;
switch (policy) {
case KActionSelector::BelowCurrent:
index = lb->currentRow();
if (index > -1) {
index += 1;
}
break;
case KActionSelector::AtTop:
index = 0;
break;
case KActionSelector::AtBottom:
index = lb->count();
break;
default:
index = -1;
}
return index;
}
int KActionSelectorPrivate::selectedRowIndex(QListWidget *lb)
{
QList<QListWidgetItem *> list = lb->selectedItems();
if (list.isEmpty()) {
return -1;
}
return lb->row(list.at(0));
}
// END Private Methods
#include "moc_kactionselector.cpp"
@@ -0,0 +1,306 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef _KACTION_SELECTOR_H_
#define _KACTION_SELECTOR_H_
#include <QWidget>
#include <kwidgetsaddons_export.h>
#include <memory>
class QListWidget;
class QListWidgetItem;
class QKeyEvent;
class QEvent;
class QIcon;
/**
@class KActionSelector kactionselector.h KActionSelector
@short A widget for selecting and arranging actions/objects
This widget allows the user to select from a set of objects and arrange
the order of the selected ones using two list boxes labeled "Available"
and "Used" with horizontal arrows in between to move selected objects between
the two, and vertical arrows on the right to arrange the order of the selected
objects.
The widget moves objects to the other listbox when doubleclicked if
the property moveOnDoubleClick is set to true (default). See moveOnDoubleClick()
and setMoveOnDoubleClick().
The user control the widget using the keyboard if enabled (default),
see keyboardEnabled.
Note that this may conflict with keyboard selection in the selected list box,
if you set that to anything else than QListWidget::Single (which is the default).
To use it, simply construct an instance and then add items to the two listboxes,
available through lbAvailable() and lbSelected(). Whenever you want, you can retrieve
the selected options using QListWidget methods on lbSelected().
This way, you can use your own QListWidgetItem class, allowing you to easily
store object data in those.
When an item is moved to a listbox, it is placed below the current item
of that listbox.
Standard arrow icons are used, but you can use icons of your own choice if desired,
see setButtonIcon(). It is also possible to set tooltips and whatsthis help
for the buttons. See setButtonTooltip() and setButtonWhatsThis().
To set whatsthis or tooltips for the listboxes, access them through
availableListWidget() and selectedListWidget().
All the moving buttons are automatically set enabled as expected.
Signals are sent each time an item is moved, allowing you to follow the
users actions if you need to. See addedToSelection(), removedFromSelection(),
movedUp() and movedDown()
\image html kactionselector.png "KActionSelector Widget"
@author Anders Lund <anders@alweb.dk>
*/
class KWIDGETSADDONS_EXPORT KActionSelector : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool moveOnDoubleClick READ moveOnDoubleClick WRITE setMoveOnDoubleClick)
Q_PROPERTY(bool keyboardEnabled READ keyboardEnabled WRITE setKeyboardEnabled)
Q_PROPERTY(QString availableLabel READ availableLabel WRITE setAvailableLabel)
Q_PROPERTY(QString selectedLabel READ selectedLabel WRITE setSelectedLabel)
Q_PROPERTY(InsertionPolicy availableInsertionPolicy READ availableInsertionPolicy WRITE setAvailableInsertionPolicy)
Q_PROPERTY(InsertionPolicy selectedInsertionPolicy READ selectedInsertionPolicy WRITE setSelectedInsertionPolicy)
Q_PROPERTY(bool showUpDownButtons READ showUpDownButtons WRITE setShowUpDownButtons)
public:
explicit KActionSelector(QWidget *parent = nullptr);
~KActionSelector() override;
/**
* @return The QListWidget holding the available actions
*/
QListWidget *availableListWidget() const;
/**
* @return The QListWidget holding the selected actions
*/
QListWidget *selectedListWidget() const;
/**
* This enum identifies the moving buttons
*/
enum MoveButton {
ButtonAdd,
ButtonRemove,
ButtonUp,
ButtonDown,
};
Q_ENUM(MoveButton)
/**
* This enum defines policies for where to insert moved items in a listbox.
*
* @sa availableInsertionPolicy(), setAvailableInsertionPolicy(),
* selectedInsertionPolicy(), setSelectedInsertionPolicy()
*/
enum InsertionPolicy {
BelowCurrent, ///< The item is inserted below the listbox' currentItem() or at the end if there is no current item.
Sorted, ///< The listbox is sort()ed after one or more items are inserted.
AtTop, ///< The item is inserted at index 0 in the listbox.
AtBottom, ///< The item is inserted at the end of the listbox.
};
Q_ENUM(InsertionPolicy)
/**
* @return Whether moveOnDoubleClcik is enabled.
*
* If enabled, an item in any listbox will be moved to the other one whenever
* double-clicked.
* This feature is enabled by default.
* @sa setMoveOnDoubleClick()
*/
bool moveOnDoubleClick() const;
/**
* Sets moveOnDoubleClick to @p enable
* @sa moveOnDoubleClick()
*/
void setMoveOnDoubleClick(bool enable);
/**
* @return Whether keyboard control is enabled.
*
* When Keyboard control is enabled, the widget will react to
* the following keyboard actions:
* @li CTRL + Right - simulate clicking the add button
* @li CTRL + Left - simulate clicking the remove button
* @li CTRL + Up - simulate clicking the up button
* @li CTRL + Down - simulate clicking the down button
*
* Additionally, pressing RETURN or ENTER on one of the list boxes
* will cause the current item of that listbox to be moved to the other
* listbox.
*
* The keyboard actions are enabled by default.
*
* @sa setKeyboardEnabled()
*/
bool keyboardEnabled() const;
/**
* Sets the keyboard enabled depending on @p enable.
* @sa keyboardEnabled()
*/
void setKeyboardEnabled(bool enable);
/**
* @return The text of the label for the available items listbox.
*/
QString availableLabel() const;
/**
* Sets the label for the available items listbox to @p text.
* Note that this label has the listbox as its @e buddy, so that
* if you have a single ampersand in the text, the following character
* will become the accelerator to focus the listbox.
*/
void setAvailableLabel(const QString &text);
/**
* @return the label of the selected items listbox.
*/
QString selectedLabel() const;
/**
* Sets the label for the selected items listbox to @p text.
* Note that this label has the listbox as its @e buddy, so that
* if you have a single ampersand in the text, the following character
* will become the accelerator to focus the listbox.
*/
void setSelectedLabel(const QString &text);
/**
* @return The current insertion policy for the available listbox.
* The default policy for the available listbox is Sorted.
* @sa InsertionPolicy, setAvailableInsertionPolicy()
*/
InsertionPolicy availableInsertionPolicy() const;
/**
* Sets the insertion policy for the available listbox.
* @sa InsertionPolicy, availableInsertionPolicy()
*/
void setAvailableInsertionPolicy(InsertionPolicy policy);
/**
* @return The current insertion policy for the selected listbox.
* The default policy for the selected listbox is BelowCurrent.
* @sa InsertionPolicy, setSelectedInsertionPolicy()
*/
InsertionPolicy selectedInsertionPolicy() const;
/**
* Sets the insertion policy for the selected listbox.
* @sa InsertionPolicy, selectedInsertionPolicy()
*/
void setSelectedInsertionPolicy(InsertionPolicy policy);
/**
* @return whether the Up and Down buttons should be displayed.
*/
bool showUpDownButtons() const;
/**
* Sets whether the Up and Down buttons should be displayed
* according to @p show
*/
void setShowUpDownButtons(bool show);
/**
* Sets the pixmap of the button @p button to @p icon.
* It calls SmallIconSet(pm) to generate the icon set.
*/
void setButtonIcon(const QString &icon, MoveButton button);
/**
* Sets the iconset for button @p button to @p iconset.
* You can use this method to set a custom icon set. Either
* created by QIconSet, or use the application instance of
* KIconLoader (recommended).
*/
void setButtonIconSet(const QIcon &iconset, MoveButton button);
/**
* Sets the tooltip for the button @p button to @p tip.
*/
void setButtonTooltip(const QString &tip, MoveButton button);
/**
* Sets the whatsthis help for button @p button to @p text.
*/
void setButtonWhatsThis(const QString &text, MoveButton button);
Q_SIGNALS:
/**
* Emitted when an item is moved to the "selected" listbox.
*/
void added(QListWidgetItem *item);
/**
* Emitted when an item is moved out of the "selected" listbox.
*/
void removed(QListWidgetItem *item);
/**
* Emitted when an item is moved upwards in the "selected" listbox.
*/
void movedUp(QListWidgetItem *item);
/**
* Emitted when an item is moved downwards in the "selected" listbox.
*/
void movedDown(QListWidgetItem *item);
/**
* Emitted when an item is moved to the "selected" listbox.
*/
// void addedToSelection( QListWidgetItem *item );
public Q_SLOTS:
/**
* Sets the enabled state of all moving buttons to reflect the current
* options.
*
* Be sure to call this if you add or removes items to either listbox after the
* widget is shown
*/
void setButtonsEnabled();
protected:
/**
* Reimplemented for internal reasons.
*/
void keyPressEvent(QKeyEvent *) override;
/**
* Reimplemented for internal reasons.
*/
bool eventFilter(QObject *, QEvent *) override;
private:
/**
* @private
* Private data storage
*/
friend class KActionSelectorPrivate;
std::unique_ptr<class KActionSelectorPrivate> const d;
Q_DISABLE_COPY(KActionSelector)
};
#endif // _KACTION_SELECTOR_H_
@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
// SPDX-FileCopyrightText: 2022 g10 Code GmbH
// SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "kadjustingscrollarea.h"
#include <QApplication>
#include <QResizeEvent>
#include <QScreen>
#include <QScrollBar>
#include <QVBoxLayout>
KAdjustingScrollArea::KAdjustingScrollArea(QWidget *parent)
: QScrollArea{parent}
{
setWidgetResizable(true);
connect(qApp, &QApplication::focusChanged, this, [this](QWidget *old, QWidget *now) {
Q_UNUSED(old);
ensureWidgetVisible(now);
});
viewport()->installEventFilter(this);
}
KAdjustingScrollArea::~KAdjustingScrollArea()
{
viewport()->removeEventFilter(this);
if (auto w = widget()) {
w->removeEventFilter(this);
}
}
QSize KAdjustingScrollArea::minimumSizeHint() const
{
const int fw = frameWidth();
QSize sz{2 * fw, 2 * fw};
sz += {widget()->minimumSizeHint().width(), 0};
if (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) {
sz.setWidth(sz.width() + verticalScrollBar()->sizeHint().width());
}
if (horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) {
sz.setHeight(sz.height() + horizontalScrollBar()->sizeHint().height());
}
return QScrollArea::minimumSizeHint().expandedTo(sz);
}
QSize KAdjustingScrollArea::sizeHint() const
{
const int fw = frameWidth();
QSize sz{2 * fw, 2 * fw};
sz += viewportSizeHint();
if (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) {
sz.setWidth(sz.width() + verticalScrollBar()->sizeHint().width());
}
if (horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) {
sz.setHeight(sz.height() + horizontalScrollBar()->sizeHint().height());
}
sz = QScrollArea::sizeHint().expandedTo(sz);
return sz;
}
static void adjustSizeOfWindowBy(const QSize &extent, QWidget *window)
{
if (window) {
const auto currentSize = window->size();
// we limit the automatic size adjustment to 2/3 of the screen's size
const auto maxWindowSize = window->screen()->geometry().size() * 2 / 3;
const auto newWindowSize = currentSize.expandedTo((currentSize + extent).boundedTo(maxWindowSize));
if (newWindowSize != currentSize) {
window->resize(newWindowSize);
}
}
}
bool KAdjustingScrollArea::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() == QEvent::ChildAdded && viewport() == obj) {
auto childAddedEvent = static_cast<QChildEvent *>(ev);
auto widget = childAddedEvent->child();
if (widget->objectName().isEmpty()) {
widget->setObjectName(QLatin1StringView("scrollarea_widget"));
}
widget->installEventFilter(this);
}
if (ev->type() == QEvent::ChildRemoved && viewport() == obj) {
auto childRemovedEvent = static_cast<QChildEvent *>(ev);
auto widget = childRemovedEvent->child();
widget->removeEventFilter(this);
}
if (ev->type() == QEvent::Resize && obj == widget() && sizeAdjustPolicy() == AdjustToContents) {
const auto *const event = static_cast<QResizeEvent *>(ev);
if (event->size().height() > event->oldSize().height()) {
const auto currentViewportHeight = viewport()->height();
const auto wantedViewportHeight = event->size().height();
const auto wantedAdditionalHeight = wantedViewportHeight - currentViewportHeight;
if (wantedAdditionalHeight > 0) {
adjustSizeOfWindowBy(QSize{0, wantedAdditionalHeight}, window());
}
}
}
return QScrollArea::eventFilter(obj, ev);
}
bool KAdjustingScrollArea::event(QEvent *event)
{
return QScrollArea::event(event);
}
#include "moc_kadjustingscrollarea.cpp"
@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
// SPDX-FileCopyrightText: 2022 g10 Code GmbH
// SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#ifndef KADJUSTINGSCROLLAREA_H
#define KADJUSTINGSCROLLAREA_H
#include <kwidgetsaddons_export.h>
#include <QScrollArea>
/**
* @class KAdjustingScrollArea kadjustingscrollarea.h KAdjustingScrollArea
*
* @short Special scroll area widget which adjust its size to avoid scroll bars
* as much as possible
*
* This widget improves a few aspects of QScrollArea in particular, for
* vertically scrollable widgets and dialogs.
*
* If sizeAdjustPolicy is set to QAbstractScrollArea::AdjustToContents,
* then the scroll area will (try to) adjust its size to the widget to avoid
* scroll bars as much as possible.
*
* In particular, this widget will automatically increase the height of its
* window when the content of the scroll area grows in height (e.g. because
* more widgets are added dynamically). The automatic resizing stops at 2/3
* of the screen's height.
*
* Instead of setting the scrollarea's content via QScrollArea::setWidget,
* add the child widgets to the QVBoxLayout of the scrollarea's widget.
*
* @code{.cpp}
* auto scrollArea = new KAdjustingScrollArea(this);
* scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
* scrollArea->setFocusPolicy(Qt::NoFocus);
* scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
* scrollArea->setWidget(new QWidget);
*
* auto scrollAreaLayout = new QVBoxLayout(scrollArea->widget());
* @endcode
*
* @since 6.9
*/
class KWIDGETSADDONS_EXPORT KAdjustingScrollArea : public QScrollArea
{
Q_OBJECT
public:
/**
* Creates a scroll area with a QWidget with QVBoxLayout that is flagged
* as resizable.
*/
explicit KAdjustingScrollArea(QWidget *parent = nullptr);
~KAdjustingScrollArea() override;
/**
* Reimplemented to add the minimum size hint of the widget.
*/
QSize minimumSizeHint() const override;
/**
* Reimplemented to remove the caching of the size/size hint of the
* widget and to add the horizontal size hint of the vertical scroll bar
* unless it is explicitly turned off.
*/
QSize sizeHint() const override;
bool event(QEvent *event) override;
private:
bool eventFilter(QObject *obj, QEvent *ev) override;
};
#endif // KADJUSTINGSCROLLAREA_H
@@ -0,0 +1,196 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include <kanimatedbutton.h>
#include <QImageReader>
#include <QMovie>
#include <QPainter>
#include <QPixmap>
#include <QTimer>
class KAnimatedButtonPrivate
{
public:
KAnimatedButtonPrivate(KAnimatedButton *qq)
: q(qq)
{
}
void updateIcons();
void updateCurrentIcon();
void movieFrameChanged(int number);
void movieFinished();
void timerUpdate();
KAnimatedButton *const q;
QMovie *movie = nullptr;
int frames;
int current_frame;
QPixmap pixmap;
QTimer timer;
QString icon_path;
QList<QPixmap *> framesCache; // We keep copies of each frame so that
// the icon code can properly cache them in QPixmapCache,
// and not fill it up with dead copies
};
KAnimatedButton::KAnimatedButton(QWidget *parent)
: QToolButton(parent)
, d(new KAnimatedButtonPrivate(this))
{
connect(&d->timer, &QTimer::timeout, this, [this]() {
d->timerUpdate();
});
}
KAnimatedButton::~KAnimatedButton()
{
d->timer.stop();
qDeleteAll(d->framesCache);
delete d->movie;
}
void KAnimatedButton::start()
{
if (d->movie) {
d->movie->start();
} else {
d->current_frame = 0;
d->timer.start(50);
}
}
void KAnimatedButton::stop()
{
if (d->movie) {
d->movie->stop();
d->movie->jumpToFrame(0);
d->movieFrameChanged(0);
} else {
d->current_frame = 0;
d->timer.stop();
d->updateCurrentIcon();
}
}
void KAnimatedButton::setAnimationPath(const QString &path)
{
if (d->icon_path == path) {
return;
}
d->timer.stop();
d->icon_path = path;
d->updateIcons();
}
QString KAnimatedButton::animationPath() const
{
return d->icon_path;
}
void KAnimatedButtonPrivate::timerUpdate()
{
if (!q->isVisible()) {
return;
}
current_frame++;
if (current_frame == frames) {
current_frame = 0;
}
updateCurrentIcon();
}
void KAnimatedButtonPrivate::updateCurrentIcon()
{
if (pixmap.isNull()) {
return;
}
QPixmap *frame = framesCache[current_frame];
if (!frame) {
const int icon_size = qMin(pixmap.width(), pixmap.height());
const int row_size = pixmap.width() / icon_size;
const int row = current_frame / row_size;
const int column = current_frame % row_size;
frame = new QPixmap(icon_size, icon_size);
frame->fill(Qt::transparent);
QPainter p(frame);
p.drawPixmap(QPoint(0, 0), pixmap, QRect(column * icon_size, row * icon_size, icon_size, icon_size));
p.end();
framesCache[current_frame] = frame;
}
q->setIcon(QIcon(*frame));
}
void KAnimatedButtonPrivate::movieFrameChanged(int number)
{
Q_UNUSED(number);
q->setIcon(QIcon(movie->currentPixmap()));
}
void KAnimatedButtonPrivate::movieFinished()
{
// if not running, make it loop
if (movie->state() == QMovie::NotRunning) {
movie->start();
}
}
void KAnimatedButtonPrivate::updateIcons()
{
pixmap = QPixmap();
QMovie *newMovie = nullptr;
QImageReader reader(icon_path);
if (QMovie::supportedFormats().contains(reader.format())) {
newMovie = new QMovie(icon_path);
frames = 0;
newMovie->setCacheMode(QMovie::CacheAll);
QObject::connect(newMovie, &QMovie::frameChanged, q, [this](int value) {
movieFrameChanged(value);
});
QObject::connect(newMovie, &QMovie::finished, q, [this] {
movieFinished();
});
} else {
const QPixmap pix(icon_path);
if (pix.isNull()) {
return;
}
const int icon_size = qMin(pix.width(), pix.height());
if ((pix.height() % icon_size != 0) || (pix.width() % icon_size != 0)) {
return;
}
frames = (pix.height() / icon_size) * (pix.width() / icon_size);
pixmap = pix;
}
current_frame = 0;
qDeleteAll(framesCache);
framesCache.fill(nullptr);
framesCache.resize(frames);
delete movie;
movie = newMovie;
if (movie) {
movie->jumpToFrame(0);
movieFrameChanged(0);
} else {
updateCurrentIcon();
}
}
#include "moc_kanimatedbutton.cpp"
@@ -0,0 +1,75 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KANIMATEDBUTTON_H
#define KANIMATEDBUTTON_H
#include <QToolButton>
#include <kwidgetsaddons_export.h>
#include <memory>
/**
* @class KAnimatedButton kanimatedbutton.h KAnimatedButton
*
* @short An extended version of QToolButton which can display an animation.
*
* This widget extends QToolButton with the ability to display an animation.
* All you need to do is pass along a path to a file containing an animation,
* it can be anything supported by QMovie, or a picture containing all the
* frames of the animation next to each other (each frame being assumed of
* having the same size).
*
* @author Kurt Granroth <granroth@kde.org>
*/
class KWIDGETSADDONS_EXPORT KAnimatedButton : public QToolButton
{
Q_OBJECT
Q_PROPERTY(QString animationPath READ animationPath WRITE setAnimationPath)
public:
/**
* Construct an animated tool button.
*
* @param parent The parent widget
*/
explicit KAnimatedButton(QWidget *parent = nullptr);
/**
* Destructor
*/
~KAnimatedButton() override;
/**
* Returns the path used to load the animation
*/
QString animationPath() const;
/**
* Sets the path to the file which contains the animation to load.
*
* @param path The path of the file containing the animation
*/
void setAnimationPath(const QString &path);
public Q_SLOTS:
/**
* Starts the animation from frame 1
*/
void start();
/**
* Stops the animation. This will also reset the widget to frame 1.
*/
void stop();
private:
std::unique_ptr<class KAnimatedButtonPrivate> const d;
Q_DISABLE_COPY(KAnimatedButton)
};
#endif // KANIMATEDBUTTON_H
@@ -0,0 +1,224 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2006 Olivier Goffart <ogoffart at kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kassistantdialog.h"
#include "kpagedialog_p.h"
#include <QApplication>
#include <QDialogButtonBox>
#include <QIcon>
#include <QPushButton>
#include <QHash>
class KAssistantDialogPrivate : public KPageDialogPrivate
{
Q_DECLARE_PUBLIC(KAssistantDialog)
Q_DECLARE_TR_FUNCTIONS(KAssistantDialog)
public:
KAssistantDialogPrivate(KAssistantDialog *qq)
: KPageDialogPrivate(qq)
{
}
QHash<KPageWidgetItem *, bool> valid;
QHash<KPageWidgetItem *, bool> appropriate;
KPageWidgetModel *pageModel = nullptr;
QPushButton *backButton = nullptr;
QPushButton *nextButton = nullptr;
QPushButton *finishButton = nullptr;
void init();
void slotUpdateButtons();
QModelIndex getNext(QModelIndex nextIndex)
{
QModelIndex currentIndex;
do {
currentIndex = nextIndex;
nextIndex = pageModel->index(0, 0, currentIndex);
if (!nextIndex.isValid()) {
nextIndex = currentIndex.sibling(currentIndex.row() + 1, 0);
}
} while (nextIndex.isValid() && !appropriate.value(pageModel->item(nextIndex), true));
return nextIndex;
}
QModelIndex getPrevious(QModelIndex nextIndex)
{
QModelIndex currentIndex;
do {
currentIndex = nextIndex;
nextIndex = currentIndex.sibling(currentIndex.row() - 1, 0);
if (!nextIndex.isValid()) {
nextIndex = currentIndex.parent();
}
} while (nextIndex.isValid() && !appropriate.value(pageModel->item(nextIndex), true));
return nextIndex;
}
};
KAssistantDialog::KAssistantDialog(QWidget *parent, Qt::WindowFlags flags)
: KPageDialog(*new KAssistantDialogPrivate(this), nullptr, parent, flags)
{
Q_D(KAssistantDialog);
d->init();
// workaround to get the page model
KPageWidget *pagewidget = findChild<KPageWidget *>();
Q_ASSERT(pagewidget);
d->pageModel = static_cast<KPageWidgetModel *>(pagewidget->model());
}
KAssistantDialog::KAssistantDialog(KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags)
: KPageDialog(*new KAssistantDialogPrivate(this), widget, parent, flags)
{
Q_D(KAssistantDialog);
d->init();
d->pageModel = static_cast<KPageWidgetModel *>(widget->model());
}
KAssistantDialog::~KAssistantDialog() = default;
void KAssistantDialogPrivate::init()
{
Q_Q(KAssistantDialog);
QDialogButtonBox *buttonBox = q->buttonBox();
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Help);
backButton = new QPushButton;
const QString iconBack = QApplication::isRightToLeft() ? QStringLiteral("go-next") : QStringLiteral("go-previous");
const QString iconNext = QApplication::isRightToLeft() ? QStringLiteral("go-previous") : QStringLiteral("go-next");
backButton->setText(tr("&Back", "@action:button go back"));
backButton->setIcon(QIcon::fromTheme(iconBack));
backButton->setToolTip(tr("Go back one step", "@info:tooltip"));
q->connect(backButton, &QAbstractButton::clicked, q, &KAssistantDialog::back);
buttonBox->addButton(backButton, QDialogButtonBox::ActionRole);
nextButton = new QPushButton;
nextButton->setText(tr("Next", "@action:button Opposite to Back"));
nextButton->setIcon(QIcon::fromTheme(iconNext));
nextButton->setDefault(true);
q->connect(nextButton, &QAbstractButton::clicked, q, &KAssistantDialog::next);
buttonBox->addButton(nextButton, QDialogButtonBox::ActionRole);
finishButton = new QPushButton;
finishButton->setText(tr("Finish", "@action:button"));
finishButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
buttonBox->addButton(finishButton, QDialogButtonBox::AcceptRole);
q->setFaceType(KPageDialog::Plain);
q->connect(q, &KAssistantDialog::currentPageChanged, q, [this]() {
slotUpdateButtons();
});
}
void KAssistantDialog::back()
{
Q_D(KAssistantDialog);
QModelIndex nextIndex = d->getPrevious(d->pageModel->index(currentPage()));
if (nextIndex.isValid()) {
setCurrentPage(d->pageModel->item(nextIndex));
}
}
void KAssistantDialog::next()
{
Q_D(KAssistantDialog);
QModelIndex nextIndex = d->getNext(d->pageModel->index(currentPage()));
if (nextIndex.isValid()) {
setCurrentPage(d->pageModel->item(nextIndex));
} else if (isValid(currentPage())) {
accept();
}
}
void KAssistantDialog::setValid(KPageWidgetItem *page, bool enable)
{
Q_D(KAssistantDialog);
d->valid[page] = enable;
if (page == currentPage()) {
d->slotUpdateButtons();
}
}
bool KAssistantDialog::isValid(KPageWidgetItem *page) const
{
Q_D(const KAssistantDialog);
return d->valid.value(page, true);
}
void KAssistantDialogPrivate::slotUpdateButtons()
{
Q_Q(KAssistantDialog);
QModelIndex currentIndex = pageModel->index(q->currentPage());
// change the title of the next/finish button
QModelIndex nextIndex = getNext(currentIndex);
finishButton->setEnabled(!nextIndex.isValid() && q->isValid(q->currentPage()));
nextButton->setEnabled(nextIndex.isValid() && q->isValid(q->currentPage()));
finishButton->setDefault(!nextIndex.isValid());
nextButton->setDefault(nextIndex.isValid());
// enable or disable the back button;
nextIndex = getPrevious(currentIndex);
backButton->setEnabled(nextIndex.isValid());
}
void KAssistantDialog::showEvent(QShowEvent *event)
{
Q_D(KAssistantDialog);
d->slotUpdateButtons(); // called because last time that function was called is when the first page was added, so the next button show "finish"
KPageDialog::showEvent(event);
}
void KAssistantDialog::setAppropriate(KPageWidgetItem *page, bool appropriate)
{
Q_D(KAssistantDialog);
d->appropriate[page] = appropriate;
d->slotUpdateButtons();
}
bool KAssistantDialog::isAppropriate(KPageWidgetItem *page) const
{
Q_D(const KAssistantDialog);
return d->appropriate.value(page, true);
}
QPushButton *KAssistantDialog::backButton() const
{
Q_D(const KAssistantDialog);
return d->backButton;
}
QPushButton *KAssistantDialog::nextButton() const
{
Q_D(const KAssistantDialog);
return d->nextButton;
}
QPushButton *KAssistantDialog::finishButton() const
{
Q_D(const KAssistantDialog);
return d->finishButton;
}
#include "moc_kassistantdialog.cpp"
@@ -0,0 +1,144 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2006 Olivier Goffart <ogoffart at kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KASSISTANTDIALOG_H
#define KASSISTANTDIALOG_H
#include <kpagedialog.h>
#include <kwidgetsaddons_export.h>
class KAssistantDialogPrivate;
/**
* @class KAssistantDialog kassistantdialog.h KAssistantDialog
*
* This class provides a framework for assistant dialogs.
*
* An assistant dialog consists of a sequence of pages.
* Its purpose is to guide the user (assist) through a process step by step.
* Assistant dialogs are useful for complex or infrequently occurring tasks
* that people may find difficult to learn or do.
* Sometimes a task requires too many input fields to fit them on a single dialog.
*
* Create and populate dialog pages that inherit from QWidget and add them
* to the assistant dialog using addPage().
*
* The functions next() and back() are virtual and may be reimplemented to
* override the default actions of the next and back buttons.
*
* \image html kassistantdialog.png "KAssistantDialog"
*
* @author Olivier Goffart <ogoffart at kde.org>
*/
class KWIDGETSADDONS_EXPORT KAssistantDialog : public KPageDialog
{
Q_OBJECT
public:
/**
* Construct a new assistant dialog with @p parent as parent.
* @param parent is the parent of the widget.
* @flags the window flags to give to the assistant dialog. The
* default of zero is usually what you want.
*/
explicit KAssistantDialog(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
~KAssistantDialog() override;
/**
* Specify if the content of the page is valid, and if the next button may be enabled on this page.
* By default all pages are valid.
*
* This will disable or enable the next button on the specified page
*
* @param page the page on which the next button will be enabled/disable
* @param enable if true the next button will be enabled, if false it will be disabled
*/
void setValid(KPageWidgetItem *page, bool enable);
/**
* return if a page is valid
* @param page the page to check the validity of
* @see setValid()
*/
bool isValid(KPageWidgetItem *page) const;
/**
* Specify whether a page is appropriate.
*
* A page is considered inappropriate if it should not be shown due to
* the contents of other pages making it inappropriate.
*
* A page which is inappropriate will not be shown.
*
* The last page in an assistant dialog should always be appropriate
* @param page the page to set as appropriate
* @param appropriate flag indicating the appropriateness of the page.
* If @p appropriate is true, then @p page is appropriate and will be
* shown in the assistant dialog. If false, @p page will not be shown.
*/
void setAppropriate(KPageWidgetItem *page, bool appropriate);
/**
* Check if a page is appropriate for use in the assistant dialog.
* @param page is the page to check the appropriateness of.
* @return true if @p page is appropriate, false if it is not
*/
bool isAppropriate(KPageWidgetItem *page) const;
/**
* @returns the next button
*/
QPushButton *nextButton() const;
/**
* @returns the finish button
*/
QPushButton *backButton() const;
/**
* @returns the finish button
*/
QPushButton *finishButton() const;
public Q_SLOTS:
/**
* Called when the user clicks the Back button.
*
* This function will show the preceding relevant page in the sequence.
* Do nothing if the current page is the first page in the sequence.
*/
virtual void back();
/**
* Called when the user clicks the Next/Finish button.
*
* This function will show the next relevant page in the sequence.
* If the current page is the last page, it will call accept()
*/
virtual void next();
protected:
/**
* Construct an assistant dialog from a single widget.
* @param widget the widget to construct the dialog with
* @param parent the parent of the assistant dialog
* @flags the window flags to use when creating the widget. The default
* of zero is usually fine.
*
* Calls the KPageDialog(KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags) constructor
*/
explicit KAssistantDialog(KPageWidget *widget, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
void showEvent(QShowEvent *event) override;
private:
Q_DECLARE_PRIVATE(KAssistantDialog)
Q_DISABLE_COPY(KAssistantDialog)
};
#endif
@@ -0,0 +1,104 @@
/*
SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kbusyindicatorwidget.h"
#include <QApplication>
#include <QIcon>
#include <QPainter>
#include <QResizeEvent>
#include <QStyle>
#include <QVariantAnimation>
class KBusyIndicatorWidgetPrivate
{
public:
KBusyIndicatorWidgetPrivate(KBusyIndicatorWidget *parent)
: q(parent)
{
animation.setLoopCount(-1);
animation.setDuration(2000);
animation.setStartValue(0);
animation.setEndValue(360);
QObject::connect(&animation, &QVariantAnimation::valueChanged, q, [this](QVariant value) {
rotation = value.toReal();
q->update(); // repaint new rotation
});
}
KBusyIndicatorWidget *const q;
QVariantAnimation animation;
QIcon icon = QIcon::fromTheme(QStringLiteral("view-refresh"));
qreal rotation = 0;
QPointF paintCenter;
};
KBusyIndicatorWidget::KBusyIndicatorWidget(QWidget *parent)
: QWidget(parent)
, d(new KBusyIndicatorWidgetPrivate(this))
{
}
KBusyIndicatorWidget::~KBusyIndicatorWidget() = default;
QSize KBusyIndicatorWidget::minimumSizeHint() const
{
const auto extent = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
return QSize(extent, extent);
}
void KBusyIndicatorWidget::start()
{
d->animation.start();
}
void KBusyIndicatorWidget::stop()
{
if (d->animation.state() == QAbstractAnimation::Running) // avoid warning if never started yet
d->animation.pause();
}
void KBusyIndicatorWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
start();
}
void KBusyIndicatorWidget::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
stop();
}
void KBusyIndicatorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
d->paintCenter = QPointF(event->size().width() / 2.0, //
event->size().height() / 2.0);
}
void KBusyIndicatorWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// Rotate around the center and then reset back to origin for icon painting.
painter.translate(d->paintCenter);
painter.rotate(d->rotation);
painter.translate(-d->paintCenter);
d->icon.paint(&painter, rect());
}
bool KBusyIndicatorWidget::event(QEvent *event)
{
// Only overridden to be flexible WRT binary compatible in the future.
// Overriding later has potential to change the call going through
// the vtable or not.
return QWidget::event(event);
}
#include "moc_kbusyindicatorwidget.cpp"
@@ -0,0 +1,85 @@
/*
SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KBUSYINDICATORWIDGET_H
#define KBUSYINDICATORWIDGET_H
#include <QWidget>
#include <kwidgetsaddons_export.h>
#include <memory>
/**
* @class KBusyIndicatorWidget kbusyindicatorwidget.h KBusyIndicatorWidget
*
* @brief Rotating spinning icon to indicate busyness
*
* When you need to communicate to the user that your application is busy with
* something you'll want to use a KBusyIndicatorWidget to display an infinitely
* spinnning indicator icon.
*
* A way of using this widget is to combine it with a QLabel to construct a
* status line:
*
* ```
* auto layout = new QHBoxLayout;
* layout->addWidget(new KBusyIndicatorWidget);
* layout->addWidget(new QLabel(QStringLiteral("Watering the flowers...")));
* ```
*
* @image html kbusyindicatorwidget.png "KBusyIndicatorWidget with label"
*
* KBusyIndicatorWidget is set apart from KPixmapSequenceWidget in that it
* does not render a pixmap sequence but rather animates a scaled Icon.
* It can support multiple semi-arbitrary sizes and quality is only limited
* by the resolution of available icons. It is also easier to use as its use
* is more specific.
*
* @since 5.61.0
*/
class KWIDGETSADDONS_EXPORT KBusyIndicatorWidget : public QWidget
{
Q_OBJECT
public:
/**
* Create a new KBusyIndicatorWidget widget
*/
explicit KBusyIndicatorWidget(QWidget *parent = nullptr);
/**
* Destroy the widget
*/
~KBusyIndicatorWidget() override;
/**
* Return the smallest reasonable size for the widget
*
* @return Minimum size as a QSize object
*/
QSize minimumSizeHint() const override;
public Q_SLOTS:
/**
* Start the spinning animation
*/
void start();
/**
* Stop the spinning animation
*/
void stop();
protected:
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *) override;
bool event(QEvent *event) override;
private:
std::unique_ptr<class KBusyIndicatorWidgetPrivate> const d;
};
#endif // KBUSYINDICATORWIDGET_H
@@ -0,0 +1,357 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kcapacitybar.h"
#include "kstyleextensions.h"
#include <math.h>
#include <QLinearGradient>
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
#include <QStyle>
#include <QStyleOptionProgressBar>
#define ROUND_MARGIN 6
#define VERTICAL_SPACING 1
static const int LightShade = 100;
static const int MidShade = 200;
static const int DarkShade = 300;
class KCapacityBarPrivate
{
public:
KCapacityBarPrivate(KCapacityBar::DrawTextMode drawTextMode)
: drawTextMode(drawTextMode)
{
}
QString text;
int value = 0;
bool fillFullBlocks = true;
bool continuous = true;
int barHeight = 12;
Qt::Alignment horizontalTextAlignment = Qt::AlignCenter;
QStyle::ControlElement ce_capacityBar = QStyle::ControlElement(0);
KCapacityBar::DrawTextMode drawTextMode;
};
KCapacityBar::KCapacityBar(QWidget *parent)
: KCapacityBar(DrawTextOutline, parent)
{
}
KCapacityBar::KCapacityBar(KCapacityBar::DrawTextMode drawTextMode, QWidget *parent)
: QWidget(parent)
, d(new KCapacityBarPrivate(drawTextMode))
{
d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this);
}
KCapacityBar::~KCapacityBar() = default;
void KCapacityBar::setValue(int value)
{
d->value = value;
update();
}
int KCapacityBar::value() const
{
return d->value;
}
void KCapacityBar::setText(const QString &text)
{
bool updateGeom = d->text.isEmpty() || text.isEmpty();
d->text = text;
if (updateGeom) {
updateGeometry();
}
#ifndef QT_NO_ACCESSIBILITY
setAccessibleName(text);
#endif
update();
}
QString KCapacityBar::text() const
{
return d->text;
}
void KCapacityBar::setFillFullBlocks(bool fillFullBlocks)
{
d->fillFullBlocks = fillFullBlocks;
update();
}
bool KCapacityBar::fillFullBlocks() const
{
return d->fillFullBlocks;
}
void KCapacityBar::setContinuous(bool continuous)
{
d->continuous = continuous;
update();
}
bool KCapacityBar::continuous() const
{
return d->continuous;
}
void KCapacityBar::setBarHeight(int barHeight)
{
// automatically convert odd values to even. This will make the bar look
// better.
d->barHeight = (barHeight % 2) ? barHeight + 1 : barHeight;
updateGeometry();
}
int KCapacityBar::barHeight() const
{
return d->barHeight;
}
void KCapacityBar::setHorizontalTextAlignment(Qt::Alignment horizontalTextAlignment)
{
Qt::Alignment alignment = horizontalTextAlignment;
// if the value came with any vertical alignment flag, remove it.
alignment &= ~Qt::AlignTop;
alignment &= ~Qt::AlignBottom;
alignment &= ~Qt::AlignVCenter;
d->horizontalTextAlignment = alignment;
update();
}
Qt::Alignment KCapacityBar::horizontalTextAlignment() const
{
return d->horizontalTextAlignment;
}
void KCapacityBar::setDrawTextMode(DrawTextMode mode)
{
d->drawTextMode = mode;
update();
}
KCapacityBar::DrawTextMode KCapacityBar::drawTextMode() const
{
return d->drawTextMode;
}
void KCapacityBar::drawCapacityBar(QPainter *p, const QRect &rect) const
{
if (d->ce_capacityBar) {
QStyleOptionProgressBar opt;
opt.initFrom(this);
opt.rect = rect;
opt.minimum = 0;
opt.maximum = 100;
opt.progress = d->value;
opt.state |= QStyle::State_Horizontal;
opt.text = d->text;
opt.textAlignment = Qt::AlignCenter;
opt.textVisible = !d->text.isEmpty();
style()->drawControl(d->ce_capacityBar, &opt, p, this);
return;
}
p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
p->save();
QRect drawRect(rect);
if (d->drawTextMode == DrawTextOutline) {
drawRect.setHeight(d->barHeight);
}
QPainterPath outline;
outline.moveTo(rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
outline.lineTo(rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, rect.top());
outline.quadTo(rect.left() + drawRect.width() + ROUND_MARGIN / 2,
drawRect.height() / 2 + rect.top(),
rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1,
drawRect.height() + rect.top());
outline.lineTo(rect.left() + ROUND_MARGIN / 4 + 1, drawRect.height() + rect.top());
outline.quadTo(-ROUND_MARGIN / 2 + rect.left(), drawRect.height() / 2 + rect.top(), rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
const QColor fillColor = palette().window().color().darker(DarkShade);
p->fillPath(outline, QColor(fillColor.red(), fillColor.green(), fillColor.blue(), 50));
QRadialGradient bottomGradient(QPointF(rect.width() / 2, drawRect.bottom() + 1), rect.width() / 2);
bottomGradient.setColorAt(0, palette().window().color().darker(LightShade));
bottomGradient.setColorAt(1, Qt::transparent);
p->fillRect(QRect(rect.left(), drawRect.bottom() + rect.top(), rect.width(), 1), bottomGradient);
p->translate(rect.left() + 2, rect.top() + 1);
drawRect.setWidth(drawRect.width() - 4);
drawRect.setHeight(drawRect.height() - 2);
QPainterPath path;
path.moveTo(ROUND_MARGIN / 4, 0);
path.lineTo(drawRect.width() - ROUND_MARGIN / 4, 0);
path.quadTo(drawRect.width() + ROUND_MARGIN / 2, drawRect.height() / 2, drawRect.width() - ROUND_MARGIN / 4, drawRect.height());
path.lineTo(ROUND_MARGIN / 4, drawRect.height());
path.quadTo(-ROUND_MARGIN / 2, drawRect.height() / 2, ROUND_MARGIN / 4, 0);
QLinearGradient linearGradient(0, 0, 0, drawRect.height());
linearGradient.setColorAt(0.5, palette().window().color().darker(MidShade));
linearGradient.setColorAt(1, palette().window().color().darker(LightShade));
p->fillPath(path, linearGradient);
p->setBrush(Qt::NoBrush);
p->setPen(Qt::NoPen);
if (d->continuous || !d->fillFullBlocks) {
int start = (layoutDirection() == Qt::LeftToRight) ? -1 : (drawRect.width() + 2) - (drawRect.width() + 2) * (d->value / 100.0);
p->setClipRect(QRect(start, 0, (drawRect.width() + 2) * (d->value / 100.0), drawRect.height()), Qt::IntersectClip);
}
int left = (layoutDirection() == Qt::LeftToRight) ? 0 : drawRect.width();
int right = (layoutDirection() == Qt::LeftToRight) ? drawRect.width() : 0;
int roundMargin = (layoutDirection() == Qt::LeftToRight) ? ROUND_MARGIN : -ROUND_MARGIN;
int spacing = 2;
int verticalSpacing = VERTICAL_SPACING;
int slotWidth = 6;
int start = roundMargin / 4;
QPainterPath internalBar;
internalBar.moveTo(left + roundMargin / 4, 0);
internalBar.lineTo(right - roundMargin / 4, 0);
internalBar.quadTo(right + roundMargin / 2, drawRect.height() / 2, right - roundMargin / 4, drawRect.height());
internalBar.lineTo(left + roundMargin / 4, drawRect.height());
internalBar.quadTo(left - roundMargin / 2, drawRect.height() / 2, left + roundMargin / 4, 0);
QLinearGradient fillInternalBar(left, 0, right, 0);
fillInternalBar.setColorAt(0, palette().window().color().darker(MidShade));
fillInternalBar.setColorAt(0.5, palette().window().color().darker(LightShade));
fillInternalBar.setColorAt(1, palette().window().color().darker(MidShade));
if (d->drawTextMode == KCapacityBar::DrawTextInline) {
p->save();
p->setOpacity(p->opacity() * 0.7);
}
if (!d->continuous) {
int numSlots = (drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) / (slotWidth + spacing);
int stopSlot = floor((numSlots + 2) * (d->value / 100.0));
int plusOffset = d->fillFullBlocks ? ((drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) - (numSlots * (slotWidth + spacing))) / 2.0 : 0;
if (!d->fillFullBlocks || stopSlot) {
QPainterPath firstSlot;
firstSlot.moveTo(left + roundMargin / 4, verticalSpacing);
firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, verticalSpacing);
firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, drawRect.height() - verticalSpacing);
firstSlot.lineTo(left + roundMargin / 4, drawRect.height() - verticalSpacing);
firstSlot.quadTo(left, drawRect.height() / 2, left + roundMargin / 4, verticalSpacing);
p->fillPath(firstSlot, fillInternalBar);
start += slotWidth + spacing + plusOffset;
bool stopped = false;
for (int i = 0; i < numSlots + 1; i++) {
if (d->fillFullBlocks && (i == (stopSlot + 1))) {
stopped = true;
break;
}
p->fillRect(QRect(rect.left() + start, rect.top() + verticalSpacing, slotWidth, drawRect.height() - verticalSpacing * 2), fillInternalBar);
start += slotWidth + spacing;
}
if (!d->fillFullBlocks || (!stopped && (stopSlot != (numSlots + 1)) && (stopSlot != numSlots))) {
QPainterPath lastSlot;
lastSlot.moveTo(start, verticalSpacing);
lastSlot.lineTo(start, drawRect.height() - verticalSpacing);
lastSlot.lineTo(start + slotWidth + plusOffset, drawRect.height() - verticalSpacing);
lastSlot.quadTo(start + roundMargin, drawRect.height() / 2, start + slotWidth + plusOffset, verticalSpacing);
lastSlot.lineTo(start, verticalSpacing);
p->fillPath(lastSlot, fillInternalBar);
}
}
} else {
p->fillPath(internalBar, fillInternalBar);
}
if (d->drawTextMode == KCapacityBar::DrawTextInline) {
p->restore();
}
p->save();
p->setClipping(false);
QRadialGradient topGradient(QPointF(rect.width() / 2, drawRect.top()), rect.width() / 2);
const QColor fillTopColor = palette().window().color().darker(LightShade);
topGradient.setColorAt(0, QColor(fillTopColor.red(), fillTopColor.green(), fillTopColor.blue(), 127));
topGradient.setColorAt(1, Qt::transparent);
p->fillRect(QRect(rect.left(), rect.top() + drawRect.top(), rect.width(), 2), topGradient);
p->restore();
p->save();
p->setClipRect(QRect(-1, 0, rect.width(), drawRect.height() / 2), Qt::ReplaceClip);
QLinearGradient glassGradient(0, -5, 0, drawRect.height());
const QColor fillGlassColor = palette().base().color();
glassGradient.setColorAt(0, QColor(fillGlassColor.red(), fillGlassColor.green(), fillGlassColor.blue(), 255));
glassGradient.setColorAt(1, Qt::transparent);
p->fillPath(internalBar, glassGradient);
p->restore();
p->restore();
if (d->drawTextMode == KCapacityBar::DrawTextInline) {
QRect rect(drawRect);
rect.setHeight(rect.height() + 4);
p->drawText(rect, Qt::AlignCenter, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width() - 2 * ROUND_MARGIN));
} else {
p->drawText(rect, Qt::AlignBottom | d->horizontalTextAlignment, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width()));
}
}
QSize KCapacityBar::minimumSizeHint() const
{
int width = fontMetrics().boundingRect(d->text).width() + ((d->drawTextMode == KCapacityBar::DrawTextInline) ? ROUND_MARGIN * 2 : 0);
int height = (d->drawTextMode == KCapacityBar::DrawTextInline) ? qMax(fontMetrics().height(), d->barHeight)
: (d->text.isEmpty() ? 0 : fontMetrics().height() + VERTICAL_SPACING * 2) + d->barHeight;
if (height % 2) {
height++;
}
return QSize(width, height);
}
void KCapacityBar::paintEvent(QPaintEvent *event)
{
QPainter p(this);
p.setClipRect(event->rect());
drawCapacityBar(&p, contentsRect());
p.end();
}
void KCapacityBar::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::StyleChange) {
d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this);
}
}
#include "moc_kcapacitybar.cpp"
@@ -0,0 +1,222 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCAPACITYBAR_H
#define KCAPACITYBAR_H
#include <QWidget>
#include <memory>
#include <kwidgetsaddons_export.h>
class QPaintEvent;
/**
* @class KCapacityBar kcapacitybar.h KCapacityBar
*
* @brief This widget shows a bar which is filled to show the level of usage of
* a certain device.
*
* This widget represents a bar which goal is to show the level of usage of a
* device. Its look is similar to a progress bar, but different, because this
* widget does not want to give a notion of progress.
*
* @since 4.2
*
* \image html kcapacitybar.png "KCapacityBar Widget"
*
* @author Rafael Fernández López <ereslibre@kde.org>
*/
class KWIDGETSADDONS_EXPORT KCapacityBar : public QWidget
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue)
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(DrawTextMode drawTextMode READ drawTextMode WRITE setDrawTextMode)
Q_PROPERTY(bool fillFullBlocks READ fillFullBlocks WRITE setFillFullBlocks)
Q_PROPERTY(bool continuous READ continuous WRITE setContinuous)
Q_PROPERTY(int barHeight READ barHeight WRITE setBarHeight)
Q_PROPERTY(Qt::Alignment horizontalTextAlignment READ horizontalTextAlignment WRITE setHorizontalTextAlignment)
public:
enum DrawTextMode {
DrawTextInline = 0, ///< If any text set, draw it into the capacity bar
DrawTextOutline, ///< If any text set, draw it out of the capacity bar
};
Q_ENUM(DrawTextMode)
/**
* Constructs a capacity bar with DrawTextOutline as draw text mode.
* @param parent The parent of the widget.
* @since 5.24
*/
explicit KCapacityBar(QWidget *parent = nullptr);
/**
* Capacity bar constructor.
*
* @param drawTextMode If any text set, whether to draw it into the capacity bar
* or not.
* @param parent The parent of the widget.
*/
explicit KCapacityBar(DrawTextMode drawTextMode, QWidget *parent = nullptr);
~KCapacityBar() override;
/**
* Capacity bar fill value.
*
* @param value This parameter can take values from 0 to 100.
*
* @note Its value is 0 by default.
*/
void setValue(int value);
/**
* @return The fill value of the capacity bar.
*/
int value() const;
/**
* Sets the text for the capacity bar.
*
* @param text The text that the capacity bar will show.
*
* @note This is an empty string by default.
*/
void setText(const QString &text);
/**
* @return The text that the capacity bar will show.
*/
QString text() const;
/**
* When the capacity bar is non-continuous, sets whether the last block
* shown should be drawn full or can be cut off (depending on the capacity
* bar width, and the value set on it).
*
* @param fillFullBlocks If true, the last block drawn will be fully filled,
* on other case, the last block drawn could be cut off.
*
* @note This method is only relevant if the capacity bar is in
* non-continuous mode.
*
* @note Its value is true by default.
*
* @see setContinuous, continuous
*/
void setFillFullBlocks(bool fillFullBlocks);
/**
* @return Whether the last block shown can be cut off when necessary.
*/
bool fillFullBlocks() const;
/**
* Sets whether the fill of the capacity bar should be continuous or in
* block mode.
*
* @param continuous If true, the fill of the capacity bar is done in a
* continuous way. In other case, the fill is done with
* separated blocks.
*
* @note Its value is true by default.
*/
void setContinuous(bool continuous);
/**
* @return Whether the fill of the capacity bar should be continuous or
* block-based.
*/
bool continuous() const;
/**
* Sets the height (in pixels) of the bar.
*
* @param barHeight The preferred height (in pixels) of the capacity bar.
*
* @note If you set a certain text and the capacity bar is in inline mode,
* the height of the bar will be the maximum of the font height and
* this value.
*
* @note If you set a certain text and the capacity bar is in outline mode,
* the height of the whole capacity bar will be bigger than this
* value. Take in count the height of this widget is got from adding
* the bar height, the font metrics height and a small separator
* between the bar and the outline text.
*
* @note Its value is 12 pixels by default.
*/
void setBarHeight(int barHeight);
/**
* @return The preferred height of the capacity bar.
*/
int barHeight() const;
/**
* If the capacity bar is in outline text mode, draw the text with
* @p textAlignment alignment.
*
* @param textAlignment Sets the horizontal alignment for the text if
* the capacity bar is in outline text mode.
*
* @note If @p textAlignemt contains vertical alignment flags, they will be
* ignored.
*
* @note If the capacity bar is in inline text mode, the text is always
* centered, and both vertical and horizontal flags set through this
* method are ignored.
*
* @note Its value is centered by default.
*/
void setHorizontalTextAlignment(Qt::Alignment textAlignment);
/**
* @return The horizontal alignment for the text that will be drawn.
*/
Qt::Alignment horizontalTextAlignment() const;
/**
* Set the way text is drawn if any is set
*
* @param mode If any text set, whether to draw it into the capacity bar
* or not.
*/
void setDrawTextMode(DrawTextMode mode);
/**
* The way text is drawn, inside the capacity bar or outside of it
*/
DrawTextMode drawTextMode() const;
/**
* This method allows you to draw the widget, directly, for example on
* item delegates. You only need the painter object and the rect where
* this widget should be drawn.
*/
void drawCapacityBar(QPainter *p, const QRect &rect) const;
// Reimplemented from QWidget
QSize minimumSizeHint() const override;
protected:
// Reimplemented from QWidget
void paintEvent(QPaintEvent *event) override;
// Reimplemented from QWidget
void changeEvent(QEvent *event) override;
private:
/**
* @internal
*/
std::unique_ptr<class KCapacityBarPrivate> const d;
};
#endif
@@ -0,0 +1,859 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# This script generates a data file containing all Unicode information needed
# by KCharSelect.
#
##############################################################################
# SPDX-FileCopyrightText: 2007 Daniel Laidig <d.laidig@gmx.de>
# SPDX-FileCopyrightText: 2016 John Zaitseff <J.Zaitseff@zap.org.au>
#
# SPDX-License-Identifier: LGPL-2.0-or-later
##############################################################################
#
# The current directory must contain the following files that can be found at
# http://www.unicode.org/Public/UNIDATA/:
# - UnicodeData.txt
# - Unihan_Readings.txt (you need to uncompress it from Unihan.zip)
# - NamesList.txt
# - Blocks.txt
#
# The generated file is named "kcharselect-data" and has to be put in
# kwidgetsaddons/src. Additionally a translation dummy named
# "kcharselect-translation.cpp" is generated and has to be placed in the same
# directory.
#
# FILE STRUCTURE
#
# The generated file is a binary file. The first 40 bytes are the header and
# contain the position of each part of the file. Each entry is uint32.
#
# pos content
# 0 names strings begin
# 4 names offsets begin
# 8 details strings begin
# 12 details offsets begin
# 16 block strings begin
# 20 block offsets begin
# 24 section strings begin
# 28 section offsets begin
# 32 unihan strings begin
# 36 unihan offsets begin
#
# The string parts always contain all strings in a row, followed by a 0x00
# byte. There is one exception: The data for seeAlso in details is only 2
# bytes (as is always is _one_ unicode character) and _not_ followed by a 0x00
# byte.
#
# The offset parts contain entries with a fixed length. Unicode characters
# are always uint16 and offsets uint32. Offsets are positions in the data
# file.
#
# names_offsets:
# each entry 6 bytes
# 16bit: unicode
# 32bit: offset to name in names_strings
#
# names_strings:
# the first byte is the category (same values as QChar::Category),
# directly followed by the character name (terminated by 0x00)
#
# nameslist_offsets:
# char, alias, alias_count, note, note_count, approxEquiv, approxEquiv_coutn, equiv, equiv_count, seeAlso, seeAlso_count
# 16 32 8 32 8 32 8 32 8 32 8
# => each entry 27 bytes
#
# blocks_offsets:
# each entry 4 bytes
# 16bit: start unicode
# 16bit: end unicode
# Note that there is no string offset.
#
# section_offsets:
# each entry 4 bytes
# 16bit: section offset
# 16bit: block offset
# Note that these offsets are _not_ positions in the data file but indexes.
# For example 0x0403 means the fourth section includes the third block.
#
# unihan_offsets:
# each entry 30 bytes
# 16bit: unicode
# 32bit: offset to unihan_strings for Definition
# 32bit: offset to unihan_strings for Cantonese
# 32bit: offset to unihan_strings for Mandarin
# 32bit: offset to unihan_strings for Tang
# 32bit: offset to unihan_strings for Korean
# 32bit: offset to unihan_strings for JapaneseKun
# 32bit: offset to unihan_strings for JapaneseOn
from struct import *
import sys
import re
import io
# Based on http://www.unicode.org/charts/, updated for Unicode 9.0
sectiondata = '''
SECTION European Scripts
Basic Latin
Latin-1 Supplement
Latin Extended-A
Latin Extended-B
Latin Extended-C
Latin Extended-D
Latin Extended-E
Latin Extended Additional
Armenian
Coptic
Cyrillic
Cyrillic Supplement
Cyrillic Extended-A
Cyrillic Extended-B
Cyrillic Extended-C
Georgian
Georgian Supplement
Georgian Extended
Glagolitic
Greek and Coptic
Greek Extended
Ogham
Runic
SECTION African Scripts
Bamum
Ethiopic
Ethiopic Supplement
Ethiopic Extended
Ethiopic Extended-A
NKo
Tifinagh
Vai
SECTION Middle Eastern Scripts
Arabic
Arabic Supplement
Arabic Extended-A
Arabic Extended-B
Arabic Presentation Forms-A
Arabic Presentation Forms-B
Hebrew
Mandaic
Samaritan
Syriac
Syriac Supplement
SECTION Central Asian Scripts
Mongolian
Phags-pa
Tibetan
SECTION South Asian Scripts
Bengali
Common Indic Number Forms
Devanagari
Devanagari Extended
Gujarati
Gurmukhi
Kannada
Lepcha
Limbu
Malayalam
Meetei Mayek
Meetei Mayek Extensions
Ol Chiki
Oriya
Saurashtra
Sinhala
Syloti Nagri
Tamil
Telugu
Thaana
Vedic Extensions
SECTION Southeast Asian Scripts
Cham
Kayah Li
Khmer
Khmer Symbols
Lao
Myanmar
Myanmar Extended-A
Myanmar Extended-B
New Tai Lue
Tai Le
Tai Tham
Tai Viet
Thai
SECTION Indonesia and Oceania Scripts
Balinese
Batak
Buginese
Buhid
Hanunoo
Javanese
Rejang
Sundanese
Sundanese Supplement
Tagalog
Tagbanwa
SECTION East Asian Scripts
Bopomofo
Bopomofo Extended
CJK Unified Ideographs
CJK Unified Ideographs Extension A
CJK Compatibility
CJK Compatibility Ideographs
CJK Compatibility Forms
CJK Radicals Supplement
CJK Strokes
CJK Symbols and Punctuation
Enclosed CJK Letters and Months
Hangul Jamo
Hangul Jamo Extended-A
Hangul Jamo Extended-B
Hangul Compatibility Jamo
Hangul Syllables
Hiragana
Ideographic Description Characters
Kanbun
Kangxi Radicals
Katakana
Katakana Phonetic Extensions
Lisu
Yi Radicals
Yi Syllables
SECTION American Scripts
Cherokee
Cherokee Supplement
Unified Canadian Aboriginal Syllabics
Unified Canadian Aboriginal Syllabics Extended
SECTION Symbols
General Punctuation
Alchemical Symbols
Braille Patterns
Chess Symbols
Control Pictures
Currency Symbols
Dingbats
Domino Tiles
Emoticons
Enclosed Alphanumerics
Enclosed Alphanumeric Supplement
Enclosed Ideographic Supplement
Mahjong Tiles
Miscellaneous Symbols
Miscellaneous Symbols and Pictographs
Miscellaneous Technical
Optical Character Recognition
Ornamental Dingbats
Playing Cards
Small Form Variants
Supplemental Punctuation
Supplemental Symbols and Pictographs
Symbols and Pictographs Extended-A
Symbols for Legacy Computing
Transport and Map Symbols
Vertical Forms
Yijing Hexagram Symbols
SECTION Mathematical Symbols
Arrows
Block Elements
Box Drawing
Geometric Shapes
Geometric Shapes Extended
Letterlike Symbols
Mathematical Operators
Miscellaneous Mathematical Symbols-A
Miscellaneous Mathematical Symbols-B
Miscellaneous Symbols and Arrows
Number Forms
Superscripts and Subscripts
Supplemental Arrows-A
Supplemental Arrows-B
Supplemental Arrows-C
Supplemental Mathematical Operators
SECTION Phonetic Symbols
IPA Extensions
Modifier Tone Letters
Phonetic Extensions
Phonetic Extensions Supplement
Spacing Modifier Letters
SECTION Combining Diacritics
Combining Diacritical Marks
Combining Diacritical Marks Extended
Combining Diacritical Marks Supplement
Combining Diacritical Marks for Symbols
Combining Half Marks
SECTION Other
Alphabetic Presentation Forms
Halfwidth and Fullwidth Forms
High Private Use Surrogates
High Surrogates
Low Surrogates
Private Use Area
Specials
Variation Selectors
'''
categoryMap = { # same values as QChar::Category
"Mn": 1,
"Mc": 2,
"Me": 3,
"Nd": 4,
"Nl": 5,
"No": 6,
"Zs": 7,
"Zl": 8,
"Zp": 9,
"Cc": 10,
"Cf": 11,
"Cs": 12,
"Co": 13,
"Cn": 14,
"Lu": 15,
"Ll": 16,
"Lt": 17,
"Lm": 18,
"Lo": 19,
"Pc": 20,
"Pd": 21,
"Ps": 22,
"Pe": 23,
"Pi": 24,
"Pf": 25,
"Po": 26,
"Sm": 27,
"Sc": 28,
"Sk": 29,
"So": 30
}
# Temporary code point remapping
#
# Initial SMP support without needing a new data file format
# - BMP U+Fxxx are remapped to U+Exxx
# - SMP symbols U+1Fxxx are remapped to U+Fxxx
# - Private Use Area is limited to U+F000 ... U+F8FF
def remap(char):
cp = int(char, 16)
if cp >= 0xE000 and cp <= 0xFFFF:
return "E"+char[1:]
if cp >= 0x1F000 and cp <= 0x1FFFF:
return char[1:]
return char
class Names:
def __init__(self):
self.names = []
self.controlpos = -1
def addName(self, uni, name, category):
self.names.append([uni, name, category])
def calculateStringSize(self):
size = 0
hadcontrol = False
for entry in self.names:
if entry[1] == "<control>":
if not hadcontrol:
size += len(entry[1]) + 2
hadcontrol = True
else:
size += len(entry[1]) + 2
return size
def calculateOffsetSize(self):
return len(self.names)*6
def writeStrings(self, out, pos):
hadcontrol = False
for entry in self.names:
if entry[1] == "<control>":
if not hadcontrol:
out.write(pack("=b", entry[2]))
out.write(entry[1].encode("utf-8") + b"\0")
size = len(entry[1]) + 2
entry[1] = pos
self.controlpos = pos
pos += size
hadcontrol = True
else:
entry[1] = self.controlpos
else:
out.write(pack("=b", entry[2]))
out.write(entry[1].encode("utf-8") + b"\0")
size = len(entry[1]) + 2
entry[1] = pos
pos += size
return pos
def writeOffsets(self, out, pos):
for entry in self.names:
out.write(pack("=HI", int(entry[0], 16), entry[1]))
pos += 6
return pos
class Details:
def __init__(self):
self.details = {}
def addEntry(self, char, category, text):
if not char in self.details:
self.details[char] = {}
if not category in self.details[char]:
self.details[char][category] = []
self.details[char][category].append(text)
def calculateStringSize(self):
size = 0
for char in self.details.values():
for cat in char.values():
for s in cat:
if type(s) is str:
size += len(s.encode("utf-8")) + 1
else:
size += 2
return size
def calculateOffsetSize(self):
return len(self.details)*27
def writeStrings(self, out, pos):
for char in self.details.values():
for cat in char.values():
for i in range(0, len(cat)):
s = cat[i]
if type(s) is str:
out.write(s.encode("utf-8") + b"\0")
size = len(s.encode("utf-8")) + 1
else:
out.write(pack("=H", s))
size = 2
cat[i] = pos
pos += size
return pos
def writeOffsets(self, out, pos):
for char in self.details.keys():
alias = 0
alias_count = 0
note = 0
note_count = 0
approxEquiv = 0
approxEquiv_count = 0
equiv = 0
equiv_count = 0
seeAlso = 0
seeAlso_count = 0
if "alias" in self.details[char]:
alias = self.details[char]["alias"][0]
alias_count = len(self.details[char]["alias"])
if "note" in self.details[char]:
note = self.details[char]["note"][0]
note_count = len(self.details[char]["note"])
if "approxEquiv" in self.details[char]:
approxEquiv = self.details[char]["approxEquiv"][0]
approxEquiv_count = len(self.details[char]["approxEquiv"])
if "equiv" in self.details[char]:
equiv = self.details[char]["equiv"][0]
equiv_count = len(self.details[char]["equiv"])
if "seeAlso" in self.details[char]:
seeAlso = self.details[char]["seeAlso"][0]
seeAlso_count = len(self.details[char]["seeAlso"])
out.write(pack("=HIbIbIbIbIb", char, alias, alias_count, note, note_count, approxEquiv, approxEquiv_count, equiv, equiv_count, seeAlso, seeAlso_count))
pos += 27
return pos
class SectionsBlocks:
def __init__(self):
self.sections = []
self.blocks = []
self.blockList = []
self.sectionList = []
def addBlock(self, begin, end, name):
self.blocks.append([begin, end, name])
self.blockList.append(name)
def addSection(self, section, block):
self.sections.append([section, block])
if not section in self.sectionList:
self.sectionList.append(section)
def calculateBlockStringSize(self):
size = 0
for block in self.blocks:
size += len(block[2]) + 1
return size
def calculateBlockOffsetSize(self):
return len(self.blocks) * 4
def calculateSectionStringSize(self):
size = 0
lastsection = ""
for section in self.sections:
if section[0] != lastsection:
size += len(section[0]) + 1
lastsection = section[0]
return size
def calculateSectionOffsetSize(self):
return len(self.sections) * 4
def writeBlockStrings(self, out, pos):
index = 0
for block in self.blocks:
out.write(block[2].encode("utf-8") + b"\0")
size = len(block[2].encode("utf-8")) + 1
found = False
for section in self.sections:
if section[1] == block[2]:
print("found", section)
section[1] = index
found = True
if not found:
print("Error: Did not find any category for block \""+block[2]+"\"")
sys.exit(1)
block[2] = index
pos += size
index += 1
return pos
def writeBlockOffsets(self, out, pos):
for block in self.blocks:
out.write(pack("=HH", int(block[0], 16), int(block[1], 16)))
pos += 4
return pos
def writeSectionStrings(self, out, pos):
lastsection = ""
lastpos = 0
index = -1
for section in self.sections:
if section[0] != lastsection:
index += 1
lastsection = section[0]
out.write(section[0].encode("utf-8") + b"\0")
size = len(section[0].encode("utf-8")) + 1
section[0] = index
lastpos = pos
pos += size
else:
section[0] = index
return pos
def writeSectionOffsets(self, out, pos):
for section in self.sections:
out.write(pack("=HH", section[0], section[1]))
pos += 4
return pos
def getBlockList(self):
return self.blockList
def getSectionList(self):
return self.sectionList
class Unihan:
def __init__(self):
self.unihan = {}
def addUnihan(self, uni, category, value):
uni = int(uni, 16)
if category != "kDefinition" and category != "kCantonese" and category != "kMandarin" and category != "kTang" and category != "kKorean" and category != "kJapaneseKun" and category != "kJapaneseOn":
return
if not uni in self.unihan:
self.unihan[uni] = [None, None, None, None, None, None, None]
if category == "kDefinition":
self.unihan[uni][0] = value
elif category == "kCantonese":
self.unihan[uni][1] = value
elif category == "kMandarin":
self.unihan[uni][2] = value
elif category == "kTang":
self.unihan[uni][3] = value
elif category == "kKorean":
self.unihan[uni][4] = value
elif category == "kJapaneseKun":
self.unihan[uni][5] = value
elif category == "kJapaneseOn":
self.unihan[uni][6] = value
def calculateStringSize(self):
size = 0
for char in self.unihan.keys():
for entry in self.unihan[char]:
if entry != None:
size += len(entry.encode("utf-8")) + 1
return size
def calculateOffsetSize(self):
return len(self.unihan) * 30
def writeStrings(self, out, pos):
for char in self.unihan.keys():
for i in range(0, 7):
if self.unihan[char][i] != None:
out.write(self.unihan[char][i].encode("utf-8") + b"\0")
size = len(self.unihan[char][i].encode("utf-8")) + 1
self.unihan[char][i] = pos
pos += size
return pos
def writeOffsets(self, out, pos):
for char in self.unihan.keys():
out.write(pack("=H", char))
for i in range(0, 7):
if self.unihan[char][i] != None:
out.write(pack("=I", self.unihan[char][i]))
else:
out.write(pack("=I", 0))
pos += 30
return pos
class Parser:
def parseUnicodeData(self, inUnicodeData, names):
regexp = re.compile(r'^([^;]+);([^;]+);([^;]+)')
for line in inUnicodeData:
line = line[:-1]
m = regexp.match(line)
if not m:
continue
uni = remap(m.group(1))
name = m.group(2)
category = m.group(3)
if len(uni) > 4:
continue
names.addName(uni, name, categoryMap[category])
def parseDetails(self, inNamesList, details):
invalidRegexp = re.compile(r'^@')
unicodeRegexp = re.compile(r'^([0-9A-F]+)')
aliasRegexp = re.compile(r'^\s+=\s+(.+)$') #equal
seeAlsoRegexp1 = re.compile(r'^\s+x\s+.*\s([0-9A-F]{4,6})\)$') #ex
seeAlsoRegexp2 = re.compile(r'^\s+x\s+([0-9A-F]{4,6})$') #ex
noteRegexp = re.compile(r'^\s+\*\s+(.+)$') #star
approxEquivalentRegexp = re.compile(r'^\s+#\s+(.+)$') #pound
equivalentRegexp = re.compile(r'^\s+:\s+(.+)$') #colon
drop = 0
currChar = 0
for line in inNamesList:
line = line[:-1]
m1 = unicodeRegexp.match(line)
m2 = aliasRegexp.match(line)
m3 = noteRegexp.match(line)
m4 = approxEquivalentRegexp.match(line)
m5 = equivalentRegexp.match(line)
m6 = seeAlsoRegexp1.match(line)
m7 = seeAlsoRegexp2.match(line)
if invalidRegexp.match(line):
continue
elif m1:
mg1 = remap(m1.group(1))
currChar = int(mg1, 16)
if len(mg1) > 4:
drop = 1
continue
elif drop == 1:
continue
elif m2:
value = m2.group(1)
details.addEntry(currChar, "alias", value)
elif m3:
value = m3.group(1)
details.addEntry(currChar, "note", value)
elif m4:
value = m4.group(1)
details.addEntry(currChar, "approxEquiv", value)
elif m5:
value = m5.group(1)
details.addEntry(currChar, "equiv", value)
elif m6:
value = int(remap(m6.group(1)), 16)
if value < 0x10000:
details.addEntry(currChar, "seeAlso", value)
elif m7:
value = int(remap(m7.group(1)), 16)
if value < 0x10000:
details.addEntry(currChar, "seeAlso", value)
def parseBlocks(self, inBlocks, sectionsBlocks):
regexp = re.compile(r'^([0-9A-F]+)\.\.([0-9A-F]+); (.+)$')
for line in inBlocks:
line = line[:-1]
m = regexp.match(line)
if not m:
continue
m1 = remap(m.group(1))
m2 = remap(m.group(2))
if len(m1) > 4:
continue
sectionsBlocks.addBlock(m1, m2, m.group(3))
def parseSections(self, inSections, sectionsBlocks):
currSection = ""
for line in inSections:
line = line[:-1]
if len(line) == 0:
continue
temp = line.split(" ")
if temp[0] == "SECTION":
currSection = line[8:]
elif currSection != "":
sectionsBlocks.addSection(currSection, line)
else:
print("error in data file")
sys.exit(1)
def parseUnihan(self, inUnihan, unihan):
regexp = re.compile(r'^U\+([0-9A-F]+)\s+([^\s]+)\s+(.+)$')
count = 0
for line in inUnihan:
if count % 100000 == 0:
print("\b."); sys.stdout.flush()
count += 1
line = line[:-1]
m = regexp.match(line)
if not m:
continue
if len(remap(m.group(1))) <= 4:
unihan.addUnihan(remap(m.group(1)), m.group(2), m.group(3))
def writeTranslationDummy(out, data):
out.write(b"""/* This file is part of the KDE libraries
SPDX-FileCopyrightText: 2007 Daniel Laidig <d.laidig@gmx.de>
SPDX-FileCopyrightText: 2016 John Zaitseff <J.Zaitseff@zap.org.au>
SPDX-License-Identifier: LGPL-2.0-or-later
This file is autogenerated by kcharselect/kcharselect-generate-datafile.py
*/\n\n""")
for group in data:
for entry in group[1]:
out.write(b"QT_TRANSLATE_NOOP3(\"KCharSelectData\", \""+entry.encode("utf-8")+b"\", \""+group[0].encode("utf-8")+b"\");\n")
out = open("kcharselect-data", "wb")
outTranslationDummy = open("kcharselect-translation.cpp", "wb")
inUnicodeData = open("UnicodeData.txt", "r")
inNamesList = open("NamesList.txt", "r")
inBlocks = open("Blocks.txt", "r")
inSections = io.StringIO(sectiondata)
inUnihan = open("Unihan_Readings.txt", "r")
if calcsize('=H') != 2 or calcsize('=I') != 4:
print("Error: Sizes of ushort and uint are not 16 and 32 bit as expected")
sys.exit(1)
names = Names()
details = Details()
sectionsBlocks = SectionsBlocks()
unihan = Unihan()
parser = Parser()
print("========== parsing files ===================")
parser.parseUnicodeData(inUnicodeData, names)
print("."); sys.stdout.flush()
parser.parseDetails(inNamesList, details)
print("\b."); sys.stdout.flush()
parser.parseBlocks(inBlocks, sectionsBlocks)
print("\b."); sys.stdout.flush()
parser.parseSections(inSections, sectionsBlocks)
print("\b."); sys.stdout.flush()
parser.parseUnihan(inUnihan, unihan)
print("\b."); sys.stdout.flush()
print("done.")
pos = 0
#write header, size: 40 bytes
print("========== writing header ==================")
out.write(pack("=I", 40))
print("names strings begin", 40)
namesOffsetBegin = names.calculateStringSize() + 40
out.write(pack("=I", namesOffsetBegin))
print("names offsets begin", namesOffsetBegin)
detailsStringBegin = namesOffsetBegin + names.calculateOffsetSize()
out.write(pack("=I", detailsStringBegin))
print("details strings begin", detailsStringBegin)
detailsOffsetBegin = detailsStringBegin + details.calculateStringSize()
out.write(pack("=I", detailsOffsetBegin))
print("details offsets begin", detailsOffsetBegin)
blocksStringBegin = detailsOffsetBegin + details.calculateOffsetSize()
out.write(pack("=I", blocksStringBegin))
print("block strings begin", blocksStringBegin)
blocksOffsetBegin = blocksStringBegin + sectionsBlocks.calculateBlockStringSize()
out.write(pack("=I", blocksOffsetBegin))
print("block offsets begin", blocksOffsetBegin)
sectionStringBegin = blocksOffsetBegin + sectionsBlocks.calculateBlockOffsetSize()
out.write(pack("=I", sectionStringBegin))
print("section strings begin", sectionStringBegin)
sectionOffsetBegin = sectionStringBegin + sectionsBlocks.calculateSectionStringSize()
out.write(pack("=I", sectionOffsetBegin))
print("section offsets begin", sectionOffsetBegin)
unihanStringBegin = sectionOffsetBegin + sectionsBlocks.calculateSectionOffsetSize()
out.write(pack("=I", unihanStringBegin))
print("unihan strings begin", unihanStringBegin)
unihanOffsetBegin = unihanStringBegin + unihan.calculateStringSize()
out.write(pack("=I", unihanOffsetBegin))
print("unihan offsets begin", unihanOffsetBegin)
end = unihanOffsetBegin + unihan.calculateOffsetSize()
print("end should be", end)
pos += 40
print("========== writing data ====================")
pos = names.writeStrings(out, pos)
print("names strings written, position", pos)
pos = names.writeOffsets(out, pos)
print("names offsets written, position", pos)
pos = details.writeStrings(out, pos)
print("details strings written, position", pos)
pos = details.writeOffsets(out, pos)
print("details offsets written, position", pos)
pos = sectionsBlocks.writeBlockStrings(out, pos)
print("block strings written, position", pos)
pos = sectionsBlocks.writeBlockOffsets(out, pos)
print("block offsets written, position", pos)
pos = sectionsBlocks.writeSectionStrings(out, pos)
print("section strings written, position", pos)
pos = sectionsBlocks.writeSectionOffsets(out, pos)
print("section offsets written, position", pos)
pos = unihan.writeStrings(out, pos)
print("unihan strings written, position", pos)
pos = unihan.writeOffsets(out, pos)
print("unihan offsets written, position", pos)
print("========== writing translation dummy ======")
translationData = [["KCharSelect section name", sectionsBlocks.getSectionList()], ["KCharselect unicode block name",sectionsBlocks.getBlockList()]]
writeTranslationDummy(outTranslationDummy, translationData)
print("done. make sure to copy both kcharselect-data and kcharselect-translation.cpp.")
@@ -0,0 +1,206 @@
/* This file is part of the KDE libraries
SPDX-FileCopyrightText: 2007 Daniel Laidig <d.laidig@gmx.de>
SPDX-FileCopyrightText: 2016 John Zaitseff <J.Zaitseff@zap.org.au>
SPDX-License-Identifier: LGPL-2.0-or-later
This file is autogenerated by kcharselect/kcharselect-generate-datafile.py
*/
QT_TRANSLATE_NOOP3("KCharSelectData", "All", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "European Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "African Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Middle Eastern Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Central Asian Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "South Asian Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Southeast Asian Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Indonesia and Oceania Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "East Asian Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "American Scripts", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Symbols", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Mathematical Symbols", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Phonetic Symbols", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Diacritics", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Other", "KCharSelect section name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Basic Latin", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin-1 Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "IPA Extensions", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Spacing Modifier Letters", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Diacritical Marks", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Greek and Coptic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cyrillic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cyrillic Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Armenian", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hebrew", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Syriac", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Thaana", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "NKo", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Samaritan", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Mandaic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Syriac Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic Extended-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Devanagari", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Bengali", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Gurmukhi", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Gujarati", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Oriya", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tamil", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Telugu", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Kannada", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Malayalam", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Sinhala", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Thai", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Lao", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tibetan", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Myanmar", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Georgian", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hangul Jamo", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ethiopic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ethiopic Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cherokee", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Unified Canadian Aboriginal Syllabics", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ogham", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Runic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tagalog", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hanunoo", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Buhid", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tagbanwa", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Khmer", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Mongolian", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Unified Canadian Aboriginal Syllabics Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Limbu", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tai Le", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "New Tai Lue", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Khmer Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Buginese", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tai Tham", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Diacritical Marks Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Balinese", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Sundanese", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Batak", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Lepcha", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ol Chiki", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cyrillic Extended-C", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Georgian Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Sundanese Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Vedic Extensions", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Phonetic Extensions", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Phonetic Extensions Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Diacritical Marks Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended Additional", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Greek Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "General Punctuation", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Superscripts and Subscripts", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Currency Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Diacritical Marks for Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Letterlike Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Number Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arrows", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Mathematical Operators", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Technical", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Control Pictures", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Optical Character Recognition", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Enclosed Alphanumerics", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Box Drawing", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Block Elements", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Geometric Shapes", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Dingbats", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Mathematical Symbols-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Arrows-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Braille Patterns", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Arrows-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Mathematical Symbols-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Mathematical Operators", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Symbols and Arrows", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Glagolitic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended-C", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Coptic", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Georgian Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tifinagh", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ethiopic Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cyrillic Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Punctuation", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Radicals Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Kangxi Radicals", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ideographic Description Characters", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Symbols and Punctuation", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hiragana", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Katakana", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Bopomofo", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hangul Compatibility Jamo", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Kanbun", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Bopomofo Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Strokes", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Katakana Phonetic Extensions", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Enclosed CJK Letters and Months", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Compatibility", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Unified Ideographs Extension A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Yijing Hexagram Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Unified Ideographs", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Yi Syllables", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Yi Radicals", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Lisu", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Vai", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cyrillic Extended-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Bamum", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Modifier Tone Letters", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended-D", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Syloti Nagri", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Common Indic Number Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Phags-pa", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Saurashtra", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Devanagari Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Kayah Li", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Rejang", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hangul Jamo Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Javanese", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Myanmar Extended-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cham", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Myanmar Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Tai Viet", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Meetei Mayek Extensions", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ethiopic Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Latin Extended-E", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Cherokee Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Meetei Mayek", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hangul Syllables", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Hangul Jamo Extended-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "High Surrogates", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "High Private Use Surrogates", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Low Surrogates", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Private Use Area", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Compatibility Ideographs", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Alphabetic Presentation Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic Presentation Forms-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Variation Selectors", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Vertical Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Combining Half Marks", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "CJK Compatibility Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Small Form Variants", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Arabic Presentation Forms-B", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Halfwidth and Fullwidth Forms", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Specials", "KCharselect unicode block name");
// initial support for SMP blocks
QT_TRANSLATE_NOOP3("KCharSelectData", "Mahjong Tiles", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Domino Tiles", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Playing Cards", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Enclosed Alphanumeric Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Enclosed Ideographic Supplement", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Miscellaneous Symbols and Pictographs", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Emoticons", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Ornamental Dingbats", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Transport and Map Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Alchemical Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Geometric Shapes Extended", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Arrows-C", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Supplemental Symbols and Pictographs", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Chess Symbols", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Symbols and Pictographs Extended-A", "KCharselect unicode block name");
QT_TRANSLATE_NOOP3("KCharSelectData", "Symbols for Legacy Computing", "KCharselect unicode block name");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,283 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef kcharselect_h
#define kcharselect_h
#include <QString>
#include <QWidget>
#include <kwidgetsaddons_export.h>
#include <memory>
class QFont;
/**
* @class KCharSelect kcharselect.h KCharSelect
*
* @short Character selection widget
*
* This widget allows the user to select a character of a
* specified font and to browse Unicode information
*
* \image html kcharselect.png "KCharSelect Widget"
*
* You can specify the font whose characters should be displayed via
* setCurrentFont(). Using the Controls argument in the constructor
* you can create a compact version of KCharSelect if there is not enough
* space and if you don't need all features.
*
* KCharSelect displays one Unicode block at a time and provides
* categorized access to them. Unicode character names and further details,
* including cross references, are displayed. Additionally, there is a search
* to find characters.
*
* By default, KCharSelect is restricted to Basic Multilingual Plane (BMP)
* characters that QChar supports, i.e. characters with code points that
* fit into a quint16 (U+0000..U+FFFF). API methods that have a QChar
* argument can only be used for this default mode:
*
* To get the current selected character, use the currentChar()
* method. You can set the character which should be displayed with
* setCurrentChar().
*
* If you want the user to select and search characters from all planes,
* i.e. characters U+0000..U+10FFFF, use setAllPlanesEnabled(true)
* and use the @c uint based methods currentCodePoint() and
* setCurrentCodePoint() instead.
*
* Since QString does not allow @c uint code points, you either must
* use QString::fromUcs4() and QString::ToUcs4() to convert between
* strings and code points, or manually do the surrogate pair handling
* using QChar::requiresSurrogates() and friends.
*
* @author Reginald Stadlbauer <reggie@kde.org>
* @author Daniel Laidig <d.laidig@gmx.de>
*/
class KWIDGETSADDONS_EXPORT KCharSelect : public QWidget
{
Q_OBJECT
Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont)
Q_PROPERTY(QChar currentChar READ currentChar WRITE setCurrentChar)
Q_PROPERTY(uint currentCodePoint READ currentCodePoint WRITE setCurrentCodePoint NOTIFY currentCodePointChanged)
Q_PROPERTY(QList<QChar> displayedChars READ displayedChars)
Q_PROPERTY(QList<uint> displayedCodePoints READ displayedCodePoints)
Q_PROPERTY(bool allPlanesEnabled READ allPlanesEnabled WRITE setAllPlanesEnabled DESIGNABLE true)
public:
/**
* Flags to set the shown widgets
* @see Controls
*/
enum Control {
/**
* Shows the search widgets
*/
SearchLine = 0x01,
/**
* Shows the font combo box
*/
FontCombo = 0x02,
/**
* Shows the font size spin box
*/
FontSize = 0x04,
/**
* Shows the category/block selection combo boxes
*/
BlockCombos = 0x08,
/**
* Shows the actual table
*/
CharacterTable = 0x10,
/**
* Shows the detail browser
*/
DetailBrowser = 0x20,
/**
* Shows the Back/Forward buttons
*/
HistoryButtons = 0x40,
/**
* Shows everything
*/
AllGuiElements = 65535,
};
/**
* Stores a combination of #Control values.
*/
Q_DECLARE_FLAGS(Controls, Control)
/**
* Constructor. @p controls can be used to show a custom set of widgets.
*
* @param parent the parent widget for this KCharSelect (see QWidget documentation)
* @param controls selects the visible controls on the KCharSelect widget
*
* @since 4.2
*/
explicit KCharSelect(QWidget *parent, const Controls controls = AllGuiElements);
/**
* Constructor. @p controls can be used to show a custom set of widgets.
*
* The widget uses the following actions:
* - KStandardActions::find() (edit_find)
* - KStandardActions::back() (go_back)
* - KStandardActions::forward() (go_forward)
*
* If you provide a KActionCollection, this will be populated with the above actions,
* which you can then manually trigger or place in menus and toolbars.
*
* @param parent the parent widget for this KCharSelect (see QWidget documentation)
* @param actionParent if this is not @c null, KCharSelect will place its actions into this
* collection
* @param controls selects the visible controls on the KCharSelect widget
*
* @since 4.2
*/
explicit KCharSelect(QWidget *parent, QObject *actionParent, const Controls controls = AllGuiElements);
~KCharSelect() override;
/**
* Reimplemented.
*/
QSize sizeHint() const override;
/**
* Sets the allowed Unicode code planes. If @p all is @c false, then
* only characters from the Basic Multilingual Plane (BMP) can be
* selected, otherwise characters from all planes are allowed.
*
* For compatibility reasons, the default is @c false.
*
* If you enable support for all planes, you must use the functions
* handling @c uint code points instead of @c QChar characters.
* @since 5.25
*/
void setAllPlanesEnabled(bool all);
/**
* @returns @c true, if characters from all Unicode code planes
* can be selected.
* @since 5.25
*/
bool allPlanesEnabled() const;
/**
* Returns the currently selected character. If characters outside the
* Basic Multilingual Plane (BMP) can be selected, use currentCodePoint
* instead.
* @sa currentCodePoint
*/
QChar currentChar() const;
/**
* Returns the Unicode code point of the currently selected character.
* @warning If you enabled support for all Unicode planes, you must use
* QChar::requiresSurrogates() to check if the code point requires
* conversion to a UTF-16 surrogate pair before converting it to QString.
* You cannot convert a code point to a QChar.
* @since 5.25
*/
uint currentCodePoint() const;
/**
* Returns the currently displayed font.
*/
QFont currentFont() const;
/**
* Returns a list of currently displayed characters. If characters outside the
* Basic Multilingual Plane (BMP) can be selected, use displayedCodePoints
* instead.
* Warning: this method can be a bit slow
* @sa displayedCodePoints
*/
QList<QChar> displayedChars() const;
/**
* Returns a list of Unicode code points of the currently displayed characters.
* @since 5.25
*/
QList<uint> displayedCodePoints() const;
public Q_SLOTS:
/**
* Highlights the character @p c. If the character is not displayed, the block is changed.
*
* @param c the character to highlight
*/
void setCurrentChar(const QChar &c);
/**
* Highlights the character with the specified @p codePoint. If the character is
* outside the Basic Multilingual Plane (BMP), then you must enable support
* for all planes for this to work.
*
* @param codePoint the Unicode code point of the character to highlight
*
* @sa allPlanesEnabled
* @since 5.25
*/
void setCurrentCodePoint(uint codePoint);
/**
* Sets the font which is displayed to @p font
*
* @param font the display font for the widget
*/
void setCurrentFont(const QFont &font);
Q_SIGNALS:
/**
* A new font is selected or the font size changed.
*
* @param font the new font
*/
void currentFontChanged(const QFont &font);
/**
* The current character is changed.
*
* @param c the new character
*/
void currentCharChanged(const QChar &c);
/**
* The current character is changed.
*
* @param codePoint the Unicode code point of the new character
* @since 5.25
*/
void currentCodePointChanged(uint codePoint);
/**
* The currently displayed characters are changed (search results or block).
*/
void displayedCharsChanged();
/**
* A character is selected to be inserted somewhere.
*
* @param c the selected character
*/
void charSelected(const QChar &c);
/**
* A character is selected to be inserted somewhere.
*
* @param codePoint the Unicode code point of the selected character
* @since 5.25
*/
void codePointSelected(uint codePoint);
private:
KWIDGETSADDONS_NO_EXPORT void initWidget(const Controls, QObject *);
private:
std::unique_ptr<class KCharSelectPrivate> const d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KCharSelect::Controls)
#endif
@@ -0,0 +1,177 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2005 Joseph Wenninger <jowenn@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCHARSELECT_P_H
#define KCHARSELECT_P_H
#include <QAbstractTableModel>
#include <QFont>
#include <QMimeData>
#include <QTableView>
#include <memory>
#include "kcharselectdata_p.h"
class KCharSelectTablePrivate;
/**
* @short Character selection table
*
* A table widget which displays the characters of a font. Internally
* used by KCharSelect. See the KCharSelect documentation for further
* details.
*
* @author Reginald Stadlbauer <reggie@kde.org>
* @author Daniel Laidig <d.laidig@gmx.de>
*/
class KCharSelectTable : public QTableView
{
Q_OBJECT
public:
/**
* Constructor. Using @p _font, draw a table of chars.
* @sa setContents
*/
KCharSelectTable(QWidget *parent, const QFont &_font);
~KCharSelectTable() override;
void resizeEvent(QResizeEvent *) override;
/** Set the font to be displayed to @p _font . */
void setFont(const QFont &_font);
/** Set the highlighted character to @p c . */
void setChar(uint c);
/** Set the contents of the table to @p chars . */
void setContents(const QList<uint> &chars);
/** @return Currently highlighted character. */
uint chr();
/**
* Returns the currently displayed font.
*/
QFont font() const;
/**
* Returns a list of currently displayed characters.
*/
QList<uint> displayedChars() const;
/**
* Reimplemented.
*/
void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override;
protected:
/**
* Reimplemented.
*/
void keyPressEvent(QKeyEvent *e) override;
Q_SIGNALS:
/** Emitted to indicate that character @p c is activated (such as by double-clicking it). */
void activated(uint c);
void focusItemChanged(uint c);
void showCharRequested(uint c);
private:
friend class KCharSelectTablePrivate;
std::unique_ptr<KCharSelectTablePrivate> const d;
Q_DISABLE_COPY(KCharSelectTable)
};
// NO D-Pointer needed, private internal class, no public API
class KCharSelectItemModel : public QAbstractTableModel
{
Q_OBJECT
public:
KCharSelectItemModel(const QList<uint> &chars, const QFont &font, QObject *parent)
: QAbstractTableModel(parent)
, m_chars(chars)
, m_font(font)
{
if (!chars.isEmpty()) {
m_columns = chars.count();
} else {
m_columns = 1;
}
}
enum internalRoles { CharacterRole = Qt::UserRole };
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid()) {
return 0;
}
if (m_chars.count() % m_columns == 0) {
return m_chars.count() / m_columns;
} else {
return m_chars.count() / m_columns + 1;
}
}
int columnCount(const QModelIndex & = QModelIndex()) const override
{
return m_columns;
}
void setFont(const QFont &font)
{
beginResetModel();
m_font = font;
endResetModel();
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
int pos = m_columns * (index.row()) + index.column();
if (pos >= m_chars.size() || index.row() < 0 || index.column() < 0) {
return Qt::ItemIsDropEnabled;
}
return (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override
{
if (indexes.size() != 1) {
return nullptr;
}
QMimeData *mimeData = new QMimeData();
char32_t character = data(indexes[0], CharacterRole).toUInt();
mimeData->setText(QString::fromUcs4(&character, 1));
return mimeData;
}
Qt::DropActions supportedDropActions() const override
{
return Qt::CopyAction;
}
QStringList mimeTypes() const override
{
return {QStringLiteral("text/plain")};
}
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
void setColumnCount(int columns);
QList<uint> chars() const
{
return m_chars;
}
private:
QList<uint> m_chars;
QFont m_font;
int m_columns;
Q_SIGNALS:
void showCharRequested(uint c);
};
#endif // KCHARSELECT_P_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2007 Daniel Laidig <d.laidig@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCHARSELECTDATA_H
#define KCHARSELECTDATA_H
#include <QChar>
#include <QFont>
#include <QFuture>
#include <QList>
#include <QString>
#include <QStringList>
// Internal class used by KCharSelect
typedef QMap<QString, QList<quint16>> Index;
class KCharSelectData
{
public:
QString formatCode(uint code, int length = 4, const QString &prefix = QStringLiteral("U+"), int base = 16);
QList<uint> blockContents(int block);
QList<int> sectionContents(int section);
QStringList sectionList();
QString block(uint c);
QString section(uint c);
QString name(uint c);
int blockIndex(uint c);
int sectionIndex(int block);
QString blockName(int index);
QString sectionName(int index);
QStringList aliases(uint c);
QStringList notes(uint c);
QList<uint> seeAlso(uint c);
QStringList equivalents(uint c);
QStringList approximateEquivalents(uint c);
QList<uint> decomposition(uint c);
QStringList unihanInfo(uint c);
QChar::Category category(uint c);
bool isPrint(uint c);
bool isDisplayable(uint c);
bool isIgnorable(uint c);
bool isCombining(uint c);
QString display(uint c, const QFont &font);
QString displayCombining(uint c);
QString categoryText(QChar::Category category);
QList<uint> find(const QString &s);
private:
bool openDataFile();
quint32 getDetailIndex(uint c) const;
QSet<uint> getMatchingChars(const QString &s);
QStringList splitString(const QString &s);
void appendToIndex(Index *index, quint16 unicode, const QString &s);
Index createIndex(const QByteArray &dataFile);
quint16 mapCodePointToDataBase(uint code) const;
uint mapDataBaseToCodePoint(quint16 code) const;
QByteArray dataFile;
QFuture<Index> futureIndex;
int remapType;
friend class RunIndexCreation;
};
#endif /* #ifndef KCHARSELECTDATA_H */
@@ -0,0 +1,343 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kcollapsiblegroupbox.h"
#include <QLabel>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
#include <QTimeLine>
class KCollapsibleGroupBoxPrivate
{
public:
KCollapsibleGroupBoxPrivate(KCollapsibleGroupBox *qq);
void updateChildrenFocus(bool expanded);
void recalculateHeaderSize();
QSize contentSize() const;
QSize contentMinimumSize() const;
KCollapsibleGroupBox *const q;
QTimeLine *animation;
QString title;
bool isExpanded = false;
bool headerContainsMouse = false;
QSize headerSize;
int shortcutId = 0;
QMap<QWidget *, Qt::FocusPolicy> focusMap; // Used to restore focus policy of widgets.
};
KCollapsibleGroupBoxPrivate::KCollapsibleGroupBoxPrivate(KCollapsibleGroupBox *qq)
: q(qq)
{
}
KCollapsibleGroupBox::KCollapsibleGroupBox(QWidget *parent)
: QWidget(parent)
, d(new KCollapsibleGroupBoxPrivate(this))
{
d->recalculateHeaderSize();
d->animation = new QTimeLine(500, this); // duration matches kmessagewidget
connect(d->animation, &QTimeLine::valueChanged, this, [this](qreal value) {
setFixedHeight((d->contentSize().height() * value) + d->headerSize.height());
});
connect(d->animation, &QTimeLine::stateChanged, this, [this](QTimeLine::State state) {
if (state == QTimeLine::NotRunning) {
d->updateChildrenFocus(d->isExpanded);
}
});
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
setFocusPolicy(Qt::TabFocus);
setMouseTracking(true);
}
KCollapsibleGroupBox::~KCollapsibleGroupBox()
{
if (d->animation->state() == QTimeLine::Running) {
d->animation->stop();
}
}
void KCollapsibleGroupBox::setTitle(const QString &title)
{
d->title = title;
d->recalculateHeaderSize();
update();
updateGeometry();
if (d->shortcutId) {
releaseShortcut(d->shortcutId);
}
d->shortcutId = grabShortcut(QKeySequence::mnemonic(title));
#ifndef QT_NO_ACCESSIBILITY
setAccessibleName(title);
#endif
Q_EMIT titleChanged();
}
QString KCollapsibleGroupBox::title() const
{
return d->title;
}
void KCollapsibleGroupBox::setExpanded(bool expanded)
{
if (expanded == d->isExpanded) {
return;
}
d->isExpanded = expanded;
Q_EMIT expandedChanged();
d->updateChildrenFocus(expanded);
d->animation->setDirection(expanded ? QTimeLine::Forward : QTimeLine::Backward);
// QTimeLine::duration() must be > 0
const int duration = qMax(1, style()->styleHint(QStyle::SH_Widget_Animation_Duration));
d->animation->stop();
d->animation->setDuration(duration);
d->animation->start();
// when going from collapsed to expanded changing the child visibility calls an updateGeometry
// which calls sizeHint with expanded true before the first frame of the animation kicks in
// trigger an effective frame 0
if (expanded) {
setFixedHeight(d->headerSize.height());
}
}
bool KCollapsibleGroupBox::isExpanded() const
{
return d->isExpanded;
}
void KCollapsibleGroupBox::collapse()
{
setExpanded(false);
}
void KCollapsibleGroupBox::expand()
{
setExpanded(true);
}
void KCollapsibleGroupBox::toggle()
{
setExpanded(!d->isExpanded);
}
void KCollapsibleGroupBox::paintEvent(QPaintEvent *event)
{
QPainter p(this);
QStyleOptionButton baseOption;
baseOption.initFrom(this);
baseOption.rect = QRect(0, 0, width(), d->headerSize.height());
baseOption.text = d->title;
if (d->headerContainsMouse) {
baseOption.state |= QStyle::State_MouseOver;
}
QStyle::PrimitiveElement element;
if (d->isExpanded) {
element = QStyle::PE_IndicatorArrowDown;
} else {
element = isLeftToRight() ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowLeft;
}
QStyleOptionButton indicatorOption = baseOption;
indicatorOption.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &indicatorOption, this);
style()->drawPrimitive(element, &indicatorOption, &p, this);
QStyleOptionButton labelOption = baseOption;
labelOption.rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &labelOption, this);
style()->drawControl(QStyle::CE_CheckBoxLabel, &labelOption, &p, this);
Q_UNUSED(event)
}
QSize KCollapsibleGroupBox::sizeHint() const
{
if (d->isExpanded) {
return d->contentSize() + QSize(0, d->headerSize.height());
} else {
return QSize(d->contentSize().width(), d->headerSize.height());
}
}
QSize KCollapsibleGroupBox::minimumSizeHint() const
{
int minimumWidth = qMax(d->contentSize().width(), d->headerSize.width());
return QSize(minimumWidth, d->headerSize.height());
}
bool KCollapsibleGroupBox::event(QEvent *event)
{
switch (event->type()) {
case QEvent::StyleChange:
/*fall through*/
case QEvent::FontChange:
d->recalculateHeaderSize();
break;
case QEvent::Shortcut: {
QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
if (d->shortcutId == se->shortcutId()) {
toggle();
return true;
}
break;
}
case QEvent::ChildAdded: {
QChildEvent *ce = static_cast<QChildEvent *>(event);
if (ce->child()->isWidgetType()) {
auto widget = static_cast<QWidget *>(ce->child());
// Needs to be called asynchronously because at this point the widget is likely a "real" QWidget,
// i.e. the QWidget base class whose constructor sets the focus policy to NoPolicy.
// But the constructor of the child class (not yet called) could set a different focus policy later.
auto focusFunc = [this, widget]() {
overrideFocusPolicyOf(widget);
};
QMetaObject::invokeMethod(this, focusFunc, Qt::QueuedConnection);
}
break;
}
case QEvent::LayoutRequest:
if (d->animation->state() == QTimeLine::NotRunning) {
setFixedHeight(sizeHint().height());
}
break;
default:
break;
}
return QWidget::event(event);
}
void KCollapsibleGroupBox::mousePressEvent(QMouseEvent *event)
{
const QRect headerRect(0, 0, width(), d->headerSize.height());
if (headerRect.contains(event->pos())) {
toggle();
}
event->setAccepted(true);
}
// if mouse has changed whether it is in the top bar or not refresh to change arrow icon
void KCollapsibleGroupBox::mouseMoveEvent(QMouseEvent *event)
{
const QRect headerRect(0, 0, width(), d->headerSize.height());
bool headerContainsMouse = headerRect.contains(event->pos());
if (headerContainsMouse != d->headerContainsMouse) {
d->headerContainsMouse = headerContainsMouse;
update();
}
QWidget::mouseMoveEvent(event);
}
void KCollapsibleGroupBox::leaveEvent(QEvent *event)
{
d->headerContainsMouse = false;
update();
QWidget::leaveEvent(event);
}
void KCollapsibleGroupBox::keyPressEvent(QKeyEvent *event)
{
// event might have just propagated up from a child, if so we don't want to react to it
if (!hasFocus()) {
return;
}
const int key = event->key();
if (key == Qt::Key_Space || key == Qt::Key_Enter || key == Qt::Key_Return) {
toggle();
event->setAccepted(true);
}
}
void KCollapsibleGroupBox::resizeEvent(QResizeEvent *event)
{
const QMargins margins = contentsMargins();
if (layout()) {
// we don't want the layout trying to fit the current frame of the animation so always set it to the target height
layout()->setGeometry(QRect(margins.left(), margins.top(), width() - margins.left() - margins.right(), layout()->sizeHint().height()));
}
QWidget::resizeEvent(event);
}
void KCollapsibleGroupBox::overrideFocusPolicyOf(QWidget *widget)
{
d->focusMap.insert(widget, widget->focusPolicy());
if (!isExpanded()) {
// Prevent tab focus if not expanded.
widget->setFocusPolicy(Qt::NoFocus);
}
}
void KCollapsibleGroupBoxPrivate::recalculateHeaderSize()
{
QStyleOption option;
option.initFrom(q);
QSize textSize = q->style()->itemTextRect(option.fontMetrics, QRect(), Qt::TextShowMnemonic, false, title).size();
headerSize = q->style()->sizeFromContents(QStyle::CT_CheckBox, &option, textSize, q);
q->setContentsMargins(q->style()->pixelMetric(QStyle::PM_IndicatorWidth), headerSize.height(), 0, 0);
}
void KCollapsibleGroupBoxPrivate::updateChildrenFocus(bool expanded)
{
const auto children = q->children();
for (QObject *child : children) {
QWidget *widget = qobject_cast<QWidget *>(child);
if (!widget) {
continue;
}
// Restore old focus policy if expanded, remove from focus chain otherwise.
if (expanded) {
widget->setFocusPolicy(focusMap.value(widget));
} else {
widget->setFocusPolicy(Qt::NoFocus);
}
}
}
QSize KCollapsibleGroupBoxPrivate::contentSize() const
{
if (q->layout()) {
const QMargins margins = q->contentsMargins();
const QSize marginSize(margins.left() + margins.right(), margins.top() + margins.bottom());
return q->layout()->sizeHint() + marginSize;
}
return QSize(0, 0);
}
QSize KCollapsibleGroupBoxPrivate::contentMinimumSize() const
{
if (q->layout()) {
const QMargins margins = q->contentsMargins();
const QSize marginSize(margins.left() + margins.right(), margins.top() + margins.bottom());
return q->layout()->minimumSize() + marginSize;
}
return QSize(0, 0);
}
#include "moc_kcollapsiblegroupbox.cpp"
@@ -0,0 +1,108 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCOLLAPSIBLEGROUPBOX_H
#define KCOLLAPSIBLEGROUPBOX_H
#include <QWidget>
#include <kwidgetsaddons_export.h>
#include <memory>
/**
* @class KCollapsibleGroupBox kcollapsiblegroupbox.h KCollapsibleGroupBox
*
* A groupbox featuring a clickable header and arrow indicator that can be
* expanded and collapsed to reveal the contents.
*
* When expanded, the widget will resize to fit the sizeHint of child items.
*
* @since 5.16
*/
class KWIDGETSADDONS_EXPORT KCollapsibleGroupBox : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged)
public:
explicit KCollapsibleGroupBox(QWidget *parent = nullptr);
~KCollapsibleGroupBox() override;
/**
* Set the title that will be permanently shown at the top of the collapsing box
* Mnemonics are supported
*/
void setTitle(const QString &title);
/**
* The title
*/
QString title() const;
/**
* Set whether contents are shown
*
* The default is false until the user clicks
*/
void setExpanded(bool expanded);
/**
* Whether contents are shown
* During animations, this will reflect the target state at the end of the animation
*/
bool isExpanded() const;
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
public Q_SLOTS:
/**
* Expands if collapsed and vice versa
*/
void toggle();
/**
* Equivalent to setExpanded(true)
*/
void expand();
/**
* Equivalent to setExpanded(false)
*/
void collapse();
Q_SIGNALS:
/**
* Emitted when the title is changed
*/
void titleChanged();
/**
* Emitted when the widget expands or collapsed
*/
void expandedChanged();
protected:
void paintEvent(QPaintEvent *) override;
bool event(QEvent *) override;
void mousePressEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void leaveEvent(QEvent *) override;
void keyPressEvent(QKeyEvent *) override;
void resizeEvent(QResizeEvent *) override;
private Q_SLOTS:
KWIDGETSADDONS_NO_EXPORT void overrideFocusPolicyOf(QWidget *widget);
private:
std::unique_ptr<class KCollapsibleGroupBoxPrivate> const d;
Q_DISABLE_COPY(KCollapsibleGroupBox)
};
#endif
@@ -0,0 +1,324 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
SPDX-FileCopyrightText: 1999 Cristian Tibirna <ctibirna@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kcolorbutton.h"
#include <QApplication>
#include <QClipboard>
#include <QColorDialog>
#include <QDrag>
#include <QMimeData>
#include <QMouseEvent>
#include <QPainter>
#include <QPointer>
#include <QStyle>
#include <QStyleOptionButton>
#include <qdrawutil.h>
class KColorButtonPrivate
{
public:
KColorButtonPrivate(KColorButton *qq);
void chooseColor();
void colorChosen();
KColorButton *q;
QColor m_defaultColor;
bool m_bdefaultColor : 1;
bool m_alphaChannel : 1;
QColor col;
QPoint mPos;
QPointer<QColorDialog> dialogPtr;
void initStyleOption(QStyleOptionButton *opt) const;
};
/////////////////////////////////////////////////////////////////////
// Functions duplicated from KColorMimeData
// Should be kept in sync
void populateMimeData(QMimeData *mimeData, const QColor &color)
{
mimeData->setColorData(color);
mimeData->setText(color.name());
}
bool canDecode(const QMimeData *mimeData)
{
if (mimeData->hasColor()) {
return true;
}
if (mimeData->hasText()) {
const QString colorName = mimeData->text();
if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) {
return true;
}
}
return false;
}
QColor fromMimeData(const QMimeData *mimeData)
{
if (mimeData->hasColor()) {
return mimeData->colorData().value<QColor>();
}
if (canDecode(mimeData)) {
return QColor(mimeData->text());
}
return QColor();
}
QDrag *createDrag(const QColor &color, QObject *dragsource)
{
QDrag *drag = new QDrag(dragsource);
QMimeData *mime = new QMimeData;
populateMimeData(mime, color);
drag->setMimeData(mime);
QPixmap colorpix(25, 20);
colorpix.fill(color);
QPainter p(&colorpix);
p.setPen(Qt::black);
p.drawRect(0, 0, 24, 19);
p.end();
drag->setPixmap(colorpix);
drag->setHotSpot(QPoint(-5, -7));
return drag;
}
/////////////////////////////////////////////////////////////////////
KColorButtonPrivate::KColorButtonPrivate(KColorButton *qq)
: q(qq)
{
m_bdefaultColor = false;
m_alphaChannel = false;
q->setAcceptDrops(true);
QObject::connect(q, &KColorButton::clicked, q, [this]() {
chooseColor();
});
}
KColorButton::KColorButton(QWidget *parent)
: QPushButton(parent)
, d(new KColorButtonPrivate(this))
{
}
KColorButton::KColorButton(const QColor &c, QWidget *parent)
: QPushButton(parent)
, d(new KColorButtonPrivate(this))
{
d->col = c;
}
KColorButton::KColorButton(const QColor &c, const QColor &defaultColor, QWidget *parent)
: QPushButton(parent)
, d(new KColorButtonPrivate(this))
{
d->col = c;
setDefaultColor(defaultColor);
}
KColorButton::~KColorButton() = default;
QColor KColorButton::color() const
{
return d->col;
}
void KColorButton::setColor(const QColor &c)
{
if (d->col != c) {
d->col = c;
update();
Q_EMIT changed(d->col);
}
}
void KColorButton::setAlphaChannelEnabled(bool alpha)
{
d->m_alphaChannel = alpha;
}
bool KColorButton::isAlphaChannelEnabled() const
{
return d->m_alphaChannel;
}
QColor KColorButton::defaultColor() const
{
return d->m_defaultColor;
}
void KColorButton::setDefaultColor(const QColor &c)
{
d->m_bdefaultColor = c.isValid();
d->m_defaultColor = c;
}
void KColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const
{
opt->initFrom(q);
opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
opt->features = QStyleOptionButton::None;
if (q->isDefault()) {
opt->features |= QStyleOptionButton::DefaultButton;
}
opt->text.clear();
opt->icon = QIcon();
}
void KColorButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QStyle *style = QWidget::style();
// First, we need to draw the bevel.
QStyleOptionButton butOpt;
d->initStyleOption(&butOpt);
style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this);
// OK, now we can muck around with drawing out pretty little color box
// First, sort out where it goes
QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this);
int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2;
labelRect.adjust(shift, shift, -shift, -shift);
int x;
int y;
int w;
int h;
labelRect.getRect(&x, &y, &w, &h);
if (isChecked() || isDown()) {
x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this);
y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this);
}
QColor fillCol = isEnabled() ? d->col : palette().color(backgroundRole());
qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, nullptr);
if (fillCol.isValid()) {
const QRect rect(x + 1, y + 1, w - 2, h - 2);
if (fillCol.alpha() < 255) {
QPixmap chessboardPattern(16, 16);
QPainter patternPainter(&chessboardPattern);
patternPainter.fillRect(0, 0, 8, 8, Qt::black);
patternPainter.fillRect(8, 8, 8, 8, Qt::black);
patternPainter.fillRect(0, 8, 8, 8, Qt::white);
patternPainter.fillRect(8, 0, 8, 8, Qt::white);
patternPainter.end();
painter.fillRect(rect, QBrush(chessboardPattern));
}
painter.fillRect(rect, fillCol);
}
if (hasFocus()) {
QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this);
QStyleOptionFocusRect focusOpt;
focusOpt.initFrom(this);
focusOpt.rect = focusRect;
focusOpt.backgroundColor = palette().window().color();
style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this);
}
}
QSize KColorButton::sizeHint() const
{
QStyleOptionButton opt;
d->initStyleOption(&opt);
return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this);
}
QSize KColorButton::minimumSizeHint() const
{
QStyleOptionButton opt;
d->initStyleOption(&opt);
return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this);
}
void KColorButton::dragEnterEvent(QDragEnterEvent *event)
{
event->setAccepted(canDecode(event->mimeData()) && isEnabled());
}
void KColorButton::dropEvent(QDropEvent *event)
{
QColor c = fromMimeData(event->mimeData());
if (c.isValid()) {
setColor(c);
}
}
void KColorButton::keyPressEvent(QKeyEvent *e)
{
int key = e->key() | e->modifiers();
if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) {
QMimeData *mime = new QMimeData;
populateMimeData(mime, color());
QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard);
} else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) {
QColor color = fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard));
setColor(color);
} else {
QPushButton::keyPressEvent(e);
}
}
void KColorButton::mousePressEvent(QMouseEvent *e)
{
d->mPos = e->pos();
QPushButton::mousePressEvent(e);
}
void KColorButton::mouseMoveEvent(QMouseEvent *e)
{
if ((e->buttons() & Qt::LeftButton) &&
(e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) {
createDrag(color(), this)->exec();
setDown(false);
}
}
void KColorButtonPrivate::chooseColor()
{
QColorDialog *dialog = dialogPtr.data();
if (dialog) {
dialog->show();
dialog->raise();
dialog->activateWindow();
return;
}
dialog = new QColorDialog(q);
dialog->setCurrentColor(q->color());
dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel);
dialog->setAttribute(Qt::WA_DeleteOnClose);
QObject::connect(dialog, &QDialog::accepted, q, [this]() {
colorChosen();
});
dialogPtr = dialog;
dialog->show();
}
void KColorButtonPrivate::colorChosen()
{
QColorDialog *dialog = dialogPtr.data();
if (!dialog) {
return;
}
if (dialog->selectedColor().isValid()) {
q->setColor(dialog->selectedColor());
} else if (m_bdefaultColor) {
q->setColor(m_defaultColor);
}
}
#include "moc_kcolorbutton.cpp"
@@ -0,0 +1,109 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCOLORBUTTON_H
#define KCOLORBUTTON_H
#include <kwidgetsaddons_export.h>
#include <QPushButton>
#include <memory>
class KColorButtonPrivate;
/**
* @class KColorButton kcolorbutton.h KColorButton
*
* @short A pushbutton to display or allow user selection of a color.
*
* This widget can be used to display or allow user selection of a color.
*
* \image html kcolorbutton.png "KColorButton Widget"
*
* @see QColorDialog
*/
class KWIDGETSADDONS_EXPORT KColorButton : public QPushButton
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed USER true)
Q_PROPERTY(QColor defaultColor READ defaultColor WRITE setDefaultColor)
Q_PROPERTY(bool alphaChannelEnabled READ isAlphaChannelEnabled WRITE setAlphaChannelEnabled)
public:
/**
* Creates a color button.
*/
explicit KColorButton(QWidget *parent = nullptr);
/**
* Creates a color button with an initial color @p c.
*/
explicit KColorButton(const QColor &c, QWidget *parent = nullptr);
/**
* Creates a color button with an initial color @p c and default color @p defaultColor.
*/
KColorButton(const QColor &c, const QColor &defaultColor, QWidget *parent = nullptr);
~KColorButton() override;
/**
* Returns the currently chosen color.
*/
QColor color() const;
/**
* Sets the current color to @p c.
*/
void setColor(const QColor &c);
/**
* When set to true, allow the user to change the alpha component
* of the color. The default value is false.
* @since 4.5
*/
void setAlphaChannelEnabled(bool alpha);
/**
* Returns true if the user is allowed to change the alpha component.
* @since 4.5
*/
bool isAlphaChannelEnabled() const;
/**
* Returns the default color or an invalid color
* if no default color is set.
*/
QColor defaultColor() const;
/**
* Sets the default color to @p c.
*/
void setDefaultColor(const QColor &c);
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
Q_SIGNALS:
/**
* Emitted when the color of the widget
* is changed, either with setColor() or via user selection.
*/
void changed(const QColor &newColor);
protected:
void paintEvent(QPaintEvent *pe) override;
void dragEnterEvent(QDragEnterEvent *) override;
void dropEvent(QDropEvent *) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
private:
std::unique_ptr<class KColorButtonPrivate> const d;
};
#endif
@@ -0,0 +1,349 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kcolorcombo.h"
#include <QAbstractItemDelegate>
#include <QApplication>
#include <QColorDialog>
#include <QStylePainter>
class KColorComboDelegate : public QAbstractItemDelegate
{
Q_OBJECT
public:
enum ItemRoles {
ColorRole = Qt::UserRole + 1,
};
enum LayoutMetrics {
FrameMargin = 3,
};
KColorComboDelegate(QObject *parent = nullptr);
~KColorComboDelegate() override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
static QBrush k_colorcombodelegate_brush(const QModelIndex &index, int role)
{
QBrush brush;
QVariant v = index.data(role);
if (v.userType() == QMetaType::QBrush) {
brush = v.value<QBrush>();
} else if (v.userType() == QMetaType::QColor) {
brush = QBrush(v.value<QColor>());
}
return brush;
}
KColorComboDelegate::KColorComboDelegate(QObject *parent)
: QAbstractItemDelegate(parent)
{
}
KColorComboDelegate::~KColorComboDelegate()
{
}
void KColorComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// background
QColor innercolor(Qt::white);
bool isSelected = (option.state & QStyle::State_Selected);
bool paletteBrush = (k_colorcombodelegate_brush(index, Qt::BackgroundRole).style() == Qt::NoBrush);
if (isSelected) {
innercolor = option.palette.color(QPalette::Highlight);
} else {
innercolor = option.palette.color(QPalette::Base);
}
// highlight selected item
QStyleOptionViewItem opt(option);
opt.showDecorationSelected = true;
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
QRect innerrect = option.rect.adjusted(FrameMargin, FrameMargin, -FrameMargin, -FrameMargin);
// inner color
QVariant cv = index.data(ColorRole);
if (cv.userType() == QMetaType::QColor) {
QColor tmpcolor = cv.value<QColor>();
if (tmpcolor.isValid()) {
innercolor = tmpcolor;
paletteBrush = false;
painter->setPen(Qt::transparent);
painter->setBrush(innercolor);
QPainter::RenderHints tmpHint = painter->renderHints();
painter->setRenderHint(QPainter::Antialiasing);
painter->drawRoundedRect(innerrect, 2, 2);
painter->setRenderHints(tmpHint);
painter->setBrush(Qt::NoBrush);
}
}
// text
QVariant tv = index.data(Qt::DisplayRole);
if (tv.userType() == QMetaType::QString) {
QString text = tv.toString();
QColor textColor;
if (paletteBrush) {
if (isSelected) {
textColor = option.palette.color(QPalette::HighlightedText);
} else {
textColor = option.palette.color(QPalette::Text);
}
} else {
int unused;
int v;
innercolor.getHsv(&unused, &unused, &v);
if (v > 128) {
textColor = Qt::black;
} else {
textColor = Qt::white;
}
}
painter->setPen(textColor);
painter->drawText(innerrect.adjusted(1, 1, -1, -1), text);
}
}
QSize KColorComboDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index)
// the width does not matter, as the view will always use the maximum width available
return QSize(100, option.fontMetrics.height() + 2 * FrameMargin);
}
static const uchar standardPalette[][4] = {
{255, 255, 255}, // white
{192, 192, 192}, // light gray
{160, 160, 160}, // gray
{128, 128, 128}, // dark gray
{0, 0, 0}, // black
{255, 128, 128}, // light red
{255, 192, 128}, // light orange
{255, 255, 128}, // light yellow
{128, 255, 128}, // light green
{128, 255, 255}, // cyan blue
{128, 128, 255}, // light blue
{255, 128, 255}, // light violet
{255, 0, 0}, // red
{255, 128, 0}, // orange
{255, 255, 0}, // yellow
{0, 255, 0}, // green
{0, 255, 255}, // light blue
{0, 0, 255}, // blue
{255, 0, 255}, // violet
{128, 0, 0}, // dark red
{128, 64, 0}, // dark orange
{128, 128, 0}, // dark yellow
{0, 128, 0}, // dark green
{0, 128, 128}, // dark light blue
{0, 0, 128}, // dark blue
{128, 0, 128} // dark violet
};
#define STANDARD_PALETTE_SIZE (int(sizeof(standardPalette) / sizeof(*standardPalette)))
static inline QColor standardColor(int i)
{
const uchar *entry = standardPalette[i];
return QColor(entry[0], entry[1], entry[2]);
}
class KColorComboPrivate
{
public:
KColorComboPrivate(KColorCombo *qq);
void addColors();
void setCustomColor(const QColor &color, bool lookupInPresets = true);
// slots
void slotActivated(int index);
void slotHighlighted(int index);
KColorCombo *q;
QList<QColor> colorList;
QColor customColor;
QColor internalcolor;
};
KColorComboPrivate::KColorComboPrivate(KColorCombo *qq)
: q(qq)
, customColor(Qt::white)
{
}
void KColorComboPrivate::setCustomColor(const QColor &color, bool lookupInPresets)
{
if (lookupInPresets) {
if (colorList.isEmpty()) {
for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
if (standardColor(i) == color) {
q->setCurrentIndex(i + 1);
internalcolor = color;
return;
}
}
} else {
int i = colorList.indexOf(color);
if (i >= 0) {
q->setCurrentIndex(i + 1);
internalcolor = color;
return;
}
}
}
internalcolor = color;
customColor = color;
q->setItemData(0, customColor, KColorComboDelegate::ColorRole);
}
KColorCombo::KColorCombo(QWidget *parent)
: QComboBox(parent)
, d(new KColorComboPrivate(this))
{
setItemDelegate(new KColorComboDelegate(this));
d->addColors();
connect(this, &QComboBox::activated, this, [this](int index) {
d->slotActivated(index);
});
connect(this, &QComboBox::highlighted, this, [this](int index) {
d->slotHighlighted(index);
});
// select the white color
setCurrentIndex(1);
d->slotActivated(1);
setMaxVisibleItems(13);
}
KColorCombo::~KColorCombo() = default;
void KColorCombo::setColors(const QList<QColor> &colors)
{
clear();
d->colorList = colors;
d->addColors();
}
QList<QColor> KColorCombo::colors() const
{
if (d->colorList.isEmpty()) {
QList<QColor> list;
list.reserve(STANDARD_PALETTE_SIZE);
for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
list += standardColor(i);
}
return list;
} else {
return d->colorList;
}
}
void KColorCombo::setColor(const QColor &col)
{
if (!col.isValid()) {
return;
}
if (count() == 0) {
d->addColors();
}
d->setCustomColor(col, true);
}
QColor KColorCombo::color() const
{
return d->internalcolor;
}
bool KColorCombo::isCustomColor() const
{
return d->internalcolor == d->customColor;
}
void KColorCombo::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
QStyleOptionComboBox opt;
initStyleOption(&opt);
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
QRect frame = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::transparent);
painter.setBrush(QBrush(d->internalcolor));
painter.drawRoundedRect(frame.adjusted(1, 1, -1, -1), 2, 2);
}
void KColorCombo::showEmptyList()
{
clear();
}
void KColorComboPrivate::slotActivated(int index)
{
if (index == 0) {
QColor c = QColorDialog::getColor(customColor, q);
if (c.isValid()) {
customColor = c;
setCustomColor(customColor, false);
}
} else if (colorList.isEmpty()) {
internalcolor = standardColor(index - 1);
} else {
internalcolor = colorList[index - 1];
}
Q_EMIT q->activated(internalcolor);
}
void KColorComboPrivate::slotHighlighted(int index)
{
if (index == 0) {
internalcolor = customColor;
} else if (colorList.isEmpty()) {
internalcolor = standardColor(index - 1);
} else {
internalcolor = colorList[index - 1];
}
Q_EMIT q->highlighted(internalcolor);
}
void KColorComboPrivate::addColors()
{
q->addItem(KColorCombo::tr("Custom…", "@item:inlistbox Custom color"));
if (colorList.isEmpty()) {
for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
q->addItem(QString());
q->setItemData(i + 1, standardColor(i), KColorComboDelegate::ColorRole);
}
} else {
for (int i = 0, count = colorList.count(); i < count; ++i) {
q->addItem(QString());
q->setItemData(i + 1, colorList[i], KColorComboDelegate::ColorRole);
}
}
}
#include "kcolorcombo.moc"
#include "moc_kcolorcombo.cpp"
@@ -0,0 +1,102 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
//-----------------------------------------------------------------------------
// KDE color selection combo box
// layout management added Oct 1997 by Mario Weilguni
// <mweilguni@sime.com>
#ifndef KCOLORCOMBO_H
#define KCOLORCOMBO_H
#include <QComboBox>
#include <QList>
#include <kwidgetsaddons_export.h>
#include <memory>
/**
* @class KColorCombo kcolorcombo.h KColorCombo
*
* Combobox for colors.
*
* The combobox provides some preset colors to be selected, and an entry to
* select a custom color using a color dialog.
*
* \image html kcolorcombo.png "KColorCombo Widget"
*/
class KWIDGETSADDONS_EXPORT KColorCombo : public QComboBox
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY activated USER true)
Q_PROPERTY(QList<QColor> colors READ colors WRITE setColors)
public:
/**
* Constructs a color combo box.
*/
explicit KColorCombo(QWidget *parent = nullptr);
~KColorCombo() override;
/**
* Selects the color @p col.
*/
void setColor(const QColor &col);
/**
* Returns the currently selected color.
*/
QColor color() const;
/**
* Find whether the currently selected color is a custom color selected
* using a color dialog.
*/
bool isCustomColor() const;
/**
* Set a custom list of colors to choose from, in place of the standard
* list.
* @param colors list of colors. If empty, the selection list reverts to
* the standard list.
*/
void setColors(const QList<QColor> &colors);
/**
* Return the list of colors available for selection.
* @return list of colors
*/
QList<QColor> colors() const;
/**
* Clear the color list and don't show it, till the next setColor() call
*/
void showEmptyList();
Q_SIGNALS:
/**
* Emitted when a new color box has been selected.
*/
void activated(const QColor &col);
/**
* Emitted when a new item has been highlighted.
*/
void highlighted(const QColor &col);
protected:
void paintEvent(QPaintEvent *event) override;
private:
friend class KColorComboPrivate;
std::unique_ptr<class KColorComboPrivate> const d;
Q_DISABLE_COPY(KColorCombo)
};
#endif // KCOLORCOMBO_H
@@ -0,0 +1,238 @@
/*
This file is part of the KDE frameworks
SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <kcolumnresizer.h>
#include "loggingcategory.h"
#include <QEvent>
#include <QGridLayout>
#include <QSet>
#include <QTimer>
#include <QWidget>
class FormLayoutWidgetItem : public QWidgetItem
{
public:
FormLayoutWidgetItem(QWidget *widget, QFormLayout *formLayout, QFormLayout::ItemRole itemRole)
: QWidgetItem(widget)
, m_formLayout(formLayout)
, m_itemRole(itemRole)
{
}
void setWidth(int width)
{
if (width != m_width) {
m_width = width;
invalidate();
}
}
QFormLayout *formLayout() const
{
return m_formLayout;
}
QSize sizeHint() const override
{
QSize size = QWidgetItem::sizeHint();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize minimumSize() const override
{
QSize size = QWidgetItem::minimumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize maximumSize() const override
{
QSize size = QWidgetItem::maximumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
void setGeometry(const QRect &_rect) override
{
QRect rect = _rect;
int width = widget()->sizeHint().width();
if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
rect.setLeft(rect.right() - width);
}
QWidgetItem::setGeometry(rect);
}
private:
QFormLayout *const m_formLayout;
int m_width = -1;
const QFormLayout::ItemRole m_itemRole;
};
struct GridColumnInfo {
GridColumnInfo(QGridLayout *layout_, int column_)
: layout(layout_)
, column(column_)
{
}
QGridLayout *layout;
int column;
};
Q_DECLARE_TYPEINFO(GridColumnInfo, Q_PRIMITIVE_TYPE);
class KColumnResizerPrivate
{
public:
KColumnResizerPrivate(KColumnResizer *q_ptr)
: q(q_ptr)
, m_updateTimer(new QTimer(q))
{
m_updateTimer->setSingleShot(true);
m_updateTimer->setInterval(0);
QObject::connect(m_updateTimer, &QTimer::timeout, q, [this]() {
updateWidth();
});
}
void scheduleWidthUpdate()
{
m_updateTimer->start();
}
void updateWidth()
{
int width = 0;
for (QWidget *widget : std::as_const(m_widgets)) {
width = qMax(widget->sizeHint().width(), width);
}
for (FormLayoutWidgetItem *item : std::as_const(m_formWidgetItemList)) {
item->setWidth(width);
item->formLayout()->update();
}
for (const GridColumnInfo &info : std::as_const(m_gridColumnInfoList)) {
info.layout->setColumnMinimumWidth(info.column, width);
}
}
void addWidgetsFromGridLayout(QGridLayout *layout, int column)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem *item = layout->itemAtPosition(row, column);
if (!item) {
continue;
}
QWidget *widget = item->widget();
if (!widget) {
continue;
}
q->addWidget(widget);
}
m_gridColumnInfoList << GridColumnInfo(layout, column);
}
void addWidgetsFromFormLayout(QFormLayout *layout, QFormLayout::ItemRole role)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem *item = layout->itemAt(row, role);
if (!item) {
continue;
}
QWidget *widget = item->widget();
if (!widget) {
continue;
}
// Replace the QWidgetItem with our own
layout->removeItem(item);
delete item;
FormLayoutWidgetItem *newItem = new FormLayoutWidgetItem(widget, layout, role);
layout->setItem(row, role, newItem);
m_formWidgetItemList << newItem;
q->addWidget(widget);
}
}
KColumnResizer *q;
QTimer *m_updateTimer;
QSet<QWidget *> m_widgets;
QList<FormLayoutWidgetItem *> m_formWidgetItemList;
QList<GridColumnInfo> m_gridColumnInfoList;
};
KColumnResizer::KColumnResizer(QObject *parent)
: QObject(parent)
, d(new KColumnResizerPrivate(this))
{
}
KColumnResizer::~KColumnResizer() = default;
void KColumnResizer::addWidget(QWidget *widget)
{
if (d->m_widgets.contains(widget)) {
return;
}
d->m_widgets.insert(widget);
widget->installEventFilter(this);
d->scheduleWidthUpdate();
}
void KColumnResizer::removeWidget(QWidget *widget)
{
if (!d->m_widgets.remove(widget)) {
return;
}
widget->removeEventFilter(this);
d->scheduleWidthUpdate();
}
bool KColumnResizer::eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::Resize) {
d->scheduleWidthUpdate();
}
return false;
}
void KColumnResizer::addWidgetsFromLayout(QLayout *layout, int column)
{
Q_ASSERT(column >= 0);
if (column < 0) {
qCWarning(KWidgetsAddonsLog) << "column must be >= 0";
return;
}
QGridLayout *gridLayout = qobject_cast<QGridLayout *>(layout);
if (gridLayout) {
d->addWidgetsFromGridLayout(gridLayout, column);
return;
}
QFormLayout *formLayout = qobject_cast<QFormLayout *>(layout);
if (formLayout) {
Q_ASSERT(column <= QFormLayout::SpanningRole);
if (column > QFormLayout::SpanningRole) {
qCWarning(KWidgetsAddonsLog) << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
return;
}
QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
d->addWidgetsFromFormLayout(formLayout, role);
} else {
qCWarning(KWidgetsAddonsLog) << "Don't know how to handle layout" << layout;
Q_ASSERT(0);
}
}
#include "moc_kcolumnresizer.cpp"
// vi: ts=4 sw=4 et
@@ -0,0 +1,113 @@
/*
This file is part of the KDE frameworks
SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KCOLUMNRESIZER_H
#define KCOLUMNRESIZER_H
#include <kwidgetsaddons_export.h>
#include <QFormLayout>
#include <QObject>
#include <memory>
class QEvent;
class QGridLayout;
class QLayout;
class QWidget;
/**
* @class KColumnResizer kcolumnresizer.h KColumnResizer
*
* @short Maintains consistent column sizes across layouts
*
* KColumnResizer is a helper class which can force columns of different layouts
* to keep the same width. It is useful to keep label columns consistent.
*
* It works with QGridLayout and QFormLayout.
*
* @image html kcolumnresizer.png "left: without KColumnResizer - right: with KColumnResizer"
*
* Here is a typical example:
*
* @code
* void Window::createWidgets()
* {
* QVBoxLayout *layout = new QVBoxLayout(this);
*
* QGroupBox *box1 = new QGroupBox();
* // Fill box1
* // ...
* layout->addWidget(box1);
*
* QGroupBox *box2 = new QGroupBox();
* // Fill box2
* // ...
* layout->addWidget(box2);
*
* KColumnResizer *resizer = new KColumnResizer(this);
* resizer->addWidgetsFromLayout(box1->layout(), 0);
* resizer->addWidgetsFromLayout(box2->layout(), 0);
* }
* @endcode
*
* In this example box1 and box2 children can be organized using QGridLayout or
* QFormLayout, resizer will ensure the first columns of the two QGroupBox stay
* the same width.
*
* @author Aurélien Gâteau <agateau@kde.org>
*
* @since 5.1
*/
class KWIDGETSADDONS_EXPORT KColumnResizer : public QObject
{
Q_OBJECT
public:
/**
* Constructs a KColumnResizer.
*/
explicit KColumnResizer(QObject *parent = nullptr);
~KColumnResizer() override;
/**
* Add all widgets from @p layout which are in column @p column to the list
* of widgets to manage.
*
* @param layout The layout containing the widgets to add. KColumnResizer
* supports QGridLayout and QFormLayout.
* @param column The column number which contains the widgets. If layout is
* a QFormLayout, column should not be higher than QFormLayout::SpanningRole
*/
void addWidgetsFromLayout(QLayout *layout, int column = 0);
/**
* Add a single widget to the list of widgets whose width is monitored.
*
* It is more common to use addWidgetsFromLayout(), but adding single
* widgets can be useful if you want to keep a single button the same width
* as a column in a layout.
*
* @param widget The widget to add
*/
void addWidget(QWidget *widget);
/**
* Remove a widget previously added by addWidget or addWidgetsFromLayout.
*
* @param widget The widget to remove
*/
void removeWidget(QWidget *widget);
protected:
bool eventFilter(QObject *, QEvent *event) override;
private:
std::unique_ptr<class KColumnResizerPrivate> const d;
Q_DISABLE_COPY(KColumnResizer)
};
#endif /* KCOLUMNRESIZER_H */
@@ -0,0 +1,152 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kcontextualhelpbutton.h"
#include <QLabel>
#include <QPointer>
#include <QTextDocumentFragment>
#include <QWidgetAction>
/**
* The private class of KContextualHelpButton used for the PIMPL idiom.
* \internal
*/
class KContextualHelpButtonPrivate
{
public:
/** @see KContextualHelpButton::KContextualHelpButton() */
explicit KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget);
/** @see KContextualHelButton::setContextualHelpText() */
void setContextualHelpText(const QString &contextualHelpText);
/** @see KContextualHelButton::contextualHelpText() */
QString contextualHelpText() const;
/** @see KContextualHelpButton::setHeightHintWidget() */
void setHeightHintWidget(const QWidget *heightHintWidget);
/** @see KContextualHelpButton::setHeightHintWidget() */
const QWidget *heightHintWidget() const;
/**
* A helper method called from KContextualHelpButton::sizeHint().
*
* @returns the preferredSize based on m_heightHintWidget and fallbackSize.
* @param fallbackSize Used as the width. Also used as the height if there is no m_heightHintWidget.
*
* @see QWidget::sizeHint()
* @see KContextualHelpButton::setHeightHintWidget()
*/
QSize preferredSize(const QSize &fallbackSize) const;
private:
KContextualHelpButton *const q_ptr;
/** The popup showing the contextualHelpText. */
QLabel *m_popupLabel = nullptr;
/** @see KContextualHelpButton::setHeightHintWidget() */
QPointer<const QWidget> m_heightHintWidget;
};
KContextualHelpButton::KContextualHelpButton(const QString &contextualHelpText, const QWidget *heightHintWidget, QWidget *parent)
: QToolButton{parent}
, d_ptr(std::make_unique<KContextualHelpButtonPrivate>(this, contextualHelpText, heightHintWidget))
{
}
KContextualHelpButton::KContextualHelpButton(QWidget *parent)
: KContextualHelpButton{QString{}, nullptr, parent}
{
}
KContextualHelpButtonPrivate::KContextualHelpButtonPrivate(KContextualHelpButton *q, const QString &contextualHelpText, const QWidget *heightHintWidget)
: q_ptr{q}
, m_heightHintWidget{heightHintWidget}
{
q_ptr->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
q_ptr->setAutoRaise(true);
q_ptr->setCursor(Qt::WhatsThisCursor);
// i18n: This text will only ever be read by assistive technology like screen readers for visually-impaired. It shows up nowhere on the visible user
// interface. Usually we start button labels with a verb, however the help text contained within this button will automatically be read by the screen
// reader without requiring additional user input. In this sense the label here is more of an announcement that the text read afterwards is help relevant
// to the current context.
q_ptr->setAccessibleName(QObject::tr("Contextual Help", "@action:button accessible name"));
q_ptr->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); // Makes sure clicking this button in whatsThisMode triggers it instead of doing nothing.
auto popup = new QWidgetAction{q_ptr};
q_ptr->addAction(popup);
q_ptr->setPopupMode(QToolButton::InstantPopup);
m_popupLabel = new QLabel{q_ptr};
m_popupLabel->setWordWrap(true);
m_popupLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); // Makes the label and links within it keyboard-accessible.
m_popupLabel->setOpenExternalLinks(true);
popup->setDefaultWidget(m_popupLabel);
setContextualHelpText(contextualHelpText);
}
KContextualHelpButton::~KContextualHelpButton() = default;
void KContextualHelpButton::setContextualHelpText(const QString &contextualHelpText)
{
d_ptr->setContextualHelpText(contextualHelpText);
}
void KContextualHelpButtonPrivate::setContextualHelpText(const QString &contextualHelpText)
{
if (contextualHelpText == q_ptr->toolTip() && contextualHelpText == m_popupLabel->text()) {
return;
}
q_ptr->setToolTip(contextualHelpText);
q_ptr->setAccessibleDescription(QTextDocumentFragment::fromHtml(contextualHelpText).toPlainText()); // Makes sure html tags aren't read out.
m_popupLabel->setText(contextualHelpText);
Q_EMIT q_ptr->contextualHelpTextChanged(contextualHelpText);
}
QString KContextualHelpButton::contextualHelpText() const
{
return d_ptr->contextualHelpText();
}
QString KContextualHelpButtonPrivate::contextualHelpText() const
{
return m_popupLabel->text();
}
void KContextualHelpButton::setHeightHintWidget(const QWidget *heightHintWidget)
{
d_ptr->setHeightHintWidget(heightHintWidget);
}
void KContextualHelpButtonPrivate::setHeightHintWidget(const QWidget *heightHintWidget)
{
m_heightHintWidget = heightHintWidget;
}
const QWidget *KContextualHelpButton::heightHintWidget() const
{
return d_ptr->heightHintWidget();
}
const QWidget *KContextualHelpButtonPrivate::heightHintWidget() const
{
return m_heightHintWidget;
}
QSize KContextualHelpButton::sizeHint() const
{
return d_ptr->preferredSize(QToolButton::sizeHint());
}
QSize KContextualHelpButtonPrivate::preferredSize(const QSize &fallbackSize) const
{
return {fallbackSize.width(), m_heightHintWidget ? m_heightHintWidget->sizeHint().height() : fallbackSize.height()};
}
@@ -0,0 +1,118 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KCONTEXTUALHELPBUTTON_H
#define KCONTEXTUALHELPBUTTON_H
#include <kwidgetsaddons_export.h>
#include <QToolButton>
#include <memory>
/**
* @class KContextualHelpButton kcontextualhelpbutton.h KContextualHelpButton
*
* @brief An icon-only button for showing contextually relevant help or explanations.
*
* KContextualHelpButton allows hiding text of any length behind a small icon-only button. Hovering over the button with a mouse cursor or pressing the button
* will show the text. It can contain images and links. It is accessible by keyboard and to screen readers.
*
* **When to Use**
*
* Sometimes users require help or an explanation, even experts. This help should be readily available exactly where and when a user needs it.
* A KContextualHelpButton makes clear that inline help is available without really cluttering up the user interface. In that sense it is superior to setting
* tooltips which are completely invisible until invoked and therefore easy to miss. Especially for touch users the KContextualHelpButton is preferable.
*
* **When Not to Use**
*
* If the text is important for the user to understand and can be kept very short, place it inline below the control it affects.
* If there is no room to put this KContextualHelpButton, use QWidget::setToolTip() for shorter texts and QWidget::SetWhatsThis() for longer text or text that
* should contain hyperlinks. If your software is not already using the KXmlGui framework, consider using KToolTipHelper from KXmlGui to make the whatsThis()
* help more discoverable.
*
* **How to Use**
*
* The most simple way is creating it like any other button and then setting the help text:
*
* \code
* auto contextualHelpButton = new KContextualHelpButton{this};
* contextualHelpButton->setContextualHelpText(xi18nc("@info", "<para>A nicely formatted help text. Notice the 'para' tags at the side!</para>"));
* \endcode
*
* The most common use of this component might be on settings pages which oftentimes use a QFormLayout.
* Here is an example on how to use it next to a QCheckBox. Unfortunately we need to make the KContextualHelpButton aware of the QCheckBox here or it will
* increase the height of the row in the QFormLayout which might look bad.
* The help text in this example contains an image and a working hyperlink:
*
* \code
* auto formLayout = new QFormLayout(this);
* auto checkBox = new QCheckBox(i18nc("@option:check", "Check this box"), this);
* auto contextualHelpButton = new KContextualHelpButton{
* xi18nc("@info",
* "<para>A help text containing an icon <img src=':/icon.png'> and a <a href=\"http://www.kde.org\">link</a>.</para>"),
* checkBox,
* this};
* auto rowLayout = new QHBoxLayout{this};
* rowLayout->addWidget(checkBox);
* rowLayout->addWidget(contextualHelpButton);
* formLayout->addRow(i18nc("@title:group", "Row label:"), rowLayout);
* \endcode
*
* This class is meant to be kept somewhat consistent with the QML equivalent which is also called ContextualHelpButton.
*
* @since 6.2
*/
class KWIDGETSADDONS_EXPORT KContextualHelpButton : public QToolButton
{
Q_OBJECT
Q_PROPERTY(QString contextualHelpText READ contextualHelpText WRITE setContextualHelpText NOTIFY contextualHelpTextChanged)
public:
/**
* @param contextualHelpText The text to show when hovering or clicking this button. Consider formatting this nicely using xi18nc().
* @param heightHintWidget The KContextualHelpButton will report the \p heightHintWidget 's sizeHint().height() as its own sizeHint().height().
* This is useful to make sure that adding this KContextualHelpButton to a layout will not increase the layout's total height.
* @param parent The parent widget that gets ownership over the lifetime of this KContextualHelpButton.
*/
explicit KContextualHelpButton(const QString &contextualHelpText, const QWidget *heightHintWidget, QWidget *parent);
/** @see KContextualHelpButton::ContextualHelpButton() */
explicit KContextualHelpButton(QWidget *parent = nullptr);
~KContextualHelpButton() override;
/**
* Sets the text to show when hovering or pressing this button. Consider formatting the text nicely using xi18nc().
*/
void setContextualHelpText(const QString &contextualHelpText);
/**
* @returns the help text which is shown when hovering or pressing this button.
*/
QString contextualHelpText() const;
/**
* The KContextualHelpButton will report the \p heightHintWidget 's sizeHint().height() as its own sizeHint().height().
* This is useful to make sure that adding this KContextualHelpButton to a layout will not increase the layout's total height.
*/
void setHeightHintWidget(const QWidget *heightHintWidget);
/** @see setHeightHintWidget() */
const QWidget *heightHintWidget() const;
/** This override is an implementation detail of setHeightHintWidget() */
QSize sizeHint() const override;
Q_SIGNALS:
void contextualHelpTextChanged(const QString &newContextualHelpText);
private:
std::unique_ptr<class KContextualHelpButtonPrivate> const d_ptr;
};
#endif // KCONTEXTUALHELPBUTTON_H
@@ -0,0 +1,258 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1998 Kurt Granroth <granroth@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kcursor.h"
#include "kcursor_p.h"
#include <QAbstractScrollArea>
#include <QCursor>
#include <QEvent>
#include <QTimer>
#include <QWidget>
void KCursor::setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter)
{
KCursorPrivate::self()->setAutoHideCursor(w, enable, customEventFilter);
}
void KCursor::autoHideEventFilter(QObject *o, QEvent *e)
{
KCursorPrivate::self()->eventFilter(o, e);
}
void KCursor::setHideCursorDelay(int ms)
{
KCursorPrivate::self()->hideCursorDelay = ms;
}
int KCursor::hideCursorDelay()
{
return KCursorPrivate::self()->hideCursorDelay;
}
// **************************************************************************
KCursorPrivateAutoHideEventFilter::KCursorPrivateAutoHideEventFilter(QWidget *widget)
: m_widget(widget)
, m_wasMouseTracking(m_widget->hasMouseTracking())
, m_isCursorHidden(false)
, m_isOwnCursor(false)
{
mouseWidget()->setMouseTracking(true);
connect(&m_autoHideTimer, &QTimer::timeout, this, &KCursorPrivateAutoHideEventFilter::hideCursor);
}
KCursorPrivateAutoHideEventFilter::~KCursorPrivateAutoHideEventFilter()
{
if (m_widget != nullptr) {
mouseWidget()->setMouseTracking(m_wasMouseTracking);
}
}
void KCursorPrivateAutoHideEventFilter::resetWidget()
{
m_widget = nullptr;
}
void KCursorPrivateAutoHideEventFilter::hideCursor()
{
m_autoHideTimer.stop();
if (m_isCursorHidden) {
return;
}
m_isCursorHidden = true;
QWidget *w = mouseWidget();
m_isOwnCursor = w->testAttribute(Qt::WA_SetCursor);
if (m_isOwnCursor) {
m_oldCursor = w->cursor();
}
w->setCursor(QCursor(Qt::BlankCursor));
}
void KCursorPrivateAutoHideEventFilter::unhideCursor()
{
m_autoHideTimer.stop();
if (!m_isCursorHidden) {
return;
}
m_isCursorHidden = false;
QWidget *w = mouseWidget();
if (w->cursor().shape() != Qt::BlankCursor) { // someone messed with the cursor already
return;
}
if (m_isOwnCursor) {
w->setCursor(m_oldCursor);
} else {
w->unsetCursor();
}
}
// The widget which gets mouse events, and that shows the cursor
// (that is the viewport, for a QAbstractScrollArea)
QWidget *KCursorPrivateAutoHideEventFilter::mouseWidget() const
{
QWidget *w = m_widget;
// Is w a QAbstractScrollArea ? Call setCursor on the viewport in that case.
QAbstractScrollArea *sv = qobject_cast<QAbstractScrollArea *>(w);
if (sv) {
w = sv->viewport();
}
return w;
}
bool KCursorPrivateAutoHideEventFilter::eventFilter(QObject *o, QEvent *e)
{
Q_UNUSED(o);
// o is m_widget or its viewport
// Q_ASSERT( o == m_widget );
switch (e->type()) {
case QEvent::Leave:
case QEvent::FocusOut:
case QEvent::WindowDeactivate:
unhideCursor();
break;
case QEvent::KeyPress:
case QEvent::ShortcutOverride:
hideCursor();
break;
case QEvent::Enter:
case QEvent::FocusIn:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::Show:
case QEvent::Hide:
case QEvent::Wheel:
unhideCursor();
if (m_widget->hasFocus()) {
m_autoHideTimer.setSingleShot(true);
m_autoHideTimer.start(KCursorPrivate::self()->hideCursorDelay);
}
break;
default:
break;
}
return false;
}
KCursorPrivate *KCursorPrivate::s_self = nullptr;
KCursorPrivate *KCursorPrivate::self()
{
if (!s_self) {
s_self = new KCursorPrivate;
}
// WABA: Don't delete KCursorPrivate, it serves no real purpose.
// Even worse it causes crashes because it seems to get deleted
// during ~QApplication and ~QApplication doesn't seem to like it
// when we delete a QCursor. No idea if that is a bug itself.
return s_self;
}
KCursorPrivate::KCursorPrivate()
{
hideCursorDelay = 5000; // 5s default value
enabled = true;
}
KCursorPrivate::~KCursorPrivate()
{
}
void KCursorPrivate::setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter)
{
if (!w || !enabled) {
return;
}
QWidget *viewport = nullptr;
QAbstractScrollArea *sv = qobject_cast<QAbstractScrollArea *>(w);
if (sv) {
viewport = sv->viewport();
}
if (enable) {
if (m_eventFilters.contains(w)) {
return;
}
KCursorPrivateAutoHideEventFilter *filter = new KCursorPrivateAutoHideEventFilter(w);
m_eventFilters.insert(w, filter);
if (viewport) {
m_eventFilters.insert(viewport, filter);
connect(viewport, &QObject::destroyed, this, &KCursorPrivate::slotViewportDestroyed);
}
if (!customEventFilter) {
w->installEventFilter(filter); // for key events
if (viewport) {
viewport->installEventFilter(filter); // for mouse events
}
}
connect(w, &QObject::destroyed, this, &KCursorPrivate::slotWidgetDestroyed);
} else {
KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.take(w);
if (filter == nullptr) {
return;
}
w->removeEventFilter(filter);
if (viewport) {
m_eventFilters.remove(viewport);
disconnect(viewport, &QObject::destroyed, this, &KCursorPrivate::slotViewportDestroyed);
viewport->removeEventFilter(filter);
}
delete filter;
disconnect(w, &QObject::destroyed, this, &KCursorPrivate::slotWidgetDestroyed);
}
}
bool KCursorPrivate::eventFilter(QObject *o, QEvent *e)
{
if (!enabled || e->type() == QEvent::ChildAdded) {
return false;
}
KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.value(o);
Q_ASSERT(filter != nullptr);
if (filter == nullptr) {
return false;
}
return filter->eventFilter(o, e);
}
void KCursorPrivate::slotViewportDestroyed(QObject *o)
{
m_eventFilters.remove(o);
}
void KCursorPrivate::slotWidgetDestroyed(QObject *o)
{
KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.take(o);
Q_ASSERT(filter != nullptr);
filter->resetWidget(); // so that dtor doesn't access it
delete filter;
}
#include "moc_kcursor_p.cpp"
@@ -0,0 +1,101 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1998 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KCURSOR_H
#define KCURSOR_H
#include <kwidgetsaddons_export.h>
class QEvent;
class QObject;
class QWidget;
/**
* @class KCursor kcursor.h KCursor
*
* The KCursor class provides a set of static
* convenience methods for auto-hiding cursors on widgets.
*/
class KWIDGETSADDONS_EXPORT KCursor
{
public:
/**
* Sets auto-hiding the cursor for widget @p w. Enabling it will result in
* the cursor being hidden when
* @li a key-event happens
* @li there are no key-events for a configured time-frame (see
* setHideCursorDelay())
*
* The cursor will be shown again when the focus is lost or a mouse-event
* happens.
*
* Side effect: when enabling auto-hide, mouseTracking is enabled for the
* specified widget, because it's needed to get mouse-move-events. So
* don't disable mouseTracking for a widget while using auto-hide for it.
*
* When disabling auto-hide, mouseTracking will be disabled, so if you need
* mouseTracking after disabling auto-hide, you have to re-enable
* mouseTracking.
*
* If you want to use auto-hiding for widgets that don't take focus, e.g.
* a QCanvasView, then you have to pass all key-events that should trigger
* auto-hiding to autoHideEventFilter().
*/
static void setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter = false);
/**
* Sets the delay time in milliseconds for auto-hiding. When no keyboard
* events arrive for that time-frame, the cursor will be hidden.
*
* Default is 5000, i.e. 5 seconds.
*/
static void setHideCursorDelay(int ms);
/**
* @returns the current auto-hide delay time.
*
* Default is 5000, i.e. 5 seconds.
*/
static int hideCursorDelay();
/**
* KCursor has to install an eventFilter over the widget you want to
* auto-hide. If you have an own eventFilter() on that widget and stop
* some events by returning true, you might break auto-hiding, because
* KCursor doesn't get those events.
*
* In this case, you need to call setAutoHideCursor( widget, true, true );
* to tell KCursor not to install an eventFilter. Then you call this method
* from the beginning of your eventFilter, for example:
* \code
* edit = new KEdit( this, "some edit widget" );
* edit->installEventFilter( this );
* KCursor::setAutoHideCursor( edit, true, true );
*
* [...]
*
* bool YourClass::eventFilter( QObject *o, QEvent *e )
* {
* if ( o == edit ) // only that widget where you enabled auto-hide!
* KCursor::autoHideEventFilter( o, e );
*
* // now you can do your own event-processing
* [...]
* }
* \endcode
*
* Note that you must not call KCursor::autoHideEventFilter() when you
* didn't enable or after disabling auto-hiding.
*/
static void autoHideEventFilter(QObject *, QEvent *);
private:
KCursor() = delete;
};
#endif // _KCURSOR_H
@@ -0,0 +1,84 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCURSOR_P_H
#define KCURSOR_P_H
#include <QCursor>
#include <QHash>
#include <QObject>
#include <QTimer>
class QWidget;
/**
* @internal
* I don't want the eventFilter to be in KCursor, so we have another class
* for that stuff
* @author John Firebaugh <jfirebaugh@kde.org>
* @author Carsten Pfeiffer <pfeiffer@kde.org>
*/
class KCursorPrivateAutoHideEventFilter : public QObject
{
Q_OBJECT
public:
explicit KCursorPrivateAutoHideEventFilter(QWidget *widget);
~KCursorPrivateAutoHideEventFilter() override;
bool eventFilter(QObject *o, QEvent *e) override;
void resetWidget();
private Q_SLOTS:
void hideCursor();
void unhideCursor();
private:
QWidget *mouseWidget() const;
QTimer m_autoHideTimer;
QWidget *m_widget;
bool m_wasMouseTracking;
bool m_isCursorHidden;
bool m_isOwnCursor;
QCursor m_oldCursor;
};
/**
* @internal
* @author Carsten Pfeiffer <pfeiffer@kde.org>
* @author John Firebaugh <jfirebaugh@kde.org>
*/
class KCursorPrivate : public QObject
{
friend class KCursor; // to shut up the compiler
Q_OBJECT
public:
static KCursorPrivate *self();
void setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter);
bool eventFilter(QObject *o, QEvent *e) override;
int hideCursorDelay;
private Q_SLOTS:
void slotViewportDestroyed(QObject *);
void slotWidgetDestroyed(QObject *);
private:
KCursorPrivate();
~KCursorPrivate() override;
bool enabled;
static KCursorPrivate *s_self;
QHash<QObject *, KCursorPrivateAutoHideEventFilter *> m_eventFilters;
};
#endif // KCURSOR_PRIVATE_H
@@ -0,0 +1,467 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdatecombobox.h"
#include "common_helpers_p.h"
#include "kdatepickerpopup.h"
#include "kdaterangecontrol_p.h"
#include <QAbstractItemView>
#include <QApplication>
#include <QDate>
#include <QKeyEvent>
#include <QLineEdit>
#include <QList>
#include <QMenu>
#include <QScreen>
#include <QWidgetAction>
#include "kdatepicker.h"
#include "kmessagebox.h"
class KDateComboBoxPrivate : public KDateRangeControlPrivate
{
public:
KDateComboBoxPrivate(KDateComboBox *qq);
// TODO: Find a way to get that from QLocale
#if 0
QDate defaultMinDate();
QDate defaultMaxDate();
#endif
QString dateFormat(QLocale::FormatType format);
QString formatDate(const QDate &date);
void initDateWidget();
void updateDateWidget();
void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg);
using KDateRangeControlPrivate::setDateRange;
void editDate(const QString &text);
void enterDate(const QDate &date);
void parseDate();
void warnDate();
KDateComboBox *const q;
KDatePickerPopup *m_dateMenu;
QDate m_date;
KDateComboBox::Options m_options;
QString m_minWarnMsg;
QString m_maxWarnMsg;
bool m_warningShown;
bool m_edited; // and dateChanged not yet emitted
QLocale::FormatType m_displayFormat;
};
KDateComboBoxPrivate::KDateComboBoxPrivate(KDateComboBox *qq)
: q(qq)
, m_dateMenu(new KDatePickerPopup(KDatePickerPopup::DatePicker | KDatePickerPopup::Words, QDate::currentDate(), qq))
, m_warningShown(false)
, m_edited(false)
, m_displayFormat(QLocale::ShortFormat)
{
m_options = KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords;
m_date = QDate::currentDate();
// m_minDate = defaultMinDate();
// m_maxDate = defaultMaxDate();
}
#if 0
QDate KDateComboBoxPrivate::defaultMinDate()
{
return m_date.calendar()->earliestValidDate();
}
QDate KDateComboBoxPrivate::defaultMaxDate()
{
return m_date.calendar()->latestValidDate();
}
#endif
QString KDateComboBoxPrivate::dateFormat(QLocale::FormatType format)
{
return dateFormatWith4DigitYear(q->locale(), format);
}
QString KDateComboBoxPrivate::formatDate(const QDate &date)
{
return q->locale().toString(date, dateFormat(m_displayFormat));
}
void KDateComboBoxPrivate::initDateWidget()
{
q->blockSignals(true);
q->clear();
// If EditTime then set the line edit
q->lineEdit()->setReadOnly((m_options & KDateComboBox::EditDate) != KDateComboBox::EditDate);
// If SelectDate then make list items visible
if ((m_options & KDateComboBox::SelectDate) == KDateComboBox::SelectDate //
|| (m_options & KDateComboBox::DatePicker) == KDateComboBox::DatePicker //
|| (m_options & KDateComboBox::DatePicker) == KDateComboBox::DateKeywords) {
q->setMaxVisibleItems(1);
} else {
q->setMaxVisibleItems(0);
}
q->setSizeAdjustPolicy(QComboBox::AdjustToContents);
q->addItem(formatDate(m_date));
q->setCurrentIndex(0);
q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
q->blockSignals(false);
KDatePickerPopup::Modes modes;
if (m_options & KDateComboBox::DatePicker) {
modes |= KDatePickerPopup::DatePicker;
}
if (m_options & KDateComboBox::DateKeywords) {
modes |= KDatePickerPopup::Words;
}
m_dateMenu->setModes(modes);
}
void KDateComboBoxPrivate::updateDateWidget()
{
q->blockSignals(true);
m_dateMenu->setDate(m_date);
int pos = q->lineEdit()->cursorPosition();
q->setItemText(0, formatDate(m_date));
q->lineEdit()->setText(formatDate(m_date));
q->lineEdit()->setCursorPosition(pos);
q->blockSignals(false);
}
void KDateComboBoxPrivate::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg)
{
if (!setDateRange(minDate, maxDate)) {
return;
}
m_dateMenu->setDateRange(minDate, maxDate);
m_minWarnMsg = minWarnMsg;
m_maxWarnMsg = maxWarnMsg;
}
void KDateComboBoxPrivate::editDate(const QString &text)
{
m_warningShown = false;
m_date = q->locale().toDate(text, dateFormat(m_displayFormat));
m_edited = true;
Q_EMIT q->dateEdited(m_date);
}
void KDateComboBoxPrivate::parseDate()
{
m_date = q->locale().toDate(q->lineEdit()->text(), dateFormat(m_displayFormat));
}
void KDateComboBoxPrivate::enterDate(const QDate &date)
{
q->setDate(date);
// Re-add the combo box item in order to retain the correct widget width
q->blockSignals(true);
q->clear();
q->setSizeAdjustPolicy(QComboBox::AdjustToContents);
q->addItem(formatDate(m_date));
q->setCurrentIndex(0);
q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
q->blockSignals(false);
m_dateMenu->hide();
warnDate();
Q_EMIT q->dateEntered(m_date);
}
void KDateComboBoxPrivate::warnDate()
{
if (!m_warningShown && !q->isValid() && (m_options & KDateComboBox::WarnOnInvalid) == KDateComboBox::WarnOnInvalid) {
QString warnMsg;
if (!m_date.isValid()) {
warnMsg = KDateComboBox::tr("The date you entered is invalid", "@info");
} else if (m_minDate.isValid() && m_date < m_minDate) {
if (m_minWarnMsg.isEmpty()) {
warnMsg = KDateComboBox::tr("Date cannot be earlier than %1", "@info").arg(formatDate(m_minDate));
} else {
warnMsg = m_minWarnMsg;
warnMsg.replace(QLatin1String("%1"), formatDate(m_minDate));
}
} else if (m_maxDate.isValid() && m_date > m_maxDate) {
if (m_maxWarnMsg.isEmpty()) {
warnMsg = KDateComboBox::tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate));
} else {
warnMsg = m_maxWarnMsg;
warnMsg.replace(QLatin1String("%1"), formatDate(m_maxDate));
}
}
m_warningShown = true;
KMessageBox::error(q, warnMsg);
}
}
KDateComboBox::KDateComboBox(QWidget *parent)
: QComboBox(parent)
, d(new KDateComboBoxPrivate(this))
{
setEditable(true);
setMaxVisibleItems(1);
setInsertPolicy(QComboBox::NoInsert);
d->initDateWidget();
d->updateDateWidget();
connect(d->m_dateMenu, &KDatePickerPopup::dateChanged, this, [this](QDate date) {
if (d->isInDateRange(date)) {
d->enterDate(date);
}
});
connect(this, &QComboBox::editTextChanged, this, [this](const QString &text) {
d->editDate(text);
});
connect(lineEdit(), &QLineEdit::returnPressed, this, [this]() {
if (d->m_edited) {
d->enterDate(date());
Q_EMIT dateChanged(date());
}
});
}
KDateComboBox::~KDateComboBox() = default;
QDate KDateComboBox::date() const
{
d->parseDate();
return d->m_date;
}
void KDateComboBox::setDate(const QDate &date)
{
if (date == d->m_date) {
return;
}
d->m_edited = false;
assignDate(date);
d->updateDateWidget();
Q_EMIT dateChanged(d->m_date);
}
void KDateComboBox::assignDate(const QDate &date)
{
d->m_date = date;
}
bool KDateComboBox::isValid() const
{
d->parseDate();
return d->isInDateRange(d->m_date);
}
bool KDateComboBox::isNull() const
{
return lineEdit()->text().isEmpty();
}
KDateComboBox::Options KDateComboBox::options() const
{
return d->m_options;
}
void KDateComboBox::setOptions(Options options)
{
if (options != d->m_options) {
d->m_options = options;
d->initDateWidget();
d->updateDateWidget();
}
}
QDate KDateComboBox::minimumDate() const
{
return d->m_minDate;
}
void KDateComboBox::setMinimumDate(const QDate &minDate, const QString &minWarnMsg)
{
if (minDate.isValid()) {
d->setDateRange(minDate, d->m_maxDate, minWarnMsg, d->m_maxWarnMsg);
}
}
void KDateComboBox::resetMinimumDate()
{
d->setDateRange(QDate(), d->m_maxDate, QString(), d->m_maxWarnMsg);
}
QDate KDateComboBox::maximumDate() const
{
return d->m_maxDate;
}
void KDateComboBox::setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg)
{
if (maxDate.isValid()) {
d->setDateRange(d->m_minDate, maxDate, d->m_minWarnMsg, maxWarnMsg);
}
}
void KDateComboBox::resetMaximumDate()
{
d->setDateRange(d->m_minDate, QDate(), d->m_minWarnMsg, QString());
}
void KDateComboBox::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg)
{
if (minDate.isValid() && maxDate.isValid()) {
d->setDateRange(minDate, maxDate, minWarnMsg, maxWarnMsg);
}
}
void KDateComboBox::resetDateRange()
{
d->setDateRange(QDate(), QDate(), QString(), QString());
}
QLocale::FormatType KDateComboBox::displayFormat() const
{
return d->m_displayFormat;
}
void KDateComboBox::setDisplayFormat(QLocale::FormatType format)
{
if (format != d->m_displayFormat) {
d->m_displayFormat = format;
d->initDateWidget();
d->updateDateWidget();
}
}
QMap<QDate, QString> KDateComboBox::dateMap() const
{
return d->m_dateMenu->dateMap();
}
void KDateComboBox::setDateMap(QMap<QDate, QString> dateMap)
{
d->m_dateMenu->setDateMap(dateMap);
}
bool KDateComboBox::eventFilter(QObject *object, QEvent *event)
{
return QComboBox::eventFilter(object, event);
}
void KDateComboBox::keyPressEvent(QKeyEvent *keyEvent)
{
QDate temp;
switch (keyEvent->key()) {
case Qt::Key_Down:
temp = d->m_date.addDays(-1);
break;
case Qt::Key_Up:
temp = d->m_date.addDays(1);
break;
case Qt::Key_PageDown:
temp = d->m_date.addMonths(-1);
break;
case Qt::Key_PageUp:
temp = d->m_date.addMonths(1);
break;
default:
QComboBox::keyPressEvent(keyEvent);
return;
}
if (d->isInDateRange(temp)) {
d->enterDate(temp);
}
}
void KDateComboBox::focusOutEvent(QFocusEvent *event)
{
d->parseDate();
d->warnDate();
if (d->m_edited) {
d->m_edited = false;
Q_EMIT dateEntered(d->m_date);
Q_EMIT dateChanged(d->m_date);
}
QComboBox::focusOutEvent(event);
}
void KDateComboBox::showPopup()
{
if (!isEditable() || !d->m_dateMenu //
|| (d->m_options & KDateComboBox::SelectDate) != KDateComboBox::SelectDate) {
return;
}
d->m_dateMenu->setDate(d->m_date);
const QRect desk = screen()->geometry();
QPoint popupPoint = mapToGlobal(QPoint(0, 0));
const int dateFrameHeight = d->m_dateMenu->sizeHint().height();
if (popupPoint.y() + height() + dateFrameHeight > desk.bottom()) {
popupPoint.setY(popupPoint.y() - dateFrameHeight);
} else {
popupPoint.setY(popupPoint.y() + height());
}
const int dateFrameWidth = d->m_dateMenu->sizeHint().width();
if (popupPoint.x() + dateFrameWidth > desk.right()) {
popupPoint.setX(desk.right() - dateFrameWidth);
}
if (popupPoint.x() < desk.left()) {
popupPoint.setX(desk.left());
}
if (popupPoint.y() < desk.top()) {
popupPoint.setY(desk.top());
}
d->m_dateMenu->popup(popupPoint);
}
void KDateComboBox::hidePopup()
{
QComboBox::hidePopup();
}
void KDateComboBox::mousePressEvent(QMouseEvent *event)
{
QComboBox::mousePressEvent(event);
}
void KDateComboBox::wheelEvent(QWheelEvent *event)
{
QDate temp;
if (event->angleDelta().y() < 0) {
temp = d->m_date.addDays(-1);
} else {
temp = d->m_date.addDays(1);
}
if (d->isInDateRange(temp)) {
d->enterDate(temp);
}
}
void KDateComboBox::focusInEvent(QFocusEvent *event)
{
QComboBox::focusInEvent(event);
}
void KDateComboBox::resizeEvent(QResizeEvent *event)
{
QComboBox::resizeEvent(event);
}
#include "moc_kdatecombobox.cpp"
@@ -0,0 +1,300 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATECOMBOBOX_H
#define KDATECOMBOBOX_H
#include <kwidgetsaddons_export.h>
#include <QComboBox>
#include <QLocale>
#include <memory>
/**
* @class KDateComboBox kdatecombobox.h KDateComboBox
*
* @short A combobox for dates.
*/
class KWIDGETSADDONS_EXPORT KDateComboBox : public QComboBox
{
Q_OBJECT
Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true)
Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate RESET resetMinimumDate)
Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate RESET resetMaximumDate)
Q_PROPERTY(Options options READ options WRITE setOptions)
public:
/**
* Options provided by the widget
* @see options()
* @see setOptions()
* @see Options
*/
enum Option {
EditDate = 0x0001, /**< Allow the user to manually edit the date in the combo line edit */
SelectDate = 0x0002, /**< Allow the user to select the date from a drop-down menu */
DatePicker = 0x0004, /**< Show a date picker in the drop-down */
DateKeywords = 0x0008, /**< Show date keywords in the drop-down */
WarnOnInvalid = 0x0010, /**< Show a warning on focus out if the date is invalid */
};
/**
* Stores a combination of #Option values.
*/
Q_DECLARE_FLAGS(Options, Option)
Q_FLAG(Options)
/**
* Create a new KDateComboBox widget
*
* By default the EditDate, SelectDate, DatePicker and DateKeywords options
* are enabled, the ShortDate format is used and the date is set to the
* current date.
*/
explicit KDateComboBox(QWidget *parent = nullptr);
/**
* Destroy the widget
*/
~KDateComboBox() override;
/**
* Return the currently selected date
*
* @return the currently selected date
*/
QDate date() const;
/**
* Return if the current user input is valid
*
* If the user input is null then it is not valid
*
* @return if the current user input is valid
*
* @see isNull()
*/
bool isValid() const;
/**
* Return if the current user input is null
*
* @return if the current user input is null
*
* @see isValid()
*/
bool isNull() const;
/**
* Return the currently set widget options
*
* @return the currently set widget options
*/
Options options() const;
/**
* Return the currently set date display format
*
* By default this is the Short Format
*
* @return the currently set date format
*/
QLocale::FormatType displayFormat() const;
/**
* Return the current minimum date
*
* @return the current minimum date
*/
QDate minimumDate() const;
/**
* Return the current maximum date
*
* @return the current maximum date
*/
QDate maximumDate() const;
/**
* Return the map of dates listed in the drop-down and their displayed
* string forms.
*
* @return the select date map
*
* @see setDateMap()
*/
QMap<QDate, QString> dateMap() const;
Q_SIGNALS:
/**
* Signal if the date has been manually entered (by typing a date and losing focus, or pressing Enter)
* or selected by the user (using the popup selector, or up, down, page up, page down keys, or the mouse wheel).
*
* The emitted date may be invalid.
*
* @param date the new date
*/
void dateEntered(const QDate &date);
/**
* Signal if the date has been changed either manually by the user
* or programmatically.
*
* The emitted date may be invalid.
*
* @param date the new date
*/
void dateChanged(const QDate &date);
/**
* Signal if the date is being manually edited by the user.
*
* The emitted date may be invalid, or may not yet be what the user intends as the final date.
*
* @param date the new date
*/
void dateEdited(const QDate &date);
public Q_SLOTS:
/**
* Set the currently selected date
*
* You can set an invalid date or a date outside the valid range, validity
* checking is only done via isValid().
*
* @param date the new date
*/
void setDate(const QDate &date);
/**
* Set the new widget options
*
* @param options the new widget options
*/
void setOptions(Options options);
/**
* Sets the date format to display.
*
* By default is the Short Format.
*
* @param format the date format to use
*/
void setDisplayFormat(QLocale::FormatType format);
/**
* Set the valid date range to be applied by isValid().
*
* Both dates must be valid and the minimum date must be less than or equal
* to the maximum date, otherwise the date range will not be set.
*
* @param minDate the minimum date
* @param maxDate the maximum date
* @param minWarnMsg the minimum warning message
* @param maxWarnMsg the maximum warning message
*/
void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg = QString(), const QString &maxWarnMsg = QString());
/**
* Reset the minimum and maximum date to the default values.
* @see setDateRange()
*/
void resetDateRange();
/**
* Set the minimum allowed date.
*
* If the date is invalid, or greater than current maximum,
* then the minimum will not be set.
*
* @param minDate the minimum date
* @param minWarnMsg the minimum warning message
*
* @see minimumDate()
* @see maximumDate()
* @see setMaximumDate()
* @see setDateRange()
*/
void setMinimumDate(const QDate &minDate, const QString &minWarnMsg = QString());
/**
* Reset the minimum date to the default.
*
* The default is to have no minimum date.
*/
void resetMinimumDate();
/**
* Set the maximum allowed date.
*
* If the date is invalid, or less than current minimum,
* then the maximum will not be set.
*
* @param maxDate the maximum date
* @param maxWarnMsg the maximum warning message
*
* @see minimumDate()
* @see maximumDate()
* @see setMaximumDate()
* @see setDateRange()
*/
void setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg = QString());
/**
* Reset the maximum date to the default
*
* The default is to have no maximum date.
*/
void resetMaximumDate();
/**
* Set the list of dates able to be selected from the drop-down and the
* string form to display for those dates, e.g. "2010-01-01" and "Yesterday".
*
* Any invalid or duplicate dates will be used, the list will NOT be
* sorted, and the minimum and maximum date will not be affected.
*
* The @p dateMap is keyed by the date to be listed and the value is the
* string to be displayed. If you want the date to be displayed in the
* default date format then the string should be null. If you want a
* separator to be displayed then set the string to "separator".
*
* @param dateMap the map of dates able to be selected
*
* @see dateMap()
*/
void setDateMap(QMap<QDate, QString> dateMap);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
void showPopup() override;
void hidePopup() override;
void mousePressEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
/**
* Assign the date for the widget.
*
* Virtual to allow sub-classes to apply extra validation rules.
*
* @param date the new date
*/
virtual void assignDate(const QDate &date);
private:
friend class KDateComboBoxPrivate;
std::unique_ptr<class KDateComboBoxPrivate> const d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KDateComboBox::Options)
#endif // KDATECOMBOBOX_H
@@ -0,0 +1,663 @@
/* -*- C++ -*-
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
SPDX-FileCopyrightText: 1998-2001 Mirko Boehm (mirko@kde.org)
SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdatepicker.h"
#include "kdatepicker_p.h"
#include "common_helpers_p.h"
#include "kdatetable_p.h"
#include <kpopupframe.h>
#include <QApplication>
#include <QComboBox>
#include <QFont>
#include <QFontDatabase>
#include <QKeyEvent>
#include <QLayout>
#include <QMenu>
#include <QStyle>
#include <QToolButton>
#include "loggingcategory.h"
#include "moc_kdatepicker.cpp"
#include "moc_kdatepicker_p.cpp"
class DatePickerValidator : public QValidator
{
Q_OBJECT
public:
explicit DatePickerValidator(KDatePicker *parent)
: QValidator(parent)
, picker(parent)
{
}
State validate(QString &text, int &) const override
{
return toDate(text).isValid() ? QValidator::Acceptable : QValidator::Intermediate;
}
QDate toDate(const QString &text) const
{
QLocale locale = picker->locale();
const QList<QString> formats{
locale.dateFormat(QLocale::LongFormat),
locale.dateFormat(QLocale::ShortFormat),
locale.dateFormat(QLocale::NarrowFormat),
dateFormatWith4DigitYear(locale, QLocale::ShortFormat),
QStringLiteral("yyyy-MM-dd"),
};
QDate date;
for (const auto &format : formats) {
date = locale.toDate(text, format);
if (date.isValid()) {
break;
}
}
if (!date.isValid()) {
qCDebug(KWidgetsAddonsLog) << "Could not parse text as date:" << text;
}
return date;
}
private:
KDatePicker *const picker;
};
// Week numbers are defined by ISO 8601
// See http://www.merlyn.demon.co.uk/weekinfo.htm for details
KDatePickerPrivateYearSelector::KDatePickerPrivateYearSelector(const QDate &currentDate, QWidget *parent)
: QLineEdit(parent)
, val(new QIntValidator(this))
, result(0)
, oldDate{currentDate}
{
setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
setFrame(false);
// TODO: Find a way to get that from QLocale
// val->setRange( calendar->year( calendar->earliestValidDate() ),
// calendar->year( calendar->latestValidDate() ) );
setValidator(val);
connect(this, &QLineEdit::returnPressed, this, &KDatePickerPrivateYearSelector::yearEnteredSlot);
}
void KDatePickerPrivateYearSelector::yearEnteredSlot()
{
bool ok;
int newYear;
// check if entered value is a number
newYear = text().toInt(&ok);
if (!ok) {
QApplication::beep();
return;
}
// check if new year will lead to a valid date
if (QDate(newYear, oldDate.month(), oldDate.day()).isValid()) {
result = newYear;
Q_EMIT closeMe(1);
} else {
QApplication::beep();
}
}
int KDatePickerPrivateYearSelector::year()
{
return result;
}
void KDatePickerPrivateYearSelector::setYear(const QDate &year)
{
setText(locale().toString(year, QStringLiteral("yyyy")).rightJustified(4, QLatin1Char('0')));
}
class KDatePickerPrivate
{
Q_DECLARE_TR_FUNCTIONS(KDatePicker)
public:
explicit KDatePickerPrivate(KDatePicker *qq)
: q(qq)
{
}
void fillWeeksCombo();
QDate validDateInYearMonth(int year, int month);
/// the date table
KDatePicker *q;
QToolButton *closeButton = nullptr;
QComboBox *selectWeek = nullptr;
QToolButton *todayButton = nullptr;
QBoxLayout *navigationLayout = nullptr;
/// the year forward button
QToolButton *yearForward = nullptr;
/// the year backward button
QToolButton *yearBackward = nullptr;
/// the month forward button
QToolButton *monthForward = nullptr;
/// the month backward button
QToolButton *monthBackward = nullptr;
/// the button for selecting the month directly
QToolButton *selectMonth = nullptr;
/// the button for selecting the year directly
QToolButton *selectYear = nullptr;
/// the line edit to enter the date directly
QLineEdit *line = nullptr;
/// the validator for the line edit:
DatePickerValidator *val = nullptr;
/// the date table
KDateTable *table = nullptr;
/// the widest month string in pixels:
QSize maxMonthRect;
/// the font size for the widget
int fontsize = -1;
};
void KDatePickerPrivate::fillWeeksCombo()
{
// every year can have a different number of weeks
// it could be that we had 53,1..52 and now 1..53 which is the same number but different
// so always fill with new values
// We show all week numbers for all weeks between first day of year to last day of year
// This of course can be a list like 53,1,2..52
const QDate thisDate = q->date();
const int thisYear = thisDate.year();
QDate day(thisDate.year(), 1, 1);
const QDate lastDayOfYear = QDate(thisDate.year() + 1, 1, 1).addDays(-1);
selectWeek->clear();
// Starting from the first day in the year, loop through the year a week at a time
// adding an entry to the week combo for each week in the year
for (; day.isValid() && day <= lastDayOfYear; day = day.addDays(7)) {
// Get the ISO week number for the current day and what year that week is in
// e.g. 1st day of this year may fall in week 53 of previous year
int weekYear = thisYear;
const int week = day.weekNumber(&weekYear);
QString weekString = tr("Week %1").arg(q->locale().toString(week));
// show that this is a week from a different year
if (weekYear != thisYear) {
weekString += QLatin1Char('*');
}
// when the week is selected, go to the same weekday as the one
// that is currently selected in the date table
QDate targetDate = day.addDays(thisDate.dayOfWeek() - day.dayOfWeek());
selectWeek->addItem(weekString, targetDate);
// make sure that the week of the lastDayOfYear is always inserted: in Chinese calendar
// system, this is not always the case
if (day < lastDayOfYear //
&& day.daysTo(lastDayOfYear) < 7 //
&& lastDayOfYear.weekNumber() != day.weekNumber()) {
day = lastDayOfYear.addDays(-7);
}
}
}
QDate KDatePickerPrivate::validDateInYearMonth(int year, int month)
{
QDate newDate;
// Try to create a valid date in this year and month
// First try the first of the month, then try last of month
if (QDate(year, month, 1).isValid()) {
newDate = QDate(year, month, 1);
} else if (QDate(year, month + 1, 1).isValid()) {
newDate = QDate(year, month + 1, 1).addDays(-1);
} else {
newDate = QDate::fromJulianDay(0);
}
return newDate;
}
KDatePicker::KDatePicker(QWidget *parent)
: QFrame(parent)
, d(new KDatePickerPrivate(this))
{
initWidget(QDate::currentDate());
}
KDatePicker::KDatePicker(const QDate &date_, QWidget *parent)
: QFrame(parent)
, d(new KDatePickerPrivate(this))
{
initWidget(date_);
}
void KDatePicker::initWidget(const QDate &date_)
{
const int horizontalSpacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
QBoxLayout *topLayout = new QVBoxLayout(this);
topLayout->setSpacing(0);
topLayout->setContentsMargins(0, 0, 0, 0);
d->navigationLayout = new QHBoxLayout();
d->navigationLayout->setSpacing(0);
d->navigationLayout->setContentsMargins(0, 0, 0, 0);
topLayout->addLayout(d->navigationLayout);
d->navigationLayout->addStretch();
d->yearBackward = new QToolButton(this);
d->yearBackward->setAutoRaise(true);
d->navigationLayout->addWidget(d->yearBackward);
d->monthBackward = new QToolButton(this);
d->monthBackward->setAutoRaise(true);
d->navigationLayout->addWidget(d->monthBackward);
d->navigationLayout->addSpacing(horizontalSpacing);
d->selectMonth = new QToolButton(this);
d->selectMonth->setAutoRaise(true);
d->navigationLayout->addWidget(d->selectMonth);
d->selectYear = new QToolButton(this);
d->selectYear->setCheckable(true);
d->selectYear->setAutoRaise(true);
d->navigationLayout->addWidget(d->selectYear);
d->navigationLayout->addSpacing(horizontalSpacing);
d->monthForward = new QToolButton(this);
d->monthForward->setAutoRaise(true);
d->navigationLayout->addWidget(d->monthForward);
d->yearForward = new QToolButton(this);
d->yearForward->setAutoRaise(true);
d->navigationLayout->addWidget(d->yearForward);
d->navigationLayout->addStretch();
d->line = new QLineEdit(this);
d->val = new DatePickerValidator(this);
d->table = new KDateTable(this);
setFocusProxy(d->table);
d->fontsize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize();
if (d->fontsize == -1) {
d->fontsize = QFontInfo(QFontDatabase::systemFont(QFontDatabase::GeneralFont)).pointSize();
}
d->fontsize++; // Make a little bigger
d->selectWeek = new QComboBox(this); // read only week selection
d->selectWeek->setFocusPolicy(Qt::NoFocus);
d->todayButton = new QToolButton(this);
d->todayButton->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-today")));
d->yearForward->setToolTip(tr("Next year", "@info:tooltip"));
d->yearBackward->setToolTip(tr("Previous year", "@info:tooltip"));
d->monthForward->setToolTip(tr("Next month", "@info:tooltip"));
d->monthBackward->setToolTip(tr("Previous month", "@info:tooltip"));
d->selectWeek->setToolTip(tr("Select a week", "@info:tooltip"));
d->selectMonth->setToolTip(tr("Select a month", "@info:tooltip"));
d->selectYear->setToolTip(tr("Select a year", "@info:tooltip"));
d->todayButton->setToolTip(tr("Select the current day", "@info:tooltip"));
// -----
setFontSize(d->fontsize);
d->line->setValidator(d->val);
d->line->installEventFilter(this);
if (QApplication::isRightToLeft()) {
d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
} else {
d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
}
connect(d->table, &KDateTable::dateChanged, this, &KDatePicker::dateChangedSlot);
connect(d->table, &KDateTable::tableClicked, this, &KDatePicker::tableClickedSlot);
connect(d->monthForward, &QAbstractButton::clicked, this, &KDatePicker::monthForwardClicked);
connect(d->monthBackward, &QAbstractButton::clicked, this, &KDatePicker::monthBackwardClicked);
connect(d->yearForward, &QAbstractButton::clicked, this, &KDatePicker::yearForwardClicked);
connect(d->yearBackward, &QAbstractButton::clicked, this, &KDatePicker::yearBackwardClicked);
connect(d->selectWeek, &QComboBox::activated, this, &KDatePicker::weekSelected);
connect(d->todayButton, &QAbstractButton::clicked, this, &KDatePicker::todayButtonClicked);
connect(d->selectMonth, &QAbstractButton::clicked, this, &KDatePicker::selectMonthClicked);
connect(d->selectYear, &QAbstractButton::toggled, this, &KDatePicker::selectYearClicked);
connect(d->line, &QLineEdit::returnPressed, this, &KDatePicker::lineEnterPressed);
topLayout->addWidget(d->table);
QBoxLayout *bottomLayout = new QHBoxLayout();
bottomLayout->setContentsMargins(0, 0, 0, 0);
bottomLayout->setSpacing(0);
topLayout->addLayout(bottomLayout);
bottomLayout->addWidget(d->todayButton);
bottomLayout->addWidget(d->line);
bottomLayout->addWidget(d->selectWeek);
d->table->setDate(date_);
dateChangedSlot(date_); // needed because table emits changed only when newDate != oldDate
}
KDatePicker::~KDatePicker() = default;
bool KDatePicker::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *k = (QKeyEvent *)e;
if ((k->key() == Qt::Key_PageUp) //
|| (k->key() == Qt::Key_PageDown) //
|| (k->key() == Qt::Key_Up) //
|| (k->key() == Qt::Key_Down)) {
QApplication::sendEvent(d->table, e);
d->table->setFocus();
return true; // eat event
}
}
return QFrame::eventFilter(o, e);
}
void KDatePicker::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
}
void KDatePicker::dateChangedSlot(const QDate &date_)
{
d->line->setText(locale().toString(date_, dateFormatWith4DigitYear(locale(), QLocale::ShortFormat)));
d->selectMonth->setText(locale().standaloneMonthName(date_.month(), QLocale::LongFormat));
d->fillWeeksCombo();
// calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week
QDate firstDay(date_.year(), 1, 1);
// If we cannot successfully create the 1st of the year, this can only mean that
// the 1st is before the earliest valid date in the current calendar system, so use
// the earliestValidDate as the first day.
// In particular covers the case of Gregorian where 1/1/-4713 is not a valid QDate
d->selectWeek->setCurrentIndex((date_.dayOfYear() + firstDay.dayOfWeek() - 2) / 7);
d->selectYear->setText(locale().toString(date_, QStringLiteral("yyyy")).rightJustified(4, QLatin1Char('0')));
Q_EMIT dateChanged(date_);
}
void KDatePicker::tableClickedSlot()
{
Q_EMIT dateSelected(date());
Q_EMIT tableClicked();
}
// TODO KF6: remove the const & from the returned QDate
const QDate &KDatePicker::date() const
{
return d->table->date();
}
bool KDatePicker::setDate(const QDate &date_)
{
// the table setDate does validity checking for us
// this also emits dateChanged() which then calls our dateChangedSlot()
return d->table->setDate(date_);
}
void KDatePicker::monthForwardClicked()
{
if (!setDate(date().addMonths(1))) {
QApplication::beep();
}
d->table->setFocus();
}
void KDatePicker::monthBackwardClicked()
{
if (!setDate(date().addMonths(-1))) {
QApplication::beep();
}
d->table->setFocus();
}
void KDatePicker::yearForwardClicked()
{
if (!setDate(d->table->date().addYears(1))) {
QApplication::beep();
}
d->table->setFocus();
}
void KDatePicker::yearBackwardClicked()
{
if (!setDate(d->table->date().addYears(-1))) {
QApplication::beep();
}
d->table->setFocus();
}
void KDatePicker::weekSelected(int index)
{
QDate targetDay = d->selectWeek->itemData(index).toDateTime().date();
if (!setDate(targetDay)) {
QApplication::beep();
}
d->table->setFocus();
}
void KDatePicker::selectMonthClicked()
{
QDate thisDate(date());
d->table->setFocus();
QMenu popup(d->selectMonth);
// Populate the pick list with all the month names, this may change by year
// JPL do we need to do something here for months that fall outside valid range?
const int monthsInYear = QDate(thisDate.year() + 1, 1, 1).addDays(-1).month();
for (int m = 1; m <= monthsInYear; m++) {
popup.addAction(locale().standaloneMonthName(m))->setData(m);
}
QAction *item = popup.actions()[thisDate.month() - 1];
// if this happens the above should already given an assertion
if (item) {
popup.setActiveAction(item);
}
// cancelled
if ((item = popup.exec(d->selectMonth->mapToGlobal(QPoint(0, 0)), item)) == nullptr) {
return;
}
// We need to create a valid date in the month selected so we can find out how many days are
// in the month.
QDate newDate(thisDate.year(), item->data().toInt(), 1);
// If we have succeeded in creating a date in the new month, then try to create the new date,
// checking we don't set a day after the last day of the month
newDate.setDate(newDate.year(), newDate.month(), qMin(thisDate.day(), newDate.daysInMonth()));
// Set the date, if it's invalid in any way then alert user and don't update
if (!setDate(newDate)) {
QApplication::beep();
}
}
void KDatePicker::selectYearClicked()
{
if (!d->selectYear->isChecked()) {
return;
}
QDate thisDate(date());
KPopupFrame *popup = new KPopupFrame(this);
KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector(date(), popup);
picker->resize(picker->sizeHint());
picker->setYear(thisDate);
picker->selectAll();
popup->setMainWidget(picker);
connect(picker, &KDatePickerPrivateYearSelector::closeMe, popup, &KPopupFrame::close);
picker->setFocus();
if (popup->exec(d->selectYear->mapToGlobal(QPoint(0, d->selectMonth->height())))) {
// We need to create a valid date in the year/month selected so we can find out how many
// days are in the month.
QDate newDate(picker->year(), thisDate.month(), 1);
// If we have succeeded in creating a date in the new month, then try to create the new
// date, checking we don't set a day after the last day of the month
newDate = QDate(newDate.year(), newDate.month(), qMin(thisDate.day(), newDate.daysInMonth()));
// Set the date, if it's invalid in any way then alert user and don't update
if (!setDate(newDate)) {
QApplication::beep();
}
}
delete popup;
d->selectYear->setChecked(false);
}
void KDatePicker::uncheckYearSelector()
{
d->selectYear->setChecked(false);
d->selectYear->update();
}
void KDatePicker::changeEvent(QEvent *event)
{
if (event && event->type() == QEvent::EnabledChange) {
if (isEnabled()) {
d->table->setFocus();
}
}
}
KDateTable *KDatePicker::dateTable() const
{
return d->table;
}
void KDatePicker::lineEnterPressed()
{
const QDate newDate = d->val->toDate(d->line->text());
if (newDate.isValid()) {
Q_EMIT dateEntered(newDate);
setDate(newDate);
d->table->setFocus();
} else {
QApplication::beep();
}
}
void KDatePicker::todayButtonClicked()
{
setDate(QDate::currentDate());
d->table->setFocus();
}
QSize KDatePicker::sizeHint() const
{
return QWidget::sizeHint();
}
void KDatePicker::setFontSize(int s)
{
QWidget *const buttons[] = {
d->selectMonth,
d->selectYear,
};
QFont font;
QRect r;
// -----
d->fontsize = s;
for (QWidget *button : buttons) {
font = button->font();
font.setPointSize(s);
button->setFont(font);
}
d->table->setFontSize(s);
QFontMetrics metrics(d->selectMonth->fontMetrics());
QString longestMonth;
for (int i = 1;; ++i) {
QString str = locale().standaloneMonthName(i, QLocale::LongFormat);
if (str.isNull()) {
break;
}
r = metrics.boundingRect(str);
if (r.width() > d->maxMonthRect.width()) {
d->maxMonthRect.setWidth(r.width());
longestMonth = str;
}
if (r.height() > d->maxMonthRect.height()) {
d->maxMonthRect.setHeight(r.height());
}
}
QStyleOptionToolButton opt;
opt.initFrom(d->selectMonth);
opt.text = longestMonth;
// stolen from QToolButton
QSize textSize = metrics.size(Qt::TextShowMnemonic, longestMonth);
textSize.setWidth(textSize.width() + metrics.horizontalAdvance(QLatin1Char(' ')) * 2);
int w = textSize.width();
int h = textSize.height();
opt.rect.setHeight(h); // PM_MenuButtonIndicator depends on the height
QSize metricBound = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), d->selectMonth);
d->selectMonth->setMinimumSize(metricBound);
}
int KDatePicker::fontSize() const
{
return d->fontsize;
}
void KDatePicker::setCloseButton(bool enable)
{
if (enable == (d->closeButton != nullptr)) {
return;
}
if (enable) {
d->closeButton = new QToolButton(this);
d->closeButton->setAutoRaise(true);
const int horizontalSpacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
d->navigationLayout->addSpacing(horizontalSpacing);
d->navigationLayout->addWidget(d->closeButton);
d->closeButton->setToolTip(tr("Close", "@action:button"));
d->closeButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
connect(d->closeButton, &QAbstractButton::clicked, topLevelWidget(), &QWidget::close);
} else {
delete d->closeButton;
d->closeButton = nullptr;
}
updateGeometry();
}
bool KDatePicker::hasCloseButton() const
{
return (d->closeButton);
}
#include "kdatepicker.moc"
@@ -0,0 +1,171 @@
/* -*- C++ -*-
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
SPDX-FileCopyrightText: 1998-2001 Mirko Boehm <mirko@kde.org>
SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATEPICKER_H
#define KDATEPICKER_H
#include <kwidgetsaddons_export.h>
#include <QFrame>
#include <memory>
class QLineEdit;
class KDateTable;
/**
* @class KDatePicker kdatepicker.h KDatePicker
*
* @short A date selection widget.
*
* Provides a widget for calendar date input.
*
* Different from the previous versions, it now emits two types of signals,
* either dateSelected() or dateEntered() (see documentation for both signals).
*
* A line edit has been added in the newer versions to allow the user
* to select a date directly by entering numbers like 19990101
* or 990101.
*
* \image html kdatepicker.png "KDatePicker Widget"
*
* @author Tim Gilman, Mirko Boehm
*/
class KWIDGETSADDONS_EXPORT KDatePicker : public QFrame
{
Q_OBJECT
Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true)
Q_PROPERTY(bool closeButton READ hasCloseButton WRITE setCloseButton)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize)
public:
/**
* The constructor. The current date will be displayed initially.
*/
explicit KDatePicker(QWidget *parent = nullptr);
/**
* The constructor. The given date will be displayed initially.
*/
explicit KDatePicker(const QDate &dt, QWidget *parent = nullptr);
/**
* The destructor.
*/
~KDatePicker() override;
/**
* The size hint for date pickers. The size hint recommends the
* minimum size of the widget so that all elements may be placed
* without clipping. This sometimes looks ugly, so when using the
* size hint, try adding 28 to each of the reported numbers of
* pixels.
*/
QSize sizeHint() const override;
/**
* Sets the date.
*
* @returns @p false and does not change anything if the date given is invalid.
*/
bool setDate(const QDate &date);
/**
* @returns the selected date.
*/
const QDate &date() const;
/**
* Sets the font size of the widgets elements.
*/
void setFontSize(int);
/**
* Returns the font size of the widget elements.
*/
int fontSize() const;
/**
* By calling this method with @p enable = true, KDatePicker will show
* a little close-button in the upper button-row. Clicking the
* close-button will cause the KDatePicker's topLevelWidget()'s close()
* method being called. This is mostly useful for toplevel datepickers
* without a window manager decoration.
* @see hasCloseButton
*/
void setCloseButton(bool enable);
/**
* @returns true if a KDatePicker shows a close-button.
* @see setCloseButton
*/
bool hasCloseButton() const;
protected:
/// to catch move keyEvents when QLineEdit has keyFocus
bool eventFilter(QObject *o, QEvent *e) override;
/// the resize event
void resizeEvent(QResizeEvent *) override;
void changeEvent(QEvent *event) override;
protected Q_SLOTS:
void dateChangedSlot(const QDate &date);
void tableClickedSlot();
void monthForwardClicked();
void monthBackwardClicked();
void yearForwardClicked();
void yearBackwardClicked();
void selectMonthClicked();
void selectYearClicked();
void uncheckYearSelector();
void lineEnterPressed();
void todayButtonClicked();
void weekSelected(int);
Q_SIGNALS:
/**
* This signal is emitted each time the selected date is changed.
* Usually, this does not mean that the date has been entered,
* since the date also changes, for example, when another month is
* selected.
* @see dateSelected
*/
void dateChanged(const QDate &date);
/**
* This signal is emitted each time a day has been selected by
* clicking on the table (hitting a day in the current month). It
* has the same meaning as dateSelected() in older versions of
* KDatePicker.
*/
void dateSelected(const QDate &date);
/**
* This signal is emitted when enter is pressed and a VALID date
* has been entered before into the line edit. Connect to both
* dateEntered() and dateSelected() to receive all events where the
* user really enters a date.
*/
void dateEntered(const QDate &date);
/**
* This signal is emitted when the day has been selected by
* clicking on it in the table.
*/
void tableClicked();
private:
KWIDGETSADDONS_NO_EXPORT KDateTable *dateTable() const;
KWIDGETSADDONS_NO_EXPORT void initWidget(const QDate &date);
private:
friend class KDatePickerPrivate;
std::unique_ptr<class KDatePickerPrivate> const d;
};
#endif // KDATEPICKER_H
@@ -0,0 +1,47 @@
/* -*- C++ -*-
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Tim D . Gilman <tdgilman@best.org>
SPDX-FileCopyrightText: 1998-2001 Mirko Boehm <mirko@kde.org>
SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATEPICKER_P_H
#define KDATEPICKER_P_H
#include <QDate>
#include <QIntValidator>
#include <QLineEdit>
/**
* @internal
* Year selection widget.
* @author Tim Gilman, Mirko Boehm, John Layt
*/
class KDatePickerPrivateYearSelector : public QLineEdit
{
Q_OBJECT
public:
explicit KDatePickerPrivateYearSelector(const QDate &currentDate, QWidget *parent = nullptr);
int year();
void setYear(const QDate &year);
public Q_SLOTS:
void yearEnteredSlot();
Q_SIGNALS:
void closeMe(int);
protected:
QIntValidator *val;
int result;
private:
QDate oldDate;
Q_DISABLE_COPY(KDatePickerPrivateYearSelector)
};
#endif // KDATEPICKER_P_H
@@ -0,0 +1,193 @@
/*
SPDX-FileCopyrightText: 2004 Bram Schoenmakers <bramschoenmakers@kde.nl>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdatepickerpopup.h"
#include "kdatepicker.h"
#include "kdaterangecontrol_p.h"
#include <QDateTime>
#include <QLocale>
#include <QWidgetAction>
class KDatePickerAction : public QWidgetAction
{
public:
KDatePickerAction(KDatePicker *widget, QObject *parent)
: QWidgetAction(parent)
, mDatePicker(widget)
, mOriginalParent(widget->parentWidget())
{
}
protected:
QWidget *createWidget(QWidget *parent) override
{
mDatePicker->setParent(parent);
return mDatePicker;
}
void deleteWidget(QWidget *widget) override
{
if (widget != mDatePicker) {
return;
}
mDatePicker->setParent(mOriginalParent);
}
private:
KDatePicker *const mDatePicker;
QWidget *const mOriginalParent;
};
class KDatePickerPopupPrivate : public KDateRangeControlPrivate
{
public:
explicit KDatePickerPopupPrivate(KDatePickerPopup *qq)
: q(qq)
{
}
void addMenuAction(const QString &text, const QDate &date);
void buildMenu();
void menuActionTriggered(QAction *action);
void slotDateChanged(QDate);
KDatePickerPopup *const q;
KDatePicker *mDatePicker = nullptr;
KDatePickerPopup::Modes mModes;
QMap<QDate, QString> m_dateMap;
};
void KDatePickerPopupPrivate::addMenuAction(const QString &text, const QDate &date)
{
// skip out-of-range dates
// an invalid date is ok though, that's for the "No Date" action
if (date.isValid() && !isInDateRange(date)) {
return;
}
QAction *action = new QAction(q);
action->setText(text);
action->setData(date);
QObject::connect(action, &QAction::triggered, q, [this, action]() {
Q_EMIT q->dateChanged(action->data().toDate());
});
q->addAction(action);
}
void KDatePickerPopupPrivate::buildMenu()
{
q->clear();
if (mModes & KDatePickerPopup::DatePicker) {
q->addAction(new KDatePickerAction(mDatePicker, q));
if ((mModes & KDatePickerPopup::NoDate) || (mModes & KDatePickerPopup::Words)) {
q->addSeparator();
}
}
if (mModes & KDatePickerPopup::Words) {
if (m_dateMap.isEmpty()) {
const QDate today = QDate::currentDate();
addMenuAction(KDatePickerPopup::tr("Next Year", "@option next year"), today.addYears(1));
addMenuAction(KDatePickerPopup::tr("Next Month", "@option next month"), today.addMonths(1));
addMenuAction(KDatePickerPopup::tr("Next Week", "@option next week"), today.addDays(7));
addMenuAction(KDatePickerPopup::tr("Tomorrow", "@option tomorrow"), today.addDays(1));
addMenuAction(KDatePickerPopup::tr("Today", "@option today"), today);
addMenuAction(KDatePickerPopup::tr("Yesterday", "@option yesterday"), today.addDays(-1));
addMenuAction(KDatePickerPopup::tr("Last Week", "@option last week"), today.addDays(-7));
addMenuAction(KDatePickerPopup::tr("Last Month", "@option last month"), today.addMonths(-1));
addMenuAction(KDatePickerPopup::tr("Last Year", "@option last year"), today.addYears(-1));
} else {
for (auto it = m_dateMap.constBegin(); it != m_dateMap.constEnd(); ++it) {
if (it.value().isEmpty()) {
addMenuAction(QLocale().toString(it.key()), it.key());
} else if (it.value().toLower() == QLatin1String("separator")) {
q->addSeparator();
} else {
addMenuAction(it.value(), it.key());
}
}
}
if (mModes & KDatePickerPopup::NoDate) {
q->addSeparator();
}
}
if (mModes & KDatePickerPopup::NoDate) {
addMenuAction(KDatePickerPopup::tr("No Date", "@option do not specify a date"), QDate());
}
}
void KDatePickerPopupPrivate::slotDateChanged(QDate date)
{
Q_EMIT q->dateChanged(date);
q->hide();
}
KDatePickerPopup::KDatePickerPopup(Modes modes, QDate date, QWidget *parent)
: QMenu(parent)
, d(new KDatePickerPopupPrivate(this))
{
d->mModes = modes;
d->mDatePicker = new KDatePicker(this);
d->mDatePicker->setCloseButton(false);
connect(d->mDatePicker, &KDatePicker::dateEntered, this, [this](QDate date) {
d->slotDateChanged(date);
});
connect(d->mDatePicker, &KDatePicker::dateSelected, this, [this](QDate date) {
d->slotDateChanged(date);
});
d->mDatePicker->setDate(date);
connect(this, &QMenu::aboutToShow, this, [this]() {
d->buildMenu();
});
}
KDatePickerPopup::~KDatePickerPopup() = default;
KDatePicker *KDatePickerPopup::datePicker() const
{
return d->mDatePicker;
}
void KDatePickerPopup::setDate(QDate date)
{
d->mDatePicker->setDate(date);
}
KDatePickerPopup::Modes KDatePickerPopup::modes() const
{
return d->mModes;
}
void KDatePickerPopup::setModes(KDatePickerPopup::Modes modes)
{
d->mModes = modes;
}
void KDatePickerPopup::setDateRange(const QDate &minDate, const QDate &maxDate)
{
d->setDateRange(minDate, maxDate);
}
QMap<QDate, QString> KDatePickerPopup::dateMap() const
{
return d->m_dateMap;
}
void KDatePickerPopup::setDateMap(const QMap<QDate, QString> &dateMap)
{
d->m_dateMap = dateMap;
}
#include "moc_kdatepickerpopup.cpp"
@@ -0,0 +1,149 @@
/*
SPDX-FileCopyrightText: 2004 Bram Schoenmakers <bramschoenmakers@kde.nl>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATEPICKERPOPUP_H
#define KDATEPICKERPOPUP_H
#include <kwidgetsaddons_export.h>
#include <QDate>
#include <QMenu>
#include <memory>
class KDatePicker;
class KDatePickerPopupPrivate;
/**
* @short This menu helps the user to select a date quickly.
*
* This menu helps the user to select a date quickly. It offers various
* modes of selecting, e.g. with a KDatePicker or with words like "Tomorrow".
*
* The available modes are:
*
* @li NoDate: A menu-item with "No Date". If chosen, the datepicker will emit
* a null QDate.
* @li DatePicker: Shows a KDatePicker-widget.
* @li Words: Shows items like "Today", "Tomorrow" or "Next Week".
*
* @author Bram Schoenmakers <bram_s@softhome.net>
*
* @since 5.94
*/
class KWIDGETSADDONS_EXPORT KDatePickerPopup : public QMenu
{
Q_OBJECT
Q_PROPERTY(Modes modes READ modes WRITE setModes)
public:
/**
* Describes the available selection modes.
*/
enum Mode {
NoDate = 1, ///< A menu-item with "No Date". Will always return an invalid date.
DatePicker = 2, ///< A menu-item with a KDatePicker.
Words = 4 ///< A menu-item with list of words that describe a date.
};
/**
* Describes a set of combined modes.
*/
Q_DECLARE_FLAGS(Modes, Mode)
/**
* Creates a new date picker popup.
*
* @param modes The selection modes that shall be offered
* @param date The initial date of date picker widget.
* @param parent The parent object.
*/
explicit KDatePickerPopup(Modes modes = DatePicker, QDate date = QDate::currentDate(), QWidget *parent = nullptr);
/**
* Destroys the date picker popup.
*/
~KDatePickerPopup() override;
/**
* Returns the currently used selection modes.
*/
Modes modes() const;
/**
* Set the selection modes to use.
*/
void setModes(Modes modes);
/**
* Sets the range of dates that can be accepted.
*
* Invalid dates can be used to define open-ended ranges.
* If both values are valid, the minimum date must be less than
* or equal to the maximum date, otherwise the date range will
* not be set.
*
* @param minDate the minimum date
* @param maxDate the maximum date
*/
void setDateRange(const QDate &minDate, const QDate &maxDate);
/**
* Return the map of dates listed in the drop-down and their displayed
* string forms.
*
* @return the select date map
*
* @see setDateMap()
*/
QMap<QDate, QString> dateMap() const;
/**
* Sets the list of dates in the drop-down menu that the user can select from
* and the text string to display for each date, e.g. "2010-01-01" and "Yesterday".
*
* The list of date/string pairs is used as-is (invalid or duplicate dates aren't removed),
* and the order will not be changed (the map is sorted by key); also and the minimum and
* maximum dates will not be affected.
*
* The @p dateMap is keyed by the date to be listed and the value is the
* string to be displayed. If you want the date to be displayed in the
* default date format then the string should be null. If you want a
* separator to be displayed then set the string to "separator".
*
* @param dateMap the map of dates the user can select from
*
* @see dateMap()
*/
void setDateMap(const QMap<QDate, QString> &dateMap);
/**
* Returns the used KDatePicker object.
*/
Q_REQUIRED_RESULT KDatePicker *datePicker() const;
public Q_SLOTS:
/**
* Sets the current @p date.
*/
void setDate(QDate date);
Q_SIGNALS:
/**
* This signal is emitted whenever the user has selected a new date.
*
* @param date The new date.
*/
void dateChanged(const QDate &date);
private:
//@cond PRIVATE
std::unique_ptr<KDatePickerPopupPrivate> const d;
//@endcond
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KDatePickerPopup::Modes)
#endif
@@ -0,0 +1,25 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdaterangecontrol_p.h"
bool KDateRangeControlPrivate::isInDateRange(const QDate &date) const
{
return date.isValid() //
&& (!m_minDate.isValid() || date >= m_minDate) //
&& (!m_maxDate.isValid() || date <= m_maxDate);
}
bool KDateRangeControlPrivate::setDateRange(const QDate &minDate, const QDate &maxDate)
{
if (minDate.isValid() && maxDate.isValid() && minDate > maxDate) {
return false;
}
m_minDate = minDate;
m_maxDate = maxDate;
return true;
}
@@ -0,0 +1,23 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATERANGECONTROL_P_H
#define KDATERANGECONTROL_P_H
#include <QDate>
/** Common code for widgets supporting input of dates within a specified min/max range. */
class KDateRangeControlPrivate
{
public:
bool setDateRange(const QDate &minDate, const QDate &maxDate);
bool isInDateRange(const QDate &date) const;
QDate m_minDate;
QDate m_maxDate;
};
#endif
@@ -0,0 +1,703 @@
/* -*- C++ -*-
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
SPDX-FileCopyrightText: 1998-2001 Mirko Boehm <mirko@kde.org>
SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdatetable_p.h"
#include <QAction>
#include <QActionEvent>
#include <QApplication>
#include <QDate>
#include <QFontDatabase>
#include <QMenu>
#include <QPainter>
#include <QStyle>
#include <QStyleOptionViewItem>
#include <cmath>
class KDateTable::KDateTablePrivate
{
public:
KDateTablePrivate(KDateTable *qq)
: q(qq)
{
m_popupMenuEnabled = false;
m_useCustomColors = false;
m_hoveredPos = -1;
setDate(QDate::currentDate());
}
~KDateTablePrivate()
{
}
void setDate(const QDate &date);
void nextMonth();
void previousMonth();
void beginningOfMonth();
void endOfMonth();
void beginningOfWeek();
void endOfWeek();
KDateTable *q;
/**
* The currently selected date.
*/
QDate m_date;
/**
* The weekday number of the first day in the month [1..daysInWeek()].
*/
int m_weekDayFirstOfMonth;
/**
* The number of days in the current month.
*/
int m_numDaysThisMonth;
/**
* Save the size of the largest used cell content.
*/
QRectF m_maxCell;
/**
* How many week rows we are to draw.
*/
int m_numWeekRows;
/**
* How many day columns we are to draw, i.e. days in a week.
*/
int m_numDayColumns;
/**
* The font size of the displayed text.
*/
int fontsize;
bool m_popupMenuEnabled;
bool m_useCustomColors;
struct DatePaintingMode {
QColor fgColor;
QColor bgColor;
BackgroundMode bgMode;
};
QHash<int, DatePaintingMode> m_customPaintingModes;
int m_hoveredPos;
};
KDateTable::KDateTable(const QDate &date, QWidget *parent)
: QWidget(parent)
, d(new KDateTablePrivate(this))
{
initWidget(date);
}
KDateTable::KDateTable(QWidget *parent)
: QWidget(parent)
, d(std::make_unique<KDateTablePrivate>(this))
{
initWidget(QDate::currentDate());
}
KDateTable::~KDateTable()
{
}
void KDateTable::initWidget(const QDate &date)
{
d->m_numWeekRows = 7;
setFontSize(10);
setFocusPolicy(Qt::StrongFocus);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
initAccels();
setAttribute(Qt::WA_Hover, true);
setDate(date);
}
void KDateTable::initAccels()
{
QAction *next = new QAction(this);
next->setObjectName(QStringLiteral("next"));
next->setShortcuts(QKeySequence::keyBindings(QKeySequence::Forward));
next->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(next, &QAction::triggered, this, [this]() {
d->nextMonth();
});
QAction *prior = new QAction(this);
prior->setObjectName(QStringLiteral("prior"));
prior->setShortcuts(QKeySequence::keyBindings(QKeySequence::Back));
prior->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(prior, &QAction::triggered, this, [this]() {
d->previousMonth();
});
QAction *beginMonth = new QAction(this);
beginMonth->setObjectName(QStringLiteral("beginMonth"));
beginMonth->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToStartOfDocument));
beginMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(beginMonth, &QAction::triggered, this, [this]() {
d->beginningOfMonth();
});
QAction *endMonth = new QAction(this);
endMonth->setObjectName(QStringLiteral("endMonth"));
endMonth->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToEndOfDocument));
endMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(endMonth, &QAction::triggered, this, [this]() {
d->endOfMonth();
});
QAction *beginWeek = new QAction(this);
beginWeek->setObjectName(QStringLiteral("beginWeek"));
beginWeek->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToStartOfLine));
beginWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(beginWeek, &QAction::triggered, this, [this]() {
d->beginningOfWeek();
});
QAction *endWeek = new QAction(this);
endWeek->setObjectName(QStringLiteral("endWeek"));
endWeek->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToEndOfLine));
endWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(endWeek, &QAction::triggered, this, [this]() {
d->endOfWeek();
});
}
int KDateTable::posFromDate(const QDate &date)
{
int initialPosition = date.day();
int offset = (d->m_weekDayFirstOfMonth - locale().firstDayOfWeek() + d->m_numDayColumns) % d->m_numDayColumns;
// make sure at least one day of the previous month is visible.
// adjust this < 1 if more days should be forced visible:
if (offset < 1) {
offset += d->m_numDayColumns;
}
return initialPosition + offset;
}
QDate KDateTable::dateFromPos(int position)
{
int offset = (d->m_weekDayFirstOfMonth - locale().firstDayOfWeek() + d->m_numDayColumns) % d->m_numDayColumns;
// make sure at least one day of the previous month is visible.
// adjust this < 1 if more days should be forced visible:
if (offset < 1) {
offset += d->m_numDayColumns;
}
return QDate(d->m_date.year(), d->m_date.month(), 1).addDays(position - offset);
}
void KDateTable::paintEvent(QPaintEvent *e)
{
QPainter p(this);
const QRect &rectToUpdate = e->rect();
double cellWidth = width() / (double)d->m_numDayColumns;
double cellHeight = height() / (double)d->m_numWeekRows;
int leftCol = (int)std::floor(rectToUpdate.left() / cellWidth);
int topRow = (int)std::floor(rectToUpdate.top() / cellHeight);
int rightCol = (int)std::ceil(rectToUpdate.right() / cellWidth);
int bottomRow = (int)std::ceil(rectToUpdate.bottom() / cellHeight);
bottomRow = qMin(bottomRow, d->m_numWeekRows - 1);
rightCol = qMin(rightCol, d->m_numDayColumns - 1);
if (layoutDirection() == Qt::RightToLeft) {
p.translate((d->m_numDayColumns - leftCol - 1) * cellWidth, topRow * cellHeight);
} else {
p.translate(leftCol * cellWidth, topRow * cellHeight);
}
for (int i = leftCol; i <= rightCol; ++i) {
for (int j = topRow; j <= bottomRow; ++j) {
paintCell(&p, j, i);
p.translate(0, cellHeight);
}
if (layoutDirection() == Qt::RightToLeft) {
p.translate(-cellWidth, 0);
} else {
p.translate(cellWidth, 0);
}
p.translate(0, -cellHeight * (bottomRow - topRow + 1));
}
}
void KDateTable::paintCell(QPainter *painter, int row, int col)
{
double w = (width() / (double)d->m_numDayColumns) - 1;
double h = (height() / (double)d->m_numWeekRows) - 1;
QRectF cell = QRectF(0, 0, w, h);
QString cellText;
QColor cellBackgroundColor;
QColor cellTextColor;
QFont cellFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
bool workingDay = false;
int cellWeekDay;
int pos;
// Calculate the position of the cell in the grid
pos = d->m_numDayColumns * (row - 1) + col;
// Calculate what day of the week the cell is
if (col + locale().firstDayOfWeek() <= d->m_numDayColumns) {
cellWeekDay = col + locale().firstDayOfWeek();
} else {
cellWeekDay = col + locale().firstDayOfWeek() - d->m_numDayColumns;
}
// FIXME This is wrong if the widget is not using the global!
// See if cell day is normally a working day
if (locale().weekdays().first() <= locale().weekdays().last()) {
if (cellWeekDay >= locale().weekdays().first() && cellWeekDay <= locale().weekdays().last()) {
workingDay = true;
}
} else {
if (cellWeekDay >= locale().weekdays().first() //
|| cellWeekDay <= locale().weekdays().last()) {
workingDay = true;
}
}
if (row == 0) {
// We are drawing a header cell
// If not a normal working day, then use "do not work today" color
if (workingDay) {
cellTextColor = palette().color(QPalette::WindowText);
} else {
cellTextColor = Qt::darkRed;
}
cellBackgroundColor = palette().color(QPalette::Window);
// Set the text to the short day name and bold it
cellFont.setBold(true);
cellText = locale().dayName(cellWeekDay, QLocale::ShortFormat);
} else {
// We are drawing a day cell
// Calculate the date the cell represents
QDate cellDate = dateFromPos(pos);
bool validDay = cellDate.isValid();
// Draw the day number in the cell, if the date is not valid then we don't want to show it
if (validDay) {
cellText = locale().toString(cellDate.day());
} else {
cellText = QString();
}
if (!validDay || cellDate.month() != d->m_date.month()) {
// we are either
// ° painting an invalid day
// ° painting a day of the previous month or
// ° painting a day of the following month or
cellBackgroundColor = palette().color(backgroundRole());
cellTextColor = palette().color(QPalette::Disabled, QPalette::Text);
} else {
// Paint a day of the current month
// Background Colour priorities will be (high-to-low):
// * Selected Day Background Colour
// * Customized Day Background Colour
// * Normal Day Background Colour
// Background Shape priorities will be (high-to-low):
// * Customized Day Shape
// * Normal Day Shape
// Text Colour priorities will be (high-to-low):
// * Customized Day Colour
// * Day of Pray Colour (Red letter)
// * Selected Day Colour
// * Normal Day Colour
// Determine various characteristics of the cell date
bool selectedDay = (cellDate == date());
bool currentDay = (cellDate == QDate::currentDate());
bool dayOfPray = (cellDate.dayOfWeek() == Qt::Sunday);
// TODO: Uncomment if QLocale ever gets the feature...
// bool dayOfPray = ( cellDate.dayOfWeek() == locale().dayOfPray() );
bool customDay = (d->m_useCustomColors && d->m_customPaintingModes.contains(cellDate.toJulianDay()));
// Default values for a normal cell
cellBackgroundColor = palette().color(backgroundRole());
cellTextColor = palette().color(foregroundRole());
// If we are drawing the current date, then draw it bold and active
if (currentDay) {
cellFont.setBold(true);
cellTextColor = palette().color(QPalette::LinkVisited);
}
// if we are drawing the day cell currently selected in the table
if (selectedDay) {
// set the background to highlighted
cellBackgroundColor = palette().color(QPalette::Highlight);
cellTextColor = palette().color(QPalette::HighlightedText);
}
// If custom colors or shape are required for this date
if (customDay) {
KDateTablePrivate::DatePaintingMode mode = d->m_customPaintingModes[cellDate.toJulianDay()];
if (mode.bgMode != NoBgMode) {
if (!selectedDay) {
cellBackgroundColor = mode.bgColor;
}
}
cellTextColor = mode.fgColor;
}
// If the cell day is the day of religious observance, then always color text red unless Custom overrides
if (!customDay && dayOfPray) {
cellTextColor = Qt::darkRed;
}
}
}
// Draw the background
if (row == 0) {
painter->setPen(cellBackgroundColor);
painter->setBrush(cellBackgroundColor);
painter->drawRect(cell);
} else if (cellBackgroundColor != palette().color(backgroundRole()) || pos == d->m_hoveredPos) {
QStyleOptionViewItem opt;
opt.initFrom(this);
opt.rect = cell.toRect();
if (cellBackgroundColor != palette().color(backgroundRole())) {
opt.palette.setBrush(QPalette::Highlight, cellBackgroundColor);
opt.state |= QStyle::State_Selected;
}
if (pos == d->m_hoveredPos && opt.state & QStyle::State_Enabled) {
opt.state |= QStyle::State_MouseOver;
} else {
opt.state &= ~QStyle::State_MouseOver;
}
opt.showDecorationSelected = true;
opt.viewItemPosition = QStyleOptionViewItem::OnlyOne;
style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, this);
}
// Draw the text
painter->setPen(cellTextColor);
painter->setFont(cellFont);
painter->drawText(cell, Qt::AlignCenter, cellText, &cell);
// Draw the base line
if (row == 0) {
painter->setPen(palette().color(foregroundRole()));
painter->drawLine(QPointF(0, h), QPointF(w, h));
}
// If the day cell we just drew is bigger than the current max cell sizes,
// then adjust the max to the current cell
if (cell.width() > d->m_maxCell.width()) {
d->m_maxCell.setWidth(cell.width());
}
if (cell.height() > d->m_maxCell.height()) {
d->m_maxCell.setHeight(cell.height());
}
}
void KDateTable::KDateTablePrivate::nextMonth()
{
// setDate does validity checking for us
q->setDate(m_date.addMonths(1));
}
void KDateTable::KDateTablePrivate::previousMonth()
{
// setDate does validity checking for us
q->setDate(m_date.addMonths(-1));
}
void KDateTable::KDateTablePrivate::beginningOfMonth()
{
// setDate does validity checking for us
q->setDate(QDate(m_date.year(), m_date.month(), 1));
}
void KDateTable::KDateTablePrivate::endOfMonth()
{
// setDate does validity checking for us
q->setDate(QDate(m_date.year(), m_date.month() + 1, 0));
}
// JPL Do these make the assumption that first day of week is weekday 1? As it may not be.
void KDateTable::KDateTablePrivate::beginningOfWeek()
{
// setDate does validity checking for us
q->setDate(m_date.addDays(1 - m_date.dayOfWeek()));
}
// JPL Do these make the assumption that first day of week is weekday 1? As it may not be.
void KDateTable::KDateTablePrivate::endOfWeek()
{
// setDate does validity checking for us
q->setDate(m_date.addDays(7 - m_date.dayOfWeek()));
}
void KDateTable::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Up:
// setDate does validity checking for us
setDate(d->m_date.addDays(-d->m_numDayColumns));
break;
case Qt::Key_Down:
// setDate does validity checking for us
setDate(d->m_date.addDays(d->m_numDayColumns));
break;
case Qt::Key_Left:
// setDate does validity checking for us
setDate(d->m_date.addDays(-1));
break;
case Qt::Key_Right:
// setDate does validity checking for us
setDate(d->m_date.addDays(1));
break;
case Qt::Key_Minus:
// setDate does validity checking for us
setDate(d->m_date.addDays(-1));
break;
case Qt::Key_Plus:
// setDate does validity checking for us
setDate(d->m_date.addDays(1));
break;
case Qt::Key_N:
// setDate does validity checking for us
setDate(QDate::currentDate());
break;
case Qt::Key_Return:
case Qt::Key_Enter:
Q_EMIT tableClicked();
break;
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Meta:
case Qt::Key_Shift:
// Don't beep for modifiers
break;
default:
if (!e->modifiers()) { // hm
QApplication::beep();
}
}
}
void KDateTable::setFontSize(int size)
{
QFontMetricsF metrics(fontMetrics());
QRectF rect;
// ----- store rectangles:
d->fontsize = size;
// ----- find largest day name:
d->m_maxCell.setWidth(0);
d->m_maxCell.setHeight(0);
for (int weekday = 1; weekday <= 7; ++weekday) {
rect = metrics.boundingRect(locale().dayName(weekday, QLocale::ShortFormat));
d->m_maxCell.setWidth(qMax(d->m_maxCell.width(), rect.width()));
d->m_maxCell.setHeight(qMax(d->m_maxCell.height(), rect.height()));
}
// ----- compare with a real wide number and add some space:
rect = metrics.boundingRect(QStringLiteral("88"));
d->m_maxCell.setWidth(qMax(d->m_maxCell.width() + 2, rect.width()));
d->m_maxCell.setHeight(qMax(d->m_maxCell.height() + 4, rect.height()));
}
void KDateTable::wheelEvent(QWheelEvent *e)
{
setDate(d->m_date.addMonths(-(int)(e->angleDelta().y() / 120)));
e->accept();
}
bool KDateTable::event(QEvent *ev)
{
switch (ev->type()) {
case QEvent::HoverMove: {
QHoverEvent *e = static_cast<QHoverEvent *>(ev);
const int row = e->position().y() * d->m_numWeekRows / height();
int col;
if (layoutDirection() == Qt::RightToLeft) {
col = d->m_numDayColumns - (e->position().x() * d->m_numDayColumns / width()) - 1;
} else {
col = e->position().x() * d->m_numDayColumns / width();
}
const int pos = row < 1 ? -1 : (d->m_numDayColumns * (row - 1)) + col;
if (pos != d->m_hoveredPos) {
d->m_hoveredPos = pos;
update();
}
break;
}
case QEvent::HoverLeave:
if (d->m_hoveredPos != -1) {
d->m_hoveredPos = -1;
update();
}
break;
default:
break;
}
return QWidget::event(ev);
}
void KDateTable::mousePressEvent(QMouseEvent *e)
{
if (e->type() != QEvent::MouseButtonPress) { // the KDatePicker only reacts on mouse press events:
return;
}
if (!isEnabled()) {
QApplication::beep();
return;
}
int row;
int col;
int pos;
QPoint mouseCoord = e->pos();
row = mouseCoord.y() * d->m_numWeekRows / height();
if (layoutDirection() == Qt::RightToLeft) {
col = d->m_numDayColumns - (mouseCoord.x() * d->m_numDayColumns / width()) - 1;
} else {
col = mouseCoord.x() * d->m_numDayColumns / width();
}
if (row < 1 || col < 0) { // the user clicked on the frame of the table
return;
}
// Rows and columns are zero indexed. The (row - 1) below is to avoid counting
// the row with the days of the week in the calculation.
// new position and date
pos = (d->m_numDayColumns * (row - 1)) + col;
QDate clickedDate = dateFromPos(pos);
// set the new date. If it is in the previous or next month, the month will
// automatically be changed, no need to do that manually...
// validity checking done inside setDate
setDate(clickedDate);
// This could be optimized to only call update over the regions
// of old and new cell, but 99% of times there is also a call to
// setDate that already calls update() so no need to optimize that
// much here
update();
Q_EMIT tableClicked();
if (e->button() == Qt::RightButton && d->m_popupMenuEnabled) {
QMenu *menu = new QMenu();
menu->addSection(locale().toString(d->m_date));
Q_EMIT aboutToShowContextMenu(menu, clickedDate);
menu->popup(e->globalPosition().toPoint());
}
}
void KDateTable::KDateTablePrivate::setDate(const QDate &date)
{
m_date = date;
m_weekDayFirstOfMonth = QDate(date.year(), date.month(), 1).dayOfWeek();
m_numDaysThisMonth = m_date.daysInMonth();
m_numDayColumns = 7;
}
bool KDateTable::setDate(const QDate &toDate)
{
if (!toDate.isValid()) {
return false;
}
if (toDate == date()) {
return true;
}
d->setDate(toDate);
Q_EMIT dateChanged(date());
update();
return true;
}
const QDate &KDateTable::date() const
{
return d->m_date;
}
void KDateTable::focusInEvent(QFocusEvent *e)
{
QWidget::focusInEvent(e);
}
void KDateTable::focusOutEvent(QFocusEvent *e)
{
QWidget::focusOutEvent(e);
}
QSize KDateTable::sizeHint() const
{
if (d->m_maxCell.height() > 0 && d->m_maxCell.width() > 0) {
return QSize(qRound(d->m_maxCell.width() * d->m_numDayColumns), (qRound(d->m_maxCell.height() + 2) * d->m_numWeekRows));
} else {
// qCDebug(KWidgetsAddonsLog) << "KDateTable::sizeHint: obscure failure - " << endl;
return QSize(-1, -1);
}
}
void KDateTable::setPopupMenuEnabled(bool enable)
{
d->m_popupMenuEnabled = enable;
}
bool KDateTable::popupMenuEnabled() const
{
return d->m_popupMenuEnabled;
}
void KDateTable::setCustomDatePainting(const QDate &date, const QColor &fgColor, BackgroundMode bgMode, const QColor &bgColor)
{
if (!fgColor.isValid()) {
unsetCustomDatePainting(date);
return;
}
KDateTablePrivate::DatePaintingMode mode;
mode.bgMode = bgMode;
mode.fgColor = fgColor;
mode.bgColor = bgColor;
d->m_customPaintingModes.insert(date.toJulianDay(), mode);
d->m_useCustomColors = true;
update();
}
void KDateTable::unsetCustomDatePainting(const QDate &date)
{
d->m_customPaintingModes.remove(date.toJulianDay());
if (d->m_customPaintingModes.isEmpty()) {
d->m_useCustomColors = false;
}
update();
}
#include "moc_kdatetable_p.cpp"
@@ -0,0 +1,163 @@
/* -*- C++ -*-
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
SPDX-FileCopyrightText: 1998-2001 Mirko Boehm <mirko@kde.org>
SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATETABLE_H
#define KDATETABLE_H
#include <QWidget>
#include <memory>
class QMenu;
/**
* @internal
* Date selection table.
* This is a support class for the KDatePicker class. It just
* draws the calendar table without titles, but could theoretically
* be used as a standalone.
*
* When a date is selected by the user, it emits a signal:
* dateSelected(QDate)
*
* \image html kdatetable.png "KDE Date Selection Table"
*
* @author Tim Gilman, Mirko Boehm
*/
class KDateTable : public QWidget
{
Q_OBJECT
Q_PROPERTY(QDate date READ date WRITE setDate)
Q_PROPERTY(bool popupMenu READ popupMenuEnabled WRITE setPopupMenuEnabled)
public:
/**
* The constructor.
*/
explicit KDateTable(QWidget *parent = nullptr);
/**
* The constructor.
*/
explicit KDateTable(const QDate &, QWidget *parent = nullptr);
/**
* The destructor.
*/
~KDateTable() override;
/**
* Returns a recommended size for the widget.
* To save some time, the size of the largest used cell content is
* calculated in each paintCell() call, since all calculations have
* to be done there anyway. The size is stored in maxCell. The
* sizeHint() simply returns a multiple of maxCell.
*/
QSize sizeHint() const override;
/**
* Set the font size of the date table.
*/
void setFontSize(int size);
/**
* Select and display this date.
*/
bool setDate(const QDate &date);
/**
* @returns the selected date.
*/
const QDate &date() const;
/**
* Enables a popup menu when right clicking on a date.
*
* When it's enabled, this object emits a aboutToShowContextMenu signal
* where you can fill in the menu items.
*/
void setPopupMenuEnabled(bool enable);
/**
* Returns if the popup menu is enabled or not
*/
bool popupMenuEnabled() const;
enum BackgroundMode { NoBgMode = 0, RectangleMode, CircleMode };
/**
* Makes a given date be painted with a given foregroundColor, and background
* (a rectangle, or a circle/ellipse) in a given color.
*/
void setCustomDatePainting(const QDate &date, const QColor &fgColor, BackgroundMode bgMode = NoBgMode, const QColor &bgColor = QColor());
/**
* Unsets the custom painting of a date so that the date is painted as usual.
*/
void unsetCustomDatePainting(const QDate &date);
protected:
/**
* calculate the position of the cell in the matrix for the given date.
* The result is the 0-based index.
*/
virtual int posFromDate(const QDate &date);
/**
* calculate the date that is displayed at a given cell in the matrix. pos is the
* 0-based index in the matrix. Inverse function to posForDate().
*/
virtual QDate dateFromPos(int pos);
void paintEvent(QPaintEvent *e) override;
/**
* React on mouse clicks that select a date.
*/
void mousePressEvent(QMouseEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
/**
* Cell highlight on mouse hovering
*/
bool event(QEvent *e) override;
Q_SIGNALS:
/**
* The selected date changed.
*/
void dateChanged(const QDate &date);
/**
* A date has been selected by clicking on the table.
*/
void tableClicked();
/**
* A popup menu for a given date is about to be shown (as when the user
* right clicks on that date and the popup menu is enabled). Connect
* the slot where you fill the menu to this signal.
*/
void aboutToShowContextMenu(QMenu *menu, const QDate &date);
private:
class KDateTablePrivate;
friend class KDateTablePrivate;
std::unique_ptr<KDateTablePrivate> const d;
void initWidget(const QDate &date);
void initAccels();
void paintCell(QPainter *painter, int row, int col);
Q_DISABLE_COPY(KDateTable)
};
#endif // KDATETABLE_H
@@ -0,0 +1,529 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdatetimeedit.h"
#include "kdatecombobox.h"
#include "kdatepicker.h"
#include "kmessagebox.h"
#include "ui_kdatetimeedit.h"
class KDateTimeEditPrivate
{
public:
KDateTimeEditPrivate(KDateTimeEdit *qq);
virtual ~KDateTimeEditPrivate();
QDateTime defaultMinDateTime();
QDateTime defaultMaxDateTime();
void initWidgets();
void initDateWidget();
void initTimeWidget();
void initCalendarWidget();
void updateCalendarWidget();
void initTimeZoneWidget();
void updateTimeZoneWidget();
void warnDateTime();
// private Q_SLOTS:
void selectCalendar(int index);
void enterCalendar(const QLocale &calendarLocale);
void selectTimeZone(int index);
void enterTimeZone(const QByteArray &zoneId);
KDateTimeEdit *const q;
KDateTimeEdit::Options m_options;
QDateTime m_dateTime;
QDateTime m_minDateTime;
QDateTime m_maxDateTime;
QString m_minWarnMsg;
QString m_maxWarnMsg;
QList<QLocale> m_calendarLocales;
QList<QTimeZone> m_zones;
Ui::KDateTimeEdit ui;
};
KDateTimeEditPrivate::KDateTimeEditPrivate(KDateTimeEdit *qq)
: q(qq)
{
m_options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime
| KDateTimeEdit::SelectTime | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords;
m_dateTime = QDateTime::currentDateTime();
m_dateTime.setTime(QTime(0, 0, 0));
m_calendarLocales << q->locale();
const auto zoneIds = QTimeZone::availableTimeZoneIds();
m_zones.reserve(zoneIds.size());
for (const QByteArray &zoneId : zoneIds) {
m_zones << QTimeZone(zoneId);
}
}
KDateTimeEditPrivate::~KDateTimeEditPrivate()
{
}
QDateTime KDateTimeEditPrivate::defaultMinDateTime()
{
// TODO: Find a way to get it from QLocale
// return KDateTime(calendar()->earliestValidDate(), QTime(0, 0, 0, 0));
return QDateTime();
}
QDateTime KDateTimeEditPrivate::defaultMaxDateTime()
{
// TODO: Find a way to get it from QLocale
// return KDateTime(calendar()->latestValidDate(), QTime(23, 59, 59, 999));
return QDateTime();
}
void KDateTimeEditPrivate::initWidgets()
{
initDateWidget();
initTimeWidget();
initCalendarWidget();
initTimeZoneWidget();
}
void KDateTimeEditPrivate::initDateWidget()
{
ui.m_dateCombo->blockSignals(true);
ui.m_dateCombo->setVisible((m_options & KDateTimeEdit::ShowDate) == KDateTimeEdit::ShowDate);
KDateComboBox::Options options;
if ((m_options & KDateTimeEdit::EditDate) == KDateTimeEdit::EditDate) {
options = options | KDateComboBox::EditDate;
}
if ((m_options & KDateTimeEdit::SelectDate) == KDateTimeEdit::SelectDate) {
options = options | KDateComboBox::SelectDate;
}
if ((m_options & KDateTimeEdit::DatePicker) == KDateTimeEdit::DatePicker) {
options = options | KDateComboBox::DatePicker;
}
if ((m_options & KDateTimeEdit::DateKeywords) == KDateTimeEdit::DateKeywords) {
options = options | KDateComboBox::DateKeywords;
}
ui.m_dateCombo->setOptions(options);
ui.m_dateCombo->blockSignals(false);
}
void KDateTimeEditPrivate::initTimeWidget()
{
ui.m_timeCombo->blockSignals(true);
ui.m_timeCombo->setVisible((m_options & KDateTimeEdit::ShowTime) == KDateTimeEdit::ShowTime);
KTimeComboBox::Options options;
if ((m_options & KDateTimeEdit::EditTime) == KDateTimeEdit::EditTime) {
options = options | KTimeComboBox::EditTime;
}
if ((m_options & KDateTimeEdit::SelectTime) == KDateTimeEdit::SelectTime) {
options = options | KTimeComboBox::SelectTime;
}
if ((m_options & KDateTimeEdit::ForceTime) == KDateTimeEdit::ForceTime) {
options = options | KTimeComboBox::ForceTime;
}
ui.m_timeCombo->setOptions(options);
ui.m_timeCombo->blockSignals(false);
}
void KDateTimeEditPrivate::initCalendarWidget()
{
ui.m_calendarCombo->blockSignals(true);
ui.m_calendarCombo->clear();
for (const QLocale &calendarLocale : std::as_const(m_calendarLocales)) {
ui.m_calendarCombo->addItem(calendarLocale.name(), calendarLocale);
}
ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale()));
ui.m_calendarCombo->setVisible((m_options & KDateTimeEdit::ShowCalendar) == KDateTimeEdit::ShowCalendar);
ui.m_calendarCombo->setEnabled((m_options & KDateTimeEdit::SelectCalendar) == KDateTimeEdit::SelectCalendar);
ui.m_calendarCombo->setEditable(false);
ui.m_calendarCombo->blockSignals(false);
}
void KDateTimeEditPrivate::updateCalendarWidget()
{
ui.m_calendarCombo->blockSignals(true);
ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale()));
ui.m_calendarCombo->blockSignals(false);
}
void KDateTimeEditPrivate::selectCalendar(int index)
{
enterCalendar(ui.m_calendarCombo->itemData(index).toLocale());
}
void KDateTimeEditPrivate::enterCalendar(const QLocale &calendarLocale)
{
q->setLocale(calendarLocale);
Q_EMIT q->calendarEntered(q->locale());
}
void KDateTimeEditPrivate::initTimeZoneWidget()
{
ui.m_timeZoneCombo->blockSignals(true);
ui.m_timeZoneCombo->clear();
ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("UTC", "@item:inlistbox UTC time zone"), QByteArray("UTC"));
ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("Floating", "@item:inlistbox No specific time zone"), QByteArray());
for (const QTimeZone &zone : std::as_const(m_zones)) {
ui.m_timeZoneCombo->addItem(QString::fromUtf8(zone.id()), zone.id());
}
ui.m_timeZoneCombo->setVisible((m_options & KDateTimeEdit::ShowTimeZone) == KDateTimeEdit::ShowTimeZone);
ui.m_timeZoneCombo->setEnabled((m_options & KDateTimeEdit::SelectTimeZone) == KDateTimeEdit::SelectTimeZone);
ui.m_timeZoneCombo->setEditable(false);
ui.m_timeZoneCombo->blockSignals(false);
}
void KDateTimeEditPrivate::updateTimeZoneWidget()
{
ui.m_timeZoneCombo->blockSignals(true);
ui.m_timeZoneCombo->blockSignals(false);
}
void KDateTimeEditPrivate::selectTimeZone(int index)
{
enterTimeZone(ui.m_timeCombo->itemData(index).toByteArray());
}
void KDateTimeEditPrivate::enterTimeZone(const QByteArray &zoneId)
{
q->setTimeZone(QTimeZone(zoneId));
Q_EMIT q->dateTimeEntered(m_dateTime);
Q_EMIT q->timeZoneEntered(m_dateTime.timeZone());
}
void KDateTimeEditPrivate::warnDateTime()
{
if (!q->isValid() && (m_options & KDateTimeEdit::WarnOnInvalid) == KDateTimeEdit::WarnOnInvalid) {
QString warnMsg;
if (!m_dateTime.isValid()) {
// TODO Add missing string
// warnMsg = tr("The date or time you entered is invalid");
} else if (m_minDateTime.isValid() && m_dateTime < m_minDateTime) {
if (m_minWarnMsg.isEmpty()) {
// TODO Add datetime to string
// warnMsg = q->tr("Date and time cannot be earlier than %1", "@info").arg(formatDate(m_minDate));
warnMsg = KDateTimeEdit::tr("The entered date and time is before the minimum allowed date and time.", "@info");
} else {
warnMsg = m_minWarnMsg;
// TODO localize properly
warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_minDateTime));
}
} else if (m_maxDateTime.isValid() && m_dateTime > m_maxDateTime) {
if (m_maxWarnMsg.isEmpty()) {
// TODO Add datetime to string
// warnMsg = q->tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate));
warnMsg = KDateTimeEdit::tr("The entered date and time is after the maximum allowed date and time.", "@info");
} else {
warnMsg = m_maxWarnMsg;
warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_maxDateTime));
}
}
KMessageBox::error(q, warnMsg);
}
}
KDateTimeEdit::KDateTimeEdit(QWidget *parent)
: QWidget(parent)
, d(new KDateTimeEditPrivate(this))
{
d->ui.setupUi(this);
// Need to do the min/max defaults here and not in private init as need to wait for ui to init
// the KDateComboBox which holds the calendar object. Revisit this???
d->m_minDateTime = d->defaultMinDateTime();
d->m_maxDateTime = d->defaultMaxDateTime();
d->ui.m_calendarCombo->installEventFilter(this);
d->ui.m_dateCombo->installEventFilter(this);
d->ui.m_timeCombo->installEventFilter(this);
d->ui.m_timeZoneCombo->installEventFilter(this);
d->initWidgets();
connect(d->ui.m_dateCombo, &KDateComboBox::dateChanged, this, &KDateTimeEdit::setDate);
connect(d->ui.m_timeCombo, &KTimeComboBox::timeChanged, this, &KDateTimeEdit::setTime);
connect(d->ui.m_calendarCombo, &QComboBox::activated, this, [this](int index) {
d->selectCalendar(index);
});
connect(d->ui.m_timeZoneCombo, &QComboBox::activated, this, [this](int index) {
d->selectTimeZone(index);
});
}
KDateTimeEdit::~KDateTimeEdit() = default;
QDateTime KDateTimeEdit::dateTime() const
{
return d->m_dateTime;
}
QDate KDateTimeEdit::date() const
{
return d->m_dateTime.date();
}
QTime KDateTimeEdit::time() const
{
return d->m_dateTime.time();
}
QTimeZone KDateTimeEdit::timeZone() const
{
return d->m_dateTime.timeZone();
}
bool KDateTimeEdit::isValid() const
{
return d->m_dateTime.isValid() //
&& (!d->m_minDateTime.isValid() || d->m_dateTime >= d->m_minDateTime) //
&& (!d->m_maxDateTime.isValid() || d->m_dateTime <= d->m_maxDateTime);
}
bool KDateTimeEdit::isNull() const
{
return isNullDate() && isNullTime();
}
bool KDateTimeEdit::isValidDate() const
{
return d->ui.m_dateCombo->isValid();
}
bool KDateTimeEdit::isNullDate() const
{
return d->ui.m_dateCombo->isNull();
}
bool KDateTimeEdit::isValidTime() const
{
return d->ui.m_timeCombo->isValid();
}
bool KDateTimeEdit::isNullTime() const
{
return d->ui.m_timeCombo->isNull();
}
void KDateTimeEdit::setOptions(Options options)
{
if (options != d->m_options) {
d->m_options = options;
d->initWidgets();
}
}
KDateTimeEdit::Options KDateTimeEdit::options() const
{
return d->m_options;
}
void KDateTimeEdit::setDateTime(const QDateTime &dateTime)
{
if (dateTime != d->m_dateTime) {
assignDateTime(dateTime);
Q_EMIT dateTimeChanged(d->m_dateTime);
Q_EMIT dateChanged(d->m_dateTime.date());
Q_EMIT timeChanged(d->m_dateTime.time());
}
}
void KDateTimeEdit::assignDateTime(const QDateTime &dateTime)
{
d->m_dateTime = dateTime;
d->ui.m_dateCombo->setDate(dateTime.date());
d->ui.m_timeCombo->setTime(dateTime.time());
}
void KDateTimeEdit::setDate(const QDate &date)
{
if (date != d->m_dateTime.date()) {
assignDate(date);
Q_EMIT dateTimeChanged(d->m_dateTime);
Q_EMIT dateChanged(d->m_dateTime.date());
}
}
void KDateTimeEdit::assignDate(const QDate &date)
{
d->m_dateTime.setDate(date);
d->ui.m_dateCombo->setDate(date);
}
void KDateTimeEdit::setTime(const QTime &time)
{
if (time != d->m_dateTime.time()) {
assignTime(time);
Q_EMIT dateTimeChanged(d->m_dateTime);
Q_EMIT timeChanged(d->m_dateTime.time());
}
}
void KDateTimeEdit::assignTime(const QTime &time)
{
d->m_dateTime.setTime(time);
d->ui.m_timeCombo->setTime(time);
}
void KDateTimeEdit::setTimeZone(const QTimeZone &zone)
{
if (zone == d->m_dateTime.timeZone() || !zone.isValid()) {
return;
}
assignTimeZone(zone);
Q_EMIT dateTimeChanged(d->m_dateTime);
Q_EMIT timeZoneChanged(d->m_dateTime.timeZone());
}
void KDateTimeEdit::assignTimeZone(const QTimeZone &zone)
{
d->m_dateTime.setTimeZone(zone);
d->updateTimeZoneWidget();
}
void KDateTimeEdit::setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg)
{
setDateTimeRange(minDateTime, maximumDateTime(), minWarnMsg, d->m_maxWarnMsg);
}
QDateTime KDateTimeEdit::minimumDateTime() const
{
return d->m_minDateTime;
}
void KDateTimeEdit::resetMinimumDateTime()
{
d->m_minDateTime = d->defaultMinDateTime();
}
void KDateTimeEdit::setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg)
{
setDateTimeRange(minimumDateTime(), maxDateTime, d->m_minWarnMsg, maxWarnMsg);
}
QDateTime KDateTimeEdit::maximumDateTime() const
{
return d->m_maxDateTime;
}
void KDateTimeEdit::resetMaximumDateTime()
{
d->m_maxDateTime = d->defaultMaxDateTime();
}
void KDateTimeEdit::setDateTimeRange(const QDateTime &minDateTime, const QDateTime &maxDateTime, const QString &minErrorMsg, const QString &maxErrorMsg)
{
if (minDateTime.isValid() && maxDateTime.isValid() && minDateTime <= maxDateTime) {
d->m_minDateTime = minDateTime;
d->m_minWarnMsg = minErrorMsg;
d->m_maxDateTime = maxDateTime;
d->m_maxWarnMsg = maxErrorMsg;
}
}
void KDateTimeEdit::resetDateTimeRange()
{
setDateTimeRange(d->defaultMinDateTime(), d->defaultMaxDateTime());
}
void KDateTimeEdit::setCalendarLocalesList(const QList<QLocale> &calendarLocales)
{
if (calendarLocales != d->m_calendarLocales) {
d->m_calendarLocales = calendarLocales;
d->updateCalendarWidget();
}
}
QList<QLocale> KDateTimeEdit::calendarLocalesList() const
{
return d->m_calendarLocales;
}
void KDateTimeEdit::setDateDisplayFormat(QLocale::FormatType format)
{
d->ui.m_dateCombo->setDisplayFormat(format);
}
QLocale::FormatType KDateTimeEdit::dateDisplayFormat() const
{
return d->ui.m_dateCombo->displayFormat();
}
void KDateTimeEdit::setDateMap(QMap<QDate, QString> dateMap)
{
d->ui.m_dateCombo->setDateMap(dateMap);
}
QMap<QDate, QString> KDateTimeEdit::dateMap() const
{
return d->ui.m_dateCombo->dateMap();
}
void KDateTimeEdit::setTimeDisplayFormat(QLocale::FormatType formatOptions)
{
d->ui.m_timeCombo->setDisplayFormat(formatOptions);
}
QLocale::FormatType KDateTimeEdit::timeDisplayFormat() const
{
return d->ui.m_timeCombo->displayFormat();
}
void KDateTimeEdit::setTimeListInterval(int minutes)
{
d->ui.m_timeCombo->setTimeListInterval(minutes);
}
int KDateTimeEdit::timeListInterval() const
{
return d->ui.m_timeCombo->timeListInterval();
}
void KDateTimeEdit::setTimeList(QList<QTime> timeList, const QString &minWarnMsg, const QString &maxWarnMsg)
{
d->ui.m_timeCombo->setTimeList(timeList, minWarnMsg, maxWarnMsg);
}
QList<QTime> KDateTimeEdit::timeList() const
{
return d->ui.m_timeCombo->timeList();
}
void KDateTimeEdit::setTimeZones(const QList<QTimeZone> &zones)
{
if (zones != d->m_zones) {
d->m_zones = zones;
d->updateTimeZoneWidget();
}
}
QList<QTimeZone> KDateTimeEdit::timeZones() const
{
return d->m_zones;
}
bool KDateTimeEdit::eventFilter(QObject *object, QEvent *event)
{
return QWidget::eventFilter(object, event);
}
void KDateTimeEdit::focusInEvent(QFocusEvent *event)
{
QWidget::focusInEvent(event);
}
void KDateTimeEdit::focusOutEvent(QFocusEvent *event)
{
d->warnDateTime();
QWidget::focusOutEvent(event);
}
void KDateTimeEdit::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
}
#include "moc_kdatetimeedit.cpp"
@@ -0,0 +1,596 @@
/*
SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDATETIMEEDIT_H
#define KDATETIMEEDIT_H
#include <kwidgetsaddons_export.h>
#include <QLocale>
#include <QTimeZone>
#include <QWidget>
#include <memory>
/**
* @class KDateTimeEdit kdatetimeedit.h KDateTimeEdit
*
* @short A widget for editing date and time.
*/
class KWIDGETSADDONS_EXPORT KDateTimeEdit : public QWidget
{
Q_OBJECT
Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true)
Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged USER true)
Q_PROPERTY(int timeListInterval READ timeListInterval WRITE setTimeListInterval)
Q_PROPERTY(Options options READ options WRITE setOptions)
public:
/**
* Options provided by the widget
* @see options
* @see setOptions
* @see Options
*/
enum Option {
ShowCalendar = 0x00001, /**< If the Calendar System edit is displayed */
ShowDate = 0x00002, /**< If the Date is displayed */
ShowTime = 0x00004, /**< If the Time is displayed */
ShowTimeZone = 0x00008, /**< If the Time Zone is displayed */
// EditCalendar = 0x00010, /**< Allow the user to manually edit the calendar */
EditDate = 0x00020, /**< Allow the user to manually edit the date */
EditTime = 0x00040, /**< Allow the user to manually edit the time */
// EditTimeZone = 0x00080, /**< Allow the user to manually edit the time zone */
SelectCalendar = 0x00100, /**< Allow the user to select a calendar */
SelectDate = 0x00200, /**< Allow the user to select a date */
SelectTime = 0x00400, /**< Allow the user to select a time */
SelectTimeZone = 0x00800, /**< Allow the user to select a time zone */
DatePicker = 0x01000, /**< Show a date picker */
DateKeywords = 0x02000, /**< Show date keywords */
ForceTime = 0x04000, /**< The entered time can only be a selected time */
WarnOnInvalid = 0x08000, /**< Show a warning on focus out if the date or time is invalid */
};
/**
* Stores a combination of #Option values.
*/
Q_DECLARE_FLAGS(Options, Option)
Q_FLAG(Options)
/**
* Create a new KDateTimeEdit widget
*/
explicit KDateTimeEdit(QWidget *parent = nullptr);
/**
* Destroy the widget
*/
~KDateTimeEdit() override;
/**
* Return the currently set widget options
*
* @return the currently set widget options
*/
Options options() const;
/**
* Return the currently selected date, time and time zone
*
* @return the currently selected date, time and time zone
*/
QDateTime dateTime() const;
/**
* Return the currently selected date
*
* @return the currently selected date
*/
QDate date() const;
/**
* Return the currently selected time
*
* @return the currently selected time
*/
QTime time() const;
/**
* Return the currently selected time zone
*
* @return the currently selected time zone
*/
QTimeZone timeZone() const;
/**
* Returns the list of Calendar Locales displayed.
*
* @return the list of calendar locales displayed
*/
QList<QLocale> calendarLocalesList() const;
/**
* Return the current minimum date and time
*
* @return the current minimum date and time
*/
QDateTime minimumDateTime() const;
/**
* Return the current maximum date and time
*
* @return the current maximum date and time
*/
QDateTime maximumDateTime() const;
/**
* Return the currently set date display format
*
* By default this is the Short Format
*
* @return the currently set date format
*/
QLocale::FormatType dateDisplayFormat() const;
/**
* Return the map of dates listed in the drop-down and their displayed
* string forms.
*
* @return the select date map
*
* @see setDateMap()
*/
QMap<QDate, QString> dateMap() const;
/**
* Return the currently set time format
*
* By default this is the Short Format
*
* @return the currently set time format
*/
QLocale::FormatType timeDisplayFormat() const;
/**
* Return the time list interval able to be selected
*
* @return the select time intervals in minutes
*/
int timeListInterval() const;
/**
* Return the list of times able to be selected in the drop-down.
*
* @return the select time list
*
* @see setTimeList()
* @see timeListInterval()
* @see setTimeListInterval()
*/
QList<QTime> timeList() const;
/**
* Return the list of time zones able to be selected
*
* @return the list of time zones displayed
*/
QList<QTimeZone> timeZones() const;
/**
* Return if the current user input is valid
*
* If the user input is null then it is not valid
*
* @return if the current user input is valid
*
* @see isNull()
*/
bool isValid() const;
/**
* Return if the current user input is null
*
* @return if the current user input is null
*
* @see isValid()
*/
bool isNull() const;
/**
* Return if the current user input date is valid
*
* If the user input date is null then it is not valid
*
* @return if the current user input date is valid
*
* @see isNullDate()
*/
bool isValidDate() const;
/**
* Return if the current user input date is null
*
* @return if the current user input date is null
*
* @see isValidDate()
*/
bool isNullDate() const;
/**
* Return if the current user input time is valid
*
* If the user input time is null then it is not valid
*
* @return if the current user input time is valid
*
* @see isNullTime()
*/
bool isValidTime() const;
/**
* Return if the current user input time is null
*
* @return if the current user input time is null
*
* @see isValidTime()
*/
bool isNullTime() const;
Q_SIGNALS:
/**
* Signal if the date or time has been manually entered by the user.
*
* The returned date and time may be invalid.
*
* @param dateTime the new date, time and time zone
*/
void dateTimeEntered(const QDateTime &dateTime);
/**
* Signal if the date or time has been changed either manually by the user
* or programmatically.
*
* The returned date and time may be invalid.
*
* @param dateTime the new date, time and time zone
*/
void dateTimeChanged(const QDateTime &dateTime);
/**
* Signal if the date or time is being manually edited by the user.
*
* The returned date and time may be invalid.
*
* @param dateTime the new date, time and time zone
*/
void dateTimeEdited(const QDateTime &dateTime);
/**
* Signal if the Calendar Locale has been manually entered by the user.
*
* @param calendarLocale the new calendar locale
*/
void calendarEntered(const QLocale &calendarLocale);
/**
* Signal if the Calendar Locale has been changed either manually by the user
* or programmatically.
*
* @param calendarLocale the new calendar locale
*/
void calendarChanged(const QLocale &calendarLocale);
/**
* Signal if the date has been manually entered by the user.
*
* The returned date may be invalid.
*
* @param date the new date
*/
void dateEntered(const QDate &date);
/**
* Signal if the date has been changed either manually by the user
* or programmatically.
*
* The returned date may be invalid.
*
* @param date the new date
*/
void dateChanged(const QDate &date);
/**
* Signal if the date is being manually edited by the user.
*
* The returned date may be invalid.
*
* @param date the new date
*/
void dateEdited(const QDate &date);
/**
* Signal if the time has been manually entered by the user.
*
* The returned time may be invalid.
*
* @param time the new time
*/
void timeEntered(const QTime &time);
/**
* Signal if the time has been changed either manually by the user
* or programmatically.
*
* The returned time may be invalid.
*
* @param time the new time
*/
void timeChanged(const QTime &time);
/**
* Signal if the time is being manually edited by the user.
*
* The returned time may be invalid.
*
* @param time the new time
*/
void timeEdited(const QTime &time);
/**
* Signal if the time zone has been changed manually by the user.
*
* @param zone the new time zone
*/
void timeZoneEntered(const QTimeZone &zone);
/**
* Signal if the time zone has been changed either manually by the user
* or programmatically.
*
* @param zone the new time zone
*/
void timeZoneChanged(const QTimeZone &zone);
public Q_SLOTS:
/**
* Set the new widget options
*
* @param options the new widget options
*/
void setOptions(Options options);
/**
* Set the currently selected date, time and time zone
*
* @param dateTime the new date, time and time zone
*/
void setDateTime(const QDateTime &dateTime);
/**
* Set the currently selected date
*
* @param date the new date
*/
void setDate(const QDate &date);
/**
* Set the currently selected time
*
* @param time the new time
*/
void setTime(const QTime &time);
/**
* Set the current time zone
*
* @param zone the new zone
*/
void setTimeZone(const QTimeZone &zone);
/**
* Set the minimum and maximum date and time range
*
* To enable range checking provide two valid dates.
* To disable range checking provide two invalid dates, or call
* clearDateRange;
*
* @param minDateTime the minimum date and time
* @param maxDateTime the maximum date and time
* @param minWarnMsg the minimum warning message
* @param maxWarnMsg the maximum warning message
*/
void
setDateTimeRange(const QDateTime &minDateTime, const QDateTime &maxDateTime, const QString &minWarnMsg = QString(), const QString &maxWarnMsg = QString());
/**
* Reset the minimum and maximum date and time to the default
*/
void resetDateTimeRange();
/**
* Set the minimum allowed date.
*
* If the date is invalid, or more than current maximum,
* then the minimum will not be set.
*
* @param minDateTime the minimum date
* @param minWarnMsg the minimum warning message
*
* @see setMaximumDateTime()
* @see setDateRange()
*/
void setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg = QString());
/**
* Reset the minimum date and time to the default
*/
void resetMinimumDateTime();
/**
* Set the maximum allowed date.
*
* If the date is invalid, or less than current minimum,
* then the maximum will not be set.
*
* @param maxDateTime the maximum date
* @param maxWarnMsg the maximum warning message
*
* @see setMinimumDateTime()
* @see setDateRange()
*/
void setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg = QString());
/**
* Reset the minimum date and time to the default
*/
void resetMaximumDateTime();
/**
* Sets the date format to display.
*
* By default is the Short Format.
*
* @param format the date format to use
*/
void setDateDisplayFormat(QLocale::FormatType format);
/**
* Set the list of Calendar Locales to display.
*
* @param calendarLocales the list of calendar locales to display
*/
void setCalendarLocalesList(const QList<QLocale> &calendarLocales);
/**
* Set the list of dates able to be selected from the drop-down and the
* string form to display for those dates, e.g. "2010-01-01" and "Yesterday".
*
* Any invalid or duplicate dates will be used, the list will NOT be
* sorted, and the minimum and maximum date will not be affected.
*
* The @p dateMap is keyed by the date to be listed and the value is the
* string to be displayed. If you want the date to be displayed in the
* default date format then the string should be null. If you want a
* separator to be displayed then set the string to "separator".
*
* @param dateMap the map of dates able to be selected
*
* @see dateMap()
*/
void setDateMap(QMap<QDate, QString> dateMap);
/**
* Sets the time format to display.
*
* By default is the Short Format.
*
* @param format the time format to use
*/
void setTimeDisplayFormat(QLocale::FormatType format);
/**
* Set the interval between times able to be selected from the drop-down.
*
* The combo drop-down will be populated with times every @p minutes
* apart, starting from the minimumTime() and ending at maximumTime().
*
* If the ForceInterval option is set then any time manually typed into the
* combo line edit will be forced to the nearest interval.
*
* This interval must be an exact divisor of the valid time range hours.
* For example with the default 24 hour range @p interval must divide 1440
* minutes exactly, meaning 1, 6 and 90 are valid but 7, 31 and 91 are not.
*
* Setting the time list interval will override any time list previously set
* via setTimeList().
*
* @param minutes the time list interval to display
*
* @see timeListInterval()
*/
void setTimeListInterval(int minutes);
/**
* Set the list of times able to be selected from the drop-down.
*
* Setting the time list will override any time interval previously set via
* setTimeListInterval().
*
* Any invalid or duplicate times will be ignored, and the list will be
* sorted.
*
* The minimum and maximum time will automatically be set to the earliest
* and latest value in the list.
*
* @param timeList the list of times able to be selected
* @param minWarnMsg the minimum warning message
* @param maxWarnMsg the maximum warning message
*
* @see timeList()
*/
void setTimeList(QList<QTime> timeList, const QString &minWarnMsg = QString(), const QString &maxWarnMsg = QString());
/**
* Set the time zones able to be selected
*
* @param zones the time zones to display
*/
void setTimeZones(const QList<QTimeZone> &zones);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
/**
* Assign the date, time and time zone for the widget.
*
* Virtual to allow sub-classes to apply extra validation rules,
* but reimplementations must call the parent method at the end.
*
* @param dateTime the new date and time
*/
virtual void assignDateTime(const QDateTime &dateTime);
/**
* Assign the date for the widget.
*
* Virtual to allow sub-classes to apply extra validation rules,
* but reimplementations must call the parent method at the end.
*
* @param date the new date
*/
virtual void assignDate(const QDate &date);
/**
* Assign the time for the widget.
*
* Virtual to allow sub-classes to apply extra validation rules,
* but reimplementations must call the parent method at the end.
*
* @param time the new time
*/
virtual void assignTime(const QTime &time);
/**
* Assign the time zone for the widget.
*
* Virtual to allow sub-classes to apply extra validation rules,
* but reimplementations must call the parent method at the end.
*
* @param zone the new time zone
*/
void assignTimeZone(const QTimeZone &zone);
private:
friend class KDateTimeEditPrivate;
std::unique_ptr<class KDateTimeEditPrivate> const d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KDateTimeEdit::Options)
#endif // KDATETIMEEDIT_H
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KDateTimeEdit</class>
<widget class="QWidget" name="KDateTimeEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>33</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="m_calendarCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="KDateComboBox" name="m_dateCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="KTimeComboBox" name="m_timeCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_timeZoneCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KDateComboBoxPrivate</class>
<extends>KComboBox</extends>
<header>kdatecombobox.h</header>
</customwidget>
<customwidget>
<class>KTimeComboBoxPrivate</class>
<extends>KComboBox</extends>
<header>ktimecombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,86 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdragwidgetdecorator.h"
#include <QApplication>
#include <QDrag>
#include <QMouseEvent>
#include <QWidget>
class KDragWidgetDecoratorBasePrivate
{
public:
bool dragEnabled = true;
QWidget *decoratedWidget = nullptr;
QPoint startPos;
};
KDragWidgetDecoratorBase::KDragWidgetDecoratorBase(QWidget *parent)
: QObject(parent)
, d(new KDragWidgetDecoratorBasePrivate)
{
parent->installEventFilter(this);
d->decoratedWidget = parent;
}
KDragWidgetDecoratorBase::~KDragWidgetDecoratorBase() = default;
bool KDragWidgetDecoratorBase::isDragEnabled() const
{
return d->dragEnabled;
}
void KDragWidgetDecoratorBase::setDragEnabled(bool enable)
{
d->dragEnabled = enable;
}
bool KDragWidgetDecoratorBase::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched) // except in Q_ASSERT
Q_ASSERT(watched == d->decoratedWidget);
if (!d->dragEnabled) {
return false;
}
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *e = static_cast<QMouseEvent *>(event);
d->startPos = e->pos();
} else if (event->type() == QEvent::MouseMove) {
QMouseEvent *e = static_cast<QMouseEvent *>(event);
if ((e->buttons() & Qt::LeftButton) && (e->pos() - d->startPos).manhattanLength() > QApplication::startDragDistance()) {
startDrag();
d->decoratedWidget->setProperty("down", false);
return true;
}
}
return false;
}
QWidget *KDragWidgetDecoratorBase::decoratedWidget() const
{
return d->decoratedWidget;
}
QDrag *KDragWidgetDecoratorBase::dragObject()
{
return nullptr;
}
void KDragWidgetDecoratorBase::startDrag()
{
QDrag *drag = dragObject();
if (drag) {
drag->exec(Qt::CopyAction);
}
}
#include "moc_kdragwidgetdecorator.cpp"
@@ -0,0 +1,142 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-FileCopyrightText: 2012 Kevin Ottens <ervin+bluesystems@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDRAGWIDGETDECORATOR_H
#define KDRAGWIDGETDECORATOR_H
#include <QObject>
#include <kwidgetsaddons_export.h>
#include <memory>
class QDrag;
/**
* @brief A decorator which adds drag-support to widgets
*
* This is a decorator using an event filter to implement drag-support
* in widgets.
* You must override the virtual method dragObject() to specify the
* QDrag to be used.
*
* @author Carsten Pfeiffer <pfeiffer@kde.org>
*/
class KWIDGETSADDONS_EXPORT KDragWidgetDecoratorBase : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isDragEnabled READ isDragEnabled WRITE setDragEnabled)
public:
/**
* Default constructor.
*/
explicit KDragWidgetDecoratorBase(QWidget *parent = nullptr);
/**
* Destructs the decorator.
*/
~KDragWidgetDecoratorBase() override;
/**
* Enables/disables drag-support. Default is enabled.
*/
void setDragEnabled(bool enable);
/**
* @returns if drag support is enabled or not.
*/
bool isDragEnabled() const;
protected:
/**
* @return the widget this decorator is attached to
*/
QWidget *decoratedWidget() const;
/**
* Reimplement this and return the QDrag object that should be used
* for the drag. Remember to give it "decoratedWidget()" as parent.
*
* Default implementation returns a null pointer, so that no drag is initiated.
*/
virtual QDrag *dragObject();
/**
* Reimplemented to add drag-support
*/
bool eventFilter(QObject *watched, QEvent *event) override;
/**
* Starts a drag (Copy by default) using dragObject()
*/
virtual void startDrag();
private:
std::unique_ptr<class KDragWidgetDecoratorBasePrivate> const d;
};
/**
* @class KDragWidgetDecorator kdragwidgetdecorator.h KDragWidgetDecorator
*
* @brief A decorator which adds drag-support to widgets
*
* This is a decorator using an event filter to implement drag-support
* in widgets.
* You must set the dragObjectFactory to specify the QDrag to be used.
*
* @author Kevin Ottens <ervin@kde.org>
*/
template<class Widget>
class KDragWidgetDecorator : public KDragWidgetDecoratorBase
{
public:
typedef QDrag *(*DragObjectFactory)(Widget *);
KDragWidgetDecorator(Widget *parent = nullptr)
: KDragWidgetDecoratorBase(parent)
, m_factory(nullptr)
{
}
/**
* @return the QDrag factory used by this decorator
*/
DragObjectFactory dragObjectFactory() const
{
return m_factory;
}
/**
* Set a factory to be used by this decorator
*
* @param factory the new QDrag factory to use
*/
void setDragObjectFactory(DragObjectFactory factory)
{
m_factory = factory;
}
private:
/**
* Reimplemented to use the QDrag factory
*/
QDrag *dragObject() override
{
if (m_factory) {
Widget *w = static_cast<Widget *>(decoratedWidget());
return m_factory(w);
} else {
return KDragWidgetDecoratorBase::dragObject();
}
}
DragObjectFactory m_factory;
};
#endif // KDRAGWIDGETDECORATOR_H
@@ -0,0 +1,165 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2010 Aurélien Gâteau <agateau@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kdualaction.h"
#include "kdualaction_p.h"
#include <QIcon>
//---------------------------------------------------------------------
// KDualActionPrivate
//---------------------------------------------------------------------
void KDualActionPrivate::init(KDualAction *q_ptr)
{
q = q_ptr;
autoToggle = true;
isActive = false;
QObject::connect(q, &KDualAction::triggered, q, [this]() {
slotTriggered();
});
}
void KDualActionPrivate::updateFromCurrentState()
{
KGuiItem &currentItem = item(isActive);
QAction *qq = static_cast<QAction *>(q);
qq->setIcon(currentItem.icon());
qq->setText(currentItem.text());
qq->setToolTip(currentItem.toolTip());
}
void KDualActionPrivate::slotTriggered()
{
if (!autoToggle) {
return;
}
q->setActive(!isActive);
Q_EMIT q->activeChangedByUser(isActive);
}
//---------------------------------------------------------------------
// KDualAction
//---------------------------------------------------------------------
KDualAction::KDualAction(const QString &inactiveText, const QString &activeText, QObject *parent)
: QAction(parent)
, d(new KDualActionPrivate)
{
d->init(this);
d->item(false).setText(inactiveText);
d->item(true).setText(activeText);
d->updateFromCurrentState();
}
KDualAction::KDualAction(QObject *parent)
: QAction(parent)
, d(new KDualActionPrivate)
{
d->init(this);
}
KDualAction::~KDualAction() = default;
void KDualAction::setActiveGuiItem(const KGuiItem &item)
{
d->setGuiItem(true, item);
}
KGuiItem KDualAction::activeGuiItem() const
{
return d->item(true);
}
void KDualAction::setInactiveGuiItem(const KGuiItem &item)
{
d->setGuiItem(false, item);
}
KGuiItem KDualAction::inactiveGuiItem() const
{
return d->item(false);
}
void KDualAction::setActiveIcon(const QIcon &icon)
{
d->setIcon(true, icon);
}
QIcon KDualAction::activeIcon() const
{
return d->item(true).icon();
}
void KDualAction::setInactiveIcon(const QIcon &icon)
{
d->setIcon(false, icon);
}
QIcon KDualAction::inactiveIcon() const
{
return d->item(false).icon();
}
void KDualAction::setActiveText(const QString &text)
{
d->setText(true, text);
}
QString KDualAction::activeText() const
{
return d->item(true).text();
}
void KDualAction::setInactiveText(const QString &text)
{
d->setText(false, text);
}
QString KDualAction::inactiveText() const
{
return d->item(false).text();
}
void KDualAction::setActiveToolTip(const QString &toolTip)
{
d->setToolTip(true, toolTip);
}
QString KDualAction::activeToolTip() const
{
return d->item(true).toolTip();
}
void KDualAction::setInactiveToolTip(const QString &toolTip)
{
d->setToolTip(false, toolTip);
}
QString KDualAction::inactiveToolTip() const
{
return d->item(false).toolTip();
}
void KDualAction::setIconForStates(const QIcon &icon)
{
setInactiveIcon(icon);
setActiveIcon(icon);
}
void KDualAction::setAutoToggle(bool value)
{
d->autoToggle = value;
}
bool KDualAction::autoToggle() const
{
return d->autoToggle;
}
void KDualAction::setActive(bool active)
{
if (active == d->isActive) {
return;
}
d->isActive = active;
d->updateFromCurrentState();
Q_EMIT activeChanged(active);
}
bool KDualAction::isActive() const
{
return d->isActive;
}
#include "moc_kdualaction.cpp"
@@ -0,0 +1,195 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2010 Aurélien Gâteau <agateau@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KDUALACTION_H
#define KDUALACTION_H
#include <QAction>
#include <kwidgetsaddons_export.h>
#include <memory>
class KGuiItem;
/**
* @class KDualAction kdualaction.h KDualAction
*
* @short An action which can alternate between two texts/icons when triggered.
*
* KDualAction should be used when you want to create an action which alternate
* between two states when triggered but which should not be rendered as a
* checkable widget because it is more appropriate to change the text and icon
* of the action instead.
*
* You should use KDualAction to implement this kind of actions instead of
* KToggleAction because KToggleAction is rendered as a checkable widget: this
* means one of your state will have a checkbox in a menu and will be
* represented as a sunken button in a toolbar.
*
* Porting from KToggleAction to KDualAction:
*
* 1. If you used the KToggleAction constructor which accepts the action text,
* adjust the constructor: KDualAction constructor accepts both inactive and
* active text.
*
* 2. Replace connections to the checked(bool) signal with a connection to the
* activeChanged(bool) (or activeChangedByUser(bool))
*
* 3. Replace calls to setChecked()/isChecked() with setActive()/isActive()
*
* 4. Replace calls to setCheckedState(guiItem) with
* setActiveGuiItem(guiItem)
*
* @author Aurélien Gâteau <agateau@kde.org>
* @since 4.6
*/
class KWIDGETSADDONS_EXPORT KDualAction : public QAction
{
Q_OBJECT
public:
/**
* Constructs a KDualAction with the specified parent. Texts must be set
* with setTextForState() or setGuiItemForState().
*/
explicit KDualAction(QObject *parent);
/**
* Constructs a KDualAction with the specified parent and texts.
*/
KDualAction(const QString &inactiveText, const QString &activeText, QObject *parent);
~KDualAction() override;
/**
* Sets the KGuiItem for the active state
*/
void setActiveGuiItem(const KGuiItem &);
/**
* Gets the KGuiItem for the active state
*/
KGuiItem activeGuiItem() const;
/**
* Sets the KGuiItem for the inactive state
*/
void setInactiveGuiItem(const KGuiItem &);
/**
* Gets the KGuiItem for the inactive state
*/
KGuiItem inactiveGuiItem() const;
/**
* Sets the icon for the active state
*/
void setActiveIcon(const QIcon &);
/**
* Gets the icon for the active state
*/
QIcon activeIcon() const;
/**
* Sets the icon for the inactive state
*/
void setInactiveIcon(const QIcon &);
/**
* Gets the icon for the inactive state
*/
QIcon inactiveIcon() const;
/**
* Sets the text for the active state
*/
void setActiveText(const QString &);
/**
* Gets the text for the active state
*/
QString activeText() const;
/**
* Sets the text for the inactive state
*/
void setInactiveText(const QString &);
/**
* Gets the text for the inactive state
*/
QString inactiveText() const;
/**
* Sets the tooltip for the active state
*/
void setActiveToolTip(const QString &);
/**
* Gets the tooltip for the active state
*/
QString activeToolTip() const;
/**
* Sets the tooltip for the inactive state
*/
void setInactiveToolTip(const QString &);
/**
* Gets the tooltip for the inactive state
*/
QString inactiveToolTip() const;
/**
* Convenience method to set the icon for both active and inactive states.
*/
void setIconForStates(const QIcon &icon);
/**
* Returns the action state.
* The action is inactive by default.
*/
bool isActive() const;
/**
* Defines whether the current action should automatically be changed when
* the user triggers this action.
*/
void setAutoToggle(bool);
/**
* Returns whether the current action will automatically be changed when
* the user triggers this action. The default value is true.
*/
bool autoToggle() const;
public Q_SLOTS:
/**
* Sets the action state.
* activeChanged() will be emitted but not activeChangedByUser().
*/
void setActive(bool state);
Q_SIGNALS:
/**
* Emitted when the state changes. This signal is emitted when the user
* trigger the action and when setActive() is called.
*/
void activeChanged(bool);
/**
* Only emitted when the state changes because the user triggered the
* action.
*/
void activeChangedByUser(bool);
private:
friend class KDualActionPrivate;
std::unique_ptr<class KDualActionPrivate> const d;
};
#endif /* KDUALACTION_H */
@@ -0,0 +1,61 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2010 Aurélien Gâteau <agateau@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KDUALACTION_P_H
#define KDUALACTION_P_H
#include "kguiitem.h"
class KDualActionPrivate
{
public:
KDualAction *q;
KGuiItem items[2];
bool autoToggle;
bool isActive;
void init(KDualAction *q_ptr);
void updateFromCurrentState();
KGuiItem &item(bool active)
{
return active ? items[1] : items[0];
}
void slotTriggered();
void updatedItem(bool active)
{
if (active == isActive) {
updateFromCurrentState();
}
}
void setGuiItem(bool active, const KGuiItem &_item)
{
item(active) = _item;
updatedItem(active);
}
void setIcon(bool active, const QIcon &icon)
{
item(active).setIcon(icon);
updatedItem(active);
}
void setText(bool active, const QString &text)
{
item(active).setText(text);
updatedItem(active);
}
void setToolTip(bool active, const QString &toolTip)
{
item(active).setToolTip(toolTip);
updatedItem(active);
}
};
#endif /* KDUALACTION_P_H */
@@ -0,0 +1,654 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
SPDX-FileCopyrightText: 2000 Alexander Neundorf <neundorf@kde.org>
SPDX-FileCopyrightText: 2000, 2002 Carsten Pfeiffer <pfeiffer@kde.org>
SPDX-FileCopyrightText: 2010 Sebastian Trueg <trueg@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "keditlistwidget.h"
#include <QApplication>
#include <QComboBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
#include <QListView>
#include <QPushButton>
#include <QStringList>
#include <QStringListModel>
#include <QVBoxLayout>
class KEditListWidgetPrivate
{
public:
KEditListWidgetPrivate(KEditListWidget *parent)
: q(parent)
{
}
QListView *listView = nullptr;
QPushButton *servUpButton = nullptr;
QPushButton *servDownButton = nullptr;
QPushButton *servNewButton = nullptr;
QPushButton *servRemoveButton = nullptr;
QLineEdit *lineEdit = nullptr;
QWidget *editingWidget = nullptr;
QVBoxLayout *mainLayout = nullptr;
QVBoxLayout *btnsLayout = nullptr;
QStringListModel *model = nullptr;
bool checkAtEntering;
KEditListWidget::Buttons buttons;
void init(bool check = false, KEditListWidget::Buttons buttons = KEditListWidget::All, QWidget *representationWidget = nullptr);
void setEditor(QLineEdit *lineEdit, QWidget *representationWidget = nullptr);
void updateButtonState();
QModelIndex selectedIndex();
private:
KEditListWidget *const q;
};
void KEditListWidgetPrivate::init(bool check, KEditListWidget::Buttons newButtons, QWidget *representationWidget)
{
checkAtEntering = check;
servNewButton = servRemoveButton = servUpButton = servDownButton = nullptr;
q->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred));
mainLayout = new QVBoxLayout(q);
mainLayout->setContentsMargins(0, 0, 0, 0);
QHBoxLayout *subLayout = new QHBoxLayout;
btnsLayout = new QVBoxLayout;
btnsLayout->addStretch();
model = new QStringListModel(q);
listView = new QListView(q);
listView->setModel(model);
subLayout->addWidget(listView);
subLayout->addLayout(btnsLayout);
mainLayout->addLayout(subLayout);
setEditor(lineEdit, representationWidget);
buttons = KEditListWidget::Buttons();
q->setButtons(newButtons);
q->connect(listView->selectionModel(), &QItemSelectionModel::selectionChanged, q, &KEditListWidget::slotSelectionChanged);
}
void KEditListWidgetPrivate::setEditor(QLineEdit *newLineEdit, QWidget *representationWidget)
{
if (editingWidget != lineEdit && editingWidget != representationWidget) {
delete editingWidget;
}
if (lineEdit != newLineEdit) {
delete lineEdit;
}
lineEdit = newLineEdit ? newLineEdit : new QLineEdit(q);
editingWidget = representationWidget ? representationWidget : lineEdit;
if (representationWidget) {
representationWidget->setParent(q);
}
mainLayout->insertWidget(0, editingWidget); // before subLayout
lineEdit->installEventFilter(q);
q->connect(lineEdit, &QLineEdit::textChanged, q, &KEditListWidget::typedSomething);
q->connect(lineEdit, &QLineEdit::returnPressed, q, &KEditListWidget::addItem);
// maybe supplied lineedit has some text already
q->typedSomething(lineEdit->text());
// fix tab ordering
q->setTabOrder(editingWidget, listView);
QWidget *w = listView;
if (servNewButton) {
q->setTabOrder(w, servNewButton);
w = servNewButton;
}
if (servRemoveButton) {
q->setTabOrder(w, servRemoveButton);
w = servRemoveButton;
}
if (servUpButton) {
q->setTabOrder(w, servUpButton);
w = servUpButton;
}
if (servDownButton) {
q->setTabOrder(w, servDownButton);
w = servDownButton;
}
}
void KEditListWidgetPrivate::updateButtonState()
{
const bool hasSelectedItem = selectedIndex().isValid();
// TODO: merge with enableMoveButtons()
QPushButton *const buttons[3] = {servUpButton, servDownButton, servRemoveButton};
for (QPushButton *button : buttons) {
if (button) {
// keep focus in widget
if (!hasSelectedItem && button->hasFocus()) {
lineEdit->setFocus(Qt::OtherFocusReason);
}
button->setEnabled(hasSelectedItem);
}
}
}
QModelIndex KEditListWidgetPrivate::selectedIndex()
{
QItemSelectionModel *selection = listView->selectionModel();
const QModelIndexList selectedIndexes = selection->selectedIndexes();
if (!selectedIndexes.isEmpty() && selectedIndexes[0].isValid()) {
return selectedIndexes[0];
} else {
return QModelIndex();
}
}
class KEditListWidgetCustomEditorPrivate
{
public:
KEditListWidgetCustomEditorPrivate(KEditListWidget::CustomEditor *qq)
: q(qq)
, representationWidget(nullptr)
, lineEdit(nullptr)
{
}
KEditListWidget::CustomEditor *q;
QWidget *representationWidget;
QLineEdit *lineEdit;
};
KEditListWidget::CustomEditor::CustomEditor()
: d(new KEditListWidgetCustomEditorPrivate(this))
{
}
KEditListWidget::CustomEditor::CustomEditor(QWidget *repWidget, QLineEdit *edit)
: d(new KEditListWidgetCustomEditorPrivate(this))
{
d->representationWidget = repWidget;
d->lineEdit = edit;
}
KEditListWidget::CustomEditor::CustomEditor(QComboBox *combo)
: d(new KEditListWidgetCustomEditorPrivate(this))
{
d->representationWidget = combo;
d->lineEdit = qobject_cast<QLineEdit *>(combo->lineEdit());
Q_ASSERT(d->lineEdit);
}
KEditListWidget::CustomEditor::~CustomEditor() = default;
void KEditListWidget::CustomEditor::setRepresentationWidget(QWidget *repWidget)
{
d->representationWidget = repWidget;
}
void KEditListWidget::CustomEditor::setLineEdit(QLineEdit *edit)
{
d->lineEdit = edit;
}
QWidget *KEditListWidget::CustomEditor::representationWidget() const
{
return d->representationWidget;
}
QLineEdit *KEditListWidget::CustomEditor::lineEdit() const
{
return d->lineEdit;
}
KEditListWidget::KEditListWidget(QWidget *parent)
: QWidget(parent)
, d(new KEditListWidgetPrivate(this))
{
d->init();
}
KEditListWidget::KEditListWidget(const CustomEditor &custom, QWidget *parent, bool checkAtEntering, Buttons buttons)
: QWidget(parent)
, d(new KEditListWidgetPrivate(this))
{
d->lineEdit = custom.lineEdit();
d->init(checkAtEntering, buttons, custom.representationWidget());
}
KEditListWidget::~KEditListWidget() = default;
void KEditListWidget::setCustomEditor(const CustomEditor &editor)
{
d->setEditor(editor.lineEdit(), editor.representationWidget());
}
QListView *KEditListWidget::listView() const
{
return d->listView;
}
QLineEdit *KEditListWidget::lineEdit() const
{
return d->lineEdit;
}
QPushButton *KEditListWidget::addButton() const
{
return d->servNewButton;
}
QPushButton *KEditListWidget::removeButton() const
{
return d->servRemoveButton;
}
QPushButton *KEditListWidget::upButton() const
{
return d->servUpButton;
}
QPushButton *KEditListWidget::downButton() const
{
return d->servDownButton;
}
int KEditListWidget::count() const
{
return int(d->model->rowCount());
}
void KEditListWidget::setButtons(Buttons buttons)
{
if (d->buttons == buttons) {
return;
}
if ((buttons & Add) && !d->servNewButton) {
d->servNewButton = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add")), tr("&Add", "@action:button"), this);
d->servNewButton->setEnabled(false);
d->servNewButton->show();
connect(d->servNewButton, &QAbstractButton::clicked, this, &KEditListWidget::addItem);
d->btnsLayout->insertWidget(0, d->servNewButton);
} else if ((buttons & Add) == 0 && d->servNewButton) {
delete d->servNewButton;
d->servNewButton = nullptr;
}
if ((buttons & Remove) && !d->servRemoveButton) {
d->servRemoveButton = new QPushButton(QIcon::fromTheme(QStringLiteral("list-remove")), tr("&Remove", "@action:button"), this);
d->servRemoveButton->setEnabled(false);
d->servRemoveButton->show();
connect(d->servRemoveButton, &QAbstractButton::clicked, this, &KEditListWidget::removeItem);
d->btnsLayout->insertWidget(1, d->servRemoveButton);
} else if ((buttons & Remove) == 0 && d->servRemoveButton) {
delete d->servRemoveButton;
d->servRemoveButton = nullptr;
}
if ((buttons & UpDown) && !d->servUpButton) {
d->servUpButton = new QPushButton(QIcon::fromTheme(QStringLiteral("arrow-up")), tr("Move &Up", "@action:button"), this);
d->servUpButton->setEnabled(false);
d->servUpButton->show();
connect(d->servUpButton, &QAbstractButton::clicked, this, &KEditListWidget::moveItemUp);
d->servDownButton = new QPushButton(QIcon::fromTheme(QStringLiteral("arrow-down")), tr("Move &Down", "@action:button"), this);
d->servDownButton->setEnabled(false);
d->servDownButton->show();
connect(d->servDownButton, &QAbstractButton::clicked, this, &KEditListWidget::moveItemDown);
d->btnsLayout->insertWidget(2, d->servUpButton);
d->btnsLayout->insertWidget(3, d->servDownButton);
} else if ((buttons & UpDown) == 0 && d->servUpButton) {
delete d->servUpButton;
d->servUpButton = nullptr;
delete d->servDownButton;
d->servDownButton = nullptr;
}
d->buttons = buttons;
}
void KEditListWidget::setCheckAtEntering(bool check)
{
d->checkAtEntering = check;
}
bool KEditListWidget::checkAtEntering()
{
return d->checkAtEntering;
}
void KEditListWidget::typedSomething(const QString &text)
{
if (currentItem() >= 0) {
if (currentText() != d->lineEdit->text()) {
// IMHO changeItem() shouldn't do anything with the value
// of currentItem() ... like changing it or emitting signals ...
// but TT disagree with me on this one (it's been that way since ages ... grrr)
bool block = d->listView->signalsBlocked();
d->listView->blockSignals(true);
QModelIndex currentIndex = d->selectedIndex();
if (currentIndex.isValid()) {
d->model->setData(currentIndex, text);
}
d->listView->blockSignals(block);
Q_EMIT changed();
}
}
if (!d->servNewButton) {
return;
}
if (!d->lineEdit->hasAcceptableInput()) {
d->servNewButton->setEnabled(false);
return;
}
if (!d->checkAtEntering) {
d->servNewButton->setEnabled(!text.isEmpty());
} else {
if (text.isEmpty()) {
d->servNewButton->setEnabled(false);
} else {
QStringList list = d->model->stringList();
bool enable = !list.contains(text, Qt::CaseSensitive);
d->servNewButton->setEnabled(enable);
}
}
}
void KEditListWidget::moveItemUp()
{
if (!d->listView->isEnabled()) {
QApplication::beep();
return;
}
QModelIndex index = d->selectedIndex();
if (index.isValid()) {
if (index.row() == 0) {
QApplication::beep();
return;
}
QModelIndex aboveIndex = d->model->index(index.row() - 1, index.column());
QString tmp = d->model->data(aboveIndex, Qt::DisplayRole).toString();
d->model->setData(aboveIndex, d->model->data(index, Qt::DisplayRole));
d->model->setData(index, tmp);
d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
d->listView->selectionModel()->select(aboveIndex, QItemSelectionModel::Select);
}
Q_EMIT changed();
}
void KEditListWidget::moveItemDown()
{
if (!d->listView->isEnabled()) {
QApplication::beep();
return;
}
QModelIndex index = d->selectedIndex();
if (index.isValid()) {
if (index.row() == d->model->rowCount() - 1) {
QApplication::beep();
return;
}
QModelIndex belowIndex = d->model->index(index.row() + 1, index.column());
QString tmp = d->model->data(belowIndex, Qt::DisplayRole).toString();
d->model->setData(belowIndex, d->model->data(index, Qt::DisplayRole));
d->model->setData(index, tmp);
d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
d->listView->selectionModel()->select(belowIndex, QItemSelectionModel::Select);
}
Q_EMIT changed();
}
void KEditListWidget::addItem()
{
// when checkAtEntering is true, the add-button is disabled, but this
// slot can still be called through Key_Return/Key_Enter. So we guard
// against this.
if (!d->servNewButton || !d->servNewButton->isEnabled()) {
return;
}
QModelIndex currentIndex = d->selectedIndex();
const QString &currentTextLE = d->lineEdit->text();
bool alreadyInList(false);
// if we didn't check for dupes at the inserting we have to do it now
if (!d->checkAtEntering) {
// first check current item instead of dumb iterating the entire list
if (currentIndex.isValid()) {
if (d->model->data(currentIndex, Qt::DisplayRole).toString() == currentTextLE) {
alreadyInList = true;
}
} else {
alreadyInList = d->model->stringList().contains(currentTextLE, Qt::CaseSensitive);
}
}
if (d->servNewButton) {
// prevent losing the focus by it being moved outside of this widget
// as well as support the user workflow a little by moving the focus
// to the lineedit. chances are that users will add some items consecutively,
// so this will save a manual focus change, and it is also consistent
// to what happens on the click on the Remove button
if (d->servNewButton->hasFocus()) {
d->lineEdit->setFocus(Qt::OtherFocusReason);
}
d->servNewButton->setEnabled(false);
}
bool block = d->lineEdit->signalsBlocked();
d->lineEdit->blockSignals(true);
d->lineEdit->clear();
d->lineEdit->blockSignals(block);
d->listView->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::Deselect);
if (!alreadyInList) {
block = d->listView->signalsBlocked();
if (currentIndex.isValid()) {
d->model->setData(currentIndex, currentTextLE);
} else {
QStringList lst;
lst << currentTextLE;
lst << d->model->stringList();
d->model->setStringList(lst);
}
Q_EMIT changed();
Q_EMIT added(currentTextLE); // TODO: pass the index too
}
d->updateButtonState();
}
int KEditListWidget::currentItem() const
{
QModelIndex selectedIndex = d->selectedIndex();
if (selectedIndex.isValid()) {
return selectedIndex.row();
} else {
return -1;
}
}
void KEditListWidget::removeItem()
{
QModelIndex currentIndex = d->selectedIndex();
if (!currentIndex.isValid()) {
return;
}
if (currentIndex.row() >= 0) {
// prevent losing the focus by it being moved outside of this widget
// as well as support the user workflow a little by moving the focus
// to the lineedit. chances are that users will add some item next,
// so this will save a manual focus change,
if (d->servRemoveButton && d->servRemoveButton->hasFocus()) {
d->lineEdit->setFocus(Qt::OtherFocusReason);
}
QString removedText = d->model->data(currentIndex, Qt::DisplayRole).toString();
d->model->removeRows(currentIndex.row(), 1);
d->listView->selectionModel()->clear();
Q_EMIT changed();
Q_EMIT removed(removedText);
}
d->updateButtonState();
}
void KEditListWidget::enableMoveButtons(const QModelIndex &newIndex, const QModelIndex &)
{
int index = newIndex.row();
// Update the lineEdit when we select a different line.
if (currentText() != d->lineEdit->text()) {
d->lineEdit->setText(currentText());
}
bool moveEnabled = d->servUpButton && d->servDownButton;
if (moveEnabled) {
if (d->model->rowCount() <= 1) {
d->servUpButton->setEnabled(false);
d->servDownButton->setEnabled(false);
} else if (index == (d->model->rowCount() - 1)) {
d->servUpButton->setEnabled(true);
d->servDownButton->setEnabled(false);
} else if (index == 0) {
d->servUpButton->setEnabled(false);
d->servDownButton->setEnabled(true);
} else {
d->servUpButton->setEnabled(true);
d->servDownButton->setEnabled(true);
}
}
if (d->servRemoveButton) {
d->servRemoveButton->setEnabled(true);
}
}
void KEditListWidget::clear()
{
d->lineEdit->clear();
d->model->setStringList(QStringList());
Q_EMIT changed();
}
void KEditListWidget::insertStringList(const QStringList &list, int index)
{
QStringList content = d->model->stringList();
if (index < 0) {
content += list;
} else {
for (int i = 0, j = index; i < list.count(); ++i, ++j) {
content.insert(j, list[i]);
}
}
d->model->setStringList(content);
}
void KEditListWidget::insertItem(const QString &text, int index)
{
QStringList list = d->model->stringList();
if (index < 0) {
list.append(text);
} else {
list.insert(index, text);
}
d->model->setStringList(list);
}
QString KEditListWidget::text(int index) const
{
const QStringList list = d->model->stringList();
return list[index];
}
QString KEditListWidget::currentText() const
{
QModelIndex index = d->selectedIndex();
if (!index.isValid()) {
return QString();
} else {
return text(index.row());
}
}
QStringList KEditListWidget::items() const
{
return d->model->stringList();
}
void KEditListWidget::setItems(const QStringList &items)
{
d->model->setStringList(items);
}
KEditListWidget::Buttons KEditListWidget::buttons() const
{
return d->buttons;
}
void KEditListWidget::slotSelectionChanged(const QItemSelection &, const QItemSelection &)
{
d->updateButtonState();
QModelIndex index = d->selectedIndex();
enableMoveButtons(index, QModelIndex());
if (index.isValid()) {
d->lineEdit->setFocus(Qt::OtherFocusReason);
}
}
bool KEditListWidget::eventFilter(QObject *o, QEvent *e)
{
if (o == d->lineEdit && e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent *)e;
if (keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_Up) {
return ((QObject *)d->listView)->event(e);
} else if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
return true;
}
}
return false;
}
#include "moc_keditlistwidget.cpp"
@@ -0,0 +1,259 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
SPDX-FileCopyrightText: 2000 Alexander Neundorf <neundorf@kde.org>
SPDX-FileCopyrightText: 2010 Sebastian Trueg <trueg@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KEDITLISTWIDGET_H
#define KEDITLISTWIDGET_H
#include <kwidgetsaddons_export.h>
#include <QWidget>
#include <memory>
class QLineEdit;
class QComboBox;
class QListView;
class QPushButton;
class QItemSelection;
/**
* @class KEditListWidget keditlistwidget.h KEditListWidget
*
* An editable listbox
*
* This class provides an editable listbox, this means
* a listbox which is accompanied by a line edit to enter new
* items into the listbox and pushbuttons to add and remove
* items from the listbox and two buttons to move items up and down.
*
* \image html keditlistbox.png "KEditListWidget"
*
* @since 4.6
*/
class KWIDGETSADDONS_EXPORT KEditListWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(Buttons buttons READ buttons WRITE setButtons)
Q_PROPERTY(QStringList items READ items WRITE setItems NOTIFY changed USER true)
Q_PROPERTY(bool checkAtEntering READ checkAtEntering WRITE setCheckAtEntering)
public:
/**
* Custom editor class
*/
class KWIDGETSADDONS_EXPORT CustomEditor
{
public:
CustomEditor();
CustomEditor(QWidget *repWidget, QLineEdit *edit);
CustomEditor(QComboBox *combo);
virtual ~CustomEditor();
void setRepresentationWidget(QWidget *repWidget);
void setLineEdit(QLineEdit *edit);
virtual QWidget *representationWidget() const;
virtual QLineEdit *lineEdit() const;
private:
friend class KEditListWidgetCustomEditorPrivate;
std::unique_ptr<class KEditListWidgetCustomEditorPrivate> const d;
Q_DISABLE_COPY(CustomEditor)
};
public:
/**
* Enumeration of the buttons, the listbox offers. Specify them in the
* constructor in the buttons parameter, or in setButtons.
* @see Buttons
*/
enum Button {
Add = 0x0001,
Remove = 0x0002,
UpDown = 0x0004,
All = Add | Remove | UpDown,
};
/**
* Stores a combination of #Button values.
*/
Q_DECLARE_FLAGS(Buttons, Button)
Q_FLAG(Buttons)
/**
* Create an editable listbox.
*/
explicit KEditListWidget(QWidget *parent = nullptr);
/**
* Constructor which allows to use a custom editing widget
* instead of the standard QLineEdit widget. E.g. you can use a
* KUrlRequester or a QComboBox as input widget. The custom
* editor must consist of a lineedit and optionally another widget that
* is used as representation. A QComboBox or a KUrlRequester have a
* QLineEdit as child-widget for example, so the QComboBox is used as
* the representation widget.
*
* @see KUrlRequester::customEditor(), setCustomEditor
*/
KEditListWidget(const CustomEditor &customEditor, QWidget *parent = nullptr, bool checkAtEntering = false, Buttons buttons = All);
~KEditListWidget() override;
/**
* @returns a pointer to the embedded QListView.
*/
QListView *listView() const;
/**
* @returns a pointer to the embedded QLineEdit.
*/
QLineEdit *lineEdit() const;
/**
* @returns a pointer to the Add button
*/
QPushButton *addButton() const;
/**
* @returns a pointer to the Remove button
*/
QPushButton *removeButton() const;
/**
* @returns a pointer to the Up button
*/
QPushButton *upButton() const;
/**
* @returns a pointer to the Down button
*/
QPushButton *downButton() const;
/**
* @returns the count of elements in the list
*/
int count() const;
/**
* Inserts a @p list of elements from the @p index element
* If @p index is negative, the elements will be appended
*/
void insertStringList(const QStringList &list, int index = -1);
/**
* Inserts a @p text element at the @p index position
* If @p index is negative, the element will be appended
*/
void insertItem(const QString &text, int index = -1);
/**
* Clears both the listbox and the line edit.
*/
void clear();
/**
* @returns the text at the @p index position
*/
QString text(int index) const;
/**
* @returns the currently selected item
*/
int currentItem() const;
/**
* @returns the currently selected item's text
*/
QString currentText() const;
/**
* @returns a list with the text of all items in the listbox
*/
QStringList items() const;
/**
* Clears the listbox and sets the contents to @p items
*/
void setItems(const QStringList &items);
/**
* @returns which buttons are visible
*/
Buttons buttons() const;
/**
* Specifies which @p buttons are visible
*/
void setButtons(Buttons buttons);
/**
* If @p check is true, after every character you type
* in the line edit KEditListWidget will enable or disable
* the Add-button, depending whether the current content of the
* line edit is already in the listbox. Maybe this can become a
* performance hit with large lists on slow machines.
* If @p check is false,
* it will be checked if you press the Add-button. It is not
* possible to enter items twice into the listbox.
* Default is false.
*/
void setCheckAtEntering(bool check);
/**
* @returns true if check at entering is enabled.
*/
bool checkAtEntering();
/**
* Allows to use a custom editing widget
* instead of the standard QLineEdit widget. E.g. you can use a
* KUrlRequester or a QComboBox as input widget. The custom
* editor must consist of a lineedit and optionally another widget that
* is used as representation. A QComboBox or a KUrlRequester have a
* QLineEdit as child-widget for example, so the QComboBox is used as
* the representation widget.
*/
void setCustomEditor(const CustomEditor &editor);
/**
* Reimplemented for internal reasons. The API is not affected.
*/
bool eventFilter(QObject *o, QEvent *e) override;
Q_SIGNALS:
void changed();
/**
* This signal is emitted when the user adds a new string to the list,
* the parameter is the added string.
*/
void added(const QString &text);
/**
* This signal is emitted when the user removes a string from the list,
* the parameter is the removed string.
*/
void removed(const QString &text);
private Q_SLOTS:
KWIDGETSADDONS_NO_EXPORT void moveItemUp();
KWIDGETSADDONS_NO_EXPORT void moveItemDown();
KWIDGETSADDONS_NO_EXPORT void addItem();
KWIDGETSADDONS_NO_EXPORT void removeItem();
KWIDGETSADDONS_NO_EXPORT void enableMoveButtons(const QModelIndex &, const QModelIndex &);
KWIDGETSADDONS_NO_EXPORT void typedSomething(const QString &text);
KWIDGETSADDONS_NO_EXPORT void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
private:
friend class KEditListWidgetPrivate;
std::unique_ptr<class KEditListWidgetPrivate> const d;
Q_DISABLE_COPY(KEditListWidget)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KEditListWidget::Buttons)
#endif
@@ -0,0 +1,213 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-FileCopyrightText: 2007 Clarence Dang <dang@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kfontaction.h"
#include "kselectaction_p.h"
#include <QFontComboBox>
#include <kfontchooser.h>
class KFontActionPrivate : public KSelectActionPrivate
{
Q_DECLARE_PUBLIC(KFontAction)
public:
KFontActionPrivate(KFontAction *qq)
: KSelectActionPrivate(qq)
{
}
void slotFontChanged(const QFont &font)
{
Q_Q(KFontAction);
// qCDebug(KWidgetsAddonsLog) << "QFontComboBox - slotFontChanged("
// << font.family() << ") settingFont=" << settingFont;
if (settingFont) {
return;
}
const QString fontFamily = font.family();
q->setFont(fontFamily);
Q_EMIT q->textTriggered(fontFamily);
// qCDebug(KWidgetsAddonsLog) << "\tslotFontChanged done";
}
int settingFont = 0;
QFontComboBox::FontFilters fontFilters = QFontComboBox::AllFonts;
};
QStringList fontList(const QFontComboBox::FontFilters &fontFilters = QFontComboBox::AllFonts)
{
QStringList families;
if (fontFilters == QFontComboBox::AllFonts) {
families = QFontDatabase::families();
} else {
const QFontComboBox::FontFilters scalableMask = (QFontComboBox::ScalableFonts | QFontComboBox::NonScalableFonts);
const QFontComboBox::FontFilters spacingMask = (QFontComboBox::ProportionalFonts | QFontComboBox::MonospacedFonts);
const auto allFamilies = QFontDatabase::families();
for (const QString &family : allFamilies) {
if ((fontFilters & scalableMask) && (fontFilters & scalableMask) != scalableMask) {
if (bool(fontFilters & QFontComboBox::ScalableFonts) != QFontDatabase::isSmoothlyScalable(family)) {
continue;
}
}
if ((fontFilters & spacingMask) && (fontFilters & spacingMask) != spacingMask) {
if (bool(fontFilters & QFontComboBox::MonospacedFonts) != QFontDatabase::isFixedPitch(family)) {
continue;
}
}
families << family;
}
}
families.sort();
return families;
}
KFontAction::KFontAction(uint fontListCriteria, QObject *parent)
: KSelectAction(*new KFontActionPrivate(this), parent)
{
Q_D(KFontAction);
if (fontListCriteria & KFontChooser::FixedWidthFonts) {
d->fontFilters |= QFontComboBox::MonospacedFonts;
}
if (fontListCriteria & KFontChooser::SmoothScalableFonts) {
d->fontFilters |= QFontComboBox::ScalableFonts;
}
KSelectAction::setItems(fontList(d->fontFilters));
setEditable(true);
}
KFontAction::KFontAction(QObject *parent)
: KSelectAction(*new KFontActionPrivate(this), parent)
{
KSelectAction::setItems(fontList());
setEditable(true);
}
KFontAction::KFontAction(const QString &text, QObject *parent)
: KSelectAction(*new KFontActionPrivate(this), parent)
{
setText(text);
KSelectAction::setItems(fontList());
setEditable(true);
}
KFontAction::KFontAction(const QIcon &icon, const QString &text, QObject *parent)
: KSelectAction(*new KFontActionPrivate(this), parent)
{
setIcon(icon);
setText(text);
KSelectAction::setItems(fontList());
setEditable(true);
}
KFontAction::~KFontAction() = default;
QString KFontAction::font() const
{
return currentText();
}
QWidget *KFontAction::createWidget(QWidget *parent)
{
Q_D(KFontAction);
// qCDebug(KWidgetsAddonsLog) << "KFontAction::createWidget()";
// This is the visual element on the screen. This method overrides
// the KSelectAction one, preventing KSelectAction from creating its
// regular KComboBox.
QFontComboBox *cb = new QFontComboBox(parent);
cb->setFontFilters(d->fontFilters);
// qCDebug(KWidgetsAddonsLog) << "\tset=" << font();
// Do this before connecting the signal so that nothing will fire.
cb->setCurrentFont(QFont(font().toLower()));
// qCDebug(KWidgetsAddonsLog) << "\tspit back=" << cb->currentFont().family();
connect(cb, &QFontComboBox::currentFontChanged, this, [this](const QFont &ft) {
Q_D(KFontAction);
d->slotFontChanged(ft);
});
cb->setMinimumWidth(cb->sizeHint().width());
return cb;
}
/*
* Maintenance note: Keep in sync with QFontComboBox::setCurrentFont()
*/
void KFontAction::setFont(const QString &family)
{
Q_D(KFontAction);
// qCDebug(KWidgetsAddonsLog) << "KFontAction::setFont(" << family << ")";
// Suppress triggered(QString) signal and prevent recursive call to ourself.
d->settingFont++;
const auto createdWidgets = this->createdWidgets();
for (QWidget *w : createdWidgets) {
QFontComboBox *cb = qobject_cast<QFontComboBox *>(w);
// qCDebug(KWidgetsAddonsLog) << "\tw=" << w << "cb=" << cb;
if (!cb) {
continue;
}
cb->setCurrentFont(QFont(family.toLower()));
// qCDebug(KWidgetsAddonsLog) << "\t\tw spit back=" << cb->currentFont().family();
}
d->settingFont--;
// qCDebug(KWidgetsAddonsLog) << "\tcalling setCurrentAction()";
QString lowerName = family.toLower();
if (setCurrentAction(lowerName, Qt::CaseInsensitive)) {
return;
}
int i = lowerName.indexOf(QLatin1String(" ["));
if (i > -1) {
lowerName.truncate(i);
i = 0;
if (setCurrentAction(lowerName, Qt::CaseInsensitive)) {
return;
}
}
lowerName += QLatin1String(" [");
if (setCurrentAction(lowerName, Qt::CaseInsensitive)) {
return;
}
// TODO: Inconsistent state if QFontComboBox::setCurrentFont() succeeded
// but setCurrentAction() did not and vice-versa.
// qCDebug(KWidgetsAddonsLog) << "Font not found " << family.toLower();
}
#include "moc_kfontaction.cpp"
@@ -0,0 +1,51 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KFONTACTION_H
#define KFONTACTION_H
#include <kselectaction.h>
class KFontActionPrivate;
/**
* @class KFontAction kfontaction.h KFontAction
*
* An action to select a font family.
* On a toolbar this will show a combobox with all the fonts on the system.
*/
class KWIDGETSADDONS_EXPORT KFontAction : public KSelectAction
{
Q_OBJECT
Q_PROPERTY(QString font READ font WRITE setFont)
public:
KFontAction(uint fontListCriteria, QObject *parent);
explicit KFontAction(QObject *parent);
KFontAction(const QString &text, QObject *parent);
KFontAction(const QIcon &icon, const QString &text, QObject *parent);
~KFontAction() override;
QString font() const;
void setFont(const QString &family);
QWidget *createWidget(QWidget *parent) override;
private:
Q_DECLARE_PRIVATE(KFontAction)
};
#endif
@@ -0,0 +1,978 @@
/*
SPDX-FileCopyrightText: 1996 Bernd Johannes Wuebben <wuebben@kde.org>
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kfontchooser.h"
#include "fonthelpers_p.h"
#include "ui_kfontchooserwidget.h"
#include "loggingcategory.h"
#include <QCheckBox>
#include <QDoubleSpinBox>
#include <QFontDatabase>
#include <QGroupBox>
#include <QGuiApplication>
#include <QLabel>
#include <QLayout>
#include <QListWidget>
#include <QLocale>
#include <QScrollBar>
#include <QSplitter>
#include <QTextEdit>
#include <QTimer>
#include <algorithm>
#include <cmath>
// When message extraction needs to be avoided.
#define TR_NOX tr
static int minimumListWidth(const QListWidget *list)
{
QFontMetrics fm = list->fontMetrics();
const int extraSpace = fm.horizontalAdvance(QLatin1Char(' ')) * 2;
// Minimum initial size
int width = 40;
for (int i = 0, rows = list->count(); i < rows; ++i) {
int itemWidth = fm.horizontalAdvance(list->item(i)->text());
// ...and add a space on both sides for a not too tight look.
itemWidth += extraSpace;
width = std::max(width, itemWidth);
}
width += list->frameWidth() * 2;
width += list->verticalScrollBar()->sizeHint().width();
return width;
}
static int minimumListHeight(const QListWidget *list, int numVisibleEntry)
{
int w = list->fontMetrics().lineSpacing();
if (w < 0) {
w = 10;
}
if (numVisibleEntry <= 0) {
numVisibleEntry = 4;
}
w = w * numVisibleEntry;
w += list->frameWidth() * 2;
w += list->horizontalScrollBar()->sizeHint().height();
return w;
}
static QString formatFontSize(qreal size)
{
return QLocale::system().toString(size, 'f', (size == floor(size)) ? 0 : 1);
}
class KFontChooserPrivate
{
Q_DECLARE_TR_FUNCTIONS(KFontChooser)
public:
KFontChooserPrivate(KFontChooser::DisplayFlags flags, KFontChooser *qq)
: q(qq)
, m_flags(flags)
{
m_palette.setColor(QPalette::Active, QPalette::Text, Qt::black);
m_palette.setColor(QPalette::Active, QPalette::Base, Qt::white);
}
void init();
void setFamilyBoxItems(const QStringList &fonts = {});
int nearestSizeRow(qreal val, bool customize);
qreal fillSizeList(const QList<qreal> &sizes = QList<qreal>());
qreal setupSizeListBox(const QString &family, const QString &style);
void setupDisplay();
QString styleIdentifier(const QFont &font);
void slotFamilySelected(const QString &);
void slotSizeSelected(const QString &);
void slotStyleSelected(const QString &);
void displaySample(const QFont &font);
void slotSizeValue(double);
void slotFeaturesChanged(const QString &features);
KFontChooser *q;
std::unique_ptr<Ui_KFontChooserWidget> m_ui;
KFontChooser::DisplayFlags m_flags = KFontChooser::NoDisplayFlags;
QPalette m_palette;
QFont m_selectedFont;
QString m_selectedStyle;
qreal m_selectedSize = -1.0;
QString m_standardSizeAtCustom;
int m_customSizeRow = -1;
bool m_signalsAllowed = true;
bool m_usingFixed = false;
// Mappings of translated to Qt originated family and style strings.
FontFamiliesMap m_qtFamilies;
std::map<QString, QString> m_qtStyles;
// Mapping of translated style strings to internal style identifiers.
std::map<QString, QString> m_styleIDs;
QTimer m_fontFeatureChangedTimer;
};
KFontChooser::KFontChooser(QWidget *parent)
: QWidget(parent)
, d(new KFontChooserPrivate(KFontChooser::DisplayFrame, this))
{
d->init();
}
KFontChooser::KFontChooser(const DisplayFlags flags, QWidget *parent)
: QWidget(parent)
, d(new KFontChooserPrivate(flags, this))
{
d->init();
}
KFontChooser::~KFontChooser() = default;
void KFontChooserPrivate::init()
{
m_usingFixed = m_flags & KFontChooser::FixedFontsOnly;
// The main layout is divided horizontally into a top part with
// the font attribute widgets (family, style, size) and a bottom
// part with a preview of the selected font
QVBoxLayout *mainLayout = new QVBoxLayout(q);
mainLayout->setContentsMargins(0, 0, 0, 0);
QWidget *page = m_flags & KFontChooser::DisplayFrame ? new QGroupBox(KFontChooser::tr("Requested Font", "@title:group"), q) : new QWidget(q);
mainLayout->addWidget(page);
m_ui.reset(new Ui_KFontChooserWidget);
m_ui->setupUi(page);
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
m_ui->fontFeaturesLabel->setVisible(false);
m_ui->fontFeaturesLineEdit->setVisible(false);
#endif
// Increase spacing on top of the preview field and then reset the other layouts
// back to a standard value.
m_ui->sampleTextEditLayout->setSpacing(q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
m_ui->mainHorizontalLayout->setSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
m_ui->gridLayout->setVerticalSpacing(q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 2);
m_ui->gridLayout->setHorizontalSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
// Deprecated, we'll call show() if building with deprecated code
m_ui->sizeIsRelativeCheckBox->hide();
const bool isDiffMode = m_flags & KFontChooser::ShowDifferences;
QObject::connect(m_ui->familyListWidget, &QListWidget::currentTextChanged, [this](const QString &family) {
slotFamilySelected(family);
});
if (isDiffMode) {
m_ui->familyLabel->hide();
m_ui->familyListWidget->setEnabled(false);
QObject::connect(m_ui->familyCheckBox, &QCheckBox::toggled, m_ui->familyListWidget, &QWidget::setEnabled);
} else {
m_ui->familyCheckBox->hide();
}
setFamilyBoxItems();
// If the calling app sets FixedFontsOnly, don't show the "show fixed only" checkbox
m_ui->onlyFixedCheckBox->setVisible(!m_usingFixed);
if (!m_ui->onlyFixedCheckBox->isHidden()) {
QObject::connect(m_ui->onlyFixedCheckBox, &QCheckBox::toggled, q, [this](const bool state) {
q->setFont(m_selectedFont, state);
});
if (isDiffMode) { // In this mode follow the state of the m_ui->familyCheckBox
m_ui->onlyFixedCheckBox->setEnabled(false);
QObject::connect(m_ui->familyCheckBox, &QCheckBox::toggled, m_ui->onlyFixedCheckBox, &QWidget::setEnabled);
}
}
// Populate usual styles, to determine minimum list width;
// will be replaced later with correct styles.
m_ui->styleListWidget->addItem(KFontChooser::tr("Normal", "@item font"));
m_ui->styleListWidget->addItem(KFontChooser::tr("Italic", "@item font"));
m_ui->styleListWidget->addItem(KFontChooser::tr("Oblique", "@item font"));
m_ui->styleListWidget->addItem(KFontChooser::tr("Bold", "@item font"));
m_ui->styleListWidget->addItem(KFontChooser::tr("Bold Condensed Oblique", "@item font"));
m_ui->styleListWidget->setMinimumWidth(minimumListWidth(m_ui->styleListWidget));
QObject::connect(m_ui->styleListWidget, &QListWidget::currentTextChanged, [this](const QString &style) {
slotStyleSelected(style);
});
if (isDiffMode) {
m_ui->styleLabel->hide();
m_ui->styleListWidget->setEnabled(false);
QObject::connect(m_ui->styleCheckBox, &QCheckBox::toggled, m_ui->styleListWidget, &QWidget::setEnabled);
} else {
m_ui->styleCheckBox->hide();
}
// Populate with usual sizes, to determine minimum list width;
// will be replaced later with correct sizes.
fillSizeList();
QObject::connect(m_ui->sizeSpinBox, &QDoubleSpinBox::valueChanged, [this](const double size) {
slotSizeValue(size);
});
QObject::connect(m_ui->sizeListWidget, &QListWidget::currentTextChanged, [this](const QString &size) {
slotSizeSelected(size);
});
m_fontFeatureChangedTimer.setInterval(200);
m_fontFeatureChangedTimer.setSingleShot(true);
m_fontFeatureChangedTimer.callOnTimeout([this]() {
slotFeaturesChanged(m_ui->fontFeaturesLineEdit->text());
});
QObject::connect(m_ui->fontFeaturesLineEdit, &QLineEdit::textChanged, [this](const QString &) {
m_fontFeatureChangedTimer.start();
});
if (isDiffMode) {
m_ui->sizeLabel->hide();
m_ui->sizeListWidget->setEnabled(false);
m_ui->sizeSpinBox->setEnabled(false);
QObject::connect(m_ui->sizeCheckBox, &QCheckBox::toggled, m_ui->sizeListWidget, &QWidget::setEnabled);
QObject::connect(m_ui->sizeCheckBox, &QCheckBox::toggled, m_ui->sizeSpinBox, &QWidget::setEnabled);
} else {
m_ui->sizeCheckBox->hide();
}
QFont tmpFont(q->font().family(), 64, QFont::Black);
m_ui->sampleTextEdit->setFont(tmpFont);
m_ui->sampleTextEdit->setMinimumHeight(m_ui->sampleTextEdit->fontMetrics().lineSpacing());
// tr: A classical test phrase, with all letters of the English alphabet.
// Replace it with a sample text in your language, such that it is
// representative of language's writing system.
// If you wish, you can input several lines of text separated by \n.
q->setSampleText(KFontChooser::tr("The Quick Brown Fox Jumps Over The Lazy Dog"));
m_ui->sampleTextEdit->setTextCursor(QTextCursor(m_ui->sampleTextEdit->document()));
QObject::connect(q, &KFontChooser::fontSelected, q, [this](const QFont &font) {
displaySample(font);
});
// lets initialize the display if possible
if (m_usingFixed) {
q->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont), true);
} else {
q->setFont(QGuiApplication::font(), false);
}
// Set the minimum height for the list widgets
q->setMinVisibleItems(4);
// Set focus to the size list as this is the most commonly changed property
m_ui->sizeListWidget->setFocus();
}
void KFontChooser::setColor(const QColor &col)
{
d->m_palette.setColor(QPalette::Active, QPalette::Text, col);
QPalette pal = d->m_ui->sampleTextEdit->palette();
pal.setColor(QPalette::Active, QPalette::Text, col);
d->m_ui->sampleTextEdit->setPalette(pal);
QTextCursor cursor = d->m_ui->sampleTextEdit->textCursor();
d->m_ui->sampleTextEdit->selectAll();
d->m_ui->sampleTextEdit->setTextColor(col);
d->m_ui->sampleTextEdit->setTextCursor(cursor);
}
QColor KFontChooser::color() const
{
return d->m_palette.color(QPalette::Active, QPalette::Text);
}
void KFontChooser::setBackgroundColor(const QColor &col)
{
d->m_palette.setColor(QPalette::Active, QPalette::Base, col);
QPalette pal = d->m_ui->sampleTextEdit->palette();
pal.setColor(QPalette::Active, QPalette::Base, col);
d->m_ui->sampleTextEdit->setPalette(pal);
}
QColor KFontChooser::backgroundColor() const
{
return d->m_palette.color(QPalette::Active, QPalette::Base);
}
QString KFontChooser::sampleText() const
{
return d->m_ui->sampleTextEdit->toPlainText();
}
void KFontChooser::setSampleText(const QString &text)
{
d->m_ui->sampleTextEdit->setPlainText(text);
}
void KFontChooser::setSampleBoxVisible(bool visible)
{
d->m_ui->sampleTextEdit->setVisible(visible);
}
QSize KFontChooser::sizeHint(void) const
{
return minimumSizeHint();
}
void KFontChooser::enableColumn(int column, bool state)
{
if (column & FamilyList) {
d->m_ui->familyListWidget->setEnabled(state);
}
if (column & StyleList) {
d->m_ui->styleListWidget->setEnabled(state);
}
if (column & SizeList) {
d->m_ui->sizeListWidget->setEnabled(state);
d->m_ui->sizeSpinBox->setEnabled(state);
}
}
void KFontChooser::setFont(const QFont &aFont, bool onlyFixed)
{
d->m_selectedFont = aFont;
d->m_selectedSize = aFont.pointSizeF();
if (d->m_selectedSize == -1) {
d->m_selectedSize = QFontInfo(aFont).pointSizeF();
}
if (onlyFixed != d->m_usingFixed) {
d->m_usingFixed = onlyFixed;
d->setFamilyBoxItems();
}
d->setupDisplay();
}
KFontChooser::FontDiffFlags KFontChooser::fontDiffFlags() const
{
FontDiffFlags diffFlags = NoFontDiffFlags;
if (d->m_ui->familyCheckBox->isChecked()) {
diffFlags |= FontDiffFamily;
}
if (d->m_ui->styleCheckBox->isChecked()) {
diffFlags |= FontDiffStyle;
}
if (d->m_ui->sizeCheckBox->isChecked()) {
diffFlags |= FontDiffSize;
}
return diffFlags;
}
QFont KFontChooser::font() const
{
return d->m_selectedFont;
}
static bool isDefaultFontStyleName(const QString &style)
{
/* clang-format off */
// Ordered by commonness, i.e. "Regular" is the most common
return style == QLatin1String("Regular")
|| style == QLatin1String("Normal")
|| style == QLatin1String("Book")
|| style == QLatin1String("Roman");
/* clang-format on */
}
void KFontChooserPrivate::slotFamilySelected(const QString &family)
{
if (!m_signalsAllowed) {
return;
}
m_signalsAllowed = false;
QString currentFamily;
if (family.isEmpty()) {
Q_ASSERT(m_ui->familyListWidget->currentItem());
if (m_ui->familyListWidget->currentItem()) {
currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
}
} else {
currentFamily = m_qtFamilies[family];
}
// Get the list of styles available in this family.
QStringList styles = QFontDatabase::styles(currentFamily);
if (styles.isEmpty()) {
// Avoid extraction, it is in kdeqt.po
styles.append(TR_NOX("Normal", "QFontDatabase"));
}
// Always prepend Regular, Normal, Book or Roman, this way if "m_selectedStyle"
// in the code below is empty, selecting index 0 should work better
std::sort(styles.begin(), styles.end(), [](const QString &a, const QString &b) {
if (isDefaultFontStyleName(a)) {
return true;
} else if (isDefaultFontStyleName(b)) {
return false;
}
return false;
});
// Filter style strings and add to the listbox.
QString pureFamily;
splitFontString(family, &pureFamily);
QStringList filteredStyles;
m_qtStyles.clear();
m_styleIDs.clear();
const QStringList origStyles = styles;
for (const QString &style : origStyles) {
// Sometimes the font database will report an invalid style,
// that falls back back to another when set.
// Remove such styles, by checking set/get round-trip.
QFont testFont = QFontDatabase::font(currentFamily, style, 10);
if (QFontDatabase::styleString(testFont) != style) {
styles.removeAll(style);
continue;
}
QString fstyle = tr("%1", "@item Font style").arg(style);
if (!filteredStyles.contains(fstyle)) {
filteredStyles.append(fstyle);
m_qtStyles.insert({fstyle, style});
m_styleIDs.insert({fstyle, styleIdentifier(testFont)});
}
}
m_ui->styleListWidget->clear();
m_ui->styleListWidget->addItems(filteredStyles);
// Try to set the current style in the listbox to that previous.
int listPos = filteredStyles.indexOf(m_selectedStyle.isEmpty() ? TR_NOX("Normal", "QFontDatabase") : m_selectedStyle);
if (listPos < 0) {
// Make extra effort to have Italic selected when Oblique was chosen,
// and vice versa, as that is what the user would probably want.
QString styleIt = tr("Italic", "@item font");
QString styleOb = tr("Oblique", "@item font");
for (int i = 0; i < 2; ++i) {
int pos = m_selectedStyle.indexOf(styleIt);
if (pos >= 0) {
QString style = m_selectedStyle;
style.replace(pos, styleIt.length(), styleOb);
listPos = filteredStyles.indexOf(style);
if (listPos >= 0) {
break;
}
}
qSwap(styleIt, styleOb);
}
}
m_ui->styleListWidget->setCurrentRow(listPos >= 0 ? listPos : 0);
const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
// Recompute the size listbox for this family/style.
qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
m_ui->sizeSpinBox->setValue(currentSize);
m_selectedFont = QFontDatabase::font(currentFamily, currentStyle, static_cast<int>(currentSize));
if (QFontDatabase::isSmoothlyScalable(currentFamily, currentStyle) && m_selectedFont.pointSize() == floor(currentSize)) {
m_selectedFont.setPointSizeF(currentSize);
}
Q_EMIT q->fontSelected(m_selectedFont);
m_signalsAllowed = true;
}
void KFontChooserPrivate::slotStyleSelected(const QString &style)
{
if (!m_signalsAllowed) {
return;
}
m_signalsAllowed = false;
const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
const QString currentStyle = !style.isEmpty() ? m_qtStyles[style] : m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
// Recompute the size listbox for this family/style.
qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
m_ui->sizeSpinBox->setValue(currentSize);
m_selectedFont = QFontDatabase::font(currentFamily, currentStyle, static_cast<int>(currentSize));
if (QFontDatabase::isSmoothlyScalable(currentFamily, currentStyle) && m_selectedFont.pointSize() == floor(currentSize)) {
m_selectedFont.setPointSizeF(currentSize);
}
Q_EMIT q->fontSelected(m_selectedFont);
if (!style.isEmpty()) {
m_selectedStyle = currentStyle;
}
m_signalsAllowed = true;
}
void KFontChooserPrivate::slotSizeSelected(const QString &size)
{
if (!m_signalsAllowed) {
return;
}
m_signalsAllowed = false;
qreal currentSize = 0.0;
if (size.isEmpty()) {
currentSize = QLocale::system().toDouble(m_ui->sizeListWidget->currentItem()->text());
} else {
currentSize = QLocale::system().toDouble(size);
}
// Reset the customized size slot in the list if not needed.
if (m_customSizeRow >= 0 && m_selectedFont.pointSizeF() != currentSize) {
m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
m_customSizeRow = -1;
}
m_ui->sizeSpinBox->setValue(currentSize);
m_selectedFont.setPointSizeF(currentSize);
Q_EMIT q->fontSelected(m_selectedFont);
if (!size.isEmpty()) {
m_selectedSize = currentSize;
}
m_signalsAllowed = true;
}
void KFontChooserPrivate::slotSizeValue(double dval)
{
if (!m_signalsAllowed) {
return;
}
m_signalsAllowed = false;
// We compare with qreal, so convert for platforms where qreal != double.
qreal val = qreal(dval);
// Reset current size slot in list if it was customized.
if (m_customSizeRow >= 0 && m_ui->sizeListWidget->currentRow() == m_customSizeRow) {
m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
m_customSizeRow = -1;
}
bool canCustomize = true;
const QString family = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
const QString style = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
// For Qt-bad-sizes workaround: skip this block unconditionally
if (!QFontDatabase::isSmoothlyScalable(family, style)) {
// Bitmap font, allow only discrete sizes.
// Determine the nearest in the direction of change.
canCustomize = false;
int nrows = m_ui->sizeListWidget->count();
int row = m_ui->sizeListWidget->currentRow();
int nrow;
if (val - m_selectedFont.pointSizeF() > 0) {
for (nrow = row + 1; nrow < nrows; ++nrow) {
if (QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) >= val) {
break;
}
}
} else {
for (nrow = row - 1; nrow >= 0; --nrow) {
if (QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) <= val) {
break;
}
}
}
// Make sure the new row is not out of bounds.
nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow;
// Get the size from the new row and set the spinbox to that size.
val = QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text());
m_ui->sizeSpinBox->setValue(val);
}
// Set the current size in the size listbox.
int row = nearestSizeRow(val, canCustomize);
m_ui->sizeListWidget->setCurrentRow(row);
m_selectedSize = val;
m_selectedFont.setPointSizeF(val);
Q_EMIT q->fontSelected(m_selectedFont);
m_signalsAllowed = true;
}
void KFontChooserPrivate::slotFeaturesChanged(const QString &features)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
m_selectedFont.clearFeatures();
if (features.isEmpty()) {
return;
}
const QStringList rawFeaturesList = features.split(QLatin1Char(','), Qt::SkipEmptyParts);
for (const QString &feature : rawFeaturesList) {
auto f = QStringView(feature).trimmed();
if (f.isEmpty()) {
continue;
}
QList<QStringView> parts = f.split(QStringLiteral("="), Qt::SkipEmptyParts);
if (parts.length() == 2) {
const auto tag = QFont::Tag::fromString(parts[0]);
bool ok = false;
const int number = parts[1].toInt(&ok);
if (tag.has_value() && ok) {
m_selectedFont.setFeature(tag.value(), number);
}
} else if (f.size() <= 4) {
const auto tag = QFont::Tag::fromString(feature);
if (tag.has_value()) {
m_selectedFont.setFeature(tag.value(), 1);
}
}
}
Q_EMIT q->fontSelected(m_selectedFont);
#endif
}
void KFontChooserPrivate::displaySample(const QFont &font)
{
m_ui->sampleTextEdit->setFont(font);
}
int KFontChooserPrivate::nearestSizeRow(qreal val, bool customize)
{
qreal diff = 1000;
int row = 0;
for (int r = 0; r < m_ui->sizeListWidget->count(); ++r) {
qreal cval = QLocale::system().toDouble(m_ui->sizeListWidget->item(r)->text());
if (qAbs(cval - val) < diff) {
diff = qAbs(cval - val);
row = r;
}
}
// For Qt-bad-sizes workaround: ignore value of customize, use true
if (customize && diff > 0) {
m_customSizeRow = row;
m_standardSizeAtCustom = m_ui->sizeListWidget->item(row)->text();
m_ui->sizeListWidget->item(row)->setText(formatFontSize(val));
}
return row;
}
qreal KFontChooserPrivate::fillSizeList(const QList<qreal> &sizes_)
{
if (m_ui->sizeListWidget->isHidden()) {
qCWarning(KWidgetsAddonsLog) << "Trying to fill the font size listwidget, but the widget is hidden.";
return 0.0;
}
QList<qreal> sizes = sizes_;
bool canCustomize = false;
if (sizes.isEmpty()) {
static const int c[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 32, 48, 64, 72, 80, 96, 128, 0};
for (int i = 0; c[i]; ++i) {
sizes.append(c[i]);
}
// Since sizes were not supplied, this is a vector font,
// and size slot customization is allowed.
canCustomize = true;
}
// Insert sizes into the listbox.
m_ui->sizeListWidget->clear();
std::sort(sizes.begin(), sizes.end());
for (qreal size : std::as_const(sizes)) {
m_ui->sizeListWidget->addItem(formatFontSize(size));
}
// Return the nearest to selected size.
// If the font is vector, the nearest size is always same as selected,
// thus size slot customization is allowed.
// If the font is bitmap, the nearest size need not be same as selected,
// thus size slot customization is not allowed.
m_customSizeRow = -1;
int row = nearestSizeRow(m_selectedSize, canCustomize);
return QLocale::system().toDouble(m_ui->sizeListWidget->item(row)->text());
}
qreal KFontChooserPrivate::setupSizeListBox(const QString &family, const QString &style)
{
QList<qreal> sizes;
const bool smoothlyScalable = QFontDatabase::isSmoothlyScalable(family, style);
if (!smoothlyScalable) {
const QList<int> smoothSizes = QFontDatabase::smoothSizes(family, style);
for (int size : smoothSizes) {
sizes.append(size);
}
}
// Fill the listbox (uses default list of sizes if the given is empty).
// Collect the best fitting size to selected size, to use if not smooth.
qreal bestFitSize = fillSizeList(sizes);
// Set the best fit size as current in the listbox if available.
const QList<QListWidgetItem *> selectedSizeList = m_ui->sizeListWidget->findItems(formatFontSize(bestFitSize), Qt::MatchExactly);
if (!selectedSizeList.isEmpty()) {
m_ui->sizeListWidget->setCurrentItem(selectedSizeList.first());
}
return bestFitSize;
}
void KFontChooserPrivate::setupDisplay()
{
qreal size = m_selectedFont.pointSizeF();
if (size == -1) {
size = QFontInfo(m_selectedFont).pointSizeF();
}
// Set font features
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
const auto tags = m_selectedFont.featureTags();
QStringList features;
for (const auto &tag : tags) {
const QString name = QString::fromUtf8(tag.toString());
const quint32 value = m_selectedFont.featureValue(tag);
if (value == 1) {
features.push_back(name);
} else {
features.push_back(QStringLiteral("%1=%2").arg(name, QString::number(value)));
}
}
m_ui->fontFeaturesLineEdit->setText(features.join(QStringLiteral(",")));
#endif
int numEntries;
int i;
// Get the styleID here before familyListWidget->setCurrentRow() is called
// as it may change the font style
const QString styleID = styleIdentifier(m_selectedFont);
QString family = m_selectedFont.family().toLower();
// Direct family match.
numEntries = m_ui->familyListWidget->count();
for (i = 0; i < numEntries; ++i) {
if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
m_ui->familyListWidget->setCurrentRow(i);
break;
}
}
// 1st family fallback.
if (i == numEntries) {
const int bracketPos = family.indexOf(QLatin1Char('['));
if (bracketPos != -1) {
family = QStringView(family).left(bracketPos).trimmed().toString();
for (i = 0; i < numEntries; ++i) {
if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
m_ui->familyListWidget->setCurrentRow(i);
break;
}
}
}
}
// 2nd family fallback.
if (i == numEntries) {
QString fallback = family + QLatin1String(" [");
for (i = 0; i < numEntries; ++i) {
if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(fallback)) {
m_ui->familyListWidget->setCurrentRow(i);
break;
}
}
}
// 3rd family fallback.
if (i == numEntries) {
for (i = 0; i < numEntries; ++i) {
if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(family)) {
m_ui->familyListWidget->setCurrentRow(i);
break;
}
}
}
// Family fallback in case nothing matched. Otherwise, diff doesn't work
if (i == numEntries) {
m_ui->familyListWidget->setCurrentRow(0);
}
// By setting the current item in the family box, the available
// styles and sizes for that family have been collected.
// Try now to set the current items in the style and size boxes.
// Set current style in the listbox.
numEntries = m_ui->styleListWidget->count();
for (i = 0; i < numEntries; ++i) {
if (styleID == m_styleIDs[m_ui->styleListWidget->item(i)->text()]) {
m_ui->styleListWidget->setCurrentRow(i);
break;
}
}
if (i == numEntries) {
// Style not found, fallback.
m_ui->styleListWidget->setCurrentRow(0);
}
// Set current size in the listbox.
// If smoothly scalable, allow customizing one of the standard size slots,
// otherwise just select the nearest available size.
const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
const bool canCustomize = QFontDatabase::isSmoothlyScalable(currentFamily, currentStyle);
m_ui->sizeListWidget->setCurrentRow(nearestSizeRow(size, canCustomize));
// Set current size in the spinbox.
m_ui->sizeSpinBox->setValue(QLocale::system().toDouble(m_ui->sizeListWidget->currentItem()->text()));
}
// static
QStringList KFontChooser::createFontList(uint fontListCriteria)
{
QStringList lstSys(QFontDatabase::families());
// if we have criteria; then check fonts before adding
if (fontListCriteria) {
QStringList lstFonts;
for (const QString &family : std::as_const(lstSys)) {
if ((fontListCriteria & FixedWidthFonts) > 0 && !QFontDatabase::isFixedPitch(family)) {
continue;
}
if (((fontListCriteria & (SmoothScalableFonts | ScalableFonts)) == ScalableFonts) && !QFontDatabase::isBitmapScalable(family)) {
continue;
}
if ((fontListCriteria & SmoothScalableFonts) > 0 && !QFontDatabase::isSmoothlyScalable(family)) {
continue;
}
lstFonts.append(family);
}
if ((fontListCriteria & FixedWidthFonts) > 0) {
// Fallback.. if there are no fixed fonts found, it's probably a
// bug in the font server or Qt. In this case, just use 'fixed'
if (lstFonts.isEmpty()) {
lstFonts.append(QStringLiteral("fixed"));
}
}
lstSys = lstFonts;
}
lstSys.sort();
return lstSys;
}
void KFontChooser::setFontListItems(const QStringList &fontList)
{
d->setFamilyBoxItems(fontList);
}
void KFontChooserPrivate::setFamilyBoxItems(const QStringList &fonts)
{
m_signalsAllowed = false;
m_ui->familyListWidget->clear();
m_qtFamilies = translateFontNameList(!fonts.isEmpty() ? fonts : KFontChooser::createFontList(m_usingFixed ? KFontChooser::FixedWidthFonts : 0));
QStringList list;
list.reserve(m_qtFamilies.size());
// Generic font names
const QStringList genericTranslatedNames{
translateFontName(QStringLiteral("Sans Serif")),
translateFontName(QStringLiteral("Serif")),
translateFontName(QStringLiteral("Monospace")),
};
// Add generic family names to the top of the list
for (const QString &s : genericTranslatedNames) {
auto nIt = m_qtFamilies.find(s);
if (nIt != m_qtFamilies.cend()) {
list.push_back(s);
}
}
for (auto it = m_qtFamilies.cbegin(); it != m_qtFamilies.cend(); ++it) {
const QString &name = it->first;
if (genericTranslatedNames.contains(name)) {
continue;
}
list.push_back(name);
}
m_ui->familyListWidget->addItems(list);
m_ui->familyListWidget->setMinimumWidth(minimumListWidth(m_ui->familyListWidget));
m_signalsAllowed = true;
}
void KFontChooser::setMinVisibleItems(int visibleItems)
{
for (auto *widget : {d->m_ui->familyListWidget, d->m_ui->styleListWidget, d->m_ui->sizeListWidget}) {
widget->setMinimumHeight(minimumListHeight(widget, visibleItems));
}
}
// Human-readable style identifiers returned by QFontDatabase::styleString()
// do not always survive round trip of QFont serialization/deserialization,
// causing wrong style in the style box to be highlighted when
// the chooser dialog is opened. This will cause the style to be changed
// when the dialog is closed and the user did not touch the style box.
// Hence, construct custom style identifiers sufficient for the purpose.
QString KFontChooserPrivate::styleIdentifier(const QFont &font)
{
const int weight = font.weight();
QString styleName = font.styleName();
// If the styleName property is empty and the weight is QFont::Normal, that
// could mean it's a "Regular"-like style with the styleName part stripped
// so that subsequent calls to setBold(true) can work properly (i.e. selecting
// the "Bold" style provided by the font itself) without resorting to font
// "emboldening" which looks ugly.
// See also KConfigGroupGui::writeEntryGui().
if (styleName.isEmpty() && weight == QFont::Normal) {
const QStringList styles = QFontDatabase::styles(font.family());
for (const QString &style : styles) {
if (isDefaultFontStyleName(style)) {
styleName = style;
break;
} else {
// nothing more we can do
}
}
}
const QChar comma(QLatin1Char(','));
return QString::number(weight) + comma //
+ QString::number((int)font.style()) + comma //
+ QString::number(font.stretch()) + comma //
+ styleName;
}
#include "moc_kfontchooser.cpp"
@@ -0,0 +1,256 @@
/*
SPDX-FileCopyrightText: 1997 Bernd Johannes Wuebben <wuebben@kde.org>
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef K_FONT_CHOOSER_H
#define K_FONT_CHOOSER_H
#include <QStringList>
#include <QWidget>
#include <kwidgetsaddons_export.h>
#include <memory>
class QFont;
/**
* @class KFontChooser kfontchooser.h KFontChooser
*
* @short A font selection widget.
*
* While KFontChooser as an ordinary widget can be embedded in
* custom dialogs and therefore is very flexible, in most cases
* it is preferable to use the convenience functions in
* QFontDialog.
*
* \image html kfontchooser.png "KFontChooser Widget"
*
* @see KFontRequester
*
* @author Preston Brown <pbrown@kde.org>, Bernd Wuebben <wuebben@kde.org>
*/
class KWIDGETSADDONS_EXPORT KFontChooser : public QWidget
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontSelected USER true)
Q_PROPERTY(QColor color READ color WRITE setColor)
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
Q_PROPERTY(QString sampleText READ sampleText WRITE setSampleText)
public:
/**
* Displayed columns.
*/
enum FontColumn {
FamilyList = 0x01, ///< Identifies the family (leftmost) list.
StyleList = 0x02, ///< Identifies the style (center) list.
SizeList = 0x04, ///< Identifies the size (rightmost) list.
};
/**
* Flags for selecting which font attributes to change
* @see FontDiffFlags
*/
enum FontDiff {
NoFontDiffFlags = 0, ///< No flags set
FontDiffFamily = 1, ///< Identifies a requested change in the font family.
FontDiffStyle = 2, ///< Identifies a requested change in the font style.
FontDiffSize = 4, ///< Identifies a requested change in the font size.
AllFontDiffs = FontDiffFamily | FontDiffStyle | FontDiffSize,
};
/**
* Stores an combination of #FontDiff values.
*/
Q_DECLARE_FLAGS(FontDiffFlags, FontDiff)
/**
* Flags for selecting what is displayed in the widget.
* @see DisplayFlags
*/
enum DisplayFlag {
NoDisplayFlags = 0, ///< No flags set
FixedFontsOnly = 1, ///< Only show monospaced/fixed-width fonts, excluding proportional fonts, (the
///< checkbox to toggle showing only monospaced fonts is not shown in this case)
DisplayFrame = 2, ///< Show a visual frame around the chooser
ShowDifferences = 4, ///< Display the font differences interfaces
};
/**
* Stores a combination of #DisplayFlag values.
*/
Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
/**
* Constructs a font picker widget.
*
* @param parent the parent widget
*
* @since 5.86
*/
explicit KFontChooser(QWidget *parent = nullptr);
/**
* Create a font picker widget.
*
* @param flags a combination of OR-ed values from the @c KFontChooser::DisplayFlags enum,
* the default is @c DisplayFonts::NoDisplayFlags
* @param parent the parent widget, if not nullptr the windowing system will use it to position
* the chooser widget relative to it
*
* @since 5.86
*/
explicit KFontChooser(DisplayFlags flags, QWidget *parent = nullptr);
/**
* Destructor.
*/
~KFontChooser() override;
/**
* Enables or disables a column (family, style, size) in the widget.
*
* Use this function if your application does not need or support all font properties.
*
* @param column specify the column(s) to enable/disable, an OR-ed combination of
* @c KFontChooser::FontColumn enum values
* @param state if @p false the columns are disabled, and vice-versa
*/
void enableColumn(int column, bool state);
/**
* Sets the currently selected font in the widget.
*
* @param font the font to select
* @param onlyFixed if @c true, the font list will only display fixed-width fonts,
* otherwise all fonts are displayed. The default is @c false.
*/
void setFont(const QFont &font, bool onlyFixed = false);
/**
* Returns the bitmask corresponding to the attributes the user wishes to change.
*/
FontDiffFlags fontDiffFlags() const;
/**
* Returns the currently selected font in the chooser.
*/
QFont font() const;
/**
* Sets the color to use for the font in the preview area.
*/
void setColor(const QColor &col);
/**
* Returns the color currently used for the font in the preview
* area (default: the text color of the active color group).
*/
QColor color() const;
/**
* Sets the background color to use in the preview area.
*/
void setBackgroundColor(const QColor &col);
/**
* Returns the background color currently used in the preview area
* (default: the base color of the active colorgroup)
*/
QColor backgroundColor() const;
/**
* @return The current text in the sample text input area.
*/
QString sampleText() const;
/**
* Sets the sample text in the preview area; this is useful if you
* want to use text in your native language.
*
* @param text the new sample text (it will replace the current text)
*/
void setSampleText(const QString &text);
/**
* If @p visible is @c true the preview area will be shown, and vice-versa
* is it's @c false.
*/
void setSampleBoxVisible(bool visible);
/**
* The selection criteria for the font families shown in the dialog.
*/
enum FontListCriteria {
/**
* If set, only show fixed fixed-width (monospace) fonts.
*/
FixedWidthFonts = 0x01,
/**
* If set, only show scalable fonts.
* Certain configurations allow bitmap fonts to remain unscaled
* and thus these fonts have limited number of sizes.
*/
ScalableFonts = 0x02,
/**
* If set, only show smooth scalable fonts.
* This will return only non-bitmap fonts which are scalable to any size requested.
* Setting this option means the @c ScalableFonts flag is ignored.
*/
SmoothScalableFonts = 0x04
};
/**
* Returns a list of font faimly name strings filtered based on @p fontListCriteria.
*
* @param fontListCriteria specifies the criteria used to select fonts to add to
* the list, a combination of OR-ed values from @ref KFontChooser::FontListCriteria
*
* @since 5.86
*/
static QStringList createFontList(uint fontListCriteria);
/**
* Uses @p fontList to fill the font family list in the widget.
*
* You can create a custom list of fonts using the static @c createFontList(uint
* criteria) to only include fonts that meet certain criteria (e.g. only
* smooth-scalable fonts).
*
* @see KFontChooser::createFontList(uint), KFontChooser::FontListCriteria
*
* Note that if @p fontList is empty, the font list in the chooser will show
* all the available fonts on the system.
* @since 5.86
*/
void setFontListItems(const QStringList &fontList);
/**
* Sets the minimum number of items that should be visible in the
* child list widgets; this number will be used to compute and set
* the minimum heights for those widgets.
*
* @since 5.86
*/
void setMinVisibleItems(int visibleItems);
/**
* Reimplemented for internal reasons.
*/
QSize sizeHint(void) const override;
Q_SIGNALS:
/**
* Emitted when the selected font changes.
*/
void fontSelected(const QFont &font);
private:
std::unique_ptr<class KFontChooserPrivate> const d;
Q_DISABLE_COPY(KFontChooser)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KFontChooser::DisplayFlags)
#endif
@@ -0,0 +1,105 @@
/*
SPDX-FileCopyrightText: 1996 Bernd Johannes Wuebben <wuebben@kde.org>
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kfontchooserdialog.h"
#include <QDialogButtonBox>
#include <QPointer>
#include <QVBoxLayout>
class KFontChooserDialogPrivate
{
public:
KFontChooser *m_fontChooser = nullptr;
};
KFontChooserDialog::KFontChooserDialog(const KFontChooser::DisplayFlags &flags, QWidget *parent)
: QDialog(parent)
, d(new KFontChooserDialogPrivate)
{
setWindowTitle(tr("Select Font", "@title:window"));
d->m_fontChooser = new KFontChooser(flags, this);
d->m_fontChooser->setMinVisibleItems(8);
d->m_fontChooser->setObjectName(QStringLiteral("fontChooser"));
connect(d->m_fontChooser, &KFontChooser::fontSelected, this, &KFontChooserDialog::fontSelected);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(d->m_fontChooser);
mainLayout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
KFontChooserDialog::~KFontChooserDialog() = default;
void KFontChooserDialog::setFont(const QFont &font, bool onlyFixed)
{
d->m_fontChooser->setFont(font, onlyFixed);
}
QFont KFontChooserDialog::font() const
{
return d->m_fontChooser->font();
}
// If the styleName property is set for a QFont, using setBold(true) would
// lead to Qt using an "emboldended"/synthetic font style instead of using
// the bold style provided by the font itself; the latter looks better than
// the former, also accorgin to upstream, the styleName property is useful
// for fancy font styles, so there is no point in setting it for "Regular"
// fonts. For more details see:
// https://bugreports.qt.io/browse/QTBUG-63792
// https://bugs.kde.org/show_bug.cgi?id=378523
static void stripRegularStyleName(QFont &font)
{
if (font.weight() == QFont::Normal //
&& (font.styleName() == QLatin1String("Regular") //
|| font.styleName() == QLatin1String("Normal") //
|| font.styleName() == QLatin1String("Book") //
|| font.styleName() == QLatin1String("Roman"))) {
font.setStyleName(QString());
}
}
// static
int KFontChooserDialog::getFontDiff(QFont &theFont, KFontChooser::FontDiffFlags &diffFlags, const KFontChooser::DisplayFlags &flags, QWidget *parent)
{
QPointer<KFontChooserDialog> dialog = new KFontChooserDialog(flags | KFontChooser::ShowDifferences, parent);
dialog->setObjectName(QStringLiteral("Font Selector"));
dialog->setFont(theFont, flags & KFontChooser::FixedFontsOnly);
const int result = dialog->exec();
if (result == Accepted) {
theFont = dialog->d->m_fontChooser->font();
diffFlags = dialog->d->m_fontChooser->fontDiffFlags();
stripRegularStyleName(theFont);
}
delete dialog;
return result;
}
// static
int KFontChooserDialog::getFont(QFont &theFont, const KFontChooser::DisplayFlags &flags, QWidget *parent)
{
QPointer<KFontChooserDialog> dialog = new KFontChooserDialog(flags, parent);
dialog->setObjectName(QStringLiteral("Font Selector"));
dialog->setFont(theFont, flags & KFontChooser::FixedFontsOnly);
const int result = dialog->exec();
if (result == Accepted) {
theFont = dialog->d->m_fontChooser->font();
stripRegularStyleName(theFont);
}
delete dialog;
return result;
}
#include "moc_kfontchooserdialog.cpp"
@@ -0,0 +1,163 @@
/*
SPDX-FileCopyrightText: 1997 Bernd Johannes Wuebben <wuebben@kde.org>
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef K_FONT_CHOOSER_DIALOG_H
#define K_FONT_CHOOSER_DIALOG_H
#include <QDialog>
#include <QStringList>
#include <kfontchooser.h>
#include <memory>
class QFont;
class KFontChooserDialogPrivate;
/**
* @class KFontChooserDialog kfontchooserdialog.h KFontChooserDialog
*
* @short A font selection dialog.
*
* The KFontChooserDialog provides a dialog for interactive font selection.
* It is basically a thin wrapper around the KFontChooser widget (the latter
* can also be used standalone). In most cases, the simplest use of this
* class is the static method KFontChooserDialog::getFont(), which shows
* the dialog, allows the user to select a font, and returns when the
* dialog is closed.
*
* Features offered by KFontChooserDialog/KFontChooser:
* - The ability to set decimal font sizes (e.g. "12.1")
* - When selecting an initial font, if the styleName property of that font
* isn't set, the dialog will try and select the correct font style from the
* styles list
* - The ability to set multiple fonts at once, for an example of this functionality
* see "Adjust All Fonts" in the Fonts KCM in Systemsettings; and you can change
* the font family, style or size separately
* - Discarding the styleName property when closing the dialog for "Regular" font
* styles, since it doesn't make sense to set that property for such fonts and
* this allows setBold(true) to work correctly for more details see:
* https://bugreports.qt.io/browse/QTBUG-63792
* https://bugs.kde.org/show_bug.cgi?id=378523
*
* Example, using the static getFont() method:
*
* \code
* QFont myFont;
* int result = KFontChooserDialog::getFont(myFont);
* if (result == QDialog::Accepted) {
* ...
* }
* \endcode
*
* Another example, this time showing the dialog with show() (or open()):
* \code
* KFontChooserDialog *fontDlg = new KFontChooserDialog(KFontChooser::NoDisplayFlags, this);
* // Delete the dialog when it's closed
* fontDlg->setAttribute(Qt::WA_DeleteOnClose);
*
* connect(fontDlg, &QDialog::accepted, this, [fontDlg]() {
* // Get the selected font via fontDlg->font() and apply it, save it to
* // the settings... etc.
* });
*
* fontDlg->show();
* \endcode
*
* \image html kfontchooserdialog.png "KFontChooserDialog"
*
* @author Preston Brown <pbrown@kde.org>, Bernd Wuebben <wuebben@kde.org>
*
* @since 5.69
*/
class KWIDGETSADDONS_EXPORT KFontChooserDialog : public QDialog
{
Q_OBJECT
public:
/**
* Constructs a font selection dialog.
*
* @param flags flags to define how the font chooser is displayed
* @param parent parent widget of the dialog, if any, the dialog will be centered relative to it
*/
explicit KFontChooserDialog(const KFontChooser::DisplayFlags &flags = KFontChooser::NoDisplayFlags, QWidget *parent = nullptr);
~KFontChooserDialog() override;
/**
* Sets the currently selected font in the dialog.
*
* @param font the font to select
* @param onlyFixed if @c true, the font list will show only fixed width (monospace)
* fonts, otherwise all available fonts are shown
*/
void setFont(const QFont &font, bool onlyFixed = false);
/**
* @return the currently selected font in the dialog
*/
QFont font() const;
/**
* Creates a modal font dialog, lets the user choose a font, and returns when
* the dialog is closed.
*
* @param theFont a reference to the font to write the chosen font into
* @param flags flags to define how the font chooser is displayed
* @param parent parent widget of the dialog, if any, the dialog will be centered relative to it
* @return QDialog::result()
*/
static int getFont(QFont &theFont, const KFontChooser::DisplayFlags &flags = KFontChooser::NoDisplayFlags, QWidget *parent = nullptr);
/**
* Creates a modal font difference dialog, lets the user choose a selection
* of changes that should be made to a set of fonts, and returns when the
* dialog is closed. Useful for choosing slight adjustments to the font set
* when the user would otherwise have to manually edit a number of fonts.
*
* @param theFont a reference to the font to write the chosen font into
* @param flags flags to define how the font chooser is displayed
* @param diffFlags a reference to the integer bitmask into which the chosen
* difference selection bitmask should be written.
* Check the bitmask afterwards like:
* \code
* if ( diffFlags & KFontChooser::FontDiffFamily ) {
* [...]
* }
* if ( diffFlags & KFontChooser::FontDiffStyle ) {
* [...]
* }
* if ( diffFlags & KFontChooser::FontDiffSize ) {
* [...]
* }
* \endcode
* @param parent parent widget of the dialog, if any, the dialog will be centered relative to it
*
* @returns QDialog::result()
*/
static int getFontDiff(QFont &theFont,
KFontChooser::FontDiffFlags &diffFlags,
const KFontChooser::DisplayFlags &flags = KFontChooser::NoDisplayFlags,
QWidget *parent = nullptr);
Q_SIGNALS:
/**
* Emitted whenever the currently selected font changes.
* Connect to this to monitor the font as it is selected if you are
* not running modal.
*/
void fontSelected(const QFont &font);
private:
std::unique_ptr<KFontChooserDialogPrivate> const d;
Q_DISABLE_COPY(KFontChooserDialog)
};
#endif
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KFontChooserWidget</class>
<widget class="QWidget" name="KFontChooserWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1029</width>
<height>838</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="1,0,0">
<item row="2" column="0">
<layout class="QVBoxLayout" name="sampleTextEditLayout">
<item>
<widget class="QLabel" name="fontFeaturesLabel">
<property name="text">
<string>Comma separated list of font features (e.g., liga, calt):</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="fontFeaturesLineEdit"/>
</item>
<item>
<widget class="QLabel" name="previewLabel">
<property name="text">
<string>Sample:</string>
</property>
<property name="buddy">
<cstring>sampleTextEdit</cstring>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="sampleTextEdit">
<property name="whatsThis">
<string>This sample text illustrates the current settings. You may edit it to test special characters.</string>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="onlyFixedCheckBox">
<property name="text">
<string>Show only monospaced fonts</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="mainHorizontalLayout" stretch="1,1,0">
<item>
<layout class="QVBoxLayout" name="fontLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="familyCheckBox">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="familyLabel">
<property name="text">
<string>Font:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="familyListWidget"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="styleLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="styleCheckBox">
<property name="text">
<string>Font style</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="styleLabel">
<property name="text">
<string>Font style:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="styleListWidget"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="sizeLayout">
<item>
<widget class="QCheckBox" name="sizeCheckBox">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sizeLabel">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="sizeIsRelativeCheckBox">
<property name="text">
<string>Relative</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="sizeSpinBox"/>
</item>
<item>
<widget class="QListWidget" name="sizeListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,204 @@
/*
SPDX-FileCopyrightText: 2003 Nadeem Hasan <nhasan@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kfontrequester.h"
#include "fonthelpers_p.h"
#include <KFontChooserDialog>
#include <QCoreApplication>
#include <QEvent>
#include <QFontDatabase>
#include <QFontInfo>
#include <QHBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <cmath>
class KFontRequesterPrivate
{
Q_DECLARE_TR_FUNCTIONS(KFontRequester)
public:
KFontRequesterPrivate(KFontRequester *qq)
: q(qq)
{
}
void displaySampleText();
void setToolTip();
void buttonClicked();
KFontRequester *q;
bool m_onlyFixed;
QString m_sampleText, m_title;
QLabel *m_sampleLabel = nullptr;
QPushButton *m_button = nullptr;
QFont m_selFont;
};
KFontRequester::KFontRequester(QWidget *parent, bool onlyFixed)
: QWidget(parent)
, d(new KFontRequesterPrivate(this))
{
d->m_onlyFixed = onlyFixed;
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
d->m_sampleLabel = new QLabel(this);
d->m_button = new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit")), QString(), this);
d->m_sampleLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
setFocusProxy(d->m_button);
setFocusPolicy(d->m_button->focusPolicy());
layout->addWidget(d->m_sampleLabel, 1);
layout->addWidget(d->m_button);
connect(d->m_button, &QPushButton::clicked, this, [this] {
d->buttonClicked();
});
d->displaySampleText();
d->setToolTip();
d->m_sampleLabel->installEventFilter(this);
}
KFontRequester::~KFontRequester() = default;
QFont KFontRequester::font() const
{
return d->m_selFont;
}
bool KFontRequester::isFixedOnly() const
{
return d->m_onlyFixed;
}
QString KFontRequester::sampleText() const
{
return d->m_sampleText;
}
QString KFontRequester::title() const
{
return d->m_title;
}
QLabel *KFontRequester::label() const
{
return d->m_sampleLabel;
}
QPushButton *KFontRequester::button() const
{
return d->m_button;
}
void KFontRequester::setFont(const QFont &font, bool onlyFixed)
{
d->m_selFont = font;
d->m_onlyFixed = onlyFixed;
d->displaySampleText();
Q_EMIT fontSelected(d->m_selFont);
}
void KFontRequester::setSampleText(const QString &text)
{
d->m_sampleText = text;
d->displaySampleText();
}
void KFontRequester::setTitle(const QString &title)
{
d->m_title = title;
d->setToolTip();
}
bool KFontRequester::eventFilter(QObject *watched, QEvent *event)
{
if (watched == d->m_sampleLabel) {
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease: {
auto *e = static_cast<QMouseEvent *>(event);
if (e->button() == Qt::LeftButton && rect().contains(e->pos())) {
if (e->type() == QEvent::MouseButtonRelease) {
d->buttonClicked();
}
e->accept();
return true;
}
break;
}
default:
break;
}
}
return QWidget::eventFilter(watched, event);
}
void KFontRequesterPrivate::buttonClicked()
{
KFontChooser::DisplayFlags flags = m_onlyFixed ? KFontChooser::FixedFontsOnly : KFontChooser::NoDisplayFlags;
const int result = KFontChooserDialog::getFont(m_selFont, flags, q->parentWidget());
if (result == QDialog::Accepted) {
displaySampleText();
Q_EMIT q->fontSelected(m_selFont);
}
}
void KFontRequesterPrivate::displaySampleText()
{
m_sampleLabel->setFont(m_selFont);
qreal size = m_selFont.pointSizeF();
if (size == -1) {
size = m_selFont.pixelSize();
}
if (m_sampleText.isEmpty()) {
QString family = translateFontName(m_selFont.family());
m_sampleLabel->setText(QStringLiteral("%1 %2").arg(family).arg(size));
} else {
m_sampleLabel->setText(m_sampleText);
}
}
void KFontRequesterPrivate::setToolTip()
{
m_button->setToolTip(tr("Choose font…", "@info:tooltip"));
m_sampleLabel->setToolTip(QString());
m_sampleLabel->setWhatsThis(QString());
if (m_title.isNull()) {
m_sampleLabel->setToolTip(tr("Preview of the selected font", "@info:tooltip"));
m_sampleLabel->setWhatsThis(
tr("This is a preview of the selected font. You can change it"
" by clicking the \"Choose Font...\" button.",
"@info:whatsthis"));
} else {
m_sampleLabel->setToolTip(tr("Preview of the \"%1\" font", "@info:tooltip").arg(m_title));
m_sampleLabel->setWhatsThis(tr("This is a preview of the \"%1\" font. You can change it"
" by clicking the \"Choose Font...\" button.",
"@info:whatsthis")
.arg(m_title));
}
}
#include "moc_kfontrequester.cpp"
@@ -0,0 +1,129 @@
/*
SPDX-FileCopyrightText: 2003 Nadeem Hasan <nhasan@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KFONTREQUESTER_H
#define KFONTREQUESTER_H
#include <QFont>
#include <QString>
#include <QWidget>
#include <memory>
#include <kwidgetsaddons_export.h>
class QLabel;
class QPushButton;
/**
* @class KFontRequester kfontrequester.h KFontRequester
*
* This class provides a widget with a lineedit and a button, which invokes
* a font dialog (KFontChooserDialog).
*
* The lineedit provides a preview of the selected font. The preview text can
* be customized. You can also have the font dialog show only the fixed fonts.
*
* \image html kfontrequester.png "KFontRequester"
*
* @author Nadeem Hasan <nhasan@kde.org>
*/
class KWIDGETSADDONS_EXPORT KFontRequester : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle)
Q_PROPERTY(QString sampleText READ sampleText WRITE setSampleText)
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontSelected USER true)
public:
/**
* Constructs a font requester widget.
*
* @param parent The parent widget.
* @param onlyFixed Only display fonts which have fixed-width character
* sizes.
*/
explicit KFontRequester(QWidget *parent = nullptr, bool onlyFixed = false);
~KFontRequester() override;
/**
* @return The currently selected font in the requester.
*/
QFont font() const;
/**
* @return Returns true if only fixed fonts are displayed.
*/
bool isFixedOnly() const;
/**
* @return The current text in the sample text input area.
*/
QString sampleText() const;
/**
* @return The current title of the widget.
*/
QString title() const;
/**
* @return Pointer to the label used for preview.
*/
QLabel *label() const;
/**
* @return Pointer to the pushbutton in the widget.
*/
QPushButton *button() const;
/**
* Sets the currently selected font in the requester.
*
* @param font The font to select.
* @param onlyFixed Display only fixed-width fonts in the font dialog
* if @p true, or vice-versa.
*/
virtual void setFont(const QFont &font, bool onlyFixed = false);
/**
* Sets the sample text.
*
* Normally you should not change this
* text, but it can be better to do this if the default text is
* too large for the edit area when using the default font of your
* application. Default text is current font name and size. Setting
* the text to QString() will restore the default.
*
* @param text The new sample text. The current will be removed.
*/
virtual void setSampleText(const QString &text);
/**
* Set the title for the widget that will be used in the tooltip and
* what's this text.
*
* @param title The title to be set.
*/
virtual void setTitle(const QString &title);
Q_SIGNALS:
/**
* Emitted when a new @p font has been selected in the underlying dialog
*/
void fontSelected(const QFont &font);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
friend class KFontRequesterPrivate;
std::unique_ptr<class KFontRequesterPrivate> const d;
Q_DISABLE_COPY(KFontRequester)
};
#endif // KFONTREQUESTER_H
@@ -0,0 +1,138 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kfontsizeaction.h"
#include "kselectaction_p.h"
#include "loggingcategory.h"
#include <QFontDatabase>
class KFontSizeActionPrivate : public KSelectActionPrivate
{
Q_DECLARE_PUBLIC(KFontSizeAction)
public:
KFontSizeActionPrivate(KFontSizeAction *qq)
: KSelectActionPrivate(qq)
{
}
void init();
};
// BEGIN KFontSizeAction
KFontSizeAction::KFontSizeAction(QObject *parent)
: KSelectAction(*new KFontSizeActionPrivate(this), parent)
{
Q_D(KFontSizeAction);
d->init();
}
KFontSizeAction::KFontSizeAction(const QString &text, QObject *parent)
: KSelectAction(*new KFontSizeActionPrivate(this), parent)
{
Q_D(KFontSizeAction);
setText(text);
d->init();
}
KFontSizeAction::KFontSizeAction(const QIcon &icon, const QString &text, QObject *parent)
: KSelectAction(*new KFontSizeActionPrivate(this), parent)
{
Q_D(KFontSizeAction);
setIcon(icon);
setText(text);
d->init();
}
KFontSizeAction::~KFontSizeAction() = default;
void KFontSizeActionPrivate::init()
{
Q_Q(KFontSizeAction);
q->setEditable(true);
const QList<int> sizes = QFontDatabase::standardSizes();
QStringList lst;
lst.reserve(sizes.count());
for (QList<int>::ConstIterator it = sizes.begin(), total = sizes.end(); it != total; ++it) {
lst.append(QString::number(*it));
}
q->setItems(lst);
}
void KFontSizeAction::setFontSize(int size)
{
if (size == fontSize()) {
const QString test = QString::number(size);
const auto actions = this->actions();
for (QAction *action : actions) {
if (action->text() == test) {
setCurrentAction(action);
return;
}
}
}
if (size < 1) {
qCWarning(KWidgetsAddonsLog) << "KFontSizeAction: Size " << size << " is out of range";
return;
}
QAction *a = action(QString::number(size));
if (!a) {
// Insert at the correct position in the list (to keep sorting)
QList<int> lst;
// Convert to list of ints
QStringListIterator itemsIt(items());
while (itemsIt.hasNext()) {
lst.append(itemsIt.next().toInt());
}
// New size
lst.append(size);
// Sort the list
std::sort(lst.begin(), lst.end());
clear();
for (int it : std::as_const(lst)) {
QAction *const action = addAction(QString::number(it));
if (it == size) {
setCurrentAction(action);
}
}
} else {
setCurrentAction(a);
}
}
int KFontSizeAction::fontSize() const
{
return currentText().toInt();
}
void KFontSizeAction::slotActionTriggered(QAction *action)
{
Q_EMIT fontSizeChanged(action->text().toInt());
KSelectAction::slotActionTriggered(action);
}
#include "moc_kfontsizeaction.cpp"
@@ -0,0 +1,58 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
*/
#ifndef KFONTSIZEACTION_H
#define KFONTSIZEACTION_H
#include <kselectaction.h>
class KFontSizeActionPrivate;
/**
* @class KFontSizeAction kfontsizeaction.h KFontSizeAction
*
* An action to allow changing of the font size.
* This action will be shown as a combobox on a toolbar with a proper set of font sizes.
*/
class KWIDGETSADDONS_EXPORT KFontSizeAction : public KSelectAction
{
Q_OBJECT
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize)
public:
explicit KFontSizeAction(QObject *parent);
KFontSizeAction(const QString &text, QObject *parent);
KFontSizeAction(const QIcon &icon, const QString &text, QObject *parent);
~KFontSizeAction() override;
int fontSize() const;
void setFontSize(int size);
Q_SIGNALS:
void fontSizeChanged(int);
protected Q_SLOTS:
/**
* This function is called whenever an action from the selections is triggered.
*/
void slotActionTriggered(QAction *action) override;
private:
Q_DECLARE_PRIVATE(KFontSizeAction)
};
#endif
@@ -0,0 +1,178 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2001 Holger Freyther <freyher@yahoo.com>
based on ideas from Martijn and Simon
many thanks to Simon
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "kguiitem.h"
#include <QPushButton>
#include <QSharedData>
class KGuiItemPrivate : public QSharedData
{
public:
KGuiItemPrivate()
{
m_enabled = true;
m_hasIcon = false;
}
KGuiItemPrivate(const KGuiItemPrivate &other) = default;
KGuiItemPrivate &operator=(const KGuiItemPrivate &other) = default;
QString m_text;
QString m_toolTip;
QString m_whatsThis;
QString m_statusText;
QString m_iconName;
QIcon m_icon;
bool m_hasIcon : 1;
bool m_enabled : 1;
};
KGuiItem::KGuiItem()
: d(new KGuiItemPrivate)
{
}
KGuiItem::KGuiItem(const QString &text, const QString &iconName, const QString &toolTip, const QString &whatsThis)
: d(new KGuiItemPrivate)
{
d->m_text = text;
d->m_toolTip = toolTip;
d->m_whatsThis = whatsThis;
setIconName(iconName);
}
KGuiItem::KGuiItem(const QString &text, const QIcon &icon, const QString &toolTip, const QString &whatsThis)
: d(new KGuiItemPrivate)
{
d->m_text = text;
d->m_toolTip = toolTip;
d->m_whatsThis = whatsThis;
setIcon(icon);
}
KGuiItem::KGuiItem(const KGuiItem &rhs) = default;
KGuiItem &KGuiItem::operator=(const KGuiItem &rhs) = default;
KGuiItem::~KGuiItem() = default;
QString KGuiItem::text() const
{
return d->m_text;
}
QString KGuiItem::plainText() const
{
const int len = d->m_text.length();
if (len == 0) {
return d->m_text;
}
// Can assume len >= 1 from now on.
QString stripped;
int resultLength = 0;
stripped.resize(len);
const QChar *data = d->m_text.unicode();
for (int pos = 0; pos < len; ++pos) {
if (data[pos] != QLatin1Char('&')) {
stripped[resultLength++] = data[pos];
} else if (pos + 1 < len && data[pos + 1] == QLatin1Char('&')) {
stripped[resultLength++] = data[pos++];
}
}
stripped.truncate(resultLength);
return stripped;
}
QIcon KGuiItem::icon() const
{
if (d->m_hasIcon) {
if (!d->m_iconName.isEmpty()) {
return QIcon::fromTheme(d->m_iconName);
} else {
return d->m_icon;
}
}
return QIcon();
}
QString KGuiItem::iconName() const
{
return d->m_iconName;
}
QString KGuiItem::toolTip() const
{
return d->m_toolTip;
}
QString KGuiItem::whatsThis() const
{
return d->m_whatsThis;
}
bool KGuiItem::isEnabled() const
{
return d->m_enabled;
}
bool KGuiItem::hasIcon() const
{
return d->m_hasIcon;
}
void KGuiItem::setText(const QString &text)
{
d->m_text = text;
}
void KGuiItem::setIcon(const QIcon &icon)
{
d->m_icon = icon;
d->m_iconName.clear();
d->m_hasIcon = !icon.isNull();
}
void KGuiItem::setIconName(const QString &iconName)
{
d->m_iconName = iconName;
d->m_icon = QIcon();
d->m_hasIcon = !iconName.isEmpty();
}
void KGuiItem::setToolTip(const QString &toolTip)
{
d->m_toolTip = toolTip;
}
void KGuiItem::setWhatsThis(const QString &whatsThis)
{
d->m_whatsThis = whatsThis;
}
void KGuiItem::setEnabled(bool enabled)
{
d->m_enabled = enabled;
}
void KGuiItem::assign(QPushButton *button, const KGuiItem &item)
{
button->setText(item.d->m_text);
button->setIcon(item.icon());
button->setToolTip(item.d->m_toolTip);
button->setWhatsThis(item.d->m_whatsThis);
}
@@ -0,0 +1,198 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2001 Holger Freyther <freyher@yahoo.com>
based on ideas from Martijn and Simon
SPDX-License-Identifier: LGPL-2.0-only
Many thanks to Simon tronical Hausmann
*/
#ifndef kguiitem_h
#define kguiitem_h
#include <kwidgetsaddons_export.h>
#include <QIcon>
#include <QSharedDataPointer>
#include <QString>
class QPushButton;
/**
* @class KGuiItem kguiitem.h KGuiItem
*
* @short An abstract class for setting the text, icon, tooltip and WhatsThis data
* on a GUI item (e.g.\ a QPushButton).
*
* @author Holger Freyther <freyher@yahoo.com>
* @see KStandardGuiItem
*/
class KWIDGETSADDONS_EXPORT KGuiItem
{
public:
/**
* Constructs an empty KGuiItem. You can use the various methods provided by
* this class to set the text, icon... etc.
*/
KGuiItem();
// This is explicit because it's easy to get subtle bugs otherwise. The
// icon name, tooltip and whatsthis text get changed behind your back if
// you do 'setButtonFoo( "Bar" );' It gives the wrong impression that you
// just change the text.
/**
* Constructs a KGuiItem with the provided arguments.
*
* @param text the text to use with the GUI item
* @param iconName the name of the icon to display next to the text on the item;
* QIcon::fromTheme() is used to get a icon with that name from
* the icon themes available on the system
* @param tooltip the tooltip to use for this item
* @param whatsThis the text to use for the WhatThis help message
*/
explicit KGuiItem(const QString &text, const QString &iconName = QString(), const QString &toolTip = QString(), const QString &whatsThis = QString());
/**
* Constructs a KGuiItem with the provided arguments.
*
* @param text the text to use with the GUI item
* @param icon the QIcon object used to get an icon to display next to the text
* on this item
* @param tooltip the tooltip to use for this item
* @param whatsThis the text to use for the WhatThis help message
*/
KGuiItem(const QString &text, const QIcon &icon, const QString &toolTip = QString(), const QString &whatsThis = QString());
/**
* Constructs a copy of @p other.
*/
KGuiItem(const KGuiItem &other);
/**
* Assigns @p other to this KGuiItem object and returns a reference to this object.
*/
KGuiItem &operator=(const KGuiItem &other);
/**
* Destructor.
*/
~KGuiItem();
/**
* Sets the text to use for this GUI item.
*/
void setText(const QString &text);
/**
* Returns the text used by this GUI item.
*
* This may contain '&' characters which denote a keyboard accelerator shortcut that
* can be used to invoke the GUI item, e.g. Alt + 'O' for button "&OK".
* (Note that the '&' is not visible to the user).
*
* You can get the plain text without the accelerator denoting character '&', by
* using plainText().
*
*/
QString text() const;
/**
* Returns the text used by this GUI item after stripping all existing '&'
* characters which denote keyboard accelerators.
*
* @see text()
*/
QString plainText() const;
/**
* Sets the icon to be shown next to the text of this GUI item.
*/
void setIcon(const QIcon &iconset);
/**
* Returns the icon used by this GUI item.
*
* This will return a null QIcon if no icon was previously set for this item.
*/
QIcon icon() const;
/**
* Sets the name of the icon that will be shown next to the text of this
* GUI item. The actual QIcon will be obtained by using QIcon::fromTheme().
*/
void setIconName(const QString &iconName);
/**
* Returns the name of the icon used by this GUI item.
*
* This will return an empty string if no icon was previously set for this item.
*/
QString iconName() const;
/**
* Returns @c true if this GUI item has an icon set for it and @c false otherwise.
*/
bool hasIcon() const;
/**
* Sets the tooltip text.
*/
void setToolTip(const QString &tooltip);
/**
* Returns the tooltip used for this GUI item.
*
* This will return an empty string if no tooltip was previously set for this item.
*/
QString toolTip() const;
/**
* Sets the WhatThis text.
*/
void setWhatsThis(const QString &whatsThis);
/**
* Returns the WhatThis text used for this GUI item.
*
* This will return an empty string if no WhatThis text was previously set for
* this item.
*/
QString whatsThis() const;
/**
* Toggles the enabled property of this GUI item.
*
* @see QWidget::setEnabled()
*/
void setEnabled(bool enable);
/**
* Returns @c true if this GUI item is enabled and @c false otherwise.
*
* @see QWidget::isEnabled()
*/
bool isEnabled() const;
/**
* A static method that can be used to set the text, icon, tooltip and WhatThis
* properties from @p item on @p button.
*
* @code
* // Create a QDialogButtonBox with two buttons, of Yes and No standard type
* auto *buttonBox = new QDialogButtonBox({QDialogButtonBox::Yes | QDialogButtonBox::No}, this);
*
* // Assign the text and icon from KStandardGuiItem::quit()/continue() to the buttons in the
* // button dialog box
* KGuiItem::assign(buttonBox->button(QDialogButtonBox::Yes), KStandardGuiItem::quit());
* KGuiItem::assign(buttonBox->button(QDialogButtonBox::No), KStandardGuiItem::continue());
* @endcode
*/
static void assign(QPushButton *button, const KGuiItem &item);
private:
QSharedDataPointer<class KGuiItemPrivate> d;
};
#endif
@@ -0,0 +1,70 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kjobwidgets.h"
#include <QPointer>
#include <QVariant>
#include <QWidget>
#include <QWindow>
void KJobWidgets::setWindow(QObject *job, QWidget *widget)
{
QPointer<QWidget> p = widget;
job->setProperty("widget", QVariant::fromValue(p));
QPointer<QWindow> windowHandle = widget ? widget->windowHandle() : nullptr;
job->setProperty("window", QVariant::fromValue(windowHandle));
if (windowHandle) {
job->setProperty("window-id", QVariant::fromValue(windowHandle->winId()));
}
}
#if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(6, 5)
void KJobWidgets::setWindowHandle(QObject *job, QWindow *window)
{
job->setProperty("window", QVariant::fromValue(window));
if (window) {
job->setProperty("window-id", QVariant::fromValue(window->winId()));
}
}
#endif
QWidget *KJobWidgets::window(QObject *job)
{
return job->property("widget").value<QPointer<QWidget>>();
}
#if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(6, 5)
QWindow *KJobWidgets::windowHandle(QObject *job)
{
return job->property("window").value<QPointer<QWindow>>();
}
#endif
// duplicated from kwindowsystem
static int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
{
quint32 time1 = time1_;
quint32 time2 = time2_;
if (time1 == time2) {
return 0;
}
return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
}
void KJobWidgets::updateUserTimestamp(QObject *job, unsigned long time)
{
unsigned long currentTimestamp = userTimestamp(job);
if (currentTimestamp == 0 || timestampCompare(time, currentTimestamp) > 0) {
job->setProperty("userTimestamp", qulonglong(time));
}
}
unsigned long KJobWidgets::userTimestamp(QObject *job)
{
return job->property("userTimestamp").toULongLong();
}
@@ -0,0 +1,92 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KJOBWIDGETS_H
#define KJOBWIDGETS_H
#include <kwidgetsaddons_export.h>
class QObject;
class QWidget;
class QWindow;
/**
* KJobWidgets namespace
*/
namespace KJobWidgets
{
/**
* Associate this job with a window given by @p window.
*
* @param job should be a KJob subclass
*
* This is used:
* @li by KDialogJobUiDelegate as parent widget for error messages
* @li by KWidgetJobTracker as parent widget for progress dialogs
* @li by KIO::AbstractJobInteractionInterface as parent widget for rename/skip dialogs
* and possibly more.
* @li by KIO::DropJob as parent widget of popup menus.
* This is required on Wayland to properly position the menu.
*
* @since 6.0
*/
KWIDGETSADDONS_EXPORT void setWindow(QObject *job, QWidget *widget);
#if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Associate this job with a window given by @p window.
*
* Note that setWindow() will set the window handle too.
*
* @param job should be a KJob subclass
*
* @since 6.4
* @deprecated since 6.5, use KJobWindows::setWindow() instead.
*/
KWIDGETSADDONS_EXPORT KWIDGETSADDONS_DEPRECATED_VERSION(6, 5, "Use KJobWindows::setWindow() instead") void setWindowHandle(QObject *job, QWindow *window);
#endif
/**
* Return the window associated with this job.
*
* @param job should be a KJob subclass
*
* @since 6.0
*/
KWIDGETSADDONS_EXPORT QWidget *window(QObject *job);
#if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(6, 5)
/**
* Returns the window handle associated with this job.
*
* @param job should be a KJob subclass
*
* @since 6.4
* @deprecated since 6.5, use KJobWindows::window() instead.
*/
KWIDGETSADDONS_EXPORT KWIDGETSADDONS_DEPRECATED_VERSION(6, 5, "Use KJobWindows::window() instead") QWindow *windowHandle(QObject *job);
#endif
/**
* Updates the last user action timestamp to the given time.
*
* @param job should be a KJob subclass
*
* @since 6.0
*/
KWIDGETSADDONS_EXPORT void updateUserTimestamp(QObject *job, unsigned long time);
/**
* Returns the last user action timestamp
*
* @param job should be a KJob subclass
*
* @since 6.0
*/
KWIDGETSADDONS_EXPORT unsigned long userTimestamp(QObject *job);
}
#endif
@@ -0,0 +1,261 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com>
SPDX-FileCopyrightText: 2010 Christoph Feck <cfeck@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kled.h"
#include <QImage>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
class KLedPrivate
{
public:
int darkFactor = 300;
QColor color;
KLed::State state = KLed::On;
KLed::Look look = KLed::Raised;
KLed::Shape shape = KLed::Circular;
QPixmap cachedPixmap[2]; // for both states
};
KLed::KLed(QWidget *parent)
: QWidget(parent)
, d(new KLedPrivate)
{
setColor(Qt::green);
updateAccessibleName();
}
KLed::KLed(const QColor &color, QWidget *parent)
: QWidget(parent)
, d(new KLedPrivate)
{
setColor(color);
updateAccessibleName();
}
KLed::KLed(const QColor &color, State state, Look look, Shape shape, QWidget *parent)
: QWidget(parent)
, d(new KLedPrivate)
{
d->state = (state == Off ? Off : On);
d->look = look;
d->shape = shape;
setColor(color);
updateAccessibleName();
}
KLed::~KLed() = default;
KLed::State KLed::state() const
{
return d->state;
}
KLed::Shape KLed::shape() const
{
return d->shape;
}
QColor KLed::color() const
{
return d->color;
}
KLed::Look KLed::look() const
{
return d->look;
}
void KLed::setState(State state)
{
if (d->state == state) {
return;
}
d->state = (state == Off ? Off : On);
updateCachedPixmap();
updateAccessibleName();
}
void KLed::setShape(Shape shape)
{
if (d->shape == shape) {
return;
}
d->shape = shape;
updateCachedPixmap();
}
void KLed::setColor(const QColor &color)
{
if (d->color == color) {
return;
}
d->color = color;
updateCachedPixmap();
}
void KLed::setDarkFactor(int darkFactor)
{
if (d->darkFactor == darkFactor) {
return;
}
d->darkFactor = darkFactor;
updateCachedPixmap();
}
int KLed::darkFactor() const
{
return d->darkFactor;
}
void KLed::setLook(Look look)
{
if (d->look == look) {
return;
}
d->look = look;
updateCachedPixmap();
}
void KLed::toggle()
{
d->state = (d->state == On ? Off : On);
updateCachedPixmap();
updateAccessibleName();
}
void KLed::on()
{
setState(On);
}
void KLed::off()
{
setState(Off);
}
void KLed::resizeEvent(QResizeEvent *)
{
updateCachedPixmap();
}
QSize KLed::sizeHint() const
{
QStyleOption option;
option.initFrom(this);
int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, &option, this);
return QSize(iconSize, iconSize);
}
QSize KLed::minimumSizeHint() const
{
return QSize(16, 16);
}
void KLed::updateAccessibleName()
{
#ifndef QT_NO_ACCESSIBILITY
QString onName = tr("LED on", "Accessible name of a Led whose state is on");
QString offName = tr("LED off", "Accessible name of a Led whose state is off");
QString lastName = accessibleName();
if (lastName.isEmpty() || lastName == onName || lastName == offName) {
// Accessible name has not been manually set.
setAccessibleName(d->state == On ? onName : offName);
}
#endif
}
void KLed::updateCachedPixmap()
{
d->cachedPixmap[Off] = QPixmap();
d->cachedPixmap[On] = QPixmap();
update();
}
void KLed::paintEvent(QPaintEvent *)
{
if (!d->cachedPixmap[d->state].isNull()) {
QPainter painter(this);
painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
return;
}
QSize size(width() - 2, height() - 2);
if (d->shape == Circular) {
// Make sure the LED is round
const int dim = qMin(width(), height()) - 2;
size = QSize(dim, dim);
}
QPointF center(size.width() / 2.0, size.height() / 2.0);
const int smallestSize = qMin(size.width(), size.height());
QPainter painter;
QImage image(size, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QRadialGradient fillGradient(center, smallestSize / 2.0, QPointF(center.x(), size.height() / 3.0));
const QColor fillColor = d->state != Off ? d->color : d->color.darker(d->darkFactor);
fillGradient.setColorAt(0.0, fillColor.lighter(250));
fillGradient.setColorAt(0.5, fillColor.lighter(130));
fillGradient.setColorAt(1.0, fillColor);
QConicalGradient borderGradient(center, d->look == Sunken ? 90 : -90);
QColor borderColor = palette().color(QPalette::Dark);
if (d->state == On) {
QColor glowOverlay = fillColor;
glowOverlay.setAlpha(80);
// This isn't the fastest way, but should be "fast enough".
// It's also the only safe way to use QPainter::CompositionMode
QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
QPainter p(&img);
QColor start = borderColor;
start.setAlpha(255); // opaque
p.fillRect(0, 0, 1, 1, start);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.fillRect(0, 0, 1, 1, glowOverlay);
p.end();
borderColor = img.pixel(0, 0);
}
borderGradient.setColorAt(0.2, borderColor);
borderGradient.setColorAt(0.5, palette().color(QPalette::Light));
borderGradient.setColorAt(0.8, borderColor);
painter.begin(&image);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(d->look == Flat ? QBrush(fillColor) : QBrush(fillGradient));
const QBrush penBrush = (d->look == Flat) ? QBrush(borderColor) : QBrush(borderGradient);
const qreal penWidth = smallestSize / 8.0;
painter.setPen(QPen(penBrush, penWidth));
QRectF r(penWidth / 2.0, penWidth / 2.0, size.width() - penWidth, size.height() - penWidth);
if (d->shape == Rectangular) {
painter.drawRect(r);
} else {
painter.drawEllipse(r);
}
painter.end();
d->cachedPixmap[d->state] = QPixmap::fromImage(image);
painter.begin(this);
painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
painter.end();
}
#include "moc_kled.cpp"
@@ -0,0 +1,250 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KLED_H
#define KLED_H
#include <kwidgetsaddons_export.h>
#include <QWidget>
#include <memory>
class QColor;
/**
* @class KLed kled.h KLed
*
* @short An LED widget.
*
* Displays a round or rectangular light emitting diode.
*
* It is configurable to arbitrary colors, the two on/off states and three
* styles (or "looks");
*
* It may display itself in a performant flat view, a round view with
* light spot or a round view sunken in the screen.
*
* \image html kled.png "KLed Widget"
*
* @author Joerg Habenicht, Richard J. Moore (rich@kde.org) 1998, 1999
*/
class KWIDGETSADDONS_EXPORT KLed : public QWidget
{
Q_OBJECT
Q_PROPERTY(State state READ state WRITE setState)
Q_PROPERTY(Shape shape READ shape WRITE setShape)
Q_PROPERTY(Look look READ look WRITE setLook)
Q_PROPERTY(QColor color READ color WRITE setColor)
Q_PROPERTY(int darkFactor READ darkFactor WRITE setDarkFactor)
public:
/**
* Status of the light is on/off.
* @short LED on/off.
*/
enum State { Off, On };
Q_ENUM(State)
/**
* Shades of the lamp.
* @short LED shape
*/
enum Shape { Rectangular, Circular };
Q_ENUM(Shape)
/**
* Displays a flat, round or sunken LED.
*
* @short LED look.
*/
enum Look {
Flat,
Raised,
Sunken,
};
Q_ENUM(Look)
/**
* Constructs a green, round LED widget which will initially
* be turned on.
*
* @param parent The parent widget.
*/
explicit KLed(QWidget *parent = nullptr);
/**
* Constructs a round LED widget with the supplied color which will
* initially be turned on.
*
* @param color Initial color of the LED.
* @param parent The parent widget.
* @short Constructor
*/
explicit KLed(const QColor &color, QWidget *parent = nullptr);
/**
* Constructor with the color, state and look.
*
* Differs from above only in the parameters, which configure all settings.
*
* @param color Initial color of the LED.
* @param state Sets the State.
* @param look Sets the Look.
* @param shape Sets the Shape (rectangular or circular).
* @param parent The parent widget.
* @short Constructor
*/
KLed(const QColor &color, KLed::State state, KLed::Look look, KLed::Shape shape, QWidget *parent = nullptr);
/**
* Destroys the LED widget.
* @short Destructor
*/
~KLed() override;
/**
* Returns the current color of the widget.
*
* @returns LED color
* @see setColor()
*/
QColor color() const;
/**
* Returns the current state of the widget (on/off).
* @returns LED state
*
* @see State
*/
State state() const;
/**
* Returns the current look of the widget.
* @returns LED look
*
* @see Look
*/
Look look() const;
/**
* Returns the current shape of the widget.
* @returns LED shape
*
* @see Shape
*/
Shape shape() const;
/**
* Returns the factor to darken the LED.
* @returns dark factor
*
* @see setDarkFactor()
*/
int darkFactor() const;
/**
* Set the color of the widget.
*
* The LED is shown with @p color when in the KLed::On state
* or with the darken color in KLed::Off state.
*
* The widget calls the update() method, so it will
* be updated when entering the main event loop.
*
* @param color New color of the LED.
*
* @see color() darkFactor()
*/
void setColor(const QColor &color);
/**
* Sets the state of the widget to On or Off.
*
* @param state The LED state: on or off.
*
* @see on() off() toggle()
*/
void setState(State state);
/**
* Sets the look of the widget.
*
* The look may be Flat, Raised or Sunken.
*
* The widget calls the update() method, so it will
* be updated when entering the main event loop.
*
* @param look New look of the LED.
*
* @see Look
*/
void setLook(Look look);
/**
* Set the shape of the LED.
*
* @param shape The LED shape.
* @short Set LED shape.
*/
void setShape(Shape shape);
/**
* Sets the factor to darken the LED in KLed::Off state.
*
* The @p darkFactor should be greater than 100, otherwise the LED
* becomes lighter in KLed::Off state.
*
* Defaults to 300.
*
* @param darkFactor Sets the factor to darken the LED.
*
* @see setColor
*/
void setDarkFactor(int darkFactor);
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
public Q_SLOTS:
/**
* Toggles the state of the led from Off to On or vice versa.
*/
void toggle();
/**
* Sets the state of the widget to On.
*
* @see off() toggle() setState()
*/
void on();
/**
* Sets the state of the widget to Off.
*
* @see on() toggle() setState()
*/
void off();
protected:
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *) override;
private:
/**
* @internal
* invalidates caches after property changes and calls update()
*/
KWIDGETSADDONS_NO_EXPORT void updateCachedPixmap();
KWIDGETSADDONS_NO_EXPORT void updateAccessibleName();
private:
std::unique_ptr<class KLedPrivate> const d;
};
#endif

Some files were not shown because too many files have changed in this diff Show More