Repository navigation
Expand file tree
/
Copy pathMain.cpp
More file actions
167 lines (149 loc) · 6.84 KB
/
Copy pathMain.cpp
File metadata and controls
167 lines (149 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Super Timecode Converter
// Copyright (c) 2026 Fiverecords -- MIT License
// https://github.com/fiverecords/SuperTimecodeConverter
#include <JuceHeader.h>
#include "MainComponent.h"
#include <csignal>
class SuperTimecodeConverterApplication : public juce::JUCEApplication
{
public:
SuperTimecodeConverterApplication() {}
const juce::String getApplicationName() override { return "Super Timecode Converter"; }
const juce::String getApplicationVersion() override { return "1.9.14"; }
bool moreThanOneInstanceAllowed() override { return false; }
void initialise(const juce::String&) override
{
#if JUCE_LINUX || JUCE_MAC
// A write to a TCP peer that has reset the connection raises
// SIGPIPE, whose default action ends the process: JUCE 9.0.3's
// StreamingSocket::write is send() without MSG_NOSIGNAL. A
// StageLinQ unit that closes its connection while STC still writes
// to it does that (simulated with a fake device, AUDIT SLQ-5; not
// seen on hardware). Ignored, the write fails with EPIPE and the
// connection's own error handling runs.
// Set before any thread starts; it applies to the whole process.
std::signal(SIGPIPE, SIG_IGN);
#endif
mainWindow.reset(new MainWindow(getApplicationName()));
}
void shutdown() override
{
mainWindow = nullptr;
}
void systemRequestedQuit() override
{
// Route through closeButtonPressed so the Show Lock guard applies
// (covers macOS Cmd+Q, dock Quit, etc.)
if (mainWindow != nullptr)
mainWindow->closeButtonPressed();
else
quit();
}
class MainWindow : public juce::DocumentWindow
{
public:
MainWindow(juce::String name)
: DocumentWindow(name,
juce::Colour(0xFF12141A), // Dark background
DocumentWindow::allButtons)
{
setUsingNativeTitleBar(false);
setTitleBarHeight(20);
setColour(juce::DocumentWindow::textColourId, juce::Colour(0xFF546E7A));
auto* mc = new MainComponent();
setContentOwned(mc, true);
setResizable(true, true);
// No maximum size (JUCE's own default maximum): a 2560x1440 cap
// kept the window from filling a 4K display (AUDIT UI-12).
setResizeLimits(800, 550, 0x3fffffff, 0x3fffffff);
centreWithSize(getWidth(), getHeight());
// Restore saved window position/size
auto saved = mc->getSavedMainWindowBounds();
if (saved.isNotEmpty())
{
auto parts = juce::StringArray::fromTokens(saved, " ", "");
if (parts.size() == 4)
{
auto b = juce::Rectangle<int>(parts[0].getIntValue(), parts[1].getIntValue(),
parts[2].getIntValue(), parts[3].getIntValue());
if (b.getWidth() >= 800 && b.getHeight() >= 550)
{
// Restored only where its title bar -- what the window
// is dragged by -- lies in a display's user area (off
// the taskbar and menu bar) for its full height and
// at least 100 px of its width. The old test, the
// window's centre on some display, let a window saved
// on a monitor since removed or rearranged come back
// with its title bar out of reach (AUDIT UI-12).
// Otherwise it stays centred as above.
const auto titleBar = b.withHeight(getTitleBarHeight());
for (auto& d : juce::Desktop::getInstance().getDisplays().displays)
{
const auto onDisplay = titleBar.getIntersection(d.userBounds.toNearestInt());
if (onDisplay.getHeight() == titleBar.getHeight() && onDisplay.getWidth() >= 100)
{
setBounds(b);
break;
}
}
}
}
}
setVisible(true);
}
void closeButtonPressed() override
{
auto* mc = dynamic_cast<MainComponent*>(getContentComponent());
// If Show Lock is active, confirm before quitting. One
// confirmation at a time: a second close or Cmd+Q while it is up
// does nothing, where it used to stack another (AUDIT UI-12).
if (mc != nullptr && mc->isShowModeLocked())
{
if (quitConfirmShowing)
return;
quitConfirmShowing = true;
auto options = juce::MessageBoxOptions()
.withIconType(juce::MessageBoxIconType::WarningIcon)
.withTitle("Show Lock Active")
.withMessage("Show Lock is active. Closing the application "
"will stop all timecode outputs.\n\n"
"Are you sure you want to quit?")
.withButton("Quit")
.withButton("Cancel");
juce::Component::SafePointer<MainWindow> safeThis(this);
juce::AlertWindow::showAsync(options, [safeThis](int result)
{
if (safeThis == nullptr) return;
safeThis->quitConfirmShowing = false;
if (result == 1)
{
auto* mc2 = dynamic_cast<MainComponent*>(safeThis->getContentComponent());
safeThis->saveWindowBoundsAndQuit(mc2);
}
});
return;
}
saveWindowBoundsAndQuit(mc);
}
private:
bool quitConfirmShowing = false; // the Show Lock quit confirmation is up
void saveWindowBoundsAndQuit(MainComponent* mc)
{
// Hand the window bounds to the settings before quitting; the
// MainComponent destructor, which quit() reaches through
// shutdown(), writes them with everything else in one save.
if (mc != nullptr)
{
auto b = getBounds();
mc->setMainWindowBounds(
juce::String(b.getX()) + " " + juce::String(b.getY()) + " "
+ juce::String(b.getWidth()) + " " + juce::String(b.getHeight()));
}
JUCEApplication::getInstance()->quit();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
private:
std::unique_ptr<MainWindow> mainWindow;
};
START_JUCE_APPLICATION(SuperTimecodeConverterApplication)