1 /*
2  *  WINGs WMRuler: nifty ruler widget for WINGs :-)
3  *
4  *  Copyright (c) 1999-2000 Nwanua Elumeze
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21 
22 #include "WINGsP.h"
23 #include "wconfig.h"
24 
25 #define MIN_DOC_WIDTH 10
26 
27 typedef struct W_Ruler {
28 	W_Class widgetClass;
29 	W_View *view;
30 	W_View *pview;		/* the parent's view (for drawing the line) */
31 
32 	WMAction *moveAction;	/* what to when while moving */
33 	WMAction *releaseAction;	/* what to do when released */
34 	void *clientData;
35 
36 	WMColor *fg;
37 	GC fgGC, bgGC;
38 	WMFont *font;
39 	WMRulerMargins margins;
40 	int offset;
41 	int motion;		/* the position of the _moving_ marker(s) */
42 	int end;		/* the last tick on the baseline (restrict markers to it) */
43 
44 	Pixmap drawBuffer;
45 
46 	struct {
47 		unsigned int whichMarker:3;
48 		/*    0,    1,     2,     3,    4,       5,            6 */
49 		/* none, left, right, first, body, tabstop, first & body */
50 
51 		unsigned int buttonPressed:1;
52 		unsigned int redraw:1;
53 		unsigned int RESERVED:27;
54 	} flags;
55 } Ruler;
56 
57 /* Marker for left margin
58 
59    |\
60    | \
61    |__\
62    |
63    |
64 
65  */
drawLeftMarker(Ruler * rPtr)66 static void drawLeftMarker(Ruler * rPtr)
67 {
68 	XPoint points[4];
69 	int xpos = (rPtr->flags.whichMarker == 1 ? rPtr->motion : rPtr->margins.left);
70 
71 	XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 8, xpos, 22);
72 	points[0].x = xpos;
73 	points[0].y = 1;
74 	points[1].x = points[0].x + 6;
75 	points[1].y = 8;
76 	points[2].x = points[0].x + 6;
77 	points[2].y = 9;
78 	points[3].x = points[0].x;
79 	points[3].y = 9;
80 	XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC,
81 		     points, 4, Convex, CoordModeOrigin);
82 }
83 
84 /* Marker for right margin
85 
86   /|
87  / |
88 /__|
89    |
90    |
91 
92  */
drawRightMarker(Ruler * rPtr)93 static void drawRightMarker(Ruler * rPtr)
94 {
95 	XPoint points[4];
96 	int xpos = (rPtr->flags.whichMarker == 2 ? rPtr->motion : rPtr->margins.right);
97 
98 	XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 8, xpos, 22);
99 	points[0].x = xpos + 1;
100 	points[0].y = 0;
101 	points[1].x = points[0].x - 6;
102 	points[1].y = 7;
103 	points[2].x = points[0].x - 6;
104 	points[2].y = 9;
105 	points[3].x = points[0].x;
106 	points[3].y = 9;
107 	XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC,
108 		     points, 4, Convex, CoordModeOrigin);
109 }
110 
111 /* Marker for first line only
112    _____
113    |___|
114    |
115 
116  */
drawFirstMarker(Ruler * rPtr)117 static void drawFirstMarker(Ruler * rPtr)
118 {
119 	int xpos = ((rPtr->flags.whichMarker == 3 || rPtr->flags.whichMarker == 6) ?
120 		    rPtr->motion : rPtr->margins.first);
121 
122 	XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos - 5, 10, 11, 5);
123 	XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 12, xpos, 22);
124 }
125 
126 /* Marker for rest of body
127    _____
128    \   /
129     \./
130  */
drawBodyMarker(Ruler * rPtr)131 static void drawBodyMarker(Ruler * rPtr)
132 {
133 	XPoint points[4];
134 	int xpos = ((rPtr->flags.whichMarker == 4 || rPtr->flags.whichMarker == 6) ?
135 		    rPtr->motion : rPtr->margins.body);
136 
137 	points[0].x = xpos - 5;
138 	points[0].y = 16;
139 	points[1].x = points[0].x + 11;
140 	points[1].y = 16;
141 	points[2].x = points[0].x + 5;
142 	points[2].y = 22;
143 	XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC,
144 		     points, 3, Convex, CoordModeOrigin);
145 }
146 
createDrawBuffer(Ruler * rPtr)147 static void createDrawBuffer(Ruler * rPtr)
148 {
149 	if (!rPtr->view->flags.realized)
150 		return;
151 
152 	if (rPtr->drawBuffer)
153 		XFreePixmap(rPtr->view->screen->display, rPtr->drawBuffer);
154 
155 	rPtr->drawBuffer = XCreatePixmap(rPtr->view->screen->display,
156 					 rPtr->view->window, rPtr->view->size.width, 40,
157 					 rPtr->view->screen->depth);
158 	XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer,
159 		       rPtr->bgGC, 0, 0, rPtr->view->size.width, 40);
160 }
161 
drawRulerOnPixmap(Ruler * rPtr)162 static void drawRulerOnPixmap(Ruler * rPtr)
163 {
164 	int i, j, w, m;
165 	char c[3];
166 	int marks[9] = { 11, 3, 5, 3, 7, 3, 5, 3 };
167 
168 	if (!rPtr->drawBuffer || !rPtr->view->flags.realized)
169 		return;
170 
171 	XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer,
172 		       rPtr->bgGC, 0, 0, rPtr->view->size.width, 40);
173 
174 	WMDrawString(rPtr->view->screen, rPtr->drawBuffer, rPtr->fg,
175 		     rPtr->font, rPtr->margins.left + 2, 26, _("0   inches"), 10);
176 
177 	/* marker ticks */
178 	i = j = m = 0;
179 	w = rPtr->view->size.width - rPtr->margins.left;
180 	while (m < w) {
181 		XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer,
182 			  rPtr->fgGC, rPtr->margins.left + m, 23, rPtr->margins.left + m, marks[i % 8] + 23);
183 		if (i != 0 && i % 8 == 0) {
184 			snprintf(c, sizeof(c), "%hu", ++j);
185 			WMDrawString(rPtr->view->screen, rPtr->drawBuffer, rPtr->fg,
186 				     rPtr->font, rPtr->margins.left + 2 + m, 26, c, 2);
187 		}
188 		m = (++i) * 10;
189 	}
190 
191 	rPtr->end = rPtr->margins.left + m - 10;
192 	if (rPtr->margins.right > rPtr->end)
193 		rPtr->margins.right = rPtr->end;
194 	/* base line */
195 	XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC,
196 		  rPtr->margins.left, 22, rPtr->margins.left + m - 10, 22);
197 
198 	drawLeftMarker(rPtr);
199 	drawRightMarker(rPtr);
200 	drawFirstMarker(rPtr);
201 	drawBodyMarker(rPtr);
202 
203 	rPtr->flags.redraw = False;
204 }
205 
paintRuler(Ruler * rPtr)206 static void paintRuler(Ruler * rPtr)
207 {
208 	if (!rPtr->drawBuffer || !rPtr->view->flags.realized)
209 		return;
210 
211 	if (rPtr->flags.redraw)
212 		drawRulerOnPixmap(rPtr);
213 	XCopyArea(rPtr->view->screen->display, rPtr->drawBuffer,
214 		  rPtr->view->window, rPtr->bgGC, 0, 0, rPtr->view->size.width, 40, 0, 0);
215 }
216 
verifyMarkerMove(Ruler * rPtr,int x)217 static Bool verifyMarkerMove(Ruler * rPtr, int x)
218 {
219 	if (rPtr->flags.whichMarker < 1 || rPtr->flags.whichMarker > 6)
220 		return False;
221 
222 	switch (rPtr->flags.whichMarker) {
223 	case 1:
224 		if (x > rPtr->margins.right - 10 || x < rPtr->offset ||
225 		    rPtr->margins.body + x > rPtr->margins.right - MIN_DOC_WIDTH ||
226 		    rPtr->margins.first + x > rPtr->margins.right - MIN_DOC_WIDTH)
227 			return False;
228 		break;
229 
230 	case 2:
231 		if (x < rPtr->margins.first + MIN_DOC_WIDTH || x < rPtr->margins.body + MIN_DOC_WIDTH || x < rPtr->margins.left + MIN_DOC_WIDTH || x > rPtr->end)	/*rPtr->view->size.width) */
232 			return False;
233 		break;
234 
235 	case 3:
236 		if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left)
237 			return False;
238 		break;
239 
240 	case 4:
241 		if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left)
242 			return False;
243 		break;
244 
245 	case 6:
246 		if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left)
247 			return False;
248 		break;
249 
250 	default:
251 		return False;
252 	}
253 
254 	rPtr->motion = x;
255 	return True;
256 }
257 
whichMarker(Ruler * rPtr,int x,int y)258 static int whichMarker(Ruler * rPtr, int x, int y)
259 {
260 	if (x < rPtr->offset || y > 22)
261 		return 0;
262 
263 	if (rPtr->margins.left - x >= -6 && y <= 9 && (rPtr->margins.left - x <= 0) && y >= 4) {
264 		rPtr->motion = rPtr->margins.left;
265 		return 1;
266 	}
267 	if (rPtr->margins.right - x >= -1 && y <= 11 && rPtr->margins.right - x <= 5 && y >= 4) {
268 		rPtr->motion = rPtr->margins.right;
269 		return 2;
270 	}
271 #if 0
272 	/* both first and body? */
273 	if (rPtr->margins.first - x <= 4 && rPtr->margins.first - x >= -5
274 	    && rPtr->margins.body - x <= 4 && rPtr->margins.body - x >= -5 && y >= 15 && y <= 17) {
275 		rPtr->motion = rPtr->margins.first;
276 		return 6;
277 	}
278 #endif
279 
280 	if (rPtr->margins.first - x <= 4 && y <= 15 && rPtr->margins.first - x >= -5 && y >= 10) {
281 		rPtr->motion = rPtr->margins.first;
282 		return 3;
283 	}
284 	if (rPtr->margins.body - x <= 4 && y <= 21 && rPtr->margins.body - x >= -5 && y >= 17) {
285 		rPtr->motion = rPtr->margins.body;
286 		return 4;
287 	}
288 	/* do tabs (5) */
289 
290 	return 0;
291 }
292 
rulerDidResize(W_ViewDelegate * self,WMView * view)293 static void rulerDidResize(W_ViewDelegate * self, WMView * view)
294 {
295 	Ruler *rPtr = (Ruler *) view->self;
296 
297 	/* Parameter not used, but tell the compiler that it is ok */
298 	(void) self;
299 
300 	createDrawBuffer(rPtr);
301 	rPtr->flags.redraw = True;
302 	paintRuler(rPtr);
303 
304 }
305 
handleEvents(XEvent * event,void * data)306 static void handleEvents(XEvent * event, void *data)
307 {
308 	Ruler *rPtr = (Ruler *) data;
309 
310 	switch (event->type) {
311 	case Expose:
312 		rulerDidResize(rPtr->view->delegate, rPtr->view);
313 		break;
314 
315 	case MotionNotify:
316 		if (rPtr->flags.buttonPressed && (event->xmotion.state & Button1Mask)) {
317 			if (verifyMarkerMove(rPtr, event->xmotion.x)) {
318 				GC gc = WMColorGC(WMDarkGrayColor(rPtr->view->screen));
319 
320 				if (rPtr->moveAction)
321 					(rPtr->moveAction) (rPtr, rPtr->clientData);
322 				rPtr->flags.redraw = True;
323 				paintRuler(rPtr);
324 				XSetLineAttributes(rPtr->view->screen->display, gc, 1,
325 						   LineSolid, CapNotLast, JoinMiter);
326 				XDrawLine(rPtr->pview->screen->display,
327 					  rPtr->pview->window,
328 					  gc, rPtr->motion + 1, 40,
329 					  rPtr->motion + 1, rPtr->pview->size.height - 5);
330 			}
331 		}
332 		break;
333 
334 	case ButtonPress:
335 		if (event->xbutton.button != Button1)
336 			return;
337 		rPtr->flags.buttonPressed = True;
338 		rPtr->flags.whichMarker = whichMarker(rPtr, event->xmotion.x, event->xmotion.y);
339 		break;
340 
341 	case ButtonRelease:
342 		if (event->xbutton.button != Button1)
343 			return;
344 		rPtr->flags.buttonPressed = False;
345 		switch (rPtr->flags.whichMarker) {
346 		case 1:{
347 				int change = rPtr->margins.left - rPtr->motion;
348 
349 				rPtr->margins.first -= change;
350 				rPtr->margins.body -= change;
351 				rPtr->margins.left = rPtr->motion;
352 				rPtr->flags.redraw = True;
353 				paintRuler(rPtr);
354 				break;
355 			}
356 		case 2:
357 			rPtr->margins.right = rPtr->motion;
358 			break;
359 		case 3:
360 			rPtr->margins.first = rPtr->motion;
361 			break;
362 		case 4:
363 			rPtr->margins.body = rPtr->motion;
364 			break;
365 		case 6:
366 			rPtr->margins.first = rPtr->margins.body = rPtr->motion;
367 			break;
368 		}
369 		if (rPtr->releaseAction)
370 			(rPtr->releaseAction) (rPtr, rPtr->clientData);
371 		break;
372 	}
373 }
374 
375 W_ViewDelegate _RulerViewDelegate = {
376 	NULL,
377 	NULL,
378 	rulerDidResize,
379 	NULL,
380 	NULL
381 };
382 
WMCreateRuler(WMWidget * parent)383 WMRuler *WMCreateRuler(WMWidget * parent)
384 {
385 	Ruler *rPtr = wmalloc(sizeof(Ruler));
386 	unsigned int w = WMWidgetWidth(parent);
387 
388 	rPtr->widgetClass = WC_Ruler;
389 
390 	rPtr->view = W_CreateView(W_VIEW(parent));
391 
392 	if (!rPtr->view) {
393 		wfree(rPtr);
394 		return NULL;
395 	}
396 
397 	rPtr->view->self = rPtr;
398 
399 	rPtr->drawBuffer = (Pixmap) NULL;
400 
401 	W_ResizeView(rPtr->view, w, 40);
402 
403 	WMCreateEventHandler(rPtr->view, ExposureMask | StructureNotifyMask
404 			     | EnterWindowMask | LeaveWindowMask | FocusChangeMask
405 			     | ButtonReleaseMask | ButtonPressMask | KeyReleaseMask
406 			     | KeyPressMask | Button1MotionMask, handleEvents, rPtr);
407 
408 	rPtr->view->delegate = &_RulerViewDelegate;
409 
410 	rPtr->fg = WMBlackColor(rPtr->view->screen);
411 	rPtr->fgGC = WMColorGC(rPtr->fg);
412 	rPtr->bgGC = WMColorGC(WMGrayColor(rPtr->view->screen));
413 	rPtr->font = WMSystemFontOfSize(rPtr->view->screen, 8);
414 
415 	rPtr->offset = 22;
416 	rPtr->margins.left = 22;
417 	rPtr->margins.body = 22;
418 	rPtr->margins.first = 42;
419 	rPtr->margins.right = (w < 502 ? w : 502);
420 	rPtr->margins.tabs = NULL;
421 
422 	rPtr->flags.whichMarker = 0;	/* none */
423 	rPtr->flags.buttonPressed = False;
424 	rPtr->flags.redraw = True;
425 
426 	rPtr->moveAction = NULL;
427 	rPtr->releaseAction = NULL;
428 
429 	rPtr->pview = W_VIEW(parent);
430 
431 	return rPtr;
432 }
433 
WMSetRulerMargins(WMRuler * rPtr,WMRulerMargins margins)434 void WMSetRulerMargins(WMRuler * rPtr, WMRulerMargins margins)
435 {
436 	if (!rPtr)
437 		return;
438 	rPtr->margins.left = margins.left + rPtr->offset;
439 	rPtr->margins.right = margins.right + rPtr->offset;
440 	rPtr->margins.first = margins.first + rPtr->offset;
441 	rPtr->margins.body = margins.body + rPtr->offset;
442 	rPtr->margins.tabs = margins.tabs;	/*for loop */
443 	rPtr->flags.redraw = True;
444 	paintRuler(rPtr);
445 
446 }
447 
WMGetRulerMargins(WMRuler * rPtr)448 WMRulerMargins *WMGetRulerMargins(WMRuler * rPtr)
449 {
450 	WMRulerMargins *margins = wmalloc(sizeof(WMRulerMargins));
451 
452 	if (!rPtr) {
453 		margins->first = margins->body = margins->left = 0;
454 		margins->right = 100;
455 		return margins;
456 	}
457 
458 	margins->left = rPtr->margins.left - rPtr->offset;
459 	margins->right = rPtr->margins.right - rPtr->offset;
460 	margins->first = rPtr->margins.first - rPtr->offset;
461 	margins->body = rPtr->margins.body - rPtr->offset;
462 	/*for */
463 	margins->tabs = rPtr->margins.tabs;
464 
465 	return margins;
466 }
467 
WMIsMarginEqualToMargin(WMRulerMargins * aMargin,WMRulerMargins * anotherMargin)468 Bool WMIsMarginEqualToMargin(WMRulerMargins * aMargin, WMRulerMargins * anotherMargin)
469 {
470 	if (aMargin == anotherMargin)
471 		return True;
472 	else if (!aMargin || !anotherMargin)
473 		return False;
474 	if (aMargin->left != anotherMargin->left)
475 		return False;
476 	if (aMargin->first != anotherMargin->first)
477 		return False;
478 	if (aMargin->body != anotherMargin->body)
479 		return False;
480 	if (aMargin->right != anotherMargin->right)
481 		return False;
482 
483 	return True;
484 }
485 
WMSetRulerOffset(WMRuler * rPtr,int pixels)486 void WMSetRulerOffset(WMRuler * rPtr, int pixels)
487 {
488 	if (!rPtr || pixels < 0 || pixels + MIN_DOC_WIDTH >= rPtr->view->size.width)
489 		return;
490 	rPtr->offset = pixels;
491 	/*rulerDidResize(rPtr, rPtr->view); */
492 }
493 
WMGetRulerOffset(WMRuler * rPtr)494 int WMGetRulerOffset(WMRuler * rPtr)
495 {
496 	if (!rPtr)
497 		return 0;	/* what value should return if no ruler? -1 or 0? */
498 	return rPtr->offset;
499 }
500 
WMSetRulerReleaseAction(WMRuler * rPtr,WMAction * action,void * clientData)501 void WMSetRulerReleaseAction(WMRuler * rPtr, WMAction * action, void *clientData)
502 {
503 	if (!rPtr)
504 		return;
505 
506 	rPtr->releaseAction = action;
507 	rPtr->clientData = clientData;
508 }
509 
WMSetRulerMoveAction(WMRuler * rPtr,WMAction * action,void * clientData)510 void WMSetRulerMoveAction(WMRuler * rPtr, WMAction * action, void *clientData)
511 {
512 	if (!rPtr)
513 		return;
514 
515 	rPtr->moveAction = action;
516 	rPtr->clientData = clientData;
517 }
518 
519 /* _which_ one was released */
WMGetReleasedRulerMargin(WMRuler * rPtr)520 int WMGetReleasedRulerMargin(WMRuler * rPtr)
521 {
522 	if (!rPtr)
523 		return 0;
524 	return rPtr->flags.whichMarker;
525 }
526 
527 /* _which_ one is being grabbed */
WMGetGrabbedRulerMargin(WMRuler * rPtr)528 int WMGetGrabbedRulerMargin(WMRuler * rPtr)
529 {
530 	if (!rPtr)
531 		return 0;
532 	return rPtr->flags.whichMarker;
533 }
534