diff options
Diffstat (limited to 'src/modules/options/iconheader.cpp')
-rw-r--r-- | src/modules/options/iconheader.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/src/modules/options/iconheader.cpp b/src/modules/options/iconheader.cpp new file mode 100644 index 0000000000..9ab44b0852 --- /dev/null +++ b/src/modules/options/iconheader.cpp @@ -0,0 +1,545 @@ +/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2007 Artem Shpynov
+Copyright 2000-2007 Miranda ICQ/IM project,
+
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "m_iconheader.h"
+
+
+extern HINSTANCE hMirandaInst;
+
+static BOOL IsAeroMode()
+{
+ BOOL result;
+ return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
+
+static BOOL IsVSMode()
+{
+ return isThemeActive && IsWinVerVistaPlus() && isThemeActive();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Internals
+
+#define ITC_BORDER_SIZE 3
+
+static LRESULT CALLBACK MIcoTabWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// structure is used for storing list of tab info
+struct MIcoTabCtrl
+{
+ __inline void* operator new( size_t size )
+ { return mir_calloc( size );
+ }
+ __inline void operator delete( void* p )
+ { mir_free( p );
+ }
+
+ MIcoTabCtrl(): pList(1) {}
+
+ HWND hwnd;
+ int nSelectedIdx, nHotIdx;
+ LIST<MIcoTab> pList;
+
+ // UI info
+ BOOL bMouseInside;
+ RECT rc;
+ int width, height;
+ int itemWidth, itemHeight;
+
+ //background bitmap
+ HBITMAP hBkgBmp;
+ HBITMAP hBkgOldBmp;
+ HDC hBkgDC;
+ SIZE BkgSize;
+
+ // control colors
+ RGBQUAD rgbBkgTop, rgbBkgBottom;
+ RGBQUAD rgbSelTop, rgbSelBottom;
+ RGBQUAD rgbHotTop, rgbHotBottom;
+ COLORREF clText;
+ COLORREF clSelText, clSelBorder;
+ COLORREF clHotText, clHotBorder;
+
+ // fonts
+ HFONT hFont;
+};
+
+typedef void (*ItemDestuctor)(void*);
+
+static void MITListDestructor(void * adr)
+{
+ MIcoTab * mit=(MIcoTab *)adr;
+ mir_free(mit->tcsName);
+ if (mit->hIcon && !(mit->flag&MITCF_SHAREDICON))
+ DestroyIcon(mit->hIcon);
+ mir_free(adr);
+}
+
+void li_ListDestruct(LIST<MIcoTab> &pList, ItemDestuctor pItemDestructor)
+{
+ for (int i=0; i<pList.getCount(); i++) pItemDestructor(pList[i]);
+ pList.destroy();
+}
+
+int LoadIcoTabsModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = MIRANDAICOTABCLASS;
+ wc.lpfnWndProc = MIcoTabWndProc;
+// wc.hCursor = LoadCursor(NULL, IDC_HAND);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(MIcoTabCtrl*);
+ wc.hbrBackground = 0; //GetStockObject(WHITE_BRUSH);
+ wc.style = CS_GLOBALCLASS/*|CS_SAVEBITS*/;
+ RegisterClassEx(&wc);
+ return 0;
+}
+
+static void MIcoTab_SetupColors(MIcoTabCtrl *dat)
+{
+ COLORREF cl;
+
+ cl = GetSysColor(COLOR_WINDOW);
+ dat->rgbBkgBottom.rgbRed = (dat->rgbBkgTop.rgbRed = GetRValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbGreen = (dat->rgbBkgTop.rgbGreen = GetGValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbBlue = (dat->rgbBkgTop.rgbBlue = GetBValue(cl)) * .95;
+
+ cl = GetSysColor(COLOR_HIGHLIGHT);
+ dat->rgbSelTop.rgbRed = (dat->rgbSelBottom.rgbRed = GetRValue(cl)) * .75;
+ dat->rgbSelTop.rgbGreen = (dat->rgbSelBottom.rgbGreen = GetGValue(cl)) * .75;
+ dat->rgbSelTop.rgbBlue = (dat->rgbSelBottom.rgbBlue = GetBValue(cl)) * .75;
+
+ dat->rgbHotTop.rgbRed = (dat->rgbSelTop.rgbRed + 255) / 2;
+ dat->rgbHotTop.rgbGreen = (dat->rgbSelTop.rgbGreen + 255) / 2;
+ dat->rgbHotTop.rgbBlue = (dat->rgbSelTop.rgbBlue + 255) / 2;
+
+ dat->rgbHotBottom.rgbRed = (dat->rgbSelBottom.rgbRed + 255) / 2;
+ dat->rgbHotBottom.rgbGreen = (dat->rgbSelBottom.rgbGreen + 255) / 2;
+ dat->rgbHotBottom.rgbBlue = (dat->rgbSelBottom.rgbBlue + 255) / 2;
+
+ dat->clText = GetSysColor(COLOR_WINDOWTEXT);
+ dat->clSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ dat->clSelBorder = RGB(dat->rgbSelTop.rgbRed, dat->rgbSelTop.rgbGreen, dat->rgbSelTop.rgbBlue);
+ dat->clHotBorder = RGB(dat->rgbHotTop.rgbRed, dat->rgbHotTop.rgbGreen, dat->rgbHotTop.rgbBlue);
+
+ if (!dat->hFont) dat->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+}
+
+static void MIcoTab_FillRect(HDC hdc, int x, int y, int width, int height, COLORREF cl)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, cl);
+
+ RECT rc; SetRect(&rc, x, y, x+width, y+height);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MIcoTab_DrawGradient(HDC hdc, int x, int y, int width, int height, RGBQUAD *rgb0, RGBQUAD *rgb1)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, 0);
+
+ RECT rc; SetRect(&rc, x, 0, x+width, 0);
+ for ( int i=y+height; --i >= y; ) {
+ COLORREF color = RGB(
+ ((height-i-1)*rgb0->rgbRed + i*rgb1->rgbRed) / height,
+ ((height-i-1)*rgb0->rgbGreen + i*rgb1->rgbGreen) / height,
+ ((height-i-1)*rgb0->rgbBlue + i*rgb1->rgbBlue) / height);
+ rc.top = rc.bottom = i;
+ ++rc.bottom;
+ SetBkColor(hdc, color);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+ }
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MIcoTab_DrawItem(HWND hwnd, HDC hdc, MIcoTabCtrl *dat, MIcoTab *tab, int i)
+{
+ int iTopSpace = IsAeroMode() ? 0 : ITC_BORDER_SIZE;
+ int itemX = ITC_BORDER_SIZE + dat->itemWidth * i;
+ int iconTop = iTopSpace + 5;
+ int textTop = iconTop + 32 + 3;
+
+ HFONT hFntSave = NULL;
+
+ if (dat->nSelectedIdx == i) {
+ LOGFONT lf;
+ GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFntSave = (HFONT)SelectObject(hdc, CreateFontIndirect(&lf));
+
+ if (IsVSMode()) {
+ RECT rc;
+ rc.left = itemX;
+ rc.top = iTopSpace;
+ rc.right = itemX + dat->itemWidth;
+ rc.bottom = iTopSpace + dat->itemHeight;
+ HANDLE hTheme = openThemeData(hwnd, L"ListView");
+ if (dat->nHotIdx == i || GetFocus() == hwnd)
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOTSELECTED, &rc, NULL);
+ else
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_SELECTED, &rc, NULL);
+
+ closeThemeData(hTheme);
+ }
+ else {
+ MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clSelBorder);
+ MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbSelTop, &dat->rgbSelBottom);
+ }
+ SetTextColor(hdc, dat->clSelText);
+ }
+ else if (dat->nHotIdx == i) {
+ if (IsVSMode()) {
+ RECT rc;
+ rc.left = itemX;
+ rc.top = iTopSpace;
+ rc.right = itemX + dat->itemWidth;
+ rc.bottom = iTopSpace + dat->itemHeight;
+ setWindowTheme(hwnd, L"explorer", NULL);
+ HANDLE hTheme = openThemeData(hwnd, L"ListView");
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOT, &rc, NULL);
+ closeThemeData(hTheme);
+ }
+ else {
+ MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clHotBorder);
+ MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbHotTop, &dat->rgbHotBottom);
+ }
+ SetTextColor(hdc, dat->clHotText);
+ }
+ else SetTextColor(hdc, dat->clText);
+
+ RECT textRect;
+ textRect.left=itemX;
+ textRect.right=itemX+dat->itemWidth;
+ textRect.top=textTop;
+ textRect.bottom=iconTop+dat->itemHeight;
+ DrawIcon(hdc,itemX+dat->itemWidth/2-16, iconTop, tab->hIcon);
+
+ if (IsVSMode()) {
+ DTTOPTS dto = {0};
+ dto.dwSize = sizeof(dto);
+ dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE;
+ dto.iGlowSize = 10;
+ HANDLE hTheme = openThemeData(hwnd, L"Window");
+ wchar_t *tcsNameW = mir_t2u(tab->tcsName);
+ drawThemeTextEx(hTheme, hdc, WP_CAPTION, CS_ACTIVE, tcsNameW, -1, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS, &textRect, &dto);
+ mir_free(tcsNameW);
+ closeThemeData(hTheme);
+ }
+ else DrawText(hdc,tab->tcsName,-1,&textRect, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS);
+
+ if (hFntSave)
+ DeleteObject(SelectObject(hdc, hFntSave));
+}
+
+static LRESULT MIcoTab_OnPaint(HWND hwndDlg, MIcoTabCtrl *mit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ HBITMAP hBmp, hOldBmp;
+ RECT temprc;
+ int i;
+
+ HDC hdc=BeginPaint(hwndDlg,&ps);
+ HDC tempDC=CreateCompatibleDC(hdc);
+
+ HFONT hFont = 0;
+
+ BITMAPINFO bmi;
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = mit->width;
+ bmi.bmiHeader.biHeight = -mit->height; // we need this for DrawThemeTextEx
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
+
+ hOldBmp=(HBITMAP)SelectObject(tempDC,hBmp);
+
+ if (IsAeroMode()) {
+ temprc.left=0;
+ temprc.right=mit->width;
+ temprc.top=0;
+ temprc.bottom=mit->width;
+ FillRect(tempDC, &temprc, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ }
+ else {
+ if (mit->hBkgBmp)
+ StretchBlt(tempDC,0,0,mit->width,mit->height,mit->hBkgDC,0,0,mit->BkgSize.cx,mit->BkgSize.cy,SRCCOPY);
+ else {
+ if (IsVSMode())
+ MIcoTab_FillRect(tempDC, 0, 0, mit->width, mit->height, GetSysColor(COLOR_WINDOW));
+ else
+ MIcoTab_DrawGradient(tempDC, 0, 0, mit->width, mit->height, &mit->rgbBkgTop, &mit->rgbBkgBottom);
+
+ MIcoTab_FillRect(tempDC, 0, mit->height-2, mit->width, 1, GetSysColor(COLOR_BTNSHADOW));
+ MIcoTab_FillRect(tempDC, 0, mit->height-1, mit->width, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
+ } }
+
+ //Draw Items
+ hFont = mit->hFont;
+ SelectObject(tempDC,hFont);
+ SetBkMode(tempDC,TRANSPARENT);
+
+ for (i=0; i<mit->pList.getCount(); i++) {
+ MIcoTab *tab = (MIcoTab *)mit->pList[i];
+ MIcoTab_DrawItem(hwndDlg, tempDC, mit, tab, i);
+ }
+
+ //Copy to output
+ BitBlt(hdc,mit->rc.left,mit->rc.top,mit->width,mit->height,tempDC,0,0,SRCCOPY);
+ SelectObject(tempDC,hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+
+ EndPaint(hwndDlg,&ps);
+
+ return TRUE;
+}
+
+static LRESULT CALLBACK MIcoTabWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MIcoTabCtrl* itc = (MIcoTabCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch(msg) {
+ case WM_NCCREATE:
+ itc = new MIcoTabCtrl; //(MIcoTabCtrl*)mir_alloc(sizeof(MIcoTabCtrl));
+ if (itc==NULL) return FALSE;
+ itc->nSelectedIdx=-1;
+ itc->nHotIdx=-1;
+ itc->bMouseInside = FALSE;
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)itc);
+ MIcoTab_SetupColors(itc);
+
+ if (IsAeroMode()) {
+ RECT rc; GetWindowRect(hwndDlg, &rc);
+ MARGINS margins = {0,0,rc.bottom-rc.top,0};
+ dwmExtendFrameIntoClientArea(GetParent(hwndDlg), &margins);
+ }
+
+ return TRUE;
+
+ case WM_SETFONT:
+ itc->hFont = (HFONT)wParam;
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwndDlg,&itc->rc);
+ itc->width=itc->rc.right-itc->rc.left;
+ itc->height=itc->rc.bottom-itc->rc.top;
+
+ if (itc->pList.getCount()) {
+ itc->itemWidth=(itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount();
+ itc->itemHeight=itc->height-2*ITC_BORDER_SIZE-2;
+ }
+ else itc->itemWidth = itc->itemHeight = 0;
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_STYLECHANGED:
+ MIcoTab_SetupColors(itc);
+ return TRUE;
+
+ case WM_MOUSEMOVE:
+ if (!itc->bMouseInside) {
+ TRACKMOUSEEVENT tme = {0};
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwndDlg;
+ _TrackMouseEvent(&tme);
+ itc->bMouseInside = TRUE;
+ }
+
+ itc->nHotIdx = (LOWORD(lParam) - ITC_BORDER_SIZE) / itc->itemWidth;
+ if (itc->nHotIdx >= itc->pList.getCount())
+ itc->nHotIdx = -1;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return 0;
+
+ case WM_MOUSELEAVE:
+ itc->bMouseInside = FALSE;
+ itc->nHotIdx = -1;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return 0;
+
+ case WM_LBUTTONUP:
+ if ((itc->nHotIdx >= 0) && (itc->nHotIdx != itc->nSelectedIdx))
+ {
+ itc->nSelectedIdx = itc->nHotIdx;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED),
+ itc->nSelectedIdx);
+ }
+ return 0;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ break;
+
+ case WM_MOUSEACTIVATE:
+ SetFocus(hwndDlg);
+ return MA_ACTIVATE;
+
+ case WM_GETDLGCODE:
+ {
+ if (lParam)
+ {
+ MSG *msg = (MSG *) lParam;
+ if (msg->message == WM_KEYDOWN)
+ {
+ if (msg->wParam == VK_TAB)
+ return 0;
+ if (msg->wParam == VK_ESCAPE)
+ return 0;
+ } else
+ if (msg->message == WM_CHAR)
+ {
+ if (msg->wParam == '\t')
+ return 0;
+ if (msg->wParam == 27)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+ }
+
+ case WM_KEYDOWN:
+ {
+ int newIdx = itc->nSelectedIdx;
+ switch (wParam)
+ {
+ case VK_NEXT:
+ case VK_RIGHT:
+ newIdx++;
+ break;
+ case VK_PRIOR:
+ case VK_LEFT:
+ newIdx--;
+ break;
+ }
+ if ((newIdx >= 0) && (newIdx < itc->pList.getCount()) && (newIdx != itc->nSelectedIdx))
+ {
+ itc->nSelectedIdx = newIdx;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGEDKBD),
+ itc->nSelectedIdx);
+ }
+ return 0;
+ }
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_NCPAINT:
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ break;
+
+ case WM_PAINT:
+ MIcoTab_OnPaint(hwndDlg, itc, msg, wParam, lParam);
+ break;
+
+ case ITCM_SETBACKGROUND:
+ itc->hBkgBmp=(HBITMAP)lParam;
+ if (!itc->hBkgDC)
+ itc->hBkgDC = CreateCompatibleDC(NULL);
+ itc->hBkgOldBmp = (HBITMAP)SelectObject(itc->hBkgDC, itc->hBkgBmp);
+ {
+ BITMAPINFO bmp;
+ GetObject(itc->hBkgBmp, sizeof(bmp), &bmp);
+ itc->BkgSize.cx=bmp.bmiHeader.biWidth;
+ itc->BkgSize.cy=bmp.bmiHeader.biHeight;
+ }
+ return TRUE;
+
+ case ITCM_ADDITEM:
+ {
+ MIcoTab* pMit=(MIcoTab *)wParam;
+ if (!pMit)
+ return FALSE;
+
+ MIcoTab* pListMit=(MIcoTab *)mir_calloc(sizeof(MIcoTab));
+ pListMit->flag=pMit->flag;
+ pListMit->data=pMit->data;
+ if (pMit->flag & MITCF_UNICODE)
+ pListMit->tcsName=mir_u2t(pMit->lpwzName);
+ else
+ pListMit->tcsName=mir_a2t(pMit->lpzName);
+ if (pMit->hIcon) {
+ if (pListMit->flag&MITCF_SHAREDICON)
+ pListMit->hIcon=pMit->hIcon;
+ else
+ pListMit->hIcon=CopyIcon(pMit->hIcon);
+ }
+ itc->pList.insert(pListMit);
+
+ itc->itemWidth=(itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount();
+ itc->itemHeight=itc->height-2*ITC_BORDER_SIZE-2;
+
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return TRUE;
+ }
+
+ case ITCM_SETSEL:
+ if ( wParam >= 0 && (int)wParam < itc->pList.getCount()) {
+ itc->nSelectedIdx = wParam;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED),
+ itc->nSelectedIdx);
+ }
+ return TRUE;
+
+ case ITCM_GETSEL:
+ return itc->nSelectedIdx;
+
+ case ITCM_GETITEMDATA:
+ if ( wParam >= 0 && (int)wParam < itc->pList.getCount())
+ return ((MIcoTab *)itc->pList[wParam])->data;
+ return 0;
+
+ case WM_DESTROY:
+ if (itc->hBkgDC) {
+ SelectObject(itc->hBkgDC, itc->hBkgOldBmp);
+ DeleteDC(itc->hBkgDC);
+ }
+ li_ListDestruct(itc->pList,MITListDestructor);
+ delete itc;
+ return TRUE;
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
|