Tk Source Code

Artifact [4ee88449]
Login

Artifact 4ee88449b75e1647c89280a72e619aa649a1e5f0:

Attachment "tkchelp.diff" to ticket [845248ff] added by a_kovalenko 2007-10-25 09:34:12.
? history
? doc/man.macros
? unix/autom4te.cache
? unix/tkConfig.sh
? unix/wish
Index: generic/tkInt.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkInt.h,v
retrieving revision 1.80
diff -u -r1.80 tkInt.h
--- generic/tkInt.h	15 Oct 2007 20:52:47 -0000	1.80
+++ generic/tkInt.h	25 Oct 2007 02:13:38 -0000
@@ -940,6 +940,16 @@
 #define ALT_MASK	(AnyModifier<<2)
 #define EXTENDED_MASK	(AnyModifier<<3)
 
+/* The following special modifier is passed to Tk_UpdatePointer to specify
+ * that the cursor should not be changed.
+ * */
+#define NOCURSOR_MASK	(AnyModifier<<4)
+
+/* The following special modifier is passed to Tk_UpdatePointer 
+ * in order to update current position but not to generate button events
+ * */
+#define NOBUTTONEVENTS_MASK	(AnyModifier<<4)
+
 /*
  * Object types not declared in tkObj.c need to be mentioned here so they can
  * be properly registered with Tcl:
Index: generic/tkPointer.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkPointer.c,v
retrieving revision 1.10
diff -u -r1.10 tkPointer.c
--- generic/tkPointer.c	15 Nov 2005 15:18:22 -0000	1.10
+++ generic/tkPointer.c	25 Oct 2007 02:13:39 -0000
@@ -239,9 +239,25 @@
     TkWindow *targetWinPtr;
     XPoint pos;
     XEvent event;
-    int changes = (state ^ tsdPtr->lastState) & ALL_BUTTONS;
+    int no_cursor = (state & NOCURSOR_MASK);
+    int no_button_events = (state & NOBUTTONEVENTS_MASK);
+    int changes; 
     int type, b, mask;
 
+    if (state & ALL_BUTTONS) {
+	tsdPtr->lastState &= ~NOCURSOR_MASK;
+    }
+    state &= ~NOCURSOR_MASK & ~NOBUTTONEVENTS_MASK;
+    if (no_cursor) {
+	tsdPtr->lastState = (tsdPtr->lastState & ~ALL_BUTTONS) |
+	    (state & ALL_BUTTONS) | NOCURSOR_MASK;
+	return;
+    } else if (tsdPtr->lastState & NOCURSOR_MASK) {
+	tsdPtr->lastState = (tsdPtr->lastState & ~NOCURSOR_MASK & ~ALL_BUTTONS) |
+	    (state & ALL_BUTTONS);
+	return;
+    }
+    changes = ( state ^ tsdPtr->lastState) & ALL_BUTTONS;
     pos.x = x;
     pos.y = y;
 
@@ -253,6 +269,9 @@
     tsdPtr->lastState = (state & ~ALL_BUTTONS) | (tsdPtr->lastState
 	    & ALL_BUTTONS);
 
+    if (no_button_events) {
+	return;
+    }
     /*
      * Generate Enter/Leave events. If the pointer has crossed window
      * boundaries, update the current mouse position so we don't generate
@@ -278,7 +297,6 @@
 		 * ButtonPress - Set restrict window if we aren't grabbed, or
 		 * if this is the first button down.
 		 */
