1/* $Id$ 2 * Copyright (c) 2003, Joe English 3 * 4 * ttk::scrollbar widget. 5 */ 6 7#include <tk.h> 8 9#include "ttkTheme.h" 10#include "ttkWidget.h" 11 12/*------------------------------------------------------------------------ 13 * +++ Scrollbar widget record. 14 */ 15typedef struct 16{ 17 Tcl_Obj *commandObj; 18 19 int orient; 20 Tcl_Obj *orientObj; 21 22 double first; /* top fraction */ 23 double last; /* bottom fraction */ 24 25 Ttk_Box troughBox; /* trough parcel */ 26 int minSize; /* minimum size of thumb */ 27} ScrollbarPart; 28 29typedef struct 30{ 31 WidgetCore core; 32 ScrollbarPart scrollbar; 33} Scrollbar; 34 35static Tk_OptionSpec ScrollbarOptionSpecs[] = 36{ 37 {TK_OPTION_STRING, "-command", "command", "Command", "", 38 Tk_Offset(Scrollbar,scrollbar.commandObj), -1, 0,0,0}, 39 40 {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "vertical", 41 Tk_Offset(Scrollbar,scrollbar.orientObj), 42 Tk_Offset(Scrollbar,scrollbar.orient), 43 0,(ClientData)ttkOrientStrings,STYLE_CHANGED }, 44 45 WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs) 46}; 47 48/*------------------------------------------------------------------------ 49 * +++ Widget hooks. 50 */ 51 52static void 53ScrollbarInitialize(Tcl_Interp *interp, void *recordPtr) 54{ 55 Scrollbar *sb = recordPtr; 56 sb->scrollbar.first = 0.0; 57 sb->scrollbar.last = 1.0; 58 59 TtkTrackElementState(&sb->core); 60} 61 62static Ttk_Layout ScrollbarGetLayout( 63 Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr) 64{ 65 Scrollbar *sb = recordPtr; 66 return TtkWidgetGetOrientedLayout( 67 interp, theme, recordPtr, sb->scrollbar.orientObj); 68} 69 70/* 71 * ScrollbarDoLayout -- 72 * Layout hook. Adjusts the position of the scrollbar thumb. 73 * 74 * Side effects: 75 * Sets sb->troughBox and sb->minSize. 76 */ 77static void ScrollbarDoLayout(void *recordPtr) 78{ 79 Scrollbar *sb = recordPtr; 80 WidgetCore *corePtr = &sb->core; 81 Ttk_Element thumb; 82 Ttk_Box thumbBox; 83 int thumbWidth, thumbHeight; 84 double first, last, size; 85 int minSize; 86 87 /* 88 * Use generic layout manager to compute initial layout: 89 */ 90 Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin)); 91 92 /* 93 * Locate thumb element, extract parcel and requested minimum size: 94 */ 95 thumb = Ttk_FindElement(corePtr->layout, "thumb"); 96 if (!thumb) /* Something has gone wrong -- bail */ 97 return; 98 99 sb->scrollbar.troughBox = thumbBox = Ttk_ElementParcel(thumb); 100 Ttk_LayoutNodeReqSize( 101 corePtr->layout, thumb, &thumbWidth,&thumbHeight); 102 103 /* 104 * Adjust thumb element parcel: 105 */ 106 first = sb->scrollbar.first; 107 last = sb->scrollbar.last; 108 109 if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) { 110 minSize = thumbHeight; 111 size = thumbBox.height - minSize; 112 thumbBox.y += (int)(size * first); 113 thumbBox.height = (int)(size * last) + minSize - (int)(size * first); 114 } else { 115 minSize = thumbWidth; 116 size = thumbBox.width - minSize; 117 thumbBox.x += (int)(size * first); 118 thumbBox.width = (int)(size * last) + minSize - (int)(size * first); 119 } 120 sb->scrollbar.minSize = minSize; 121 Ttk_PlaceElement(corePtr->layout, thumb, thumbBox); 122} 123 124/*------------------------------------------------------------------------ 125 * +++ Widget commands. 126 */ 127 128/* $sb set $first $last -- 129 * Set the position of the scrollbar. 130 */ 131static int 132ScrollbarSetCommand( 133 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 134{ 135 Scrollbar *scrollbar = recordPtr; 136 Tcl_Obj *firstObj, *lastObj; 137 double first, last; 138 139 if (objc != 4) { 140 Tcl_WrongNumArgs(interp, 2, objv, "first last"); 141 return TCL_ERROR; 142 } 143 144 firstObj = objv[2]; 145 lastObj = objv[3]; 146 if (Tcl_GetDoubleFromObj(interp, firstObj, &first) != TCL_OK 147 || Tcl_GetDoubleFromObj(interp, lastObj, &last) != TCL_OK) 148 return TCL_ERROR; 149 150 /* Range-checks: 151 */ 152 if (first < 0.0) { 153 first = 0.0; 154 } else if (first > 1.0) { 155 first = 1.0; 156 } 157 158 if (last < first) { 159 last = first; 160 } else if (last > 1.0) { 161 last = 1.0; 162 } 163 164 /* ASSERT: 0.0 <= first <= last <= 1.0 */ 165 166 scrollbar->scrollbar.first = first; 167 scrollbar->scrollbar.last = last; 168 if (first <= 0.0 && last >= 1.0) { 169 scrollbar->core.state |= TTK_STATE_DISABLED; 170 } else { 171 scrollbar->core.state &= ~TTK_STATE_DISABLED; 172 } 173 174 TtkRedisplayWidget(&scrollbar->core); 175 176 return TCL_OK; 177} 178 179/* $sb get -- 180 * Returns the last thing passed to 'set'. 181 */ 182static int 183ScrollbarGetCommand( 184 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 185{ 186 Scrollbar *scrollbar = recordPtr; 187 Tcl_Obj *result[2]; 188 189 if (objc != 2) { 190 Tcl_WrongNumArgs(interp, 2, objv, ""); 191 return TCL_ERROR; 192 } 193 194 result[0] = Tcl_NewDoubleObj(scrollbar->scrollbar.first); 195 result[1] = Tcl_NewDoubleObj(scrollbar->scrollbar.last); 196 Tcl_SetObjResult(interp, Tcl_NewListObj(2, result)); 197 198 return TCL_OK; 199} 200 201/* $sb delta $dx $dy -- 202 * Returns the percentage change corresponding to a mouse movement 203 * of $dx, $dy. 204 */ 205static int 206ScrollbarDeltaCommand( 207 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 208{ 209 Scrollbar *sb = recordPtr; 210 double dx, dy; 211 double delta = 0.0; 212 213 if (objc != 4) { 214 Tcl_WrongNumArgs(interp, 2, objv, "dx dy"); 215 return TCL_ERROR; 216 } 217 218 if (Tcl_GetDoubleFromObj(interp, objv[2], &dx) != TCL_OK 219 || Tcl_GetDoubleFromObj(interp, objv[3], &dy) != TCL_OK) 220 { 221 return TCL_ERROR; 222 } 223 224 delta = 0.0; 225 if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) { 226 int size = sb->scrollbar.troughBox.height - sb->scrollbar.minSize; 227 if (size > 0) { 228 delta = (double)dy / (double)size; 229 } 230 } else { 231 int size = sb->scrollbar.troughBox.width - sb->scrollbar.minSize; 232 if (size > 0) { 233 delta = (double)dx / (double)size; 234 } 235 } 236 237 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(delta)); 238 return TCL_OK; 239} 240 241/* $sb fraction $x $y -- 242 * Returns a real number between 0 and 1 indicating where the 243 * point given by x and y lies in the trough area of the scrollbar. 244 */ 245static int 246ScrollbarFractionCommand( 247 void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 248{ 249 Scrollbar *sb = recordPtr; 250 Ttk_Box b = sb->scrollbar.troughBox; 251 int minSize = sb->scrollbar.minSize; 252 double x, y; 253 double fraction = 0.0; 254 255 if (objc != 4) { 256 Tcl_WrongNumArgs(interp, 2, objv, "x y"); 257 return TCL_ERROR; 258 } 259 260 if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK 261 || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) 262 { 263 return TCL_ERROR; 264 } 265 266 fraction = 0.0; 267 if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) { 268 if (b.height > minSize) { 269 fraction = (double)(y - b.y) / (double)(b.height - minSize); 270 } 271 } else { 272 if (b.width > minSize) { 273 fraction = (double)(x - b.x) / (double)(b.width - minSize); 274 } 275 } 276 277 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(fraction)); 278 return TCL_OK; 279} 280 281static const Ttk_Ensemble ScrollbarCommands[] = { 282 { "configure", TtkWidgetConfigureCommand,0 }, 283 { "cget", TtkWidgetCgetCommand,0 }, 284 { "delta", ScrollbarDeltaCommand,0 }, 285 { "fraction", ScrollbarFractionCommand,0 }, 286 { "get", ScrollbarGetCommand,0 }, 287 { "identify", TtkWidgetIdentifyCommand,0 }, 288 { "instate", TtkWidgetInstateCommand,0 }, 289 { "set", ScrollbarSetCommand,0 }, 290 { "state", TtkWidgetStateCommand,0 }, 291 { 0,0,0 } 292}; 293 294/*------------------------------------------------------------------------ 295 * +++ Widget specification. 296 */ 297static WidgetSpec ScrollbarWidgetSpec = 298{ 299 "TScrollbar", /* className */ 300 sizeof(Scrollbar), /* recordSize */ 301 ScrollbarOptionSpecs, /* optionSpecs */ 302 ScrollbarCommands, /* subcommands */ 303 ScrollbarInitialize, /* initializeProc */ 304 TtkNullCleanup, /* cleanupProc */ 305 TtkCoreConfigure, /* configureProc */ 306 TtkNullPostConfigure, /* postConfigureProc */ 307 ScrollbarGetLayout, /* getLayoutProc */ 308 TtkWidgetSize, /* sizeProc */ 309 ScrollbarDoLayout, /* layoutProc */ 310 TtkWidgetDisplay /* displayProc */ 311}; 312 313TTK_BEGIN_LAYOUT(VerticalScrollbarLayout) 314 TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y, 315 TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP) 316 TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM) 317 TTK_NODE( 318 "Vertical.Scrollbar.thumb", TTK_PACK_TOP|TTK_EXPAND|TTK_FILL_BOTH)) 319TTK_END_LAYOUT 320 321TTK_BEGIN_LAYOUT(HorizontalScrollbarLayout) 322 TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X, 323 TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT) 324 TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT) 325 TTK_NODE( 326 "Horizontal.Scrollbar.thumb", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH)) 327TTK_END_LAYOUT 328 329/*------------------------------------------------------------------------ 330 * +++ Initialization. 331 */ 332 333MODULE_SCOPE 334void TtkScrollbar_Init(Tcl_Interp *interp) 335{ 336 Ttk_Theme theme = Ttk_GetDefaultTheme(interp); 337 338 Ttk_RegisterLayout(theme,"Vertical.TScrollbar",VerticalScrollbarLayout); 339 Ttk_RegisterLayout(theme,"Horizontal.TScrollbar",HorizontalScrollbarLayout); 340 341 RegisterWidget(interp, "ttk::scrollbar", &ScrollbarWidgetSpec); 342} 343 344/*EOF*/ 345