summaryrefslogtreecommitdiffstats
path: root/src/objects
diff options
context:
space:
mode:
authorChristopher Davis <loafier@gmail.com>2006-08-12 22:16:53 +0000
committerChristopher Davis <loafier@gmail.com>2006-08-12 22:16:53 +0000
commit3a028090359e5d5d24ccbfc11d9b6ff5681aab4f (patch)
tree0cfb8ec1eb8a49366fc663bef00bf4dfb1f7c307 /src/objects
parentf13ea25509e932d426ebd69d90368fe9b1d4c1ab (diff)
downloadirssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.gz
irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.xz
irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.zip
directory structure change
git-svn-id: http://svn.irssi.org/repos/irssi-python@4312 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src/objects')
-rw-r--r--src/objects/Makefile28
-rw-r--r--src/objects/ban-object.c155
-rw-r--r--src/objects/ban-object.h18
-rw-r--r--src/objects/base-objects.c235
-rw-r--r--src/objects/base-objects.h105
-rw-r--r--src/objects/channel-object.c379
-rw-r--r--src/objects/channel-object.h22
-rw-r--r--src/objects/chatnet-object.c182
-rw-r--r--src/objects/chatnet-object.h22
-rw-r--r--src/objects/command-object.c145
-rw-r--r--src/objects/command-object.h18
-rw-r--r--src/objects/connect-object.c198
-rw-r--r--src/objects/connect-object.h22
-rw-r--r--src/objects/dcc-chat-object.c137
-rw-r--r--src/objects/dcc-chat-object.h21
-rw-r--r--src/objects/dcc-get-object.c138
-rw-r--r--src/objects/dcc-get-object.h18
-rw-r--r--src/objects/dcc-object.c347
-rw-r--r--src/objects/dcc-object.h24
-rw-r--r--src/objects/dcc-send-object.c138
-rw-r--r--src/objects/dcc-send-object.h18
-rw-r--r--src/objects/factory.c326
-rw-r--r--src/objects/factory.h58
-rw-r--r--src/objects/ignore-object.c284
-rw-r--r--src/objects/ignore-object.h21
-rw-r--r--src/objects/irc-channel-object.c173
-rw-r--r--src/objects/irc-channel-object.h21
-rw-r--r--src/objects/irc-connect-object.c86
-rw-r--r--src/objects/irc-connect-object.h21
-rw-r--r--src/objects/irc-server-object.c491
-rw-r--r--src/objects/irc-server-object.h21
-rw-r--r--src/objects/log-object.c477
-rw-r--r--src/objects/log-object.h21
-rw-r--r--src/objects/logitem-object.c156
-rw-r--r--src/objects/logitem-object.h21
-rw-r--r--src/objects/main-window-object.c199
-rw-r--r--src/objects/main-window-object.h20
-rw-r--r--src/objects/netsplit-channel-object.c170
-rw-r--r--src/objects/netsplit-channel-object.h21
-rw-r--r--src/objects/netsplit-object.c184
-rw-r--r--src/objects/netsplit-object.h19
-rw-r--r--src/objects/netsplit-server-object.c157
-rw-r--r--src/objects/netsplit-server-object.h18
-rw-r--r--src/objects/nick-object.c237
-rw-r--r--src/objects/nick-object.h22
-rw-r--r--src/objects/notifylist-object.c221
-rw-r--r--src/objects/notifylist-object.h18
-rw-r--r--src/objects/process-object.c222
-rw-r--r--src/objects/process-object.h22
-rw-r--r--src/objects/pyscript-object.c801
-rw-r--r--src/objects/pyscript-object.h33
-rw-r--r--src/objects/query-object.c172
-rw-r--r--src/objects/query-object.h21
-rw-r--r--src/objects/rawlog-object.c323
-rw-r--r--src/objects/rawlog-object.h22
-rw-r--r--src/objects/reconnect-object.c151
-rw-r--r--src/objects/reconnect-object.h20
-rw-r--r--src/objects/server-object.c807
-rw-r--r--src/objects/server-object.h28
-rw-r--r--src/objects/statusbar-item-object.c253
-rw-r--r--src/objects/statusbar-item-object.h22
-rw-r--r--src/objects/textdest-object.c285
-rw-r--r--src/objects/textdest-object.h24
-rw-r--r--src/objects/theme-object.c195
-rw-r--r--src/objects/theme-object.h18
-rw-r--r--src/objects/window-item-object.c334
-rw-r--r--src/objects/window-item-object.h26
-rw-r--r--src/objects/window-object.c678
-rw-r--r--src/objects/window-object.h21
69 files changed, 10311 insertions, 0 deletions
diff --git a/src/objects/Makefile b/src/objects/Makefile
new file mode 100644
index 0000000..6cc56fb
--- /dev/null
+++ b/src/objects/Makefile
@@ -0,0 +1,28 @@
+CC = gcc
+
+PYTHON = /usr/include/python2.4
+IRSSI = /home/chrisd/irssi-0.8.10
+CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \
+-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \
+-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \
+-I$(IRSSI)/src/irc/notifylist -I.. \
+`pkg-config glib-2.0 --cflags`
+
+OBJ = pyscript-object.o base-objects.o window-item-object.o channel-object.o \
+query-object.o server-object.o connect-object.o irc-server-object.o \
+irc-connect-object.o irc-channel-object.o ban-object.o nick-object.o \
+chatnet-object.o reconnect-object.o window-object.o textdest-object.o \
+rawlog-object.o log-object.o logitem-object.o ignore-object.o \
+dcc-object.o dcc-chat-object.o dcc-get-object.o dcc-send-object.o \
+netsplit-object.o netsplit-server-object.o netsplit-channel-object.o \
+notifylist-object.o process-object.o command-object.o theme-object.o \
+statusbar-item-object.o main-window-object.o factory.o
+
+pyobjects.a: $(OBJ)
+ ar r pyobjects.a $(OBJ)
+
+%.o: %.c
+ $(CC) -c $< $(CFLAGS)
+
+clean:
+ rm -f *.o *.so *.a
diff --git a/src/objects/ban-object.c b/src/objects/ban-object.c
new file mode 100644
index 0000000..908bae2
--- /dev/null
+++ b/src/objects/ban-object.c
@@ -0,0 +1,155 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "ban-object.h"
+#include "pycore.h"
+
+/* monitor "ban remove" signal */
+static void ban_cleanup(CHANNEL_REC *chan, BAN_REC *ban)
+{
+ PyBan *pyban = signal_get_user_data();
+
+ if (ban == pyban->data)
+ {
+ pyban->data = NULL;
+ pyban->cleanup_installed = 0;
+ signal_remove_data("ban remove", ban_cleanup, pyban);
+ }
+}
+
+static void PyBan_dealloc(PyBan *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("ban remove", ban_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyBan_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyBan *self;
+
+ self = (PyBan *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+PyDoc_STRVAR(PyBan_ban_doc,
+ "The ban"
+);
+static PyObject *PyBan_ban_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(data->ban);
+}
+
+PyDoc_STRVAR(PyBan_setby_doc,
+ "Nick of who set the ban"
+);
+static PyObject *PyBan_setby_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(data->setby);
+}
+
+PyDoc_STRVAR(PyBan_time_doc,
+ "Timestamp when ban was set"
+);
+static PyObject *PyBan_time_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(data->time);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyBan_getseters[] = {
+ {"ban", (getter)PyBan_ban_get, NULL,
+ PyBan_ban_doc, NULL},
+ {"setby", (getter)PyBan_setby_get, NULL,
+ PyBan_setby_doc, NULL},
+ {"time", (getter)PyBan_time_get, NULL,
+ PyBan_time_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyBan_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyBanType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Ban", /*tp_name*/
+ sizeof(PyBan), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyBan_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyBan objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyBan_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyBan_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyBan_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyban_new(void *ban)
+{
+ PyBan *pyban;
+
+ pyban = py_inst(PyBan, PyBanType);
+ if (!pyban)
+ return NULL;
+
+ pyban->data = ban;
+ pyban->cleanup_installed = 1;
+ signal_add_last_data("ban remove", ban_cleanup, pyban);
+
+ return (PyObject *)pyban;
+}
+
+int ban_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyBanType) < 0)
+ return 0;
+
+ Py_INCREF(&PyBanType);
+ PyModule_AddObject(py_module, "Ban", (PyObject *)&PyBanType);
+
+ return 1;
+}
diff --git a/src/objects/ban-object.h b/src/objects/ban-object.h
new file mode 100644
index 0000000..59ec9e7
--- /dev/null
+++ b/src/objects/ban-object.h
@@ -0,0 +1,18 @@
+#ifndef _BAN_OBJECT_H_
+#define _BAN_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyBan;
+
+extern PyTypeObject PyBanType;
+
+int ban_object_init(void);
+PyObject *pyban_new(void *ban);
+#define pyban_check(op) PyObject_TypeCheck(op, &PyBanType)
+
+#endif
diff --git a/src/objects/base-objects.c b/src/objects/base-objects.c
new file mode 100644
index 0000000..42f5cac
--- /dev/null
+++ b/src/objects/base-objects.c
@@ -0,0 +1,235 @@
+#include <Python.h>
+#include "structmember.h"
+#include "pymodule.h"
+#include "base-objects.h"
+#include "pyirssi.h"
+
+/* This is the base type for Irssi objects with a type id. The user can find
+ * the type name, type id, and check if the object is wrapping a valid Irssi
+ * record.
+ */
+
+static void PyIrssiBase_dealloc(PyIrssiBase *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIrssiBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIrssiBase *self;
+
+ self = (PyIrssiBase *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIrssiBase_type_id_doc,
+ "Irssi's type id for object"
+);
+static PyObject *PyIrssiBase_type_id_get(PyIrssiBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->type);
+}
+
+PyDoc_STRVAR(PyIrssiBase_type_doc,
+ "Irssi's name for object"
+);
+static PyObject *PyIrssiBase_type_get(PyIrssiBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->base_name);
+}
+
+PyDoc_STRVAR(PyIrssiBase_valid_doc,
+ "True if the object is valid"
+);
+static PyObject *PyIrssiBase_valid_get(PyIrssiBase *self, void *closure)
+{
+ if (self->data != NULL)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrssiBase_getseters[] = {
+ {"type_id", (getter)PyIrssiBase_type_id_get, NULL,
+ PyIrssiBase_type_id_doc, NULL},
+ {"type", (getter)PyIrssiBase_type_get, NULL,
+ PyIrssiBase_type_doc, NULL},
+ {"valid", (getter)PyIrssiBase_valid_get, NULL,
+ PyIrssiBase_valid_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyIrssiBase_methods[] = {
+ {NULL}
+};
+
+PyTypeObject PyIrssiBaseType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrssiBase", /*tp_name*/
+ sizeof(PyIrssiBase), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIrssiBase_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrssiBase objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrssiBase_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrssiBase_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIrssiBase_new, /* tp_new */
+};
+
+
+/* IrssiChatBase is a base type for any object with a chat type. The user
+ can find the chat type string name with the chat_type member or
+ the type id with the chat_type_id member. It inherits from IrssiBase
+ so the type, valid, and type_id members are visible to the user, too */
+
+static void PyIrssiChatBase_dealloc(PyIrssiChatBase *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIrssiChatBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIrssiChatBase *self;
+
+ self = (PyIrssiChatBase *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIrssiChatBase_chat_type_id_doc,
+ "Chat Type id (int)"
+);
+static PyObject *PyIrssiChatBase_chat_type_id_get(PyIrssiChatBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->chat_type);
+}
+
+PyDoc_STRVAR(PyIrssiChatBase_chat_type_doc,
+ "Chat name (str)"
+);
+static PyObject *PyIrssiChatBase_chat_type_get(PyIrssiChatBase *self, void *closure)
+{
+ CHAT_PROTOCOL_REC *rec;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ rec = chat_protocol_find_id(self->data->chat_type);
+ if (rec)
+ RET_AS_STRING_OR_NONE(rec->name);
+ else
+ Py_RETURN_NONE;
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrssiChatBase_getseters[] = {
+ {"chat_type_id", (getter)PyIrssiChatBase_chat_type_id_get, NULL,
+ PyIrssiChatBase_chat_type_id_doc, NULL},
+ {"chat_type", (getter)PyIrssiChatBase_chat_type_get, NULL,
+ PyIrssiChatBase_chat_type_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+static PyMethodDef PyIrssiChatBase_methods[] = {
+ {NULL}
+};
+
+PyTypeObject PyIrssiChatBaseType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrssiChatBase", /*tp_name*/
+ sizeof(PyIrssiChatBase), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIrssiChatBase_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrssiChatBase objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrssiChatBase_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrssiChatBase_getseters, /* tp_getset */
+ &PyIrssiBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIrssiChatBase_new, /* tp_new */
+};
+
+int base_objects_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrssiBaseType) < 0)
+ return 0;
+ if (PyType_Ready(&PyIrssiChatBaseType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrssiBaseType);
+ Py_INCREF(&PyIrssiChatBaseType);
+ PyModule_AddObject(py_module, "IrssiBase", (PyObject *)&PyIrssiBaseType);
+ PyModule_AddObject(py_module, "IrssiChatBase", (PyObject *)&PyIrssiChatBaseType);
+
+ return 1;
+}
diff --git a/src/objects/base-objects.h b/src/objects/base-objects.h
new file mode 100644
index 0000000..5351ead
--- /dev/null
+++ b/src/objects/base-objects.h
@@ -0,0 +1,105 @@
+#ifndef _BASE_OBJECTS_H_
+#define _BASE_OBJECTS_H_
+
+#include <Python.h>
+
+/* data is a pointer to the underlying Irssi record */
+/* base_name is the type name of the object returned from the type member
+ it can be SERVER, CHANNEL, QUERY, etc. Note: base_name isn't freed, so
+ it cannot point to heap memory */
+/* cleanup_installed: 1 = a cleanup signal handler is installed, 0 = not installed */
+#define PyIrssi_HEAD(type) \
+ PyObject_HEAD \
+ type *data; \
+ const char *base_name; \
+ int cleanup_installed;
+
+/* for uninheritable objects without a type id (Ban, Log, etc) */
+#define PyIrssiFinal_HEAD(type) \
+ PyObject_HEAD \
+ type *data; \
+ int cleanup_installed;
+
+/* to access data from any irssi object */
+typedef struct
+{
+ PyObject_HEAD
+ void *data;
+} PyIrssiObject;
+
+#define DATA(obj) (obj? ((PyIrssiObject *)obj)->data : NULL)
+
+/* base for classes with a type */
+typedef struct
+{
+ int type;
+} IRSSI_BASE_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(IRSSI_BASE_REC)
+} PyIrssiBase;
+
+
+/* base for classes with type and a chat type */
+typedef struct
+{
+ int type;
+ int chat_type;
+} IRSSI_CHAT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(IRSSI_CHAT_REC)
+} PyIrssiChatBase;
+
+extern PyTypeObject PyIrssiBaseType;
+extern PyTypeObject PyIrssiChatBaseType;
+
+#define pybase_check(op) PyObject_TypeCheck(op, &PyIrssiBaseType)
+#define pychatbase_check(op) PyObject_TypeCheck(op, &PyIrssiChatBaseType)
+#define py_instp(tp, to) ((tp *) (to)->tp_alloc(to, 0))
+#define py_inst(tp, to) py_instp(tp, &to)
+
+int base_objects_init(void);
+
+#define RET_NULL_IF_INVALID(data) \
+ if (data == NULL) \
+ return PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid")
+
+#define RET_N1_IF_INVALID(data) \
+do { \
+ if (data == NULL) \
+ { \
+ PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid"); \
+ return -1; \
+ } \
+} while (0)
+
+#define RET_AS_STRING_OR_NONE(str) \
+do { \
+ if (str) \
+ return PyString_FromString(str); \
+ else \
+ { \
+ Py_INCREF(Py_None); \
+ return Py_None; \
+ } \
+} while (0)
+
+
+#define RET_AS_STRING_OR_EMPTY(str) return PyString_FromString(str? str : "")
+
+#define RET_AS_OBJ_OR_NONE(obj) \
+do { \
+ PyObject *tmp = obj; \
+ if (!tmp) \
+ tmp = Py_None; \
+ Py_INCREF(tmp); \
+ return tmp; \
+} while (0)
+
+#define INVALID_MEMBER(member) \
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member)
+
+#endif
diff --git a/src/objects/channel-object.c b/src/objects/channel-object.c
new file mode 100644
index 0000000..fa4edd4
--- /dev/null
+++ b/src/objects/channel-object.c
@@ -0,0 +1,379 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "factory.h"
+#include "channel-object.h"
+#include "pycore.h"
+
+/* monitor "channel destroyed" signal */
+static void chan_cleanup(CHANNEL_REC *chan)
+{
+ PyChannel *pychan = signal_get_user_data();
+
+ if (chan == pychan->data)
+ {
+ pychan->data = NULL;
+ pychan->cleanup_installed = 0;
+ signal_remove_data("channel destroyed", chan_cleanup, pychan);
+ }
+}
+
+static void PyChannel_dealloc(PyChannel *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("channel destroyed", chan_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyChannel_topic_doc,
+ "Channel topic"
+);
+static PyObject *PyChannel_topic_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->topic);
+}
+
+PyDoc_STRVAR(PyChannel_topic_by_doc,
+ "Nick who set the topic"
+);
+static PyObject *PyChannel_topic_by_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->topic_by);
+}
+
+PyDoc_STRVAR(PyChannel_topic_time_doc,
+ "Timestamp when the topic was set"
+);
+static PyObject *PyChannel_topic_time_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->topic_time);
+}
+
+PyDoc_STRVAR(PyChannel_no_modes_doc,
+ "Channel is modeless"
+);
+static PyObject *PyChannel_no_modes_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->no_modes);
+}
+
+PyDoc_STRVAR(PyChannel_mode_doc,
+ "Channel mode"
+);
+static PyObject *PyChannel_mode_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->mode);
+}
+
+PyDoc_STRVAR(PyChannel_limit_doc,
+ "Max. users in channel (+l mode)"
+);
+static PyObject *PyChannel_limit_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->limit);
+}
+
+PyDoc_STRVAR(PyChannel_key_doc,
+ "Channel key (password)"
+);
+static PyObject *PyChannel_key_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->key);
+}
+
+PyDoc_STRVAR(PyChannel_chanop_doc,
+ "You are channel operator"
+);
+static PyObject *PyChannel_chanop_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->chanop);
+}
+
+PyDoc_STRVAR(PyChannel_names_got_doc,
+ "/NAMES list has been received"
+);
+static PyObject *PyChannel_names_got_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->names_got);
+}
+
+PyDoc_STRVAR(PyChannel_wholist_doc,
+ "/WHO list has been received"
+);
+static PyObject *PyChannel_wholist_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->wholist);
+}
+
+PyDoc_STRVAR(PyChannel_synced_doc,
+ "Channel is fully synchronized"
+);
+static PyObject *PyChannel_synced_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->synced);
+}
+
+PyDoc_STRVAR(PyChannel_joined_doc,
+ "JOIN event for this channel has been received"
+);
+static PyObject *PyChannel_joined_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->joined);
+}
+
+PyDoc_STRVAR(PyChannel_left_doc,
+ "You just left the channel (for 'channel destroyed' event)"
+);
+static PyObject *PyChannel_left_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->left);
+}
+
+PyDoc_STRVAR(PyChannel_kicked_doc,
+ "You were just kicked out of the channel (for 'channel destroyed' event)"
+);
+static PyObject *PyChannel_kicked_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->kicked);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyChannel_getseters[] = {
+ {"topic", (getter)PyChannel_topic_get, NULL,
+ PyChannel_topic_doc, NULL},
+ {"topic_by", (getter)PyChannel_topic_by_get, NULL,
+ PyChannel_topic_by_doc, NULL},
+ {"topic_time", (getter)PyChannel_topic_time_get, NULL,
+ PyChannel_topic_time_doc, NULL},
+ {"no_modes", (getter)PyChannel_no_modes_get, NULL,
+ PyChannel_no_modes_doc, NULL},
+ {"mode", (getter)PyChannel_mode_get, NULL,
+ PyChannel_mode_doc, NULL},
+ {"limit", (getter)PyChannel_limit_get, NULL,
+ PyChannel_limit_doc, NULL},
+ {"key", (getter)PyChannel_key_get, NULL,
+ PyChannel_key_doc, NULL},
+ {"chanop", (getter)PyChannel_chanop_get, NULL,
+ PyChannel_chanop_doc, NULL},
+ {"names_got", (getter)PyChannel_names_got_get, NULL,
+ PyChannel_names_got_doc, NULL},
+ {"wholist", (getter)PyChannel_wholist_get, NULL,
+ PyChannel_wholist_doc, NULL},
+ {"synced", (getter)PyChannel_synced_get, NULL,
+ PyChannel_synced_doc, NULL},
+ {"joined", (getter)PyChannel_joined_get, NULL,
+ PyChannel_joined_doc, NULL},
+ {"left", (getter)PyChannel_left_get, NULL,
+ PyChannel_left_doc, NULL},
+ {"kicked", (getter)PyChannel_kicked_get, NULL,
+ PyChannel_kicked_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyChannel_nicks_doc,
+ "nicks() -> list of Nick objects\n"
+ "\n"
+ "Return a list of nicks in the channel.\n"
+);
+static PyObject *PyChannel_nicks(PyChannel *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(nicklist_getnicks(self->data), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nicks_find_mask_doc,
+ "nicks_find_mask(mask) -> Nick object or None\n"
+ "\n"
+ "Find nick mask from nicklist, wildcards allowed.\n"
+);
+static PyObject *PyChannel_nicks_find_mask(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", NULL};
+ char *mask = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &mask))
+ return NULL;
+
+ return py_irssi_chat_new(nicklist_find_mask(self->data, mask), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nick_find_doc,
+ "nick_find(nick) -> Nick object or None\n"
+ "\n"
+ "Find nick from nicklist.\n"
+);
+static PyObject *PyChannel_nick_find(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return py_irssi_chat_new(nicklist_find(self->data, nick), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nick_remove_doc,
+ "nick_remove(nick) -> None\n"
+ "\n"
+ "Remove nick from nicklist.\n"
+);
+static PyObject *PyChannel_nick_remove(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ PyObject *nick = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &nick))
+ return NULL;
+
+ if (!pynick_check(nick))
+ return PyErr_Format(PyExc_TypeError, "arg must be nick");
+
+ nicklist_remove(self->data, ((PyNick*)nick)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyChannel_nick_insert_obj_doc,
+ "nick_insert(nick) -> None\n"
+ "\n"
+ "Insert nick object into nicklist.\n"
+);
+static PyObject *PyChannel_nick_insert_obj(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ PyObject *nick = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &nick))
+ return NULL;
+
+ if (!pynick_check(nick))
+ return PyErr_Format(PyExc_TypeError, "arg must be nick");
+
+ nicklist_insert(self->data, ((PyNick*)nick)->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyChannel_methods[] = {
+ {"nicks", (PyCFunction)PyChannel_nicks, METH_NOARGS,
+ PyChannel_nicks_doc},
+ {"nicks_find_mask", (PyCFunction)PyChannel_nicks_find_mask, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nicks_find_mask_doc},
+ {"nick_find", (PyCFunction)PyChannel_nick_find, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_find_doc},
+ {"nick_remove", (PyCFunction)PyChannel_nick_remove, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_remove_doc},
+ {"nick_insert_obj", (PyCFunction)PyChannel_nick_insert_obj, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_insert_obj_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Channel", /*tp_name*/
+ sizeof(PyChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyChannel_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyChannel_getseters, /* tp_getset */
+ &PyWindowItemType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type)
+{
+ PyObject *pychan;
+
+ pychan = pywindow_item_sub_new(chan, name, type);
+ if (pychan)
+ {
+ PyChannel *pych = (PyChannel *)pychan;
+ signal_add_last_data("channel destroyed", chan_cleanup, pych);
+ pych->cleanup_installed = 1;
+ }
+
+ return pychan;
+}
+
+PyObject *pychannel_new(void *chan)
+{
+ static const char *name = "CHANNEL";
+ return pychannel_sub_new(chan, name, &PyChannelType);
+}
+
+int channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyChannelType);
+ PyModule_AddObject(py_module, "Channel", (PyObject *)&PyChannelType);
+
+ return 1;
+}
diff --git a/src/objects/channel-object.h b/src/objects/channel-object.h
new file mode 100644
index 0000000..0858e7b
--- /dev/null
+++ b/src/objects/channel-object.h
@@ -0,0 +1,22 @@
+#ifndef _CHANNEL_OBJECT_H_
+#define _CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _CHANNEL_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _CHANNEL_REC)
+} PyChannel;
+
+extern PyTypeObject PyChannelType;
+
+int channel_object_init(void);
+PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type);
+PyObject *pychannel_new(void *chan);
+#define pychannel_check(op) PyObject_TypeCheck(op, &PyChannelType)
+
+#endif
diff --git a/src/objects/chatnet-object.c b/src/objects/chatnet-object.c
new file mode 100644
index 0000000..c22f0cb
--- /dev/null
+++ b/src/objects/chatnet-object.c
@@ -0,0 +1,182 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "chatnet-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void chatnet_cleanup(CHATNET_REC *cn)
+{
+ PyChatnet *pycn = signal_get_user_data();
+
+ if (cn == pycn->data)
+ {
+ pycn->data = NULL;
+ pycn->cleanup_installed = 0;
+ signal_remove_data("chatnet destroyed", chatnet_cleanup, pycn);
+ }
+}
+
+static void PyChatnet_dealloc(PyChatnet *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("chatnet destroyed", chatnet_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyChatnet_name_doc,
+ "name of chat network"
+);
+static PyObject *PyChatnet_name_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyChatnet_nick_doc,
+ "if not empty, nick preferred in this network"
+);
+static PyObject *PyChatnet_nick_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyChatnet_username_doc,
+ "if not empty, username preferred in this network"
+);
+static PyObject *PyChatnet_username_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->username);
+}
+
+PyDoc_STRVAR(PyChatnet_realname_doc,
+ "if not empty, realname preferred in this network"
+);
+static PyObject *PyChatnet_realname_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+PyDoc_STRVAR(PyChatnet_own_host_doc,
+ "address to use when connecting to this network"
+);
+static PyObject *PyChatnet_own_host_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->own_host);
+}
+
+PyDoc_STRVAR(PyChatnet_autosendcmd_doc,
+ "command to send after connecting to this network"
+);
+static PyObject *PyChatnet_autosendcmd_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->autosendcmd);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyChatnet_getseters[] = {
+ {"name", (getter)PyChatnet_name_get, NULL,
+ PyChatnet_name_doc, NULL},
+ {"nick", (getter)PyChatnet_nick_get, NULL,
+ PyChatnet_nick_doc, NULL},
+ {"username", (getter)PyChatnet_username_get, NULL,
+ PyChatnet_username_doc, NULL},
+ {"realname", (getter)PyChatnet_realname_get, NULL,
+ PyChatnet_realname_doc, NULL},
+ {"own_host", (getter)PyChatnet_own_host_get, NULL,
+ PyChatnet_own_host_doc, NULL},
+ {"autosendcmd", (getter)PyChatnet_autosendcmd_get, NULL,
+ PyChatnet_autosendcmd_doc, NULL},
+ {NULL}
+};
+
+static PyMethodDef PyChatnet_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyChatnetType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Chatnet", /*tp_name*/
+ sizeof(PyChatnet), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyChatnet_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyChatnet objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyChatnet_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyChatnet_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* chatnet factory function */
+PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass)
+{
+ static const char *name = "CHATNET";
+ PyChatnet *pycn = NULL;
+
+ pycn = py_instp(PyChatnet, subclass);
+ if (!pycn)
+ return NULL;
+
+ pycn->data = cn;
+ pycn->base_name = name;
+ signal_add_last_data("chatnet destroyed", chatnet_cleanup, pycn);
+ pycn->cleanup_installed = 1;
+
+ return (PyObject *)pycn;
+}
+
+PyObject *pychatnet_new(void *cn)
+{
+ return pychatnet_sub_new(cn, &PyChatnetType);
+}
+
+int chatnet_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyChatnetType) < 0)
+ return 0;
+
+ Py_INCREF(&PyChatnetType);
+ PyModule_AddObject(py_module, "Chatnet", (PyObject *)&PyChatnetType);
+
+ return 1;
+}
diff --git a/src/objects/chatnet-object.h b/src/objects/chatnet-object.h
new file mode 100644
index 0000000..8a6d1fc
--- /dev/null
+++ b/src/objects/chatnet-object.h
@@ -0,0 +1,22 @@
+#ifndef _CHATNET_OBJECT_H_
+#define _CHATNET_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _CHATNET_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _CHATNET_REC)
+} PyChatnet;
+
+extern PyTypeObject PyChatnetType;
+
+int chatnet_object_init(void);
+PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass);
+PyObject *pychatnet_new(void *cn);
+#define pychatnet_check(op) PyObject_TypeCheck(op, &PyChatnetType)
+
+#endif
diff --git a/src/objects/command-object.c b/src/objects/command-object.c
new file mode 100644
index 0000000..ad60012
--- /dev/null
+++ b/src/objects/command-object.c
@@ -0,0 +1,145 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "command-object.h"
+#include "pycore.h"
+
+#define COMMAND(cmd) ((COMMAND_REC *)cmd)
+
+/* monitor "commandlist remove" signal */
+static void command_cleanup(COMMAND_REC *command)
+{
+ PyCommand *pycommand = signal_get_user_data();
+
+ if (command == pycommand->data)
+ {
+ pycommand->data = NULL;
+ pycommand->cleanup_installed = 0;
+ signal_remove_data("commandlist remove", command_cleanup, pycommand);
+ }
+}
+
+static void PyCommand_dealloc(PyCommand *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("commandlist remove", command_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyCommand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyCommand *self;
+
+ self = (PyCommand *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyCommand_cmd_doc,
+ "Command name"
+);
+static PyObject *PyCommand_cmd_get(PyCommand *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(COMMAND(self->data)->cmd);
+}
+
+PyDoc_STRVAR(PyCommand_category_doc,
+ "Category"
+);
+static PyObject *PyCommand_category_get(PyCommand *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(COMMAND(self->data)->category);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyCommand_getseters[] = {
+ {"cmd", (getter)PyCommand_cmd_get, NULL,
+ PyCommand_cmd_doc, NULL},
+ {"category", (getter)PyCommand_category_get, NULL,
+ PyCommand_category_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyCommand_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyCommandType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Command", /*tp_name*/
+ sizeof(PyCommand), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyCommand_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyCommand objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyCommand_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyCommand_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyCommand_new, /* tp_new */
+};
+
+
+/* command factory function */
+PyObject *pycommand_new(void *command)
+{
+ PyCommand *pycommand;
+
+ pycommand = py_inst(PyCommand, PyCommandType);
+ if (!pycommand)
+ return NULL;
+
+ pycommand->data = command;
+ pycommand->cleanup_installed = 1;
+ signal_add_last_data("commandlist remove", command_cleanup, pycommand);
+
+ return (PyObject *)pycommand;
+}
+
+int command_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyCommandType) < 0)
+ return 0;
+
+ Py_INCREF(&PyCommandType);
+ PyModule_AddObject(py_module, "Command", (PyObject *)&PyCommandType);
+
+ return 1;
+}
diff --git a/src/objects/command-object.h b/src/objects/command-object.h
new file mode 100644
index 0000000..e6402cf
--- /dev/null
+++ b/src/objects/command-object.h
@@ -0,0 +1,18 @@
+#ifndef _COMMAND_OBJECT_H_
+#define _COMMAND_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyCommand;
+
+extern PyTypeObject PyCommandType;
+
+int command_object_init(void);
+PyObject *pycommand_new(void *command);
+#define pycommand_check(op) PyObject_TypeCheck(op, &PyCommandType)
+
+#endif
diff --git a/src/objects/connect-object.c b/src/objects/connect-object.c
new file mode 100644
index 0000000..8ef9a29
--- /dev/null
+++ b/src/objects/connect-object.c
@@ -0,0 +1,198 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "connect-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void connect_cleanup(SERVER_CONNECT_REC *connect)
+{
+ PyConnect *pyconn = signal_get_user_data();
+
+ /*
+ if (server == pyconn->data)
+ {
+ pyserver->data = NULL;
+ pyserver->cleanup_installed = 0;
+ signal_remove_data("server disconnected", connect_cleanup, pyserver);
+ }
+ */
+}
+
+static void PyConnect_dealloc(PyConnect *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("server disconnected", connect_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyConnect_address_doc,
+ "Address where we connected (irc.blah.org)"
+);
+static PyObject *PyConnect_address_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->address);
+}
+
+PyDoc_STRVAR(PyConnect_port_doc,
+ "Port where we're connected"
+);
+static PyObject *PyConnect_port_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->port);
+}
+
+PyDoc_STRVAR(PyConnect_chatnet_doc,
+ "Chat network"
+);
+static PyObject *PyConnect_chatnet_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->chatnet);
+}
+
+PyDoc_STRVAR(PyConnect_password_doc,
+ "Password we used in connection."
+);
+static PyObject *PyConnect_password_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->password);
+}
+
+PyDoc_STRVAR(PyConnect_wanted_nick_doc,
+ "Nick which we would prefer to use"
+);
+static PyObject *PyConnect_wanted_nick_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyConnect_username_doc,
+ "User name"
+);
+static PyObject *PyConnect_username_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->username);
+}
+
+PyDoc_STRVAR(PyConnect_realname_doc,
+ "Real name"
+);
+static PyObject *PyConnect_realname_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+/* Get/Set */
+static PyGetSetDef PyConnect_getseters[] = {
+ {"address", (getter)PyConnect_address_get, NULL,
+ PyConnect_address_doc, NULL},
+ {"port", (getter)PyConnect_port_get, NULL,
+ PyConnect_port_doc, NULL},
+ {"chatnet", (getter)PyConnect_chatnet_get, NULL,
+ PyConnect_chatnet_doc, NULL},
+ {"password", (getter)PyConnect_password_get, NULL,
+ PyConnect_password_doc, NULL},
+ {"wanted_nick", (getter)PyConnect_wanted_nick_get, NULL,
+ PyConnect_wanted_nick_doc, NULL},
+ {"username", (getter)PyConnect_username_get, NULL,
+ PyConnect_username_doc, NULL},
+ {"realname", (getter)PyConnect_realname_get, NULL,
+ PyConnect_realname_doc, NULL},
+ {NULL}
+};
+
+PyTypeObject PyConnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Connect", /*tp_name*/
+ sizeof(PyConnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyConnect_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyConnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ PyConnect_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* server connect factory function (managed == 0, don't do signal cleanup, 1 == do sig cleanup */
+PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subclass, int managed)
+{
+ static const char *CONNECT_TYPE = "SERVER CONNECT";
+ PyConnect *pyconn = NULL;
+
+ g_return_val_if_fail(connect != NULL, NULL);
+
+ pyconn = py_instp(PyConnect, subclass);
+ if (!pyconn)
+ return NULL;
+
+ pyconn->base_name = CONNECT_TYPE;
+ pyconn->data = connect;
+
+ if (managed)
+ {
+ //XXX: how to handle cleanup?
+ //signal_add_last_data("server disconnected", connect_cleanup, pyconn);
+ //pyconn->cleanup_installed = 1;
+ }
+
+ return (PyObject *)pyconn;
+}
+
+PyObject *pyconnect_new(void *connect, int managed)
+{
+ return pyconnect_sub_new(connect, &PyConnectType, managed);
+}
+
+int connect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyConnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyConnectType);
+ PyModule_AddObject(py_module, "Connect", (PyObject *)&PyConnectType);
+
+ return 1;
+}
diff --git a/src/objects/connect-object.h b/src/objects/connect-object.h
new file mode 100644
index 0000000..53bd509
--- /dev/null
+++ b/src/objects/connect-object.h
@@ -0,0 +1,22 @@
+#ifndef _CONNECT_OBJECT_H_
+#define _CONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _SERVER_CONNECT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _SERVER_CONNECT_REC)
+} PyConnect;
+
+extern PyTypeObject PyConnectType;
+
+int connect_object_init(void);
+PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subtype, int managed);
+PyObject *pyconnect_new(void *connect, int managed);
+#define pyconnect_check(op) PyObject_TypeCheck(op, &PyConnectType)
+
+#endif
diff --git a/src/objects/dcc-chat-object.c b/src/objects/dcc-chat-object.c
new file mode 100644
index 0000000..5cfe796
--- /dev/null
+++ b/src/objects/dcc-chat-object.c
@@ -0,0 +1,137 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-chat-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* inherit destroy and cleanup from DccChat type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccChat_id_doc,
+ "Unique identifier - usually same as nick"
+);
+static PyObject *PyDccChat_id_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->id);
+}
+
+PyDoc_STRVAR(PyDccChat_mirc_ctcp_doc,
+ "Send CTCPs without the CTCP_MESSAGE prefix"
+);
+static PyObject *PyDccChat_mirc_ctcp_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->mirc_ctcp);
+}
+
+PyDoc_STRVAR(PyDccChat_connection_lost_doc,
+ "Other side closed connection"
+);
+static PyObject *PyDccChat_connection_lost_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connection_lost);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccChat_getseters[] = {
+ {"id", (getter)PyDccChat_id_get, NULL,
+ PyDccChat_id_doc, NULL},
+ {"mirc_ctcp", (getter)PyDccChat_mirc_ctcp_get, NULL,
+ PyDccChat_mirc_ctcp_doc, NULL},
+ {"connection_lost", (getter)PyDccChat_connection_lost_get, NULL,
+ PyDccChat_connection_lost_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyDccChat_chat_send_doc,
+ "chat_send(data) -> None\n"
+ "\n"
+ "Send data to a dcc chat session.\n"
+);
+static PyObject *PyDccChat_chat_send(PyDccChat *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &data))
+ return NULL;
+
+ dcc_chat_send(self->data, data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyDccChat_methods[] = {
+ {"chat_send", (PyCFunction)PyDccChat_chat_send, METH_VARARGS | METH_KEYWORDS,
+ PyDccChat_chat_send_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccChatType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccChat", /*tp_name*/
+ sizeof(PyDccChat), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccChat objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccChat_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccChat_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_chat_new(void *dcc)
+{
+ static const char *name = "DCC CHAT";
+ return pydcc_sub_new(dcc, name, &PyDccChatType);
+}
+
+int dcc_chat_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccChatType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccChatType);
+ PyModule_AddObject(py_module, "DccChat", (PyObject *)&PyDccChatType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-chat-object.h b/src/objects/dcc-chat-object.h
new file mode 100644
index 0000000..955e6be
--- /dev/null
+++ b/src/objects/dcc-chat-object.h
@@ -0,0 +1,21 @@
+#ifndef _DCC_CHAT_OBJECT_H_
+#define _DCC_CHAT_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+/* forward */
+struct CHAT_DCC_REC;
+
+typedef struct
+{
+ PyDcc_HEAD(struct CHAT_DCC_REC)
+} PyDccChat;
+
+extern PyTypeObject PyDccChatType;
+
+PyObject *pydcc_chat_new(void *dcc);
+#define pydcc_chat_check(op) PyObject_TypeCheck(op, &PyDccChatType)
+int dcc_chat_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-get-object.c b/src/objects/dcc-get-object.c
new file mode 100644
index 0000000..fa7c13a
--- /dev/null
+++ b/src/objects/dcc-get-object.c
@@ -0,0 +1,138 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-get-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define DCC_GET_CAST(rec) ((GET_DCC_REC *)rec)
+
+/* inherit destroy and cleanup from Dcc type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccGet_size_doc,
+ "File size"
+);
+static PyObject *PyDccGet_size_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->size);
+}
+
+PyDoc_STRVAR(PyDccGet_skipped_doc,
+ "Bytes skipped from start (resuming file)"
+);
+static PyObject *PyDccGet_skipped_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->skipped);
+}
+
+PyDoc_STRVAR(PyDccGet_get_type_doc,
+ "What to do if file exists? 0=default, 1=rename, 2=overwrite, 3=resume"
+);
+static PyObject *PyDccGet_get_type_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(DCC_GET_CAST(self->data)->get_type);
+}
+
+PyDoc_STRVAR(PyDccGet_file_doc,
+ "The real file name which we use."
+);
+static PyObject *PyDccGet_file_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC_GET_CAST(self->data)->file);
+}
+
+PyDoc_STRVAR(PyDccGet_file_quoted_doc,
+ "true if file name was received quoted (\"file name\")"
+);
+static PyObject *PyDccGet_file_quoted_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_GET_CAST(self->data)->file_quoted);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccGet_getseters[] = {
+ {"size", (getter)PyDccGet_size_get, NULL,
+ PyDccGet_size_doc, NULL},
+ {"skipped", (getter)PyDccGet_skipped_get, NULL,
+ PyDccGet_skipped_doc, NULL},
+ {"get_type", (getter)PyDccGet_get_type_get, NULL,
+ PyDccGet_get_type_doc, NULL},
+ {"file", (getter)PyDccGet_file_get, NULL,
+ PyDccGet_file_doc, NULL},
+ {"file_quoted", (getter)PyDccGet_file_quoted_get, NULL,
+ PyDccGet_file_quoted_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyDccGet_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccGetType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccGet", /*tp_name*/
+ sizeof(PyDccGet), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccGet objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccGet_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccGet_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_get_new(void *dcc)
+{
+ static const char *name = "DCC GET";
+ return pydcc_sub_new(dcc, name, &PyDccGetType);
+}
+
+int dcc_get_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccGetType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccGetType);
+ PyModule_AddObject(py_module, "DccGet", (PyObject *)&PyDccGetType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-get-object.h b/src/objects/dcc-get-object.h
new file mode 100644
index 0000000..532b854
--- /dev/null
+++ b/src/objects/dcc-get-object.h
@@ -0,0 +1,18 @@
+#ifndef _DCC_GET_OBJECT_H_
+#define _DCC_GET_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDccGet;
+
+extern PyTypeObject PyDccGetType;
+
+PyObject *pydcc_get_new(void *dcc);
+#define pydcc_get_check(op) PyObject_TypeCheck(op, &PyDccGetType)
+int dcc_get_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-object.c b/src/objects/dcc-object.c
new file mode 100644
index 0000000..95a3a4a
--- /dev/null
+++ b/src/objects/dcc-object.c
@@ -0,0 +1,347 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+
+/* monitor "dcc destroyed signal" */
+static void dcc_cleanup(DCC_REC *dcc)
+{
+ PyDcc *pydcc = signal_get_user_data();
+
+ if (dcc == pydcc->data)
+ {
+ pydcc->data = NULL;
+ pydcc->cleanup_installed = 0;
+ signal_remove_data("dcc destroyed", dcc_cleanup, pydcc);
+ }
+}
+
+static void PyDcc_dealloc(PyDcc *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("dcc destroyed", dcc_cleanup, self);
+
+ Py_XDECREF(self->server);
+ Py_XDECREF(self->chat);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyDcc_orig_type_doc,
+ "Original DCC type that was sent to us - same as type except GET and SEND are swapped"
+);
+static PyObject *PyDcc_orig_type_get(PyDcc *self, void *closure)
+{
+ const char *type;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ type = module_find_id_str("DCC", DCC(self->data)->orig_type);
+ RET_AS_STRING_OR_NONE(type);
+}
+
+PyDoc_STRVAR(PyDcc_created_doc,
+ "Time stamp when the DCC record was created"
+);
+static PyObject *PyDcc_created_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->created);
+}
+
+PyDoc_STRVAR(PyDcc_server_doc,
+ "Server record where the DCC was initiated."
+);
+static PyObject *PyDcc_server_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyDcc_servertag_doc,
+ "Tag of the server where the DCC was initiated."
+);
+static PyObject *PyDcc_servertag_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->servertag);
+}
+
+PyDoc_STRVAR(PyDcc_mynick_doc,
+ "Our nick to use in DCC chat."
+);
+static PyObject *PyDcc_mynick_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->mynick);
+}
+
+PyDoc_STRVAR(PyDcc_nick_doc,
+ "Other side's nick name."
+);
+static PyObject *PyDcc_nick_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->nick);
+}
+
+PyDoc_STRVAR(PyDcc_chat_doc,
+ "Dcc chat record if the request came through DCC chat"
+);
+static PyObject *PyDcc_chat_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->chat);
+}
+
+PyDoc_STRVAR(PyDcc_target_doc,
+ "Who the request was sent to - your nick, channel or empty if you sent the request"
+);
+static PyObject *PyDcc_target_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->target);
+}
+
+PyDoc_STRVAR(PyDcc_arg_doc,
+ "Given argument .. file name usually"
+);
+static PyObject *PyDcc_arg_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->arg);
+}
+
+PyDoc_STRVAR(PyDcc_addr_doc,
+ "Other side's IP address."
+);
+static PyObject *PyDcc_addr_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->addrstr);
+}
+
+PyDoc_STRVAR(PyDcc_port_doc,
+ "Port we're connecting in."
+);
+static PyObject *PyDcc_port_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(DCC(self->data)->port);
+}
+
+PyDoc_STRVAR(PyDcc_starttime_doc,
+ "Unix time stamp when the DCC transfer was started"
+);
+static PyObject *PyDcc_starttime_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->starttime);
+}
+
+PyDoc_STRVAR(PyDcc_transfd_doc,
+ "Bytes transferred"
+);
+static PyObject *PyDcc_transfd_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->transfd);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDcc_getseters[] = {
+ {"orig_type", (getter)PyDcc_orig_type_get, NULL,
+ PyDcc_orig_type_doc, NULL},
+ {"created", (getter)PyDcc_created_get, NULL,
+ PyDcc_created_doc, NULL},
+ {"server", (getter)PyDcc_server_get, NULL,
+ PyDcc_server_doc, NULL},
+ {"servertag", (getter)PyDcc_servertag_get, NULL,
+ PyDcc_servertag_doc, NULL},
+ {"mynick", (getter)PyDcc_mynick_get, NULL,
+ PyDcc_mynick_doc, NULL},
+ {"nick", (getter)PyDcc_nick_get, NULL,
+ PyDcc_nick_doc, NULL},
+ {"chat", (getter)PyDcc_chat_get, NULL,
+ PyDcc_chat_doc, NULL},
+ {"target", (getter)PyDcc_target_get, NULL,
+ PyDcc_target_doc, NULL},
+ {"arg", (getter)PyDcc_arg_get, NULL,
+ PyDcc_arg_doc, NULL},
+ {"addr", (getter)PyDcc_addr_get, NULL,
+ PyDcc_addr_doc, NULL},
+ {"port", (getter)PyDcc_port_get, NULL,
+ PyDcc_port_doc, NULL},
+ {"starttime", (getter)PyDcc_starttime_get, NULL,
+ PyDcc_starttime_doc, NULL},
+ {"transfd", (getter)PyDcc_transfd_get, NULL,
+ PyDcc_transfd_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyDcc_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy DCC connection\n"
+);
+static PyObject *PyDcc_destroy(PyDcc *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ dcc_destroy(DCC(self->data));
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyDcc_reject_doc,
+ "reject() -> None\n"
+ "\n"
+ "?\n"
+);
+static PyObject *PyDcc_reject(PyDcc *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyirc_server_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be IRC server object");
+
+ dcc_reject(self->data, ((PyIrcServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyDcc_close_doc,
+ "close() -> None\n"
+ "\n"
+ "Close and destroy DCC connection.\n"
+);
+static PyObject *PyDcc_close(PyDcc *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ dcc_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyDcc_methods[] = {
+ {"destroy", (PyCFunction)PyDcc_destroy, METH_NOARGS,
+ PyDcc_destroy_doc},
+ {"reject", (PyCFunction)PyDcc_reject, METH_VARARGS | METH_KEYWORDS,
+ PyDcc_reject_doc},
+ {"close", (PyCFunction)PyDcc_close, METH_NOARGS,
+ PyDcc_close_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Dcc", /*tp_name*/
+ sizeof(PyDcc), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyDcc_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDcc objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDcc_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDcc_getseters, /* tp_getset */
+ &PyIrssiBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* Dcc factory function */
+PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass)
+{
+ PyObject *chat = NULL, *server = NULL;
+ PyDcc *pydcc;
+ DCC_REC *rec = dcc;
+
+ server = py_irssi_chat_new(rec->server, 1);
+ if (!server)
+ return NULL;
+
+ chat = py_irssi_chat_new(rec->chat, 1);
+ if (!chat)
+ {
+ Py_DECREF(server);
+ return NULL;
+ }
+
+ pydcc = py_instp(PyDcc, subclass);
+ if (!pydcc)
+ {
+ Py_DECREF(server);
+ Py_DECREF(chat);
+ return NULL;
+ }
+
+ pydcc->data = dcc;
+ pydcc->server = server;
+ pydcc->chat = chat;
+ pydcc->base_name = name;
+
+ pydcc->cleanup_installed = 1;
+ signal_add_last_data("dcc destroyed", dcc_cleanup, pydcc);
+
+ return (PyObject *)pydcc;
+}
+
+PyObject *pydcc_new(void *dcc)
+{
+ static const char *name = "DCC";
+ return pydcc_sub_new(dcc, name, &PyDccType);
+}
+
+int dcc_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccType);
+ PyModule_AddObject(py_module, "Dcc", (PyObject *)&PyDccType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-object.h b/src/objects/dcc-object.h
new file mode 100644
index 0000000..d44d3db
--- /dev/null
+++ b/src/objects/dcc-object.h
@@ -0,0 +1,24 @@
+#ifndef _DCC_OBJECT_H_
+#define _DCC_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+#define PyDcc_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *server; \
+ PyObject *chat;
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDcc;
+
+extern PyTypeObject PyDccType;
+
+PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass);
+PyObject *pydcc_new(void *dcc);
+#define pydcc_check(op) PyObject_TypeCheck(op, &PyDccType)
+int dcc_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-send-object.c b/src/objects/dcc-send-object.c
new file mode 100644
index 0000000..643f741
--- /dev/null
+++ b/src/objects/dcc-send-object.c
@@ -0,0 +1,138 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-send-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define DCC_SEND_CAST(rec) ((SEND_DCC_REC *)rec)
+
+/* inherit destroy and cleanup from Dcc type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccSend_size_doc,
+ "File size"
+);
+static PyObject *PyDccSend_size_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->size);
+}
+
+PyDoc_STRVAR(PyDccSend_skipped_doc,
+ "Bytes skipped from start (resuming file)"
+);
+static PyObject *PyDccSend_skipped_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->skipped);
+}
+
+PyDoc_STRVAR(PyDccSend_file_quoted_doc,
+ "True if file name was received quoted (\"file name\")"
+);
+static PyObject *PyDccSend_file_quoted_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->file_quoted);
+}
+
+PyDoc_STRVAR(PyDccSend_waitforend_doc,
+ "File is sent, just wait for the replies from the other side"
+);
+static PyObject *PyDccSend_waitforend_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->waitforend);
+}
+
+PyDoc_STRVAR(PyDccSend_gotalldata_doc,
+ "Got all acks from the other end"
+);
+static PyObject *PyDccSend_gotalldata_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->gotalldata);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccSend_getseters[] = {
+ {"size", (getter)PyDccSend_size_get, NULL,
+ PyDccSend_size_doc, NULL},
+ {"skipped", (getter)PyDccSend_skipped_get, NULL,
+ PyDccSend_skipped_doc, NULL},
+ {"file_quoted", (getter)PyDccSend_file_quoted_get, NULL,
+ PyDccSend_file_quoted_doc, NULL},
+ {"waitforend", (getter)PyDccSend_waitforend_get, NULL,
+ PyDccSend_waitforend_doc, NULL},
+ {"gotalldata", (getter)PyDccSend_gotalldata_get, NULL,
+ PyDccSend_gotalldata_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyDccSend_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccSendType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccSend", /*tp_name*/
+ sizeof(PyDccSend), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccSend objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccSend_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccSend_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_send_new(void *dcc)
+{
+ static const char *name = "DCC SEND";
+ return pydcc_sub_new(dcc, name, &PyDccSendType);
+}
+
+int dcc_send_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccSendType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccSendType);
+ PyModule_AddObject(py_module, "DccSend", (PyObject *)&PyDccSendType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-send-object.h b/src/objects/dcc-send-object.h
new file mode 100644
index 0000000..6303f86
--- /dev/null
+++ b/src/objects/dcc-send-object.h
@@ -0,0 +1,18 @@
+#ifndef _DCC_SEND_OBJECT_H_
+#define _DCC_SEND_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDccSend;
+
+extern PyTypeObject PyDccSendType;
+
+PyObject *pydcc_send_new(void *dcc);
+#define pydcc_send_check(op) PyObject_TypeCheck(op, &PyDccSendType)
+int dcc_send_object_init(void);
+
+#endif
diff --git a/src/objects/factory.c b/src/objects/factory.c
new file mode 100644
index 0000000..49336e6
--- /dev/null
+++ b/src/objects/factory.c
@@ -0,0 +1,326 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "factory.h"
+
+/* Irssi object factory works for all items with at least a type member.
+ *
+ * Use py_irssi_new() or py_irssi_chat_new() to get a new wrapper for an
+ * IrssiObject or an IrssiChatObject, respectively.
+ *
+ * For objects not descending from IrssiObject or IrssiChatObject, you must
+ * use the object-specific init function directly.
+ */
+
+#define MAKEKEY(type, chat) ((chat << 16 ) | type)
+#define GETTYPE(key) (key & 0xffff)
+#define GETCHAT(key) ((key >> 16) & 0xffff)
+
+
+GHashTable *init_map = NULL;
+
+static int init_objects(void);
+static void register_chat(CHAT_PROTOCOL_REC *rec);
+static void unregister_chat(CHAT_PROTOCOL_REC *rec);
+static void insert_map(int type, int chat_type, InitFunc func);
+static int remove_chat(void *key, void *value, void *chat_typep);
+static void register_nonchat(void);
+static InitFunc find_map(int type, int chat_type);
+
+static int init_objects(void)
+{
+ if (!pyscript_init())
+ return 0;
+
+ /* order is somewhat important here */
+ if (!base_objects_init())
+ return 0;
+
+ if (!window_item_object_init())
+ return 0;
+
+ if (!channel_object_init())
+ return 0;
+
+ if (!query_object_init())
+ return 0;
+
+ if (!server_object_init())
+ return 0;
+
+ if (!connect_object_init())
+ return 0;
+
+ if (!irc_server_object_init())
+ return 0;
+
+ if (!irc_connect_object_init())
+ return 0;
+
+ if (!irc_channel_object_init())
+ return 0;
+
+ if (!ban_object_init())
+ return 0;
+
+ if (!nick_object_init())
+ return 0;
+
+ if (!chatnet_object_init())
+ return 0;
+
+ if (!reconnect_object_init())
+ return 0;
+
+ if (!window_object_init())
+ return 0;
+
+ if (!textdest_object_init())
+ return 0;
+
+ if (!rawlog_object_init())
+ return 0;
+
+ if (!log_object_init())
+ return 0;
+
+ if (!logitem_object_init())
+ return 0;
+
+ if (!ignore_object_init())
+ return 0;
+
+ if (!dcc_object_init())
+ return 0;
+
+ if (!dcc_chat_object_init())
+ return 0;
+
+ if (!dcc_get_object_init())
+ return 0;
+
+ if (!dcc_send_object_init())
+ return 0;
+
+ if (!netsplit_object_init())
+ return 0;
+
+ if (!netsplit_server_object_init())
+ return 0;
+
+ if (!netsplit_channel_object_init())
+ return 0;
+
+ if (!notifylist_object_init())
+ return 0;
+
+ if (!process_object_init())
+ return 0;
+
+ if (!command_object_init())
+ return 0;
+
+ if (!theme_object_init())
+ return 0;
+
+ if (!statusbar_item_object_init())
+ return 0;
+
+ if (!main_window_object_init())
+ return 0;
+
+ return 1;
+}
+
+static InitFunc find_map(int type, int chat_type)
+{
+ unsigned hash;
+
+ g_return_val_if_fail(type <= 0xffff, NULL);
+ g_return_val_if_fail(chat_type <= 0xffff, NULL);
+
+ hash = MAKEKEY(type, chat_type);
+ return g_hash_table_lookup(init_map, GUINT_TO_POINTER(hash));
+}
+
+static void insert_map(int type, int chat_type, InitFunc func)
+{
+ unsigned hash;
+
+ g_return_if_fail(type <= 0xffff);
+ g_return_if_fail(chat_type <= 0xffff);
+
+ hash = MAKEKEY(type, chat_type);
+ g_hash_table_insert(init_map, GUINT_TO_POINTER(hash), func);
+}
+
+static void register_chat(CHAT_PROTOCOL_REC *rec)
+{
+ int type;
+ int chat_type;
+ int is_irc = 0;
+
+ /* chat_type == rec->id ??? */
+ chat_type = chat_protocol_lookup(rec->name);
+ g_return_if_fail(chat_type >= 0 && chat_type < 0xffff);
+
+ if (!g_strcasecmp(rec->name, "IRC"))
+ is_irc = 1;
+
+ type = module_get_uniq_id("SERVER", 0);
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_server_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pyserver_new);
+
+ type = module_get_uniq_id("SERVER CONNECT", 0);
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_connect_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pyconnect_new);
+
+ type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_channel_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pychannel_new);
+
+ type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+ insert_map(type, chat_type, (InitFunc)pyquery_new);
+
+ type = module_get_uniq_id("CHATNET", 0);
+ insert_map(type, chat_type, (InitFunc)pychatnet_new);
+
+ type = module_get_uniq_id("NICK", 0);
+ insert_map(type, chat_type, (InitFunc)pynick_new);
+}
+
+/* register funcs for objects without a chat type */
+static void register_nonchat(void)
+{
+ int type;
+ int chat_type = 0xffff;
+
+ type = module_get_uniq_id_str("DCC", "CHAT");
+ insert_map(type, chat_type, (InitFunc)pydcc_chat_new);
+
+ type = module_get_uniq_id_str("DCC", "GET");
+ insert_map(type, chat_type, (InitFunc)pydcc_get_new);
+
+ type = module_get_uniq_id_str("DCC", "SEND");
+ insert_map(type, chat_type, (InitFunc)pydcc_send_new);
+
+ type = module_get_uniq_id_str("DCC", "SERVER");
+ insert_map(type, chat_type, (InitFunc)pydcc_new);
+}
+
+static int remove_chat(void *key, void *value, void *chat_typep)
+{
+ unsigned hash = GPOINTER_TO_UINT(key);
+ int chat_type = GPOINTER_TO_INT(chat_type);
+
+ if (GETCHAT(hash) == chat_type)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* remove all items matching chat_type */
+static void unregister_chat(CHAT_PROTOCOL_REC *rec)
+{
+ /*int chat_type = chat_protocol_lookup(rec->name);*/
+ g_hash_table_foreach_remove(init_map,
+ (GHRFunc)remove_chat,
+ GINT_TO_POINTER(rec->id));
+}
+
+PyObject *py_irssi_new(void *typeobj, int managed)
+{
+ IRSSI_BASE_REC *base = typeobj;
+ InitFunc ifunc;
+
+ if (!base)
+ Py_RETURN_NONE;
+
+ ifunc = find_map(base->type, 0xffff);
+
+ if (ifunc)
+ return ifunc(typeobj, managed);
+
+ return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d", base->type);
+}
+
+PyObject *py_irssi_chat_new(void *typeobj, int managed)
+{
+ IRSSI_CHAT_REC *chat = typeobj;
+ InitFunc ifunc;
+
+ if (!chat)
+ Py_RETURN_NONE;
+
+ ifunc = find_map(chat->type, chat->chat_type);
+
+ if (ifunc)
+ return ifunc(typeobj, managed);
+
+ return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d, chat_type %d",
+ chat->type, chat->chat_type);
+}
+
+PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init)
+{
+ PyObject *list = NULL;
+
+ list = PyList_New(0);
+ if (!list)
+ goto error;
+
+ for (; node != NULL; node = node->next)
+ {
+ int ret;
+ PyObject *obj = init(node->data, managed);
+
+ if (!obj)
+ goto error;
+
+ ret = PyList_Append(list, obj);
+ Py_DECREF(obj);
+
+ if (ret != 0)
+ goto error;
+ }
+
+ return list;
+
+error:
+ Py_XDECREF(list);
+ return NULL;
+}
+
+int factory_init(void)
+{
+ g_return_val_if_fail(init_map == NULL, 0);
+
+ if (!init_objects())
+ return 0;
+
+ init_map = g_hash_table_new(g_direct_hash, g_direct_equal);
+ g_slist_foreach(chat_protocols, (GFunc) register_chat, NULL);
+ register_nonchat();
+
+ signal_add("chat protocol created", (SIGNAL_FUNC) register_chat);
+ signal_add("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat);
+
+ return 1;
+}
+
+void factory_deinit(void)
+{
+ g_return_if_fail(init_map != NULL);
+
+ g_hash_table_destroy(init_map);
+ init_map = NULL;
+
+ signal_remove("chat protocol created", (SIGNAL_FUNC) register_chat);
+ signal_remove("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat);
+}
+
diff --git a/src/objects/factory.h b/src/objects/factory.h
new file mode 100644
index 0000000..9d4bd79
--- /dev/null
+++ b/src/objects/factory.h
@@ -0,0 +1,58 @@
+#ifndef _FACTORY_H_
+#define _FACTORY_H_
+
+#include <Python.h>
+#include "pyscript-object.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "channel-object.h"
+#include "query-object.h"
+#include "server-object.h"
+#include "connect-object.h"
+#include "irc-server-object.h"
+#include "irc-connect-object.h"
+#include "irc-channel-object.h"
+#include "ban-object.h"
+#include "nick-object.h"
+#include "chatnet-object.h"
+#include "reconnect-object.h"
+#include "window-object.h"
+#include "textdest-object.h"
+#include "rawlog-object.h"
+#include "log-object.h"
+#include "logitem-object.h"
+#include "ignore-object.h"
+#include "dcc-object.h"
+#include "dcc-chat-object.h"
+#include "dcc-get-object.h"
+#include "dcc-send-object.h"
+#include "netsplit-object.h"
+#include "netsplit-server-object.h"
+#include "netsplit-channel-object.h"
+#include "notifylist-object.h"
+#include "process-object.h"
+#include "command-object.h"
+#include "theme-object.h"
+#include "statusbar-item-object.h"
+#include "main-window-object.h"
+
+int factory_init(void);
+void factory_deinit(void);
+
+/*managed == 1: object invalidates itself
+ *managed == 0: caller responsible for invalidating object
+ *XXX: most objects invalidate themselves, ignoring "managed" switch,
+ * and some are never managed (Reconnect)
+ */
+
+/* For objects with a type member but no chat_type */
+PyObject *py_irssi_new(void *typeobj, int managed);
+/* For objects with both type and chat_type members */
+PyObject *py_irssi_chat_new(void *typeobj, int managed);
+
+typedef PyObject *(*InitFunc)(void *, int);
+PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init);
+#define py_irssi_chatlist_new(n, m) py_irssi_objlist_new(n, m, py_irssi_chat_new)
+#define py_irssi_list_new(n, m) py_irssi_objlist_new(n, m, py_irssi_new)
+
+#endif
diff --git a/src/objects/ignore-object.c b/src/objects/ignore-object.c
new file mode 100644
index 0000000..979c9b1
--- /dev/null
+++ b/src/objects/ignore-object.c
@@ -0,0 +1,284 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "ignore-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* monitor "ignore destroy" signal */
+static void ignore_cleanup(IGNORE_REC *ignore)
+{
+ PyIgnore *pyignore = signal_get_user_data();
+
+ if (ignore == pyignore->data)
+ {
+ pyignore->data = NULL;
+ pyignore->cleanup_installed = 0;
+ signal_remove_data("ignore destroy", ignore_cleanup, pyignore);
+ }
+}
+
+static void PyIgnore_dealloc(PyIgnore *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("ignore destroy", ignore_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIgnore_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIgnore *self;
+
+ self = (PyIgnore *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIgnore_mask_doc,
+ "Ignore mask"
+);
+static PyObject *PyIgnore_mask_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->mask);
+}
+
+PyDoc_STRVAR(PyIgnore_servertag_doc,
+ "Ignore only in server"
+);
+static PyObject *PyIgnore_servertag_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->servertag);
+}
+
+PyDoc_STRVAR(PyIgnore_pattern_doc,
+ "Ignore text patern"
+);
+static PyObject *PyIgnore_pattern_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->pattern);
+}
+
+PyDoc_STRVAR(PyIgnore_level_doc,
+ "Ignore level"
+);
+static PyObject *PyIgnore_level_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyIgnore_exception_doc,
+ "This is an exception ignore"
+);
+static PyObject *PyIgnore_exception_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->exception);
+}
+
+PyDoc_STRVAR(PyIgnore_regexp_doc,
+ "Regexp pattern matching"
+);
+static PyObject *PyIgnore_regexp_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->regexp);
+}
+
+PyDoc_STRVAR(PyIgnore_fullword_doc,
+ "Pattern matches only full words"
+);
+static PyObject *PyIgnore_fullword_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->fullword);
+}
+
+PyDoc_STRVAR(PyIgnore_replies_doc,
+ "Ignore replies to nick in channel"
+);
+static PyObject *PyIgnore_replies_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->replies);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIgnore_getseters[] = {
+ {"mask", (getter)PyIgnore_mask_get, NULL,
+ PyIgnore_mask_doc, NULL},
+ {"servertag", (getter)PyIgnore_servertag_get, NULL,
+ PyIgnore_servertag_doc, NULL},
+ {"pattern", (getter)PyIgnore_pattern_get, NULL,
+ PyIgnore_pattern_doc, NULL},
+ {"level", (getter)PyIgnore_level_get, NULL,
+ PyIgnore_level_doc, NULL},
+ {"exception", (getter)PyIgnore_exception_get, NULL,
+ PyIgnore_exception_doc, NULL},
+ {"regexp", (getter)PyIgnore_regexp_get, NULL,
+ PyIgnore_regexp_doc, NULL},
+ {"fullword", (getter)PyIgnore_fullword_get, NULL,
+ PyIgnore_fullword_doc, NULL},
+ {"replies", (getter)PyIgnore_replies_get, NULL,
+ PyIgnore_replies_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyIgnore_channels_doc,
+ "channels() -> list of str\n"
+ "\n"
+ "Ignore only in channels (list of names)\n"
+);
+static PyObject *PyIgnore_channels(PyIgnore *self, PyObject *args)
+{
+ char **p;
+ PyObject *list;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ list = PyList_New(0);
+ if (!list)
+ return NULL;
+
+ for (p = self->data->channels; *p; p++)
+ {
+ int ret;
+ PyObject *str;
+
+ str = PyString_FromString(*p);
+ if (!str)
+ {
+ Py_XDECREF(list);
+ return NULL;
+ }
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ {
+ Py_XDECREF(list);
+ return NULL;
+ }
+ }
+
+ return list;
+}
+
+PyDoc_STRVAR(PyIgnore_add_rec_doc,
+ "add_rec() -> None\n"
+ "\n"
+ "Add ignore record"
+);
+static PyObject *PyIgnore_add_rec(PyIgnore *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ ignore_add_rec(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIgnore_update_rec_doc,
+ "update_rec() -> None\n"
+ "\n"
+ "Update ignore record in configuration"
+);
+static PyObject *PyIgnore_update_rec(PyIgnore *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ ignore_update_rec(self->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyIgnore_methods[] = {
+ {"add_rec", (PyCFunction)PyIgnore_add_rec, METH_NOARGS,
+ PyIgnore_add_rec_doc},
+ {"update_rec", (PyCFunction)PyIgnore_update_rec, METH_NOARGS,
+ PyIgnore_update_rec_doc},
+ {"channels", (PyCFunction)PyIgnore_channels, METH_NOARGS,
+ PyIgnore_channels_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIgnoreType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Ignore", /*tp_name*/
+ sizeof(PyIgnore), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIgnore_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIgnore objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIgnore_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIgnore_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIgnore_new, /* tp_new */
+};
+
+
+/* ignore factory function */
+PyObject *pyignore_new(void *ignore)
+{
+ PyIgnore *pyignore;
+
+ pyignore = py_inst(PyIgnore, PyIgnoreType);
+ if (!pyignore)
+ return NULL;
+
+ pyignore->data = ignore;
+ pyignore->cleanup_installed = 1;
+ signal_add_last_data("ignore destroy", ignore_cleanup, pyignore);
+
+ return (PyObject *)pyignore;
+}
+
+int ignore_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIgnoreType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIgnoreType);
+ PyModule_AddObject(py_module, "Ignore", (PyObject *)&PyIgnoreType);
+
+ return 1;
+}
diff --git a/src/objects/ignore-object.h b/src/objects/ignore-object.h
new file mode 100644
index 0000000..39f7425
--- /dev/null
+++ b/src/objects/ignore-object.h
@@ -0,0 +1,21 @@
+#ifndef _IGNORE_OBJECT_H_
+#define _IGNORE_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _IGNORE_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _IGNORE_REC)
+} PyIgnore;
+
+extern PyTypeObject PyIgnoreType;
+
+int ignore_object_init(void);
+PyObject *pyignore_new(void *ignore);
+#define pyignore_check(op) PyObject_TypeCheck(op, &PyIgnoreType)
+
+#endif
diff --git a/src/objects/irc-channel-object.c b/src/objects/irc-channel-object.c
new file mode 100644
index 0000000..5e9531d
--- /dev/null
+++ b/src/objects/irc-channel-object.c
@@ -0,0 +1,173 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "pycore.h"
+#include "irc-channel-object.h"
+#include "factory.h"
+
+/* PyIrcChannel destructor is inherited from PyChannel */
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrcChannel_getseters[] = {
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(bans_doc,
+ "bans() -> list of Ban objects\n"
+ "\n"
+ "Returns a list of bans in the channel.\n"
+);
+static PyObject *PyIrcChannel_bans(PyIrcChannel *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_objlist_new(self->data->banlist, 1, (InitFunc)pyban_new);
+}
+
+PyDoc_STRVAR(ban_get_mask_doc,
+ "ban_get_mask(nick, ban_type=0) -> str\n"
+ "\n"
+ "Get ban mask for 'nick'.\n"
+);
+static PyObject *PyIrcChannel_ban_get_mask(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "ban_type", NULL};
+ char *nick, *str;
+ int ban_type = 0;
+ PyObject *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &nick, &ban_type))
+ return NULL;
+
+ str = ban_get_mask(self->data, nick, ban_type);
+ if (!str)
+ Py_RETURN_NONE;
+
+ ret = PyString_FromString(str);
+ g_free(str);
+
+ return ret;
+}
+
+PyDoc_STRVAR(banlist_add_doc,
+ "banlist_add(ban, nick, time) -> Ban object or None\n"
+ "\n"
+ "Add a new ban to channel. Return None if duplicate."
+);
+static PyObject *PyIrcChannel_banlist_add(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ban", "nick", "time", NULL};
+ char *ban, *nick;
+ time_t btime;
+ BAN_REC *newban;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssk", kwlist, &ban, &nick, &btime))
+ return NULL;
+
+ newban = banlist_add(self->data, ban, nick, btime);
+ /* XXX: return none or throw error? */
+ if (!newban)
+ Py_RETURN_NONE;
+
+ return pyban_new(newban);
+}
+
+PyDoc_STRVAR(banlist_remove_doc,
+ "banlist_remove(ban, nick) -> None\n"
+ "\n"
+ "Remove a new ban from channel.\n"
+);
+static PyObject *PyIrcChannel_banlist_remove(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ban", "nick", NULL};
+ char *ban, *nick;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &ban, &nick))
+ return NULL;
+
+ banlist_remove(self->data, ban, nick);
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyIrcChannel_methods[] = {
+ {"bans", (PyCFunction)PyIrcChannel_bans, METH_NOARGS,
+ bans_doc},
+ {"ban_get_mask", (PyCFunction)PyIrcChannel_ban_get_mask, METH_VARARGS | METH_KEYWORDS,
+ ban_get_mask_doc},
+ {"banlist_add", (PyCFunction)PyIrcChannel_banlist_add, METH_VARARGS | METH_KEYWORDS,
+ banlist_add_doc},
+ {"banlist_remove", (PyCFunction)PyIrcChannel_banlist_remove, METH_VARARGS | METH_KEYWORDS,
+ banlist_remove_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIrcChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcChannel", /*tp_name*/
+ sizeof(PyIrcChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrcChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcChannel_getseters, /* tp_getset */
+ &PyChannelType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* irc channel factory function */
+PyObject *pyirc_channel_new(void *chan)
+{
+ static const char *BASE_NAME = "CHANNEL";
+ return pychannel_sub_new(chan, BASE_NAME, &PyIrcChannelType);
+}
+
+int irc_channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcChannelType);
+ PyModule_AddObject(py_module, "IrcChannel", (PyObject *)&PyIrcChannelType);
+
+ return 1;
+}
diff --git a/src/objects/irc-channel-object.h b/src/objects/irc-channel-object.h
new file mode 100644
index 0000000..a1d845a
--- /dev/null
+++ b/src/objects/irc-channel-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_CHANNEL_OBJECT_H_
+#define _IRC_CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _IRC_CHANNEL_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _IRC_CHANNEL_REC)
+} PyIrcChannel;
+
+extern PyTypeObject PyIrcChannelType;
+
+int irc_channel_object_init(void);
+PyObject *pyirc_channel_new(void *chan);
+#define pyirc_channel_check(op) PyObject_TypeCheck(op, &PyIrcChannelType)
+
+#endif
diff --git a/src/objects/irc-connect-object.c b/src/objects/irc-connect-object.c
new file mode 100644
index 0000000..5068dea
--- /dev/null
+++ b/src/objects/irc-connect-object.c
@@ -0,0 +1,86 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "irc-connect-object.h"
+#include "pyirssi_irc.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* cleanup and deallocation handled by Connect base */
+
+/* Getters */
+PyDoc_STRVAR(PyIrcConnect_alternate_nick_doc,
+ "Alternate nick to use if default nick is taken"
+);
+static PyObject *PyIrcConnect_alternate_nick_get(PyIrcConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->alternate_nick);
+}
+
+/* Get/Set */
+static PyGetSetDef PyIrcConnect_getseters[] = {
+ {"alternate_nick", (getter)PyIrcConnect_alternate_nick_get, NULL,
+ PyIrcConnect_alternate_nick_doc, NULL},
+ {NULL}
+};
+
+PyTypeObject PyIrcConnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcConnect", /*tp_name*/
+ sizeof(PyIrcConnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcConnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcConnect_getseters, /* tp_getset */
+ &PyConnectType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pyirc_connect_new(void *connect, int managed)
+{
+ return pyconnect_sub_new(connect, &PyIrcConnectType, managed);
+}
+
+int irc_connect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcConnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcConnectType);
+ PyModule_AddObject(py_module, "IrcConnect", (PyObject *)&PyIrcConnectType);
+
+ return 1;
+}
diff --git a/src/objects/irc-connect-object.h b/src/objects/irc-connect-object.h
new file mode 100644
index 0000000..3f6cad4
--- /dev/null
+++ b/src/objects/irc-connect-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_CONNECT_OBJECT_H_
+#define _IRC_CONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "connect-object.h"
+
+/* forward */
+struct _IRC_SERVER_CONNECT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _IRC_SERVER_CONNECT_REC)
+} PyIrcConnect;
+
+extern PyTypeObject PyIrcConnectType;
+
+int irc_connect_object_init(void);
+PyObject *pyirc_connect_new(void *connect, int managed);
+#define pyirc_connect_check(op) PyObject_TypeCheck(op, &PyIrcConnectType)
+
+#endif
diff --git a/src/objects/irc-server-object.c b/src/objects/irc-server-object.c
new file mode 100644
index 0000000..4e9a28e
--- /dev/null
+++ b/src/objects/irc-server-object.c
@@ -0,0 +1,491 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "irc-server-object.h"
+#include "factory.h"
+#include "pyirssi_irc.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* cleanup and dealloc inherited from base Server */
+
+/* Getters */
+PyDoc_STRVAR(PyIrcServer_real_address_doc,
+ "Address the IRC server gives"
+);
+static PyObject *PyIrcServer_real_address_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->real_address);
+}
+
+PyDoc_STRVAR(PyIrcServer_usermode_doc,
+ "User mode in server"
+);
+static PyObject *PyIrcServer_usermode_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->usermode);
+}
+
+PyDoc_STRVAR(PyIrcServer_userhost_doc,
+ "Your user host in server"
+);
+static PyObject *PyIrcServer_userhost_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->userhost);
+}
+
+static PyGetSetDef PyIrcServer_getseters[] = {
+ {"real_address", (getter)PyIrcServer_real_address_get, NULL,
+ PyIrcServer_real_address_doc, NULL},
+ {"usermode", (getter)PyIrcServer_usermode_get, NULL,
+ PyIrcServer_usermode_doc, NULL},
+ {"userhost", (getter)PyIrcServer_userhost_get, NULL,
+ PyIrcServer_userhost_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(get_channels_doc,
+ "get_channels() -> str\n"
+ "\n"
+ "Return a string of all channels (and keys, if any have them) in server,\n"
+ "like '#a,#b,#c,#d x,b_chan_key,x,x' or just '#e,#f,#g'\n"
+);
+static PyObject *PyIrcServer_get_channels(PyIrcServer *self, PyObject *args)
+{
+ char *list;
+ PyObject *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ list = irc_server_get_channels(self->data);
+ ret = PyString_FromString(list);
+ g_free(list);
+
+ return ret;
+}
+
+PyDoc_STRVAR(send_raw_doc,
+ "send_raw(cmd) -> None\n"
+ "\n"
+ "Send raw message to server, it will be flood protected so you\n"
+ "don't need to worry about it.\n"
+);
+static PyObject *PyIrcServer_send_raw(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ irc_send_cmd(self->data, cmd);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(send_raw_now_doc,
+ "send_raw_now(cmd) -> None\n"
+ "\n"
+ "Send raw message to server immediately without flood protection.\n"
+);
+static PyObject *PyIrcServer_send_raw_now(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ irc_send_cmd_now(self->data, cmd);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(send_raw_split_doc,
+ "send_raw_split(cmd, nickarg, max_nicks) -> None\n"
+ "\n"
+ "Split the `cmd' into several commands so `nickarg' argument has only\n"
+ "`max_nicks' number of nicks.\n"
+ "\n"
+ "Example:\n"
+ "server.send_raw_split('KICK #channel nick1,nick2,nick3 :byebye', 3, 2)\n"
+ "\n"
+ "Irssi will send commands 'KICK #channel nick1,nick2 :byebye' and\n"
+ "'KICK #channel nick3 :byebye' to server.\n"
+);
+static PyObject *PyIrcServer_send_raw_split(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "nickarg", "max_nicks", NULL};
+ char *cmd;
+ int nickarg;
+ int max_nicks;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", kwlist, &cmd, &nickarg, &max_nicks))
+ return NULL;
+
+ irc_send_cmd_split(self->data, cmd, nickarg, max_nicks);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(ctcp_send_reply_doc,
+ "ctcp_send_reply(data) -> None\n"
+ "\n"
+ "Send CTCP reply. This will be 'CTCP flood protected' so if there's too\n"
+ "many CTCP requests in buffer, this reply might not get sent. The data\n"
+ "is the full raw command to be sent to server, like\n"
+ "'NOTICE nick :\001VERSION irssi\001'\n"
+);
+static PyObject *PyIrcServer_ctcp_send_reply(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data))
+ return NULL;
+
+ ctcp_send_reply(self->data, data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(isupport_doc,
+ "isupport(name) -> str or None\n"
+ "\n"
+ "Returns the value of the named item in the ISUPPORT (005) numeric to the\n"
+ "script. If the item is not present returns undef, if the item has no value\n"
+ "then '' is returned use defined server.isupport('name') if you need to\n"
+ "check whether a property is present.\n"
+ "See http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-01.txt\n"
+ "for more information on the ISUPPORT numeric.\n"
+);
+static PyObject *PyIrcServer_isupport(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name;
+ char *found;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name))
+ return NULL;
+
+ found = g_hash_table_lookup(self->data->isupport, name);
+
+ RET_AS_STRING_OR_NONE(found);
+}
+
+PyDoc_STRVAR(PyIrcServer_netsplit_find_doc,
+ "netsplit_find(nick, address) -> Netsplit object or None\n"
+ "\n"
+ "Check if nick!address is on the other side of netsplit. Netsplit records\n"
+ "are automatically removed after 30 minutes (current default)..\n"
+);
+static PyObject *PyIrcServer_netsplit_find(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "address", NULL};
+ char *nick = "";
+ char *address = "";
+ NETSPLIT_REC *ns;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &nick, &address))
+ return NULL;
+
+ ns = netsplit_find(self->data, nick, address);
+ if (ns)
+ return pynetsplit_new(ns);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_netsplit_find_channel_doc,
+ "netsplit_find_channel(nick, address, channel) -> NetsplitChannel object or None\n"
+ "\n"
+ "Find nick record for nick!address in channel `channel'.\n"
+);
+static PyObject *PyIrcServer_netsplit_find_channel(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "address", "channel", NULL};
+ char *nick = "";
+ char *address = "";
+ char *channel = "";
+ NETSPLIT_CHAN_REC *nsc;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &nick, &address, &channel))
+ return NULL;
+
+ nsc = netsplit_find_channel(self->data, nick, address, channel);
+ if (nsc)
+ return pynetsplit_channel_new(nsc);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_notifylist_ison_doc,
+ "notifylist_ison(nick) -> bool\n"
+ "\n"
+ "Check if nick is on server\n"
+);
+static PyObject *PyIrcServer_notifylist_ison(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return PyBool_FromLong(notifylist_ison_server(self->data, nick));
+}
+
+/* expect a list of tuples [('str', 'str'), ...] */
+static GSList *py_event_conv(PyObject *list)
+{
+ int i;
+ GSList *ret = NULL;
+
+ if (!PyList_Check(list))
+ {
+ PyErr_Format(PyExc_TypeError, "expect a list of tuples of two strings");
+ return NULL;
+ }
+
+ for (i = 0; i < PyList_Size(list); i++)
+ {
+ char *key;
+ char *val;
+ PyObject *tup = PyList_GET_ITEM(list, i);
+
+ if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "ss", &key, &val))
+ {
+ GSList *node;
+
+ for (node = ret; node; node = node->next)
+ g_free(node->data);
+
+ g_slist_free(ret);
+
+ if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError))
+ {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "expect a list of tuples of two strings");
+ }
+
+ return NULL;
+ }
+
+ ret = g_slist_append(ret, g_strdup(key));
+ ret = g_slist_append(ret, g_strdup(val));
+ }
+
+ return ret;
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_event_doc,
+ "redirect_event(command, signals, arg=None, count=1, remote=-1, failure_signal=None) -> None\n"
+ "\n"
+ "Specify that the next command sent to server will be redirected.\n"
+ "NOTE: This command MUST be called before sending the command to server.\n"
+ "\n"
+ "`command' - Name of the registered redirection that we're using.\n"
+ "\n"
+ "`count' - How many times to execute the redirection. Some commands may\n"
+ "send multiple stop events, like MODE #a,#b.\n"
+ "\n"
+ "`arg' - The argument to be compared in event strings. You can give multiple\n"
+ "arguments separated with space.\n"
+ "\n"
+ "`remote' - Specifies if the command is a remote command, -1 = use default.\n"
+ "\n"
+ "`failure_signal' - If irssi can't find the stop signal for the redirection,\n"
+ "this signal is called.\n"
+ "\n"
+ "`signals' - hash reference with \"event\" => \"redir signal\" entries.\n"
+ "If the event is "", all the events belonging to the redirection but not\n"
+ "specified here, will be sent there.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ "# ignore all events generated by whois query, except 311.\n"
+ "\n"
+ "server.redirect_event(\"whois\",\n"
+ " remote = 0,\n"
+ " arg = \"cras\",\n"
+ " signals = [\n"
+ " ('event 311', 'redir whois'),\n"
+ " ('', 'event empty') \n"
+ " ]\n"
+ ")\n"
+ "server.send_raw(\"WHOIS :cras\")\n"
+);
+static PyObject *PyIrcServer_redirect_event(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"command", "signals", "arg", "count", "remote", "failure_signal", NULL};
+ char *command = "";
+ int count = 1;
+ char *arg = NULL;
+ int remote = -1;
+ char *failure_signal = NULL;
+ PyObject *signals = NULL;
+ GSList *gsignals;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|ziiz", kwlist,
+ &command, &signals, &arg, &count, &remote, &failure_signal))
+ return NULL;
+
+ gsignals = py_event_conv(signals);
+ if (!gsignals)
+ return NULL;
+
+ server_redirect_event(self->data, command, count, arg, remote, failure_signal, gsignals);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_get_signal_doc,
+ "redirect_get_signal(event, args) -> str\n"
+);
+static PyObject *PyIrcServer_redirect_get_signal(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"event", "args", NULL};
+ char *event = "";
+ char *pargs = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &event, &pargs))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(server_redirect_get_signal(self->data, event, pargs));
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_peek_signal_doc,
+ "redirect_peek_signal(event, args) -> str\n"
+);
+static PyObject *PyIrcServer_redirect_peek_signal(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"event", "args", NULL};
+ char *event = "";
+ char *pargs = "";
+ int redirection;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &event, &pargs))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(server_redirect_peek_signal(self->data, event, pargs, &redirection));
+}
+
+/* Methods for object */
+static PyMethodDef PyIrcServer_methods[] = {
+ {"get_channels", (PyCFunction)PyIrcServer_get_channels, METH_NOARGS,
+ get_channels_doc},
+ {"send_raw", (PyCFunction)PyIrcServer_send_raw, METH_VARARGS | METH_KEYWORDS,
+ send_raw_doc},
+ {"send_raw_now", (PyCFunction)PyIrcServer_send_raw_now, METH_VARARGS | METH_KEYWORDS,
+ send_raw_now_doc},
+ {"send_raw_split", (PyCFunction)PyIrcServer_send_raw_split, METH_VARARGS | METH_KEYWORDS,
+ send_raw_split_doc},
+ {"ctcp_send_reply", (PyCFunction)PyIrcServer_ctcp_send_reply, METH_VARARGS | METH_KEYWORDS,
+ ctcp_send_reply_doc},
+ {"isupport", (PyCFunction)PyIrcServer_isupport, METH_VARARGS | METH_KEYWORDS,
+ isupport_doc},
+ {"netsplit_find", (PyCFunction)PyIrcServer_netsplit_find, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_netsplit_find_doc},
+ {"netsplit_find_channel", (PyCFunction)PyIrcServer_netsplit_find_channel, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_netsplit_find_channel_doc},
+ {"notifylist_ison", (PyCFunction)PyIrcServer_notifylist_ison, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_notifylist_ison_doc},
+ {"redirect_event", (PyCFunction)PyIrcServer_redirect_event, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_event_doc},
+ {"redirect_get_signal", (PyCFunction)PyIrcServer_redirect_get_signal, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_get_signal_doc},
+ {"redirect_peek_signal", (PyCFunction)PyIrcServer_redirect_peek_signal, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_peek_signal_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIrcServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcServer", /*tp_name*/
+ sizeof(PyIrcServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrcServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcServer_getseters, /* tp_getset */
+ &PyServerType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pyirc_server_new(void *server)
+{
+ return pyserver_sub_new(server, &PyIrcServerType);
+}
+
+int irc_server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcServerType);
+ PyModule_AddObject(py_module, "IrcServer", (PyObject *)&PyIrcServerType);
+
+ return 1;
+}
diff --git a/src/objects/irc-server-object.h b/src/objects/irc-server-object.h
new file mode 100644
index 0000000..b3eb76a
--- /dev/null
+++ b/src/objects/irc-server-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_SERVER_OBJECT_H_
+#define _IRC_SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "server-object.h"
+
+/* forward */
+struct _IRC_SERVER_REC;
+
+typedef struct
+{
+ PyServer_HEAD(struct _IRC_SERVER_REC)
+} PyIrcServer;
+
+extern PyTypeObject PyIrcServerType;
+
+int irc_server_object_init(void);
+PyObject *pyirc_server_new(void *server);
+#define pyirc_server_check(op) PyObject_TypeCheck(op, &PyIrcServerType)
+
+#endif
diff --git a/src/objects/log-object.c b/src/objects/log-object.c
new file mode 100644
index 0000000..b18f826
--- /dev/null
+++ b/src/objects/log-object.c
@@ -0,0 +1,477 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "log-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item);
+static void log_cleanup(LOG_REC *log);
+static int logtype(int *type, int target, int window);
+
+/* find/convert a py log item */
+static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item)
+{
+ int type;
+ char *name;
+ char *servertag = NULL;
+
+ if (!item->type || !item->name)
+ return NULL;
+
+ type = PyInt_AS_LONG(item->type);
+ name = PyString_AS_STRING(item->name);
+ if (item->servertag)
+ servertag = PyString_AS_STRING(item->servertag);
+
+ return log_item_find(log, type, name, servertag);
+}
+
+/* monitor "log remove" signal */
+static void log_cleanup(LOG_REC *log)
+{
+ PyLog *pylog = signal_get_user_data();
+
+ if (log == pylog->data)
+ {
+ pylog->data = NULL;
+ pylog->cleanup_installed = 0;
+ signal_remove_data("log remove", log_cleanup, pylog);
+ }
+}
+
+static void PyLog_dealloc(PyLog *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("log remove", log_cleanup, self);
+
+ if (self->data && !g_slist_find(logs, self->data))
+ {
+ printtext(NULL, NULL, MSGLEVEL_CRAP, "destroying orphan log %s", self->data->fname);
+ log_close(self->data);
+ }
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyLog_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyLog *self;
+
+ self = (PyLog *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* function to create the log */
+PyDoc_STRVAR(PyLog_doc,
+ "__init__(fname, level=MSGLEVEL_ALL)\n"
+ "\n"
+ "Create a log\n"
+);
+static int PyLog_init(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ char *fname;
+ int level = MSGLEVEL_ALL;
+ LOG_REC *log;
+
+ static char *kwlist[] = {"fname", "level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &fname, &level))
+ return -1;
+
+ /*XXX: anything better than RuntimeError ? */
+ if (self->data || self->cleanup_installed)
+ {
+ PyErr_Format(PyExc_RuntimeError, "log already opened; close it first");
+ return -1;
+ }
+
+ log = log_create_rec(fname, level);
+ if (!log)
+ {
+ PyErr_Format(PyExc_RuntimeError, "failed to create log");
+ return -1;
+ }
+
+ self->data = log;
+ self->cleanup_installed = 1;
+ signal_add_last_data("log remove", log_cleanup, self);
+
+ return 0;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyLog_fname_doc,
+ "Log file name"
+);
+static PyObject *PyLog_fname_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->fname);
+}
+
+PyDoc_STRVAR(PyLog_real_fname_doc,
+ "The actual opened log file (after %d.%m.Y etc. are expanded)"
+);
+static PyObject *PyLog_real_fname_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->real_fname);
+}
+
+PyDoc_STRVAR(PyLog_opened_doc,
+ "Log file is open"
+);
+static PyObject *PyLog_opened_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->opened);
+}
+
+PyDoc_STRVAR(PyLog_level_doc,
+ "Log only these levels"
+);
+static PyObject *PyLog_level_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyLog_last_doc,
+ "Timestamp when last message was written"
+);
+static PyObject *PyLog_last_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last);
+}
+
+PyDoc_STRVAR(PyLog_autoopen_doc,
+ "Automatically open log at startup"
+);
+static PyObject *PyLog_autoopen_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->autoopen);
+}
+
+PyDoc_STRVAR(PyLog_failed_doc,
+ "Opening log failed last time"
+);
+static PyObject *PyLog_failed_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->failed);
+}
+
+PyDoc_STRVAR(PyLog_temp_doc,
+ "Log isn't saved to config file"
+);
+static PyObject *PyLog_temp_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->temp);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyLog_getseters[] = {
+ {"fname", (getter)PyLog_fname_get, NULL,
+ PyLog_fname_doc, NULL},
+ {"real_fname", (getter)PyLog_real_fname_get, NULL,
+ PyLog_real_fname_doc, NULL},
+ {"opened", (getter)PyLog_opened_get, NULL,
+ PyLog_opened_doc, NULL},
+ {"level", (getter)PyLog_level_get, NULL,
+ PyLog_level_doc, NULL},
+ {"last", (getter)PyLog_last_get, NULL,
+ PyLog_last_doc, NULL},
+ {"autoopen", (getter)PyLog_autoopen_get, NULL,
+ PyLog_autoopen_doc, NULL},
+ {"failed", (getter)PyLog_failed_get, NULL,
+ PyLog_failed_doc, NULL},
+ {"temp", (getter)PyLog_temp_get, NULL,
+ PyLog_temp_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyLog_items_doc,
+ "items() -> list of Log objects\n"
+ "\n"
+ "Return a list of log items\n"
+);
+static PyObject *PyLog_items(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_objlist_new(self->data->items, 1, (InitFunc)pylogitem_new);
+}
+
+PyDoc_STRVAR(PyLog_update_doc,
+ "update() -> None\n"
+ "\n"
+ "Add log to list of logs / save changes to config file.\n"
+);
+static PyObject *PyLog_update(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_update(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_close_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy the log file\n"
+);
+static PyObject *PyLog_close(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_start_logging_doc,
+ "start_logging() -> None\n"
+ "\n"
+ "Open log file and start logging.\n"
+);
+static PyObject *PyLog_start_logging(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_start_logging(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_stop_logging_doc,
+ "stop_logging() -> None\n"
+ "\n"
+ "Stop and close the log file.\n"
+);
+static PyObject *PyLog_stop_logging(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_stop_logging(self->data);
+
+ Py_RETURN_NONE;
+}
+
+static int logtype(int *type, int target, int window)
+{
+ if (target || window)
+ {
+ if (target && window)
+ {
+ PyErr_SetString(PyExc_TypeError, "must specify target or window, not both");
+ return 0;
+ }
+
+ *type = target? 0 : 1;
+ }
+ else if (*type < 0)
+ {
+ PyErr_SetString(PyExc_TypeError, "must specify type, target, or window");
+ return 0;
+ }
+
+ return 1;
+}
+
+PyDoc_STRVAR(PyLog_item_add_doc,
+ "item_add(item, servertag=None, type=0, target=False, window=False) -> None\n"
+ "\n"
+ "Add a log item to log.\n"
+ "\n"
+ "Add a target item (nick, chan): \n"
+ " item_add('#linux', target=True)\n"
+ " item_add('#linux', type=0)\n"
+ "\n"
+ "Add a window ref: \n"
+ " item_add('2', window=True)\n"
+ " item_add('2', type=1)\n"
+);
+static PyObject *PyLog_item_add(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL};
+ char *item = "";
+ char *servertag = NULL;
+ int type = 0;
+ int target = 0;
+ int window = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist,
+ &item, &servertag, &type, &target, &window))
+ return NULL;
+
+ if (!logtype(&type, target, window))
+ return NULL;
+
+ log_item_add(self->data, type, item, servertag);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_item_destroy_doc,
+ "item_destroy(item) -> None\n"
+ "\n"
+ "Remove log item from log.\n"
+);
+static PyObject *PyLog_item_destroy(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+ LOG_ITEM_REC *li;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pylogitem_check(item))
+ return PyErr_Format(PyExc_TypeError, "arg 1 should be log item");
+
+ li = find_item(self->data, (PyLogitem *)item);
+ if (!li)
+ return PyErr_Format(PyExc_TypeError, "log item invalid or not found");
+
+ log_item_destroy(self->data, li);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_item_find_doc,
+ "item_find(item, servertag=None, type=-1, target=False, window=False) -> item or None\n"
+ "\n"
+ "Find item from log.\n"
+);
+static PyObject *PyLog_item_find(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL};
+ char *item = "";
+ char *server = NULL;
+ int type = 0;
+ int target = 0;
+ int window = 0;
+ LOG_ITEM_REC *li;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist,
+ &item, &server, &type, &target, &window))
+ return NULL;
+
+ if (!logtype(&type, target, window))
+ return NULL;
+
+ li = log_item_find(self->data, type, item, server);
+ if (li)
+ return pylogitem_new(li);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyLog_methods[] = {
+ {"items", (PyCFunction)PyLog_items, METH_NOARGS,
+ PyLog_items_doc},
+ {"update", (PyCFunction)PyLog_update, METH_NOARGS,
+ PyLog_update_doc},
+ {"close", (PyCFunction)PyLog_close, METH_NOARGS,
+ PyLog_close_doc},
+ {"start_logging", (PyCFunction)PyLog_start_logging, METH_NOARGS,
+ PyLog_start_logging_doc},
+ {"stop_logging", (PyCFunction)PyLog_stop_logging, METH_NOARGS,
+ PyLog_stop_logging_doc},
+ {"item_add", (PyCFunction)PyLog_item_add, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_add_doc},
+ {"item_destroy", (PyCFunction)PyLog_item_destroy, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_destroy_doc},
+ {"item_find", (PyCFunction)PyLog_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_find_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyLogType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Log", /*tp_name*/
+ sizeof(PyLog), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyLog_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ PyLog_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyLog_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyLog_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyLog_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyLog_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pylog_new(void *log)
+{
+ PyLog *pylog;
+
+ pylog = (PyLog *)PyLogType.tp_alloc(&PyLogType, 0);
+ if (!pylog)
+ return NULL;
+
+ pylog->data = log;
+ pylog->cleanup_installed = 1;
+ signal_add_last_data("log remove", log_cleanup, pylog);
+
+ return (PyObject *)pylog;
+}
+
+int log_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyLogType) < 0)
+ return 0;
+
+ Py_INCREF(&PyLogType);
+ PyModule_AddObject(py_module, "Log", (PyObject *)&PyLogType);
+
+ return 1;
+}
diff --git a/src/objects/log-object.h b/src/objects/log-object.h
new file mode 100644
index 0000000..9893bc5
--- /dev/null
+++ b/src/objects/log-object.h
@@ -0,0 +1,21 @@
+#ifndef _LOG_OBJECT_H_
+#define _LOG_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _LOG_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _LOG_REC)
+} PyLog;
+
+extern PyTypeObject PyLogType;
+
+int log_object_init(void);
+PyObject *pylog_new(void *log);
+#define pylog_check(op) PyObject_TypeCheck(op, &PyLogType)
+
+#endif
diff --git a/src/objects/logitem-object.c b/src/objects/logitem-object.c
new file mode 100644
index 0000000..4dc2275
--- /dev/null
+++ b/src/objects/logitem-object.c
@@ -0,0 +1,156 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "logitem-object.h"
+#include "pycore.h"
+
+/* no special cleanup -- value copy is made */
+
+static void PyLogitem_dealloc(PyLogitem *self)
+{
+ Py_XDECREF(self->type);
+ Py_XDECREF(self->name);
+ Py_XDECREF(self->servertag);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyLogitem_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyLogitem *self;
+
+ self = (PyLogitem *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyLogitem_type_doc,
+ "0=target, 1=window refnum"
+);
+static PyObject *PyLogitem_type_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->type);
+}
+
+PyDoc_STRVAR(PyLogitem_name_doc,
+ "Name"
+);
+static PyObject *PyLogitem_name_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->name);
+}
+
+PyDoc_STRVAR(PyLogitem_servertag_doc,
+ "Server tag"
+);
+static PyObject *PyLogitem_servertag_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->servertag);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyLogitem_getseters[] = {
+ {"type", (getter)PyLogitem_type_get, NULL,
+ PyLogitem_type_doc, NULL},
+ {"name", (getter)PyLogitem_name_get, NULL,
+ PyLogitem_name_doc, NULL},
+ {"servertag", (getter)PyLogitem_servertag_get, NULL,
+ PyLogitem_servertag_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyLogitem_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyLogitemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Logitem", /*tp_name*/
+ sizeof(PyLogitem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyLogitem_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyLogitem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyLogitem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyLogitem_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyLogitem_new, /* tp_new */
+};
+
+
+/* log item factory function */
+PyObject *pylogitem_new(void *log)
+{
+ LOG_ITEM_REC *li = log;
+ PyLogitem *pylog = NULL;
+
+ pylog = py_inst(PyLogitem, PyLogitemType);
+ if (!pylog)
+ return NULL;
+
+ pylog->type = PyInt_FromLong(li->type);
+ if (!pylog->type)
+ goto error;
+
+ pylog->name = PyString_FromString(li->name);
+ if (!pylog->name)
+ goto error;
+
+ if (li->servertag)
+ {
+ pylog->servertag = PyString_FromString(li->servertag);
+ if (!pylog->servertag)
+ goto error;
+ }
+
+ return (PyObject *)pylog;
+
+error:
+ Py_XDECREF(pylog);
+ return NULL;
+}
+
+int logitem_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyLogitemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyLogitemType);
+ PyModule_AddObject(py_module, "Logitem", (PyObject *)&PyLogitemType);
+
+ return 1;
+}
diff --git a/src/objects/logitem-object.h b/src/objects/logitem-object.h
new file mode 100644
index 0000000..b7fd588
--- /dev/null
+++ b/src/objects/logitem-object.h
@@ -0,0 +1,21 @@
+#ifndef _LOG_ITEM_OBJECT_H_
+#define _LOG_ITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ PyObject *type;
+ PyObject *name;
+ PyObject *servertag;
+} PyLogitem;
+
+extern PyTypeObject PyLogitemType;
+
+int logitem_object_init(void);
+PyObject *pylogitem_new(void *log);
+#define pylogitem_check(op) PyObject_TypeCheck(op, &PyLogitemType)
+
+#endif
diff --git a/src/objects/main-window-object.c b/src/objects/main-window-object.c
new file mode 100644
index 0000000..6541f2b
--- /dev/null
+++ b/src/objects/main-window-object.c
@@ -0,0 +1,199 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "main-window-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define MW(data) ((MAIN_WINDOW_REC *) data)
+
+/* monitor "mainwindow destroyed" signal */
+static void main_window_cleanup(MAIN_WINDOW_REC *mw)
+{
+ PyMainWindow *pymw = signal_get_user_data();
+
+ if (mw == pymw->data)
+ {
+ pymw->data = NULL;
+ pymw->cleanup_installed = 0;
+ signal_remove_data("mainwindow destroyed", main_window_cleanup, pymw);
+ }
+}
+
+static void PyMainWindow_dealloc(PyMainWindow *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("mainwindow destroyed", main_window_cleanup, self);
+
+ Py_XDECREF(self->active);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyMainWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyMainWindow *self;
+
+ self = (PyMainWindow *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* getters */
+PyDoc_STRVAR(PyMainWindow_active_doc,
+ "active window object"
+);
+static PyObject *PyMainWindow_active_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->active);
+}
+
+PyDoc_STRVAR(PyMainWindow_first_line_doc,
+ "first line used by this window (0..x) (includes statusbars)"
+);
+static PyObject *PyMainWindow_first_line_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->first_line);
+}
+
+PyDoc_STRVAR(PyMainWindow_last_line_doc,
+ "last line used by this window (0..x) (includes statusbars)"
+);
+static PyObject *PyMainWindow_last_line_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->last_line);
+}
+
+PyDoc_STRVAR(PyMainWindow_width_doc,
+ "width of the window (includes statusbars)"
+);
+static PyObject *PyMainWindow_width_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->width);
+}
+
+PyDoc_STRVAR(PyMainWindow_height_doc,
+ "height of the window (includes statusbars)"
+);
+static PyObject *PyMainWindow_height_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->height);
+}
+
+PyDoc_STRVAR(PyMainWindow_statusbar_lines_doc,
+ "???"
+);
+static PyObject *PyMainWindow_statusbar_lines_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->statusbar_lines);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyMainWindow_getseters[] = {
+ {"active", (getter)PyMainWindow_active_get, NULL,
+ PyMainWindow_active_doc, NULL},
+ {"first_line", (getter)PyMainWindow_first_line_get, NULL,
+ PyMainWindow_first_line_doc, NULL},
+ {"last_line", (getter)PyMainWindow_last_line_get, NULL,
+ PyMainWindow_last_line_doc, NULL},
+ {"width", (getter)PyMainWindow_width_get, NULL,
+ PyMainWindow_width_doc, NULL},
+ {"height", (getter)PyMainWindow_height_get, NULL,
+ PyMainWindow_height_doc, NULL},
+ {"statusbar_lines", (getter)PyMainWindow_statusbar_lines_get, NULL,
+ PyMainWindow_statusbar_lines_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyMainWindow_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyMainWindowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "MainWindow", /*tp_name*/
+ sizeof(PyMainWindow), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyMainWindow_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyMainWindow objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyMainWindow_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyMainWindow_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyMainWindow_new, /* tp_new */
+};
+
+
+/* main window wrapper factory function */
+PyObject *pymain_window_new(MAIN_WINDOW_REC *mw)
+{
+ PyObject *pyactive = NULL;
+ PyMainWindow *pymw;
+
+ pyactive = pywindow_new(mw->active);
+ if (!pyactive)
+ return NULL;
+
+ pymw = py_inst(PyMainWindow, PyMainWindowType);
+ if (!pymw)
+ {
+ Py_DECREF(pyactive);
+ return NULL;
+ }
+
+ pymw->active = pyactive;
+ pymw->data = mw;
+ pymw->cleanup_installed = 1;
+ signal_add_last_data("mainwindow destroyed", main_window_cleanup, pymw);
+
+ return (PyObject *)pymw;
+}
+
+int main_window_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyMainWindowType) < 0)
+ return 0;
+
+ Py_INCREF(&PyMainWindowType);
+ PyModule_AddObject(py_module, "MainWindow", (PyObject *)&PyMainWindowType);
+
+ return 1;
+}
diff --git a/src/objects/main-window-object.h b/src/objects/main-window-object.h
new file mode 100644
index 0000000..fbbdac0
--- /dev/null
+++ b/src/objects/main-window-object.h
@@ -0,0 +1,20 @@
+#ifndef _MAIN_WINDOW_OBJECT_H_
+#define _MAIN_WINDOW_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+#include "pyirssi.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *active;
+} PyMainWindow;
+
+extern PyTypeObject PyMainWindowType;
+
+int main_window_object_init(void);
+PyObject *pymain_window_new(MAIN_WINDOW_REC *mw);
+#define pymain_window_check(op) PyObject_TypeCheck(op, &PyMainWindowType)
+
+#endif
diff --git a/src/objects/netsplit-channel-object.c b/src/objects/netsplit-channel-object.c
new file mode 100644
index 0000000..3e2639b
--- /dev/null
+++ b/src/objects/netsplit-channel-object.c
@@ -0,0 +1,170 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-channel-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* value copied -- no special cleanup */
+
+static void PyNetsplitChannel_dealloc(PyNetsplitChannel *self)
+{
+ Py_XDECREF(self->name);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplitChannel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplitChannel *self;
+
+ self = (PyNetsplitChannel *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplitChannel_name_doc,
+ "Channel name"
+);
+static PyObject *PyNetsplitChannel_name_get(PyNetsplitChannel *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->name);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_op_doc,
+ "is op"
+);
+static PyObject *PyNetsplitChannel_op_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->op);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_halfop_doc,
+ "is halfop"
+);
+static PyObject *PyNetsplitChannel_halfop_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->halfop);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_voice_doc,
+ "is voice"
+);
+static PyObject *PyNetsplitChannel_voice_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->voice);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_other_doc,
+ "?"
+);
+static PyObject *PyNetsplitChannel_other_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyInt_FromLong(self->other);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplitChannel_getseters[] = {
+ {"name", (getter)PyNetsplitChannel_name_get, NULL,
+ PyNetsplitChannel_name_doc, NULL},
+ {"op", (getter)PyNetsplitChannel_op_get, NULL,
+ PyNetsplitChannel_op_doc, NULL},
+ {"halfop", (getter)PyNetsplitChannel_halfop_get, NULL,
+ PyNetsplitChannel_halfop_doc, NULL},
+ {"voice", (getter)PyNetsplitChannel_voice_get, NULL,
+ PyNetsplitChannel_voice_doc, NULL},
+ {"other", (getter)PyNetsplitChannel_other_get, NULL,
+ PyNetsplitChannel_other_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyNetsplitChannel_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "NetsplitChannel", /*tp_name*/
+ sizeof(PyNetsplitChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplitChannel_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplitChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplitChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplitChannel_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplitChannel_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_channel_new(void *netsplit)
+{
+ NETSPLIT_CHAN_REC *rec = netsplit;
+ PyNetsplitChannel *pynetsplit;
+ PyObject *name;
+
+ name = PyString_FromString(rec->name);
+ if (!name)
+ return NULL;
+
+ pynetsplit = py_inst(PyNetsplitChannel, PyNetsplitChannelType);
+ if (!pynetsplit)
+ {
+ Py_DECREF(name);
+ return NULL;
+ }
+
+ pynetsplit->name = name;
+ pynetsplit->op = rec->op;
+ pynetsplit->halfop = rec->halfop;
+ pynetsplit->other = rec->other;
+
+ return (PyObject *)pynetsplit;
+}
+
+int netsplit_channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitChannelType);
+ PyModule_AddObject(py_module, "NetsplitChannel", (PyObject *)&PyNetsplitChannelType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-channel-object.h b/src/objects/netsplit-channel-object.h
new file mode 100644
index 0000000..3a00fbb
--- /dev/null
+++ b/src/objects/netsplit-channel-object.h
@@ -0,0 +1,21 @@
+#ifndef _NETSPLIT_CHANNEL_OBJECT_H_
+#define _NETSPLIT_CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ PyObject *name;
+ int op, halfop;
+ int voice, other;
+} PyNetsplitChannel;
+
+extern PyTypeObject PyNetsplitChannelType;
+
+int netsplit_channel_object_init(void);
+PyObject *pynetsplit_channel_new(void *ns);
+#define pynetsplit_channel_check(op) PyObject_TypeCheck(op, &PyNetsplitChannelType)
+
+#endif
diff --git a/src/objects/netsplit-object.c b/src/objects/netsplit-object.c
new file mode 100644
index 0000000..d64bd97
--- /dev/null
+++ b/src/objects/netsplit-object.c
@@ -0,0 +1,184 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define NETSPLIT(ns) ((NETSPLIT_REC*)ns)
+
+/* monitor "netsplit remove" signal */
+static void netsplit_cleanup(NETSPLIT_REC *netsplit)
+{
+ PyNetsplit *pynetsplit = signal_get_user_data();
+
+ if (netsplit == pynetsplit->data)
+ {
+ pynetsplit->data = NULL;
+ pynetsplit->cleanup_installed = 0;
+ signal_remove_data("netsplit remove", netsplit_cleanup, pynetsplit);
+ }
+}
+
+static void PyNetsplit_dealloc(PyNetsplit *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("netsplit remove", netsplit_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplit_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplit *self;
+
+ self = (PyNetsplit *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplit_nick_doc,
+ "Nick"
+);
+static PyObject *PyNetsplit_nick_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->nick);
+}
+
+PyDoc_STRVAR(PyNetsplit_address_doc,
+ "Nick's host"
+);
+static PyObject *PyNetsplit_address_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->address);
+}
+
+PyDoc_STRVAR(PyNetsplit_destroy_doc,
+ "Timestamp when this record should be destroyed"
+);
+static PyObject *PyNetsplit_destroy_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(NETSPLIT(self->data)->destroy);
+}
+
+PyDoc_STRVAR(PyNetsplit_server_doc,
+ "Netsplitserver object"
+);
+static PyObject *PyNetsplit_server_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplit_getseters[] = {
+ {"nick", (getter)PyNetsplit_nick_get, NULL,
+ PyNetsplit_nick_doc, NULL},
+ {"address", (getter)PyNetsplit_address_get, NULL,
+ PyNetsplit_address_doc, NULL},
+ {"destroy", (getter)PyNetsplit_destroy_get, NULL,
+ PyNetsplit_destroy_doc, NULL},
+ {"server", (getter)PyNetsplit_server_get, NULL,
+ PyNetsplit_server_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyNetsplit_channels_doc,
+ "channels() -> list of NetsplitChannel objects\n"
+ "\n"
+ "Return list of NetsplitChannel objects\n"
+);
+static PyObject *PyNetsplit_channels(PyNetsplit *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_objlist_new(NETSPLIT(self->data)->channels, 1,
+ (InitFunc)pynetsplit_channel_new);
+}
+
+/* Methods for object */
+static PyMethodDef PyNetsplit_methods[] = {
+ {"channels", (PyCFunction)PyNetsplit_channels, METH_NOARGS,
+ PyNetsplit_channels_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Netsplit", /*tp_name*/
+ sizeof(PyNetsplit), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplit_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplit objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplit_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplit_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplit_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_new(void *netsplit)
+{
+ PyNetsplit *pynetsplit;
+
+ //FIXME: add netsplit server
+
+ pynetsplit = py_inst(PyNetsplit, PyNetsplitType);
+ if (!pynetsplit)
+ return NULL;
+
+ pynetsplit->data = netsplit;
+ pynetsplit->cleanup_installed = 1;
+ signal_add_last_data("netsplit remove", netsplit_cleanup, pynetsplit);
+
+ return (PyObject *)pynetsplit;
+}
+
+int netsplit_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitType);
+ PyModule_AddObject(py_module, "Netsplit", (PyObject *)&PyNetsplitType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-object.h b/src/objects/netsplit-object.h
new file mode 100644
index 0000000..bd166e0
--- /dev/null
+++ b/src/objects/netsplit-object.h
@@ -0,0 +1,19 @@
+#ifndef _NETSPLIT_OBJECT_H_
+#define _NETSPLIT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *server;
+} PyNetsplit;
+
+extern PyTypeObject PyNetsplitType;
+
+int netsplit_object_init(void);
+PyObject *pynetsplit_new(void *ns);
+#define pynetsplit_check(op) PyObject_TypeCheck(op, &PyNetsplitType)
+
+#endif
diff --git a/src/objects/netsplit-server-object.c b/src/objects/netsplit-server-object.c
new file mode 100644
index 0000000..7208ae1
--- /dev/null
+++ b/src/objects/netsplit-server-object.c
@@ -0,0 +1,157 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define NETSPLIT_SERVER(ns) ((NETSPLIT_SERVER_REC*)ns)
+
+/* monitor "netsplit remove" signal */
+static void netsplit_server_cleanup(NETSPLIT_SERVER_REC *netsplit)
+{
+ PyNetsplitServer *pynetsplit = signal_get_user_data();
+
+ if (netsplit == pynetsplit->data)
+ {
+ pynetsplit->data = NULL;
+ pynetsplit->cleanup_installed = 0;
+ signal_remove_data("netsplit remove", netsplit_server_cleanup, pynetsplit);
+ }
+}
+
+static void PyNetsplitServer_dealloc(PyNetsplitServer *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("netsplit remove", netsplit_server_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplitServer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplitServer *self;
+
+ self = (PyNetsplitServer *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplitServer_server_doc,
+ "The server nick was in"
+);
+static PyObject *PyNetsplitServer_server_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->server);
+}
+
+PyDoc_STRVAR(PyNetsplitServer_destserver_doc,
+ "The other server where split occured."
+);
+static PyObject *PyNetsplitServer_destserver_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->destserver);
+}
+
+PyDoc_STRVAR(PyNetsplitServer_count_doc,
+ "Number of splits in server"
+);
+static PyObject *PyNetsplitServer_count_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(NETSPLIT_SERVER(self->data)->count);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplitServer_getseters[] = {
+ {"server", (getter)PyNetsplitServer_server_get, NULL,
+ PyNetsplitServer_server_doc, NULL},
+ {"destserver", (getter)PyNetsplitServer_destserver_get, NULL,
+ PyNetsplitServer_destserver_doc, NULL},
+ {"count", (getter)PyNetsplitServer_count_get, NULL,
+ PyNetsplitServer_count_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyNetsplitServer_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "NetsplitServer", /*tp_name*/
+ sizeof(PyNetsplitServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplitServer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplitServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplitServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplitServer_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplitServer_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_server_new(void *nss)
+{
+ PyNetsplitServer *pynss;
+
+ pynss = py_inst(PyNetsplitServer, PyNetsplitServerType);
+ if (!pynss)
+ return NULL;
+
+ pynss->data = nss;
+ pynss->cleanup_installed = 1;
+ signal_add_last_data("netsplit server remove", netsplit_server_cleanup, pynss);
+
+ return (PyObject *)pynss;
+}
+
+int netsplit_server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitServerType);
+ PyModule_AddObject(py_module, "NetsplitServer", (PyObject *)&PyNetsplitServerType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-server-object.h b/src/objects/netsplit-server-object.h
new file mode 100644
index 0000000..f1d5fbf
--- /dev/null
+++ b/src/objects/netsplit-server-object.h
@@ -0,0 +1,18 @@
+#ifndef _NETSPLIT_SERVER_OBJECT_H_
+#define _NETSPLIT_SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyNetsplitServer;
+
+extern PyTypeObject PyNetsplitServerType;
+
+int netsplit_server_object_init(void);
+PyObject *pynetsplit_server_new(void *nss);
+#define pynetsplit_server_check(op) PyObject_TypeCheck(op, &PyNetsplitServerType)
+
+#endif
diff --git a/src/objects/nick-object.c b/src/objects/nick-object.c
new file mode 100644
index 0000000..79f3a85
--- /dev/null
+++ b/src/objects/nick-object.c
@@ -0,0 +1,237 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "nick-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void nick_cleanup(CHANNEL_REC *chan, NICK_REC *nick)
+{
+ PyNick *pynick = signal_get_user_data();
+
+ if (nick == pynick->data)
+ {
+ pynick->data = NULL;
+ pynick->cleanup_installed = 0;
+ signal_remove_data("nicklist remove", nick_cleanup, pynick);
+ }
+}
+
+static void PyNick_dealloc(PyNick *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("nicklist remove", nick_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNick_send_massjoin_doc,
+ "Waiting to be sent in a 'massjoin' signal, True or False"
+);
+static PyObject *PyNick_send_massjoin_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->send_massjoin);
+}
+
+PyDoc_STRVAR(PyNick_nick_doc,
+ "Plain nick"
+);
+static PyObject *PyNick_nick_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyNick_host_doc,
+ "Host address"
+);
+static PyObject *PyNick_host_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->host);
+}
+
+PyDoc_STRVAR(PyNick_realname_doc,
+ "Real name"
+);
+static PyObject *PyNick_realname_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+PyDoc_STRVAR(PyNick_hops_doc,
+ "Hop count to the server the nick is using"
+);
+static PyObject *PyNick_hops_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->hops);
+}
+
+PyDoc_STRVAR(PyNick_gone_doc,
+ "User status"
+);
+static PyObject *PyNick_gone_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->gone);
+}
+
+PyDoc_STRVAR(PyNick_serverop_doc,
+ "User status"
+);
+static PyObject *PyNick_serverop_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->serverop);
+}
+
+PyDoc_STRVAR(PyNick_op_doc,
+ "User status"
+);
+static PyObject *PyNick_op_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->op);
+}
+
+PyDoc_STRVAR(PyNick_voice_doc,
+ "User status"
+);
+static PyObject *PyNick_voice_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->voice);
+}
+
+PyDoc_STRVAR(PyNick_halfop_doc,
+ "User status"
+);
+static PyObject *PyNick_halfop_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->halfop);
+}
+
+PyDoc_STRVAR(PyNick_last_check_doc,
+ "timestamp when last checked gone/ircop status."
+);
+static PyObject *PyNick_last_check_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_check);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNick_getseters[] = {
+ {"send_massjoin", (getter)PyNick_send_massjoin_get, NULL,
+ PyNick_send_massjoin_doc, NULL},
+ {"nick", (getter)PyNick_nick_get, NULL,
+ PyNick_nick_doc, NULL},
+ {"host", (getter)PyNick_host_get, NULL,
+ PyNick_host_doc, NULL},
+ {"realname", (getter)PyNick_realname_get, NULL,
+ PyNick_realname_doc, NULL},
+ {"hops", (getter)PyNick_hops_get, NULL,
+ PyNick_hops_doc, NULL},
+ {"gone", (getter)PyNick_gone_get, NULL,
+ PyNick_gone_doc, NULL},
+ {"serverop", (getter)PyNick_serverop_get, NULL,
+ PyNick_serverop_doc, NULL},
+ {"op", (getter)PyNick_op_get, NULL,
+ PyNick_op_doc, NULL},
+ {"voice", (getter)PyNick_voice_get, NULL,
+ PyNick_voice_doc, NULL},
+ {"halfop", (getter)PyNick_halfop_get, NULL,
+ PyNick_halfop_doc, NULL},
+ {"last_check", (getter)PyNick_last_check_get, NULL,
+ PyNick_last_check_doc, NULL},
+ {NULL}
+};
+
+static PyMethodDef PyNick_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNickType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Nick", /*tp_name*/
+ sizeof(PyNick), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNick_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNick objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNick_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNick_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* nick factory function */
+PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass)
+{
+ static const char *name = "NICK";
+ PyNick *pynick = NULL;
+
+ pynick = py_instp(PyNick, subclass);
+ if (!pynick)
+ return NULL;
+
+ pynick->data = nick;
+ pynick->base_name = name;
+ signal_add_last_data("nicklist remove", nick_cleanup, pynick);
+ pynick->cleanup_installed = 1;
+
+ return (PyObject *)pynick;
+}
+
+PyObject *pynick_new(void *nick)
+{
+ return pynick_sub_new(nick, &PyNickType);
+}
+
+int nick_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNickType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNickType);
+ PyModule_AddObject(py_module, "Nick", (PyObject *)&PyNickType);
+
+ return 1;
+}
diff --git a/src/objects/nick-object.h b/src/objects/nick-object.h
new file mode 100644
index 0000000..6831994
--- /dev/null
+++ b/src/objects/nick-object.h
@@ -0,0 +1,22 @@
+#ifndef _NICK_OBJECT_H_
+#define _NICK_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _NICK_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _NICK_REC)
+} PyNick;
+
+extern PyTypeObject PyNickType;
+
+int nick_object_init(void);
+PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass);
+PyObject *pynick_new(void *nick);
+#define pynick_check(op) PyObject_TypeCheck(op, &PyNickType)
+
+#endif
diff --git a/src/objects/notifylist-object.c b/src/objects/notifylist-object.c
new file mode 100644
index 0000000..3366d0f
--- /dev/null
+++ b/src/objects/notifylist-object.c
@@ -0,0 +1,221 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "notifylist-object.h"
+#include "pycore.h"
+
+#define NOTIFYLIST(nl) ((NOTIFYLIST_REC *)nl)
+
+/* monitor "notifylist remove" signal */
+static void notifylist_cleanup(NOTIFYLIST_REC *notifylist)
+{
+ PyNotifylist *pynotifylist = signal_get_user_data();
+
+ if (notifylist == pynotifylist->data)
+ {
+ pynotifylist->data = NULL;
+ pynotifylist->cleanup_installed = 0;
+ signal_remove_data("notifylist remove", notifylist_cleanup, pynotifylist);
+ }
+}
+
+static void PyNotifylist_dealloc(PyNotifylist *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("notifylist remove", notifylist_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNotifylist_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNotifylist *self;
+
+ self = (PyNotifylist *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNotifylist_mask_doc,
+ "Notify nick mask"
+);
+static PyObject *PyNotifylist_mask_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NOTIFYLIST(self->data)->mask);
+}
+
+PyDoc_STRVAR(PyNotifylist_away_check_doc,
+ "Notify away status changes"
+);
+static PyObject *PyNotifylist_away_check_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(NOTIFYLIST(self->data)->away_check);
+}
+
+PyDoc_STRVAR(PyNotifylist_idle_check_time_doc,
+ "Notify when idle time is reset and idle was bigger than this (seconds)"
+);
+static PyObject *PyNotifylist_idle_check_time_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(NOTIFYLIST(self->data)->idle_check_time);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNotifylist_getseters[] = {
+ {"mask", (getter)PyNotifylist_mask_get, NULL,
+ PyNotifylist_mask_doc, NULL},
+ {"away_check", (getter)PyNotifylist_away_check_get, NULL,
+ PyNotifylist_away_check_doc, NULL},
+ {"idle_check_time", (getter)PyNotifylist_idle_check_time_get, NULL,
+ PyNotifylist_idle_check_time_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyNotifylist_ircnets_doc,
+ "ircnets() -> list of str\n"
+ "\n"
+ "Return list of ircnets the notify is checked\n"
+);
+static PyObject *PyNotifylist_ircnets(PyNotifylist *self, PyObject *args)
+{
+ PyObject *list;
+ char **nets;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ nets = NOTIFYLIST(self->data)->ircnets;
+ list = PyList_New(0);
+ if (!list)
+ return NULL;
+
+ while (nets && *nets)
+ {
+ int ret;
+ PyObject *str = PyString_FromString(*nets);
+
+ if (!str)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ nets++;
+ }
+
+ return list;
+}
+
+PyDoc_STRVAR(PyNotifylist_ircnets_match_doc,
+ "ircnets_match(ircnet) -> bool\n"
+ "\n"
+ "Return True if notify is checked in ircnet\n"
+);
+static PyObject *PyNotifylist_ircnets_match(PyNotifylist *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ircnet", NULL};
+ char *ircnet = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &ircnet))
+ return NULL;
+
+ return PyBool_FromLong(notifylist_ircnets_match(self->data, ircnet));
+}
+
+/* Methods for object */
+static PyMethodDef PyNotifylist_methods[] = {
+ {"ircnets", (PyCFunction)PyNotifylist_ircnets, METH_NOARGS,
+ PyNotifylist_ircnets_doc},
+ {"ircnets_match", (PyCFunction)PyNotifylist_ircnets_match, METH_VARARGS | METH_KEYWORDS,
+ PyNotifylist_ircnets_match_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNotifylistType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Notifylist", /*tp_name*/
+ sizeof(PyNotifylist), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNotifylist_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNotifylist objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNotifylist_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNotifylist_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNotifylist_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynotifylist_new(void *notifylist)
+{
+ PyNotifylist *pynotifylist;
+
+ pynotifylist = py_inst(PyNotifylist, PyNotifylistType);
+ if (!pynotifylist)
+ return NULL;
+
+ pynotifylist->data = notifylist;
+ pynotifylist->cleanup_installed = 1;
+ signal_add_last_data("notifylist remove", notifylist_cleanup, pynotifylist);
+
+ return (PyObject *)pynotifylist;
+}
+
+int notifylist_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNotifylistType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNotifylistType);
+ PyModule_AddObject(py_module, "Notifylist", (PyObject *)&PyNotifylistType);
+
+ return 1;
+}
diff --git a/src/objects/notifylist-object.h b/src/objects/notifylist-object.h
new file mode 100644
index 0000000..9db6922
--- /dev/null
+++ b/src/objects/notifylist-object.h
@@ -0,0 +1,18 @@
+#ifndef _NOTIFYLIST_OBJECT_H_
+#define _NOTIFYLIST_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyNotifylist;
+
+extern PyTypeObject PyNotifylistType;
+
+int notifylist_object_init(void);
+PyObject *pynotifylist_new(void *notifylist);
+#define pynotifylist_check(op) PyObject_TypeCheck(op, &PyNotifylistType)
+
+#endif
diff --git a/src/objects/process-object.c b/src/objects/process-object.c
new file mode 100644
index 0000000..43c0fd7
--- /dev/null
+++ b/src/objects/process-object.c
@@ -0,0 +1,222 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "process-object.h"
+#include "pycore.h"
+
+/* monitor "exec remove" signal */
+static void process_cleanup(PROCESS_REC *process, int status)
+{
+ PyProcess *pyprocess = signal_get_user_data();
+
+ if (process == pyprocess->data)
+ {
+ pyprocess->data = NULL;
+ pyprocess->cleanup_installed = 0;
+ signal_remove_data("exec remove", process_cleanup, pyprocess);
+ }
+}
+
+static void PyProcess_dealloc(PyProcess *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("exec remove", process_cleanup, self);
+
+ Py_XDECREF(self->target_win);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyProcess_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyProcess *self;
+
+ self = (PyProcess *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyProcess_id_doc,
+ "ID for the process"
+);
+static PyObject *PyProcess_id_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->id);
+}
+
+PyDoc_STRVAR(PyProcess_name_doc,
+ "Name for the process (if given)"
+);
+static PyObject *PyProcess_name_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyProcess_args_doc,
+ "The command that is being executed"
+);
+static PyObject *PyProcess_args_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->args);
+}
+
+PyDoc_STRVAR(PyProcess_pid_doc,
+ "PID for the executed command"
+);
+static PyObject *PyProcess_pid_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->pid);
+}
+
+PyDoc_STRVAR(PyProcess_target_doc,
+ "send text with /msg <target> ..."
+);
+static PyObject *PyProcess_target_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->target);
+}
+
+PyDoc_STRVAR(PyProcess_target_win_doc,
+ "print text to this window"
+);
+static PyObject *PyProcess_target_win_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->target_win);
+}
+
+PyDoc_STRVAR(PyProcess_shell_doc,
+ "start the program via /bin/sh"
+);
+static PyObject *PyProcess_shell_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->shell);
+}
+
+PyDoc_STRVAR(PyProcess_notice_doc,
+ "send text with /notice, not /msg if target is set"
+);
+static PyObject *PyProcess_notice_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->notice);
+}
+
+PyDoc_STRVAR(PyProcess_silent_doc,
+ "don't print \"process exited with level xx\""
+);
+static PyObject *PyProcess_silent_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->silent);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyProcess_getseters[] = {
+ {"id", (getter)PyProcess_id_get, NULL,
+ PyProcess_id_doc, NULL},
+ {"name", (getter)PyProcess_name_get, NULL,
+ PyProcess_name_doc, NULL},
+ {"args", (getter)PyProcess_args_get, NULL,
+ PyProcess_args_doc, NULL},
+ {"pid", (getter)PyProcess_pid_get, NULL,
+ PyProcess_pid_doc, NULL},
+ {"target", (getter)PyProcess_target_get, NULL,
+ PyProcess_target_doc, NULL},
+ {"target_win", (getter)PyProcess_target_win_get, NULL,
+ PyProcess_target_win_doc, NULL},
+ {"shell", (getter)PyProcess_shell_get, NULL,
+ PyProcess_shell_doc, NULL},
+ {"notice", (getter)PyProcess_notice_get, NULL,
+ PyProcess_notice_doc, NULL},
+ {"silent", (getter)PyProcess_silent_get, NULL,
+ PyProcess_silent_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyProcess_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyProcessType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Process", /*tp_name*/
+ sizeof(PyProcess), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyProcess_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyProcess objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyProcess_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyProcess_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyProcess_new, /* tp_new */
+};
+
+
+/* process factory function */
+PyObject *pyprocess_new(void *process)
+{
+ PyProcess *pyprocess;
+
+ pyprocess = py_inst(PyProcess, PyProcessType);
+ if (!pyprocess)
+ return NULL;
+
+ pyprocess->data = process;
+ pyprocess->cleanup_installed = 1;
+ signal_add_last_data("exec remove", process_cleanup, pyprocess);
+
+ return (PyObject *)pyprocess;
+}
+
+int process_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyProcessType) < 0)
+ return 0;
+
+ Py_INCREF(&PyProcessType);
+ PyModule_AddObject(py_module, "Process", (PyObject *)&PyProcessType);
+
+ return 1;
+}
diff --git a/src/objects/process-object.h b/src/objects/process-object.h
new file mode 100644
index 0000000..041f187
--- /dev/null
+++ b/src/objects/process-object.h
@@ -0,0 +1,22 @@
+#ifndef _PROCESS_OBJECT_H_
+#define _PROCESS_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct PROCESS_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct PROCESS_REC)
+ PyObject *target_win;
+} PyProcess;
+
+extern PyTypeObject PyProcessType;
+
+int process_object_init(void);
+PyObject *pyprocess_new(void *process);
+#define pyprocess_check(op) PyObject_TypeCheck(op, &PyProcessType)
+
+#endif
diff --git a/src/objects/pyscript-object.c b/src/objects/pyscript-object.c
new file mode 100644
index 0000000..ae4770e
--- /dev/null
+++ b/src/objects/pyscript-object.c
@@ -0,0 +1,801 @@
+#include <Python.h>
+#include <structmember.h>
+#include "pyscript-object.h"
+#include "pyirssi.h"
+#include "pysignals.h"
+#include "pymodule.h"
+#include "pysource.h"
+#include "pythemes.h"
+#include "pystatusbar.h"
+
+/* handle cycles...
+ Can't think of any reason why the user would put script into one of the lists
+ but who knows. Call GC after unloading module.
+*/
+static int PyScript_traverse(PyScript *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->module);
+ Py_VISIT(self->argv);
+ Py_VISIT(self->modules);
+
+ return 0;
+}
+
+static int PyScript_clear(PyScript *self)
+{
+ Py_CLEAR(self->module);
+ Py_CLEAR(self->argv);
+ Py_CLEAR(self->modules);
+
+ return 0;
+}
+
+static void PyScript_dealloc(PyScript* self)
+{
+ PyScript_clear(self);
+ pyscript_remove_signals((PyObject*)self);
+ pyscript_remove_sources((PyObject*)self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyScript *self;
+ PyObject *argv = NULL, *modules = NULL;
+
+ argv = PyList_New(0);
+ if (!argv)
+ goto error;
+
+ modules = PyDict_New();
+ if (!modules)
+ goto error;
+
+ self = (PyScript *)type->tp_alloc(type, 0);
+ if (!self)
+ goto error;
+
+ self->argv = argv;
+ self->modules = modules;
+
+ return (PyObject *)self;
+
+error:
+ Py_XDECREF(argv);
+ Py_XDECREF(modules);
+ return NULL;
+}
+
+PyDoc_STRVAR(PyScript_command_bind_doc,
+ "command_bind(command, func, catetory=None, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n"
+ "\n"
+ "Add handler for a command\n"
+);
+static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "func", "category", "priority", NULL};
+ char *cmd;
+ PyObject *func;
+ char *category = NULL;
+ int priority = SIGNAL_PRIORITY_DEFAULT;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|zi", kwlist,
+ &cmd, &func, &category, &priority))
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func must be callable");
+
+ if (!pysignals_command_bind_list(&self->signals, cmd, func, category, priority))
+ return PyErr_Format(PyExc_RuntimeError, "unable to bind command");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_add_doc,
+ "signal_add(signal, func, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n"
+ "\n"
+ "Add handler for signal"
+);
+static PyObject *PyScript_signal_add(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "func", "priority", NULL};
+ char *signal;
+ PyObject *func;
+ int priority = SIGNAL_PRIORITY_DEFAULT;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|i", kwlist,
+ &signal, &func, &priority))
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func must be callable");
+
+ if (!pysignals_signal_add_list(&self->signals, signal, func, priority))
+ return PyErr_Format(PyExc_KeyError, "unable to find signal, '%s'", signal);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_remove_doc,
+ "signal_remove(signal, func=None) -> None\n"
+ "\n"
+ "Remove signal handler\n"
+);
+static PyObject *PyScript_signal_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "func", NULL};
+ char *signal = "";
+ PyObject *func = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
+ &signal, &func))
+ return NULL;
+
+ if (!PyCallable_Check(func) && func != Py_None)
+ return PyErr_Format(PyExc_TypeError, "func must be callable or None");
+
+ if (func == Py_None)
+ func = NULL;
+
+ if (!pysignals_remove_search(&self->signals, signal, func, PSG_SIGNAL))
+ return PyErr_Format(PyExc_KeyError, "can't find signal");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_command_unbind_doc,
+ "command_unbind(command, func=None) -> None\n"
+ "\n"
+ "Remove command handler\n"
+);
+static PyObject *PyScript_command_unbind(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"command", "func", NULL};
+ char *command = "";
+ PyObject *func = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
+ &command, &func))
+ return NULL;
+
+ if (!PyCallable_Check(func) && func != Py_None)
+ return PyErr_Format(PyExc_TypeError, "func must be callable or None");
+
+ if (func == Py_None)
+ func = NULL;
+
+ if (!pysignals_remove_search(&self->signals, command, func, PSG_COMMAND))
+ return PyErr_Format(PyExc_KeyError, "can't find command");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_register_doc,
+ "signal_register(signal, arglist) -> None\n"
+ "\n"
+ "Register a new dynamic signal for use with irssi_python\n"
+ "arglist is a string of character codes representing the type of each argument\n"
+ "of the signal handler function.\n"
+ "\n"
+ " Scalars\n"
+ " s -> char *\n"
+ " i -> int\n"
+ "\n"
+ " Chat objects\n"
+ " c -> CHATNET_REC\n"
+ " S -> SERVER_REC\n"
+ " C -> CHANNEL_REC\n"
+ " q -> QUERY_REC\n"
+ " n -> NICK_REC\n"
+ " W -> WI_ITEM_REC\n"
+ "\n"
+ " Irssi objects\n"
+ " d -> DCC_REC\n"
+ "\n"
+ " Other objects\n"
+ " r -> RECONNECT_REC\n"
+ " o -> COMMAND_REC\n"
+ " l -> LOG_REC\n"
+ " a -> RAWLOG_REC\n"
+ " g -> IGNORE_REC\n"
+ " b -> BAN_REC\n"
+ " N -> NETSPLIT_REC\n"
+ " e -> NETSPLIT_SERVER_REC\n"
+ " O -> NOTIFYLIST_REC\n"
+ " p -> PROCESS_REC\n"
+ " t -> TEXT_DEST_REC\n"
+ " w -> WINDOW_REC\n"
+);
+static PyObject *PyScript_signal_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "arglist", NULL};
+ static const char *good_codes = "sicSCqnWdrolagbNeOptw";
+ char *signal = "";
+ char *arglist = "";
+ int i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &signal, &arglist))
+ return NULL;
+
+ for (i = 0; arglist[i]; i++)
+ if (!strchr(good_codes, arglist[i]))
+ return PyErr_Format(PyExc_TypeError, "invalid code, %c", arglist[i]);
+
+ if (i >= SIGNAL_MAX_ARGUMENTS)
+ return PyErr_Format(PyExc_TypeError,
+ "arglist greater than SIGNAL_MAX_ARGUMENTS (%d)",
+ SIGNAL_MAX_ARGUMENTS);
+
+ if (!pysignals_register(signal, arglist))
+ return PyErr_Format(PyExc_TypeError, "signal present with different args");
+
+ self->registered_signals = g_slist_append(self->registered_signals,
+ g_strdup(signal));
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_unregister_doc,
+ "signal_unregister(signal) -> None\n"
+ "\n"
+ "Unregister dynamic signal\n"
+);
+static PyObject *PyScript_signal_unregister(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", NULL};
+ char *signal = "";
+ GSList *search;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &signal))
+ return NULL;
+
+ search = g_slist_find_custom(self->registered_signals, signal, (GCompareFunc)strcmp);
+ if (!search)
+ return PyErr_Format(PyExc_KeyError, "script has not registered that signal");
+
+ g_free(search->data);
+ self->registered_signals = g_slist_delete_link(self->registered_signals, search);
+
+ if (!pysignals_unregister(signal))
+ return PyErr_Format(PyExc_SystemError,
+ "script registered signal, but signal does not exist");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_timeout_add_doc,
+ "timeout_add(msecs, func, data=None) -> int source tag\n"
+ "\n"
+ "Add a timeout handler called every 'msecs' milliseconds until func\n"
+ "returns False or the source is removed with source_remove().\n"
+ "\n"
+ "func is called as func(data) or func(), depending on whether data\n"
+ "is specified or not.\n"
+);
+static PyObject *PyScript_timeout_add(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"msecs", "func", "data", NULL};
+ int msecs = 0;
+ PyObject *func = NULL;
+ PyObject *data = NULL;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|O", kwlist,
+ &msecs, &func, &data))
+ return NULL;
+
+ if (msecs < 10)
+ return PyErr_Format(PyExc_ValueError, "msecs must be at least 10");
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func not callable");
+
+ ret = pysource_timeout_add_list(&self->sources, msecs, func, data);
+
+ return PyInt_FromLong(ret);
+}
+
+PyDoc_STRVAR(PyScript_io_add_watch_doc,
+ "io_add_watch(fd, func, data=None, condition=IO_IN|IO_PRI) -> int source tag\n"
+);
+static PyObject *PyScript_io_add_watch(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fd", "func", "data", "condition", NULL};
+ int fd = 0;
+ PyObject *pyfd = NULL;
+ PyObject *func = NULL;
+ PyObject *data = NULL;
+ int condition = G_IO_IN | G_IO_PRI;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oi", kwlist,
+ &pyfd, &func, &data, &condition))
+ return NULL;
+
+ fd = PyObject_AsFileDescriptor(pyfd);
+ if (fd < 0)
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func not callable");
+
+ ret = pysource_io_add_watch_list(&self->sources, fd, condition, func, data);
+
+ return PyInt_FromLong(ret);
+}
+
+PyDoc_STRVAR(PyScript_source_remove_doc,
+ "source_remove(tag) -> bool\n"
+ "\n"
+ "Remove IO or timeout source by tag. Return True if tag found and removed.\n"
+);
+static PyObject *PyScript_source_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"tag", NULL};
+ int tag = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &tag))
+ return NULL;
+
+ /* the destroy notify func will remove the list link, but first
+ check that the tag exists in this Script object */
+ if (g_slist_find(self->sources, GINT_TO_POINTER(tag)))
+ return PyBool_FromLong(g_source_remove(tag));
+
+ Py_RETURN_FALSE;
+}
+
+static int py_settings_add(PyScript *self, const char *name)
+{
+ GSList *node;
+
+ node = gslist_find_icase_string(self->settings, name);
+ if (node)
+ return 0;
+
+ self->settings = g_slist_append(self->settings, g_strdup(name));
+
+ return 1;
+}
+
+static int py_settings_remove(PyScript *self, const char *name)
+{
+ GSList *node;
+
+ node = gslist_find_icase_string(self->settings, name);
+ if (!node)
+ return 0;
+
+ settings_remove(node->data);
+ g_free(node->data);
+
+ self->settings = g_slist_delete_link(self->settings, node);
+
+ return 1;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_str_doc,
+ "settings_add_str(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_str(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_str_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_int_doc,
+ "settings_add_int(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_int(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ int def = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_int_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_bool_doc,
+ "settings_add_bool(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_bool(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ int def = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_bool_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_time_doc,
+ "settings_add_time(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_time(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_time_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_level_doc,
+ "settings_add_level(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_level(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_level_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_size_doc,
+ "settings_add_size(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_size(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_size_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_remove_doc,
+ "settings_remove(key) -> bool\n"
+);
+static PyObject *PyScript_settings_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyBool_FromLong(py_settings_remove(self, key));
+}
+
+PyDoc_STRVAR(PyScript_theme_register_doc,
+ "theme_register(list) -> None\n"
+);
+static PyObject *PyScript_theme_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"list", NULL};
+ PyObject *list = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &list))
+ return NULL;
+
+ if (!pythemes_register(pyscript_get_name(self), list))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_statusbar_item_register_doc,
+ "statusbar_item_register(name, value=None, func=None) -> None\n"
+);
+static PyObject *PyScript_statusbar_item_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", "value", "func", NULL};
+ char *name = "";
+ char *value = NULL;
+ PyObject *func = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zO", kwlist,
+ &name, &value, &func))
+ return NULL;
+
+ pystatusbar_item_register((PyObject *)self, name, value, func);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyScript_methods[] = {
+ {"command_bind", (PyCFunction)PyScript_command_bind, METH_VARARGS | METH_KEYWORDS,
+ PyScript_command_bind_doc},
+ {"signal_add", (PyCFunction)PyScript_signal_add, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_add_doc},
+ {"signal_remove", (PyCFunction)PyScript_signal_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_remove_doc},
+ {"command_unbind", (PyCFunction)PyScript_command_unbind, METH_VARARGS | METH_KEYWORDS,
+ PyScript_command_unbind_doc},
+ {"signal_register", (PyCFunction)PyScript_signal_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_register_doc},
+ {"signal_unregister", (PyCFunction)PyScript_signal_unregister, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_unregister_doc},
+ {"timeout_add", (PyCFunction)PyScript_timeout_add, METH_VARARGS | METH_KEYWORDS,
+ PyScript_timeout_add_doc},
+ {"io_add_watch", (PyCFunction)PyScript_io_add_watch, METH_VARARGS | METH_KEYWORDS,
+ PyScript_io_add_watch_doc},
+ {"source_remove", (PyCFunction)PyScript_source_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_source_remove_doc},
+ {"settings_add_str", (PyCFunction)PyScript_settings_add_str, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_str_doc},
+ {"settings_add_int", (PyCFunction)PyScript_settings_add_int, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_int_doc},
+ {"settings_add_bool", (PyCFunction)PyScript_settings_add_bool, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_bool_doc},
+ {"settings_add_time", (PyCFunction)PyScript_settings_add_time, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_time_doc},
+ {"settings_add_level", (PyCFunction)PyScript_settings_add_level, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_level_doc},
+ {"settings_add_size", (PyCFunction)PyScript_settings_add_size, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_size_doc},
+ {"settings_remove", (PyCFunction)PyScript_settings_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_remove_doc},
+ {"theme_register", (PyCFunction)PyScript_theme_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_theme_register_doc},
+ {"statusbar_item_register", (PyCFunction)PyScript_statusbar_item_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_statusbar_item_register_doc},
+ {NULL} /* Sentinel */
+};
+
+static PyMemberDef PyScript_members[] = {
+ {"argv", T_OBJECT, offsetof(PyScript, argv), 0, "Script arguments"},
+ {"module", T_OBJECT_EX, offsetof(PyScript, module), RO, "Script module"},
+ {"modules", T_OBJECT_EX, offsetof(PyScript, modules), 0, "Imported modules"},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyScriptType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Script", /*tp_name*/
+ sizeof(PyScript), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyScript_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ "PyScript objects", /* tp_doc */
+ (traverseproc)PyScript_traverse, /* tp_traverse */
+ (inquiry)PyScript_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyScript_methods, /* tp_methods */
+ PyScript_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyScript_new, /* tp_new */
+};
+
+/* PyScript factory function */
+PyObject *pyscript_new(PyObject *module, char **argv)
+{
+ PyObject *script;
+
+ script = PyObject_CallFunction((PyObject*)&PyScriptType, "()");
+
+ if (script)
+ {
+ PyScript *scr = (PyScript *)script;
+
+ while (*argv)
+ {
+ if (**argv != '\0')
+ {
+ PyObject *str = PyString_FromString(*argv);
+ if (!str)
+ {
+ /* The destructor should DECREF argv */
+ Py_DECREF(script);
+ return NULL;
+ }
+
+ PyList_Append(scr->argv, str);
+ Py_DECREF(str);
+ }
+
+ *argv++;
+ }
+
+ Py_INCREF(module);
+ scr->module = module;
+ }
+
+ return script;
+}
+
+void pyscript_remove_signals(PyObject *script)
+{
+ GSList *node;
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ /* remove bound signals */
+ pysignals_remove_list(self->signals);
+ g_slist_free(self->signals);
+ self->signals = NULL;
+
+ /* remove registered signals */
+ for (node = self->registered_signals; node; node = node->next)
+ {
+ pysignals_unregister(node->data);
+ g_free(node->data);
+ }
+
+ g_slist_free(self->registered_signals);
+ self->registered_signals = NULL;
+}
+
+void pyscript_remove_sources(PyObject *script)
+{
+ GSList *node;
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ node = self->sources;
+ while (node)
+ {
+ /* the notify func will destroy the link so save next */
+ GSList *next = node->next;
+ g_source_remove(GPOINTER_TO_INT(node->data));
+ node = next;
+ }
+
+ g_return_if_fail(self->sources == NULL);
+}
+
+void pyscript_remove_settings(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ g_slist_foreach(self->settings, (GFunc)settings_remove, NULL);
+ g_slist_foreach(self->settings, (GFunc)g_free, NULL);
+ g_slist_free(self->settings);
+}
+
+void pyscript_remove_themes(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ pythemes_unregister(pyscript_get_name(script));
+}
+
+void pyscript_remove_statusbars(PyObject *script)
+{
+ g_return_if_fail(pyscript_check(script));
+
+ pystatusbar_cleanup_script(script);
+}
+
+void pyscript_clear_modules(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ PyDict_Clear(self->modules);
+}
+
+void pyscript_cleanup(PyObject *script)
+{
+ pyscript_remove_signals(script);
+ pyscript_remove_sources(script);
+ pyscript_remove_settings(script);
+ pyscript_remove_themes(script);
+ pyscript_remove_statusbars(script);
+ pyscript_clear_modules(script);
+}
+
+int pyscript_init(void)
+{
+ if (PyType_Ready(&PyScriptType) < 0)
+ return 0;
+
+ Py_INCREF(&PyScriptType);
+ PyModule_AddObject(py_module, "Script", (PyObject *)&PyScriptType);
+
+ return 1;
+}
+
diff --git a/src/objects/pyscript-object.h b/src/objects/pyscript-object.h
new file mode 100644
index 0000000..624a578
--- /dev/null
+++ b/src/objects/pyscript-object.h
@@ -0,0 +1,33 @@
+#ifndef _PYSCRIPT_OBJECT_H_
+#define _PYSCRIPT_OBJECT_H_
+#include <Python.h>
+#include <glib.h>
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *module; /* module object */
+ PyObject *argv; /* list of argument strings from the load command */
+ PyObject *modules; /* dict of imported modules for script */
+ GSList *signals; /* list of bound signals and commands */
+ GSList *registered_signals; /* list of signal names registered */
+ GSList *sources; /* list of io and timeout sources */
+ GSList *settings; /* list of settings from settings_add_*() */
+} PyScript;
+
+extern PyTypeObject PyScriptType;
+
+int pyscript_init(void);
+PyObject *pyscript_new(PyObject *module, char **argv);
+void pyscript_remove_signals(PyObject *script);
+void pyscript_remove_sources(PyObject *script);
+void pyscript_remove_settings(PyObject *script);
+void pyscript_remove_themes(PyObject *script);
+void pyscript_remove_statusbars(PyObject *script);
+void pyscript_clear_modules(PyObject *script);
+void pyscript_cleanup(PyObject *script);
+#define pyscript_check(op) PyObject_TypeCheck(op, &PyScriptType)
+#define pyscript_get_name(scr) PyModule_GetName(((PyScript*)scr)->module)
+#define pyscript_get_filename(scr) PyModule_GetFilename(((PyScript*)scr)->module)
+#define pyscript_get_module(scr) (((PyScript*)scr)->module)
+
+#endif
diff --git a/src/objects/query-object.c b/src/objects/query-object.c
new file mode 100644
index 0000000..47badd5
--- /dev/null
+++ b/src/objects/query-object.c
@@ -0,0 +1,172 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "query-object.h"
+#include "server-object.h"
+#include "pycore.h"
+
+/* monitor "query destroyed" signal */
+static void query_cleanup(QUERY_REC *query)
+{
+ PyQuery *pyquery = signal_get_user_data();
+
+ if (query == pyquery->data)
+ {
+ pyquery->data = NULL;
+ pyquery->cleanup_installed = 0;
+ signal_remove_data("query destroyed", query_cleanup, pyquery);
+ }
+}
+
+static void PyQuery_dealloc(PyQuery *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("query destroyed", query_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyQuery_address_doc,
+ "Host address of the queries nick"
+);
+static PyObject *PyQuery_address_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->address);
+}
+
+PyDoc_STRVAR(PyQuery_server_tag_doc,
+ "Server tag used for this nick (doesn't get erased if server gets disconnected)"
+);
+static PyObject *PyQuery_server_tag_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->server_tag);
+}
+
+PyDoc_STRVAR(PyQuery_unwanted_doc,
+ "1 if the other side closed or some error occured (DCC chats)"
+);
+static PyObject *PyQuery_unwanted_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->unwanted);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyQuery_getseters[] = {
+ {"address", (getter)PyQuery_address_get, NULL,
+ PyQuery_address_doc, NULL},
+ {"server_tag", (getter)PyQuery_server_tag_get, NULL,
+ PyQuery_server_tag_doc, NULL},
+ {"unwanted", (getter)PyQuery_unwanted_get, NULL,
+ PyQuery_unwanted_doc, NULL},
+ {NULL}
+};
+
+PyDoc_STRVAR(change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change the active server for the query.\n"
+);
+static PyObject *PyQuery_change_server(PyQuery *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "argument must be server object");
+
+ query_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyQuery_methods[] = {
+ {"change_server", (PyCFunction)PyQuery_change_server, METH_VARARGS | METH_KEYWORDS,
+ change_server_doc},
+
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyQueryType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Query", /*tp_name*/
+ sizeof(PyQuery), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyQuery_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyQuery objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyQuery_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyQuery_getseters, /* tp_getset */
+ &PyWindowItemType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* query factory function */
+PyObject *pyquery_new(void *query)
+{
+ static const char *BASE_NAME = "QUERY";
+ PyObject *pyquery;
+
+ pyquery = pywindow_item_sub_new(query, BASE_NAME, &PyQueryType);
+ if (pyquery)
+ {
+ PyQuery *pyq = (PyQuery *)pyquery;
+ signal_add_last_data("query destroyed", query_cleanup, pyq);
+ pyq->cleanup_installed = 1;
+ }
+
+ return pyquery;
+}
+
+int query_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyQueryType) < 0)
+ return 0;
+
+ Py_INCREF(&PyQueryType);
+ PyModule_AddObject(py_module, "Query", (PyObject *)&PyQueryType);
+
+ return 1;
+}
diff --git a/src/objects/query-object.h b/src/objects/query-object.h
new file mode 100644
index 0000000..844ce55
--- /dev/null
+++ b/src/objects/query-object.h
@@ -0,0 +1,21 @@
+#ifndef _QUERY_OBJECT_H_
+#define _QUERY_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _QUERY_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _QUERY_REC)
+} PyQuery;
+
+extern PyTypeObject PyQueryType;
+
+int query_object_init(void);
+PyObject *pyquery_new(void *query);
+#define pyquery_check(op) PyObject_TypeCheck(op, &PyQueryType)
+
+#endif
diff --git a/src/objects/rawlog-object.c b/src/objects/rawlog-object.c
new file mode 100644
index 0000000..340d60a
--- /dev/null
+++ b/src/objects/rawlog-object.c
@@ -0,0 +1,323 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "rawlog-object.h"
+#include "pycore.h"
+
+/* monitor "????" signal */
+static void rawlog_cleanup(RAWLOG_REC *ban)
+{
+ /* XXX */
+}
+
+static void PyRawlog_dealloc(PyRawlog *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyRawlog_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyRawlog *self;
+
+ self = (PyRawlog *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* XXX: Need function to create the rawlog */
+
+/* Getters */
+PyDoc_STRVAR(PyRawlog_logging_doc,
+ "The raw log is being written to file currently."
+);
+static PyObject *PyRawlog_logging_get(PyRawlog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->logging);
+}
+
+PyDoc_STRVAR(PyRawlog_nlines_doc,
+ "Number of lines in rawlog."
+);
+static PyObject *PyRawlog_nlines_get(PyRawlog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->nlines);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyRawlog_getseters[] = {
+ {"logging", (getter)PyRawlog_logging_get, NULL,
+ PyRawlog_logging_doc, NULL},
+ {"nlines", (getter)PyRawlog_nlines_get, NULL,
+ PyRawlog_nlines_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyRawlog_get_lines_doc,
+ "get_lines() -> list of str\n"
+ "\n"
+ "Return a list of lines for rawlog.\n"
+);
+static PyObject *PyRawlog_get_lines(PyRawlog *self, PyObject *args)
+{
+ PyObject *lines = NULL;
+ GSList *node;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ lines = PyList_New(0);
+ if (!lines)
+ return NULL;
+
+ for (node = self->data->lines; node; node = node->next)
+ {
+ int ret;
+ PyObject *line = PyString_FromString(node->data);
+
+ if (!line)
+ {
+ Py_XDECREF(lines);
+ return NULL;
+ }
+
+ ret = PyList_Append(lines, line);
+ Py_DECREF(line);
+ if (ret != 0)
+ {
+ Py_XDECREF(lines);
+ return NULL;
+ }
+ }
+
+ return lines;
+}
+
+PyDoc_STRVAR(PyRawlog_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy rawlog\n"
+);
+static PyObject *PyRawlog_destroy(PyRawlog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ rawlog_destroy(self->data);
+
+ /*XXX: what about signal handler ? */
+ self->data = NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_input_doc,
+ "input(str) -> None\n"
+ "\n"
+ "Send str to rawlog as input text.\n"
+);
+static PyObject *PyRawlog_input(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_input(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_output_doc,
+ "output(str) -> None\n"
+ "\n"
+ "Send str to rawlog as output text.\n"
+);
+static PyObject *PyRawlog_output(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_output(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_redirect_doc,
+ "redirect(str) -> None\n"
+ "\n"
+ "Send str to rawlog as redirection text."
+);
+static PyObject *PyRawlog_redirect(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_redirect(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_open_doc,
+ "open(fname) -> None\n"
+ "\n"
+ "Start logging new messages in rawlog to specified file.\n"
+);
+static PyObject *PyRawlog_open(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ rawlog_open(self->data, fname);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_close_doc,
+ "close() -> None\n"
+ "\n"
+ "Stop logging to file\n"
+);
+static PyObject *PyRawlog_close(PyRawlog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ rawlog_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_save_doc,
+ "save(fname) -> None\n"
+ "\n"
+ "Save the current rawlog history to specified file.\n"
+);
+static PyObject *PyRawlog_save(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ rawlog_save(self->data, fname);
+
+ Py_RETURN_NONE;
+}
+/* Methods for object */
+static PyMethodDef PyRawlog_methods[] = {
+ {"get_lines", (PyCFunction)PyRawlog_get_lines, METH_NOARGS,
+ PyRawlog_get_lines_doc},
+ {"destroy", (PyCFunction)PyRawlog_destroy, METH_NOARGS,
+ PyRawlog_destroy_doc},
+ {"input", (PyCFunction)PyRawlog_input, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_input_doc},
+ {"output", (PyCFunction)PyRawlog_output, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_output_doc},
+ {"redirect", (PyCFunction)PyRawlog_redirect, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_redirect_doc},
+ {"open", (PyCFunction)PyRawlog_open, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_open_doc},
+ {"close", (PyCFunction)PyRawlog_close, METH_NOARGS,
+ PyRawlog_close_doc},
+ {"save", (PyCFunction)PyRawlog_save, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_save_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyRawlogType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Rawlog", /*tp_name*/
+ sizeof(PyRawlog), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyRawlog_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyRawlog objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyRawlog_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyRawlog_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyRawlog_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyrawlog_new(void *rlog)
+{
+ PyRawlog *pyrlog;
+
+ pyrlog = py_inst(PyRawlog, PyRawlogType);
+ if (!pyrlog)
+ return NULL;
+
+ pyrlog->data = rlog;
+
+ return (PyObject *)pyrlog;
+}
+
+int rawlog_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyRawlogType) < 0)
+ return 0;
+
+ Py_INCREF(&PyRawlogType);
+ PyModule_AddObject(py_module, "Rawlog", (PyObject *)&PyRawlogType);
+
+ return 1;
+}
diff --git a/src/objects/rawlog-object.h b/src/objects/rawlog-object.h
new file mode 100644
index 0000000..d2a9a2d
--- /dev/null
+++ b/src/objects/rawlog-object.h
@@ -0,0 +1,22 @@
+#ifndef _RAWLOG_OBJECT_H_
+#define _RAWLOG_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _RAWLOG_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _RAWLOG_REC)
+ int owned;
+} PyRawlog;
+
+extern PyTypeObject PyRawlogType;
+
+int rawlog_object_init(void);
+PyObject *pyrawlog_new(void *rlog);
+#define pyrawlog_check(op) PyObject_TypeCheck(op, &PyRawlogType)
+
+#endif
diff --git a/src/objects/reconnect-object.c b/src/objects/reconnect-object.c
new file mode 100644
index 0000000..6483242
--- /dev/null
+++ b/src/objects/reconnect-object.c
@@ -0,0 +1,151 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "pycore.h"
+#include "factory.h"
+#include "reconnect-object.h"
+
+/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */
+
+static void PyReconnect_dealloc(PyReconnect *self)
+{
+ Py_XDECREF(self->connect);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyReconnect_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyReconnect *self;
+
+ self = (PyReconnect *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyReconnect_tag_doc,
+ "Unique numeric tag"
+);
+static PyObject *PyReconnect_tag_get(PyReconnect *self, void *closure)
+{
+ RECONNECT_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyInt_FromLong(data->tag);
+}
+
+PyDoc_STRVAR(PyReconnect_next_connect_doc,
+ "Unix time stamp when the next connection occurs"
+);
+static PyObject *PyReconnect_next_connect_get(PyReconnect *self, void *closure)
+{
+ RECONNECT_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyLong_FromUnsignedLong(data->next_connect);
+}
+
+PyDoc_STRVAR(PyReconnect_connect_doc,
+ "Connection object"
+);
+static PyObject *PyReconnect_connect_get(PyReconnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->connect);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyReconnect_getseters[] = {
+ {"tag", (getter)PyReconnect_tag_get, NULL,
+ PyReconnect_tag_doc, NULL},
+ {"next_connect", (getter)PyReconnect_next_connect_get, NULL,
+ PyReconnect_next_connect_doc, NULL},
+ {"connect", (getter)PyReconnect_connect_get, NULL,
+ PyReconnect_connect_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyReconnect_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyReconnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Reconnect", /*tp_name*/
+ sizeof(PyReconnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyReconnect_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyReconnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyReconnect_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyReconnect_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyReconnect_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyreconnect_new(void *recon)
+{
+ RECONNECT_REC *rec = recon;
+ PyObject *connect;
+ PyReconnect *pyrecon;
+
+ /* XXX: get a managed connect because there's no signals to manage reconnect */
+ connect = py_irssi_chat_new(rec->conn, 1);
+ if (!connect)
+ return NULL;
+
+ pyrecon = py_inst(PyReconnect, PyReconnectType);
+ if (!pyrecon)
+ return NULL;
+
+ pyrecon->data = recon;
+ pyrecon->connect = connect;
+
+ return (PyObject *)pyrecon;
+}
+
+int reconnect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyReconnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyReconnectType);
+ PyModule_AddObject(py_module, "Reconnect", (PyObject *)&PyReconnectType);
+
+ return 1;
+}
diff --git a/src/objects/reconnect-object.h b/src/objects/reconnect-object.h
new file mode 100644
index 0000000..ec0b094
--- /dev/null
+++ b/src/objects/reconnect-object.h
@@ -0,0 +1,20 @@
+#ifndef _RECONNECT_OBJECT_H_
+#define _RECONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *connect;
+} PyReconnect;
+
+extern PyTypeObject PyReconnectType;
+
+int reconnect_object_init(void);
+PyObject *pyreconnect_new(void *recon);
+#define pyreconnect_check(op) PyObject_TypeCheck(op, &PyReconnectType)
+
+#endif
diff --git a/src/objects/server-object.c b/src/objects/server-object.c
new file mode 100644
index 0000000..31415b8
--- /dev/null
+++ b/src/objects/server-object.c
@@ -0,0 +1,807 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "factory.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void server_cleanup(SERVER_REC *server)
+{
+ PyServer *pyserver = signal_get_user_data();
+
+ if (server == pyserver->data)
+ {
+ if (pyserver->connect)
+ ((PyConnect *)pyserver->connect)->data = NULL;
+
+ if (pyserver->rawlog)
+ ((PyRawlog *)pyserver->rawlog)->data = NULL;
+
+ pyserver->data = NULL;
+ pyserver->cleanup_installed = 0;
+ signal_remove_data("server disconnected", server_cleanup, pyserver);
+ }
+}
+
+static void PyServer_dealloc(PyServer *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("server disconnected", server_cleanup, self);
+
+ Py_XDECREF(self->connect);
+ Py_XDECREF(self->rawlog);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyServer_connect_time_doc,
+ "Time when connect() to server finished"
+);
+static PyObject *PyServer_connect_time_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->connect_time);
+}
+
+PyDoc_STRVAR(PyServer_real_connect_time_doc,
+ "Time when server sent 'connected' message"
+);
+static PyObject *PyServer_real_connect_time_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->real_connect_time);
+}
+
+PyDoc_STRVAR(PyServer_tag_doc,
+ "Unique server tag"
+);
+static PyObject *PyServer_tag_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->tag);
+}
+
+PyDoc_STRVAR(PyServer_nick_doc,
+ "Current nick"
+);
+static PyObject *PyServer_nick_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyServer_connected_doc,
+ "Is connection finished? 1|0"
+);
+static PyObject *PyServer_connected_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connected);
+}
+
+PyDoc_STRVAR(PyServer_connection_lost_doc,
+ "Did we lose the connection (1) or was the connection just /DISCONNECTed (0)"
+);
+static PyObject *PyServer_connection_lost_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connection_lost);
+}
+
+PyDoc_STRVAR(PyServer_rawlog_doc,
+ "Rawlog object for the server"
+);
+static PyObject *PyServer_rawlog_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->rawlog);
+}
+
+PyDoc_STRVAR(PyServer_connect_doc,
+ "Connect object for the server"
+);
+static PyObject *PyServer_connect_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->connect);
+}
+
+PyDoc_STRVAR(PyServer_version_doc,
+ "Server version"
+);
+static PyObject *PyServer_version_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->version);
+}
+
+PyDoc_STRVAR(PyServer_last_invite_doc,
+ "Last channel we were invited to"
+);
+static PyObject *PyServer_last_invite_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->last_invite);
+}
+
+PyDoc_STRVAR(PyServer_server_operator_doc,
+ "Are we server operator (IRC op) 1|0"
+);
+static PyObject *PyServer_server_operator_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->server_operator);
+}
+
+PyDoc_STRVAR(PyServer_usermode_away_doc,
+ "Are we marked as away? 1|0"
+);
+static PyObject *PyServer_usermode_away_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->usermode_away);
+}
+
+PyDoc_STRVAR(PyServer_away_reason_doc,
+ "Away reason message"
+);
+static PyObject *PyServer_away_reason_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->away_reason);
+}
+
+PyDoc_STRVAR(PyServer_banned_doc,
+ "Were we banned from this server? 1|0"
+);
+static PyObject *PyServer_banned_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->banned);
+}
+
+PyDoc_STRVAR(PyServer_lag_doc,
+ "Current lag to server in milliseconds"
+);
+static PyObject *PyServer_lag_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->lag);
+}
+
+static PyGetSetDef PyServer_getseters[] = {
+ {"connect_time", (getter)PyServer_connect_time_get, NULL,
+ PyServer_connect_time_doc, NULL},
+ {"real_connect_time", (getter)PyServer_real_connect_time_get, NULL,
+ PyServer_real_connect_time_doc, NULL},
+ {"tag", (getter)PyServer_tag_get, NULL,
+ PyServer_tag_doc, NULL},
+ {"nick", (getter)PyServer_nick_get, NULL,
+ PyServer_nick_doc, NULL},
+ {"connected", (getter)PyServer_connected_get, NULL,
+ PyServer_connected_doc, NULL},
+ {"connection_lost", (getter)PyServer_connection_lost_get, NULL,
+ PyServer_connection_lost_doc, NULL},
+ {"rawlog", (getter)PyServer_rawlog_get, NULL,
+ PyServer_rawlog_doc, NULL},
+ {"connect", (getter)PyServer_connect_get, NULL,
+ PyServer_connect_doc, NULL},
+ {"version", (getter)PyServer_version_get, NULL,
+ PyServer_version_doc, NULL},
+ {"last_invite", (getter)PyServer_last_invite_get, NULL,
+ PyServer_last_invite_doc, NULL},
+ {"server_operator", (getter)PyServer_server_operator_get, NULL,
+ PyServer_server_operator_doc, NULL},
+ {"usermode_away", (getter)PyServer_usermode_away_get, NULL,
+ PyServer_usermode_away_doc, NULL},
+ {"away_reason", (getter)PyServer_away_reason_get, NULL,
+ PyServer_away_reason_doc, NULL},
+ {"banned", (getter)PyServer_banned_get, NULL,
+ PyServer_banned_doc, NULL},
+ {"lag", (getter)PyServer_lag_get, NULL,
+ PyServer_lag_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(print_doc,
+ "prnt(channel, str, level) -> None\n"
+ "\n"
+ "Print to server\n"
+);
+static PyObject *PyServer_prnt(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"channel", "str", "level", NULL};
+ char *str, *channel;
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &channel, &str, &level))
+ return NULL;
+
+ printtext_string(self->data, channel, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command\n"
+);
+static PyObject *PyServer_command(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ py_command(cmd, self->data, NULL);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(disconnect_doc,
+ "disconnect() -> None\n"
+ "\n"
+ "Disconnect from server\n"
+);
+static PyObject *PyServer_disconnect(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ server_disconnect(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(isnickflag_doc,
+ "isnickflag(flag) -> bool\n"
+ "\n"
+ "Returns True if flag is a nick mode flag (@, + or % in IRC)\n"
+);
+static PyObject *PyServer_isnickflag(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"flag", NULL};
+ char flag;
+ int ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "c", kwlist, &flag))
+ return NULL;
+
+ ret = self->data->isnickflag(self->data, flag);
+
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(ischannel_doc,
+ "ischannel(data) -> bool\n"
+ "\n"
+ "Returns True if start of `data' seems to mean channel.\n"
+);
+static PyObject *PyServer_ischannel(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data;
+ int ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data))
+ return NULL;
+
+ ret = self->data->ischannel(self->data, data);
+
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(get_nick_flags_doc,
+ "get_nick_flags() -> str\n"
+ "\n"
+ "Returns nick flag characters in order: op, voice, halfop (\"@+%\") in IRC\n"
+);
+static PyObject *PyServer_get_nick_flags(PyServer *self, PyObject *args)
+{
+ char *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ ret = (char *)self->data->get_nick_flags(self->data);
+
+ return PyString_FromString(ret);
+}
+
+PyDoc_STRVAR(send_message_doc,
+ "send_message(target, msg, target_type) -> None\n"
+ "\n"
+ "Sends a message to nick/channel. target_type 0 = channel, 1 = nick\n"
+);
+static PyObject *PyServer_send_message(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"target", "msg", "target_type", NULL};
+ char *target, *msg;
+ int target_type;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, &target, &msg, &target_type))
+ return NULL;
+
+ self->data->send_message(self->data, target, msg, target_type);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(channels_join_doc,
+ "channels_join(channels, automatic=False) -> None\n"
+ "\n"
+ "Join to channels in server. `channels' may also contain keys for\n"
+ "channels just like with /JOIN command. `automatic' specifies if this\n"
+ "channel was joined 'automatically' or if it was joined because join\n"
+ "was requested by user. If channel join is 'automatic', irssi doesn't\n"
+ "jump to the window where the channel was joined.\n"
+);
+static PyObject *PyServer_channels_join(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"channels", "automatic", NULL};
+ char *channels;
+ int automatic = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &channels, &automatic))
+ return NULL;
+
+ self->data->channels_join(self->data, channels, automatic);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_item_find_doc,
+ "window_item_find(name) -> WindowItem object or None\n"
+ "\n"
+ "Find window item that matches best to given arguments\n"
+);
+static PyObject *PyServer_window_item_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(window_item_find(self->data, name), 1);
+}
+
+PyDoc_STRVAR(PyServer_window_find_item_doc,
+ "window_find_item(name) -> Window object or None\n"
+ "\n"
+ "Find window which contains window item with specified name/server\n"
+);
+static PyObject *PyServer_window_find_item(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ win = window_find_item(self->data, name);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_find_level_doc,
+ "window_find_level(level) -> Window object or None\n"
+ "\n"
+ "Find window with level\n"
+);
+static PyObject *PyServer_window_find_level(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ int level = 0;
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &level))
+ return NULL;
+
+ win = window_find_level(self->data, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_find_closest_doc,
+ "window_find_closest(name, level) -> Window object or None\n"
+ "\n"
+ "Find window that matches best to given arguments. `name' can be either\n"
+ "window name or name of one of the window items.\n"
+);
+static PyObject *PyServer_window_find_closest(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", "level", NULL};
+ char *name = "";
+ int level = 0;
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist,
+ &name, &level))
+ return NULL;
+
+ win = window_find_closest(self->data, name, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_channels_doc,
+ "channels() -> list of Channel objects\n"
+ "\n"
+ "Return list of channels for server\n"
+);
+static PyObject *PyServer_channels(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(self->data->channels, 1);
+}
+
+PyDoc_STRVAR(PyServer_channel_find_doc,
+ "channel_find(name) -> Channel object or None\n"
+ "\n"
+ "Find channel from this server\n"
+);
+static PyObject *PyServer_channel_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(channel_find(self->data, name), 1);
+}
+
+PyDoc_STRVAR(PyServer_nicks_get_same_doc,
+ "nicks_get_same(nick)\n"
+ "\n"
+ "Return all nick objects in all channels in server. List is in format:\n"
+ "[(Channel, Nick), (Channel, Nick), ...]\n"
+);
+static PyObject *PyServer_nicks_get_same(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+ GSList *list, *node;
+ PyObject *pylist = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ pylist = PyList_New(0);
+ if (!pylist)
+ return NULL;
+
+ list = nicklist_get_same(self->data, nick);
+ for (node = list; node != NULL; node = node->next->next)
+ {
+ int ret;
+ PyObject *tup;
+
+ tup = Py_BuildValue("(NN)",
+ py_irssi_chat_new(node->data, 1),
+ py_irssi_chat_new(node->next->data, 1));
+ if (!tup)
+ {
+ Py_XDECREF(pylist);
+ return NULL;
+ }
+
+ ret = PyList_Append(pylist, tup);
+ Py_DECREF(tup);
+ if (ret != 0)
+ {
+ Py_XDECREF(pylist);
+ return NULL;
+ }
+ }
+
+ return pylist;
+}
+
+PyDoc_STRVAR(PyServer_queries_doc,
+ "queries() -> list of Query objects\n"
+ "\n"
+ "Return a list of queries for server.\n"
+);
+static PyObject *PyServer_queries(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(self->data->queries, 1);
+}
+
+PyDoc_STRVAR(PyServer_query_find_doc,
+ "query_find(nick) -> Query object or None\n"
+ "\n"
+ "Find a query on this server.\n"
+);
+static PyObject *PyServer_query_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return py_irssi_chat_new(query_find(self->data, nick), 1);
+}
+
+PyDoc_STRVAR(PyServer_mask_match_doc,
+ "mask_match(mask, nick, user, host) -> bool\n"
+ "\n"
+ "Return true if mask matches nick!user@host\n"
+);
+static PyObject *PyServer_mask_match(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "user", "host", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *user = "";
+ char *host = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist,
+ &mask, &nick, &user, &host))
+ return NULL;
+
+ return PyBool_FromLong(mask_match(self->data, mask, nick, user, host));
+}
+
+PyDoc_STRVAR(PyServer_mask_match_address_doc,
+ "mask_match_address(mask, nick, address) -> bool\n"
+ "\n"
+ "Return True if mask matches nick!address\n"
+);
+static PyObject *PyServer_mask_match_address(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "address", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *address = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &mask, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(mask_match_address(self->data, mask, nick, address));
+}
+
+PyDoc_STRVAR(PyServer_masks_match_doc,
+ "masks_match(masks, nick, address) -> bool\n"
+ "\n"
+ "Return True if any mask in the masks (string separated by spaces)\n"
+ "matches nick!address\n"
+);
+static PyObject *PyServer_masks_match(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"masks", "nick", "address", NULL};
+ char *masks = "";
+ char *nick = "";
+ char *address = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &masks, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(masks_match(self->data, masks, nick, address));
+}
+
+PyDoc_STRVAR(PyServer_ignore_check_doc,
+ "ignore_check(nick, host, channel, text, level) -> bool\n"
+ "\n"
+ "Return True if ignore matches\n"
+);
+static PyObject *PyServer_ignore_check(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL};
+ char *nick = "";
+ char *host = "";
+ char *channel = "";
+ char *text = "";
+ int level = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist,
+ &nick, &host, &channel, &text, &level))
+ return NULL;
+
+ return PyBool_FromLong(ignore_check(self->data,
+ nick, host, channel, text, level));
+}
+
+/* Methods for object */
+static PyMethodDef PyServer_methods[] = {
+ {"prnt", (PyCFunction)PyServer_prnt, METH_VARARGS | METH_KEYWORDS,
+ print_doc},
+ {"command", (PyCFunction)PyServer_command, METH_VARARGS | METH_KEYWORDS,
+ command_doc},
+ {"disconnect", (PyCFunction)PyServer_disconnect, METH_NOARGS,
+ disconnect_doc},
+ {"isnickflag", (PyCFunction)PyServer_isnickflag, METH_VARARGS | METH_KEYWORDS,
+ isnickflag_doc},
+ {"ischannel", (PyCFunction)PyServer_ischannel, METH_VARARGS | METH_KEYWORDS,
+ ischannel_doc},
+ {"get_nick_flags", (PyCFunction)PyServer_get_nick_flags, METH_NOARGS,
+ get_nick_flags_doc},
+ {"send_message", (PyCFunction)PyServer_send_message, METH_VARARGS | METH_KEYWORDS,
+ send_message_doc},
+ {"channels_join", (PyCFunction)PyServer_channels_join, METH_VARARGS | METH_KEYWORDS,
+ channels_join_doc},
+ {"window_item_find", (PyCFunction)PyServer_window_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_item_find_doc},
+ {"window_find_item", (PyCFunction)PyServer_window_find_item, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_item_doc},
+ {"window_find_level", (PyCFunction)PyServer_window_find_level, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_level_doc},
+ {"window_find_closest", (PyCFunction)PyServer_window_find_closest, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_closest_doc},
+ {"channels", (PyCFunction)PyServer_channels, METH_NOARGS,
+ PyServer_channels_doc},
+ {"channel_find", (PyCFunction)PyServer_channel_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_channel_find_doc},
+ {"nicks_get_same", (PyCFunction)PyServer_nicks_get_same, METH_VARARGS | METH_KEYWORDS,
+ PyServer_nicks_get_same_doc},
+ {"queries", (PyCFunction)PyServer_queries, METH_NOARGS,
+ PyServer_queries_doc},
+ {"query_find", (PyCFunction)PyServer_query_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_query_find_doc},
+ {"mask_match", (PyCFunction)PyServer_mask_match, METH_VARARGS | METH_KEYWORDS,
+ PyServer_mask_match_doc},
+ {"mask_match_address", (PyCFunction)PyServer_mask_match_address, METH_VARARGS | METH_KEYWORDS,
+ PyServer_mask_match_address_doc},
+ {"masks_match", (PyCFunction)PyServer_masks_match, METH_VARARGS | METH_KEYWORDS,
+ PyServer_masks_match_doc},
+ {"ignore_check", (PyCFunction)PyServer_ignore_check, METH_VARARGS | METH_KEYWORDS,
+ PyServer_ignore_check_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Server", /*tp_name*/
+ sizeof(PyServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyServer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyServer_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* server factory function
+ connect arg should point to a wrapped SERVER_CONNECT */
+PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass)
+{
+ static const char *SERVER_TYPE = "SERVER";
+ SERVER_REC *srec = server;
+ PyServer *pyserver = NULL;
+ PyObject *rawlog = NULL;
+ PyObject *connect = NULL;
+
+ g_return_val_if_fail(server != NULL, NULL);
+
+ connect = py_irssi_chat_new(srec->connrec, 0);
+ if (!connect)
+ return NULL;
+
+ /* FIXME */
+ /*
+ if (srec->rawlog)
+ {
+ rawlog = pyrawlog_new(srec->rawlog);
+ if (!rawlog)
+ return NULL;
+ }
+ */
+
+ pyserver = py_instp(PyServer, subclass);
+ if (!pyserver)
+ return NULL;
+
+ pyserver->base_name = SERVER_TYPE;
+ pyserver->data = server;
+ signal_add_last_data("server disconnected", server_cleanup, pyserver);
+ pyserver->cleanup_installed = 1;
+ pyserver->rawlog = rawlog;
+ pyserver->connect = connect;
+
+ return (PyObject *)pyserver;
+}
+
+PyObject *pyserver_new(void *server)
+{
+ return pyserver_sub_new(server, &PyServerType);
+}
+
+int server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyServerType);
+ PyModule_AddObject(py_module, "Server", (PyObject *)&PyServerType);
+
+ return 1;
+}
diff --git a/src/objects/server-object.h b/src/objects/server-object.h
new file mode 100644
index 0000000..0703f47
--- /dev/null
+++ b/src/objects/server-object.h
@@ -0,0 +1,28 @@
+#ifndef _SERVER_OBJECT_H_
+#define _SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _SERVER_REC;
+
+#define PyServer_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *connect; \
+ PyObject *rawlog;
+
+typedef struct
+{
+ PyServer_HEAD(struct _SERVER_REC)
+} PyServer;
+
+extern PyTypeObject PyServerType;
+
+int server_object_init(void);
+PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass);
+PyObject *pyserver_new(void *server);
+
+#define pyserver_check(op) PyObject_TypeCheck(op, &PyServerType)
+
+#endif
diff --git a/src/objects/statusbar-item-object.c b/src/objects/statusbar-item-object.c
new file mode 100644
index 0000000..f6768af
--- /dev/null
+++ b/src/objects/statusbar-item-object.c
@@ -0,0 +1,253 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "factory.h"
+#include "statusbar-item-object.h"
+
+/* monitor "statusbar item destroyed" signal */
+static void statusbar_item_cleanup(SBAR_ITEM_REC *sbar_item)
+{
+ PyStatusbarItem *pysbar_item = signal_get_user_data();
+
+ if (sbar_item == pysbar_item->data)
+ {
+ pysbar_item->data = NULL;
+ pysbar_item->cleanup_installed = 0;
+ signal_remove_data("statusbar item_destroy", statusbar_item_cleanup, pysbar_item);
+ }
+}
+
+static void PyStatusbarItem_dealloc(PyStatusbarItem *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("sbar_itemlist remove", statusbar_item_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyStatusbarItem_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyStatusbarItem *self;
+
+ self = (PyStatusbarItem *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getter */
+PyDoc_STRVAR(PyStatusbarItem_min_size_doc,
+ "min size"
+);
+static PyObject *PyStatusbarItem_min_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->min_size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_max_size_doc,
+ "max size"
+);
+static PyObject *PyStatusbarItem_max_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->max_size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_xpos_doc,
+ "x position"
+);
+static PyObject *PyStatusbarItem_xpos_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->xpos);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_size_doc,
+ "size"
+);
+static PyObject *PyStatusbarItem_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_window_doc,
+ "parent window for statusbar item"
+);
+static PyObject *PyStatusbarItem_window_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->window);
+}
+
+/* setters */
+static int py_setint(int *iv, PyObject *value)
+{
+ int tmp;
+
+ if (value == NULL)
+ {
+ PyErr_SetString(PyExc_AttributeError, "can't delete member");
+ return -1;
+ }
+
+ if (!PyInt_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "value must be int");
+ return -1;
+ }
+
+ tmp = PyInt_AsLong(value);
+ if (PyErr_Occurred())
+ return -1;
+
+ *iv = tmp;
+
+ return 0;
+}
+
+static int PyStatusbarItem_min_size_set(PyStatusbarItem *self, PyObject *value, void *closure)
+{
+ return py_setint(&self->data->min_size, value);
+}
+
+static int PyStatusbarItem_max_size_set(PyStatusbarItem *self, PyObject *value, void *closure)
+{
+ return py_setint(&self->data->max_size, value);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyStatusbarItem_getseters[] = {
+ {"min_size", (getter)PyStatusbarItem_min_size_get, (setter)PyStatusbarItem_min_size_set,
+ PyStatusbarItem_min_size_doc, NULL},
+ {"max_size", (getter)PyStatusbarItem_max_size_get, (setter)PyStatusbarItem_max_size_set,
+ PyStatusbarItem_max_size_doc, NULL},
+ {"xpos", (getter)PyStatusbarItem_xpos_get, NULL,
+ PyStatusbarItem_xpos_doc, NULL},
+ {"size", (getter)PyStatusbarItem_size_get, NULL,
+ PyStatusbarItem_size_doc, NULL},
+ {"window", (getter)PyStatusbarItem_window_get, NULL,
+ PyStatusbarItem_window_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyStatusbarItem_default_handler_doc,
+ "default_handler(get_size_only, str=None, data="", escape_vars=True) -> None\n"
+ "\n"
+ "Run default handler of item to print to statusbar\n"
+);
+static PyObject *PyStatusbarItem_default_handler(PyStatusbarItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"get_size_only", "str", "data", "escape_vars", NULL};
+ int get_size_only = 0;
+ char *str = NULL;
+ char *data = "";
+ int escape_vars = TRUE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|zsi", kwlist,
+ &get_size_only, &str, &data, &escape_vars))
+ return NULL;
+
+ if (str && !*str)
+ str = NULL;
+
+ statusbar_item_default_handler(self->data, get_size_only, str, data, escape_vars);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyStatusbarItem_methods[] = {
+ {"default_handler", (PyCFunction)PyStatusbarItem_default_handler, METH_VARARGS | METH_KEYWORDS,
+ PyStatusbarItem_default_handler_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyStatusbarItemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "StatusbarItem", /*tp_name*/
+ sizeof(PyStatusbarItem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyStatusbarItem_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyStatusbarItem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyStatusbarItem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyStatusbarItem_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyStatusbarItem_new, /* tp_new */
+};
+
+
+/* sbar_item factory function */
+PyObject *pystatusbar_item_new(void *sbar_item)
+{
+ SBAR_ITEM_REC *si;
+ PyStatusbarItem *pysbar_item;
+ PyObject *window = NULL;
+
+ si = sbar_item;
+ if (si->bar->parent_window)
+ {
+ window = pywindow_new(si->bar->parent_window);
+ if (!window)
+ return NULL;
+ }
+
+ pysbar_item = py_inst(PyStatusbarItem, PyStatusbarItemType);
+ if (!pysbar_item)
+ return NULL;
+
+ pysbar_item->window = window;
+
+ pysbar_item->data = sbar_item;
+ pysbar_item->cleanup_installed = 1;
+ signal_add_last_data("statusbar item destroyed", statusbar_item_cleanup, pysbar_item);
+
+ return (PyObject *)pysbar_item;
+}
+
+int statusbar_item_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyStatusbarItemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyStatusbarItemType);
+ PyModule_AddObject(py_module, "StatusbarItem", (PyObject *)&PyStatusbarItemType);
+
+ return 1;
+}
diff --git a/src/objects/statusbar-item-object.h b/src/objects/statusbar-item-object.h
new file mode 100644
index 0000000..19243f4
--- /dev/null
+++ b/src/objects/statusbar-item-object.h
@@ -0,0 +1,22 @@
+#ifndef _SBAR_ITEM_OBJECT_H_
+#define _SBAR_ITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct SBAR_ITEM_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct SBAR_ITEM_REC)
+ PyObject *window;
+} PyStatusbarItem;
+
+extern PyTypeObject PyStatusbarItemType;
+
+int statusbar_item_object_init(void);
+PyObject *pystatusbar_item_new(void *sbar_item);
+#define pystatusbar_item_check(op) PyObject_TypeCheck(op, &PyStatusbarItemType)
+
+#endif
diff --git a/src/objects/textdest-object.c b/src/objects/textdest-object.c
new file mode 100644
index 0000000..2ad6523
--- /dev/null
+++ b/src/objects/textdest-object.c
@@ -0,0 +1,285 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "textdest-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned);
+
+/* XXX: no cleanup signal for textdest */
+static void PyTextDest_dealloc(PyTextDest *self)
+{
+ Py_XDECREF(self->window);
+ Py_XDECREF(self->server);
+
+ if (self->owned)
+ {
+ g_free((char*)self->data->target);
+ g_free(self->data);
+ }
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyTextDest_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyTextDest *self;
+
+ self = (PyTextDest *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* init function to create the textdest */
+PyDoc_STRVAR(PyTextDest_doc,
+ "__init__(target, level=MSGLEVEL_CLIENTNOTICE, server=None, window=None)\n"
+ "\n"
+ "Create a TextDest\n"
+);
+static int PyTextDest_init(PyTextDest *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"target", "level", "server", "window", NULL};
+ char *target;
+ int level = MSGLEVEL_CLIENTNOTICE;
+ PyObject *server = NULL, *window = NULL;
+ TEXT_DEST_REC *dest;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ioo", kwlist,
+ &target, &level, &server, &window))
+ return -1;
+
+ if (server == Py_None)
+ server = NULL;
+ if (window == Py_None)
+ window = NULL;
+
+ if (server && !pyserver_check(server))
+ {
+ PyErr_Format(PyExc_TypeError, "arg 3 isnt server");
+ return -1;
+ }
+
+ if (window && !pywindow_check(window))
+ {
+ PyErr_Format(PyExc_TypeError, "arg 4 isnt window");
+ return -1;
+ }
+
+ if (self->data)
+ {
+ PyErr_Format(PyExc_RuntimeError, "TextDest already wrapped");
+ return -1;
+ }
+
+ dest = g_new0(TEXT_DEST_REC, 1);
+ format_create_dest(dest, DATA(server), g_strdup(target), level, DATA(window));
+
+ if (!pytextdest_setup(self, dest, 1))
+ return -1;
+
+ return 0;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyTextDest_window_doc,
+ "Window where the text will be written"
+);
+static PyObject *PyTextDest_window_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->window);
+}
+
+PyDoc_STRVAR(PyTextDest_server_doc,
+ "Target server"
+);
+static PyObject *PyTextDest_server_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyTextDest_target_doc,
+ "Target channel/query/etc name"
+);
+static PyObject *PyTextDest_target_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->target);
+}
+
+PyDoc_STRVAR(PyTextDest_level_doc,
+ "Text level"
+);
+static PyObject *PyTextDest_level_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyTextDest_hilight_priority_doc,
+ "Priority for the hilighted text"
+);
+static PyObject *PyTextDest_hilight_priority_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->hilight_priority);
+}
+
+PyDoc_STRVAR(PyTextDest_hilight_color_doc,
+ "Color for the hilighted text"
+);
+static PyObject *PyTextDest_hilight_color_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyTextDest_getseters[] = {
+ {"window", (getter)PyTextDest_window_get, NULL,
+ PyTextDest_window_doc, NULL},
+ {"server", (getter)PyTextDest_server_get, NULL,
+ PyTextDest_server_doc, NULL},
+ {"target", (getter)PyTextDest_target_get, NULL,
+ PyTextDest_target_doc, NULL},
+ {"level", (getter)PyTextDest_level_get, NULL,
+ PyTextDest_level_doc, NULL},
+ {"hilight_priority", (getter)PyTextDest_hilight_priority_get, NULL,
+ PyTextDest_hilight_priority_doc, NULL},
+ {"hilight_color", (getter)PyTextDest_hilight_color_get, NULL,
+ PyTextDest_hilight_color_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyTextDest_prnt_doc,
+ "prnt(str) -> None\n"
+ "\n"
+ "Print str to TextDest\n"
+);
+static PyObject *PyTextDest_prnt(PyTextDest *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ printtext_dest(self->data, "%s", str);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyTextDest_methods[] = {
+ {"prnt", (PyCFunction)PyTextDest_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyTextDest_prnt_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyTextDestType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "TextDest", /*tp_name*/
+ sizeof(PyTextDest), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyTextDest_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ PyTextDest_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyTextDest_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyTextDest_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyTextDest_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyTextDest_new, /* tp_new */
+};
+
+static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned)
+{
+ PyObject *window, *server;
+ TEXT_DEST_REC *tdest = td;
+
+ if (tdest->window)
+ {
+ window = pywindow_new(tdest->window);
+ if (!window)
+ return 0;
+ }
+
+ server = py_irssi_chat_new(tdest->server, 1);
+ if (!server)
+ {
+ Py_DECREF(window);
+ return 0;
+ }
+
+ pytdest->data = td;
+ pytdest->window = window;
+ pytdest->server = server;
+ pytdest->owned = owned;
+
+ return 1;
+}
+
+/* TextDest factory function */
+PyObject *pytextdest_new(void *td)
+{
+ PyTextDest *pytdest;
+
+ pytdest = py_inst(PyTextDest, PyTextDestType);
+ if (!pytdest)
+ return NULL;
+
+ if (!pytextdest_setup(pytdest, td, 0))
+ {
+ Py_DECREF(pytdest);
+ return NULL;
+ }
+
+ return (PyObject *)pytdest;
+}
+
+int textdest_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyTextDestType) < 0)
+ return 0;
+
+ Py_INCREF(&PyTextDestType);
+ PyModule_AddObject(py_module, "TextDest", (PyObject *)&PyTextDestType);
+
+ return 1;
+}
diff --git a/src/objects/textdest-object.h b/src/objects/textdest-object.h
new file mode 100644
index 0000000..3e273e6
--- /dev/null
+++ b/src/objects/textdest-object.h
@@ -0,0 +1,24 @@
+#ifndef _TEXTDEST_OBJECT_H_
+#define _TEXTDEST_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _TEXT_DEST_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _TEXT_DEST_REC)
+ PyObject *window;
+ PyObject *server;
+ int owned;
+} PyTextDest;
+
+extern PyTypeObject PyTextDestType;
+
+int textdest_object_init(void);
+PyObject *pytextdest_new(void *td);
+#define pytextdest_check(op) PyObject_TypeCheck(op, &PyTextDestType)
+
+#endif
diff --git a/src/objects/theme-object.c b/src/objects/theme-object.c
new file mode 100644
index 0000000..0f12217
--- /dev/null
+++ b/src/objects/theme-object.c
@@ -0,0 +1,195 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "theme-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* monitor "theme destroyed" signal */
+static void theme_cleanup(THEME_REC *rec)
+{
+ PyTheme *pytheme = signal_get_user_data();
+ if (pytheme->data == rec)
+ {
+ pytheme->data = NULL;
+ pytheme->cleanup_installed = 0;
+ signal_remove_data("theme destroyed", theme_cleanup, pytheme);
+ }
+}
+
+static void PyTheme_dealloc(PyTheme *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("theme destroyed", theme_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyTheme_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyTheme *self;
+
+ self = (PyTheme *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+/* specialized getters/setters */
+static PyGetSetDef PyTheme_getseters[] = {
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyTheme_format_expand_doc,
+ "format_expand(format, flags=0) -> str or None\n"
+);
+static PyObject *PyTheme_format_expand(PyTheme *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"format", "flags", NULL};
+ char *format = "";
+ int flags = 0;
+ char *ret;
+ PyObject *pyret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &format, &flags))
+ return NULL;
+
+ if (flags == 0)
+ ret = theme_format_expand(self->data, format);
+ else
+ ret = theme_format_expand_data(self->data, (const char **)&format, 'n', 'n',
+ NULL, NULL, EXPAND_FLAG_ROOT | flags);
+
+ if (ret)
+ {
+ pyret = PyString_FromString(ret);
+ g_free(ret);
+ return pyret;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyTheme_get_format_doc,
+ "get_format(module, tag) -> str\n"
+);
+static PyObject *PyTheme_get_format(PyTheme *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"module", "tag", NULL};
+ char *module = "";
+ char *tag = "";
+ THEME_REC *theme = self->data;
+ FORMAT_REC *formats;
+ MODULE_THEME_REC *modtheme;
+ int i;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &module, &tag))
+ return NULL;
+
+ formats = g_hash_table_lookup(default_formats, module);
+ if (!formats)
+ return PyErr_Format(PyExc_KeyError, "unknown module, %s", module);
+
+ for (i = 0; formats[i].def; i++)
+ {
+ if (formats[i].tag && !g_strcasecmp(formats[i].tag, tag))
+ {
+ modtheme = g_hash_table_lookup(theme->modules, module);
+ if (modtheme && modtheme->formats[i])
+ return PyString_FromString(modtheme->formats[i]);
+ else
+ return PyString_FromString(formats[i].def);
+ }
+ }
+
+ return PyErr_Format(PyExc_KeyError, "unknown format tag, %s", tag);
+}
+
+/* Methods for object */
+static PyMethodDef PyTheme_methods[] = {
+ {"format_expand", (PyCFunction)PyTheme_format_expand, METH_VARARGS | METH_KEYWORDS,
+ PyTheme_format_expand_doc},
+ {"get_format", (PyCFunction)PyTheme_get_format, METH_VARARGS | METH_KEYWORDS,
+ PyTheme_get_format_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyThemeType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Theme", /*tp_name*/
+ sizeof(PyTheme), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyTheme_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyTheme_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyTheme_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyTheme_new, /* tp_new */
+};
+
+/* Theme factory function */
+PyObject *pytheme_new(void *td)
+{
+ PyTheme *pytheme;
+
+ pytheme = py_inst(PyTheme, PyThemeType);
+ if (!pytheme)
+ return NULL;
+
+ pytheme->data = td;
+ signal_add_last_data("theme destroyed", theme_cleanup, pytheme);
+ pytheme->cleanup_installed = 1;
+
+ return (PyObject *)pytheme;
+}
+
+int theme_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyThemeType) < 0)
+ return 0;
+
+ Py_INCREF(&PyThemeType);
+ PyModule_AddObject(py_module, "Theme", (PyObject *)&PyThemeType);
+
+ return 1;
+}
diff --git a/src/objects/theme-object.h b/src/objects/theme-object.h
new file mode 100644
index 0000000..299d54d
--- /dev/null
+++ b/src/objects/theme-object.h
@@ -0,0 +1,18 @@
+#ifndef _THEME_OBJECT_H_
+#define _THEME_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyTheme;
+
+extern PyTypeObject PyThemeType;
+
+int theme_object_init(void);
+PyObject *pytheme_new(void *td);
+#define pytheme_check(op) PyObject_TypeCheck(op, &PyThemeType)
+
+#endif
diff --git a/src/objects/window-item-object.c b/src/objects/window-item-object.c
new file mode 100644
index 0000000..2f18f7d
--- /dev/null
+++ b/src/objects/window-item-object.c
@@ -0,0 +1,334 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+#include "factory.h"
+
+/* Dealloc is overridden by sub types */
+
+PyDoc_STRVAR(PyWindowItem_server_doc,
+ "Active name for item"
+);
+static PyObject *PyWindowItem_server_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyWindowItem_name_doc,
+ "Name of the item"
+);
+static PyObject *PyWindowItem_name_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->visible_name);
+}
+
+PyDoc_STRVAR(PyWindowItem_createtime_doc,
+ "Time the witem was created"
+);
+static PyObject *PyWindowItem_createtime_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->createtime);
+}
+
+PyDoc_STRVAR(PyWindowItem_data_level_doc,
+ "0=no new data, 1=text, 2=msg, 3=highlighted text"
+);
+static PyObject *PyWindowItem_data_level_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->data_level);
+}
+
+PyDoc_STRVAR(PyWindowItem_hilight_color_doc,
+ "Color of the last highlighted text"
+);
+static PyObject *PyWindowItem_hilight_color_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyWindowItem_getseters[] = {
+ {"server", (getter)PyWindowItem_server_get, NULL,
+ PyWindowItem_server_doc, NULL},
+ {"name", (getter)PyWindowItem_name_get, NULL,
+ PyWindowItem_name_doc, NULL},
+ {"createtime", (getter)PyWindowItem_createtime_get, NULL,
+ PyWindowItem_createtime_doc, NULL},
+ {"data_level", (getter)PyWindowItem_data_level_get, NULL,
+ PyWindowItem_data_level_doc, NULL},
+ {"hilight_color", (getter)PyWindowItem_hilight_color_get, NULL,
+ PyWindowItem_hilight_color_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyWindowItem_prnt_doc,
+ "prnt(str, level) -> None\n"
+ "\n"
+ "Print to window item\n"
+);
+static PyObject *PyWindowItem_prnt(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", "level", NULL};
+ char *str;
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &str, &level))
+ return NULL;
+
+ printtext_string(self->data->server, self->data->visible_name, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command to window item\n"
+);
+static PyObject *PyWindowItem_command(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ py_command(cmd, self->data->server, self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_window_doc,
+ "window() -> Window object or None\n"
+ "\n"
+ "Return parent window for window item\n"
+);
+static PyObject *PyWindowItem_window(PyWindowItem *self, PyObject *args)
+{
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ win = window_item_window(self->data);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change server for window item\n"
+);
+static PyObject *PyWindowItem_change_server(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be server");
+
+ window_item_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_is_active_doc,
+ "is_active() -> bool\n"
+ "\n"
+ "Returns true if window item is active\n"
+);
+static PyObject *PyWindowItem_is_active(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyBool_FromLong(window_item_is_active(self->data));
+}
+
+PyDoc_STRVAR(PyWindowItem_set_active_doc,
+ "set_active() -> None\n"
+ "\n"
+ "Set window item active\n"
+);
+static PyObject *PyWindowItem_set_active(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_set_active(window_item_window(self->data), self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_activity_doc,
+ "activity(data_level, hilight_color) -> None\n"
+ "\n"
+);
+static PyObject *PyWindowItem_activity(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data_level", "hilight_color", NULL};
+ int data_level = 0;
+ char *hilight_color = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist,
+ &data_level, &hilight_color))
+ return NULL;
+
+ window_item_activity(self->data, data_level, hilight_color);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy channel or query\n"
+);
+static PyObject *PyWindowItem_destroy(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_destroy(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_get_dcc_doc,
+ "get_dcc() -> DccChat object or None\n"
+ "\n"
+ "If item is a query of a =nick, return DCC chat record of nick\n"
+);
+static PyObject *PyWindowItem_get_dcc(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_new(self->data, 1);
+}
+
+/* Methods for object */
+static PyMethodDef PyWindowItem_methods[] = {
+ {"prnt", (PyCFunction)PyWindowItem_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_prnt_doc},
+ {"command", (PyCFunction)PyWindowItem_command, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_command_doc},
+ {"window", (PyCFunction)PyWindowItem_window, METH_NOARGS,
+ PyWindowItem_window_doc},
+ {"change_server", (PyCFunction)PyWindowItem_change_server, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_change_server_doc},
+ {"is_active", (PyCFunction)PyWindowItem_is_active, METH_NOARGS,
+ PyWindowItem_is_active_doc},
+ {"set_active", (PyCFunction)PyWindowItem_set_active, METH_NOARGS,
+ PyWindowItem_set_active_doc},
+ {"activity", (PyCFunction)PyWindowItem_activity, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_activity_doc},
+ {"destroy", (PyCFunction)PyWindowItem_destroy, METH_NOARGS,
+ PyWindowItem_destroy_doc},
+ {"get_dcc", (PyCFunction)PyWindowItem_get_dcc, METH_NOARGS,
+ PyWindowItem_get_dcc_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyWindowItemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "WindowItem", /*tp_name*/
+ sizeof(PyWindowItem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyWindowItem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyWindowItem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyWindowItem_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass)
+{
+ WI_ITEM_REC *rec = witem;
+ PyWindowItem *pywitem = NULL;
+ PyObject *server;
+
+ g_return_val_if_fail(witem != NULL, NULL);
+
+ server = py_irssi_chat_new(rec->server, 1);
+ if (!server)
+ return NULL;
+
+ pywitem = py_instp(PyWindowItem, subclass);
+ if (!pywitem)
+ return NULL;
+
+ pywitem->data = witem;
+ pywitem->base_name = name;
+ pywitem->server = server;
+
+ return (PyObject *)pywitem;
+}
+
+PyObject *pywindow_item_new(void *witem)
+{
+ return pywindow_item_sub_new(witem, NULL, &PyWindowItemType);
+}
+
+int window_item_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyWindowItemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyWindowItemType);
+ PyModule_AddObject(py_module, "WindowItem", (PyObject *)&PyWindowItemType);
+
+ return 1;
+}
diff --git a/src/objects/window-item-object.h b/src/objects/window-item-object.h
new file mode 100644
index 0000000..e205ebe
--- /dev/null
+++ b/src/objects/window-item-object.h
@@ -0,0 +1,26 @@
+#ifndef _WITEM_OBJECT_H_
+#define _WITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+#define PyWindowItem_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *server;
+
+/* forward */
+struct _WI_ITEM_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _WI_ITEM_REC)
+} PyWindowItem;
+
+extern PyTypeObject PyWindowItemType;
+
+int window_item_object_init(void);
+PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass);
+PyObject *pywindow_item_new(void *witem);
+#define pywindow_item_check(op) PyObject_TypeCheck(op, &PyWindowItemType)
+
+#endif
diff --git a/src/objects/window-object.c b/src/objects/window-object.c
new file mode 100644
index 0000000..d5aa475
--- /dev/null
+++ b/src/objects/window-object.c
@@ -0,0 +1,678 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "window-object.h"
+#include "factory.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* monitor "window destroyed" signal */
+static void window_cleanup(WINDOW_REC *win)
+{
+ PyWindow *pywindow = signal_get_user_data();
+
+ if (win == pywindow->data)
+ {
+ pywindow->data = NULL;
+ pywindow->cleanup_installed = 0;
+ signal_remove_data("window destroyed", window_cleanup, pywindow);
+ }
+}
+
+static void PyWindow_dealloc(PyWindow *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("window destroyed", window_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyWindow *self;
+
+ self = (PyWindow *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyWindow_refnum_doc,
+ "Reference number"
+);
+static PyObject *PyWindow_refnum_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->refnum);
+}
+
+PyDoc_STRVAR(PyWindow_name_doc,
+ "Name"
+);
+static PyObject *PyWindow_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyWindow_width_doc,
+ "Width"
+);
+static PyObject *PyWindow_width_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->width);
+}
+
+PyDoc_STRVAR(PyWindow_height_doc,
+ "Height"
+);
+static PyObject *PyWindow_height_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->height);
+}
+
+PyDoc_STRVAR(PyWindow_history_name_doc,
+ "Name of named historylist for this window"
+);
+static PyObject *PyWindow_history_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->history_name);
+}
+
+PyDoc_STRVAR(PyWindow_active_doc,
+ "Active window item"
+);
+static PyObject *PyWindow_active_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chat_new(self->data->active, 1);
+}
+
+PyDoc_STRVAR(PyWindow_active_server_doc,
+ "Active server"
+);
+static PyObject *PyWindow_active_server_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chat_new(self->data->active_server, 1);
+}
+
+PyDoc_STRVAR(PyWindow_servertag_doc,
+ "active_server must be either None or have this same tag"
+ "(unless there's items in this window). This is used by"
+ "/WINDOW SERVER -sticky"
+);
+static PyObject *PyWindow_servertag_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->servertag);
+}
+
+PyDoc_STRVAR(PyWindow_level_doc,
+ "Current window level"
+);
+static PyObject *PyWindow_level_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyWindow_sticky_refnum_doc,
+ "True if reference number is sticky"
+);
+static PyObject *PyWindow_sticky_refnum_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->sticky_refnum);
+}
+
+PyDoc_STRVAR(PyWindow_data_level_doc,
+ "Current data level"
+);
+static PyObject *PyWindow_data_level_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->data_level);
+}
+
+PyDoc_STRVAR(PyWindow_hilight_color_doc,
+ "Current activity hilight color"
+);
+static PyObject *PyWindow_hilight_color_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+PyDoc_STRVAR(PyWindow_last_timestamp_doc,
+ "Last time timestamp was written in window"
+);
+static PyObject *PyWindow_last_timestamp_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_timestamp);
+}
+
+PyDoc_STRVAR(PyWindow_last_line_doc,
+ "Last time text was written in window"
+);
+static PyObject *PyWindow_last_line_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_line);
+}
+
+PyDoc_STRVAR(PyWindow_theme_name_doc,
+ "Active theme in window, None = default"
+);
+static PyObject *PyWindow_theme_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->theme_name);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyWindow_getseters[] = {
+ {"refnum", (getter)PyWindow_refnum_get, NULL,
+ PyWindow_refnum_doc, NULL},
+ {"name", (getter)PyWindow_name_get, NULL,
+ PyWindow_name_doc, NULL},
+ {"width", (getter)PyWindow_width_get, NULL,
+ PyWindow_width_doc, NULL},
+ {"height", (getter)PyWindow_height_get, NULL,
+ PyWindow_height_doc, NULL},
+ {"history_name", (getter)PyWindow_history_name_get, NULL,
+ PyWindow_history_name_doc, NULL},
+ {"active", (getter)PyWindow_active_get, NULL,
+ PyWindow_active_doc, NULL},
+ {"active_server", (getter)PyWindow_active_server_get, NULL,
+ PyWindow_active_server_doc, NULL},
+ {"servertag", (getter)PyWindow_servertag_get, NULL,
+ PyWindow_servertag_doc, NULL},
+ {"level", (getter)PyWindow_level_get, NULL,
+ PyWindow_level_doc, NULL},
+ {"sticky_refnum", (getter)PyWindow_sticky_refnum_get, NULL,
+ PyWindow_sticky_refnum_doc, NULL},
+ {"data_level", (getter)PyWindow_data_level_get, NULL,
+ PyWindow_data_level_doc, NULL},
+ {"hilight_color", (getter)PyWindow_hilight_color_get, NULL,
+ PyWindow_hilight_color_doc, NULL},
+ {"last_timestamp", (getter)PyWindow_last_timestamp_get, NULL,
+ PyWindow_last_timestamp_doc, NULL},
+ {"last_line", (getter)PyWindow_last_line_get, NULL,
+ PyWindow_last_line_doc, NULL},
+ {"theme_name", (getter)PyWindow_theme_name_get, NULL,
+ PyWindow_theme_name_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyWindow_items_doc,
+ "items() -> list of WindowItem objects\n"
+ "\n"
+ "Return a list of items in window.\n"
+);
+static PyObject *PyWindow_items(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chatlist_new(self->data->items, 1);
+}
+
+PyDoc_STRVAR(PyWindow_prnt_doc,
+ "prnt(str, level=MSGLEVEL_CLIENTNOTICE) -> None\n"
+ "\n"
+ "Print to window\n"
+);
+static PyObject *PyWindow_prnt(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", "level", NULL};
+ char *str = "";
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &str, &level))
+ return NULL;
+
+ printtext_string_window(self->data, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command to window\n"
+);
+static PyObject *PyWindow_command(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd = "";
+ WINDOW_REC *old;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &cmd))
+ return NULL;
+
+ old = active_win;
+ active_win = self->data;
+ py_command(cmd, active_win->active_server, active_win->active);
+ if (g_slist_find(windows, old) != NULL)
+ active_win = old;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_add_doc,
+ "item_add(item, automatic=False) -> None\n"
+ "\n"
+ "Add window item\n"
+);
+static PyObject *PyWindow_item_add(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "automatic", NULL};
+ PyObject *item = NULL;
+ int automatic = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist,
+ &item, &automatic))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_add(self->data, ((PyWindowItem*)item)->data, automatic);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_remove_doc,
+ "item_remove(item) -> None\n"
+ "\n"
+ "Remove window item\n"
+);
+static PyObject *PyWindow_item_remove(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_remove(((PyWindowItem*)item)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_destroy_doc,
+ "item_destroy(item) -> None\n"
+ "\n"
+ "Destroy window item\n"
+);
+static PyObject *PyWindow_item_destroy(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_destroy(((PyWindowItem*)item)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_prev_doc,
+ "item_prev() -> None\n"
+ "\n"
+ "Change to previous window item\n"
+);
+static PyObject *PyWindow_item_prev(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_prev(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_next_doc,
+ "item_next() -> None\n"
+ "\n"
+ "Change to next window item\n"
+);
+static PyObject *PyWindow_item_next(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_next(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy the window.\n"
+);
+static PyObject *PyWindow_destroy(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_destroy(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_active_doc,
+ "set_active() -> None\n"
+ "\n"
+ "Set window active.\n"
+);
+static PyObject *PyWindow_set_active(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_set_active(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change server in window\n"
+);
+static PyObject *PyWindow_change_server(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be server");
+
+ window_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_refnum_doc,
+ "set_refnum(refnum) -> None\n"
+ "\n"
+ "Set window refnum\n"
+);
+static PyObject *PyWindow_set_refnum(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"refnum", NULL};
+ int refnum = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &refnum))
+ return NULL;
+
+ window_set_refnum(self->data, refnum);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_name_doc,
+ "set_name(name) -> None\n"
+ "\n"
+ "Set window name\n"
+);
+static PyObject *PyWindow_set_name(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ window_set_name(self->data, name);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_history_doc,
+ "set_history(history) -> None\n"
+ "\n"
+ "Set window history\n"
+);
+static PyObject *PyWindow_set_history(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"history", NULL};
+ char *history = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &history))
+ return NULL;
+
+ window_set_history(self->data, history);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_level_doc,
+ "set_level(level) -> None\n"
+ "\n"
+ "Set window level\n"
+);
+static PyObject *PyWindow_set_level(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ int level = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &level))
+ return NULL;
+
+ window_set_level(self->data, level);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_activity_doc,
+ "activity(data_level, hilight_color) -> None\n"
+ "\n"
+);
+static PyObject *PyWindow_activity(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data_level", "hilight_color", NULL};
+ int data_level = 0;
+ char *hilight_color = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|s", kwlist,
+ &data_level, &hilight_color))
+ return NULL;
+
+ window_activity(self->data, data_level, hilight_color);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_get_active_name_doc,
+ "get_active_name() -> str or None\n"
+ "\n"
+ "Return active item's name, or if none is active, window's name.\n"
+);
+static PyObject *PyWindow_get_active_name(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(window_get_active_name(self->data));
+}
+
+PyDoc_STRVAR(PyWindow_item_find_doc,
+ "item_find(server, name) -> WindowItem or None\n"
+ "\n"
+ "Find window item that matches best to given arguments\n"
+);
+static PyObject *PyWindow_item_find(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", "name", NULL};
+ PyObject *server = NULL;
+ char *name = "";
+ WI_ITEM_REC *witem;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", kwlist,
+ &server, &name))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg 1 must be server");
+
+ witem = window_item_find_window(self->data, ((PyServer*)server)->data, name);
+ return py_irssi_chat_new(witem, 1);
+}
+
+static PyMethodDef PyWindow_methods[] = {
+ {"items", (PyCFunction)PyWindow_items, METH_NOARGS,
+ PyWindow_items_doc},
+ {"prnt", (PyCFunction)PyWindow_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_prnt_doc},
+ {"command", (PyCFunction)PyWindow_command, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_command_doc},
+ {"item_add", (PyCFunction)PyWindow_item_add, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_add_doc},
+ {"item_remove", (PyCFunction)PyWindow_item_remove, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_remove_doc},
+ {"item_destroy", (PyCFunction)PyWindow_item_destroy, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_destroy_doc},
+ {"item_prev", (PyCFunction)PyWindow_item_prev, METH_NOARGS,
+ PyWindow_item_prev_doc},
+ {"item_next", (PyCFunction)PyWindow_item_next, METH_NOARGS,
+ PyWindow_item_next_doc},
+ {"destroy", (PyCFunction)PyWindow_destroy, METH_NOARGS,
+ PyWindow_destroy_doc},
+ {"set_active", (PyCFunction)PyWindow_set_active, METH_NOARGS,
+ PyWindow_set_active_doc},
+ {"change_server", (PyCFunction)PyWindow_change_server, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_change_server_doc},
+ {"set_refnum", (PyCFunction)PyWindow_set_refnum, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_refnum_doc},
+ {"set_name", (PyCFunction)PyWindow_set_name, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_name_doc},
+ {"set_history", (PyCFunction)PyWindow_set_history, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_history_doc},
+ {"set_level", (PyCFunction)PyWindow_set_level, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_level_doc},
+ {"activity", (PyCFunction)PyWindow_activity, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_activity_doc},
+ {"get_active_name", (PyCFunction)PyWindow_get_active_name, METH_NOARGS,
+ PyWindow_get_active_name_doc},
+ {"item_find", (PyCFunction)PyWindow_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_find_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyWindowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Window", /*tp_name*/
+ sizeof(PyWindow), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyWindow_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyWindow objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyWindow_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyWindow_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyWindow_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pywindow_new(void *win)
+{
+ PyWindow *pywindow;
+
+ pywindow = py_inst(PyWindow, PyWindowType);
+ if (!pywindow)
+ return NULL;
+
+ pywindow->data = win;
+ pywindow->cleanup_installed = 1;
+ signal_add_last_data("window destroyed", window_cleanup, pywindow);
+
+ return (PyObject *)pywindow;
+}
+
+int window_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyWindowType) < 0)
+ return 0;
+
+ Py_INCREF(&PyWindowType);
+ PyModule_AddObject(py_module, "Window", (PyObject *)&PyWindowType);
+
+ return 1;
+}
diff --git a/src/objects/window-object.h b/src/objects/window-object.h
new file mode 100644
index 0000000..b9860af
--- /dev/null
+++ b/src/objects/window-object.h
@@ -0,0 +1,21 @@
+#ifndef _WINDOW_OBJECT_H_
+#define _WINDOW_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _WINDOW_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _WINDOW_REC)
+} PyWindow;
+
+extern PyTypeObject PyWindowType;
+
+int window_object_init(void);
+PyObject *pywindow_new(void *win);
+#define pywindow_check(op) PyObject_TypeCheck(op, &PyWindowType)
+
+#endif