Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/control/listview.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <sfx2/listview.hxx>
11
12
#include <sfx2/sfxresid.hxx>
13
#include <tools/urlobj.hxx>
14
#include <tools/datetime.hxx>
15
#include <sfx2/strings.hrc>
16
#include <osl/file.hxx>
17
#include <osl/time.h>
18
#include <comphelper/fileurl.hxx>
19
20
#include <svtools/svtresid.hxx>
21
#include <svtools/strings.hrc>
22
#include <unotools/localedatawrapper.hxx>
23
#include <unotools/collatorwrapper.hxx>
24
#include <unotools/syslocale.hxx>
25
#include <unotools/intlwrapper.hxx>
26
#include <vcl/vclenum.hxx>
27
#include <vcl/wintypes.hxx>
28
29
#include <bitmaps.hlst>
30
#include <rtl/math.hxx>
31
32
#include <sfx2/templatelocalview.hxx>
33
34
0
#define COLUMN_IMG_ISDEFAULT 0
35
0
#define COLUMN_NAME 1
36
0
#define COLUMN_CATEGORY 2
37
0
#define COLUMN_APPLICATION 3
38
0
#define COLUMN_MODIFIED 4
39
0
#define COLUMN_SIZE 5
40
0
#define NUMBER_OF_COLUMNS 6
41
42
static sal_uInt64 getFileSize(const OUString& rURL);
43
static sal_uInt32 getFileModifyTime(const OUString& rURL);
44
static OUString getDisplayFileSize(const OUString& rURL);
45
static OUString getDisplayFileModifyTime(const OUString& rURL);
46
static OUString getApplication(std::u16string_view rURL);
47
48
ListView::ListView(std::unique_ptr<weld::TreeView> xTreeView)
49
0
    : mxTreeView(std::move(xTreeView))
50
0
    , mnSortColumn(-2)
51
0
{
52
0
    auto nDigitWidth = mxTreeView->get_approximate_digit_width();
53
0
    std::vector<int> aWidths{
54
0
        static_cast<int>(nDigitWidth * 5), /* Icon Column */
55
0
        static_cast<int>(nDigitWidth * 24), /* Name Column */
56
0
        static_cast<int>(nDigitWidth * 22), /* Category Column */
57
0
        static_cast<int>(nDigitWidth * 15), /* Application Column */
58
0
        static_cast<int>(nDigitWidth * 18) /* Modify Column */
59
0
    };
60
61
    // tdf#151143 Make the size of ListView and ThumbnailView the same
62
0
    mxTreeView->set_size_request(TEMPLATE_ITEM_MAX_WIDTH * 5, TEMPLATE_ITEM_MAX_HEIGHT_SUB * 3);
63
64
0
    mxTreeView->set_column_fixed_widths(aWidths);
65
0
    mxTreeView->set_selection_mode(SelectionMode::Multiple);
66
0
    mxTreeView->connect_query_tooltip(LINK(this, ListView, QueryTooltipHdl));
67
0
    mxTreeView->connect_column_header_clicked(LINK(this, ListView, ColumnClickedHdl));
68
0
}
69
0
ListView::~ListView() {}
70
71
void ListView::AppendItem(const OUString& rId, const OUString& rTitle, const OUString& rSubtitle,
72
                          const OUString& rPath, bool bDefault)
73
0
{
74
0
    INetURLObject aUrl(rPath, INetProtocol::File);
75
76
0
    std::unique_ptr<ListViewItem> pItem(new ListViewItem);
77
0
    pItem->maId = rId;
78
0
    pItem->maTitle = rTitle;
79
0
    pItem->maSubtitle = rSubtitle;
80
0
    pItem->maApplication = getApplication(rPath);
81
0
    pItem->maPath = rPath;
82
0
    pItem->mbDefault = bDefault;
83
0
    pItem->mnModify = getFileModifyTime(rPath);
84
0
    pItem->mnSize = getFileSize(rPath);
85
0
    pItem->maDisplayModify = getDisplayFileModifyTime(rPath);
86
0
    pItem->maDisplaySize = getDisplayFileSize(rPath);
87
0
    pItem->maDisplayPath = aUrl.getFSysPath(FSysStyle::Detect);
88
89
0
    OUString sImage(u""_ustr);
90
0
    if (pItem->mbDefault)
91
0
        sImage = BMP_DEFAULT;
92
93
0
    AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
94
0
              pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
95
96
0
    mListViewItems.push_back(std::move(pItem));
97
0
}
98
99
void ListView::AppendRow(const OUString& rImage, const OUString& rTitle, const OUString& rSubtitle,
100
                         const OUString& rApplication, const OUString& rModify,
101
                         const OUString& rSize, const OUString& rId)
102
0
{
103
0
    std::unique_ptr<weld::TreeIter> xIter(mxTreeView->make_iterator());
104
0
    mxTreeView->append(xIter.get());
105
0
    mxTreeView->set_image(*xIter, rImage, COLUMN_IMG_ISDEFAULT);
106
0
    mxTreeView->set_text(*xIter, rTitle, COLUMN_NAME);
107
0
    mxTreeView->set_text(*xIter, rSubtitle, COLUMN_CATEGORY);
108
0
    mxTreeView->set_text(*xIter, rApplication, COLUMN_APPLICATION);
109
0
    mxTreeView->set_text(*xIter, rModify, COLUMN_MODIFIED);
110
0
    mxTreeView->set_text(*xIter, rSize, COLUMN_SIZE);
111
0
    mxTreeView->set_id(*xIter, rId);
112
0
}
113
114
void ListView::UpdateRow(int nIndex, const OUString& rImage, const OUString& rTitle,
115
                         const OUString& rSubtitle, const OUString& rApplication,
116
                         const OUString& rModify, const OUString& rSize, const OUString& rId)
117
0
{
118
0
    mxTreeView->set_image(nIndex, rImage, COLUMN_IMG_ISDEFAULT);
119
0
    mxTreeView->set_text(nIndex, rTitle, COLUMN_NAME);
120
0
    mxTreeView->set_text(nIndex, rSubtitle, COLUMN_CATEGORY);
121
0
    mxTreeView->set_text(nIndex, rApplication, COLUMN_APPLICATION);
122
0
    mxTreeView->set_text(nIndex, rModify, COLUMN_MODIFIED);
123
0
    mxTreeView->set_text(nIndex, rSize, COLUMN_SIZE);
124
0
    mxTreeView->set_id(nIndex, rId);
125
0
}
126
127
void ListView::ReloadRows()
128
0
{
129
0
    OUString sCursorId = get_id(get_cursor_index());
130
0
    mxTreeView->clear();
131
0
    for (const auto& pItem : mListViewItems)
132
0
    {
133
0
        OUString sImage(u""_ustr);
134
0
        if (pItem->mbDefault)
135
0
            sImage = BMP_DEFAULT;
136
0
        AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
137
0
                  pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
138
0
    }
139
0
    unselect_all();
140
0
    if (!sCursorId.isEmpty())
141
0
    {
142
0
        select_id(sCursorId);
143
0
        set_cursor(get_selected_index());
144
0
    }
145
0
}
146
147
bool ListView::UpdateRows()
148
0
{
149
0
    if (static_cast<int>(mListViewItems.size()) != mxTreeView->n_children())
150
0
        return false;
151
0
    OUString sCursorId = get_id(get_cursor_index());
152
0
    int nIndex = 0;
153
0
    for (const auto& pItem : mListViewItems)
154
0
    {
155
0
        OUString sImage(u""_ustr);
156
0
        if (pItem->mbDefault)
157
0
            sImage = BMP_DEFAULT;
158
0
        UpdateRow(nIndex, sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
159
0
                  pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
160
0
        ++nIndex;
161
0
    }
162
0
    unselect_all();
163
0
    if (!sCursorId.isEmpty())
164
0
    {
165
0
        select_id(sCursorId);
166
0
        set_cursor(get_selected_index());
167
0
    }
168
0
    return true;
169
0
}
170
171
IMPL_LINK(ListView, ColumnClickedHdl, const int, col, void)
172
0
{
173
0
    if (col <= 0 || col > NUMBER_OF_COLUMNS)
174
0
        return;
175
176
0
    if (mnSortColumn >= 0 && mnSortColumn != col)
177
0
        mxTreeView->set_sort_indicator(TriState::TRISTATE_INDET, mnSortColumn);
178
179
0
    mxTreeView->set_sort_indicator((mxTreeView->get_sort_indicator(col) == TriState::TRISTATE_TRUE
180
0
                                        ? TriState::TRISTATE_FALSE
181
0
                                        : TriState::TRISTATE_TRUE),
182
0
                                   col);
183
0
    sortColumn(col);
184
0
}
185
186
void ListView::sortColumn(const int col)
187
0
{
188
0
    if (col <= 0 || col > NUMBER_OF_COLUMNS)
189
0
        return;
190
191
0
    bool isAscending = mxTreeView->get_sort_indicator(col) != TriState::TRISTATE_FALSE;
192
193
0
    auto comp = [&](std::unique_ptr<ListViewItem> const& pItemA,
194
0
                    std::unique_ptr<ListViewItem> const& pItemB) {
195
0
        sal_Int32 res = 0;
196
0
        IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
197
0
        const CollatorWrapper* pCollatorWrapper = aIntlWrapper.getCollator();
198
0
        switch (col)
199
0
        {
200
0
            case COLUMN_NAME:
201
0
            {
202
0
                OUString sNameA = pItemA->maTitle;
203
0
                OUString sNameB = pItemB->maTitle;
204
0
                res = pCollatorWrapper->compareString(sNameA, sNameB);
205
0
            }
206
0
            break;
207
0
            case COLUMN_CATEGORY:
208
0
            {
209
0
                OUString sCategoryA = pItemA->maSubtitle;
210
0
                OUString sCategoryB = pItemB->maSubtitle;
211
0
                res = pCollatorWrapper->compareString(sCategoryA, sCategoryB);
212
0
            }
213
0
            break;
214
0
            case COLUMN_MODIFIED:
215
0
            {
216
0
                sal_uInt32 nModA, nModB;
217
0
                nModA = pItemA->mnModify;
218
0
                nModB = pItemB->mnModify;
219
220
0
                if (nModA < nModB)
221
0
                    res = -1;
222
0
                else if (nModA > nModB)
223
0
                    res = 1;
224
0
            }
225
0
            break;
226
0
            case COLUMN_SIZE:
227
0
            {
228
0
                sal_uInt64 nSizeA, nSizeB;
229
0
                nSizeA = pItemA->mnSize;
230
0
                nSizeB = pItemB->mnSize;
231
232
0
                if (nSizeA < nSizeB)
233
0
                    res = -1;
234
0
                else if (nSizeA > nSizeB)
235
0
                    res = 1;
236
0
            }
237
0
            break;
238
0
            case COLUMN_APPLICATION:
239
0
            {
240
0
                OUString sPathA = pItemA->maApplication;
241
0
                OUString sPathB = pItemB->maApplication;
242
0
                res = pCollatorWrapper->compareString(sPathA, sPathB);
243
0
            }
244
0
            break;
245
0
        }
246
0
        return isAscending ? (res > 0) : (res < 0);
247
0
    };
248
0
    std::stable_sort(mListViewItems.begin(), mListViewItems.end(), comp);
249
250
0
    if (!UpdateRows())
251
0
        ReloadRows();
252
0
    mnSortColumn = col;
253
0
}
254
255
0
void ListView::sort() { sortColumn(mnSortColumn); }
256
257
void ListView::refreshDefaultColumn()
258
0
{
259
0
    for (const auto& pItem : mListViewItems)
260
0
    {
261
0
        bool bDefault = TemplateLocalView::IsDefaultTemplate(pItem->maPath);
262
0
        if (pItem->mbDefault != bDefault)
263
0
        {
264
0
            pItem->mbDefault = bDefault;
265
0
            OUString sImage(u""_ustr);
266
0
            if (bDefault)
267
0
                sImage = BMP_DEFAULT;
268
0
            mxTreeView->set_image(mxTreeView->find_id(pItem->maId), sImage, COLUMN_IMG_ISDEFAULT);
269
0
        }
270
0
    }
271
0
}
272
273
void ListView::rename(const OUString& rId, const OUString& rTitle)
274
0
{
275
0
    mxTreeView->set_text(mxTreeView->find_id(rId), rTitle, COLUMN_NAME);
276
0
    for (const auto& pItem : mListViewItems)
277
0
        if (pItem->maId == rId)
278
0
        {
279
0
            pItem->maTitle = rTitle;
280
0
            break;
281
0
        }
282
0
}
283
284
void ListView::clearListView()
285
0
{
286
0
    mxTreeView->clear();
287
0
    mListViewItems.clear();
288
0
}
289
290
IMPL_LINK(ListView, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
291
0
{
292
0
    OUString sId = mxTreeView->get_id(rIter);
293
0
    for (const auto& pItem : mListViewItems)
294
0
    {
295
0
        if (pItem->maId == sId)
296
0
            return pItem->maDisplayPath;
297
0
    }
298
0
    return OUString();
299
0
}
300
301
sal_uInt16 ListView::get_nId(int pos) const
302
0
{
303
0
    return static_cast<sal_uInt16>(mxTreeView->get_id(pos).toInt32());
304
0
}
305
306
static sal_uInt32 getFileModifyTime(const OUString& rURL)
307
0
{
308
0
    sal_uInt32 nModify = 0;
309
0
    if (!comphelper::isFileUrl(rURL))
310
0
        return nModify;
311
312
0
    osl::DirectoryItem aItem;
313
0
    if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
314
0
        return nModify;
315
316
0
    osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime);
317
0
    if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
318
0
        return nModify;
319
320
0
    TimeValue systemTimeValue = aStatus.getModifyTime();
321
322
0
    nModify = systemTimeValue.Seconds;
323
0
    return nModify;
324
0
}
325
static OUString getDisplayFileModifyTime(const OUString& rURL)
326
0
{
327
0
    if (!comphelper::isFileUrl(rURL))
328
0
        return OUString();
329
330
0
    osl::DirectoryItem aItem;
331
0
    if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
332
0
        return OUString();
333
334
0
    osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime);
335
0
    if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
336
0
        return OUString();
337
338
0
    TimeValue systemTimeValue = aStatus.getModifyTime();
339
0
    if (systemTimeValue.Seconds == 0)
340
0
        return OUString();
341
0
    TimeValue localTimeValue;
342
0
    osl_getLocalTimeFromSystemTime(&systemTimeValue, &localTimeValue);
343
0
    const SvtSysLocale aSysLocale;
344
0
    const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
345
0
    DateTime aDateTime = DateTime::CreateFromUnixTime(localTimeValue.Seconds);
346
0
    OUString aDisplayDateTime
347
0
        = rLocaleWrapper.getDate(aDateTime) + ", " + rLocaleWrapper.getTime(aDateTime, false);
348
0
    return aDisplayDateTime;
349
0
}
350
351
static OUString getDisplayFileSize(const OUString& rURL)
352
0
{
353
0
    if (!comphelper::isFileUrl(rURL))
354
0
        return OUString();
355
356
0
    osl::DirectoryItem aItem;
357
0
    if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
358
0
        return OUString();
359
360
0
    osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize);
