1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2021-07-24
7  * Description : MJPEG Stream configuration dialog - Views methods
8  *
9  * Copyright (C) 2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2021 by Quoc Hưng Tran <quochungtran1999 at gmail dot com>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "mjpegstreamdlg_p.h"
25 
26 namespace DigikamGenericMjpegStreamPlugin
27 {
28 
setupItemsView()29 QWidget* MjpegStreamDlg::setupItemsView()
30 {
31     d->albumSupport   = (d->settings.iface && d->settings.iface->supportAlbums());
32     QWidget* itemsSel = nullptr;
33 
34     if (d->albumSupport)
35     {
36         d->albumSelector = d->settings.iface->albumChooser(this);
37         itemsSel         = d->albumSelector;
38 
39         connect(d->settings.iface, SIGNAL(signalAlbumChooserSelectionChanged()),
40                 this, SLOT(slotSelectionChanged()));
41     }
42     else
43     {
44         d->listView = new DItemsList(this);
45         d->listView->setObjectName(QLatin1String("MjpegStream ImagesList"));
46         d->listView->setControlButtonsPlacement(DItemsList::ControlButtonsRight);
47         d->listView->setIface(d->settings.iface);
48 
49         // Add all items currently loaded in application.
50 
51         d->listView->loadImagesFromCurrentSelection();
52 
53         // Replug the previous shared items list.
54 
55         d->listView->slotAddImages(d->mngr->itemsList());
56         itemsSel    = d->listView;
57 
58         connect(d->listView, SIGNAL(signalImageListChanged()),
59                 this, SLOT(slotSelectionChanged()));
60     }
61 
62     return itemsSel;
63 }
64 
setupServerView()65 void MjpegStreamDlg::setupServerView()
66 {
67     QWidget* const serverSettings = new QWidget(d->tabView);
68 
69     QLabel* const portLbl         = new QLabel(i18nc("@label", "Server Port:"), serverSettings);
70     d->srvPort                    = new DIntNumInput(serverSettings);
71     d->srvPort->setDefaultValue(8080);
72     d->srvPort->setRange(1025, 65535, 1);
73     d->srvPort->setWhatsThis(i18nc("@info", "The MJPEG server IP port."));
74     portLbl->setBuddy(d->srvPort);
75 
76     d->startOnStartup             = new QCheckBox(i18nc("@option", "Start Server at Startup"));
77     d->startOnStartup->setWhatsThis(i18nc("@info", "Set this option to turn-on the MJPEG server at application start-up automatically"));
78     d->startOnStartup->setChecked(true);
79 
80     // ---
81 
82     QLabel* const explanation     = new QLabel(serverSettings);
83     explanation->setOpenExternalLinks(true);
84     explanation->setWordWrap(true);
85     explanation->setFrameStyle(QFrame::Box | QFrame::Plain);
86 
87     explanation->setText(i18nc("@info",
88         "The %1 server allows to share items through the local network using a web browser. "
89         "Motion JPEG is a video compression format in which each video frame is compressed "
90         "separately as a JPEG image. MJPEG streams is a standard which allows network clients to be "
91         "connected without additional module. Most major web browsers and players support MJPEG stream. "
92         "To access to stream from your browser, just use http://address:port as url, with address "       // krazy:exclude=insecurenet
93         "the MJPEG server IP, and port the server port set in this config dialog.",
94         QLatin1String("<a href='https://en.wikipedia.org/wiki/Motion_JPEG'>Motion JPEG</a>")));
95 
96     d->srvButton                  = new QPushButton(serverSettings);
97     d->srvStatus                  = new QLabel(serverSettings);
98     d->aStats                     = new QLabel(serverSettings);
99     d->separator                  = new QLabel(QLatin1String(" / "), serverSettings);
100     d->iStats                     = new QLabel(serverSettings);
101     d->progress                   = new WorkingWidget(serverSettings);
102     d->srvPreview                 = new QPushButton(i18nc("@action: button", "Preview..."), serverSettings);
103     d->srvPreview->setWhatsThis(i18nc("@info", "Press this button to preview the stream on your computer with your browser."));
104 
105     QGridLayout* const grid3      = new QGridLayout(serverSettings);
106     grid3->addWidget(portLbl,           0, 0, 1, 1);
107     grid3->addWidget(d->srvPort,        0, 1, 1, 1);
108     grid3->addWidget(d->startOnStartup, 0, 2, 1, 5);
109     grid3->addWidget(d->srvButton,      1, 0, 1, 1);
110     grid3->addWidget(d->srvStatus,      1, 1, 1, 1);
111     grid3->addWidget(d->aStats,         1, 2, 1, 1);
112     grid3->addWidget(d->separator,      1, 3, 1, 1);
113     grid3->addWidget(d->iStats,         1, 4, 1, 1);
114     grid3->addWidget(d->progress,       1, 5, 1, 1);
115     grid3->addWidget(d->srvPreview,     1, 6, 1, 1);
116     grid3->addWidget(explanation,       2, 0, 1, 7);
117     grid3->setSpacing(d->spacing);
118 
119     d->tabView->insertTab(Private::Server, serverSettings, i18nc("@title", "Server"));
120 
121     connect(d->srvButton, SIGNAL(clicked()),
122             this, SLOT(slotToggleMjpegServer()));
123 
124     connect(d->srvPort, SIGNAL(valueChanged(int)),
125             this, SLOT(slotSettingsChanged()));
126 
127     connect(d->srvPreview, SIGNAL(clicked()),
128             this, SLOT(slotOpenPreview()));
129 }
130 
setupStreamView()131 void MjpegStreamDlg::setupStreamView()
132 {
133     d->streamSettings         = new QWidget(d->tabView);
134 
135     QLabel* const qualityLbl  = new QLabel(i18nc("@label", "JPEG Quality:"), d->streamSettings);
136     d->quality                = new DIntNumInput(d->streamSettings);
137     d->quality->setDefaultValue(75);
138     d->quality->setRange(50, 100, 1);
139     d->quality->setWhatsThis(i18nc("@info", "The JPEG quality [50:lower - 100:higher]. "
140                                             "Warning: better quality require more network bandwidth"));
141 
142     qualityLbl->setBuddy(d->quality);
143 
144     // ---
145 
146     QLabel* const typeLabel   = new QLabel(d->streamSettings);
147     typeLabel->setWordWrap(false);
148     typeLabel->setText(i18nc("@label", "JPEG Size:"));
149     d->typeVal                = new DComboBox(d->streamSettings);
150     d->typeVal->combo()->setEditable(false);
151 
152     QMap<VidSlideSettings::VidType, QString> map                = VidSlideSettings::videoTypeNames();
153     QMap<VidSlideSettings::VidType, QString>::const_iterator it = map.constBegin();
154 
155     while (it != map.constEnd())
156     {
157         d->typeVal->addItem(it.value(), (int)it.key());
158         ++it;
159     }
160 
161     d->typeVal->setDefaultIndex(VidSlideSettings::BLUERAY);
162     d->typeVal->setWhatsThis(i18nc("@info", "The JPEG image size in pixels, using standard screen resolutions. "
163                                             "Warning: larger size require more network bandwidth"));
164     typeLabel->setBuddy(d->typeVal);
165 
166     // ---
167 
168     QLabel* const delayLbl    = new QLabel(i18nc("@label", "Delay in Seconds:"), d->streamSettings);
169     d->delay                  = new DIntNumInput(d->streamSettings);
170     d->delay->setDefaultValue(5);
171     d->delay->setRange(1, 3600, 1);
172     d->delay->setWhatsThis(i18nc("@info", "The delay in seconds between images."));
173     delayLbl->setBuddy(d->delay);
174 
175     QLabel* const rateLbl     = new QLabel(i18nc("@label", "Frames by Second:"), d->streamSettings);
176     d->rate                   = new DIntNumInput(d->streamSettings);
177     d->rate->setDefaultValue(10);
178     d->rate->setRange(5, 24, 1);
179     d->rate->setWhatsThis(i18nc("@info", "The number of frames by second to render the stream. "
180                                          "Warning: larger rate require more network bandwidth"));
181     rateLbl->setBuddy(d->rate);
182 
183     d->streamLoop             = new QCheckBox(i18nc("@option:check", "Stream in Loop"), d->streamSettings);
184     d->streamLoop->setChecked(true);
185     d->streamLoop->setWhatsThis(i18nc("@info", "The MJPEG stream will be played in loop instead once."));
186 
187     QGridLayout* const grid2  = new QGridLayout(d->streamSettings);
188     grid2->addWidget(qualityLbl,    0, 0, 1, 1);
189     grid2->addWidget(d->quality,    0, 1, 1, 1);
190     grid2->addWidget(typeLabel,     1, 0, 1, 1);
191     grid2->addWidget(d->typeVal,    1, 1, 1, 1);
192     grid2->addWidget(delayLbl,      2, 0, 1, 1);
193     grid2->addWidget(d->delay,      2, 1, 1, 1);
194     grid2->addWidget(rateLbl,       3, 0, 1, 1);
195     grid2->addWidget(d->rate,       3, 1, 1, 1);
196     grid2->addWidget(d->streamLoop, 4, 0, 1, 2);
197     grid2->setSpacing(d->spacing);
198 
199     d->tabView->insertTab(Private::Stream, d->streamSettings, i18nc("@title", "Stream"));
200 
201     connect(d->delay, SIGNAL(valueChanged(int)),
202             this, SLOT(slotSettingsChanged()));
203 
204     connect(d->rate, SIGNAL(valueChanged(int)),
205             this, SLOT(slotSettingsChanged()));
206 
207     connect(d->quality, SIGNAL(valueChanged(int)),
208             this, SLOT(slotSettingsChanged()));
209 
210     connect(d->streamLoop, SIGNAL(stateChanged(int)),
211             this, SLOT(slotSettingsChanged()));
212 
213     connect(d->typeVal, SIGNAL(currentIndexChanged(int)),
214             this, SLOT(slotSettingsChanged()));
215 }
216 
setupTransitionView()217 void MjpegStreamDlg::setupTransitionView()
218 {
219     QWidget* const transitionSettings = new QWidget(d->tabView);
220 
221     QLabel* const transLabel          = new QLabel(transitionSettings);
222     transLabel->setWordWrap(false);
223     transLabel->setText(i18nc("@label", "Type:"));
224     d->transVal                       = new DComboBox(transitionSettings);
225     d->transVal->combo()->setEditable(false);
226 
227     QMap<TransitionMngr::TransType, QString> map4                = TransitionMngr::transitionNames();
228     QMap<TransitionMngr::TransType, QString>::const_iterator it4 = map4.constBegin();
229 
230     while (it4 != map4.constEnd())
231     {
232         d->transVal->addItem(it4.value(), (int)it4.key());
233         ++it4;
234     }
235 
236     d->transVal->setDefaultIndex(TransitionMngr::None);
237     transLabel->setBuddy(d->transVal);
238 
239     QLabel* const transNote  = new QLabel(transitionSettings);
240     transNote->setWordWrap(true);
241     transNote->setText(i18nc("@label", "A transition is an visual effect applied between two images. "
242                                        "For some effects, the duration can depend of random values and "
243                                        "can change while the stream."));
244 
245     d->transPreview              = new TransitionPreview(transitionSettings);
246     d->transPreview->setImagesList(QList<QUrl>());
247 
248     QGridLayout* const transGrid = new QGridLayout(transitionSettings);
249     transGrid->setSpacing(d->spacing);
250     transGrid->addWidget(transLabel,      0, 0, 1, 1);
251     transGrid->addWidget(d->transVal,     0, 1, 1, 1);
252     transGrid->addWidget(transNote,       1, 0, 1, 2);
253     transGrid->addWidget(d->transPreview, 0, 2, 2, 1);
254     transGrid->setColumnStretch(1, 10);
255     transGrid->setRowStretch(1, 10);
256 
257     d->tabView->insertTab(Private::Transition, transitionSettings, i18nc("@title", "Transition"));
258 
259     connect(d->transVal, SIGNAL(currentIndexChanged(int)),
260             this, SLOT(slotSettingsChanged()));
261 }
262 
setupEffectView()263 void MjpegStreamDlg::setupEffectView()
264 {
265     QWidget* const effectSettings = new QWidget(d->tabView);
266 
267     QLabel* const effLabel        = new QLabel(effectSettings);
268     effLabel->setWordWrap(false);
269     effLabel->setText(i18nc("@label", "Type:"));
270     d->effVal                     = new DComboBox(effectSettings);
271     d->effVal->combo()->setEditable(false);
272 
273     QMap<EffectMngr::EffectType, QString> map6                = EffectMngr::effectNames();
274     QMap<EffectMngr::EffectType, QString>::const_iterator it6 = map6.constBegin();
275 
276     while (it6 != map6.constEnd())
277     {
278         d->effVal->insertItem((int)it6.key(), it6.value(), (int)it6.key());
279         ++it6;
280     }
281 
282     d->effVal->setDefaultIndex(EffectMngr::None);
283     effLabel->setBuddy(d->effVal);
284 
285     QLabel* const effNote      = new QLabel(effectSettings);
286     effNote->setWordWrap(true);
287     effNote->setText(i18nc("@label", "An effect is an visual panning or zooming applied while an image "
288                                      "is displayed in MJPEG stream."));
289 
290     d->effPreview              = new EffectPreview(effectSettings);
291     d->effPreview->setImagesList(QList<QUrl>());
292 
293     QGridLayout* const effGrid = new QGridLayout(effectSettings);
294     effGrid->setSpacing(d->spacing);
295     effGrid->addWidget(effLabel,      0, 0, 1, 1);
296     effGrid->addWidget(d->effVal,     0, 1, 1, 1);
297     effGrid->addWidget(effNote,       1, 0, 1, 2);
298     effGrid->addWidget(d->effPreview, 0, 2, 2, 1);
299     effGrid->setColumnStretch(1, 10);
300     effGrid->setRowStretch(1, 10);
301 
302     d->tabView->insertTab(Private::Effect, effectSettings, i18nc("@title", "Effect"));
303 
304     connect(d->effVal, SIGNAL(currentIndexChanged(int)),
305             this, SLOT(slotSettingsChanged()));
306 }
307 
setupOSDView()308 void MjpegStreamDlg::setupOSDView()
309 {
310     QWidget* const OSDSettings = new QWidget(d->tabView);
311 
312     d->showName                = new QCheckBox(i18nc("@option:check", "Show image file name"), OSDSettings);
313     d->showName->setWhatsThis(i18nc("@info", "Show the image file name at the bottom of the screen."));
314 
315     d->showDate                = new QCheckBox(i18nc("@option:check", "Show image creation date"), OSDSettings);
316     d->showDate->setWhatsThis(i18nc("@info", "Show the image creation time/date at the bottom of the screen."));
317 
318     d->showApertureFocal       = new QCheckBox(i18nc("@option:check", "Show camera aperture and focal length"), OSDSettings);
319     d->showApertureFocal->setWhatsThis(i18nc("@info", "Show the camera aperture and focal length at the bottom of the screen."));
320 
321     d->showExpoSensitivity     = new QCheckBox(i18nc("@option:check", "Show camera exposure and sensitivity"), OSDSettings);
322     d->showExpoSensitivity->setWhatsThis(i18nc("@info", "Show the camera exposure and sensitivity at the bottom of the screen."));
323 
324     d->showMakeModel           = new QCheckBox(i18nc("@option:check", "Show camera make and model"), OSDSettings);
325     d->showMakeModel->setWhatsThis(i18nc("@info", "Show the camera make and model at the bottom of the screen."));
326 
327     d->showLensModel           = new QCheckBox(i18nc("@option:check", "Show camera lens model"), OSDSettings);
328     d->showLensModel->setWhatsThis(i18nc("@info", "Show the camera lens model at the bottom of the screen."));
329 
330     d->showComment             = new QCheckBox(i18nc("@option:check", "Show image caption"), OSDSettings);
331     d->showComment->setWhatsThis(i18nc("@info", "Show the image caption at the bottom of the screen."));
332 
333     d->showTitle               = new QCheckBox(i18nc("@option:check", "Show image title"), OSDSettings);
334     d->showTitle->setWhatsThis(i18nc("@info", "Show the image title at the bottom of the screen."));
335 
336     d->showCapIfNoTitle        = new QCheckBox(i18nc("@option:check", "Show image caption if it has not title"), OSDSettings);
337     d->showCapIfNoTitle->setWhatsThis(i18nc("@info", "Show the image caption at the bottom of the screen if no titles existed."));
338 
339     d->showRating              = new QCheckBox(i18nc("@option:check", "Show image rating"), OSDSettings);
340     d->showRating->setWhatsThis(i18nc("@info", "Show the digiKam image rating at the bottom of the screen."));
341 
342     d->showTags                = new QCheckBox(i18nc("@option:check", "Show image tags"), OSDSettings);
343     d->showTags->setWhatsThis(i18nc("@info", "Show the digiKam image tag names at the bottom of the screen."));
344 
345     d->osdFont                 = new DFontSelect(i18nc("@option", "On Screen Display Font:"), OSDSettings);
346     d->osdFont->setToolTip(i18nc("@info", "Select here the font used to display text in the MJPEG."));
347 
348     QGridLayout* const grid    = new QGridLayout(OSDSettings);
349     grid->addWidget(d->showName,             1, 0, 1, 1);
350     grid->addWidget(d->showRating,           1, 1, 1, 1);
351     grid->addWidget(d->showApertureFocal,    2, 0, 1, 1);
352     grid->addWidget(d->showDate,             2, 1, 1, 1);
353     grid->addWidget(d->showMakeModel,        3, 0, 1, 1);
354     grid->addWidget(d->showExpoSensitivity,  3, 1, 1, 1);
355     grid->addWidget(d->showLensModel,        4, 0, 1, 1);
356     grid->addWidget(d->showComment,          4, 1, 1, 1);
357     grid->addWidget(d->showTitle,            5, 0, 1, 1);
358     grid->addWidget(d->showCapIfNoTitle,     5, 1, 1, 1);
359     grid->addWidget(d->showTags,             6, 0, 1, 1);
360     grid->addWidget(d->osdFont,              7, 0, 1, 2);
361 
362     d->tabView->insertTab(Private::OSD, OSDSettings, i18nc("@title: On Screen Display", "OSD"));
363 
364     connect(d->showName, SIGNAL(stateChanged(int)),
365             this, SLOT(slotSettingsChanged()));
366 
367     connect(d->showApertureFocal, SIGNAL(stateChanged(int)),
368             this, SLOT(slotSettingsChanged()));
369 
370     connect(d->showDate, SIGNAL(stateChanged(int)),
371             this, SLOT(slotSettingsChanged()));
372 
373     connect(d->showMakeModel, SIGNAL(stateChanged(int)),
374             this, SLOT(slotSettingsChanged()));
375 
376     connect(d->showExpoSensitivity, SIGNAL(stateChanged(int)),
377             this, SLOT(slotSettingsChanged()));
378 
379     connect(d->showLensModel, SIGNAL(stateChanged(int)),
380             this, SLOT(slotSettingsChanged()));
381 
382     connect(d->showComment, SIGNAL(stateChanged(int)),
383             this, SLOT(slotSettingsChanged()));
384 
385    // Disable and uncheck the "Show captions if no title" checkbox if the "Show comment" checkbox enabled
386 
387     connect(d->showComment, SIGNAL(stateChanged(int)),
388             this, SLOT(slotSetUnchecked(int)));
389 
390     connect(d->showComment, SIGNAL(toggled(bool)),
391             d->showCapIfNoTitle, SLOT(setDisabled(bool)));
392 
393     connect(d->showTitle, SIGNAL(stateChanged(int)),
394             this, SLOT(slotSettingsChanged()));
395 
396     connect(d->showCapIfNoTitle, SIGNAL(stateChanged(int)),
397             this, SLOT(slotSettingsChanged()));
398 
399     connect(d->showTags, SIGNAL(stateChanged(int)),
400             this, SLOT(slotSettingsChanged()));
401 
402     connect(d->showRating, SIGNAL(stateChanged(int)),
403             this, SLOT(slotSettingsChanged()));
404 
405     connect(d->osdFont, SIGNAL(signalFontChanged()),
406             this, SLOT(slotSettingsChanged()));
407 }
408 
slotSetUnchecked(int)409 void MjpegStreamDlg::slotSetUnchecked(int)
410 {
411     d->showCapIfNoTitle->setCheckState(Qt::Unchecked);
412 }
413 
414 } // namespace DigikamGenericMjpegStreamPlugin
415