Spell 'serialize' with a 'z'
[notion.git] / ioncore / screen-notify.c
blob6a5d94a245e5af6f49103a5206ce6212f3d4dce1
1 /*
2 * ion/ioncore/screen-notify.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <libtu/minmax.h>
12 #include <libmainloop/defer.h>
13 #include <libmainloop/signal.h>
15 #include "common.h"
16 #include "global.h"
17 #include "infowin.h"
18 #include "activity.h"
19 #include "tags.h"
20 #include "gr.h"
21 #include "gr-util.h"
22 #include "stacking.h"
23 #include "names.h"
24 #include "screen.h"
25 #include "screen-notify.h"
26 #include "strings.h"
29 /*{{{ Generic stuff */
32 static WInfoWin *do_get_popup_win(WScreen *scr, Watch *watch, uint pos,
33 char *style)
36 WInfoWin *iw=(WInfoWin*)(watch->obj);
38 if(iw==NULL){
39 WMPlexAttachParams param=MPLEXATTACHPARAMS_INIT;
41 param.flags=(MPLEX_ATTACH_UNNUMBERED|
42 MPLEX_ATTACH_SIZEPOLICY|
43 MPLEX_ATTACH_GEOM|
44 MPLEX_ATTACH_LEVEL|
45 MPLEX_ATTACH_PASSIVE);
46 param.level=STACKING_LEVEL_ON_TOP;
48 param.geom.x=0;
49 param.geom.y=0;
50 param.geom.w=1;
51 param.geom.h=1;
53 switch(pos){
54 case MPLEX_STDISP_TL:
55 param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST;
56 param.geom.x=0;
57 break;
59 case MPLEX_STDISP_TR:
60 param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST;
61 param.geom.x=REGION_GEOM(scr).w-1;
62 break;
64 case MPLEX_STDISP_BL:
65 param.szplcy=SIZEPOLICY_GRAVITY_SOUTHWEST;
66 param.geom.x=0;
67 param.geom.y=REGION_GEOM(scr).h-1;
68 break;
70 case MPLEX_STDISP_BR:
71 param.szplcy=SIZEPOLICY_GRAVITY_SOUTHEAST;
72 param.geom.x=REGION_GEOM(scr).w-1;
73 param.geom.y=REGION_GEOM(scr).h-1;
74 break;
78 iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, &param,
79 (WRegionCreateFn*)create_infowin,
80 style);
82 if(iw!=NULL)
83 watch_setup(watch, (Obj*)iw, NULL);
86 return iw;
90 static void do_unnotify(Watch *watch)
92 Obj *iw=watch->obj;
93 if(iw!=NULL){
94 watch_reset(watch);
95 mainloop_defer_destroy((Obj*)iw);
99 // returns the position or the stdisp, or -1 if there isn't one
100 static int get_stdisp_pos(WScreen *scr)
102 WRegion* stdisp=NULL;
103 WMPlexSTDispInfo info;
105 mplex_get_stdisp(&scr->mplex, &stdisp, &info);
107 return (stdisp!=NULL) ? info.pos : -1;
110 /*}}}*/
113 /*{{{ Notifywin */
116 static WInfoWin *get_notifywin(WScreen *scr)
118 int stdisp_pos = get_stdisp_pos(scr);
119 return do_get_popup_win(scr,
120 &scr->notifywin_watch,
121 (stdisp_pos >= 0) ? stdisp_pos : MPLEX_STDISP_TL,
122 "actnotify");
126 void screen_notify(WScreen *scr, const char *str)
128 WInfoWin *iw=get_notifywin(scr);
130 if(iw!=NULL){
131 int maxw=REGION_GEOM(scr).w/3;
132 infowin_set_text(iw, str, maxw);
137 void screen_unnotify_notifywin(WScreen *scr)
139 do_unnotify(&scr->notifywin_watch);
143 static bool ws_mapped(WScreen *scr, WRegion *reg)
145 while(1){
146 WRegion *mgr=REGION_MANAGER(reg);
148 if(mgr==NULL)
149 return FALSE;
151 if(mgr==(WRegion*)scr)
152 return REGION_IS_MAPPED(reg);
154 reg=mgr;
159 static void screen_managed_activity(WScreen *scr)
161 char *notstr=NULL;
162 WRegion *reg;
163 ObjListIterTmp tmp;
164 PtrListIterTmp tmp2;
165 ObjList *actlist=ioncore_activity_list();
166 WInfoWin *iw=NULL;
167 PtrList *found=NULL;
168 int nfound=0, nadded=0;
169 int w=0, maxw=REGION_GEOM(scr).w/4;
171 /* Lisäksi minimipituus (10ex tms.), ja sen yli menevät jätetään
172 * pois (+ n)
174 FOR_ALL_ON_OBJLIST(WRegion*, reg, actlist, tmp){
175 if(!ioncore_g.activity_notification_on_all_screens &&
176 (region_screen_of(reg)!=scr || ws_mapped(scr, reg)))
177 continue;
178 if(region_name(reg)==NULL)
179 continue;
180 if(ptrlist_insert_last(&found, reg))
181 nfound++;
184 if(found==NULL)
185 goto unnotify;
187 iw=get_notifywin(scr);
189 if(iw==NULL)
190 return;
192 if(iw->brush==NULL)
193 goto unnotify;
195 notstr=scopy(TR("act: "));
197 if(notstr==NULL)
198 goto unnotify;
200 FOR_ALL_ON_PTRLIST(WRegion*, reg, found, tmp2){
201 const char *nm=region_name(reg);
202 char *nstr=NULL, *label=NULL;
204 w=grbrush_get_text_width(iw->brush, notstr, strlen(notstr));
206 if(w>=maxw)
207 break;
209 label=grbrush_make_label(iw->brush, nm, maxw-w);
210 if(label!=NULL){
211 nstr=(nadded>0
212 ? scat3(notstr, ", ", label)
213 : scat(notstr, label));
215 if(nstr!=NULL){
216 free(notstr);
217 notstr=nstr;
218 nadded++;
220 free(label);
224 if(nfound > nadded){
225 char *nstr=NULL;
227 libtu_asprintf(&nstr, "%s +%d", notstr, nfound-nadded);
229 if(nstr!=NULL){
230 free(notstr);
231 notstr=nstr;
235 ptrlist_clear(&found);
237 infowin_set_text(iw, notstr, 0);
239 free(notstr);
241 return;
243 unnotify:
244 screen_unnotify_notifywin(scr);
248 static void _screen_do_update_notifywin(WScreen *scr)
250 if(ioncore_g.screen_notify)
251 screen_managed_activity(scr);
252 else
253 screen_unnotify_notifywin(scr);
256 static void screen_do_update_notifywin(WScreen *scr)
258 if( ioncore_g.activity_notification_on_all_screens )
260 WScreen* scr_iterated;
261 FOR_ALL_SCREENS(scr_iterated)
263 _screen_do_update_notifywin(scr_iterated);
266 else
267 _screen_do_update_notifywin(scr);
270 /*}}}*/
273 /*{{{ Infowin */
276 static WInfoWin *get_infowin(WScreen *scr)
278 int stdisp_pos = get_stdisp_pos(scr);
279 return do_get_popup_win(scr,
280 &scr->infowin_watch,
281 (stdisp_pos == MPLEX_STDISP_TR) ? MPLEX_STDISP_BR : MPLEX_STDISP_TR,
282 "tab-info");
286 void screen_unnotify_infowin(WScreen *scr)
288 do_unnotify(&scr->infowin_watch);
292 GR_DEFATTR(active);
293 GR_DEFATTR(inactive);
294 GR_DEFATTR(selected);
295 GR_DEFATTR(tagged);
296 GR_DEFATTR(not_tagged);
297 GR_DEFATTR(not_dragged);
298 GR_DEFATTR(activity);
299 GR_DEFATTR(no_activity);
302 static void init_attr()
304 GR_ALLOCATTR_BEGIN;
305 GR_ALLOCATTR(active);
306 GR_ALLOCATTR(inactive);
307 GR_ALLOCATTR(selected);
308 GR_ALLOCATTR(tagged);
309 GR_ALLOCATTR(not_tagged);
310 GR_ALLOCATTR(not_dragged);
311 GR_ALLOCATTR(no_activity);
312 GR_ALLOCATTR(activity);
313 GR_ALLOCATTR_END;
317 static void screen_do_update_infowin(WScreen *scr)
319 WRegion *reg=mplex_mx_current(&(scr->mplex));
320 bool tag=(reg!=NULL && region_is_tagged(reg));
321 bool act=(reg!=NULL && region_is_activity_r(reg) && !REGION_IS_ACTIVE(scr));
322 bool sac=REGION_IS_ACTIVE(scr);
324 if(tag || act){
325 const char *n=region_displayname(reg);
326 WInfoWin *iw=get_infowin(scr);
328 if(iw!=NULL){
329 int maxw=REGION_GEOM(scr).w/3;
330 infowin_set_text(iw, n, maxw);
332 GrStyleSpec *spec=infowin_stylespec(iw);
334 init_attr();
336 gr_stylespec_unalloc(spec);
338 gr_stylespec_set(spec, GR_ATTR(selected));
339 gr_stylespec_set(spec, GR_ATTR(not_dragged));
340 gr_stylespec_set(spec, sac ? GR_ATTR(active) : GR_ATTR(inactive));
341 gr_stylespec_set(spec, tag ? GR_ATTR(tagged) : GR_ATTR(not_tagged));
342 gr_stylespec_set(spec, act ? GR_ATTR(activity) : GR_ATTR(no_activity));
345 }else{
346 screen_unnotify_infowin(scr);
352 /*}}}*/
355 /*{{{ workspace indicator win */
357 // A single timer for ALL the screens
358 static WTimer* workspace_indicator_remove_timer = NULL;
359 static WScreen* workspace_indicator_active_screen = NULL;
360 static WGroupWS* workspace_indicator_last_workspace = NULL;
362 static WInfoWin *get_workspace_indicatorwin(WScreen *scr)
364 return do_get_popup_win(scr,
365 &scr->workspace_indicatorwin_watch,
366 MPLEX_STDISP_BL,
367 "tab-info");
371 void screen_unnotify_workspace_indicatorwin(void)
373 if( workspace_indicator_active_screen != NULL)
375 do_unnotify(&workspace_indicator_active_screen->workspace_indicatorwin_watch);
376 workspace_indicator_active_screen = NULL;
379 if( workspace_indicator_remove_timer )
380 timer_reset( workspace_indicator_remove_timer );
383 static void timer_expired__workspace_indicator_remove(WTimer* UNUSED(dummy1), Obj* UNUSED(dummy2))
385 if( workspace_indicator_active_screen != NULL )
386 screen_unnotify_workspace_indicatorwin();
389 // Called when a region is focused. In response this function may either put up
390 // a new workspace-indicator or remove an existing one
391 void screen_update_workspace_indicatorwin(WRegion* reg_focused)
393 if( ioncore_g.workspace_indicator_timeout <= 0 )
394 return;
396 WScreen* scr = region_screen_of(reg_focused);
397 WRegion* mplex;
398 if( scr == NULL ||
399 (mplex = mplex_mx_current(&(scr->mplex))) == NULL ||
400 !OBJ_IS(mplex, WGroupWS) ||
401 mplex->ni.name == NULL)
403 return;
406 // remove the indicator from the other screen that it is on (if it IS on
407 // another screen)
408 if( workspace_indicator_active_screen != NULL &&
409 workspace_indicator_active_screen != scr )
411 screen_unnotify_workspace_indicatorwin();
413 else if( (WGroupWS*)mplex == workspace_indicator_last_workspace )
414 // If I'm already on this workspace, do nothing
415 return;
417 const char* name = mplex->ni.name;
419 WInfoWin *iw=get_workspace_indicatorwin(scr);
421 if(iw!=NULL){
422 int maxw=REGION_GEOM(scr).w/3;
423 infowin_set_text(iw, name, maxw);
425 GrStyleSpec *spec=infowin_stylespec(iw);
426 init_attr();
427 gr_stylespec_unalloc(spec);
429 gr_stylespec_set(spec, GR_ATTR(selected));
430 gr_stylespec_set(spec, GR_ATTR(not_dragged));
431 gr_stylespec_set(spec, GR_ATTR(active));
434 // set up the indicator on THIS screen
435 workspace_indicator_active_screen = scr;
436 workspace_indicator_last_workspace = (WGroupWS*)mplex;
438 // create and set the timer
439 if( workspace_indicator_remove_timer == NULL )
440 workspace_indicator_remove_timer = create_timer();
441 if( workspace_indicator_remove_timer == NULL )
442 return;
444 timer_set(workspace_indicator_remove_timer, ioncore_g.workspace_indicator_timeout,
445 (WTimerHandler*)timer_expired__workspace_indicator_remove, NULL);
448 void screen_unnotify_if_screen( WScreen* reg )
450 if( reg == workspace_indicator_active_screen )
451 screen_unnotify_workspace_indicatorwin();
454 void screen_unnotify_if_workspace( WGroupWS* reg)
456 if( reg == workspace_indicator_last_workspace )
458 screen_unnotify_workspace_indicatorwin();
459 workspace_indicator_last_workspace = NULL;
463 /*}}}*/
466 /*{{{ Notification callbacks and deferred updates*/
469 void screen_update_infowin(WScreen *scr)
471 mainloop_defer_action((Obj*)scr,
472 (WDeferredAction*)screen_do_update_infowin);
476 void screen_update_notifywin(WScreen *scr)
478 mainloop_defer_action((Obj*)scr,
479 (WDeferredAction*)screen_do_update_notifywin);
483 void screen_managed_notify(WScreen *scr, WRegion *UNUSED(reg), WRegionNotify how)
485 if(how==ioncore_g.notifies.tag)
486 screen_update_infowin(scr);
490 void ioncore_screen_activity_notify(WRegion *reg, WRegionNotify how)
492 WScreen *scr=region_screen_of(reg);
493 if(scr!=NULL){
494 if(how==ioncore_g.notifies.activity){
495 screen_update_notifywin(region_screen_of(reg));
496 }else if(how==ioncore_g.notifies.name){
497 if(region_is_activity(reg))
498 screen_update_notifywin(scr);
499 if((WRegion*)scr==REGION_MANAGER(reg))
500 screen_do_update_infowin(scr);
506 /*}}}*/