| 1 | // Aseprite |
|---|---|
| 2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
| 3 | // Copyright (C) 2001-2018 David Capello |
| 4 | // |
| 5 | // This program is distributed under the terms of |
| 6 | // the End-User License Agreement for Aseprite. |
| 7 | |
| 8 | #ifdef HAVE_CONFIG_H |
| 9 | #include "config.h" |
| 10 | #endif |
| 11 | |
| 12 | #include "app/ui/home_view.h" |
| 13 | |
| 14 | #include "app/app.h" |
| 15 | #include "app/app_menus.h" |
| 16 | #include "app/commands/commands.h" |
| 17 | #include "app/commands/params.h" |
| 18 | #include "app/crash/data_recovery.h" |
| 19 | #include "app/i18n/strings.h" |
| 20 | #include "app/ui/data_recovery_view.h" |
| 21 | #include "app/ui/main_window.h" |
| 22 | #include "app/ui/recent_listbox.h" |
| 23 | #include "app/ui/skin/skin_theme.h" |
| 24 | #include "app/ui/status_bar.h" |
| 25 | #include "app/ui/workspace.h" |
| 26 | #include "app/ui/workspace_tabs.h" |
| 27 | #include "app/ui_context.h" |
| 28 | #include "base/exception.h" |
| 29 | #include "fmt/format.h" |
| 30 | #include "ui/label.h" |
| 31 | #include "ui/resize_event.h" |
| 32 | #include "ui/system.h" |
| 33 | #include "ui/textbox.h" |
| 34 | #include "ui/view.h" |
| 35 | #include "ver/info.h" |
| 36 | |
| 37 | #ifdef ENABLE_NEWS |
| 38 | #include "app/ui/news_listbox.h" |
| 39 | #endif |
| 40 | |
| 41 | #if ENABLE_SENTRY |
| 42 | #include "app/sentry_wrapper.h" |
| 43 | #endif |
| 44 | |
| 45 | #ifdef ENABLE_DRM |
| 46 | #include "drm/drm.h" |
| 47 | #include "aseprite_update.h" |
| 48 | #endif |
| 49 | |
| 50 | namespace app { |
| 51 | |
| 52 | using namespace ui; |
| 53 | using namespace app::skin; |
| 54 | |
| 55 | HomeView::HomeView() |
| 56 | : m_files(new RecentFilesListBox) |
| 57 | , m_folders(new RecentFoldersListBox) |
| 58 | #ifdef ENABLE_NEWS |
| 59 | , m_news(new NewsListBox) |
| 60 | #endif |
| 61 | , m_dataRecovery(App::instance()->dataRecovery()) |
| 62 | , m_dataRecoveryView(nullptr) |
| 63 | { |
| 64 | newFile()->Click.connect([this]{ onNewFile(); }); |
| 65 | openFile()->Click.connect([this]{ onOpenFile(); }); |
| 66 | if (m_dataRecovery) |
| 67 | recoverSprites()->Click.connect([this]{ onRecoverSprites(); }); |
| 68 | else |
| 69 | recoverSprites()->setVisible(false); |
| 70 | |
| 71 | filesView()->attachToView(m_files); |
| 72 | foldersView()->attachToView(m_folders); |
| 73 | #ifdef ENABLE_NEWS |
| 74 | newsView()->attachToView(m_news); |
| 75 | #endif |
| 76 | |
| 77 | checkUpdate()->setVisible(false); |
| 78 | shareCrashdb()->setVisible(false); |
| 79 | |
| 80 | #if ENABLE_SENTRY |
| 81 | // Show this option in home tab only when we require consent for the |
| 82 | // first time and there is crash data available to report |
| 83 | if (Sentry::requireConsent() && |
| 84 | Sentry::areThereCrashesToReport()) { |
| 85 | shareCrashdb()->setVisible(true); |
| 86 | shareCrashdb()->Click.connect( |
| 87 | [this]{ |
| 88 | if (shareCrashdb()->isSelected()) |
| 89 | Sentry::giveConsent(); |
| 90 | else |
| 91 | Sentry::revokeConsent(); |
| 92 | }); |
| 93 | } |
| 94 | #endif |
| 95 | |
| 96 | InitTheme.connect( |
| 97 | [this]{ |
| 98 | auto theme = SkinTheme::get(this); |
| 99 | setBgColor(theme->colors.workspace()); |
| 100 | setChildSpacing(8 * guiscale()); |
| 101 | }); |
| 102 | initTheme(); |
| 103 | } |
| 104 | |
| 105 | HomeView::~HomeView() |
| 106 | { |
| 107 | #ifdef ENABLE_DATA_RECOVERY |
| 108 | if (m_dataRecoveryView) { |
| 109 | ASSERT(!m_dataRecoveryView->parent()); |
| 110 | delete m_dataRecoveryView; |
| 111 | } |
| 112 | #endif |
| 113 | } |
| 114 | |
| 115 | void HomeView::dataRecoverySessionsAreReady() |
| 116 | { |
| 117 | #ifdef ENABLE_DATA_RECOVERY |
| 118 | |
| 119 | #ifdef ENABLE_TRIAL_MODE |
| 120 | DRM_INVALID{ |
| 121 | return; |
| 122 | } |
| 123 | #endif |
| 124 | |
| 125 | if (App::instance()->dataRecovery()->hasRecoverySessions()) { |
| 126 | // We highlight the "Recover Files" options because we came from a crash |
| 127 | auto theme = SkinTheme::get(this); |
| 128 | recoverSprites()->setStyle(theme->styles.workspaceUpdateLink()); |
| 129 | layout(); |
| 130 | } |
| 131 | if (m_dataRecoveryView) { |
| 132 | m_dataRecoveryView->refreshListNotification(); |
| 133 | } |
| 134 | #endif |
| 135 | } |
| 136 | |
| 137 | #if ENABLE_SENTRY |
| 138 | void HomeView::updateConsentCheckbox() |
| 139 | { |
| 140 | if (Sentry::requireConsent()) { |
| 141 | shareCrashdb()->setVisible(true); |
| 142 | shareCrashdb()->setSelected(false); |
| 143 | } |
| 144 | else if (Sentry::consentGiven()) { |
| 145 | shareCrashdb()->setVisible(false); |
| 146 | shareCrashdb()->setSelected(true); |
| 147 | } |
| 148 | layout(); |
| 149 | } |
| 150 | #endif |
| 151 | |
| 152 | std::string HomeView::getTabText() |
| 153 | { |
| 154 | return Strings::home_view_title(); |
| 155 | } |
| 156 | |
| 157 | TabIcon HomeView::getTabIcon() |
| 158 | { |
| 159 | return TabIcon::HOME; |
| 160 | } |
| 161 | |
| 162 | gfx::Color HomeView::getTabColor() |
| 163 | { |
| 164 | return gfx::ColorNone; |
| 165 | } |
| 166 | |
| 167 | bool HomeView::onCloseView(Workspace* workspace, bool quitting) |
| 168 | { |
| 169 | workspace->removeView(this); |
| 170 | return true; |
| 171 | } |
| 172 | |
| 173 | void HomeView::onAfterRemoveView(Workspace* workspace) |
| 174 | { |
| 175 | if (m_dataRecoveryView && |
| 176 | m_dataRecoveryView->parent()) { |
| 177 | workspace->removeView(m_dataRecoveryView); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | void HomeView::onTabPopup(Workspace* workspace) |
| 182 | { |
| 183 | Menu* menu = AppMenus::instance()->getTabPopupMenu(); |
| 184 | if (!menu) |
| 185 | return; |
| 186 | |
| 187 | menu->showPopup(mousePosInDisplay(), display()); |
| 188 | } |
| 189 | |
| 190 | void HomeView::onWorkspaceViewSelected() |
| 191 | { |
| 192 | StatusBar::instance()->showDefaultText(); |
| 193 | } |
| 194 | |
| 195 | void HomeView::onNewFile() |
| 196 | { |
| 197 | Command* command = Commands::instance()->byId(CommandId::NewFile()); |
| 198 | UIContext::instance()->executeCommandFromMenuOrShortcut(command); |
| 199 | } |
| 200 | |
| 201 | void HomeView::onOpenFile() |
| 202 | { |
| 203 | Command* command = Commands::instance()->byId(CommandId::OpenFile()); |
| 204 | UIContext::instance()->executeCommandFromMenuOrShortcut(command); |
| 205 | } |
| 206 | |
| 207 | void HomeView::onResize(ui::ResizeEvent& ev) |
| 208 | { |
| 209 | headerPlaceholder()->setVisible(ev.bounds().h > 200*ui::guiscale()); |
| 210 | foldersPlaceholder()->setVisible(ev.bounds().h > 150*ui::guiscale()); |
| 211 | #ifdef ENABLE_NEWS |
| 212 | newsPlaceholder()->setVisible(ev.bounds().w > 200*ui::guiscale()); |
| 213 | #else |
| 214 | newsPlaceholder()->setVisible(false); |
| 215 | #endif |
| 216 | |
| 217 | ui::VBox::onResize(ev); |
| 218 | } |
| 219 | |
| 220 | #ifdef ENABLE_UPDATER |
| 221 | |
| 222 | void HomeView::onCheckingUpdates() |
| 223 | { |
| 224 | checkUpdate()->setText(Strings::home_view_checking_updates()); |
| 225 | checkUpdate()->setVisible(true); |
| 226 | |
| 227 | layout(); |
| 228 | } |
| 229 | |
| 230 | void HomeView::onUpToDate() |
| 231 | { |
| 232 | checkUpdate()->setVisible(false); |
| 233 | |
| 234 | layout(); |
| 235 | } |
| 236 | |
| 237 | void HomeView::onNewUpdate(const std::string& url, const std::string& version) |
| 238 | { |
| 239 | checkUpdate()->setText( |
| 240 | fmt::format(Strings::home_view_new_version_available(), |
| 241 | get_app_name(), version)); |
| 242 | #ifdef ENABLE_DRM |
| 243 | DRM_INVALID { |
| 244 | checkUpdate()->setUrl(url); |
| 245 | } |
| 246 | else { |
| 247 | checkUpdate()->setUrl(""); |
| 248 | checkUpdate()->Click.connect([version] { |
| 249 | app::AsepriteUpdate dlg(version); |
| 250 | dlg.openWindowInForeground(); |
| 251 | }); |
| 252 | } |
| 253 | #else |
| 254 | checkUpdate()->setUrl(url); |
| 255 | #endif |
| 256 | checkUpdate()->setVisible(true); |
| 257 | checkUpdate()->InitTheme.connect( |
| 258 | [this]{ |
| 259 | auto theme = SkinTheme::get(this); |
| 260 | checkUpdate()->setStyle(theme->styles.workspaceUpdateLink()); |
| 261 | }); |
| 262 | checkUpdate()->initTheme(); |
| 263 | |
| 264 | layout(); |
| 265 | } |
| 266 | |
| 267 | #endif // ENABLE_UPDATER |
| 268 | |
| 269 | void HomeView::onRecoverSprites() |
| 270 | { |
| 271 | #ifdef ENABLE_DATA_RECOVERY |
| 272 | |
| 273 | #ifdef ENABLE_TRIAL_MODE |
| 274 | DRM_INVALID{ |
| 275 | return; |
| 276 | } |
| 277 | #endif |
| 278 | |
| 279 | ASSERT(m_dataRecovery); // "Recover Files" button is hidden when |
| 280 | // data recovery is disabled (m_dataRecovery == nullptr) |
| 281 | if (!m_dataRecovery) |
| 282 | return; |
| 283 | |
| 284 | if (!m_dataRecoveryView) { |
| 285 | m_dataRecoveryView = new DataRecoveryView(m_dataRecovery); |
| 286 | |
| 287 | // Restore the "Recover Files" link style when the |
| 288 | // DataRecoveryView is empty (so there is no more warning icon on |
| 289 | // it). |
| 290 | m_dataRecoveryView->Empty.connect( |
| 291 | [this]{ |
| 292 | auto theme = SkinTheme::get(this); |
| 293 | recoverSprites()->setStyle(theme->styles.workspaceLink()); |
| 294 | layout(); |
| 295 | }); |
| 296 | } |
| 297 | |
| 298 | if (!m_dataRecoveryView->parent()) |
| 299 | App::instance()->workspace()->addView(m_dataRecoveryView); |
| 300 | |
| 301 | App::instance()->mainWindow()->getTabsBar()->selectTab(m_dataRecoveryView); |
| 302 | #endif |
| 303 | } |
| 304 | |
| 305 | } // namespace app |
| 306 |