size_t split_line(SplitStringLine *result, unsigned wid, std::wstring_view str, size_t offset, unsigned options) { str.remove_prefix(offset); const wchar_t *s = str.data(); size_t slen = str.length(); size_t cur = 0; // Current position unsigned curwid = 0;// Used screen width for (;;) { if (cur >= slen) { result->begin = offset; result->len = slen; result->wid = curwid; return (offset + slen); } if (!(options & SPLIT_NEWLINE_AS_SPACE) && s[cur] == L'\n') { result->begin = offset; result->len = cur; result->wid = curwid; return (offset + cur + 1); // Skip the newline character } unsigned w = ucs_width (s[cur]); if (curwid + w > wid) { break; } curwid += w; ++cur; } // Not the whold string. Neither has a newline character been encountered // Now scan backward to find a proper line-breaking point unsigned extra_skip = 0; if (!(options & SPLIT_CUT_WORD)) { unsigned xcur = cur; for (;;) { if (xcur == 0) { // This means that a single word is longer than a line. We have to split it break; } if ((!ucs_isalnum (s[xcur-1]) || !ucs_isalnum (s[xcur])) && allow_line_end (s[xcur-1]) && allow_line_beginning (s[xcur])) { cur = xcur; break; } curwid -= ucs_width (s[--xcur]); } while (cur+extra_skip+1<slen && s[cur+extra_skip]==L' ') { ++extra_skip; } } result->begin = offset; result->len = cur; result->wid = curwid; return (offset + cur + extra_skip); }
void append_richtext_line (std::wstring &text, RichTextLineList &lst, PaletteID id, const wchar_t *text_a, const std::wstring &text_b) { size_t len_a = wcslen (text_a); RichTextLine tmp_line = { text.length (), len_a + text_b.length (), id, ucs_width (text_a, len_a) + ucs_width (text_b) }; lst.push_back (tmp_line); text.append (text_a, len_a); text.append (text_b); }
void UIString::update () { unsigned lines = 0; unsigned max_width = 0; unsigned cur_line_width = 0; const wchar_t *cls = get_text ().c_str (); // Current line starting point const wchar_t *s; for (s = cls; *s; ++s) { if (*s == L'\n') { ++lines; max_width = maxU (max_width, cur_line_width); cur_line_width = 0; cls = s + 1; } else { cur_line_width += ucs_width (*s); } } if (s != cls) { ++lines; max_width = maxU (max_width, cur_line_width); } this->lines = lines; this->max_width = max_width; split_cache_wid = 0; split_cache.clear (); }
void append_richtext_line (std::wstring &text, RichTextLineList &lst, PaletteID id, unsigned repeat, wchar_t ch) { RichTextLine tmp_line = { text.length (), repeat, id, repeat * ucs_width (ch) }; lst.push_back (tmp_line); text.append (repeat, ch); }
void append_richtext_line (std::wstring &text, RichTextLineList &lst, PaletteID id, const std::wstring &line_text) { RichTextLine tmp_line = { text.length (), line_text.length (), id, ucs_width (line_text) }; lst.push_back (tmp_line); text += line_text; }
void append_richtext_line (std::wstring &text, RichTextLineList &lst, PaletteID id, const wchar_t *line_text) { size_t len = wcslen (line_text); RichTextLine tmp_line = { text.length (), len, id, ucs_width (line_text, len) }; lst.push_back (tmp_line); text.append (line_text, len); }
SplitStringLineList split_line(unsigned wid, std::wstring_view s) { SplitStringLineList ret; if (wid < 2) { // Robustness. Avoid dead loops for (size_t k = 0; k < s.length(); ++k) { ret.push_back({k, 1, ucs_width(s[k])}); } } else { for (unsigned offset = 0; offset < s.length(); ) { SplitStringLine line; offset = split_line(&line, wid, s, offset, 0); ret.push_back(std::move(line)); } } return ret; }
RichTextLineList combine_lines (std::wstring &str, const RichTextLineC *linec, size_t nlines) { str.clear (); RichTextLineList line_list (nlines); RichTextLineList::iterator it = line_list.begin (); for (; nlines; --nlines) { it->id = linec->id; size_t len = wcslen (linec->text); it->offset = str.length (); it->len = len; it->screen_wid = ucs_width (linec->text, len); str.append (linec->text, len); ++linec; ++it; } return line_list; }
void ListBox::redraw () { unsigned disp_wid = get_size().x - 1; // Reserve one for scrollbar unsigned disp_hgt = get_size().y; if (disp_wid==0 || disp_hgt==0) { return; } choose_palette (PALETTE_ID_LISTBOX); clear (); const Scroll::Info info = scroll_.get_info(); for (unsigned i=0; i<info.len; ++i) { const std::wstring &str = items[info.first+i]; choose_palette ((select_any && i==info.focus_pos) ? PALETTE_ID_LISTBOX_SELECT : PALETTE_ID_LISTBOX); Size pos{0, i}; unsigned strwid = ucs_width (str); // Are we going to overflow? if (strwid <= disp_wid) { //NO: pos = put (pos, str); if (select_any && i==info.focus_pos) // Focus? Highlight the whole line clear(pos, {disp_wid - pos.x, 1}); } else { // YES: Cannot display all size_t display_wchars = max_chars_in_width (str, maxS (disp_wid - 4, 0)); put (pos, str.c_str (), display_wchars); choose_palette (PALETTE_ID_LISTBOX); pos = put(Size(maxS(disp_wid - 4, 0), i), L' '); attribute_toggle (REVERSE); pos = put (pos, L"..."); } } // Display the scroll bar choose_palette (PALETTE_ID_LISTBOX); clear({disp_wid, 0}, {1, disp_hgt}); attribute_toggle (REVERSE); ScrollBarInfo scrollbar = scrollbar_info (disp_hgt, items.size(), info.first); clear({disp_wid, scrollbar.pos}, {1, scrollbar.size}); move_cursor({0, info.focus_pos}); }
void MainCtrl::redraw () { choose_palette (ui::PALETTE_ID_ENTRY); clear (); if (w().entries.empty ()) { put(ui::Size{}, L"No entry yet."); put(ui::Size{0, 1}, L"Press \"a\" to create one; press Esc for the menu."); #ifdef TIARY_USE_MOUSE put(ui::Size{0, 2}, L"You can also use your mouse."); #endif return; } if (w().filtered_entries_ && w().filtered_entries_->empty ()) { put(ui::Size{}, L"This is in filtered mode."); put(ui::Size{0, 1}, L"But no entry satisfies your requirement."); put(ui::Size{0, 2}, L"Press Ctrl-G to modify your filter; or LEFT to see all entries."); return; } unsigned expand_lines = w().global_options.get_num (GLOBAL_OPTION_EXPAND_LINES); scroll_.modify_height(get_size().y - expand_lines + 1); ui::Scroll::Info info = scroll_.get_info(); ui::Size pos{}; std::wstring date_format = w().global_options.get_wstring (GLOBAL_OPTION_DATETIME_FORMAT); // Build a map to get entry ID from pointer std::map <const DiaryEntry *, unsigned> id_map; for (size_t i=0; i<w().entries.size (); ++i) { id_map.insert (std::make_pair (w().entries[i], i+1)); } // Is there a filter? const DiaryEntryList &ent_lst = w().get_current_list (); wchar_t *disp_buffer = new wchar_t [get_size ().x]; for (unsigned i=0; i<info.len; ++i) { choose_palette (i == info.focus_pos ? ui::PALETTE_ID_ENTRY_SELECT : ui::PALETTE_ID_ENTRY); if (i == info.focus_pos) { move_cursor (pos); clear(pos, ui::Size{get_size().x, expand_lines}); } const DiaryEntry &entry = *ent_lst[i+info.first]; // Entry ID pos = put(pos, format(L"%04a "sv, id_map[&entry])); // Date choose_palette (i == info.focus_pos ? ui::PALETTE_ID_ENTRY_DATE_SELECT : ui::PALETTE_ID_ENTRY_DATE); pos = put(pos, entry.local_time.format(date_format)); pos.x++; // Title SplitStringLine split_info; const std::wstring &title = entry.title; choose_palette (i == info.focus_pos ? ui::PALETTE_ID_ENTRY_TITLE_SELECT : ui::PALETTE_ID_ENTRY_TITLE); split_line(&split_info, maxS (0, get_size().x-pos.x), title, 0, SPLIT_NEWLINE_AS_SPACE|SPLIT_CUT_WORD); pos = put (pos, disp_buffer, std::replace_copy_if ( &title[split_info.begin], &title[split_info.begin+split_info.len], disp_buffer, [](auto x) { return !iswprint(x); }, L' ') - disp_buffer); pos.x++; // Labels const DiaryEntry::LabelList &labels = entry.labels; choose_palette (i == info.focus_pos ? ui::PALETTE_ID_ENTRY_LABELS_SELECT : ui::PALETTE_ID_ENTRY_LABELS); int left_wid = get_size().x - pos.x; for (DiaryEntry::LabelList::const_iterator it=labels.begin(); it!=labels.end(); ) { if (left_wid < 3) { break; } unsigned labelwid = ucs_width (*it); if (labelwid + 2 > unsigned (left_wid)) { pos = put (pos, L"...", 3); break; } pos = put (pos, *it); if (++it != labels.end ()) { pos = put (pos, L','); } } pos.x++; choose_palette (i == info.focus_pos ? ui::PALETTE_ID_ENTRY_TEXT_SELECT : ui::PALETTE_ID_ENTRY_TEXT); const std::wstring &text = entry.text; size_t offset = 0; if (i == info.focus_pos && expand_lines >= 2) { // Current entry // [Date] [Title] [Labels] // [...] for (unsigned j=1; j<expand_lines; ++j) { pos = ui::Size{0, pos.y + 1}; offset = split_line(&split_info, get_size().x, text, offset, SPLIT_NEWLINE_AS_SPACE); wchar_t *bufend = std::replace_copy_if ( &text[split_info.begin], &text[split_info.begin+split_info.len], disp_buffer, [](auto x) { return !iswprint(x); }, L' '); pos = put (pos, disp_buffer, bufend-disp_buffer); } } else { // Other entry // [Date] [Title] [Labels] [...] offset = split_line(&split_info, maxS (0, get_size().x - pos.x), text, offset, SPLIT_NEWLINE_AS_SPACE|SPLIT_CUT_WORD); wchar_t *bufend = std::replace_copy_if ( &text[split_info.begin], &text[split_info.begin+split_info.len], disp_buffer, [](auto x) { return !iswprint(x); }, L' '); pos = put (pos, disp_buffer, bufend-disp_buffer); } pos = ui::Size{0, pos.y + 1}; } delete [] disp_buffer; }