361
0
    if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
362
0
        return OUString();
363
364
0
    sal_uInt64 nSize = aStatus.getFileSize();
365
0
    double fSize(static_cast<double>(nSize));
366
0
    sal_uInt32 nDec;
367
368
0
    sal_uInt64 nMega = 1024 * 1024;
369
0
    sal_uInt64 nGiga = nMega * 1024;
370
371
0
    OUString aUnitStr(' ');
372
373
0
    if (nSize < 10000)
374
0
    {
375
0
        aUnitStr += SvtResId(STR_SVT_BYTES);
376
0
        nDec = 0;
377
0
    }
378
0
    else if (nSize < nMega)
379
0
    {
380
0
        fSize /= 1024;
381
0
        aUnitStr += SvtResId(STR_SVT_KB);
382
0
        nDec = 1;
383
0
    }
384
0
    else if (nSize < nGiga)
385
0
    {
386
0
        fSize /= nMega;
387
0
        aUnitStr += SvtResId(STR_SVT_MB);
388
0
        nDec = 2;
389
0
    }
390
0
    else
391
0
    {
392
0
        fSize /= nGiga;
393
0
        aUnitStr += SvtResId(STR_SVT_GB);
394
0
        nDec = 3;
395
0
    }
396
397
0
    OUString aSizeStr
