~aleteoryx/muditaos

5398907b6f137be5c1a35a622c7d51bc8a3595c0 — Przemyslaw Brudny 4 years ago 890e4e8
[EGD-7158] Added text paste into Phonebook

Added text paste into Phonebook. Added save
verification.
27 files changed, 306 insertions(+), 112 deletions(-)

M image/assets/lang/Deutsch.json
M image/assets/lang/English.json
M image/assets/lang/Espanol.json
M image/assets/lang/Francais.json
M image/assets/lang/Polski.json
M image/assets/lang/Svenska.json
M module-apps/application-messages/windows/OptionsMessages.cpp
M module-apps/application-notes/windows/NotesOptions.cpp
M module-apps/application-phonebook/ApplicationPhonebook.cpp
M module-apps/application-phonebook/CMakeLists.txt
M module-apps/application-phonebook/data/PhonebookItemData.hpp
M module-apps/application-phonebook/data/PhonebookStyle.hpp
M module-apps/application-phonebook/include/application-phonebook/ApplicationPhonebook.hpp
M module-apps/application-phonebook/models/NewContactModel.cpp
M module-apps/application-phonebook/models/NewContactModel.hpp
R module-apps/application-phonebook/widgets/{InputLinesWithLabelIWidget => InputLinesWithLabelWidget}.cpp
R module-apps/application-phonebook/widgets/{InputLinesWithLabelIWidget => InputLinesWithLabelWidget}.hpp
A module-apps/application-phonebook/windows/PhonebookInputOptions.cpp
A module-apps/application-phonebook/windows/PhonebookInputOptions.hpp
M module-apps/application-phonebook/windows/PhonebookNewContact.cpp
M module-gui/gui/widgets/ListItem.hpp
M module-gui/gui/widgets/header/Header.cpp
M module-gui/gui/widgets/header/Style.hpp
M module-gui/gui/widgets/text/Text.cpp
M module-gui/gui/widgets/text/Text.hpp
M module-gui/gui/widgets/text/TextConstants.hpp
M module-utils/utility/Utils.hpp
M image/assets/lang/Deutsch.json => image/assets/lang/Deutsch.json +3 -2
@@ 77,6 77,8 @@
  "common_minutes_lower": "minuten",
  "common_minute_short": "min",
  "common_paused": "Pausiert",
  "common_text_copy": "Text kopieren",
  "common_text_paste": "Text einfügen",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 164,8 166,6 @@
  "app_options_contact_edit": "Kontakte ändern",
  "app_notes_title_main": "Notizen",
  "app_notes_edit_new_note": "Ändern/Neue Notiz",
  "app_notes_copy_text": "Text kopieren",
  "app_notes_copy_paste": "Text einfügen",
  "app_notes_edit": "ÄNDERN",
  "app_notes_edited": "Geändert",
  "app_notes_delete_note": "Löschen",


@@ 547,6 547,7 @@
  "app_phonebook_new_contact_address": "Adresse",
  "app_phonebook_new_contact_note": "Notiz",
  "app_phonebook_new_speed_dial_key": "Kurzwahltaste",
  "app_phonebook_new_contact_invalid_number": "<text>Dieser Kontakt kann nicht gespeichert werden.<br></br>Die von Ihnen eingegebene Telefonnummer<br></br>hat ein ungültiges Format.</text>",
  "app_phonebook_new_add_to_fav": "Zu Favoriten hinzufügen",
  "app_phonebook_new_add_to_ice": "Zu ICE hinzufügen",
  "app_phonebook_check": "AUSWÄHLEN",

M image/assets/lang/English.json => image/assets/lang/English.json +3 -2
@@ 80,6 80,8 @@
  "common_minutes_lower": "minutes",
  "common_minute_short": "min",
  "common_paused": "Paused",
  "common_text_copy": "Copy text",
  "common_text_paste": "Paste text",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 136,8 138,6 @@
  "app_options_contact_edit": "Edit Contact",
  "app_notes_title_main": "Notes",
  "app_notes_edit_new_note": "Edit/New Note",
  "app_notes_copy_text": "Copy text",
  "app_notes_copy_paste": "Paste text",
  "app_notes_edit": "EDIT",
  "app_notes_edited": "Edited",
  "app_notes_delete_note": "Delete",


@@ 523,6 523,7 @@
  "app_phonebook_new_contact_address": "Address",
  "app_phonebook_new_contact_note": "Note",
  "app_phonebook_new_speed_dial_key": "Speed dial key",
  "app_phonebook_new_contact_invalid_number": "<text>Cannot save this contact.<br></br>The phone number you entered <br></br>is in an invalid format.</text>",
  "app_phonebook_new_add_to_fav": "Add to favourites",
  "app_phonebook_new_add_to_ice": "Emergency Contact (ICE)",
  "app_phonebook_check": "CHECK",

