From 37191b981631f460ef060b8312e2b570e9631eab Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Tue, 11 Sep 2012 23:18:53 +0200 Subject: [PATCH 01/19] Added Tools: * rect --- cournal/document/document.py | 26 +++---- cournal/document/layer.py | 12 ++-- cournal/document/page.py | 70 ++++++++++--------- cournal/document/xojparser.py | 2 +- cournal/mainwindow.glade | 119 ++++++++++++++++++++++++++++++++- cournal/mainwindow.py | 64 +++++++++++++++--- cournal/network.py | 32 ++++----- cournal/server/server.py | 47 ++++++------- cournal/viewer/pagewidget.py | 28 ++++---- cournal/viewer/tools/eraser.py | 24 +++---- cournal/viewer/tools/pen.py | 13 ++-- 11 files changed, 298 insertions(+), 139 deletions(-) diff --git a/cournal/document/document.py b/cournal/document/document.py index 8c8a782..d8d743d 100644 --- a/cournal/document/document.py +++ b/cournal/document/document.py @@ -56,19 +56,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 object on it. Otherwise False """ for page in self.pages: - if len(page.layers[0].strokes) != 0: + if len(page.layers[0].obj) != 0: return False return True def clear_pages(self): - """Deletes all strokes on all pages of this document""" + """Deletes all objects 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 obj in page.layers[0].obj[:]: + page.delete_obj(obj, send_to_network=False) def export_pdf(self, filename): """ @@ -90,8 +90,8 @@ def export_pdf(self, filename): page.pdf.render_for_printing(context) - for stroke in page.layers[0].strokes: - stroke.draw(context) + for obj in page.layers[0].obj: + obj.draw(context) surface.show_page() # aka "next page" @@ -127,13 +127,13 @@ def save_xoj_file(self, filename): for layer in page.layers: r += "\n" - for stroke in layer.strokes: - red, g, b, opacity = stroke.color - r += "\n".format(red, g, b, opacity, stroke.linewidth) - first = stroke.coords[0] - for coord in stroke.coords: + for obj in layer.obj: + red, g, b, opacity = obj.color + r += "\n".format(red, g, b, opacity, obj.linewidth) + first = obj.coords[0] + for coord in obj.coords: r += " {} {}".format(coord[0], coord[1]) - if len(stroke.coords) < 2: + if len(obj.coords) < 2: r += " {} {}".format(first[0], first[1]) r += "\n\n" r += "\n" diff --git a/cournal/document/layer.py b/cournal/document/layer.py index 890067f..4008def 100644 --- a/cournal/document/layer.py +++ b/cournal/document/layer.py @@ -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 objects. """ - def __init__(self, page, number, strokes=None): + def __init__(self, page, number, obj=None): """ Constructor @@ -30,11 +30,11 @@ def __init__(self, page, number, strokes=None): number -- Layer number Keyword arguments: - strokes -- List of Stroke objects (defaults to []) + obj -- List of objects (defaults to []) """ self.number = number self.page = page - self.strokes = strokes + self.obj = obj - if self.strokes is None: - self.strokes = [] + if self.obj is None: + self.obj = [] diff --git a/cournal/document/page.py b/cournal/document/page.py index 6e4cdbd..d29e768 100644 --- a/cournal/document/page.py +++ b/cournal/document/page.py @@ -21,6 +21,7 @@ from cournal.document.layer import Layer from cournal.document.stroke import Stroke +from cournal.document.rect import Rect from cournal.network import network class Page: @@ -49,26 +50,26 @@ def __init__(self, document, pdf, number, layers=None): self.widget = None self.width, self.height = pdf.get_size() - def new_stroke(self, stroke, send_to_network=False): + def new_obj(self, obj, send_to_network=False): """ - Add a new stroke to this page and possibly send it to the server, if + Add a new object to this page and possibly send it to the server, if connected. Positional arguments: - stroke -- The Stroke object, that will be added to this page + obj -- The object, that will be added to this page Keyword arguments: - send_to_network -- Set True, to send the stroke to the server + send_to_network -- Set True, to send the object to the server (defaults to False) """ - self.layers[0].strokes.append(stroke) - stroke.calculate_bounding_box() - stroke.layer = self.layers[0] + self.layers[0].obj.append(obj) + obj.calculate_bounding_box() + obj.layer = self.layers[0] if self.widget: - self.widget.draw_remote_stroke(stroke) + self.widget.draw_remote_obj(obj) if send_to_network: - network.new_stroke(self.number, stroke) - + network.new_obj(self.number, obj) + def new_unfinished_stroke(self, color, linewidth): """ Add a new empty stroke, which is not sent to the server, till @@ -90,53 +91,56 @@ def finish_stroke(self, stroke): """ #TODO: rerender that part of the screen. stroke.calculate_bounding_box() - network.new_stroke(self.number, stroke) + network.new_obj(self.number, stroke) - def delete_stroke_with_coords(self, coords): + def delete_objects_with_coords(self, coords): """ - Delete all strokes, which have exactly the same coordinates as given. + Delete all objects, which have exactly the same coordinates as given. Positional arguments coords -- The list of coordinates """ - for stroke in self.layers[0].strokes[:]: - if stroke.coords == coords: - self.delete_stroke(stroke, send_to_network=False) + for obj in self.layers[0].obj[:]: + if obj.coords == coords: + self.delete_obj(obj, send_to_network=False) - def delete_stroke(self, stroke, send_to_network=False): + def delete_obj(self, obj, send_to_network=False): """ - Delete a stroke on this page and possibly send this request to the server, + Delete a object on this page and possibly send this request to the server, if connected. Positional arguments: - stroke -- The Stroke object, that will be deleted. + obj -- The object, that will be deleted. Keyword arguments: send_to_network -- Set to True, to send the request for deletion the server (defaults to False) """ - self.layers[0].strokes.remove(stroke) + self.layers[0].obj.remove(obj) if self.widget: - self.widget.delete_remote_stroke(stroke) + self.widget.delete_remote_obj(obj) if send_to_network: - network.delete_stroke_with_coords(self.number, stroke.coords) + network.delete_objects_with_coords(self.number, obj.coords) - def get_strokes_near(self, x, y, radius): + def get_objects_near(self, x, y, radius): """ - Finds strokes near a given point + Finds objects near a given point Positional arguments: x -- x coordinate of the given point y -- y coordinate of the given point radius -- Radius in pt, which influences the decision of what is considered "near" - Return value: Generator for a list of all strokes, which are near that point + Return value: Generator for a list of all objects, which are near that point """ - for stroke in self.layers[0].strokes[:]: - if stroke.in_bounds(x, y): - for coord in stroke.coords: - s_x = coord[0] - s_y = coord[1] - if ((s_x-x)**2 + (s_y-y)**2) < radius**2: - yield stroke - break + for obj in self.layers[0].obj[:]: + if obj.in_bounds(x, y): + if isinstance(obj, Stroke): + for coord in obj.coords: + s_x = coord[0] + s_y = coord[1] + if ((s_x-x)**2 + (s_y-y)**2) < radius**2: + yield obj + break + elif isinstance(obj, Rect): + yield obj diff --git a/cournal/document/xojparser.py b/cournal/document/xojparser.py index 956a578..260c48c 100644 --- a/cournal/document/xojparser.py +++ b/cournal/document/xojparser.py @@ -73,7 +73,7 @@ def import_into_document(document, filename, window): strokes = pages[p].findall("layer/stroke") for s in range(len(strokes)): stroke = _parse_stroke(strokes[s], document.pages[p].layers[0]) - document.pages[p].new_stroke(stroke, send_to_network=True) + document.pages[p].new_obj(stroke, send_to_network=True) return document def _parse_stroke(stroke, layer): diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index cc3a38f..90a46c3 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -283,6 +283,80 @@ True + + + False + True + 1 + + + + + True + False + icons + + + False + True + False + False + Pen + True + True + + + False + True + + + + + False + True + False + False + Rect + True + tool_pen + + + False + True + + + + + False + True + False + False + Line + True + True + tool_pen + + + False + True + + + + + False + True + False + False + Circle + True + True + tool_pen + + + False + True + + False @@ -319,6 +393,45 @@ False + + + False + True + False + False + + + False + True + True + True + False + none + True + Fill Color + rgb(245,121,0) + + + + + False + + + + + False + True + False + False + Fill + True + True + + + False + True + + False @@ -365,7 +478,7 @@ False True - 1 + 2 @@ -386,7 +499,7 @@ True True - 2 + 3 @@ -505,7 +618,7 @@ False False end - 3 + 4 diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index 6fde5e2..31bbb26 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -24,7 +24,7 @@ import cournal from cournal.viewer.layout import Layout -from cournal.viewer.tools import pen +from cournal.viewer.tools import pen, rect, primary from cournal.document.document import Document from cournal.document import xojparser from cournal.network import network @@ -91,9 +91,15 @@ def __init__(self, **args): self.tool_zoom_out = builder.get_object("tool_zoom_out") self.tool_zoom_100 = builder.get_object("tool_zoom_100") self.tool_pen_color = builder.get_object("tool_pen_color") + self.tool_pen_bg_color = builder.get_object("tool_pen_bg_color") self.tool_pensize_small = builder.get_object("tool_pensize_small") self.tool_pensize_normal = builder.get_object("tool_pensize_normal") self.tool_pensize_big = builder.get_object("tool_pensize_big") + self.tool_pen = builder.get_object("tool_pen") + self.tool_rect = builder.get_object("tool_rect") + self.tool_line = builder.get_object("tool_line") + self.tool_circle = builder.get_object("tool_circle") + self.tool_fill = builder.get_object("tool_fill") self.menu_connect.set_sensitive(False) self.menu_save.set_sensitive(False) @@ -106,9 +112,15 @@ def __init__(self, **args): self.tool_zoom_out.set_sensitive(False) self.tool_zoom_100.set_sensitive(False) self.tool_pen_color.set_sensitive(False) + self.tool_pen_bg_color.set_sensitive(False) self.tool_pensize_small.set_sensitive(False) self.tool_pensize_normal.set_sensitive(False) self.tool_pensize_big.set_sensitive(False) + self.tool_pen.set_sensitive(False) + self.tool_rect.set_sensitive(False) + self.tool_line.set_sensitive(False) + self.tool_circle.set_sensitive(False) + self.tool_fill.set_sensitive(False) self.menu_open_xoj.connect("activate", self.run_open_xoj_dialog) self.menu_open_pdf.connect("activate", self.run_open_pdf_dialog) @@ -125,10 +137,13 @@ def __init__(self, **args): self.tool_zoom_in.connect("clicked", self.zoom_in) self.tool_zoom_out.connect("clicked", self.zoom_out) self.tool_zoom_100.connect("clicked", self.zoom_100) - self.tool_pen_color.connect("color-set", self.change_pen_color) - self.tool_pensize_small.connect("clicked", self.change_pen_size, LINEWIDTH_SMALL) - self.tool_pensize_normal.connect("clicked", self.change_pen_size, LINEWIDTH_NORMAL) - self.tool_pensize_big.connect("clicked", self.change_pen_size, LINEWIDTH_BIG) + self.tool_pen_color.connect("color-set", self.change_primary_color) + self.tool_pen_bg_color.connect("color-set", self.change_primary_bg_color) + self.tool_pensize_small.connect("clicked", self.change_primary_size, LINEWIDTH_SMALL) + self.tool_pensize_normal.connect("clicked", self.change_primary_size, LINEWIDTH_NORMAL) + self.tool_pensize_big.connect("clicked", self.change_primary_size, LINEWIDTH_BIG) + self.tool_pen.connect("clicked", self.set_tool) + self.tool_rect.connect("clicked", self.set_tool) # Statusbar: self.statusbar_icon = builder.get_object("image_statusbar") @@ -159,6 +174,15 @@ def __init__(self, **args): Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) self.add_accel_group(self.accelgroup) + self.set_tool(pen) + + def set_tool(self, tool): + if tool == self.tool_pen: + tool = pen + if tool == self.tool_rect: + tool = rect + primary.current_tool = tool + def connect_event(self): """ Called by the networking layer when a connection is established. @@ -223,11 +247,14 @@ def _set_document(self, document): self.tool_zoom_out.set_sensitive(True) self.tool_zoom_100.set_sensitive(True) self.tool_pen_color.set_sensitive(True) + self.tool_pen_bg_color.set_sensitive(True) self.tool_pensize_small.set_sensitive(True) self.tool_pensize_normal.set_sensitive(True) self.tool_pensize_big.set_sensitive(True) self.statusbar_pagenum.set_sensitive(True) self.statusbar_pagenum_entry.set_sensitive(True) + self.tool_pen.set_sensitive(True) + self.tool_rect.set_sensitive(True) if self.document.num_of_pages > 1: self.button_next_page.set_sensitive(True) @@ -475,9 +502,24 @@ def run_error_dialog(self, first, second): message.connect("response", lambda _,x: message.destroy()) message.show() - def change_pen_color(self, colorbutton): + def change_primary_color(self, colorbutton): + """ + Change the primary tool to a user defined color. + + Positional arguments: + colorbutton -- The Gtk.ColorButton, that triggered this function + """ + color = colorbutton.get_rgba() + red = int(color.red*255) + green = int(color.green*255) + blue = int(color.blue*255) + opacity = int(color.alpha*255) + + primary.color = red, green, blue, opacity + + def change_primary_bg_color(self, colorbutton): """ - Change the pen to a user defined color. + Change the primary tool to a user defined color. Positional arguments: colorbutton -- The Gtk.ColorButton, that triggered this function @@ -488,17 +530,17 @@ def change_pen_color(self, colorbutton): blue = int(color.blue*255) opacity = int(color.alpha*255) - pen.color = red, green, blue, opacity + primary.fillcolor = red, green, blue, opacity - def change_pen_size(self, menuitem, linewidth): + def change_primary_size(self, menuitem, linewidth): """ - Change the pen to a user defined line width. + Change the primary tool to a user defined line width. Positional arguments: menuitem -- The menu item, that triggered this function linewidth -- New line width of the pen """ - pen.linewidth = linewidth + primary.linewidth = linewidth def zoom_in(self, menuitem): """Magnify document""" diff --git a/cournal/network.py b/cournal/network.py index fc30a0c..af618d5 100644 --- a/cournal/network.py +++ b/cournal/network.py @@ -182,19 +182,19 @@ def got_server_document(self, server_document, name): debug(2, _("Started editing {}").format(name)) self.server_document = server_document - def remote_new_stroke(self, pagenum, stroke): + def remote_new_obj(self, pagenum, obj): """ - Called by the server, to inform us about a new stroke + Called by the server, to inform us about a new object Positional arguments: - pagenum -- On which page shall we add the stroke - stroke -- The received Stroke object + pagenum -- On which page shall we add the object + obj -- The received object """ self.data_received() if self.document and pagenum < len(self.document.pages): - self.document.pages[pagenum].new_stroke(stroke) + self.document.pages[pagenum].new_obj(obj) - def new_stroke(self, pagenum, stroke): + def new_obj(self, pagenum, obj): """ Called by local code to send a new stroke to the server @@ -203,31 +203,31 @@ def new_stroke(self, pagenum, stroke): stroke -- The Stroke object to send """ if self.is_connected: - d = self.server_document.callRemote("new_stroke", pagenum, stroke) + d = self.server_document.callRemote("new_obj", pagenum, obj) d.addCallbacks(lambda x: self.data_received(), self.disconnect) - def remote_delete_stroke_with_coords(self, pagenum, coords): + def remote_delete_objects_with_coords(self, pagenum, coords): """ - Called by the server, when a remote user deleted a stroke + Called by the server, when a remote user deleted a object Positional arguments: - pagenum -- On which page the stroke was deleted - coords -- The list of coordinates identifying a stroke + pagenum -- On which page the object was deleted + coords -- The list of coordinates identifying a object """ self.data_received() if self.document and pagenum < len(self.document.pages): - self.document.pages[pagenum].delete_stroke_with_coords(coords) + self.document.pages[pagenum].delete_objects_with_coords(coords) - def delete_stroke_with_coords(self, pagenum, coords): + def delete_objects_with_coords(self, pagenum, coords): """ Called by local code to send a delete command to the server Positional arguments: - pagenum -- On which page the stroke was deleted - coords -- The list of coordinates identifying the stroke + pagenum -- On which page the object was deleted + coords -- The list of coordinates identifying the object """ if self.is_connected: - d = self.server_document.callRemote("delete_stroke_with_coords", pagenum, coords) + d = self.server_document.callRemote("delete_objects_with_coords", pagenum, coords) d.addCallback(lambda x,y: self.data_received(), self.disconnect) def ping(self): diff --git a/cournal/server/server.py b/cournal/server/server.py index 54984b3..4c47eac 100755 --- a/cournal/server/server.py +++ b/cournal/server/server.py @@ -36,6 +36,7 @@ from cournal import __versionstring__ as cournal_version from cournal.document.stroke import Stroke +from cournal.document.rect import Rect # 0 - none @@ -55,10 +56,10 @@ class Page: """ - A page in a document, having multiple strokes. + A page in a document, having multiple objects. """ def __init__(self): - self.strokes = [] + self.obj = [] class CournalServer: """ @@ -333,7 +334,7 @@ def __init__(self, documentname): def add_user(self, user): """ - Called, when a user starts editing this document. Send him all strokes + Called, when a user starts editing this document. Send him all objects that are currently in the document. Positional arguments: @@ -341,8 +342,8 @@ def add_user(self, user): """ self.users.append(user) for pagenum in range(len(self.pages)): - for stroke in self.pages[pagenum].strokes: - user.call_remote("new_stroke", pagenum, stroke) + for obj in self.pages[pagenum].obj: + user.call_remote("new_obj", pagenum, obj) def remove_user(self, user): """ @@ -368,43 +369,43 @@ def broadcast(self, method, *args, except_user=None): if user != except_user: user.call_remote(method, *args) - def view_new_stroke(self, from_user, pagenum, stroke): + def view_new_obj(self, from_user, pagenum, obj): """ - Broadcast the stroke received from one to all other clients. - Called by clients to add a new stroke. + Broadcast the object received from one to all other clients. + Called by clients to add a new obj. Positional arguments: from_user -- The User object of the initiiating user. - pagenum -- Page number the new stroke. - stroke -- The new stroke + pagenum -- Page number the new object. + obj -- The new object """ self.has_unsaved_changes = True while len(self.pages) <= pagenum: self.pages.append(Page()) - self.pages[pagenum].strokes.append(stroke) + self.pages[pagenum].obj.append(obj) - debug(3, _("New stroke on page {}").format(pagenum + 1)) - self.broadcast("new_stroke", pagenum, stroke, except_user=from_user) + debug(3, _("New object on page {}").format(pagenum + 1)) + self.broadcast("new_obj", pagenum, obj, except_user=from_user) - def view_delete_stroke_with_coords(self, from_user, pagenum, coords): + def view_delete_objects_with_coords(self, from_user, pagenum, coords): """ - Broadcast the delete stroke command from one to all other clients. - Called by Clients to delete a stroke. + Broadcast the delete objects command from one to all other clients. + Called by Clients to delete a object. Positional arguments: from_user -- The User object of the initiiating user. - pagenum -- Page number the deleted stroke - coords -- The list coordinates of the deleted stroke + pagenum -- Page number the deleted object + coords -- The list coordinates of the deleted object """ self.has_unsaved_changes = True - for stroke in self.pages[pagenum].strokes: - if stroke.coords == coords: - self.pages[pagenum].strokes.remove(stroke) + for obj in self.pages[pagenum].obj: + if obj.coords == coords: + self.pages[pagenum].obj.remove(obj) - debug(3, _("Deleted stroke on page {}").format(pagenum + 1)) - self.broadcast("delete_stroke_with_coords", pagenum, coords, except_user=from_user) + debug(3, _("Deleted object on page {}").format(pagenum + 1)) + self.broadcast("delete_objects_with_coords", pagenum, coords, except_user=from_user) class CmdlineParser(): """ diff --git a/cournal/viewer/pagewidget.py b/cournal/viewer/pagewidget.py index 0400173..1b55255 100644 --- a/cournal/viewer/pagewidget.py +++ b/cournal/viewer/pagewidget.py @@ -20,7 +20,7 @@ from gi.repository import Gtk, Gdk import cairo -from cournal.viewer.tools import pen, eraser, navigation +from cournal.viewer.tools import pen, eraser, navigation, rect, primary class PageWidget(Gtk.DrawingArea): """ @@ -108,7 +108,7 @@ def on_size_allocate(self, widget, alloc): def draw(self, widget, context): """ - Draw the widget (the PDF, all strokes and the background). Called by Gtk. + Draw the widget (the PDF, all objects and the background). Called by Gtk. Positional arguments: widget -- The widget to redraw @@ -130,8 +130,8 @@ def draw(self, widget, context): self.page.pdf.render(bb_ctx) bb_ctx.restore() - for stroke in self.page.layers[0].strokes: - stroke.draw(bb_ctx, scaling) + for obj in self.page.layers[0].obj: + obj.draw(bb_ctx, scaling) # Then the image is painted on top of a white "page". Instead of # creating a second image, painting it white, then painting the @@ -153,7 +153,7 @@ def press(self, widget, event): event -- The Gdk.Event, which stores the location of the pointer """ if event.button == 1: - self.active_tool = pen + self.active_tool = primary.current_tool elif event.button == 2: self.active_tool = navigation elif event.button == 3: @@ -181,20 +181,20 @@ def release(self, widget, event): self.active_tool.release(self, event) self.active_tool = None - def draw_remote_stroke(self, stroke): + def draw_remote_obj(self, obj): """ - Draw a single stroke on the widget. - Meant to be called by networking code, when a remote user drew a stroke. + Draw a single object on the widget. + Meant to be called by networking code, when a remote user drew a object. Positional arguments: - stroke -- The Stroke object, which is to be drawn. + obj -- The object, which is to be drawn. """ if self.backbuffer: scaling = self.widget_width / self.page.width context = cairo.Context(self.backbuffer) context.scale(scaling, scaling) - x, y, x2, y2 = stroke.draw(context, scaling) + x, y, x2, y2 = obj.draw(context, scaling) update_rect = Gdk.Rectangle() update_rect.x = x-2 @@ -204,13 +204,13 @@ def draw_remote_stroke(self, stroke): if self.get_window(): self.get_window().invalidate_rect(update_rect, False) - def delete_remote_stroke(self, stroke): + def delete_remote_obj(self, obj): """ - Rerender the part of the widget, where a stroke was deleted - Meant do be called by networking code, when a remote user deleted a stroke. + Rerender the part of the widget, where a object was deleted + Meant do be called by networking code, when a remote user deleted a object. Positional arguments: - stroke -- The Stroke object, which was deleted. + obj -- The object, which was deleted. """ if self.backbuffer: self.backbuffer_valid = False diff --git a/cournal/viewer/tools/eraser.py b/cournal/viewer/tools/eraser.py index 94c3186..3d99f62 100644 --- a/cournal/viewer/tools/eraser.py +++ b/cournal/viewer/tools/eraser.py @@ -18,28 +18,28 @@ # along with Cournal. If not, see . """ -An eraser tool, that this deletes the complete stroke. +An eraser tool, that this deletes the complete object. """ THICKNESS = 6 # pt def press(widget, event): """ - Mouse down event. Delete all strokes near the pointer location. + Mouse down event. Delete all objects near the pointer location. Positional arguments: widget -- The PageWidget, which triggered the event event -- The Gdk.Event, which stores the location of the pointer """ - _delete_strokes_near(widget, event.x, event.y) + _delete_objects_near(widget, event.x, event.y) def motion(widget, event): """ - Mouse motion event. Delete all strokes near the pointer location. + Mouse motion event. Delete all objects near the pointer location. Positional arguments: see press() """ - _delete_strokes_near(widget, event.x, event.y) + _delete_objects_near(widget, event.x, event.y) def release(widget, event): """ @@ -50,18 +50,18 @@ def release(widget, event): """ pass -def _delete_strokes_near(widget, x, y): +def _delete_objects_near(widget, x, y): """ - Delete all strokes near a given point. + Delete all objects near a given point. Positional arguments: - widget -- The PageWidget to delete strokes on. - x -- Delete stroke near this point (x coordinate on the widget (!)) - y -- Delete stroke near this point (y coordinate on the widget (!)) + widget -- The PageWidget to delete objects on. + x -- Delete object near this point (x coordinate on the widget (!)) + y -- Delete object near this point (y coordinate on the widget (!)) """ scaling = widget.page.width / widget.get_allocation().width x *= scaling y *= scaling - for stroke in widget.page.get_strokes_near(x, y, THICKNESS): - widget.page.delete_stroke(stroke, send_to_network=True) + for obj in widget.page.get_objects_near(x, y, THICKNESS): + widget.page.delete_obj(obj, send_to_network=True) diff --git a/cournal/viewer/tools/pen.py b/cournal/viewer/tools/pen.py index 1c87bdb..e7157ce 100644 --- a/cournal/viewer/tools/pen.py +++ b/cournal/viewer/tools/pen.py @@ -19,6 +19,7 @@ import cairo from gi.repository import Gdk +from cournal.viewer.tools import primary """ A pen tool. Draws a stroke with a certain color and size. @@ -27,8 +28,6 @@ _last_point = None _current_coords = None _current_stroke = None -linewidth = 1.5 -color = (0,0,128,255) def press(widget, event): """ @@ -38,16 +37,16 @@ def press(widget, event): widget -- The PageWidget, which triggered the event event -- The Gdk.Event, which stores the location of the pointer """ - global _last_point, _current_coords, _current_stroke, linewidth, color + global _last_point, _current_coords, _current_stroke actualWidth = widget.get_allocation().width - _current_stroke = widget.page.new_unfinished_stroke(color=color, linewidth=linewidth) + _current_stroke = widget.page.new_unfinished_stroke(color=primary.color, linewidth=primary.linewidth) _current_coords = _current_stroke.coords _last_point = [event.x, event.y] motion(widget, event) - widget.page.layers[0].strokes.append(_current_stroke) + widget.page.layers[0].obj.append(_current_stroke) def motion(widget, event): """ @@ -57,14 +56,14 @@ def motion(widget, event): """ global _last_point, _current_coords, _current_stroke - r, g, b, opacity = color + r, g, b, opacity = primary.color actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) context.set_source_rgba(r/255, g/255, b/255, opacity/255) context.set_antialias(cairo.ANTIALIAS_GRAY) context.set_line_cap(cairo.LINE_CAP_ROUND) - context.set_line_width(linewidth*actualWidth/widget.page.width) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) context.move_to(_last_point[0], _last_point[1]) context.line_to(event.x, event.y) From e91b674d466ebf93f7838c1a8e2f773a6bbfd3ae Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Tue, 11 Sep 2012 23:19:49 +0200 Subject: [PATCH 02/19] added missing files --- cournal/document/rect.py | 131 ++++++++++++++++++++++++++++++++ cournal/viewer/tools/primary.py | 30 ++++++++ cournal/viewer/tools/rect.py | 111 +++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 cournal/document/rect.py create mode 100644 cournal/viewer/tools/primary.py create mode 100644 cournal/viewer/tools/rect.py diff --git a/cournal/document/rect.py b/cournal/document/rect.py new file mode 100644 index 0000000..7743b91 --- /dev/null +++ b/cournal/document/rect.py @@ -0,0 +1,131 @@ +#!/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 . + +import cairo + +from twisted.spread import pb + +class Rect(pb.Copyable, pb.RemoteCopy): + def __init__(self, layer, color, fill_color, linewidth, coords=None): + """ + 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.linewidth = linewidth + self.coords = coords + if self.coords is None: + self.coords = [] + + def in_bounds(self, x, y): + """ + Test if point is in bounding box of the stroke. + + Positional arguments: + x, y -- point + + Returns: + true, if point is in bounding box + """ + if self.coords[0] < self.coords[2]: + s_x = self.coords[0] + e_x = self.coords[2] + else: + s_x = self.coords[2] + e_x = self.coords[0] + if self.coords[1] < self.coords[3]: + s_y = self.coords[1] + e_y = self.coords[3] + else: + s_y = self.coords[3] + e_y = self.coords[1] + + if x > s_x and x < e_x and y > s_y and y < e_y: + 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["coords"] = self.coords + 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) + """ + 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) + + # Fill rect + context.move_to(self.coords[0], self.coords[1]) + context.line_to(self.coords[0], self.coords[3]) + context.line_to(self.coords[2], self.coords[3]) + context.line_to(self.coords[2], self.coords[1]) + context.close_path() + context.fill() + + # Draw border + context.move_to(self.coords[0], self.coords[1]) + context.line_to(self.coords[0], self.coords[3]) + context.line_to(self.coords[2], self.coords[3]) + context.line_to(self.coords[2], self.coords[1]) + context.close_path() + + 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(Rect, Rect) diff --git a/cournal/viewer/tools/primary.py b/cournal/viewer/tools/primary.py new file mode 100644 index 0000000..ba0a980 --- /dev/null +++ b/cournal/viewer/tools/primary.py @@ -0,0 +1,30 @@ +#!/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 . + +import cairo +from gi.repository import Gdk + +""" +The primary tool. Defines stroke color, fill color, width and the current tool +""" + +linewidth = 1.5 +color = (0,0,128,255) +fillcolor = (255,128,0,255) +current_tool = None \ No newline at end of file diff --git a/cournal/viewer/tools/rect.py b/cournal/viewer/tools/rect.py new file mode 100644 index 0000000..a7cd328 --- /dev/null +++ b/cournal/viewer/tools/rect.py @@ -0,0 +1,111 @@ +#!/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 . + +import cairo +from gi.repository import Gdk +from cournal.viewer.tools import primary +from cournal.document.rect import Rect + +_current_object = None +_current_coords = None +_start_point = None + +def press(widget, event): + """ + Mouse down event. Draw a point on the pointer location. + + Positional arguments: + widget -- The PageWidget, which triggered the event + event -- The Gdk.Event, which stores the location of the pointer + """ + global _start_point, _current_coords, _current_object + + actualWidth = widget.get_allocation().width + + _current_object = Rect(widget.page.layers[0], primary.color, primary.fillcolor, primary.linewidth, []) + #_current_stroke = widget.page.new_unfinished_stroke(color=primary.color, linewidth=primary.linewidth) + _current_coords = _current_object.coords + + _start_point = [event.x, event.y] + _current_coords.append(event.x*widget.page.width/actualWidth) + _current_coords.append(event.y*widget.page.width/actualWidth) + + #widget.page.layers[0].obj.append(_current_stroke) + +def motion(widget, event): + pass + +def release(widget, event): + """ + Mouse release event. Inform the corresponding Page instance, that the stroke + is finished. + + This will cause the stroke to be sent to the server, if it is connected. + + Positional arguments: see press() + """ + global _start_point, _current_coords, _current_object + r, g, b, opacity = primary.fillcolor + actualWidth = widget.get_allocation().width + + context = cairo.Context(widget.backbuffer) + + # Fill rect + context.move_to(_start_point[0], _start_point[1]) + context.line_to(_start_point[0], event.y) + context.line_to(event.x, event.y) + context.line_to(event.x, _start_point[1]) + context.close_path() + + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + context.set_antialias(cairo.ANTIALIAS_GRAY) + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + x, y, x2, y2 = context.stroke_extents() + context.fill() + + # Draw border + context.move_to(_start_point[0], _start_point[1]) + context.line_to(_start_point[0], event.y) + context.line_to(event.x, event.y) + context.line_to(event.x, _start_point[1]) + context.close_path() + + r, g, b, opacity = primary.color + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + context.stroke() + + update_rect = Gdk.Rectangle() + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 + widget.get_window().invalidate_rect(update_rect, False) + + #_current_coords.append([_last_point[0]*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) + _current_coords.append(event.x*widget.page.width/actualWidth) + _current_coords.append(event.y*widget.page.width/actualWidth) + #_current_coords.append([event.x*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) + #_current_coords.append([_last_point[0]*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) + #widget.page.finish_stroke(_current_stroke) + widget.page.new_obj(_current_object, send_to_network=True) + + _start_point = None + _current_coords = None + _current_object = None From 890d5a160d0c6be1a511994b5ce142ee7d914201 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Wed, 12 Sep 2012 00:58:40 +0200 Subject: [PATCH 03/19] Everything works with new items --- cournal/document/document.py | 35 +++++++++--------- cournal/document/history.py | 44 +++++++++++------------ cournal/document/layer.py | 12 +++---- cournal/document/page.py | 65 +++++++++++++++++----------------- cournal/document/xojparser.py | 2 +- cournal/network.py | 38 ++++++++++---------- cournal/server/server.py | 48 ++++++++++++------------- cournal/viewer/pagewidget.py | 22 ++++++------ cournal/viewer/tools/eraser.py | 24 ++++++------- cournal/viewer/tools/pen.py | 2 +- cournal/viewer/tools/rect.py | 21 +++++------ 11 files changed, 157 insertions(+), 156 deletions(-) diff --git a/cournal/document/document.py b/cournal/document/document.py index 9aa7136..51c3684 100644 --- a/cournal/document/document.py +++ b/cournal/document/document.py @@ -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: """ @@ -60,19 +61,19 @@ def __init__(self, pdfname): def is_empty(self): """ - Returns True, if no page of this document has a object 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].obj) != 0: + if len(page.layers[0].items) != 0: return False return True def clear_pages(self): - """Deletes all objects on all pages of this document""" + """Deletes all item on all pages of this document""" for page in self.pages: - for obj in page.layers[0].obj[:]: - page.delete_obj(obj, send_to_network=False) + for item in page.layers[0].items[:]: + page.delete_item(item, send_to_network=False) def export_pdf(self, filename): """ @@ -94,8 +95,8 @@ def export_pdf(self, filename): page.pdf.render_for_printing(context) - for obj in page.layers[0].obj: - obj.draw(context) + for item in page.layers[0].items: + item.draw(context) surface.show_page() # aka "next page" @@ -131,15 +132,17 @@ def save_xoj_file(self, filename): for layer in page.layers: r += "\n" - for obj in layer.obj: - red, g, b, opacity = obj.color - r += "\n".format(red, g, b, opacity, obj.linewidth) - first = obj.coords[0] - for coord in obj.coords: - r += " {} {}".format(coord[0], coord[1]) - if len(obj.coords) < 2: - r += " {} {}".format(first[0], first[1]) - r += "\n\n" + for item in layer.items: + #TODO: Parse other Items + if isinstance(item, Stroke): + red, g, b, opacity = item.color + r += "\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\n" r += "\n" r += "\n" r += "" diff --git a/cournal/document/history.py b/cournal/document/history.py index a616ad5..4603cc0 100644 --- a/cournal/document/history.py +++ b/cournal/document/history.py @@ -62,25 +62,25 @@ def redo(menuitem): if len(_redo_list) == 0: deactivate_redo() -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): """ @@ -133,26 +133,26 @@ def activate_redo(): _menu_redo.set_sensitive(True) _tool_redo.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_obj(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_obj(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_obj(self.stroke, send_to_network=True) + self.page.new_item(self.item, send_to_network=True) def redo(self): - self.page.delete_obj(self.stroke, send_to_network=True, register_in_history=False) + self.page.delete_item(self.item, send_to_network=True, register_in_history=False) diff --git a/cournal/document/layer.py b/cournal/document/layer.py index 4008def..8f4d89f 100644 --- a/cournal/document/layer.py +++ b/cournal/document/layer.py @@ -19,9 +19,9 @@ class Layer: """ - A layer on a page, having a number and multiple objects. + A layer on a page, having a number and multiple item. """ - def __init__(self, page, number, obj=None): + def __init__(self, page, number, items=None): """ Constructor @@ -30,11 +30,11 @@ def __init__(self, page, number, obj=None): number -- Layer number Keyword arguments: - obj -- List of objects (defaults to []) + item -- List of items (defaults to []) """ self.number = number self.page = page - self.obj = obj + self.items = items - if self.obj is None: - self.obj = [] + if self.items is None: + self.items = [] diff --git a/cournal/document/page.py b/cournal/document/page.py index 9ae8a55..13b1dd9 100644 --- a/cournal/document/page.py +++ b/cournal/document/page.py @@ -51,25 +51,25 @@ def __init__(self, document, pdf, number, layers=None): self.width, self.height = pdf.get_size() self.search_marker = None - def new_obj(self, obj, send_to_network=False): + def new_item(self, item, send_to_network=False): """ - Add a new object to this page and possibly send it to the server, if + Add a new item to this page and possibly send it to the server, if connected. Positional arguments: - obj -- The object, that will be added to this page + item -- The item, that will be added to this page Keyword arguments: - send_to_network -- Set True, to send the object to the server + send_to_network -- Set True, to send the item to the server (defaults to False) """ - self.layers[0].obj.append(obj) - obj.calculate_bounding_box() - obj.layer = self.layers[0] + self.layers[0].items.append(item) + item.calculate_bounding_box() + item.layer = self.layers[0] if self.widget: - self.widget.draw_remote_obj(obj) + self.widget.draw_remote_item(item) if send_to_network: - network.new_obj(self.number, obj) + network.new_item(self.number, item) def new_unfinished_stroke(self, color, linewidth): """ @@ -91,45 +91,45 @@ def finish_stroke(self, stroke): stroke -- The Stroke object, that was finished """ #TODO: rerender that part of the screen. - history.register_draw_stroke(stroke, self) + history.register_draw_item(stroke, self) stroke.calculate_bounding_box() - network.new_obj(self.number, stroke) + network.new_item(self.number, stroke) - def delete_objects_with_coords(self, coords): + def delete_item_with_coords(self, coords): """ - Delete all objects, which have exactly the same coordinates as given. + Delete all items, which have exactly the same coordinates as given. Positional arguments coords -- The list of coordinates """ - for obj in self.layers[0].obj[:]: - if obj.coords == coords: - self.delete_obj(obj, send_to_network=False) + for item in self.layers[0].items[:]: + if item.coords == coords: + self.delete_item(item, send_to_network=False) - def delete_obj(self, obj, send_to_network=False, register_in_history=True): + def delete_item(self, item, send_to_network=False, register_in_history=True): """ - Delete a object on this page and possibly send this request to the server, + Delete a item on this page and possibly send this request to the server, if connected. Positional arguments: - obj -- The object, that will be deleted. + item -- The item, that will be deleted. Keyword arguments: send_to_network -- Set to True, to send the request for deletion the server (defaults to False) register_in_history -- Make this command undoable """ - self.layers[0].obj.remove(obj) + self.layers[0].items.remove(item) if self.widget: - self.widget.delete_remote_obj(obj) + self.widget.delete_remote_item(item) if send_to_network: - network.delete_objects_with_coords(self.number, obj.coords) + network.delete_item_with_coords(self.number, item.coords) if register_in_history: - history.register_delete_stroke(obj, self) + history.register_delete_item(item, self) - def get_objects_near(self, x, y, radius): + def get_items_near(self, x, y, radius): """ - Finds objects near a given point + Finds items near a given point Positional arguments: x -- x coordinate of the given point @@ -138,14 +138,15 @@ def get_objects_near(self, x, y, radius): Return value: Generator for a list of all objects, which are near that point """ - for obj in self.layers[0].obj[:]: - if obj.in_bounds(x, y): - if isinstance(obj, Stroke): - for coord in obj.coords: + #TODO: Create tool dependent delete if coord match function + for item in self.layers[0].items[:]: + if item.in_bounds(x, y): + if isinstance(item, Stroke): + for coord in item.coords: s_x = coord[0] s_y = coord[1] if ((s_x-x)**2 + (s_y-y)**2) < radius**2: - yield obj + yield item break - elif isinstance(obj, Rect): - yield obj + elif isinstance(item, Rect): + yield item diff --git a/cournal/document/xojparser.py b/cournal/document/xojparser.py index 260c48c..a6de72a 100644 --- a/cournal/document/xojparser.py +++ b/cournal/document/xojparser.py @@ -73,7 +73,7 @@ def import_into_document(document, filename, window): strokes = pages[p].findall("layer/stroke") for s in range(len(strokes)): stroke = _parse_stroke(strokes[s], document.pages[p].layers[0]) - document.pages[p].new_obj(stroke, send_to_network=True) + document.pages[p].new_item(stroke, send_to_network=True) return document def _parse_stroke(stroke, layer): diff --git a/cournal/network.py b/cournal/network.py index af618d5..9245a6e 100644 --- a/cournal/network.py +++ b/cournal/network.py @@ -182,52 +182,52 @@ def got_server_document(self, server_document, name): debug(2, _("Started editing {}").format(name)) self.server_document = server_document - def remote_new_obj(self, pagenum, obj): + def remote_new_item(self, pagenum, item): """ - Called by the server, to inform us about a new object + Called by the server, to inform us about a new item Positional arguments: - pagenum -- On which page shall we add the object - obj -- The received object + pagenum -- On which page shall we add the item + item -- The received item """ self.data_received() if self.document and pagenum < len(self.document.pages): - self.document.pages[pagenum].new_obj(obj) + self.document.pages[pagenum].new_item(item) - def new_obj(self, pagenum, obj): + def new_item(self, pagenum, item): """ - Called by local code to send a new stroke to the server + Called by local code to send a new item to the server Positional arguments: - pagenum -- On which page the stroke was added - stroke -- The Stroke object to send + pagenum -- On which page the item was added + item -- The Stroke item to send """ if self.is_connected: - d = self.server_document.callRemote("new_obj", pagenum, obj) + d = self.server_document.callRemote("new_item", pagenum, item) d.addCallbacks(lambda x: self.data_received(), self.disconnect) - def remote_delete_objects_with_coords(self, pagenum, coords): + def remote_delete_item_with_coords(self, pagenum, coords): """ - Called by the server, when a remote user deleted a object + Called by the server, when a remote user deleted a item Positional arguments: - pagenum -- On which page the object was deleted - coords -- The list of coordinates identifying a object + pagenum -- On which page the item was deleted + coords -- The list of coordinates identifying a item """ self.data_received() if self.document and pagenum < len(self.document.pages): - self.document.pages[pagenum].delete_objects_with_coords(coords) + self.document.pages[pagenum].delete_item_with_coords(coords) - def delete_objects_with_coords(self, pagenum, coords): + def delete_item_with_coords(self, pagenum, coords): """ Called by local code to send a delete command to the server Positional arguments: - pagenum -- On which page the object was deleted - coords -- The list of coordinates identifying the object + pagenum -- On which page the item was deleted + coords -- The list of coordinates identifying the item """ if self.is_connected: - d = self.server_document.callRemote("delete_objects_with_coords", pagenum, coords) + d = self.server_document.callRemote("delete_item_with_coords", pagenum, coords) d.addCallback(lambda x,y: self.data_received(), self.disconnect) def ping(self): diff --git a/cournal/server/server.py b/cournal/server/server.py index 4c47eac..02ed363 100755 --- a/cournal/server/server.py +++ b/cournal/server/server.py @@ -59,7 +59,7 @@ class Page: A page in a document, having multiple objects. """ def __init__(self): - self.obj = [] + self.items = [] class CournalServer: """ @@ -334,7 +334,7 @@ def __init__(self, documentname): def add_user(self, user): """ - Called, when a user starts editing this document. Send him all objects + Called, when a user starts editing this document. Send him all items that are currently in the document. Positional arguments: @@ -342,8 +342,8 @@ def add_user(self, user): """ self.users.append(user) for pagenum in range(len(self.pages)): - for obj in self.pages[pagenum].obj: - user.call_remote("new_obj", pagenum, obj) + for item in self.pages[pagenum].items: + user.call_remote("new_item", pagenum, item) def remove_user(self, user): """ @@ -369,43 +369,43 @@ def broadcast(self, method, *args, except_user=None): if user != except_user: user.call_remote(method, *args) - def view_new_obj(self, from_user, pagenum, obj): + def view_new_item(self, from_user, pagenum, item): """ - Broadcast the object received from one to all other clients. - Called by clients to add a new obj. + Broadcast the item received from one to all other clients. + Called by clients to add a new item. Positional arguments: - from_user -- The User object of the initiiating user. - pagenum -- Page number the new object. - obj -- The new object + from_user -- The User item of the initiiating user. + pagenum -- Page number the new item. + item -- The new item """ self.has_unsaved_changes = True while len(self.pages) <= pagenum: self.pages.append(Page()) - self.pages[pagenum].obj.append(obj) + self.pages[pagenum].items.append(item) - debug(3, _("New object on page {}").format(pagenum + 1)) - self.broadcast("new_obj", pagenum, obj, except_user=from_user) + debug(3, _("New item on page {}").format(pagenum + 1)) + self.broadcast("new_item", pagenum, item, except_user=from_user) - def view_delete_objects_with_coords(self, from_user, pagenum, coords): + def view_delete_item_with_coords(self, from_user, pagenum, coords): """ - Broadcast the delete objects command from one to all other clients. - Called by Clients to delete a object. + Broadcast the delete items command from one to all other clients. + Called by Clients to delete a item. Positional arguments: - from_user -- The User object of the initiiating user. - pagenum -- Page number the deleted object - coords -- The list coordinates of the deleted object + from_user -- The User item of the initiiating user. + pagenum -- Page number the deleted item + coords -- The list coordinates of the deleted item """ self.has_unsaved_changes = True - for obj in self.pages[pagenum].obj: - if obj.coords == coords: - self.pages[pagenum].obj.remove(obj) + for item in self.pages[pagenum].items: + if item.coords == coords: + self.pages[pagenum].items.remove(item) - debug(3, _("Deleted object on page {}").format(pagenum + 1)) - self.broadcast("delete_objects_with_coords", pagenum, coords, except_user=from_user) + debug(3, _("Deleted item on page {}").format(pagenum + 1)) + self.broadcast("delete_item_with_coords", pagenum, coords, except_user=from_user) class CmdlineParser(): """ diff --git a/cournal/viewer/pagewidget.py b/cournal/viewer/pagewidget.py index ba0395e..d910129 100644 --- a/cournal/viewer/pagewidget.py +++ b/cournal/viewer/pagewidget.py @@ -132,8 +132,8 @@ def draw(self, widget, context): self.page.pdf.render(bb_ctx) bb_ctx.restore() - for obj in self.page.layers[0].obj: - obj.draw(bb_ctx, scaling) + for item in self.page.layers[0].items: + item.draw(bb_ctx, scaling) # Highlight search result if self.page.search_marker: @@ -187,20 +187,20 @@ def release(self, widget, event): self.active_tool.release(self, event) self.active_tool = None - def draw_remote_obj(self, obj): + def draw_remote_item(self, item): """ - Draw a single object on the widget. - Meant to be called by networking code, when a remote user drew a object. + Draw a single item on the widget. + Meant to be called by networking code, when a remote user drew a item. Positional arguments: - obj -- The object, which is to be drawn. + item -- The item, which is to be drawn. """ if self.backbuffer: scaling = self.widget_width / self.page.width context = cairo.Context(self.backbuffer) context.scale(scaling, scaling) - x, y, x2, y2 = obj.draw(context, scaling) + x, y, x2, y2 = item.draw(context, scaling) update_rect = Gdk.Rectangle() update_rect.x = x-2 @@ -210,13 +210,13 @@ def draw_remote_obj(self, obj): if self.get_window(): self.get_window().invalidate_rect(update_rect, False) - def delete_remote_obj(self, obj): + def delete_remote_item(self, item): """ - Rerender the part of the widget, where a object was deleted - Meant do be called by networking code, when a remote user deleted a object. + Rerender the part of the widget, where a item was deleted + Meant do be called by networking code, when a remote user deleted a item. Positional arguments: - obj -- The object, which was deleted. + item -- The item, which was deleted. """ if self.backbuffer: self.backbuffer_valid = False diff --git a/cournal/viewer/tools/eraser.py b/cournal/viewer/tools/eraser.py index 3d99f62..93ca86e 100644 --- a/cournal/viewer/tools/eraser.py +++ b/cournal/viewer/tools/eraser.py @@ -18,28 +18,28 @@ # along with Cournal. If not, see . """ -An eraser tool, that this deletes the complete object. +An eraser tool, that this deletes the complete item. """ THICKNESS = 6 # pt def press(widget, event): """ - Mouse down event. Delete all objects near the pointer location. + Mouse down event. Delete all item near the pointer location. Positional arguments: widget -- The PageWidget, which triggered the event event -- The Gdk.Event, which stores the location of the pointer """ - _delete_objects_near(widget, event.x, event.y) + _delete_items_near(widget, event.x, event.y) def motion(widget, event): """ - Mouse motion event. Delete all objects near the pointer location. + Mouse motion event. Delete all items near the pointer location. Positional arguments: see press() """ - _delete_objects_near(widget, event.x, event.y) + _delete_items_near(widget, event.x, event.y) def release(widget, event): """ @@ -50,18 +50,18 @@ def release(widget, event): """ pass -def _delete_objects_near(widget, x, y): +def _delete_items_near(widget, x, y): """ - Delete all objects near a given point. + Delete all items near a given point. Positional arguments: - widget -- The PageWidget to delete objects on. - x -- Delete object near this point (x coordinate on the widget (!)) - y -- Delete object near this point (y coordinate on the widget (!)) + widget -- The PageWidget to delete items on. + x -- Delete item near this point (x coordinate on the widget (!)) + y -- Delete item near this point (y coordinate on the widget (!)) """ scaling = widget.page.width / widget.get_allocation().width x *= scaling y *= scaling - for obj in widget.page.get_objects_near(x, y, THICKNESS): - widget.page.delete_obj(obj, send_to_network=True) + for item in widget.page.get_items_near(x, y, THICKNESS): + widget.page.delete_item(item, send_to_network=True) diff --git a/cournal/viewer/tools/pen.py b/cournal/viewer/tools/pen.py index e7157ce..53d719d 100644 --- a/cournal/viewer/tools/pen.py +++ b/cournal/viewer/tools/pen.py @@ -46,7 +46,7 @@ def press(widget, event): _last_point = [event.x, event.y] motion(widget, event) - widget.page.layers[0].obj.append(_current_stroke) + widget.page.layers[0].items.append(_current_stroke) def motion(widget, event): """ diff --git a/cournal/viewer/tools/rect.py b/cournal/viewer/tools/rect.py index 00e53e1..b6b8a01 100644 --- a/cournal/viewer/tools/rect.py +++ b/cournal/viewer/tools/rect.py @@ -23,32 +23,29 @@ from cournal.document.rect import Rect from cournal.document import history -_current_object = None +_current_item = None _current_coords = None _start_point = None def press(widget, event): """ - Mouse down event. Draw a point on the pointer location. + Mouse down event. Set the start point for the rect Positional arguments: widget -- The PageWidget, which triggered the event event -- The Gdk.Event, which stores the location of the pointer """ - global _start_point, _current_coords, _current_object + global _start_point, _current_coords, _current_item actualWidth = widget.get_allocation().width - _current_object = Rect(widget.page.layers[0], primary.color, primary.fillcolor, primary.linewidth, []) - #_current_stroke = widget.page.new_unfinished_stroke(color=primary.color, linewidth=primary.linewidth) - _current_coords = _current_object.coords + _current_item = Rect(widget.page.layers[0], primary.color, primary.fillcolor, primary.linewidth, []) + _current_coords = _current_item.coords _start_point = [event.x, event.y] _current_coords.append(event.x*widget.page.width/actualWidth) _current_coords.append(event.y*widget.page.width/actualWidth) - #widget.page.layers[0].obj.append(_current_stroke) - def motion(widget, event): pass @@ -61,7 +58,7 @@ def release(widget, event): Positional arguments: see press() """ - global _start_point, _current_coords, _current_object + global _start_point, _current_coords, _current_item r, g, b, opacity = primary.fillcolor actualWidth = widget.get_allocation().width @@ -105,9 +102,9 @@ def release(widget, event): #_current_coords.append([event.x*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) #_current_coords.append([_last_point[0]*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) #widget.page.finish_stroke(_current_stroke) - widget.page.new_obj(_current_object, send_to_network=True) - history.register_draw_stroke(_current_object, widget.page) + widget.page.new_item(_current_item, send_to_network=True) + history.register_draw_item(_current_item, widget.page) _start_point = None _current_coords = None - _current_object = None + _current_item = None From b83242c76231a25d15f834284d6be858598c2958 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Wed, 12 Sep 2012 01:20:28 +0200 Subject: [PATCH 04/19] Shape fill flag --- ChangeLog | 2 ++ TODO | 1 - cournal/document/rect.py | 18 +++++++++-------- cournal/mainwindow.py | 29 ++++++++++++++++++++++++++-- cournal/viewer/tools/primary.py | 3 ++- cournal/viewer/tools/rect.py | 34 ++++++++++++++++----------------- 6 files changed, 57 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index 069c2d3..bacd806 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ Version 0.3 -- not yet released * Undo/Redo * Accelerated stroke deletion + * Search + * Rect tool Version 0.2.1 -- 2012-07-31 * Support for system-wide installation via setup.py diff --git a/TODO b/TODO index 27e78d0..0bfbfef 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/cournal/document/rect.py b/cournal/document/rect.py index 7743b91..706f7a9 100644 --- a/cournal/document/rect.py +++ b/cournal/document/rect.py @@ -22,7 +22,7 @@ from twisted.spread import pb class Rect(pb.Copyable, pb.RemoteCopy): - def __init__(self, layer, color, fill_color, linewidth, coords=None): + def __init__(self, layer, color, fill, fill_color, linewidth, coords=None): """ Constructor @@ -37,6 +37,7 @@ def __init__(self, layer, color, fill_color, linewidth, coords=None): self.layer = layer self.color = color self.fill_color = fill_color + self.fill = fill self.linewidth = linewidth self.coords = coords if self.coords is None: @@ -103,13 +104,14 @@ def draw(self, context, scaling=1): context.set_line_cap(cairo.LINE_CAP_ROUND) context.set_line_width(self.linewidth) - # Fill rect - context.move_to(self.coords[0], self.coords[1]) - context.line_to(self.coords[0], self.coords[3]) - context.line_to(self.coords[2], self.coords[3]) - context.line_to(self.coords[2], self.coords[1]) - context.close_path() - context.fill() + if self.fill: + # Fill rect + context.move_to(self.coords[0], self.coords[1]) + context.line_to(self.coords[0], self.coords[3]) + context.line_to(self.coords[2], self.coords[3]) + context.line_to(self.coords[2], self.coords[1]) + context.close_path() + context.fill() # Draw border context.move_to(self.coords[0], self.coords[1]) diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index 262e9f4..ac736f7 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -163,6 +163,9 @@ def __init__(self, **args): self.tool_pensize_big.connect("clicked", self.change_primary_size, LINEWIDTH_BIG) self.tool_pen.connect("clicked", self.set_tool) self.tool_rect.connect("clicked", self.set_tool) + self.tool_line.connect("clicked", self.set_tool) + self.tool_circle.connect("clicked", self.set_tool) + self.tool_fill.connect("clicked", self.set_fill) # Statusbar: self.statusbar_icon = builder.get_object("image_statusbar") @@ -215,12 +218,32 @@ def __init__(self, **args): self.search_button.connect("clicked", self.search_document) def set_tool(self, tool): + """ + Set the tool to be used + + Positional arguments: + tool -- clicked tool button + """ if tool == self.tool_pen: tool = pen - if tool == self.tool_rect: + self.tool_fill.set_sensitive(False) + elif tool == self.tool_rect: tool = rect + self.tool_fill.set_sensitive(True) primary.current_tool = tool + def set_fill(self, tool): + """ + Set the shape filling method + + Positional arguments: + tool -- fill button + """ + if tool.get_active(): + primary.fill = True + else: + primary.fill = False + def connect_event(self): """ Called by the networking layer when a connection is established. @@ -349,8 +372,10 @@ def _set_document(self, document): self.statusbar_pagenum_entry.set_sensitive(True) self.tool_pen.set_sensitive(True) self.tool_rect.set_sensitive(True) + #self.tool_line.set_sensitive(True) + #self.tool_circle.set_sensitive(True) self.menu_search.set_sensitive(True) - + if self.document.num_of_pages > 1: self.button_next_page.set_sensitive(True) diff --git a/cournal/viewer/tools/primary.py b/cournal/viewer/tools/primary.py index ba0a980..bcbadce 100644 --- a/cournal/viewer/tools/primary.py +++ b/cournal/viewer/tools/primary.py @@ -27,4 +27,5 @@ linewidth = 1.5 color = (0,0,128,255) fillcolor = (255,128,0,255) -current_tool = None \ No newline at end of file +current_tool = None +fill = True \ No newline at end of file diff --git a/cournal/viewer/tools/rect.py b/cournal/viewer/tools/rect.py index b6b8a01..63019e8 100644 --- a/cournal/viewer/tools/rect.py +++ b/cournal/viewer/tools/rect.py @@ -39,7 +39,7 @@ def press(widget, event): actualWidth = widget.get_allocation().width - _current_item = Rect(widget.page.layers[0], primary.color, primary.fillcolor, primary.linewidth, []) + _current_item = Rect(widget.page.layers[0], primary.color, primary.fill, primary.fillcolor, primary.linewidth, []) _current_coords = _current_item.coords _start_point = [event.x, event.y] @@ -59,25 +59,26 @@ def release(widget, event): Positional arguments: see press() """ global _start_point, _current_coords, _current_item - r, g, b, opacity = primary.fillcolor actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) - - # Fill rect - context.move_to(_start_point[0], _start_point[1]) - context.line_to(_start_point[0], event.y) - context.line_to(event.x, event.y) - context.line_to(event.x, _start_point[1]) - context.close_path() - - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - context.set_antialias(cairo.ANTIALIAS_GRAY) context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_line_join(cairo.LINE_JOIN_ROUND) context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - x, y, x2, y2 = context.stroke_extents() - context.fill() + context.set_antialias(cairo.ANTIALIAS_GRAY) + if primary.fill: + # Fill rect + context.move_to(_start_point[0], _start_point[1]) + context.line_to(_start_point[0], event.y) + context.line_to(event.x, event.y) + context.line_to(event.x, _start_point[1]) + context.close_path() + + r, g, b, opacity = primary.fillcolor + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + context.fill() + # Draw border context.move_to(_start_point[0], _start_point[1]) context.line_to(_start_point[0], event.y) @@ -87,6 +88,7 @@ def release(widget, event): r, g, b, opacity = primary.color context.set_source_rgba(r/255, g/255, b/255, opacity/255) + x, y, x2, y2 = context.stroke_extents() context.stroke() update_rect = Gdk.Rectangle() @@ -96,12 +98,8 @@ def release(widget, event): update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - #_current_coords.append([_last_point[0]*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) _current_coords.append(event.x*widget.page.width/actualWidth) _current_coords.append(event.y*widget.page.width/actualWidth) - #_current_coords.append([event.x*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) - #_current_coords.append([_last_point[0]*widget.page.width/actualWidth, _last_point[1]*widget.page.width/actualWidth]) - #widget.page.finish_stroke(_current_stroke) widget.page.new_item(_current_item, send_to_network=True) history.register_draw_item(_current_item, widget.page) From 9b607d9a4d15961347a4f7bbd0a78f822109451a Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Wed, 12 Sep 2012 02:29:33 +0200 Subject: [PATCH 05/19] Added tool: * line --- ChangeLog | 1 + TODO | 4 +++- cournal/mainwindow.py | 10 ++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index bacd806..798592f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Version 0.3 -- not yet released * Accelerated stroke deletion * Search * Rect tool + * Line tool Version 0.2.1 -- 2012-07-31 * Support for system-wide installation via setup.py diff --git a/TODO b/TODO index 0bfbfef..2085d2c 100644 --- a/TODO +++ b/TODO @@ -14,4 +14,6 @@ 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 - +* Show tools preview +* Tool icons +* Item deletion functions diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index ac736f7..5238694 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -24,7 +24,7 @@ import cournal from cournal.viewer.layout import Layout -from cournal.viewer.tools import pen, rect, primary +from cournal.viewer.tools import pen, rect, primary, line from cournal.document.document import Document from cournal.document import xojparser from cournal.network import network @@ -227,9 +227,15 @@ def set_tool(self, tool): if tool == self.tool_pen: tool = pen self.tool_fill.set_sensitive(False) + self.tool_pen_bg_color.set_sensitive(False) elif tool == self.tool_rect: tool = rect self.tool_fill.set_sensitive(True) + self.tool_pen_bg_color.set_sensitive(True) + elif tool == self.tool_line: + tool = line + self.tool_fill.set_sensitive(False) + self.tool_pen_bg_color.set_sensitive(False) primary.current_tool = tool def set_fill(self, tool): @@ -372,7 +378,7 @@ def _set_document(self, document): self.statusbar_pagenum_entry.set_sensitive(True) self.tool_pen.set_sensitive(True) self.tool_rect.set_sensitive(True) - #self.tool_line.set_sensitive(True) + self.tool_line.set_sensitive(True) #self.tool_circle.set_sensitive(True) self.menu_search.set_sensitive(True) From 0f478f2f84c9205001798381aa8efa571bb233f3 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Thu, 13 Sep 2012 01:21:27 +0200 Subject: [PATCH 06/19] New tools: * line * oval New icons for tool bar --- TODO | 3 + cournal/document/page.py | 3 + cournal/document/rect.py | 1 + cournal/mainwindow.glade | 126 ++++--- cournal/mainwindow.py | 8 +- cournal/server/server.py | 1 + .../scalable/actions/cournal-circle-tool.svg | 179 ++++++++++ .../scalable/actions/cournal-fill-tool.svg | 245 ++++++++++++++ .../scalable/actions/cournal-line-tool.svg | 210 ++++++++++++ .../scalable/actions/cournal-pen-big.svg | 319 ++++++++++++++++++ .../scalable/actions/cournal-pen-medium.svg | 240 +++++++++++++ .../scalable/actions/cournal-pen-small.svg | 241 +++++++++++++ .../scalable/actions/cournal-pen-tool.svg | 201 +++++++++++ .../scalable/actions/cournal-rect-tool.svg | 178 ++++++++++ 14 files changed, 1900 insertions(+), 55 deletions(-) create mode 100644 icons/hicolor/scalable/actions/cournal-circle-tool.svg create mode 100644 icons/hicolor/scalable/actions/cournal-fill-tool.svg create mode 100644 icons/hicolor/scalable/actions/cournal-line-tool.svg create mode 100644 icons/hicolor/scalable/actions/cournal-pen-big.svg create mode 100644 icons/hicolor/scalable/actions/cournal-pen-medium.svg create mode 100644 icons/hicolor/scalable/actions/cournal-pen-small.svg create mode 100644 icons/hicolor/scalable/actions/cournal-pen-tool.svg create mode 100644 icons/hicolor/scalable/actions/cournal-rect-tool.svg diff --git a/TODO b/TODO index 2085d2c..c4545f3 100644 --- a/TODO +++ b/TODO @@ -17,3 +17,6 @@ This is a TODO List with (hopefully) future Cournal Features! * Show tools preview * Tool icons * Item deletion functions +* Import/Open/Annotate? +* Error handling +* Change Pickle to JSON / XML diff --git a/cournal/document/page.py b/cournal/document/page.py index 13b1dd9..e41833d 100644 --- a/cournal/document/page.py +++ b/cournal/document/page.py @@ -21,6 +21,7 @@ from cournal.document.layer import Layer from cournal.document.stroke import Stroke from cournal.document.rect import Rect +from cournal.document.circle import Circle from cournal.network import network from cournal.document import history @@ -150,3 +151,5 @@ def get_items_near(self, x, y, radius): break elif isinstance(item, Rect): yield item + elif isinstance(item, Circle): + yield item diff --git a/cournal/document/rect.py b/cournal/document/rect.py index 706f7a9..d6aa073 100644 --- a/cournal/document/rect.py +++ b/cournal/document/rect.py @@ -81,6 +81,7 @@ def getStateToCopy(self): d = dict() d["color"] = self.color d["fill_color"] = self.fill_color + d["fill"] = self.fill d["coords"] = self.coords d["linewidth"] = self.linewidth return d diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index f78f093..09ec6b2 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -328,27 +328,12 @@ True - - - False - True - 1 - - - - - True - False - icons - + False True False False - Pen - True - True False @@ -356,14 +341,13 @@ - + False True False False - Rect True - tool_pen + gtk-undo False @@ -371,31 +355,41 @@ - + False True False False - Line True - True - tool_pen + gtk-redo False True + + + False + True + 1 + + + + + True + False + icons - + False True False False - Circle + Pen True + cournal-pen-tool True - tool_pen False @@ -403,11 +397,15 @@ - + False True False False + Rect + True + cournal-rect-tool + tool_pen False @@ -415,13 +413,16 @@ - + False True False False + Line True - gtk-undo + cournal-line-tool + True + tool_pen False @@ -429,13 +430,16 @@ - + False True False False + Circle True - gtk-redo + cournal-circle-tool + True + tool_pen False @@ -510,6 +514,7 @@ False Fill True + cournal-fill-tool True @@ -517,6 +522,18 @@ True + + + False + True + False + False + + + False + True + + False @@ -524,6 +541,7 @@ False False 0.7 + cournal-pen-small tool_pensize_normal @@ -538,6 +556,7 @@ False False 1.5 + cournal-pen-medium True @@ -552,6 +571,7 @@ False False 8.0 + cournal-pen-big tool_pensize_normal @@ -566,9 +586,30 @@ 2 + + + True + + + True + True + always + in + + + + + + + + True + True + 3 + + - False + False True @@ -622,27 +663,6 @@ 4 - - - True - - - True - True - always - in - - - - - - - - True - True - 3 - - True diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index 5238694..177883a 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -24,7 +24,7 @@ import cournal from cournal.viewer.layout import Layout -from cournal.viewer.tools import pen, rect, primary, line +from cournal.viewer.tools import pen, rect, primary, line, circle from cournal.document.document import Document from cournal.document import xojparser from cournal.network import network @@ -236,6 +236,10 @@ def set_tool(self, tool): tool = line self.tool_fill.set_sensitive(False) self.tool_pen_bg_color.set_sensitive(False) + elif tool == self.tool_circle: + tool = circle + self.tool_fill.set_sensitive(True) + self.tool_pen_bg_color.set_sensitive(True) primary.current_tool = tool def set_fill(self, tool): @@ -379,7 +383,7 @@ def _set_document(self, document): self.tool_pen.set_sensitive(True) self.tool_rect.set_sensitive(True) self.tool_line.set_sensitive(True) - #self.tool_circle.set_sensitive(True) + self.tool_circle.set_sensitive(True) self.menu_search.set_sensitive(True) if self.document.num_of_pages > 1: diff --git a/cournal/server/server.py b/cournal/server/server.py index 02ed363..571909f 100755 --- a/cournal/server/server.py +++ b/cournal/server/server.py @@ -37,6 +37,7 @@ from cournal import __versionstring__ as cournal_version from cournal.document.stroke import Stroke from cournal.document.rect import Rect +from cournal.document.circle import Circle # 0 - none diff --git a/icons/hicolor/scalable/actions/cournal-circle-tool.svg b/icons/hicolor/scalable/actions/cournal-circle-tool.svg new file mode 100644 index 0000000..cd30d24 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-circle-tool.svg @@ -0,0 +1,179 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/hicolor/scalable/actions/cournal-fill-tool.svg b/icons/hicolor/scalable/actions/cournal-fill-tool.svg new file mode 100644 index 0000000..225ff06 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-fill-tool.svg @@ -0,0 +1,245 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/hicolor/scalable/actions/cournal-line-tool.svg b/icons/hicolor/scalable/actions/cournal-line-tool.svg new file mode 100644 index 0000000..33e23a5 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-line-tool.svg @@ -0,0 +1,210 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/hicolor/scalable/actions/cournal-pen-big.svg b/icons/hicolor/scalable/actions/cournal-pen-big.svg new file mode 100644 index 0000000..5db585a --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-pen-big.svg @@ -0,0 +1,319 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/hicolor/scalable/actions/cournal-pen-medium.svg b/icons/hicolor/scalable/actions/cournal-pen-medium.svg new file mode 100644 index 0000000..cd1e7f5 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-pen-medium.svg @@ -0,0 +1,240 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.5 + diff --git a/icons/hicolor/scalable/actions/cournal-pen-small.svg b/icons/hicolor/scalable/actions/cournal-pen-small.svg new file mode 100644 index 0000000..ab52396 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-pen-small.svg @@ -0,0 +1,241 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.7 + + + diff --git a/icons/hicolor/scalable/actions/cournal-pen-tool.svg b/icons/hicolor/scalable/actions/cournal-pen-tool.svg new file mode 100644 index 0000000..201eb17 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-pen-tool.svg @@ -0,0 +1,201 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/hicolor/scalable/actions/cournal-rect-tool.svg b/icons/hicolor/scalable/actions/cournal-rect-tool.svg new file mode 100644 index 0000000..48243d5 --- /dev/null +++ b/icons/hicolor/scalable/actions/cournal-rect-tool.svg @@ -0,0 +1,178 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 695b28d4a4d91870cfa972722d9853cdcac05df7 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Thu, 13 Sep 2012 01:24:48 +0200 Subject: [PATCH 07/19] Added missing files --- cournal/document/circle.py | 129 ++++++++++++++++++++++++++++++ cournal/viewer/tools/circle.py | 142 +++++++++++++++++++++++++++++++++ cournal/viewer/tools/line.py | 92 +++++++++++++++++++++ 3 files changed, 363 insertions(+) create mode 100644 cournal/document/circle.py create mode 100644 cournal/viewer/tools/circle.py create mode 100644 cournal/viewer/tools/line.py diff --git a/cournal/document/circle.py b/cournal/document/circle.py new file mode 100644 index 0000000..53534f8 --- /dev/null +++ b/cournal/document/circle.py @@ -0,0 +1,129 @@ +#!/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 . + +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): + """ + Test if point is in bounding box of the stroke. + + Positional arguments: + x, y -- point + + Returns: + true, if point is in bounding box + """ + radius = (self.coords[0] - x)**2 + (self.coords[1] - y)**2 + if radius < max(self.scale[0], self.scale[1])**2: + # TODO Improve + 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) + """ + 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) diff --git a/cournal/viewer/tools/circle.py b/cournal/viewer/tools/circle.py new file mode 100644 index 0000000..27c08b1 --- /dev/null +++ b/cournal/viewer/tools/circle.py @@ -0,0 +1,142 @@ +#!/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 . + +import cairo +from gi.repository import Gdk +from cournal.viewer.tools import primary +from cournal.document.circle import Circle +from cournal.document import history +import math + +""" +A pen tool. Draws a stroke with a certain color and size. +""" + +_start_point = None + +def press(widget, event): + """ + Mouse down event. Draw a point on the pointer location. + + Positional arguments: + widget -- The PageWidget, which triggered the event + event -- The Gdk.Event, which stores the location of the pointer + """ + global _start_point + _start_point = [event.x, event.y] + +def motion(widget, event): + pass + +def release(widget, event): + """ + Mouse release event. Inform the corresponding Page instance, that the stroke + is finished. + + This will cause the stroke to be sent to the server, if it is connected. + + Positional arguments: see press() + """ + global _start_point + _end_point = [0,0] + actualWidth = widget.get_allocation().width + + # Sort Coords + if event.x < _start_point[0]: + _end_point[0] = _start_point[0] + _start_point[0] = event.x + else: + _end_point[0] = event.x + + if event.y < _start_point[1]: + _end_point[1] = _start_point[1] + _start_point[1] = event.y + else: + _end_point[1] = event.y + + # Calculate center + center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] + + # Calculate width and height + width = _end_point[0] - _start_point[0] + height = _end_point[1] - _start_point[1] + + # Stop it here if there's nothing to draw + if height * width == 0: + return + + scale = (width + height) / 2 + scale_width = width / scale + scale_height = height / scale + + actualWidth = widget.get_allocation().width + + context = cairo.Context(widget.backbuffer) + context.set_antialias(cairo.ANTIALIAS_GRAY) + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + + # Fill circle + if primary.fill: + r, g, b, opacity = primary.fillcolor + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + # Draw Circle and scale it to oval + context.translate(center[0], center[1]) + context.scale(scale_width / 2., scale_height / 2.) + context.arc(0., 0., scale, 0., 2 * math.pi) + context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) + context.translate(-center[0], -center[1]) + context.fill() + + # border + r, g, b, opacity = primary.color + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + i = 0 + context.move_to(center[0], center[1] + height/2) + # We draw the border with strokes to keep konstant border width + while i < (math.pi * 2): + i += 0.1 + context.line_to( + center[0] + math.sin(i)*width/2, + center[1] + math.cos(i)*height/2) + x, y, x2, y2 = context.stroke_extents() + context.stroke() + + update_rect = Gdk.Rectangle() + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 + widget.get_window().invalidate_rect(update_rect, False) + + #_current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) + + #widget.page.finish_stroke(_current_stroke) + current_item = Circle( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [center[0]*widget.page.width/actualWidth, center[1]*widget.page.width/actualWidth], + [width/2*widget.page.width/actualWidth, height/2*widget.page.width/actualWidth, scale*widget.page.width/actualWidth]) + widget.page.new_item(current_item, send_to_network=True) + history.register_draw_item(current_item, widget.page) + + _start_point = None diff --git a/cournal/viewer/tools/line.py b/cournal/viewer/tools/line.py new file mode 100644 index 0000000..a46a79a --- /dev/null +++ b/cournal/viewer/tools/line.py @@ -0,0 +1,92 @@ +#!/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 . + +import cairo +from gi.repository import Gdk +from cournal.viewer.tools import primary + +""" +A pen tool. Draws a stroke with a certain color and size. +""" + +_start_point = None +_current_coords = None +_current_stroke = None + +def press(widget, event): + """ + Mouse down event. Draw a point on the pointer location. + + Positional arguments: + widget -- The PageWidget, which triggered the event + event -- The Gdk.Event, which stores the location of the pointer + """ + global _start_point, _current_coords, _current_stroke + actualWidth = widget.get_allocation().width + + _current_stroke = widget.page.new_unfinished_stroke(color=primary.color, linewidth=primary.linewidth) + _current_coords = _current_stroke.coords + _current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) + + _start_point = [event.x, event.y] + + widget.page.layers[0].items.append(_current_stroke) + +def motion(widget, event): + pass + +def release(widget, event): + """ + Mouse release event. Inform the corresponding Page instance, that the stroke + is finished. + + This will cause the stroke to be sent to the server, if it is connected. + + Positional arguments: see press() + """ + global _start_point, _current_coords, _current_stroke + + r, g, b, opacity = primary.color + actualWidth = widget.get_allocation().width + context = cairo.Context(widget.backbuffer) + + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + context.set_antialias(cairo.ANTIALIAS_GRAY) + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + + context.move_to(_start_point[0], _start_point[1]) + context.line_to(event.x, event.y) + x, y, x2, y2 = context.stroke_extents() + context.stroke() + + update_rect = Gdk.Rectangle() + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 + widget.get_window().invalidate_rect(update_rect, False) + + _current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) + + widget.page.finish_stroke(_current_stroke) + + _start_point = None + _current_coords = None + _current_stroke = None From ff853dc167be19c0fc531f3d005503d3ce123012 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Thu, 13 Sep 2012 01:49:02 +0200 Subject: [PATCH 08/19] Changed tool icons colors --- .../scalable/actions/cournal-circle-tool.svg | 36 +++++++++++--- .../scalable/actions/cournal-fill-tool.svg | 40 ++++++++++++---- .../scalable/actions/cournal-line-tool.svg | 12 ++--- .../scalable/actions/cournal-pen-big.svg | 33 +++---------- .../scalable/actions/cournal-pen-medium.svg | 46 +++++++++++------- .../scalable/actions/cournal-pen-small.svg | 47 ++++++++++++------- .../scalable/actions/cournal-pen-tool.svg | 42 +++++++++++++---- .../scalable/actions/cournal-rect-tool.svg | 36 +++++++++++--- 8 files changed, 198 insertions(+), 94 deletions(-) diff --git a/icons/hicolor/scalable/actions/cournal-circle-tool.svg b/icons/hicolor/scalable/actions/cournal-circle-tool.svg index cd30d24..3d6b283 100644 --- a/icons/hicolor/scalable/actions/cournal-circle-tool.svg +++ b/icons/hicolor/scalable/actions/cournal-circle-tool.svg @@ -8,10 +8,34 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-circle-tool.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,22 +54,22 @@ id="linearGradient6170"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-fill-tool.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,33 +54,33 @@ id="linearGradient6341"> image/svg+xml - + @@ -42,7 +42,7 @@ id="namedview28" showgrid="true" inkscape:zoom="2.9690871" - inkscape:cx="73.659003" + inkscape:cx="-0.10104264" inkscape:cy="81.488136" inkscape:window-x="-2" inkscape:window-y="-3" @@ -60,20 +60,20 @@ + style="stop-color:#787878;stop-opacity:1;" /> + style="stop-color:#505050;stop-opacity:1;" /> diff --git a/icons/hicolor/scalable/actions/cournal-pen-big.svg b/icons/hicolor/scalable/actions/cournal-pen-big.svg index 5db585a..fba11c6 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-big.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-big.svg @@ -24,7 +24,7 @@ image/svg+xml - + @@ -42,7 +42,7 @@ id="namedview28" showgrid="true" inkscape:zoom="2.9690871" - inkscape:cx="148.34679" + inkscape:cx="74.586744" inkscape:cy="81.488136" inkscape:window-x="-2" inkscape:window-y="-3" @@ -60,20 +60,20 @@ + style="stop-color:#505050;stop-opacity:1;" /> + style="stop-color:#282828;stop-opacity:1;" /> @@ -293,27 +293,8 @@ sodipodi:cy="81.47142" sodipodi:rx="22.5" sodipodi:ry="22.5" - d="m 105,81.47142 a 22.5,22.5 0 1 1 -45,0 22.5,22.5 0 1 1 45,0 z" + d="m 105,81.47142 c 0,12.426407 -10.073593,22.5 -22.5,22.5 -12.426407,0 -22.5,-10.073593 -22.5,-22.5 0,-12.426407 10.073593,-22.5 22.5,-22.5 12.426407,0 22.5,10.073593 22.5,22.5 z" transform="matrix(4.2622826,-4.2622826,4.2622826,4.2622826,-593.52507,-11.077801)" /> - - - - - diff --git a/icons/hicolor/scalable/actions/cournal-pen-medium.svg b/icons/hicolor/scalable/actions/cournal-pen-medium.svg index cd1e7f5..b8587de 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-medium.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-medium.svg @@ -8,10 +8,34 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-pen-medium.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,22 +54,22 @@ id="linearGradient6341"> - 1.5 diff --git a/icons/hicolor/scalable/actions/cournal-pen-small.svg b/icons/hicolor/scalable/actions/cournal-pen-small.svg index ab52396..3e79669 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-small.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-small.svg @@ -8,10 +8,34 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-pen-small.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,22 +54,22 @@ id="linearGradient6341"> - 0.7 diff --git a/icons/hicolor/scalable/actions/cournal-pen-tool.svg b/icons/hicolor/scalable/actions/cournal-pen-tool.svg index 201eb17..7e02203 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-tool.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-tool.svg @@ -8,10 +8,34 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-pen-tool.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,33 +54,33 @@ id="linearGradient6178"> + style="fill:url(#linearGradient5091);fill-opacity:1;stroke:#372319;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> diff --git a/icons/hicolor/scalable/actions/cournal-rect-tool.svg b/icons/hicolor/scalable/actions/cournal-rect-tool.svg index 48243d5..e2ee38b 100644 --- a/icons/hicolor/scalable/actions/cournal-rect-tool.svg +++ b/icons/hicolor/scalable/actions/cournal-rect-tool.svg @@ -8,10 +8,34 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2"> + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="cournal-rect-tool.svg"> + @@ -20,7 +44,7 @@ image/svg+xml - + @@ -30,22 +54,22 @@ id="linearGradient5642"> Date: Thu, 13 Sep 2012 02:43:34 +0200 Subject: [PATCH 09/19] Redesigned window after merging --- cournal/mainwindow.glade | 136 ++++++++++++++++++++------------------- cournal/mainwindow.py | 10 +-- 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index 91f653c..1032b7f 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -3,7 +3,6 @@ - accelgroup gtk-open @@ -28,7 +27,6 @@ - accelgroup gtk-save @@ -139,6 +137,7 @@ False + False False _File True @@ -235,6 +234,7 @@ + False True False _Edit @@ -276,6 +276,7 @@ + False True False _Help @@ -315,6 +316,7 @@ True False Annotate PDF + action_open_pdf False @@ -328,6 +330,7 @@ False True Connect to server + action_connect False @@ -340,6 +343,7 @@ True False Save current document + action_save True @@ -362,6 +366,7 @@ action_zoom_in True False + action_zoom_in True @@ -374,6 +379,7 @@ action_zoom_out True False + action_zoom_out True @@ -386,6 +392,7 @@ action_zoom_fit True False + action_zoom_fit True @@ -408,6 +415,7 @@ action_undo True False + action_undo True @@ -420,6 +428,7 @@ action_redo True False + action_redo True @@ -521,11 +530,15 @@ action_pen_color True False + action_pen_color + False + False True True True + False none True Pen Color @@ -594,6 +607,7 @@ action_pensize_small True False + action_pensize_small 0.7 cournal-pen-small tool_pensize_normal @@ -608,6 +622,7 @@ action_pensize_normal True False + action_pensize_normal 1.5 cournal-pen-medium @@ -621,6 +636,7 @@ action_pensize_big True False + action_pensize_big 8.0 cournal-pen-big tool_pensize_normal @@ -659,24 +675,60 @@ - - True + + False + True + 5 - + True True - always - in - - - + + gtk-find + + True + True + 0 + + + + + gtk-find + False + True + True + True + False + True + + + False + True + 1 + + + + + gtk-close + False + True + True + True + False + True + + + False + True + 2 + - True + False True - 2 + 4 @@ -703,10 +755,12 @@ 1 + False True False False True + False image_btn_prev none False @@ -750,10 +804,12 @@ + False True False False True + False image_btn_next none @@ -791,61 +847,7 @@ False False end - 4 - - - - - False - True - 5 - - - True - True - - gtk-find - - - True - True - 0 - - - - - gtk-find - True - True - True - True - - - False - True - 1 - - - - - gtk-close - True - True - True - True - - - False - True - 2 - - - - - False - True - end - 4 + 5 diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index 3b19c20..ac74ac2 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -83,7 +83,7 @@ def __init__(self, **args): self.tool_circle = builder.get_object("tool_circle") self.tool_fill = builder.get_object("tool_fill") - history.init(self.menu_undo, self.menu_redo, self.tool_undo, self.tool_redo) + #history.init(self.menu_undo, self.menu_redo, self.tool_undo, self.tool_redo) # Actions (always sensitive): action_open_xoj = builder.get_object("action_open_xoj") @@ -149,10 +149,10 @@ def __init__(self, **args): action_zoom_in.connect("activate", self.zoom_in) action_zoom_out.connect("activate", self.zoom_out) action_zoom_fit.connect("activate", self.zoom_fit) - tool_pen_color.connect("color-set", self.change_pen_color) - action_pensize_small.connect("activate", self.change_pen_size, LINEWIDTH_SMALL) - action_pensize_normal.connect("activate", self.change_pen_size, LINEWIDTH_NORMAL) - action_pensize_big.connect("activate", self.change_pen_size, LINEWIDTH_BIG) + tool_pen_color.connect("color-set", self.change_primary_color) + action_pensize_small.connect("activate", self.change_primary_size, LINEWIDTH_SMALL) + action_pensize_normal.connect("activate", self.change_primary_size, LINEWIDTH_NORMAL) + action_pensize_big.connect("activate", self.change_primary_size, LINEWIDTH_BIG) self.tool_pen_bg_color.connect("color-set", self.change_primary_bg_color) self.tool_pen.connect("clicked", self.set_tool) From a443b401f0108ee8d2a498d09042607d44782269 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Thu, 13 Sep 2012 04:16:24 +0200 Subject: [PATCH 10/19] Added tools to the actions --- ChangeLog | 4 ++ TODO | 6 +-- cournal/mainwindow.glade | 65 ++++++++++++++++++++++++++++++++- cournal/mainwindow.py | 79 ++++++++++++++++++++-------------------- 4 files changed, 109 insertions(+), 45 deletions(-) diff --git a/ChangeLog b/ChangeLog index 798592f..35791bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,10 @@ Version 0.3 -- not yet released * Search * Rect tool * Line tool + * Circle tool + * Fill color + * Tool bar with icons + * Keyboard shortcuts Version 0.2.1 -- 2012-07-31 * Support for system-wide installation via setup.py diff --git a/TODO b/TODO index c4545f3..c2ccde9 100644 --- a/TODO +++ b/TODO @@ -15,8 +15,8 @@ This is a TODO List with (hopefully) future Cournal Features! * Android Port * Show position of other users in scrollbar * Show tools preview -* Tool icons * Item deletion functions -* Import/Open/Annotate? * Error handling -* Change Pickle to JSON / XML +* Change Pickle to JSON +* Make fill color chooser more discreet +* Set sensitivity for not incompatible actions \ No newline at end of file diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index 1032b7f..dba4a81 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -90,12 +90,14 @@ 1.5 + 0.7 action_pensize_normal + @@ -103,6 +105,7 @@ 3 action_pensize_normal + @@ -114,6 +117,46 @@ + + + Pen + True + + + + + + Rectangle + True + action_tool_pen + + + + + + Line + True + action_tool_pen + + + + + + Circle + True + action_tool_pen + + + + + + Fill + + + + + + True @@ -451,8 +494,10 @@ False + action_tool_pen True False + action_tool_pen False Pen True @@ -467,8 +512,10 @@ False + action_tool_rect True False + action_tool_rect False Rect True @@ -483,8 +530,10 @@ False + action_tool_line True False + action_tool_line False Line True @@ -500,8 +549,10 @@ False + action_tool_circle True False + action_tool_circle False Circle True @@ -525,7 +576,7 @@ - + False action_pen_color True @@ -553,11 +604,13 @@ False + action_pen_fillcolor True False + action_pen_fillcolor False - + False True True @@ -577,8 +630,10 @@ False + action_fill_tool True False + action_fill_tool False Fill True @@ -604,10 +659,12 @@ + False action_pensize_small True False action_pensize_small + False 0.7 cournal-pen-small tool_pensize_normal @@ -619,10 +676,12 @@ + False action_pensize_normal True False action_pensize_normal + False 1.5 cournal-pen-medium @@ -633,10 +692,12 @@ + False action_pensize_big True False action_pensize_big + False 8.0 cournal-pen-big tool_pensize_normal diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index ac74ac2..96adc35 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -75,16 +75,6 @@ def __init__(self, **args): self.overlay = builder.get_object("overlay") self.scrolledwindow = builder.get_object("scrolledwindow") - # Toolbar: - self.tool_pen_bg_color = builder.get_object("tool_pen_bg_color") - self.tool_pen = builder.get_object("tool_pen") - self.tool_rect = builder.get_object("tool_rect") - self.tool_line = builder.get_object("tool_line") - self.tool_circle = builder.get_object("tool_circle") - self.tool_fill = builder.get_object("tool_fill") - - #history.init(self.menu_undo, self.menu_redo, self.tool_undo, self.tool_redo) - # Actions (always sensitive): action_open_xoj = builder.get_object("action_open_xoj") action_open_pdf = builder.get_object("action_open_pdf") @@ -107,6 +97,13 @@ def __init__(self, **args): action_pensize_normal = builder.get_object("action_pensize_normal") action_pensize_big = builder.get_object("action_pensize_big") tool_pen_color = builder.get_object("tool_pen_color") + tool_pen_fillcolor = builder.get_object("tool_pen_fillcolor") + action_tool_pen = builder.get_object("action_tool_pen") + action_tool_rect = builder.get_object("action_tool_rect") + action_tool_line = builder.get_object("action_tool_line") + action_tool_circle = builder.get_object("action_tool_circle") + action_fill_tool = builder.get_object("action_fill_tool") + self.actiongroup_document_specific = builder.get_object("actiongroup_document_specific") self.actiongroup_document_specific.set_sensitive(False) builder.get_object("tool_pensize_normal").set_active(True) @@ -133,6 +130,11 @@ def __init__(self, **args): a.connect_by_path(action_pensize_small.get_accel_path(), lambda a,b,c,d: action_pensize_small.activate()) a.connect_by_path(action_pensize_normal.get_accel_path(), lambda a,b,c,d: action_pensize_normal.activate()) a.connect_by_path(action_pensize_big.get_accel_path(), lambda a,b,c,d: action_pensize_big.activate()) + a.connect_by_path(action_tool_pen.get_accel_path(), lambda a,b,c,d: action_tool_pen.activate()) + a.connect_by_path(action_tool_rect.get_accel_path(), lambda a,b,c,d: action_tool_rect.activate()) + a.connect_by_path(action_tool_line.get_accel_path(), lambda a,b,c,d: action_tool_line.activate()) + a.connect_by_path(action_tool_circle.get_accel_path(), lambda a,b,c,d: action_tool_circle.activate()) + a.connect_by_path(action_fill_tool.get_accel_path(), lambda a,b,c,d: action_fill_tool.activate()) action_open_xoj.connect("activate", self.run_open_xoj_dialog) action_open_pdf.connect("activate", self.run_open_pdf_dialog) @@ -153,13 +155,12 @@ def __init__(self, **args): action_pensize_small.connect("activate", self.change_primary_size, LINEWIDTH_SMALL) action_pensize_normal.connect("activate", self.change_primary_size, LINEWIDTH_NORMAL) action_pensize_big.connect("activate", self.change_primary_size, LINEWIDTH_BIG) - - self.tool_pen_bg_color.connect("color-set", self.change_primary_bg_color) - self.tool_pen.connect("clicked", self.set_tool) - self.tool_rect.connect("clicked", self.set_tool) - self.tool_line.connect("clicked", self.set_tool) - self.tool_circle.connect("clicked", self.set_tool) - self.tool_fill.connect("clicked", self.set_fill) + tool_pen_fillcolor.connect("color-set", self.change_primary_fillcolor) + action_tool_pen.connect("activate", self.set_tool) + action_tool_rect.connect("activate", self.set_tool) + action_tool_line.connect("activate", self.set_tool) + action_tool_circle.connect("activate", self.set_tool) + action_fill_tool.connect("activate", self.set_fill) # Statusbar: self.statusbar_icon = builder.get_object("image_statusbar") @@ -197,22 +198,26 @@ def set_tool(self, tool): Positional arguments: tool -- clicked tool button """ - if tool == self.tool_pen: - tool = pen - self.tool_fill.set_sensitive(False) - self.tool_pen_bg_color.set_sensitive(False) - elif tool == self.tool_rect: - tool = rect - self.tool_fill.set_sensitive(True) - self.tool_pen_bg_color.set_sensitive(True) - elif tool == self.tool_line: - tool = line - self.tool_fill.set_sensitive(False) - self.tool_pen_bg_color.set_sensitive(False) - elif tool == self.tool_circle: - tool = circle - self.tool_fill.set_sensitive(True) - self.tool_pen_bg_color.set_sensitive(True) + #TODO: Check what happend to the action + #TODO: Maybe change sensitivity + + if tool not in [pen, rect, line, circle]: + if tool.get_name() == "action_tool_pen": + tool = pen + #action_fill_tool.set_sensitive(False) + #action_tool_pen_bg_color.set_sensitive(False) + elif tool.get_name() == "action_tool_rect": + tool = rect + #action_fill_tool.set_sensitive(True) + #action_tool_pen_bg_color.set_sensitive(True) + elif tool.get_name() == "action_tool_line": + tool = line + #action_tool_fill.set_sensitive(False) + #action_tool_pen_bg_color.set_sensitive(False) + elif tool.get_name() == "action_tool_circle": + tool = circle + #action_tool_fill.set_sensitive(True) + #action_tool_pen_bg_color.set_sensitive(True) primary.current_tool = tool def set_fill(self, tool): @@ -336,12 +341,6 @@ def _set_document(self, document): self.scrolledwindow.show_all() self.last_filename = None - self.tool_pen_bg_color.set_sensitive(True) - self.tool_pen.set_sensitive(True) - self.tool_rect.set_sensitive(True) - self.tool_line.set_sensitive(True) - self.tool_circle.set_sensitive(True) - self.actiongroup_document_specific.set_sensitive(True) if self.document.num_of_pages > 1: @@ -605,7 +604,7 @@ def change_primary_color(self, colorbutton): primary.color = red, green, blue, opacity - def change_primary_bg_color(self, colorbutton): + def change_primary_fillcolor(self, colorbutton): """ Change the primary tool to a user defined color. From 5e32bbf0a70be7344206eaad3401c708658abbfd Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Thu, 13 Sep 2012 04:30:26 +0200 Subject: [PATCH 11/19] Removed Inkscape artifacts from tool icons --- .../scalable/actions/cournal-circle-tool.svg | 48 ++---- .../scalable/actions/cournal-fill-tool.svg | 52 ++---- .../scalable/actions/cournal-line-tool.svg | 88 +++------- .../scalable/actions/cournal-pen-big.svg | 162 +++++++----------- .../scalable/actions/cournal-pen-medium.svg | 48 ++---- .../scalable/actions/cournal-pen-small.svg | 48 ++---- .../scalable/actions/cournal-pen-tool.svg | 52 ++---- .../scalable/actions/cournal-rect-tool.svg | 48 ++---- 8 files changed, 165 insertions(+), 381 deletions(-) diff --git a/icons/hicolor/scalable/actions/cournal-circle-tool.svg b/icons/hicolor/scalable/actions/cournal-circle-tool.svg index 3d6b283..04faa08 100644 --- a/icons/hicolor/scalable/actions/cournal-circle-tool.svg +++ b/icons/hicolor/scalable/actions/cournal-circle-tool.svg @@ -8,34 +8,10 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2" - inkscape:version="0.48.3.1 r9886" - sodipodi:docname="cournal-circle-tool.svg"> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,22 +30,22 @@ id="linearGradient6170"> + id="feGaussianBlur3519" + stdDeviation="0.59409041" /> + id="feGaussianBlur3519-6" + stdDeviation="0.97623979" /> + id="feGaussianBlur3519-1" + stdDeviation="1.1714877" /> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,33 +30,33 @@ id="linearGradient6341"> + id="feGaussianBlur3519" + stdDeviation="0.59409041" /> + id="feGaussianBlur3519-6" + stdDeviation="0.97623979" /> + id="feGaussianBlur3519-1" + stdDeviation="1.1714877" /> + id="svg2"> @@ -24,58 +20,33 @@ image/svg+xml - + - - - + style="stop-color:#787878;stop-opacity:1" + offset="0" /> + style="stop-color:#505050;stop-opacity:1" + offset="1" /> + id="stop6282" + style="stop-color:#505050;stop-opacity:1" + offset="0" /> + id="stop6284" + style="stop-color:#323232;stop-opacity:1" + offset="1" /> @@ -101,11 +72,11 @@ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" /> + id="feGaussianBlur3519" /> + id="feGaussianBlur3519-6" /> + id="feGaussianBlur3519-1" /> + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,83.310744,-32.476409)" + id="g3040"> + style="fill:url(#linearGradient6294);fill-opacity:1;stroke:url(#linearGradient6286);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> diff --git a/icons/hicolor/scalable/actions/cournal-pen-big.svg b/icons/hicolor/scalable/actions/cournal-pen-big.svg index fba11c6..b1369a9 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-big.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-big.svg @@ -8,14 +8,10 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2" - inkscape:version="0.48.3.1 r9886" - sodipodi:docname="cournal-pen-big.svg"> + id="svg2"> @@ -24,80 +20,55 @@ image/svg+xml - + - - - + style="stop-color:#505050;stop-opacity:1" + offset="0" /> + style="stop-color:#282828;stop-opacity:1" + offset="1" /> + id="stop6327" + style="stop-color:#787878;stop-opacity:1" + offset="0" /> + id="stop6329" + style="stop-color:#505050;stop-opacity:1" + offset="1" /> + style="stop-color:#f03c00;stop-opacity:1" + offset="0" /> + style="stop-color:#a02900;stop-opacity:1" + offset="1" /> + id="stop6282" + style="stop-color:#a02800;stop-opacity:1" + offset="0" /> + id="stop6284" + style="stop-color:#641a00;stop-opacity:1" + offset="1" /> @@ -123,11 +94,11 @@ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" /> + id="feGaussianBlur3519" /> + id="feGaussianBlur3519-6" /> + id="feGaussianBlur3519-1" /> + id="stop6327-0" + style="stop-color:#f03c00;stop-opacity:1" + offset="0" /> + id="stop6329-4" + style="stop-color:#a02900;stop-opacity:1" + offset="1" /> + style="stop-color:#a02800;stop-opacity:1" + offset="0" /> + style="stop-color:#501500;stop-opacity:1" + offset="1" /> + gradientUnits="userSpaceOnUse" /> + gradientUnits="userSpaceOnUse" /> + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,83.310744,-32.476409)" + id="g3040"> + id="g6367" + style="stroke-width:1.04999995;stroke-miterlimit:4;stroke-dasharray:none"> + style="fill:url(#linearGradient6464);fill-opacity:1;stroke:url(#linearGradient6456);stroke-width:0.87096775;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> diff --git a/icons/hicolor/scalable/actions/cournal-pen-medium.svg b/icons/hicolor/scalable/actions/cournal-pen-medium.svg index b8587de..de0f4f2 100644 --- a/icons/hicolor/scalable/actions/cournal-pen-medium.svg +++ b/icons/hicolor/scalable/actions/cournal-pen-medium.svg @@ -8,34 +8,10 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="158.97142" height="158.97142" - id="svg2" - inkscape:version="0.48.3.1 r9886" - sodipodi:docname="cournal-pen-medium.svg"> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,22 +30,22 @@ id="linearGradient6341"> + stdDeviation="0.59409041" + id="feGaussianBlur3519" /> + stdDeviation="0.97623979" + id="feGaussianBlur3519-6" /> + stdDeviation="1.1714877" + id="feGaussianBlur3519-1" /> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,22 +30,22 @@ id="linearGradient6341"> + id="feGaussianBlur3519" + stdDeviation="0.59409041" /> + id="feGaussianBlur3519-6" + stdDeviation="0.97623979" /> + id="feGaussianBlur3519-1" + stdDeviation="1.1714877" /> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,33 +30,33 @@ id="linearGradient6178"> + id="feGaussianBlur3519" + stdDeviation="0.59409041" /> + id="feGaussianBlur3519-6" + stdDeviation="0.97623979" /> + id="feGaussianBlur3519-1" + stdDeviation="1.1714877" /> - + id="svg2"> @@ -44,7 +20,7 @@ image/svg+xml - + @@ -54,22 +30,22 @@ id="linearGradient5642"> + id="feGaussianBlur3519" + stdDeviation="0.59409041" /> + id="feGaussianBlur3519-6" + stdDeviation="0.97623979" /> + id="feGaussianBlur3519-1" + stdDeviation="1.1714877" /> Date: Thu, 13 Sep 2012 04:37:33 +0200 Subject: [PATCH 12/19] fixed inital tool selection bug --- cournal/mainwindow.glade | 2 -- 1 file changed, 2 deletions(-) diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index dba4a81..3753e20 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -538,7 +538,6 @@ Line True cournal-line-tool - True tool_pen @@ -557,7 +556,6 @@ Circle True cournal-circle-tool - True tool_pen From 7111c39fe7884d04dd4795ea3f47229188961721 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Fri, 14 Sep 2012 01:15:18 +0200 Subject: [PATCH 13/19] Item preview --- cournal/viewer/pagewidget.py | 5 +++++ cournal/viewer/tools/pen.py | 32 +++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/cournal/viewer/pagewidget.py b/cournal/viewer/pagewidget.py index 8877364..3d5b956 100644 --- a/cournal/viewer/pagewidget.py +++ b/cournal/viewer/pagewidget.py @@ -49,6 +49,7 @@ def __init__(self, page, parent, **args): self.backbuffer = None self.backbuffer_valid = True self.active_tool = None + self.preview_item = None self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | @@ -149,6 +150,10 @@ def draw(self, widget, context): context.set_source_surface(self.backbuffer, 0, 0) context.paint() + if self.preview_item: + context.scale(scaling, scaling) + self.preview_item.draw(context, scaling) + def press(self, widget, event): """ Mouse down event. Select a tool depending on the mouse button and call it. diff --git a/cournal/viewer/tools/pen.py b/cournal/viewer/tools/pen.py index 1c87bdb..bb36fba 100644 --- a/cournal/viewer/tools/pen.py +++ b/cournal/viewer/tools/pen.py @@ -57,19 +57,15 @@ def motion(widget, event): """ global _last_point, _current_coords, _current_stroke - r, g, b, opacity = color actualWidth = widget.get_allocation().width + + # item preview context = cairo.Context(widget.backbuffer) - - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - context.set_antialias(cairo.ANTIALIAS_GRAY) - context.set_line_cap(cairo.LINE_CAP_ROUND) context.set_line_width(linewidth*actualWidth/widget.page.width) - context.move_to(_last_point[0], _last_point[1]) context.line_to(event.x, event.y) x, y, x2, y2 = context.stroke_extents() - context.stroke() + widget.preview_item = _current_stroke update_rect = Gdk.Rectangle() update_rect.x = x-2 @@ -92,6 +88,28 @@ def release(widget, event): """ global _last_point, _current_coords, _current_stroke widget.page.finish_stroke(_current_stroke) + + # render stroke to backbuffer + widget.preview_item = None + r, g, b, opacity = color + actualWidth = widget.get_allocation().width + context = cairo.Context(widget.backbuffer) + context.set_source_rgba(r/255, g/255, b/255, opacity/255) + context.set_antialias(cairo.ANTIALIAS_GRAY) + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_line_width(linewidth*actualWidth/widget.page.width) + context.move_to(_current_coords[0][0]/widget.page.width*actualWidth, _current_coords[0][1]/widget.page.width*actualWidth) + for coord in _current_coords[1:]: + context.line_to(coord[0]/widget.page.width*actualWidth, coord[1]/widget.page.width*actualWidth) + context.line_to(event.x, event.y) + x, y, x2, y2 = context.stroke_extents() + context.stroke() + update_rect = Gdk.Rectangle() + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 + widget.get_window().invalidate_rect(update_rect, False) _last_point = None _current_coords = None From 24b7ac0ab486bda6560d807a01b561e516268240 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Fri, 14 Sep 2012 03:39:15 +0200 Subject: [PATCH 14/19] Tool item preview --- cournal/mainwindow.glade | 1 - cournal/mainwindow.py | 2 + cournal/viewer/tools/circle.py | 157 +++++++++++++++++++++++++-------- cournal/viewer/tools/line.py | 54 +++++++++++- cournal/viewer/tools/pen.py | 22 ++--- cournal/viewer/tools/rect.py | 102 +++++++++++++-------- 6 files changed, 247 insertions(+), 91 deletions(-) diff --git a/cournal/mainwindow.glade b/cournal/mainwindow.glade index 3753e20..e694efd 100644 --- a/cournal/mainwindow.glade +++ b/cournal/mainwindow.glade @@ -502,7 +502,6 @@ Pen True cournal-pen-tool - True False diff --git a/cournal/mainwindow.py b/cournal/mainwindow.py index 96adc35..d3d2fe7 100644 --- a/cournal/mainwindow.py +++ b/cournal/mainwindow.py @@ -107,6 +107,7 @@ def __init__(self, **args): self.actiongroup_document_specific = builder.get_object("actiongroup_document_specific") self.actiongroup_document_specific.set_sensitive(False) builder.get_object("tool_pensize_normal").set_active(True) + builder.get_object("tool_pen").set_active(True) # Workaround for bug https://bugzilla.gnome.org/show_bug.cgi?id=671786 if not Gtk.check_version(3,4,5) == None and not Gtk.check_version(3,6,0) == None: @@ -176,6 +177,7 @@ def __init__(self, **args): self.button_next_page.connect("clicked", self.jump_to_next_page) self.set_tool(pen) + #action_tool_pen.activate() history.init(action_undo, action_redo) diff --git a/cournal/viewer/tools/circle.py b/cournal/viewer/tools/circle.py index 27c08b1..c756389 100644 --- a/cournal/viewer/tools/circle.py +++ b/cournal/viewer/tools/circle.py @@ -25,14 +25,16 @@ import math """ -A pen tool. Draws a stroke with a certain color and size. +A circle tool. Draws a circle with certain colors and radius. """ _start_point = None +_last_point = None +_start_point_copy = [0,0] def press(widget, event): """ - Mouse down event. Draw a point on the pointer location. + Mouse down event. Keep the position Positional arguments: widget -- The PageWidget, which triggered the event @@ -42,7 +44,109 @@ def press(widget, event): _start_point = [event.x, event.y] def motion(widget, event): - pass + """ + Mouse motion event. Generate preview item and set render borders + + Positional arguments: see press() + """ + global _last_point, _start_point, start_point_copy + _end_point = [0,0] + _start_point_copy[0]=_start_point[0] + _start_point_copy[1]=_start_point[1] + + actualWidth = widget.get_allocation().width + + if _last_point: + # Sort Coords + if _last_point[0] < _start_point[0]: + _end_point[0] = _start_point[0] + _start_point[0] = _last_point[0] + else: + _end_point[0] = _last_point[0] + + if _last_point[1] < _start_point[1]: + _end_point[1] = _start_point[1] + _start_point[1] = _last_point[1] + else: + _end_point[1] = _last_point[1] + center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] + width = _end_point[0] - _start_point[0] + height = _end_point[1] - _start_point[1] + if height != 0 and width != 0: + scale = (width + height) / 2 + scale_width = width / scale + scale_height = height / scale + context = cairo.Context(widget.backbuffer) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + # Draw Circle and scale it to oval + context.translate(center[0], center[1]) + context.scale(scale_width / 2., scale_height / 2.) + context.arc(0., 0., scale, 0., 2 * math.pi) + context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) + context.translate(-center[0], -center[1]) + x, y, x2, y2 = context.stroke_extents() + update_rect = Gdk.Rectangle() + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + widget.get_window().invalidate_rect(update_rect, False) + else: + _last_point = [0,0] + _last_point[0] = event.x + _last_point[1] = event.y + + _start_point[0] = _start_point_copy[0] + _start_point[1] = _start_point_copy[1] + + # Sort Coords + if event.x < _start_point[0]: + _end_point[0] = _start_point[0] + _start_point[0] = event.x + else: + _end_point[0] = event.x + + if event.y < _start_point[1]: + _end_point[1] = _start_point[1] + _start_point[1] = event.y + else: + _end_point[1] = event.y + + center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] + width = _end_point[0] - _start_point[0] + height = _end_point[1] - _start_point[1] + if height == 0 or width == 0: + widget.preview_item = None + else: + scale = (width + height) / 2 + scale_width = width / scale + scale_height = height / scale + context = cairo.Context(widget.backbuffer) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + # Draw Circle and scale it to oval + context.translate(center[0], center[1]) + context.scale(scale_width / 2., scale_height / 2.) + context.arc(0., 0., scale, 0., 2 * math.pi) + context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) + context.translate(-center[0], -center[1]) + x, y, x2, y2 = context.stroke_extents() + update_rect = Gdk.Rectangle() + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + widget.get_window().invalidate_rect(update_rect, False) + widget.preview_item = Circle( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [center[0]*widget.page.width/actualWidth, center[1]*widget.page.width/actualWidth], + [width/2*widget.page.width/actualWidth, height/2*widget.page.width/actualWidth, scale*widget.page.width/actualWidth]) + + _start_point[0] = _start_point_copy[0] + _start_point[1] = _start_point_copy[1] def release(widget, event): """ @@ -54,6 +158,7 @@ def release(widget, event): Positional arguments: see press() """ global _start_point + widget.preview_item = None _end_point = [0,0] actualWidth = widget.get_allocation().width @@ -88,46 +193,20 @@ def release(widget, event): actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) - context.set_antialias(cairo.ANTIALIAS_GRAY) - context.set_line_cap(cairo.LINE_CAP_ROUND) context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - - # Fill circle - if primary.fill: - r, g, b, opacity = primary.fillcolor - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - # Draw Circle and scale it to oval - context.translate(center[0], center[1]) - context.scale(scale_width / 2., scale_height / 2.) - context.arc(0., 0., scale, 0., 2 * math.pi) - context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) - context.translate(-center[0], -center[1]) - context.fill() - - # border - r, g, b, opacity = primary.color - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - i = 0 - context.move_to(center[0], center[1] + height/2) - # We draw the border with strokes to keep konstant border width - while i < (math.pi * 2): - i += 0.1 - context.line_to( - center[0] + math.sin(i)*width/2, - center[1] + math.cos(i)*height/2) + # Draw Circle and scale it to oval + context.translate(center[0], center[1]) + context.scale(scale_width / 2., scale_height / 2.) + context.arc(0., 0., scale, 0., 2 * math.pi) + context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) + context.translate(-center[0], -center[1]) x, y, x2, y2 = context.stroke_extents() - context.stroke() - update_rect = Gdk.Rectangle() - update_rect.x = x-2 - update_rect.y = y-2 - update_rect.width = x2-x+4 - update_rect.height = y2-y+4 + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width widget.get_window().invalidate_rect(update_rect, False) - - #_current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) - - #widget.page.finish_stroke(_current_stroke) current_item = Circle( widget.page.layers[0], primary.color, diff --git a/cournal/viewer/tools/line.py b/cournal/viewer/tools/line.py index a46a79a..d5f3d9f 100644 --- a/cournal/viewer/tools/line.py +++ b/cournal/viewer/tools/line.py @@ -22,12 +22,14 @@ from cournal.viewer.tools import primary """ -A pen tool. Draws a stroke with a certain color and size. +A line tool. Draws a straight line with a certain color and size. """ _start_point = None _current_coords = None _current_stroke = None +_last_point = None +from cournal.document.stroke import Stroke def press(widget, event): """ @@ -49,7 +51,53 @@ def press(widget, event): widget.page.layers[0].items.append(_current_stroke) def motion(widget, event): - pass + """ + Mouse motion event. Generate preview item and set render borders + + Positional arguments: see press() + """ + global _last_point + + if _last_point: + # Bounding Box for last line + actualWidth = widget.get_allocation().width + context = cairo.Context(widget.backbuffer) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + context.move_to(_start_point[0], _start_point[1]) + context.line_to(_last_point[0], _last_point[1]) + x, y, x2, y2 = context.stroke_extents() + update_rect = Gdk.Rectangle() + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + widget.get_window().invalidate_rect(update_rect, False) + else: + _last_point = [0,0] + _last_point[0] = event.x + _last_point[1] = event.y + + # Bounding Box for new line + actualWidth = widget.get_allocation().width + context = cairo.Context(widget.backbuffer) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + context.move_to(_start_point[0], _start_point[1]) + context.line_to(event.x, event.y) + x, y, x2, y2 = context.stroke_extents() + update_rect = Gdk.Rectangle() + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + widget.get_window().invalidate_rect(update_rect, False) + + widget.preview_item = Stroke( + widget.page.layers[0], + primary.color, + primary.linewidth, + [[_start_point[0]*widget.page.width/actualWidth, _start_point[1]*widget.page.width/actualWidth], + [event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]] + ) def release(widget, event): """ @@ -62,6 +110,8 @@ def release(widget, event): """ global _start_point, _current_coords, _current_stroke + widget.preview_item = None + r, g, b, opacity = primary.color actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) diff --git a/cournal/viewer/tools/pen.py b/cournal/viewer/tools/pen.py index 32e544e..ce930dc 100644 --- a/cournal/viewer/tools/pen.py +++ b/cournal/viewer/tools/pen.py @@ -61,17 +61,17 @@ def motion(widget, event): # item preview context = cairo.Context(widget.backbuffer) - context.set_line_width(linewidth*actualWidth/widget.page.width) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) context.move_to(_last_point[0], _last_point[1]) context.line_to(event.x, event.y) x, y, x2, y2 = context.stroke_extents() widget.preview_item = _current_stroke update_rect = Gdk.Rectangle() - update_rect.x = x-2 - update_rect.y = y-2 - update_rect.width = x2-x+4 - update_rect.height = y2-y+4 + update_rect.x = x-5 + update_rect.y = y-5 + update_rect.width = x2-x+10 + update_rect.height = y2-y+10 widget.get_window().invalidate_rect(update_rect, False) _last_point = [event.x, event.y] @@ -91,13 +91,13 @@ def release(widget, event): # render stroke to backbuffer widget.preview_item = None - r, g, b, opacity = color + r, g, b, opacity = primary.color actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) context.set_source_rgba(r/255, g/255, b/255, opacity/255) context.set_antialias(cairo.ANTIALIAS_GRAY) context.set_line_cap(cairo.LINE_CAP_ROUND) - context.set_line_width(linewidth*actualWidth/widget.page.width) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) context.move_to(_current_coords[0][0]/widget.page.width*actualWidth, _current_coords[0][1]/widget.page.width*actualWidth) for coord in _current_coords[1:]: context.line_to(coord[0]/widget.page.width*actualWidth, coord[1]/widget.page.width*actualWidth) @@ -105,10 +105,10 @@ def release(widget, event): x, y, x2, y2 = context.stroke_extents() context.stroke() update_rect = Gdk.Rectangle() - update_rect.x = x-2 - update_rect.y = y-2 - update_rect.width = x2-x+4 - update_rect.height = y2-y+4 + update_rect.x = x-4 + update_rect.y = y-4 + update_rect.width = x2-x+8 + update_rect.height = y2-y+8 widget.get_window().invalidate_rect(update_rect, False) _last_point = None diff --git a/cournal/viewer/tools/rect.py b/cournal/viewer/tools/rect.py index 63019e8..e5c7777 100644 --- a/cournal/viewer/tools/rect.py +++ b/cournal/viewer/tools/rect.py @@ -26,6 +26,7 @@ _current_item = None _current_coords = None _start_point = None +_last_point = None def press(widget, event): """ @@ -47,56 +48,81 @@ def press(widget, event): _current_coords.append(event.y*widget.page.width/actualWidth) def motion(widget, event): - pass - -def release(widget, event): """ - Mouse release event. Inform the corresponding Page instance, that the stroke - is finished. - - This will cause the stroke to be sent to the server, if it is connected. + Mouse motion event. Generate preview item and set render borders Positional arguments: see press() """ - global _start_point, _current_coords, _current_item - actualWidth = widget.get_allocation().width + global _last_point + if _last_point: + # Bounding Box for last line + actualWidth = widget.get_allocation().width + context = cairo.Context(widget.backbuffer) + context.set_line_width(primary.linewidth*actualWidth/widget.page.width) + context.move_to(_start_point[0], _start_point[1]) + context.line_to(_last_point[0], _last_point[1]) + x, y, x2, y2 = context.stroke_extents() + update_rect = Gdk.Rectangle() + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + widget.get_window().invalidate_rect(update_rect, False) + widget.preview_item = Rect( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [_start_point[0]*widget.page.width/actualWidth, + _start_point[1]*widget.page.width/actualWidth, + event.x*widget.page.width/actualWidth, + event.y*widget.page.width/actualWidth] + ) + else: + _last_point = [0,0] + _last_point[0] = event.x + _last_point[1] = event.y + + actualWidth = widget.get_allocation().width context = cairo.Context(widget.backbuffer) - context.set_line_cap(cairo.LINE_CAP_ROUND) - context.set_line_join(cairo.LINE_JOIN_ROUND) context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - context.set_antialias(cairo.ANTIALIAS_GRAY) - - if primary.fill: - # Fill rect - context.move_to(_start_point[0], _start_point[1]) - context.line_to(_start_point[0], event.y) - context.line_to(event.x, event.y) - context.line_to(event.x, _start_point[1]) - context.close_path() - - r, g, b, opacity = primary.fillcolor - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - context.fill() - - # Draw border context.move_to(_start_point[0], _start_point[1]) - context.line_to(_start_point[0], event.y) context.line_to(event.x, event.y) - context.line_to(event.x, _start_point[1]) - context.close_path() - - r, g, b, opacity = primary.color - context.set_source_rgba(r/255, g/255, b/255, opacity/255) x, y, x2, y2 = context.stroke_extents() - context.stroke() - update_rect = Gdk.Rectangle() - update_rect.x = x-2 - update_rect.y = y-2 - update_rect.width = x2-x+4 - update_rect.height = y2-y+4 + update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width + update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width + update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width + update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width widget.get_window().invalidate_rect(update_rect, False) + widget.preview_item = Rect( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [_start_point[0]*widget.page.width/actualWidth, + _start_point[1]*widget.page.width/actualWidth, + event.x*widget.page.width/actualWidth, + event.y*widget.page.width/actualWidth] + ) + +def release(widget, event): + """ + Mouse release event. Inform the corresponding Page instance, that the stroke + is finished. + + This will cause the stroke to be sent to the server, if it is connected. + + Positional arguments: see press() + """ + + widget.preview_item = None + + global _start_point, _current_coords, _current_item + actualWidth = widget.get_allocation().width _current_coords.append(event.x*widget.page.width/actualWidth) _current_coords.append(event.y*widget.page.width/actualWidth) From 858b0901a5184c8b101299fa2e4db9b95ca021b9 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Fri, 14 Sep 2012 18:35:17 +0200 Subject: [PATCH 15/19] Better tool item preview --- cournal/document/circle.py | 3 + cournal/viewer/tools/circle.py | 187 ++++++++++----------------------- cournal/viewer/tools/line.py | 97 ++++++----------- cournal/viewer/tools/rect.py | 92 +++++++--------- 4 files changed, 133 insertions(+), 246 deletions(-) diff --git a/cournal/document/circle.py b/cournal/document/circle.py index 53534f8..a4149ee 100644 --- a/cournal/document/circle.py +++ b/cournal/document/circle.py @@ -86,6 +86,9 @@ def draw(self, context, scaling=1): 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 diff --git a/cournal/viewer/tools/circle.py b/cournal/viewer/tools/circle.py index c756389..0c33bdb 100644 --- a/cournal/viewer/tools/circle.py +++ b/cournal/viewer/tools/circle.py @@ -42,112 +42,64 @@ def press(widget, event): """ global _start_point _start_point = [event.x, event.y] + scaling = widget.backbuffer.get_width()/widget.page.width + widget.preview_item = Circle( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [event.x/scaling ,event.y/scaling], + [0,0,1]) def motion(widget, event): """ - Mouse motion event. Generate preview item and set render borders + Mouse motion event. Update item and set render borders Positional arguments: see press() """ - global _last_point, _start_point, start_point_copy - _end_point = [0,0] - _start_point_copy[0]=_start_point[0] - _start_point_copy[1]=_start_point[1] - - actualWidth = widget.get_allocation().width - - if _last_point: - # Sort Coords - if _last_point[0] < _start_point[0]: - _end_point[0] = _start_point[0] - _start_point[0] = _last_point[0] - else: - _end_point[0] = _last_point[0] - - if _last_point[1] < _start_point[1]: - _end_point[1] = _start_point[1] - _start_point[1] = _last_point[1] - else: - _end_point[1] = _last_point[1] - center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] - width = _end_point[0] - _start_point[0] - height = _end_point[1] - _start_point[1] - if height != 0 and width != 0: - scale = (width + height) / 2 - scale_width = width / scale - scale_height = height / scale - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - # Draw Circle and scale it to oval - context.translate(center[0], center[1]) - context.scale(scale_width / 2., scale_height / 2.) - context.arc(0., 0., scale, 0., 2 * math.pi) - context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) - context.translate(-center[0], -center[1]) - x, y, x2, y2 = context.stroke_extents() - update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width - widget.get_window().invalidate_rect(update_rect, False) - else: - _last_point = [0,0] - _last_point[0] = event.x - _last_point[1] = event.y - - _start_point[0] = _start_point_copy[0] - _start_point[1] = _start_point_copy[1] - - # Sort Coords - if event.x < _start_point[0]: - _end_point[0] = _start_point[0] - _start_point[0] = event.x - else: - _end_point[0] = event.x - - if event.y < _start_point[1]: - _end_point[1] = _start_point[1] - _start_point[1] = event.y - else: - _end_point[1] = event.y + global _last_point, _preview_copy - center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] - width = _end_point[0] - _start_point[0] - height = _end_point[1] - _start_point[1] - if height == 0 or width == 0: - widget.preview_item = None - else: - scale = (width + height) / 2 - scale_width = width / scale - scale_height = height / scale - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - # Draw Circle and scale it to oval - context.translate(center[0], center[1]) - context.scale(scale_width / 2., scale_height / 2.) - context.arc(0., 0., scale, 0., 2 * math.pi) - context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) - context.translate(-center[0], -center[1]) - x, y, x2, y2 = context.stroke_extents() + scaling = widget.backbuffer.get_width()/widget.page.width + if _last_point: update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + x = min(_last_point[0], _start_point[0]) - primary.linewidth*scaling/2 + y = min(_last_point[1], _start_point[1]) - primary.linewidth*scaling/2 + x2 = max(_last_point[0], _start_point[0]) + primary.linewidth*scaling/2 + y2 = max(_last_point[1], _start_point[1]) + primary.linewidth*scaling/2 + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - widget.preview_item = Circle( - widget.page.layers[0], - primary.color, - primary.fill, - primary.fillcolor, - primary.linewidth, - [center[0]*widget.page.width/actualWidth, center[1]*widget.page.width/actualWidth], - [width/2*widget.page.width/actualWidth, height/2*widget.page.width/actualWidth, scale*widget.page.width/actualWidth]) - - _start_point[0] = _start_point_copy[0] - _start_point[1] = _start_point_copy[1] - + _last_point = [event.x, event.y] + + update_rect = Gdk.Rectangle() + x = min(_start_point[0], event.x) - primary.linewidth*scaling/2 + y = min(_start_point[1], event.y) - primary.linewidth*scaling/2 + x2 = max(_start_point[0], event.x) + primary.linewidth*scaling/2 + y2 = max(_start_point[1], event.y) + primary.linewidth*scaling/2 + width = max(_start_point[0], event.x)-min(_start_point[0], event.x) + height = max(_start_point[1], event.y)-min(_start_point[1], event.y) + if width * height != 0: + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 + widget.get_window().invalidate_rect(update_rect, False) + widget.preview_item.coords = [ + (x+x2)/scaling/2, + (y+y2)/scaling/2] + scale = (width + height) / 2 + widget.preview_item.scale = [ + width/scaling/2, + height/scaling/2, + scale/scaling + ] + else: + widget.preview_item.scale = [0, 0, 1] + + def release(widget, event): """ Mouse release event. Inform the corresponding Page instance, that the stroke @@ -159,28 +111,20 @@ def release(widget, event): """ global _start_point widget.preview_item = None - _end_point = [0,0] actualWidth = widget.get_allocation().width # Sort Coords - if event.x < _start_point[0]: - _end_point[0] = _start_point[0] - _start_point[0] = event.x - else: - _end_point[0] = event.x - - if event.y < _start_point[1]: - _end_point[1] = _start_point[1] - _start_point[1] = event.y - else: - _end_point[1] = event.y + cx = min(_start_point[0], event.x) + cy = min(_start_point[1], event.y) + cx2 = max(_start_point[0], event.x) + cy2 = max(_start_point[1], event.y) # Calculate center - center = [(_end_point[0] + _start_point[0]) / 2, (_end_point[1] + _start_point[1]) / 2] + center = [(cx+cx2) / 2, (cy+cy2) / 2] # Calculate width and height - width = _end_point[0] - _start_point[0] - height = _end_point[1] - _start_point[1] + width = cx2-cx + height = cy2-cy # Stop it here if there's nothing to draw if height * width == 0: @@ -190,23 +134,6 @@ def release(widget, event): scale_width = width / scale scale_height = height / scale - actualWidth = widget.get_allocation().width - - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - # Draw Circle and scale it to oval - context.translate(center[0], center[1]) - context.scale(scale_width / 2., scale_height / 2.) - context.arc(0., 0., scale, 0., 2 * math.pi) - context.scale(1 / (scale_width / 2.), 1 / (scale_height / 2.)) - context.translate(-center[0], -center[1]) - x, y, x2, y2 = context.stroke_extents() - update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width - widget.get_window().invalidate_rect(update_rect, False) current_item = Circle( widget.page.layers[0], primary.color, @@ -219,3 +146,5 @@ def release(widget, event): history.register_draw_item(current_item, widget.page) _start_point = None + + \ No newline at end of file diff --git a/cournal/viewer/tools/line.py b/cournal/viewer/tools/line.py index d5f3d9f..4a49269 100644 --- a/cournal/viewer/tools/line.py +++ b/cournal/viewer/tools/line.py @@ -20,6 +20,7 @@ import cairo from gi.repository import Gdk from cournal.viewer.tools import primary +from cournal.document.stroke import Stroke """ A line tool. Draws a straight line with a certain color and size. @@ -29,7 +30,6 @@ _current_coords = None _current_stroke = None _last_point = None -from cournal.document.stroke import Stroke def press(widget, event): """ @@ -49,94 +49,65 @@ def press(widget, event): _start_point = [event.x, event.y] widget.page.layers[0].items.append(_current_stroke) - + widget.preview_item = Stroke( + widget.page.layers[0], + primary.color, + primary.linewidth, + [[_start_point[0]*widget.page.width/actualWidth, _start_point[1]*widget.page.width/actualWidth], + [_start_point[0]*widget.page.width/actualWidth, _start_point[1]*widget.page.width/actualWidth]] + ) + def motion(widget, event): """ - Mouse motion event. Generate preview item and set render borders + Mouse motion event. Update item and set render borders Positional arguments: see press() """ global _last_point + scaling = widget.backbuffer.get_width()/widget.page.width if _last_point: - # Bounding Box for last line - actualWidth = widget.get_allocation().width - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - context.move_to(_start_point[0], _start_point[1]) - context.line_to(_last_point[0], _last_point[1]) - x, y, x2, y2 = context.stroke_extents() update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + x = min(_last_point[0], _start_point[0]) - primary.linewidth*scaling/2 + y = min(_last_point[1], _start_point[1]) - primary.linewidth*scaling/2 + x2 = max(_last_point[0], _start_point[0]) + primary.linewidth*scaling/2 + y2 = max(_last_point[1], _start_point[1]) + primary.linewidth*scaling/2 + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - else: - _last_point = [0,0] - _last_point[0] = event.x - _last_point[1] = event.y + _last_point = [event.x, event.y] - # Bounding Box for new line - actualWidth = widget.get_allocation().width - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - context.move_to(_start_point[0], _start_point[1]) - context.line_to(event.x, event.y) - x, y, x2, y2 = context.stroke_extents() update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + x = min(_start_point[0], event.x) - primary.linewidth*scaling/2 + y = min(_start_point[1], event.y) - primary.linewidth*scaling/2 + x2 = max(_start_point[0], event.x) + primary.linewidth*scaling/2 + y2 = max(_start_point[1], event.y) + primary.linewidth*scaling/2 + + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - - widget.preview_item = Stroke( - widget.page.layers[0], - primary.color, - primary.linewidth, - [[_start_point[0]*widget.page.width/actualWidth, _start_point[1]*widget.page.width/actualWidth], - [event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]] - ) + widget.preview_item.coords[1] = [event.x/scaling, event.y/scaling] def release(widget, event): """ - Mouse release event. Inform the corresponding Page instance, that the stroke - is finished. + Mouse release event. - This will cause the stroke to be sent to the server, if it is connected. + This will cause the item to be sent to the server, if it is connected. Positional arguments: see press() """ + global _start_point, _current_coords, _current_stroke - - widget.preview_item = None - - r, g, b, opacity = primary.color actualWidth = widget.get_allocation().width - context = cairo.Context(widget.backbuffer) - - context.set_source_rgba(r/255, g/255, b/255, opacity/255) - context.set_antialias(cairo.ANTIALIAS_GRAY) - context.set_line_cap(cairo.LINE_CAP_ROUND) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - - context.move_to(_start_point[0], _start_point[1]) - context.line_to(event.x, event.y) - x, y, x2, y2 = context.stroke_extents() - context.stroke() - - update_rect = Gdk.Rectangle() - update_rect.x = x-2 - update_rect.y = y-2 - update_rect.width = x2-x+4 - update_rect.height = y2-y+4 - widget.get_window().invalidate_rect(update_rect, False) _current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) - widget.page.finish_stroke(_current_stroke) + widget.preview_item = None _start_point = None _current_coords = None _current_stroke = None diff --git a/cournal/viewer/tools/rect.py b/cournal/viewer/tools/rect.py index e5c7777..d663038 100644 --- a/cournal/viewer/tools/rect.py +++ b/cournal/viewer/tools/rect.py @@ -30,7 +30,7 @@ def press(widget, event): """ - Mouse down event. Set the start point for the rect + Mouse down event. Set the start point for the item Positional arguments: widget -- The PageWidget, which triggered the event @@ -46,80 +46,63 @@ def press(widget, event): _start_point = [event.x, event.y] _current_coords.append(event.x*widget.page.width/actualWidth) _current_coords.append(event.y*widget.page.width/actualWidth) + widget.preview_item = Rect( + widget.page.layers[0], + primary.color, + primary.fill, + primary.fillcolor, + primary.linewidth, + [_start_point[0]*widget.page.width/actualWidth, + _start_point[1]*widget.page.width/actualWidth, + _start_point[0]*widget.page.width/actualWidth, + _start_point[1]*widget.page.width/actualWidth] + ) def motion(widget, event): """ - Mouse motion event. Generate preview item and set render borders + Mouse motion event. Update item and set render borders Positional arguments: see press() """ global _last_point + scaling = widget.backbuffer.get_width()/widget.page.width if _last_point: - # Bounding Box for last line - actualWidth = widget.get_allocation().width - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - context.move_to(_start_point[0], _start_point[1]) - context.line_to(_last_point[0], _last_point[1]) - x, y, x2, y2 = context.stroke_extents() update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + x = min(_last_point[0], _start_point[0]) - primary.linewidth*scaling/2 + y = min(_last_point[1], _start_point[1]) - primary.linewidth*scaling/2 + x2 = max(_last_point[0], _start_point[0]) + primary.linewidth*scaling/2 + y2 = max(_last_point[1], _start_point[1]) + primary.linewidth*scaling/2 + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - widget.preview_item = Rect( - widget.page.layers[0], - primary.color, - primary.fill, - primary.fillcolor, - primary.linewidth, - [_start_point[0]*widget.page.width/actualWidth, - _start_point[1]*widget.page.width/actualWidth, - event.x*widget.page.width/actualWidth, - event.y*widget.page.width/actualWidth] - ) - else: - _last_point = [0,0] - _last_point[0] = event.x - _last_point[1] = event.y - - actualWidth = widget.get_allocation().width - context = cairo.Context(widget.backbuffer) - context.set_line_width(primary.linewidth*actualWidth/widget.page.width) - context.move_to(_start_point[0], _start_point[1]) - context.line_to(event.x, event.y) - x, y, x2, y2 = context.stroke_extents() + _last_point = [event.x, event.y] + update_rect = Gdk.Rectangle() - update_rect.x = x-2*primary.linewidth*actualWidth/widget.page.width - update_rect.y = y-2*primary.linewidth*actualWidth/widget.page.width - update_rect.width = x2-x+4*primary.linewidth*actualWidth/widget.page.width - update_rect.height = y2-y+4*primary.linewidth*actualWidth/widget.page.width + x = min(_start_point[0], event.x) - primary.linewidth*scaling/2 + y = min(_start_point[1], event.y) - primary.linewidth*scaling/2 + x2 = max(_start_point[0], event.x) + primary.linewidth*scaling/2 + y2 = max(_start_point[1], event.y) + primary.linewidth*scaling/2 + + update_rect.x = x-2 + update_rect.y = y-2 + update_rect.width = x2-x+4 + update_rect.height = y2-y+4 widget.get_window().invalidate_rect(update_rect, False) - widget.preview_item = Rect( - widget.page.layers[0], - primary.color, - primary.fill, - primary.fillcolor, - primary.linewidth, - [_start_point[0]*widget.page.width/actualWidth, - _start_point[1]*widget.page.width/actualWidth, - event.x*widget.page.width/actualWidth, - event.y*widget.page.width/actualWidth] - ) + widget.preview_item.coords[2] = event.x/scaling + widget.preview_item.coords[3] = event.y/scaling def release(widget, event): """ - Mouse release event. Inform the corresponding Page instance, that the stroke - is finished. + Mouse release event. - This will cause the stroke to be sent to the server, if it is connected. + This will cause the item to be sent to the server, if it is connected. Positional arguments: see press() """ - widget.preview_item = None global _start_point, _current_coords, _current_item actualWidth = widget.get_allocation().width @@ -129,6 +112,7 @@ def release(widget, event): widget.page.new_item(_current_item, send_to_network=True) history.register_draw_item(_current_item, widget.page) + widget.preview_item = None _start_point = None _current_coords = None _current_item = None From 505eccba4acfda6f9cae1e19377567b2f9255def Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Fri, 14 Sep 2012 19:00:57 +0200 Subject: [PATCH 16/19] Fixed both mouse button bug --- ChangeLog | 3 ++- TODO | 4 ++-- cournal/viewer/pagewidget.py | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 35791bd..d84f1e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,7 +8,8 @@ Version 0.3 -- not yet released * Fill color * Tool bar with icons * Keyboard shortcuts - + * Tool item preview + 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 diff --git a/TODO b/TODO index c2ccde9..437581a 100644 --- a/TODO +++ b/TODO @@ -14,9 +14,9 @@ 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 -* Show tools preview * Item deletion functions * Error handling * Change Pickle to JSON * Make fill color chooser more discreet -* Set sensitivity for not incompatible actions \ No newline at end of file +* Set sensitivity for not incompatible actions +* Print \ No newline at end of file diff --git a/cournal/viewer/pagewidget.py b/cournal/viewer/pagewidget.py index 8cfa797..22e7fa4 100644 --- a/cournal/viewer/pagewidget.py +++ b/cournal/viewer/pagewidget.py @@ -166,8 +166,12 @@ def press(self, widget, event): if event.button == 1: self.active_tool = primary.current_tool elif event.button == 2: + if self.preview_item: + self.active_tool.release(self, event) self.active_tool = navigation elif event.button == 3: + if self.preview_item: + self.active_tool.release(self, event) self.active_tool = eraser else: return From 9bdcf6af6f29c22402aabb83ff3a7e7c383800ee Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Fri, 14 Sep 2012 19:33:11 +0200 Subject: [PATCH 17/19] fix dissapearing lines bug --- cournal/viewer/tools/line.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cournal/viewer/tools/line.py b/cournal/viewer/tools/line.py index 4a49269..97fd3a0 100644 --- a/cournal/viewer/tools/line.py +++ b/cournal/viewer/tools/line.py @@ -102,10 +102,16 @@ def release(widget, event): """ global _start_point, _current_coords, _current_stroke - actualWidth = widget.get_allocation().width + scaling = widget.backbuffer.get_width()/widget.page.width - _current_coords.append([event.x*widget.page.width/actualWidth, event.y*widget.page.width/actualWidth]) + actualWidth = widget.get_allocation().width + _current_coords.append([event.x/scaling, event.y/scaling]) widget.page.finish_stroke(_current_stroke) + + context = cairo.Context(widget.backbuffer) + context.scale(scaling, scaling) + _current_stroke.draw(context, scaling) + widget.preview_item = None _start_point = None From 25de28b970c9bff2db7ad608318dff6af8002885 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Sun, 16 Sep 2012 23:20:34 +0200 Subject: [PATCH 18/19] New item deletion functions --- TODO | 1 - cournal/document/circle.py | 10 ++++++---- cournal/document/page.py | 15 ++------------- cournal/document/rect.py | 7 +++++-- cournal/document/stroke.py | 34 +++++++++++++++++++++------------- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/TODO b/TODO index 437581a..af1e9d2 100644 --- a/TODO +++ b/TODO @@ -14,7 +14,6 @@ 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 -* Item deletion functions * Error handling * Change Pickle to JSON * Make fill color chooser more discreet diff --git a/cournal/document/circle.py b/cournal/document/circle.py index a4149ee..f48d7b9 100644 --- a/cournal/document/circle.py +++ b/cournal/document/circle.py @@ -43,7 +43,7 @@ def __init__(self, layer, color, fill, fill_color, linewidth, coords, scale): self.coords = coords self.scale = scale - def in_bounds(self, x, y): + def in_bounds(self, x, y, radius): """ Test if point is in bounding box of the stroke. @@ -53,9 +53,11 @@ def in_bounds(self, x, y): Returns: true, if point is in bounding box """ - radius = (self.coords[0] - x)**2 + (self.coords[1] - y)**2 - if radius < max(self.scale[0], self.scale[1])**2: - # TODO Improve + 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 diff --git a/cournal/document/page.py b/cournal/document/page.py index edb0a96..92c24ca 100644 --- a/cournal/document/page.py +++ b/cournal/document/page.py @@ -138,17 +138,6 @@ def get_items_near(self, x, y, radius): Return value: Generator for a list of all objects, which are near that point """ - #TODO: Create tool dependent delete if coord match function for item in self.layers[0].items[:]: - if item.in_bounds(x, y): - if isinstance(item, Stroke): - for coord in item.coords: - s_x = coord[0] - s_y = coord[1] - if ((s_x-x)**2 + (s_y-y)**2) < radius**2: - yield item - break - elif isinstance(item, Rect): - yield item - elif isinstance(item, Circle): - yield item + if item.in_bounds(x, y, radius): + yield item diff --git a/cournal/document/rect.py b/cournal/document/rect.py index d6aa073..3d68078 100644 --- a/cournal/document/rect.py +++ b/cournal/document/rect.py @@ -43,7 +43,7 @@ def __init__(self, layer, color, fill, fill_color, linewidth, coords=None): if self.coords is None: self.coords = [] - def in_bounds(self, x, y): + def in_bounds(self, x, y, radius): """ Test if point is in bounding box of the stroke. @@ -66,7 +66,10 @@ def in_bounds(self, x, y): s_y = self.coords[3] e_y = self.coords[1] - if x > s_x and x < e_x and y > s_y and y < e_y: + if x > s_x-radius and x < e_x+radius and y > s_y-radius and y < e_y+radius: + if not self.fill: + if x > s_x+radius and x < e_x-radius and y > s_y+radius and y < e_y-radius: + return False return True else: return False diff --git a/cournal/document/stroke.py b/cournal/document/stroke.py index 61feba9..c4b6238 100644 --- a/cournal/document/stroke.py +++ b/cournal/document/stroke.py @@ -47,8 +47,9 @@ def __init__(self, layer, color, linewidth, coords=None): self.coords = coords if self.coords is None: self.coords = [] - - def in_bounds(self, x, y): + self.bound_min = None + + def in_bounds(self, x, y, radius): """ Test if point is in bounding box of the stroke. @@ -58,17 +59,24 @@ def in_bounds(self, x, y): Returns: true, if point is in bounding box """ - try: - if x > self.bound_min[0] and x < self.bound_max[0] and y > self.bound_min[1] and y < self.bound_max[1]: - return True - else: - return False - except: - self.calculate_bounding_box() - if x > self.bound_min[0] and x < self.bound_max[0] and y > self.bound_min[1] and y < self.bound_max[1]: - return True - else: - return False + if not self.bound_min: + self.calculate_bounding_box(radius) + + if x > self.bound_min[0] and x < self.bound_max[0] and y > self.bound_min[1] and y < self.bound_max[1]: + for coord in self.coords: + s_x = coord[0] + s_y = coord[1] + if ((s_x-x)**2 + (s_y-y)**2) < radius**2: + return True + if len(self.coords) == 2: # line + dx1 = (x - self.coords[0][0]) + dy1 = (y - self.coords[0][1]) + dx2 = (self.coords[1][0] - self.coords[0][0]) + dy2 = (self.coords[1][1] - self.coords[0][1]) + if (dy1 != 0 and dy2 != 0): + return (abs(abs(dx1/dy1) - abs(dx2/dy2)) < 0.02) + else: + return False def calculate_bounding_box(self, radius=5): """ From fc88d4769ffb9e07ae0b4ead1763a483f3e23f52 Mon Sep 17 00:00:00 2001 From: Simon Vetter Date: Sun, 16 Sep 2012 23:26:38 +0200 Subject: [PATCH 19/19] Fixed bug: Drawing after connecting, but without choosing document caused crash. --- cournal/network.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cournal/network.py b/cournal/network.py index 9245a6e..3f43bfd 100644 --- a/cournal/network.py +++ b/cournal/network.py @@ -48,7 +48,7 @@ def __init__(self): pb.Referenceable.__init__(self) self.document = None self.window = None - self.is_connected = False + self.is_connected = 0 self.is_stalled = True self.last_data_received = 0 self.watchdog = None @@ -102,7 +102,7 @@ def connected(self, perspective): # here, otherwise it will get garbage collected at the end of this # function and the server will think we logged out. self.is_stalled = False - self.is_connected = True + self.is_connected = 1 self.perspective = perspective self.perspective.notifyOnDisconnect(self.disconnect_event) self.data_received() @@ -117,7 +117,7 @@ def connection_failed(self, reason): reason -- A twisted Failure object with the reason the connection failed """ debug(0, _("Connection failed due to: {}").format(reason.getErrorMessage())) - self.is_connected = False + self.is_connected = 0 return reason @@ -130,7 +130,7 @@ def disconnect(self, _=None): def disconnect_event(self, event): """Called, when the client gets disconnected from the server.""" - self.is_connected = False + self.is_connected = 0 self.connection_problems() if self.window: self.window.disconnect_event() @@ -180,6 +180,7 @@ def got_server_document(self, server_document, name): """ self.data_received() debug(2, _("Started editing {}").format(name)) + self.is_connected = 2 self.server_document = server_document def remote_new_item(self, pagenum, item): @@ -202,7 +203,7 @@ def new_item(self, pagenum, item): pagenum -- On which page the item was added item -- The Stroke item to send """ - if self.is_connected: + if self.is_connected > 1: d = self.server_document.callRemote("new_item", pagenum, item) d.addCallbacks(lambda x: self.data_received(), self.disconnect) @@ -226,7 +227,7 @@ def delete_item_with_coords(self, pagenum, coords): pagenum -- On which page the item was deleted coords -- The list of coordinates identifying the item """ - if self.is_connected: + if self.is_connected > 1: d = self.server_document.callRemote("delete_item_with_coords", pagenum, coords) d.addCallback(lambda x,y: self.data_received(), self.disconnect)