/** * @RDE_HEADER@ * Base Layout Widget with all Areas (Info, Main, Detail, Menu) * $Id$ * $HeadURL$ * $Author$ */ #pragma once #include "lts_base.h" #include "lts_widgets_export.h" #include "lts_notification_box.h" #include "qevent.h" #include #include #include #include class QButtonGroup; class QPropertyAnimation; class QPushButton; class QTimer; class QToolButton; namespace Ui { class LtsBaseLayoutWidget; } namespace IDL { enum class PartitionType; } namespace LtsUtils { class Promise; } namespace LtsWidgets { class LtsDragIcon; class LtsProgressDialogHelper; class LtsTabWidget; class LtsToolBarArea; class LtsToolBox; struct ExerciseData { QString exName; QString exDescription; int phId = -1; QString phName; QString phDescription; QString exStateString; // Already translated. Todo: Probably won't change, if Language is changed on the fly. ExcState exState = ExerciseState::eUnknown; QColor exStateCol; QString partitionType; bool operator==(const ExerciseData &o) const { return exName == o.exName && exDescription == o.exDescription && phId == o.phId && phName == o.phName && phDescription == o.phDescription && exStateString == o.exStateString && exState == o.exState && exStateCol == o.exStateCol && partitionType == o.partitionType; } bool operator!=(const ExerciseData &o) const { return !operator ==(o); } }; /** * @brief The LtsBaseLayoutWidget class represents the Patric-Layout-System (all Areas: Info, Main, Detail, Menu), which is used as stand-alone widget per Screen. * It is created per Session by the LtsSession, created by the LtsSessionManager whenever a new Session was created. */ class LTSWIDGETS_EXPORT LtsBaseLayoutWidget : public QWidget { Q_OBJECT public: explicit LtsBaseLayoutWidget(QWidget *parent = nullptr, bool splitMenu = true); ~LtsBaseLayoutWidget(); /** * @brief hideVisibleLayouts hides all layouts without interfering layout sizes and saves all layout's old states that can be restored by calling resetLayoutVisibility */ void hideVisibleLayouts(); /** * @brief resetLayoutVisibility reverts the actions done by hideVisibleLayouts - reshows all hidden layouts */ void resetLayoutVisibility(); /** * @brief setMenuVisible sets the visibility of the menu (and the special tab, too!) * @param visible true to reshow the menu and special tab, false to completely hide them */ void setMenuVisible(bool visible); /** * @brief isMenuVisible returns the visibility state of the menu (and special tab, too!) * @return true if both are visibly, false if not */ bool isMenuVisible() const; bool isTopBarVisible() const; void setTopBarVisible(bool visible); /** * @brief isMenuCollapsed returns if the menu was collapsed programmatically by calling clappseMenu. * NOTE: does not consider the collapsed state if the user collapsed the menu himself by reducing the menu size to zero by moving the splitter. * @return true is collapsed before, false if menu is not size zero. */ bool isMenuCollapsed() const; /** * @brief collapseMenu reduces the size of the menu to zero, but the visibility doesn't change, so that the user can still restore the menu with the splitter. * NOTE: the special tab widget is collapsed, too. * @param collapse true to collapse, false to restore to the state before the collapse. */ void collapseMenu(bool collapse = true); /** * @brief setSessionInfo sets the informations about the Session of this BaseLayout, so that the window can request a Session close (by logging out) when this window receives a close request. */ void setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId); /** * @brief addMenuAction adds an action (aka. menu entry) to the menu layout. The Action will be added into the given group. * @param plData Action data with information about where to put the menu entry and what to do when the menu entry is clicked * @return the LtsDragIcon that was now created and put into the menu layout */ LtsDragIcon *addMenuAction(const PluginData &plData, bool isFirstRun = false); /** * @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the information in the given plData * @param plData Action data to identify the menu entry with (group name + captioin text of the menu entry) * @return true when found & deleted, false when not found and also not deleted */ bool removeMenuAction(const PluginData &plData); /** * @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the pointer value * @param icon to identify the action (aka. menu entry) by the menu entry the LtsDragIcon that was created by the addMenuAction method and put into the menu layout * @return true when found & deleted, false when not found and also not deleted (as it was deleted before) */ bool removeMenuAction(LtsDragIcon *icon); bool removeMenuAction(const QString &byIdentifier); /** * @brief clearMenuActions deletes all actions (aka. menu entries) in the menu layout */ void clearMenuActions(); void setAppName(const QString &appName); bool isDialogFocused(const LtsDialog *dialog) const; /** * @brief setCustomText shows a custom text to the top layout of the base layout, right after the partition type text. * Also prepends a bullet before the text to clearly differenciate between the partition type text and the custom text. * Call with an empty string to hide the custom text again. * @param text to print on the label */ void setCustomText(const QString &text); const QString customText() const; /** * @brief getTabFromLayoutArea returns the pointer to the LtsTabWidget that is of the type LayoutArea 'area' * @param area the area of which the LtsTabWidget's type of * @return the pointer to the LtsTabWidget */ LtsTabWidget *getTabFromLayoutArea(LayoutArea area) const; /** TODO: rename to getDialog * @brief getWidget returns the Dialog which is located in the given LayoutArea, identified by the group + identifier strings * @param menuGroup the name of the main tab of the LayoutArea * @param identifier the name of the sub tab (when sub-tabbed) of the LayoutArea, otherwise the identifier is added to the menuGroup string * @param area the LayoutArea (/LtsTabWidget) the Dialog is located * @return the LtsDialog when found, otherwise nullptr when not found */ LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown); /** * @brief getMenu returns the Widget of the Menu Layout Area (as the Menu is not an actual LayoutArea (not an LtsTabWidget which could be returned by 'getTabFromLayoutArea') * @return the pointer to the Widget of the Menu Layout */ QWidget *getMenu() const; /** * @brief getToolbar returns the LtsToolBarArea of the Menu Layout Area * @return the pointer to the LtsToolBarArea of the Menu Layout */ LtsToolBarArea *getToolbar() const; /** * @brief setOperatorName sets the operator name of this session's operator and sets the operator name widget's text * @param operatorName the human readable operator name of this session's operator */ void setOperatorName(const QString &operatorName, bool showTerminalButton); /** * @brief setProfilePicture sets the profile picture of this session's operator and sets the profile picture widget's picture * @param profilePicture the profgile picture of this session's operator */ void setProfilePicture(const QPixmap &profilePicture); /** * @brief setExerciseData sets the name and phase of this Exercise (if it is an Exercise) of this session - only sets the text of a label (label is hidden of not set) * @param exerciseData necessary data to show */ void setExerciseData(const ExerciseData &data); /** * @brief Call this function when the base layout was properly constructed so baselayout can do some internal stuff */ void initilizationFinished(); /** * @brief addNotification [Do not use.] used by LtsSession to add a Notification to the Notification Bar, but as a QToolButton * @param noti the noti to add to the Notification Bar */ void addNotification(QToolButton *noti); /** * @brief isWidgetsHidden returns the visibility state of the detail tab - the state changes with calling either of * these methods: onReShowAnimated, onHideAnimated, showWidgets, hideWidgets * @return detail tab visible (not to be confused with resizing the detail widget with the splitter, that only * minized the tab, but the tab is still visible) */ bool isWidgetsHidden() const; bool notificationExists(const QString &identifier) const; const QMap &mapButtons() const { return m_mapButtons; } void setToolBarActions(const QStringList &actions); QStringList toolBarActions() const; void setFunctionKeys(const QMap &actions); QMap functionKeys() const; void setWorkspaceId(const QString &workspaceId) { m_workspaceId = workspaceId; } /** * @brief showMessage adds a notification bubble to the bottom right of the blw. Text can be formatted with HTML. * If a message is already visible, it will be replaced and will be shown after the new message was closed. * A message with the same identifier will be replaced in the queue or if currently shown. * @param title The title in bold which is show obove the text. * @param text The text to show in the bubble - should be as short as possible as bubbles are small. Can be HTML. * @param type The type which sets the color and icon the bubble should have. See LtsNotificationBox::MessageType. * @param clickedLinkCallback The function to call if a link inside the bubble has been clicked (HTML 'a' tag) * @param closeOnLinkClicked Closes the bubble if a link has been clicked. Note: queued messages (if any) will be shown next * @param identifier An invisible identifier a message will be queued with. Can be left empty. If another message with the * same identifier (if not empty) is shown, the other message with the same identifier will be removed from the queue or if * it's the shown message, it will be closed. */ void showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type = LtsNotificationBox::MessageType::Info, const std::function &clickedLinkCallback = nullptr, bool closeOnLinkClicked = true, const QString &identifier = {}); bool messageIdentifierExists(const QString &identifier) const; void executeAction(const QString &actionIdentifier); bool isSecondaryWidget() const { return m_isSecondaryWidget; } // for the LtsScreenContainer to determine if this is a 'Hilfsansicht' for GUEZ SWP 2023 (https://devhub.defence-elec.de/confluence/x/yLjeDg) (siehe 1.1.1) void setSecondaryWidget() { m_isSecondaryWidget = true; } /** * @brief Remove Messages from Queue, as it's handled by someones else * @param message identifier to remote from que or diplay. */ void removeQueuedMessage(const QString &messageIdentifier); public slots: bool slotClose(); // checks for unsaved changes first and aborts if there are any void slotLogout(); // checks for unsaved changes first and aborts if there are any void slotScreenCountChanged(int count); /** * @brief onMenuEntryClicked is triggered when an action (aka. menu entry) in the menu layout is clicked, executes the action's payload (or focuses the coherant dialog when already open) */ void onMenuEntryClicked(); void onHideAnimated(); void onReShowAnimated(); /** * @brief addWidget adds a widget to the operator-specified base layout (either selection by cursor or by permanent selectioin) NOTE: deletes dialog if it already exists * @param widget the Dialog to be added * @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into * @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs * @param area the LayoutArea to add the Dialog into * @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator * @return true when added, false when not (invalid widget or non-existant LayoutArea) */ bool addWidget(LtsWidgets::LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true); /** * @brief Like addWidget addOrReplaceWidget adds a dialog widget to base layout with the difference that an existing widget with same identifier is removed from layout before adding he new one. * The new dialog will not be deleted (compare with adddWidget) * @param widget the Dialog to be added * @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into * @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs * @param area the LayoutArea to add the Dialog into * @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator * @return true when added, false when not (invalid widget or non-existant LayoutArea) * @see addWidget */ bool addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true ); bool focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area); /** * @brief executeCreateInstantFunctions executes both the createWidgetFunc and the directFunc (if available). (only used by LtsSession & this addMenuAction) * @param plData */ void executeCreateInstantFunctions(const LtsWidgets::PluginData &plData); void setExerciseTime(qint64 mSecsSinceEpoch); bool haveUnsavedChanges(); /** * @brief notifyUnsavedChanges * @return true if there were NO unsaved changes (method 'failed') and vice versa * */ bool notifyUnsavedChanges(); // LtsUtils::Promise *notifyUnsavedChangesAsync(); void setIrreplaceableOnce() { m_irreplaceableOnce = true; } bool irreplaceableReset() { bool r = m_irreplaceableOnce; m_irreplaceableOnce = false; return r; } bool isIrreplaceable() const { return m_irreplaceableOnce; } /** * @brief showWidgets shows the Dialog Layout again after it was previously hidden */ void showWidgets(); /** * @brief hideWidgets hides the visible Dialog Layout (for example e.g. when more space is needed for selection on the map) */ void hideWidgets(); private slots: /** * @brief slotOperatorMenuClicked shows a menu to change things affecting the operator (logout, etc.) */ void slotOperatorMenuClicked(); void setCurrentTime(); signals: /** * @brief signalCloseRequest is emitted by the closeEvent and requests a logout / close of the current Session (if this is the last BaseLayout) */ //void signalCloseRequest(const int domainId, const IDL::PartitionType &partitionType, const int partitionId); void signalClose(); void signalLogout(); void signalShowOperatorSettings(); void signalRetranslateActions(); void signalSwap(); void signalStapleTopicLockHandler(LtsWidgets::LtsDialog *widget); void signalSuccessfullyPlaced(); // called by MMM if this widget was successfully placed (even works if MMM is inactive (off)) /** * Notify about unsaved changes before popup message box. */ void signalNotifyUnsavedChanges(bool &reject); protected: /** * @brief closeEvent requests to close a session. As a BaseLayout is linked to a Session, the Session must be closed before allowing * the BaseLayout to close. So a logout is executed before closing the BaseLayout. * TODO: One or more BaseLayouts can be linked to a Session - look if it's the last BaseLayout * @param event */ void closeEvent(QCloseEvent *event) override; void showEvent(QShowEvent *event) override; void changeEvent(QEvent *e) override; private: void addDebugMenu(); void retranslate(); void addTouchScroll(QWidget *widget); LtsDragIcon *createButton(const PluginData &plData); int getIndexOfTab(LtsTabWidget *tab); LtsToolBox *getToolBox(const ExercisePart &exercisePart) const; QPushButton *getExercisePartButton(const ExercisePart &exercisePart) const; void checkExercisePartButtonVisibility(); void checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart); bool isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const; bool isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const; void applyExerciseStateRestricition(); void executePluginData(const PluginData &plData); void showNextQueuedMessage(); //void removeQueuedMessage(const QString &messageIdentifier); struct Message { public: QString title; QString text; LtsNotificationBox::MessageType type; std::function clickedLinkCallback; bool closeOnLinkClicked; QString identifier; }; ExerciseData m_exerciseData; QMap m_mapButtons; QList m_mainSplitterSizes; QList m_menuSplitterSizes; bool m_isHidden; QPropertyAnimation *m_currentAnimation{nullptr}; Ui::LtsBaseLayoutWidget *m_ui{nullptr}; IDL::PartitionType m_partitionType; int m_domainId; int m_partitionId; QString m_operatorName; bool m_firstShow; bool m_showTerminalButton; qint64 m_exerciseTime; QTimer *m_setCurrentTimeTimer{nullptr}; bool m_irreplaceableOnce; QButtonGroup *m_bg{nullptr}; QString m_workspaceId; int m_screenCount; bool m_menuCollapsed; QMap, bool> m_oldLayoutVisibilityStates; LtsNotificationBox *m_notiBox{nullptr}; QStringList m_notFoundYet; bool m_isSecondaryWidget { false }; QVector m_currentMessage; // optional QVector m_messageQueue; }; } // namespace LtsWidgets