398
0
        = ::rtl::math::doubleToUString(fSize, rtl_math_StringFormat_F, nDec,
399
0
                                       SvtSysLocale().GetLocaleData().getNumDecimalSep()[0])
400
0
          + aUnitStr;
401
402
0
    return aSizeStr;
403
0
}
404
405
static sal_uInt64 getFileSize(const OUString& rURL)
406
0
{
407
0
    sal_uInt64 nSize = 0;
408
0
    if (!comphelper::isFileUrl(rURL))
409
0
        return nSize;
410
411
0
    osl::DirectoryItem aItem;
412
0
    if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
413
0
        return nSize;
414
415
0
    osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize);
416
0
    if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
417
0
        return nSize;
418
419
0
    nSize = aStatus.getFileSize();
420
0
    return nSize;
421
0
}
422
423
static OUString getApplication(std::u16string_view rURL)
424
0
{
425
0
    INetURLObject aUrl(rURL);
426
0
    OUString aExt = aUrl.getExtension();
427
428
0
    if (aExt == "ott" || aExt == "stw" || aExt == "oth" || aExt == "dot" || aExt == "dotx")
429
0
    {
430
0
        return SfxResId(STR_DOCUMENT);
431
0
    }
432
0
    else if (aExt == "ots" || aExt == "stc" || aExt == "xlt" || aExt == "xltm" || aExt == "xltx")
433
0
    {
434
0
        return SfxResId(STR_SPREADSHEET);
435
0
    }
436
0
    else if (aExt == "otp" || aExt == "sti" || aExt == "pot" || aExt == "potm" || aExt == "potx")
437
0
    {
438
0
        return SfxResId(STR_PRESENTATION);
439
0
    }
440
0
    else if (aExt == "otg" || aExt == "std")
441
0
    {
442
0
        return SfxResId(STR_DRAWING);
443
0
    }
444
0
    return OUString();
445
0
}
446
447
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */