1 /*--- xmenu.c ------------------------------------------------------------------
2 Copyright (C) 2004, 2005 Sylvain Fourmanoit <syfou@users.sourceforge.net>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to
6 deal in the Software without restriction, including without limitation the
7 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 sell copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies of the Software and its documentation and acknowledgment shall be
13 given in the documentation and software packages that this Software was
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 ------------------------------------------------------------------------------*/
23 /* Simple X Windows menu API */
25 /*----------------------------------------------------------------------------*/
26 #include "xmenu.h" /* xmenu headers */
29 #include <string.h> /* strlen, strcpy functions */
32 /*----------------------------------------------------------------------------*/
33 #define XMENU_X_BORDER 10
34 #define XMENU_Y_BORDER 5
36 /*----------------------------------------------------------------------------*/
37 /* This is logically part of command.h/command.c, but we need it here also.
39 extern char * dupstr_utf8(const char *);
41 /*----------------------------------------------------------------------------*/
46 if ((menu
=(xmenu
*)malloc(sizeof(xmenu
)))) {
47 if (!(menu
->content
=tree_init())) {
51 menu
->pos
=menu
->content
;
56 /*----------------------------------------------------------------------------*/
58 xmenu_push_item(xmenu
* menu
, const char * item
)
63 TRY_CATCH(str
=malloc(strlen(item
)+1));
65 if (tree_push(menu
->pos
,str
))
71 /*----------------------------------------------------------------------------*/
73 xmenu_push_submenu(xmenu
* menu
, const char * item
)
79 TRY_CATCH(str
=malloc(strlen(item
)));
81 if (tree_push(menu
->pos
,str
)) {
83 (tree
*)menu
->pos
->children
->content
[menu
->pos
->children
->pos
-1];
90 /*----------------------------------------------------------------------------*/
92 xmenu_end_submenu(xmenu
* menu
)
94 return (menu
->pos
->parent
)?(menu
->pos
=menu
->pos
->parent
)!=NULL
:0;
97 /*----------------------------------------------------------------------------*/
99 xmenu_list_callback(tree
* mytree
,uint depth
, uint pos
, void *params
) {
100 printf("String: %s\tDepth: %u\tPos: %u\n",(char*)mytree
->content
,depth
,pos
);
103 /*----------------------------------------------------------------------------*/
105 xmenu_build_image(tree
*pos
, int width
, int height
, int active
)
114 if ((font
=imlib_load_font("Vera/12")) &&
115 (image
=imlib_create_image(width
,height
))) {
116 imlib_context_set_font(font
);
117 imlib_context_set_image(image
);
118 /* Fill image in light grey */
119 imlib_context_set_color(200, 200, 200, 255);
120 imlib_image_fill_rectangle(0,0,width
,height
);
121 imlib_context_set_color(0,0,0,255);
122 /* Iterate and generate text, normal or inverted */
123 for(h
=XMENU_Y_BORDER
,i
=0;i
<pos
->children
->pos
;++i
,h
+=y
) {
124 item
=dupstr_utf8(((tree
*)(pos
->children
->content
[i
]))->content
);
125 if (*((char*)((tree
*)pos
->children
->content
[i
])->content
)!='-')
127 imlib_get_text_size(item
, &x
, &y
);
131 x
=-1; y
=XMENU_Y_BORDER
;
135 imlib_image_fill_rectangle(0,h
,width
,y
);
136 imlib_context_set_color(255,255,255,255);
139 if (item
) imlib_text_draw(XMENU_X_BORDER
,h
,item
);
141 imlib_image_draw_line(0,h
+y
/2,width
,h
+y
/2,0);
143 /* Potential submenu item: show it! */
144 if (((tree
*)(pos
->children
->content
[i
]))->children
->pos
) {
145 if ((poly
=imlib_polygon_new())) {
146 imlib_polygon_add_point(poly
,width
-XMENU_X_BORDER
+3,h
+y
);
147 imlib_polygon_add_point(poly
,width
-XMENU_X_BORDER
+3,h
);
148 imlib_polygon_add_point(poly
,width
,h
+y
/2);
149 imlib_image_fill_polygon(poly
);
150 imlib_polygon_free(poly
);
152 if ((poly
=imlib_polygon_new())) {
153 imlib_polygon_add_point(poly
,XMENU_X_BORDER
-3,h
+y
);
154 imlib_polygon_add_point(poly
,XMENU_X_BORDER
-3,h
);
155 imlib_polygon_add_point(poly
,0,h
+y
/2);
156 imlib_image_fill_polygon(poly
);
157 imlib_polygon_free(poly
);
160 if (i
==active
) imlib_context_set_color(0,0,0,255);
169 /*----------------------------------------------------------------------------*/
170 /* This (too long) function just generates a menu, prints it, and manages it
171 until the user made its choice, including submenu generation and
172 parent window update. Returns a pointer to the string of what was selected,
176 xmenu_fire(Display
* display
, int scr
,
178 Imlib_Image parent_background
, Imlib_Image parent_foreground
,
182 #ifndef X_DISPLAY_MISSING
183 char *item
, * result
= NULL
;
184 int x
, y
, z
, width
, height
, depth
,
185 active_item
, exit_flag
;
190 XWindowAttributes attr
;
192 Imlib_Updates updates
;
196 /* Set original position */
199 /* Return silently if no items */
200 if(!pos
->children
->pos
) return NULL
;
203 active_item
=-1; exit_flag
=0;
205 TRY_CATCH(heights
=(int*)malloc(sizeof(int)*pos
->children
->pos
));
207 /* Imlib2 context save */
208 xwindow_context_save(IMLIB_VISUAL
|IMLIB_DRAWABLE
|
209 IMLIB_IMAGE
|IMLIB_FONT
|IMLIB_DIRECTION
);
210 /* Determine width and height of menu */
211 imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT
);
212 if ((font
=imlib_load_font("Vera/12"))) {
213 /* Get screen dimentions via its root window attributes */
214 XGetWindowAttributes(display
,
215 RootWindowOfScreen(ScreenOfDisplay(display
,scr
)),
217 imlib_context_set_font(font
);
218 for(width
=0,height
=0,z
=0;
219 z
<pos
->children
->pos
;
220 heights
[z
++]=(height
+=y
),width
=(x
>width
)?x
:width
) {
221 if (*((char*)((tree
*)pos
->children
->content
[z
])->content
)!='-') {
223 if ((item
=dupstr_utf8(((tree
*)(pos
->children
->content
[z
]))->content
))) {
224 imlib_get_text_size(item
, &x
, &y
);
230 x
=0; y
=XMENU_Y_BORDER
;
235 /* Verify that there is something to write */
236 if (width
&& height
) {
237 width
+=2*XMENU_X_BORDER
; height
+=2*XMENU_Y_BORDER
;
238 /* Get actual pointer position: to work, the pointer need
239 to be on the same screen */
240 if(XQueryPointer(display
,
241 RootWindowOfScreen(ScreenOfDisplay(display
,scr
)),
242 &window
,&window
,&x
,&y
,&z
,&z
,(uint
*)&z
)) {
243 /* Windows coordinates computation */
244 x
=((x
-=width
/2)<0)?0:x
;
245 y
=((y
-=height
/2)<0)?0:y
;
246 x
=((x
+width
)<=attr
.width
)?x
:attr
.width
-width
;
247 y
=((y
+height
)<=attr
.height
)?y
:attr
.height
-height
;
248 /* Create the window */
250 XCreateWindow(display
,
251 RootWindowOfScreen(ScreenOfDisplay(display
,scr
)),
252 x
, y
, width
, height
, 0,
253 DefaultDepth(display
,scr
),
256 imlib_get_best_visual(display
,scr
,&depth
),
257 CWOverrideRedirect
|CWBackPixel
,
258 &XDefaultWindowAttributes
))) {
260 XSelectInput(display
, window
,
261 PointerMotionMask
| ButtonPressMask
|
262 ExposureMask
| LeaveWindowMask
);
263 /* Imlib2 settings */
264 imlib_context_set_visual(visual
);
265 imlib_context_set_drawable(window
);
267 /* Finally, map and manage the window */
268 XMapRaised(display
,window
);
270 updates
= imlib_updates_init();
271 while(XCheckWindowEvent(display
,parent
,
274 XCheckWindowEvent(display
,window
,
275 PointerMotionMask
| ButtonPressMask
|
276 ExposureMask
| LeaveWindowMask
,
280 if(ev
.xexpose
.window
==parent
) {
281 /* Refresh updates list */
282 updates
= imlib_update_append_rect(updates
,
291 z
<pos
->children
->pos
&&
292 ((XMotionEvent
*)&ev
.xbutton
)->y
-XMENU_Y_BORDER
>
295 z
=(z
==pos
->children
->pos
)?pos
->children
->pos
-1:z
;
296 z
=(*((char*)((tree
*)pos
->children
->content
[z
])->content
)
297 !='-')?z
:active_item
;
299 xmenu_build_image(pos
,width
,height
,z
)) {
300 imlib_render_image_on_drawable(0,0);
313 /* Perform parent window updates as needed */
314 xwindow_update_window(parent
, &updates
,
318 if(updates
) imlib_updates_free(updates
);
320 /* Now a bit of sleep */
321 usleep(X_POLLING_PERIOD
);
324 /* Unmap and destroy the window */
325 XUnmapWindow(display
,window
);
326 XDestroyWindow(display
,window
);
328 XSync(display
, False
);
330 /* Others cleaning */
336 /* Imlib2 restore original context settings */
337 xwindow_context_restore();
338 /* Do it again entirely if the user clicked on a submenu */
339 } while(active_item
!=-1 &&
340 (((tree
*)pos
->children
->content
[active_item
])->children
->pos
)?
341 pos
=(tree
*)pos
->children
->content
[active_item
],1:0);
343 result
=(active_item
!=-1)?
344 ((char*)((tree
*)pos
->children
->content
[active_item
])->content
):NULL
;
347 #else /* X_MISSING_DISPLAY */
352 /*----------------------------------------------------------------------------*/
354 xmenu_free(xmenu
* menu
)
356 if ((menu
->content
=tree_free(menu
->content
))==NULL
) {
363 /*----------------------------------------------------------------------------*/
365 xmenu_vector_free_item(void* menu
)
367 return xmenu_free((xmenu
*)menu
);
370 /*----------------------------------------------------------------------------*/