X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fprompt.c;h=88c74b68d048baf3519a753303b547be07e4b7c0;hb=HEAD;hp=54ea469d921f38912e23c4a01b301f435d8600f0;hpb=0e9cfd7c77d8608a4be29f43413575d9553bf21c;p=chaz%2Fopenbox diff --git a/openbox/prompt.c b/openbox/prompt.c index 54ea469d..88c74b68 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -20,6 +20,8 @@ #include "openbox.h" #include "screen.h" #include "client.h" +#include "group.h" +#include "event.h" #include "obt/display.h" #include "obt/keyboard.h" #include "obt/prop.h" @@ -28,48 +30,35 @@ static GList *prompt_list = NULL; /* we construct these */ +static RrAppearance *prompt_a_bg; static RrAppearance *prompt_a_button; static RrAppearance *prompt_a_focus; static RrAppearance *prompt_a_press; /* we change the max width which would screw with others */ static RrAppearance *prompt_a_msg; +/* sizing stuff */ +#define OUTSIDE_MARGIN 4 +#define MSG_BUTTON_SEPARATION 4 +#define BUTTON_SEPARATION 4 +#define BUTTON_VMARGIN 4 +#define BUTTON_HMARGIN 12 +#define MAX_WIDTH 400 + static void prompt_layout(ObPrompt *self); static void render_all(ObPrompt *self); static void render_button(ObPrompt *self, ObPromptElement *e); +static void prompt_resize(ObPrompt *self, gint w, gint h); +static void prompt_run_callback(ObPrompt *self, gint result); void prompt_startup(gboolean reconfig) { - RrColor *c_button, *c_focus, *c_press; - - prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close); - prompt_a_focus = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); - prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); - - c_button = prompt_a_button->texture[0].data.mask.color; - c_focus = prompt_a_button->texture[0].data.mask.color; - c_press = prompt_a_button->texture[0].data.mask.color; - - RrAppearanceRemoveTextures(prompt_a_button); - RrAppearanceRemoveTextures(prompt_a_focus); - RrAppearanceRemoveTextures(prompt_a_press); - - RrAppearanceAddTextures(prompt_a_button, 1); - RrAppearanceAddTextures(prompt_a_focus, 1); - RrAppearanceAddTextures(prompt_a_press, 1); + /* note: this is not a copy, don't free it */ + prompt_a_bg = ob_rr_theme->osd_bg; - /* totally cheating here.. */ - prompt_a_button->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; - prompt_a_focus->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; - prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; - - prompt_a_button->texture[0].data.text.justify = RR_JUSTIFY_CENTER; - prompt_a_focus->texture[0].data.text.justify = RR_JUSTIFY_CENTER; - prompt_a_press->texture[0].data.text.justify = RR_JUSTIFY_CENTER; - - prompt_a_button->texture[0].data.text.color = c_button; - prompt_a_focus->texture[0].data.text.color = c_focus; - prompt_a_press->texture[0].data.text.color = c_press; + prompt_a_button = RrAppearanceCopy(ob_rr_theme->osd_unpressed_button); + prompt_a_focus = RrAppearanceCopy(ob_rr_theme->osd_focused_button); + prompt_a_press = RrAppearanceCopy(ob_rr_theme->osd_pressed_button); prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); prompt_a_msg->texture[0].data.text.flow = TRUE; @@ -86,48 +75,64 @@ void prompt_startup(gboolean reconfig) void prompt_shutdown(gboolean reconfig) { + GList *it, *next; + + if (!reconfig) { + for (it = prompt_list; it; it = next) { + ObPrompt *p = it->data; + next = it->next; + if (p->cleanup) p->cleanup(p, p->data); + } + + g_assert(prompt_list == NULL); + } + RrAppearanceFree(prompt_a_button); RrAppearanceFree(prompt_a_focus); RrAppearanceFree(prompt_a_press); RrAppearanceFree(prompt_a_msg); } -ObPrompt* prompt_new(const gchar *msg, +ObPrompt* prompt_new(const gchar *msg, const gchar *title, const ObPromptAnswer *answers, gint n_answers, gint default_result, gint cancel_result, - ObPromptCallback func, gpointer data) + ObPromptCallback func, ObPromptCleanup cleanup, + gpointer data) { ObPrompt *self; XSetWindowAttributes attrib; gint i; attrib.override_redirect = FALSE; - attrib.border_pixel = RrColorPixel(ob_rr_theme->osd_border_color); - self = g_new0(ObPrompt, 1); + self = g_slice_new0(ObPrompt); self->ref = 1; self->func = func; + self->cleanup = cleanup; self->data = data; self->default_result = default_result; self->cancel_result = cancel_result; self->super.type = OB_WINDOW_CLASS_PROMPT; self->super.window = XCreateWindow(obt_display, obt_root(ob_screen), - 0, 0, 1, 1, ob_rr_theme->obwidth, + 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, - CWOverrideRedirect | CWBorderPixel, + CWOverrideRedirect, &attrib); + self->ic = obt_keyboard_context_new(self->super.window, + self->super.window); /* make it a dialog type window */ OBT_PROP_SET32(self->super.window, NET_WM_WINDOW_TYPE, ATOM, OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG)); + /* set the window's title */ + if (title) + OBT_PROP_SETS(self->super.window, NET_WM_NAME, title); + /* listen for key presses on the window */ self->event_mask = KeyPressMask; - /* we make a copy of this appearance for each prompt */ - self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); - /* set up the text message widow */ self->msg.text = g_strdup(msg); self->msg.window = XCreateWindow(obt_display, self->super.window, @@ -184,19 +189,21 @@ void prompt_unref(ObPrompt *self) if (self && --self->ref == 0) { gint i; + if (self->mapped) + prompt_hide(self); + prompt_list = g_list_remove(prompt_list, self); + obt_keyboard_context_unref(self->ic); + for (i = 0; i < self->n_buttons; ++i) { window_remove(self->button[i].window); XDestroyWindow(obt_display, self->button[i].window); } XDestroyWindow(obt_display, self->msg.window); - - RrAppearanceFree(self->a_bg); - XDestroyWindow(obt_display, self->super.window); - g_free(self); + g_slice_free(ObPrompt, self); } } @@ -208,23 +215,15 @@ static void prompt_layout(ObPrompt *self) gint w, h; gint maxw; - const gint OUTSIDE_MARGIN = 4; - const gint MSG_BUTTON_SEPARATION = 4; - const gint BUTTON_SEPARATION = 4; - const gint BUTTON_VMARGIN = 4; - const gint BUTTON_HMARGIN = 12; - const gint MAX_WIDTH = 600; - - RrMargins(self->a_bg, &l, &t, &r, &b); + RrMargins(prompt_a_bg, &l, &t, &r, &b); l += OUTSIDE_MARGIN; t += OUTSIDE_MARGIN; r += OUTSIDE_MARGIN; b += OUTSIDE_MARGIN; { - Rect *area = screen_physical_area_all_monitors(); + const Rect *area = screen_physical_area_all_monitors(); maxw = MIN(MAX_WIDTH, area->width*4/5); - g_free(area); } /* find the button sizes and how much space we need for them */ @@ -277,11 +276,9 @@ static void prompt_layout(ObPrompt *self) } /* size and position the toplevel window */ - self->width = w + l + r; - self->height = h + t + b; + prompt_resize(self, w + l + r, h + t + b); - /* move and resize the actual windows */ - XResizeWindow(obt_display, self->super.window, self->width, self->height); + /* move and resize the internal windows */ XMoveResizeWindow(obt_display, self->msg.window, self->msg.x, self->msg.y, self->msg.width, self->msg.height); @@ -291,29 +288,109 @@ static void prompt_layout(ObPrompt *self) self->button[i].width, self->button[i].height); } +static void prompt_resize(ObPrompt *self, gint w, gint h) +{ + XConfigureRequestEvent req; + XSizeHints hints; + + self->width = w; + self->height = h; + + /* the user can't resize the prompt */ + hints.flags = PMinSize | PMaxSize; + hints.min_width = hints.max_width = w; + hints.min_height = hints.max_height = h; + XSetWMNormalHints(obt_display, self->super.window, &hints); + + if (self->mapped) { + /* send a configure request like a normal client would */ + req.type = ConfigureRequest; + req.display = obt_display; + req.parent = obt_root(ob_screen); + req.window = self->super.window; + req.width = w; + req.height = h; + req.value_mask = CWWidth | CWHeight; + XSendEvent(req.display, req.window, FALSE, StructureNotifyMask, + (XEvent*)&req); + } + else + XResizeWindow(obt_display, self->super.window, w, h); +} + +static void setup_button_focus_tex(ObPromptElement *e, RrAppearance *a, + gboolean on) +{ + gint i, l, r, t, b; + + for (i = 1; i < 5; ++i) + a->texture[i].type = on ? RR_TEXTURE_LINE_ART : RR_TEXTURE_NONE; + + if (!on) return; + + RrMargins(a, &l, &t, &r, &b); + l += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + r += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + t += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + b += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + + /* top line */ + a->texture[1].data.lineart.x1 = l; + a->texture[1].data.lineart.x2 = e->width - r - 1; + a->texture[1].data.lineart.y1 = t; + a->texture[1].data.lineart.y2 = t; + + /* bottom line */ + a->texture[2].data.lineart.x1 = l; + a->texture[2].data.lineart.x2 = e->width - r - 1; + a->texture[2].data.lineart.y1 = e->height - b - 1; + a->texture[2].data.lineart.y2 = e->height - b - 1; + + /* left line */ + a->texture[3].data.lineart.x1 = l; + a->texture[3].data.lineart.x2 = l; + a->texture[3].data.lineart.y1 = t; + a->texture[3].data.lineart.y2 = e->height - b - 1; + + /* right line */ + a->texture[4].data.lineart.x1 = e->width - r - 1; + a->texture[4].data.lineart.x2 = e->width - r - 1; + a->texture[4].data.lineart.y1 = t; + a->texture[4].data.lineart.y2 = e->height - b - 1; +} + static void render_button(ObPrompt *self, ObPromptElement *e) { RrAppearance *a; - if (e->pressed) a = prompt_a_press; - else if (self->focus == e) a = prompt_a_focus; - else a = prompt_a_button; + if (e->hover && e->pressed) a = prompt_a_press; + else if (self->focus == e) a = prompt_a_focus; + else a = prompt_a_button; - a->surface.parent = self->a_bg; + a->surface.parent = prompt_a_bg; a->surface.parentx = e->x; - a->surface.parentx = e->y; + a->surface.parenty = e->y; + + /* draw the keyfocus line */ + if (self->focus == e) + setup_button_focus_tex(e, a, TRUE); a->texture[0].data.text.string = e->text; RrPaint(a, e->window, e->width, e->height); + + /* turn off the keyfocus line so that it doesn't affect size calculations + */ + if (self->focus == e) + setup_button_focus_tex(e, a, FALSE); } static void render_all(ObPrompt *self) { gint i; - RrPaint(self->a_bg, self->super.window, self->width, self->height); + RrPaint(prompt_a_bg, self->super.window, self->width, self->height); - prompt_a_msg->surface.parent = self->a_bg; + prompt_a_msg->surface.parent = prompt_a_bg; prompt_a_msg->surface.parentx = self->msg.x; prompt_a_msg->surface.parenty = self->msg.y; @@ -325,12 +402,19 @@ static void render_all(ObPrompt *self) render_button(self, &self->button[i]); } -void prompt_show(ObPrompt *self, ObClient *parent) +void prompt_show(ObPrompt *self, ObClient *parent, gboolean modal) { - XSizeHints hints; gint i; - if (self->mapped) return; + if (self->mapped) { + /* activate the prompt */ + OBT_PROP_MSG(ob_screen, self->super.window, NET_ACTIVE_WINDOW, + 1, /* from an application.. */ + event_time(), + 0, + 0, 0); + return; + } /* set the focused button (if not found then the first button is used) */ self->focus = &self->button[0]; @@ -340,14 +424,34 @@ void prompt_show(ObPrompt *self, ObClient *parent) break; } - /* you can't resize the prompt */ - hints.flags = PMinSize | PMaxSize; - hints.min_width = hints.max_width = self->width; - hints.min_height = hints.max_height = self->height; - XSetWMNormalHints(obt_display, self->super.window, &hints); + if (parent) { + Atom states[1]; + gint nstates; + Window p; + XWMHints h; + + if (parent->group) { + /* make it transient for the window's group */ + h.flags = WindowGroupHint; + h.window_group = parent->group->leader; + p = obt_root(ob_screen); + } + else { + /* make it transient for the window directly */ + h.flags = 0; + p = parent->window; + } - XSetTransientForHint(obt_display, (parent ? parent->window : 0), - self->super.window); + XSetWMHints(obt_display, self->super.window, &h); + OBT_PROP_SET32(self->super.window, WM_TRANSIENT_FOR, WINDOW, p); + + states[0] = OBT_PROP_ATOM(NET_WM_STATE_MODAL); + nstates = (modal ? 1 : 0); + OBT_PROP_SETA32(self->super.window, NET_WM_STATE, ATOM, + states, nstates); + } + else + OBT_PROP_ERASE(self->super.window, WM_TRANSIENT_FOR); /* set up the dialog and render it */ prompt_layout(self); @@ -364,35 +468,39 @@ void prompt_hide(ObPrompt *self) self->mapped = FALSE; } -void prompt_key_event(ObPrompt *self, XEvent *e) +gboolean prompt_key_event(ObPrompt *self, XEvent *e) { gboolean shift; - guint shift_mask; + guint shift_mask, mods; + KeySym sym; - if (e->type != KeyPress) return; + if (e->type != KeyPress) return FALSE; shift_mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT); - shift = !!(e->xkey.state & shift_mask); + mods = obt_keyboard_only_modmasks(e->xkey.state); + shift = !!(mods & shift_mask); /* only accept shift */ - if (e->xkey.state != 0 && e->xkey.state != shift_mask) - return; + if (mods != 0 && mods != shift_mask) + return FALSE; + + sym = obt_keyboard_keypress_to_keysym(e); - if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) + if (sym == XK_Escape) prompt_cancel(self); - else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { - if (self->func) self->func(self, self->focus->result, self->data); - prompt_hide(self); - } - else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB)) { + else if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_space) + prompt_run_callback(self, self->focus->result); + else if (sym == XK_Tab || sym == XK_Left || sym == XK_Right) { gint i; + gboolean left; ObPromptElement *oldfocus; + left = (sym == XK_Left) || ((sym == XK_Tab) && shift); oldfocus = self->focus; for (i = 0; i < self->n_buttons; ++i) if (self->focus == &self->button[i]) break; - i += (shift ? -1 : 1); + i += (left ? -1 : 1); if (i < 0) i = self->n_buttons - 1; else if (i >= self->n_buttons) i = 0; self->focus = &self->button[i]; @@ -400,17 +508,19 @@ void prompt_key_event(ObPrompt *self, XEvent *e) if (oldfocus != self->focus) render_button(self, oldfocus); render_button(self, self->focus); } + return TRUE; } -void prompt_mouse_event(ObPrompt *self, XEvent *e) +gboolean prompt_mouse_event(ObPrompt *self, XEvent *e) { gint i; ObPromptElement *but; if (e->type != ButtonPress && e->type != ButtonRelease && - e->type != MotionNotify) return; + e->type != MotionNotify) return FALSE; /* find the button */ + but = NULL; for (i = 0; i < self->n_buttons; ++i) if (self->button[i].window == (e->type == MotionNotify ? e->xmotion.window : e->xbutton.window)) @@ -418,40 +528,77 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) but = &self->button[i]; break; } - g_assert(but != NULL); + if (!but) return FALSE; if (e->type == ButtonPress) { ObPromptElement *oldfocus; oldfocus = self->focus; - but->pressed = TRUE; + but->pressed = but->hover = TRUE; self->focus = but; if (oldfocus != but) render_button(self, oldfocus); render_button(self, but); } else if (e->type == ButtonRelease) { - if (but->pressed) { - if (self->func) self->func(self, but->result, self->data); - prompt_hide(self); - } + if (but->hover) + prompt_run_callback(self, but->result); + but->pressed = FALSE; } else if (e->type == MotionNotify) { - gboolean press; + if (but->pressed) { + gboolean hover; - press = (e->xmotion.x >= 0 && e->xmotion.y >= 0 && - e->xmotion.x < but->width && e->xmotion.y < but->height); + hover = (e->xmotion.x >= 0 && e->xmotion.y >= 0 && + e->xmotion.x < but->width && e->xmotion.y < but->height); - if (press != but->pressed) { - but->pressed = press; - render_button(self, but); + if (hover != but->hover) { + but->hover = hover; + render_button(self, but); + } } } + return TRUE; } void prompt_cancel(ObPrompt *self) { - if (self->func) self->func(self, self->cancel_result, self->data); + prompt_run_callback(self, self->cancel_result); +} + +static gboolean prompt_show_message_cb(ObPrompt *p, int res, gpointer data) +{ + return TRUE; /* call the cleanup func */ +} + +static void prompt_show_message_cleanup(ObPrompt *p, gpointer data) +{ + prompt_unref(p); +} + +ObPrompt* prompt_show_message(const gchar *msg, const gchar *title, + const gchar *answer) +{ + ObPrompt *p; + ObPromptAnswer ans[] = { + { answer, 0 } + }; + + p = prompt_new(msg, title, ans, 1, 0, 0, + prompt_show_message_cb, prompt_show_message_cleanup, NULL); + prompt_show(p, NULL, FALSE); + return p; +} + +static void prompt_run_callback(ObPrompt *self, gint result) +{ + prompt_ref(self); + if (self->func) { + gboolean clean = self->func(self, result, self->data); + if (clean && self->cleanup) + self->cleanup(self, self->data); + } prompt_hide(self); + prompt_unref(self); }