📄 tklistbox.c
字号:
* *---------------------------------------------------------------------- */static voidListboxComputeGeometry(listPtr, fontChanged, maxIsStale, updateGrid) Listbox *listPtr; /* Listbox whose geometry is to be * recomputed. */ int fontChanged; /* Non-zero means the font may have changed * so per-element width information also * has to be computed. */ int maxIsStale; /* Non-zero means the "maxWidth" field may * no longer be up-to-date and must * be recomputed. If fontChanged is 1 then * this must be 1. */ int updateGrid; /* Non-zero means call Tk_SetGrid or * Tk_UnsetGrid to update gridding for * the window. */{ register Element *elPtr; int width, height, pixelWidth, pixelHeight; Tk_FontMetrics fm; if (fontChanged || maxIsStale) { listPtr->xScrollUnit = Tk_TextWidth(listPtr->tkfont, "0", 1); if (listPtr->xScrollUnit == 0) { listPtr->xScrollUnit = 1; } listPtr->maxWidth = 0; for (elPtr = listPtr->firstPtr; elPtr != NULL; elPtr = elPtr->nextPtr) { if (fontChanged) { elPtr->pixelWidth = Tk_TextWidth(listPtr->tkfont, elPtr->text, elPtr->textLength); elPtr->lBearing = 0; } if (elPtr->pixelWidth > listPtr->maxWidth) { listPtr->maxWidth = elPtr->pixelWidth; } } } Tk_GetFontMetrics(listPtr->tkfont, &fm); listPtr->lineHeight = fm.linespace + 1 + 2*listPtr->selBorderWidth; width = listPtr->width; if (width <= 0) { width = (listPtr->maxWidth + listPtr->xScrollUnit - 1) /listPtr->xScrollUnit; if (width < 1) { width = 1; } } pixelWidth = width*listPtr->xScrollUnit + 2*listPtr->inset + 2*listPtr->selBorderWidth; height = listPtr->height; if (listPtr->height <= 0) { height = listPtr->numElements; if (height < 1) { height = 1; } } pixelHeight = height*listPtr->lineHeight + 2*listPtr->inset; Tk_GeometryRequest(listPtr->tkwin, pixelWidth, pixelHeight); Tk_SetInternalBorder(listPtr->tkwin, listPtr->inset); if (updateGrid) { if (listPtr->setGrid) { Tk_SetGrid(listPtr->tkwin, width, height, listPtr->xScrollUnit, listPtr->lineHeight); } else { Tk_UnsetGrid(listPtr->tkwin); } }}/* *---------------------------------------------------------------------- * * InsertEls -- * * Add new elements to a listbox widget. * * Results: * None. * * Side effects: * New information gets added to listPtr; it will be redisplayed * soon, but not immediately. * *---------------------------------------------------------------------- */static voidInsertEls(listPtr, index, argc, argv) register Listbox *listPtr; /* Listbox that is to get the new * elements. */ int index; /* Add the new elements before this * element. */ int argc; /* Number of new elements to add. */ char **argv; /* New elements (one per entry). */{ register Element *prevPtr, *newPtr; int length, i, oldMaxWidth; /* * Find the element before which the new ones will be inserted. */ if (index <= 0) { index = 0; } if (index > listPtr->numElements) { index = listPtr->numElements; } if (index == 0) { prevPtr = NULL; } else if (index == listPtr->numElements) { prevPtr = listPtr->lastPtr; } else { for (prevPtr = listPtr->firstPtr, i = index - 1; i > 0; i--) { prevPtr = prevPtr->nextPtr; } } /* * For each new element, create a record, initialize it, and link * it into the list of elements. */ oldMaxWidth = listPtr->maxWidth; for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) { length = strlen(*argv); newPtr = (Element *) ckalloc(ElementSize(length)); newPtr->textLength = length; strcpy(newPtr->text, *argv); newPtr->pixelWidth = Tk_TextWidth(listPtr->tkfont, newPtr->text, newPtr->textLength); newPtr->lBearing = 0; if (newPtr->pixelWidth > listPtr->maxWidth) { listPtr->maxWidth = newPtr->pixelWidth; } newPtr->selected = 0; if (prevPtr == NULL) { newPtr->nextPtr = listPtr->firstPtr; listPtr->firstPtr = newPtr; } else { newPtr->nextPtr = prevPtr->nextPtr; prevPtr->nextPtr = newPtr; } } if ((prevPtr != NULL) && (prevPtr->nextPtr == NULL)) { listPtr->lastPtr = prevPtr; } listPtr->numElements += argc; /* * Update the selection and other indexes to account for the * renumbering that has just occurred. Then arrange for the new * information to be displayed. */ if (index <= listPtr->selectAnchor) { listPtr->selectAnchor += argc; } if (index < listPtr->topIndex) { listPtr->topIndex += argc; } if (index <= listPtr->active) { listPtr->active += argc; if ((listPtr->active >= listPtr->numElements) && (listPtr->numElements > 0)) { listPtr->active = listPtr->numElements-1; } } listPtr->flags |= UPDATE_V_SCROLLBAR; if (listPtr->maxWidth != oldMaxWidth) { listPtr->flags |= UPDATE_H_SCROLLBAR; } ListboxComputeGeometry(listPtr, 0, 0, 0); ListboxRedrawRange(listPtr, index, listPtr->numElements-1);}/* *---------------------------------------------------------------------- * * DeleteEls -- * * Remove one or more elements from a listbox widget. * * Results: * None. * * Side effects: * Memory gets freed, the listbox gets modified and (eventually) * redisplayed. * *---------------------------------------------------------------------- */static voidDeleteEls(listPtr, first, last) register Listbox *listPtr; /* Listbox widget to modify. */ int first; /* Index of first element to delete. */ int last; /* Index of last element to delete. */{ register Element *prevPtr, *elPtr; int count, i, widthChanged; /* * Adjust the range to fit within the existing elements of the * listbox, and make sure there's something to delete. */ if (first < 0) { first = 0; } if (last >= listPtr->numElements) { last = listPtr->numElements-1; } count = last + 1 - first; if (count <= 0) { return; } /* * Find the element just before the ones to delete. */ if (first == 0) { prevPtr = NULL; } else { for (i = first-1, prevPtr = listPtr->firstPtr; i > 0; i--) { prevPtr = prevPtr->nextPtr; } } /* * Delete the requested number of elements. */ widthChanged = 0; for (i = count; i > 0; i--) { if (prevPtr == NULL) { elPtr = listPtr->firstPtr; listPtr->firstPtr = elPtr->nextPtr; if (listPtr->firstPtr == NULL) { listPtr->lastPtr = NULL; } } else { elPtr = prevPtr->nextPtr; prevPtr->nextPtr = elPtr->nextPtr; if (prevPtr->nextPtr == NULL) { listPtr->lastPtr = prevPtr; } } if (elPtr->pixelWidth == listPtr->maxWidth) { widthChanged = 1; } if (elPtr->selected) { listPtr->numSelected -= 1; } ckfree((char *) elPtr); } listPtr->numElements -= count; /* * Update the selection and viewing information to reflect the change * in the element numbering, and redisplay to slide information up over * the elements that were deleted. */ if (first <= listPtr->selectAnchor) { listPtr->selectAnchor -= count; if (listPtr->selectAnchor < first) { listPtr->selectAnchor = first; } } if (first <= listPtr->topIndex) { listPtr->topIndex -= count; if (listPtr->topIndex < first) { listPtr->topIndex = first; } } if (listPtr->topIndex > (listPtr->numElements - listPtr->fullLines)) { listPtr->topIndex = listPtr->numElements - listPtr->fullLines; if (listPtr->topIndex < 0) { listPtr->topIndex = 0; } } if (listPtr->active > last) { listPtr->active -= count; } else if (listPtr->active >= first) { listPtr->active = first; if ((listPtr->active >= listPtr->numElements) && (listPtr->numElements > 0)) { listPtr->active = listPtr->numElements-1; } } listPtr->flags |= UPDATE_V_SCROLLBAR; ListboxComputeGeometry(listPtr, 0, widthChanged, 0); if (widthChanged) { listPtr->flags |= UPDATE_H_SCROLLBAR; } ListboxRedrawRange(listPtr, first, listPtr->numElements-1);}/* *-------------------------------------------------------------- * * ListboxEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on listboxes. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */static voidListboxEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */{ Listbox *listPtr = (Listbox *) clientData; if (eventPtr->type == Expose) { ListboxRedrawRange(listPtr, NearestListboxElement(listPtr, eventPtr->xexpose.y), NearestListboxElement(listPtr, eventPtr->xexpose.y + eventPtr->xexpose.height)); } else if (eventPtr->type == DestroyNotify) { if (listPtr->tkwin != NULL) { if (listPtr->setGrid) { Tk_UnsetGrid(listPtr->tkwin); } listPtr->tkwin = NULL; Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd); } if (listPtr->flags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr); } Tcl_EventuallyFree((ClientData) listPtr, DestroyListbox); } else if (eventPtr->type == ConfigureNotify) { int vertSpace; vertSpace = Tk_Height(listPtr->tkwin) - 2*listPtr->inset; listPtr->fullLines = vertSpace / listPtr->lineHeight; if ((listPtr->fullLines*listPtr->lineHeight) < vertSpace) { listPtr->partialLine = 1; } else { listPtr->partialLine = 0; } listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR; ChangeListboxView(listPtr, listPtr->topIndex); ChangeListboxOffset(listPtr, listPtr->xOffset); /* * Redraw the whole listbox. It's hard to tell what needs * to be redrawn (e.g. if the listbox has shrunk then we * may only need to redraw the borders), so just redraw * everything for safety. */ ListboxRedrawRange(listPtr, 0, listPtr->numElements-1); } else if (eventPtr->type == FocusIn) { if (eventPtr->xfocus.detail != NotifyInferior) { listPtr->flags |= GOT_FOCUS; ListboxRedrawRange(listPtr, 0, listPtr->numElements-1); } } else if (eventPtr->type == FocusOut) { if (eventPtr->xfocus.detail != NotifyInferior) { listPtr->flags &= ~GOT_FOCUS; ListboxRedrawRange(listPtr, 0, listPtr->numElements-1); } }}/* *---------------------------------------------------------------------- * * ListboxCmdDeletedProc -- * * 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 voidListboxCmdDeletedProc(clientData) ClientData clientData; /* Pointer to widget record for widget. */{ Listbox *listPtr = (Listbox *) clientData; Tk_Window tkwin = listPtr->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) { if (listPtr->setGrid) { Tk_UnsetGrid(listPtr->tkwin); } listPtr->tkwin = NULL; Tk_DestroyWindow(tkwin); }}/* *-------------------------------------------------------------- * * GetListboxIndex -- * * Parse an index into a listbox and return either its value * or an error.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -