• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gansm / finalcut / #760

06 Feb 2026 01:45AM UTC coverage: 69.064% (-0.02%) from 69.083%
#760

push

travis-ci

gansm
Reduce pointer arithmetic and use safe iterators more often

206 of 432 new or added lines in 27 files covered. (47.69%)

25 existing lines in 6 files now uncovered.

37622 of 54474 relevant lines covered (69.06%)

243.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/final/dialog/ffiledialog.cpp
1
/***********************************************************************
2
* ffiledialog.cpp - Widget FFileDialog (a file chooser dialog)         *
3
*                                                                      *
4
* This file is part of the FINAL CUT widget toolkit                    *
5
*                                                                      *
6
* Copyright 2014-2026 Markus Gans                                      *
7
*                                                                      *
8
* FINAL CUT is free software; you can redistribute it and/or modify    *
9
* it under the terms of the GNU Lesser General Public License as       *
10
* published by the Free Software Foundation; either version 3 of       *
11
* the License, or (at your option) any later version.                  *
12
*                                                                      *
13
* FINAL CUT is distributed in the hope that it will be useful, but     *
14
* WITHOUT ANY WARRANTY; without even the implied warranty of           *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
16
* GNU Lesser General Public License for more details.                  *
17
*                                                                      *
18
* You should have received a copy of the GNU Lesser General Public     *
19
* License along with this program.  If not, see                        *
20
* <http://www.gnu.org/licenses/>.                                      *
21
***********************************************************************/
22

23
#include <array>
24
#include <utility>
25
#include <vector>
26

27
#if defined(__CYGWIN__)
28
  #define _BSD_SOURCE  // for D_TYPE in readdir
29
#endif
30

31
#include "final/dialog/ffiledialog.h"
32
#include "final/fevent.h"
33
#include "final/util/fsystem.h"
34

35
#if defined(__GNU__)
36
  #define MAXPATHLEN 4096  // Necessity at GNU Hurd
37
#endif
38

