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>
@@ -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
|
||||
)
|
||||
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 616 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 978 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 443 B |
|
After Width: | Height: | Size: 381 B |
|
After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 127 B |
|
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");
|
||||
@@ -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
|
||||
@@ -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 ¤tDate, 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 ¤tDate, 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 ¤tItem = 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 ¤tTextLE = 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
|
||||