Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
Version 0.3 -- not yet released
* Undo/Redo
* Accelerated stroke deletion
* Search
* Rect tool
* Line tool
* Circle tool
* Fill color
* Tool bar with icons
* Keyboard shortcuts
* Tool item preview
* Changed servers file format from Pickle to JSON

>>>>>>> upstream/master
Version 0.2.1 -- 2012-07-31
* Support for system-wide installation via setup.py
* Fix a bug, which caused cournal to consume too much CPU if the connection
Expand Down
7 changes: 5 additions & 2 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
This is a TODO List with (hopefully) future Cournal Features!
(The order is no representation of a ranking of any kind)

* Keymapping (Ctrl+s for saving, Ctrl+o for opening a file, etc)
* Remember the last Server connected to
* Commandlineinterface (CLI)
* Chat with both keyboard and handwriting compatibility
Expand All @@ -15,4 +14,8 @@ This is a TODO List with (hopefully) future Cournal Features!
* Support to open more than one PDF Document
* Android Port
* Show position of other users in scrollbar

* Error handling
* Change Pickle to JSON
* Make fill color chooser more discreet
* Set sensitivity for not incompatible actions
* Print
134 changes: 134 additions & 0 deletions cournal/document/circle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# This file is part of Cournal.
# Copyright (C) 2012 Simon Vetter
#
# Cournal 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.
#
# Cournal 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 Cournal. If not, see <http://www.gnu.org/licenses/>.

import cairo
import math

from twisted.spread import pb

class Circle(pb.Copyable, pb.RemoteCopy):
def __init__(self, layer, color, fill, fill_color, linewidth, coords, scale):
"""
Constructor

Positional arguments:
layer -- The Layer object, which is the parent of the stroke.
color -- tuple of four: (red, green, blue, opacity)
linewidth -- Line width in pt

Keyword arguments:
coords -- A list of coordinates (defaults to [])
"""
self.layer = layer
self.color = color
self.fill_color = fill_color
self.fill = fill
self.linewidth = linewidth
self.coords = coords
self.scale = scale

def in_bounds(self, x, y, radius):
"""
Test if point is in bounding box of the stroke.

Positional arguments:
x, y -- point

Returns:
true, if point is in bounding box
"""
distance = ((self.coords[0] - x)/self.scale[0])**2 + ((self.coords[1] - y)/self.scale[1])**2
if distance < 1+radius/min(self.scale[0],self.scale[1]):
if not self.fill:
if distance < 1-radius/min(self.scale[0],self.scale[1]):
return False
return True
else:
return False

def calculate_bounding_box(self, radius=5):
pass

def getStateToCopy(self):
"""Gather state to send when I am serialized for a peer."""

# d would be self.__dict__.copy()
d = dict()
d["color"] = self.color
d["fill_color"] = self.fill_color
d["fill"] = self.fill
d["coords"] = self.coords
d["scale"] = self.scale
d["linewidth"] = self.linewidth
return d

def draw(self, context, scaling=1):
"""
Render this stroke

Positional arguments:
context -- The cairo context to draw on

Keyword arguments:
scaling -- scale the stroke by this factor (defaults to 1.0)
"""
if self.scale[0] * self.scale[1] * self.scale[2] == 0:
return

context.save()
r, g, b, opacity = self.fill_color

context.set_source_rgba(r/255, g/255, b/255, opacity/255)
context.set_antialias(cairo.ANTIALIAS_GRAY)
context.set_line_join(cairo.LINE_JOIN_ROUND)
context.set_line_cap(cairo.LINE_CAP_ROUND)
context.set_line_width(self.linewidth)

if self.fill:
r, g, b, opacity = self.fill_color
context.set_source_rgba(r/255, g/255, b/255, opacity/255)
# Draw Circle and scale it to oval
context.translate(self.coords[0], self.coords[1])
context.scale(self.scale[0]/self.scale[2], self.scale[1]/self.scale[2])
context.arc(0., 0., self.scale[2], 0., 2 * math.pi)
context.scale(1 / (self.scale[0]/self.scale[2]), 1 / (self.scale[1]/self.scale[2]))
context.translate(-self.coords[0], -self.coords[1])
context.fill()

# Draw border
i = 0
context.move_to(self.coords[0], self.coords[1] + self.scale[1])
# We draw the border with strokes to keep konstant border width
while i < (math.pi * 2):
i += 0.1
context.line_to(
self.coords[0] + math.sin(i)*self.scale[0],
self.coords[1] + math.cos(i)*self.scale[1])

r, g, b, opacity = self.color
context.set_source_rgba(r/255, g/255, b/255, opacity/255)

x, y, x2, y2 = (a*scaling for a in context.stroke_extents())
context.stroke()
context.restore()

return (x, y, x2, y2)

# Tell Twisted, that this class is allowed to be transmitted over the network.
pb.setUnjellyableForClass(Circle, Circle)
35 changes: 19 additions & 16 deletions cournal/document/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from cournal.document.page import Page
from cournal.document import history
from cournal.document import search
from cournal.document.stroke import Stroke

