diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index fa14d93dc..a002b7f8a 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -361,9 +361,10 @@ bool PythonToolbox::selectLeaf(int selectedRow) { m_selectableTableView.deselectTable(); ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow); const char * editedText = I18n::translate(node->insertedText()); + // strippedEditedText array needs to be in the same scope as editedText + char strippedEditedText[k_maxMessageSize]; if (node->stripInsertedText()) { int strippedEditedTextMaxLength = strlen(editedText)+1; - char strippedEditedText[k_maxMessageSize]; assert(strippedEditedTextMaxLength <= k_maxMessageSize); Shared::ToolboxHelpers::TextToInsertForCommandMessage(node->insertedText(), strippedEditedText, strippedEditedTextMaxLength, true); editedText = strippedEditedText; diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index d252fb0d2..16c3581dc 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -1,5 +1,6 @@ #include "graph_controller.h" #include "../shared/poincare_helpers.h" +#include "../shared/text_helpers.h" #include "../apps_container.h" #include @@ -110,16 +111,15 @@ void GraphController::reloadBannerView() { numberOfChar += strlcpy(buffer, legend, bufferSize); if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { legend = I18n::translate(I18n::Message::MeanDot); - numberOfChar += strlcpy(buffer+numberOfChar, legend, bufferSize - numberOfChar); + numberOfChar += strlcpy(buffer + numberOfChar, legend, bufferSize - numberOfChar); } else if (*m_selectedDotIndex < 0) { legend = I18n::translate(I18n::Message::Reg); - numberOfChar += strlcpy(buffer+numberOfChar, legend, bufferSize - numberOfChar); + numberOfChar += strlcpy(buffer + numberOfChar, legend, bufferSize - numberOfChar); } else { - numberOfChar += PrintFloat::convertFloatToText(std::round((float)*m_selectedDotIndex+1.0f), buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits), Constant::ShortNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); + numberOfChar += PrintFloat::convertFloatToText(std::round((float)*m_selectedDotIndex+1.0f), buffer + numberOfChar, bufferSize - numberOfChar, Constant::ShortNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); } legend = ") "; - strlcpy(buffer+numberOfChar, legend, bufferSize - numberOfChar); - buffer[k_maxLegendLength] = 0; + strlcpy(buffer + numberOfChar, legend, bufferSize - numberOfChar); m_bannerView.dotNameView()->setText(buffer); // Set "x=..." or "xmean=..." @@ -133,12 +133,9 @@ void GraphController::reloadBannerView() { } m_bannerView.abscissaSymbol()->setText(legend); - numberOfChar = PoincareHelpers::ConvertFloatToText(x, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits); - assert(UTF8Decoder::CharSizeOfCodePoint(' ') == 1); - for (int i = numberOfChar; i < k_maxLegendLength; i++) { - buffer[numberOfChar++] = ' '; - } - buffer[k_maxLegendLength] = 0; + numberOfChar = PoincareHelpers::ConvertFloatToText(x, buffer, bufferSize, Constant::MediumNumberOfSignificantDigits); + // Padding + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_bannerView.abscissaValue()->setText(buffer); // Set "y=..." or "ymean=..." @@ -151,11 +148,9 @@ void GraphController::reloadBannerView() { y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1); } numberOfChar += strlcpy(buffer, legend, bufferSize); - numberOfChar += PoincareHelpers::ConvertFloatToText(y, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits); - for (int i = numberOfChar; i < k_maxLegendLength; i++) { - buffer[numberOfChar++] = ' '; - } - buffer[k_maxLegendLength] = 0; + numberOfChar += PoincareHelpers::ConvertFloatToText(y, buffer + numberOfChar, bufferSize - numberOfChar, Constant::MediumNumberOfSignificantDigits); + // Padding + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_bannerView.ordinateView()->setText(buffer); // Set formula @@ -176,10 +171,8 @@ void GraphController::reloadBannerView() { // Force the "Data not suitable" message to be on the next line int numberOfCharToCompleteLine = maxInt(Ion::Display::Width / BannerView::Font()->glyphSize().width() - strlen(I18n::translate(formula)), 0); numberOfChar = 0; - for (int i = 0; i < numberOfCharToCompleteLine-1; i++) { - buffer[numberOfChar++] = ' '; - } - buffer[numberOfChar] = 0; + // Padding + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, numberOfCharToCompleteLine - 1); m_bannerView.subTextAtIndex(0)->setText(buffer); const char * dataNotSuitableMessage = I18n::translate(I18n::Message::DataNotSuitableForRegression); @@ -195,8 +188,7 @@ void GraphController::reloadBannerView() { char leg[] = {' ', coefficientName, '=', 0}; legend = leg; numberOfChar += strlcpy(buffer, legend, bufferSize); - numberOfChar += PoincareHelpers::ConvertFloatToText(coefficients[i], buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - buffer[k_maxLegendLength] = 0; + numberOfChar += PoincareHelpers::ConvertFloatToText(coefficients[i], buffer + numberOfChar, bufferSize - numberOfChar, Constant::LargeNumberOfSignificantDigits); m_bannerView.subTextAtIndex(i)->setText(buffer); coefficientName++; } @@ -207,8 +199,7 @@ void GraphController::reloadBannerView() { legend = " r="; double r = m_store->correlationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); - numberOfChar += PoincareHelpers::ConvertFloatToText(r, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - buffer[k_maxLegendLength+10] = 0; + numberOfChar += PoincareHelpers::ConvertFloatToText(r, buffer + numberOfChar, bufferSize - numberOfChar, Constant::LargeNumberOfSignificantDigits); m_bannerView.subTextAtIndex(2)->setText(buffer); // Set "r2=..." @@ -216,8 +207,7 @@ void GraphController::reloadBannerView() { legend = " r2="; double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); - numberOfChar += PoincareHelpers::ConvertFloatToText(r2, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - buffer[k_maxLegendLength] = 0; + numberOfChar += PoincareHelpers::ConvertFloatToText(r2, buffer + numberOfChar, bufferSize - numberOfChar, Constant::LargeNumberOfSignificantDigits); m_bannerView.subTextAtIndex(3)->setText(buffer); // Clean the last subview diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 0658f1b6e..1ef3a07b6 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -65,6 +65,7 @@ app_src += $(addprefix apps/shared/,\ text_field_delegate.cpp \ text_field_delegate_app.cpp \ text_field_with_extension.cpp \ + text_helpers.cpp \ toolbox_helpers.cpp \ values_controller.cpp \ values_function_parameter_controller.cpp \ diff --git a/apps/shared/text_helpers.cpp b/apps/shared/text_helpers.cpp new file mode 100644 index 000000000..199f9f29d --- /dev/null +++ b/apps/shared/text_helpers.cpp @@ -0,0 +1,25 @@ +#include "text_helpers.h" +#include +#include +#include + +namespace Shared { +namespace TextHelpers { + +void PadWithSpaces(char * buffer, int bufferSize, int * currentNumberOfChar, int maxGlyphLengthWithPadding) { + assert(*currentNumberOfChar <= bufferSize); + size_t currentGlyphLength = UTF8Helper::StringGlyphLength(buffer, *currentNumberOfChar); + bool addedPadding = false; + assert(UTF8Decoder::CharSizeOfCodePoint(' ') == 1); + while (currentGlyphLength < maxGlyphLengthWithPadding && *currentNumberOfChar < bufferSize) { + *currentNumberOfChar = *currentNumberOfChar + UTF8Decoder::CodePointToChars(' ', buffer + *currentNumberOfChar, bufferSize - *currentNumberOfChar); + addedPadding = true; + currentGlyphLength++; + } + if (addedPadding) { + buffer[*currentNumberOfChar-1] = 0; + } +} + +} +} diff --git a/apps/shared/text_helpers.h b/apps/shared/text_helpers.h new file mode 100644 index 000000000..06b5f7c66 --- /dev/null +++ b/apps/shared/text_helpers.h @@ -0,0 +1,12 @@ +#ifndef SHARED_TEXT_HELPERS_H +#define SHARED_TEXT_HELPERS_H + +namespace Shared { +namespace TextHelpers { + +void PadWithSpaces(char * buffer, int bufferSize, int * currentNumberOfChar, int maxGlyphLengthWithPadding); + +} +} + +#endif diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index fcd94ed73..60b0e5338 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -1,6 +1,7 @@ #include "histogram_controller.h" #include "../apps_container.h" #include "../shared/poincare_helpers.h" +#include "../shared/text_helpers.h" #include "app.h" #include #include @@ -90,20 +91,6 @@ Responder * HistogramController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()->parentResponder()); } -void pad(char * buffer, int bufferSize, int * currentNumberOfChar, int maxGlyphLengthWithPadding) { - assert(*currentNumberOfChar <= bufferSize); - size_t currentGlyphLength = UTF8Helper::StringGlyphLength(buffer, *currentNumberOfChar); - bool addedPadding = false; - while (currentGlyphLength < maxGlyphLengthWithPadding && *currentNumberOfChar < bufferSize) { - *currentNumberOfChar = *currentNumberOfChar + UTF8Decoder::CodePointToChars(' ', buffer + *currentNumberOfChar, bufferSize - *currentNumberOfChar); - addedPadding = true; - currentGlyphLength++; - } - if (addedPadding) { - buffer[*currentNumberOfChar-1] = 0; - } -} - void HistogramController::reloadBannerView() { if (selectedSeriesIndex() < 0) { return; @@ -134,7 +121,7 @@ void HistogramController::reloadBannerView() { numberOfChar+= UTF8Decoder::CodePointToChars('[', buffer + numberOfChar, bufferSize - numberOfChar); // Padding - pad(buffer, bufferSize, &numberOfChar, k_maxIntervalLegendLength); + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, k_maxIntervalLegendLength); m_view.bannerView()->intervalView()->setText(buffer); // Add Size Data @@ -149,7 +136,7 @@ void HistogramController::reloadBannerView() { numberOfChar += PoincareHelpers::ConvertFloatToText(size, buffer+numberOfChar, bufferSize-numberOfChar, Constant::LargeNumberOfSignificantDigits); } // Padding - pad(buffer, bufferSize, &numberOfChar, k_maxLegendLength); + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_view.bannerView()->sizeView()->setText(buffer); // Add Frequency Data @@ -163,7 +150,7 @@ void HistogramController::reloadBannerView() { numberOfChar += PoincareHelpers::ConvertFloatToText(frequency, buffer+numberOfChar, bufferSize - numberOfChar, Constant::LargeNumberOfSignificantDigits); } // Padding - pad(buffer, bufferSize, &numberOfChar, k_maxLegendLength); + Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_view.bannerView()->frequencyView()->setText(buffer); m_view.bannerView()->reload(); diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 912e070c7..491458d66 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -287,25 +287,37 @@ void LayoutField::insertLayoutAtCursor(Layout layoutR, Poincare::Expression corr cursor->showEmptyLayoutIfNeeded(); bool layoutWillBeMerged = layoutR.type() == LayoutNode::Type::HorizontalLayout; - Layout lastMergedLayoutChild = layoutWillBeMerged ? layoutR.childAtIndex(layoutR.numberOfChildren()-1) : Layout(); - - // Find the layout where the cursor will point - assert(!correspondingExpression.isUninitialized()); - Layout cursorLayout = forceCursorRightOfLayout ? layoutR : layoutR.layoutToPointWhenInserting(&correspondingExpression); - assert(!cursorLayout.isUninitialized()); + Layout lastMergedLayoutChild = (layoutWillBeMerged && layoutR.numberOfChildren() > 0) ? layoutR.childAtIndex(layoutR.numberOfChildren()-1) : Layout(); // Add the layout. This puts the cursor at the right of the added layout cursor->addLayoutAndMoveCursor(layoutR); /* Move the cursor if needed. - * If the layout to point to has been merged, it means that only its children - * have been inserted in the layout, so we must not move the cursor to the - * parent. In this case, addLayoutAndMoveCursor made the cursor point to the - * last merged child, which is what is wanted. - * For other cases, move the cursor to the computed layout. */ - if (!(layoutWillBeMerged && cursorLayout == layoutR)) { - cursor->setLayout(cursorLayout); - cursor->setPosition(LayoutCursor::Position::Right); + * + * If forceCursorRightOfLayout is true, there is no need to move the cursor + * because it already points to the right of the added layout. + * + * If the layout to point to has been merged, only its children have been + * inserted in the layout, so we must not move the cursor to the parent. + * addLayoutAndMoveCursor made the cursor point to the last merged child, + * which is what is wanted. + * + * For other cases, move the cursor to the layout indicated by + * layoutToPointWhenInserting. This pointed layout cannot be computed before + * adding layoutR, because addLayoutAndMoveCursor might have changed layoutR's + * children. + * For instance, if we add an absolute value with an empty child left of a 0, + * the empty child is deleted and the 0 is collapsed into the absolute value. + * Sketch of the situation, ' being the cursor: + * Initial layout: '0 + * "abs(x)" pressed in the toolbox => |•| is added, • being an empty layout + * Final layout: |0'| + * */ + + if (!forceCursorRightOfLayout && !layoutWillBeMerged) { + assert(!correspondingExpression.isUninitialized()); + m_contentView.cursor()->setLayout(layoutR.layoutToPointWhenInserting(&correspondingExpression)); + m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); } // Handle matrices diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index a972fc079..2b4391f7d 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -340,13 +340,16 @@ size_t StringGlyphLength(const char * s, int maxSize) { return 0; } UTF8Decoder decoder(s); - CodePoint codePoint = decoder.nextCodePoint(); + CodePoint codePoint = 0; size_t glyphIndex = 0; - while (codePoint != UCodePointNull && (maxSize < 0 || ((decoder.stringPosition() - s) <= maxSize))) { + while (maxSize < 0 || ((decoder.stringPosition() - s) < maxSize)) { + codePoint = decoder.nextCodePoint(); + if (codePoint == UCodePointNull) { + break; + } if (!codePoint.isCombining()) { glyphIndex++; } - codePoint = decoder.nextCodePoint(); } return glyphIndex; } diff --git a/ion/test/utf8_helper.cpp b/ion/test/utf8_helper.cpp index 975b5cff1..23912121f 100644 --- a/ion/test/utf8_helper.cpp +++ b/ion/test/utf8_helper.cpp @@ -10,5 +10,6 @@ QUIZ_CASE(ion_utf8_helper_glyph_length) { assert_string_glyph_length_is("1ᴇ3", -1, 3); assert_string_glyph_length_is("∑∫𝐢", -1, 3); assert_string_glyph_length_is("123", 2, 2); - assert_string_glyph_length_is("1ᴇ3", 2, 1); + uint8_t testString[] = {'a', 'b', 'c', 0b11111111, 0b11111111, 0}; // Malformed utf-8 string + assert_string_glyph_length_is((const char *)testString, 3, 3); }