summaryrefslogtreecommitdiff
path: root/libmdock/mdock-widget.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmdock/mdock-widget.c')
-rw-r--r--libmdock/mdock-widget.c189
1 files changed, 186 insertions, 3 deletions
diff --git a/libmdock/mdock-widget.c b/libmdock/mdock-widget.c
index af3b165..004a590 100644
--- a/libmdock/mdock-widget.c
+++ b/libmdock/mdock-widget.c
@@ -28,6 +28,9 @@
#include "mdock-item.h"
#include "matcher.h"
+#define POPUP_SHOW_TIMEOUT 200
+#define POPUP_HIDE_TIMEOUT 500
+
struct _MDockWidgetPrivate
{
gchar *settings_path;
@@ -37,6 +40,9 @@ struct _MDockWidgetPrivate
GHashTable *appid_to_item;
GHashTable *desktopid_to_item;
GHashTable *window_to_item;
+ MDockItem *current_pointed_item;
+ MDockItem *current_popup_item;
+ guint popup_timer;
gboolean loading_settings : 1;
gboolean just_dropped : 1;
};
@@ -67,6 +73,12 @@ static const GtkTargetEntry drag_types[] = {
"text/uri-list", 0, DRAG_TYPE_URILIST
};
+static inline MDockItemWindowSelector * selector_from_item(MDockItem *item)
+{
+ return g_object_get_qdata((GObject*)item,
+ mdock_widget_item_window_selector_quark());
+}
+
static void save_items_to_settings(MDockWidget *self)
{
GSettings *settings = self->priv->settings;
@@ -101,6 +113,77 @@ static void move_item_to_position(MDockWidget *self, GSequenceIter *iter, GSeque
g_sequence_move(iter, position);
}
+static void hide_item_popup(MDockWidget *self)
+{
+ g_return_if_fail(self->priv->current_popup_item);
+ g_debug("hiding popup %p", self->priv->current_popup_item);
+
+ MDockItemWindowSelector *selector =
+ MDOCK_ITEM_WINDOW_SELECTOR(g_object_get_qdata(G_OBJECT(self->priv->current_popup_item),
+ mdock_widget_item_window_selector_quark()));
+
+ gtk_widget_hide(GTK_WIDGET(selector));
+ self->priv->current_popup_item = NULL;
+}
+
+static void show_item_popup(MDockWidget *self, MDockItem *item)
+{
+ g_warn_if_fail(!self->priv->current_popup_item);
+ g_debug("showing popup %p", item);
+
+ MDockItemWindowSelector *selector =
+ MDOCK_ITEM_WINDOW_SELECTOR(g_object_get_qdata(G_OBJECT(item),
+ mdock_widget_item_window_selector_quark()));
+
+ // Position the popup
+ GtkOrientation orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(self));
+ GdkWindow *item_window = gtk_widget_get_window(GTK_WIDGET(item));
+ GdkScreen *screen = gdk_window_get_screen(item_window);
+ gint monitor_num = gdk_screen_get_monitor_at_window(screen, item_window);
+ GdkRectangle monitor;
+ GtkRequisition requisition;
+ gint item_x, item_y, item_w, item_h;
+ gint x, y;
+
+ gdk_window_get_origin(item_window, &item_x, &item_y);
+ gdk_window_get_size(item_window, &item_w, &item_h);
+ gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor);
+ gtk_widget_size_request(GTK_WIDGET(selector), &requisition);
+
+ if (requisition.width > 0 || requisition.height > 0) {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL) {
+ x = item_x + item_w / 2 - requisition.width / 2;
+ if (item_y > (monitor.y + monitor.height / 2)) {
+ y = item_y - requisition.height;
+ } else {
+ y = item_y + item_h;
+ }
+ } else {
+ y = item_y + item_y / 2 - requisition.height / 2;
+ if (item_x > (monitor.x + monitor.width / 2)) {
+ x = item_x - requisition.width;
+ } else {
+ x = item_x + item_w;
+ }
+ }
+
+ if (x + requisition.width > monitor.x + monitor.width) {
+ x -= x - (monitor.x + monitor.width) + requisition.width;
+ } else if (x < monitor.x) {
+ x = monitor.x;
+ }
+
+ if (y + requisition.height > monitor.y + monitor.height) {
+ y -= y - (monitor.y + monitor.height) + requisition.height;
+ }
+
+ gtk_window_move(GTK_WINDOW(selector), x, y);
+ gtk_widget_show(GTK_WIDGET(selector));
+ }
+
+ self->priv->current_popup_item = item;
+}
+
static void handle_item_pinned_changed(MDockWidget *self, GParamSpec *spec, MDockItem *item)
{
if (!self->priv->loading_settings) {
@@ -112,6 +195,14 @@ static gboolean handle_item_button_press(MDockWidget *self, GdkEventButton *even
{
switch (event->button) {
case 3:
+ if (self->priv->popup_timer) {
+ g_source_remove(self->priv->popup_timer);
+ self->priv->popup_timer = 0;
+ }
+ if (self->priv->current_popup_item) {
+ hide_item_popup(self);
+ }
+
gtk_menu_popup(GTK_MENU(g_object_get_qdata(G_OBJECT(item), mdock_widget_item_menu_quark())),
NULL, NULL, NULL, NULL, event->button, event->time);
@@ -303,9 +394,75 @@ static gboolean mdock_widget_drag_motion(GtkWidget *widget, GdkDragContext *cont
return TRUE;
}
+static gboolean handle_popup_timer(gpointer user_data)
+{
+ MDockWidget *self = MDOCK_WIDGET(user_data);
+ if (self->priv->current_popup_item) {
+ hide_item_popup(self);
+ } else if (self->priv->current_pointed_item) {
+ show_item_popup(self, self->priv->current_pointed_item);
+ }
+
+ self->priv->popup_timer = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean handle_item_enter(MDockWidget *self, GdkEventCrossing *event, MDockItem *item)
+{
+ g_debug("Item enter");
+ self->priv->current_pointed_item = item;
+ if (self->priv->current_popup_item) {
+ if (self->priv->popup_timer) {
+ g_source_remove(self->priv->popup_timer);
+ self->priv->popup_timer = 0;
+ }
+ hide_item_popup(self);
+ show_item_popup(self, item);
+
+ } else if (!self->priv->popup_timer) {
+ self->priv->popup_timer = g_timeout_add(POPUP_SHOW_TIMEOUT, handle_popup_timer, self);
+ }
+ return FALSE;
+}
+
+static gboolean handle_item_leave(MDockWidget *self, GdkEventCrossing *event, MDockItem *item)
+{
+ g_debug("Item leave");
+ g_warn_if_fail(self->priv->current_pointed_item == item);
+ g_warn_if_fail(!self->priv->current_popup_item || self->priv->current_pointed_item == item);
+ self->priv->current_pointed_item = NULL;
+ if (self->priv->current_popup_item && !self->priv->popup_timer) {
+ self->priv->popup_timer = g_timeout_add(POPUP_HIDE_TIMEOUT, handle_popup_timer, self);
+ }
+ return FALSE;
+}
+
+static gboolean handle_item_selector_enter(MDockWidget *self, GdkEventCrossing *event, MDockItemWindowSelector *selector)
+{
+ g_debug("Item selector enter %d", event->detail);
+ if (event->detail != GDK_NOTIFY_NONLINEAR) return FALSE;
+ g_warn_if_fail(self->priv->current_popup_item);
+ if (self->priv->popup_timer) {
+ g_source_remove(self->priv->popup_timer);
+ self->priv->popup_timer = 0;
+ }
+ return FALSE;
+}
+
+static gboolean handle_item_selector_leave(MDockWidget *self, GdkEventCrossing *event, MDockItemWindowSelector *selector)
+{
+ g_debug("Item selector leave %d", event->detail);
+ if (event->detail != GDK_NOTIFY_NONLINEAR) return FALSE;
+ g_warn_if_fail(self->priv->current_popup_item);
+ if (!self->priv->popup_timer) {
+ self->priv->popup_timer = g_timeout_add(POPUP_HIDE_TIMEOUT, handle_popup_timer, self);
+ }
+ return FALSE;
+}
+
static void connect_item(MDockWidget *self, MDockItem *item, GSequenceIter *position)
{
- g_object_set_qdata(G_OBJECT(item), mdock_widget_item_iter_quark(), position);
+ g_object_set_qdata(G_OBJECT(item), mdock_widget_item_iter_quark(), position);;
g_signal_connect_object(item, "notify::pinned",
G_CALLBACK(handle_item_pinned_changed), self,
@@ -336,12 +493,30 @@ static void connect_item(MDockWidget *self, MDockItem *item, GSequenceIter *posi
g_signal_connect_object(item, "drag-data-delete",
G_CALLBACK(handle_item_drag_data_delete), self,
G_CONNECT_SWAPPED);
+ g_signal_connect_object(item, "enter-notify-event",
+ G_CALLBACK(handle_item_enter), self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object(item, "leave-notify-event",
+ G_CALLBACK(handle_item_leave), self,
+ G_CONNECT_SWAPPED);
+ MDockItemMenu *menu = mdock_item_menu_new(item);
g_object_set_qdata_full(G_OBJECT(item), mdock_widget_item_menu_quark(),
- mdock_item_menu_new(item), (GDestroyNotify)gtk_widget_destroy);
+ menu, (GDestroyNotify)gtk_widget_destroy);
+ MDockItemWindowSelector *selector = mdock_item_window_selector_new(item);
g_object_set_qdata_full(G_OBJECT(item), mdock_widget_item_window_selector_quark(),
- mdock_item_window_selector_new(item), (GDestroyNotify)gtk_widget_destroy);
+ selector, (GDestroyNotify)gtk_widget_destroy);
+
+ g_object_bind_property(self, "orientation",
+ selector, "orientation",
+ G_BINDING_DEFAULT);
+ g_signal_connect_object(selector, "enter-notify-event",
+ G_CALLBACK(handle_item_selector_enter), self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object(selector, "leave-notify-event",
+ G_CALLBACK(handle_item_selector_leave), self,
+ G_CONNECT_SWAPPED);
}
@@ -403,6 +578,7 @@ static void handle_active_window_changed(MDockWidget *self, WnckWindow *previous
if (iter) {
MDockItem *item = MDOCK_ITEM(g_sequence_get(iter));
mdock_item_set_last_active_window(item, window);
+ mdock_item_window_selector_set_active_window(selector_from_item(item), window);
}
}
@@ -421,6 +597,7 @@ static void handle_window_closed(MDockWidget *self, WnckWindow *window, WnckScre
MDockItem *item = MDOCK_ITEM(g_sequence_get(iter));
mdock_item_remove_window(item, window);
+ mdock_item_window_selector_remove_window(selector_from_item(item), window);
g_hash_table_remove(self->priv->window_to_item, window);
if (!mdock_item_has_windows(item) && !mdock_item_get_pinned(item)) {
@@ -428,6 +605,9 @@ static void handle_window_closed(MDockWidget *self, WnckWindow *window, WnckScre
g_hash_table_foreach_remove(self->priv->appid_to_item, filter_deleted_group, iter);
g_hash_table_foreach_remove(self->priv->desktopid_to_item, filter_deleted_group, iter);
g_sequence_remove(iter);
+ if (self->priv->current_popup_item == item) {
+ hide_item_popup(self);
+ }
gtk_widget_destroy(GTK_WIDGET(item));
}
}
@@ -443,6 +623,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre
if (iter) {
MDockItem *item = MDOCK_ITEM(g_sequence_get(iter));
mdock_item_add_window(item, window);
+ mdock_item_window_selector_add_window(selector_from_item(item), window);
app_id_destroy(appid);
g_hash_table_insert(self->priv->window_to_item, window, iter);
return;
@@ -454,6 +635,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre
if (iter) {
MDockItem *item = MDOCK_ITEM(g_sequence_get(iter));
mdock_item_add_window(item, window);
+ mdock_item_window_selector_add_window(selector_from_item(item), window);
g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid
g_hash_table_insert(self->priv->window_to_item, window, iter);
return;
@@ -471,6 +653,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre
connect_item(self, item, iter);
mdock_item_add_window(item, window);
+ mdock_item_window_selector_add_window(selector_from_item(item), window);
g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid
if (desktopid) {