-
 		if (!tsdPtr->restrictWinPtr) {
 		    if (!tsdPtr->grabWinPtr) {
 			/*
@@ -287,7 +305,6 @@
 
 			tsdPtr->restrictWinPtr = winPtr;
 			TkpSetCapture(tsdPtr->restrictWinPtr);
-
 		    } else if ((tsdPtr->lastState & ALL_BUTTONS) == 0) {
 			/*
 			 * Mouse is in a non-button grab, so ensure the button
@@ -303,7 +320,6 @@
 			TkpSetCapture(tsdPtr->restrictWinPtr);
 		    }
 		}
-
 	    } else {
 		type = ButtonRelease;
 
@@ -312,7 +328,6 @@
 		 * restrict window when the last button is released and we
 		 * aren't in a global grab.
 		 */
-
 		if ((tsdPtr->lastState & ALL_BUTTONS) == mask) {
 		    if (!tsdPtr->grabWinPtr) {
 			TkpSetCapture(NULL);
Index: unix/tkUnixRFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixRFont.c,v
retrieving revision 1.20
diff -u -r1.20 tkUnixRFont.c
--- unix/tkUnixRFont.c	15 May 2007 19:37:58 -0000	1.20
+++ unix/tkUnixRFont.c	25 Oct 2007 02:13:42 -0000
@@ -35,6 +35,9 @@
     XftDraw *ftDraw;
     Drawable drawable;
     XftColor color;
+
+    int underline_position;
+    int underline_thickness;
 } UnixFtFont;
 
 
@@ -118,6 +121,7 @@
     FcCharSet *charset;
     FcResult result;
     XftFont *ftFont;
+    FT_Face ftFace;
 
     if (!fontPtr) {
 	fontPtr = (UnixFtFont *) ckalloc(sizeof(UnixFtFont));
@@ -167,9 +171,26 @@
     fontPtr->color.color.blue = 0;
     fontPtr->color.color.alpha = 0xffff;
     fontPtr->color.pixel = 0xffffffff;
-
     ftFont = GetFont(fontPtr, 0);
 
+    fontPtr->underline_position = -1;
+    fontPtr->underline_thickness = -1;
+
+    ftFace = XftLockFace(ftFont);
+    if (ftFace->face_flags & FT_FACE_FLAG_SCALABLE) {
+	fontPtr->underline_position = 
+	    - FT_MulFix(ftFace->underline_position,
+		    ftFace->size->metrics.y_scale) / 64; 
+	fontPtr->underline_thickness =
+	    (32 + FT_MulFix(ftFace->underline_thickness,
+			    ftFace->size->metrics.y_scale)) / 64; 
+    } 
+    if (fontPtr->underline_position <=0 || fontPtr->underline_thickness <=0) {
+	fontPtr->underline_position = ftFont->descent / 2;
+	fontPtr->underline_thickness = 1;
+    }
+    XftUnlockFace(ftFont);
+
     /*
      * Build the Tk font structure
      */
@@ -224,7 +245,7 @@
     faPtr->weight = weight;
     faPtr->slant = slant;
     faPtr->underline = 0;
-    faPtr->overstrike = 0;
+    faPtr->overstrike = 0; 
 
     fmPtr = &fontPtr->font.fm;
     fmPtr->ascent = ftFont->ascent;
@@ -283,7 +304,10 @@
 
     pattern = XftXlfdParse(name, FcFalse, FcFalse);
     if (!pattern) {
-	return NULL;
+	if (!strncmp("xft:",name,4)) 
+	    pattern = FcNameParse(name+4);
+	if (!pattern)
+	    return NULL;
     }
 
     /*
@@ -361,6 +385,8 @@
     if (!fontPtr) {
 	return NULL;
     }
+    fontPtr->font.fa.underline = faPtr->underline;
+    fontPtr->font.fa.overstrike = faPtr->overstrike;
     return &fontPtr->font;
 }
 
@@ -669,6 +695,16 @@
     int clen, nspec;
     XftGlyphFontSpec specs[NUM_SPEC];
     XGlyphInfo metrics;
+    int underlinePos, overstrikePos, barHeight;
+
+    underlinePos = fontPtr->underline_position;
+    barHeight = fontPtr->underline_thickness;
+
+    if (underlinePos <= 0) 
+	underlinePos = fontPtr->font.fm.descent / 2;
+    if (barHeight <= 0)
+	barHeight = 1;
+    overstrikePos = fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
 
     if (fontPtr->ftDraw == 0) {
 #if DEBUG_FONTSEL
@@ -721,6 +757,18 @@
 	    specs[nspec].y = y;
 	    XftGlyphExtents(fontPtr->display, ftFont, &specs[nspec].glyph, 1,
 		    &metrics);
+	    if (fontPtr->font.fa.underline != 0) {
+		XftDrawRect(fontPtr->ftDraw, &fontPtr->color,
+			x, y + underlinePos, 
+			(unsigned) metrics.xOff,
+			(unsigned) barHeight);
+	    }
+	    if (fontPtr->font.fa.overstrike != 0) {
+		XftDrawRect(fontPtr->ftDraw, &fontPtr->color,
+			x, y - overstrikePos, 
+			(unsigned) metrics.xOff,
+			(unsigned) barHeight);
+	    }
 	    x += metrics.xOff;
 	    y += metrics.yOff;
 	    nspec++;
Index: win/tkWinCursor.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinCursor.c,v
retrieving revision 1.14
diff -u -r1.14 tkWinCursor.c
--- win/tkWinCursor.c	8 Sep 2007 16:13:45 -0000	1.14
+++ win/tkWinCursor.c	25 Oct 2007 02:13:42 -0000
@@ -246,6 +246,10 @@
 TkpSetCursor(
     TkpCursor cursor)
 {
+   /* the hcursor_idc_help test is necessary for the CONTEXT HELP to work
+      by using wm attributes $window -contexthelp */
+ 
+    static HCURSOR hcursor_idc_help=NULL;
     HCURSOR hcursor;
     TkWinCursor *winCursor = (TkWinCursor *) cursor;
 
@@ -255,6 +259,8 @@
 	hcursor = winCursor->winCursor;
     }
 
+    if (hcursor_idc_help==NULL) hcursor_idc_help=LoadCursor(NULL,IDC_HELP);
+    if (GetCursor()==hcursor_idc_help) return;
     if (hcursor != NULL) {
 	SetCursor(hcursor);
     }
Index: win/tkWinDialog.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinDialog.c,v
retrieving revision 1.46
diff -u -r1.46 tkWinDialog.c
--- win/tkWinDialog.c	8 Sep 2007 16:13:45 -0000	1.46
+++ win/tkWinDialog.c	25 Oct 2007 02:13:43 -0000
@@ -234,15 +234,9 @@
 static void
 EatSpuriousMessageBugFix(void)
 {
-    MSG msg;
-    DWORD nTime = GetTickCount() + 250;
-
-    while (GetTickCount() < nTime) {
-	if (PeekMessage(&msg, 0, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE)){
-	    break;
-	}
-	PeekMessage(&msg, 0, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE);
-    }
+    int state = TkWinGetModifierState();
+    Tk_UpdatePointer(NULL,0,0, state | NOBUTTONEVENTS_MASK);
+    return;
 }
 
 /*
@@ -790,7 +784,7 @@
 	winCode = GetSaveFileNameW(&ofn);
     }
     Tcl_SetServiceMode(oldMode);
-    EatSpuriousMessageBugFix();
+    EatSpuriousMessageBugFix(); 
 
     /*
      * Ensure that hWnd is enabled, because it can happen that we have updated
Index: win/tkWinInt.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinInt.h,v
retrieving revision 1.28
diff -u -r1.28 tkWinInt.h
--- win/tkWinInt.h	4 May 2007 21:29:23 -0000	1.28
+++ win/tkWinInt.h	25 Oct 2007 02:13:43 -0000
@@ -40,6 +40,10 @@
 #define WS_EX_TOOLWINDOW	0x00000080L
 #endif
 
+#ifndef CS_DROPSHADOW
+#define CS_DROPSHADOW		0x00020000
+#endif
+
 /*
  * The TkWinDCState is used to save the state of a device context so that it
  * can be restored later.
@@ -121,6 +125,7 @@
  */
 
 #define TK_WIN_TOPLEVEL_CLASS_NAME "TkTopLevel"
+#define TK_WIN_TOPLEVEL_SHADOW_CLASS_NAME "TkTopLevelShadow"
 #define TK_WIN_TOPLEVEL_NOCDC_CLASS_NAME "TkTopLevelNoCDC"
 #define TK_WIN_CHILD_CLASS_NAME "TkChild"
 
Index: win/tkWinWm.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinWm.c,v
retrieving revision 1.119
diff -u -r1.119 tkWinWm.c
--- win/tkWinWm.c	15 Oct 2007 20:52:48 -0000	1.119
+++ win/tkWinWm.c	25 Oct 2007 02:13:47 -0000
@@ -332,6 +332,7 @@
 #define WM_HEIGHT_NOT_RESIZABLE		(1<<11)
 #define WM_WITHDRAWN			(1<<12)
 #define WM_FULLSCREEN			(1<<13)
+#define WM_DROPSHADOW			(1<<14)
 
 /*
  * Window styles for various types of toplevel windows.
@@ -946,9 +947,9 @@
 	     */
 
 #ifdef TCL_THREADS
-	    class.style = CS_HREDRAW | CS_VREDRAW;
+	    class.style = 0; //CS_HREDRAW | CS_VREDRAW;
 #else
-	    class.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
+	    class.style = /*CS_HREDRAW | CS_VREDRAW | */ CS_CLASSDC;
 #endif
 	    class.hInstance = Tk_GetHINSTANCE();
 	    Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_CLASS_NAME, -1, &classString);
@@ -982,7 +983,7 @@
 	     */
 
 	    if (setLayeredWindowAttributesProc != NULL) {
-		class.style = CS_HREDRAW | CS_VREDRAW;
+		class.style = 0; // CS_HREDRAW | CS_VREDRAW;
 		Tcl_DStringFree(&classString);
 		Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_NOCDC_CLASS_NAME,
 			-1, &classString);
@@ -992,6 +993,14 @@
 		}
 	    }
 #endif