M image/assets/lang/Espanol.json => image/assets/lang/Espanol.json +3 -2
@@ 77,6 77,8 @@
  "common_minutes_lower": "minutos",
  "common_minute_short": "min",
  "common_paused": "Pausado",
  "common_text_copy": "Copiar texto",
  "common_text_paste": "Pegar texto",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 164,8 166,6 @@
  "app_options_contact_edit": "Editar contacto",
  "app_notes_title_main": "Notas",
  "app_notes_edit_new_note": "Editar/Nueva nota",
  "app_notes_copy_text": "Copiar texto",
  "app_notes_copy_paste": "Pegar texto",
  "app_notes_edit": "EDITAR",
  "app_notes_edited": "Editado",
  "app_notes_delete_note": "Eliminar",


@@ 547,6 547,7 @@
  "app_phonebook_new_contact_address": "Dirección",
  "app_phonebook_new_contact_note": "Nota",
  "app_phonebook_new_speed_dial_key": "Tecla de marcación rápida",
  "app_phonebook_new_contact_invalid_number": "<text>No se puede guardar este contacto.<br></br>El número de teléfono que ingresó <br></br>tiene un formato no válido.</text>",
  "app_phonebook_new_add_to_fav": "Añadir a favoritos",
  "app_phonebook_new_add_to_ice": "Añadir a contactos de emergencia",
  "app_phonebook_check": "MARCAR",

M image/assets/lang/Francais.json => image/assets/lang/Francais.json +3 -2
@@ 77,6 77,8 @@
  "common_minutes_lower": "minutes",
  "common_minute_short": "min",
  "common_paused": "En pause",
  "common_text_copy": "Copier le texte",
  "common_text_paste": "Coller le texte",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 133,8 135,6 @@
  "app_options_contact_edit": "Modifier le contact",
  "app_notes_title_main": "Notes",
  "app_notes_edit_new_note": "Modifier/ajouter une note",
  "app_notes_copy_text": "Copier le texte",
  "app_notes_copy_paste": "Coller le texte",
  "app_notes_edit": "MODIFIER",
  "app_notes_edited": "Modifiée",
  "app_notes_delete_note": "Supprimer",


@@ 514,6 514,7 @@
  "app_phonebook_new_contact_address": "Adresse",
  "app_phonebook_new_contact_note": "Note",
  "app_phonebook_new_speed_dial_key": "Touche de numérotation rapide",
  "app_phonebook_new_contact_invalid_number": "<text>Impossible d'enregistrer ce contact.<br></br>Le numéro de téléphone que vous avez entré <br></br>est dans un format invalide.</text>",
  "app_phonebook_new_add_to_fav": "Ajouter aux favoris",
  "app_phonebook_new_add_to_ice": "Ajouter aux contacts d'urgence",
  "app_phonebook_check": "COCHER",

M image/assets/lang/Polski.json => image/assets/lang/Polski.json +3 -2
@@ 77,6 77,8 @@
  "common_minutes_lower": "minuty",
  "common_minute_short": "min",
  "common_paused": "Pauza",
  "common_text_copy": "Kopiuj tekst",
  "common_text_paste": "Wklej tekst",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 168,8 170,6 @@
  "app_options_contact_edit": "Edytuj kontakt",
  "app_notes_title_main": "Notatki",
  "app_notes_edit_new_note": "Edytuj / Dodaj notatkę",
  "app_notes_copy_text": "Kopiuj tekst",
  "app_notes_copy_paste": "Wklej tekst",
  "app_notes_edit": "EDYTUJ",
  "app_notes_edited": "Edytowano",
  "app_notes_delete_note": "Usuń",


@@ 557,6 557,7 @@
  "app_phonebook_new_contact_address": "Adres",
  "app_phonebook_new_contact_note": "Notatka",
  "app_phonebook_new_speed_dial_key": "Klawisz szybkiego wybierania",
  "app_phonebook_new_contact_invalid_number": "<text>Nie można zapisać tego kontaktu.<br></br>Wprowadzony numer telefonu<br></br>ma nieprawidłowy format.</text>",
  "app_phonebook_new_add_to_ice": "Dodaj do kontaktów ICE",
  "app_phonebook_check": "ZAZNACZ",
  "app_phonebook_uncheck": "ODZNACZ",

M image/assets/lang/Svenska.json => image/assets/lang/Svenska.json +3 -2
@@ 65,6 65,8 @@
  "common_today": "Idag",
  "common_results_prefix": "Resultat: ",
  "common_search": "SÖK",
  "common_text_copy": "Kopiera text",
  "common_text_paste": "Klistra in text",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",


@@ 118,8 120,6 @@
  "app_options_contact_edit": "Redigera kontakt",
  "app_notes_title_main": "Anteckningar",
  "app_notes_edit_new_note": "Redigera/Skapa anteckning",
  "app_notes_copy_text": "Kopiera text",
  "app_notes_copy_paste": "Klistra in text",
  "app_notes_edit": "REDIGERA",
  "app_notes_edited": "Redigerad",
  "app_notes_delete_note": "Radera",


