Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 
      3 Copyright 1989, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included
     12 in all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     17 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 OTHER DEALINGS IN THE SOFTWARE.
     21 
     22 Except as contained in this notice, the name of The Open Group shall
     23 not be used in advertising or otherwise to promote the sale, use or
     24 other dealings in this Software without prior written authorization
     25 from The Open Group.
     26 
     27 */
     28 
     29 /*
     30  * Author:  Davor Matic, MIT X Consortium
     31  */
     32 
     33 #ifdef HAVE_CONFIG_H
     34 #include "config.h"
     35 #endif
     36 
     37 #include <stdio.h>
     38 #include <ctype.h>
     39 #include <math.h>
     40 #include <stdlib.h>
     41 
     42 #include <X11/IntrinsicP.h>
     43 #include <X11/StringDefs.h>
     44 #include <X11/Xos.h>
     45 #include <X11/Xaw/XawInit.h>
     46 
     47 #include "CutPaste.h"
     48 #include "ScaleP.h"
     49 
     50 #ifdef HAVE_LRINT
     51 #define myrint(x) lrint(x)
     52 #else
     53 #define myrint(x) floor(x + 0.5)
     54 #endif
     55 
     56 #define streq(a,b) (strcmp( (a), (b) ) == 0)
     57 #ifndef min
     58 #define min(x, y) ((x) > (y) ? (y) : (x))
     59 #endif
     60 #ifndef max
     61 #define max(x, y) ((x) < (y) ? (y) : (x))
     62 #endif
     63 
     64 #define DefaultBufferSize 1024
     65 #define DefaultScaleFactor NULL
     66 
     67 #define Offset(field) XtOffsetOf(ScaleRec, scale.field)
     68 
     69 static XtResource resources[] = {
     70 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
     71      Offset(foreground_pixel), XtRString, (XtPointer) XtDefaultForeground},
     72 {XtNgravity, XtCGravity, XtRGravity, sizeof(XtGravity),
     73      Offset(gravity), XtRImmediate, (XtPointer) "ForgetGravity"},
     74 {XtNinternalWidth, XtCWidth, XtRDimension,  sizeof(Dimension),
     75      Offset(internal_width), XtRImmediate, (XtPointer) 2},
     76 {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension),
     77      Offset(internal_height), XtRImmediate, (XtPointer) 2},
     78 {XtNresize, XtCResize, XtRBoolean, sizeof(Boolean),
     79      Offset(resize), XtRImmediate, (XtPointer) True},
     80 {XtNautoscale, XtCAutoscale, XtRBoolean, sizeof(Boolean),
     81      Offset(autoscale), XtRImmediate, (XtPointer) True},
     82 {XtNproportional, XtCProportional, XtRBoolean, sizeof(Boolean),
     83      Offset(proportional), XtRImmediate, (XtPointer) True},
     84 {XtNscaleX, XtCScaleFactor, XtRString, sizeof(String),
     85      Offset(scale_x_str), XtRImmediate, (XtPointer) DefaultScaleFactor},
     86 {XtNscaleY, XtCScaleFactor, XtRString, sizeof(String),
     87      Offset(scale_y_str), XtRImmediate, (XtPointer) DefaultScaleFactor},
     88 {XtNaspectRatio, XtCAspectRatio, XtRString, sizeof(String),
     89      Offset(aspect_ratio_str), XtRImmediate, (XtPointer) "1.0"},
     90 {XtNprecision, XtCPrecision, XtRString, sizeof(String),
     91      Offset(precision_str), XtRImmediate, (XtPointer) "0.001"},
     92 {XtNimage, XtCImage, XtRImage, sizeof(XImage*),
     93      Offset(image), XtRImmediate, (XtPointer) NULL},
     94 {XtNpasteBuffer, XtCPasteBuffer, XtRBoolean, sizeof(Boolean),
     95      Offset(paste_buffer), XtRImmediate, (XtPointer) False},
     96 {XtNbufferSize, XtCBufferSize, XtRCardinal, sizeof(Cardinal),
     97      Offset(buffer_size), XtRImmediate, (XtPointer) DefaultBufferSize},
     98 {XtNuserData, XtCuserData, XtRuserData, sizeof(XtPointer),
     99      Offset(userData), XtRImmediate, (XtPointer) NULL},
    100 { XtNvisual, XtCvisual, XtRVisual, sizeof(Visual*),
    101      Offset(visual), XtRImmediate, CopyFromParent}
    102 };
    103 
    104 #undef Offset
    105 
    106 static void ClassInitialize ( void );
    107 static void GetGC ( ScaleWidget sw );
    108 static void GetInitialScaleValues ( ScaleWidget sw );
    109 static void GetRectangleBuffer ( ScaleWidget sw, Cardinal buffer_size );
    110 static void Initialize ( Widget request, Widget new, ArgList args,
    111 			 Cardinal *num_args );
    112 static void BuildTable ( ScaleWidget sw );
    113 static void FlushRectangles ( ScaleWidget sw, Drawable drawable, GC gc );
    114 static void FillRectangle ( ScaleWidget sw, Drawable drawable, GC gc,
    115 			    Position x, Position y,
    116 			    Dimension width, Dimension height );
    117 static void ScaleImage ( ScaleWidget sw, Drawable drawable,
    118 			 Position img_x, Position img_y,
    119 			 Position dst_x, Position dst_y,
    120 			 Dimension img_width, Dimension img_height );
    121 static int FindPixel ( ScaleWidget sw, Position x, Position y,
    122 		       Position *img_x, Position *img_y, Pixel *img_pixel );
    123 static void Redisplay ( Widget w, XEvent *event, Region region );
    124 static void TryResize ( ScaleWidget sw );
    125 static void Precision ( ScaleWidget sw );
    126 static void Proportional ( ScaleWidget sw );
    127 static void GetScaledSize ( ScaleWidget sw );
    128 static void GetScaleValues ( ScaleWidget sw );
    129 static void Unscale ( ScaleWidget sw );
    130 static void Autoscale ( ScaleWidget sw );
    131 static void PositionImage ( ScaleWidget sw );
    132 static void Resize ( Widget w );
    133 static void Realize ( Widget wid, Mask *vmask, XSetWindowAttributes *attr );
    134 static void Destroy ( Widget w );
    135 static Boolean SetValues ( Widget current, Widget request, Widget new,
    136 			   ArgList args, Cardinal *num_args );
    137 
    138 
    139 
    140 static XtActionsRec actions[] =
    141 {
    142 {"unscale", SWUnscale},
    143 {"autoscale", SWAutoscale},
    144 {"initial-size", SWInitialSize},
    145 {"paste", RequestSelection},
    146 {"cut", GrabSelection}
    147 };
    148 
    149 static char translations[] =
    150 "\
    151  <Key>u:           unscale()\n\
    152  <Key>a:           autoscale()\n\
    153  <Key>i:           initial-size()\n\
    154 ";
    155 
    156 ScaleClassRec scaleClassRec = {
    157 {   /* core fields */
    158     /* superclass		*/	(WidgetClass) &simpleClassRec,
    159     /* class_name		*/	"Scale",
    160     /* widget_size		*/	sizeof(ScaleRec),
    161     /* class_initialize		*/	ClassInitialize,
    162     /* class_part_initialize	*/	NULL,
    163     /* class_inited		*/	FALSE,
    164     /* initialize		*/	Initialize,
    165     /* initialize_hook		*/	NULL,
    166     /* realize			*/	Realize,
    167     /* actions			*/	actions,
    168     /* num_actions		*/	XtNumber(actions),
    169     /* resources		*/	resources,
    170     /* num_resources		*/	XtNumber(resources),
    171     /* xrm_class		*/	NULLQUARK,
    172     /* compress_motion		*/	TRUE,
    173     /* compress_exposure	*/      XtExposeCompressMaximal|
    174                                         XtExposeGraphicsExposeMerged,
    175     /* compress_enterleave	*/	TRUE,
    176     /* visible_interest		*/	TRUE,
    177     /* destroy			*/	Destroy,
    178     /* resize			*/	Resize,
    179     /* expose			*/	Redisplay,
    180     /* set_values		*/	SetValues,
    181     /* set_values_hook		*/	NULL,
    182     /* set_values_almost	*/	XtInheritSetValuesAlmost,
    183     /* get_values_hook		*/	NULL,
    184     /* accept_focus		*/	NULL,
    185     /* version			*/	XtVersion,
    186     /* callback_private		*/	NULL,
    187     /* tm_table			*/      translations,
    188     /* query_geometry		*/	XtInheritQueryGeometry,
    189     /* display_accelerator	*/	XtInheritDisplayAccelerator,
    190     /* extension		*/	NULL
    191   },
    192   {
    193     /* change_sensitive		*/	XtInheritChangeSensitive,
    194   }
    195 };
    196 
    197 WidgetClass scaleWidgetClass = (WidgetClass) &scaleClassRec;
    198 
    199 
    200 
    202 /*
    203  * Private Procedures
    204  */
    205 
    206 
    207 static void
    208 ClassInitialize(void)
    209 {
    210 }
    211 
    212 
    213 
    215 static void
    216 GetGC(ScaleWidget sw)
    217 {
    218     XGCValues values;
    219 
    220     values.foreground = sw->scale.foreground_pixel;
    221     values.background = sw->core.background_pixel;
    222     values.function = GXcopy;
    223 
    224     sw->scale.gc = XtGetGC((Widget) sw,
    225 			   GCForeground |
    226 			   GCBackground |
    227 			   GCFunction,
    228 			   &values);
    229 }
    230 
    231 
    232 
    234 
    235 static void
    236 GetInitialScaleValues(ScaleWidget sw)
    237 {
    238     if (sw->scale.proportional) {
    239 	sw->scale.scale_x = sw->scale.scale_y =
    240 	    ((sw->scale.aspect_ratio > 1.0) ?
    241 	     sw->scale.aspect_ratio : 1.0 / sw->scale.aspect_ratio) *
    242 		 (sw->scale.precision > 1.0 ?
    243 		  sw->scale.precision : 1.0);
    244 	Proportional(sw); /* need to cut them down to proper values */
    245     }
    246     else
    247 	sw->scale.scale_x = sw->scale.scale_y = 1.0;
    248 }
    249 
    250 
    251 
    253 static void
    254 GetRectangleBuffer(ScaleWidget sw, Cardinal buffer_size)
    255     /*
    256      * This procedure will realloc a new rectangles buffer.
    257      * If the new buffer size is less than nrectangles, some
    258      * information will be lost.
    259      */
    260 {
    261     if (buffer_size == 0) {
    262 	buffer_size = DefaultBufferSize;
    263 	XtWarning("buffer size has to be a positive number greater than zero");
    264     }
    265     sw->scale.rectangles = (XRectangle *)
    266 	XtRealloc((char *) sw->scale.rectangles,
    267 		  buffer_size * sizeof(XRectangle));
    268     sw->scale.buffer_size = buffer_size;
    269 }
    270 
    271 
    272 
    274 static void
    275 Initialize(_X_UNUSED Widget request, Widget new,
    276            _X_UNUSED ArgList args, _X_UNUSED Cardinal *num_args)
    277 {
    278     ScaleWidget new_sw = (ScaleWidget) new;
    279 
    280     new_sw->scale.table.x = (Position *) NULL;
    281     new_sw->scale.table.y = (Position *) NULL;
    282     new_sw->scale.table.width = (Dimension *) NULL;
    283     new_sw->scale.table.height = (Dimension *) NULL;
    284 
    285     new_sw->scale.nrectangles = 0;
    286     new_sw->scale.rectangles = (XRectangle *) NULL;
    287 
    288     GetRectangleBuffer(new_sw, new_sw->scale.buffer_size);
    289 
    290     GetGC(new_sw);
    291 
    292     if (new_sw->scale.image != NULL) {
    293 	if (new_sw->core.width == 0)
    294 	    new_sw->core.width =
    295 		new_sw->scale.image->width + 2 * new_sw->scale.internal_width;
    296 	if (new_sw->core.height == 0)
    297 	    new_sw->core.height =
    298 		new_sw->scale.image->height + 2 *new_sw->scale.internal_height;
    299     }
    300     else {
    301 	if (new_sw->core.width == 0)
    302 	    new_sw->core.width = 1 + 2 * new_sw->scale.internal_width;
    303 	if (new_sw->core.height == 0)
    304 	    new_sw->core.height = 1 + 2 * new_sw->scale.internal_height;
    305 	new_sw->scale.image = XCreateImage(XtDisplay(new),
    306 				       DefaultVisual(XtDisplay(new),
    307 					        DefaultScreen(XtDisplay(new))),
    308 				       1, XYBitmap, 0,
    309 				       XtCalloc(1, sizeof(char)),
    310 				       1, 1, 8, 0);
    311     }
    312 
    313     if ((new_sw->scale.aspect_ratio =
    314 	 atof(new_sw->scale.aspect_ratio_str)) < 0.0) {
    315 	new_sw->scale.aspect_ratio = 1.0;
    316 	XtWarning("AspectRatio has to be a positive number. (forced to 1.0)");
    317     }
    318 
    319     if ((new_sw->scale.precision =
    320          atof(new_sw->scale.precision_str)) < 0.0) {
    321 	new_sw->scale.precision = 0.001;
    322 	XtWarning("Precision has to be a positive number. (forced to 0.001)");
    323     }
    324 
    325     if (new_sw->scale.scale_x_str == DefaultScaleFactor
    326 	||
    327 	new_sw->scale.scale_y_str == DefaultScaleFactor)
    328 	GetInitialScaleValues(new_sw);
    329     else {
    330 	if ((new_sw->scale.scale_x =
    331 	     atof(new_sw->scale.scale_x_str)) < 0.0) {
    332 	    new_sw->scale.scale_x = 1.0;
    333 	    XtWarning("ScaleValue has to be a positive number. (forced to 1.0)");
    334 	}
    335 	if ((new_sw->scale.scale_y =
    336 	     atof(new_sw->scale.scale_y_str)) < 0.0) {
    337 	    new_sw->scale.scale_y = 1.0;
    338 	    XtWarning("ScaleValue has to be a positive number. (forced to 1.0)");
    339 	}
    340     }
    341 }
    342 
    343 
    344 
    346 static void
    347 BuildTable(ScaleWidget sw)
    348     /*
    349      * This procedure builds scaling table for image in the scale struct
    350      * Requires image, scale_x and scale_y to be set properly
    351      */
    352 {
    353     Position x, y;
    354 
    355     XtFree((char *) sw->scale.table.x);
    356     XtFree((char *) sw->scale.table.y);
    357     XtFree((char *) sw->scale.table.width);
    358     XtFree((char *) sw->scale.table.height);
    359     sw->scale.table.x =
    360 	(Position *) XtMalloc(sizeof(Position) * sw->scale.image->width);
    361     sw->scale.table.y =
    362 	(Position *) XtMalloc(sizeof(Position) * sw->scale.image->height);
    363     sw->scale.table.width =
    364 	(Dimension *) XtMalloc(sizeof(Dimension) * sw->scale.image->width);
    365     sw->scale.table.height =
    366 	(Dimension *) XtMalloc(sizeof(Dimension) * sw->scale.image->height);
    367 
    368     /* Build the scaling table */
    369     for (x = 0; x < sw->scale.image->width; x++) {
    370 	sw->scale.table.x[(int) x] = (Position) myrint(sw->scale.scale_x * x);
    371 	sw->scale.table.width[(int) x] = (Dimension)
    372 	    myrint(sw->scale.scale_x *(x + 1)) - myrint(sw->scale.scale_x * x);
    373     }
    374     for (y = 0; y < sw->scale.image->height; y++) {
    375 	sw->scale.table.y[(int) y] = (Position) myrint(sw->scale.scale_y * y);
    376 	sw->scale.table.height[(int) y] = (Dimension)
    377 	    myrint(sw->scale.scale_y *(y + 1)) - myrint(sw->scale.scale_y * y);
    378     }
    379 }
    380 
    381 
    382 
    384 static void
    385 FlushRectangles(ScaleWidget sw, Drawable drawable, GC gc)
    386 {
    387     XFillRectangles(XtDisplay(sw), drawable, gc,
    388 		    sw->scale.rectangles, sw->scale.nrectangles);
    389 
    390     sw->scale.nrectangles = 0;
    391 }
    392 
    393 
    394 
    396 static void
    397 FillRectangle(ScaleWidget sw, Drawable drawable, GC gc,
    398 	      Position x, Position y, Dimension width, Dimension height)
    399 {
    400 
    401     if (sw->scale.nrectangles == sw->scale.buffer_size)
    402 	FlushRectangles(sw, drawable, gc);
    403 
    404     sw->scale.rectangles[(int) sw->scale.nrectangles].x = x;
    405     sw->scale.rectangles[(int) sw->scale.nrectangles].y = y;
    406     sw->scale.rectangles[(int) sw->scale.nrectangles].width = width;
    407     sw->scale.rectangles[(int) sw->scale.nrectangles].height = height;
    408 
    409     ++sw->scale.nrectangles;
    410 }
    411 
    412 
    413 
    415 static void
    416 ScaleImage(ScaleWidget sw, Drawable drawable, Position img_x, Position img_y,
    417 	   Position dst_x, Position dst_y,
    418 	   Dimension img_width, Dimension img_height)
    419     /*
    420      * This procedure scales image into the specified drawable
    421      * It assumes scaling table was already built
    422      */
    423 {
    424     GC gc;
    425     XGCValues gcv;
    426     Position x, y;
    427     Pixel pixel;
    428 
    429     /* Clip the img coordinates */
    430     img_x = min(max(img_x, 0), (Position) sw->scale.image->width - 1);
    431     img_y = min(max(img_y, 0), (Position) sw->scale.image->height - 1);
    432     img_width =
    433       min(img_width, (Dimension)(sw->scale.image->width - (Dimension)img_x));
    434     img_height =
    435       min(img_height, (Dimension)(sw->scale.image->height - (Dimension)img_y));
    436 
    437     if (sw->scale.scale_x == 1.0 && sw->scale.scale_y == 1.0)
    438 	XPutImage(XtDisplay(sw), drawable, sw->scale.gc, sw->scale.image,
    439 		  img_x, img_y, dst_x, dst_y,
    440 		  img_width, img_height);
    441     else {
    442 	dst_x = dst_x - sw->scale.table.x[(int) img_x];
    443 	dst_y = dst_y - sw->scale.table.y[(int) img_y];
    444 
    445 	gc = XCreateGC(XtDisplay(sw), drawable, 0, NULL);
    446 
    447 	gcv.function = GXcopy;
    448 	XChangeGC(XtDisplay(sw), gc, GCFunction, &gcv);
    449 
    450 	/* make sure gc knows the right background */
    451 	gcv.background = sw->core.background_pixel;
    452 	XChangeGC(XtDisplay(sw), gc, GCBackground, &gcv);
    453 
    454 	/* Set the background of drawable.  If the most frequent color
    455 	   is the background color it can speed up scaling process. */
    456 	gcv.foreground = gcv.background;
    457 	XChangeGC(XtDisplay(sw), gc, GCForeground, &gcv);
    458 	XFillRectangle(XtDisplay(sw), drawable, gc,
    459 		       sw->scale.table.x[(int) img_x] + dst_x,
    460 		       sw->scale.table.y[(int) img_y] + dst_y,
    461 		       sw->scale.table.x[(int) img_x + img_width - 1] -
    462 		       sw->scale.table.x[(int) img_x],
    463 		       sw->scale.table.y[(int) img_y + img_height - 1] -
    464 		       sw->scale.table.y[(int) img_y]);
    465 
    466 	if (sw->scale.image->format == XYBitmap) {
    467 	    for (x = img_x; x < img_x + (Position)img_width; x++)
    468 		for (y = img_y; y < img_y + (Position)img_height; y++) {
    469 		    pixel = XGetPixel(sw->scale.image, x, y);
    470 		    if (pixel) /* Do not draw background */
    471 			FillRectangle(sw, drawable, sw->scale.gc,
    472 				      sw->scale.table.x[(int) x] + dst_x,
    473 				      sw->scale.table.y[(int) y] + dst_y,
    474 				      sw->scale.table.width[(int) x],
    475 				      sw->scale.table.height[(int) y]);
    476 		}
    477 	    FlushRectangles(sw, drawable, sw->scale.gc);
    478 	}
    479 	else {
    480 	    for (x = img_x; x < img_x + (Position)img_width; x++)
    481 		for (y = img_y; y < img_y + (Position)img_height; y++) {
    482 		    pixel = XGetPixel(sw->scale.image, x, y);
    483 		    if (pixel != gcv.background) { /* Do not draw background */
    484 			if (gcv.foreground != pixel) { /* Change fg to pixel */
    485 			    gcv.foreground = pixel;
    486 			    XChangeGC(XtDisplay(sw), gc, GCForeground, &gcv);
    487 			}
    488 			XFillRectangle(XtDisplay(sw), drawable, gc,
    489 				       sw->scale.table.x[(int) x] + dst_x,
    490 				       sw->scale.table.y[(int) y] + dst_y,
    491 				       sw->scale.table.width[(int) x],
    492 				       sw->scale.table.height[(int) y]);
    493 		    }
    494 		}
    495 	}
    496 	XFreeGC(XtDisplay(sw), gc);
    497     }
    498 }
    499 
    500 
    501 
    503 static int
    504 FindPixel(ScaleWidget sw, Position x, Position y,
    505 	  Position *img_x, Position *img_y, Pixel *img_pixel)
    506           /* (x,y) == (0,0) where image starts in sw window*/
    507 {
    508     if (*img_x < 0 || *img_x >= sw->scale.image->width
    509 	||
    510 	*img_y < 0 || *img_y >= sw->scale.image->height)
    511 	return (-1);
    512 
    513     if (sw->scale.table.x[(int) *img_x] >= x) {
    514 	--*img_x;
    515 	return FindPixel(sw, x, y, img_x, img_y, img_pixel);
    516     }
    517     if (sw->scale.table.x[(int) *img_x] +
    518 	(Position)sw->scale.table.width[(int) *img_x] < x) {
    519 	++*img_x;
    520 	return FindPixel(sw, x, y, img_x, img_y, img_pixel);
    521     }
    522     if (sw->scale.table.y[(int) *img_y] >= y) {
    523 	--*img_y;
    524 	return FindPixel(sw, x, y, img_x, img_y, img_pixel);
    525     }
    526     if (sw->scale.table.y[(int) *img_y] +
    527 	(Position)sw->scale.table.height[(int) *img_y] < y) {
    528 	++*img_y;
    529 	return FindPixel(sw, x, y, img_x, img_y, img_pixel);
    530     }
    531 
    532     *img_pixel = XGetPixel(sw->scale.image, *img_x, *img_y);
    533 
    534     return (0);
    535 }
    536 
    537 
    538 
    540 int
    541 SWGetImagePixel(Widget w, Position x, Position y,
    542 		Position *img_x, Position *img_y, Pixel *img_pixel)
    543 {
    544     ScaleWidget sw = (ScaleWidget) w;
    545 
    546     x -= sw->scale.x;
    547     y -= sw->scale.y;
    548 
    549     *img_x = (Position) floor(x / sw->scale.scale_x);
    550     *img_y = (Position) floor(y / sw->scale.scale_y);
    551 
    552     return FindPixel(sw, x, y, img_x, img_y, img_pixel);
    553 }
    554 
    555 
    556 
    558 static void
    559 Redisplay(Widget w, XEvent *event, _X_UNUSED Region region)
    560 {
    561     ScaleWidget sw = (ScaleWidget) w;
    562     Position  x, y, img_x, img_y;
    563     Dimension width, height;
    564 
    565     if (event->type == Expose) {
    566 
    567 	if (event->xexpose.x < sw->scale.x) {
    568 	    x = 0;
    569 	    width = event->xexpose.width -
    570 		(sw->scale.x - event->xexpose.x);
    571 	}
    572 	else {
    573 	    x = event->xexpose.x - sw->scale.x;
    574 	    width = event->xexpose.width;
    575 	}
    576 
    577 	if (event->xexpose.y < sw->scale.y) {
    578 	    y = 0;
    579 	    height = event->xexpose.height -
    580 		(sw->scale.y - event->xexpose.y);
    581 	}
    582 	else {
    583 	    y = event->xexpose.y - sw->scale.y;
    584 	    height = event->xexpose.height;
    585 	}
    586 
    587 	img_x = min(max((Position) floor(x / sw->scale.scale_x), 0),
    588 		    (Position) sw->scale.image->width - 1);
    589 
    590 	img_y = min(max((Position) floor(y / sw->scale.scale_y), 0),
    591 		    (Position) sw->scale.image->height - 1);
    592 
    593 	if (sw->core.visible) {
    594 	    ScaleImage(sw, XtWindow(w),
    595 		       img_x, img_y,
    596 		       sw->scale.x + sw->scale.table.x[(int) img_x],
    597 		       sw->scale.y + sw->scale.table.y[(int) img_y],
    598 		       (Dimension) ceil(width
    599 					/ sw->scale.scale_x) + 1,
    600 		       (Dimension) ceil(height
    601 					/ sw->scale.scale_y) + 1);
    602 	}
    603     }
    604 }
    605 
    606 
    607 
    609 static void
    610 TryResize(ScaleWidget sw)
    611 {
    612     Dimension width, height;
    613     XtGeometryResult result;
    614 
    615     width = (Dimension)
    616 	floor(sw->scale.image->width * sw->scale.scale_x)
    617 	    + 2 * sw->scale.internal_width;
    618     height = (Dimension)
    619 	floor(sw->scale.image->height * sw->scale.scale_y)
    620 	    + 2 * sw->scale.internal_height;
    621 
    622     while ((result =
    623 /* SUPPRESS 530 */XtMakeResizeRequest((Widget)sw,width,height,&width,&height))
    624 	   == XtGeometryAlmost);
    625 
    626     if (result != XtGeometryNo) {
    627 	sw->core.width = width;
    628 	sw->core.height = height;
    629     }
    630 }
    631 
    632 
    633 
    635 static void
    636 Precision(ScaleWidget sw)
    637 {
    638     if (sw->scale.scale_x != 1.0)
    639 	sw->scale.scale_x = floor(sw->scale.scale_x / sw->scale.precision)
    640 	    		    * sw->scale.precision;
    641 
    642     if (sw->scale.scale_y != 1.0)
    643 	sw->scale.scale_y = floor(sw->scale.scale_y / sw->scale.precision)
    644 			    * sw->scale.precision;
    645 }
    646 
    647 
    648 
    650 static void
    651 Proportional(ScaleWidget sw)
    652 {
    653     double scale_x, scale_y;
    654 
    655     scale_x = sw->scale.scale_y / sw->scale.aspect_ratio;
    656     scale_y = sw->scale.scale_x * sw->scale.aspect_ratio;
    657 
    658     if (scale_x <= sw->scale.scale_x && scale_y <= sw->scale.scale_y) {
    659 	if (scale_x > scale_y)
    660 	    sw->scale.scale_x = scale_x;
    661 	else
    662 	    sw->scale.scale_y = scale_y;
    663     }
    664     else if (scale_x <= sw->scale.scale_x)
    665 	sw->scale.scale_x = scale_x;
    666     else if (scale_y <= sw->scale.scale_y)
    667 	sw->scale.scale_y = scale_y;
    668     else {
    669 	double x_ratio, y_ratio;
    670 
    671 	x_ratio = scale_x / sw->scale.scale_x;
    672 	y_ratio = scale_y / sw->scale.scale_y;
    673 
    674 	if (x_ratio < y_ratio)
    675 	    sw->scale.scale_y /= x_ratio;
    676 	else
    677 	    sw->scale.scale_x /= y_ratio;
    678     }
    679 
    680     if (fabs(sw->scale.scale_x / sw->scale.scale_y * sw->scale.aspect_ratio
    681 	    - 1.0) > sw->scale.precision)
    682 	XtWarning("can not preserve aspect ratio");
    683 }
    684 
    685 
    686 
    688 static void
    689 GetScaledSize(ScaleWidget sw)
    690 {
    691     sw->scale.width = (Dimension)
    692 	max(myrint(sw->scale.scale_x * sw->scale.image->width), 1);
    693     sw->scale.height = (Dimension)
    694 	max(myrint(sw->scale.scale_y * sw->scale.image->height), 1);
    695 }
    696 
    697 
    698 
    700 static void
    701 GetScaleValues(ScaleWidget sw)
    702 {
    703     /*
    704      * Make sure to subtract internal width and height.
    705      */
    706 
    707     sw->scale.scale_x =
    708 	max((int)(sw->core.width - 2 * sw->scale.internal_width), 1)
    709 	    / sw->scale.image->width;
    710 
    711     sw->scale.scale_y =
    712 	max((int)(sw->core.height - 2 * sw->scale.internal_height), 1)
    713 	    / sw->scale.image->height;
    714 }
    715 
    716 
    717 
    719 static void
    720 Unscale(ScaleWidget sw)
    721 {
    722     sw->scale.scale_x = sw->scale.scale_y = 1.0;
    723 
    724     GetScaledSize(sw);
    725 
    726     BuildTable(sw);
    727 }
    728 
    729 
    730 
    732 static void
    733 Autoscale(ScaleWidget sw)
    734 {
    735     GetScaleValues(sw);
    736 
    737     if (sw->scale.proportional) Proportional(sw);
    738 
    739     Precision(sw);
    740 
    741     GetScaledSize(sw);
    742 
    743     BuildTable(sw);
    744 }
    745 
    746 
    747 
    749 static void
    750 PositionImage(ScaleWidget sw)
    751 {
    752     /*
    753      * Set as if for ForgegGravity (that is center the image)
    754      */
    755     sw->scale.x = (Position)
    756 	(sw->core.width - sw->scale.width) / 2;
    757     sw->scale.y = (Position)
    758 	(sw->core.height - sw->scale.height) / 2;
    759 
    760 /*****
    761     if (sw->scale.gravity & WestGravity) {
    762     }
    763     if (sw->scale.gravity & EastGravity) {
    764     }
    765     if (sw->scale.gravity & NorthGravity) {
    766     }
    767     if (sw->scale.gravity & SouthGravity) {
    768     }
    769 *****/
    770 }
    771 
    772 
    773 
    775 static void
    776 Resize(Widget w)
    777 {
    778     ScaleWidget sw = (ScaleWidget) w;
    779 
    780     if (sw->scale.autoscale) Autoscale(sw);
    781 
    782     PositionImage(sw);
    783 }
    784 
    785 
    786 
    788 static void
    789 Realize(Widget wid, Mask *vmask, XSetWindowAttributes *attr)
    790 {
    791   ScaleWidget sw = (ScaleWidget) wid;
    792   XtCreateWindow(wid, (unsigned int) InputOutput,
    793 		 sw->scale.visual, *vmask, attr);
    794 }
    795 
    796 
    797 
    799 static void
    800 Destroy(Widget w)
    801 {
    802     ScaleWidget sw = (ScaleWidget) w;
    803 
    804     XtFree((char *) sw->scale.table.x);
    805     XtFree((char *) sw->scale.table.y);
    806     XtFree((char *) sw->scale.table.width);
    807     XtFree((char *) sw->scale.table.height);
    808 
    809     XtFree((char *) sw->scale.rectangles);
    810 
    811     XtReleaseGC(w, sw->scale.gc);
    812 
    813     XDestroyImage(sw->scale.image);
    814 }
    815 
    816 
    817 
    819 static Boolean
    820 SetValues(Widget current, _X_UNUSED Widget request, Widget new,
    821 	  ArgList args, Cardinal *num_args)
    822 {
    823     ScaleWidget cur_sw = (ScaleWidget) current;
    824     /* ScaleWidget req_sw = (ScaleWidget) request; */
    825     ScaleWidget new_sw = (ScaleWidget) new;
    826     Boolean redisplay = False;
    827     Cardinal i;
    828 
    829     for (i = 0; i < *num_args; i++) {
    830 	if (streq(XtNbackground, args[i].name)) {
    831 	    XSetBackground(XtDisplay(new), new_sw->scale.gc,
    832 			   new_sw->core.background_pixel);
    833 	}
    834 	if (streq(XtNforeground, args[i].name)) {
    835 	    XSetForeground(XtDisplay(new), new_sw->scale.gc,
    836 			   new_sw->scale.foreground_pixel);
    837 	}
    838 	if (streq(XtNimage, args[i].name)) {
    839 	    XDestroyImage(cur_sw->scale.image);
    840 	    if (new_sw->scale.image == NULL)
    841 		new_sw->scale.image = XCreateImage(XtDisplay(new),
    842 						DefaultVisual(XtDisplay(new),
    843 						DefaultScreen(XtDisplay(new))),
    844 						   1, XYBitmap, 0,
    845 						   XtCalloc(1, sizeof(char)),
    846 						   1, 1, 8, 0);
    847 	    else
    848 		new_sw->scale.image =
    849 		    XSubImage(new_sw->scale.image,
    850 			      0, 0,
    851 			      new_sw->scale.image->width,
    852 			      new_sw->scale.image->height);
    853 
    854 	    if (new_sw->scale.resize)
    855 		TryResize(new_sw);
    856 	    if (new_sw->scale.autoscale)
    857 		Autoscale(new_sw);
    858 	    else {
    859 		GetScaledSize(new_sw);
    860 		BuildTable(new_sw);
    861 	    }
    862 	    PositionImage(new_sw);
    863 	    redisplay = True;
    864 	}
    865 
    866 	if (streq(XtNuserData, args[i].name))
    867 	  new_sw->scale.userData = (XtPointer)args[i].value;
    868 
    869 	if (streq(XtNbufferSize, args[i].name)) {
    870 	    if (new_sw->scale.buffer_size != cur_sw->scale.buffer_size) {
    871 		GetRectangleBuffer(new_sw, new_sw->scale.buffer_size);
    872 	    }
    873 	}
    874 
    875 	if (streq(XtNaspectRatio, args[i].name)) {
    876 	    if ((new_sw->scale.aspect_ratio =
    877 		 atof(new_sw->scale.aspect_ratio_str)) < 0.0) {
    878 		new_sw->scale.aspect_ratio = cur_sw->scale.aspect_ratio;
    879 		XtWarning("AspectRatio has to be a positive number.");
    880 	    }
    881 	    else if (new_sw->scale.aspect_ratio != cur_sw->scale.aspect_ratio){
    882 		if (new_sw->scale.proportional) {
    883 		    Proportional(new_sw);
    884 		    Precision(new_sw);
    885 		    GetScaledSize(new_sw);
    886 		    BuildTable(new_sw);
    887 		    PositionImage(new_sw);
    888 		    redisplay = True;
    889 		}
    890 	    }
    891 	}
    892 
    893 	if (streq(XtNproportional, args[i].name)) {
    894 	    if (new_sw->scale.proportional != cur_sw->scale.proportional) {
    895 		if (new_sw->scale.proportional) Proportional(new_sw);
    896 		Precision(new_sw);
    897 		GetScaledSize(new_sw);
    898 		BuildTable(new_sw);
    899 		PositionImage(new_sw);
    900 		redisplay = True;
    901 	    }
    902 	}
    903 
    904 	if (streq(XtNscaleX, args[i].name)
    905 	    ||
    906 	    streq(XtNscaleY, args[i].name)) {
    907 	    if (new_sw->scale.scale_x_str == DefaultScaleFactor
    908 		||
    909 		new_sw->scale.scale_y_str == DefaultScaleFactor)
    910 		GetInitialScaleValues(new_sw);
    911 	    else {
    912 		if ((new_sw->scale.scale_x =
    913 		     atof(new_sw->scale.scale_x_str)) < 0.0) {
    914 		    new_sw->scale.scale_x = cur_sw->scale.scale_x;
    915 		    XtWarning("ScaleValue has to be a positive number.");
    916 		}
    917 		if ((new_sw->scale.scale_y =
    918 		     atof(new_sw->scale.scale_y_str)) < 0.0) {
    919 		    new_sw->scale.scale_y = cur_sw->scale.scale_y;
    920 		    XtWarning("ScaleValue has to be a positive number.");
    921 		}
    922 	    }
    923 	    if (new_sw->scale.scale_x != cur_sw->scale.scale_x
    924 		||
    925 		new_sw->scale.scale_y != cur_sw->scale.scale_y) {
    926 
    927 		/*?*?*?*?*?*?*?*?*?*?*?*?*?*?**?*?*?*?*?*?*?*?*?***?*/
    928 		fprintf(stderr, "================>>%f %f\n",
    929 			new_sw->scale.scale_x, new_sw->scale.scale_y);
    930 
    931 		if (new_sw->scale.proportional) Proportional(new_sw);
    932 		Precision(new_sw);
    933 		if (new_sw->scale.resize)
    934 		    TryResize(new_sw);
    935 		GetScaledSize(new_sw);
    936 		BuildTable(new_sw);
    937 		PositionImage(new_sw);
    938 		redisplay = True;
    939 	    }
    940 	}
    941 
    942 	if (streq(XtNprecision, args[i].name)) {
    943 	    if ((new_sw->scale.precision =
    944 		 atof(new_sw->scale.precision_str)) < 0.0) {
    945 		new_sw->scale.precision = cur_sw->scale.precision;
    946 		XtWarning("Precision has to be a positive number.");
    947 	    }
    948 	    if (new_sw->scale.precision != cur_sw->scale.precision) {
    949 		if (new_sw->scale.proportional) Proportional(new_sw);
    950 		Precision(new_sw);
    951 		GetScaledSize(new_sw);
    952 		BuildTable(new_sw);
    953 		PositionImage(new_sw);
    954 		redisplay = True;
    955 	    }
    956 	}
    957     }
    958     return(redisplay);
    959 }
    960 
    961 
    962 
    964 void
    965 SWUnscale(Widget w, _X_UNUSED XEvent *event,
    966           _X_UNUSED String *params, _X_UNUSED Cardinal *num_params)
    967 {
    968     ScaleWidget sw = (ScaleWidget) w;
    969 
    970     Unscale(sw);
    971     PositionImage(sw);
    972     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
    973 }
    974 
    975 
    976 
    978 void
    979 SWAutoscale(Widget w, _X_UNUSED XEvent *event,
    980             _X_UNUSED String *params, _X_UNUSED Cardinal *num_params)
    981 {
    982     ScaleWidget sw = (ScaleWidget) w;
    983 
    984     Autoscale(sw);
    985     PositionImage(sw);
    986     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
    987 }
    988 
    989 
    990 
    992 void
    993 SWInitialSize(Widget w, _X_UNUSED XEvent *event,
    994               _X_UNUSED String *params, _X_UNUSED Cardinal *num_params)
    995 {
    996     ScaleWidget sw = (ScaleWidget) w;
    997 
    998     GetInitialScaleValues(sw);
    999 
   1000     if (sw->scale.proportional) Proportional(sw);
   1001     Precision(sw);
   1002     if (sw->scale.resize)
   1003 	TryResize(sw);
   1004     GetScaledSize(sw);
   1005     BuildTable(sw);
   1006     PositionImage(sw);
   1007     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
   1008 }
   1009 
   1010 
   1011 
   1013 void
   1014 SWSetImage(Widget w, XImage *image)
   1015 {
   1016     int n;
   1017     Arg wargs[2];
   1018 
   1019     n = 0;
   1020     XtSetArg(wargs[n], XtNimage, (XtArgVal) image); n++;
   1021     XtSetValues(w, wargs, n);
   1022 }
   1023 
   1024 
   1025 
   1027 
   1028 void
   1029 RequestSelection(Widget w, XEvent *event,
   1030                  _X_UNUSED String *params, _X_UNUSED Cardinal *num_params)
   1031 {
   1032     SWRequestSelection(w, event->xbutton.time);
   1033 }
   1034 
   1035 
   1036 
   1038 void
   1039 GrabSelection(Widget w, XEvent *event,
   1040               _X_UNUSED String *params, _X_UNUSED Cardinal *num_params)
   1041 {
   1042     SWGrabSelection(w, event->xbutton.time);
   1043 }
   1044 
   1045 
   1046 
   1048 Pixmap
   1049 SWGetPixmap(Widget w)
   1050 {
   1051     ScaleWidget sw = (ScaleWidget) w;
   1052     Pixmap pixmap;
   1053 
   1054     pixmap = XCreatePixmap(XtDisplay(w), XtWindow(w),
   1055 			   sw->scale.width,
   1056 			   sw->scale.height,
   1057 			   sw->scale.image->depth);
   1058     ScaleImage(sw, pixmap,
   1059 	       0, 0, 0, 0,
   1060 	       (Dimension) sw->scale.image->width,
   1061 	       (Dimension) sw->scale.image->height);
   1062 
   1063     return(pixmap);
   1064 }
   1065