+	    class.style = /* CS_HREDRAW | CS_VREDRAW | */ CS_DROPSHADOW;
+	    Tcl_DStringFree(&classString);
+	    Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_SHADOW_CLASS_NAME,
+		    -1, &classString);
+	    class.lpszClassName = (LPCTSTR) Tcl_DStringValue(&classString);
+	    if (!(*tkWinProcs->registerClass)(&class)) {
+		Tcl_Panic("Unable to register TkTopLevelShadow class");
+	    }
 	    Tcl_DStringFree(&classString);
 	}
 	Tcl_MutexUnlock(&winWmMutex);
@@ -1988,6 +1997,7 @@
     tsdPtr->initialized = 0;
 
     UnregisterClass(TK_WIN_TOPLEVEL_CLASS_NAME, hInstance);
+    UnregisterClass(TK_WIN_TOPLEVEL_SHADOW_CLASS_NAME, hInstance);
 
 #ifndef TCL_THREADS
     /*
@@ -2189,7 +2199,7 @@
 	    if (! ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
 		    (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE))) {
 		wmPtr->style |= WS_THICKFRAME;
-	    }
+	    } 
 	} else {
 	    wmPtr->style = WM_TOPLEVEL_STYLE;
 	    wmPtr->exStyle = EX_TOPLEVEL_STYLE;
@@ -2244,6 +2254,10 @@
 	Tcl_WinUtfToTChar(((wmPtr->title != NULL) ?
 		wmPtr->title : winPtr->nameUid), -1, &titleString);
 
+	if (wmPtr->flags & WM_DROPSHADOW) {
+	    Tcl_WinUtfToTChar(TK_WIN_TOPLEVEL_SHADOW_CLASS_NAME,
+		    -1, &classString);
+	} else
 #ifndef TCL_THREADS
 	/*
 	 * Transparent windows require a non-CS_CLASSDC window class.
@@ -3166,19 +3180,22 @@
     LONG style, exStyle, styleBit, *stylePtr = NULL;
     char *string;
     int i, boolean, length;
-    int config_fullscreen = 0;
+    int config_fullscreen = 0, config_shadow = 0;
     int fullscreen_attr_changed = 0, fullscreen_attr = 0;
+    int shadow_attr_changed = 0, shadow_attr = 0;
 
     if ((objc < 3) || ((objc > 5) && ((objc%2) == 0))) {
     configArgs:
 	Tcl_WrongNumArgs(interp, 2, objv,
 		"window"
 		" ?-alpha ?double??"
+		" ?-contexthelp ?bool??"
 		" ?-transparentcolor ?color??"
 		" ?-disabled ?bool??"
 		" ?-fullscreen ?bool??"
 		" ?-toolwindow ?bool??"
-		" ?-topmost ?bool??");
+		" ?-topmost ?bool??"
+		" ?-shadow ?bool??");
 	return TCL_ERROR;
     }
     exStyle = wmPtr->exStyleConfig;
@@ -3208,6 +3225,14 @@
 		Tcl_NewStringObj("-topmost", -1));
 	Tcl_ListObjAppendElement(NULL, objPtr,
 		Tcl_NewBooleanObj((exStyle & WS_EX_TOPMOST)));
+	Tcl_ListObjAppendElement(NULL, objPtr,
+		Tcl_NewStringObj("-contexthelp", -1));
+	Tcl_ListObjAppendElement(NULL, objPtr,
+		Tcl_NewBooleanObj((style & WS_EX_CONTEXTHELP)));
+	Tcl_ListObjAppendElement(NULL, objPtr,
+		Tcl_NewStringObj("-shadow", -1));
+	Tcl_ListObjAppendElement(NULL, objPtr,
+		Tcl_NewBooleanObj((wmPtr->flags & WM_DROPSHADOW)));
 	Tcl_SetObjResult(interp, objPtr);
 	return TCL_OK;
     }
@@ -3227,6 +3252,12 @@
 	} else if (strncmp(string, "-fullscreen", (unsigned) length) == 0) {
 	    config_fullscreen = 1;
 	    styleBit = 0;
+	} else if (strncmp(string, "-shadow", (unsigned) length) == 0) {
+	    config_shadow = 1;
+	    styleBit = 0;
+	} else if (strncmp(string,"-contexthelp", length) == 0) {
+	    stylePtr=&exStyle;
+	    styleBit=WS_EX_CONTEXTHELP;
 	} else if ((length > 3)
 		&& (strncmp(string, "-toolwindow", (unsigned) length) == 0)) {
 	    stylePtr = &exStyle;
@@ -3334,6 +3365,15 @@
 		    fullscreen_attr = boolean;
 		}
 		config_fullscreen = 0;
+	    } else if (config_shadow) {
+		if (objc == 4) {
+		    Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
+			(wmPtr->flags & WM_DROPSHADOW));
+		} else {
+		    shadow_attr_changed = 1;
+		    shadow_attr = boolean;
+		}
+		config_shadow = 0;
 	    } else if (objc == 4) {
 		Tcl_SetIntObj(Tcl_GetObjResult(interp),
 			((*stylePtr & styleBit) != 0));
@@ -3408,6 +3448,16 @@
 
 	TkpWmSetFullScreen(winPtr, fullscreen_attr);
     }
+    if (shadow_attr_changed) {
+	if (shadow_attr) {
+	    wmPtr->flags |= WM_DROPSHADOW;
+	} else {
+	    wmPtr->flags &= ~WM_DROPSHADOW;
+	}
+	if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+	    UpdateWrapper(winPtr);
+	}
+    }
 
     return TCL_OK;
 }
@@ -7845,6 +7895,8 @@
 				 * one level deep. */
     LRESULT result;
     TkWindow *winPtr = NULL;
+    POINT point;
+    int state;
 
     switch (message) {
     case WM_KILLFOCUS:
@@ -7968,6 +8020,19 @@
 	break;
     }
 
+    case WM_NCLBUTTONDOWN:
+    case WM_NCLBUTTONDBLCLK:
+    case WM_NCLBUTTONUP:
+    case WM_NCMBUTTONDOWN:
+    case WM_NCMBUTTONDBLCLK:
+    case WM_NCMBUTTONUP:
+    case WM_NCRBUTTONDOWN:
+    case WM_NCRBUTTONDBLCLK:
+    case WM_NCRBUTTONUP: 
+	Tk_UpdatePointer(NULL, 0, 0, TkWinGetModifierState() | NOCURSOR_MASK);
+	result = (*tkWinProcs->defWindowProc)(hwnd, message, wParam, lParam);
+	goto done;
+
     case WM_MOUSEACTIVATE: {
 	ActivateEvent *eventPtr;
 	winPtr = GetTopLevel((HWND) wParam);
Index: win/tkWinX.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinX.c,v
retrieving revision 1.55
diff -u -r1.55 tkWinX.c
--- win/tkWinX.c	11 Jan 2007 15:35:41 -0000	1.55
+++ win/tkWinX.c	25 Oct 2007 02:13:48 -0000
@@ -284,9 +284,9 @@
      */
 
 #ifdef TCL_THREADS
-    childClass.style = CS_HREDRAW | CS_VREDRAW;
+    childClass.style = 0; // CS_HREDRAW | CS_VREDRAW;
 #else
-    childClass.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
+    childClass.style = /* CS_HREDRAW | CS_VREDRAW | */ CS_CLASSDC;
 #endif
 
     childClass.cbClsExtra = 0;
@@ -794,6 +794,62 @@
 /*
  *----------------------------------------------------------------------
  *
+ * HelpEvent --
+ *
+ *	Generates a "Help" virtual event. This can be used to do
+ *	context-sensitive help.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Places a virtual event on the event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HelpEvent (hwnd, message, wParam, lParam)
+    HWND hwnd;
+    UINT message;
+    WPARAM wParam;
+    LPARAM lParam;
+{
+    TkWindow *winPtr = (TkWindow *)Tk_HWNDToWindow(hwnd);
+    XVirtualEvent event;
+    POINTS rootPoint;
+    POINT clientPoint;
+    DWORD msgPos;
+
+    event.type = VirtualEvent;
+    event.serial = winPtr->display->request;
+    event.send_event = 0;
+    event.display = winPtr->display;
+    event.event = Tk_WindowId(winPtr);
+    event.root = XRootWindow(winPtr->display, 0);
+    event.subwindow = None;
+    event.time = TkpGetMS();
+
+    msgPos = GetMessagePos();
+    rootPoint = MAKEPOINTS(msgPos);
+    clientPoint.x = rootPoint.x;
+    clientPoint.y = rootPoint.y;
+    ScreenToClient(hwnd, &clientPoint);
+    event.x = clientPoint.x;
+    event.y = clientPoint.y;
+    event.x_root = rootPoint.x;
+    event.y_root = rootPoint.y;
+    event.state = TkWinGetModifierState();
+    event.same_screen = 1;
+    event.name = Tk_GetUid("Help");
+    event.user_data = NULL;
+    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
  * TkWinChildProc --
  *
  *	Callback from Windows whenever an event occurs on a child window.
@@ -817,6 +873,13 @@
     LRESULT result;
 
     switch (message) {
+
+    case WM_HELP:
+	Tk_UpdatePointer(NULL, 0, 0, TkWinGetModifierState() | NOBUTTONEVENTS_MASK);
+	HelpEvent(hwnd, message, wParam, lParam);
+	result = DefWindowProc(hwnd, message, wParam, lParam);
+	break;
+
     case WM_INPUTLANGCHANGE:
 	UpdateInputLanguage((int) wParam);
 	result = 1;
@@ -962,6 +1025,7 @@
     case WM_MBUTTONUP:
     case WM_RBUTTONUP:
     case WM_MOUSEMOVE:
+
 	Tk_PointerEvent(hwnd, (short) LOWORD(lParam), (short) HIWORD(lParam));
 	return 1;