@@ 455,6 455,7 @@
  "app_phonebook_new_contact_address": "Adress",
  "app_phonebook_new_contact_note": "Anteckning",
  "app_phonebook_new_speed_dial_key": "Förvalsnummer",
  "app_phonebook_new_contact_invalid_number": "<text>Det går inte att spara den här kontakten.<br></br>Telefonnumret du angav<br></br>är i ett ogiltigt format.</text>",
  "app_phonebook_new_add_to_fav": "Lägg till bland favoriter",
  "app_phonebook_new_add_to_ice": "Lägg till bland ICE (kontakter i fall av olycka)",
  "app_phonebook_check": "MARKERA",

M module-apps/application-messages/windows/OptionsMessages.cpp => module-apps/application-messages/windows/OptionsMessages.cpp +1 -1
@@ 78,7 78,7 @@ std::list<gui::Option> newMessageWindowOptions(app::ApplicationMessages *app,

    if (Clipboard::getInstance().gotData()) {
        options.emplace_back(utils::translate("sms_paste"), [=](gui::Item &item) {
            text->addText(Clipboard::getInstance().paste());
            text->addText(Clipboard::getInstance().paste(), gui::AdditionType::perBlock);
            app->returnToPreviousWindow();
            return true;
        });

M module-apps/application-notes/windows/NotesOptions.cpp => module-apps/application-notes/windows/NotesOptions.cpp +4 -4
@@ 66,7 66,7 @@ namespace app::notes
    {
        std::list<gui::Option> options;
        addOption(
            {"app_notes_copy_text"},
            {"common_text_copy"},
            [application, textWidget](gui::Item &item) {
                if (textWidget != nullptr) {
                    Clipboard::getInstance().copy(textWidget->getText());


@@ 91,7 91,7 @@ namespace app::notes
    {
        std::list<gui::Option> options;
        addOption(
            {"app_notes_copy_text"},
            {"common_text_copy"},
            [application, textWidget](gui::Item &item) {
                if (textWidget != nullptr) {
                    Clipboard::getInstance().copy(textWidget->getText());


@@ 101,10 101,10 @@ namespace app::notes
            },
            options);
        addOption(
            {"app_notes_copy_paste"},
            {"common_text_paste"},
            [application, textWidget](gui::Item &item) {
                if (textWidget != nullptr) {
                    textWidget->addText(Clipboard::getInstance().paste());
                    textWidget->addText(Clipboard::getInstance().paste(), gui::AdditionType::perBlock);
                }
                application->returnToPreviousWindow();
                return true;

M module-apps/application-phonebook/ApplicationPhonebook.cpp => module-apps/application-phonebook/ApplicationPhonebook.cpp +4 -1
@@ 13,6 13,7 @@
#include "windows/PhonebookSearch.hpp"
#include "windows/PhonebookSearchResults.hpp"
#include "windows/PhonebookIceContacts.hpp"
#include "windows/PhonebookInputOptions.hpp"
#include <service-appmgr/Controller.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>


@@ 129,7 130,9 @@ namespace app
        windowsFactory.attach(gui::window::name::ice_contacts, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::PhonebookIceContacts>(app);
        });

        windowsFactory.attach(gui::window::name::input_options, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::PhonebookInputOptions>(app, name);
        });
        windowsFactory.attach(gui::window::name::new_contact, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::PhonebookNewContact>(app);
        });

M module-apps/application-phonebook/CMakeLists.txt => module-apps/application-phonebook/CMakeLists.txt +3 -2
@@ 21,13 21,14 @@ target_sources(application-phonebook
        widgets/ContactFlagsWidget.cpp
        widgets/InformationWidget.cpp
        widgets/InputBoxWithLabelAndIconWidget.cpp
        widgets/InputLinesWithLabelIWidget.cpp
        widgets/InputLinesWithLabelWidget.cpp
        widgets/OutputLinesTextWithLabelWidget.cpp
        widgets/PhonebookItem.cpp
        widgets/PhonebookListView.cpp
        windows/PhonebookContactDetails.cpp
        windows/PhonebookContactOptions.cpp
        windows/PhonebookIceContacts.cpp
        windows/PhonebookInputOptions.cpp
        windows/PhonebookMainWindow.cpp
        windows/PhonebookNamecardOptions.cpp
        windows/PhonebookNewContact.cpp


@@ 41,7 42,7 @@ target_sources(application-phonebook
        widgets/ContactListItem.hpp
        widgets/InformationWidget.hpp
        widgets/InputBoxWithLabelAndIconWidget.hpp
        widgets/InputLinesWithLabelIWidget.hpp
        widgets/InputLinesWithLabelWidget.hpp
        widgets/OutputLinesTextWithLabelWidget.hpp
        widgets/PhonebookItem.hpp
        widgets/PhonebookListView.hpp

M module-apps/application-phonebook/data/PhonebookItemData.hpp => module-apps/application-phonebook/data/PhonebookItemData.hpp +15 -0
@@ 60,3 60,18 @@ class PhonebookSearchRequest : public gui::SwitchData
    PhonebookSearchRequest()              = default;
    std::shared_ptr<ContactRecord> result = nullptr;
};

class PhonebookInputOptionData : public gui::SwitchData
{
  private:
    gui::Text *inputText;

  public:
    explicit PhonebookInputOptionData(gui::Text *inputText) : inputText(inputText)
    {}

    gui::Text *getInputText()
    {
        return inputText;
    }
};

M module-apps/application-phonebook/data/PhonebookStyle.hpp => module-apps/application-phonebook/data/PhonebookStyle.hpp +4 -0
@@ 112,6 112,10 @@ namespace phonebookStyle
        inline constexpr uint32_t span_size        = 8;
        inline constexpr uint32_t line_spacing     = 15;
        inline constexpr int32_t underline_padding = 4;

        inline constexpr auto minimum_signs_limit = 31U;
        inline constexpr auto medium_signs_limit  = 40U;
        inline constexpr auto max_signs_limit     = 200U;
    } // namespace inputLinesWithLabelWidget

    namespace outputLinesTextWithLabelWidget

M module-apps/application-phonebook/include/application-phonebook/ApplicationPhonebook.hpp => module-apps/application-phonebook/include/application-phonebook/ApplicationPhonebook.hpp +1 -0
@@ 12,6 12,7 @@ namespace gui::window::name
    inline constexpr auto contact                = "Contact";
    inline constexpr auto contact_options        = "Options";
    inline constexpr auto namecard_options       = "Namecard Options";
    inline constexpr auto input_options          = "Input Options";
    inline constexpr auto new_contact            = "New";
    inline constexpr auto search                 = "Search";
    inline constexpr auto search_results         = "SearchResults";

M module-apps/application-phonebook/models/NewContactModel.cpp => module-apps/application-phonebook/models/NewContactModel.cpp +58 -31
@@ 3,14 3,12 @@

#include "NewContactModel.hpp"

#include "AppWindow.hpp"
#include "application-phonebook/widgets/ContactListItem.hpp"
#include "application-phonebook/widgets/InputBoxWithLabelAndIconWidget.hpp"
#include "application-phonebook/widgets/InputLinesWithLabelIWidget.hpp"
#include "application-phonebook/widgets/InputLinesWithLabelWidget.hpp"
#include "application-phonebook/ApplicationPhonebook.hpp"

#include <ListView.hpp>
#include <time/ScopedTime.hpp>
#include <NavBar.hpp>
#include <messages/DialogMetadataMessage.hpp>

NewContactModel::NewContactModel(app::ApplicationCommon *app) : application(app)
{}


@@ 40,40 38,40 @@ void NewContactModel::createData()
{
    auto app = application;

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::FirstName,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->ContactDataChanged(); }));
        [&](gui::Text *text) { openTextOptions(text); }));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::SecondName,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->ContactDataChanged(); }));
        [&](gui::Text *text) { openTextOptions(text); }));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::Number,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->ContactDataChanged(); }));
        [&](gui::Text *text) { openTextOptions(text); }));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::SecondNumber,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->ContactDataChanged(); }));
        [&](gui::Text *text) { openTextOptions(text); }));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::Email,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->ContactDataChanged(); }));
        [&](gui::Text *text) { openTextOptions(text); }));

    internalData.push_back(new gui::InputBoxWithLabelAndIconWidget(
        phonebookInternals::ListItemName::AddToFavourites,


@@ 90,20 88,20 @@ void NewContactModel::createData()
    internalData.back()->setMargins(
        gui::Margins(style::widgets::leftMargin, style::margins::big, 0, style::margins::very_small));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::Address,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text, false); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        nullptr,
        [&](gui::Text *text) { openTextOptions(text); },
        2));

    internalData.push_back(new gui::InputLinesWithLabelIWidget(
    internalData.push_back(new gui::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName::Note,
        [app](const UTF8 &text) { app->getCurrentWindow()->navBarTemporaryMode(text, false); },
        [app](const UTF8 &text, bool emptyOthers) { app->getCurrentWindow()->navBarTemporaryMode(text, emptyOthers); },
        [app]() { app->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        nullptr,
        [&](gui::Text *text) { openTextOptions(text); },
        2));

    for (auto item : internalData) {


@@ 122,6 120,31 @@ void NewContactModel::clearData()
    list->rebuildList();
}

bool NewContactModel::verifyData()
{
    for (auto item : internalData) {
        if (item->onVerifyCallback) {
            std::string errorMessage;
            if (!item->onVerifyCallback(errorMessage)) {
                auto metaData = std::make_unique<gui::DialogMetadataMessage>(
                    gui::DialogMetadata{errorMessage,
                                        "error_W_G",
                                        utils::translate("app_phonebook_new_contact_invalid_number"),
                                        "",
                                        [=]() -> bool {
                                            application->returnToPreviousWindow();
                                            return true;
                                        }});

                application->switchWindow(gui::window::name::dialog, std::move(metaData));

                return false;
            }
        }
    }
    return true;
}

void NewContactModel::saveData(std::shared_ptr<ContactRecord> contactRecord)
{
    for (auto item : internalData) {


@@ 140,16 163,20 @@ void NewContactModel::loadData(std::shared_ptr<ContactRecord> contactRecord)
    }
}

void NewContactModel::ContactDataChanged()
bool NewContactModel::emptyData()
{
    for (auto item : internalData) {
        if (item->onEmptyCallback) {
            if (!item->onEmptyCallback()) {
                application->getCurrentWindow()->setNavBarActive(gui::nav_bar::Side::Center, true); // SAVE button
                return;
                return false;
            }
        }
    }
    application->getCurrentWindow()->setNavBarActive(gui::nav_bar::Side::Center, false); // SAVE button
    return;
    return true;
}

void NewContactModel::openTextOptions(gui::Text *text)
{
    std::unique_ptr<gui::SwitchData> data = std::make_unique<PhonebookInputOptionData>(text);
    application->switchWindow(gui::window::name::input_options, std::move(data));
}

M module-apps/application-phonebook/models/NewContactModel.hpp => module-apps/application-phonebook/models/NewContactModel.hpp +6 -4
@@ 12,22 12,24 @@

class NewContactModel : public app::InternalModel<gui::ContactListItem *>, public gui::ListItemProvider
{
  private:
    app::ApplicationCommon *application = nullptr;

    void openTextOptions(gui::Text *text);

  public:
    NewContactModel(app::ApplicationCommon *app);
    explicit NewContactModel(app::ApplicationCommon *app);

    void clearData();
    void saveData(std::shared_ptr<ContactRecord> contactRecord);
    void loadData(std::shared_ptr<ContactRecord> contactRecord);
    void createData();
    bool verifyData();
    bool emptyData();

    [[nodiscard]] auto requestRecordsCount() -> unsigned int override;

    [[nodiscard]] auto getMinimalItemSpaceRequired() const -> unsigned int override;

    auto getItem(gui::Order order) -> gui::ListItem * override;

    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    void ContactDataChanged();
};

R module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp => module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.cpp +76 -23
@@ 1,24 1,24 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "InputLinesWithLabelIWidget.hpp"

#include <Span.hpp>
#include "InputLinesWithLabelWidget.hpp"
#include "application-phonebook/data/PhonebookStyle.hpp"

#include <ContactRecord.hpp>
#include <Clipboard.hpp>
#include <i18n/i18n.hpp>
#include <utility>

namespace gui
{
    InputLinesWithLabelIWidget::InputLinesWithLabelIWidget(phonebookInternals::ListItemName listItemName,
                                                           std::function<void(const UTF8 &)> navBarTemporaryMode,
                                                           std::function<void()> navBarRestoreFromTemporaryMode,
                                                           std::function<void()> selectSpecialCharacter,
                                                           std::function<void()> contentChanged,
                                                           unsigned int lines)
        : listItemName(listItemName), checkTextContent(std::move(contentChanged))
    InputLinesWithLabelWidget::InputLinesWithLabelWidget(
        phonebookInternals::ListItemName listItemName,
        const std::function<void(const UTF8 &text, bool emptyOthers)> &navBarTemporaryMode,
        const std::function<void()> &navBarRestoreFromTemporaryMode,
        const std::function<void()> &selectSpecialCharacter,
        const std::function<void(Text *text)> &inputOptions,
        unsigned int lines)
        : listItemName(listItemName), navBarTemporaryMode(navBarTemporaryMode),
          navBarRestoreFromTemporaryMode(navBarRestoreFromTemporaryMode), inputOptions(inputOptions)
    {
        setMinimumSize(phonebookStyle::inputLinesWithLabelWidget::w,
                       phonebookStyle::inputLinesWithLabelWidget::title_label_h +


@@ 52,8 52,8 @@ namespace gui
        inputText->setFont(style::window::font::medium);
        inputText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=](const UTF8 &text) { this->navBarTemporaryMode(text, true); },
            [=]() { this->navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));
        inputText->setPenFocusWidth(style::window::default_border_focus_w);
        inputText->setPenWidth(style::window::default_border_no_focus_w);


@@ 68,20 68,36 @@ namespace gui
                inputText->setCursorStartPosition(CursorStartPosition::DocumentEnd);
                inputText->setUnderlineThickness(style::window::default_border_focus_w);
                inputText->setFont(style::window::font::mediumbold);

                if (!inputText->isEmpty() || Clipboard::getInstance().gotData()) {
                    this->navBarTemporaryMode(utils::translate(style::strings::common::options), false);
                }
            }
            else {
                inputText->setCursorStartPosition(CursorStartPosition::DocumentBegin);
                inputText->setUnderlineThickness(style::window::default_border_rect_no_focus);
                inputText->setFont(style::window::font::medium);

                this->navBarRestoreFromTemporaryMode();
            }
            return true;
        };

        inputCallback = [&](Item &item, const InputEvent &event) {
            auto result = inputText->onInput(event);
            if (checkTextContent != nullptr) {
                checkTextContent();

            if (!event.isShortRelease(gui::KeyCode::KEY_AST) &&
                (!inputText->isEmpty() || Clipboard::getInstance().gotData())) {
                this->navBarTemporaryMode(utils::translate(style::strings::common::options), false);
            }

            if (event.isShortRelease(gui::KeyCode::KEY_LF) &&
                (!inputText->isEmpty() || Clipboard::getInstance().gotData())) {
                if (this->inputOptions) {
                    this->inputOptions(inputText);
                }
            }

            return result;
        };



@@ 93,7 109,7 @@ namespace gui
        setEdges(RectangleEdge::None);
    }

    void InputLinesWithLabelIWidget::applyItemNameSpecificSettings()
    void InputLinesWithLabelWidget::applyItemNameSpecificSettings()
    {
        switch (listItemName) {
        case phonebookInternals::ListItemName::FirstName:


@@ 129,19 145,23 @@ namespace gui
            break;
        }
    }
    void InputLinesWithLabelIWidget::firstNameHandler()
    void InputLinesWithLabelWidget::firstNameHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_first_name"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::minimum_signs_limit);

        onSaveCallback  = [&](std::shared_ptr<ContactRecord> contact) { contact->primaryName = inputText->getText(); };
        onLoadCallback  = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->primaryName); };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void InputLinesWithLabelIWidget::secondNameHandler()
    void InputLinesWithLabelWidget::secondNameHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_last_name"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::minimum_signs_limit);

        onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) {
            contact->alternativeName = inputText->getText();


@@ 149,11 169,13 @@ namespace gui
        onLoadCallback  = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->alternativeName); };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void InputLinesWithLabelIWidget::numberHandler()
    void InputLinesWithLabelWidget::numberHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_number"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setInputMode(new InputMode({InputMode::phone}));
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::medium_signs_limit);

        onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) {
            if (inputText->getText().length() > 0) {


@@ 166,13 188,26 @@ namespace gui
                inputText->setText(contact->numbers[0].number.getEntered());
            }
        };

        onVerifyCallback = [&](std::string &errorMessage) {
            if (utils::is_phone_number(inputText->getText())) {
                return true;
            }
            else {
                errorMessage = inputText->getText();
                return false;
            }
        };

        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void InputLinesWithLabelIWidget::secondNumberHandler()
    void InputLinesWithLabelWidget::secondNumberHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_second_number"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setInputMode(new InputMode({InputMode::phone}));
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::medium_signs_limit);

        onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) {
            if (inputText->getText().length() > 0) {


@@ 180,34 215,52 @@ namespace gui
                    ContactRecord::Number(utils::PhoneNumber(inputText->getText()).getView()));
            }
        };

        onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) {
            if (contact->numbers.size() > 1) {
                inputText->setText(contact->numbers[1].number.getEntered());
            }
        };

        onVerifyCallback = [&](std::string &errorMessage) {
            if (utils::is_phone_number(inputText->getText())) {
                return true;
            }
            else {
                errorMessage = inputText->getText();
                return false;
            }
        };

        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void InputLinesWithLabelIWidget::emailHandler()
    void InputLinesWithLabelWidget::emailHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_email"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::max_signs_limit);

        onSaveCallback  = [&](std::shared_ptr<ContactRecord> contact) { contact->mail = inputText->getText(); };
        onLoadCallback  = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->mail); };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void InputLinesWithLabelIWidget::addressHandler()
    void InputLinesWithLabelWidget::addressHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_address"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::max_signs_limit);

        onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->address = inputText->getText(); };
        onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->address); };
    }
    void InputLinesWithLabelIWidget::noteHandler()
    void InputLinesWithLabelWidget::noteHandler()
    {
        titleLabel->setText(utils::translate("app_phonebook_new_contact_note"));
        inputText->setTextType(TextType::SingleLine);
        inputText->setTextLimitType(TextLimitType::MaxSignsCount,
                                    phonebookStyle::inputLinesWithLabelWidget::max_signs_limit);

        onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->note = inputText->getText(); };
        onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->note); };

R module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.hpp => module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.hpp +14 -12
@@ 13,27 13,29 @@

namespace gui
{
    class InputLinesWithLabelIWidget : public ContactListItem
    class InputLinesWithLabelWidget : public ContactListItem
    {
        phonebookInternals::ListItemName listItemName;

      public:
        InputLinesWithLabelIWidget(phonebookInternals::ListItemName listItemName,
                                   std::function<void(const UTF8 &text)> navBarTemporaryMode = nullptr,
                                   std::function<void()> navBarRestoreFromTemporaryMode      = nullptr,
                                   std::function<void()> selectSpecialCharacter              = nullptr,
                                   std::function<void()> contentChanged                      = nullptr,
                                   unsigned int lines                                        = 1);

        ~InputLinesWithLabelIWidget() override = default;
        explicit InputLinesWithLabelWidget(
            phonebookInternals::ListItemName listItemName,
            const std::function<void(const UTF8 &text, bool emptyOthers)> &navBarTemporaryMode = nullptr,
            const std::function<void()> &navBarRestoreFromTemporaryMode                        = nullptr,
            const std::function<void()> &selectSpecialCharacter                                = nullptr,
            const std::function<void(Text *text)> &inputOptions                                = nullptr,
            unsigned int lines                                                                 = 1);

