mirror of
https://github.com/zebrajr/SamRewritten.git
synced 2025-12-06 12:19:51 +01:00
Added a store achievements mods view and model, but controller not yet implemented
This commit is contained in:
parent
6394ebc539
commit
133a084008
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
|
|
@ -35,7 +35,9 @@
|
||||||
"${workspaceRoot}"
|
"${workspaceRoot}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"intelliSenseMode": "clang-x64"
|
"intelliSenseMode": "clang-x64",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++17"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Win32",
|
"name": "Win32",
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,10 @@ GameEmulator::update_view() {
|
||||||
* STEAM API CALLBACKS BELOW
|
* STEAM API CALLBACKS BELOW
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all achievemnts data, then pipes the data to the
|
||||||
|
* parent process.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
||||||
// Check if we received the values for the good app
|
// Check if we received the values for the good app
|
||||||
|
|
@ -266,4 +270,5 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
||||||
std::cerr << "Received stats for the game, but an erorr occurrred." << std::endl;
|
std::cerr << "Received stats for the game, but an erorr occurrred." << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// => OnUserStatsReceived
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include "GtkAchievementBoxRow.h"
|
#include "GtkAchievementBoxRow.h"
|
||||||
|
#include "MySteam.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
@ -6,18 +8,23 @@ extern "C"
|
||||||
on_achievement_button_toggle(GtkToggleButton* but, gpointer achievement) {
|
on_achievement_button_toggle(GtkToggleButton* but, gpointer achievement) {
|
||||||
const bool active = gtk_toggle_button_get_active(but);
|
const bool active = gtk_toggle_button_get_active(but);
|
||||||
const bool achieved = (*(Achievement_t *)achievement).achieved;
|
const bool achieved = (*(Achievement_t *)achievement).achieved;
|
||||||
|
const std::string ach_id = std::string((*(Achievement_t *)achievement).id);
|
||||||
|
|
||||||
if(active && achieved) {
|
if(active && achieved) {
|
||||||
gtk_button_set_label(GTK_BUTTON(but), "Unlocked");
|
gtk_button_set_label(GTK_BUTTON(but), "Unlocked");
|
||||||
|
g_steam->remove_modification_ach(ach_id);
|
||||||
}
|
}
|
||||||
else if (active && !achieved) {
|
else if (active && !achieved) {
|
||||||
gtk_button_set_label(GTK_BUTTON(but), "To unlock");
|
gtk_button_set_label(GTK_BUTTON(but), "To unlock");
|
||||||
|
g_steam->add_modification_ach(ach_id, true);
|
||||||
}
|
}
|
||||||
else if (!active && achieved) {
|
else if (!active && achieved) {
|
||||||
gtk_button_set_label(GTK_BUTTON(but), "To relock");
|
gtk_button_set_label(GTK_BUTTON(but), "To relock");
|
||||||
|
g_steam->add_modification_ach(ach_id, false);
|
||||||
}
|
}
|
||||||
else if (!active && !achieved) {
|
else if (!active && !achieved) {
|
||||||
gtk_button_set_label(GTK_BUTTON(but), "Locked");
|
gtk_button_set_label(GTK_BUTTON(but), "Locked");
|
||||||
|
g_steam->remove_modification_ach(ach_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include "Achievement.h"
|
#include "Achievement.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ m_stats_list_view(nullptr)
|
||||||
m_game_list_view = GTK_SCROLLED_WINDOW(gtk_builder_get_object(m_builder, "game_list_view"));
|
m_game_list_view = GTK_SCROLLED_WINDOW(gtk_builder_get_object(m_builder, "game_list_view"));
|
||||||
m_stats_list_view = GTK_SCROLLED_WINDOW(gtk_builder_get_object(m_builder, "stats_list_view"));
|
m_stats_list_view = GTK_SCROLLED_WINDOW(gtk_builder_get_object(m_builder, "stats_list_view"));
|
||||||
m_back_button = GTK_BUTTON(gtk_builder_get_object(m_builder, "back_button"));
|
m_back_button = GTK_BUTTON(gtk_builder_get_object(m_builder, "back_button"));
|
||||||
|
m_store_button = GTK_BUTTON(gtk_builder_get_object(m_builder, "store_button"));
|
||||||
GtkWidget* game_placeholder = GTK_WIDGET(gtk_builder_get_object(m_builder, "game_placeholder"));
|
GtkWidget* game_placeholder = GTK_WIDGET(gtk_builder_get_object(m_builder, "game_placeholder"));
|
||||||
GtkWidget* stats_placeholder = GTK_WIDGET(gtk_builder_get_object(m_builder, "stats_placeholder"));
|
GtkWidget* stats_placeholder = GTK_WIDGET(gtk_builder_get_object(m_builder, "stats_placeholder"));
|
||||||
|
|
||||||
|
|
@ -218,7 +219,7 @@ unsigned long
|
||||||
MainPickerWindow::get_corresponding_appid_for_row(GtkListBoxRow *row) {
|
MainPickerWindow::get_corresponding_appid_for_row(GtkListBoxRow *row) {
|
||||||
for(std::map<unsigned long, GtkWidget*>::iterator it = m_game_list_rows.begin(); it != m_game_list_rows.end(); ++it)
|
for(std::map<unsigned long, GtkWidget*>::iterator it = m_game_list_rows.begin(); it != m_game_list_rows.end(); ++it)
|
||||||
{
|
{
|
||||||
if((gpointer)it->second == (gpointer)row) {
|
if( (gpointer)it->second == (gpointer)row ) {
|
||||||
return it->first;
|
return it->first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +230,7 @@ MainPickerWindow::get_corresponding_appid_for_row(GtkListBoxRow *row) {
|
||||||
void
|
void
|
||||||
MainPickerWindow::switch_to_stats_page() {
|
MainPickerWindow::switch_to_stats_page() {
|
||||||
gtk_widget_set_visible(GTK_WIDGET(m_back_button), TRUE);
|
gtk_widget_set_visible(GTK_WIDGET(m_back_button), TRUE);
|
||||||
|
gtk_widget_set_visible(GTK_WIDGET(m_store_button), TRUE);
|
||||||
gtk_stack_set_visible_child(GTK_STACK(m_main_stack), GTK_WIDGET(m_stats_list_view));
|
gtk_stack_set_visible_child(GTK_STACK(m_main_stack), GTK_WIDGET(m_stats_list_view));
|
||||||
}
|
}
|
||||||
// => switch_to_stats_page
|
// => switch_to_stats_page
|
||||||
|
|
@ -237,6 +239,7 @@ MainPickerWindow::switch_to_stats_page() {
|
||||||
void
|
void
|
||||||
MainPickerWindow::switch_to_games_page() {
|
MainPickerWindow::switch_to_games_page() {
|
||||||
gtk_widget_set_visible(GTK_WIDGET(m_back_button), FALSE);
|
gtk_widget_set_visible(GTK_WIDGET(m_back_button), FALSE);
|
||||||
|
gtk_widget_set_visible(GTK_WIDGET(m_store_button), FALSE);
|
||||||
gtk_stack_set_visible_child(GTK_STACK(m_main_stack), GTK_WIDGET(m_game_list_view));
|
gtk_stack_set_visible_child(GTK_STACK(m_main_stack), GTK_WIDGET(m_game_list_view));
|
||||||
|
|
||||||
//TODO Clear achievments list
|
//TODO Clear achievments list
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GtkWidget *m_main_window;
|
GtkWidget *m_main_window;
|
||||||
GtkButton *m_back_button;
|
GtkButton *m_back_button;
|
||||||
|
GtkButton *m_store_button;
|
||||||
GtkListBox *m_game_list;
|
GtkListBox *m_game_list;
|
||||||
GtkListBox *m_stats_list;
|
GtkListBox *m_stats_list;
|
||||||
GtkBuilder *m_builder;
|
GtkBuilder *m_builder;
|
||||||
|
|
|
||||||
|
|
@ -174,4 +174,28 @@ MySteam::refresh_icons() {
|
||||||
appDAO->download_app_icon(i.app_id);
|
appDAO->download_app_icon(i.app_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// => refresh_icons
|
// => refresh_icons
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an achievement to the list of achievements to unlock/lock
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
MySteam::add_modification_ach(const std::string& ach_id, const bool& new_value) {
|
||||||
|
std::cout << ach_id << ": " << (new_value ? "to unlock" : "to relock") << std::endl;
|
||||||
|
m_pending_ach_modifications.insert( std::pair<std::string, bool>(ach_id, new_value) );
|
||||||
|
}
|
||||||
|
// => add_modification_ach
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an achievement to the list of achievements to unlock/lock
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
MySteam::remove_modification_ach(const std::string& ach_id) {
|
||||||
|
std::cout << ach_id << ": Cancel modification" << std::endl;
|
||||||
|
if ( m_pending_ach_modifications.find(ach_id) == m_pending_ach_modifications.end() ) {
|
||||||
|
std::cerr << "WARNING: Could not cancel: modification was not pending";
|
||||||
|
} else {
|
||||||
|
m_pending_ach_modifications.erase(ach_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// => remove_modification_ach
|
||||||
|
|
@ -40,6 +40,16 @@ public:
|
||||||
*/
|
*/
|
||||||
static std::string get_steam_install_path();
|
static std::string get_steam_install_path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple getter for m_pending_ach_modifications
|
||||||
|
*/
|
||||||
|
std::map<std::string, bool> get_pending_ach_modifications() const { return m_pending_ach_modifications; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple getter for m_pending_stat_modifications
|
||||||
|
*/
|
||||||
|
std::map<std::string, double> get_pending_stat_modifications() const { return m_pending_stat_modifications; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a process that will emulate a steam game with the
|
* Starts a process that will emulate a steam game with the
|
||||||
* given appId. Returns false if this process failed to launch.
|
* given appId. Returns false if this process failed to launch.
|
||||||
|
|
@ -82,13 +92,19 @@ public:
|
||||||
* Adds a modification to be done on the launched app.
|
* Adds a modification to be done on the launched app.
|
||||||
* Commit the change with commit_modifications.
|
* Commit the change with commit_modifications.
|
||||||
*/
|
*/
|
||||||
//void add_modification_ach(const std::string& ach_id, const bool& new_value); //TODO IMPLEMENT
|
void add_modification_ach(const std::string& ach_id, const bool& new_value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a modification to be done on the launched app.
|
* Adds a modification to be done on the launched app.
|
||||||
* Commit the change with commit_modifications.
|
* Commit the change with commit_modifications.
|
||||||
*/
|
*/
|
||||||
//void add_modification_stat(const std::string& stat_id, const double& new_value); // THIS TOO
|
void remove_modification_ach(const std::string& ach_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a modification to be done on the launched app.
|
||||||
|
* Commit the change with commit_modifications.
|
||||||
|
*/
|
||||||
|
//void add_modification_stat(const std::string& stat_id, const double& new_value); // TODO: IMPLEMENT
|
||||||
|
|
||||||
MySteam(MySteam const&) = delete;
|
MySteam(MySteam const&) = delete;
|
||||||
void operator=(MySteam const&) = delete;
|
void operator=(MySteam const&) = delete;
|
||||||
|
|
@ -96,4 +112,6 @@ private:
|
||||||
MySteam();
|
MySteam();
|
||||||
|
|
||||||
std::vector<Game_t> m_all_subscribed_apps;
|
std::vector<Game_t> m_all_subscribed_apps;
|
||||||
|
std::map<std::string, bool> m_pending_ach_modifications;
|
||||||
|
std::map<std::string, double> m_pending_stat_modifications;
|
||||||
};
|
};
|
||||||
|
|
@ -17,6 +17,25 @@ extern "C"
|
||||||
}
|
}
|
||||||
// => on_close_button_clicked
|
// => on_close_button_clicked
|
||||||
|
|
||||||
|
void
|
||||||
|
on_store_button_clicked() {
|
||||||
|
std::cerr << "Saving stats and achievements." << std::endl;
|
||||||
|
const std::map<std::string, bool> pending_achs = g_steam->get_pending_ach_modifications();
|
||||||
|
const std::map<std::string, double> pending_stats = g_steam->get_pending_stat_modifications();
|
||||||
|
|
||||||
|
// TODO: JUST DO IT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* foreach(pending_achs as &val) {
|
||||||
|
* if(val.second == true)
|
||||||
|
* gameEmulator->unlock(val.first)
|
||||||
|
* } else {
|
||||||
|
* gameEmulator->relock(val.first)
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
// => on_store_button_clicked
|
||||||
|
|
||||||
void
|
void
|
||||||
on_ask_game_refresh() {
|
on_ask_game_refresh() {
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,7 @@ extern "C"
|
||||||
|
|
||||||
void
|
void
|
||||||
on_back_button_clicked();
|
on_back_button_clicked();
|
||||||
|
|
||||||
|
void
|
||||||
|
on_store_button_clicked();
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,31 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.20.3 -->
|
<!-- Generated with glade 3.22.1
|
||||||
|
|
||||||
|
Copyright (C)
|
||||||
|
|
||||||
|
This file is part of SamRewritten.
|
||||||
|
|
||||||
|
SamRewritten is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
SamRewritten is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with SamRewritten. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Author: Paul Combaldieu
|
||||||
|
|
||||||
|
-->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<!-- interface-license-type lgplv3 -->
|
||||||
|
<!-- interface-name SamRewritten -->
|
||||||
|
<!-- interface-authors Paul Combaldieu -->
|
||||||
<object class="GtkBox" id="game_placeholder">
|
<object class="GtkBox" id="game_placeholder">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
|
@ -120,64 +144,6 @@ He's pretty confident they won't take much longer to arrive, but is also wrong m
|
||||||
<property name="icon_name">face-wink</property>
|
<property name="icon_name">face-wink</property>
|
||||||
<signal name="delete-event" handler="on_close_button_clicked" swapped="no"/>
|
<signal name="delete-event" handler="on_close_button_clicked" swapped="no"/>
|
||||||
<signal name="show" handler="on_main_window_show" swapped="no"/>
|
<signal name="show" handler="on_main_window_show" swapped="no"/>
|
||||||
<child>
|
|
||||||
<object class="GtkStack" id="main_stack">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="transition_type">slide-left-right</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="game_list_view">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="hscrollbar_policy">never</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkViewport">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox" id="game_list">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="selection_mode">none</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">page0</property>
|
|
||||||
<property name="title" translatable="yes">page0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="stats_list_view">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="hscrollbar_policy">never</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkViewport">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox" id="stats_list">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="selection_mode">none</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">page1</property>
|
|
||||||
<property name="title" translatable="yes">page1</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="titlebar">
|
<child type="titlebar">
|
||||||
<object class="GtkHeaderBar">
|
<object class="GtkHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
@ -243,6 +209,82 @@ He's pretty confident they won't take much longer to arrive, but is also wrong m
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="store_button">
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="on_store_button_clicked" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">mail-send-receive-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="main_stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="transition_type">slide-left-right</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="game_list_view">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="game_list">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="selection_mode">none</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">page0</property>
|
||||||
|
<property name="title" translatable="yes">page0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="stats_list_view">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="stats_list">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="selection_mode">none</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">page1</property>
|
||||||
|
<property name="title" translatable="yes">page1</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user