1/9
167
CHANGELOG.mkdn
|
@ -1,167 +0,0 @@
|
|||
Pesterchum 3.41
|
||||
===============
|
||||
|
||||
Visit http://nova.xzibition.com/~illuminatedwax/help.html for tutorial.
|
||||
|
||||
**Stable**: Visit https://github.com/illuminatedwax/pesterchum for git access and source code.
|
||||
|
||||
**Bleeding Edge**: Visit https://github.com/kiooeht/pesterchum for git access and source code.
|
||||
|
||||
**Mac**: For Mac specific git access and source code visit https://github.com/Lexicality/pesterchum.
|
||||
|
||||
(Note: Bleeding Edge is up-to-date with all Mac specific changes.)
|
||||
|
||||
CHANGELOG
|
||||
---------
|
||||
## 3.41.4
|
||||
* Makefile for Linux installing - Kiooeht [evacipatedBox]
|
||||
* Recognize www. as link - Kiooeht [evacipatedBox]
|
||||
* Pester menu option to just pester a handle - Kiooeht [evacipatedBox]
|
||||
* Update to randomEncounter interface - Kiooeht [evacipatedBox]
|
||||
* Italics, bold, and underline - Kiooeht [evacipatedBox]
|
||||
* FTP and Magnet links - oakwhiz
|
||||
* Userlist search - oakwhiz
|
||||
* Chanserv in menus - Cerxi [binaryCabalist]
|
||||
* Lua quirks
|
||||
* Multi-select memo chooser - [alGore]
|
||||
* Auto-identify with NickServ - Kiooeht [evacipatedBox]
|
||||
* Auto-join memos - Kiooeht [evacipatedBox]
|
||||
* Bug fixes
|
||||
* Don't require pygame (it's kind of optional, you just don't get sound) - Kiooeht [evacipatedBox]
|
||||
* Allow add chum dialog to open after adding an existing chum - Kiooeht [evacipatedBox]
|
||||
* Unicode everything - ghostDunk
|
||||
* Delete groups when using online numbers - Kiooeht [evacipatedBox]
|
||||
* Add chums when using manual sorting - Kiooeht [evacipatedBox]
|
||||
* Memo case insensitive for userlist and modes - Kiooeht [evacipatedBox]
|
||||
* Move hidden chums when deleting group - Kiooeht [evacipatedBox]
|
||||
* Don't allow rename groups with parenthesis - Kiooeht [evacipatedBox]
|
||||
* Wrap long error messages - Kiooeht [evacipatedBox]
|
||||
* chdir into quirks folder for Lua quirks - [alGore]
|
||||
* Toast notifications don't require sound to be on - Kiooeht [evacipatedBox]
|
||||
* Don't close Pesterchum if a window is closed while main window minimized - Kiooeht [evacipatedBox]
|
||||
|
||||
|
||||
### 3.41.3
|
||||
* Add group option when adding chum - ghostDunk
|
||||
* OOC Mode - ghostDunk
|
||||
* Improve animated gifs - ghostDunk
|
||||
* Set IRC away on idle - Kiooeht [evacipatedBox]
|
||||
* Remote quirk shutoff in memos - Kiooeht [evacipatedBox]
|
||||
* Compress exit dumps into one line - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Display channel mode change message - Kiooeht [evacipatedBox]
|
||||
* Disable quirks in +c memos - Lexi [lexicalNuance]
|
||||
* Founder, admin, and halfop support - Kiooeht [evacipatedBox]
|
||||
* Button for direct access to logs directory - Kiooeht [evacipatedBox]
|
||||
* Auto-update from zip and tar - Kiooeht [evacipatedBox]
|
||||
* Minimizable memo userlist - Kiooeht [evacipatedBox] (Idea: [alGore], [lostGash])
|
||||
* Chumroll notifications on chum sign-in/out - Kiooeht [evacipatedBox]
|
||||
* Chum notes - Kiooeht [evacipatedBox]
|
||||
* Customizable name alerts - Kiooeht [evacipatedBox]
|
||||
* Update bug reporter - Kiooeht [evacipatedBox]
|
||||
* Explain why a chumhandle is invalid - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Netsplit notification in memos - Kiooeht [evacipatedBox]
|
||||
* Toast Notifications - Kiooeht [evacipatedBox]
|
||||
* Disable randomEncounter options when it's offline - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Sort list of memos alphabetically or by number of users - Kiooeht [evacipatedBox] (Idea: [lostGash])
|
||||
* Low-bandwidth mode - Kiooeht [evacipatedBox] (Idea: [canLover])
|
||||
* New smilies - Kiooeht [evacipatedBox]
|
||||
* Refresh theme in options - Kiooeht [evacipatedBox]
|
||||
* Separate tabbed/untabbed windows for conversaions and memos - Kiooeht [evacipatedBox]
|
||||
* Manually rearrange chumroll - Kiooeht [evacipatedBox] (Idea: [turntableAbbess (aka. TA of SGRILL)])
|
||||
* Using user data directory for all OSs - Kiooeht [evacipatedBox]
|
||||
* Lots more user created themes - ghostDunk
|
||||
* Bug fixes
|
||||
* Don't delete random chum when blocking someone not on chumroll - Kiooeht [evacipatedBox]
|
||||
* Openning global userlist doesn't reset OP status of memo users - Kiooeht [evacipatedBox]
|
||||
* Alt characters don't break on random replace - Kiooeht [evacipatedBox]
|
||||
* Trollian 2.5 tray icon is now Trollian icon - Kiooeht [evacipatedBox]
|
||||
* Don't screw up <c> tags with the mispeller - Kiooeht [evacipatedBox]
|
||||
* Don't break if profile uses non-existant theme - Kiooeht [evacipatedBox]
|
||||
* Properly rearrange groups when not displaying number of online chums - Kiooeht [evacipatedBox]
|
||||
* Mac Bug fixes
|
||||
* Create all datadir stuff - Lexi [lexicalNuance]
|
||||
|
||||
### 3.41
|
||||
* Individually turn quirks on and off - Kiooeht [evacipatedBox]
|
||||
* More canon trollian theme timeline indicators - [binaryCabalist]
|
||||
* By mood chum sorting - Kiooeht [evacipatedBox]
|
||||
* Chum groups - Kiooeht [evacipatedBox]
|
||||
* Turn logging on and off - Kiooeht [evacipatedBox]
|
||||
* Customizable idle time - Kiooeht [evacipatedBox]
|
||||
* Different sound for memos - Kiooeht [evacipatedBox]
|
||||
* Animated smilies - Kiooeht [evacipatedBox]
|
||||
* Delete profiles - Kiooeht [evacipatedBox]
|
||||
* Customize minimize and close button actions - Kiooeht [evacipatedBox]
|
||||
* Receive notices from services you're talking to - Kiooeht [evacipatedBox]
|
||||
* Automatically turn off quirks when talking to bots - Kiooeht [evacipatedBox]
|
||||
* Rearrange options menu, make tabbed - Kiooeht [evacipatedBox]
|
||||
* Rearrange memos window for readability - Kiooeht [evacipatedBox]
|
||||
* Give voice to memo users - Kiooeht [evacipatedBox]
|
||||
* Theme checking - Kiooeht [evacipatedBox]
|
||||
* Display (De)OP/Voice messages in memos - Kiooeht [evacipatedBox]
|
||||
* Advanced Mode: Alter IRC user mode - Kiooeht [evacipatedBox]
|
||||
* Logviewer chum search - Kiooeht [evacipatedBox]
|
||||
* Logviewer log search - Kiooeht [evacipatedBox]
|
||||
* Set server and port from command line - Kiooeht [evacipatedBox]
|
||||
* Invite-only memos, invite chums to memos - Kiooeht [evacipatedBox]
|
||||
* Check Pyqt4 and pygame are installed and correct versions - Kiooeht [evacipatedBox]
|
||||
* Advanced Mode: View memo (channel) modes - Kiooeht [evacipatedBox]
|
||||
* Quirk groups - Kiooeht [evacipatedBox]
|
||||
* CTCP Version reply - Kiooeht [evacipatedBox]
|
||||
* Check for Pesterchum updates - Kiooeht [evacipatedBox]
|
||||
* Memo OP options: Secret, Invite-only, Mute - Kiooeht [evacipatedBox]
|
||||
* Notify user if channel blocks message - Kiooeht [evacipatedBox]
|
||||
* Bug reporter - Kiooeht [evacipatedBox]
|
||||
* Python quirks (users can create own quirk functions) - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Incorporate support for the new randomEncounter - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Only GETMOOD for people online (less spam!) - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Quirk tester in quirk window - Kiooeht [evacipatedBox] (Idea: [alGore])
|
||||
* Show and support giving kick reasons - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Make adding quirks into multi-page wizard - Kiooeht [evacipatedBox]
|
||||
* Flash the taskbar on new messages - Kiooeht [evacipatedBox]
|
||||
* Third beep sound for when your initials are mentioned in memos - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* Ctrl + click to copy links - Kiooeht [evacipatedBox]
|
||||
* Say something when server is full - Kiooeht [evacipatedBox]
|
||||
* Ping server if no ping from server to test connection - Kiooeht [evacipatedBox] (Idea: Lexi [lexicalNuance])
|
||||
* MSPA comic update notifier - Kiooeht [evacipatedBox]
|
||||
* Volume control - Kiooeht [evacipatedBox]
|
||||
* Debug mode - illuminatedwax [ghostDunk]
|
||||
* Bug fixes
|
||||
* Logviewer updates - Kiooeht [evacipatedBox]
|
||||
* Memo scrollbar thing - Kiooeht [evacipatedBox]
|
||||
* Time arrows in enamel - Kiooeht [evacipatedBox]
|
||||
* Quirk order actually works - Kiooeht [evacipatedBox]
|
||||
* Stay in memos on profile switch - Kiooeht [evacipatedBox]
|
||||
* Auto rejoin memos on reconnect - Kiooeht [evacipatedBox]
|
||||
* De-Op in memos correctly - Kiooeht [evacipatedBox]
|
||||
* Don't blow up if someone's not using Pesterchum in a memo - Kiooeht [evacipatedBox]
|
||||
* Make 'logs' and 'profiles' directories if non-existant - Kiooeht [evacipatedBox]
|
||||
* Don't split messages in bad places - Kiooeht [evacipatedBox]
|
||||
* Chumhandles must match EXACTLY to register mood changes - Kiooeht [evacipatedBox]
|
||||
* Menu bar text colour correct when default system colour isn't black - Kiooeht [evacipatedBox]
|
||||
* End all colour tags and restart them on split messages - Kiooeht [evacipatedBox]
|
||||
* Chat input box right-click menus - Kiooeht [evacipatedBox]
|
||||
* Don't overflow random colours into colourless messages - Kiooeht [evacipatedBox]
|
||||
* Only open links on left click - Kiooeht [evacipatedBox]
|
||||
|
||||
### 3.14.1
|
||||
* Pesterchum 3.14 - illuminatedwax [ghostDunk]
|
||||
* Art - Grimlive [aquaMarinist]
|
||||
* Quirks lower() function - Kiooeht [evacipatedBox]
|
||||
* Quirks scrabble() function - Kiooeht [evacipatedBox]
|
||||
* Quirks reverse() function - illuminatedwax [ghostDunk]
|
||||
* Timestamps - Kiooeht [evacipatedBox]
|
||||
* Logviewer - Kiooeht [evacipatedBox]
|
||||
* Quirk ordering - [alGore]
|
||||
* # of users in a memo - [alGore]
|
||||
* @links to users - illuminatedwax [ghostDunk]
|
||||
* Support for REPORT and ALT to calSprite built in - illuminatedwax [ghostDunk]
|
||||
* Bug fixes:
|
||||
* mixer bug fixed
|
||||
* "flags" bug fixed
|
||||
* incorrect characters in memos no longer break log file names
|
||||
* memos now do not break on case-sensitivity
|
||||
* fixed QDB address
|
||||
* now lines too long to send in a single message are split up correctly
|
||||
* quirk replace bug fixed
|
||||
* pesterClientXXX profiles no longer saved
|
28
INSTALL
|
@ -1,28 +0,0 @@
|
|||
There is no installing.
|
||||
|
||||
Basically this is a mishmosh of source code that will run by invoking:
|
||||
|
||||
python pesterchum.py
|
||||
|
||||
You will need these libraries:
|
||||
PyQt >= 4.6 (implies Qt 4.6!)
|
||||
pygame
|
||||
|
||||
The setup files are all broken. If you are building on Mac, Archaemic
|
||||
has been kind enough to give us a head start in the setup-py2app.py
|
||||
and py2app.sh files. Check out the MACBUILD file for his comments.
|
||||
|
||||
If you are building on Windows, the setup.py file has some
|
||||
commented-out lines that should give you a clue as to what you need to
|
||||
do. Talk to me if you reeeeally want to build on Windows.
|
||||
|
||||
On Linux, you need to have the PyQt4 and pygame libraries installed:
|
||||
|
||||
Debian: apt-get install python-qt4 python-pgame
|
||||
Arch: pacman -S pyqt4 python-pygame
|
||||
|
||||
then run ./pesterchum (basically a shell script that runs python pesterchum.py)
|
||||
|
||||
The point of all this is that the only person besides myself that I
|
||||
expect to create builds are the awesome people that volunteer to help
|
||||
me build on a Mac.
|
687
LICENSE
|
@ -1,687 +0,0 @@
|
|||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
1151
MacBuild/GPL.res
|
@ -1,18 +0,0 @@
|
|||
Don't.
|
||||
|
||||
If you still want to, here's my terse step by step guide. I only know that this works in Snow Leopard on my mac. It might not work for you.
|
||||
1) Get python 2.6 from http://www.python.org/ftp/python/2.5.6/Python-2.5.6.tgz
|
||||
2) Compile it, using configure_python.sh in this directory. It expects to be placed one directory up from python, simply because I compiled everything from ~/python_compile/
|
||||
3) export PATH=/Library/Frameworks/Python.framework/Versions/2.6/bin/:$PATH for both you and root. Confirm with `which python`
|
||||
4) sudo su; easy_install pygame; easy_install py2app
|
||||
5) Get the QT library from http://qt.nokia.com/downloads/qt-for-open-source-cpp-development-on-mac-os-x/. Do not even consider
|
||||
trying to compile this whore of a library yourself, it's not worth it. Get the precompiled for your system. Install it.
|
||||
6) Get SIP from http://www.riverbankcomputing.co.uk/software/sip/download and compile it, making sure to use configure_sip.sh.
|
||||
7) Get PyQt4 from http://www.riverbankcomputing.co.uk/software/pyqt/download and compile it, also with my configure script (configure_pyqt4.sh). This will take ages.
|
||||
8) ./py2app.sh and wait for it to build everything and make itself into a purdy DMG
|
||||
9) Rejoice that you didn't have to spend two weeks compiling hundreds of versions of all things mentioned here, including an entire day simply waiting for QT to compile by
|
||||
itself on the offchance that this would make it work.
|
||||
|
||||
If you want to make a nicer looking dmg file, play with dmg_background.png
|
||||
|
||||
~Lex
|
|
@ -1,20 +0,0 @@
|
|||
#!/bin/bash
|
||||
echo "Lexi's lazy configuration. This is not an official configure script. Press enter to confirm this."
|
||||
read confirm
|
||||
if [ ! -d pyqt4 ]
|
||||
then
|
||||
echo "Rename your pyqt4 folder to pyqt4."
|
||||
exit 1
|
||||
fi
|
||||
cd pyqt4
|
||||
if [ -e makefile ]
|
||||
then
|
||||
make clean
|
||||
fi
|
||||
python configure.py \
|
||||
--confirm-license \
|
||||
--use-arch=i386 \
|
||||
--use-arch=x86_64 \
|
||||
--destdir=/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages \
|
||||
--verbose
|
||||
echo "---~ Done ~---"
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Lexi's ./configure script
|
||||
#
|
||||
echo "Lexi's lazy configuration. This is not an official configure script. Press enter to confirm this."
|
||||
read confirm
|
||||
if [ ! -d python ]
|
||||
then
|
||||
echo "Rename your python folder to python."
|
||||
exit 1
|
||||
fi
|
||||
cd python
|
||||
if [ ! -d python ]
|
||||
then
|
||||
echo "Rename your python folder to python."
|
||||
exit 1
|
||||
fi
|
||||
if [ -e makefile ]
|
||||
then
|
||||
make clean
|
||||
fi
|
||||
./configure --enable-framework --enable-universalsdk=/ --with-universal-archs=intel MACOSX_DEPLOYMENT_TARGET=10.5
|
||||
echo "---~ Done ~---"
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Lexi's Configurations
|
||||
#
|
||||
|
||||
echo "Lexi's lazy configuration. This is not an official configure script. Press enter to confirm this."
|
||||
read confirm
|
||||
if [ ! -d sip ]
|
||||
then
|
||||
echo "Rename your sip folder to sip."
|
||||
exit 1
|
||||
fi
|
||||
cd sip
|
||||
if [ -e makefile ]
|
||||
then
|
||||
make clean
|
||||
fi
|
||||
python configure.py --arch=i386 --arch=x86_64 \
|
||||
--universal --deployment-target=10.5 \
|
||||
--destdir=/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/
|
||||
echo "---~ Done ~---"
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Lex's gif->png script
|
||||
|
||||
for file in *.gif
|
||||
do
|
||||
convert ${file} ${file:0:$((${#file}-3))}"png"
|
||||
done
|
||||
|
||||
for file in `ls | grep -G -e "-[1-9]\+.png"`
|
||||
do
|
||||
rm $file
|
||||
done
|
||||
|
||||
for file in `ls | grep -e "-0.png"`
|
||||
do
|
||||
newfile=`echo $file | sed -e 's|\(.*\)-0.png|\1.png|'`
|
||||
mv $file $newfile
|
||||
done
|
||||
|
||||
# for file in *.gif
|
||||
# do
|
||||
# rm ${file:0:$((${#file}-3))}"png"
|
||||
# done
|
Before Width: | Height: | Size: 13 KiB |
36
Makefile
|
@ -1,36 +0,0 @@
|
|||
PYTHON2_CMD ?= /usr/bin/python
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LIBINSTALLDIR ?= /lib
|
||||
XDGCONFDIR ?= /etc/xdg
|
||||
|
||||
PESTERCHUMBINDIR = ${DESTDIR}${PREFIX}/bin
|
||||
PESTERCHUMLIBDIR = $(DESTDIR)$(PREFIX)$(LIBINSTALLDIR)/pesterchum
|
||||
|
||||
all:
|
||||
@echo "Ready to install..."
|
||||
|
||||
make-install-dirs:
|
||||
mkdir -p ${PESTERCHUMBINDIR}
|
||||
mkdir -p ${PESTERCHUMLIBDIR}
|
||||
mkdir -p ${PESTERCHUMLIBDIR}/libs
|
||||
mkdir -p ${PESTERCHUMLIBDIR}/oyoyo
|
||||
mkdir -p ${PESTERCHUMLIBDIR}/quirks
|
||||
mkdir -p ${PESTERCHUMLIBDIR}/smilies
|
||||
mkdir -p ${PESTERCHUMLIBDIR}/themes
|
||||
|
||||
uninstall:
|
||||
rm -f ${PESTERCHUMBINDIR}/pesterchum
|
||||
rm -rf ${PESTERCHUMLIBDIR}
|
||||
|
||||
install: make-install-dirs
|
||||
install -m 644 *.py ${PESTERCHUMLIBDIR}
|
||||
install -m 644 libs/*.py ${PESTERCHUMLIBDIR}/libs
|
||||
install -m 644 oyoyo/*.py ${PESTERCHUMLIBDIR}/oyoyo
|
||||
install -m 644 quirks/*.py ${PESTERCHUMLIBDIR}/quirks
|
||||
install -m 644 smilies/* ${PESTERCHUMLIBDIR}/smilies
|
||||
cp -r themes/* ${PESTERCHUMLIBDIR}/themes
|
||||
@echo '#!/bin/sh' > ${PESTERCHUMBINDIR}/pesterchum
|
||||
@echo 'cd ${PREFIX}$(LIBINSTALLDIR)/pesterchum' >> ${PESTERCHUMBINDIR}/pesterchum
|
||||
@echo 'python2 pesterchum.py' >> ${PESTERCHUMBINDIR}/pesterchum
|
||||
chmod +x ${PESTERCHUMBINDIR}/pesterchum
|
BIN
PCskins.png
Before Width: | Height: | Size: 483 KiB |
|
@ -1,88 +0,0 @@
|
|||
Python Quirk Functions
|
||||
===============
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
1. Introduction
|
||||
2. Create A Module
|
||||
3. Functions In A Module
|
||||
4. Command Requirements
|
||||
5. Completed Quirk Function
|
||||
|
||||
Introduction
|
||||
---------------
|
||||
Over the course of this short tutorial you will learn:
|
||||
|
||||
* How to create your own Quirk Functions
|
||||
* VERY basic Python syntax
|
||||
|
||||
You will not learn:
|
||||
|
||||
* How to write Python
|
||||
* How to bake a cake
|
||||
|
||||
Throughout this tutorial there will be
|
||||
<pre>
|
||||
Instructions in special boxes.
|
||||
If you follow the instructions in these boxes, by the end of this tutorial
|
||||
you will have recreated the default reverse() Quirk Function.
|
||||
</pre>
|
||||
|
||||
Create A Module
|
||||
-------------------
|
||||
All Quirk Function Modules should be created in the 'quirks/' directory. File names <b>must</b> end in '.py'.
|
||||
You can have multiple Quirk Functions per Module.
|
||||
|
||||
Each Module can also have a 'setup' function which will be called once, the moment the Module is loaded.
|
||||
|
||||
<pre>
|
||||
Create 'reverse.py' in the 'quirks/' directory.
|
||||
</pre>
|
||||
|
||||
Functions In A Module
|
||||
--------------------------
|
||||
If you've ever done programming before, you should know what a function is. If not, I suggest picking up a good programming book (or e-book).
|
||||
|
||||
In Python, function syntax looks like this:
|
||||
|
||||
def function_name(myvar1, myvar2):
|
||||
|
||||
'def' is used to declare that this is a function, and 'function_name' is obviously the name of your function.
|
||||
'myvar1' and 'myvar2' are variables coming into your function. For most of your functions, the only variable being passed will be 'text'.
|
||||
|
||||
In Python, curly braces ({}) are not used to declare the beginning and end of a function. Instead, a colon (:) is used to declare the beginning of a function. After that, indentation is used to declare the body and end of a function.
|
||||
|
||||
```python
|
||||
def reverserep(text):
|
||||
return text[::-1]
|
||||
```
|
||||
|
||||
Command Requirements
|
||||
------------------------
|
||||
For a function to be registered as a Quirk Function, it must conform to three simple rules:
|
||||
|
||||
1. It must have a command name.
|
||||
2. It must take exactly one arguement.
|
||||
3. It must return a string.
|
||||
|
||||
What is meant by having a command name, is that a name for the Quirk Function has to be defined. This is done by defining the 'command' variable for a function.
|
||||
|
||||
function_name.command = "name"
|
||||
|
||||
```python
|
||||
reverserep.command = "reverse"
|
||||
```
|
||||
|
||||
Completed Quirk Function
|
||||
---------------------------
|
||||
Below is the completed, fully working, reverse Quirk Function. After it I will break down the pieces of each line.
|
||||
|
||||
```python
|
||||
def reverserep(text):
|
||||
return text[::-1]
|
||||
reverserep.command = "reverse"
|
||||
```
|
||||
|
||||
As stated before, to start a function, you need to use the keyword 'def'. All Quirk Functions must take exactly one argument (in this case 'text').
|
||||
In this example, the text is reversed and returned all in one line. 'text[::-1]' is the Pythonic way of reversing a list or string.
|
||||
The last line is the most important part. This tells Pesterchum to call this function whenever 'reverse()' is used in a quirk.
|
BIN
Pesterchum.png
Before Width: | Height: | Size: 7.8 KiB |
|
@ -1,184 +0,0 @@
|
|||
Welcome to Pesterchum 3.41!
|
||||
=============================
|
||||
|
||||
## FOR NEW USERS
|
||||
This modification of Pesterchum is intended for people who are already familiar
|
||||
with using the base client.
|
||||
|
||||
If you aren't, please check the standard build's [documentation][pchum-doc].
|
||||
|
||||
[pchum-orig]: https://github.com/illuminatedwax/pesterchum
|
||||
[pchum-doc]: https://github.com/illuminatedwax/pesterchum/blob/master/README.mkdn
|
||||
|
||||
## FOR EVERYONE ELSE
|
||||
Greetings! This is a modification of Pesterchum, made because Pesterchum is
|
||||
effectively no longer maintained, that is intended to fix a number of issues.
|
||||
|
||||
The code used as a base is a newer version of Pesterchum than the one in
|
||||
circulation, and thus has plenty of useful features that can be found in the
|
||||
[CHANGELOG][changes].
|
||||
|
||||
In addition, there are other features and improvements that have been made, and
|
||||
there are many more planned. Check the [TODO list][todo-done] to see what's
|
||||
been fixed, as well as what's [planned][todo-upcoming].
|
||||
|
||||
[changes]: https://github.com/karxi/pesterchum/blob/master/CHANGELOG.mkdn
|
||||
[todo-done]: https://github.com/karxi/pesterchum/blob/master/TODO.mkdn#tododone
|
||||
[todo-upcoming]: https://github.com/karxi/pesterchum/blob/master/TODO.mkdn#features
|
||||
|
||||
### Installation
|
||||
There isn't an automated installer yet, but the steps involved aren't all that
|
||||
difficult.
|
||||
|
||||
#### Pitfalls
|
||||
* If you're on a 64-bit system (and most are these days), use those links. You
|
||||
can also use the 32 bit versions, if you choose, but you *cannot* mix them.
|
||||
* If you're on a 32-bit system but use the 64-bit installers, you'll get an
|
||||
error along the lines of "not a valid Win32 application", or the
|
||||
installer will simply fail.
|
||||
* This version of Pesterchum does not come compiled as an executable, and thus
|
||||
does not have Python/PyQt4 packaged with it. Those are necessary for
|
||||
Pesterchum to run, so you have to install them manually.
|
||||
|
||||
#### First-Time Install
|
||||
Download links are for Windows, but a quick Google search will find everything
|
||||
necessary.
|
||||
|
||||
1. Install **Python 2.7** or higher if you don't already have it:
|
||||
* [32 bit][python2-32]
|
||||
* [64 bit][python2-64]
|
||||
|
||||
Be **sure** to add Python to the PATH if asked to do so during
|
||||
installation. It means Python will be usable from the console,
|
||||
which is necessary for this to run.
|
||||
|
||||
2. Install **PyQt4**:
|
||||
* [32 bit][pyqt4-32]
|
||||
* [64 bit][pyqt4-64]
|
||||
|
||||
3. **(LINUX)** Install **pygame**:
|
||||
* [pygame download page][pygame-dl]
|
||||
* You don't need to install this if you're using Windows. Linux users need
|
||||
to install it to enable sound, but it will otherwise work without it.
|
||||
* If you want to download this, you should probably do so using your native
|
||||
package manager.
|
||||
|
||||
4. Download **Pesterchum**:
|
||||
* [Download from main branch][pchum-zip]
|
||||
|
||||
5. Unzip Pesterchum somewhere easily-accessible.
|
||||
|
||||
6. **If you have any custom themes**, copy/paste them into the 'themes' folder.
|
||||
You can find this in `%LOCALAPPDATA%\pesterchum`, which is the same as
|
||||
`%APPDATA%\..\Local\pesterchum`. Copy/paste one into Explorer's address bar
|
||||
and you'll end up where you need to be.
|
||||
|
||||
Oftentimes the 'themes' folder doesn't exist in Pesterchum's user-specific
|
||||
config folder, so you'll have to make it and copy the custom themes into
|
||||
it.
|
||||
|
||||
__If, for some reason, that doesn't work:__
|
||||
|
||||
You can also copy the missing themes into the 'themes' folder of the version
|
||||
you just unzipped. **Don't overwrite any files** if you do this - the themes
|
||||
used by this have been updated, and the older default themes may break when
|
||||
used.
|
||||
|
||||
7. Run Pesterchum! How you do this depends on the OS:
|
||||
* **(WINDOWS)** Run `w32-run-pchum.bat`.
|
||||
* **(LINUX)** Run `./pesterchum`, preferably via terminal.
|
||||
* Note that this is made to provide debugging information - so that if
|
||||
errors come up, they can be reported to me, and I can fix them.
|
||||
|
||||
[python2-32]: https://www.python.org/ftp/python/2.7.12/python-2.7.12.msi
|
||||
[python2-64]: https://www.python.org/ftp/python/2.7.12/python-2.7.12.amd64.msi
|
||||
[pyqt4-32]: http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe
|
||||
[pyqt4-64]: http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x64.exe
|
||||
[pygame-dl]: http://www.pygame.org/download.shtml
|
||||
[pchum-zip]: https://github.com/karxi/pesterchum/archive/master.zip
|
||||
|
||||
#### Upgrading
|
||||
**NOTE: This only applies to those who already have this patched Pesterchum
|
||||
installed.**
|
||||
**DO NOT extract this into a folder containing pesterchum.exe, because it WILL
|
||||
break.** Read up to **First-Time Install** if you're installing this version
|
||||
for the first time.
|
||||
Otherwise...
|
||||
|
||||
Just re-download the [Pesterchum zip][pchum-zip] and extract it over your old
|
||||
installation, replacing everything that was already there. That's all there is
|
||||
to it!
|
||||
|
||||
#### Having Problems?
|
||||
I can't offer much help in this regard; if you're getting errors, feel free to
|
||||
try to contact me, but if you're having trouble with the installers, there's
|
||||
little I can do. [How to install Python][howtogetpython] might help; failing
|
||||
that, Google saves lives.
|
||||
|
||||
[howtogetpython]: http://www.howtogeek.com/197947/how-to-install-python-on-windows/
|
||||
|
||||
|
||||
|
||||
SMILIES
|
||||
-------
|
||||
None of the smilies have changed or been added, yet; this list is kept for
|
||||
posterity and easy reference.
|
||||
|
||||
* `:rancorous:`
|
||||
* `:apple:`
|
||||
* `:bathearst:`
|
||||
* `:cathearst:`
|
||||
* `:woeful:`
|
||||
* `:pleasant:`
|
||||
* `:blueghost:`
|
||||
* `:slimer:`
|
||||
* `:candycorn:`
|
||||
* `:cheer:`
|
||||
* `:duhjohn:`
|
||||
* `:datrump:`
|
||||
* `:facepalm:`
|
||||
* `:bonk:`
|
||||
* `:mspa:`
|
||||
* `:gun:`
|
||||
* `:cal:`
|
||||
* `:amazedfirman:`
|
||||
* `:amazed:`
|
||||
* `:chummy:`
|
||||
* `:cool:`
|
||||
* `:smooth:`
|
||||
* `:distraughtfirman:`
|
||||
* `:distraught:`
|
||||
* `:insolent:`
|
||||
* `:bemused:`
|
||||
* `:3:`
|
||||
* `:mystified:`
|
||||
* `:pranky:`
|
||||
* `:tense:`
|
||||
* `:record:`
|
||||
* `:squiddle:`
|
||||
* `:tab:`
|
||||
* `:beetip:`
|
||||
* `:flipout:`
|
||||
* `:befuddled:`
|
||||
* `:pumpkin:`
|
||||
* `:trollcool:`
|
||||
* `:jadecry:`
|
||||
* `:ecstatic:`
|
||||
* `:relaxed:`
|
||||
* `:discontent:`
|
||||
* `:devious:`
|
||||
* `:sleek:`
|
||||
* `:detestful:`
|
||||
* `:mirthful:`
|
||||
* `:manipulative:`
|
||||
* `:vigorous:`
|
||||
* `:perky:`
|
||||
* `:acceptant:`
|
||||
* `:olliesouty:`
|
||||
* `:billiards:`
|
||||
* `:billiardslarge:`
|
||||
* `:whatdidyoudo:`
|
||||
|
||||
|
||||
|
||||
[modeline]: vim:set autoindent ts=4 sts=4 sw=4 tw=79 expandtab:
|
|
@ -1,840 +0,0 @@
|
|||
Welcome to Pesterchum 3.41!
|
||||
=============================
|
||||
|
||||
WHAT'S NEW?
|
||||
-----------
|
||||
* Check out the CHANGELOG file to see what's changed!
|
||||
|
||||
What do I do now?
|
||||
-----------------
|
||||
|
||||
Most questions can be answered by visiting the forums! Go to HELP->HELP
|
||||
and you'll be transported to the proper thread!
|
||||
|
||||
Here's some tips to help you get started:
|
||||
-----------------------------------------
|
||||
|
||||
- Some themes can be confusing if you haven't used the program
|
||||
already! Some hints:
|
||||
* Trollian: Moods are set by clicking the timelines, and you
|
||||
can add chums by clicking "Chumproll." Moods correspond to the troll
|
||||
that would most likely exhibit them. You can go offline by hitting the
|
||||
"Timelines" menu bar.
|
||||
|
||||
* Gold: Add chums by hitting the two chumpeoples in the upper left
|
||||
corner. Go offline by clicking the "CHUMHANDLE:" label.
|
||||
|
||||
* Enamel: Add chums by hitting the "CHUMROLL" label. Go offline by
|
||||
clicking the upper left hand corner.
|
||||
|
||||
- Right-click is your friend! There are useful right click
|
||||
options on the chumroll, by clicking the chumhandle in a conversation,
|
||||
online userlist, or the list of memo browsers.
|
||||
|
||||
Cool features:
|
||||
--------------
|
||||
|
||||
- Profile switching. Instantly switch profiles, loading your color and
|
||||
quirks with it.
|
||||
- Theme switching and creation. So far this comes with a few official
|
||||
themes! But you can also make your own: just make a new directory in
|
||||
the themes folder with the proper images and style.js file. The
|
||||
style.js file will be documented soon, but feel free to poke at it.
|
||||
- Memos. Memos that are a lot more like the ones in the comic and
|
||||
allow you to appear at multiple times in one chat.
|
||||
- Quirks: Prefix, suffix, simple replace, regexp replace (like in
|
||||
2.5), random replacement, and an auto-mispeller :P
|
||||
- Chum groups. Organize your chums into collapsible groups for easy
|
||||
management.
|
||||
- Block/user list
|
||||
- Add/block chums directly from a conversation, the userlist, or memo
|
||||
userlist.
|
||||
- Timestamps saved in logs and shown in conversations if wanted.
|
||||
- Logging. Logs are output in bbcode (for easy forum posting), html,
|
||||
and plain text.
|
||||
- Logviewer for easy log reading inside Pesterchum
|
||||
- Idling. You can set yourself idle manually, and the computer will
|
||||
set it for you after a configurable amount of time.
|
||||
- Improved /me. Any letters immediately following /me will be
|
||||
processed correctly. e.g. /me'd rather be fishing -> `-- ghostDunk'd
|
||||
[GD'D] rather be fishing --`
|
||||
- Hyperlinks! Now if someone types http://whatever it will turn into a
|
||||
link you can just click and follow. No more copy/paste.
|
||||
- Memo links. Link your friends to your memos.
|
||||
- Smilies. We've added about 30-40 smilies from the forums. There is a
|
||||
list later on in this readme.
|
||||
- Submit quotes directly to the Pesterchum QDB!
|
||||
|
||||
FA%
|
||||
---
|
||||
__Q:__ Norton says it has a virus and then deletes it!
|
||||
__A:__ Read this helpful Norton FAQ:
|
||||
|
||||
Alright, here's a guide to by-passing Norton:
|
||||
|
||||
* First, to download Pesterchum:
|
||||
1. Make sure you're on a Moderator account. Moreso for the Norton steps than these ones.
|
||||
2. Download the .zip file, not the .exe file.
|
||||
3. Unzip the .zip file onto memory. Pesterchum should now be installed.
|
||||
|
||||
* Now, to by-pass Norton:
|
||||
1. Make sure you're still on a moderator account.
|
||||
2. Open up Norton.
|
||||
3. Click on 'Settings' up in the upperright hand corner.
|
||||
4. Click on 'Anitivirus', off to the upper left. It has a small image of a needle or something similar off to it's side.
|
||||
5. There's a word that reads 'SONAR protection' halfway to the bottomleft. Off to it's right, there's a bar that's half green. Click on the bar.
|
||||
6. It will warn you about turning off SONAR. Have it set to turn back on when the system restarts.
|
||||
7. If done properly, the background for the main page of Norton(what you saw on steps 2-3) has turned an apocaliptic red. Feel free to close Norton now. Keep in mind to stay off suspicious online sites now.
|
||||
8. Open up Pesterchum, and let the chummy convos begin.
|
||||
|
||||
* When finished:
|
||||
1. Log off of Pesterchum. LOG OFF, NOT CLOSE IT.
|
||||
2. Then, you can either shut off your comp, and Norton will re-enable SONAR, or you can repeat steps 1-5, except turning the red bar green. If done right, Norton will be it's happy color again.
|
||||
3, Keep in mind that you must repeat all of this(other than the download) every time you want to get on Pesterchum.
|
||||
|
||||
* Hope this is helpful!
|
||||
|
||||
(This guide brought to you by the slightly combined efforts of empireomega and Xanaomin)
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ I can't connect because my school/university/network/stolen wifi is blocking my connection! OR I can't seem to connect to the server at all and I'm not running any firewalls!
|
||||
__A:__ Edit your pesterchum.js file. Open it up in notepad or something, and then edit the beginning so it looks like this:
|
||||
|
||||
```
|
||||
{"port": "1413", ....
|
||||
```
|
||||
|
||||
where the .... is the rest of the gobbledygook there.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ The mood buttons on Pesterchum 6.0 don't match up to what it sets your mood to! What gives?
|
||||
__A:__ The mood names are just there to look canon. It is intentional.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ I'm appearing as offline to 2.5 users/other users appear the wrong
|
||||
mood? What's happeninggggg
|
||||
__A:__ The 2.5 people decided to change the mood protocol. When I made
|
||||
this program, I decided to go with Tinychat's original protocol (and
|
||||
extend it). So some moods will appear wrong between 2.5
|
||||
users. (\*COUGH\*tell them to switch to 3.14\*COUGH\*)
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ Pesterchum 2.5 users don't get my /me messages correctly!
|
||||
__A:__ That's because they implemented the /me command differently.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ Can we resize the main window?
|
||||
__A:__ No. This is done so we can offer more flexible UI creation. It's a
|
||||
lot easier to make themes that look canon this way.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ Can we have different chum rolls for different users?
|
||||
__A:__ No. Instead what we now have crum groups to organize people.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ Can we delete profiles?
|
||||
__A:__ Yes. Go to the profile switcher, choose a profile and press DELETE.
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
__Q:__ You should make it so you can ban specific time frames in memos.
|
||||
__A:__ This was too complicated to implement, and I don't have the UI
|
||||
quite figured out. This will probably go in a future update.
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
-------------
|
||||
|
||||
STARTING
|
||||
--------
|
||||
|
||||
If this is your first time running Pesterchum 3.14, you need to create
|
||||
a new profile. Just type in your chum handle in the box and click the
|
||||
color swatch to pick your color. Check the "default" checkbox to make
|
||||
this your default profile.
|
||||
|
||||
BASIC PESTERING
|
||||
---------------
|
||||
|
||||
To begin pestering, first click the "ADD CHUM" button and type in
|
||||
their pester handle. The handle must be all lower case except for one
|
||||
capital letter. Once you've added that person, they will appear on
|
||||
your chumroll. You can double click to begin pestering them, or
|
||||
right-click to bring up a menu where you can pester them, block them,
|
||||
or remove them from your chumroll. (Or you can select them and hit
|
||||
"enter" OR hit the "PESTER" button.)
|
||||
|
||||
Once you begin pestering somebody (or they begin pestering you), it
|
||||
will bring up the conversation window. Here you can type to your
|
||||
chum. Also remember that if you right-click on the area just above the
|
||||
Pesterlog, it will bring up a list of options: Quirks Off will turn
|
||||
your quirks off, Add Chum will add this chum to your list, and Block
|
||||
will block them. (Those last two options are useful if you are being
|
||||
pestered by someone you don't have on your list yet!)
|
||||
|
||||
While pestering your chum, here are some useful features:
|
||||
|
||||
* Type /me to create a system message. "/me facepalms." will generate:
|
||||
|
||||
```
|
||||
-- ghostDunk [GD] facepalms. --
|
||||
```
|
||||
|
||||
You can also append 's after /me like so: "/me's computer exploded."
|
||||
|
||||
```
|
||||
-- ghostDunk's [GD'S] computer exploded. --
|
||||
```
|
||||
|
||||
In fact, any characters you type after a /me before the space will
|
||||
be added: "/meing is the Ghost Nation's official pastime."
|
||||
|
||||
```
|
||||
-- ghostDunking [GDING] is the Ghost Nation's official pastime. --
|
||||
```
|
||||
|
||||
* Color tags! If you feel the need to talk about The Green Sun or add
|
||||
some appleberry blast to your conversation, just use color
|
||||
tags. These work like in TC 1.5: `<c=(color)>colored text</c>`. But in
|
||||
PC 3.14, you can type your color in a lot of different ways:
|
||||
|
||||
- You can use the familiar r,g,b method:
|
||||
`<c=0,255,0>The Green Sun</c>`
|
||||
- You can use HTML tags:
|
||||
`<c=#7f7f7f>DURR I'M KARKAT AND I'M A HUGE IDIOT</c>`
|
||||
- You can even use [plain color names](http://en.wikipedia.org/wiki/Web_colors):
|
||||
`<c=red>D4V3 TH1S 1S SO D3C4D3NT</c>`
|
||||
- You don't even have to add the `</c>` if you are lazy. Just use a
|
||||
new color tag whenever you want to change colors and PC 3.14 will
|
||||
add the extra tags for you.
|
||||
|
||||
* URLs (anything with `http://` or `www.` in front of it) will automatically be
|
||||
detected and made into a link you can CLICK.
|
||||
|
||||
* You can also link people to memos by typing "#" and the name of the
|
||||
menu like so: #R41NBOW_RUMPUS_P4RTYTOWN
|
||||
Clicking the link will open up the memo select menu.
|
||||
|
||||
* Smilies! There are a list of smilies at the end of this document;
|
||||
they are based on the MSPA Forum smilies.
|
||||
|
||||
* Don't worry about your quirks screwing up any of the above: PC will
|
||||
apply your quirks AFTER it figures out color codes, links, smilies, etc.
|
||||
|
||||
* Pressing the up arrow will cycle through a history of your comments,
|
||||
so if you want to retype something, you can pull it up.
|
||||
|
||||
* You can submit directly to the Pesterchum Quote Database! If you
|
||||
have a particualarly awesome conversation, you can submit it to the
|
||||
database by simply highlighting the good part of the conversation,
|
||||
right clicking it and choosing "Submit to Pesterchum QDB!"
|
||||
|
||||
|
||||
MEMOS
|
||||
-----
|
||||
|
||||
One of the most interesting features to make was the memos, and make
|
||||
them as close to the comic as I could without actually inventing time
|
||||
travel. So here is the TIME TUTORIAL:
|
||||
|
||||
* __Joining:__ When you go CLIENT->MEMOS, you'll see a list of memos pop up
|
||||
-- those are memos people already have open. To join one, just
|
||||
highlight one of them. If you want to make a new memo, just type it in
|
||||
the input. If you'd like to make it secret, so that it doesn't appear
|
||||
in the list, check "HIDDEN CHANNEL". Then, choose what timeframe you
|
||||
want to appear to be in. So if you wanted to be in the future, you
|
||||
could move the slider to the right. You can also enter the time
|
||||
manually. Then hit JOIN.
|
||||
|
||||
* __Explaining time:__ Time in memos, unlike Homestuck, will not be relative
|
||||
to your position. That is, if you choose 4:13 in the future, you will
|
||||
not see someone who has set their time as "current" (or "0") in the
|
||||
past: you will see them as "current" and yourself as "future." This is
|
||||
because we do not have time travel! Memo time setting is basically an
|
||||
RP mechanic: you are pretending to be from the future! It will also
|
||||
help keep everyone straight: everyone will see the same thing!
|
||||
|
||||
* __The time slider:__ The slider shows your current position in the time
|
||||
stream. If you want to change your time frame, simply move the slider
|
||||
(or type a time in) and hit GO. This will open a new time frame, and
|
||||
the next time you type a message, the memo will show that you've
|
||||
responded to it in that time frame. You can now switch between your
|
||||
time frames simply by clicking the arrows in the right hand
|
||||
corner. (THIS COMES IN HANDY IF YOU WANT TO ARGUE WITH YOURSELF.) You
|
||||
can have any number of open time frames, and the program will number
|
||||
them in the order in which you open them (like in the comic). You can
|
||||
have one of your time frames cease responding to the memo by hitting
|
||||
"CLOSE." If you open that time frame again, the program will remember
|
||||
the number it originally gave it. If you want to be mysteeeeeeeerious,
|
||||
you can type in "?" and you will appear as ???.
|
||||
|
||||
* __The memo viewer list:__ To the right is a list of people currently
|
||||
browsing the memo. A shade icon next to their name means they are the
|
||||
"operator" of the memo: meaning they can kick ("ban") people from the
|
||||
memo and make other people operators as well. A "ban" is not permanent
|
||||
(like in the comic), and the program will ask if you want to reconnect
|
||||
to the memo. You kick and op people by right clicking their name in
|
||||
the window. You can also add them to your chumroll!
|
||||
|
||||
* __Inviting people to your memo:__ You can link to a memo by simply typing
|
||||
"#nameofmemo" in any conversation or memo window. So you can say:
|
||||
|
||||
```
|
||||
CG: NOW YOU, ME, AND EGBERT NEED TO HAVE A CHAT.
|
||||
CG: CLICK IT.
|
||||
CG: #FRUITYRUMPUSASSHOLEFACTORY
|
||||
```
|
||||
|
||||
and it will appear as a link that you can click, which will open the
|
||||
memo chooser window.
|
||||
|
||||
CLIENT MENU
|
||||
-----------
|
||||
|
||||
### OPTIONS: ###
|
||||
|
||||
* __Chum List__
|
||||
* __Hide Offline Chums:__ Turning this option on will hide all offline chums
|
||||
off your chumroll.
|
||||
|
||||
* __Show Empty Groups:__ Turning this option on will show empty groups.
|
||||
|
||||
* __Show Number of Online Chums:__ Show number of online chums in each group.
|
||||
|
||||
* __Sort Chums:__ How would you like your chums sorted?
|
||||
|
||||
* __Conversations__
|
||||
* __Time Stamps:__ Turning this on will show timestamps in your chats.
|
||||
|
||||
* __12/24 hour:__ Formatting for timestamps. Whether you want them in 12 or
|
||||
24 hour time.
|
||||
|
||||
* __Show Seconds:__ Turning this on will show the seconds in your timestamps.
|
||||
|
||||
* __Show OP and Voice Messages in Memos:__ Whether or not you would like
|
||||
to see messages when people gain/lose OP or Voice.
|
||||
|
||||
* __Use animated smilies:__ To animate or not to animate.
|
||||
|
||||
* __Interface__
|
||||
* __Tabbed Conversations:__ Turns tabbed conversations on and off. Don't
|
||||
worry if you do this in the middle of a conversation, PC will save
|
||||
them for you.
|
||||
|
||||
* __Minimize:__ What do you want the minimize button to do?
|
||||
|
||||
* __Close:__ What do you want the close button to do?
|
||||
|
||||
* __Sound__
|
||||
* __Sounds On:__ Uncheck to shut it the fuck up.
|
||||
|
||||
* __Pester Sounds:__ Uncheck to only turn off Pester sounds.
|
||||
|
||||
* __Memo Sounds:__ Uncheck to only turn off Memo sounds.
|
||||
|
||||
* __Memo Mentions:__ Check to have a separate noise when your initials
|
||||
get mentioned in a memo.
|
||||
|
||||
* __Logging__
|
||||
* __Log all Pesters:__ Log one-on-one chats.
|
||||
|
||||
* __Log all Memos:__ Log everything said in memos.
|
||||
|
||||
* __Log Time Stamps for Pesters__
|
||||
|
||||
* __Log Time Stamps for Memos__
|
||||
|
||||
* __Idle/Updates__
|
||||
* __Minutes before Idle:__ How long before you should be considered idle.
|
||||
|
||||
* __Check for Pesterchum Updates:__ How often to check for updates
|
||||
to Pesterchum.
|
||||
|
||||
* __Check for MSPA Updates:__ Check the MSPA site for updates to comics.
|
||||
|
||||
* __Theme__
|
||||
* __Pick a Theme__
|
||||
|
||||
### MEMOS: ###
|
||||
|
||||
Opens the Memo list as above.
|
||||
|
||||
### USERLIST: ###
|
||||
|
||||
Shows a list of all the users that are currently logged onto
|
||||
Pesterchum. Right-click their names and select "ADD CHUM" to add them
|
||||
to your chum roll!
|
||||
|
||||
### IDLE: ###
|
||||
|
||||
Make yourself an idle chum. You will appear as idle until you
|
||||
uncheck this box, or if you *actually* go idle (stop using the
|
||||
computer) for 10 minutes and then come back.
|
||||
|
||||
### IMPORT: ###
|
||||
|
||||
Imports your old Pesterchum 2.0, 2.5 and Tinychum chum
|
||||
rolls. This will also import your old quirks from Pesterchum 2.5.
|
||||
|
||||
### RECONNECT: ###
|
||||
|
||||
Forces PC to reconnect to the server.
|
||||
|
||||
### EXIT: ###
|
||||
|
||||
noooooooooooooooooooooooo
|
||||
|
||||
|
||||
PROFILE MENU
|
||||
------------
|
||||
|
||||
### QUIRKS: ###
|
||||
|
||||
Opens the quirks menu. More on that below!
|
||||
|
||||
### TROLLSLUM: ###
|
||||
|
||||
Opens up the window where you can view people you've
|
||||
blocked. You can add and remove people to the list from here as well.
|
||||
|
||||
### COLOR: ####
|
||||
|
||||
Change your text color here!
|
||||
|
||||
### SWITCH: ###
|
||||
|
||||
Switch your profile! You can have any number of profiles, and
|
||||
PC will save your color, quirks, and theme for that profile. Chumrolls
|
||||
and block lists are the same for all profiles. Feel free to have
|
||||
multiple instances of PC running on two or more handles!
|
||||
|
||||
|
||||
HELP MENU
|
||||
---------
|
||||
|
||||
### HELP: ###
|
||||
|
||||
Get taken to a handy dandy tutorial for Pesterchum!
|
||||
|
||||
### CALSPRITE: ###
|
||||
|
||||
Open a chat with calSprite (learn more about calSprite below).
|
||||
|
||||
### NICKSERV: ###
|
||||
|
||||
Open a chat with NickServ. If you don't know what NickServ is, you don't need to.
|
||||
|
||||
### ABOUT: ###
|
||||
|
||||
See which version of Pesterchum you have. Learn about all the awesome people
|
||||
that helped bring Pesterchum 3.14 to you!
|
||||
|
||||
### REPORT BUG: ###
|
||||
|
||||
Report any bugs you come across so we can fix them and make Pesterchum
|
||||
even better!
|
||||
|
||||
|
||||
CALSPRITE
|
||||
---------
|
||||
|
||||
calSprite is the bot that helps moderate canon handle usage! Simply pester
|
||||
calSprite with the world "HELP" (turn your quirks off!) and you
|
||||
will get instructions on how to use calSprite!
|
||||
|
||||
|
||||
QUIRKS
|
||||
------
|
||||
|
||||
There are six kinds of quirks! I'll teach you how to use them all!
|
||||
(In this section, I will use quotes ("") around things so it's clearer
|
||||
to see exactly what to type! Don't include these quotes when using
|
||||
these examples!
|
||||
|
||||
Also, note that your quirks will not work until you save them by
|
||||
hitting "OK" on the Quirk window.
|
||||
|
||||
* __Prefix/Suffix:__
|
||||
This will put text before or after everything you
|
||||
say. So for example, we can use prefixes to emulate part of Nepeta or
|
||||
Equius' quirks:
|
||||
|
||||
```
|
||||
PREFIX: ":33 < "
|
||||
You type: "*ac twitches her friendly whiskers at ct*"
|
||||
Result:
|
||||
AC: :33 < *ac twitches her friendly whiskers at ct*
|
||||
```
|
||||
|
||||
```
|
||||
PREFIX: "D --> "
|
||||
You type: "Hi"
|
||||
Result:
|
||||
CT: D --> Hi
|
||||
```
|
||||
|
||||
Suffixes work the same way, but at the end of the message:
|
||||
|
||||
```
|
||||
SUFFIX: "!!!"
|
||||
You type: hey there
|
||||
Result:
|
||||
GD: hey there!!!
|
||||
```
|
||||
|
||||
Remember that it doesn't automatically add a space! You'll need to add
|
||||
it in (see CT and AC examples again!)
|
||||
|
||||
* __Simple Replace:__
|
||||
This will simply take a set of characters and replace them with other
|
||||
characters.
|
||||
* Let's add a quirk to our Nepeta:
|
||||
|
||||
```
|
||||
Replace: "ee" With: "33"
|
||||
You type: "*ac saunters from her dark cave a little bit sleepy from
|
||||
the recent kill*"
|
||||
Result:
|
||||
AC: :33 < *ac saunters from her dark cave a little bit sl33py from the
|
||||
recent kill*
|
||||
```
|
||||
|
||||
* Let's add two to Equius:
|
||||
|
||||
```
|
||||
Replace: "loo" With: "100"
|
||||
Replace: "x" With "%"
|
||||
You type: "look"
|
||||
Result:
|
||||
CT: D --> 100k
|
||||
```
|
||||
|
||||
```
|
||||
You type: "What are you expecting to accomplish with this"
|
||||
Result:
|
||||
CT: D --> What are you e%pecting to accomplish with this
|
||||
```
|
||||
|
||||
* Aradia:
|
||||
|
||||
```
|
||||
Replace: "o" With: "0"
|
||||
You type: "and the reward would be within our reach"
|
||||
Result:
|
||||
AA: and the reward w0uld be within 0ur reach
|
||||
```
|
||||
|
||||
Notice that it is CASE SENSITIVE. So in the above case, if you typed
|
||||
"ABSCOND", it would not replace the "O".
|
||||
|
||||
* Sollux:
|
||||
|
||||
```
|
||||
Replace: "i" With: "ii"
|
||||
Replace: "s" With: "2"
|
||||
```
|
||||
|
||||
* Eridan:
|
||||
|
||||
```
|
||||
Replace: "v" With: "vv"
|
||||
Replace: "w" With: "ww"
|
||||
```
|
||||
|
||||
* Feferi:
|
||||
|
||||
```
|
||||
Replace: "h" with: ")("
|
||||
Replace: "H" with: ")("
|
||||
Replace: "E" with: "-E"
|
||||
```
|
||||
|
||||
* __Regexp Replace:__
|
||||
This is a more complex kind of replacement. [Regexp](http://en.wikipedia.org/wiki/Regexp)
|
||||
stands for "regular expression", a kind of programming language (yes, it is a language)
|
||||
used to find and replace text. PC 3.14 also includes a few functions (`upper()`,
|
||||
`lower()`, `scramble()`). If you want to learn it on your own,
|
||||
I suggest you start with the [Python tutorial](http://docs.python.org/howto/regex.html)
|
||||
since PC 3.14 uses Python's regexps. Check out V2.5's tutorial too, as that is a pretty
|
||||
good start as well.
|
||||
|
||||
* Let's start with Karkat. Regexps are just like your every day find and
|
||||
replace: they search for a string that matches what you want to
|
||||
replace, and replaces it with... the replacement.
|
||||
|
||||
```
|
||||
Regexp: "(.)" Replace with: "upper(\1)"
|
||||
```
|
||||
|
||||
Three concepts here. Let's look at the regexp. "(.)" has two things
|
||||
going on. The first is that ".". In regexp speak, "." is the wildcard:
|
||||
it will match *any* character -- and just one.
|
||||
|
||||
The parentheses tell the regexp to *save* what's inside them so you
|
||||
can put it back when you replace. That's what the "\1" is for -- it
|
||||
means, "put the match inside parentheses #1 here". You can have any
|
||||
number of parentheses.
|
||||
|
||||
* __"upper()"__ is a function special to PC 3.14 -- it will uppercase
|
||||
anything inside the parentheses. So in this case, upper will uppercase
|
||||
"\1" -- which, as you recall is what we found inside the
|
||||
parentheses. Which was *every* character. So to sum up, it replaces
|
||||
every character with an uppercase version of that character. WHICH
|
||||
MAKES YOU TALK LIKE THIS.
|
||||
|
||||
* Let's look at Terezi next.
|
||||
|
||||
```
|
||||
Regexp: "[aA]" Replace with: "4"
|
||||
Regexp: "[iI]" Replace with: "1"
|
||||
Regexp: "[eE]" Replace with: "3"
|
||||
Regexp: "(.)" Replace with: "upper(\1)"
|
||||
```
|
||||
|
||||
We already know what the last line does. But what's up with those
|
||||
brackets? What's their deal? Basically, in regular expressions,
|
||||
brackets indicate a list of matching characters. So, basically any
|
||||
single character within the brackets will be matched. In this case,
|
||||
either "a" or "A" will be matched and replaced with "4," and likewise,
|
||||
"i" and "I" will be replaced with "1", and "e" and "E" will be
|
||||
replaced with "3."
|
||||
|
||||
Just like there is an `upper()` function, there is also a `lower()`
|
||||
function. It acts just like `upper()` but instead makes everything
|
||||
inside the parentheses lowercase. This allows you to do things like:
|
||||
|
||||
```
|
||||
Regexp: "(.)" Replace with: "lower(\1)"
|
||||
You type: "I AM YELLING"
|
||||
Result:
|
||||
GD: i am yelling
|
||||
```
|
||||
|
||||
Along with the upper and lower functions is a `scramble()` function.
|
||||
The purpose of this function is to randomly scramble anything inside
|
||||
the parentheses.
|
||||
|
||||
```
|
||||
Regexp: "(\w)(\w*)(\w)" Replace with: "\1scramble(\2)\3"
|
||||
You type: "hello there"
|
||||
Result:
|
||||
GD: hlelo trhee
|
||||
```
|
||||
|
||||
This particular regular expression scrambles all of the letters in
|
||||
the middle of a word. Notice that the "h" and "o" at the beginning
|
||||
and end of hello remain in place while the other letters are scrambled.
|
||||
|
||||
You should also know that "^" is a special character in brackets. If
|
||||
placed immediately after the opening bracket (like "[^"), then the
|
||||
brackets instead match every character *except* the ones in the
|
||||
brackets. So, for example, if you wanted to have a quirk where you
|
||||
capitalized all your letters *except* o, you'd do this:
|
||||
|
||||
```
|
||||
Regexp: "([^o])" Replace with: "upper(\1)"
|
||||
You type: "hello there"
|
||||
Result:
|
||||
GD: HELLo THERE
|
||||
```
|
||||
|
||||
You can also specify a *range* of characters inside the brackets, by
|
||||
using the "-" character. [a-z] will match any lowercase letter. You
|
||||
can combine them, too: [a-z0-9] will match any digit and lowercase letter.
|
||||
|
||||
There are also different shortcuts for character types:
|
||||
|
||||
```
|
||||
\d matches any digit; same as [0-9]
|
||||
\D matches any non-digit; same as [^0-9]
|
||||
\s matches any spaces
|
||||
\S matches any non-space
|
||||
\w matches any alphanumeric character; same as [a-zA-Z0-9_]
|
||||
\W matches any non-alphanumeric character; same as [^a-zA-Z0-9_]
|
||||
|
||||
Note \w and \W also match extended Latin or Unicode alphanumerics.
|
||||
```
|
||||
|
||||
You can include this inside brackets, too.
|
||||
|
||||
There's also a special character, \\b. What \\b does is make sure that
|
||||
you are at the beginning or end of a word.
|
||||
* So with that knowledge, let's try Kanaya:
|
||||
|
||||
```
|
||||
Regexp: \b(\w) Replace with: upper(\1)
|
||||
You type: "i suggest you come to terms with it"
|
||||
Result:
|
||||
GA: I Suggest You Come To Terms With It
|
||||
```
|
||||
|
||||
Another feature of regular expressions is the ability to match
|
||||
*repeated* characters. There are three repeat characters: the "\*", the
|
||||
"+", "?", and "{m,n}". They work by playing them after the character,
|
||||
or character type you want to match. (So, you could say "\s+" or ".*")
|
||||
|
||||
The "\*" character matches ZERO or more of that character. So, for
|
||||
example, "f\*" would match "", "f" and "ff" -- and any other character!
|
||||
That's right, every character counts as matching it zero times. Yeah,
|
||||
it's weird. I suggest you use...
|
||||
|
||||
The "+" character matches ONE or more of that character. So, if we
|
||||
wanted to have a character that wanted to elongate their s's so that
|
||||
they used four 's's every time, like sssso, but didn't want to have
|
||||
eight s's when using words with double s's, like pass, we'd do this:
|
||||
|
||||
```
|
||||
Regexp: "s+" Replace with: "ssss"
|
||||
You type: "you shall not pass"
|
||||
Result:
|
||||
UU: you sssshall not passss
|
||||
```
|
||||
|
||||
As for the other two, I can't really think of any useful quirks to be
|
||||
made with them. But to let you know, "?" matches either 0 or 1 of that
|
||||
character, so "trolls?" would match "troll" and "trolls". "{m,n}"
|
||||
matches between m and n characters. (If you leave out 'n', any number
|
||||
of characters more than m will be matched: "s{3,}" will match 3 or more 's'.)
|
||||
So "s{2,4}" will match "ss", "sss", and "ssss" and that's it.
|
||||
|
||||
Also, "?" is equivalent to "{0,1}", "*" is equivalent to "{0,}", and
|
||||
"+" is equivalent to "{1,}".
|
||||
|
||||
Now with repeating expressions, we can do something like make EVERY
|
||||
other WORD capitalized:
|
||||
|
||||
```
|
||||
Regexp: "(\w+) (\w+)" Replace with: "upper(\1) \2"
|
||||
You type: "this is pretty annoying i bet"
|
||||
Result:
|
||||
GD: THIS is PRETTY annoying I bet
|
||||
```
|
||||
|
||||
The \1 represents the first word -- which has been matched because the
|
||||
word is alphanumeric characters, repeated once or more -- and \2
|
||||
represents the second word.
|
||||
|
||||
Another operator to use is the "|", which will match more than one set
|
||||
of characters. For example, "black|red" will match "black" or
|
||||
"red". If you want to match something in the middle of words, you have
|
||||
to use parentheses: "(black|red) romance" will match "black romance"
|
||||
and "red romance".
|
||||
|
||||
Finally, there are the "^" and "$" characters. Yes, we already did the
|
||||
"^" character, but this is OUTSIDE of brackets, not INSIDE. "^"
|
||||
matches the beginning of a message, and "$" matches the end of it. You
|
||||
can use this to make more sophisticated prefix and suffix
|
||||
behaviors. For example, if we have a quirk that adds "..." to the end
|
||||
of all our messages, we can set it up so it doesn't do that if we put
|
||||
punctuation [?!.] at the end. So:
|
||||
|
||||
```
|
||||
Regexp: "([^?!.])$" Replace with: "\1..."
|
||||
```
|
||||
|
||||
This will match the end of any message as long as it doesn't have
|
||||
"?", "!", or "." at the end. Then it will replace it with whatever the
|
||||
last character of the sentence was (remember we're replacing it, so we
|
||||
have to put it back!) and add "..." at the end.
|
||||
|
||||
Careful with the beginning and ending replaces -- if you use more than
|
||||
one, you may not get what you expect because they will ALL be applied,
|
||||
one after the other! This is a bug in my opinion, that I plan to fix!
|
||||
|
||||
* __Random replace:__
|
||||
Just like the regexp replace, except that instead of just one thing to
|
||||
replace it with, you give it a list. PC will then choose from that
|
||||
list randomly. So let's say I want to randomly end my sentences with
|
||||
either "bro" or "dog":
|
||||
|
||||
```
|
||||
Regexp: "$" Replace with: "bro" and "dog"
|
||||
```
|
||||
|
||||
* You can also imitate Araida's random "ribbits" in between words:
|
||||
|
||||
```
|
||||
Regexp: "\s" Replace with: " ribbit ", " ", " ", " ", " ", " ", etc....
|
||||
```
|
||||
|
||||
where " " is just a blank space added a bunch of times. (You can see
|
||||
how many blank spaces you've added by clicking on the list.) You have
|
||||
to add the spaces because each entry has the same chance of being
|
||||
selected. (Yes, I know this could be improved.) If you add " ribbit "
|
||||
and 9 spaces, " ribbit " will have a 1/10 chance of being picked.
|
||||
|
||||
Also note that if you add more than one prefix or more than one
|
||||
suffix, it will pick randomly from them instead of adding them both!
|
||||
|
||||
* __Mispeller__:
|
||||
Be careful with thsi one. The mispeller will randomly mispell x% of
|
||||
the words you type -- where x is the percentage you set the slider
|
||||
to. I have attempted to mimic SBaHJ mispelling style but whoof knows
|
||||
what will happen oh god ive created a mosnter
|
||||
|
||||
|
||||
SMILIES
|
||||
-------
|
||||
Here's a list of smilies:
|
||||
|
||||
* `:rancorous:`
|
||||
* `:apple:`
|
||||
* `:bathearst:`
|
||||
* `:cathearst:`
|
||||
* `:woeful:`
|
||||
* `:pleasant:`
|
||||
* `:blueghost:`
|
||||
* `:slimer:`
|
||||
* `:candycorn:`
|
||||
* `:cheer:`
|
||||
* `:duhjohn:`
|
||||
* `:datrump:`
|
||||
* `:facepalm:`
|
||||
* `:bonk:`
|
||||
* `:mspa:`
|
||||
* `:gun:`
|
||||
* `:cal:`
|
||||
* `:amazedfirman:`
|
||||
* `:amazed:`
|
||||
* `:chummy:`
|
||||
* `:cool:`
|
||||
* `:smooth:`
|
||||
* `:distraughtfirman:`
|
||||
* `:distraught:`
|
||||
* `:insolent:`
|
||||
* `:bemused:`
|
||||
* `:3:`
|
||||
* `:mystified:`
|
||||
* `:pranky:`
|
||||
* `:tense:`
|
||||
* `:record:`
|
||||
* `:squiddle:`
|
||||
* `:tab:`
|
||||
* `:beetip:`
|
||||
* `:flipout:`
|
||||
* `:befuddled:`
|
||||
* `:pumpkin:`
|
||||
* `:trollcool:`
|
||||
* `:jadecry:`
|
||||
* `:ecstatic:`
|
||||
* `:relaxed:`
|
||||
* `:discontent:`
|
||||
* `:devious:`
|
||||
* `:sleek:`
|
||||
* `:detestful:`
|
||||
* `:mirthful:`
|
||||
* `:manipulative:`
|
||||
* `:vigorous:`
|
||||
* `:perky:`
|
||||
* `:acceptant:`
|
||||
* `:olliesouty:`
|
||||
* `:billiards:`
|
||||
* `:billiardslarge:`
|
||||
* `:whatdidyoudo:`
|
11
README.md
|
@ -1,11 +0,0 @@
|
|||
# Pesterchum Alternate Servers
|
||||
Pesterchum with added functionality to connect to alternative servers. (And some other stuff.)
|
||||
|
||||
Normal pesterchum documentation is in "README-pesterchum.mkdn" and the one for Karxi's fork is README-karxi.mkdn.
|
||||
|
||||
## Servers
|
||||
If you'd like to connect to a different server than the default "pesterchum.xyz", put the server you'd like to connect to in the server.json file.
|
||||
|
||||
## Tips for building
|
||||
- For windows use "setup.py". PyQt4 binaries for windows can be installed from it's sourceforce page if you don't want to go through the hell that's compiling it manually.
|
||||
- On mac you can install most of the dependencies via macports and build with "python setup-py2app.py py2app".
|
358
TODO.mkdn
|
@ -1,358 +0,0 @@
|
|||
# Todo
|
||||
|
||||
## Git
|
||||
* Set up issue tracking for this fork, if possible (and move the TODO list)
|
||||
|
||||
## Features
|
||||
* Log viewer needs to have BBCode/HTML/Text copy modes
|
||||
* Turn @ and # links on/off?
|
||||
* Show true bans? COMPRESS QUIT MESSAGES ON BAN
|
||||
* Colour saving boxes things?
|
||||
* Whowas for last seen online?
|
||||
* Tab completion of two letter names
|
||||
* Auto download/install updates via Windows installer
|
||||
* Make toast notifications only on certain chums
|
||||
* Local alisas for chums
|
||||
* Don't make new windows be all in your face and shit
|
||||
* Hide offline friends per group
|
||||
|
||||
|
||||
* Add support for displaying more verbose information (e.g. Cease messages which
|
||||
tell you more than the abbreviation of who left)
|
||||
* Make Pesterchum recognize conventional /mes so they aren't invisible
|
||||
* Make @XY and @xxxYyy formats ping their targets
|
||||
* Allow matches like @?XY and @FXY or @PXY3 - make them only match the target
|
||||
currently set to that.
|
||||
* Make @ notation not match @u@; and similar (make invalid nick chars break
|
||||
matches)
|
||||
* Allow use of @ and # links to switch between logs in log viewer (start with
|
||||
the closest time to the line of the log that's linking)
|
||||
* Improve log viewer in general (unbind instances from specific users/memos)
|
||||
* Fix hyperlink escaping (Qt seems to do this automatically - need a workaround)
|
||||
* Show bans if +h or higher (+h, +o, +a, +q)
|
||||
* Auto-invite (for people on the friends list?)
|
||||
* Right click on names for inviting, ACTUALLY banning, PMing, ... See also:
|
||||
Things proper IRC clients can do. (Set basic mode stuff up first, get the
|
||||
infrastructure in place.)
|
||||
|
||||
* Add more comprehensive status support - IDLE, DND, INVISIBLE for now - or at
|
||||
least add similar functionality.
|
||||
* Improve Pesterchum's tracking of disconnections (check memos for quits...)
|
||||
* SEPARATE FUNCTIONALITY from CONNECTED STATE!! This is a huge problem! Being
|
||||
shunted off our nick closes windows and breaks things! Just D/C and
|
||||
reconnect?
|
||||
* It'd probably be best to give an option to either CHANGE NICKS or
|
||||
DISCONNECT upon nick collision...? But, then how would we GHOST?
|
||||
* Auto-disconnect if collision before joining channels, make it
|
||||
possible to disconnect (but not send messages, obviously)
|
||||
without losing everything
|
||||
* Maybe GHOSTing should use auto-identify to ensure- no, that doesn't
|
||||
work, because we can't ident into a specified nick without being ON
|
||||
it. Need GD's help to fix....
|
||||
|
||||
* Make it possible to test quirk things and such without connecting? This'd be
|
||||
hard to separate out, but useful.
|
||||
* Make a quirk 'bin' that exists independent of profiles, and can be
|
||||
copied to/from at will.
|
||||
* Allow us to set quirks that apply to all profiles! For things like
|
||||
replacement quirks.
|
||||
|
||||
* Right-click Time entry field to see those used? (Replace left/right buttons?)
|
||||
* Save commonly-used times on a per-handle basis!
|
||||
* Make the memo list highlight/recolor the names of channels you're in
|
||||
(including secret ones)
|
||||
* Add an option to Cycle (for log separation)
|
||||
|
||||
* Add a separate 'Tweaks' section in Options
|
||||
* Fix graphical issues with dark themes vs. light themes (Qt/text too
|
||||
light/etc.)
|
||||
|
||||
### Services
|
||||
* Use web connection to send offline messages in email-like fashion
|
||||
(Idea: ghostDunk)
|
||||
* Better NickServ registering
|
||||
* Implement **MemoServ** support
|
||||
* Tell user when NickServ handles are going to expire
|
||||
* Tell user when old handles have D/C'd? Offer autoghost support?!
|
||||
|
||||
### GUI
|
||||
* Refactor code to make way for QShortcut usage. (Unify shortcut processing?)
|
||||
* Enable/Disable toggle (Firefox style option sheet-esque? Seems okay.)
|
||||
* Ctrl+W closes tab
|
||||
* Ctrl+Shift+PGUP/PGDN moves tab
|
||||
* Option to disable Ctrl+Tab's jump to newest
|
||||
* Ctrl+Shift+V "Mass Paste" option (parse lines in sequence)?
|
||||
* Make mouseovers (refocusing) reset idle timer (disableable)
|
||||
* Set up EVENT FILTERS in windows to redirect events to the right places
|
||||
* Make the context key (if used in the text area) append the menu options
|
||||
from the right-click menu
|
||||
* Make system messages use timestamps like everything else
|
||||
* Offer option for timestamps in memos
|
||||
* Make a status window/popup to contain logs of information like invites
|
||||
|
||||
### "Security"
|
||||
**Note: The idea of security on this platform is pretty much laughable. Most of
|
||||
these changes are simply bandages being placed over fundamentally flawed
|
||||
design.**
|
||||
|
||||
If you want Pesterchum to be more secure, either get ghostDunk to make changes
|
||||
to the server and its administration policies, or get everyone to switch to this
|
||||
version of the client. There aren't really any other options.
|
||||
|
||||
* Flood protection (don't send because of the same target too many times in a
|
||||
row)
|
||||
* Just requires a timer + lastmessage date check.
|
||||
* Lock exploitable functionality to those on your friends list (optional)
|
||||
* Canon handles are excluded from this concern, but subject to flood
|
||||
controls regardless.
|
||||
* A measure of politeness is reasonable here - checking if a friend is on
|
||||
as a different handle should be okay, once IRC access is revamped.
|
||||
* Don't send MOOD answers to those not friended
|
||||
* Ignore pesters from those not on your friends list (optional)
|
||||
* Ignore pesters from those not sharing a memo? (optional)
|
||||
* Make BLOCKED list persistent, if it isn't already
|
||||
* Offer option to block hosts, not just handles...
|
||||
* Explain to the user that this option is very dangerous.
|
||||
|
||||
### Advanced
|
||||
* Offer option for 'new' syntax adjustments
|
||||
* Replace: "???, ???, ??? ceased responding to memo." for parts.
|
||||
* "XY ({handle}) ({times}) ceased responding to memo."
|
||||
* If +h or above: Include host as well as handle.
|
||||
* Replace: "??? ceased responding to memo." for nickchanges.
|
||||
* "XY ({handle} {host?}) left memo; AB ({handle} joined memo."
|
||||
* Can violate the norms somewhat, these aren't theme-controlled.
|
||||
They're basically for power users/admins.
|
||||
* Offer GUI changes
|
||||
* Make tabs "{name} ({# of unread msgs})" if unread
|
||||
* Make themes able to define colors that are too dark to read for an
|
||||
individual window
|
||||
* Set up code for 'nudging' color codes into readable territory; presumably
|
||||
via an adjustable setting. (This CANNOT affect logs - just the log
|
||||
reader.)
|
||||
* Come up with a solution for duplicate times/handle abbreviations
|
||||
* Maybe make mouseover for the handles display the full handle?
|
||||
|
||||
* Option to disable backwards compatibility:
|
||||
* For those that are *really* sure that this build is the build for
|
||||
them.
|
||||
* Should enable extra features, including ctag compression.
|
||||
* Allow manual compression changes via memo right-click menu for
|
||||
'advanced' (per the setting) users
|
||||
|
||||
## Todo/Done
|
||||
**Everything in this section has already been completed.**
|
||||
|
||||
### GUI
|
||||
* Toggle individual tab flash / alert sounds (from the same right-click memo
|
||||
that lets us toggle OOC)
|
||||
* Make CTRL+PGUP/PGDN switch memo/pester tabs
|
||||
* Make Ctrl+J/K usable for tab changing
|
||||
* Make right-clicking on a tab open up the right-click menu one would get on
|
||||
right-clicking the title (frame??)
|
||||
* Right-click in userlist offers option to Pester
|
||||
* Make certain dialogues start on the safer of the two options
|
||||
* Make the Reconnect dialog start on Reconnect
|
||||
* Make the Rejoin dialog start on Rejoin
|
||||
* Make the Invite dialog start on Decline
|
||||
|
||||
### Usability
|
||||
* Fix parser text-loss bug that plagues everyone (especially Chumdroid users)
|
||||
* Make /me messages that cut continue into more /me messages
|
||||
* Make sound work on Windows through QSound (disables volume control)
|
||||
* Color tags are now posted as their shorter hexadecimal forms, if applicable
|
||||
(255,255,255 -> #FFFFFF, for example)
|
||||
* Separate auto-idle and checkbox idle so they don't share a state (and make
|
||||
the first send set the timer for additional idle responses)
|
||||
* Stop us from sending IDLE messages to NickServ
|
||||
* Fix NickServ auto-login things
|
||||
* Make a window that can be used to interface with the script directly - a
|
||||
simple Python console
|
||||
* Make the memo name entry box accept a comma-separated list
|
||||
* Make console users able to check widget theme/style information via mouseover
|
||||
and key combo (for people making themes or debugging)
|
||||
* This is presently Ctrl+Alt+w, after opening the console (Ctrl+~).
|
||||
* LET PEOPLE TURN OFF HONKING - people already rename the soundfile and such to
|
||||
do this manually (append "honk":false to the end of pesterchum.js ; that kills it.)
|
||||
|
||||
### Backend
|
||||
* Perpetual code cleanup, refactoring, simplification and general improvements
|
||||
* Syntax changes/updates and the like
|
||||
|
||||
## Code
|
||||
**Improvements and changes pertaining to Pesterchum's internals.**
|
||||
|
||||
### General
|
||||
* Implement new Lexer for the sake of everyone's sanity
|
||||
* This is half-done - rendering still uses the old lexer
|
||||
* Redo `PESTERCHUM:` processing/redo whole msg received processing chain
|
||||
* Redo text processing in general
|
||||
* Redo quirk processing (use pieces from Textsub if needed)
|
||||
* Pare down the PesterMemo object so it inherits more things from PesterConvo
|
||||
*implicitly*
|
||||
* SOONER OR LATER: Redo internal chum storage, centralize data into widely
|
||||
accessible manager objects, etc.
|
||||
* Also: Overhaul settings storage. Bring it more in line with the system Textsub
|
||||
used (if feeling masochistic), but simpler.
|
||||
* **Overhaul information storage** - chums, conversations, memos; all should be
|
||||
handled by a backend and merely RENDERED into Qt objects!!
|
||||
* Overhaul debugging
|
||||
* Give an actual (small) window with traceback that can be sent to dev(s)
|
||||
* Use the console for this?
|
||||
* Debug generic.py's CaseInsensitiveDict/replace it with mine
|
||||
* Overhaul messaging so **Chan/Nick/Memo Servs** all use the same code (and
|
||||
lexer)
|
||||
* **Separate Pesterchum system handling from window handling.** Dicts should be
|
||||
stored and maintained via dicts - even a refined version of what I used for
|
||||
textsub.
|
||||
* Doing it this way means I can fix the case in/sensitivity issue, too.
|
||||
* Set up framework for easily logging/accessing channels, users, etc...like
|
||||
what hexchat has.
|
||||
* More efficient framework for accessing stored user information - right now,
|
||||
Pesterchum keeps information on user colors and such on hand *forever*,
|
||||
meaning that things inevitably get clogged up with handles that are never
|
||||
really seen, or only seen once, or even just randomly generated. This is
|
||||
silly and should be changed; I'll probably make a namedtuple for users or
|
||||
something, and save it all to a separate JSON file or two.
|
||||
Said JSON file should keep the extra information on hand - or rather, the
|
||||
'recent users' JSON file should keep most of the detailed information and be
|
||||
accessed first, with the larger 'inactive users' file being accessed to
|
||||
check for unfamiliar handles.
|
||||
These would have to output the old data to pesterchum.js for backwards
|
||||
compatibility purposes.
|
||||
* Finish creating the sound wrapper. Just make it check what the type of sound
|
||||
needed is upon creation, and instantiate a private class based off of that.
|
||||
* There is basically no good way to do this without moving to Qt5. I
|
||||
might try that myself later, but that's a long-term goal.
|
||||
* Stop from sending TIME notifications when unable
|
||||
* Until then: Hide resulting "no external messages"/+m messages.
|
||||
* Stop sending auto-IDLE messages unless the chat has been updated since the
|
||||
last one.
|
||||
* Make groups, chums, etc. preferentially load from the main directory, *then*
|
||||
check logs.
|
||||
* Compatibility is important, so update both if they exist. (Bluh!)
|
||||
|
||||
### Debugging
|
||||
* Set up a simple function to display a traceback instead of silently moving on!
|
||||
* Use the console for this?
|
||||
* Make small, simplistic windows that allow the viewing of internal variables
|
||||
pertaining to things like set quirks, users present, etc.
|
||||
* Also let it display the stylesheet settings of the current window, or
|
||||
similar.
|
||||
* Make a console to display debug info without requiring us to run from terminal
|
||||
* Allow us to specify flags via command line
|
||||
* Let us specify a separate config (pesterchum.js) file!!
|
||||
* Make the console support color (not ctags, more stylesheet stuff) (need
|
||||
parser work for this)
|
||||
|
||||
## Bugs
|
||||
* weird memo time bug
|
||||
* Windows doesn't show style sheet sometimes?? Maybe related to themes.
|
||||
* Issues with connecting? Client not closing connection right? People keep
|
||||
getting "nick taken" messages
|
||||
* When using mood sort, scroll position jumps to last selected chum
|
||||
* Closing a timeclone doesn't actually cease for everyone else
|
||||
* Kill Zalgo
|
||||
* Random invisible, tiny links to last link at end of every message
|
||||
* Chums not appearing on chumroll when initals are the same? (bS)
|
||||
* Recognize IRC 471, 473, 474 and 475
|
||||
* memo links aren't case sensitive
|
||||
|
||||
* Mentions occasionally don't work (e.g. in /me)
|
||||
* Character times aren't 'forgotten' on Part
|
||||
* +c is not properly recognized on join, nor does it stop someone from
|
||||
reenabling their quirk (let ops and above ignore it?)
|
||||
* Chumlist handles groups pretty badly (no using the same name as a handle, for
|
||||
example? Needs an errormessage at least)
|
||||
* The whole chumlist handling system really ought to be refactored into
|
||||
something sane...the objects should only be there to render
|
||||
* PESTERCHUM: messages are sent to things like NickServ
|
||||
* Log folder/file names are not case-sensitive, so they break on non-Windows
|
||||
systems
|
||||
* Log viewer needs adjustments and sanity checking for log directories
|
||||
* Capitalized /me's don't render (should forcibly lowercase them)
|
||||
* 'pcd10' and similar users don't get proper abbreviations on part (ugh)
|
||||
|
||||
* Volume control doesn't work without pygame
|
||||
* Sound on Linux doesn't work without pygame
|
||||
* Update checking code gives false positives (update to use json file from git?)
|
||||
* Pesterchum doesn't seem to close all of its file handles - it runs out of
|
||||
handles to use on Linux
|
||||
* Others have reported memory leak-induced crashes on Windows. These
|
||||
may or may not be related.
|
||||
* Pesterchum groups aren't carried over when profiles are copied!
|
||||
* Malformed Pesterchum profiles cause the program to crash and burn
|
||||
* What causes these? Exiting in the middle of a write operation? Threading
|
||||
issues?
|
||||
* Malformed Python quirks try to open an error dialog and crash if Pesterchum
|
||||
is opened as a module
|
||||
* Pesterchum's threading is messy and scary, and should probably be cleaned up
|
||||
some via the addition of locks
|
||||
* Pesterchum's file handling is atrocious - inefficient to the extreme.
|
||||
* There are multiple copies of things that really need only be loaded once
|
||||
in a lot of different places.
|
||||
* Memos don't seem to close their file handles properly. (Logs too??)
|
||||
|
||||
## Windows Bugs
|
||||
* XP SP2: sometimes mouse clicks dont register? must be some kinda crash
|
||||
* On reconnect and nick change, momentary theme change causes menu items to
|
||||
stop working
|
||||
* Random chums won't show up on chumroll
|
||||
* Popup toast notifications cause main window to raise
|
||||
|
||||
## Mac Bugs
|
||||
**Due to my lack of access to a Mac, these are unlikely to be fixed.**
|
||||
* Mac doesn't show tabs right, display gifs, highlighting thing?
|
||||
* SS: also the background image is broken
|
||||
* SS: in the one-on-one pester it resizes with the window
|
||||
* SS: but the memo one doesn't resize
|
||||
* SS: and the arrows next to the time thing overlap the CLOSE button
|
||||
* Lex: There seems to be a faint outline around most non-square themes.
|
||||
|
||||
|
||||
## Things that won't be done
|
||||
**Requests that, for one reason or another, will not be fulfilled.**
|
||||
|
||||
### Scrapped Features
|
||||
**I'll explain why these ones won't happen.**
|
||||
|
||||
> * More complex quirks: by-sound
|
||||
|
||||
* This would require a way to determine what maps to a sound, and
|
||||
replace it.
|
||||
I've played with the idea before. It resulted in me needing to look
|
||||
up things like the [Metaphone Algorithm][metaphone] to figure out
|
||||
how it might be even remotely possible. The results were NOT a fun
|
||||
time; if this is ever implemented, it will be much, much later than
|
||||
just about everything else.
|
||||
[metaphone]: https://en.wikipedia.org/wiki/Metaphone
|
||||
|
||||
> * Spy mode
|
||||
|
||||
* I feel as though I shouldn't need to tell anyone why this is a bad idea.
|
||||
Some people already have this capability anyway; I used to be one of them.
|
||||
There's no real need to implement an inferior version into every single
|
||||
client on Pesterchum.
|
||||
|
||||
> * "Someone has friended you" notifier
|
||||
> * Spectation notices (Idea: lexicalNuance) (probly WONTFIX)
|
||||
|
||||
* These are milder invasions of privacy than the above, but they are still
|
||||
invasions of privacy.
|
||||
|
||||
> * When 'banned' make impossible to connect using timestamp banned under
|
||||
|
||||
* This is a lot of work for something that purely affects immersion - while
|
||||
also breaking a number of things in the process. Too much work for too
|
||||
little payoff.
|
||||
|
||||
> * Use web connection to save profiles (Idea: ghostDunk)
|
||||
|
||||
* There is no way to do this now that Pesterchum is basically unsupported; an
|
||||
external server would be necessary for storage.
|
||||
As such, you'll just have to settle for copying your profiles and logs when
|
||||
you change computers.
|
||||
|
||||
|
||||
|
||||
[modeline]: vim:set autoindent ts=4 sts=4 sw=4 tw=79 expandtab:
|
|
@ -1 +0,0 @@
|
|||
{"major": 3.41, "minor": 4, "status": "A", "rev": 13, "utype": "dev"}
|
528
console.py
|
@ -1,528 +0,0 @@
|
|||
# vim: set autoindent ts=4 sts=4 sw=4 textwidth=79 expandtab:
|
||||
# -*- coding=UTF-8; tab-width: 4 -*-
|
||||
from __future__ import print_function
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
import re, os, traceback, sys
|
||||
import time, datetime
|
||||
from os import remove
|
||||
|
||||
import dataobjs, generic, memos, parsetools, ostools
|
||||
from version import _pcVersion
|
||||
|
||||
try:
|
||||
from pnc.attrdict import AttrDict
|
||||
except ImportError:
|
||||
# Fall back on the old location, just in case
|
||||
from pnc.dep.attrdict import AttrDict
|
||||
#~from styling import styler
|
||||
|
||||
_datadir = ostools.getDataDir()
|
||||
|
||||
import logging
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
|
||||
|
||||
|
||||
class ConsoleWindow(QtGui.QDialog):
|
||||
#~class ConsoleWindow(styler.PesterBaseWindow):
|
||||
# A simple console class, cobbled together from the corpse of another.
|
||||
|
||||
stylesheet_path = "main/defaultwindow/style"
|
||||
# This is a holder for our text inputs.
|
||||
text = AttrDict()
|
||||
# I should probably put up constants for 'direction' if this is going to
|
||||
# get this complicated. TODO!
|
||||
incoming_prefix = "<<<"
|
||||
miscinfo_prefix = "==>"
|
||||
outgoing_prefix = ">>>"
|
||||
neutral_prefix = "!!!"
|
||||
waiting_prefix = "..."
|
||||
|
||||
selected_widget = None
|
||||
show_info_on_select = True
|
||||
|
||||
_CUSTOM_ENV = {}
|
||||
|
||||
def __init__(self, parent):
|
||||
super(ConsoleWindow, self).__init__(parent)
|
||||
self.prnt = parent
|
||||
try:
|
||||
self.mainwindow = parent.mainwindow
|
||||
except:
|
||||
self.mainwindow = parent
|
||||
theme = self.mainwindow.theme
|
||||
# This won't initialize the sub-objects, because they don't exist yet.
|
||||
self.initTheme(theme)
|
||||
|
||||
self.text = AttrDict()
|
||||
self.text.area = ConsoleText(theme, self)
|
||||
self.text.input = ConsoleInput(theme, self)
|
||||
self.text.input.setFocus()
|
||||
|
||||
self.connect(self.text.input, QtCore.SIGNAL('returnPressed()'),
|
||||
self, QtCore.SLOT('sentMessage()'))
|
||||
|
||||
self.text.history = dataobjs.PesterHistory()
|
||||
|
||||
# For backing these up
|
||||
self.stdout = self.stderr = None
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0.addWidget(self.text.area)
|
||||
layout_0.addWidget(self.text.input)
|
||||
self.setLayout(layout_0)
|
||||
|
||||
def parent(self):
|
||||
return self.prnt
|
||||
|
||||
def clearNewMessage(self):
|
||||
pass
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def sentMessage(self):
|
||||
text = self.text.input.text()
|
||||
# TODO: Make this deal with unicode text, it'll crash and burn as-is.
|
||||
text = str(text)
|
||||
text = text.rstrip()
|
||||
|
||||
self.text.history.add(text)
|
||||
self.text.input.setText("")
|
||||
|
||||
self.execInConsole(text)
|
||||
# Scroll down to the bottom so we can see the results.
|
||||
sb = self.text.area.verticalScrollBar()
|
||||
sb.setValue(sb.maximum())
|
||||
|
||||
def addTraceback(self, tb=None):
|
||||
# We should do the formatting here, but eventually pass it to text.area
|
||||
# to addMessage whatever output we produced.
|
||||
# If we're called by addMessage - and we should be - then sys.stdout is
|
||||
# still being redirected into the console.
|
||||
# TODO: Just make an object for setting contexts (and thus optionally
|
||||
# redirecting prints). Use 'with', of course.
|
||||
# TODO: Make this exclude *our* processing from the traceback stack.
|
||||
try:
|
||||
self.addMessage(traceback.format_exc(), direction=0)
|
||||
except Exception as err:
|
||||
logging.error("Failed to display error message (???): %s" % err)
|
||||
|
||||
def addMessage(self, msg, direction):
|
||||
# Redirect to where these things belong.
|
||||
self.text.area.addMessage(msg, direction=direction)
|
||||
|
||||
def closeEvent(self, event):
|
||||
# TODO: Set up ESC to close the console...or refer to hiding it as
|
||||
# closing it. Not sure which is preferable.
|
||||
parent = self.parent()
|
||||
parent.console.is_open = False
|
||||
parent.console.window = None
|
||||
return super(ConsoleWindow, self).closeEvent(event)
|
||||
|
||||
def hideEvent(self, event):
|
||||
parent = self.parent()
|
||||
parent.console.is_open = False
|
||||
|
||||
def initTheme(self, theme):
|
||||
# Set up our style/window specifics
|
||||
self.changeTheme(theme)
|
||||
self.resize(400,600)
|
||||
|
||||
def changeTheme(self, theme):
|
||||
self.setStyleSheet(theme[self.stylesheet_path])
|
||||
self.setWindowTitle("==> Console")
|
||||
if "area" in self.text and "input" in self.text:
|
||||
self.text.area.changeTheme(theme)
|
||||
self.text.input.changeTheme(theme)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def designateCurrentWidget(self):
|
||||
# Display and save the current widget!
|
||||
# TODO: Consider (reversible) highlighting or selection or something
|
||||
# fancy. It'd help people write styles, wouldn't it?
|
||||
# ...just remember to use mouseRelease() if you work with hovering.
|
||||
|
||||
# Direction: Misc. Info
|
||||
direction = 2
|
||||
|
||||
pos = QtGui.QCursor.pos()
|
||||
wgt = QtGui.QApplication.widgetAt(pos)
|
||||
if wgt is None:
|
||||
# Don't set None, for now. May change this later.
|
||||
self.addMessage("You need to have your cursor over something " + \
|
||||
"in Pesterchum to use that.",
|
||||
direction=direction)
|
||||
return
|
||||
|
||||
self.selected_widget = wgt
|
||||
nchild = len(wgt.children())
|
||||
output = []
|
||||
output.append("CONSOLE.selected_widget = {0!r}".format(wgt))
|
||||
output.append("{0: <4}Parent: {1!r}".format('', wgt.parent()))
|
||||
output.append("{0: <4}{1:4d} child{2}".format('',
|
||||
nchild, ("ren" if abs(nchild) != 1 else "") ))
|
||||
if self.show_info_on_select:
|
||||
qtss = None
|
||||
uses_ss = None
|
||||
try:
|
||||
qtss = wgt.styleSheet()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if unicode(qtss) == unicode(""):
|
||||
uses_ss, ss_msg = False, "No"
|
||||
elif qtss is not None:
|
||||
uses_ss, ss_msg = True, "Yes"
|
||||
else:
|
||||
uses_ss, ss_msg = None, "Invalid"
|
||||
|
||||
ss_par, ss_par_msg = None, ""
|
||||
if uses_ss is False:
|
||||
# TODO: Split this into a sub-function or integrate it into
|
||||
# Styler or *something*.
|
||||
# The stylesheet was probably defined on a parent higher up.
|
||||
# Rungs above the start
|
||||
i = 0
|
||||
# qtss is still "" from earlier
|
||||
while not qtss:
|
||||
try:
|
||||
ss_par = wgt.parent()
|
||||
qtss = ss_par.styleSheet()
|
||||
except:
|
||||
# Can't ascend...and we're still in loop, so we don't
|
||||
# have what we came for.
|
||||
# Either that, or it's incompatible, which means the
|
||||
# ones above are anyway.
|
||||
ss_par = False
|
||||
break
|
||||
else:
|
||||
# Indicate that we got this from a parent
|
||||
i += 1
|
||||
|
||||
if not qtss:
|
||||
# There are no stylesheets here.
|
||||
if ss_par is False:
|
||||
# We had parent issues.
|
||||
# TODO: Specifically indicate invalid parent.
|
||||
uses_ss, ss_msg = None, "Invalid"
|
||||
else:
|
||||
uses_ss, ss_msg = False, "No"
|
||||
else:
|
||||
# We got a stylesheet out of this!
|
||||
uses_ss, ss_msg = True, "Yes"
|
||||
#~ss_par_msg = "{0: <4}...on parent ↑{1:d}: {2!r}".format('',
|
||||
ss_par_msg = "{0: <4}...on parent #{1:d}: {2!r}".format('',
|
||||
i, ss_par)
|
||||
|
||||
msg = []
|
||||
msg.append("{0: <4}QtSS?: {1}".format('', ss_msg))
|
||||
# A stylesheet analyzer would be wonderful here. Perhaps something
|
||||
# that tells us how many parent classes define stylesheets?
|
||||
if uses_ss:
|
||||
if ss_par_msg:
|
||||
# We got this stylesheet from a parent object somewhere.
|
||||
msg.append(ss_par_msg)
|
||||
msg.append("{0: <4}".format("Stylesheet:"))
|
||||
for ln in qtss.split('\n'):
|
||||
msg.append("{0: <8}".format(ln))
|
||||
|
||||
# Actually add this stuff to the result we're constructing
|
||||
output.extend(msg)
|
||||
|
||||
output = '\n'.join(output)
|
||||
self.addMessage(output, direction=direction)
|
||||
|
||||
|
||||
# Actual console stuff.
|
||||
def execInConsole(self, scriptstr, env=None):
|
||||
# Since that's what imports *us*, this should be okay
|
||||
# Tab completion could be set up in ConsoleInput, and would be nice
|
||||
import pesterchum as pchum
|
||||
|
||||
if env is None:
|
||||
env = pchum._retrieveGlobals()
|
||||
|
||||
# Modify the environment the script will execute in.
|
||||
# Fetch from the class/instance first.
|
||||
_CUSTOM_ENV = self._CUSTOM_ENV.copy()
|
||||
# Modify with some hard-coded environmental additions.
|
||||
_CUSTOM_ENV.update({
|
||||
"CONSOLE": self,
|
||||
"MAINWIN": self.mainwindow,
|
||||
"PCONFIG": self.mainwindow.config,
|
||||
"exit": lambda: self.mainwindow.exitaction.trigger()
|
||||
})
|
||||
# Aliases.
|
||||
_CUSTOM_ENV.update({
|
||||
"quit": _CUSTOM_ENV["exit"]
|
||||
})
|
||||
# Add whatever additions were set in the main pesterchum file.
|
||||
_CUSTOM_ENV.update(pchum._CONSOLE_ENV)
|
||||
|
||||
_CUSTOM_ENV_USED = []
|
||||
cenv = pchum.__dict__
|
||||
# Display the input we provided
|
||||
# We do this here, *before* we do our variable injection, so that it
|
||||
# doesn't have to be part of the try statement, where it could
|
||||
# potentially complicate matters/give false positives.
|
||||
self.addMessage(scriptstr, 1)
|
||||
for k in _CUSTOM_ENV:
|
||||
if k not in cenv:
|
||||
# Inject the variable for ease of use.
|
||||
cenv[k] = _CUSTOM_ENV[k]
|
||||
# Record that we injected it.
|
||||
_CUSTOM_ENV_USED.append(k)
|
||||
else:
|
||||
# Don't overwrite anything!
|
||||
warn = "Console environment item {0!r} already exists in CENV."
|
||||
warn.format(k)
|
||||
logging.warning(warn)
|
||||
# Because all we did was change a linked AttrDict, we should be fine
|
||||
# here.
|
||||
try:
|
||||
# Replace the old writer (for now)
|
||||
sysout, sys.stdout = sys.stdout, self
|
||||
try:
|
||||
code = compile(scriptstr + '\n', "<string>", "single")
|
||||
# Will using cenv instead of env cause problems?...
|
||||
result = eval(code, cenv)
|
||||
except:
|
||||
# Something went wrong.
|
||||
self.addTraceback(sys.exc_info()[2])
|
||||
else:
|
||||
# No errors.
|
||||
if result is not None:
|
||||
print(repr(result))
|
||||
finally:
|
||||
# Restore system output.
|
||||
sys.stdout = sysout
|
||||
finally:
|
||||
# Try to clean us out of globals - this might be disabled
|
||||
# later.
|
||||
for k in _CUSTOM_ENV_USED:
|
||||
# Remove the key we added.
|
||||
cenv.pop(k, None)
|
||||
|
||||
def write(self, data):
|
||||
# Replaces sys.stdout briefly
|
||||
# We only ever use this for receiving, so it's safe to assume the
|
||||
# direction is always -1.
|
||||
if not isinstance(data, list):
|
||||
data = data.split('\n')
|
||||
for line in data:
|
||||
if len(line):
|
||||
self.addMessage(line, -1)
|
||||
|
||||
|
||||
class ConsoleText(QtGui.QTextEdit):
|
||||
stylesheet_template = """
|
||||
QScrollBar:vertical {{ {style[convo/scrollbar/style]} }}
|
||||
QScrollBar::handle:vertical {{ {style[convo/scrollbar/handle]} }}
|
||||
QScrollBar::add-line:vertical {{ {style[convo/scrollbar/downarrow]} }}
|
||||
QScrollBar::sub-line:vertical {{ {style[convo/scrollbar/uparrow]} }}
|
||||
QScrollBar:up-arrow:vertical {{ {style[convo/scrollbar/uarrowstyle]} }}
|
||||
QScrollBar:down-arrow:vertical {{ {style[convo/scrollbar/darrowstyle]} }}
|
||||
"""
|
||||
stylesheet_path = "convo/textarea/style"
|
||||
# NOTE: Qt applies stylesheets like switching CSS files. They are NOT
|
||||
# applied piecemeal.
|
||||
# TODO: Consider parsing the themes out into stylesheets with pieces that
|
||||
# we can hand to each widget.
|
||||
|
||||
def __init__(self, theme, parent=None):
|
||||
super(ConsoleText, self).__init__(parent)
|
||||
if hasattr(self.window(), 'mainwindow'):
|
||||
self.mainwindow = self.window().mainwindow
|
||||
else:
|
||||
self.mainwindow = self.window()
|
||||
|
||||
self.hasTabs = False
|
||||
self.initTheme(theme)
|
||||
self.setReadOnly(True)
|
||||
self.setMouseTracking(True)
|
||||
self.textSelected = False
|
||||
|
||||
self.connect(self, QtCore.SIGNAL('copyAvailable(bool)'),
|
||||
self, QtCore.SLOT('textReady(bool)'))
|
||||
self.urls = {}
|
||||
|
||||
# Stripped out animation init - it's all cruft to us.
|
||||
|
||||
@QtCore.pyqtSlot(bool)
|
||||
def textReady(self, ready):
|
||||
self.textSelected = ready
|
||||
|
||||
def initTheme(self, theme):
|
||||
# The basic style...
|
||||
stylesheet = "QTextEdit {{ {style[convo/textarea/style]} }}"
|
||||
if "convo/scrollbar" in theme:
|
||||
# TODO: Make all of this into a Styler mixin, so we can just feed
|
||||
# it a theme whenever we want to change.
|
||||
# We'd have to define the keys we're affecting, but that shouldn't
|
||||
# be too hard - it's what dicts are for.
|
||||
|
||||
# Add the rest.
|
||||
stylesheet += '\n' + self.stylesheet_template
|
||||
stylesheet = stylesheet.format(style=theme)
|
||||
self.setStyleSheet(stylesheet)
|
||||
|
||||
def addMessage(self, msg, direction):
|
||||
# Display a message we've received.
|
||||
# Direction > 0 == out (sent by us); < 0 == in (sent by script).
|
||||
if len(msg) == 0:
|
||||
return
|
||||
#~color = chum.colorcmd()
|
||||
#~initials = chum.initials()
|
||||
parent = self.window()
|
||||
mwindow = parent.mainwindow
|
||||
|
||||
systemColor = QtGui.QColor(mwindow.theme["convo/systemMsgColor"])
|
||||
|
||||
if mwindow.config.showTimeStamps():
|
||||
if mwindow.config.time12Format():
|
||||
timestamp = time.strftime("[%I:%M")
|
||||
else:
|
||||
timestamp = time.strftime("[%H:%M")
|
||||
if mwindow.config.showSeconds():
|
||||
timestamp += time.strftime(":%S] ")
|
||||
else:
|
||||
timestamp += "] "
|
||||
else:
|
||||
timestamp = ""
|
||||
|
||||
# Figure out what prefix to use.
|
||||
if direction > 1:
|
||||
# Misc. Info
|
||||
prefix = parent.miscinfo_prefix
|
||||
elif direction > 0:
|
||||
# Outgoing.
|
||||
prefix = parent.outgoing_prefix
|
||||
elif direction < 0:
|
||||
# Incoming.
|
||||
prefix = parent.incoming_prefix
|
||||
elif direction == 0:
|
||||
# We could just 'else' here, but there might be some oddness later.
|
||||
prefix = parent.neutral_prefix
|
||||
|
||||
# Later, this will have to escape things so we don't parse them,
|
||||
# likely...hm.
|
||||
#~result = "<span style=\"color:#000000\">{} {} {!r}</span>"
|
||||
# The input we get is already repr'd...we pass it via print, and thus
|
||||
# do that there.
|
||||
result = "{}{} {}\n"
|
||||
result = result.format(timestamp, prefix, msg)
|
||||
self.appendPlainText(result)
|
||||
|
||||
# Direction doesn't matter here - it's the console.
|
||||
self.lastmsg = datetime.datetime.now()
|
||||
# This needs to finish being rewritten....
|
||||
|
||||
def appendPlainText(self, text):
|
||||
"""Add plain text to the end of the document, a la insertPlainText."""
|
||||
# Save the old cursor
|
||||
oldcur = self.textCursor()
|
||||
# Move the cursor to the end of the document for insertion
|
||||
self.moveCursor(QtGui.QTextCursor.End)
|
||||
# Insert the text
|
||||
self.insertPlainText(text)
|
||||
# Return the cursor to wherever it was prior
|
||||
self.setTextCursor(oldcur)
|
||||
|
||||
def changeTheme(self, theme):
|
||||
self.initTheme(theme)
|
||||
sb = self.verticalScrollBar()
|
||||
sb.setValue(sb.maximum())
|
||||
|
||||
def focusInEvent(self, event):
|
||||
self.window().clearNewMessage()
|
||||
super(ConsoleText, self).focusInEvent(event)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
# NOTE: This doesn't give focus to the input bar, which it probably
|
||||
# should.
|
||||
# karxi: Test for tab changing?
|
||||
if self.window().text.input:
|
||||
if event.key() not in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
|
||||
QtCore.Qt.Key_Up, QtCore.Qt.Key_Down):
|
||||
self.window().text.input.keyPressEvent(event)
|
||||
|
||||
super(ConsoleText, self).keyPressEvent(event)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
url = self.anchorAt(event.pos())
|
||||
if url != "":
|
||||
# Skip memo/handle recognition
|
||||
# NOTE: Ctrl+Click copies the URL. Maybe it should select it?
|
||||
if event.modifiers() == QtCore.Qt.ControlModifier:
|
||||
QtGui.QApplication.clipboard().setText(url)
|
||||
else:
|
||||
# This'll probably be removed. May change the lexer out.
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
|
||||
|
||||
super(ConsoleText, self).mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
# Change our cursor when we roll over links (anchors).
|
||||
super(ConsoleText, self).mouseMoveEvent(event)
|
||||
if self.anchorAt(event.pos()):
|
||||
if self.viewport().cursor().shape != QtCore.Qt.PointingHandCursor:
|
||||
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
|
||||
else:
|
||||
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
# This is almost certainly no longer necessary.
|
||||
textMenu = self.createStandardContextMenu()
|
||||
#~if self.textSelected:
|
||||
#~ self.submitLogAction = QtGui.QAction("Submit to Pesterchum QDB", self)
|
||||
#~ self.connect(self.submitLogAction, QtCore.SIGNAL('triggered()'),
|
||||
#~ self, QtCore.SLOT('submitLog()'))
|
||||
#~ textMenu.addAction(self.submitLogAction)
|
||||
textMenu.exec_(event.globalPos())
|
||||
|
||||
|
||||
class ConsoleInput(QtGui.QLineEdit):
|
||||
"""The actual text entry box on a ConsoleWindow."""
|
||||
# I honestly feel like this could just be made a private class of
|
||||
# ConsoleWindow, but...best not to overcomplicate things.
|
||||
stylesheet_path = "convo/input/style"
|
||||
|
||||
def __init__(self, theme, parent=None):
|
||||
super(ConsoleInput, self).__init__(parent)
|
||||
|
||||
self.changeTheme(theme)
|
||||
|
||||
def changeTheme(self, theme):
|
||||
self.setStyleSheet(theme[self.stylesheet_path])
|
||||
|
||||
def focusInEvent(self, event):
|
||||
# We gained focus. Notify the parent window that this happened.
|
||||
self.window().clearNewMessage()
|
||||
self.window().text.area.textCursor().clearSelection()
|
||||
|
||||
super(ConsoleInput, self).focusInEvent(event)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
evtkey = event.key()
|
||||
parent = self.window()
|
||||
|
||||
# If a key is pressed here, we're not idle....
|
||||
# NOTE: Do we really want everyone knowing we're around if we're
|
||||
# messing around in the console? Hm.
|
||||
parent.mainwindow.idler.time = 0
|
||||
|
||||
if evtkey == QtCore.Qt.Key_Up:
|
||||
text = unicode(self.text())
|
||||
next = parent.text.history.next(text)
|
||||
if next is not None:
|
||||
self.setText(next)
|
||||
elif evtkey == QtCore.Qt.Key_Down:
|
||||
prev = parent.text.history.prev()
|
||||
if prev is not None:
|
||||
self.setText(prev)
|
||||
elif evtkey in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown):
|
||||
parent.text.area.keyPressEvent(event)
|
||||
else:
|
||||
super(ConsoleInput, self).keyPressEvent(event)
|
183
convo.py
|
@ -1,11 +1,11 @@
|
|||
from string import Template
|
||||
import re
|
||||
import platform
|
||||
import httplib, urllib
|
||||
import http.client, urllib.request, urllib.parse, urllib.error
|
||||
from time import strftime
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from mood import Mood
|
||||
from dataobjs import PesterProfile, PesterHistory
|
||||
|
@ -21,49 +21,42 @@ except ImportError:
|
|||
# Fall back on the old location - just in case
|
||||
from pnc.dep.attrdict import AttrDict
|
||||
|
||||
class PesterTabWindow(QtGui.QFrame):
|
||||
class PesterTabWindow(QtWidgets.QFrame):
|
||||
def __init__(self, mainwindow, parent=None, convo="convo"):
|
||||
super(PesterTabWindow, self).__init__(parent)
|
||||
self.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
|
||||
self.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
self.mainwindow = mainwindow
|
||||
|
||||
self.tabs = QtGui.QTabBar(self)
|
||||
self.tabs = QtWidgets.QTabBar(self)
|
||||
self.tabs.setMovable(True)
|
||||
self.tabs.setTabsClosable(True)
|
||||
self.connect(self.tabs, QtCore.SIGNAL('currentChanged(int)'),
|
||||
self, QtCore.SLOT('changeTab(int)'))
|
||||
self.connect(self.tabs, QtCore.SIGNAL('tabCloseRequested(int)'),
|
||||
self, QtCore.SLOT('tabClose(int)'))
|
||||
self.connect(self.tabs, QtCore.SIGNAL('tabMoved(int, int)'),
|
||||
self, QtCore.SLOT('tabMoved(int, int)'))
|
||||
self.tabs.currentChanged[int].connect(self.changeTab)
|
||||
self.tabs.tabCloseRequested[int].connect(self.tabClose)
|
||||
self.tabs.tabMoved[int, int].connect(self.tabMoved)
|
||||
|
||||
self.shortcuts = AttrDict()
|
||||
self.shortcuts.tabNext = QtGui.QShortcut(
|
||||
self.shortcuts.tabNext = QtWidgets.QShortcut(
|
||||
QtGui.QKeySequence('Ctrl+j'), self,
|
||||
context=QtCore.Qt.WidgetWithChildrenShortcut)
|
||||
self.shortcuts.tabLast = QtGui.QShortcut(
|
||||
self.shortcuts.tabLast = QtWidgets.QShortcut(
|
||||
QtGui.QKeySequence('Ctrl+k'), self,
|
||||
context=QtCore.Qt.WidgetWithChildrenShortcut)
|
||||
# Note that we use reversed keys here.
|
||||
self.shortcuts.tabUp = QtGui.QShortcut(
|
||||
self.shortcuts.tabUp = QtWidgets.QShortcut(
|
||||
QtGui.QKeySequence('Ctrl+PgDown'), self,
|
||||
context=QtCore.Qt.WidgetWithChildrenShortcut)
|
||||
self.shortcuts.tabDn = QtGui.QShortcut(
|
||||
self.shortcuts.tabDn = QtWidgets.QShortcut(
|
||||
QtGui.QKeySequence('Ctrl+PgUp'), self,
|
||||
context=QtCore.Qt.WidgetWithChildrenShortcut)
|
||||
|
||||
self.connect(self.shortcuts.tabNext, QtCore.SIGNAL('activated()'),
|
||||
self, QtCore.SLOT('nudgeTabNext()'))
|
||||
self.connect(self.shortcuts.tabUp, QtCore.SIGNAL('activated()'),
|
||||
self, QtCore.SLOT('nudgeTabNext()'))
|
||||
self.connect(self.shortcuts.tabLast, QtCore.SIGNAL('activated()'),
|
||||
self, QtCore.SLOT('nudgeTabLast()'))
|
||||
self.connect(self.shortcuts.tabDn, QtCore.SIGNAL('activated()'),
|
||||
self, QtCore.SLOT('nudgeTabLast()'))
|
||||
self.shortcuts.tabNext.activated.connect(self.nudgeTabNext)
|
||||
self.shortcuts.tabUp.activated.connect(self.nudgeTabNext)
|
||||
self.shortcuts.tabLast.activated.connect(self.nudgeTabLast)
|
||||
self.shortcuts.tabDn.activated.connect(self.nudgeTabLast)
|
||||
|
||||
self.initTheme(self.mainwindow.theme)
|
||||
self.layout = QtGui.QVBoxLayout()
|
||||
self.layout = QtWidgets.QVBoxLayout()
|
||||
self.layout.setContentsMargins(0,0,0,0)
|
||||
self.layout.addWidget(self.tabs)
|
||||
self.setLayout(self.layout)
|
||||
|
@ -116,7 +109,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
mods = event.modifiers()
|
||||
if ((mods & QtCore.Qt.ControlModifier) and
|
||||
keypress == QtCore.Qt.Key_Tab):
|
||||
handles = self.convos.keys()
|
||||
handles = list(self.convos.keys())
|
||||
waiting = self.mainwindow.waitingMessages.waitingHandles()
|
||||
waitinghandles = list(set(handles) & set(waiting))
|
||||
if len(waitinghandles) > 0:
|
||||
|
@ -153,7 +146,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
# The new index would be higher than the maximum; loop.
|
||||
nind = nind % ct
|
||||
# Otherwise, negative syntax should get it for us.
|
||||
nind = range(ct)[nind]
|
||||
nind = list(range(ct))[nind]
|
||||
# Change to the selected tab.
|
||||
# Note that this will send out the usual callbacks that handle
|
||||
# focusing and such.
|
||||
|
@ -164,7 +157,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
tabi = self.tabs.tabAt(event.pos())
|
||||
if tabi < 0:
|
||||
tabi = self.tabs.currentIndex()
|
||||
for h, i in self.tabIndices.items():
|
||||
for h, i in list(self.tabIndices.items()):
|
||||
if i == tabi:
|
||||
# Our index matches, grab the object using our handle.
|
||||
convo = self.convos[h]
|
||||
|
@ -203,7 +196,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos()))
|
||||
if i == -1:
|
||||
i = self.tabs.currentIndex()
|
||||
handle = unicode(self.tabs.tabText(i))
|
||||
handle = str(self.tabs.tabText(i))
|
||||
self.clearNewMessage(handle)
|
||||
def convoHasFocus(self, handle):
|
||||
i = self.tabIndices[handle]
|
||||
|
@ -237,24 +230,24 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
|
||||
def changeTheme(self, theme):
|
||||
self.initTheme(theme)
|
||||
for c in self.convos.values():
|
||||
for c in list(self.convos.values()):
|
||||
tabi = self.tabIndices[c.title()]
|
||||
self.tabs.setTabIcon(tabi, c.icon())
|
||||
currenttabi = self.tabs.currentIndex()
|
||||
if currenttabi >= 0:
|
||||
currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex()))
|
||||
currentHandle = str(self.tabs.tabText(self.tabs.currentIndex()))
|
||||
self.setWindowIcon(self.convos[currentHandle].icon())
|
||||
self.defaultTabTextColor = self.getTabTextColor()
|
||||
|
||||
@QtCore.pyqtSlot(int)
|
||||
def tabClose(self, i):
|
||||
handle = unicode(self.tabs.tabText(i))
|
||||
handle = str(self.tabs.tabText(i))
|
||||
self.mainwindow.waitingMessages.messageAnswered(handle)
|
||||
convo = self.convos[handle]
|
||||
del self.convos[handle]
|
||||
del self.tabIndices[handle]
|
||||
self.tabs.removeTab(i)
|
||||
for (h, j) in self.tabIndices.iteritems():
|
||||
for (h, j) in self.tabIndices.items():
|
||||
if j > i:
|
||||
self.tabIndices[h] = j-1
|
||||
self.layout.removeWidget(convo)
|
||||
|
@ -264,7 +257,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
return
|
||||
if self.currentConvo == convo:
|
||||
currenti = self.tabs.currentIndex()
|
||||
currenth = unicode(self.tabs.tabText(currenti))
|
||||
currenth = str(self.tabs.tabText(currenti))
|
||||
self.currentConvo = self.convos[currenth]
|
||||
self.currentConvo.raiseChat()
|
||||
|
||||
|
@ -275,7 +268,7 @@ class PesterTabWindow(QtGui.QFrame):
|
|||
if self.changedTab:
|
||||
self.changedTab = False
|
||||
return
|
||||
handle = unicode(self.tabs.tabText(i))
|
||||
handle = str(self.tabs.tabText(i))
|
||||
convo = self.convos[handle]
|
||||
if self.currentConvo:
|
||||
self.layout.removeWidget(self.currentConvo)
|
||||
|
@ -310,7 +303,7 @@ class PesterMovie(QtGui.QMovie):
|
|||
if text.mainwindow.config.animations():
|
||||
movie = self
|
||||
url = text.urls[movie].toString()
|
||||
html = unicode(text.toHtml())
|
||||
html = str(text.toHtml())
|
||||
if html.find(url) != -1:
|
||||
if text.hasTabs:
|
||||
i = text.tabobject.tabIndices[text.parent().title()]
|
||||
|
@ -323,7 +316,7 @@ class PesterMovie(QtGui.QMovie):
|
|||
text.urls[movie], movie.currentPixmap())
|
||||
text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth())
|
||||
|
||||
class PesterText(QtGui.QTextEdit):
|
||||
class PesterText(QtWidgets.QTextEdit):
|
||||
def __init__(self, theme, parent=None):
|
||||
super(PesterText, self).__init__(parent)
|
||||
if hasattr(self.parent(), 'mainwindow'):
|
||||
|
@ -339,33 +332,30 @@ class PesterText(QtGui.QTextEdit):
|
|||
self.setReadOnly(True)
|
||||
self.setMouseTracking(True)
|
||||
self.textSelected = False
|
||||
self.connect(self, QtCore.SIGNAL('copyAvailable(bool)'),
|
||||
self, QtCore.SLOT('textReady(bool)'))
|
||||
self.copyAvailable[bool].connect(self.textReady)
|
||||
self.urls = {}
|
||||
for k in smiledict:
|
||||
self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k]))
|
||||
self.connect(self.mainwindow, QtCore.SIGNAL('animationSetting(bool)'),
|
||||
self, QtCore.SLOT('animateChanged(bool)'))
|
||||
self.mainwindow.animationSetting[bool].connect(self.animateChanged)
|
||||
def addAnimation(self, url, fileName):
|
||||
movie = PesterMovie(self)
|
||||
movie.setFileName(fileName)
|
||||
movie.setCacheMode(QtGui.QMovie.CacheAll)
|
||||
if movie.frameCount() > 1:
|
||||
self.urls[movie] = url
|
||||
movie.connect(movie, QtCore.SIGNAL('frameChanged(int)'),
|
||||
movie, QtCore.SLOT('animate(int)'))
|
||||
movie.frameChanged[int].connect(movie.animate)
|
||||
#movie.start()
|
||||
@QtCore.pyqtSlot(bool)
|
||||
def animateChanged(self, animate):
|
||||
if animate:
|
||||
for m in self.urls:
|
||||
html = unicode(self.toHtml())
|
||||
html = str(self.toHtml())
|
||||
if html.find(self.urls[m].toString()) != -1:
|
||||
if m.frameCount() > 1:
|
||||
m.start()
|
||||
else:
|
||||
for m in self.urls:
|
||||
html = unicode(self.toHtml())
|
||||
html = str(self.toHtml())
|
||||
if html.find(self.urls[m].toString()) != -1:
|
||||
if m.frameCount() > 1:
|
||||
m.stop()
|
||||
|
@ -374,7 +364,7 @@ class PesterText(QtGui.QTextEdit):
|
|||
def textReady(self, ready):
|
||||
self.textSelected = ready
|
||||
def initTheme(self, theme):
|
||||
if theme.has_key("convo/scrollbar"):
|
||||
if "convo/scrollbar" in theme:
|
||||
self.setStyleSheet("QTextEdit { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] ))
|
||||
else:
|
||||
self.setStyleSheet("QTextEdit { %s }" % (theme["convo/textarea/style"]))
|
||||
|
@ -472,7 +462,7 @@ class PesterText(QtGui.QTextEdit):
|
|||
sb.setValue(sb.maximum())
|
||||
def focusInEvent(self, event):
|
||||
self.parent().clearNewMessage()
|
||||
QtGui.QTextEdit.focusInEvent(self, event)
|
||||
QtWidgets.QTextEdit.focusInEvent(self, event)
|
||||
|
||||
def isBot(self, *args, **kwargs):
|
||||
return self.parent().isBot(*args, **kwargs)
|
||||
|
@ -499,16 +489,16 @@ class PesterText(QtGui.QTextEdit):
|
|||
if url[0] == "#" and url != "#pesterchum":
|
||||
self.parent().mainwindow.showMemos(url[1:])
|
||||
elif url[0] == "@":
|
||||
handle = unicode(url[1:])
|
||||
handle = str(url[1:])
|
||||
self.parent().mainwindow.newConversation(handle)
|
||||
else:
|
||||
if event.modifiers() == QtCore.Qt.ControlModifier:
|
||||
QtGui.QApplication.clipboard().setText(url)
|
||||
QtWidgets.QApplication.clipboard().setText(url)
|
||||
else:
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
|
||||
QtGui.QTextEdit.mousePressEvent(self, event)
|
||||
QtWidgets.QTextEdit.mousePressEvent(self, event)
|
||||
def mouseMoveEvent(self, event):
|
||||
QtGui.QTextEdit.mouseMoveEvent(self, event)
|
||||
QtWidgets.QTextEdit.mouseMoveEvent(self, event)
|
||||
if self.anchorAt(event.pos()):
|
||||
if self.viewport().cursor().shape != QtCore.Qt.PointingHandCursor:
|
||||
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
|
||||
|
@ -535,22 +525,21 @@ class PesterText(QtGui.QTextEdit):
|
|||
textdoc = QtGui.QTextDocument()
|
||||
textdoc.setHtml(htmldata)
|
||||
logdata = "%s\n%s" % (self.submitLogTitle(), textdoc.toPlainText())
|
||||
self.sending = QtGui.QDialog(self)
|
||||
layout = QtGui.QVBoxLayout()
|
||||
self.sending.sendinglabel = QtGui.QLabel("S3ND1NG...", self.sending)
|
||||
cancelbutton = QtGui.QPushButton("OK", self.sending)
|
||||
self.sending.connect(cancelbutton, QtCore.SIGNAL('clicked()'),
|
||||
self.sending, QtCore.SLOT('close()'))
|
||||
self.sending = QtWidgets.QDialog(self)
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
self.sending.sendinglabel = QtWidgets.QLabel("S3ND1NG...", self.sending)
|
||||
cancelbutton = QtWidgets.QPushButton("OK", self.sending)
|
||||
cancelbutton.clicked.connect(self.sending.close)
|
||||
layout.addWidget(self.sending.sendinglabel)
|
||||
layout.addWidget(cancelbutton)
|
||||
self.sending.setLayout(layout)
|
||||
self.sending.show()
|
||||
params = urllib.urlencode({'quote': logdata, 'do': "add"})
|
||||
params = urllib.parse.urlencode({'quote': logdata, 'do': "add"})
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded",
|
||||
"Accept": "text/plain"}
|
||||
try:
|
||||
pass
|
||||
hconn = httplib.HTTPConnection('qdb.pesterchum.net', 80,
|
||||
hconn = http.client.HTTPConnection('qdb.pesterchum.net', 80,
|
||||
timeout=15)
|
||||
hconn.request("POST", "/index.php", params, headers)
|
||||
response = hconn.getresponse()
|
||||
|
@ -563,7 +552,7 @@ class PesterText(QtGui.QTextEdit):
|
|||
self.sending.sendinglabel.setText("F41L3D: %s" % (e))
|
||||
del self.sending
|
||||
|
||||
class PesterInput(QtGui.QLineEdit):
|
||||
class PesterInput(QtWidgets.QLineEdit):
|
||||
stylesheet_path = "convo/input/style"
|
||||
def __init__(self, theme, parent=None):
|
||||
super(PesterInput, self).__init__(parent)
|
||||
|
@ -576,7 +565,7 @@ class PesterInput(QtGui.QLineEdit):
|
|||
super(PesterInput, self).focusInEvent(event)
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == QtCore.Qt.Key_Up:
|
||||
text = unicode(self.text())
|
||||
text = str(self.text())
|
||||
next = self.parent().history.next(text)
|
||||
if next is not None:
|
||||
self.setText(next)
|
||||
|
@ -589,7 +578,7 @@ class PesterInput(QtGui.QLineEdit):
|
|||
self.parent().mainwindow.idler.time = 0
|
||||
super(PesterInput, self).keyPressEvent(event)
|
||||
|
||||
class PesterConvo(QtGui.QFrame):
|
||||
class PesterConvo(QtWidgets.QFrame):
|
||||
def __init__(self, chum, initiated, mainwindow, parent=None):
|
||||
super(PesterConvo, self).__init__(parent)
|
||||
self.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
|
||||
|
@ -605,20 +594,19 @@ class PesterConvo(QtGui.QFrame):
|
|||
|
||||
t = Template(self.mainwindow.theme["convo/chumlabel/text"])
|
||||
|
||||
self.chumLabel = QtGui.QLabel(t.safe_substitute(handle=chum.handle), self)
|
||||
self.chumLabel = QtWidgets.QLabel(t.safe_substitute(handle=chum.handle), self)
|
||||
self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"])
|
||||
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
|
||||
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
|
||||
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
|
||||
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding))
|
||||
self.chumLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding))
|
||||
self.textArea = PesterText(self.mainwindow.theme, self)
|
||||
self.textInput = PesterInput(self.mainwindow.theme, self)
|
||||
self.textInput.setFocus()
|
||||
|
||||
self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'),
|
||||
self, QtCore.SLOT('sentMessage()'))
|
||||
self.textInput.returnPressed.connect(self.sentMessage)
|
||||
|
||||
self.layout = QtGui.QVBoxLayout()
|
||||
self.layout = QtWidgets.QVBoxLayout()
|
||||
self.layout.addWidget(self.chumLabel)
|
||||
self.layout.addWidget(self.textArea)
|
||||
self.layout.addWidget(self.textInput)
|
||||
|
@ -629,31 +617,24 @@ class PesterConvo(QtGui.QFrame):
|
|||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.optionsMenu = QtGui.QMenu(self)
|
||||
self.optionsMenu = QtWidgets.QMenu(self)
|
||||
self.optionsMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"])
|
||||
self.addChumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
|
||||
self.connect(self.addChumAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('addThisChum()'))
|
||||
self.blockAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self)
|
||||
self.connect(self.blockAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('blockThisChum()'))
|
||||
self.quirksOff = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
|
||||
self.addChumAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
|
||||
self.addChumAction.triggered.connect(self.addThisChum)
|
||||
self.blockAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self)
|
||||
self.blockAction.triggered.connect(self.blockThisChum)
|
||||
self.quirksOff = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
|
||||
self.quirksOff.setCheckable(True)
|
||||
self.connect(self.quirksOff, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleQuirks(bool)'))
|
||||
self.oocToggle = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
|
||||
self.quirksOff.toggled[bool].connect(self.toggleQuirks)
|
||||
self.oocToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
|
||||
self.oocToggle.setCheckable(True)
|
||||
self.connect(self.oocToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleOOC(bool)'))
|
||||
self.unblockchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self)
|
||||
self.connect(self.unblockchum, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('unblockChumSlot()'))
|
||||
self.reportchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self)
|
||||
self.connect(self.reportchum, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('reportThisChum()'))
|
||||
self.logchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
|
||||
self.connect(self.logchum, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('openChumLogs()'))
|
||||
self.oocToggle.toggled[bool].connect(self.toggleOOC)
|
||||
self.unblockchum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self)
|
||||
self.unblockchum.triggered.connect(self.unblockChumSlot)
|
||||
self.reportchum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self)
|
||||
self.reportchum.triggered.connect(self.reportThisChum)
|
||||
self.logchum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
|
||||
self.logchum.triggered.connect(self.openChumLogs)
|
||||
|
||||
# For this, we'll want to use setChecked to toggle these so they match
|
||||
# the user's setting. Alternately (better), use a tristate checkbox, so
|
||||
|
@ -661,20 +642,17 @@ class PesterConvo(QtGui.QFrame):
|
|||
# Easiest solution: Implement a 'Mute' option that overrides all
|
||||
# notifications for that window, save for mentions.
|
||||
# TODO: Look into setting up theme support here.
|
||||
self._beepToggle = QtGui.QAction("Beep on Message", self)
|
||||
self._beepToggle = QtWidgets.QAction("Beep on Message", self)
|
||||
self._beepToggle.setCheckable(True)
|
||||
self.connect(self._beepToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleBeep(bool)'))
|
||||
self._beepToggle.toggled[bool].connect(self.toggleBeep)
|
||||
|
||||
self._flashToggle = QtGui.QAction("Flash on Message", self)
|
||||
self._flashToggle = QtWidgets.QAction("Flash on Message", self)
|
||||
self._flashToggle.setCheckable(True)
|
||||
self.connect(self._flashToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleFlash(bool)'))
|
||||
self._flashToggle.toggled[bool].connect(self.toggleFlash)
|
||||
|
||||
self._muteToggle = QtGui.QAction("Mute Notifications", self)
|
||||
self._muteToggle = QtWidgets.QAction("Mute Notifications", self)
|
||||
self._muteToggle.setCheckable(True)
|
||||
self.connect(self._muteToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleMute(bool)'))
|
||||
self._muteToggle.toggled[bool].connect(self.toggleMute)
|
||||
|
||||
self.optionsMenu.addAction(self.quirksOff)
|
||||
self.optionsMenu.addAction(self.oocToggle)
|
||||
|
@ -755,7 +733,7 @@ class PesterConvo(QtGui.QFrame):
|
|||
def updateColor(self, color):
|
||||
self.chum.color = color
|
||||
def addMessage(self, msg, me=True):
|
||||
if type(msg) in [str, unicode]:
|
||||
if type(msg) in [str, str]:
|
||||
lexmsg = lexMessage(msg)
|
||||
else:
|
||||
lexmsg = msg
|
||||
|
@ -870,7 +848,7 @@ class PesterConvo(QtGui.QFrame):
|
|||
self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]])
|
||||
self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"])
|
||||
self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"])
|
||||
self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
|
||||
self.chumLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding))
|
||||
self.quirksOff.setText(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"])
|
||||
self.addChumAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/addchum"])
|
||||
self.blockAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"])
|
||||
|
@ -886,7 +864,7 @@ class PesterConvo(QtGui.QFrame):
|
|||
# Offloaded to another function, like its sisters.
|
||||
# Fetch the raw text from the input box.
|
||||
text = self.textInput.text()
|
||||
text = unicode(self.textInput.text())
|
||||
text = str(self.textInput.text())
|
||||
|
||||
return parsetools.kxhandleInput(self, text, flavor="convo")
|
||||
|
||||
|
@ -912,8 +890,7 @@ class PesterConvo(QtGui.QFrame):
|
|||
def openChumLogs(self):
|
||||
currentChum = self.chum.handle
|
||||
self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow)
|
||||
self.connect(self.mainwindow.chumList.pesterlogviewer, QtCore.SIGNAL('rejected()'),
|
||||
self.mainwindow.chumList, QtCore.SLOT('closeActiveLog()'))
|
||||
self.mainwindow.chumList.pesterlogviewer.rejected.connect(self.mainwindow.chumList.closeActiveLog)
|
||||
self.mainwindow.chumList.pesterlogviewer.show()
|
||||
self.mainwindow.chumList.pesterlogviewer.raise_()
|
||||
self.mainwindow.chumList.pesterlogviewer.activateWindow()
|
||||
|
@ -930,8 +907,8 @@ class PesterConvo(QtGui.QFrame):
|
|||
def toggleMute(self, toggled):
|
||||
self.notifications_muted = toggled
|
||||
|
||||
messageSent = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
windowClosed = QtCore.pyqtSignal(QtCore.QString)
|
||||
messageSent = QtCore.pyqtSignal('QString', 'QString')
|
||||
windowClosed = QtCore.pyqtSignal('QString')
|
||||
|
||||
aligndict = {"h": {"center": QtCore.Qt.AlignHCenter,
|
||||
"left": QtCore.Qt.AlignLeft,
|
||||
|
|
16
dataobjs.py
|
@ -1,4 +1,4 @@
|
|||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui
|
||||
from datetime import *
|
||||
import re
|
||||
import random
|
||||
|
@ -111,7 +111,7 @@ class pesterQuirks(object):
|
|||
|
||||
newlist = []
|
||||
for (i, o) in enumerate(lexed):
|
||||
if type(o) not in [str, unicode]:
|
||||
if type(o) not in [str, str]:
|
||||
if i == 0:
|
||||
string = " "
|
||||
for p in prefix:
|
||||
|
@ -135,7 +135,7 @@ class pesterQuirks(object):
|
|||
|
||||
final = []
|
||||
for n in newlist:
|
||||
if type(n) in [str, unicode]:
|
||||
if type(n) in [str, str]:
|
||||
final.extend(lexMessage(n))
|
||||
else:
|
||||
final.append(n)
|
||||
|
@ -191,9 +191,9 @@ class PesterProfile(object):
|
|||
def plaindict(self):
|
||||
return (self.handle, {"handle": self.handle,
|
||||
"mood": self.mood.name(),
|
||||
"color": unicode(self.color.name()),
|
||||
"group": unicode(self.group),
|
||||
"notes": unicode(self.notes)})
|
||||
"color": str(self.color.name()),
|
||||
"group": str(self.group),
|
||||
"notes": str(self.notes)})
|
||||
def blocked(self, config):
|
||||
return self.handle in config.getBlocklist()
|
||||
|
||||
|
@ -238,7 +238,7 @@ class PesterProfile(object):
|
|||
(opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials))
|
||||
else:
|
||||
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>." % \
|
||||
(opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials), unicode(reason))
|
||||
(opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials), str(reason))
|
||||
else:
|
||||
initials = timeGrammar.pcf+self.initials()+timeGrammar.number
|
||||
if opchum.handle == reason:
|
||||
|
@ -246,7 +246,7 @@ class PesterProfile(object):
|
|||
(opchum.colorhtml(), opinit, self.colorhtml(), initials)
|
||||
else:
|
||||
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>." % \
|
||||
(opchum.colorhtml(), opinit, self.colorhtml(), initials, unicode(reason))
|
||||
(opchum.colorhtml(), opinit, self.colorhtml(), initials, str(reason))
|
||||
def memopermabanmsg(self, opchum, opgrammar, syscolor, timeGrammar):
|
||||
initials = timeGrammar.pcf+self.initials()+timeGrammar.number
|
||||
opinit = opgrammar.pcf+opchum.initials()+opgrammar.number
|
||||
|
|
42
generic.py
|
@ -1,4 +1,4 @@
|
|||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from datetime import timedelta
|
||||
|
||||
class mysteryTime(timedelta):
|
||||
|
@ -17,7 +17,7 @@ class CaseInsensitiveDict(dict):
|
|||
def __contains__(self, key):
|
||||
return super(CaseInsensitiveDict, self).__contains__(key.lower())
|
||||
def has_key(self, key):
|
||||
return super(CaseInsensitiveDict, self).has_key(key.lower())
|
||||
return key.lower() in super(CaseInsensitiveDict, self)
|
||||
def __delitem__(self, key):
|
||||
super(CaseInsensitiveDict, self).__delitem__(key.lower())
|
||||
|
||||
|
@ -28,7 +28,7 @@ class PesterList(list):
|
|||
class PesterIcon(QtGui.QIcon):
|
||||
def __init__(self, *x):
|
||||
super(PesterIcon, self).__init__(x[0])
|
||||
if type(x[0]) in [str, unicode]:
|
||||
if type(x[0]) in [str, str]:
|
||||
self.icon_pixmap = QtGui.QPixmap(x[0])
|
||||
else:
|
||||
self.icon_pixmap = None
|
||||
|
@ -41,7 +41,7 @@ class PesterIcon(QtGui.QIcon):
|
|||
except IndexError:
|
||||
return None
|
||||
|
||||
class RightClickList(QtGui.QListWidget):
|
||||
class RightClickList(QtWidgets.QListWidget):
|
||||
def contextMenuEvent(self, event):
|
||||
#fuckin Qt
|
||||
if event.reason() == QtGui.QContextMenuEvent.Mouse:
|
||||
|
@ -53,7 +53,7 @@ class RightClickList(QtGui.QListWidget):
|
|||
def getOptionsMenu(self):
|
||||
return self.optionsMenu
|
||||
|
||||
class RightClickTree(QtGui.QTreeWidget):
|
||||
class RightClickTree(QtWidgets.QTreeWidget):
|
||||
def contextMenuEvent(self, event):
|
||||
if event.reason() == QtGui.QContextMenuEvent.Mouse:
|
||||
listing = self.itemAt(event.pos())
|
||||
|
@ -64,49 +64,47 @@ class RightClickTree(QtGui.QTreeWidget):
|
|||
def getOptionsMenu(self):
|
||||
return self.optionsMenu
|
||||
|
||||
class MultiTextDialog(QtGui.QDialog):
|
||||
class MultiTextDialog(QtWidgets.QDialog):
|
||||
def __init__(self, title, parent, *queries):
|
||||
super(MultiTextDialog, self).__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
if len(queries) == 0:
|
||||
return
|
||||
self.inputs = {}
|
||||
layout_1 = QtGui.QHBoxLayout()
|
||||
layout_1 = QtWidgets.QHBoxLayout()
|
||||
for d in queries:
|
||||
label = d["label"]
|
||||
inputname = d["inputname"]
|
||||
value = d.get("value", "")
|
||||
l = QtGui.QLabel(label, self)
|
||||
l = QtWidgets.QLabel(label, self)
|
||||
layout_1.addWidget(l)
|
||||
self.inputs[inputname] = QtGui.QLineEdit(value, self)
|
||||
self.inputs[inputname] = QtWidgets.QLineEdit(value, self)
|
||||
layout_1.addWidget(self.inputs[inputname])
|
||||
self.ok = QtGui.QPushButton("OK", self)
|
||||
self.ok = QtWidgets.QPushButton("OK", self)
|
||||
self.ok.setDefault(True)
|
||||
self.connect(self.ok, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('accept()'))
|
||||
self.cancel = QtGui.QPushButton("CANCEL", self)
|
||||
self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('reject()'))
|
||||
layout_ok = QtGui.QHBoxLayout()
|
||||
self.ok.clicked.connect(self.accept)
|
||||
self.cancel = QtWidgets.QPushButton("CANCEL", self)
|
||||
self.cancel.clicked.connect(self.reject)
|
||||
layout_ok = QtWidgets.QHBoxLayout()
|
||||
layout_ok.addWidget(self.cancel)
|
||||
layout_ok.addWidget(self.ok)
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0 = QtWidgets.QVBoxLayout()
|
||||
layout_0.addLayout(layout_1)
|
||||
layout_0.addLayout(layout_ok)
|
||||
|
||||
self.setLayout(layout_0)
|
||||
def getText(self):
|
||||
r = self.exec_()
|
||||
if r == QtGui.QDialog.Accepted:
|
||||
if r == QtWidgets.QDialog.Accepted:
|
||||
retval = {}
|
||||
for (name, widget) in self.inputs.iteritems():
|
||||
retval[name] = unicode(widget.text())
|
||||
for (name, widget) in self.inputs.items():
|
||||
retval[name] = str(widget.text())
|
||||
return retval
|
||||
else:
|
||||
return None
|
||||
|
||||
class MovingWindow(QtGui.QFrame):
|
||||
class MovingWindow(QtWidgets.QFrame):
|
||||
def __init__(self, *x, **y):
|
||||
super(MovingWindow, self).__init__(*x, **y)
|
||||
self.moving = None
|
||||
|
@ -133,7 +131,7 @@ class NoneSound(object):
|
|||
def play(self): pass
|
||||
def setVolume(self, v): pass
|
||||
|
||||
class WMButton(QtGui.QPushButton):
|
||||
class WMButton(QtWidgets.QPushButton):
|
||||
def __init__(self, icon, parent=None):
|
||||
super(WMButton, self).__init__(icon, "", parent)
|
||||
self.setIconSize(icon.realsize())
|
||||
|
|
128
irc.py
|
@ -1,4 +1,4 @@
|
|||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui
|
||||
from oyoyo.client import IRCClient
|
||||
from oyoyo.cmdhandler import DefaultCommandHandler
|
||||
from oyoyo import helpers, services
|
||||
|
@ -13,6 +13,12 @@ from generic import PesterList
|
|||
from version import _pcVersion
|
||||
|
||||
import ostools
|
||||
try:
|
||||
QString = unicode
|
||||
except NameError:
|
||||
# Python 3
|
||||
QString = str
|
||||
|
||||
if ostools.isOSXBundle():
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
else:
|
||||
|
@ -39,7 +45,7 @@ class PesterIRC(QtCore.QThread):
|
|||
def run(self):
|
||||
try:
|
||||
self.IRCConnect()
|
||||
except socket.error, se:
|
||||
except socket.error as se:
|
||||
self.stopIRC = se
|
||||
return
|
||||
while 1:
|
||||
|
@ -47,12 +53,12 @@ class PesterIRC(QtCore.QThread):
|
|||
try:
|
||||
logging.debug("updateIRC()")
|
||||
res = self.updateIRC()
|
||||
except socket.timeout, se:
|
||||
except socket.timeout as se:
|
||||
logging.debug("timeout in thread %s" % (self))
|
||||
self.cli.close()
|
||||
self.stopIRC = se
|
||||
return
|
||||
except socket.error, se:
|
||||
except socket.error as se:
|
||||
if self.registeredIRC:
|
||||
self.stopIRC = None
|
||||
else:
|
||||
|
@ -74,13 +80,13 @@ class PesterIRC(QtCore.QThread):
|
|||
@QtCore.pyqtSlot()
|
||||
def updateIRC(self):
|
||||
try:
|
||||
res = self.conn.next()
|
||||
except socket.timeout, se:
|
||||
res = next(self.conn)
|
||||
except socket.timeout as se:
|
||||
if self.registeredIRC:
|
||||
return True
|
||||
else:
|
||||
raise se
|
||||
except socket.error, se:
|
||||
except socket.error as se:
|
||||
raise se
|
||||
except StopIteration:
|
||||
self.conn = self.cli.conn()
|
||||
|
@ -98,18 +104,18 @@ class PesterIRC(QtCore.QThread):
|
|||
@QtCore.pyqtSlot(PesterList)
|
||||
def getMoods(self, chums):
|
||||
self.cli.command_handler.getMood(*chums)
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def sendNotice(self, text, handle):
|
||||
h = unicode(handle)
|
||||
t = unicode(text)
|
||||
h = str(handle)
|
||||
t = str(text)
|
||||
try:
|
||||
helpers.notice(self.cli, h, t)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def sendMessage(self, text, handle):
|
||||
h = unicode(handle)
|
||||
textl = [unicode(text)]
|
||||
h = str(handle)
|
||||
textl = [str(text)]
|
||||
def splittext(l):
|
||||
if len(l[0]) > 450:
|
||||
space = l[0].rfind(" ", 0,430)
|
||||
|
@ -152,18 +158,18 @@ class PesterIRC(QtCore.QThread):
|
|||
helpers.msg(self.cli, h, t)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString, bool)
|
||||
@QtCore.pyqtSlot(QString, bool)
|
||||
def startConvo(self, handle, initiated):
|
||||
h = unicode(handle)
|
||||
h = str(handle)
|
||||
try:
|
||||
if initiated:
|
||||
helpers.msg(self.cli, h, "PESTERCHUM:BEGIN")
|
||||
helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def endConvo(self, handle):
|
||||
h = unicode(handle)
|
||||
h = str(handle)
|
||||
try:
|
||||
helpers.msg(self.cli, h, "PESTERCHUM:CEASE")
|
||||
except socket.error:
|
||||
|
@ -191,28 +197,28 @@ class PesterIRC(QtCore.QThread):
|
|||
@QtCore.pyqtSlot()
|
||||
def updateColor(self):
|
||||
me = self.mainwindow.profile()
|
||||
for h in self.mainwindow.convos.keys():
|
||||
for h in list(self.mainwindow.convos.keys()):
|
||||
try:
|
||||
helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def blockedChum(self, handle):
|
||||
h = unicode(handle)
|
||||
h = str(handle)
|
||||
try:
|
||||
helpers.msg(self.cli, h, "PESTERCHUM:BLOCK")
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def unblockedChum(self, handle):
|
||||
h = unicode(handle)
|
||||
h = str(handle)
|
||||
try:
|
||||
helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK")
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def requestNames(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
try:
|
||||
helpers.names(self.cli, c)
|
||||
except socket.error:
|
||||
|
@ -223,60 +229,60 @@ class PesterIRC(QtCore.QThread):
|
|||
helpers.channel_list(self.cli)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def joinChannel(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
try:
|
||||
helpers.join(self.cli, c)
|
||||
helpers.mode(self.cli, c, "", None)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def leftChannel(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
try:
|
||||
helpers.part(self.cli, c)
|
||||
self.cli.command_handler.joined = False
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def kickUser(self, handle, channel):
|
||||
l = handle.split(":")
|
||||
c = unicode(channel)
|
||||
h = unicode(l[0])
|
||||
c = str(channel)
|
||||
h = str(l[0])
|
||||
if len(l) > 1:
|
||||
reason = unicode(l[1])
|
||||
reason = str(l[1])
|
||||
if len(l) > 2:
|
||||
for x in l[2:]:
|
||||
reason += unicode(":") + unicode(x)
|
||||
reason += str(":") + str(x)
|
||||
else:
|
||||
reason = ""
|
||||
try:
|
||||
helpers.kick(self.cli, h, c, reason)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString, QString)
|
||||
def setChannelMode(self, channel, mode, command):
|
||||
c = unicode(channel)
|
||||
m = unicode(mode)
|
||||
cmd = unicode(command)
|
||||
c = str(channel)
|
||||
m = str(mode)
|
||||
cmd = str(command)
|
||||
if cmd == "":
|
||||
cmd = None
|
||||
try:
|
||||
helpers.mode(self.cli, c, m, cmd)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def channelNames(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
try:
|
||||
helpers.names(self.cli, c)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def inviteChum(self, handle, channel):
|
||||
h = unicode(handle)
|
||||
c = unicode(channel)
|
||||
h = str(handle)
|
||||
c = str(channel)
|
||||
try:
|
||||
helpers.invite(self.cli, h, c)
|
||||
except socket.error:
|
||||
|
@ -299,34 +305,34 @@ class PesterIRC(QtCore.QThread):
|
|||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def killSomeQuirks(self, channel, handle):
|
||||
c = unicode(channel)
|
||||
h = unicode(handle)
|
||||
c = str(channel)
|
||||
h = str(handle)
|
||||
try:
|
||||
helpers.ctcp(self.cli, c, "NOQUIRKS", h)
|
||||
except socket.error:
|
||||
self.setConnectionBroken()
|
||||
|
||||
moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood)
|
||||
colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
|
||||
messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
memoReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
|
||||
noticeReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
inviteReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
timeCommand = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
|
||||
namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList)
|
||||
moodUpdated = QtCore.pyqtSignal('QString', Mood)
|
||||
colorUpdated = QtCore.pyqtSignal('QString', QtGui.QColor)
|
||||
messageReceived = QtCore.pyqtSignal('QString', 'QString')
|
||||
memoReceived = QtCore.pyqtSignal('QString', 'QString', 'QString')
|
||||
noticeReceived = QtCore.pyqtSignal('QString', 'QString')
|
||||
inviteReceived = QtCore.pyqtSignal('QString', 'QString')
|
||||
timeCommand = QtCore.pyqtSignal('QString', 'QString', 'QString')
|
||||
namesReceived = QtCore.pyqtSignal('QString', PesterList)
|
||||
channelListReceived = QtCore.pyqtSignal(PesterList)
|
||||
nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
myHandleChanged = QtCore.pyqtSignal(QtCore.QString)
|
||||
chanInviteOnly = QtCore.pyqtSignal(QtCore.QString)
|
||||
modesUpdated = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
nickCollision = QtCore.pyqtSignal('QString', 'QString')
|
||||
myHandleChanged = QtCore.pyqtSignal('QString')
|
||||
chanInviteOnly = QtCore.pyqtSignal('QString')
|
||||
modesUpdated = QtCore.pyqtSignal('QString', 'QString')
|
||||
connected = QtCore.pyqtSignal()
|
||||
userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
|
||||
QtCore.QString)
|
||||
cannotSendToChan = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
|
||||
userPresentUpdate = QtCore.pyqtSignal('QString', 'QString',
|
||||
'QString')
|
||||
cannotSendToChan = QtCore.pyqtSignal('QString', 'QString')
|
||||
tooManyPeeps = QtCore.pyqtSignal()
|
||||
quirkDisable = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
|
||||
quirkDisable = QtCore.pyqtSignal('QString', 'QString', 'QString')
|
||||
|
||||
class PesterHandler(DefaultCommandHandler):
|
||||
def notice(self, nick, chan, msg):
|
||||
|
|
|
@ -66,11 +66,12 @@ TIDY_MARKUP = 0
|
|||
PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
|
||||
|
||||
# ---------- required modules (should come with any Python distribution) ----------
|
||||
import sgmllib, re, sys, copy, urlparse, time, rfc822, types, cgi, urllib, urllib2
|
||||
import sgmllib, re, sys, copy, urllib.parse, time, rfc822, types, cgi, urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse
|
||||
# We *really* need to find a proper alternative for smgllib
|
||||
try:
|
||||
from cStringIO import StringIO as _StringIO
|
||||
from io import StringIO as _StringIO
|
||||
except:
|
||||
from StringIO import StringIO as _StringIO
|
||||
from io import StringIO as _StringIO
|
||||
|
||||
# ---------- optional modules (feedparser will work without these, but with reduced functionality) ----------
|
||||
|
||||
|
@ -191,7 +192,7 @@ class FeedParserDict(UserDict):
|
|||
if key == 'categories':
|
||||
return [(tag['scheme'], tag['term']) for tag in UserDict.__getitem__(self, 'tags')]
|
||||
realkey = self.keymap.get(key, key)
|
||||
if type(realkey) == types.ListType:
|
||||
if type(realkey) == list:
|
||||
for k in realkey:
|
||||
if UserDict.has_key(self, k):
|
||||
return UserDict.__getitem__(self, k)
|
||||
|
@ -200,21 +201,21 @@ class FeedParserDict(UserDict):
|
|||
return UserDict.__getitem__(self, realkey)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
for k in self.keymap.keys():
|
||||
for k in list(self.keymap.keys()):
|
||||
if key == k:
|
||||
key = self.keymap[k]
|
||||
if type(key) == types.ListType:
|
||||
if type(key) == list:
|
||||
key = key[0]
|
||||
return UserDict.__setitem__(self, key, value)
|
||||
|
||||
def get(self, key, default=None):
|
||||
if self.has_key(key):
|
||||
if key in self:
|
||||
return self[key]
|
||||
else:
|
||||
return default
|
||||
|
||||
def setdefault(self, key, value):
|
||||
if not self.has_key(key):
|
||||
if key not in self:
|
||||
self[key] = value
|
||||
return self[key]
|
||||
|
||||
|
@ -233,7 +234,7 @@ class FeedParserDict(UserDict):
|
|||
assert not key.startswith('_')
|
||||
return self.__getitem__(key)
|
||||
except:
|
||||
raise AttributeError, "object has no attribute '%s'" % key
|
||||
raise AttributeError("object has no attribute '%s'" % key)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key.startswith('_') or key == 'data':
|
||||
|
@ -242,7 +243,7 @@ class FeedParserDict(UserDict):
|
|||
return self.__setitem__(key, value)
|
||||
|
||||
def __contains__(self, key):
|
||||
return self.has_key(key)
|
||||
return key in self
|
||||
|
||||
def zopeCompatibilityHack():
|
||||
global FeedParserDict
|
||||
|
@ -277,13 +278,13 @@ def _ebcdic_to_ascii(s):
|
|||
)
|
||||
import string
|
||||
_ebcdic_to_ascii_map = string.maketrans( \
|
||||
''.join(map(chr, range(256))), ''.join(map(chr, emap)))
|
||||
''.join(map(chr, list(range(256)))), ''.join(map(chr, emap)))
|
||||
return s.translate(_ebcdic_to_ascii_map)
|
||||
|
||||
_urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
|
||||
def _urljoin(base, uri):
|
||||
uri = _urifixer.sub(r'\1\3', uri)
|
||||
return urlparse.urljoin(base, uri)
|
||||
return urllib.parse.urljoin(base, uri)
|
||||
|
||||
class _FeedParserMixin:
|
||||
namespaces = {'': '',
|
||||
|
@ -357,7 +358,7 @@ class _FeedParserMixin:
|
|||
def __init__(self, baseuri=None, baselang=None, encoding='utf-8'):
|
||||
if _debug: sys.stderr.write('initializing FeedParser\n')
|
||||
if not self._matchnamespaces:
|
||||
for k, v in self.namespaces.items():
|
||||
for k, v in list(self.namespaces.items()):
|
||||
self._matchnamespaces[k.lower()] = v
|
||||
self.feeddata = FeedParserDict() # feed-level data
|
||||
self.encoding = encoding # character encoding
|
||||
|
@ -420,7 +421,7 @@ class _FeedParserMixin:
|
|||
self.trackNamespace(None, uri)
|
||||
|
||||
# track inline content
|
||||
if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
if self.incontent and 'type' in self.contentparams and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
# element declared itself as escaped markup, but it isn't really
|
||||
self.contentparams['type'] = 'application/xhtml+xml'
|
||||
if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
|
||||
|
@ -436,7 +437,7 @@ class _FeedParserMixin:
|
|||
return self.handle_data('<%s%s>' % (tag, ''.join([' %s="%s"' % t for t in attrs])), escape=0)
|
||||
|
||||
# match namespaces
|
||||
if tag.find(':') <> -1:
|
||||
if tag.find(':') != -1:
|
||||
prefix, suffix = tag.split(':', 1)
|
||||
else:
|
||||
prefix, suffix = '', tag
|
||||
|
@ -461,7 +462,7 @@ class _FeedParserMixin:
|
|||
def unknown_endtag(self, tag):
|
||||
if _debug: sys.stderr.write('end %s\n' % tag)
|
||||
# match namespaces
|
||||
if tag.find(':') <> -1:
|
||||
if tag.find(':') != -1:
|
||||
prefix, suffix = tag.split(':', 1)
|
||||
else:
|
||||
prefix, suffix = '', tag
|
||||
|
@ -478,7 +479,7 @@ class _FeedParserMixin:
|
|||
self.pop(prefix + suffix)
|
||||
|
||||
# track inline content
|
||||
if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
if self.incontent and 'type' in self.contentparams and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
# element declared itself as escaped markup, but it isn't really
|
||||
self.contentparams['type'] = 'application/xhtml+xml'
|
||||
if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
|
||||
|
@ -506,7 +507,7 @@ class _FeedParserMixin:
|
|||
c = int(ref[1:], 16)
|
||||
else:
|
||||
c = int(ref)
|
||||
text = unichr(c).encode('utf-8')
|
||||
text = chr(c).encode('utf-8')
|
||||
self.elementstack[-1][2].append(text)
|
||||
|
||||
def handle_entityref(self, ref):
|
||||
|
@ -518,16 +519,16 @@ class _FeedParserMixin:
|
|||
else:
|
||||
# entity resolution graciously donated by Aaron Swartz
|
||||
def name2cp(k):
|
||||
import htmlentitydefs
|
||||
import html.entities
|
||||
if hasattr(htmlentitydefs, 'name2codepoint'): # requires Python 2.3
|
||||
return htmlentitydefs.name2codepoint[k]
|
||||
k = htmlentitydefs.entitydefs[k]
|
||||
return html.entities.name2codepoint[k]
|
||||
k = html.entities.entitydefs[k]
|
||||
if k.startswith('&#') and k.endswith(';'):
|
||||
return int(k[2:-1]) # not in latin-1
|
||||
return ord(k)
|
||||
try: name2cp(ref)
|
||||
except KeyError: text = '&%s;' % ref
|
||||
else: text = unichr(name2cp(ref)).encode('utf-8')
|
||||
else: text = chr(name2cp(ref)).encode('utf-8')
|
||||
self.elementstack[-1][2].append(text)
|
||||
|
||||
def handle_data(self, text, escape=1):
|
||||
|
@ -579,11 +580,11 @@ class _FeedParserMixin:
|
|||
self.version = 'rss10'
|
||||
if loweruri == 'http://www.w3.org/2005/atom' and not self.version:
|
||||
self.version = 'atom10'
|
||||
if loweruri.find('backend.userland.com/rss') <> -1:
|
||||
if loweruri.find('backend.userland.com/rss') != -1:
|
||||
# match any backend.userland.com namespace
|
||||
uri = 'http://backend.userland.com/rss'
|
||||
loweruri = uri
|
||||
if self._matchnamespaces.has_key(loweruri):
|
||||
if loweruri in self._matchnamespaces:
|
||||
self.namespacemap[prefix] = self._matchnamespaces[loweruri]
|
||||
self.namespacesInUse[self._matchnamespaces[loweruri]] = uri
|
||||
else:
|
||||
|
@ -645,9 +646,9 @@ class _FeedParserMixin:
|
|||
if element in self.can_contain_dangerous_markup:
|
||||
output = _sanitizeHTML(output, self.encoding)
|
||||
|
||||
if self.encoding and type(output) != type(u''):
|
||||
if self.encoding and type(output) != type(''):
|
||||
try:
|
||||
output = unicode(output, self.encoding)
|
||||
output = str(output, self.encoding)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -704,7 +705,7 @@ class _FeedParserMixin:
|
|||
|
||||
def _mapToStandardPrefix(self, name):
|
||||
colonpos = name.find(':')
|
||||
if colonpos <> -1:
|
||||
if colonpos != -1:
|
||||
prefix = name[:colonpos]
|
||||
suffix = name[colonpos+1:]
|
||||
prefix = self.namespacemap.get(prefix, prefix)
|
||||
|
@ -767,11 +768,11 @@ class _FeedParserMixin:
|
|||
_start_feedinfo = _start_channel
|
||||
|
||||
def _cdf_common(self, attrsD):
|
||||
if attrsD.has_key('lastmod'):
|
||||
if 'lastmod' in attrsD:
|
||||
self._start_modified({})
|
||||
self.elementstack[-1][-1] = attrsD['lastmod']
|
||||
self._end_modified()
|
||||
if attrsD.has_key('href'):
|
||||
if 'href' in attrsD:
|
||||
self._start_link({})
|
||||
self.elementstack[-1][-1] = attrsD['href']
|
||||
self._end_link()
|
||||
|
@ -1147,7 +1148,7 @@ class _FeedParserMixin:
|
|||
attrsD.setdefault('rel', 'alternate')
|
||||
attrsD.setdefault('type', 'text/html')
|
||||
attrsD = self._itsAnHrefDamnIt(attrsD)
|
||||
if attrsD.has_key('href'):
|
||||
if 'href' in attrsD:
|
||||
attrsD['href'] = self.resolveURI(attrsD['href'])
|
||||
expectingText = self.infeed or self.inentry or self.insource
|
||||
context = self._getContext()
|
||||
|
@ -1155,7 +1156,7 @@ class _FeedParserMixin:
|
|||
context['links'].append(FeedParserDict(attrsD))
|
||||
if attrsD['rel'] == 'enclosure':
|
||||
self._start_enclosure(attrsD)
|
||||
if attrsD.has_key('href'):
|
||||
if 'href' in attrsD:
|
||||
expectingText = 0
|
||||
if (attrsD.get('rel') == 'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types):
|
||||
context['link'] = attrsD['href']
|
||||
|
@ -1178,7 +1179,7 @@ class _FeedParserMixin:
|
|||
|
||||
def _end_guid(self):
|
||||
value = self.pop('id')
|
||||
self._save('guidislink', self.guidislink and not self._getContext().has_key('link'))
|
||||
self._save('guidislink', self.guidislink and 'link' not in self._getContext())
|
||||
if self.guidislink:
|
||||
# guid acts as link, but only if 'ispermalink' is not present or is 'true',
|
||||
# and only if the item doesn't already have a link element
|
||||
|
@ -1201,7 +1202,7 @@ class _FeedParserMixin:
|
|||
|
||||
def _start_description(self, attrsD):
|
||||
context = self._getContext()
|
||||
if context.has_key('summary'):
|
||||
if 'summary' in context:
|
||||
self._summaryKey = 'content'
|
||||
self._start_content(attrsD)
|
||||
else:
|
||||
|
@ -1234,7 +1235,7 @@ class _FeedParserMixin:
|
|||
def _start_generator(self, attrsD):
|
||||
if attrsD:
|
||||
attrsD = self._itsAnHrefDamnIt(attrsD)
|
||||
if attrsD.has_key('href'):
|
||||
if 'href' in attrsD:
|
||||
attrsD['href'] = self.resolveURI(attrsD['href'])
|
||||
self._getContext()['generator_detail'] = FeedParserDict(attrsD)
|
||||
self.push('generator', 1)
|
||||
|
@ -1242,7 +1243,7 @@ class _FeedParserMixin:
|
|||
def _end_generator(self):
|
||||
value = self.pop('generator')
|
||||
context = self._getContext()
|
||||
if context.has_key('generator_detail'):
|
||||
if 'generator_detail' in context:
|
||||
context['generator_detail']['name'] = value
|
||||
|
||||
def _start_admin_generatoragent(self, attrsD):
|
||||
|
@ -1262,7 +1263,7 @@ class _FeedParserMixin:
|
|||
|
||||
def _start_summary(self, attrsD):
|
||||
context = self._getContext()
|
||||
if context.has_key('summary'):
|
||||
if 'summary' in context:
|
||||
self._summaryKey = 'content'
|
||||
self._start_content(attrsD)
|
||||
else:
|
||||
|
@ -1352,7 +1353,7 @@ if _XML_AVAILABLE:
|
|||
def startElementNS(self, name, qname, attrs):
|
||||
namespace, localname = name
|
||||
lowernamespace = str(namespace or '').lower()
|
||||
if lowernamespace.find('backend.userland.com/rss') <> -1:
|
||||
if lowernamespace.find('backend.userland.com/rss') != -1:
|
||||
# match any backend.userland.com namespace
|
||||
namespace = 'http://backend.userland.com/rss'
|
||||
lowernamespace = namespace
|
||||
|
@ -1361,12 +1362,12 @@ if _XML_AVAILABLE:
|
|||
else:
|
||||
givenprefix = None
|
||||
prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
|
||||
if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix):
|
||||
raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
|
||||
if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and givenprefix not in self.namespacesInUse:
|
||||
raise UndeclaredNamespace("'%s' is not associated with a namespace" % givenprefix)
|
||||
if prefix:
|
||||
localname = prefix + ':' + localname
|
||||
localname = str(localname).lower()
|
||||
if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
|
||||
if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, list(attrs.items()), localname))
|
||||
|
||||
# qname implementation is horribly broken in Python 2.1 (it
|
||||
# doesn't report any), and slightly broken in Python 2.2 (it
|
||||
|
@ -1376,7 +1377,7 @@ if _XML_AVAILABLE:
|
|||
# at all). Thanks to MatejC for helping me test this and
|
||||
# tirelessly telling me that it didn't work yet.
|
||||
attrsD = {}
|
||||
for (namespace, attrlocalname), attrvalue in attrs._attrs.items():
|
||||
for (namespace, attrlocalname), attrvalue in list(attrs._attrs.items()):
|
||||
lowernamespace = (namespace or '').lower()
|
||||
prefix = self._matchnamespaces.get(lowernamespace, '')
|
||||
if prefix:
|
||||
|
@ -1384,7 +1385,7 @@ if _XML_AVAILABLE:
|
|||
attrsD[str(attrlocalname).lower()] = attrvalue
|
||||
for qname in attrs.getQNames():
|
||||
attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
|
||||
self.unknown_starttag(localname, attrsD.items())
|
||||
self.unknown_starttag(localname, list(attrsD.items()))
|
||||
|
||||
def characters(self, text):
|
||||
self.handle_data(text)
|
||||
|
@ -1436,7 +1437,7 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser):
|
|||
data = re.sub(r'<([^<\s]+?)\s*/>', self._shorttag_replace, data)
|
||||
data = data.replace(''', "'")
|
||||
data = data.replace('"', '"')
|
||||
if self.encoding and type(data) == type(u''):
|
||||
if self.encoding and type(data) == type(''):
|
||||
data = data.encode(self.encoding)
|
||||
sgmllib.SGMLParser.feed(self, data)
|
||||
|
||||
|
@ -1454,10 +1455,10 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser):
|
|||
uattrs = []
|
||||
# thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
|
||||
for key, value in attrs:
|
||||
if type(value) != type(u''):
|
||||
value = unicode(value, self.encoding)
|
||||
uattrs.append((unicode(key, self.encoding), value))
|
||||
strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
|
||||
if type(value) != type(''):
|
||||
value = str(value, self.encoding)
|
||||
uattrs.append((str(key, self.encoding), value))
|
||||
strattrs = ''.join([' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
|
||||
if tag in self.elements_no_end_tag:
|
||||
self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
|
||||
else:
|
||||
|
@ -1541,7 +1542,7 @@ class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
|
|||
data = data.replace('"', '"')
|
||||
data = data.replace(''', ''')
|
||||
data = data.replace(''', ''')
|
||||
if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
if 'type' in self.contentparams and not self.contentparams.get('type', 'xml').endswith('xml'):
|
||||
data = data.replace('<', '<')
|
||||
data = data.replace('>', '>')
|
||||
data = data.replace('&', '&')
|
||||
|
@ -1671,12 +1672,12 @@ def _sanitizeHTML(htmlSource, encoding):
|
|||
except:
|
||||
pass
|
||||
if _tidy:
|
||||
utf8 = type(data) == type(u'')
|
||||
utf8 = type(data) == type('')
|
||||
if utf8:
|
||||
data = data.encode('utf-8')
|
||||
data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
|
||||
if utf8:
|
||||
data = unicode(data, 'utf-8')
|
||||
data = str(data, 'utf-8')
|
||||
if data.count('<body'):
|
||||
data = data.split('<body', 1)[1]
|
||||
if data.count('>'):
|
||||
|
@ -1686,7 +1687,7 @@ def _sanitizeHTML(htmlSource, encoding):
|
|||
data = data.strip().replace('\r\n', '\n')
|
||||
return data
|
||||
|
||||
class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
|
||||
class _FeedURLHandler(urllib.request.HTTPDigestAuthHandler, urllib.request.HTTPRedirectHandler, urllib.request.HTTPDefaultErrorHandler):
|
||||
def http_error_default(self, req, fp, code, msg, headers):
|
||||
if ((code / 100) == 3) and (code != 304):
|
||||
return self.http_error_302(req, fp, code, msg, headers)
|
||||
|
@ -1695,8 +1696,8 @@ class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler
|
|||
return infourl
|
||||
|
||||
def http_error_302(self, req, fp, code, msg, headers):
|
||||
if headers.dict.has_key('location'):
|
||||
infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
|
||||
if 'location' in headers.dict:
|
||||
infourl = urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
|
||||
else:
|
||||
infourl = urllib.addinfourl(fp, headers, req.get_full_url())
|
||||
if not hasattr(infourl, 'status'):
|
||||
|
@ -1704,8 +1705,8 @@ class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler
|
|||
return infourl
|
||||
|
||||
def http_error_301(self, req, fp, code, msg, headers):
|
||||
if headers.dict.has_key('location'):
|
||||
infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
|
||||
if 'location' in headers.dict:
|
||||
infourl = urllib.request.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
|
||||
else:
|
||||
infourl = urllib.addinfourl(fp, headers, req.get_full_url())
|
||||
if not hasattr(infourl, 'status'):
|
||||
|
@ -1727,7 +1728,7 @@ class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler
|
|||
# header the server sent back (for the realm) and retry
|
||||
# the request with the appropriate digest auth headers instead.
|
||||
# This evil genius hack has been brought to you by Aaron Swartz.
|
||||
host = urlparse.urlparse(req.get_full_url())[1]
|
||||
host = urllib.parse.urlparse(req.get_full_url())[1]
|
||||
try:
|
||||
assert sys.version.split()[0] >= '2.3.3'
|
||||
assert base64 != None
|
||||
|
@ -1773,21 +1774,21 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h
|
|||
if url_file_stream_or_string == '-':
|
||||
return sys.stdin
|
||||
|
||||
if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp'):
|
||||
if urllib.parse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp'):
|
||||
if not agent:
|
||||
agent = USER_AGENT
|
||||
# test for inline user:password for basic auth
|
||||
auth = None
|
||||
if base64:
|
||||
urltype, rest = urllib.splittype(url_file_stream_or_string)
|
||||
realhost, rest = urllib.splithost(rest)
|
||||
urltype, rest = urllib.parse.splittype(url_file_stream_or_string)
|
||||
realhost, rest = urllib.parse.splithost(rest)
|
||||
if realhost:
|
||||
user_passwd, realhost = urllib.splituser(realhost)
|
||||
user_passwd, realhost = urllib.parse.splituser(realhost)
|
||||
if user_passwd:
|
||||
url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
|
||||
auth = base64.encodestring(user_passwd).strip()
|
||||
# try to open with urllib2 (to use optional headers)
|
||||
request = urllib2.Request(url_file_stream_or_string)
|
||||
request = urllib.request.Request(url_file_stream_or_string)
|
||||
request.add_header('User-Agent', agent)
|
||||
if etag:
|
||||
request.add_header('If-None-Match', etag)
|
||||
|
@ -1814,7 +1815,7 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h
|
|||
if ACCEPT_HEADER:
|
||||
request.add_header('Accept', ACCEPT_HEADER)
|
||||
request.add_header('A-IM', 'feed') # RFC 3229 support
|
||||
opener = apply(urllib2.build_opener, tuple([_FeedURLHandler()] + handlers))
|
||||
opener = urllib.request.build_opener(*tuple([_FeedURLHandler()] + handlers))
|
||||
opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent
|
||||
try:
|
||||
return opener.open(request)
|
||||
|
@ -1910,7 +1911,7 @@ def _parse_date_iso8601(dateString):
|
|||
day = int(day)
|
||||
# special case of the century - is the first year of the 21st century
|
||||
# 2000 or 2001 ? The debate goes on...
|
||||
if 'century' in params.keys():
|
||||
if 'century' in list(params.keys()):
|
||||
year = (int(params['century']) - 1) * 100 + 1
|
||||
# in ISO 8601 most fields are optional
|
||||
for field in ['hour', 'minute', 'second', 'tzhour', 'tzmin']:
|
||||
|
@ -1946,17 +1947,17 @@ def _parse_date_iso8601(dateString):
|
|||
registerDateHandler(_parse_date_iso8601)
|
||||
|
||||
# 8-bit date handling routines written by ytrewq1.
|
||||
_korean_year = u'\ub144' # b3e2 in euc-kr
|
||||
_korean_month = u'\uc6d4' # bff9 in euc-kr
|
||||
_korean_day = u'\uc77c' # c0cf in euc-kr
|
||||
_korean_am = u'\uc624\uc804' # bfc0 c0fc in euc-kr
|
||||
_korean_pm = u'\uc624\ud6c4' # bfc0 c8c4 in euc-kr
|
||||
_korean_year = '\ub144' # b3e2 in euc-kr
|
||||
_korean_month = '\uc6d4' # bff9 in euc-kr
|
||||
_korean_day = '\uc77c' # c0cf in euc-kr
|
||||
_korean_am = '\uc624\uc804' # bfc0 c0fc in euc-kr
|
||||
_korean_pm = '\uc624\ud6c4' # bfc0 c8c4 in euc-kr
|
||||
|
||||
_korean_onblog_date_re = \
|
||||
re.compile('(\d{4})%s\s+(\d{2})%s\s+(\d{2})%s\s+(\d{2}):(\d{2}):(\d{2})' % \
|
||||
(_korean_year, _korean_month, _korean_day))
|
||||
_korean_nate_date_re = \
|
||||
re.compile(u'(\d{4})-(\d{2})-(\d{2})\s+(%s|%s)\s+(\d{,2}):(\d{,2}):(\d{,2})' % \
|
||||
re.compile('(\d{4})-(\d{2})-(\d{2})\s+(%s|%s)\s+(\d{,2}):(\d{,2}):(\d{,2})' % \
|
||||
(_korean_am, _korean_pm))
|
||||
def _parse_date_onblog(dateString):
|
||||
'''Parse a string according to the OnBlog 8-bit date format'''
|
||||
|
@ -2006,40 +2007,40 @@ registerDateHandler(_parse_date_mssql)
|
|||
# Unicode strings for Greek date strings
|
||||
_greek_months = \
|
||||
{ \
|
||||
u'\u0399\u03b1\u03bd': u'Jan', # c9e1ed in iso-8859-7
|
||||
u'\u03a6\u03b5\u03b2': u'Feb', # d6e5e2 in iso-8859-7
|
||||
u'\u039c\u03ac\u03ce': u'Mar', # ccdcfe in iso-8859-7
|
||||
u'\u039c\u03b1\u03ce': u'Mar', # cce1fe in iso-8859-7
|
||||
u'\u0391\u03c0\u03c1': u'Apr', # c1f0f1 in iso-8859-7
|
||||
u'\u039c\u03ac\u03b9': u'May', # ccdce9 in iso-8859-7
|
||||
u'\u039c\u03b1\u03ca': u'May', # cce1fa in iso-8859-7
|
||||
u'\u039c\u03b1\u03b9': u'May', # cce1e9 in iso-8859-7
|
||||
u'\u0399\u03bf\u03cd\u03bd': u'Jun', # c9effded in iso-8859-7
|
||||
u'\u0399\u03bf\u03bd': u'Jun', # c9efed in iso-8859-7
|
||||
u'\u0399\u03bf\u03cd\u03bb': u'Jul', # c9effdeb in iso-8859-7
|
||||
u'\u0399\u03bf\u03bb': u'Jul', # c9f9eb in iso-8859-7
|
||||
u'\u0391\u03cd\u03b3': u'Aug', # c1fde3 in iso-8859-7
|
||||
u'\u0391\u03c5\u03b3': u'Aug', # c1f5e3 in iso-8859-7
|
||||
u'\u03a3\u03b5\u03c0': u'Sep', # d3e5f0 in iso-8859-7
|
||||
u'\u039f\u03ba\u03c4': u'Oct', # cfeaf4 in iso-8859-7
|
||||
u'\u039d\u03bf\u03ad': u'Nov', # cdefdd in iso-8859-7
|
||||
u'\u039d\u03bf\u03b5': u'Nov', # cdefe5 in iso-8859-7
|
||||
u'\u0394\u03b5\u03ba': u'Dec', # c4e5ea in iso-8859-7
|
||||
'\u0399\u03b1\u03bd': 'Jan', # c9e1ed in iso-8859-7
|
||||
'\u03a6\u03b5\u03b2': 'Feb', # d6e5e2 in iso-8859-7
|
||||
'\u039c\u03ac\u03ce': 'Mar', # ccdcfe in iso-8859-7
|
||||
'\u039c\u03b1\u03ce': 'Mar', # cce1fe in iso-8859-7
|
||||
'\u0391\u03c0\u03c1': 'Apr', # c1f0f1 in iso-8859-7
|
||||
'\u039c\u03ac\u03b9': 'May', # ccdce9 in iso-8859-7
|
||||
'\u039c\u03b1\u03ca': 'May', # cce1fa in iso-8859-7
|
||||
'\u039c\u03b1\u03b9': 'May', # cce1e9 in iso-8859-7
|
||||
'\u0399\u03bf\u03cd\u03bd': 'Jun', # c9effded in iso-8859-7
|
||||
'\u0399\u03bf\u03bd': 'Jun', # c9efed in iso-8859-7
|
||||
'\u0399\u03bf\u03cd\u03bb': 'Jul', # c9effdeb in iso-8859-7
|
||||
'\u0399\u03bf\u03bb': 'Jul', # c9f9eb in iso-8859-7
|
||||
'\u0391\u03cd\u03b3': 'Aug', # c1fde3 in iso-8859-7
|
||||
'\u0391\u03c5\u03b3': 'Aug', # c1f5e3 in iso-8859-7
|
||||
'\u03a3\u03b5\u03c0': 'Sep', # d3e5f0 in iso-8859-7
|
||||
'\u039f\u03ba\u03c4': 'Oct', # cfeaf4 in iso-8859-7
|
||||
'\u039d\u03bf\u03ad': 'Nov', # cdefdd in iso-8859-7
|
||||
'\u039d\u03bf\u03b5': 'Nov', # cdefe5 in iso-8859-7
|
||||
'\u0394\u03b5\u03ba': 'Dec', # c4e5ea in iso-8859-7
|
||||
}
|
||||
|
||||
_greek_wdays = \
|
||||
{ \
|
||||
u'\u039a\u03c5\u03c1': u'Sun', # caf5f1 in iso-8859-7
|
||||
u'\u0394\u03b5\u03c5': u'Mon', # c4e5f5 in iso-8859-7
|
||||
u'\u03a4\u03c1\u03b9': u'Tue', # d4f1e9 in iso-8859-7
|
||||
u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7
|
||||
u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7
|
||||
u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7
|
||||
u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7
|
||||
'\u039a\u03c5\u03c1': 'Sun', # caf5f1 in iso-8859-7
|
||||
'\u0394\u03b5\u03c5': 'Mon', # c4e5f5 in iso-8859-7
|
||||
'\u03a4\u03c1\u03b9': 'Tue', # d4f1e9 in iso-8859-7
|
||||
'\u03a4\u03b5\u03c4': 'Wed', # d4e5f4 in iso-8859-7
|
||||
'\u03a0\u03b5\u03bc': 'Thu', # d0e5ec in iso-8859-7
|
||||
'\u03a0\u03b1\u03c1': 'Fri', # d0e1f1 in iso-8859-7
|
||||
'\u03a3\u03b1\u03b2': 'Sat', # d3e1e2 in iso-8859-7
|
||||
}
|
||||
|
||||
_greek_date_format_re = \
|
||||
re.compile(u'([^,]+),\s+(\d{2})\s+([^\s]+)\s+(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([^\s]+)')
|
||||
re.compile('([^,]+),\s+(\d{2})\s+([^\s]+)\s+(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([^\s]+)')
|
||||
|
||||
def _parse_date_greek(dateString):
|
||||
'''Parse a string according to a Greek 8-bit date format.'''
|
||||
|
@ -2061,22 +2062,22 @@ registerDateHandler(_parse_date_greek)
|
|||
# Unicode strings for Hungarian date strings
|
||||
_hungarian_months = \
|
||||
{ \
|
||||
u'janu\u00e1r': u'01', # e1 in iso-8859-2
|
||||
u'febru\u00e1ri': u'02', # e1 in iso-8859-2
|
||||
u'm\u00e1rcius': u'03', # e1 in iso-8859-2
|
||||
u'\u00e1prilis': u'04', # e1 in iso-8859-2
|
||||
u'm\u00e1ujus': u'05', # e1 in iso-8859-2
|
||||
u'j\u00fanius': u'06', # fa in iso-8859-2
|
||||
u'j\u00falius': u'07', # fa in iso-8859-2
|
||||
u'augusztus': u'08',
|
||||
u'szeptember': u'09',
|
||||
u'okt\u00f3ber': u'10', # f3 in iso-8859-2
|
||||
u'november': u'11',
|
||||
u'december': u'12',
|
||||
'janu\u00e1r': '01', # e1 in iso-8859-2
|
||||
'febru\u00e1ri': '02', # e1 in iso-8859-2
|
||||
'm\u00e1rcius': '03', # e1 in iso-8859-2
|
||||
'\u00e1prilis': '04', # e1 in iso-8859-2
|
||||
'm\u00e1ujus': '05', # e1 in iso-8859-2
|
||||
'j\u00fanius': '06', # fa in iso-8859-2
|
||||
'j\u00falius': '07', # fa in iso-8859-2
|
||||
'augusztus': '08',
|
||||
'szeptember': '09',
|
||||
'okt\u00f3ber': '10', # f3 in iso-8859-2
|
||||
'november': '11',
|
||||
'december': '12',
|
||||
}
|
||||
|
||||
_hungarian_date_format_re = \
|
||||
re.compile(u'(\d{4})-([^-]+)-(\d{,2})T(\d{,2}):(\d{2})((\+|-)(\d{,2}:\d{2}))')
|
||||
re.compile('(\d{4})-([^-]+)-(\d{,2})T(\d{,2}):(\d{2})((\+|-)(\d{,2}:\d{2}))')
|
||||
|
||||
def _parse_date_hungarian(dateString):
|
||||
'''Parse a string according to a Hungarian 8-bit date format.'''
|
||||
|
@ -2232,7 +2233,7 @@ def _parse_date(dateString):
|
|||
if len(date9tuple) != 9:
|
||||
if _debug: sys.stderr.write('date handler function must return 9-tuple\n')
|
||||
raise ValueError
|
||||
map(int, date9tuple)
|
||||
list(map(int, date9tuple))
|
||||
return date9tuple
|
||||
except Exception as e:
|
||||
if _debug: sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
|
||||
|
@ -2313,39 +2314,39 @@ def _getCharacterEncoding(http_headers, xml_data):
|
|||
elif xml_data[:4] == '\x00\x3c\x00\x3f':
|
||||
# UTF-16BE
|
||||
sniffed_xml_encoding = 'utf-16be'
|
||||
xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
|
||||
xml_data = str(xml_data, 'utf-16be').encode('utf-8')
|
||||
elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') and (xml_data[2:4] != '\x00\x00'):
|
||||
# UTF-16BE with BOM
|
||||
sniffed_xml_encoding = 'utf-16be'
|
||||
xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
|
||||
xml_data = str(xml_data[2:], 'utf-16be').encode('utf-8')
|
||||
elif xml_data[:4] == '\x3c\x00\x3f\x00':
|
||||
# UTF-16LE
|
||||
sniffed_xml_encoding = 'utf-16le'
|
||||
xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
|
||||
xml_data = str(xml_data, 'utf-16le').encode('utf-8')
|
||||
elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and (xml_data[2:4] != '\x00\x00'):
|
||||
# UTF-16LE with BOM
|
||||
sniffed_xml_encoding = 'utf-16le'
|
||||
xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
|
||||
xml_data = str(xml_data[2:], 'utf-16le').encode('utf-8')
|
||||
elif xml_data[:4] == '\x00\x00\x00\x3c':
|
||||
# UTF-32BE
|
||||
sniffed_xml_encoding = 'utf-32be'
|
||||
xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
|
||||
xml_data = str(xml_data, 'utf-32be').encode('utf-8')
|
||||
elif xml_data[:4] == '\x3c\x00\x00\x00':
|
||||
# UTF-32LE
|
||||
sniffed_xml_encoding = 'utf-32le'
|
||||
xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
|
||||
xml_data = str(xml_data, 'utf-32le').encode('utf-8')
|
||||
elif xml_data[:4] == '\x00\x00\xfe\xff':
|
||||
# UTF-32BE with BOM
|
||||
sniffed_xml_encoding = 'utf-32be'
|
||||
xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
|
||||
xml_data = str(xml_data[4:], 'utf-32be').encode('utf-8')
|
||||
elif xml_data[:4] == '\xff\xfe\x00\x00':
|
||||
# UTF-32LE with BOM
|
||||
sniffed_xml_encoding = 'utf-32le'
|
||||
xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
|
||||
xml_data = str(xml_data[4:], 'utf-32le').encode('utf-8')
|
||||
elif xml_data[:3] == '\xef\xbb\xbf':
|
||||
# UTF-8 with BOM
|
||||
sniffed_xml_encoding = 'utf-8'
|
||||
xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
|
||||
xml_data = str(xml_data[3:], 'utf-8').encode('utf-8')
|
||||
else:
|
||||
# ASCII-compatible
|
||||
pass
|
||||
|
@ -2369,7 +2370,7 @@ def _getCharacterEncoding(http_headers, xml_data):
|
|||
true_encoding = http_encoding or 'us-ascii'
|
||||
elif http_content_type.startswith('text/'):
|
||||
true_encoding = http_encoding or 'us-ascii'
|
||||
elif http_headers and (not http_headers.has_key('content-type')):
|
||||
elif http_headers and ('content-type' not in http_headers):
|
||||
true_encoding = xml_encoding or 'iso-8859-1'
|
||||
else:
|
||||
true_encoding = xml_encoding or 'utf-8'
|
||||
|
@ -2418,14 +2419,14 @@ def _toUTF8(data, encoding):
|
|||
sys.stderr.write('trying utf-32le instead\n')
|
||||
encoding = 'utf-32le'
|
||||
data = data[4:]
|
||||
newdata = unicode(data, encoding)
|
||||
newdata = str(data, encoding)
|
||||
if _debug: sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
|
||||
declmatch = re.compile('^<\?xml[^>]*?>')
|
||||
newdecl = '''<?xml version='1.0' encoding='utf-8'?>'''
|
||||
if declmatch.search(newdata):
|
||||
newdata = declmatch.sub(newdecl, newdata)
|
||||
else:
|
||||
newdata = newdecl + u'\n' + newdata
|
||||
newdata = newdecl + '\n' + newdata
|
||||
return newdata.encode('utf-8')
|
||||
|
||||
def _stripDoctype(data):
|
||||
|
@ -2511,7 +2512,7 @@ def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, refer
|
|||
result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type = \
|
||||
_getCharacterEncoding(http_headers, data)
|
||||
if http_headers and (not acceptable_content_type):
|
||||
if http_headers.has_key('content-type'):
|
||||
if 'content-type' in http_headers:
|
||||
bozo_message = '%s is not an XML media type' % http_headers['content-type']
|
||||
else:
|
||||
bozo_message = 'no Content-type specified'
|
||||
|
@ -2629,18 +2630,18 @@ def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, refer
|
|||
|
||||
if __name__ == '__main__':
|
||||
if not sys.argv[1:]:
|
||||
print __doc__
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
else:
|
||||
urls = sys.argv[1:]
|
||||
zopeCompatibilityHack()
|
||||
from pprint import pprint
|
||||
for url in urls:
|
||||
print url
|
||||
print
|
||||
print(url)
|
||||
print()
|
||||
result = parse(url)
|
||||
pprint(result)
|
||||
print
|
||||
print()
|
||||
|
||||
#REVISION HISTORY
|
||||
#1.0 - 9/27/2002 - MAP - fixed namespace processing on prefixed RSS 2.0 elements,
|
||||
|
|
|
@ -45,8 +45,8 @@ def init(host="127.0.0.1", port=None):
|
|||
|
||||
class Notification(object):
|
||||
def __init__(self, title="", msg="", icon=""):
|
||||
self.title = unicode(title)
|
||||
self.msg = unicode(msg)
|
||||
self.title = str(title)
|
||||
self.msg = str(msg)
|
||||
if icon.startswith("file://"):
|
||||
icon = icon[7:]
|
||||
self.icon = icon
|
||||
|
|
104
logviewer.py
|
@ -3,19 +3,19 @@ import codecs
|
|||
import re
|
||||
import ostools
|
||||
from time import strftime, strptime
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from generic import RightClickList, RightClickTree
|
||||
from parsetools import convertTags
|
||||
from convo import PesterText
|
||||
|
||||
_datadir = ostools.getDataDir()
|
||||
|
||||
class PesterLogSearchInput(QtGui.QLineEdit):
|
||||
class PesterLogSearchInput(QtWidgets.QLineEdit):
|
||||
def __init__(self, theme, parent=None):
|
||||
QtGui.QLineEdit.__init__(self, parent)
|
||||
QtWidgets.QLineEdit.__init__(self, parent)
|
||||
self.setStyleSheet(theme["convo/input/style"] + "margin-right:0px;")
|
||||
def keyPressEvent(self, event):
|
||||
QtGui.QLineEdit.keyPressEvent(self, event)
|
||||
QtWidgets.QLineEdit.keyPressEvent(self, event)
|
||||
if hasattr(self.parent(), 'textArea'):
|
||||
if event.key() == QtCore.Qt.Key_Return:
|
||||
self.parent().logSearch(self.text())
|
||||
|
@ -33,12 +33,12 @@ class PesterLogHighlighter(QtGui.QSyntaxHighlighter):
|
|||
self.hilightstyle.setForeground(QtGui.QBrush(QtCore.Qt.black))
|
||||
def highlightBlock(self, text):
|
||||
for i in range(0, len(text)-(len(self.searchTerm)-1)):
|
||||
if unicode(text[i:i+len(self.searchTerm)]).lower() == unicode(self.searchTerm).lower():
|
||||
if str(text[i:i+len(self.searchTerm)]).lower() == str(self.searchTerm).lower():
|
||||
self.setFormat(i, len(self.searchTerm), self.hilightstyle)
|
||||
|
||||
class PesterLogUserSelect(QtGui.QDialog):
|
||||
class PesterLogUserSelect(QtWidgets.QDialog):
|
||||
def __init__(self, config, theme, parent):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
self.setModal(False)
|
||||
self.config = config
|
||||
self.theme = theme
|
||||
|
@ -49,7 +49,7 @@ class PesterLogUserSelect(QtGui.QDialog):
|
|||
self.setStyleSheet(self.theme["main/defaultwindow/style"])
|
||||
self.setWindowTitle("Pesterlogs")
|
||||
|
||||
instructions = QtGui.QLabel("Pick a memo or chumhandle:")
|
||||
instructions = QtWidgets.QLabel("Pick a memo or chumhandle:")
|
||||
|
||||
if os.path.exists("%s/%s" % (self.logpath, self.handle)):
|
||||
chumMemoList = os.listdir("%s/%s/" % (self.logpath, self.handle))
|
||||
|
@ -63,31 +63,28 @@ class PesterLogUserSelect(QtGui.QDialog):
|
|||
|
||||
self.chumsBox = RightClickList(self)
|
||||
self.chumsBox.setStyleSheet(self.theme["main/chums/style"])
|
||||
self.chumsBox.optionsMenu = QtGui.QMenu(self)
|
||||
self.chumsBox.optionsMenu = QtWidgets.QMenu(self)
|
||||
|
||||
for (i, t) in enumerate(chumMemoList):
|
||||
item = QtGui.QListWidgetItem(t)
|
||||
item = QtWidgets.QListWidgetItem(t)
|
||||
item.setTextColor(QtGui.QColor(self.theme["main/chums/userlistcolor"]))
|
||||
self.chumsBox.addItem(item)
|
||||
|
||||
self.search = PesterLogSearchInput(theme, self)
|
||||
self.search.setFocus()
|
||||
|
||||
self.cancel = QtGui.QPushButton("CANCEL", self)
|
||||
self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('reject()'))
|
||||
self.ok = QtGui.QPushButton("OK", self)
|
||||
self.cancel = QtWidgets.QPushButton("CANCEL", self)
|
||||
self.cancel.clicked.connect(self.reject)
|
||||
self.ok = QtWidgets.QPushButton("OK", self)
|
||||
self.ok.setDefault(True)
|
||||
self.connect(self.ok, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('viewActivatedLog()'))
|
||||
layout_ok = QtGui.QHBoxLayout()
|
||||
self.ok.clicked.connect(self.viewActivatedLog)
|
||||
layout_ok = QtWidgets.QHBoxLayout()
|
||||
layout_ok.addWidget(self.cancel)
|
||||
layout_ok.addWidget(self.ok)
|
||||
self.directory = QtGui.QPushButton("LOG DIRECTORY", self)
|
||||
self.connect(self.directory, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('openDir()'))
|
||||
self.directory = QtWidgets.QPushButton("LOG DIRECTORY", self)
|
||||
self.directory.clicked.connect(self.openDir)
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0 = QtWidgets.QVBoxLayout()
|
||||
layout_0.addWidget(instructions)
|
||||
layout_0.addWidget(self.chumsBox)
|
||||
layout_0.addWidget(self.search)
|
||||
|
@ -111,8 +108,7 @@ class PesterLogUserSelect(QtGui.QDialog):
|
|||
self.pesterlogviewer = None
|
||||
if not self.pesterlogviewer:
|
||||
self.pesterlogviewer = PesterLogViewer(selectedchum, self.config, self.theme, self.parent)
|
||||
self.connect(self.pesterlogviewer, QtCore.SIGNAL('rejected()'),
|
||||
self, QtCore.SLOT('closeActiveLog()'))
|
||||
self.pesterlogviewer.rejected.connect(self.closeActiveLog)
|
||||
self.pesterlogviewer.show()
|
||||
self.pesterlogviewer.raise_()
|
||||
self.pesterlogviewer.activateWindow()
|
||||
|
@ -127,9 +123,9 @@ class PesterLogUserSelect(QtGui.QDialog):
|
|||
def openDir(self):
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + os.path.join(_datadir, "logs"), QtCore.QUrl.TolerantMode))
|
||||
|
||||
class PesterLogViewer(QtGui.QDialog):
|
||||
class PesterLogViewer(QtWidgets.QDialog):
|
||||
def __init__(self, chum, config, theme, parent):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
self.setModal(False)
|
||||
self.config = config
|
||||
self.theme = theme
|
||||
|
@ -151,27 +147,26 @@ class PesterLogViewer(QtGui.QDialog):
|
|||
self.logList = []
|
||||
|
||||
if not os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)) or len(self.logList) == 0:
|
||||
instructions = QtGui.QLabel("No Pesterlogs were found")
|
||||
instructions = QtWidgets.QLabel("No Pesterlogs were found")
|
||||
|
||||
self.ok = QtGui.QPushButton("CLOSE", self)
|
||||
self.ok = QtWidgets.QPushButton("CLOSE", self)
|
||||
self.ok.setDefault(True)
|
||||
self.connect(self.ok, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('reject()'))
|
||||
layout_ok = QtGui.QHBoxLayout()
|
||||
self.ok.clicked.connect(self.reject)
|
||||
layout_ok = QtWidgets.QHBoxLayout()
|
||||
layout_ok.addWidget(self.ok)
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0 = QtWidgets.QVBoxLayout()
|
||||
layout_0.addWidget(instructions)
|
||||
layout_0.addLayout(layout_ok)
|
||||
|
||||
self.setLayout(layout_0)
|
||||
else:
|
||||
self.instructions = QtGui.QLabel("Pesterlog with " +self.chum+ " on")
|
||||
self.instructions = QtWidgets.QLabel("Pesterlog with " +self.chum+ " on")
|
||||
|
||||
self.textArea = PesterLogText(theme, self.parent)
|
||||
self.textArea.setReadOnly(True)
|
||||
self.textArea.setFixedWidth(600)
|
||||
if theme.has_key("convo/scrollbar"):
|
||||
if "convo/scrollbar" in theme:
|
||||
self.textArea.setStyleSheet("QTextEdit { width:500px; %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] ))
|
||||
else:
|
||||
self.textArea.setStyleSheet("QTextEdit { width:500px; %s }" % (theme["convo/textarea/style"]))
|
||||
|
@ -180,15 +175,14 @@ class PesterLogViewer(QtGui.QDialog):
|
|||
self.logList.reverse()
|
||||
|
||||
self.tree = RightClickTree()
|
||||
self.tree.optionsMenu = QtGui.QMenu(self)
|
||||
self.tree.optionsMenu = QtWidgets.QMenu(self)
|
||||
self.tree.setFixedSize(260, 300)
|
||||
self.tree.header().hide()
|
||||
if theme.has_key("convo/scrollbar"):
|
||||
if "convo/scrollbar" in theme:
|
||||
self.tree.setStyleSheet("QTreeWidget { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] ))
|
||||
else:
|
||||
self.tree.setStyleSheet("%s" % (theme["convo/textarea/style"]))
|
||||
self.connect(self.tree, QtCore.SIGNAL('itemSelectionChanged()'),
|
||||
self, QtCore.SLOT('loadSelectedLog()'))
|
||||
self.tree.itemSelectionChanged.connect(self.loadSelectedLog)
|
||||
self.tree.setSortingEnabled(False)
|
||||
|
||||
child_1 = None
|
||||
|
@ -196,11 +190,11 @@ class PesterLogViewer(QtGui.QDialog):
|
|||
for (i,l) in enumerate(self.logList):
|
||||
my = self.fileToMonthYear(l)
|
||||
if my[0] != last[0]:
|
||||
child_1 = QtGui.QTreeWidgetItem(["%s %s" % (my[0], my[1])])
|
||||
child_1 = QtWidgets.QTreeWidgetItem(["%s %s" % (my[0], my[1])])
|
||||
self.tree.addTopLevelItem(child_1)
|
||||
if i == 0:
|
||||
child_1.setExpanded(True)
|
||||
child_1.addChild(QtGui.QTreeWidgetItem([self.fileToTime(l)]))
|
||||
child_1.addChild(QtWidgets.QTreeWidgetItem([self.fileToTime(l)]))
|
||||
last = self.fileToMonthYear(l)
|
||||
|
||||
self.hilight = PesterLogHighlighter(self.textArea)
|
||||
|
@ -208,38 +202,36 @@ class PesterLogViewer(QtGui.QDialog):
|
|||
|
||||
self.search = PesterLogSearchInput(theme, self)
|
||||
self.search.setFocus()
|
||||
self.find = QtGui.QPushButton("Find", self)
|
||||
self.find = QtWidgets.QPushButton("Find", self)
|
||||
font = self.find.font()
|
||||
font.setPointSize(8)
|
||||
self.find.setFont(font)
|
||||
self.find.setDefault(True)
|
||||
self.find.setFixedSize(40, 20)
|
||||
layout_search = QtGui.QHBoxLayout()
|
||||
layout_search = QtWidgets.QHBoxLayout()
|
||||
layout_search.addWidget(self.search)
|
||||
layout_search.addWidget(self.find)
|
||||
|
||||
self.qdb = QtGui.QPushButton("Pesterchum QDB", self)
|
||||
self.qdb = QtWidgets.QPushButton("Pesterchum QDB", self)
|
||||
self.qdb.setFixedWidth(260)
|
||||
self.connect(self.qdb, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('openQDB()'))
|
||||
self.ok = QtGui.QPushButton("CLOSE", self)
|
||||
self.qdb.clicked.connect(self.openQDB)
|
||||
self.ok = QtWidgets.QPushButton("CLOSE", self)
|
||||
self.ok.setFixedWidth(80)
|
||||
self.connect(self.ok, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('reject()'))
|
||||
layout_ok = QtGui.QHBoxLayout()
|
||||
self.ok.clicked.connect(self.reject)
|
||||
layout_ok = QtWidgets.QHBoxLayout()
|
||||
# Website is offline so there's no point.
|
||||
#layout_ok.addWidget(self.qdb)
|
||||
layout_ok.addWidget(self.ok)
|
||||
layout_ok.setAlignment(self.ok, QtCore.Qt.AlignRight)
|
||||
|
||||
layout_logs = QtGui.QHBoxLayout()
|
||||
layout_logs = QtWidgets.QHBoxLayout()
|
||||
layout_logs.addWidget(self.tree)
|
||||
layout_right = QtGui.QVBoxLayout()
|
||||
layout_right = QtWidgets.QVBoxLayout()
|
||||
layout_right.addWidget(self.textArea)
|
||||
layout_right.addLayout(layout_search)
|
||||
layout_logs.addLayout(layout_right)
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0 = QtWidgets.QVBoxLayout()
|
||||
layout_0.addWidget(self.instructions)
|
||||
layout_0.addLayout(layout_logs)
|
||||
layout_0.addLayout(layout_ok)
|
||||
|
@ -265,7 +257,7 @@ class PesterLogViewer(QtGui.QDialog):
|
|||
textCur = self.textArea.textCursor()
|
||||
textCur.movePosition(1)
|
||||
self.textArea.setTextCursor(textCur)
|
||||
self.instructions.setText("Pesterlog with " +self.chum+ " on " + self.fileToTime(unicode(fname)))
|
||||
self.instructions.setText("Pesterlog with " +self.chum+ " on " + self.fileToTime(str(fname)))
|
||||
|
||||
def logSearch(self, search):
|
||||
self.hilight.searchTerm = search
|
||||
|
@ -285,20 +277,20 @@ class PesterLogText(PesterText):
|
|||
PesterText.__init__(self, theme, parent)
|
||||
|
||||
def focusInEvent(self, event):
|
||||
QtGui.QTextEdit.focusInEvent(self, event)
|
||||
QtWidgets.QTextEdit.focusInEvent(self, event)
|
||||
def mousePressEvent(self, event):
|
||||
url = self.anchorAt(event.pos())
|
||||
if url != "":
|
||||
if url[0] == "#" and url != "#pesterchum":
|
||||
self.parent().parent.showMemos(url[1:])
|
||||
elif url[0] == "@":
|
||||
handle = unicode(url[1:])
|
||||
handle = str(url[1:])
|
||||
self.parent().parent.newConversation(handle)
|
||||
else:
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
|
||||
QtGui.QTextEdit.mousePressEvent(self, event)
|
||||
QtWidgets.QTextEdit.mousePressEvent(self, event)
|
||||
def mouseMoveEvent(self, event):
|
||||
QtGui.QTextEdit.mouseMoveEvent(self, event)
|
||||
QtWidgets.QTextEdit.mouseMoveEvent(self, event)
|
||||
if self.anchorAt(event.pos()):
|
||||
if self.viewport().cursor().shape != QtCore.Qt.PointingHandCursor:
|
||||
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
|
||||
|
|
10
luaquirks.py
|
@ -4,7 +4,7 @@ try:
|
|||
except ImportError:
|
||||
lua = None
|
||||
from quirks import ScriptQuirks
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class LuaQuirks(ScriptQuirks):
|
||||
def loadModule(self, name, filename):
|
||||
|
@ -18,7 +18,7 @@ class LuaQuirks(ScriptQuirks):
|
|||
try:
|
||||
return lua.require(name)
|
||||
except Error as e:
|
||||
print e
|
||||
print(e)
|
||||
return None
|
||||
finally:
|
||||
os.chdir(CurrentDir)
|
||||
|
@ -48,11 +48,11 @@ class LuaQuirks(ScriptQuirks):
|
|||
for name in module.commands:
|
||||
CommandWrapper = Wrapper(module,name)
|
||||
try:
|
||||
if not isinstance(CommandWrapper("test"), basestring):
|
||||
if not isinstance(CommandWrapper("test"), str):
|
||||
raise Exception
|
||||
except:
|
||||
print "Quirk malformed: %s" % (name)
|
||||
msgbox = QtGui.QMessageBox()
|
||||
print("Quirk malformed: %s" % (name))
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setWindowTitle("Error!")
|
||||
msgbox.setText("Quirk malformed: %s" % (name))
|
||||
msgbox.exec_()
|
||||
|
|
263
memos.py
|
@ -1,7 +1,7 @@
|
|||
from string import Template
|
||||
import re
|
||||
from copy import copy
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from datetime import time, timedelta, datetime
|
||||
|
||||
from mood import Mood
|
||||
|
@ -13,6 +13,12 @@ from parsetools import convertTags, addTimeInitial, timeProtocol, \
|
|||
import parsetools
|
||||
from logviewer import PesterLogViewer
|
||||
|
||||
try:
|
||||
QString = unicode
|
||||
except NameError:
|
||||
# Python 3
|
||||
QString = str
|
||||
|
||||
def delta2txt(d, format="pc"):
|
||||
if type(d) is mysteryTime:
|
||||
return "?"
|
||||
|
@ -153,7 +159,7 @@ class TimeTracker(list):
|
|||
except ValueError:
|
||||
return None
|
||||
def openTime(self, time):
|
||||
if self.open.has_key(time):
|
||||
if time in self.open:
|
||||
self.open[time] = True
|
||||
def openCurrentTime(self):
|
||||
timed = self.getTime()
|
||||
|
@ -179,21 +185,19 @@ class TimeTracker(list):
|
|||
return TimeGrammar(temporal, pcf, when, 0)
|
||||
return TimeGrammar(temporal, pcf, when, self.getRecord(timed))
|
||||
|
||||
class TimeInput(QtGui.QLineEdit):
|
||||
class TimeInput(QtWidgets.QLineEdit):
|
||||
def __init__(self, timeslider, parent):
|
||||
super(TimeInput, self).__init__(parent)
|
||||
self.timeslider = timeslider
|
||||
self.setText("+0:00")
|
||||
self.connect(self.timeslider, QtCore.SIGNAL('valueChanged(int)'),
|
||||
self, QtCore.SLOT('setTime(int)'))
|
||||
self.connect(self, QtCore.SIGNAL('editingFinished()'),
|
||||
self, QtCore.SLOT('setSlider()'))
|
||||
self.timeslider.valueChanged[int].connect(self.setTime)
|
||||
self.editingFinished.connect(self.setSlider)
|
||||
@QtCore.pyqtSlot(int)
|
||||
def setTime(self, sliderval):
|
||||
self.setText(self.timeslider.getTime())
|
||||
@QtCore.pyqtSlot()
|
||||
def setSlider(self):
|
||||
value = unicode(self.text())
|
||||
value = str(self.text())
|
||||
timed = txt2delta(value)
|
||||
if type(timed) is mysteryTime:
|
||||
self.timeslider.setValue(0)
|
||||
|
@ -210,7 +214,7 @@ class TimeInput(QtGui.QLineEdit):
|
|||
text = delta2txt(timed)
|
||||
self.setText(text)
|
||||
|
||||
class TimeSlider(QtGui.QSlider):
|
||||
class TimeSlider(QtWidgets.QSlider):
|
||||
def __init__(self, orientation, parent):
|
||||
super(TimeSlider, self).__init__(orientation, parent)
|
||||
self.setTracking(True)
|
||||
|
@ -258,22 +262,20 @@ class MemoText(PesterText):
|
|||
self.setReadOnly(True)
|
||||
self.setMouseTracking(True)
|
||||
self.textSelected = False
|
||||
self.connect(self, QtCore.SIGNAL('copyAvailable(bool)'),
|
||||
self, QtCore.SLOT('textReady(bool)'))
|
||||
self.copyAvailable[bool].connect(self.textReady)
|
||||
self.urls = {}
|
||||
for k in smiledict:
|
||||
self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k]))
|
||||
self.connect(self.mainwindow, QtCore.SIGNAL('animationSetting(bool)'),
|
||||
self, QtCore.SLOT('animateChanged(bool)'))
|
||||
self.mainwindow.animationSetting[bool].connect(self.animateChanged)
|
||||
|
||||
def initTheme(self, theme):
|
||||
if theme.has_key("memos/scrollbar"):
|
||||
if "memos/scrollbar" in theme:
|
||||
self.setStyleSheet("QTextEdit { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["memos/textarea/style"], theme["memos/scrollbar/style"], theme["memos/scrollbar/handle"], theme["memos/scrollbar/downarrow"], theme["memos/scrollbar/uparrow"], theme["memos/scrollbar/uarrowstyle"], theme["memos/scrollbar/darrowstyle"] ))
|
||||
else:
|
||||
self.setStyleSheet("QTextEdit { %s }" % theme["memos/textarea/style"])
|
||||
|
||||
def addMessage(self, msg, chum):
|
||||
if type(msg) in [str, unicode]:
|
||||
if type(msg) in [str, str]:
|
||||
lexmsg = lexMessage(msg)
|
||||
else:
|
||||
lexmsg = msg
|
||||
|
@ -304,7 +306,7 @@ class MemoText(PesterText):
|
|||
chum.color = color
|
||||
systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
|
||||
if chum is not me:
|
||||
if parent.times.has_key(chum.handle):
|
||||
if chum.handle in parent.times:
|
||||
time = parent.times[chum.handle]
|
||||
if time.getTime() is None:
|
||||
# MY WAY OR THE HIGHWAY
|
||||
|
@ -352,81 +354,67 @@ class MemoInput(PesterInput):
|
|||
class PesterMemo(PesterConvo):
|
||||
# TODO: Clean up inheritance between these!! The inits are ugly.
|
||||
def __init__(self, channel, timestr, mainwindow, parent=None):
|
||||
QtGui.QFrame.__init__(self, parent)
|
||||
QtWidgets.QFrame.__init__(self, parent)
|
||||
self.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
|
||||
self.channel = channel
|
||||
self.setObjectName(self.channel)
|
||||
self.mainwindow = mainwindow
|
||||
self.time = TimeTracker(txt2delta(timestr))
|
||||
self.setWindowTitle(channel)
|
||||
self.channelLabel = QtGui.QLabel(self)
|
||||
self.channelLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
|
||||
self.channelLabel = QtWidgets.QLabel(self)
|
||||
self.channelLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding))
|
||||
|
||||
self.textArea = MemoText(self.mainwindow.theme, self)
|
||||
self.textInput = MemoInput(self.mainwindow.theme, self)
|
||||
self.textInput.setFocus()
|
||||
|
||||
self.miniUserlist = QtGui.QPushButton(">\n>", self)
|
||||
self.miniUserlist = QtWidgets.QPushButton(">\n>", self)
|
||||
#self.miniUserlist.setStyleSheet("border:1px solid #a68168; border-width: 2px 0px 2px 2px; height: 90px; width: 10px; color: #cd8f9d; font-family: 'Arial'; background: white; margin-left: 2px;")
|
||||
self.connect(self.miniUserlist, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('toggleUserlist()'))
|
||||
self.miniUserlist.clicked.connect(self.toggleUserlist)
|
||||
|
||||
|
||||
self.userlist = RightClickList(self)
|
||||
self.userlist.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding))
|
||||
self.userlist.optionsMenu = QtGui.QMenu(self)
|
||||
self.pesterChumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self)
|
||||
self.connect(self.pesterChumAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('newPesterSlot()'))
|
||||
self.addchumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
|
||||
self.connect(self.addchumAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('addChumSlot()'))
|
||||
self.banuserAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self)
|
||||
self.connect(self.banuserAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('banSelectedUser()'))
|
||||
self.opAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self)
|
||||
self.connect(self.opAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('opSelectedUser()'))
|
||||
self.voiceAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self)
|
||||
self.connect(self.voiceAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('voiceSelectedUser()'))
|
||||
self.quirkDisableAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self)
|
||||
self.connect(self.quirkDisableAction, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('killQuirkUser()'))
|
||||
self.userlist.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding))
|
||||
self.userlist.optionsMenu = QtWidgets.QMenu(self)
|
||||
self.pesterChumAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self)
|
||||
self.pesterChumAction.triggered.connect(self.newPesterSlot)
|
||||
self.addchumAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
|
||||
self.addchumAction.triggered.connect(self.addChumSlot)
|
||||
self.banuserAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self)
|
||||
self.banuserAction.triggered.connect(self.banSelectedUser)
|
||||
self.opAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self)
|
||||
self.opAction.triggered.connect(self.opSelectedUser)
|
||||
self.voiceAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self)
|
||||
self.voiceAction.triggered.connect(self.voiceSelectedUser)
|
||||
self.quirkDisableAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self)
|
||||
self.quirkDisableAction.triggered.connect(self.killQuirkUser)
|
||||
self.userlist.optionsMenu.addAction(self.pesterChumAction)
|
||||
self.userlist.optionsMenu.addAction(self.addchumAction)
|
||||
# ban & op list added if we are op
|
||||
|
||||
self.optionsMenu = QtGui.QMenu(self)
|
||||
self.oocToggle = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
|
||||
self.optionsMenu = QtWidgets.QMenu(self)
|
||||
self.oocToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
|
||||
self.oocToggle.setCheckable(True)
|
||||
self.connect(self.oocToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleOOC(bool)'))
|
||||
self.quirksOff = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
|
||||
self.oocToggle.toggled[bool].connect(self.toggleOOC)
|
||||
self.quirksOff = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
|
||||
self.quirksOff.setCheckable(True)
|
||||
self.connect(self.quirksOff, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleQuirks(bool)'))
|
||||
self.logchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
|
||||
self.connect(self.logchum, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('openChumLogs()'))
|
||||
self.invitechum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self)
|
||||
self.connect(self.invitechum, QtCore.SIGNAL('triggered()'),
|
||||
self, QtCore.SLOT('inviteChums()'))
|
||||
self.quirksOff.toggled[bool].connect(self.toggleQuirks)
|
||||
self.logchum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
|
||||
self.logchum.triggered.connect(self.openChumLogs)
|
||||
self.invitechum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self)
|
||||
self.invitechum.triggered.connect(self.inviteChums)
|
||||
|
||||
self._beepToggle = QtGui.QAction("Beep on Message", self)
|
||||
self._beepToggle = QtWidgets.QAction("Beep on Message", self)
|
||||
self._beepToggle.setCheckable(True)
|
||||
self.connect(self._beepToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleBeep(bool)'))
|
||||
self._beepToggle.toggled[bool].connect(self.toggleBeep)
|
||||
|
||||
self._flashToggle = QtGui.QAction("Flash on Message", self)
|
||||
self._flashToggle = QtWidgets.QAction("Flash on Message", self)
|
||||
self._flashToggle.setCheckable(True)
|
||||
self.connect(self._flashToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleFlash(bool)'))
|
||||
self._flashToggle.toggled[bool].connect(self.toggleFlash)
|
||||
|
||||
self._muteToggle = QtGui.QAction("Mute Notifications", self)
|
||||
self._muteToggle = QtWidgets.QAction("Mute Notifications", self)
|
||||
self._muteToggle.setCheckable(True)
|
||||
self.connect(self._muteToggle, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('toggleMute(bool)'))
|
||||
self._muteToggle.toggled[bool].connect(self.toggleMute)
|
||||
|
||||
self.optionsMenu.addAction(self.quirksOff)
|
||||
self.optionsMenu.addAction(self.oocToggle)
|
||||
|
@ -438,23 +426,19 @@ class PesterMemo(PesterConvo):
|
|||
self.optionsMenu.addAction(self.logchum)
|
||||
self.optionsMenu.addAction(self.invitechum)
|
||||
|
||||
self.chanModeMenu = QtGui.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self)
|
||||
self.chanNoquirks = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self)
|
||||
self.chanModeMenu = QtWidgets.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self)
|
||||
self.chanNoquirks = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self)
|
||||
self.chanNoquirks.setCheckable(True)
|
||||
self.connect(self.chanNoquirks, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('noquirksChan(bool)'))
|
||||
self.chanHide = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self)
|
||||
self.chanNoquirks.toggled[bool].connect(self.noquirksChan)
|
||||
self.chanHide = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self)
|
||||
self.chanHide.setCheckable(True)
|
||||
self.connect(self.chanHide, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('hideChan(bool)'))
|
||||
self.chanInvite = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self)
|
||||
self.chanHide.toggled[bool].connect(self.hideChan)
|
||||
self.chanInvite = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self)
|
||||
self.chanInvite.setCheckable(True)
|
||||
self.connect(self.chanInvite, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('inviteChan(bool)'))
|
||||
self.chanMod = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self)
|
||||
self.chanInvite.toggled[bool].connect(self.inviteChan)
|
||||
self.chanMod = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self)
|
||||
self.chanMod.setCheckable(True)
|
||||
self.connect(self.chanMod, QtCore.SIGNAL('toggled(bool)'),
|
||||
self, QtCore.SLOT('modChan(bool)'))
|
||||
self.chanMod.toggled[bool].connect(self.modChan)
|
||||
self.chanModeMenu.addAction(self.chanNoquirks)
|
||||
self.chanModeMenu.addAction(self.chanHide)
|
||||
self.chanModeMenu.addAction(self.chanInvite)
|
||||
|
@ -464,33 +448,28 @@ class PesterMemo(PesterConvo):
|
|||
self.timeinput = TimeInput(self.timeslider, self)
|
||||
self.timeinput.setText(timestr)
|
||||
self.timeinput.setSlider()
|
||||
self.timetravel = QtGui.QPushButton("GO", self)
|
||||
self.timeclose = QtGui.QPushButton("CLOSE", self)
|
||||
self.timeswitchl = QtGui.QPushButton(self)
|
||||
self.timeswitchr = QtGui.QPushButton(self)
|
||||
self.timetravel = QtWidgets.QPushButton("GO", self)
|
||||
self.timeclose = QtWidgets.QPushButton("CLOSE", self)
|
||||
self.timeswitchl = QtWidgets.QPushButton(self)
|
||||
self.timeswitchr = QtWidgets.QPushButton(self)
|
||||
|
||||
self.connect(self.timetravel, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('sendtime()'))
|
||||
self.connect(self.timeclose, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('smashclock()'))
|
||||
self.connect(self.timeswitchl, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('prevtime()'))
|
||||
self.connect(self.timeswitchr, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('nexttime()'))
|
||||
self.timetravel.clicked.connect(self.sendtime)
|
||||
self.timeclose.clicked.connect(self.smashclock)
|
||||
self.timeswitchl.clicked.connect(self.prevtime)
|
||||
self.timeswitchr.clicked.connect(self.nexttime)
|
||||
|
||||
self.times = {}
|
||||
|
||||
self.initTheme(self.mainwindow.theme)
|
||||
|
||||
# connect
|
||||
self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'),
|
||||
self, QtCore.SLOT('sentMessage()'))
|
||||
self.textInput.returnPressed.connect(self.sentMessage)
|
||||
|
||||
layout_0 = QtGui.QVBoxLayout()
|
||||
layout_0 = QtWidgets.QVBoxLayout()
|
||||
layout_0.addWidget(self.textArea)
|
||||
layout_0.addWidget(self.textInput)
|
||||
|
||||
layout_1 = QtGui.QHBoxLayout()
|
||||
layout_1 = QtWidgets.QHBoxLayout()
|
||||
layout_1.addLayout(layout_0)
|
||||
layout_1.addWidget(self.miniUserlist)
|
||||
layout_1.addWidget(self.userlist)
|
||||
|
@ -498,14 +477,14 @@ class PesterMemo(PesterConvo):
|
|||
# layout_1 = QtGui.QGridLayout()
|
||||
# layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
|
||||
# layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
|
||||
layout_2 = QtGui.QHBoxLayout()
|
||||
layout_2 = QtWidgets.QHBoxLayout()
|
||||
layout_2.addWidget(self.timeslider)
|
||||
layout_2.addWidget(self.timeinput)
|
||||
layout_2.addWidget(self.timetravel)
|
||||
layout_2.addWidget(self.timeclose)
|
||||
layout_2.addWidget(self.timeswitchl)
|
||||
layout_2.addWidget(self.timeswitchr)
|
||||
self.layout = QtGui.QVBoxLayout()
|
||||
self.layout = QtWidgets.QVBoxLayout()
|
||||
|
||||
self.layout.addWidget(self.channelLabel)
|
||||
self.layout.addLayout(layout_1)
|
||||
|
@ -595,9 +574,9 @@ class PesterMemo(PesterConvo):
|
|||
|
||||
self.userlist.optionsMenu.setStyleSheet(theme["main/defaultwindow/style"])
|
||||
scrolls = "width: 12px; height: 12px; border: 0; padding: 0;"
|
||||
if theme.has_key("main/chums/scrollbar"):
|
||||
if "main/chums/scrollbar" in theme:
|
||||
self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["main/chums/scrollbar/style"] + scrolls, theme["main/chums/scrollbar/handle"], theme["main/chums/scrollbar/downarrow"], theme["main/chums/scrollbar/uparrow"], theme["main/chums/scrollbar/uarrowstyle"], theme["main/chums/scrollbar/darrowstyle"] ))
|
||||
elif theme.has_key("convo/scrollbar"):
|
||||
elif "convo/scrollbar" in theme:
|
||||
self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["convo/scrollbar/style"] + scrolls, theme["convo/scrollbar/handle"], "display:none;", "display:none;", "display:none;", "display:none;" ))
|
||||
else:
|
||||
self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s }" % (theme["memos/userlist/style"], scrolls, "background-color: black;"))
|
||||
|
@ -684,7 +663,7 @@ class PesterMemo(PesterConvo):
|
|||
elif handle[0] == '&':
|
||||
admin = True
|
||||
handle = handle[1:]
|
||||
item = QtGui.QListWidgetItem(handle)
|
||||
item = QtWidgets.QListWidgetItem(handle)
|
||||
if handle == self.mainwindow.profile().handle:
|
||||
color = self.mainwindow.profile().color
|
||||
else:
|
||||
|
@ -720,7 +699,7 @@ class PesterMemo(PesterConvo):
|
|||
systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
|
||||
chum = self.mainwindow.profile()
|
||||
opchum = PesterProfile(op)
|
||||
if self.times.has_key(op):
|
||||
if op in self.times:
|
||||
opgrammar = self.times[op].getGrammar()
|
||||
elif op == self.mainwindow.profile().handle:
|
||||
opgrammar = self.time.getGrammar()
|
||||
|
@ -815,7 +794,7 @@ class PesterMemo(PesterConvo):
|
|||
else:
|
||||
timed = timeProtocol(cmd)
|
||||
|
||||
if self.times.has_key(handle):
|
||||
if handle in self.times:
|
||||
if close is not None:
|
||||
if close in self.times[handle]:
|
||||
self.times[handle].setCurrent(close)
|
||||
|
@ -835,13 +814,13 @@ class PesterMemo(PesterConvo):
|
|||
|
||||
@QtCore.pyqtSlot()
|
||||
def sentMessage(self):
|
||||
text = unicode(self.textInput.text())
|
||||
text = str(self.textInput.text())
|
||||
|
||||
return parsetools.kxhandleInput(self, text, flavor="memos")
|
||||
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def namesUpdated(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
if c.lower() != self.channel.lower(): return
|
||||
# get namesdb
|
||||
namesdb = self.mainwindow.namesdb
|
||||
|
@ -849,28 +828,27 @@ class PesterMemo(PesterConvo):
|
|||
self.userlist.clear()
|
||||
for n in self.mainwindow.namesdb[self.channel]:
|
||||
self.addUser(n)
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString)
|
||||
def modesUpdated(self, channel, modes):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
if c.lower() == self.channel.lower():
|
||||
self.updateChanModes(modes, None)
|
||||
|
||||
@QtCore.pyqtSlot(QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString)
|
||||
def closeInviteOnly(self, channel):
|
||||
c = unicode(channel)
|
||||
c = str(channel)
|
||||
if c.lower() == self.channel.lower():
|
||||
self.disconnect(self.mainwindow, QtCore.SIGNAL('inviteOnlyChan(QString)'),
|
||||
self, QtCore.SLOT('closeInviteOnly(QString)'))
|
||||
self.mainwindow.inviteOnlyChan['QString'].disconnect(self.closeInviteOnly)
|
||||
if self.parent():
|
||||
print self.channel
|
||||
print(self.channel)
|
||||
i = self.parent().tabIndices[self.channel]
|
||||
self.parent().tabClose(i)
|
||||
else:
|
||||
self.close()
|
||||
msgbox = QtGui.QMessageBox()
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setText("%s: Invites only!" % (c))
|
||||
msgbox.setInformativeText("This channel is invite-only. You must get an invitation from someone on the inside before entering.")
|
||||
msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
|
||||
msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
ret = msgbox.exec_()
|
||||
|
||||
def quirkDisable(self, op, msg):
|
||||
|
@ -883,7 +861,7 @@ class PesterMemo(PesterConvo):
|
|||
systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
|
||||
chum = self.mainwindow.profile()
|
||||
opchum = PesterProfile(op)
|
||||
if self.times.has_key(op):
|
||||
if op in self.times:
|
||||
opgrammar = self.times[op].getGrammar()
|
||||
elif op == self.mainwindow.profile().handle:
|
||||
opgrammar = self.time.getGrammar()
|
||||
|
@ -899,12 +877,12 @@ class PesterMemo(PesterConvo):
|
|||
chum = self.mainwindow.profile()
|
||||
ttracker = self.time
|
||||
curtime = self.time.getTime()
|
||||
elif self.times.has_key(h):
|
||||
elif h in self.times:
|
||||
ttracker = self.times[h]
|
||||
else:
|
||||
ttracker = TimeTracker(timedelta(0))
|
||||
opchum = PesterProfile(op)
|
||||
if self.times.has_key(op):
|
||||
if op in self.times:
|
||||
opgrammar = self.times[op].getGrammar()
|
||||
elif op == self.mainwindow.profile().handle:
|
||||
opgrammar = self.time.getGrammar()
|
||||
|
@ -933,11 +911,11 @@ class PesterMemo(PesterConvo):
|
|||
self.mainwindow.chatlog.log(self.channel, msg)
|
||||
del self.netsplit
|
||||
|
||||
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
|
||||
@QtCore.pyqtSlot(QString, QString, QString)
|
||||
def userPresentChange(self, handle, channel, update):
|
||||
h = unicode(handle)
|
||||
c = unicode(channel)
|
||||
update = unicode(update)
|
||||
h = str(handle)
|
||||
c = str(channel)
|
||||
update = str(update)
|
||||
if update[0:4] == "kick": # yeah, i'm lazy.
|
||||
l = update.split(":")
|
||||
update = l[0]
|
||||
|
@ -968,7 +946,7 @@ class PesterMemo(PesterConvo):
|
|||
for c in chums:
|
||||
chum = PesterProfile(h)
|
||||
self.userlist.takeItem(self.userlist.row(c))
|
||||
if not self.times.has_key(h):
|
||||
if h not in self.times:
|
||||
self.times[h] = TimeTracker(timedelta(0))
|
||||
allinitials = []
|
||||
while self.times[h].getTime() is not None:
|
||||
|
@ -1002,13 +980,13 @@ class PesterMemo(PesterConvo):
|
|||
chum = self.mainwindow.profile()
|
||||
ttracker = self.time
|
||||
curtime = self.time.getTime()
|
||||
elif self.times.has_key(h):
|
||||
elif h in self.times:
|
||||
ttracker = self.times[h]
|
||||
else:
|
||||
ttracker = TimeTracker(timedelta(0))
|
||||
allinitials = []
|
||||
opchum = PesterProfile(op)
|
||||
if self.times.has_key(op):
|
||||
if op in self.times:
|
||||
opgrammar = self.times[op].getGrammar()
|
||||
elif op == self.mainwindow.profile().handle:
|
||||
opgrammar = self.time.getGrammar()
|
||||
|
@ -1024,13 +1002,13 @@ class PesterMemo(PesterConvo):
|
|||
|
||||
if chum is self.mainwindow.profile():
|
||||
# are you next?
|
||||
msgbox = QtGui.QMessageBox()
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setText(self.mainwindow.theme["convo/text/kickedmemo"])
|
||||
msgbox.setInformativeText("press 0k to rec0nnect or cancel to absc0nd")
|
||||
msgbox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
|
||||
msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
||||
# Find the OK button and make it default
|
||||
for b in msgbox.buttons():
|
||||
if msgbox.buttonRole(b) == QtGui.QMessageBox.AcceptRole:
|
||||
if msgbox.buttonRole(b) == QtWidgets.QMessageBox.AcceptRole:
|
||||
# We found the 'OK' button, set it as the default
|
||||
b.setDefault(True)
|
||||
b.setAutoDefault(True)
|
||||
|
@ -1039,7 +1017,7 @@ class PesterMemo(PesterConvo):
|
|||
b.setFocus()
|
||||
break
|
||||
ret = msgbox.exec_()
|
||||
if ret == QtGui.QMessageBox.Ok:
|
||||
if ret == QtWidgets.QMessageBox.Ok:
|
||||
self.userlist.clear()
|
||||
self.time = TimeTracker(curtime)
|
||||
self.resetSlider(curtime)
|
||||
|
@ -1049,7 +1027,7 @@ class PesterMemo(PesterConvo):
|
|||
msg = me.memoopenmsg(systemColor, self.time.getTime(), self.time.getGrammar(), self.mainwindow.theme["convo/text/openmemo"], self.channel)
|
||||
self.textArea.append(convertTags(msg))
|
||||
self.mainwindow.chatlog.log(self.channel, msg)
|
||||
elif ret == QtGui.QMessageBox.Cancel:
|
||||
elif ret == QtWidgets.QMessageBox.Cancel:
|
||||
if self.parent():
|
||||
i = self.parent().tabIndices[self.channel]
|
||||
self.parent().tabClose(i)
|
||||
|
@ -1082,7 +1060,7 @@ class PesterMemo(PesterConvo):
|
|||
for c in chums:
|
||||
c.op = True
|
||||
self.iconCrap(c)
|
||||
if unicode(c.text()) == self.mainwindow.profile().handle:
|
||||
if str(c.text()) == self.mainwindow.profile().handle:
|
||||
self.userlist.optionsMenu.addAction(self.opAction)
|
||||
self.userlist.optionsMenu.addAction(self.voiceAction)
|
||||
self.userlist.optionsMenu.addAction(self.banuserAction)
|
||||
|
@ -1099,7 +1077,7 @@ class PesterMemo(PesterConvo):
|
|||
for c in chums:
|
||||
c.op = False
|
||||
self.iconCrap(c)
|
||||
if unicode(c.text()) == self.mainwindow.profile().handle:
|
||||
if str(c.text()) == self.mainwindow.profile().handle:
|
||||
self.userlist.optionsMenu.removeAction(self.opAction)
|
||||
self.userlist.optionsMenu.removeAction(self.voiceAction)
|
||||
self.userlist.optionsMenu.removeAction(self.banuserAction)
|
||||
|
@ -1115,7 +1093,7 @@ class PesterMemo(PesterConvo):
|
|||
for c in chums:
|
||||
c.halfop = True
|
||||
self.iconCrap(c)
|
||||
if unicode(c.text()) == self.mainwindow.profile().handle:
|
||||
if str(c.text()) == self.mainwindow.profile().handle:
|
||||
self.userlist.optionsMenu.addAction(self.opAction)
|
||||
self.userlist.optionsMenu.addAction(self.voiceAction)
|
||||
self.userlist.optionsMenu.addAction(self.banuserAction)
|
||||
|
@ -1132,7 +1110,7 @@ class PesterMemo(PesterConvo):
|
|||
for c in chums:
|
||||
c.halfop = False
|
||||
self.iconCrap(c)
|
||||
if unicode(c.text()) == self.mainwindow.profile().handle:
|
||||
if str(c.text()) == self.mainwindow.profile().handle:
|
||||
self.userlist.optionsMenu.removeAction(self.opAction)
|
||||
self.userlist.optionsMenu.removeAction(self.voiceAction)
|
||||
self.userlist.optionsMenu.removeAction(self.banuserAction)
|
||||
|
@ -1180,40 +1158,40 @@ class PesterMemo(PesterConvo):
|
|||
user = self.userlist.currentItem()
|
||||
if not user:
|
||||
return
|
||||
user = unicode(user.text())
|
||||
user = str(user.text())
|
||||
self.mainwindow.newConversation(user)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def addChumSlot(self):
|
||||
if not self.userlist.currentItem():
|
||||
return
|
||||
currentChum = PesterProfile(unicode(self.userlist.currentItem().text()))
|
||||
currentChum = PesterProfile(str(self.userlist.currentItem().text()))
|
||||
self.mainwindow.addChum(currentChum)
|
||||
@QtCore.pyqtSlot()
|
||||
def banSelectedUser(self):
|
||||
if not self.userlist.currentItem():
|
||||
return
|
||||
currentHandle = unicode(self.userlist.currentItem().text())
|
||||
(reason, ok) = QtGui.QInputDialog.getText(self, "Ban User", "Enter the reason you are banning this user (optional):")
|
||||
currentHandle = str(self.userlist.currentItem().text())
|
||||
(reason, ok) = QtWidgets.QInputDialog.getText(self, "Ban User", "Enter the reason you are banning this user (optional):")
|
||||
if ok:
|
||||
self.mainwindow.kickUser.emit("%s:%s" % (currentHandle, reason), self.channel)
|
||||
@QtCore.pyqtSlot()
|
||||
def opSelectedUser(self):
|
||||
if not self.userlist.currentItem():
|
||||
return
|
||||
currentHandle = unicode(self.userlist.currentItem().text())
|
||||
currentHandle = str(self.userlist.currentItem().text())
|
||||
self.mainwindow.setChannelMode.emit(self.channel, "+o", currentHandle)
|
||||
@QtCore.pyqtSlot()
|
||||
def voiceSelectedUser(self):
|
||||
if not self.userlist.currentItem():
|
||||
return
|
||||
currentHandle = unicode(self.userlist.currentItem().text())
|
||||
currentHandle = str(self.userlist.currentItem().text())
|
||||
self.mainwindow.setChannelMode.emit(self.channel, "+v", currentHandle)
|
||||
@QtCore.pyqtSlot()
|
||||
def killQuirkUser(self):
|
||||
if not self.userlist.currentItem():
|
||||
return
|
||||
currentHandle = unicode(self.userlist.currentItem().text())
|
||||
currentHandle = str(self.userlist.currentItem().text())
|
||||
self.mainwindow.killSomeQuirks.emit(self.channel, currentHandle)
|
||||
|
||||
def resetSlider(self, time, send=True):
|
||||
|
@ -1226,8 +1204,7 @@ class PesterMemo(PesterConvo):
|
|||
def openChumLogs(self):
|
||||
currentChum = self.channel
|
||||
self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow)
|
||||
self.connect(self.mainwindow.chumList.pesterlogviewer, QtCore.SIGNAL('rejected()'),
|
||||
self.mainwindow.chumList, QtCore.SLOT('closeActiveLog()'))
|
||||
self.mainwindow.chumList.pesterlogviewer.rejected.connect(self.mainwindow.chumList.closeActiveLog)
|
||||
self.mainwindow.chumList.pesterlogviewer.show()
|
||||
self.mainwindow.chumList.pesterlogviewer.raise_()
|
||||
self.mainwindow.chumList.pesterlogviewer.activateWindow()
|
||||
|
@ -1237,9 +1214,9 @@ class PesterMemo(PesterConvo):
|
|||
if not hasattr(self, 'invitechums'):
|
||||
self.invitechums = None
|
||||
if not self.invitechums:
|
||||
(chum, ok) = QtGui.QInputDialog.getText(self, "Invite to Chat", "Enter the chumhandle of the user you'd like to invite:")
|
||||
(chum, ok) = QtWidgets.QInputDialog.getText(self, "Invite to Chat", "Enter the chumhandle of the user you'd like to invite:")
|
||||
if ok:
|
||||
chum = unicode(chum)
|
||||
chum = str(chum)
|
||||
self.mainwindow.inviteChum.emit(chum, self.channel)
|
||||
self.invitechums = None
|
||||
|
||||
|
@ -1303,7 +1280,7 @@ class PesterMemo(PesterConvo):
|
|||
self.mainwindow.waitingMessages.messageAnswered(self.channel)
|
||||
self.windowClosed.emit(self.title())
|
||||
|
||||
windowClosed = QtCore.pyqtSignal(QtCore.QString)
|
||||
windowClosed = QtCore.pyqtSignal('QString')
|
||||
|
||||
|
||||
timelist = ["0:00", "0:01", "0:02", "0:04", "0:06", "0:10", "0:14", "0:22", "0:30", "0:41", "1:00", "1:34", "2:16", "3:14", "4:13", "4:20", "5:25", "6:12", "7:30", "8:44", "10:25", "11:34", "14:13", "16:12", "17:44", "22:22", "25:10", "33:33", "42:00", "43:14", "50:00", "62:12", "75:00", "88:44", "100", "133", "143", "188", "200", "222", "250", "314", "333", "413", "420", "500", "600", "612", "888", "1000", "1025"]
|
||||
|
|
|
@ -21,12 +21,12 @@ def mispeller(word):
|
|||
num = 1
|
||||
else:
|
||||
num = random.choice([1,2])
|
||||
wordseq = range(0, len(word))
|
||||
wordseq = list(range(0, len(word)))
|
||||
random.shuffle(wordseq)
|
||||
letters = wordseq[0:num]
|
||||
def mistype(string, i):
|
||||
l = string[i]
|
||||
if not kbdict.has_key(l):
|
||||
if l not in kbdict:
|
||||
return string
|
||||
lpos = kbdict[l]
|
||||
newpos = lpos
|
||||
|
|
18
mood.py
|
@ -1,4 +1,4 @@
|
|||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from generic import PesterIcon
|
||||
|
||||
|
@ -49,15 +49,13 @@ class PesterMoodHandler(QtCore.QObject):
|
|||
self.buttons[b.mood.value()] = b
|
||||
if b.mood.value() == self.mainwindow.profile().mood.value():
|
||||
b.setSelected(True)
|
||||
self.connect(b, QtCore.SIGNAL('clicked()'),
|
||||
b, QtCore.SLOT('updateMood()'))
|
||||
self.connect(b, QtCore.SIGNAL('moodUpdated(int)'),
|
||||
self, QtCore.SLOT('updateMood(int)'))
|
||||
b.clicked.connect(b.updateMood)
|
||||
b.moodUpdated[int].connect(self.updateMood)
|
||||
def removeButtons(self):
|
||||
for b in self.buttons.values():
|
||||
for b in list(self.buttons.values()):
|
||||
b.close()
|
||||
def showButtons(self):
|
||||
for b in self.buttons.values():
|
||||
for b in list(self.buttons.values()):
|
||||
b.show()
|
||||
b.raise_()
|
||||
@QtCore.pyqtSlot(int)
|
||||
|
@ -81,14 +79,14 @@ class PesterMoodHandler(QtCore.QObject):
|
|||
moodicon = newmood.icon(self.mainwindow.theme)
|
||||
self.mainwindow.currentMoodIcon.setPixmap(moodicon.pixmap(moodicon.realsize()))
|
||||
if oldmood.name() != newmood.name():
|
||||
for c in self.mainwindow.convos.values():
|
||||
for c in list(self.mainwindow.convos.values()):
|
||||
c.myUpdateMood(newmood)
|
||||
self.mainwindow.moodUpdated.emit()
|
||||
|
||||
class PesterMoodButton(QtGui.QPushButton):
|
||||
class PesterMoodButton(QtWidgets.QPushButton):
|
||||
def __init__(self, parent, **options):
|
||||
icon = PesterIcon(options["icon"])
|
||||
QtGui.QPushButton.__init__(self, icon, options["text"], parent)
|
||||
QtWidgets.QPushButton.__init__(self, icon, options["text"], parent)
|
||||
self.setIconSize(icon.realsize())
|
||||
self.setFlat(True)
|
||||
self.resize(*options["size"])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import os, sys
|
||||
import platform
|
||||
from PyQt4.QtGui import QDesktopServices
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
|
||||
def isOSX():
|
||||
return sys.platform == "darwin"
|
||||
|
@ -32,10 +33,10 @@ def getDataDir():
|
|||
# in the Pesterchum install directory (like before)
|
||||
try:
|
||||
if isOSX():
|
||||
return os.path.join(unicode(QDesktopServices.storageLocation(QDesktopServices.DataLocation)), "Pesterchum/")
|
||||
return os.path.join(str(QStandardPaths.writableLocation(QStandardPaths.DataLocation)), "Pesterchum/")
|
||||
elif isLinux():
|
||||
return os.path.join(unicode(QDesktopServices.storageLocation(QDesktopServices.HomeLocation)), ".pesterchum/")
|
||||
return os.path.join(str(QStandardPaths.writableLocation(QStandardPaths.HomeLocation)), ".pesterchum/")
|
||||
else:
|
||||
return os.path.join(unicode(QDesktopServices.storageLocation(QDesktopServices.DataLocation)), "pesterchum/")
|
||||
return os.path.join(str(QStandardPaths.writableLocation(QStandardPaths.DataLocation)), "pesterchum/")
|
||||
except UnicodeDecodeError:
|
||||
return ''
|
||||
|
|
|
@ -127,7 +127,7 @@ class IRCClient:
|
|||
logging.info('---> send "%s"' % msg)
|
||||
try:
|
||||
self.socket.send(msg + bytes("\r\n", "ascii"))
|
||||
except socket.error, se:
|
||||
except socket.error as se:
|
||||
try: # a little dance of compatibility to get the errno
|
||||
errno = se.errno
|
||||
except AttributeError:
|
||||
|
@ -160,12 +160,12 @@ class IRCClient:
|
|||
while not self._end:
|
||||
try:
|
||||
buffer += self.socket.recv(1024)
|
||||
except socket.timeout, e:
|
||||
except socket.timeout as e:
|
||||
if self._end:
|
||||
break
|
||||
logging.debug("timeout in client.py")
|
||||
raise e
|
||||
except socket.error, e:
|
||||
except socket.error as e:
|
||||
if self._end:
|
||||
break
|
||||
logging.debug("error %s" % e)
|
||||
|
@ -195,10 +195,10 @@ class IRCClient:
|
|||
pass
|
||||
|
||||
yield True
|
||||
except socket.timeout, se:
|
||||
except socket.timeout as se:
|
||||
logging.debug("passing timeout")
|
||||
raise se
|
||||
except socket.error, se:
|
||||
except socket.error as se:
|
||||
logging.debug("problem: %s" % (se))
|
||||
if self.socket:
|
||||
logging.info('error: closing socket')
|
||||
|
@ -264,12 +264,12 @@ class IRCApp:
|
|||
while self.running:
|
||||
found_one_alive = False
|
||||
|
||||
for client, clientdesc in self._clients.iteritems():
|
||||
for client, clientdesc in self._clients.items():
|
||||
if clientdesc.con is None:
|
||||
clientdesc.con = client.connect()
|
||||
|
||||
try:
|
||||
clientdesc.con.next()
|
||||
next(clientdesc.con)
|
||||
except Exception as e:
|
||||
logging.error('client error %s' % e)
|
||||
logging.error(traceback.format_exc())
|
||||
|
|
|
@ -21,7 +21,7 @@ class MyHandler(DefaultCommandHandler):
|
|||
match = re.match('\!say (.*)', msg)
|
||||
if match:
|
||||
to_say = match.group(1).strip()
|
||||
print('Saying, "%s"' % to_say)
|
||||
print(('Saying, "%s"' % to_say))
|
||||
helpers.msg(self.client, chan, to_say)
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ def main():
|
|||
conn = cli.connect()
|
||||
|
||||
while True:
|
||||
conn.next() ## python 2
|
||||
next(conn) ## python 2
|
||||
# next(conn) ## python 3
|
||||
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ def _addNumerics():
|
|||
cli.send(cmd_num, *args)
|
||||
return f
|
||||
m = sys.modules[__name__]
|
||||
for num, name in ircevents.numeric_events.iteritems():
|
||||
for num, name in ircevents.numeric_events.items():
|
||||
setattr(m, name, numericcmd(num, name))
|
||||
|
||||
_addNumerics()
|
||||
|
|
|
@ -206,5 +206,5 @@ protocol_events = [
|
|||
"pong",
|
||||
]
|
||||
|
||||
all_events = generated_events + protocol_events + numeric_events.values()
|
||||
all_events = generated_events + protocol_events + list(numeric_events.values())
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import sys
|
||||
from helpers import msg
|
||||
from .helpers import msg
|
||||
|
||||
# NickServ basic functions
|
||||
_nickservfuncs = (
|
||||
|
@ -103,7 +103,7 @@ def _addServ(serv, funcs, prefix=""):
|
|||
if prefix:
|
||||
cmd_name = prefix.upper() + " " + cmd_name
|
||||
def f(cli, *args):
|
||||
print cmd_name, " ".join(args)
|
||||
print(cmd_name, " ".join(args))
|
||||
#cli.send(cmd_name, serv.name, *args)
|
||||
return f
|
||||
for t in funcs:
|
||||
|
|
|
@ -4,7 +4,7 @@ import ostools
|
|||
import collections
|
||||
from copy import copy
|
||||
from datetime import timedelta
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from generic import mysteryTime
|
||||
from quirks import ScriptQuirks
|
||||
|
@ -17,6 +17,12 @@ import pnc.lexercon as lexercon
|
|||
|
||||
# I'll clean up the things that are no longer needed once the transition is
|
||||
# actually finished.
|
||||
try:
|
||||
QString = unicode
|
||||
except NameError:
|
||||
# Python 3
|
||||
QString = str
|
||||
|
||||
_ctag_begin = re.compile(r'(?i)<c=(.*?)>')
|
||||
_gtag_begin = re.compile(r'(?i)<g[a-f]>')
|
||||
_ctag_end = re.compile(r'(?i)</c>')
|
||||
|
@ -36,7 +42,7 @@ quirkloader = ScriptQuirks()
|
|||
quirkloader.add(PythonQuirks())
|
||||
quirkloader.add(LuaQuirks())
|
||||
quirkloader.loadAll()
|
||||
print quirkloader.funcre()
|
||||
print(quirkloader.funcre())
|
||||
_functionre = re.compile(r"%s" % quirkloader.funcre())
|
||||
_groupre = re.compile(r"\\([0-9]+)")
|
||||
|
||||
|
@ -51,7 +57,7 @@ def lexer(string, objlist):
|
|||
for (oType, regexp) in objlist:
|
||||
newstringlist = []
|
||||
for (stri, s) in enumerate(stringlist):
|
||||
if type(s) not in [str, unicode]:
|
||||
if type(s) not in [str, str]:
|
||||
newstringlist.append(s)
|
||||
continue
|
||||
lasti = 0
|
||||
|
@ -220,7 +226,7 @@ kxpclexer = lexercon.Pesterchum()
|
|||
|
||||
def kxlexMsg(string):
|
||||
# Do a bit of sanitization.
|
||||
msg = unicode(string)
|
||||
msg = str(string)
|
||||
# TODO: Let people paste line-by-line normally. Maybe have a mass-paste
|
||||
# right-click option?
|
||||
msg = msg.replace('\n', ' ').replace('\r', ' ')
|
||||
|
@ -247,9 +253,9 @@ def lexMessage(string):
|
|||
(smiley, _smilere),
|
||||
(honker, _honk)]
|
||||
|
||||
string = unicode(string)
|
||||
string = str(string)
|
||||
string = string.replace("\n", " ").replace("\r", " ")
|
||||
lexed = lexer(unicode(string), lexlist)
|
||||
lexed = lexer(str(string), lexlist)
|
||||
|
||||
balanced = []
|
||||
beginc = 0
|
||||
|
@ -271,7 +277,7 @@ def lexMessage(string):
|
|||
balanced.append(colorEnd("</c>"))
|
||||
if len(balanced) == 0:
|
||||
balanced.append("")
|
||||
if type(balanced[len(balanced)-1]) not in [str, unicode]:
|
||||
if type(balanced[len(balanced)-1]) not in [str, str]:
|
||||
balanced.append("")
|
||||
return balanced
|
||||
|
||||
|
@ -279,12 +285,12 @@ def convertTags(lexed, format="html"):
|
|||
if format not in ["html", "bbcode", "ctag", "text"]:
|
||||
raise ValueError("Color format not recognized")
|
||||
|
||||
if type(lexed) in [str, unicode]:
|
||||
if type(lexed) in [str, str]:
|
||||
lexed = lexMessage(lexed)
|
||||
escaped = ""
|
||||
firststr = True
|
||||
for (i, o) in enumerate(lexed):
|
||||
if type(o) in [str, unicode]:
|
||||
if type(o) in [str, str]:
|
||||
if format == "html":
|
||||
escaped += o.replace("&", "&").replace(">", ">").replace("<","<")
|
||||
else:
|
||||
|
@ -392,7 +398,7 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
while len(lexed) > 0:
|
||||
rounds += 1
|
||||
if debug:
|
||||
print "[Starting round {}...]".format(rounds)
|
||||
print("[Starting round {}...]".format(rounds))
|
||||
msg = lexed.popleft()
|
||||
msglen = 0
|
||||
is_text = False
|
||||
|
@ -433,9 +439,9 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
# instead?
|
||||
subround += 1
|
||||
if debug:
|
||||
print "[Splitting round {}-{}...]".format(
|
||||
print("[Splitting round {}-{}...]".format(
|
||||
rounds, subround
|
||||
)
|
||||
))
|
||||
point = msg.rfind(' ', 0, lenl)
|
||||
if point < 0:
|
||||
# No spaces to break on...ugh. Break at the last space
|
||||
|
@ -448,12 +454,12 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
# Remove what we just added.
|
||||
msg = msg[point:]
|
||||
if debug:
|
||||
print "msg = {!r}".format(msg)
|
||||
print("msg = {!r}".format(msg))
|
||||
else:
|
||||
# Catch the remainder.
|
||||
stack.append(msg)
|
||||
if debug:
|
||||
print "msg caught; stack = {!r}".format(stack)
|
||||
print("msg caught; stack = {!r}".format(stack))
|
||||
# Done processing. Pluck out the first portion so we can
|
||||
# continue processing, clean it up a bit, then add the rest to
|
||||
# our waiting list.
|
||||
|
@ -494,16 +500,16 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
cte = lexercon.CTagEnd("</c>", fmt, None)
|
||||
working.extend([cte] * len(open_ctags))
|
||||
if debug:
|
||||
print "\tRound {0} linebreak: Added {1} closing ctags".format(
|
||||
print("\tRound {0} linebreak: Added {1} closing ctags".format(
|
||||
rounds, len(open_ctags)
|
||||
)
|
||||
))
|
||||
|
||||
# Run it through the lexer again to render it.
|
||||
working = u''.join(kxpclexer.list_convert(working))
|
||||
working = ''.join(kxpclexer.list_convert(working))
|
||||
if debug:
|
||||
print "\tRound {0} add: len == {1} (of {2})".format(
|
||||
print("\tRound {0} add: len == {1} (of {2})".format(
|
||||
rounds, len(working), maxlen
|
||||
)
|
||||
))
|
||||
# Now that it's done the work for us, append and resume.
|
||||
output.append(working)
|
||||
|
||||
|
@ -518,7 +524,7 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
# We have more to go.
|
||||
# Reset working, starting it with the unclosed ctags.
|
||||
if debug:
|
||||
print "\tRound {0}: More to lex".format(rounds)
|
||||
print("\tRound {0}: More to lex".format(rounds))
|
||||
working = open_ctags[:]
|
||||
# Calculate the length of the starting tags, add it before
|
||||
# anything else.
|
||||
|
@ -533,7 +539,7 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
if debug or True:
|
||||
# This probably shouldn't happen, and if it does, I want to
|
||||
# know if it *works* properly.
|
||||
print "\tRound {0}: No more to lex".format(rounds)
|
||||
print("\tRound {0}: No more to lex".format(rounds))
|
||||
# Clean up, just in case.
|
||||
working = []
|
||||
open_ctags = []
|
||||
|
@ -582,8 +588,8 @@ def kxsplitMsg(lexed, fmt="pchum", maxlen=None, debug=False):
|
|||
working = kxpclexer.list_convert(working)
|
||||
if len(working) > 0:
|
||||
if debug:
|
||||
print "Adding end trails: {!r}".format(working)
|
||||
working = u''.join(working)
|
||||
print("Adding end trails: {!r}".format(working))
|
||||
working = ''.join(working)
|
||||
output.append(working)
|
||||
|
||||
# We're...done?
|
||||
|
@ -596,7 +602,7 @@ def splitMessage(msg, format="ctag"):
|
|||
# split long text lines
|
||||
buf = []
|
||||
for o in msg:
|
||||
if type(o) in [str, unicode] and len(o) > 200:
|
||||
if type(o) in [str, str] and len(o) > 200:
|
||||
# Split with a step of 200. I.e., cut long segments into chunks of
|
||||
# 200 characters.
|
||||
# I'm...not sure why this is done. I'll probably factor it out
|
||||
|
@ -614,7 +620,7 @@ def splitMessage(msg, format="ctag"):
|
|||
cbegintags = []
|
||||
# This is the final result.
|
||||
output = []
|
||||
print repr(msg)
|
||||
print(repr(msg))
|
||||
for o in msg:
|
||||
oldctag = None
|
||||
# Add to the working segment.
|
||||
|
@ -687,9 +693,7 @@ def _is_ooc(msg, strict=True):
|
|||
# We have a match....
|
||||
ooc1, ooc2 = oocDetected.group(1, 2)
|
||||
# Make sure the appropriate braces are used.
|
||||
mbraces = map(
|
||||
lambda br: ooc1 == br[0] and ooc2 == br[1],
|
||||
braces)
|
||||
mbraces = [ooc1 == br[0] and ooc2 == br[1] for br in braces]
|
||||
if any(mbraces):
|
||||
# If any of those passes matched, we're good to go; it's OOC.
|
||||
return True
|
||||
|
@ -711,7 +715,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
if text is None:
|
||||
# Fetch the raw text from the input box.
|
||||
text = ctx.textInput.text()
|
||||
text = unicode(ctx.textInput.text())
|
||||
text = str(ctx.textInput.text())
|
||||
|
||||
# Preprocessing stuff.
|
||||
msg = text.strip()
|
||||
|
@ -731,7 +735,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
# Determine if the line actually *is* OOC.
|
||||
if is_ooc and not oocDetected:
|
||||
# If we're supposed to be OOC, apply it artificially.
|
||||
msg = u"(( {} ))".format(msg)
|
||||
msg = "(( {} ))".format(msg)
|
||||
# Also, quirk stuff.
|
||||
should_quirk = ctx.applyquirks
|
||||
else:
|
||||
|
@ -764,7 +768,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
except Exception as err:
|
||||
# Tell the user we couldn't do quirk things.
|
||||
# TODO: Include the actual error...and the quirk it came from?
|
||||
msgbox = QtGui.QMessageBox()
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setText("Whoa there! There seems to be a problem.")
|
||||
err_info = "A quirk seems to be having a problem. (Error: {!s})"
|
||||
err_info = err_info.format(err)
|
||||
|
@ -776,9 +780,9 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
try:
|
||||
# Turns out that Windows consoles can't handle unicode, heh...who'da
|
||||
# thunk. We have to repr() this, as such.
|
||||
print repr(msg)
|
||||
print(repr(msg))
|
||||
except Exception as err:
|
||||
print "(Couldn't print processed message: {!s})".format(err)
|
||||
print("(Couldn't print processed message: {!s})".format(err))
|
||||
|
||||
# karxi: We have a list...but I'm not sure if we ever get anything else, so
|
||||
# best to play it safe. I may remove this during later refactoring.
|
||||
|
@ -790,26 +794,23 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
# an object type I provided - just so I could pluck them out
|
||||
# later.
|
||||
msg[i] = m.convert(format="ctag")
|
||||
msg = u''.join(msg)
|
||||
msg = ''.join(msg)
|
||||
|
||||
# Quirks have been applied. Lex the messages (finally).
|
||||
msg = kxlexMsg(msg)
|
||||
|
||||
# Debug output.
|
||||
try:
|
||||
print repr(msg)
|
||||
print(repr(msg))
|
||||
except Exception as err:
|
||||
print "(Couldn't print lexed message: {!s})".format(err)
|
||||
print("(Couldn't print lexed message: {!s})".format(err))
|
||||
|
||||
# Remove coloring if this is a /me!
|
||||
if is_action:
|
||||
# Filter out formatting specifiers (just ctags, at the moment).
|
||||
msg = filter(
|
||||
lambda m: not isinstance(m,
|
||||
msg = [m for m in msg if not isinstance(m,
|
||||
(lexercon.CTag, lexercon.CTagEnd)
|
||||
),
|
||||
msg
|
||||
)
|
||||
)]
|
||||
# We'll also add /me to the beginning of any new messages, later.
|
||||
|
||||
# Put what's necessary in before splitting.
|
||||
|
@ -850,7 +851,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
# if ceased, rebegin
|
||||
if hasattr(ctx, 'chumopen') and not ctx.chumopen:
|
||||
ctx.mainwindow.newConvoStarted.emit(
|
||||
QtCore.QString(ctx.title()), True
|
||||
QString(ctx.title()), True
|
||||
)
|
||||
ctx.setChumOpen(True)
|
||||
|
||||
|
@ -859,7 +860,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
# If we're working with an action and we split, it should have /mes.
|
||||
if is_action and i > 0:
|
||||
# Add them post-split.
|
||||
lm = u"/me " + lm
|
||||
lm = "/me " + lm
|
||||
# NOTE: No reason to reassign for now, but...why not?
|
||||
lexmsgs[i] = lm
|
||||
|
||||
|
@ -876,11 +877,11 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
|||
# We fetched the information outside of the loop, so just
|
||||
# construct the messages.
|
||||
|
||||
clientMsg = u"<c={1}>{2}{3}{4}: {0}</c>".format(
|
||||
clientMsg = "<c={1}>{2}{3}{4}: {0}</c>".format(
|
||||
clientMsg, colorcmd, grammar.pcf, initials, grammar.number
|
||||
)
|
||||
# Not sure if this needs a space at the end...?
|
||||
serverMsg = u"<c={1}>{2}: {0}</c>".format(
|
||||
serverMsg = "<c={1}>{2}: {0}</c>".format(
|
||||
serverMsg, colorcmd, initials)
|
||||
|
||||
ctx.addMessage(clientMsg, True)
|
||||
|
@ -984,7 +985,7 @@ def parseRegexpFunctions(to):
|
|||
backr = _groupre.search(mo.group())
|
||||
if backr is not None:
|
||||
current.append(backreference(backr.group(1)))
|
||||
elif mo.group()[:-1] in functiondict.keys():
|
||||
elif mo.group()[:-1] in list(functiondict.keys()):
|
||||
p = parseLeaf(functiondict[mo.group()[:-1]], current)
|
||||
current.append(p)
|
||||
current = p
|
||||
|
@ -1001,7 +1002,7 @@ def parseRegexpFunctions(to):
|
|||
|
||||
|
||||
def img2smiley(string):
|
||||
string = unicode(string)
|
||||
string = str(string)
|
||||
def imagerep(mo):
|
||||
return reverse_smiley[mo.group(1)]
|
||||
string = re.sub(r'<img src="smilies/(\S+)" />', imagerep, string)
|
||||
|
@ -1082,8 +1083,8 @@ if ostools.isOSXBundle():
|
|||
|
||||
|
||||
|
||||
reverse_smiley = dict((v,k) for k, v in smiledict.iteritems())
|
||||
_smilere = re.compile("|".join(smiledict.keys()))
|
||||
reverse_smiley = dict((v,k) for k, v in smiledict.items())
|
||||
_smilere = re.compile("|".join(list(smiledict.keys())))
|
||||
|
||||
class ThemeException(Exception):
|
||||
def __init__(self, value):
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd `dirname $0`
|
||||
python2 pesterchum.py $@
|
|
@ -1,104 +0,0 @@
|
|||
|
||||
; The name of the installer
|
||||
Name "PESTERCHUM3.14 to 3.41"
|
||||
|
||||
; The file to write
|
||||
OutFile "pesterchum3.14to3.41.exe"
|
||||
|
||||
RequestExecutionLevel admin
|
||||
|
||||
Page components
|
||||
Page instfiles
|
||||
|
||||
UninstPage uninstConfirm
|
||||
UninstPage instfiles
|
||||
|
||||
; The stuff to install
|
||||
Section "Pesterchum"
|
||||
|
||||
SectionIn RO
|
||||
|
||||
ReadRegStr $INSTDIR HKLM "SOFTWARE\Pesterchum" "Install_Dir"
|
||||
|
||||
StrCmp $INSTDIR "" error
|
||||
|
||||
; Set output path to the installation directory.
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Check and see if this is really 3.14
|
||||
IfFileExists library.zip 0 error
|
||||
|
||||
ClearErrors
|
||||
CreateDirectory $TEMP\pesterchum_backup
|
||||
IfErrors backuperror 0
|
||||
CopyFiles $INSTDIR\pesterchum.js $TEMP\pesterchum_backup
|
||||
CopyFiles $INSTDIR\profiles $TEMP\pesterchum_backup
|
||||
CopyFiles $INSTDIR\logs $TEMP\pesterchum_backup
|
||||
IfErrors cantcopy 0
|
||||
|
||||
Delete $INSTDIR\uninstall.exe
|
||||
|
||||
; Remove shortcuts, if any
|
||||
Delete "$SMPROGRAMS\Pesterchum\*.*"
|
||||
|
||||
; Remove directories used
|
||||
RMDir "$SMPROGRAMS\Pesterchum"
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
; Put file there
|
||||
File /r *.*
|
||||
Rename $INSTDIR\README.mkdn $INSTDIR\readme.txt
|
||||
Rename $INSTDIR\CHANGELOG.mkdn $INSTDIR\changelog.txt
|
||||
|
||||
; Copy backup files
|
||||
ClearErrors
|
||||
CopyFiles $TEMP\pesterchum_backup\*.* $INSTDIR
|
||||
IfErrors brokeinstall 0
|
||||
RMDIR /r "$TEMP\pesterchum_backup"
|
||||
|
||||
WriteUninstaller "uninstall.exe"
|
||||
|
||||
CreateDirectory "$SMPROGRAMS\Pesterchum"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Pesterchum.lnk" "$INSTDIR\pesterchum.exe"
|
||||
CreateShortcut "$DESKTOP\Pesterchum.lnk" "$INSTDIR\pesterchum.exe"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Readme.lnk" "$INSTDIR\readme.txt"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Logs.lnk" "$LOCALAPPDATA\pesterchum\logs"
|
||||
|
||||
Goto done
|
||||
|
||||
error:
|
||||
MessageBox MB_OK "Pesterchum 3.14 (or 3.41 beta) not found on this machine!"
|
||||
Goto done
|
||||
backuperror:
|
||||
IfFileExists $TEMP\pesterchum_backup brokeinstall cantmaketmp
|
||||
cantmaketmp:
|
||||
MessageBox MB_OK "Error! Can't make temporary directory (to save your files) for some raisin. Check your privileges?? i dunno tbqh, soryr *sorry"
|
||||
Goto done
|
||||
brokeinstall:
|
||||
MessageBox MB_OK "Broken install detected. Please copy the files in $TEMP\pesterchum_backup to some place safe and then delete that folder."
|
||||
Goto done
|
||||
cantcopy:
|
||||
MessageBox MB_OK "Can't seem to copy Pesterchum backup files to temp directory."
|
||||
Goto done
|
||||
done:
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
; Remove registry keys
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum"
|
||||
DeleteRegKey HKLM SOFTWARE\Pesterchum
|
||||
|
||||
; Remove files and uninstaller
|
||||
Delete $INSTDIR\uninstall.exe
|
||||
|
||||
; Remove shortcuts, if any
|
||||
Delete "$SMPROGRAMS\Pesterchum\*.*"
|
||||
|
||||
; Remove directories used
|
||||
RMDir "$SMPROGRAMS\Pesterchum"
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
SectionEnd
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
; The name of the installer
|
||||
Name "PESTERCHUM3.41"
|
||||
|
||||
; The file to write
|
||||
OutFile "pesterchum3.41.update.exe"
|
||||
|
||||
RequestExecutionLevel admin
|
||||
|
||||
Page components
|
||||
Page instfiles
|
||||
|
||||
; The stuff to install
|
||||
Section "Pesterchum"
|
||||
|
||||
SectionIn RO
|
||||
|
||||
ReadRegStr $INSTDIR HKLM "SOFTWARE\Pesterchum" "Install_Dir"
|
||||
|
||||
StrCmp $INSTDIR "" error
|
||||
|
||||
; Set output path to the installation directory.
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Put file there
|
||||
File /r themes
|
||||
File /r smilies
|
||||
File /r quirks
|
||||
File README.mkdn
|
||||
File CHANGELOG.mkdn
|
||||
File version.py
|
||||
File pesterchum.exe
|
||||
File pesterchum.exe.manifest
|
||||
|
||||
|
||||
Rename $INSTDIR\README.mkdn $INSTDIR\readme.txt
|
||||
Rename $INSTDIR\CHANGELOG.mkdn $INSTDIR\changelog.txt
|
||||
|
||||
Delete "$SMPROGRAMS\Pesterchum\Pesterchum.lnk"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Pesterchum.lnk" "$INSTDIR\pesterchum.exe"
|
||||
|
||||
Goto done
|
||||
error:
|
||||
MessageBox MB_OK "Pesterchum not found on this machine!"
|
||||
done:
|
||||
|
||||
SectionEnd
|
|
@ -1,32 +0,0 @@
|
|||
#PESTERCHUM CONFIG V2.5
|
||||
debug: false
|
||||
notify: false
|
||||
tray: true
|
||||
claf: false
|
||||
handle: elegantDiversion
|
||||
mood: 0
|
||||
color: -15420406
|
||||
skin: trollian
|
||||
#CHUMROLL BEGIN
|
||||
handle: adiosToreador
|
||||
color: -5614336
|
||||
handle: arachnidsGrip
|
||||
color: -6206085
|
||||
#CHUMROLL END
|
||||
#QUIRKS BEGIN
|
||||
search: [p|P]
|
||||
replace: 9
|
||||
search: [Z|z]
|
||||
replace: 2
|
||||
search: [S|s]
|
||||
replace: 5
|
||||
search: [O|o]
|
||||
replace: 0
|
||||
search: [L|l]
|
||||
replace: |
|
||||
prefix: ==>
|
||||
suffix:
|
||||
#QUIRKS END
|
||||
#TROLLS BEGIN
|
||||
#TROLLS END
|
||||
#EOF
|
BIN
pesterchum.ico
Before Width: | Height: | Size: 3.6 KiB |
|
@ -1,69 +0,0 @@
|
|||
|
||||
; The name of the installer
|
||||
Name "PESTERCHUM3.41"
|
||||
|
||||
; The file to write
|
||||
OutFile "pesterchum3.41.exe"
|
||||
|
||||
InstallDir C:\Pesterchum
|
||||
|
||||
InstallDirRegKey HKLM "Software\Pesterchum" "Install_Dir"
|
||||
RequestExecutionLevel admin
|
||||
|
||||
Page components
|
||||
Page directory
|
||||
Page instfiles
|
||||
|
||||
UninstPage uninstConfirm
|
||||
UninstPage instfiles
|
||||
|
||||
|
||||
; The stuff to install
|
||||
Section "Pesterchum"
|
||||
|
||||
SectionIn RO
|
||||
|
||||
; Set output path to the installation directory.
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Put file there
|
||||
File /r *.*
|
||||
Rename $INSTDIR\README.mkdn $INSTDIR\readme.txt
|
||||
Rename $INSTDIR\CHANGELOG.mkdn $INSTDIR\changelog.txt
|
||||
|
||||
; Write the installation path into the registry
|
||||
WriteRegStr HKLM SOFTWARE\Pesterchum "Install_Dir" "$INSTDIR"
|
||||
|
||||
; Write the uninstall keys for Windows
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum" "DisplayName" "PESTERCHUM"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum" "NoRepair" 1
|
||||
WriteUninstaller "uninstall.exe"
|
||||
|
||||
CreateDirectory "$SMPROGRAMS\Pesterchum"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Pesterchum.lnk" "$INSTDIR\pesterchum.exe"
|
||||
CreateShortcut "$DESKTOP\Pesterchum.lnk" "$INSTDIR\pesterchum.exe"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Readme.lnk" "$INSTDIR\readme.txt"
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
CreateShortcut "$SMPROGRAMS\Pesterchum\Logs.lnk" "$LOCALAPPDATA\pesterchum\logs"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
; Remove registry keys
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pesterchum"
|
||||
DeleteRegKey HKLM SOFTWARE\Pesterchum
|
||||
|
||||
; Remove files and uninstaller
|
||||
Delete $INSTDIR\uninstall.exe
|
||||
|
||||
; Remove shortcuts, if any
|
||||
Delete "$SMPROGRAMS\Pesterchum\*.*"
|
||||
|
||||
; Remove directories used
|
||||
RMDir "$SMPROGRAMS\Pesterchum"
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
SectionEnd
|
896
pesterchum.py
|
@ -13,7 +13,7 @@ class AttrDict(dict):
|
|||
super(AttrDict, self).__init__(init)
|
||||
|
||||
def __getstate__(self):
|
||||
return self.__dict__.items()
|
||||
return list(self.__dict__.items())
|
||||
def __setstate__(self, items):
|
||||
for key, val in items: self.__dict__[key] = val
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding=UTF-8; tab-width: 4 -*-
|
||||
from __future__ import division
|
||||
|
||||
|
||||
from .unicolor import Color
|
||||
|
||||
|
@ -8,7 +8,7 @@ import re
|
|||
global basestr
|
||||
basestr = str
|
||||
try:
|
||||
basestr = basestring
|
||||
basestr = str
|
||||
except NameError:
|
||||
# We're running Python 3. Leave it be.
|
||||
pass
|
||||
|
@ -160,7 +160,7 @@ class CTag(Specifier):
|
|||
cmatch = Pesterchum._ctag_rgb.match(text)
|
||||
if cmatch:
|
||||
working = cmatch.groups()
|
||||
working = map(int, working)
|
||||
working = list(map(int, working))
|
||||
inst.color = Color(*working)
|
||||
else:
|
||||
try:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding=UTF-8; tab-width: 4 -*-
|
||||
from __future__ import division
|
||||
|
||||
|
||||
__all__ = ["Color"]
|
||||
|
||||
|
@ -16,7 +16,7 @@ import sys
|
|||
|
||||
# Python 3 checking
|
||||
if sys.version_info[0] == 2:
|
||||
basestr = basestring
|
||||
basestr = str
|
||||
else:
|
||||
basestr = str
|
||||
|
||||
|
@ -119,7 +119,7 @@ class Color(object):
|
|||
## self.red, self.green, self.blue
|
||||
## ])
|
||||
# 2012-12-08T13:34-07:00: This should improve accuracy
|
||||
result = map(hash, self.cielab)
|
||||
result = list(map(hash, self.cielab))
|
||||
result = functools.reduce(lambda x, y: x ^ y, result)
|
||||
return result
|
||||
|
||||
|
@ -201,7 +201,7 @@ class Color(object):
|
|||
closest, cldist = None, None
|
||||
targs = _irc_colors
|
||||
|
||||
for code, other in targs.items():
|
||||
for code, other in list(targs.items()):
|
||||
dist = self - other
|
||||
##if (not strict and dist > self.jnd) or dist == 0:
|
||||
if dist == 0:
|
||||
|
@ -217,7 +217,7 @@ class Color(object):
|
|||
closest, cldist = None, None
|
||||
targs = _svg_colors
|
||||
|
||||
for name, other in targs.items():
|
||||
for name, other in list(targs.items()):
|
||||
dist = self - other
|
||||
if (not strict and dist > self.jnd) or dist == 0:
|
||||
# The difference is below the Just-Noticeable Difference
|
||||
|
@ -236,7 +236,7 @@ class Color(object):
|
|||
# http://en.wikipedia.org/wiki/Color_difference
|
||||
slab, olab = self.to_cielab_tuple(), other.to_cielab_tuple()
|
||||
# Calculate the distance between the points for each
|
||||
dist = map(lambda p1, p2: (p2 - p1)**2, slab, olab)
|
||||
dist = list(map(lambda p1, p2: (p2 - p1)**2, slab, olab))
|
||||
# Add the results up, and sqrt to compensate for earlier squaring
|
||||
dist = sum(dist) ** .5
|
||||
return dist
|
||||
|
@ -252,7 +252,7 @@ class Color(object):
|
|||
### Square the results from the above
|
||||
##dist = [x**2 for x in dist]
|
||||
# Do what we WOULD have done in those two lines with a single one
|
||||
dist = map(lambda x1, x2: (x1 - x2)**2, srgb, orgb)
|
||||
dist = list(map(lambda x1, x2: (x1 - x2)**2, srgb, orgb))
|
||||
# Add the results up
|
||||
dist = sum(dist)
|
||||
# Fetch the square root to compensate for the earlier squaring
|
||||
|
@ -284,7 +284,7 @@ class Color(object):
|
|||
@staticmethod
|
||||
def rgb_to_hexstr(red, green, blue, compress=False):
|
||||
rgb = [red, green, blue]
|
||||
rgb = map(abs, rgb)
|
||||
rgb = list(map(abs, rgb))
|
||||
result = []
|
||||
for c in rgb:
|
||||
c = "%02X" % c
|
||||
|
@ -547,7 +547,7 @@ _svg_colors.update({
|
|||
"yellow": Color(255, 255, 0),
|
||||
"yellowgreen": Color(154, 205, 50)
|
||||
})
|
||||
for k, v in _svg_colors.items():
|
||||
for k, v in list(_svg_colors.items()):
|
||||
v.closest_name = v.name = k
|
||||
|
||||
# 2012-12-08T14:29-07:00: Copied over from Colors.hexstr_for_ccodes in the main
|
||||
|
@ -594,7 +594,7 @@ _irc_colors.update({
|
|||
|
||||
99: Color(0x999999) # Until I think of a better solution to this
|
||||
})
|
||||
for k, v in _irc_colors.items():
|
||||
for k, v in list(_irc_colors.items()):
|
||||
v.ccode = "%02d" % k
|
||||
del k, v
|
||||
|
||||
|
|
76
profile.py
|
@ -7,7 +7,7 @@ import codecs
|
|||
import platform
|
||||
from datetime import *
|
||||
from time import strftime, time
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
import ostools
|
||||
from mood import Mood
|
||||
|
@ -48,22 +48,22 @@ class PesterLog(object):
|
|||
html = time + convertTags(msg, "html")+"<br />"
|
||||
msg = time +convertTags(msg, "text")
|
||||
modes = {"bbcode": bbcodemsg, "html": html, "text": msg}
|
||||
if not self.convos.has_key(handle):
|
||||
if handle not in self.convos:
|
||||
time = datetime.now().strftime("%Y-%m-%d.%H.%M")
|
||||
self.convos[handle] = {}
|
||||
for (format, t) in modes.iteritems():
|
||||
for (format, t) in modes.items():
|
||||
if not os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, handle, format)):
|
||||
os.makedirs("%s/%s/%s/%s" % (self.logpath, self.handle, handle, format))
|
||||
try:
|
||||
fp = codecs.open("%s/%s/%s/%s/%s.%s.txt" % (self.logpath, self.handle, handle, format, handle, time), encoding='utf-8', mode='a')
|
||||
except IOError:
|
||||
errmsg = QtGui.QMessageBox(self)
|
||||
errmsg = QtWidgets.QMessageBox(self)
|
||||
errmsg.setText("Warning: Pesterchum could not open the log file for %s!" % (handle))
|
||||
errmsg.setInformativeText("Your log for %s will not be saved because something went wrong. We suggest restarting Pesterchum. Sorry :(" % (handle))
|
||||
errmsg.show()
|
||||
continue
|
||||
self.convos[handle][format] = fp
|
||||
for (format, t) in modes.iteritems():
|
||||
for (format, t) in modes.items():
|
||||
f = self.convos[handle][format]
|
||||
if platform.system() == "Windows":
|
||||
f.write(t+"\r\n")
|
||||
|
@ -71,14 +71,14 @@ class PesterLog(object):
|
|||
f.write(t+"\r\n")
|
||||
f.flush()
|
||||
def finish(self, handle):
|
||||
if not self.convos.has_key(handle):
|
||||
if handle not in self.convos:
|
||||
return
|
||||
for f in self.convos[handle].values():
|
||||
for f in list(self.convos[handle].values()):
|
||||
f.close()
|
||||
del self.convos[handle]
|
||||
def close(self):
|
||||
for h in self.convos.keys():
|
||||
for f in self.convos[h].values():
|
||||
for h in list(self.convos.keys()):
|
||||
for f in list(self.convos[h].values()):
|
||||
f.close()
|
||||
|
||||
class userConfig(object):
|
||||
|
@ -104,7 +104,7 @@ class userConfig(object):
|
|||
# No such file or directory:
|
||||
# u'XXX\\AppData\\Local\\pesterchum/profiles/XXX.js'
|
||||
# Part 2 :(
|
||||
if self.config.has_key("defaultprofile"):
|
||||
if "defaultprofile" in self.config:
|
||||
try:
|
||||
self.userprofile = userProfile(self.config["defaultprofile"])
|
||||
except:
|
||||
|
@ -125,7 +125,7 @@ class userConfig(object):
|
|||
json.dump(self.groups, fp)
|
||||
|
||||
def chums(self):
|
||||
if not self.config.has_key('chums'):
|
||||
if 'chums' not in self.config:
|
||||
self.set("chums", [])
|
||||
return self.config.get('chums', [])
|
||||
def setChums(self, newchums):
|
||||
|
@ -148,19 +148,19 @@ class userConfig(object):
|
|||
def tabs(self):
|
||||
return self.config.get("tabs", True)
|
||||
def tabMemos(self):
|
||||
if not self.config.has_key('tabmemos'):
|
||||
if 'tabmemos' not in self.config:
|
||||
self.set("tabmemos", self.tabs())
|
||||
return self.config.get("tabmemos", True)
|
||||
def showTimeStamps(self):
|
||||
if not self.config.has_key('showTimeStamps'):
|
||||
if 'showTimeStamps' not in self.config:
|
||||
self.set("showTimeStamps", True)
|
||||
return self.config.get('showTimeStamps', True)
|
||||
def time12Format(self):
|
||||
if not self.config.has_key('time12Format'):
|
||||
if 'time12Format' not in self.config:
|
||||
self.set("time12Format", True)
|
||||
return self.config.get('time12Format', True)
|
||||
def showSeconds(self):
|
||||
if not self.config.has_key('showSeconds'):
|
||||
if 'showSeconds' not in self.config:
|
||||
self.set("showSeconds", False)
|
||||
return self.config.get('showSeconds', False)
|
||||
def sortMethod(self):
|
||||
|
@ -174,11 +174,11 @@ class userConfig(object):
|
|||
return g[1]
|
||||
return True
|
||||
def showEmptyGroups(self):
|
||||
if not self.config.has_key('emptyGroups'):
|
||||
if 'emptyGroups' not in self.config:
|
||||
self.set("emptyGroups", False)
|
||||
return self.config.get('emptyGroups', False)
|
||||
def showOnlineNumbers(self):
|
||||
if not self.config.has_key('onlineNumbers'):
|
||||
if 'onlineNumbers' not in self.config:
|
||||
self.set("onlineNumbers", False)
|
||||
return self.config.get('onlineNumbers', False)
|
||||
def logPesters(self):
|
||||
|
@ -238,7 +238,7 @@ class userConfig(object):
|
|||
newchums = [c for c in self.config['chums'] if c != handle]
|
||||
self.set("chums", newchums)
|
||||
def getBlocklist(self):
|
||||
if not self.config.has_key('block'):
|
||||
if 'block' not in self.config:
|
||||
self.set('block', [])
|
||||
return self.config['block']
|
||||
def addBlocklist(self, handle):
|
||||
|
@ -251,7 +251,7 @@ class userConfig(object):
|
|||
l.pop(l.index(handle))
|
||||
self.set('block', l)
|
||||
def getGroups(self):
|
||||
if not self.groups.has_key('groups'):
|
||||
if 'groups' not in self.groups:
|
||||
self.saveGroups([["Chums", True]])
|
||||
return self.groups.get('groups', [["Chums", True]])
|
||||
def addGroup(self, group, open=True):
|
||||
|
@ -301,7 +301,7 @@ class userConfig(object):
|
|||
return self.parent.portOverride
|
||||
return self.config.get('port', '6667')
|
||||
def soundOn(self):
|
||||
if not self.config.has_key('soundon'):
|
||||
if 'soundon' not in self.config:
|
||||
self.set('soundon', True)
|
||||
return self.config['soundon']
|
||||
def chatSound(self):
|
||||
|
@ -356,7 +356,7 @@ class userProfile(object):
|
|||
if type(user) is PesterProfile:
|
||||
self.chat = user
|
||||
self.userprofile = {"handle":user.handle,
|
||||
"color": unicode(user.color.name()),
|
||||
"color": str(user.color.name()),
|
||||
"quirks": [],
|
||||
"theme": "pesterchum"}
|
||||
self.theme = pesterTheme("pesterchum")
|
||||
|
@ -382,8 +382,8 @@ class userProfile(object):
|
|||
self.userprofile = json.load(fp)
|
||||
except:
|
||||
|
||||
msgBox = QtGui.QMessageBox()
|
||||
msgBox.setIcon(QtGui.QMessageBox.Information)
|
||||
msgBox = QtWidgets.QMessageBox()
|
||||
msgBox.setIcon(QtWidgets.QMessageBox.Information)
|
||||
msgBox.setWindowTitle(":(")
|
||||
self.filename = _datadir+"pesterchum.js"
|
||||
msgBox.setText("Failed to open \"" + \
|
||||
|
@ -449,7 +449,7 @@ class userProfile(object):
|
|||
self.save()
|
||||
def setColor(self, color):
|
||||
self.chat.color = color
|
||||
self.userprofile["color"] = unicode(color.name())
|
||||
self.userprofile["color"] = str(color.name())
|
||||
self.save()
|
||||
def setQuirks(self, quirks):
|
||||
self.quirks = quirks
|
||||
|
@ -467,7 +467,7 @@ class userProfile(object):
|
|||
try:
|
||||
for (i,m) in enumerate(mentions):
|
||||
re.compile(m)
|
||||
except re.error, e:
|
||||
except re.error as e:
|
||||
logging.error("#%s Not a valid regular expression: %s" % (i, e))
|
||||
else:
|
||||
self.mentions = mentions
|
||||
|
@ -516,7 +516,7 @@ class userProfile(object):
|
|||
fp.write(jsonoutput)
|
||||
def saveNickServPass(self):
|
||||
# remove profiles with no passwords
|
||||
for h,t in self.passwd.items():
|
||||
for h,t in list(self.passwd.items()):
|
||||
if "auto" not in t and ("pw" not in t or t["pw"] == ""):
|
||||
del self.passwd[h]
|
||||
try:
|
||||
|
@ -550,7 +550,7 @@ class PesterProfileDB(dict):
|
|||
json.dump(chumdict, fp)
|
||||
|
||||
u = []
|
||||
for (handle, c) in chumdict.iteritems():
|
||||
for (handle, c) in chumdict.items():
|
||||
options = dict()
|
||||
if 'group' in c:
|
||||
options['group'] = c['group']
|
||||
|
@ -567,38 +567,38 @@ class PesterProfileDB(dict):
|
|||
def save(self):
|
||||
try:
|
||||
with open("%s/chums.js" % (self.logpath), 'w') as fp:
|
||||
chumdict = dict([p.plaindict() for p in self.itervalues()])
|
||||
chumdict = dict([p.plaindict() for p in self.values()])
|
||||
json.dump(chumdict, fp)
|
||||
except Exception as e:
|
||||
raise e
|
||||
def getColor(self, handle, default=None):
|
||||
if not self.has_key(handle):
|
||||
if handle not in self:
|
||||
return default
|
||||
else:
|
||||
return self[handle].color
|
||||
def setColor(self, handle, color):
|
||||
if self.has_key(handle):
|
||||
if handle in self:
|
||||
self[handle].color = color
|
||||
else:
|
||||
self[handle] = PesterProfile(handle, color)
|
||||
def getGroup(self, handle, default="Chums"):
|
||||
if not self.has_key(handle):
|
||||
if handle not in self:
|
||||
return default
|
||||
else:
|
||||
return self[handle].group
|
||||
def setGroup(self, handle, theGroup):
|
||||
if self.has_key(handle):
|
||||
if handle in self:
|
||||
self[handle].group = theGroup
|
||||
else:
|
||||
self[handle] = PesterProfile(handle, group=theGroup)
|
||||
self.save()
|
||||
def getNotes(self, handle, default=""):
|
||||
if not self.has_key(handle):
|
||||
if handle not in self:
|
||||
return default
|
||||
else:
|
||||
return self[handle].notes
|
||||
def setNotes(self, handle, notes):
|
||||
if self.has_key(handle):
|
||||
if handle in self:
|
||||
self[handle].notes = notes
|
||||
else:
|
||||
self[handle] = PesterProfile(handle, notes=notes)
|
||||
|
@ -626,7 +626,7 @@ class pesterTheme(dict):
|
|||
except IOError:
|
||||
theme = json.loads("{}")
|
||||
self.update(theme)
|
||||
if self.has_key("inherits"):
|
||||
if "inherits" in self:
|
||||
self.inheritedTheme = pesterTheme(self["inherits"])
|
||||
if not default:
|
||||
self.defaultTheme = pesterTheme("pesterchum", default=True)
|
||||
|
@ -653,8 +653,8 @@ class pesterTheme(dict):
|
|||
raise e
|
||||
return v
|
||||
def pathHook(self, d):
|
||||
for (k, v) in d.iteritems():
|
||||
if isinstance(v, unicode):
|
||||
for (k, v) in d.items():
|
||||
if isinstance(v, str):
|
||||
s = Template(v)
|
||||
d[k] = s.safe_substitute(path=self.path)
|
||||
return d
|
||||
|
@ -680,6 +680,6 @@ class pesterTheme(dict):
|
|||
return (v is not None)
|
||||
except KeyError:
|
||||
if hasattr(self, 'inheritedTheme'):
|
||||
return self.inheritedTheme.has_key(key)
|
||||
return key in self.inheritedTheme
|
||||
else:
|
||||
return False
|
||||
|
|
139
py2app.sh
|
@ -1,139 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
## Cleanup
|
||||
rm -rf build/ dist/
|
||||
rm -f Pesterchum.dmg
|
||||
|
||||
### Force build with custom installed frameworky python not system python
|
||||
/Library/Frameworks/Python.framework/Versions/2.6/bin/python setup-py2app.py py2app
|
||||
#python setup-py2app.py py2app
|
||||
#
|
||||
### Do some .app tings
|
||||
touch dist/Pesterchum.app/Contents/Resources/qt.conf
|
||||
find dist/Pesterchum.app -iname "*_debug" -exec rm -f '{}' \;
|
||||
|
||||
## Create a dmg file to hold everything
|
||||
VERSION=$(python version.py) #"3.41.2 Beta 5"
|
||||
SIZE=2000
|
||||
name="Pesterchum"
|
||||
title="${name} ${VERSION}"
|
||||
CHANGELOG="Changelog.rtf"
|
||||
PYQUIRKS="Python Quirks.rtf"
|
||||
TODO="To Do.rtf"
|
||||
README="Read Me!.rtf"
|
||||
## Make a proper installer dmg not just effectively a zip file.
|
||||
#
|
||||
# Most of this is from http://stackoverflow.com/questions/96882/
|
||||
# I've fiddled with it a little
|
||||
|
||||
# Store the background picture (in PNG format) in a folder called ".background"
|
||||
# in the DMG, and store its name in the "backgroundPictureName" variable.
|
||||
|
||||
mkdir dist/.background
|
||||
cp MacBuild/dmg_background.png dist/.background/display.png
|
||||
|
||||
# Convert markdown files to rich text files
|
||||
convert=~/Library/Haskell/bin/pandoc
|
||||
if ! test -e "${convert}"
|
||||
then
|
||||
echo "Please install pandoc from http://johnmacfarlane.net/pandoc/" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Converting CHANGELOG . . . "
|
||||
$convert --standalone --smart --from=markdown --to=rtf --output="dist/${CHANGELOG}" CHANGELOG.mkdn
|
||||
echo "Converting PYQUIRKS . . ."
|
||||
$convert --standalone --smart --from=markdown --to=rtf --output="dist/${PYQUIRKS}" PYQUIRKS.mkdn
|
||||
echo "Converting TODO . . ."
|
||||
$convert --standalone --smart --from=markdown --to=rtf --output="dist/${TODO}" TODO.mkdn
|
||||
echo "Converting README . . ."
|
||||
$convert --standalone --smart --from=markdown --to=rtf --output="dist/${README}" README.mkdn
|
||||
|
||||
# Create a R/W DMG. It must be larger than the result will be.
|
||||
# In this example, the bash variable "size" contains the size
|
||||
# in Kb and the contents of the folder in the "source" bash
|
||||
# variable will be copied into the DMG:
|
||||
# Note: I've removed the size argument
|
||||
|
||||
echo "Creating initial DMG file . . ."
|
||||
hdiutil create -srcfolder "./dist" -volname "${title}" -fs HFS+ \
|
||||
-fsargs "-c c=64,a=16,e=16" -format UDRW pack.temp.dmg
|
||||
|
||||
# Mount the disk image, and store the device name
|
||||
# (you might want to use sleep for a few seconds after this operation):
|
||||
|
||||
echo "Mounting initial DMG file . . ."
|
||||
device=$(hdiutil attach -readwrite -noverify -noautoopen "pack.temp.dmg" | \
|
||||
egrep '^/dev/' | sed 1q | awk '{print $1}')
|
||||
sleep 2
|
||||
|
||||
|
||||
|
||||
# Use AppleScript to set the visual styles (name of .app must be in bash variable
|
||||
# "applicationName", use variables for the other properties as needed):
|
||||
base=100
|
||||
iconsize=72
|
||||
padding=18
|
||||
echo "Making DMG file pretty with Applescript . . ."
|
||||
echo '
|
||||
tell application "Finder"
|
||||
tell disk "'${title}'"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {400, 100, 885, 430}
|
||||
set theViewOptions to the icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 72
|
||||
set background picture of theViewOptions to file ".background:display.png"
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
-- Positions
|
||||
set position of item "'${name}'.app" of container window to {100, 100}
|
||||
set position of item "Applications" of container window to {375, 100}
|
||||
set position of item "'${README}'" of container window to {'${base}+${iconsize}*0+${padding}*0', 244}
|
||||
set position of item "'${CHANGELOG}'" of container window to {'${base}+${iconsize}*1+${padding}*1', 244}
|
||||
set position of item "'${PYQUIRKS}'" of container window to {'${base}+${iconsize}*2+${padding}*2', 244}
|
||||
set position of item "'${TODO}'" of container window to {'${base}+${iconsize}*3+${padding}*3', 244}
|
||||
-- Visibility
|
||||
set extension hidden of item "'${CHANGELOG}'" of container window to true
|
||||
set extension hidden of item "'${PYQUIRKS}'" of container window to true
|
||||
set extension hidden of item "'${TODO}'" of container window to true
|
||||
set extension hidden of item "'${README}'" of container window to true
|
||||
close
|
||||
open
|
||||
update without registering applications
|
||||
delay 5
|
||||
eject
|
||||
end tell
|
||||
end tell
|
||||
' | osascript
|
||||
|
||||
# This took so long to work out how to do ._.
|
||||
# Stolen from http://lists.apple.com/archives/darwin-userlevel/2007/Oct/msg00000.html
|
||||
# Set the SLA to read only (dunno why)
|
||||
echo "Converting initial DMG file to a UDRO one . . ."
|
||||
hdiutil convert -ov -format UDRO -o "sla.temp.dmg" "pack.temp.dmg"
|
||||
# Inflate the dmg
|
||||
echo "Inflating UDRO DMG file . . ."
|
||||
hdiutil unflatten "sla.temp.dmg"
|
||||
# Attach the GPL
|
||||
echo "Attaching GPL licence . . ."
|
||||
Rez -a MacBuild/GPL.res -o "sla.temp.dmg"
|
||||
# Steamroller again
|
||||
echo "Deflating UDRO DMG file . . ."
|
||||
hdiutil flatten "sla.temp.dmg"
|
||||
|
||||
# Finialize the DMG by setting permissions properly, compressing and releasing it:
|
||||
|
||||
#chmod -Rf go-w /Volumes/"${title}"
|
||||
sync
|
||||
#hdiutil detach ${device}
|
||||
echo "Compressing UDRO DMG file to UDZO for release . . ."
|
||||
hdiutil convert "sla.temp.dmg" -format UDZO -imagekey zlib-level=6 -o "${name}.dmg"
|
||||
|
||||
# Get rid of the bits
|
||||
echo "Cleaning up . . ."
|
||||
rm -f pack.temp.dmg
|
||||
rm -f sla.temp.dmg
|
||||
rm -rf build/ dist/
|
12
pyquirks.py
|
@ -1,6 +1,6 @@
|
|||
import os, sys, imp, re, ostools
|
||||
from quirks import ScriptQuirks
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class PythonQuirks(ScriptQuirks):
|
||||
def loadModule(self, name, filename):
|
||||
|
@ -12,21 +12,21 @@ class PythonQuirks(ScriptQuirks):
|
|||
def modHas(self, module, attr):
|
||||
if attr == 'commands':
|
||||
variables = vars(module)
|
||||
for name, obj in variables.iteritems():
|
||||
for name, obj in variables.items():
|
||||
if self.modHas(obj, 'command'):
|
||||
return True
|
||||
return hasattr(module, attr)
|
||||
|
||||
def register(self, module):
|
||||
variables = vars(module)
|
||||
for name, obj in variables.iteritems():
|
||||
for name, obj in variables.items():
|
||||
if self.modHas(obj, 'command'):
|
||||
try:
|
||||
if not isinstance(obj("test"), basestring):
|
||||
if not isinstance(obj("test"), str):
|
||||
raise Exception
|
||||
except:
|
||||
print "Quirk malformed: %s" % (obj.command)
|
||||
msgbox = QtGui.QMessageBox()
|
||||
print("Quirk malformed: %s" % (obj.command))
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setWindowTitle("Error!")
|
||||
msgbox.setText("Quirk malformed: %s" % (obj.command))
|
||||
msgbox.exec_()
|
||||
|
|
12
quirks.py
|
@ -1,5 +1,5 @@
|
|||
import os, sys, re, ostools
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class ScriptQuirks(object):
|
||||
def __init__(self):
|
||||
|
@ -20,7 +20,7 @@ class ScriptQuirks(object):
|
|||
self.last = self.quirks.copy()
|
||||
self.quirks.clear()
|
||||
for script in self.scripts:
|
||||
print script.getExtension()
|
||||
print(script.getExtension())
|
||||
script.load()
|
||||
#print script.quirks
|
||||
for q in script.quirks:
|
||||
|
@ -31,9 +31,9 @@ class ScriptQuirks(object):
|
|||
del self.quirks[k]
|
||||
#print self.quirks
|
||||
if self.quirks:
|
||||
print 'Registered quirks:', '(), '.join(self.quirks) + "()"
|
||||
print('Registered quirks:', '(), '.join(self.quirks) + "()")
|
||||
else:
|
||||
print "Warning: Couldn't find any script quirks"
|
||||
print("Warning: Couldn't find any script quirks")
|
||||
|
||||
def add(self, script):
|
||||
self.scripts.append(script)
|
||||
|
@ -65,8 +65,8 @@ class ScriptQuirks(object):
|
|||
if module is None:
|
||||
continue
|
||||
except Exception as e:
|
||||
print "Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e)
|
||||
msgbox = QtGui.QMessageBox()
|
||||
print("Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e))
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setWindowTitle("Error!")
|
||||
msgbox.setText("Error loading %s: %s (in quirks.py)" % (os.path.basename(filename), e))
|
||||
msgbox.exec_()
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
module(..., package.seeall)
|
||||
commands = {}
|
||||
|
||||
local function upper(text)
|
||||
return string.upper(text)
|
||||
end
|
||||
commands.luaupper = upper
|
||||
|
||||
local function lower(text)
|
||||
return string.lower(text)
|
||||
end
|
||||
commands.lualower = lower
|
||||
|
||||
local function utf8reverse(text)
|
||||
return text:gsub("([\194-\244][\128-\191]+)", string.reverse):reverse()
|
||||
end
|
||||
commands.luareverse = utf8reverse
|
||||
|
10
randomer.py
|
@ -1,4 +1,4 @@
|
|||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
RANDNICK = "randomEncounter"
|
||||
|
||||
|
@ -58,12 +58,12 @@ class RandomHandler(QtCore.QObject):
|
|||
pass
|
||||
elif code == "!":
|
||||
if l[1] == "x":
|
||||
from PyQt4 import QtGui
|
||||
msgbox = QtGui.QMessageBox()
|
||||
from PyQt5 import QtGui, QtWidgets
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setText("Unable to fetch you a random encounter!")
|
||||
msgbox.setInformativeText("Try again later :(")
|
||||
msgbox.exec_()
|
||||
return
|
||||
name = unicode(l[1])
|
||||
print name
|
||||
name = str(l[1])
|
||||
print(name)
|
||||
self.mainwindow.newConversation(name)
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"server": "pesterchum.xyz"
|
||||
}
|
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 320 B |
Before Width: | Height: | Size: 430 B |
Before Width: | Height: | Size: 381 B |
Before Width: | Height: | Size: 303 B |
Before Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 416 B |
BIN
smilies/box.png
Before Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 399 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 625 B |
Before Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 510 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 594 B |
BIN
smilies/honk.png
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 767 B |
Before Width: | Height: | Size: 542 B |
Before Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 279 B |
Before Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 288 B |
Before Width: | Height: | Size: 261 B |
Before Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 261 B |
Before Width: | Height: | Size: 261 B |
Before Width: | Height: | Size: 263 B |