        VBox *vBox               = nullptr;
        Label *titleLabel        = nullptr;
        TextFixedSize *inputText = nullptr;

      private:
        std::function<void()> checkTextContent = nullptr;
        phonebookInternals::ListItemName listItemName;
        void applyItemNameSpecificSettings();

        std::function<void(const UTF8 &text, bool emptyOthers)> navBarTemporaryMode = nullptr;
        std::function<void()> navBarRestoreFromTemporaryMode                        = nullptr;
        std::function<void(Text *text)> inputOptions                                = nullptr;

        void firstNameHandler();
        void secondNameHandler();
        void numberHandler();

A module-apps/application-phonebook/windows/PhonebookInputOptions.cpp => module-apps/application-phonebook/windows/PhonebookInputOptions.cpp +46 -0
@@ 0,0 1,46 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PhonebookInputOptions.hpp"
#include "application-phonebook/data/PhonebookItemData.hpp"

#include <Clipboard.hpp>
#include <ApplicationCommon.hpp>

namespace gui
{
    PhonebookInputOptions::PhonebookInputOptions(app::ApplicationCommon *app, std::string windowName)
        : OptionWindow(app, windowName)
    {}

    void PhonebookInputOptions::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (auto message = dynamic_cast<PhonebookInputOptionData *>(data)) {
            addOptions(inputOptionsList(message->getInputText()));
            optionsList->rebuildList();
        }
    }

