/* * Copyright (C) 2010-2012 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 * Marco Trevisan (TreviƱo) <3v1n0@ubuntu.com> */ #include #include "PanelTray.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UnitySettings.h" #include DECLARE_LOGGER(logger, "unity.panel.tray"); namespace { const std::string SETTINGS_NAME = "com.canonical.Unity.Panel"; const int PADDING = 12; const std::array WHITELIST {{ "JavaEmbeddedFrame", "Wine" }}; } namespace unity { PanelTray::PanelTray(int monitor) : View(NUX_TRACKER_LOCATION) , window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)) , monitor_(monitor) { int panel_height = panel::Style::Instance().PanelHeight(monitor_); auto gtkwindow = glib::object_cast(window_); gtk_window_set_type_hint(gtkwindow, GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_set_keep_above(gtkwindow, TRUE); gtk_window_set_skip_pager_hint(gtkwindow, TRUE); gtk_window_set_skip_taskbar_hint(gtkwindow, TRUE); gtk_window_resize(gtkwindow, 1, panel_height); gtk_window_move(gtkwindow, -panel_height,-panel_height); gtk_widget_set_name(window_, "UnityPanelApplet"); gtk_widget_set_visual(window_, gdk_screen_get_rgba_visual(gdk_screen_get_default())); gtk_widget_realize(window_); gtk_widget_set_app_paintable(window_, TRUE); draw_signal_.Connect(window_, "draw", sigc::mem_fun(this, &PanelTray::OnTrayDraw)); if (!g_getenv("UNITY_PANEL_TRAY_DISABLE")) { tray_ = na_tray_new_for_screen(gdk_screen_get_default(), GTK_ORIENTATION_HORIZONTAL, (NaTrayFilterCallback)FilterTrayCallback, this); na_tray_set_icon_size(tray_, panel_height-6); icon_removed_signal_.Connect(na_tray_get_manager(tray_), "tray_icon_removed", sigc::mem_fun(this, &PanelTray::OnTrayIconRemoved)); gtk_container_add(GTK_CONTAINER(window_.RawPtr()), GTK_WIDGET(tray_.RawPtr())); gtk_widget_show(GTK_WIDGET(tray_.RawPtr())); } SetMinMaxSize(1, panel_height); } PanelTray::~PanelTray() { if (gtk_widget_get_realized(window_)) { // We call Release since we're deleting the window here manually, // and we don't want the smart pointer to try and delete it as well. gtk_widget_destroy(window_.Release()); // We also need to release the tray to avoid the extra unref and invalid read. tray_.Release(); } } Window PanelTray::xid() { if (!window_ || !gtk_widget_get_realized(window_)) return 0; return gdk_x11_window_get_xid(gtk_widget_get_window(window_)); } void PanelTray::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) { nux::Geometry geo = GetAbsoluteGeometry(); gfx_context.PushClippingRectangle(geo); nux::GetPainter().PaintBackground(gfx_context, geo); gfx_context.PopClippingRectangle(); if (geo != last_geo_) { last_geo_ = geo; gtk_window_move(GTK_WINDOW(window_.RawPtr()), geo.x + PADDING, geo.y); } } void PanelTray::Sync() { if (tray_) { SetMinMaxSize(WidthOfTray() + (PADDING * 2), panel::Style::Instance().PanelHeight(monitor_)); QueueRelayout(); QueueDraw(); if (!children_.empty()) gtk_widget_show(window_); else gtk_widget_hide(window_); } } gboolean PanelTray::FilterTrayCallback(NaTray* tray, NaTrayChild* icon, PanelTray* self) { glib::String title(na_tray_child_get_title(icon)); glib::String res_class; glib::String res_name; na_tray_child_get_wm_class(icon, &res_name, &res_class); bool accept = FilterTray(title.Str(), res_name.Str(), res_class.Str()); if (accept) { if (na_tray_child_has_alpha(icon)) na_tray_child_set_composited(icon, TRUE); self->children_.push_back(icon); self->sync_idle_.reset(new glib::Idle(sigc::mem_fun(self, &PanelTray::IdleSync))); } LOG_DEBUG(logger) << "TrayChild " << (accept ? "Accepted: " : "Rejected: ") << na_tray_child_get_title(icon) << " " << res_name << " " << res_class; return accept ? TRUE : FALSE; } bool PanelTray::FilterTray(std::string const& title, std::string const& res_name, std::string const& res_class) { for (auto const& item : WHITELIST) if (title.find(item) == 0 || res_name.find(item) == 0 || res_class.find(item) == 0) return true; return false; } void PanelTray::OnTrayIconRemoved(NaTrayManager* manager, NaTrayChild* removed) { for (auto child : children_) { if (child == removed) { sync_idle_.reset(new glib::Idle(sigc::mem_fun(this, &PanelTray::IdleSync))); children_.remove(child); break; } } } bool PanelTray::IdleSync() { int width = WidthOfTray(); gtk_window_resize(GTK_WINDOW(window_.RawPtr()), width, panel::Style::Instance().PanelHeight(monitor_)); Sync(); return false; } int PanelTray::WidthOfTray() { int width = 0; for (auto child: children_) { int w = gtk_widget_get_allocated_width(GTK_WIDGET(child)); width += w > 24 ? w : 24; } return width; } gboolean PanelTray::OnTrayDraw(GtkWidget* widget, cairo_t* cr) { GtkAllocation alloc; gtk_widget_get_allocation(widget, &alloc); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_rectangle(cr, 0, 0, alloc.width, alloc.height); cairo_fill(cr); gtk_container_propagate_draw(GTK_CONTAINER(widget), gtk_bin_get_child(GTK_BIN(widget)), cr); return FALSE; } std::string PanelTray::GetName() const { return "Tray"; } void PanelTray::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("children_count", children_.size()); } } // namespace unity