IPC window operation tweaks:
- Enable targeting multiple windows (wildcard in name spec). - Enable non-group operations. SVN revision: 22964
This commit is contained in:
parent
e571517853
commit
d05850e996
|
@ -387,7 +387,6 @@ EWin *EwinFindByPtr(const EWin * ewin);
|
||||||
EWin *EwinFindByFrame(Window win);
|
EWin *EwinFindByFrame(Window win);
|
||||||
EWin *EwinFindByClient(Window win);
|
EWin *EwinFindByClient(Window win);
|
||||||
EWin *EwinFindByChildren(Window win);
|
EWin *EwinFindByChildren(Window win);
|
||||||
EWin *EwinFindByString(const char *win, int type);
|
|
||||||
EWin **EwinListTransients(const EWin * ewin, int *num, int group);
|
EWin **EwinListTransients(const EWin * ewin, int *num, int group);
|
||||||
EWin **EwinListTransientFor(const EWin * ewin, int *num);
|
EWin **EwinListTransientFor(const EWin * ewin, int *num);
|
||||||
|
|
||||||
|
|
|
@ -96,52 +96,6 @@ EwinFindByChildren(Window win)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EWin *
|
|
||||||
EwinFindByString(const char *match, int type)
|
|
||||||
{
|
|
||||||
EWin *ewin = NULL;
|
|
||||||
EWin *const *ewins;
|
|
||||||
int i, num, len;
|
|
||||||
char ewinid[FILEPATH_LEN_MAX];
|
|
||||||
const char *name;
|
|
||||||
|
|
||||||
len = strlen(match);
|
|
||||||
if (len <= 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
ewins = EwinListGetAll(&num);
|
|
||||||
if (ewins == NULL)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
for (i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
if (type == '+')
|
|
||||||
{
|
|
||||||
/* Match start of window ID */
|
|
||||||
sprintf(ewinid, "%x", (unsigned)_EwinGetClientXwin(ewins[i]));
|
|
||||||
if (strncmp(ewinid, match, len))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (type == '=')
|
|
||||||
{
|
|
||||||
/* Match name (substring) */
|
|
||||||
name = ewins[i]->icccm.wm_name;
|
|
||||||
if (!name)
|
|
||||||
continue;
|
|
||||||
if (!strstr(name, match))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
ewin = ewins[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
return ewin;
|
|
||||||
}
|
|
||||||
|
|
||||||
EWin **
|
EWin **
|
||||||
ListWinGroupMembersForEwin(const EWin * ewin, int action, char nogroup,
|
ListWinGroupMembersForEwin(const EWin * ewin, int action, char nogroup,
|
||||||
int *pnum)
|
int *pnum)
|
||||||
|
|
333
src/ipc.c
333
src/ipc.c
|
@ -79,28 +79,132 @@ IpcPrintf(const char *fmt, ...)
|
||||||
bufsiz += len;
|
bufsiz += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static EWin *
|
static EWin **
|
||||||
IpcFindEwin(const char *windowid)
|
IpcFindEwins(const char *match, int *pnum, int *pflags)
|
||||||
|
{
|
||||||
|
EWin *ewin, **lst;
|
||||||
|
EWin *const *ewins;
|
||||||
|
int type;
|
||||||
|
int i, num, len, nfound, match_one, flags;
|
||||||
|
|
||||||
|
if (pnum)
|
||||||
|
*pnum = 0;
|
||||||
|
|
||||||
|
if (!match || !match[0])
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ewin = NULL;
|
||||||
|
flags = 0;
|
||||||
|
|
||||||
|
if (!strcmp(match, "*") || !strcmp(match, "=") || !strcmp(match, "current"))
|
||||||
|
{
|
||||||
|
ewin = GetContextEwin();
|
||||||
|
if (match[0] == '=')
|
||||||
|
flags = 1; /* Nogroup */
|
||||||
|
goto do_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit(match[0]))
|
||||||
{
|
{
|
||||||
unsigned int win;
|
unsigned int win;
|
||||||
|
|
||||||
if (!strcmp(windowid, "*") || !strcmp(windowid, "%")
|
sscanf(match, "%x", &win);
|
||||||
|| !strcmp(windowid, "current"))
|
ewin = EwinFindByChildren(win);
|
||||||
return GetContextEwin();
|
goto do_one;
|
||||||
|
|
||||||
if (isdigit(windowid[0]))
|
|
||||||
{
|
|
||||||
sscanf(windowid, "%x", &win);
|
|
||||||
return EwinFindByChildren(win);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windowid[0] == '+')
|
match_one = 1;
|
||||||
return EwinFindByString(windowid + 1, '+');
|
if (!strcmp(match, "all"))
|
||||||
|
{
|
||||||
|
type = 'a';
|
||||||
|
match_one = 0;
|
||||||
|
flags = 1; /* Nogroup */
|
||||||
|
}
|
||||||
|
else if (match[0] == '=')
|
||||||
|
{
|
||||||
|
type = 's';
|
||||||
|
match++;
|
||||||
|
flags = 1; /* Nogroup */
|
||||||
|
}
|
||||||
|
else if (strchr(match, '*'))
|
||||||
|
{
|
||||||
|
type = 'w';
|
||||||
|
match_one = 0;
|
||||||
|
flags = 1; /* Nogroup */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = 's';
|
||||||
|
}
|
||||||
|
|
||||||
if (windowid[0] == '=')
|
len = strlen(match);
|
||||||
return EwinFindByString(windowid + 1, '=');
|
if (len <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return EwinFindByString(windowid, '=');
|
ewins = EwinListGetAll(&num);
|
||||||
|
if (!ewins)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nfound = 0;
|
||||||
|
lst = NULL;
|
||||||
|
for (i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
ewin = ewins[i];
|
||||||
|
|
||||||
|
if (type == 'a') /* All */
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (type == 'w') /* Wildcard */
|
||||||
|
{
|
||||||
|
if (!matchregexp(match, ewin->icccm.wm_name))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else /* Match name (substring) */
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
name = ewin->icccm.wm_name;
|
||||||
|
if (!name)
|
||||||
|
continue;
|
||||||
|
if (!strstr(name, match))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nfound++;
|
||||||
|
lst = Erealloc(lst, nfound * sizeof(EWin *));
|
||||||
|
lst[nfound - 1] = ewin;
|
||||||
|
if (match_one)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
do_one:
|
||||||
|
if (!ewin)
|
||||||
|
return NULL;
|
||||||
|
nfound = 1;
|
||||||
|
lst = Emalloc(sizeof(EWin *));
|
||||||
|
if (!lst)
|
||||||
|
return NULL;
|
||||||
|
lst[0] = ewin;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (pnum)
|
||||||
|
*pnum = nfound;
|
||||||
|
if (pflags)
|
||||||
|
*pflags = flags;
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EWin *
|
||||||
|
IpcFindEwin(const char *match)
|
||||||
|
{
|
||||||
|
EWin *ewin, **lst;
|
||||||
|
|
||||||
|
lst = IpcFindEwins(match, NULL, NULL);
|
||||||
|
if (!lst)
|
||||||
|
return NULL;
|
||||||
|
ewin = lst[0];
|
||||||
|
Efree(lst);
|
||||||
|
return ewin;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -315,18 +419,33 @@ static void
|
||||||
IPC_WinList(const char *params, Client * c __UNUSED__)
|
IPC_WinList(const char *params, Client * c __UNUSED__)
|
||||||
{
|
{
|
||||||
static const char *const TxtPG[] = { "NW", "NE", "SW", "SE" };
|
static const char *const TxtPG[] = { "NW", "NE", "SW", "SE" };
|
||||||
char param1[FILEPATH_LEN_MAX];
|
char format[8];
|
||||||
EWin *const *lst, *e;
|
const char *match;
|
||||||
|
EWin **lst, *e;
|
||||||
int num, i;
|
int num, i;
|
||||||
|
|
||||||
param1[0] = '\0';
|
format[0] = '\0';
|
||||||
word(params, 1, param1);
|
match = params;
|
||||||
|
if (match)
|
||||||
|
{
|
||||||
|
num = 0;
|
||||||
|
sscanf(params, "%8s %n", format, &num);
|
||||||
|
match += num;
|
||||||
|
}
|
||||||
|
if (!match || !match[0])
|
||||||
|
match = "all";
|
||||||
|
|
||||||
|
lst = IpcFindEwins(match, &num, NULL);
|
||||||
|
if (!lst)
|
||||||
|
{
|
||||||
|
IpcPrintf("No windows matching %s\n", match);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lst = EwinListGetAll(&num);
|
|
||||||
for (i = 0; i < num; i++)
|
for (i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
e = lst[i];
|
e = lst[i];
|
||||||
switch (param1[0])
|
switch (format[0])
|
||||||
{
|
{
|
||||||
case '\0':
|
case '\0':
|
||||||
IpcPrintf("%#lx : %s\n", _EwinGetClientXwin(e),
|
IpcPrintf("%#lx : %s\n", _EwinGetClientXwin(e),
|
||||||
|
@ -366,8 +485,7 @@ IPC_WinList(const char *params, Client * c __UNUSED__)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (num <= 0)
|
Efree(lst);
|
||||||
IpcPrintf("No windows\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 /* TBD */
|
#if 0 /* TBD */
|
||||||
|
@ -398,51 +516,15 @@ doMoveConstrainedNoGroup(EWin * ewin, const char *params)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
IPC_WinOps(const char *params, Client * c __UNUSED__)
|
IpcWinop(const WinOp * wop, EWin * ewin, const char *prm)
|
||||||
{
|
{
|
||||||
EWin *ewin;
|
char param1[128], param2[128];
|
||||||
char windowid[FILEPATH_LEN_MAX];
|
|
||||||
char operation[FILEPATH_LEN_MAX];
|
|
||||||
char param1[FILEPATH_LEN_MAX];
|
|
||||||
const char *p;
|
|
||||||
const WinOp *wop;
|
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
char on;
|
char on;
|
||||||
int a, b;
|
int a, b;
|
||||||
|
|
||||||
if (params == NULL)
|
param1[0] = param2[0] = '\0';
|
||||||
{
|
sscanf(prm, "%128s %128s", param1, param2);
|
||||||
IpcPrintf("Error: no window specified");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
operation[0] = 0;
|
|
||||||
param1[0] = 0;
|
|
||||||
|
|
||||||
windowid[0] = 0;
|
|
||||||
word(params, 1, windowid);
|
|
||||||
ewin = IpcFindEwin(windowid);
|
|
||||||
if (!ewin)
|
|
||||||
{
|
|
||||||
IpcPrintf("Error: no such window: %s", windowid);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
word(params, 2, operation);
|
|
||||||
word(params, 3, param1);
|
|
||||||
|
|
||||||
if (!operation[0])
|
|
||||||
{
|
|
||||||
IpcPrintf("Error: no operation specified");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
wop = EwinOpFind(operation);
|
|
||||||
if (!wop)
|
|
||||||
{
|
|
||||||
IpcPrintf("Error: unknown operation");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (wop->op)
|
switch (wop->op)
|
||||||
{
|
{
|
||||||
|
@ -466,19 +548,18 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EWIN_OP_TITLE:
|
case EWIN_OP_TITLE:
|
||||||
p = atword(params, 3);
|
if (!prm[0])
|
||||||
if (!p)
|
|
||||||
{
|
{
|
||||||
IpcPrintf("Error: no title specified");
|
IpcPrintf("Error: no title specified");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (!strcmp(p, "?"))
|
if (!strcmp(prm, "?"))
|
||||||
{
|
{
|
||||||
IpcPrintf("title: %s", ewin->icccm.wm_name);
|
IpcPrintf("title: %s", ewin->icccm.wm_name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
_EFREE(ewin->icccm.wm_name);
|
_EFREE(ewin->icccm.wm_name);
|
||||||
ewin->icccm.wm_name = Estrdup(p);
|
ewin->icccm.wm_name = Estrdup(prm);
|
||||||
XStoreName(disp, _EwinGetClientXwin(ewin), ewin->icccm.wm_name);
|
XStoreName(disp, _EwinGetClientXwin(ewin), ewin->icccm.wm_name);
|
||||||
EwinBorderUpdateInfo(ewin);
|
EwinBorderUpdateInfo(ewin);
|
||||||
break;
|
break;
|
||||||
|
@ -555,14 +636,15 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
else if (!strcmp(param1, "move"))
|
else if (!strcmp(param1, "move"))
|
||||||
{
|
{
|
||||||
a = b = 0;
|
a = b = 0;
|
||||||
sscanf(params, "%*s %*s %*s %i %i", &a, &b);
|
sscanf(prm, "%*s %i %i", &a, &b);
|
||||||
EwinMoveToArea(ewin, ewin->area_x + a, ewin->area_y + b);
|
EwinMoveToArea(ewin, ewin->area_x + a, ewin->area_y + b);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
a = ewin->area_x;
|
a = ewin->area_x;
|
||||||
b = ewin->area_y;
|
b = ewin->area_y;
|
||||||
sscanf(params, "%*s %*s %i %i", &a, &b);
|
sscanf(param1, "%i", &a);
|
||||||
|
sscanf(param2, "%i", &b);
|
||||||
EwinMoveToArea(ewin, a, b);
|
EwinMoveToArea(ewin, a, b);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -575,7 +657,7 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
}
|
}
|
||||||
if (!strcmp(param1, "ptr"))
|
if (!strcmp(param1, "ptr"))
|
||||||
{
|
{
|
||||||
ActionMoveStart(ewin, 1, 0, 0);
|
ActionMoveStart(ewin, 1, 0, Mode.nogroup);
|
||||||
}
|
}
|
||||||
else if (!strcmp(param1, "?"))
|
else if (!strcmp(param1, "?"))
|
||||||
{
|
{
|
||||||
|
@ -589,7 +671,10 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sscanf(params, "%*s %*s %i %i", &a, &b);
|
a = EoGetX(ewin);
|
||||||
|
b = EoGetY(ewin);
|
||||||
|
sscanf(param1, "%i", &a);
|
||||||
|
sscanf(param2, "%i", &b);
|
||||||
EwinOpMove(ewin, OPSRC_USER, a, b);
|
EwinOpMove(ewin, OPSRC_USER, a, b);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -620,7 +705,10 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sscanf(params, "%*s %*s %i %i", &a, &b);
|
a = ewin->client.w;
|
||||||
|
b = ewin->client.h;
|
||||||
|
sscanf(param1, "%i", &a);
|
||||||
|
sscanf(param2, "%i", &b);
|
||||||
EwinOpResize(ewin, OPSRC_USER, a, b);
|
EwinOpResize(ewin, OPSRC_USER, a, b);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -628,8 +716,8 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
case EWIN_OP_MOVE_REL:
|
case EWIN_OP_MOVE_REL:
|
||||||
if (!param1[0])
|
if (!param1[0])
|
||||||
goto done;
|
goto done;
|
||||||
|
a = b = 0;
|
||||||
sscanf(params, "%*s %*s %i %i", &a, &b);
|
sscanf(prm, "%i %i", &a, &b);
|
||||||
a += EoGetX(ewin);
|
a += EoGetX(ewin);
|
||||||
b += EoGetY(ewin);
|
b += EoGetY(ewin);
|
||||||
EwinOpMove(ewin, OPSRC_USER, a, b);
|
EwinOpMove(ewin, OPSRC_USER, a, b);
|
||||||
|
@ -638,8 +726,8 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
case EWIN_OP_SIZE_REL:
|
case EWIN_OP_SIZE_REL:
|
||||||
if (!param1[0])
|
if (!param1[0])
|
||||||
goto done;
|
goto done;
|
||||||
|
a = b = 0;
|
||||||
sscanf(params, "%*s %*s %i %i", &a, &b);
|
sscanf(prm, "%i %i", &a, &b);
|
||||||
a += ewin->client.w;
|
a += ewin->client.w;
|
||||||
b += ewin->client.h;
|
b += ewin->client.h;
|
||||||
EwinOpResize(ewin, OPSRC_USER, a, b);
|
EwinOpResize(ewin, OPSRC_USER, a, b);
|
||||||
|
@ -708,7 +796,7 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EWIN_OP_SNAP:
|
case EWIN_OP_SNAP:
|
||||||
SnapshotEwinParse(ewin, atword(params, 3));
|
SnapshotEwinParse(ewin, prm);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EWIN_OP_SKIP_LISTS:
|
case EWIN_OP_SKIP_LISTS:
|
||||||
|
@ -810,6 +898,58 @@ IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
IPC_WinOps(const char *params, Client * c __UNUSED__)
|
||||||
|
{
|
||||||
|
char match[128];
|
||||||
|
char operation[128];
|
||||||
|
const char *p;
|
||||||
|
EWin **lst;
|
||||||
|
int i, num, flags;
|
||||||
|
const WinOp *wop;
|
||||||
|
|
||||||
|
if (!params)
|
||||||
|
{
|
||||||
|
IpcPrintf("Error: no window specified");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match[0] = operation[0] = '\0';
|
||||||
|
num = 0;
|
||||||
|
sscanf(params, "%128s %128s %n", match, operation, &num);
|
||||||
|
p = params + num;
|
||||||
|
|
||||||
|
if (!operation[0])
|
||||||
|
{
|
||||||
|
IpcPrintf("Error: no operation specified");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wop = EwinOpFind(operation);
|
||||||
|
if (!wop)
|
||||||
|
{
|
||||||
|
IpcPrintf("Error: unknown operation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lst = IpcFindEwins(match, &num, &flags);
|
||||||
|
if (!lst)
|
||||||
|
{
|
||||||
|
IpcPrintf("No windows matching %s\n", match);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
Mode.nogroup = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++)
|
||||||
|
IpcWinop(wop, lst[i], p);
|
||||||
|
|
||||||
|
Mode.nogroup = 0;
|
||||||
|
|
||||||
|
Efree(lst);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
IPC_Remember(const char *params, Client * c __UNUSED__)
|
IPC_Remember(const char *params, Client * c __UNUSED__)
|
||||||
{
|
{
|
||||||
|
@ -1045,30 +1185,27 @@ EwinShowInfo(const EWin * ewin)
|
||||||
static void
|
static void
|
||||||
IPC_EwinInfo(const char *params, Client * c __UNUSED__)
|
IPC_EwinInfo(const char *params, Client * c __UNUSED__)
|
||||||
{
|
{
|
||||||
char param1[FILEPATH_LEN_MAX];
|
char match[FILEPATH_LEN_MAX];
|
||||||
EWin *ewin;
|
EWin **lst;
|
||||||
|
|
||||||
if (params == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
sscanf(params, "%1000s", param1);
|
|
||||||
|
|
||||||
if (!strcmp(param1, "all"))
|
|
||||||
{
|
|
||||||
EWin *const *lst;
|
|
||||||
int i, num;
|
int i, num;
|
||||||
|
|
||||||
lst = EwinListGetAll(&num);
|
if (!params)
|
||||||
for (i = 0; i < num; i++)
|
return;
|
||||||
EwinShowInfo(lst[i]);
|
|
||||||
}
|
sscanf(params, "%1000s", match);
|
||||||
else
|
|
||||||
|
lst = IpcFindEwins(match, &num, NULL);
|
||||||
|
if (!lst)
|
||||||
{
|
{
|
||||||
ewin = IpcFindEwin(param1);
|
IpcPrintf("No windows matching %s\n", match);
|
||||||
if (ewin)
|
return;
|
||||||
EwinShowInfo(ewin);
|
}
|
||||||
else
|
|
||||||
IpcPrintf("No matching EWin found\n");
|
for (i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
EwinShowInfo(lst[i]);
|
||||||
|
if (i != num - 1)
|
||||||
|
IpcPrintf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue