1 /******************************************************************************
2 Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include <QMessageBox>
19 #include "window-basic-main.hpp"
20 #include "window-basic-source-select.hpp"
21 #include "qt-wrappers.hpp"
22 #include "obs-app.hpp"
23
24 struct AddSourceData {
25 obs_source_t *source;
26 bool visible;
27 };
28
EnumSources(void * data,obs_source_t * source)29 bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)
30 {
31 if (obs_source_is_hidden(source))
32 return false;
33
34 OBSBasicSourceSelect *window =
35 static_cast<OBSBasicSourceSelect *>(data);
36 const char *name = obs_source_get_name(source);
37 const char *id = obs_source_get_unversioned_id(source);
38
39 if (strcmp(id, window->id) == 0)
40 window->ui->sourceList->addItem(QT_UTF8(name));
41
42 return true;
43 }
44
EnumGroups(void * data,obs_source_t * source)45 bool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source)
46 {
47 OBSBasicSourceSelect *window =
48 static_cast<OBSBasicSourceSelect *>(data);
49 const char *name = obs_source_get_name(source);
50 const char *id = obs_source_get_unversioned_id(source);
51
52 if (strcmp(id, window->id) == 0) {
53 OBSBasic *main =
54 reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
55 OBSScene scene = main->GetCurrentScene();
56
57 obs_sceneitem_t *existing = obs_scene_get_group(scene, name);
58 if (!existing)
59 window->ui->sourceList->addItem(QT_UTF8(name));
60 }
61
62 return true;
63 }
64
OBSSourceAdded(void * data,calldata_t * calldata)65 void OBSBasicSourceSelect::OBSSourceAdded(void *data, calldata_t *calldata)
66 {
67 OBSBasicSourceSelect *window =
68 static_cast<OBSBasicSourceSelect *>(data);
69 obs_source_t *source = (obs_source_t *)calldata_ptr(calldata, "source");
70
71 QMetaObject::invokeMethod(window, "SourceAdded",
72 Q_ARG(OBSSource, source));
73 }
74
OBSSourceRemoved(void * data,calldata_t * calldata)75 void OBSBasicSourceSelect::OBSSourceRemoved(void *data, calldata_t *calldata)
76 {
77 OBSBasicSourceSelect *window =
78 static_cast<OBSBasicSourceSelect *>(data);
79 obs_source_t *source = (obs_source_t *)calldata_ptr(calldata, "source");
80
81 QMetaObject::invokeMethod(window, "SourceRemoved",
82 Q_ARG(OBSSource, source));
83 }
84
SourceAdded(OBSSource source)85 void OBSBasicSourceSelect::SourceAdded(OBSSource source)
86 {
87 const char *name = obs_source_get_name(source);
88 const char *sourceId = obs_source_get_unversioned_id(source);
89
90 if (strcmp(sourceId, id) != 0)
91 return;
92
93 ui->sourceList->addItem(name);
94 }
95
SourceRemoved(OBSSource source)96 void OBSBasicSourceSelect::SourceRemoved(OBSSource source)
97 {
98 const char *name = obs_source_get_name(source);
99 const char *sourceId = obs_source_get_unversioned_id(source);
100
101 if (strcmp(sourceId, id) != 0)
102 return;
103
104 QList<QListWidgetItem *> items =
105 ui->sourceList->findItems(name, Qt::MatchFixedString);
106
107 if (!items.count())
108 return;
109
110 delete items[0];
111 }
112
AddSource(void * _data,obs_scene_t * scene)113 static void AddSource(void *_data, obs_scene_t *scene)
114 {
115 AddSourceData *data = (AddSourceData *)_data;
116 obs_sceneitem_t *sceneitem;
117
118 sceneitem = obs_scene_add(scene, data->source);
119 obs_sceneitem_set_visible(sceneitem, data->visible);
120 }
121
get_new_source_name(const char * name)122 static char *get_new_source_name(const char *name)
123 {
124 struct dstr new_name = {0};
125 int inc = 0;
126
127 dstr_copy(&new_name, name);
128
129 for (;;) {
130 obs_source_t *existing_source =
131 obs_get_source_by_name(new_name.array);
132 if (!existing_source)
133 break;
134
135 obs_source_release(existing_source);
136
137 dstr_printf(&new_name, "%s %d", name, ++inc + 1);
138 }
139
140 return new_name.array;
141 }
142
AddExisting(const char * name,bool visible,bool duplicate)143 static void AddExisting(const char *name, bool visible, bool duplicate)
144 {
145 OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
146 OBSScene scene = main->GetCurrentScene();
147 if (!scene)
148 return;
149
150 obs_source_t *source = obs_get_source_by_name(name);
151 if (source) {
152 if (duplicate) {
153 obs_source_t *from = source;
154 char *new_name = get_new_source_name(name);
155 source = obs_source_duplicate(from, new_name, false);
156 bfree(new_name);
157 obs_source_release(from);
158
159 if (!source)
160 return;
161 }
162
163 AddSourceData data;
164 data.source = source;
165 data.visible = visible;
166
167 obs_enter_graphics();
168 obs_scene_atomic_update(scene, AddSource, &data);
169 obs_leave_graphics();
170
171 obs_source_release(source);
172 }
173 }
174
AddNew(QWidget * parent,const char * id,const char * name,const bool visible,OBSSource & newSource)175 bool AddNew(QWidget *parent, const char *id, const char *name,
176 const bool visible, OBSSource &newSource)
177 {
178 OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
179 OBSScene scene = main->GetCurrentScene();
180 bool success = false;
181 if (!scene)
182 return false;
183
184 obs_source_t *source = obs_get_source_by_name(name);
185 if (source && parent) {
186 OBSMessageBox::information(parent, QTStr("NameExists.Title"),
187 QTStr("NameExists.Text"));
188
189 } else {
190 const char *v_id = obs_get_latest_input_type_id(id);
191 source = obs_source_create(v_id, name, NULL, nullptr);
192
193 if (source) {
194 AddSourceData data;
195 data.source = source;
196 data.visible = visible;
197
198 obs_enter_graphics();
199 obs_scene_atomic_update(scene, AddSource, &data);
200 obs_leave_graphics();
201
202 newSource = source;
203
204 /* set monitoring if source monitors by default */
205 uint32_t flags = obs_source_get_output_flags(source);
206 if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) {
207 obs_source_set_monitoring_type(
208 source,
209 OBS_MONITORING_TYPE_MONITOR_ONLY);
210 }
211
212 success = true;
213 }
214 }
215
216 obs_source_release(source);
217 return success;
218 }
219
on_buttonBox_accepted()220 void OBSBasicSourceSelect::on_buttonBox_accepted()
221 {
222 bool useExisting = ui->selectExisting->isChecked();
223 bool visible = ui->sourceVisible->isChecked();
224
225 if (useExisting) {
226 QListWidgetItem *item = ui->sourceList->currentItem();
227 if (!item)
228 return;
229
230 AddExisting(QT_TO_UTF8(item->text()), visible, false);
231 } else {
232 if (ui->sourceName->text().isEmpty()) {
233 OBSMessageBox::warning(this,
234 QTStr("NoNameEntered.Title"),
235 QTStr("NoNameEntered.Text"));
236 return;
237 }
238
239 if (!AddNew(this, id, QT_TO_UTF8(ui->sourceName->text()),
240 visible, newSource))
241 return;
242
243 OBSBasic *main =
244 reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
245 std::string scene_name =
246 obs_source_get_name(main->GetCurrentSceneSource());
247 auto undo = [scene_name, main](const std::string &data) {
248 obs_source_t *source =
249 obs_get_source_by_name(data.c_str());
250 obs_source_release(source);
251 obs_source_remove(source);
252
253 obs_source_t *scene_source =
254 obs_get_source_by_name(scene_name.c_str());
255 main->SetCurrentScene(scene_source, true);
256 obs_source_release(scene_source);
257
258 main->RefreshSources(main->GetCurrentScene());
259 };
260 obs_data_t *wrapper = obs_data_create();
261 obs_data_set_string(wrapper, "id", id);
262 obs_sceneitem_t *item = obs_scene_sceneitem_from_source(
263 main->GetCurrentScene(), newSource);
264 obs_data_set_int(wrapper, "item_id",
265 obs_sceneitem_get_id(item));
266 obs_data_set_string(
267 wrapper, "name",
268 ui->sourceName->text().toUtf8().constData());
269 obs_data_set_bool(wrapper, "visible", visible);
270
271 auto redo = [scene_name, main](const std::string &data) {
272 obs_source_t *scene_source =
273 obs_get_source_by_name(scene_name.c_str());
274 main->SetCurrentScene(scene_source, true);
275 obs_source_release(scene_source);
276
277 obs_data_t *dat =
278 obs_data_create_from_json(data.c_str());
279 OBSSource source;
280 AddNew(NULL, obs_data_get_string(dat, "id"),
281 obs_data_get_string(dat, "name"),
282 obs_data_get_bool(dat, "visible"), source);
283 obs_sceneitem_t *item = obs_scene_sceneitem_from_source(
284 main->GetCurrentScene(), source);
285 obs_sceneitem_set_id(item, (int64_t)obs_data_get_int(
286 dat, "item_id"));
287
288 main->RefreshSources(main->GetCurrentScene());
289 obs_data_release(dat);
290 obs_sceneitem_release(item);
291 };
292 undo_s.add_action(QTStr("Undo.Add").arg(ui->sourceName->text()),
293 undo, redo,
294 std::string(obs_source_get_name(newSource)),
295 std::string(obs_data_get_json(wrapper)));
296 obs_data_release(wrapper);
297 obs_sceneitem_release(item);
298 }
299
300 done(DialogCode::Accepted);
301 }
302
on_buttonBox_rejected()303 void OBSBasicSourceSelect::on_buttonBox_rejected()
304 {
305 done(DialogCode::Rejected);
306 }
307
GetSourceDisplayName(const char * id)308 static inline const char *GetSourceDisplayName(const char *id)
309 {
310 if (strcmp(id, "scene") == 0)
311 return Str("Basic.Scene");
312 const char *v_id = obs_get_latest_input_type_id(id);
313 return obs_source_get_display_name(v_id);
314 }
315
316 Q_DECLARE_METATYPE(OBSScene);
317
GetOBSRef(QListWidgetItem * item)318 template<typename T> static inline T GetOBSRef(QListWidgetItem *item)
319 {
320 return item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();
321 }
322
OBSBasicSourceSelect(OBSBasic * parent,const char * id_,undo_stack & undo_s)323 OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_,
324 undo_stack &undo_s)
325 : QDialog(parent),
326 ui(new Ui::OBSBasicSourceSelect),
327 id(id_),
328 undo_s(undo_s)
329 {
330 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
331
332 ui->setupUi(this);
333
334 ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false);
335
336 QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};
337
338 QString text{placeHolderText};
339 int i = 2;
340 obs_source_t *source = nullptr;
341 while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {
342 obs_source_release(source);
343 text = QString("%1 %2").arg(placeHolderText).arg(i++);
344 }
345
346 ui->sourceName->setText(text);
347 ui->sourceName->setFocus(); //Fixes deselect of text.
348 ui->sourceName->selectAll();
349
350 installEventFilter(CreateShortcutFilter());
351
352 if (strcmp(id_, "scene") == 0) {
353 OBSBasic *main =
354 reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
355 OBSSource curSceneSource = main->GetCurrentSceneSource();
356
357 ui->selectExisting->setChecked(true);
358 ui->createNew->setChecked(false);
359 ui->createNew->setEnabled(false);
360 ui->sourceName->setEnabled(false);
361
362 int count = main->ui->scenes->count();
363 for (int i = 0; i < count; i++) {
364 QListWidgetItem *item = main->ui->scenes->item(i);
365 OBSScene scene = GetOBSRef<OBSScene>(item);
366 OBSSource sceneSource = obs_scene_get_source(scene);
367
368 if (curSceneSource == sceneSource)
369 continue;
370
371 const char *name = obs_source_get_name(sceneSource);
372 ui->sourceList->addItem(QT_UTF8(name));
373 }
374 } else if (strcmp(id_, "group") == 0) {
375 obs_enum_sources(EnumGroups, this);
376 } else {
377 obs_enum_sources(EnumSources, this);
378 }
379 }
380
SourcePaste(const char * name,bool visible,bool dup)381 void OBSBasicSourceSelect::SourcePaste(const char *name, bool visible, bool dup)
382 {
383 AddExisting(name, visible, dup);
384 }
385