]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/xcut.c
new upstream version
[1ts-debian.git] / zephyr / zwgc / xcut.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It is one of the source files comprising zwgc, the Zephyr WindowGram
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id: xcut.c 2336 2009-03-22 18:59:56Z kcr $
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_xcut_c[] = "$Id: xcut.c 2336 2009-03-22 18:59:56Z kcr $";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21
22 /****************************************************************************/
23 /*                                                                          */
24 /*                    Code to deal with handling X events:                  */
25 /*                                                                          */
26 /****************************************************************************/
27
28 #ifndef X_DISPLAY_MISSING
29
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <zephyr/zephyr.h>
33 #include "new_memory.h"
34 #include "new_string.h"
35 #include "X_gram.h"
36 #include "zwgc.h"
37 #include "xselect.h"
38 #include "xmark.h"
39 #include "error.h"
40 #include "xrevstack.h"
41 #include "X_driver.h"
42 #include "xcut.h"
43 #ifdef CMU_ZWGCPLUS
44 #include "plus.h"
45 #include "variables.h"
46 #endif
47
48 /*
49  *
50  */
51
52 extern long ttl;
53
54 static char *selected_text=NULL;
55 static Window selecting_in = 0;
56
57 char *
58 getSelectedText(void)
59 {
60    return(selected_text);
61 }
62
63 #ifdef notdef
64 static string
65 x_gram_to_string(x_gram *gram)
66 {
67     int i, index, len;
68     int last_y = -1;
69     string temp;
70     string text_so_far = string_Copy("");
71     char *text;
72
73     text = gram->text;
74     for (i=0; i<gram->numblocks; i++) {
75         if (last_y != -1 && last_y != gram->blocks[i].y)
76           text_so_far = string_Concat2(text_so_far, "\n");
77         index = gram->blocks[i].strindex;
78         len = gram->blocks[i].strlen;
79         temp = string_CreateFromData(text+index, len);
80         text_so_far = string_Concat2(text_so_far, temp);
81         free(temp);
82         last_y = gram->blocks[i].y;
83     }
84
85     text_so_far = string_Concat2(text_so_far, "\n");
86     return(text_so_far);
87 }
88 #endif
89
90 /*
91  *
92  */
93
94 /*ARGSUSED*/
95 Bool
96 isShiftButton1(Display *dpy,
97                XEvent *event,
98                char *arg)
99 {
100    return(event->xbutton.state & (ShiftMask|Button1Mask));
101 }
102
103 /*ARGSUSED*/
104 Bool
105 isShiftButton3(Display *dpy,
106                XEvent *event,
107                char *arg)
108 {
109    return(event->xbutton.state & (ShiftMask|Button3Mask));
110 }
111
112 void
113 getLastEvent(Display *dpy,
114              unsigned int state,
115              XEvent *event)
116 {
117    XEvent xev;
118
119    if (state & Button1Mask) {
120       while(XCheckIfEvent(dpy,&xev,isShiftButton1,NULL))
121          *event=xev;
122    } else if (state & Button3Mask) {
123       while(XCheckIfEvent(dpy,&xev,isShiftButton3,NULL))
124          *event=xev;
125    }
126 }
127
128 void
129 xunmark(Display *dpy,
130         Window w,
131         x_gram *gram,
132         XContext desc_context)
133 {
134    if (gram == NULL)
135      if (XFindContext(dpy, w, desc_context, (caddr_t *) &gram))
136        return;
137
138    xmarkClear();
139    xmarkRedraw(dpy,w,gram,XMARK_REDRAW_OLD);
140 }
141
142 /* This is out here so xdestroygram can get at it */
143
144 #define PRESSOP_NONE 0  /* nothing */
145 #define PRESSOP_KILL 1  /* normal click */
146 #define PRESSOP_SEL  2  /* shift left */
147 #define PRESSOP_EXT  3  /* shift right */
148 #define PRESSOP_NUKE 4 /* ctrl */
149 #define PRESSOP_STOP 5  /* pressop cancelled by moving out of window */
150
151 static int current_pressop = PRESSOP_NONE;
152
153 void
154 xdestroygram(Display *dpy,
155              Window w,
156              XContext desc_context,
157              x_gram *gram)
158 {
159     struct timeval now;
160
161     gettimeofday(&now,NULL);
162     if ((gram->can_die.tv_sec == 0) ||
163         (gram->can_die.tv_sec > now.tv_sec) ||
164         ((gram->can_die.tv_sec == now.tv_sec) &&
165          (gram->can_die.tv_usec > now.tv_usec)))
166         return;
167
168     if (w == selecting_in) {
169         selecting_in = 0;
170         xmarkClear();
171     }
172     current_pressop = PRESSOP_NONE;
173     XDeleteContext(dpy, w, desc_context);
174     XDestroyWindow(dpy, w);
175     delete_gram(gram);
176     free(gram->text);
177     free(gram->blocks);
178 #ifdef CMU_ZWGCPLUS
179     if (gram->notice)
180       list_del_notice(gram->notice);
181 #endif
182     free(gram);
183
184 #ifdef CMU_ZWGCPLUS
185     XFlush(dpy);
186 #endif
187
188     if (bottom_gram == NULL && unlinked == NULL) {
189        /* flush colormap here */
190     }
191 }
192
193 void
194 xcut(Display *dpy,
195      XEvent *event,
196      XContext desc_context)
197 {
198     x_gram *gram;
199     Window w = event->xany.window;
200     int changedbound;
201
202     /*
203      * If event is for a window that's not ours anymore (say we're
204      * in the process of deleting it...), ignore it:
205      */
206     if (XFindContext(dpy, w, desc_context, (caddr_t *) &gram))
207       return;
208
209     /*
210      * Dispatch on the event type:
211      */
212     switch(event->type) {
213 #ifdef CMU_ZWGCPLUS
214     case KeyPress:
215       {
216         char c;
217         char *plusvar;
218         int res, metaflag;
219         res = XLookupString(&(event->xkey), &c, 1, NULL, NULL);
220         metaflag = event->xkey.state & Mod1Mask;
221
222         /* Recheck if zwgcplus is turned on;
223          *  Zephyr variables override zwgc variables
224          */
225
226         zwgcplus = 1;
227         plusvar = ZGetVariable("zwgcplus")
228             ? ZGetVariable("zwgcplus") : var_get_variable("zwgcplus");
229
230         if ((plusvar[0]=='\0') || (strcmp(plusvar,"no") == 0))
231           zwgcplus = 0;
232         else {
233           if (strcmp(plusvar,"no") == 0)
234             zwgcplus = 0;
235           if (strcmp(plusvar,"new") == 0)
236             zwgcplus = 2;
237         }
238
239         if (res != 0 && zwgcplus != 0)
240           plus_retry_notice(gram->notice, c, metaflag);
241       }
242       break;
243 #endif
244       case ClientMessage:
245         if ((event->xclient.message_type == XA_WM_PROTOCOLS) &&
246             (event->xclient.format == 32) &&
247             (event->xclient.data.l[0] == XA_WM_DELETE_WINDOW))
248             xdestroygram(dpy,w,desc_context,gram);
249         break;
250
251       case MapNotify:
252         /* I don't like using the local time, but MapNotify events don't
253          * come with a timestamp, and there's no way to query the server
254          */
255
256         if (gram->can_die.tv_sec == 0) {
257             gettimeofday(&(gram->can_die),NULL);
258             gram->can_die.tv_sec += (int) (ttl/1000);
259             gram->can_die.tv_usec += (ttl%1000)*1000;
260         }
261         break;
262
263       case UnmapNotify:
264         unlink_gram(gram);
265         break;
266
267       case LeaveNotify:
268         if (current_pressop == PRESSOP_KILL ||
269             current_pressop == PRESSOP_NUKE)
270            current_pressop = PRESSOP_STOP;
271         break;
272
273       case MotionNotify:
274         if (current_pressop == PRESSOP_SEL) {
275            /*     getLastEvent(dpy,Button1Mask,event); */
276            changedbound=xmarkExtendFromFirst(gram,event->xmotion.x,
277                                              event->xmotion.y);
278            xmarkRedraw(dpy,w,gram,changedbound);
279         } else if (current_pressop == PRESSOP_EXT) {
280            /*     getLastEvent(dpy,Button3Mask,event); */
281            changedbound=xmarkExtendFromNearest(gram,event->xmotion.x,
282                                                event->xmotion.y);
283            xmarkRedraw(dpy,w,gram,changedbound);
284         } 
285         break;
286
287       case ButtonPress:
288         if (current_pressop != PRESSOP_NONE) {
289            current_pressop = PRESSOP_STOP;
290         } else if ((event->xbutton.button==Button4 ||
291                     event->xbutton.button==Button5) &&
292                    !get_bool_resource("scrollDelete","ScrollDelete",0)) {
293            /* Ignore scroll wheel movement. */
294            break;
295         } else if ( (event->xbutton.state)&ShiftMask ) {
296            if (event->xbutton.button==Button1) {
297               if (selecting_in)
298                  xunmark(dpy,selecting_in,NULL,desc_context);
299               if (selected_text) free(selected_text);
300               selected_text = NULL;
301               if (! xselGetOwnership(dpy,w,event->xbutton.time)) {
302                  XBell(dpy,0);
303                  ERROR("Unable to get ownership of PRIMARY selection.\n");
304                  selecting_in = 0;
305                  current_pressop = PRESSOP_STOP;
306               } else {
307                  selecting_in = w;
308                  xmarkStart(gram,event->xbutton.x,event->xbutton.y);
309                  current_pressop = PRESSOP_SEL;
310               }
311            } else if ((event->xbutton.button==Button3) &&
312                       (w == selecting_in)) {
313               if (selected_text) free(selected_text);
314               selected_text = NULL;
315               changedbound=xmarkExtendFromNearest(gram,event->xbutton.x,
316                                                   event->xbutton.y);
317               xmarkRedraw(dpy,w,gram,changedbound);
318               selected_text = xmarkGetText();
319               /* this is ok, since to get here, the selection must be owned */
320               current_pressop = PRESSOP_EXT;
321 #ifdef CMU_ZWGCPLUS
322               if (selected_text)
323                 XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
324 #endif
325            }
326         } else if ( (event->xbutton.state)&ControlMask ) {
327            current_pressop = PRESSOP_NUKE;
328         } else {
329            current_pressop = PRESSOP_KILL;
330         }
331         break;
332
333       case ButtonRelease:
334         if (current_pressop == PRESSOP_KILL) {
335            xdestroygram(dpy,w,desc_context,gram);
336         } else if (current_pressop == PRESSOP_SEL ||
337                    current_pressop == PRESSOP_EXT) {
338            if (selected_text) free(selected_text);
339            selected_text = xmarkGetText();
340 #ifdef CMU_ZWGCPLUS
341            if (selected_text)
342              XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
343 #endif
344         } else if (current_pressop == PRESSOP_NUKE) {
345            XWindowAttributes wa;
346            int gx,gy;
347            Window temp;
348            x_gram *next;
349
350            for (gram = bottom_gram ; gram ; gram = next) {
351               XGetWindowAttributes(dpy,gram->w,&wa);
352               XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
353                                     &temp);
354
355               next = gram->above;
356
357               if ((wa.map_state == IsViewable) &&
358                   (gx <= event->xbutton.x_root) &&
359                   (event->xbutton.x_root < gx+wa.width) &&
360                   (gy <= event->xbutton.y_root) &&
361                   (event->xbutton.y_root < gy+wa.height)) {
362                  xdestroygram(dpy,gram->w,desc_context,gram);
363               }
364            }
365            for (gram = unlinked ; gram ; gram = next) {
366               XGetWindowAttributes(dpy,gram->w,&wa);
367               XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
368                                     &temp);
369
370               next = gram->above;
371
372               if ((wa.map_state == IsViewable) &&
373                   (gx <= event->xbutton.x_root) &&
374                   (event->xbutton.x_root < gx+wa.width) &&
375                   (gy <= event->xbutton.y_root) &&
376                   (event->xbutton.y_root < gy+wa.height)) {
377                  xdestroygram(dpy,gram->w,desc_context,gram);
378               }
379            }
380         }
381         current_pressop = PRESSOP_NONE;
382         break;
383
384      case SelectionRequest:
385        xselProcessSelection(dpy,w,event);
386        break;
387
388      case SelectionClear:
389        xselOwnershipLost(event->xselectionclear.time);
390        if (w == selecting_in) {
391           selecting_in = 0;
392           xunmark(dpy,w,gram,desc_context);
393           if (selected_text) free(selected_text);
394           selected_text = NULL;
395        }
396        break;
397
398 #ifdef notdef
399       case ConfigureNotify:
400 #ifdef DEBUG
401         if (zwgc_debug)
402           printf("ConfigureNotify received for wid %lx above wid %lx\n",
403                  (long) w,(long) event->xconfigure.above);
404 #endif
405         if (gram->above==gram) {
406            /* a new zgram.  Straight to the bottom! */
407            add_to_bottom(gram);
408         } else if (event->xconfigure.above)  {
409            /* some zgram was pulled to the top */
410            pull_to_top(gram);
411         } else {
412            /* Some zgram was pushed to the bottom */
413            push_to_bottom(gram);
414         }
415         /* Note that there is no option to configure a zgram to the middle */
416         break;
417 #endif
418       default:
419         break;
420     }
421
422     XFlush(dpy);
423 }
424
425 #endif
426