39
namespace finalcut
40
{
41

42
// non-member functions
43
//----------------------------------------------------------------------
44
auto sortByName ( const FFileDialog::FDirEntry& lhs
×
45
                , const FFileDialog::FDirEntry& rhs ) -> bool
46
{
47
  // lhs < rhs
48
  return FStringCaseCompare(lhs.name, rhs.name) < 0;
×
49
}
50

51
//----------------------------------------------------------------------
52
auto sortDirFirst (const FFileDialog::FDirEntry& entry) -> bool
×
53
{
54
  // sort directories first
55
  return entry.directory;
×
56
}
57

58
//----------------------------------------------------------------------
59
auto isRegularFile (const std::string& path) noexcept -> bool
×
60
{
61
  struct stat sb{};
×
62
  return stat(path.c_str(), &sb) == 0 && S_ISREG(sb.st_mode);
×
63
}
64

65
//----------------------------------------------------------------------
66
auto fileChooser ( FWidget* parent
×
67
                 , const FString& dirname
68
                 , const FString& filter
69
                 , FFileDialog::DialogType type ) -> FString
70
{
71
  std::string chosen_file{};
×
72
  FString path{dirname};
×
73
  FString file_filter{filter};
×
74

75
  if ( path.isEmpty() )
×
76
  {
77
    path.setString(FFileDialog::getHomeDir());
×
78

79
    if ( path.isEmpty() )
×
80
      path.setString("/");
×
81
  }
82

83
  if ( file_filter.isEmpty() )
×
84
    file_filter.setString("*");
×
85

86
  FFileDialog fileopen ( path
87
                       , std::move(file_filter)
×
88
                       , type
89
                       , parent );
×
90

91
  if ( fileopen.exec() == FDialog::ResultCode::Accept )
×
92
    chosen_file = fileopen.getPath().toString()
×
93
                + fileopen.getSelectedFilename();
×
94

NEW
95
  if ( ! chosen_file.empty() && ! isRegularFile(chosen_file) )
×
96
  {
97
    FMessageBox::error (&fileopen, chosen_file + "\nis not a file");
×
98
    return {};
×
99
  }
100

101
  return chosen_file;
×
102
}
×
103

104

105
//----------------------------------------------------------------------
106
// class FFileDialog
107
//----------------------------------------------------------------------
108

109
// constructors and destructor
110
//----------------------------------------------------------------------
111
FFileDialog::FFileDialog (FWidget* parent)
×
112
  : FDialog{parent}
×
113
{
114
  init();
×
115
}
×
116

117
//----------------------------------------------------------------------
118
FFileDialog::FFileDialog ( const FString& dirname
×
119
                         , FString&& filter
120
                         , DialogType type
121
                         , FWidget* parent )
×
122
  : FDialog{parent}
123
  , filter_pattern{std::move(filter)}
×
124
  , dlg_type{type}
×
125
{
126
  if ( ! dirname.isEmpty() )
×
127
    setPath(dirname);
×
128

129
  init();
×
130
}
×
131

132
//----------------------------------------------------------------------
133
FFileDialog::~FFileDialog() noexcept = default;  // destructor
×
134

135

136
// public methods of FFileDialog
137
//----------------------------------------------------------------------
138
auto FFileDialog::getSelectedFile() const -> FString
×
139
{
140
  return {getSelectedFilename()};
×
141
}
142

143
//----------------------------------------------------------------------
144
void FFileDialog::setPath (const FString& dir)
×
145
{
146
  const auto& dirname = dir.c_str();
×
147
  std::array<char, MAXPATHLEN> resolved_path{};
×
148
  FString r_dir{};
×
149
  struct stat sb{};
×
150

151
  if ( stat(dirname, &sb) != 0 )
×
152
  {
153
    directory = '/';
×
154
    return;
×
155
  }
156

157
  if ( S_ISLNK(sb.st_mode) && lstat(dirname, &sb) != 0 )
×
158
  {
159
    directory = '/';
×
160
    return;
×
161
  }
162

163
  if ( ! S_ISDIR(sb.st_mode) )
×
164
  {
165
    directory = '/';
×
166
    return;
×
167
  }
168

169
  const auto& fsystem = FSystem::getInstance();
×
170

171
  if ( fsystem->realpath(dir.c_str(), resolved_path.data()) != nullptr )
×
172
    r_dir.setString(resolved_path.data());
×
173
  else
174
    r_dir.setString(dir);
×
175

176
  if ( r_dir[r_dir.getLength() - 1] != '/' )
×
177
    directory = r_dir + "/";
×
178
  else
179
    directory = std::move(r_dir);
×
180
}
×
181

182
//----------------------------------------------------------------------
183
void FFileDialog::setFilter (const FString& filter)
×
184
{
185
  filter_pattern = filter;
×
186
  setTitelbarText();
×
187
}
×
188

189
//----------------------------------------------------------------------
190
void FFileDialog::setShowHiddenFiles (bool enable)
×
191
{
192
  if ( show_hidden == enable )
×
193
    return;
×
194

195
  show_hidden = enable;
×
196
  readDir();
×
197
  filebrowser.redraw();
×
198
}
199

200
//----------------------------------------------------------------------
201
void FFileDialog::onKeyPress (FKeyEvent* ev)
×
202
{
203
  if ( ! isEnabled() )
×
204
    return;
×
205

206
  FDialog::onKeyPress (ev);
×
207

208
  if ( ! filebrowser.hasFocus() )
×
209
    return;
×
210

211
  const auto& key = ev->key();
×
212

213
  if ( key == FKey::Erase || key == FKey::Backspace )
×
214
  {
215
    changeDir("..");
×
216
    ev->accept();
×
217
  }
218
}
219

220
//----------------------------------------------------------------------
221
auto FFileDialog::fileOpenChooser ( FWidget* parent
×
222
                                  , const FString& dirname
223
                                  , const FString& filter ) -> FString
224
{
225
  return fileChooser (parent, dirname, filter, DialogType::Open);
×
226
}
227

228
//----------------------------------------------------------------------
229
auto FFileDialog::fileSaveChooser ( FWidget* parent
×
230
                                  , const FString& dirname
231
                                  , const FString& filter ) -> FString
232
{
233
  return fileChooser (parent, dirname, filter, DialogType::Save);
×
234
}
235

236

237
// protected methods of FFileDialog
238
//----------------------------------------------------------------------
239
void FFileDialog::adjustSize()
×
240
{
241
  const auto& root_widget = getRootWidget();
×
242
  // Set maximum values with fallback to xterm default size
243
  auto max_width = root_widget ? root_widget->getClientWidth() : 80;
×
244
  auto max_height = root_widget ? root_widget->getClientHeight() : 24;
×
245
  std::size_t h = max_height - 6;
×
246
  h = std::max(h, std::size_t(15));  // minimum 15
×
247
  h = std::min(h, std::size_t(30));  // maximum 30
×
248
  setHeight (h, false);
×
249
  const int X = 1 + int((max_width - getWidth()) / 2);
×
250
  const int Y = 1 + int((max_height - getHeight()) / 3);
×
251
  setPos(FPoint{X, Y}, false);
×
252
  filebrowser.setHeight (h - 8, false);
×
253
  hidden_check.setY (int(h) - 4, false);
×
254
  cancel_btn.setY (int(h) - 4, false);
×
255
  open_btn.setY (int(h) - 4, false);
×
256
  FDialog::adjustSize();
×
257
  printPath(directory);
×
258
}
×
259

260

261
// private methods of FFileDialog
262
//----------------------------------------------------------------------
263
void FFileDialog::init()
×
264
{
265
  static constexpr std::size_t w = 42;
266
  static constexpr std::size_t h = 15;
267
  FWindow::setGeometry(FPoint{1, 1}, FSize{w, h}, false);
×
268
  const auto& parent_widget = getParentWidget();
×
269
  int x = parent_widget ? 1 + int((parent_widget->getWidth() - w) / 2) : 1;
×
270
  int y = parent_widget ? 1 + int((parent_widget->getHeight() - h) / 3) : 1;
×
271
  setTitelbarText();
×
272
  widgetSettings (FPoint{x, y});  // Create widgets
×
273
  initCallbacks();
×
274
  setModal();
×
275
  readDir();
×
276
}
×
277

278
//----------------------------------------------------------------------
279
inline void FFileDialog::widgetSettings (const FPoint& pos)
×
280
{
281
  filename.setLabelText ("File&name");
×
282
  filename.setText (filter_pattern);
×
283
  filename.setGeometry (FPoint{11, 1}, FSize{28, 1});
×
284
  filename.setFocus();
×
285

286
  filebrowser.setGeometry (FPoint{2, 3}, FSize{38, 6});
×
287
  printPath (directory);
×
288

289
  hidden_check.setText ("&hidden files");
×
290
  hidden_check.setGeometry (FPoint{2, 10}, FSize{16, 1});
×
291

292
  cancel_btn.setText ("&Cancel");
×
293
  cancel_btn.setGeometry(FPoint{19, 10}, FSize{9, 1});
×
294

295
  if ( dlg_type == DialogType::Save )
×
296
    open_btn.setText ("&Save");
×
297
  else
298
    open_btn.setText ("&Open");
×
299

300
  open_btn.setGeometry(FPoint{30, 10}, FSize{9, 1});
×
301
  FWindow::setGeometry (pos, getSize());
×
302
}
×
303

304
//----------------------------------------------------------------------
305
void FFileDialog::initCallbacks()
×
306
{
307
  filename.addCallback
308
  (
×
309
    "activate",
310
    this, &FFileDialog::cb_processActivate
×
311
  );
312

313
  filebrowser.addCallback
314
  (
×
315
    "row-changed",
316
    this, &FFileDialog::cb_processRowChanged
×
317
  );
318

319
  filebrowser.addCallback
320
  (
×
321
    "clicked",
322
    this, &FFileDialog::cb_processClicked
×
323
  );
324

325
  hidden_check.addCallback
326
  (
×
327
    "toggled",
328
    this, &FFileDialog::cb_processShowHidden
×
329
  );
330

331
  cancel_btn.addCallback
332
  (
×
333
    "clicked",
334
    this, &FFileDialog::cb_processCancel
×
335
  );
336

337
  open_btn.addCallback
338
  (
×
339
    "clicked",
340
    this, &FFileDialog::cb_processOpen
×
341
  );
342
}
×
343

344
//----------------------------------------------------------------------
345
inline auto FFileDialog::patternMatch ( const std::string& pattern
×
346
                                      , const std::string& fname ) const -> bool
347
{
348
  std::string search{};
×
349
  search.reserve(128);
×
350

351
  if ( show_hidden && fname[0] == '.' && fname[1] != '\0' )  // hidden files
×
352
  {
353
    search = ".";
×
354
    search.append(pattern);
×
355
  }
356
  else
357
    search = pattern;
×
358

359
  return ( fnmatch(search.data(), fname.data(), FNM_PERIOD) == 0 );
×
360
}
×
361

362
//----------------------------------------------------------------------
363
void FFileDialog::clear()
×
364
{
365
  if ( dir_entries.empty() )
×
366
    return;
×
367

368
  dir_entries.clear();
×
369
  dir_entries.shrink_to_fit();
×
370
}
371

372
//----------------------------------------------------------------------
373
auto FFileDialog::numOfDirs() -> sInt64
×
374
{
375
  if ( dir_entries.empty() )
×
376
    return 0;
×
377

378
  const sInt64 n = std::count_if ( std::begin(dir_entries)
×
379
                                 , std::end(dir_entries)
×
380
                                 , [] (const auto& entry)
×
381
                                   {
382
                                     return entry.directory
×
383
                                         && entry.name != ".";
×
384
                                   }
385
                                 );
386
  return n;
×
387
}
388

389
//----------------------------------------------------------------------
390
void FFileDialog::sortDir()
×
391
{
392
  const sInt64 start = dir_entries.cbegin()->name == ".." ? 1 : 0;
×
393
  const sInt64 dir_num = numOfDirs();
×
394
  // directories first
395
  std::partition ( dir_entries.begin() + start
×
396
                 , dir_entries.end()
397
                 , sortDirFirst );
398
  // sort directories by name
399
  std::sort ( dir_entries.begin() + start
×
400
            , dir_entries.begin() + dir_num
×
401
            , sortByName );
402
  // sort files by name
403
  std::sort ( dir_entries.begin() + dir_num
×
404
            , dir_entries.end()
405
            , sortByName );
406
}
×
407

408
//----------------------------------------------------------------------
409
auto FFileDialog::readDir() -> int
×
410
{
411
  auto directory_stream = openDirectory();
×
412

413
  if ( ! directory_stream )
×
414
    return -1;
×
415

416
  clear();
×
417
  readDirEntries (directory_stream);
×
418

419
  if ( closeDirectory (directory_stream) == CloseDir::error )
×
420
    return -2;
×
421

422
  sortDir();
×
423

424
  // Insert directory entries into the list
425
  dirEntriesToList();
×
426
  return 0;
×
427
}
428

429
//----------------------------------------------------------------------
NEW
430
void FFileDialog::getEntry (const std::string& dir, std::string&& name, const struct dirent* d_entry)
×
431
{
432
  const auto& filter = filter_pattern.toString();
×
433
  FDirEntry entry{};
×
434

NEW
435
  entry.name = std::move(name);
×
436

437
#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE
438
  entry.fifo             = (d_entry->d_type & DT_FIFO) == DT_FIFO;
×
439
  entry.character_device = (d_entry->d_type & DT_CHR ) == DT_CHR;
×
440
  entry.directory        = (d_entry->d_type & DT_DIR ) == DT_DIR;
×
441
  entry.block_device     = (d_entry->d_type & DT_BLK ) == DT_BLK;
×
442
  entry.regular_file     = (d_entry->d_type & DT_REG ) == DT_REG;
×
443
  entry.symbolic_link    = (d_entry->d_type & DT_LNK ) == DT_LNK;
×
444
  entry.socket           = (d_entry->d_type & DT_SOCK) == DT_SOCK;
×
445
#else
446
  struct stat s{};
447
  stat (entry.name.c_str(), &s);
448
  entry.fifo             = S_ISFIFO (s.st_mode);
449
  entry.character_device = S_ISCHR (s.st_mode);
450
  entry.directory        = S_ISDIR (s.st_mode);
451
  entry.block_device     = S_ISBLK (s.st_mode);
452
  entry.regular_file     = S_ISREG (s.st_mode);
453
  entry.symbolic_link    = S_ISLNK (s.st_mode);
454
  entry.socket           = S_ISSOCK (s.st_mode);
455
#endif
456

457
  followSymLink (dir, entry);
×
458

459
  if ( entry.directory || patternMatch(filter, entry.name) )
×
460
    dir_entries.push_back (std::move(entry));
×
UNCOV
461
}
×
462

463
//----------------------------------------------------------------------
NEW
464
void FFileDialog::followSymLink (const std::string& dir, FDirEntry& entry) const
×
465
{
466
  if ( ! entry.symbolic_link )
×
467
    return;  // No symbolic link
×
468

469
  std::array<char, MAXPATHLEN> resolved_path{};
×
470
  std::string symLink{};
×
471
  symLink.reserve(MAXPATHLEN);
×
472
  struct stat sb{};
×
473
  const auto& fsystem = FSystem::getInstance();
×
474
  symLink = dir + entry.name;
×
475

476
  if ( fsystem->realpath(symLink.data(), resolved_path.data()) == nullptr )
×
477
    return;  // Cannot follow the symlink
×
478

479
  if ( lstat(resolved_path.data(), &sb) == -1 )
×
480
    return;  // Cannot get file status
×
481

482
  entry.directory = S_ISDIR(sb.st_mode);
×
483
}
×
484

485
//----------------------------------------------------------------------
486
auto FFileDialog::openDirectory() -> DIR*
×
487
{
488
  const auto& dir = directory.c_str();
×
489
  auto directory_stream = opendir(dir);
×
490

491
  if ( ! directory_stream )
×
492
  {
493
    FMessageBox::error (this, "Can't open directory\n" + directory);
×
494
  }
495

496
  return directory_stream;
×
497
}
498

499
//----------------------------------------------------------------------
500
auto FFileDialog::closeDirectory (DIR* directory_stream) -> CloseDir
×
501
{
502
  auto ret = closedir(directory_stream);
×
503

504
  if ( ret )
×
505
  {
506
    FMessageBox::error (this, "Closing directory\n" + directory);
×
507
    return CloseDir::error;
×
508
  }
509

510
  return CloseDir::success;
×
511
}
512

513
//----------------------------------------------------------------------
514
void FFileDialog::readDirEntries (DIR* directory_stream)
×
515
{
NEW
516
  const auto& dir = directory.toString();
×
517

518
  while ( true )
519
  {
520
    errno = 0;
×
521
    const struct dirent* next = readdir(directory_stream);
×
522

523
    if ( next )
×
524
    {
NEW
525
      auto name{std::string(next->d_name)};
×
526

NEW
527
      if ( isCurrentDirectory(name) )
×
UNCOV
528
        continue;  // Skip name = "."
×
529

NEW
530
      if ( ! show_hidden && isHiddenEntry(name) )
×
531
        continue;  // Skip hidden entries
×
532

NEW
533
      if ( isRootDirectory(dir) && isParentDirectory(name) )
×
534
        continue;  // Skip ".." for the root directory
×
535

NEW
536
      getEntry(dir, std::move(name), next);
×
UNCOV
537
    }
×
538
    else
539
    {
540
      if ( errno != 0 )
×
541
        FMessageBox::error (this, "Reading directory\n" + directory);
×
542

543
      break;
×
544
    }
545
  }  // end while
×
546
}
×
547

548
//----------------------------------------------------------------------
549
auto FFileDialog::getSelectedFilename() const -> std::string
×
550
{
551
  const auto n = uLong(filebrowser.currentItem() - 1);
×
552

553
  if ( dir_entries[n].directory )
×
554
    return {""};
×
555

556
  return dir_entries[n].name;
×
557
}
558

559
//----------------------------------------------------------------------
NEW
560
auto FFileDialog::isCurrentDirectory (const std::string& name) const -> bool
×
561
{
562
  // name = "." (current directory)
NEW
563
  return name[0] == '.'
×
NEW
564
      && name[1] == '\0';
×
565
}
566

567
//----------------------------------------------------------------------
NEW
568
auto FFileDialog::isParentDirectory (const std::string& name) const -> bool
×
569
{
570
  // name = ".." (parent directory)
NEW
571
  return name[0] == '.'
×
NEW
572
      && name[1] == '.'
×
NEW
573
      && name[2] == '\0';
×
574
}
575

576
//----------------------------------------------------------------------
NEW
577
auto FFileDialog::isHiddenEntry (const std::string& name) const -> bool
×
578
{
579
  // name = "." + one or more character
NEW
580
  return name[0] == '.'
×
NEW
581
      && name[1] != '\0'
×
NEW
582
      && (name[1] != '.' || name[2] != '\0');
×
583
}
584

585
//----------------------------------------------------------------------
NEW
586
auto FFileDialog::isRootDirectory (const std::string& dir) const -> bool
×
587
{
588
  return dir[0] == '/'
×
589
      && dir[1] == '\0';
×
590
}
591

592
//----------------------------------------------------------------------
593
void FFileDialog::dirEntriesToList()
×
594
{
595
  // Fill list with directory entries
596

597
  filebrowser.clear();
×
598

599
  if ( dir_entries.empty() )
×
600
    return;
×
601

602
  std::for_each ( dir_entries.cbegin()
×
603
                , dir_entries.cend()
604
                , [this] (const auto& entry)
×
605
                  {
606
                    if ( entry.directory )
×
607
                      filebrowser.insert(FString{entry.name}, BracketType::Brackets);
×
608
                    else
609
                      filebrowser.insert(FString{entry.name});
×
610
                  }
×
611
                );
612
}
613

614
//----------------------------------------------------------------------
615
void FFileDialog::selectDirectoryEntry (const std::string& name)
×
616
{
617
  if ( dir_entries.empty() )
×
618
    return;
×
619

620
  std::size_t i{1};
×
621

622
  for (const auto& entry : dir_entries)
×
623
  {
624
    if ( entry.name == name )
×
625
    {
626
      filebrowser.setCurrentItem(i);
×
627
      filename.setText(name + '/');
×
628
      break;
×
629
    }
630

631
    i++;
×
632
  }
633
}
634

635
//----------------------------------------------------------------------
636
auto FFileDialog::changeDir (const FString& dirname) -> int
×
637
{
638
  FString lastdir{directory};
×
639
  FString newdir{dirname};
×
640

641
  if ( newdir.includes('~') )
×
642
    newdir = newdir.replace('~', getHomeDir());
×
643

644
  if ( newdir[0] == '/' )
×
645
    setPath(newdir);
×
646
  else
647
    setPath(directory + newdir);
×
648

649
  switch ( readDir() )
×
650
  {
651
    case -1:
×
652
      setPath(lastdir);
×
653
      return -1;
×
654

655
    case -2:
×
656
      setPath(lastdir);
×
657
      readDir();
×
658
      return -2;
×
659

660
    case 0:
×
661
      if ( newdir == FString{".."} )
×
662
      {
663
        if ( lastdir == FString{'/'} )
×
664
          filename.setText('/');
×
665
        else
666
        {
667
          auto baseName = std::string(basename(lastdir.c_str()));
×
668
          selectDirectoryEntry (baseName);
×
669
        }
×
670
      }
671
      else
672
      {
673
        FString firstname{dir_entries[0].name};
×
674

675
        if ( dir_entries[0].directory )
×
676
          filename.setText(firstname + '/');
×
677
        else
678
          filename.setText(firstname);
×
679
      }
×
680

681
      printPath(directory);
×
682
      filename.redraw();
×
683
      filebrowser.redraw();
×
684
      // fall through
685
    default:
×
686
      return 0;
×
687
  }
688
}
×
689

690
//----------------------------------------------------------------------
691
void FFileDialog::printPath (const FString& txt)
×
692
{
693
  const auto& path = txt;
×
694
  const std::size_t max_width = filebrowser.getWidth() - 4;
×
695
  const auto column_width = getColumnWidth(path);
×
696

697
  if ( column_width > max_width )
×
698
  {
699
    const std::size_t width = max_width - 2;
×
700
    const std::size_t first = column_width + 1 - width;
×
701
    const FString sub_str{getColumnSubString (path, first, width)};
×
702
    filebrowser.setText(".." + sub_str);
×
703
  }
×
704
  else
705
    filebrowser.setText(path);
×
706
}
×
707

708
//----------------------------------------------------------------------
709
void FFileDialog::setTitelbarText()
×
710
{
711
  const auto suffix = [this] ()
×
712
  {
713
    if ( ! filter_pattern.isEmpty() && filter_pattern != L"*" )
×
714
      return FString(L" (") + filter_pattern + L")";
×
715

716
    return FString{};
×
717
  };
×
718

719
  if ( dlg_type == DialogType::Save )
×
720
    FDialog::setText("Save file" + suffix());
×
721
  else
722
    FDialog::setText("Open file" + suffix());
×
723
}
×
724

725
//----------------------------------------------------------------------
726
auto FFileDialog::getHomeDir() -> FString
×
727
{
728
  struct passwd pwd{};
×
729
  struct passwd* pwd_ptr{};
×
730
  std::array<char, 1024> buf{};
×
731

732
  const auto& fsystem = FSystem::getInstance();
×
733
  const uid_t euid = fsystem->geteuid();
×
734

735
  if ( fsystem->getpwuid_r(euid, &pwd, buf.data(), buf.size(), &pwd_ptr) )
×
736
    return {""};
×
737

738
  return {pwd.pw_dir};
×
739
}
740

741
//----------------------------------------------------------------------
742
inline auto FFileDialog::isFilterInput() const -> bool
×
743
{
744
  return filename.getText().includes('*')
×
745
      || filename.getText().includes('?');
×
746
}
747

748
//----------------------------------------------------------------------
749
inline auto FFileDialog::isDirectoryInput() const -> bool
×
750
{
751
  return filename.getText().trim() == FString{".."}
×
752
      || filename.getText().includes('/')
×
753
      || filename.getText().includes('~');
×
754
}
755

756
//----------------------------------------------------------------------
757
inline void FFileDialog::activateNewFilter()
×
758
{
759
  setFilter(filename.getText());
×
760
  redraw();  // Show new filter in title bar
×
761
  readDir();
×
762
  filebrowser.redraw();
×
763
}
×
764

765
//----------------------------------------------------------------------
766
inline void FFileDialog::activateDefaultFilter()
×
767
{
768
  setFilter("*");
×
769
  redraw();  // Delete filter from title bar
×
770
  readDir();
×
771
  filebrowser.redraw();
×
772
}
×
773

774
//----------------------------------------------------------------------
775
inline void FFileDialog::findItem (const FString& search_text)
×
776
{
777
  auto iter = filebrowser.findItem(search_text);
×
778

779
  if ( iter != filebrowser.getData().end() )
×
780
  {
781
    filebrowser.setCurrentItem(iter);
×
782
    done (ResultCode::Accept);
×
783
  }
784
  else
785
    done (ResultCode::Reject);
×
786
}
×
787

788
//----------------------------------------------------------------------
789
inline void FFileDialog::changeIntoSubDir()
×
790
{
791
  bool found{false};
×
792
  const auto& input = filename.getText().trim();
×
793

794
  if ( dir_entries.empty() )
×
795
    done (ResultCode::Reject);
×
796

797
  found = std::any_of ( std::begin(dir_entries)
×
798
                      , std::end(dir_entries)
×
799
                      , [&input] (auto& entry)
×
800
                        {
801
                          return ! entry.name.empty()
×
802
                              && input
×
803
                              && ! input.isEmpty()
×
804
                              && entry.name == input.toString()
×
805
                              && entry.directory;
×
806
                        }
807
                      );
808

809
  if ( found )
×
810
  {
811
    changeDir(input);
×
812
    return;
×
813
  }
814

815
  findItem(input);
×
816
}
×
817

818
//----------------------------------------------------------------------
819
void FFileDialog::cb_processActivate()
×
820
{
821
  if ( isFilterInput() )
×
822
    activateNewFilter();
×
823
  else if ( filename.getText().isEmpty() )
×
824
    activateDefaultFilter();
×
825
  else if ( isDirectoryInput() )
×
826
    changeDir(filename.getText().trim());
×
827
  else
828
    changeIntoSubDir();
×
829
}
×
830

831
//----------------------------------------------------------------------
832
void FFileDialog::cb_processRowChanged()
×
833
{
834
  const std::size_t n = filebrowser.currentItem();
×
835

836
  if ( n == 0 )
×
837
    return;
×
838

839
  const auto& name = FString{dir_entries[n - 1].name};
×
840

841
  if ( dir_entries[n - 1].directory )
×
842
    filename.setText(name + '/');
×
843
  else
844
    filename.setText(name);
×
845

846
  filename.redraw();
×
847
}
×
848

849
//----------------------------------------------------------------------
850
void FFileDialog::cb_processClicked()
×
851
{
852
  const auto n = uLong(filebrowser.currentItem() - 1);
×
853

854
  if ( dir_entries[n].directory )
×
855
    changeDir(dir_entries[n].name);
×
856
  else
857
    done (ResultCode::Accept);
×
858
}
×
859

860
//----------------------------------------------------------------------
861
void FFileDialog::cb_processCancel()
×
862
{
863
  done (ResultCode::Reject);
×
864
}
×
865

866
//----------------------------------------------------------------------
867
void FFileDialog::cb_processOpen()
×
868
{
869
  done (ResultCode::Accept);
×
870
}
×
871

872
//----------------------------------------------------------------------
873
void FFileDialog::cb_processShowHidden()
×
874
{
875
  setShowHiddenFiles(! show_hidden);
×
876
}
×
877

878
}  // namespace finalcut
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc