// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
/*
* Copyright (C) 2010 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authored by: Neil Jagdish Patel
* Andrea Azzarone
*/
#include "config.h"
#include
#include
#include
#include
#include "VolumeLauncherIcon.h"
#include "FavoriteStore.h"
namespace unity
{
namespace launcher
{
//
// Start private implementation
//
class VolumeLauncherIcon::Impl
{
public:
typedef glib::Signal ItemSignal;
Impl(Volume::Ptr const& volume,
DevicesSettings::Ptr const& devices_settings,
DeviceNotificationDisplay::Ptr const& notification,
FileManager::Ptr const& fm,
VolumeLauncherIcon* parent)
: parent_(parent)
, volume_(volume)
, devices_settings_(devices_settings)
, notification_(notification)
, file_manager_(parent_->file_manager_)
{
UpdateIcon();
UpdateVisibility();
ConnectSignals();
}
void UpdateIcon()
{
parent_->tooltip_text = volume_->GetName();
parent_->icon_name = volume_->GetIconName();
}
void UpdateVisibility()
{
parent_->SetQuirk(Quirk::VISIBLE, IsVisible());
}
bool IsBlackListed()
{
return devices_settings_->IsABlacklistedDevice(volume_->GetIdentifier());
}
bool IsVisible()
{
if (IsBlackListed() && parent_->GetManagedWindows().empty())
return false;
if (volume_->IsShadowed())
return false;
return true;
}
void ConnectSignals()
{
connections_.Add(volume_->changed.connect([this] { UpdateIcon(); }));
connections_.Add(volume_->removed.connect(sigc::mem_fun(this, &Impl::OnVolumeRemoved)));
connections_.Add(devices_settings_->changed.connect([this] { UpdateVisibility(); }));
connections_.Add(parent_->windows_changed.connect([this] (int) { UpdateVisibility(); }));
}
void OnVolumeRemoved()
{
devices_settings_->TryToUnblacklist(volume_->GetIdentifier());
parent_->UnStick();
parent_->Remove();
}
bool CanEject() const
{
return volume_->CanBeEjected();
}
void EjectAndShowNotification()
{
if (!CanEject())
return;
auto conn = std::make_shared();
*conn = volume_->ejected.connect([this, conn] {
notification_->Display(volume_->GetName());
conn->disconnect();
});
connections_.Add(*conn);
volume_->Eject();
}
bool CanStop() const
{
return volume_->CanBeStopped();
}
void StopDrive()
{
volume_->StopDrive();
}
void DoActionWhenMounted(std::function const& callback)
{
if (!volume_->IsMounted())
{
auto conn = std::make_shared();
*conn = volume_->mounted.connect([this, conn, callback] {
callback();
conn->disconnect();
});
connections_.Add(*conn);
volume_->Mount();
}
else
{
callback();
}
}
void OpenInFileManager(uint64_t timestamp)
{
DoActionWhenMounted([this, timestamp] {
file_manager_->Open(volume_->GetUri(), timestamp);
});
}
void CopyFilesToVolume(std::set const& files, uint64_t timestamp)
{
DoActionWhenMounted([this, files, timestamp] {
file_manager_->CopyFiles(files, volume_->GetUri(), timestamp);
});
}
MenuItemsVector GetMenus()
{
MenuItemsVector result;
AppendOpenItem(result);
AppendFormatItem(result);
AppendSeparatorItem(result);
AppendNameItem(result);
AppendSeparatorItem(result);
AppendWindowsItems(result);
AppendToggleLockFromLauncherItem(result);
AppendEjectItem(result);
AppendSafelyRemoveItem(result);
AppendUnmountItem(result);
AppendQuitItem(result);
return result;
}
void AppendToggleLockFromLauncherItem(MenuItemsVector& menu)
{
if (volume_->GetIdentifier().empty())
return;
glib::Object menu_item(dbusmenu_menuitem_new());
const char* label = IsBlackListed() ? _("Lock to Launcher") : _("Unlock from Launcher");
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, label);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) {
if (!IsBlackListed())
{
parent_->UnStick();
devices_settings_->TryToBlacklist(volume_->GetIdentifier());
}
else
{
devices_settings_->TryToUnblacklist(volume_->GetIdentifier());
}
}));
menu.push_back(menu_item);
}
void AppendSeparatorItem(MenuItemsVector& menu)
{
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
menu.push_back(menu_item);
}
void AppendNameItem(MenuItemsVector& menu)
{
std::ostringstream bold_volume_name;
bold_volume_name << "" << volume_->GetName() << "";
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, bold_volume_name.str().c_str());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC, volume_->GetName().c_str());
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, true);
dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) {
OpenInFileManager(timestamp);
}));
menu.push_back(menu_item);
}
void AppendWindowsItems(MenuItemsVector& menu)
{
if (!parent_->IsRunning())
return;
auto const& windows_items = parent_->GetWindowsMenuItems();
if (!windows_items.empty())
{
menu.insert(end(menu), begin(windows_items), end(windows_items));
AppendSeparatorItem(menu);
}
}
void AppendOpenItem(MenuItemsVector& menu)
{
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Open"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) {
OpenInFileManager(timestamp);
}));
menu.push_back(menu_item);
}
void AppendEjectItem(MenuItemsVector& menu)
{
if (!volume_->CanBeEjected())
return;
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Eject parent drive") : _("Eject"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) {
parent_->Quit();
EjectAndShowNotification();
}));
menu.push_back(menu_item);
}
void AppendSafelyRemoveItem(MenuItemsVector& menu)
{
if (!volume_->CanBeStopped())
return;
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Safely remove parent drive") : _("Safely remove"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) {
parent_->Quit();
volume_->StopDrive();
}));
menu.push_back(menu_item);
}
void AppendFormatItem(MenuItemsVector& menu)
{
glib::Object gd(g_desktop_app_info_new("gnome-disks.desktop"));
if (!volume_->CanBeFormatted() || !gd)
return;
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Format…"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) {
OpenFormatPrompt(timestamp);
}));
menu.push_back(menu_item);
}
void OpenFormatPrompt(Time timestamp)
{
glib::Object gd_desktop_app_info(g_desktop_app_info_new("gnome-disks.desktop"));
if (!gd_desktop_app_info)
return;
auto gd_app_info = glib::object_cast(gd_desktop_app_info);
std::string command_line = glib::gchar_to_string(g_app_info_get_executable(gd_app_info)) +
" --block-device " +
volume_->GetUnixDevicePath() +
" --format-device";
GdkDisplay* display = gdk_display_get_default();
glib::Object app_launch_context(gdk_display_get_app_launch_context(display));
gdk_app_launch_context_set_timestamp(app_launch_context, timestamp);
glib::Object app_info(g_app_info_create_from_commandline(command_line.c_str(),
nullptr,
G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
nullptr));
g_app_info_launch_uris(app_info, nullptr, glib::object_cast(app_launch_context), nullptr);
}
void AppendUnmountItem(MenuItemsVector& menu)
{
if (!volume_->IsMounted() || volume_->CanBeEjected() || volume_->CanBeStopped())
return;
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Unmount"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) {
volume_->Unmount();
}));
menu.push_back(menu_item);
}
void AppendQuitItem(MenuItemsVector& menu)
{
if (!parent_->IsRunning())
return;
if (!menu.empty())
AppendSeparatorItem(menu);
glib::Object menu_item(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Quit"));
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) {
parent_->Quit();
}));
menu.push_back(menu_item);
}
std::string GetRemoteUri() const
{
auto const& identifier = volume_->GetIdentifier();
if (identifier.empty())
return "";
return FavoriteStore::URI_PREFIX_DEVICE + identifier;
}
VolumeLauncherIcon* parent_;
Volume::Ptr volume_;
DevicesSettings::Ptr devices_settings_;
DeviceNotificationDisplay::Ptr notification_;
FileManager::Ptr file_manager_;
connection::Manager connections_;
};
//
// End private implementation
//
VolumeLauncherIcon::VolumeLauncherIcon(Volume::Ptr const& volume,
DevicesSettings::Ptr const& devices_settings,
DeviceNotificationDisplay::Ptr const& notification,
FileManager::Ptr const& fm)
: WindowedLauncherIcon(IconType::DEVICE)
, StorageLauncherIcon(GetIconType(), fm)
, pimpl_(new Impl(volume, devices_settings, notification, fm, this))
{
UpdateStorageWindows();
}
VolumeLauncherIcon::~VolumeLauncherIcon()
{}
void VolumeLauncherIcon::AboutToRemove()
{
StorageLauncherIcon::AboutToRemove();
if (CanEject())
EjectAndShowNotification();
else if (CanStop())
StopDrive();
}
bool VolumeLauncherIcon::CanEject() const
{
return pimpl_->CanEject();
}
void VolumeLauncherIcon::EjectAndShowNotification()
{
pimpl_->EjectAndShowNotification();
}
bool VolumeLauncherIcon::CanStop() const
{
return pimpl_->CanStop();
}
void VolumeLauncherIcon::StopDrive()
{
return pimpl_->StopDrive();
}
AbstractLauncherIcon::MenuItemsVector VolumeLauncherIcon::GetMenus()
{
return pimpl_->GetMenus();
}
std::string VolumeLauncherIcon::GetRemoteUri() const
{
return pimpl_->GetRemoteUri();
}
void VolumeLauncherIcon::Stick(bool save)
{
SimpleLauncherIcon::Stick(save);
pimpl_->devices_settings_->TryToUnblacklist(pimpl_->volume_->GetIdentifier());
}
void VolumeLauncherIcon::UnStick()
{
SimpleLauncherIcon::UnStick();
SetQuirk(Quirk::VISIBLE, true);
}
nux::DndAction VolumeLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data)
{
return dnd_data.Uris().empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY;
}
void VolumeLauncherIcon::OnAcceptDrop(DndData const& dnd_data)
{
auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp;
pimpl_->CopyFilesToVolume(dnd_data.Uris(), timestamp);
SetQuirk(Quirk::PULSE_ONCE, true);
FullyAnimateQuirkDelayed(100, LauncherIcon::Quirk::SHIMMER);
}
std::string VolumeLauncherIcon::GetVolumeUri() const
{
return pimpl_->volume_->GetUri();
}
WindowList VolumeLauncherIcon::GetStorageWindows() const
{
return file_manager_->WindowsForLocation(GetVolumeUri());
}
void VolumeLauncherIcon::OpenInstanceLauncherIcon(Time timestamp)
{
pimpl_->OpenInFileManager(timestamp);
}
//
// Introspection
//
std::string VolumeLauncherIcon::GetName() const
{
return "VolumeLauncherIcon";
}
} // namespace launcher
} // namespace unity