Chris@127
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@127
|
2
|
Chris@127
|
3 /*
|
Chris@127
|
4 Sonic Visualiser
|
Chris@127
|
5 An audio file viewer and annotation editor.
|
Chris@127
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@182
|
7 This file copyright 2006 Chris Cannam and QMUL.
|
Chris@127
|
8
|
Chris@127
|
9 This program is free software; you can redistribute it and/or
|
Chris@127
|
10 modify it under the terms of the GNU General Public License as
|
Chris@127
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@127
|
12 License, or (at your option) any later version. See the file
|
Chris@127
|
13 COPYING included with this distribution for more information.
|
Chris@127
|
14 */
|
Chris@127
|
15
|
Chris@127
|
16 #include "ViewManager.h"
|
Chris@128
|
17 #include "base/AudioPlaySource.h"
|
Chris@1210
|
18 #include "base/AudioRecordTarget.h"
|
Chris@502
|
19 #include "base/RealTime.h"
|
Chris@128
|
20 #include "data/model/Model.h"
|
Chris@376
|
21 #include "widgets/CommandHistory.h"
|
Chris@211
|
22 #include "View.h"
|
Chris@503
|
23 #include "Overview.h"
|
Chris@127
|
24
|
Chris@1577
|
25 #include "system/System.h"
|
Chris@1577
|
26
|
Chris@133
|
27 #include <QSettings>
|
Chris@292
|
28 #include <QApplication>
|
Chris@1575
|
29 #include <QStyleFactory>
|
Chris@133
|
30
|
Chris@127
|
31 #include <iostream>
|
Chris@127
|
32
|
Chris@643
|
33 //#define DEBUG_VIEW_MANAGER 1
|
Chris@127
|
34
|
Chris@127
|
35 ViewManager::ViewManager() :
|
Chris@1408
|
36 m_playSource(nullptr),
|
Chris@1408
|
37 m_recordTarget(nullptr),
|
Chris@127
|
38 m_globalCentreFrame(0),
|
Chris@1327
|
39 m_globalZoom(ZoomLevel::FramesPerPixel, 1024),
|
Chris@127
|
40 m_playbackFrame(0),
|
Chris@127
|
41 m_mainModelSampleRate(0),
|
Chris@127
|
42 m_lastLeft(0),
|
Chris@127
|
43 m_lastRight(0),
|
Chris@127
|
44 m_inProgressExclusive(true),
|
Chris@127
|
45 m_toolMode(NavigateMode),
|
Chris@127
|
46 m_playLoopMode(false),
|
Chris@127
|
47 m_playSelectionMode(false),
|
Chris@301
|
48 m_playSoloMode(false),
|
Chris@314
|
49 m_alignMode(false),
|
Chris@741
|
50 m_overlayMode(StandardOverlays),
|
Chris@292
|
51 m_zoomWheelsEnabled(true),
|
Chris@1468
|
52 m_opportunisticEditingEnabled(true),
|
Chris@607
|
53 m_showCentreLine(true),
|
Chris@326
|
54 m_illuminateLocalFeatures(true),
|
Chris@326
|
55 m_showWorkTitle(false),
|
matthiasm@822
|
56 m_showDuration(true),
|
Chris@292
|
57 m_lightPalette(QApplication::palette()),
|
Chris@292
|
58 m_darkPalette(QApplication::palette())
|
Chris@127
|
59 {
|
Chris@133
|
60 QSettings settings;
|
Chris@133
|
61 settings.beginGroup("MainWindow");
|
Chris@133
|
62 m_overlayMode = OverlayMode
|
Chris@133
|
63 (settings.value("overlay-mode", int(m_overlayMode)).toInt());
|
Chris@607
|
64
|
Chris@607
|
65 if (m_overlayMode != NoOverlays &&
|
Chris@741
|
66 m_overlayMode != StandardOverlays &&
|
Chris@607
|
67 m_overlayMode != AllOverlays) {
|
Chris@741
|
68 m_overlayMode = StandardOverlays;
|
Chris@607
|
69 }
|
Chris@607
|
70
|
Chris@133
|
71 m_zoomWheelsEnabled =
|
Chris@133
|
72 settings.value("zoom-wheels-enabled", m_zoomWheelsEnabled).toBool();
|
Chris@607
|
73 m_showCentreLine =
|
Chris@607
|
74 settings.value("show-centre-line", m_showCentreLine).toBool();
|
Chris@133
|
75 settings.endGroup();
|
Chris@292
|
76
|
Chris@292
|
77 if (getGlobalDarkBackground()) {
|
Chris@1573
|
78 // i.e. widgets are already dark; create a light palette in
|
Chris@1573
|
79 // case we are asked to switch to it, but don't create a dark
|
Chris@1573
|
80 // one because it will be assigned from the actual application
|
Chris@1573
|
81 // palette if we switch
|
Chris@1573
|
82
|
Chris@293
|
83 m_lightPalette = QPalette(QColor("#000000"), // WindowText
|
Chris@293
|
84 QColor("#dddfe4"), // Button
|
Chris@293
|
85 QColor("#ffffff"), // Light
|
Chris@293
|
86 QColor("#555555"), // Dark
|
Chris@293
|
87 QColor("#c7c7c7"), // Mid
|
Chris@293
|
88 QColor("#000000"), // Text
|
Chris@293
|
89 QColor("#ffffff"), // BrightText
|
Chris@293
|
90 QColor("#ffffff"), // Base
|
Chris@293
|
91 QColor("#efefef")); // Window
|
Chris@1577
|
92
|
Chris@1577
|
93 m_lightPalette.setColor(QPalette::Highlight, Qt::darkBlue);
|
Chris@1577
|
94 if (!OSReportsDarkThemeActive()) {
|
Chris@1577
|
95 int r, g, b;
|
Chris@1577
|
96 if (OSQueryAccentColour(r, g, b)) {
|
Chris@1577
|
97 m_lightPalette.setColor(QPalette::Highlight, QColor(r, g, b));
|
Chris@1577
|
98 }
|
Chris@1577
|
99 }
|
Chris@293
|
100
|
Chris@292
|
101 } else {
|
Chris@1573
|
102 // i.e. widgets are currently light; create a dark palette in
|
Chris@1573
|
103 // case we are asked to switch to it, but don't create a light
|
Chris@1573
|
104 // one because it will be assigned from the actual application
|
Chris@1573
|
105 // palette if we switch
|
Chris@1573
|
106
|
Chris@1573
|
107 m_darkPalette = QPalette(QColor("#f0f0f0"), // WindowText
|
Chris@293
|
108 QColor("#3e3e3e"), // Button
|
Chris@293
|
109 QColor("#808080"), // Light
|
Chris@293
|
110 QColor("#1e1e1e"), // Dark
|
Chris@293
|
111 QColor("#404040"), // Mid
|
Chris@1573
|
112 QColor("#f0f0f0"), // Text
|
Chris@293
|
113 QColor("#ffffff"), // BrightText
|
Chris@293
|
114 QColor("#000000"), // Base
|
Chris@293
|
115 QColor("#202020")); // Window
|
Chris@1573
|
116
|
Chris@1576
|
117 m_darkPalette.setColor(QPalette::Highlight, QColor(25, 130, 220));
|
Chris@1577
|
118 if (OSReportsDarkThemeActive()) {
|
Chris@1577
|
119 int r, g, b;
|
Chris@1577
|
120 if (OSQueryAccentColour(r, g, b)) {
|
Chris@1577
|
121 m_darkPalette.setColor(QPalette::Highlight, QColor(r, g, b));
|
Chris@1577
|
122 }
|
Chris@1577
|
123 }
|
Chris@1577
|
124
|
Chris@1576
|
125 m_darkPalette.setColor(QPalette::Link, QColor(50, 175, 255));
|
Chris@1576
|
126 m_darkPalette.setColor(QPalette::LinkVisited, QColor(50, 175, 255));
|
Chris@1576
|
127
|
Chris@1573
|
128 m_darkPalette.setColor(QPalette::Disabled, QPalette::WindowText,
|
Chris@1573
|
129 QColor("#808080"));
|
Chris@1573
|
130 m_darkPalette.setColor(QPalette::Disabled, QPalette::Text,
|
Chris@1573
|
131 QColor("#808080"));
|
Chris@1573
|
132 m_darkPalette.setColor(QPalette::Disabled, QPalette::Shadow,
|
Chris@1573
|
133 QColor("#000000"));
|
Chris@292
|
134 }
|
Chris@127
|
135 }
|
Chris@127
|
136
|
Chris@127
|
137 ViewManager::~ViewManager()
|
Chris@127
|
138 {
|
Chris@127
|
139 }
|
Chris@127
|
140
|
Chris@902
|
141 sv_frame_t
|
Chris@127
|
142 ViewManager::getGlobalCentreFrame() const
|
Chris@127
|
143 {
|
Chris@127
|
144 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
145 cerr << "ViewManager::getGlobalCentreFrame: returning " << m_globalCentreFrame << endl;
|
Chris@127
|
146 #endif
|
Chris@127
|
147 return m_globalCentreFrame;
|
Chris@127
|
148 }
|
Chris@127
|
149
|
Chris@180
|
150 void
|
Chris@902
|
151 ViewManager::setGlobalCentreFrame(sv_frame_t f)
|
Chris@180
|
152 {
|
Chris@180
|
153 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
154 cerr << "ViewManager::setGlobalCentreFrame to " << f << endl;
|
Chris@180
|
155 #endif
|
Chris@180
|
156 m_globalCentreFrame = f;
|
Chris@211
|
157 emit globalCentreFrameChanged(f);
|
Chris@180
|
158 }
|
Chris@180
|
159
|
Chris@1327
|
160 ZoomLevel
|
Chris@127
|
161 ViewManager::getGlobalZoom() const
|
Chris@127
|
162 {
|
Chris@127
|
163 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
164 cerr << "ViewManager::getGlobalZoom: returning " << m_globalZoom << endl;
|
Chris@127
|
165 #endif
|
Chris@127
|
166 return m_globalZoom;
|
Chris@127
|
167 }
|
Chris@127
|
168
|
Chris@902
|
169 sv_frame_t
|
Chris@127
|
170 ViewManager::getPlaybackFrame() const
|
Chris@127
|
171 {
|
Chris@1210
|
172 if (isRecording()) {
|
Chris@1210
|
173 m_playbackFrame = m_recordTarget->getRecordDuration();
|
Chris@1210
|
174 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1210
|
175 cout << "ViewManager::getPlaybackFrame(recording) -> " << m_playbackFrame << endl;
|
Chris@1210
|
176 #endif
|
Chris@1210
|
177 } else if (isPlaying()) {
|
Chris@1266
|
178 m_playbackFrame = m_playSource->getCurrentPlayingFrame();
|
Chris@1185
|
179 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1185
|
180 cout << "ViewManager::getPlaybackFrame(playing) -> " << m_playbackFrame << endl;
|
Chris@1185
|
181 #endif
|
Chris@1185
|
182 } else {
|
Chris@1185
|
183 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1185
|
184 cout << "ViewManager::getPlaybackFrame(not playing) -> " << m_playbackFrame << endl;
|
Chris@1185
|
185 #endif
|
Chris@127
|
186 }
|
Chris@127
|
187 return m_playbackFrame;
|
Chris@127
|
188 }
|
Chris@127
|
189
|
Chris@127
|
190 void
|
Chris@902
|
191 ViewManager::setPlaybackFrame(sv_frame_t f)
|
Chris@127
|
192 {
|
Chris@1185
|
193 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1185
|
194 cerr << "ViewManager::setPlaybackFrame(" << f << ")" << endl;
|
Chris@1185
|
195 #endif
|
Chris@968
|
196 if (f < 0) f = 0;
|
Chris@127
|
197 if (m_playbackFrame != f) {
|
Chris@1266
|
198 m_playbackFrame = f;
|
Chris@1266
|
199 emit playbackFrameChanged(f);
|
Chris@1266
|
200 if (isPlaying()) {
|
Chris@1266
|
201 m_playSource->play(f);
|
Chris@1266
|
202 }
|
Chris@127
|
203 }
|
Chris@127
|
204 }
|
Chris@127
|
205
|
Chris@1479
|
206 ModelId
|
Chris@301
|
207 ViewManager::getPlaybackModel() const
|
Chris@301
|
208 {
|
Chris@301
|
209 return m_playbackModel;
|
Chris@301
|
210 }
|
Chris@301
|
211
|
Chris@301
|
212 void
|
Chris@1479
|
213 ViewManager::setPlaybackModel(ModelId model)
|
Chris@301
|
214 {
|
Chris@301
|
215 m_playbackModel = model;
|
Chris@301
|
216 }
|
Chris@301
|
217
|
Chris@902
|
218 sv_frame_t
|
Chris@902
|
219 ViewManager::alignPlaybackFrameToReference(sv_frame_t frame) const
|
Chris@333
|
220 {
|
Chris@830
|
221 #ifdef DEBUG_VIEW_MANAGER
|
Chris@830
|
222 cerr << "ViewManager::alignPlaybackFrameToReference(" << frame << "): playback model is " << m_playbackModel << endl;
|
Chris@830
|
223 #endif
|
Chris@1479
|
224 if (m_playbackModel.isNone() || !m_alignMode) {
|
Chris@830
|
225 return frame;
|
Chris@830
|
226 } else {
|
Chris@1479
|
227 auto playbackModel = ModelById::get(m_playbackModel);
|
Chris@1479
|
228 if (!playbackModel) {
|
Chris@1479
|
229 return frame;
|
Chris@1479
|
230 }
|
Chris@1479
|
231 sv_frame_t f = playbackModel->alignToReference(frame);
|
Chris@830
|
232 #ifdef DEBUG_VIEW_MANAGER
|
Chris@830
|
233 cerr << "aligned frame = " << f << endl;
|
Chris@830
|
234 #endif
|
Chris@830
|
235 return f;
|
Chris@830
|
236 }
|
Chris@333
|
237 }
|
Chris@333
|
238
|
Chris@902
|
239 sv_frame_t
|
Chris@902
|
240 ViewManager::alignReferenceToPlaybackFrame(sv_frame_t frame) const
|
Chris@333
|
241 {
|
Chris@830
|
242 #ifdef DEBUG_VIEW_MANAGER
|
Chris@830
|
243 cerr << "ViewManager::alignReferenceToPlaybackFrame(" << frame << "): playback model is " << m_playbackModel << endl;
|
Chris@830
|
244 #endif
|
Chris@1479
|
245 if (m_playbackModel.isNone() || !m_alignMode) {
|
Chris@830
|
246 return frame;
|
Chris@830
|
247 } else {
|
Chris@1479
|
248 auto playbackModel = ModelById::get(m_playbackModel);
|
Chris@1479
|
249 if (!playbackModel) {
|
Chris@1479
|
250 return frame;
|
Chris@1479
|
251 }
|
Chris@1479
|
252 sv_frame_t f = playbackModel->alignFromReference(frame);
|
Chris@830
|
253 #ifdef DEBUG_VIEW_MANAGER
|
Chris@830
|
254 cerr << "aligned frame = " << f << endl;
|
Chris@830
|
255 #endif
|
Chris@830
|
256 return f;
|
Chris@830
|
257 }
|
Chris@333
|
258 }
|
Chris@333
|
259
|
Chris@127
|
260 bool
|
Chris@127
|
261 ViewManager::haveInProgressSelection() const
|
Chris@127
|
262 {
|
Chris@127
|
263 return !m_inProgressSelection.isEmpty();
|
Chris@127
|
264 }
|
Chris@127
|
265
|
Chris@127
|
266 const Selection &
|
Chris@127
|
267 ViewManager::getInProgressSelection(bool &exclusive) const
|
Chris@127
|
268 {
|
Chris@127
|
269 exclusive = m_inProgressExclusive;
|
Chris@127
|
270 return m_inProgressSelection;
|
Chris@127
|
271 }
|
Chris@127
|
272
|
Chris@127
|
273 void
|
Chris@127
|
274 ViewManager::setInProgressSelection(const Selection &selection, bool exclusive)
|
Chris@127
|
275 {
|
Chris@127
|
276 m_inProgressExclusive = exclusive;
|
Chris@127
|
277 m_inProgressSelection = selection;
|
Chris@127
|
278 if (exclusive) clearSelections();
|
Chris@127
|
279 emit inProgressSelectionChanged();
|
Chris@127
|
280 }
|
Chris@127
|
281
|
Chris@127
|
282 void
|
Chris@127
|
283 ViewManager::clearInProgressSelection()
|
Chris@127
|
284 {
|
Chris@127
|
285 m_inProgressSelection = Selection();
|
Chris@127
|
286 emit inProgressSelectionChanged();
|
Chris@127
|
287 }
|
Chris@127
|
288
|
Chris@127
|
289 const MultiSelection &
|
Chris@127
|
290 ViewManager::getSelection() const
|
Chris@127
|
291 {
|
Chris@127
|
292 return m_selections;
|
Chris@127
|
293 }
|
Chris@127
|
294
|
Chris@127
|
295 const MultiSelection::SelectionList &
|
Chris@127
|
296 ViewManager::getSelections() const
|
Chris@127
|
297 {
|
Chris@127
|
298 return m_selections.getSelections();
|
Chris@127
|
299 }
|
Chris@127
|
300
|
Chris@127
|
301 void
|
Chris@127
|
302 ViewManager::setSelection(const Selection &selection)
|
Chris@127
|
303 {
|
Chris@127
|
304 MultiSelection ms(m_selections);
|
Chris@127
|
305 ms.setSelection(selection);
|
Chris@127
|
306 setSelections(ms);
|
Chris@127
|
307 }
|
Chris@127
|
308
|
Chris@127
|
309 void
|
Chris@127
|
310 ViewManager::addSelection(const Selection &selection)
|
Chris@127
|
311 {
|
Chris@127
|
312 MultiSelection ms(m_selections);
|
Chris@127
|
313 ms.addSelection(selection);
|
Chris@127
|
314 setSelections(ms);
|
Chris@127
|
315 }
|
Chris@127
|
316
|
Chris@127
|
317 void
|
Chris@762
|
318 ViewManager::addSelectionQuietly(const Selection &selection)
|
Chris@762
|
319 {
|
Chris@762
|
320 MultiSelection ms(m_selections);
|
Chris@762
|
321 ms.addSelection(selection);
|
Chris@762
|
322 setSelections(ms, true);
|
Chris@762
|
323 }
|
Chris@762
|
324
|
Chris@762
|
325 void
|
Chris@127
|
326 ViewManager::removeSelection(const Selection &selection)
|
Chris@127
|
327 {
|
Chris@127
|
328 MultiSelection ms(m_selections);
|
Chris@127
|
329 ms.removeSelection(selection);
|
Chris@127
|
330 setSelections(ms);
|
Chris@127
|
331 }
|
Chris@127
|
332
|
Chris@127
|
333 void
|
Chris@127
|
334 ViewManager::clearSelections()
|
Chris@127
|
335 {
|
Chris@127
|
336 MultiSelection ms(m_selections);
|
Chris@127
|
337 ms.clearSelections();
|
Chris@127
|
338 setSelections(ms);
|
Chris@127
|
339 }
|
Chris@127
|
340
|
Chris@127
|
341 void
|
Chris@762
|
342 ViewManager::setSelections(const MultiSelection &ms, bool quietly)
|
Chris@127
|
343 {
|
Chris@127
|
344 if (m_selections.getSelections() == ms.getSelections()) return;
|
Chris@127
|
345 SetSelectionCommand *command = new SetSelectionCommand(this, ms);
|
Chris@127
|
346 CommandHistory::getInstance()->addCommand(command);
|
Chris@762
|
347 if (!quietly) {
|
Chris@762
|
348 emit selectionChangedByUser();
|
Chris@762
|
349 }
|
Chris@127
|
350 }
|
Chris@127
|
351
|
Chris@902
|
352 sv_frame_t
|
Chris@902
|
353 ViewManager::constrainFrameToSelection(sv_frame_t frame) const
|
Chris@271
|
354 {
|
Chris@271
|
355 MultiSelection::SelectionList sl = getSelections();
|
Chris@271
|
356 if (sl.empty()) return frame;
|
Chris@271
|
357
|
Chris@368
|
358 for (MultiSelection::SelectionList::const_iterator i = sl.begin();
|
Chris@368
|
359 i != sl.end(); ++i) {
|
Chris@368
|
360
|
Chris@368
|
361 if (frame < i->getEndFrame()) {
|
Chris@368
|
362 if (frame < i->getStartFrame()) {
|
Chris@368
|
363 return i->getStartFrame();
|
Chris@368
|
364 } else {
|
Chris@368
|
365 return frame;
|
Chris@368
|
366 }
|
Chris@368
|
367 }
|
Chris@271
|
368 }
|
Chris@271
|
369
|
Chris@368
|
370 return sl.begin()->getStartFrame();
|
Chris@271
|
371 }
|
Chris@271
|
372
|
Chris@127
|
373 void
|
Chris@127
|
374 ViewManager::signalSelectionChange()
|
Chris@127
|
375 {
|
Chris@127
|
376 emit selectionChanged();
|
Chris@127
|
377 }
|
Chris@127
|
378
|
Chris@127
|
379 ViewManager::SetSelectionCommand::SetSelectionCommand(ViewManager *vm,
|
Chris@1266
|
380 const MultiSelection &ms) :
|
Chris@127
|
381 m_vm(vm),
|
Chris@127
|
382 m_oldSelection(vm->m_selections),
|
Chris@127
|
383 m_newSelection(ms)
|
Chris@127
|
384 {
|
Chris@127
|
385 }
|
Chris@127
|
386
|
Chris@127
|
387 ViewManager::SetSelectionCommand::~SetSelectionCommand() { }
|
Chris@127
|
388
|
Chris@127
|
389 void
|
Chris@127
|
390 ViewManager::SetSelectionCommand::execute()
|
Chris@127
|
391 {
|
Chris@127
|
392 m_vm->m_selections = m_newSelection;
|
Chris@127
|
393 m_vm->signalSelectionChange();
|
Chris@127
|
394 }
|
Chris@127
|
395
|
Chris@127
|
396 void
|
Chris@127
|
397 ViewManager::SetSelectionCommand::unexecute()
|
Chris@127
|
398 {
|
Chris@127
|
399 m_vm->m_selections = m_oldSelection;
|
Chris@127
|
400 m_vm->signalSelectionChange();
|
Chris@127
|
401 }
|
Chris@127
|
402
|
Chris@127
|
403 QString
|
Chris@127
|
404 ViewManager::SetSelectionCommand::getName() const
|
Chris@127
|
405 {
|
Chris@127
|
406 if (m_newSelection.getSelections().empty()) return tr("Clear Selection");
|
Chris@502
|
407 if (m_newSelection.getSelections().size() > 1) return tr("Select Multiple Regions");
|
Chris@502
|
408 else return tr("Select Region");
|
Chris@127
|
409 }
|
Chris@127
|
410
|
Chris@127
|
411 Selection
|
Chris@902
|
412 ViewManager::getContainingSelection(sv_frame_t frame, bool defaultToFollowing) const
|
Chris@127
|
413 {
|
Chris@127
|
414 return m_selections.getContainingSelection(frame, defaultToFollowing);
|
Chris@127
|
415 }
|
Chris@127
|
416
|
Chris@127
|
417 void
|
Chris@127
|
418 ViewManager::setToolMode(ToolMode mode)
|
Chris@127
|
419 {
|
Chris@127
|
420 m_toolMode = mode;
|
Chris@127
|
421
|
Chris@127
|
422 emit toolModeChanged();
|
Chris@502
|
423
|
Chris@502
|
424 switch (mode) {
|
Chris@502
|
425 case NavigateMode: emit activity(tr("Enter Navigate mode")); break;
|
Chris@502
|
426 case SelectMode: emit activity(tr("Enter Select mode")); break;
|
Chris@502
|
427 case EditMode: emit activity(tr("Enter Edit mode")); break;
|
Chris@502
|
428 case DrawMode: emit activity(tr("Enter Draw mode")); break;
|
Chris@502
|
429 case EraseMode: emit activity(tr("Enter Erase mode")); break;
|
Chris@502
|
430 case MeasureMode: emit activity(tr("Enter Measure mode")); break;
|
gyorgyf@645
|
431 case NoteEditMode: emit activity(tr("Enter NoteEdit mode")); break; // GF: NoteEditMode activity (I'm not yet certain why we need to emit this.)
|
Chris@502
|
432 };
|
Chris@127
|
433 }
|
Chris@127
|
434
|
Chris@711
|
435 ViewManager::ToolMode
|
Chris@711
|
436 ViewManager::getToolModeFor(const View *v) const
|
Chris@711
|
437 {
|
Chris@711
|
438 if (m_toolModeOverrides.find(v) == m_toolModeOverrides.end()) {
|
Chris@711
|
439 return getToolMode();
|
Chris@711
|
440 } else {
|
Chris@711
|
441 return m_toolModeOverrides.find(v)->second;
|
Chris@711
|
442 }
|
Chris@711
|
443 }
|
Chris@711
|
444
|
Chris@711
|
445 void
|
Chris@711
|
446 ViewManager::setToolModeFor(const View *v, ToolMode mode)
|
Chris@711
|
447 {
|
Chris@711
|
448 m_toolModeOverrides[v] = mode;
|
Chris@711
|
449 }
|
Chris@711
|
450
|
Chris@711
|
451 void
|
Chris@711
|
452 ViewManager::clearToolModeOverrides()
|
Chris@711
|
453 {
|
Chris@711
|
454 m_toolModeOverrides.clear();
|
Chris@711
|
455 }
|
Chris@711
|
456
|
Chris@127
|
457 void
|
Chris@127
|
458 ViewManager::setPlayLoopMode(bool mode)
|
Chris@127
|
459 {
|
Chris@177
|
460 if (m_playLoopMode != mode) {
|
Chris@127
|
461
|
Chris@177
|
462 m_playLoopMode = mode;
|
Chris@177
|
463
|
Chris@177
|
464 emit playLoopModeChanged();
|
Chris@177
|
465 emit playLoopModeChanged(mode);
|
Chris@502
|
466
|
Chris@502
|
467 if (mode) emit activity(tr("Switch on Loop mode"));
|
Chris@502
|
468 else emit activity(tr("Switch off Loop mode"));
|
Chris@177
|
469 }
|
Chris@127
|
470 }
|
Chris@127
|
471
|
Chris@127
|
472 void
|
Chris@127
|
473 ViewManager::setPlaySelectionMode(bool mode)
|
Chris@127
|
474 {
|
Chris@177
|
475 if (m_playSelectionMode != mode) {
|
Chris@127
|
476
|
Chris@177
|
477 m_playSelectionMode = mode;
|
Chris@177
|
478
|
Chris@177
|
479 emit playSelectionModeChanged();
|
Chris@177
|
480 emit playSelectionModeChanged(mode);
|
Chris@502
|
481
|
Chris@502
|
482 if (mode) emit activity(tr("Switch on Play Selection mode"));
|
Chris@502
|
483 else emit activity(tr("Switch off Play Selection mode"));
|
Chris@177
|
484 }
|
Chris@127
|
485 }
|
Chris@127
|
486
|
Chris@301
|
487 void
|
Chris@301
|
488 ViewManager::setPlaySoloMode(bool mode)
|
Chris@301
|
489 {
|
Chris@301
|
490 if (m_playSoloMode != mode) {
|
Chris@301
|
491
|
Chris@301
|
492 m_playSoloMode = mode;
|
Chris@301
|
493
|
Chris@301
|
494 emit playSoloModeChanged();
|
Chris@301
|
495 emit playSoloModeChanged(mode);
|
Chris@502
|
496
|
Chris@502
|
497 if (mode) emit activity(tr("Switch on Play Solo mode"));
|
Chris@502
|
498 else emit activity(tr("Switch off Play Solo mode"));
|
Chris@301
|
499 }
|
Chris@301
|
500 }
|
Chris@301
|
501
|
Chris@314
|
502 void
|
Chris@314
|
503 ViewManager::setAlignMode(bool mode)
|
Chris@314
|
504 {
|
Chris@314
|
505 if (m_alignMode != mode) {
|
Chris@314
|
506
|
Chris@314
|
507 m_alignMode = mode;
|
Chris@314
|
508
|
Chris@314
|
509 emit alignModeChanged();
|
Chris@314
|
510 emit alignModeChanged(mode);
|
Chris@502
|
511
|
Chris@502
|
512 if (mode) emit activity(tr("Switch on Alignment mode"));
|
Chris@502
|
513 else emit activity(tr("Switch off Alignment mode"));
|
Chris@314
|
514 }
|
Chris@314
|
515 }
|
Chris@314
|
516
|
Chris@902
|
517 sv_samplerate_t
|
Chris@224
|
518 ViewManager::getPlaybackSampleRate() const
|
Chris@224
|
519 {
|
Chris@224
|
520 if (m_playSource) {
|
Chris@224
|
521 return m_playSource->getSourceSampleRate();
|
Chris@224
|
522 }
|
Chris@224
|
523 return 0;
|
Chris@224
|
524 }
|
Chris@224
|
525
|
Chris@902
|
526 sv_samplerate_t
|
Chris@1181
|
527 ViewManager::getDeviceSampleRate() const
|
Chris@127
|
528 {
|
Chris@127
|
529 if (m_playSource) {
|
Chris@1266
|
530 return m_playSource->getDeviceSampleRate();
|
Chris@127
|
531 }
|
Chris@127
|
532 return 0;
|
Chris@127
|
533 }
|
Chris@127
|
534
|
Chris@127
|
535 void
|
Chris@127
|
536 ViewManager::setAudioPlaySource(AudioPlaySource *source)
|
Chris@127
|
537 {
|
Chris@127
|
538 if (!m_playSource) {
|
Chris@1266
|
539 QTimer::singleShot(100, this, SLOT(checkPlayStatus()));
|
Chris@127
|
540 }
|
Chris@127
|
541 m_playSource = source;
|
Chris@127
|
542 }
|
Chris@127
|
543
|
Chris@127
|
544 void
|
Chris@1210
|
545 ViewManager::setAudioRecordTarget(AudioRecordTarget *target)
|
Chris@1210
|
546 {
|
Chris@1210
|
547 if (!m_recordTarget) {
|
Chris@1266
|
548 QTimer::singleShot(100, this, SLOT(checkPlayStatus()));
|
Chris@1210
|
549 }
|
Chris@1210
|
550 m_recordTarget = target;
|
Chris@1210
|
551 }
|
Chris@1210
|
552
|
Chris@1210
|
553 void
|
Chris@248
|
554 ViewManager::playStatusChanged(bool /* playing */)
|
Chris@127
|
555 {
|
Chris@689
|
556 #ifdef DEBUG_VIEW_MANAGER
|
Chris@689
|
557 cerr << "ViewManager::playStatusChanged" << endl;
|
Chris@689
|
558 #endif
|
Chris@127
|
559 checkPlayStatus();
|
Chris@127
|
560 }
|
Chris@127
|
561
|
Chris@127
|
562 void
|
Chris@1210
|
563 ViewManager::recordStatusChanged(bool /* recording */)
|
Chris@1210
|
564 {
|
Chris@1210
|
565 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1210
|
566 cerr << "ViewManager::recordStatusChanged" << endl;
|
Chris@1210
|
567 #endif
|
Chris@1210
|
568 checkPlayStatus();
|
Chris@1210
|
569 }
|
Chris@1210
|
570
|
Chris@1210
|
571 void
|
Chris@127
|
572 ViewManager::checkPlayStatus()
|
Chris@127
|
573 {
|
Chris@1210
|
574 if (isRecording()) {
|
Chris@1210
|
575
|
Chris@1266
|
576 float left = 0, right = 0;
|
Chris@1266
|
577 if (m_recordTarget->getInputLevels(left, right)) {
|
Chris@1266
|
578 if (left != m_lastLeft || right != m_lastRight) {
|
Chris@1266
|
579 emit monitoringLevelsChanged(left, right);
|
Chris@1266
|
580 m_lastLeft = left;
|
Chris@1266
|
581 m_lastRight = right;
|
Chris@1266
|
582 }
|
Chris@1266
|
583 }
|
Chris@1210
|
584
|
Chris@1266
|
585 m_playbackFrame = m_recordTarget->getRecordDuration();
|
Chris@1210
|
586
|
Chris@1210
|
587 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1266
|
588 cerr << "ViewManager::checkPlayStatus: Recording, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << endl;
|
Chris@1210
|
589 #endif
|
Chris@1210
|
590
|
Chris@1266
|
591 emit playbackFrameChanged(m_playbackFrame);
|
Chris@1210
|
592
|
Chris@1266
|
593 QTimer::singleShot(500, this, SLOT(checkPlayStatus()));
|
Chris@1210
|
594
|
Chris@1210
|
595 } else if (isPlaying()) {
|
Chris@127
|
596
|
Chris@1266
|
597 float left = 0, right = 0;
|
Chris@1266
|
598 if (m_playSource->getOutputLevels(left, right)) {
|
Chris@1266
|
599 if (left != m_lastLeft || right != m_lastRight) {
|
Chris@1266
|
600 emit monitoringLevelsChanged(left, right);
|
Chris@1266
|
601 m_lastLeft = left;
|
Chris@1266
|
602 m_lastRight = right;
|
Chris@1266
|
603 }
|
Chris@1266
|
604 }
|
Chris@127
|
605
|
Chris@1266
|
606 m_playbackFrame = m_playSource->getCurrentPlayingFrame();
|
Chris@127
|
607
|
Chris@127
|
608 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1266
|
609 cerr << "ViewManager::checkPlayStatus: Playing, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << endl;
|
Chris@127
|
610 #endif
|
Chris@127
|
611
|
Chris@1266
|
612 emit playbackFrameChanged(m_playbackFrame);
|
Chris@127
|
613
|
Chris@1266
|
614 QTimer::singleShot(20, this, SLOT(checkPlayStatus()));
|
Chris@127
|
615
|
Chris@127
|
616 } else {
|
Chris@127
|
617
|
Chris@1266
|
618 if (m_lastLeft != 0.0 || m_lastRight != 0.0) {
|
Chris@1266
|
619 emit monitoringLevelsChanged(0.0, 0.0);
|
Chris@1266
|
620 m_lastLeft = 0.0;
|
Chris@1266
|
621 m_lastRight = 0.0;
|
Chris@1266
|
622 }
|
Chris@127
|
623
|
Chris@127
|
624 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1266
|
625 cerr << "ViewManager::checkPlayStatus: Not playing or recording" << endl;
|
Chris@127
|
626 #endif
|
Chris@127
|
627 }
|
Chris@127
|
628 }
|
Chris@127
|
629
|
Chris@127
|
630 bool
|
Chris@127
|
631 ViewManager::isPlaying() const
|
Chris@127
|
632 {
|
Chris@127
|
633 return m_playSource && m_playSource->isPlaying();
|
Chris@127
|
634 }
|
Chris@127
|
635
|
Chris@1210
|
636 bool
|
Chris@1210
|
637 ViewManager::isRecording() const
|
Chris@1210
|
638 {
|
Chris@1210
|
639 return m_recordTarget && m_recordTarget->isRecording();
|
Chris@1210
|
640 }
|
Chris@1210
|
641
|
Chris@127
|
642 void
|
Chris@902
|
643 ViewManager::viewCentreFrameChanged(sv_frame_t f, bool locked,
|
Chris@211
|
644 PlaybackFollowMode mode)
|
Chris@127
|
645 {
|
Chris@211
|
646 View *v = dynamic_cast<View *>(sender());
|
Chris@211
|
647
|
Chris@398
|
648 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
649 cerr << "ViewManager::viewCentreFrameChanged(" << f << ", " << locked << ", " << mode << "), view is " << v << endl;
|
Chris@398
|
650 #endif
|
Chris@394
|
651
|
Chris@127
|
652 if (locked) {
|
Chris@211
|
653 m_globalCentreFrame = f;
|
Chris@211
|
654 emit globalCentreFrameChanged(f);
|
Chris@211
|
655 } else {
|
Chris@211
|
656 if (v) emit viewCentreFrameChanged(v, f);
|
Chris@127
|
657 }
|
Chris@127
|
658
|
Chris@815
|
659 if (!dynamic_cast<Overview *>(v) || (mode != PlaybackIgnore)) {
|
Chris@527
|
660 if (m_mainModelSampleRate != 0) {
|
Chris@527
|
661 emit activity(tr("Scroll to %1")
|
Chris@527
|
662 .arg(RealTime::frame2RealTime
|
Chris@527
|
663 (f, m_mainModelSampleRate).toText().c_str()));
|
Chris@527
|
664 }
|
Chris@503
|
665 }
|
Chris@502
|
666
|
Chris@815
|
667 if (mode == PlaybackScrollPageWithCentre ||
|
Chris@815
|
668 mode == PlaybackScrollContinuous) {
|
Chris@815
|
669 seek(f);
|
Chris@211
|
670 }
|
Chris@211
|
671 }
|
Chris@211
|
672
|
Chris@211
|
673 void
|
Chris@902
|
674 ViewManager::seek(sv_frame_t f)
|
Chris@211
|
675 {
|
Chris@127
|
676 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
677 cerr << "ViewManager::seek(" << f << ")" << endl;
|
Chris@127
|
678 #endif
|
Chris@127
|
679
|
Chris@1210
|
680 if (isRecording()) {
|
Chris@1210
|
681 // ignore
|
Chris@1210
|
682 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1210
|
683 cerr << "ViewManager::seek: Ignoring during recording" << endl;
|
Chris@1210
|
684 #endif
|
Chris@1210
|
685 return;
|
Chris@1210
|
686 }
|
Chris@1210
|
687
|
Chris@1210
|
688 if (isPlaying()) {
|
Chris@1266
|
689 sv_frame_t playFrame = m_playSource->getCurrentPlayingFrame();
|
Chris@1266
|
690 sv_frame_t diff = std::max(f, playFrame) - std::min(f, playFrame);
|
Chris@1266
|
691 if (diff > 20000) {
|
Chris@1266
|
692 m_playbackFrame = f;
|
Chris@1266
|
693 m_playSource->play(f);
|
Chris@127
|
694 #ifdef DEBUG_VIEW_MANAGER
|
Chris@1266
|
695 cerr << "ViewManager::seek: reseeking from " << playFrame << " to " << f << endl;
|
Chris@127
|
696 #endif
|
Chris@211
|
697 emit playbackFrameChanged(f);
|
Chris@1266
|
698 }
|
Chris@127
|
699 } else {
|
Chris@211
|
700 if (m_playbackFrame != f) {
|
Chris@211
|
701 m_playbackFrame = f;
|
Chris@211
|
702 emit playbackFrameChanged(f);
|
Chris@211
|
703 }
|
Chris@127
|
704 }
|
Chris@127
|
705 }
|
Chris@127
|
706
|
Chris@127
|
707 void
|
Chris@1327
|
708 ViewManager::viewZoomLevelChanged(ZoomLevel z, bool locked)
|
Chris@127
|
709 {
|
Chris@222
|
710 View *v = dynamic_cast<View *>(sender());
|
Chris@222
|
711
|
Chris@222
|
712 if (!v) {
|
Chris@587
|
713 SVDEBUG << "ViewManager::viewZoomLevelChanged: WARNING: sender is not a view" << endl;
|
Chris@222
|
714 return;
|
Chris@222
|
715 }
|
Chris@222
|
716
|
Chris@222
|
717 //!!! emit zoomLevelChanged();
|
Chris@133
|
718
|
Chris@127
|
719 if (locked) {
|
Chris@1266
|
720 m_globalZoom = z;
|
Chris@127
|
721 }
|
Chris@127
|
722
|
Chris@127
|
723 #ifdef DEBUG_VIEW_MANAGER
|
Chris@682
|
724 cerr << "ViewManager::viewZoomLevelChanged(" << v << ", " << z << ", " << locked << ")" << endl;
|
Chris@127
|
725 #endif
|
Chris@222
|
726
|
Chris@222
|
727 emit viewZoomLevelChanged(v, z, locked);
|
Chris@503
|
728
|
Chris@503
|
729 if (!dynamic_cast<Overview *>(v)) {
|
Chris@1327
|
730 if (z.zone == ZoomLevel::FramesPerPixel) {
|
Chris@1327
|
731 emit activity(tr("Zoom to %n sample(s) per pixel", "", z.level));
|
Chris@1327
|
732 } else {
|
Chris@1327
|
733 emit activity(tr("Zoom to %n pixels per sample", "", z.level));
|
Chris@1327
|
734 }
|
Chris@503
|
735 }
|
Chris@127
|
736 }
|
Chris@127
|
737
|
Chris@127
|
738 void
|
Chris@127
|
739 ViewManager::setOverlayMode(OverlayMode mode)
|
Chris@127
|
740 {
|
Chris@127
|
741 if (m_overlayMode != mode) {
|
Chris@127
|
742 m_overlayMode = mode;
|
Chris@127
|
743 emit overlayModeChanged();
|
Chris@502
|
744 emit activity(tr("Change overlay level"));
|
Chris@127
|
745 }
|
Chris@133
|
746
|
Chris@133
|
747 QSettings settings;
|
Chris@133
|
748 settings.beginGroup("MainWindow");
|
Chris@133
|
749 settings.setValue("overlay-mode", int(m_overlayMode));
|
Chris@133
|
750 settings.endGroup();
|
Chris@127
|
751 }
|
Chris@127
|
752
|
Chris@133
|
753 void
|
Chris@133
|
754 ViewManager::setZoomWheelsEnabled(bool enabled)
|
Chris@133
|
755 {
|
Chris@133
|
756 if (m_zoomWheelsEnabled != enabled) {
|
Chris@133
|
757 m_zoomWheelsEnabled = enabled;
|
Chris@133
|
758 emit zoomWheelsEnabledChanged();
|
Chris@502
|
759 if (enabled) emit activity("Show zoom wheels");
|
Chris@502
|
760 else emit activity("Hide zoom wheels");
|
Chris@133
|
761 }
|
Chris@127
|
762
|
Chris@133
|
763 QSettings settings;
|
Chris@133
|
764 settings.beginGroup("MainWindow");
|
Chris@133
|
765 settings.setValue("zoom-wheels-enabled", m_zoomWheelsEnabled);
|
Chris@133
|
766 settings.endGroup();
|
Chris@133
|
767 }
|
Chris@133
|
768
|
Chris@292
|
769 void
|
Chris@1468
|
770 ViewManager::setOpportunisticEditingEnabled(bool enabled)
|
Chris@1468
|
771 {
|
Chris@1468
|
772 if (m_opportunisticEditingEnabled != enabled) {
|
Chris@1468
|
773 m_opportunisticEditingEnabled = enabled;
|
Chris@1468
|
774 emit opportunisticEditingEnabledChanged();
|
Chris@1468
|
775 }
|
Chris@1468
|
776 }
|
Chris@1468
|
777
|
Chris@1468
|
778 void
|
Chris@607
|
779 ViewManager::setShowCentreLine(bool show)
|
Chris@607
|
780 {
|
Chris@607
|
781 if (m_showCentreLine != show) {
|
Chris@607
|
782 m_showCentreLine = show;
|
Chris@607
|
783 emit showCentreLineChanged();
|
Chris@607
|
784 if (show) emit activity("Show centre line");
|
Chris@607
|
785 else emit activity("Hide centre line");
|
Chris@607
|
786 }
|
Chris@607
|
787
|
Chris@607
|
788 QSettings settings;
|
Chris@607
|
789 settings.beginGroup("MainWindow");
|
Chris@607
|
790 settings.setValue("show-centre-line", int(m_showCentreLine));
|
Chris@607
|
791 settings.endGroup();
|
Chris@607
|
792 }
|
Chris@607
|
793
|
Chris@607
|
794 void
|
Chris@292
|
795 ViewManager::setGlobalDarkBackground(bool dark)
|
Chris@292
|
796 {
|
Chris@292
|
797 // also save the current palette, in case the user has changed it
|
Chris@292
|
798 // since construction
|
Chris@292
|
799 if (getGlobalDarkBackground()) {
|
Chris@292
|
800 m_darkPalette = QApplication::palette();
|
Chris@292
|
801 } else {
|
Chris@292
|
802 m_lightPalette = QApplication::palette();
|
Chris@292
|
803 }
|
Chris@292
|
804
|
Chris@1574
|
805 #ifdef Q_OS_MAC
|
Chris@1574
|
806 return;
|
Chris@1574
|
807 #endif
|
Chris@1574
|
808
|
Chris@292
|
809 if (dark) {
|
Chris@1574
|
810
|
Chris@1574
|
811 #ifdef Q_OS_WIN32
|
Chris@1575
|
812 // The Windows Vista style doesn't use the palette for many of
|
Chris@1575
|
813 // its controls. They can be styled with stylesheets, but that
|
Chris@1575
|
814 // takes a lot of fiddly and fragile custom bits. Easier and
|
Chris@1575
|
815 // more reliable to switch to a non-Vista style which does use
|
Chris@1575
|
816 // the palette.
|
Chris@1574
|
817
|
Chris@1575
|
818 QStyle *plainWindowsStyle = QStyleFactory::create("windows");
|
Chris@1575
|
819 if (!plainWindowsStyle) {
|
Chris@1575
|
820 SVCERR << "Failed to load plain Windows style" << endl;
|
Chris@1575
|
821 } else {
|
Chris@1575
|
822 qApp->setStyle(plainWindowsStyle);
|
Chris@1574
|
823 }
|
Chris@1574
|
824 #endif
|
Chris@1574
|
825
|
Chris@292
|
826 QApplication::setPalette(m_darkPalette);
|
Chris@1574
|
827
|
Chris@292
|
828 } else {
|
Chris@1574
|
829
|
Chris@1574
|
830 #ifdef Q_OS_WIN32
|
Chris@1575
|
831 // Switch back to Vista style
|
Chris@1575
|
832
|
Chris@1575
|
833 QStyle *fancyWindowsStyle = QStyleFactory::create("windowsvista");
|
Chris@1575
|
834 if (!fancyWindowsStyle) {
|
Chris@1575
|
835 SVCERR << "Failed to load fancy Windows style" << endl;
|
Chris@1575
|
836 } else {
|
Chris@1575
|
837 qApp->setStyle(fancyWindowsStyle);
|
Chris@1575
|
838 }
|
Chris@1574
|
839 #endif
|
Chris@1574
|
840
|
Chris@292
|
841 QApplication::setPalette(m_lightPalette);
|
Chris@292
|
842 }
|
Chris@292
|
843 }
|
Chris@292
|
844
|
Chris@292
|
845 bool
|
Chris@292
|
846 ViewManager::getGlobalDarkBackground() const
|
Chris@292
|
847 {
|
Chris@292
|
848 bool dark = false;
|
Chris@292
|
849 QColor windowBg = QApplication::palette().color(QPalette::Window);
|
Chris@292
|
850 if (windowBg.red() + windowBg.green() + windowBg.blue() < 384) {
|
Chris@292
|
851 dark = true;
|
Chris@292
|
852 }
|
Chris@292
|
853 return dark;
|
Chris@292
|
854 }
|
Chris@292
|
855
|
Chris@894
|
856 int
|
Chris@894
|
857 ViewManager::scalePixelSize(int pixels)
|
Chris@894
|
858 {
|
Chris@908
|
859 static double ratio = 0.0;
|
Chris@908
|
860 if (ratio == 0.0) {
|
Chris@908
|
861 double baseEm;
|
Chris@894
|
862 #ifdef Q_OS_MAC
|
Chris@908
|
863 baseEm = 17.0;
|
Chris@894
|
864 #else
|
Chris@908
|
865 baseEm = 15.0;
|
Chris@894
|
866 #endif
|
Chris@908
|
867 double em = QFontMetrics(QFont()).height();
|
Chris@894
|
868 ratio = em / baseEm;
|
Chris@1224
|
869
|
Chris@1224
|
870 SVDEBUG << "ViewManager::scalePixelSize: ratio is " << ratio
|
Chris@1224
|
871 << " (em = " << em << ")" << endl;
|
Chris@894
|
872 }
|
Chris@894
|
873
|
Chris@894
|
874 int scaled = int(pixels * ratio + 0.5);
|
Chris@1273
|
875 // SVDEBUG << "scaledSize: " << pixels << " -> " << scaled << " at ratio " << ratio << endl;
|
Chris@894
|
876 if (pixels != 0 && scaled == 0) scaled = 1;
|
Chris@894
|
877 return scaled;
|
Chris@894
|
878 }
|
Chris@946
|
879
|