    auto PhonebookInputOptions::inputOptionsList(gui::Text *text) -> std::list<gui::Option>
    {
        std::list<gui::Option> options;

        if (!text->isEmpty()) {
            options.emplace_back(utils::translate("common_text_copy"), [=](gui::Item &item) {
                Clipboard::getInstance().copy(text->getText());
                application->returnToPreviousWindow();
                return true;
            });
        }

        if (Clipboard::getInstance().gotData()) {
            options.emplace_back(utils::translate("common_text_paste"), [=](gui::Item &item) {
                text->addText(Clipboard::getInstance().paste(), AdditionType::perBlock);
                application->returnToPreviousWindow();
                return true;
            });
        }

        return options;
    }
} // namespace gui

A module-apps/application-phonebook/windows/PhonebookInputOptions.hpp => module-apps/application-phonebook/windows/PhonebookInputOptions.hpp +19 -0
@@ 0,0 1,19 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "OptionWindow.hpp"

namespace gui
{
    class PhonebookInputOptions : public OptionWindow
    {
      public:
        PhonebookInputOptions(app::ApplicationCommon *app, std::string windowName);
        void onBeforeShow(ShowMode mode, SwitchData *data) override;

      private:
        auto inputOptionsList(gui::Text *text) -> std::list<gui::Option>;
    };
} // namespace gui

M module-apps/application-phonebook/windows/PhonebookNewContact.cpp => module-apps/application-phonebook/windows/PhonebookNewContact.cpp +7 -14
@@ 53,6 53,7 @@ namespace gui
    {
        if (mode != ShowMode::GUI_SHOW_RETURN) {
            newContactModel->clearData();
            newContactModel->loadData(contact);
        }

        if (mode == ShowMode::GUI_SHOW_INIT) {


@@ 71,7 72,7 @@ namespace gui
            break;
        }

        newContactModel->loadData(contact);
        !newContactModel->emptyData() ? setSaveButtonVisible(true) : setSaveButtonVisible(false);
    }

    auto PhonebookNewContact::handleSwitchData(SwitchData *data) -> bool


@@ 89,20 90,17 @@ namespace gui
        if (contact == nullptr) {
            contactAction = ContactAction::Add;
            contact       = std::make_shared<ContactRecord>();
            setSaveButtonVisible(false);
            return true;
        }

        if (contact->ID == DB_ID_NONE) {
            contactAction = ContactAction::Add;
            setSaveButtonVisible(false);
        }
        else if (contact->isTemporary()) {
            contactAction = ContactAction::EditTemporary;
        }
        else {
            contactAction = ContactAction::Edit;
            setSaveButtonVisible(true);
        }

        return true;


@@ 115,15 113,12 @@ namespace gui

    auto PhonebookNewContact::onInput(const InputEvent &inputEvent) -> bool
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }
        auto ret = AppWindow::onInput(inputEvent);

        if (!inputEvent.isShortRelease()) {
            return false;
        }
        !newContactModel->emptyData() ? setSaveButtonVisible(true) : setSaveButtonVisible(false);

        if (inputEvent.is(gui::KeyCode::KEY_ENTER)) {
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER) && !newContactModel->emptyData() &&
            newContactModel->verifyData()) {
            auto tmpId  = contact->ID;
            contact     = std::make_shared<ContactRecord>();
            contact->ID = tmpId;


@@ 134,9 129,7 @@ namespace gui
            return true;
        }

        application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);

        return false;
        return ret;
    }

    auto PhonebookNewContact::verifyAndSave() -> bool

