1 /**
2 * @file popup.c
3 * @author Joe Wingbermuehle
4 * @date 2004-2006
5 *
6 * @brief Functions for displaying popup windows.
7 *
8 */
9
10 #include "jwm.h"
11 #include "popup.h"
12 #include "main.h"
13 #include "color.h"
14 #include "font.h"
15 #include "screen.h"
16 #include "cursor.h"
17 #include "timing.h"
18 #include "misc.h"
19 #include "settings.h"
20 #include "event.h"
21 #include "hint.h"
22
23 typedef struct PopupType {
24 int x, y; /* The coordinates of the upper-left corner of the popup. */
25 int mx, my; /* The mouse position when the popup was created. */
26 Window mw;
27 int width, height;
28 char *text; /* The raw popup text. */
29 char *lines; /* Popup text split into NUL-separated lines. */
30 int lineCount; /* The number of lines. */
31 Window window;
32 Pixmap pmap;
33 } PopupType;
34
35 static PopupType popup;
36
37 static void MeasurePopupText();
38 static void SignalPopup(const TimeType *now, int x, int y, Window w,
39 void *data);
40
41 /** Startup popups. */
StartupPopup(void)42 void StartupPopup(void)
43 {
44 popup.text = NULL;
45 popup.window = None;
46 RegisterCallback(100, SignalPopup, NULL);
47 }
48
49 /** Shutdown popups. */
ShutdownPopup(void)50 void ShutdownPopup(void)
51 {
52 UnregisterCallback(SignalPopup, NULL);
53 if(popup.text) {
54 Release(popup.text);
55 Release(popup.lines);
56 popup.text = NULL;
57 }
58 if(popup.window != None) {
59 JXDestroyWindow(display, popup.window);
60 JXFreePixmap(display, popup.pmap);
61 popup.window = None;
62 }
63 }
64
65 /** Calculate dimensions of a popup window given the popup text. */
MeasurePopupText()66 void MeasurePopupText()
67 {
68 const int textHeight = GetStringHeight(FONT_POPUP) + 1;
69 char *ptr;
70
71 popup.lines = CopyString(popup.text);
72 ptr = popup.lines;
73
74 popup.width = 0;
75 popup.height = 1;
76 popup.lineCount = 0;
77 for(;;) {
78 char *end = strchr(ptr, '\n');
79 int currentWidth;
80 if(end) {
81 *end = 0;
82 }
83 currentWidth = GetStringWidth(FONT_POPUP, ptr) + 9;
84 popup.width = Max(popup.width, currentWidth);
85 popup.height += textHeight;
86 popup.lineCount += 1;
87 if(end) {
88 ptr = end + 1;
89 } else {
90 break;
91 }
92 }
93 }
94
95 /** Show a popup window. */
ShowPopup(int x,int y,const char * text,const PopupMaskType context)96 void ShowPopup(int x, int y, const char *text,
97 const PopupMaskType context)
98 {
99 const ScreenType *sp;
100 char *ptr;
101 int textHeight;
102 int i;
103
104 if(!(settings.popupMask & context)) {
105 return;
106 }
107
108 if(popup.text) {
109 if(x == popup.x && y == popup.y && !strcmp(popup.text, text)) {
110 /* This popup is already shown. */
111 return;
112 }
113 Release(popup.text);
114 Release(popup.lines);
115 popup.text = NULL;
116 }
117
118 if(text[0] == 0) {
119 return;
120 }
121
122 GetMousePosition(&popup.mx, &popup.my, &popup.mw);
123 popup.text = CopyString(text);
124
125 MeasurePopupText();
126 sp = GetCurrentScreen(x, y);
127 if(popup.width > sp->width) {
128 popup.width = sp->width;
129 }
130
131 popup.x = x;
132 if(y + 2 * popup.height + 2 >= sp->height) {
133 popup.y = y - popup.height - 2;
134 } else {
135 popup.y = y + GetStringHeight(FONT_POPUP) + 2;
136 }
137
138 if(popup.width + popup.x > sp->x + sp->width) {
139 popup.x = sp->x + sp->width - popup.width - 2;
140 }
141 if(popup.height + popup.y > sp->y + sp->height) {
142 popup.y = sp->y + sp->height - popup.height - 2;
143 }
144 if(popup.x < 2) {
145 popup.x = 2;
146 }
147 if(popup.y < 2) {
148 popup.y = 2;
149 }
150
151 if(popup.window == None) {
152
153 XSetWindowAttributes attr;
154 unsigned long attrMask = 0;
155
156 attrMask |= CWEventMask;
157 attr.event_mask = ExposureMask
158 | PointerMotionMask | PointerMotionHintMask;
159
160 attrMask |= CWSaveUnder;
161 attr.save_under = True;
162
163 attrMask |= CWDontPropagate;
164 attr.do_not_propagate_mask = PointerMotionMask
165 | ButtonPressMask
166 | ButtonReleaseMask;
167
168 popup.window = JXCreateWindow(display, rootWindow, popup.x, popup.y,
169 popup.width, popup.height, 0,
170 CopyFromParent, InputOutput,
171 CopyFromParent, attrMask, &attr);
172 SetAtomAtom(popup.window, ATOM_NET_WM_WINDOW_TYPE,
173 ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION);
174 JXMapRaised(display, popup.window);
175
176 } else {
177
178 JXMoveResizeWindow(display, popup.window, popup.x, popup.y,
179 popup.width, popup.height);
180 JXFreePixmap(display, popup.pmap);
181
182 }
183
184 popup.pmap = JXCreatePixmap(display, popup.window,
185 popup.width, popup.height,
186 rootDepth);
187
188 JXSetForeground(display, rootGC, colors[COLOR_POPUP_BG]);
189 JXFillRectangle(display, popup.pmap, rootGC, 0, 0,
190 popup.width - 1, popup.height - 1);
191 JXSetForeground(display, rootGC, colors[COLOR_POPUP_OUTLINE]);
192 JXDrawRectangle(display, popup.pmap, rootGC, 0, 0,
193 popup.width - 1, popup.height - 1);
194 ptr = popup.lines;
195 textHeight = GetStringHeight(FONT_POPUP) + 1;
196 for(i = 0; i < popup.lineCount; i++) {
197 RenderString(popup.pmap, FONT_POPUP, COLOR_POPUP_FG, 4,
198 textHeight * i + 1, popup.width, ptr);
199 ptr += strlen(ptr) + 1;
200 }
201 JXCopyArea(display, popup.pmap, popup.window, rootGC,
202 0, 0, popup.width, popup.height, 0, 0);
203
204 }
205
206 /** Signal popup (this is used to hide popups after awhile). */
SignalPopup(const TimeType * now,int x,int y,Window w,void * data)207 void SignalPopup(const TimeType *now, int x, int y, Window w, void *data)
208 {
209 if(popup.window != None) {
210 if(popup.mw != w ||
211 abs(popup.mx - x) > 0 || abs(popup.my - y) > 0) {
212 JXDestroyWindow(display, popup.window);
213 JXFreePixmap(display, popup.pmap);
214 popup.window = None;
215 }
216 }
217 }
218
219 /** Process an event on a popup window. */
ProcessPopupEvent(const XEvent * event)220 char ProcessPopupEvent(const XEvent *event)
221 {
222 if(popup.window != None && event->xany.window == popup.window) {
223 if(event->type == Expose && event->xexpose.count == 0) {
224 JXCopyArea(display, popup.pmap, popup.window, rootGC,
225 0, 0, popup.width, popup.height, 0, 0);
226 } else if(event->type == MotionNotify) {
227 JXDestroyWindow(display, popup.window);
228 JXFreePixmap(display, popup.pmap);
229 popup.window = None;
230 }
231 return 1;
232 }
233 return 0;
234 }
235