class Document:
"""
Expand Down Expand Up @@ -60,19 +61,19 @@ def __init__(self, pdfname):

def is_empty(self):
"""
Returns True, if no page of this document has a stroke on it.
Returns True, if no page of this document has a item on it.
Otherwise False
"""
for page in self.pages:
if len(page.layers[0].strokes) != 0:
if len(page.layers[0].items) != 0:
return False
return True

def clear_pages(self):
"""Deletes all strokes on all pages of this document"""
"""Deletes all item on all pages of this document"""
for page in self.pages:
for stroke in page.layers[0].strokes[:]:
page.delete_stroke(stroke, send_to_network=False)
for item in page.layers[0].items[:]:
page.delete_item(item, send_to_network=False)

def export_pdf(self, filename):
"""
Expand All @@ -94,8 +95,8 @@ def export_pdf(self, filename):

page.pdf.render_for_printing(context)

for stroke in page.layers[0].strokes:
stroke.draw(context)
for item in page.layers[0].items:
item.draw(context)

surface.show_page() # aka "next page"

Expand Down Expand Up @@ -131,15 +132,17 @@ def save_xoj_file(self, filename):

for layer in page.layers:
r += "<layer>\n"
for stroke in layer.strokes:
red, g, b, opacity = stroke.color
r += "<stroke tool=\"pen\" color=\"#{:02X}{:02X}{:02X}{:02X}\" width=\"{}\">\n".format(red, g, b, opacity, stroke.linewidth)
first = stroke.coords[0]
for coord in stroke.coords:
r += " {} {}".format(coord[0], coord[1])
if len(stroke.coords) < 2:
r += " {} {}".format(first[0], first[1])
r += "\n</stroke>\n"
for item in layer.items:
#TODO: Parse other Items
if isinstance(item, Stroke):
red, g, b, opacity = item.color
r += "<stroke tool=\"pen\" color=\"#{:02X}{:02X}{:02X}{:02X}\" width=\"{}\">\n".format(red, g, b, opacity, item.linewidth)
first = item.coords[0]
for coord in item.coords:
r += " {} {}".format(coord[0], coord[1])
if len(item.coords) < 2:
r += " {} {}".format(first[0], first[1])
r += "\n</stroke>\n"
r += "</layer>\n"
r += "</page>\n"
r += "</xournal>"
Expand Down
44 changes: 22 additions & 22 deletions cournal/document/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,25 @@ def redo(action):
if len(_redo_list) == 0:
_redo_action.set_sensitive(False)

def register_draw_stroke(stroke, page):
def register_draw_item(item, page):
"""
Register draw stroke command in history.
Register draw item command in history.

Positional arguments:
stroke -- drawn stroke
page -- page stroke was drawn on
item -- drawn item
page -- page item was drawn on
"""
add_undo_command(CommandDrawStroke(stroke, page))
add_undo_command(CommandDrawItem(item, page))

def register_delete_stroke(stroke, page):
def register_delete_item(item, page):
"""
Register delete stroke command in history.
Register delete item command in history.

Positional arguments:
stroke -- deleted stroke
page -- page stroke was deleted from
item -- deleted item
page -- page item was deleted from
"""
add_undo_command(CommandDeleteStroke(stroke, page))
add_undo_command(CommandDeleteItem(item, page))

def add_undo_command(command, clear_redo=True):
"""
Expand Down Expand Up @@ -108,26 +108,26 @@ def add_redo_command(command):
if len(_redo_list) == 1:
_redo_action.set_sensitive(True)

class CommandDrawStroke:
"""Draw stroke command."""
def __init__(self, stroke, page):
self.stroke = stroke
class CommandDrawItem:
"""Draw item command."""
def __init__(self, item, page):
self.item = item
self.page = page

def undo(self):
self.page.delete_stroke(self.stroke, send_to_network=True, register_in_history=False)
self.page.delete_item(self.item, send_to_network=True, register_in_history=False)

def redo(self):
self.page.new_stroke(self.stroke, send_to_network=True)
self.page.new_item(self.item, send_to_network=True)

class CommandDeleteStroke:
"""Delete stroke command."""
def __init__(self, stroke, page):
self.stroke = stroke
class CommandDeleteItem:
"""Delete item command."""
def __init__(self, item, page):
self.item = item
self.page = page

def undo(self):
self.page.new_stroke(self.stroke, send_to_network=True)
self.page.new_item(self.item, send_to_network=True)

def redo(self):
self.page.delete_stroke(self.stroke, send_to_network=True, register_in_history=False)
self.page.delete_item(self.item, send_to_network=True, register_in_history=False)
12 changes: 6 additions & 6 deletions cournal/document/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

class Layer:
"""
A layer on a page, having a number and multiple strokes.
A layer on a page, having a number and multiple item.
"""
def __init__(self, page, number, strokes=None):
def __init__(self, page, number, items=None):
"""
Constructor

Expand All @@ -30,11 +30,11 @@ def __init__(self, page, number, strokes=None):
number -- Layer number

Keyword arguments:
strokes -- List of Stroke objects (defaults to [])
item -- List of items (defaults to [])
"""
self.number = number
self.page = page
self.strokes = strokes
self.items = items

if self.strokes is None:
self.strokes = []
if self.items is None:
self.items = []
Loading