1 /*
2 SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
3 SPDX-FileCopyrightText: 2010-2019 Mladen Milinkovic <max@smoothware.net>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "useraction.h"
9 #include "application.h"
10 #include "core/subtitle.h"
11 #include "videoplayer/videoplayer.h"
12 #include "gui/treeview/lineswidget.h"
13 #include "gui/currentlinewidget.h"
14
15 #include <QApplication>
16
17 using namespace SubtitleComposer;
18
UserAction(QAction * action,int enableFlags)19 UserAction::UserAction(QAction *action, int enableFlags) :
20 m_action(action),
21 m_enableFlags(enableFlags),
22 m_actionEnabled(action->isEnabled()),
23 m_contextEnabled(false),
24 m_ignoreActionEnabledSignal(false)
25 {
26 updateEnabledState();
27
28 connect(action, &QAction::changed, this, &UserAction::onActionChanged);
29 }
30
31 void
onActionChanged()32 UserAction::onActionChanged()
33 {
34 if(!m_ignoreActionEnabledSignal) {
35 if(m_action->isEnabled() != isEnabled()) {
36 // qDebug() << "enabled state externally changed for" << m_action->objectName();
37
38 m_actionEnabled = m_action->isEnabled();
39 updateEnabledState();
40 }
41 }
42 }
43
44 int
enableFlags()45 UserAction::enableFlags()
46 {
47 return m_enableFlags;
48 }
49
50 QAction *
action()51 UserAction::action()
52 {
53 return m_action;
54 }
55
56 bool
isEnabled()57 UserAction::isEnabled()
58 {
59 return m_actionEnabled && m_contextEnabled;
60 }
61
62 void
setActionEnabled(bool enabled)63 UserAction::setActionEnabled(bool enabled)
64 {
65 if(m_actionEnabled != enabled) {
66 m_actionEnabled = enabled;
67 updateEnabledState();
68 }
69 }
70
71 void
setContextFlags(int contextFlags)72 UserAction::setContextFlags(int contextFlags)
73 {
74 bool contextEnabled = (contextFlags & m_enableFlags) == m_enableFlags;
75 if(m_contextEnabled != contextEnabled) {
76 m_contextEnabled = contextEnabled;
77 updateEnabledState();
78 }
79 }
80
81 void
updateEnabledState()82 UserAction::updateEnabledState()
83 {
84 bool enabled = m_actionEnabled && m_contextEnabled;
85
86 if(m_action->isEnabled() != enabled) {
87 m_ignoreActionEnabledSignal = true;
88
89 m_action->setEnabled(enabled);
90
91 // QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
92
93 // qDebug() << "setting action" << m_action->text() << (m_action->isEnabled() ? "ENABLED" : "DISABLED");
94
95 m_ignoreActionEnabledSignal = false;
96 }
97 }
98
99 /// USER ACTION MANAGER
100
UserActionManager()101 UserActionManager::UserActionManager()
102 : m_actionSpecs(),
103 m_subtitle(nullptr),
104 m_linesWidget(nullptr),
105 m_translationMode(false),
106 m_contextFlags(UserAction::SubClosed | UserAction::SubTrClosed | UserAction::VideoClosed | UserAction::FullScreenOff | UserAction::AnchorsNone)
107 {
108 VideoPlayer *videoPlayer = VideoPlayer::instance();
109 connect(videoPlayer, &VideoPlayer::fileOpened, this, &UserActionManager::onPlayerStateChanged);
110 connect(videoPlayer, &VideoPlayer::fileClosed, this, &UserActionManager::onPlayerStateChanged);
111 connect(videoPlayer, &VideoPlayer::playing, this, &UserActionManager::onPlayerStateChanged);
112 connect(videoPlayer, &VideoPlayer::paused, this, &UserActionManager::onPlayerStateChanged);
113 connect(videoPlayer, &VideoPlayer::stopped, this, &UserActionManager::onPlayerStateChanged);
114 onPlayerStateChanged();
115 }
116
117 UserActionManager *
instance()118 UserActionManager::instance()
119 {
120 static UserActionManager *actionManager = nullptr;
121 if(!actionManager) {
122 actionManager = new UserActionManager();
123 actionManager->setParent(QApplication::instance());
124 }
125 return actionManager;
126 }
127
128 void
addAction(QAction * action,int enableFlags)129 UserActionManager::addAction(QAction *action, int enableFlags)
130 {
131 UserAction *a = new UserAction(action, enableFlags);
132 addAction(a);
133 a->setParent(this); // UserActionManager will delete *a
134 }
135
136 void
addAction(UserAction * actionSpec)137 UserActionManager::addAction(UserAction *actionSpec)
138 {
139 m_actionSpecs.append(actionSpec);
140
141 actionSpec->setContextFlags(m_contextFlags);
142 }
143
144 void
setSubtitle(const Subtitle * subtitle)145 UserActionManager::setSubtitle(const Subtitle *subtitle)
146 {
147 if(m_subtitle) {
148 disconnect(m_subtitle.constData(), &Subtitle::linesRemoved, this, &UserActionManager::onSubtitleLinesChanged);
149 disconnect(m_subtitle.constData(), &Subtitle::linesInserted, this, &UserActionManager::onSubtitleLinesChanged);
150
151 disconnect(m_subtitle.constData(), &Subtitle::primaryDirtyStateChanged, this, &UserActionManager::onPrimaryDirtyStateChanged);
152 disconnect(m_subtitle.constData(), &Subtitle::secondaryDirtyStateChanged, this, &UserActionManager::onSecondaryDirtyStateChanged);
153
154 disconnect(m_subtitle.constData(), &Subtitle::lineAnchorChanged, this, &UserActionManager::onSubtitleAnchorsChanged);
155 }
156
157 m_subtitle = subtitle;
158
159 int newContextFlags = m_contextFlags & ~UserAction::SubtitleMask;
160
161 if(m_subtitle) {
162 connect(m_subtitle.constData(), &Subtitle::linesRemoved, this, &UserActionManager::onSubtitleLinesChanged);
163 connect(m_subtitle.constData(), &Subtitle::linesInserted, this, &UserActionManager::onSubtitleLinesChanged);
164
165 connect(m_subtitle.constData(), &Subtitle::primaryDirtyStateChanged, this, &UserActionManager::onPrimaryDirtyStateChanged);
166 connect(m_subtitle.constData(), &Subtitle::secondaryDirtyStateChanged, this, &UserActionManager::onSecondaryDirtyStateChanged);
167
168 connect(m_subtitle.constData(), &Subtitle::lineAnchorChanged, this, &UserActionManager::onSubtitleAnchorsChanged);
169
170 newContextFlags |= UserAction::SubOpened;
171
172 if(m_subtitle->isPrimaryDirty())
173 newContextFlags |= UserAction::SubPDirty;
174 else
175 newContextFlags |= UserAction::SubPClean;
176
177 if(m_translationMode) {
178 newContextFlags |= UserAction::SubTrOpened;
179
180 if(m_subtitle->isSecondaryDirty())
181 newContextFlags |= UserAction::SubSDirty;
182 else
183 newContextFlags |= UserAction::SubSClean;
184 } else {
185 newContextFlags |= (UserAction::SubTrClosed | UserAction::SubSClean);
186 }
187
188 if(m_subtitle->linesCount() > 0)
189 newContextFlags |= UserAction::SubHasLine;
190 if(m_subtitle->linesCount() > 1)
191 newContextFlags |= UserAction::SubHasLines;
192
193 if(!m_subtitle->hasAnchors()) {
194 newContextFlags |= UserAction::AnchorsNone;
195 newContextFlags |= UserAction::EditableShowTime;
196 } else {
197 newContextFlags |= UserAction::AnchorsSome;
198 const SubtitleLine *selected = m_subtitle->line(m_linesWidget->firstSelectedIndex());
199 if(m_subtitle->isLineAnchored(selected))
200 newContextFlags |= UserAction::EditableShowTime;
201 }
202 } else {
203 newContextFlags |= (UserAction::SubClosed | UserAction::SubPClean | UserAction::SubTrClosed | UserAction::SubSClean);
204 }
205
206 updateActionsContext(newContextFlags);
207 }
208
209 void
onSubtitleLinesChanged()210 UserActionManager::onSubtitleLinesChanged()
211 {
212 int newContextFlags = m_contextFlags & ~(UserAction::SubHasLine | UserAction::SubHasLines);
213
214 if(m_subtitle->linesCount() > 0)
215 newContextFlags |= UserAction::SubHasLine;
216 if(m_subtitle->linesCount() > 1)
217 newContextFlags |= UserAction::SubHasLines;
218
219 updateActionsContext(newContextFlags);
220 }
221
222 void
onPrimaryDirtyStateChanged(bool dirty)223 UserActionManager::onPrimaryDirtyStateChanged(bool dirty)
224 {
225 int newContextFlags = m_contextFlags & ~(UserAction::SubPDirty | UserAction::SubPClean);
226
227 if(dirty)
228 newContextFlags |= UserAction::SubPDirty;
229 else
230 newContextFlags |= UserAction::SubPClean;
231
232 updateActionsContext(newContextFlags);
233 }
234
235 void
onSecondaryDirtyStateChanged(bool dirty)236 UserActionManager::onSecondaryDirtyStateChanged(bool dirty)
237 {
238 int newContextFlags = m_contextFlags & ~(UserAction::SubSDirty | UserAction::SubSClean);
239
240 if(m_translationMode) {
241 if(dirty)
242 newContextFlags |= UserAction::SubSDirty;
243 else
244 newContextFlags |= UserAction::SubSClean;
245 } else {
246 newContextFlags |= UserAction::SubSClean;
247 }
248
249 updateActionsContext(newContextFlags);
250 }
251
252 void
setLinesWidget(LinesWidget * linesWidget)253 UserActionManager::setLinesWidget(LinesWidget *linesWidget)
254 {
255 if(m_linesWidget)
256 disconnect(m_linesWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &UserActionManager::onLinesWidgetSelectionChanged);
257
258 m_linesWidget = linesWidget;
259
260 if(m_linesWidget) {
261 connect(m_linesWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &UserActionManager::onLinesWidgetSelectionChanged);
262 onLinesWidgetSelectionChanged();
263 }
264 }
265
266 void
onLinesWidgetSelectionChanged()267 UserActionManager::onLinesWidgetSelectionChanged()
268 {
269 int newContextFlags = m_contextFlags & ~(UserAction::SelectionMask | UserAction::EditableShowTime);
270
271 int selectedIndex = m_linesWidget->firstSelectedIndex();
272
273 if(selectedIndex >= 0)
274 newContextFlags |= UserAction::HasSelection;
275
276 if(m_subtitle) {
277 if(!m_subtitle->hasAnchors()) {
278 newContextFlags |= UserAction::EditableShowTime;
279 } else {
280 const SubtitleLine *line = m_subtitle->line(selectedIndex);
281 if(m_subtitle->isLineAnchored(line))
282 newContextFlags |= UserAction::EditableShowTime;
283 }
284 }
285
286 updateActionsContext(newContextFlags);
287 }
288
289 void
onPlayerStateChanged()290 UserActionManager::onPlayerStateChanged()
291 {
292 int newContextFlags = m_contextFlags & ~UserAction::VideoMask;
293
294 const int state = VideoPlayer::instance()->state();
295 if(state > VideoPlayer::Opening) {
296 newContextFlags |= UserAction::VideoOpened;
297 if(state < VideoPlayer::Playing)
298 newContextFlags |= UserAction::VideoStopped;
299 else
300 newContextFlags |= UserAction::VideoPlaying;
301 } else {
302 newContextFlags |= UserAction::VideoClosed;
303 }
304
305 updateActionsContext(newContextFlags);
306 }
307
308 void
onSubtitleAnchorsChanged()309 UserActionManager::onSubtitleAnchorsChanged()
310 {
311 int newContextFlags = m_contextFlags & ~UserAction::AnchorsMask;
312
313 if(!m_subtitle->hasAnchors()) {
314 newContextFlags |= UserAction::AnchorsNone;
315 newContextFlags |= UserAction::EditableShowTime;
316 } else {
317 newContextFlags |= UserAction::AnchorsSome;
318 const SubtitleLine *selected = m_subtitle->line(m_linesWidget->firstSelectedIndex());
319 if(m_subtitle->isLineAnchored(selected))
320 newContextFlags |= UserAction::EditableShowTime;
321 }
322
323 updateActionsContext(newContextFlags);
324 }
325
326 void
setTranslationMode(bool translationMode)327 UserActionManager::setTranslationMode(bool translationMode)
328 {
329 m_translationMode = translationMode;
330
331 int newContextFlags = m_contextFlags & ~(UserAction::SubTrClosed | UserAction::SubTrOpened | UserAction::SubSDirty | UserAction::SubSClean);
332
333 if(m_subtitle && m_translationMode) {
334 newContextFlags |= UserAction::SubTrOpened;
335
336 if(m_subtitle->isSecondaryDirty())
337 newContextFlags |= UserAction::SubSDirty;
338 else
339 newContextFlags |= UserAction::SubSClean;
340 } else {
341 newContextFlags |= (UserAction::SubTrClosed | UserAction::SubSClean);
342 }
343
344 updateActionsContext(newContextFlags);
345 }
346
347 void
setFullScreenMode(bool fullScreenMode)348 UserActionManager::setFullScreenMode(bool fullScreenMode)
349 {
350 int newContextFlags = m_contextFlags & ~UserAction::FullScreenMask;
351
352 if(fullScreenMode)
353 newContextFlags |= UserAction::FullScreenOn;
354 else
355 newContextFlags |= UserAction::FullScreenOff;
356
357 updateActionsContext(newContextFlags);
358 }
359
360 void
updateActionsContext(int contextFlags)361 UserActionManager::updateActionsContext(int contextFlags)
362 {
363 // if ( (m_contextFlags & UserAction::SubHasLine) != (contextFlags & UserAction::SubHasLine) )
364 // qDebug() << "has line:" << ((contextFlags & UserAction::SubHasLine) != 0);
365 // if ( (m_contextFlags & UserAction::SubHasLines) != (contextFlags & UserAction::SubHasLines) )
366 // qDebug() << "has lines:" << ((contextFlags & UserAction::SubHasLines) != 0);
367 // if ( (m_contextFlags & UserAction::HasSelection) != (contextFlags & UserAction::HasSelection) )
368 // qDebug() << "has selection:" << ((contextFlags & UserAction::HasSelection) != 0);
369
370 if(m_contextFlags != contextFlags) {
371 m_contextFlags = contextFlags;
372 for(QList<UserAction *>::ConstIterator it = m_actionSpecs.constBegin(), end = m_actionSpecs.constEnd(); it != end; ++it)
373 (*it)->setContextFlags(m_contextFlags);
374 }
375 }
376
377
378