1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #include <cctype> |
19 | |
20 | #include "ScrollBarWidget.hxx" |
21 | #include "FileListWidget.hxx" |
22 | #include "TimerManager.hxx" |
23 | |
24 | #include "bspf.hxx" |
25 | |
26 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
27 | FileListWidget::FileListWidget(GuiObject* boss, const GUI::Font& font, |
28 | int x, int y, int w, int h) |
29 | : StringListWidget(boss, font, x, y, w, h), |
30 | _fsmode(FilesystemNode::ListMode::All), |
31 | _selected(0), |
32 | _quickSelectTime(0) |
33 | { |
34 | // This widget is special, in that it catches signals and redirects them |
35 | setTarget(this); |
36 | |
37 | // By default, all filenames are valid |
38 | _filter = [](const FilesystemNode& node) { return true; }; |
39 | } |
40 | |
41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
42 | void FileListWidget::setDirectory(const FilesystemNode& node, string select) |
43 | { |
44 | _node = node; |
45 | |
46 | // We always want a directory listing |
47 | if(!_node.isDirectory() && _node.hasParent()) |
48 | { |
49 | select = _node.getName(); |
50 | _node = _node.getParent(); |
51 | } |
52 | |
53 | // Initialize history |
54 | FilesystemNode tmp = _node; |
55 | while(tmp.hasParent()) |
56 | { |
57 | string name = tmp.getName(); |
58 | if(name.back() == '/' || name.back() == '\\') |
59 | name.pop_back(); |
60 | if(!BSPF::startsWithIgnoreCase(name, " [" )) |
61 | name = " [" + name + "]" ; |
62 | |
63 | _history.push(name); |
64 | tmp = tmp.getParent(); |
65 | } |
66 | // History is in reverse order; we need to fix that |
67 | _history.reverse(); |
68 | |
69 | // Finally, go to this location |
70 | setLocation(_node, select); |
71 | } |
72 | |
73 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
74 | void FileListWidget::setLocation(const FilesystemNode& node, string select) |
75 | { |
76 | _node = node; |
77 | |
78 | // Read in the data from the file system (start with an empty list) |
79 | _fileList.clear(); |
80 | _fileList.reserve(512); |
81 | _node.getChildren(_fileList, _fsmode, _filter); |
82 | |
83 | // Now fill the list widget with the names from the file list |
84 | StringList l; |
85 | for(const auto& file: _fileList) |
86 | l.push_back(file.getName()); |
87 | |
88 | setList(l); |
89 | setSelected(select); |
90 | |
91 | ListWidget::recalc(); |
92 | } |
93 | |
94 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
95 | void FileListWidget::selectDirectory() |
96 | { |
97 | _history.push(selected().getName()); |
98 | setLocation(selected()); |
99 | } |
100 | |
101 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
102 | void FileListWidget::selectParent() |
103 | { |
104 | if(_node.hasParent()) |
105 | setLocation(_node.getParent(), !_history.empty() ? _history.pop() : EmptyString); |
106 | } |
107 | |
108 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
109 | void FileListWidget::reload() |
110 | { |
111 | if(_node.exists()) |
112 | setLocation(_node, selected().getName()); |
113 | } |
114 | |
115 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
116 | bool FileListWidget::handleText(char text) |
117 | { |
118 | // Quick selection mode: Go to first list item starting with this key |
119 | // (or a substring accumulated from the last couple key presses). |
120 | // Only works in a useful fashion if the list entries are sorted. |
121 | uInt64 time = TimerManager::getTicks() / 1000; |
122 | if(_quickSelectTime < time) |
123 | { |
124 | if(std::isupper(text)) |
125 | { |
126 | // Select directories when the first character is uppercase |
127 | _quickSelectStr = " [" ; |
128 | _quickSelectStr.push_back(text); |
129 | } |
130 | else |
131 | _quickSelectStr = text; |
132 | } |
133 | else |
134 | _quickSelectStr += text; |
135 | _quickSelectTime = time + _QUICK_SELECT_DELAY; |
136 | |
137 | int selectedItem = 0; |
138 | for(const auto& i: _list) |
139 | { |
140 | if(BSPF::startsWithIgnoreCase(i, _quickSelectStr)) |
141 | break; |
142 | selectedItem++; |
143 | } |
144 | |
145 | if(selectedItem > 0) |
146 | setSelected(selectedItem); |
147 | |
148 | return true; |
149 | } |
150 | |
151 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
152 | void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) |
153 | { |
154 | switch (cmd) |
155 | { |
156 | case ListWidget::kPrevDirCmd: |
157 | selectParent(); |
158 | break; |
159 | |
160 | case ListWidget::kSelectionChangedCmd: |
161 | _selected = data; |
162 | cmd = ItemChanged; |
163 | break; |
164 | |
165 | case ListWidget::kActivatedCmd: |
166 | case ListWidget::kDoubleClickedCmd: |
167 | _selected = data; |
168 | if(selected().isDirectory()) |
169 | { |
170 | cmd = ItemChanged; |
171 | selectDirectory(); |
172 | } |
173 | else |
174 | cmd = ItemActivated; |
175 | break; |
176 | |
177 | case ListWidget::kLongButtonPressCmd: |
178 | // do nothing, let boss handle this one |
179 | break; |
180 | |
181 | default: |
182 | // If we don't know about the command, send it to the parent and exit |
183 | StringListWidget::handleCommand(sender, cmd, data, id); |
184 | return; |
185 | } |
186 | |
187 | // Send command to boss, then revert to target 'this' |
188 | setTarget(_boss); |
189 | sendCommand(cmd, data, id); |
190 | setTarget(this); |
191 | } |
192 | |
193 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
194 | uInt64 FileListWidget::_QUICK_SELECT_DELAY = 300; |
195 | |