📄 tkcanvas.c
字号:
DisplayCanvas(clientData) ClientData clientData; /* Information about widget. */{ TkCanvas *canvasPtr = (TkCanvas *) clientData; Tk_Window tkwin = canvasPtr->tkwin; Tk_Item *itemPtr; Pixmap pixmap; int screenX1, screenX2, screenY1, screenY2, width, height; if (canvasPtr->tkwin == NULL) { return; } if (!Tk_IsMapped(tkwin)) { goto done; } /* * Choose a new current item if that is needed (this could cause * event handlers to be invoked). */ while (canvasPtr->flags & REPICK_NEEDED) { Tcl_Preserve((ClientData) canvasPtr); canvasPtr->flags &= ~REPICK_NEEDED; PickCurrentItem(canvasPtr, &canvasPtr->pickEvent); tkwin = canvasPtr->tkwin; Tcl_Release((ClientData) canvasPtr); if (tkwin == NULL) { return; } } /* * Compute the intersection between the area that needs redrawing * and the area that's visible on the screen. */ if ((canvasPtr->redrawX1 < canvasPtr->redrawX2) && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) { screenX1 = canvasPtr->xOrigin + canvasPtr->inset; screenY1 = canvasPtr->yOrigin + canvasPtr->inset; screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset; screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset; if (canvasPtr->redrawX1 > screenX1) { screenX1 = canvasPtr->redrawX1; } if (canvasPtr->redrawY1 > screenY1) { screenY1 = canvasPtr->redrawY1; } if (canvasPtr->redrawX2 < screenX2) { screenX2 = canvasPtr->redrawX2; } if (canvasPtr->redrawY2 < screenY2) { screenY2 = canvasPtr->redrawY2; } if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) { goto borders; } /* * Redrawing is done in a temporary pixmap that is allocated * here and freed at the end of the procedure. All drawing * is done to the pixmap, and the pixmap is copied to the * screen at the end of the procedure. The temporary pixmap * serves two purposes: * * 1. It provides a smoother visual effect (no clearing and * gradual redraw will be visible to users). * 2. It allows us to redraw only the objects that overlap * the redraw area. Otherwise incorrect results could * occur from redrawing things that stick outside of * the redraw area (we'd have to redraw everything in * order to make the overlaps look right). * * Some tricky points about the pixmap: * * 1. We only allocate a large enough pixmap to hold the * area that has to be redisplayed. This saves time in * in the X server for large objects that cover much * more than the area being redisplayed: only the area * of the pixmap will actually have to be redrawn. * 2. Some X servers (e.g. the one for DECstations) have troubles * with characters that overlap an edge of the pixmap (on the * DEC servers, as of 8/18/92, such characters are drawn one * pixel too far to the right). To handle this problem, * make the pixmap a bit larger than is absolutely needed * so that for normal-sized fonts the characters that overlap * the edge of the pixmap will be outside the area we care * about. */ canvasPtr->drawableXOrigin = screenX1 - 30; canvasPtr->drawableYOrigin = screenY1 - 30; pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), (screenX2 + 30 - canvasPtr->drawableXOrigin), (screenY2 + 30 - canvasPtr->drawableYOrigin), Tk_Depth(tkwin)); /* * Clear the area to be redrawn. */ width = screenX2 - screenX1; height = screenY2 - screenY1; XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC, screenX1 - canvasPtr->drawableXOrigin, screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width, (unsigned int) height); /* * Scan through the item list, redrawing those items that need it. * An item must be redraw if either (a) it intersects the smaller * on-screen area or (b) it intersects the full canvas area and its * type requests that it be redrawn always (e.g. so subwindows can * be unmapped when they move off-screen). */ for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; itemPtr = itemPtr->nextPtr) { if ((itemPtr->x1 >= screenX2) || (itemPtr->y1 >= screenY2) || (itemPtr->x2 < screenX1) || (itemPtr->y2 < screenY1)) { if (!itemPtr->typePtr->alwaysRedraw || (itemPtr->x1 >= canvasPtr->redrawX2) || (itemPtr->y1 >= canvasPtr->redrawY2) || (itemPtr->x2 < canvasPtr->redrawX1) || (itemPtr->y2 < canvasPtr->redrawY1)) { continue; } } (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr, canvasPtr->display, pixmap, screenX1, screenY1, width, height); } /* * Copy from the temporary pixmap to the screen, then free up * the temporary pixmap. */ XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), canvasPtr->pixmapGC, screenX1 - canvasPtr->drawableXOrigin, screenY1 - canvasPtr->drawableYOrigin, (unsigned) (screenX2 - screenX1), (unsigned) (screenY2 - screenY1), screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin); Tk_FreePixmap(Tk_Display(tkwin), pixmap); } /* * Draw the window borders, if needed. */ borders: if (canvasPtr->flags & REDRAW_BORDERS) { canvasPtr->flags &= ~REDRAW_BORDERS; if (canvasPtr->borderWidth > 0) { Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), canvasPtr->bgBorder, canvasPtr->highlightWidth, canvasPtr->highlightWidth, Tk_Width(tkwin) - 2*canvasPtr->highlightWidth, Tk_Height(tkwin) - 2*canvasPtr->highlightWidth, canvasPtr->borderWidth, canvasPtr->relief); } if (canvasPtr->highlightWidth != 0) { GC gc; if (canvasPtr->textInfo.gotFocus) { gc = Tk_GCForColor(canvasPtr->highlightColorPtr, Tk_WindowId(tkwin)); } else { gc = Tk_GCForColor(canvasPtr->highlightBgColorPtr, Tk_WindowId(tkwin)); } Tk_DrawFocusHighlight(tkwin, gc, canvasPtr->highlightWidth, Tk_WindowId(tkwin)); } } done: canvasPtr->flags &= ~REDRAW_PENDING; canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0; canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0; if (canvasPtr->flags & UPDATE_SCROLLBARS) { CanvasUpdateScrollbars(canvasPtr); }}/* *-------------------------------------------------------------- * * CanvasEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on canvases. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */static voidCanvasEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */{ TkCanvas *canvasPtr = (TkCanvas *) clientData; if (eventPtr->type == Expose) { int x, y; x = eventPtr->xexpose.x + canvasPtr->xOrigin; y = eventPtr->xexpose.y + canvasPtr->yOrigin; Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y, x + eventPtr->xexpose.width, y + eventPtr->xexpose.height); if ((eventPtr->xexpose.x < canvasPtr->inset) || (eventPtr->xexpose.y < canvasPtr->inset) || ((eventPtr->xexpose.x + eventPtr->xexpose.width) > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset)) || ((eventPtr->xexpose.y + eventPtr->xexpose.height) > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) { canvasPtr->flags |= REDRAW_BORDERS; } } else if (eventPtr->type == DestroyNotify) { if (canvasPtr->tkwin != NULL) { canvasPtr->tkwin = NULL; Tcl_DeleteCommandFromToken(canvasPtr->interp, canvasPtr->widgetCmd); } if (canvasPtr->flags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr); } Tcl_EventuallyFree((ClientData) canvasPtr, DestroyCanvas); } else if (eventPtr->type == ConfigureNotify) { canvasPtr->flags |= UPDATE_SCROLLBARS; /* * The call below is needed in order to recenter the canvas if * it's confined and its scroll region is smaller than the window. */ CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin); Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin, canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); canvasPtr->flags |= REDRAW_BORDERS; } else if (eventPtr->type == FocusIn) { if (eventPtr->xfocus.detail != NotifyInferior) { CanvasFocusProc(canvasPtr, 1); } } else if (eventPtr->type == FocusOut) { if (eventPtr->xfocus.detail != NotifyInferior) { CanvasFocusProc(canvasPtr, 0); } } else if (eventPtr->type == UnmapNotify) { Tk_Item *itemPtr; /* * Special hack: if the canvas is unmapped, then must notify * all items with "alwaysRedraw" set, so that they know that * they are no longer displayed. */ for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; itemPtr = itemPtr->nextPtr) { if (itemPtr->typePtr->alwaysRedraw) { (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr, canvasPtr->display, None, 0, 0, 0, 0); } } }}/* *---------------------------------------------------------------------- * * CanvasCmdDeletedProc -- * * This procedure is invoked when a widget command is deleted. If * the widget isn't already in the process of being destroyed, * this command destroys it. * * Results: * None. * * Side effects: * The widget is destroyed. * *---------------------------------------------------------------------- */static voidCanvasCmdDeletedProc(clientData) ClientData clientData; /* Pointer to widget record for widget. */{ TkCanvas *canvasPtr = (TkCanvas *) clientData; Tk_Window tkwin = canvasPtr->tkwin; /* * This procedure could be invoked either because the window was * destroyed and the command was then deleted (in which case tkwin * is NULL) or because the command was deleted, and then this procedure * destroys the widget. */ if (tkwin != NULL) { canvasPtr->tkwin = NULL; Tk_DestroyWindow(tkwin); }}/* *-------------------------------------------------------------- * * Tk_CanvasEventuallyRedraw -- * * Arrange for part or all of a canvas widget to redrawn at * some convenient time in the future. * * Results: * None. * * Side effects: * The screen will eventually be refreshed. * *-------------------------------------------------------------- */voidTk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2) Tk_Canvas canvas; /* Information about widget. */ int x1, y1; /* Upper left corner of area to redraw. * Pixels on edge are redrawn. */ int x2, y2; /* Lower right corner of area to redraw. * Pixels on edge are not redrawn. */{ TkCanvas *canvasPtr = (TkCanvas *) canvas; if ((x1 == x2) || (y1 == y2)) { return; } if (canvasPtr->flags & REDRAW_PENDING) { if (x1 <= canvasPtr->redrawX1) { canvasPtr->redrawX1 = x1; } if (y1 <= canvasPtr->redrawY1) { canvasPtr->redrawY1 = y1; } if (x2 >= canvasPtr->redrawX2) { canvasPtr->redrawX2 = x2; } if (y2 >= canvasPtr->redrawY2) { canvasPtr->redrawY2 = y2; } } else { canvasPtr->redrawX1 = x1; canvasPtr->redrawY1 = y1; canvasPtr->redrawX2 = x2; canvasPtr->redrawY2 = y2; Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr); canvasPtr->flags |= REDRAW_PENDING; }}/* *-------------------------------------------------------------- * * Tk_CreateItemType -- * * This procedure may be invoked to add a new kind of canvas * element to the core item types supported by Tk. * * Results: * None. * * Side effects: * From now on, the new item type will be useable in canvas * widgets (e.g. typePtr->name can be used as the item type * in "create" widget commands). If there was already a * type with the same name as in typePtr, it is replaced with * the new type. * *-------------------------------------------------------------- */voidTk_CreateItemType(typePtr) Tk_ItemType *typePtr; /* Information about item type; * storage must be statically * allocated (must live forever). */{ Tk_ItemType *typePtr2, *prevPtr; if (typeList == NULL) { InitCanvas(); } /* * If there's already an item type with the given name, remove it.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -