1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 #define O(t, e) ((long)(&((t*)0)->e)) 6 7 typedef struct TkPanel TkPanel; 8 struct TkPanel 9 { 10 Image* image; 11 Image* matte; 12 Point view; /* vector from image origin to widget origin */ 13 Rectangle r; /* drawn rectangle (in image coords) */ 14 int anchor; 15 int hasalpha; /* does the image include an alpha channel? */ 16 }; 17 18 static TkOption tkpanelopts[] = 19 { 20 "anchor", OPTflag, O(TkPanel, anchor), tkanchor, 21 nil 22 }; 23 24 static int 25 tkdrawnrect(Image *image, Image *matte, Rectangle *r) 26 { 27 *r = image->clipr; 28 if (matte != nil) { 29 if (!rectclip(r, matte->clipr)) 30 return 0; 31 if (!matte->repl && !rectclip(r, matte->r)) 32 return 0; 33 } 34 if (!image->repl && !rectclip(r, image->r)) 35 return 0; 36 return 1; 37 } 38 39 char* 40 tkpanel(TkTop *t, char *arg, char **ret) 41 { 42 TkOptab tko[3]; 43 Tk *tk; 44 TkPanel *tkp; 45 TkName *names; 46 char *e; 47 48 tk = tknewobj(t, TKpanel, sizeof(Tk)+sizeof(TkPanel)); 49 if(tk == nil) 50 return TkNomem; 51 52 tkp = TKobj(TkPanel, tk); 53 tkp->anchor = Tkcenter; 54 55 tko[0].ptr = tk; 56 tko[0].optab = tkgeneric; 57 tko[1].ptr = tkp; 58 tko[1].optab = tkpanelopts; 59 tko[2].ptr = nil; 60 names = nil; 61 62 e = tkparse(t, arg, tko, &names); 63 if(e != nil) { 64 tkfreeobj(tk); 65 return e; 66 } 67 68 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 69 70 e = tkaddchild(t, tk, &names); 71 72 tkfreename(names); 73 if (e != nil) { 74 tkfreeobj(tk); 75 return e; 76 } 77 78 tk->name->link = nil; 79 return tkvalue(ret, "%s", tk->name->name); 80 } 81 82 void 83 tkgetpanelimage(Tk *tk, Image **i, Image **m) 84 { 85 TkPanel *tkp = TKobj(TkPanel, tk); 86 *i = tkp->image; 87 *m = tkp->matte; 88 } 89 90 void 91 tksetpanelimage(Tk *tk, Image *image, Image *matte) 92 { 93 TkPanel *tkp = TKobj(TkPanel, tk); 94 int ishuge; 95 TkGeom g; 96 97 g = tk->req; 98 99 tkp->image = image; 100 tkp->matte = matte; 101 102 if (!tkdrawnrect(image, matte, &tkp->r)) { 103 tkp->r.min = image->r.min; 104 tkp->r.max = image->r.min; 105 } 106 107 tkp->view = tkp->r.min; /* XXX do we actually want to keep the old one? */ 108 /* 109 * if both image and matte are replicated, then we've got no idea what 110 * the rectangle should be, so request zero size, and set origin to (0, 0). 111 */ 112 ishuge = (Dx(tkp->r) >= 10000000); 113 if((tk->flag & Tksetwidth) == 0){ 114 if(ishuge) 115 tk->req.width = 0; 116 else 117 tk->req.width = Dx(tkp->r); 118 } 119 if(ishuge) 120 tkp->view.x = 0; 121 122 ishuge = (Dy(tkp->r) >= 10000000); 123 if((tk->flag & Tksetheight) == 0){ 124 if(ishuge) 125 tk->req.height = 0; 126 else 127 tk->req.height = Dy(tkp->r); 128 } 129 if(ishuge) 130 tkp->view.y = 0; 131 132 tkp->hasalpha = tkchanhastype(image->chan, CAlpha); 133 tkgeomchg(tk, &g, tk->borderwidth); 134 tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd)); 135 tk->dirty = tkrect(tk, 0); 136 } 137 138 static void 139 tkfreepanel(Tk *tk) 140 { 141 TkPanel *tkp = TKobj(TkPanel, tk); 142 tkdelpanelimage(tk->env->top, tkp->image); 143 tkdelpanelimage(tk->env->top, tkp->matte); 144 } 145 146 static Point 147 tkpanelview(Tk *tk) 148 { 149 int dx, dy; 150 Point view; 151 TkPanel *tkp = TKobj(TkPanel, tk); 152 153 dx = tk->act.width - Dx(tkp->r); 154 dy = tk->act.height - Dy(tkp->r); 155 156 view = tkp->view; 157 158 if (dx > 0) { 159 if((tkp->anchor & (Tkeast|Tkwest)) == 0) 160 view.x -= dx/2; 161 else 162 if(tkp->anchor & Tkeast) 163 view.x -= dx; 164 } 165 if (dy > 0) { 166 if((tkp->anchor & (Tknorth|Tksouth)) == 0) 167 view.y -= dy/2; 168 else 169 if(tkp->anchor & Tksouth) 170 view.y -= dy; 171 } 172 return view; 173 } 174 175 static char* 176 tkdrawpanel(Tk *tk, Point orig) 177 { 178 Rectangle r, pr; 179 TkPanel *tkp = TKobj(TkPanel, tk); 180 Image *i; 181 int any; 182 Point view, p; 183 184 i = tkimageof(tk); 185 if (i == nil) 186 return nil; 187 188 p.x = orig.x + tk->act.x + tk->borderwidth; 189 p.y = orig.y + tk->act.y + tk->borderwidth; 190 191 view = tkpanelview(tk); 192 193 /* 194 * if the image doesn't fully cover the dirty rectangle, then 195 * paint some background in there 196 */ 197 r = rectsubpt(tkp->r, view); /* convert to widget coords */ 198 pr = tkrect(tk, 0); 199 any = rectclip(&r, pr); /* clip to inside widget borders */ 200 201 if (!any || tkp->hasalpha || !rectinrect(tk->dirty, r)) 202 draw(i, rectaddpt(tk->dirty, p), tkgc(tk->env, TkCbackgnd), nil, ZP); 203 204 if (any && rectclip(&r, tk->dirty)) 205 draw(i, rectaddpt(r, p), tkp->image, tkp->matte, addpt(r.min, view)); 206 207 if (!rectinrect(tk->dirty, pr)) { 208 p.x -= tk->borderwidth; 209 p.y -= tk->borderwidth; 210 tkdrawrelief(i, tk, p, TkCbackgnd, tk->relief); 211 } 212 return nil; 213 } 214 215 static char* 216 tkpanelcget(Tk *tk, char *arg, char **val) 217 { 218 TkOptab tko[3]; 219 TkPanel *tkp = TKobj(TkPanel, tk); 220 221 tko[0].ptr = tk; 222 tko[0].optab = tkgeneric; 223 tko[1].ptr = tkp; 224 tko[1].optab = tkpanelopts; 225 tko[2].ptr = nil; 226 227 return tkgencget(tko, arg, val, tk->env->top); 228 } 229 230 static char* 231 tkpanelcvt(Tk *tk, char *arg, int rel, int *p) 232 { 233 char buf[Tkmaxitem]; 234 235 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 236 if(buf[0] == '\0') 237 return TkBadvl; 238 *p = atoi(buf) + rel; 239 return nil; 240 } 241 242 /* 243 * screen to image 244 */ 245 static char* 246 tkpanelpanelx(Tk *tk, char *arg, char **val) 247 { 248 Point p; 249 char *e; 250 251 USED(val); 252 p = subpt(tkposn(tk), tkpanelview(tk)); 253 e = tkpanelcvt(tk, arg, -p.x, &p.x); 254 if (e != nil) 255 return e; 256 return tkvalue(val, "%d", p.x); 257 } 258 259 static char* 260 tkpanelpanely(Tk *tk, char *arg, char **val) 261 { 262 Point p; 263 char *e; 264 265 USED(val); 266 p = subpt(tkposn(tk), tkpanelview(tk)); 267 e = tkpanelcvt(tk, arg, -p.y, &p.y); 268 if (e != nil) 269 return e; 270 return tkvalue(val, "%d", p.y); 271 } 272 273 /* 274 * image to screen 275 */ 276 static char* 277 tkpanelscreenx(Tk *tk, char *arg, char **val) 278 { 279 Point p; 280 char *e; 281 282 USED(val); 283 p = subpt(tkposn(tk), tkpanelview(tk)); 284 e = tkpanelcvt(tk, arg, p.x, &p.x); 285 if (e != nil) 286 return e; 287 return tkvalue(val, "%d", p.x); 288 } 289 290 static char* 291 tkpanelscreeny(Tk *tk, char *arg, char **val) 292 { 293 Point p; 294 char *e; 295 296 USED(val); 297 p = subpt(tkposn(tk), tkpanelview(tk)); 298 e = tkpanelcvt(tk, arg, p.y, &p.y); 299 if (e != nil) 300 return e; 301 return tkvalue(val, "%d", p.y); 302 } 303 304 static char* 305 tkpanelconf(Tk *tk, char *arg, char **val) 306 { 307 char *e; 308 TkGeom g; 309 int bd; 310 TkOptab tko[3]; 311 TkPanel *tkp = TKobj(TkPanel, tk); 312 313 tko[0].ptr = tk; 314 tko[0].optab = tkgeneric; 315 tko[1].ptr = tkp; 316 tko[1].optab = tkpanelopts; 317 tko[2].ptr = nil; 318 319 if(*arg == '\0') 320 return tkconflist(tko, val); 321 322 g = tk->req; 323 bd = tk->borderwidth; 324 e = tkparse(tk->env->top, arg, tko, nil); 325 tkgeomchg(tk, &g, bd); 326 tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd)); 327 328 tk->dirty = tkrect(tk, 1); 329 330 return e; 331 } 332 333 static char* 334 tkpaneldirty(Tk *tk, char *arg, char **val) 335 { 336 char buf[Tkmaxitem]; 337 int n, coords[4]; 338 Rectangle r; 339 char *e, *p; 340 TkPanel *tkp = TKobj(TkPanel, tk); 341 342 USED(val); 343 n = 0; 344 while (n < 4) { 345 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 346 if (buf[0] == 0) 347 break; 348 p = buf; 349 e = tkfrac(&p, &coords[n++], nil); 350 if (e != nil) 351 return TkBadvl; 352 } 353 if (n == 0) 354 r = tkp->r; 355 else { 356 if (n != 4) 357 return TkBadvl; 358 r.min.x = TKF2I(coords[0]); 359 r.min.y = TKF2I(coords[1]); 360 r.max.x = TKF2I(coords[2]); 361 r.max.y = TKF2I(coords[3]); 362 } 363 if (rectclip(&r, tkp->r)) { 364 r = rectsubpt(r, tkpanelview(tk)); /* convert to widget coords */ 365 if (rectclip(&r, tkrect(tk, 0))) /* clip to visible area */ 366 combinerect(&tk->dirty, r); 367 } 368 return nil; 369 } 370 371 static char* 372 tkpanelorigin(Tk *tk, char *arg, char **val) 373 { 374 char *e; 375 Point view; 376 TkPanel *tkp = TKobj(TkPanel, tk); 377 378 e = tkxyparse(tk, &arg, &view); 379 if (e != nil) { 380 if (e == TkOparg) 381 return tkvalue(val, "%d %d", tkp->view.x, tkp->view.y); 382 return e; 383 } 384 tkp->view = view; 385 tk->dirty = tkrect(tk, 0); 386 return nil; 387 } 388 389 static 390 TkCmdtab tkpanelcmd[] = 391 { 392 "cget", tkpanelcget, 393 "configure", tkpanelconf, 394 "dirty", tkpaneldirty, 395 "origin", tkpanelorigin, 396 "panelx", tkpanelpanelx, 397 "panely", tkpanelpanely, 398 "screenx", tkpanelscreenx, 399 "screeny", tkpanelscreeny, 400 nil 401 }; 402 403 TkMethod panelmethod = { 404 "panel", 405 tkpanelcmd, 406 tkfreepanel, 407 tkdrawpanel 408 }; 409