M module-gui/gui/widgets/ListItem.hpp => module-gui/gui/widgets/ListItem.hpp +5 -4
@@ 21,9 21,10 @@ namespace gui
    template <class T> class ListItemWithCallbacks : public ListItem
    {
      public:
        std::function<bool()> onEmptyCallback                         = nullptr;
        std::function<bool()> onContentChangedCallback                = nullptr;
        std::function<void(std::shared_ptr<T> record)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<T> record)> onLoadCallback = nullptr;
        std::function<bool()> onEmptyCallback                           = nullptr;
        std::function<bool()> onContentChangedCallback                  = nullptr;
        std::function<bool(std::string &errorMessage)> onVerifyCallback = nullptr;
        std::function<void(std::shared_ptr<T> record)> onSaveCallback   = nullptr;
        std::function<void(std::shared_ptr<T> record)> onLoadCallback   = nullptr;
    };
} /* namespace gui */

M module-gui/gui/widgets/header/Header.cpp => module-gui/gui/widgets/header/Header.cpp +1 -1
@@ 34,7 34,7 @@ namespace gui::header
    Item *Header::createTitle(const UTF8 &text)
    {
        title = new gui::Label(nullptr, 0, 0, 0, 0);
        title->setMaximumSize(getWidth(), getHeight());
        title->setMaximumSize(getWidth() - 2 * style::header::title::margins, getHeight());
        title->setFont(style::header::font::title);
        title->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        title->setPadding(gui::Padding(0, style::header::title::top_padding, 0, 0));

M module-gui/gui/widgets/header/Style.hpp => module-gui/gui/widgets/header/Style.hpp +1 -0
@@ 10,6 10,7 @@ namespace style::header
    namespace title
    {
        inline constexpr auto top_padding = 6u;
        inline constexpr auto margins     = 30u;
    }; // namespace title

    namespace navigation_indicator

M module-gui/gui/widgets/text/Text.cpp => module-gui/gui/widgets/text/Text.cpp +11 -2
@@ 121,12 121,21 @@ namespace gui
        textChangedCallback = std::move(callback);
    }

    void Text::addText(const UTF8 &text)
    void Text::addText(const UTF8 &text, AdditionType additionType)
    {
        if (text.length() == 0) {
            return;
        }
        *cursor << text;

        if (additionType == AdditionType::perChar) {
            *cursor << text;
        }
        else if (additionType == AdditionType::perBlock) {
            for (const auto &block : textToTextBlocks(text, format)) {
                *cursor << block;
            }
        }

        onTextChanged();
        drawLines();
    }

M module-gui/gui/widgets/text/Text.hpp => module-gui/gui/widgets/text/Text.hpp +1 -1
@@ 138,7 138,7 @@ namespace gui

        void setTextChangedCallback(TextChangedCallback &&callback);

        void addText(const UTF8 &text);
        void addText(const UTF8 &text, AdditionType additionType = AdditionType::perChar);
        void addText(TextBlock text);
        /// @defgroup richtext can be virtualized by parametrized RichTextParser virtual api ( as second param )
        /// @{

M module-gui/gui/widgets/text/TextConstants.hpp => module-gui/gui/widgets/text/TextConstants.hpp +6 -0
@@ 68,4 68,10 @@ namespace gui
        CantAdd
    };

    enum class AdditionType
    {
        perChar,
        perBlock
    };

} // namespace gui

M module-utils/utility/Utils.hpp => module-utils/utility/Utils.hpp +5 -0
@@ 45,6 45,11 @@ namespace utils
        return !s.empty() && std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
    }

    static inline bool is_phone_number(const std::string &s)
    {
        return s.find_first_not_of(" +#0123456789") == std::string::npos;
    }

    static inline std::string ltrim(const std::string &s)
    {
        size_t start = s.find_first_not_of(WHITESPACE);