void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y, gint *dx, gint *dy) { const Rect *a = NULL; Rect search = self->area; gint pos, half, monitor; *dx = *dy = 0; RECT_SET_POINT(search, x, y); if (self->parent) monitor = self->parent->monitor; else monitor = screen_find_monitor(&search); a = screen_physical_area_monitor(monitor); half = g_list_length(self->entries) / 2; pos = g_list_index(self->entries, self->selected); /* if in the bottom half then check this stuff first, will keep the bottom edge of the menu visible */ if (pos > half) { *dx = MAX(*dx, a->x - x); *dy = MAX(*dy, a->y - y); } *dx = MIN(*dx, (a->x + a->width) - (x + self->area.width)); *dy = MIN(*dy, (a->y + a->height) - (y + self->area.height)); /* if in the top half then check this stuff last, will keep the top edge of the menu visible */ if (pos <= half) { *dx = MAX(*dx, a->x - x); *dy = MAX(*dy, a->y - y); } }
static void do_edge_warp(gint x, gint y) { guint i; ObDirection dir; if (!config_mouse_screenedgetime) return; dir = -1; for (i = 0; i < screen_num_monitors; ++i) { const Rect *a = screen_physical_area_monitor(i); if (!RECT_CONTAINS(*a, x, y)) continue; if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST; if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST; if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH; if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH; /* try check for xinerama boundaries */ if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) && (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST)) { dir = -1; } if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) && (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH)) { dir = -1; } } if (dir != edge_warp_dir) { cancel_edge_warp(); if (dir != (ObDirection)-1) { edge_warp_odd = TRUE; /* switch on the first timeout */ edge_warp_timer = g_timeout_add(config_mouse_screenedgetime, edge_warp_delay_func, NULL); } edge_warp_dir = dir; } }
/* Always return FALSE because its not interactive */ static gboolean run_func(ObActionsData *data, gpointer options) { Options *o = options; GravityPoint position = { { 0, }, }; gint monitor = -1; if (o->use_position) { if (o->monitor >= 0) monitor = o->monitor; else switch (o->monitor_type) { case OB_PLACE_MONITOR_ANY: case OB_PLACE_MONITOR_PRIMARY: monitor = screen_monitor_primary(FALSE); break; case OB_PLACE_MONITOR_MOUSE: monitor = screen_monitor_pointer(); break; case OB_PLACE_MONITOR_ACTIVE: monitor = screen_monitor_active(); break; case OB_PLACE_MONITOR_ALL: monitor = screen_num_monitors; break; default: g_assert_not_reached(); } position = o->position; } else { const Rect *allmon; monitor = screen_num_monitors; allmon = screen_physical_area_monitor(monitor); position.x.pos = data->x - allmon->x; position.y.pos = data->y - allmon->y; } /* you cannot call ShowMenu from inside a menu */ if (data->uact != OB_USER_ACTION_MENU_SELECTION && o->name) menu_show(o->name, &position, monitor, data->button != 0, o->use_position, data->client); return FALSE; }
static void menu_frame_update(ObMenuFrame *self) { GList *mit, *fit; const Rect *a; gint h; menu_pipe_execute(self->menu); menu_find_submenus(self->menu); self->selected = NULL; /* start at show_from */ mit = g_list_nth(self->menu->entries, self->show_from); /* go through the menu's and frame's entries and connect the frame entries to the menu entries */ for (fit = self->entries; mit && fit; mit = g_list_next(mit), fit = g_list_next(fit)) { ObMenuEntryFrame *f = fit->data; f->entry = mit->data; } /* if there are more menu entries than in the frame, add them */ while (mit) { ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self); self->entries = g_list_append(self->entries, e); mit = g_list_next(mit); } /* if there are more frame entries than menu entries then get rid of them */ while (fit) { GList *n = g_list_next(fit); menu_entry_frame_free(fit->data); self->entries = g_list_delete_link(self->entries, fit); fit = n; } /* * make the menu fit on the screen */ /* calculate the height of the menu */ h = 0; for (fit = self->entries; fit; fit = g_list_next(fit)) h += menu_entry_frame_get_height(fit->data, fit == self->entries, g_list_next(fit) == NULL); /* add the border at the top and bottom */ h += ob_rr_theme->mbwidth * 2; a = screen_physical_area_monitor(self->monitor); if (h > a->height) { GList *flast, *tmp; gboolean last_entry = TRUE; /* take the height of our More... entry into account */ h += menu_entry_frame_get_height(NULL, FALSE, TRUE); /* start at the end of the entries */ flast = g_list_last(self->entries); /* pull out all the entries from the frame that don't fit on the screen, leaving at least 1 though */ while (h > a->height && g_list_previous(flast) != NULL) { /* update the height, without this entry */ h -= menu_entry_frame_get_height(flast->data, FALSE, last_entry); /* destroy the entry we're not displaying */ tmp = flast; flast = g_list_previous(flast); menu_entry_frame_free(tmp->data); self->entries = g_list_delete_link(self->entries, tmp); /* only the first one that we see is the last entry in the menu */ last_entry = FALSE; }; { ObMenuEntry *more_entry; ObMenuEntryFrame *more_frame; /* make the More... menu entry frame which will display in this frame. if self->menu->more_menu is NULL that means that this is already More... menu, so just use ourself. */ more_entry = menu_get_more((self->menu->more_menu ? self->menu->more_menu : self->menu), /* continue where we left off */ self->show_from + g_list_length(self->entries)); more_frame = menu_entry_frame_new(more_entry, self); /* make it get deleted when the menu frame goes away */ menu_entry_unref(more_entry); /* add our More... entry to the frame */ self->entries = g_list_append(self->entries, more_frame); } } menu_frame_render(self); }
static void menu_frame_place_topmenu(ObMenuFrame *self, const GravityPoint *pos, gint *x, gint *y, gint monitor, gboolean user_positioned) { gint dx, dy; screen_apply_gravity_point(x, y, self->area.width, self->area.height, pos, screen_physical_area_monitor(monitor)); if (user_positioned) return; if (config_menu_middle) { gint myx; myx = *x; *y -= self->area.height / 2; /* try to the right of the cursor */ menu_frame_move_on_screen(self, myx, *y, &dx, &dy); self->direction_right = TRUE; if (dx != 0) { /* try to the left of the cursor */ myx = *x - self->area.width; menu_frame_move_on_screen(self, myx, *y, &dx, &dy); self->direction_right = FALSE; } if (dx != 0) { /* if didnt fit on either side so just use what it says */ myx = *x; menu_frame_move_on_screen(self, myx, *y, &dx, &dy); self->direction_right = TRUE; } *x = myx + dx; *y += dy; } else { gint myx, myy; myx = *x; myy = *y; /* try to the bottom right of the cursor */ menu_frame_move_on_screen(self, myx, myy, &dx, &dy); self->direction_right = TRUE; if (dx != 0 || dy != 0) { /* try to the bottom left of the cursor */ myx = *x - self->area.width; myy = *y; menu_frame_move_on_screen(self, myx, myy, &dx, &dy); self->direction_right = FALSE; } if (dx != 0 || dy != 0) { /* try to the top right of the cursor */ myx = *x; myy = *y - self->area.height; menu_frame_move_on_screen(self, myx, myy, &dx, &dy); self->direction_right = TRUE; } if (dx != 0 || dy != 0) { /* try to the top left of the cursor */ myx = *x - self->area.width; myy = *y - self->area.height; menu_frame_move_on_screen(self, myx, myy, &dx, &dy); self->direction_right = FALSE; } if (dx != 0 || dy != 0) { /* if didnt fit on either side so just use what it says */ myx = *x; myy = *y; menu_frame_move_on_screen(self, myx, myy, &dx, &dy); self->direction_right = TRUE; } *x = myx + dx; *y = myy + dy; } }
void popup_delay_show(ObPopup *self, gulong msec, gchar *text) { gint l, t, r, b; gint x, y, w, h; guint m; gint emptyx, emptyy; /* empty space between elements */ gint textx, texty, textw, texth; gint iconx, icony, iconw, iconh; const Rect *area; Rect mon; gboolean hasicon = self->hasicon; /* when there is no icon and the text is not parent relative, then fill the whole dialog with the text appearance, don't use the bg at all */ if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL) RrMargins(self->a_bg, &l, &t, &r, &b); else l = t = r = b = 0; /* set up the textures */ self->a_text->texture[0].data.text.string = text; /* measure the text out */ if (text[0] != '\0') { RrMinSize(self->a_text, &textw, &texth); } else { textw = 0; texth = RrMinHeight(self->a_text); } /* get the height, which is also used for the icon width */ emptyy = t + b + ob_rr_theme->paddingy * 2; if (self->h) texth = self->h - emptyy; h = texth * self->iconhm + emptyy; if (self->textw) textw = self->textw; iconx = textx = l + ob_rr_theme->paddingx; emptyx = l + r + ob_rr_theme->paddingx * 2; if (hasicon) { iconw = texth * self->iconwm; iconh = texth * self->iconhm; textx += iconw + ob_rr_theme->paddingx; if (textw) emptyx += ob_rr_theme->paddingx; /* between the icon and text */ icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy; } else iconw = 0; texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy; /* when there is no icon, then fill the whole dialog with the text appearance */ if (!hasicon) { textx = texty = 0; texth += emptyy; textw += emptyx; emptyx = emptyy = 0; } w = textw + emptyx + iconw; /* cap it at maxw/minw */ if (self->maxw) w = MIN(w, self->maxw); if (self->minw) w = MAX(w, self->minw); textw = w - emptyx - iconw; /* sanity checks to avoid crashes! */ if (w < 1) w = 1; if (h < 1) h = 1; if (texth < 1) texth = 1; /* set up the x coord */ x = self->x; switch (self->gravity) { case NorthGravity: case CenterGravity: case SouthGravity: x -= w / 2; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: x -= w; break; } /* set up the y coord */ y = self->y; switch (self->gravity) { case WestGravity: case CenterGravity: case EastGravity: y -= h / 2; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: y -= h; break; } /* Find the monitor which contains the biggest part of the popup. * If the popup is completely off screen, limit it to the intersection * of all monitors and then try again. If it's still off screen, put it * on monitor 0. */ RECT_SET(mon, x, y, w, h); m = screen_find_monitor(&mon); area = screen_physical_area_monitor(m); x=MAX(MIN(x, area->x+area->width-w),area->x); y=MAX(MIN(y, area->y+area->height-h),area->y); if (m == screen_num_monitors) { RECT_SET(mon, x, y, w, h); m = screen_find_monitor(&mon); if (m == screen_num_monitors) m = 0; area = screen_physical_area_monitor(m); x=MAX(MIN(x, area->x+area->width-w),area->x); y=MAX(MIN(y, area->y+area->height-h),area->y); } /* set the windows/appearances up */ XMoveResizeWindow(obt_display, self->bg, x, y, w, h); /* when there is no icon and the text is not parent relative, then fill the whole dialog with the text appearance, don't use the bg at all */ if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL) RrPaint(self->a_bg, self->bg, w, h); if (textw) { self->a_text->surface.parent = self->a_bg; self->a_text->surface.parentx = textx; self->a_text->surface.parenty = texty; XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth); RrPaint(self->a_text, self->text, textw, texth); } if (hasicon) self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data); /* do the actual showing */ if (!self->mapped) { if (msec) { /* don't kill previous show timers */ if (!self->delay_mapped) { self->delay_timer = g_timeout_add(msec, popup_show_timeout, self); self->delay_mapped = TRUE; } } else { popup_show_timeout(self); } } }