added text widget from nwanua
[wmaker-crm.git] / WINGs / wruler.c
blob19165804ed6963456f4eaeed725dd216f5a4dfc1
2 /* WMRuler: nifty ruler widget for WINGs (OK, for WMText ;-) */
3 /* Copyleft (>) 1999, 2000 Nwanua Elumeze <nwanua@colorado.edu> */
6 #include <WINGsP.h>
7 #include <WUtil.h>
8 #include <WMaker.h>
10 #include "wruler.h"
12 #define DEFAULT_X_OFFSET 22
13 #define MIN_DOC_WIDTH 200
14 #define DEFAULT_RULER_WIDTH 240
15 #define DEFAULT_RULER_HEIGHT 40
18 /* a pixel is defined here as 1/8 of an "inch" */
19 /* an "inch" is not the British unit inch... just so you know :-P */
21 typedef struct W_Ruler {
22 W_Class widgetClass;
23 W_View *view;
25 W_View *pview;
27 void *clientData;
28 WMAction *action, *moveaction;
30 struct {
31 int left, body, first, right, dleft;
32 } margins;
33 int which_marker;
35 int end;
36 int pressed;
37 int offset;
39 struct {
40 unsigned int showtabs:1;
41 unsigned int buttonPressed:1;
42 } flags;
44 } Ruler;
47 static void
48 resizeRuler(W_ViewDelegate *self, WMView *view)
50 Ruler *rPtr = (Ruler *)view->self;
52 CHECK_CLASS(rPtr, WC_Ruler);
54 W_ResizeView(rPtr->view, view->size.width,
55 DEFAULT_RULER_HEIGHT); /* more like DEFACTO_RULER_HEIGHT ;-) */
57 rPtr->end = view->size.width;
58 rPtr->margins.right = view->size.width;
62 W_ViewDelegate _RulerViewDelegate =
64 NULL,
65 NULL,
66 resizeRuler,
67 NULL,
68 NULL
69 };
71 static void
72 paintRuler(Ruler *rPtr)
74 GC gc;
75 XPoint points[6];
76 WMFont *font;
77 int i, j, xpos;
78 char c[3];
79 int actual_x;
81 CHECK_CLASS(rPtr, WC_Ruler);
83 if(!rPtr->view->flags.mapped)
84 return;
86 gc = WMColorGC(WMBlackColor(rPtr->view->screen));
87 font = WMSystemFontOfSize(rPtr->view->screen, 8);
90 WMDrawString(rPtr->view->screen, rPtr->view->window, gc,
91 font, rPtr->margins.dleft+2, 25, "0 inches", 10);
93 /* marker ticks */
94 j=0;
95 for(i=80; i<rPtr->view->size.width; i+=80) {
96 if(j<10)
97 snprintf(c,3,"%d",++j);
98 else
99 snprintf(c,3,"%2d",++j);
100 WMDrawString(rPtr->view->screen, rPtr->view->window, gc,
101 font, rPtr->margins.dleft+2+i, 25, c, 2);
104 if(rPtr->flags.showtabs){
105 points[0].y = 9;
106 points[1].y = 15;
107 points[2].y = 20;
110 for(i=0; i<rPtr->view->size.width; i+=40) {
111 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
112 gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 31);
114 if(rPtr->flags.showtabs){
115 points[0].x = rPtr->margins.dleft+i+40;
116 points[1].x = points[0].x+6;
117 points[2].x = points[0].x;
118 XFillPolygon (rPtr->view->screen->display, rPtr->view->window,
119 WMColorGC(WMDarkGrayColor(rPtr->view->screen)),
120 points, 3, Convex, CoordModeOrigin);
122 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
123 WMColorGC(WMDarkGrayColor(rPtr->view->screen)),
124 rPtr->margins.dleft+i, 18, rPtr->margins.dleft+i, 21);
128 for(i=0; i<rPtr->view->size.width; i+=20)
129 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
130 gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 27);
132 for(i=0; i<rPtr->view->size.width-20; i+=10)
133 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
134 gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 24);
136 /* guide the end marker in future drawings till next resize */
137 rPtr->end = i+12;
139 /* base line */
140 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
141 gc, rPtr->margins.dleft, 21, rPtr->margins.left+i-10, 21);
144 /* Marker for right margin
147 / |
148 /__|
150 | */
152 xpos = rPtr->margins.right;
153 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
154 WMColorGC(WMDarkGrayColor(rPtr->view->screen)), xpos, 10, xpos, 20);
156 points[0].x = xpos+1;
157 points[0].y = 2;
158 points[1].x = points[0].x-6;
159 points[1].y = 9;
160 points[2].x = points[0].x-6;
161 points[2].y = 11;
162 points[3].x = points[0].x;
163 points[3].y = 11;
164 XFillPolygon (rPtr->view->screen->display, rPtr->view->window,
165 gc, points, 4, Convex, CoordModeOrigin);
168 /* Marker for left margin
172 |__\
174 | */
176 xpos = rPtr->margins.left;
177 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
178 WMColorGC(WMDarkGrayColor(rPtr->view->screen)), xpos, 10, xpos, 20);
180 points[0].x = xpos;
181 points[0].y = 3;
182 points[1].x = points[0].x+6;
183 points[1].y = 10;
184 points[2].x = points[0].x+6;
185 points[2].y = 11;
186 points[3].x = points[0].x;
187 points[3].y = 11;
188 XFillPolygon (rPtr->view->screen->display, rPtr->view->window,
189 gc, points, 4, Convex, CoordModeOrigin);
192 points[3].x =
194 /* Marker for first line only
195 _____
196 |___|
201 xpos = rPtr->margins.first + rPtr->margins.dleft;
202 XFillRectangle(rPtr->view->screen->display, rPtr->view->window,
203 gc, xpos-5, 7, 11, 5);
205 XDrawLine(rPtr->view->screen->display, rPtr->view->window,
206 gc, xpos, 10, xpos, 20);
209 /* Marker for rest of body
210 _____
212 \./ */
215 xpos = rPtr->margins.body + rPtr->margins.dleft;
216 points[0].x = xpos-5;
217 points[0].y = 14;
218 points[1].x = points[0].x+11;
219 points[1].y = 14;
220 points[2].x = points[0].x+5;
221 points[2].y = 20;
222 XFillPolygon (rPtr->view->screen->display, rPtr->view->window,
223 gc, points, 3, Convex, CoordModeOrigin);
226 if(!rPtr->flags.buttonPressed)
227 return;
229 /* actual_x is used as a shortcut is all... */
230 switch(rPtr->which_marker){
231 case WRulerLeft: actual_x = rPtr->margins.left; break;
232 case WRulerBody:
233 actual_x = rPtr->margins.body + rPtr->margins.dleft;
234 break;
235 case WRulerFirst:
236 actual_x = rPtr->margins.first + rPtr->margins.dleft;
237 break;
238 case WRulerRight: actual_x = rPtr->margins.right; break;
239 default: return;
243 gc = WMColorGC(WMDarkGrayColor(rPtr->view->screen)),
245 XSetLineAttributes(rPtr->view->screen->display, gc, 1,
246 LineOnOffDash, CapNotLast, JoinMiter);
248 XDrawLine(rPtr->pview->screen->display, rPtr->pview->window,
249 gc, actual_x+1, 45, actual_x+1, rPtr->pview->size.height-5);
251 XSetLineAttributes(rPtr->view->screen->display, gc, 1,
252 LineSolid, CapNotLast, JoinMiter);
257 static int
258 whichMarker(Ruler *rPtr, int x, int y)
260 CHECK_CLASS(rPtr, WC_Ruler);
262 if(y>22)
263 return;
265 if(x<0)
266 return;
268 if( rPtr->margins.dleft-x >= -6 && y <= 9 &&
269 rPtr->margins.dleft-x <=0 && y>=4)
270 return WRulerLeft;
272 if( rPtr->margins.right-x >= -1 && y <= 11 &&
273 rPtr->margins.right-x <=5 && y>=4)
274 return WRulerRight;
276 x -= rPtr->margins.dleft;
277 if(x<0)
278 return;
280 if( rPtr->margins.first-x <= 4 && y<=12 &&
281 rPtr->margins.first-x >= -5 && y>=5)
282 return WRulerFirst;
286 if( rPtr->margins.body-x <= 4 && y<=19 &&
287 rPtr->margins.body-x >= -5 && y>=15)
288 return WRulerBody;
291 /* both first and body? */
292 if( rPtr->margins.first-x <= 4 && rPtr->margins.first-x >= -5
293 && rPtr->margins.body-x <= 4 && rPtr->margins.body-x >= -5
294 && y>=13 && y<=14)
295 return WRulerBoth;
297 return -1;
300 static Bool
301 verifyMarkerMove(Ruler *rPtr, int x)
303 if(!rPtr)
304 return;
306 switch(rPtr->which_marker){
307 case WRulerLeft:
308 if(x < rPtr->margins.right-MIN_DOC_WIDTH &&
309 rPtr->margins.body + x <= rPtr->margins.right-MIN_DOC_WIDTH &&
310 rPtr->margins.first + x <= rPtr->margins.right-MIN_DOC_WIDTH)
311 rPtr->margins.left = x;
312 else return False;
313 break;
316 case WRulerBody:
317 if(x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left)
318 rPtr->margins.body = x - rPtr->margins.dleft;
319 else return False;
320 break;
322 case WRulerFirst:
323 if(x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left)
324 rPtr->margins.first = x - rPtr->margins.dleft;
325 else return False;
326 break;
328 case WRulerRight:
329 if( x >= rPtr->margins.first+MIN_DOC_WIDTH &&
330 x >= rPtr->margins.body+MIN_DOC_WIDTH &&
331 x >= rPtr->margins.left+MIN_DOC_WIDTH &&
332 x <= rPtr->end)
333 rPtr->margins.right = x;
334 else return False;
335 break;
337 case WRulerBoth:
338 if( x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left) {
339 rPtr->margins.first = x - rPtr->margins.dleft;
340 rPtr->margins.body = x - rPtr->margins.dleft;
341 } else return False;
342 break;
344 default : return False;
347 return True;
351 static void
352 moveMarker(Ruler *rPtr, int x)
355 CHECK_CLASS(rPtr, WC_Ruler);
357 if(x < rPtr->offset || x > rPtr->end)
358 return;
360 /* restrictive ticks */
362 if(x%10-2 != 0)
363 return;
365 if(!verifyMarkerMove(rPtr, x))
366 return;
368 /* clear around the motion area */
369 #if 0
370 XClearWindow(rPtr->view->screen->display, rPtr->view->window);
371 #else
373 XClearArea(rPtr->view->screen->display, rPtr->view->window,
374 x-10, 3, 20, rPtr->view->size.height-4, False);
376 #endif
378 #if 0
379 { //while(1) {
380 if (QLength(rPtr->view->screen->display) > 0 ) {
381 XEvent * event;
382 while(QLength(rPtr->view->screen->display) > 0 ) {
383 XNextEvent(rPtr->view->screen->display, event);
384 if(event->type == MotionNotify ||
385 event->type == ButtonRelease)
386 break;
390 #endif
394 rPtr->pressed = x;
396 if(rPtr->moveaction)
397 (rPtr->moveaction)(rPtr, rPtr->clientData);
399 paintRuler(rPtr);
402 static void
403 handleEvents(XEvent *event, void *data)
405 Ruler *rPtr = (Ruler*)data;
407 CHECK_CLASS(rPtr, WC_Ruler);
410 switch (event->type) {
412 case Expose:
413 if(event->xexpose.count)
414 break;
415 if(rPtr->view->flags.realized)
416 paintRuler(rPtr);
417 break;
419 case ButtonPress:
420 if(event->xbutton.button != Button1)
421 return;
423 rPtr->flags.buttonPressed = True;
424 rPtr->which_marker =
425 whichMarker(rPtr, event->xmotion.x,
426 event->xmotion.y);
428 #if 0
429 /* clear around the clicked area */
430 if(rPtr->which_marker>=0 && rPtr->which_marker <= 4) {
432 XClearArea(rPtr->view->screen->display, rPtr->view->window,
433 WMGetRulerMargin(rPtr, rPtr->which_marker)-10, 2, 20, 21, True);
435 paintRuler(rPtr);
437 #endif
438 paintRuler(rPtr);
439 break;
441 case ButtonRelease:
442 if(event->xbutton.button != Button1)
443 return;
445 rPtr->flags.buttonPressed = False;
446 rPtr->pressed = event->xmotion.x;
447 if(rPtr->which_marker == 0) {
448 rPtr->margins.dleft = rPtr->margins.left;
450 /* clear entire (moved) ruler */
451 XClearArea(rPtr->view->screen->display,
452 rPtr->view->window, 0, 0, rPtr->view->size.width,
453 rPtr->view->size.height, True);
456 if(rPtr->action)
457 (rPtr->action)(rPtr, rPtr->clientData);
459 paintRuler(rPtr);
460 break;
462 case MotionNotify:
463 if(rPtr->flags.buttonPressed &&
464 (event->xmotion.state & Button1Mask)) {
465 moveMarker(rPtr, event->xmotion.x);
467 break;
473 WMRuler *
474 WMCreateRuler(WMWidget *parent)
476 Ruler *rPtr = wmalloc(sizeof(Ruler));
478 memset(rPtr, 0, sizeof(Ruler));
480 rPtr->widgetClass = WC_Ruler;
482 rPtr->view = W_CreateView(W_VIEW(parent));
483 if (!rPtr->view) {
484 free(rPtr);
485 return NULL;
487 rPtr->view->self = rPtr;
489 W_ResizeView(rPtr->view, DEFAULT_RULER_WIDTH, DEFAULT_RULER_HEIGHT);
491 WMCreateEventHandler(rPtr->view, ExposureMask|StructureNotifyMask
492 |EnterWindowMask|LeaveWindowMask|FocusChangeMask
493 |ButtonReleaseMask|ButtonPressMask|KeyReleaseMask
494 |KeyPressMask|Button1MotionMask, handleEvents, rPtr);
496 rPtr->view->delegate = &_RulerViewDelegate;
498 rPtr->which_marker = WRulerLeft; /* none */
499 rPtr->pressed = 0;
502 rPtr->offset = DEFAULT_X_OFFSET;
503 rPtr->margins.left = DEFAULT_X_OFFSET;
504 rPtr->margins.dleft = DEFAULT_X_OFFSET;
505 rPtr->margins.body = 0; /* relative */
506 rPtr->margins.first = 0; /* relative */
507 rPtr->margins.right = 300+DEFAULT_X_OFFSET;
508 rPtr->end = 320+DEFAULT_X_OFFSET;
510 rPtr->flags.showtabs = False;
511 rPtr->flags.buttonPressed = False;
513 rPtr->pview = W_VIEW(parent);
515 return rPtr;
519 void
520 WMShowRulerTabs(WMRuler *rPtr, Bool show)
522 if(!rPtr)
523 return;
524 CHECK_CLASS(rPtr, WC_Ruler);
525 rPtr->flags.showtabs = show;
528 void
529 WMSetRulerMargin(WMRuler *rPtr, int which, int pixels)
531 if(!rPtr)
532 return;
535 if(pixels<rPtr->offset)
536 pixels = rPtr->offset;
538 rPtr->which_marker = which;
539 if(!verifyMarkerMove(rPtr, pixels))
540 return;
542 rPtr->pressed = pixels;
543 if(which == WRulerLeft)
544 rPtr->margins.dleft = rPtr->margins.left;
546 if(!rPtr->view->flags.realized)
547 return;
549 XClearArea(rPtr->view->screen->display,
550 rPtr->view->window, 0, 0, rPtr->view->size.width,
551 rPtr->view->size.height, True);
553 paintRuler(rPtr);
557 WMGetRulerMargin(WMRuler *rPtr, int which)
559 if(!rPtr)
560 return;
562 CHECK_CLASS(rPtr, WC_Ruler);
564 switch(which) {
565 case WRulerLeft: return rPtr->margins.left; break;
566 case WRulerBody: return rPtr->margins.body; break;
567 case WRulerFirst: return rPtr->margins.first; break;
568 case WRulerRight: return rPtr->margins.right; break;
569 case WRulerBoth: return rPtr->margins.body; break;
570 case WRulerDocLeft: return rPtr->margins.dleft; break;
571 default: return rPtr->margins.dleft;
575 /* _which_ one was released */
576 int
577 WMGetReleasedRulerMargin(WMRuler *rPtr)
579 if(!rPtr)
580 return WRulerLeft;
581 CHECK_CLASS(rPtr, WC_Ruler);
582 if(rPtr->which_marker != -1)
583 return rPtr->which_marker;
584 else
585 return WRulerLeft;
588 /* _which_ one is being grabbed */
589 int
590 WMGetGrabbedRulerMargin(WMRuler *rPtr)
592 if(!rPtr)
593 return WRulerLeft;
594 CHECK_CLASS(rPtr, WC_Ruler);
595 if(rPtr->which_marker != -1)
596 return rPtr->which_marker;
597 else
598 return WRulerLeft;
603 void
604 WMSetRulerOffset(WMRuler *rPtr, int pixels)
606 if(!rPtr || pixels<0)
607 return;
608 CHECK_CLASS(rPtr, WC_Ruler);
609 rPtr->offset = pixels;
612 int
613 WMGetRulerOffset(WMRuler *rPtr)
615 if(!rPtr)
616 return;
617 CHECK_CLASS(rPtr, WC_Ruler);
618 return rPtr->offset;
622 void
623 WMSetRulerAction(WMRuler *rPtr, WMAction *action, void *clientData)
625 if(!rPtr)
626 return;
628 CHECK_CLASS(rPtr, WC_Ruler);
629 rPtr->action = action;
630 rPtr->clientData = clientData;
633 void
634 WMSetRulerMoveAction(WMRuler *rPtr, WMAction *moveaction, void *clientData)
636 if(!rPtr)
637 return;
639 CHECK_CLASS(rPtr, WC_Ruler);
640 rPtr->moveaction = moveaction;
641 rPtr->clientData = clientData;