# -*- coding: utf-8 -*- """ $Id$ Copyright 2011 Lars Kruse This file is part of PyCAM. PyCAM 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. PyCAM 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 PyCAM. If not, see . """ import StringIO # imported later (on demand) #import gtk import pycam.Plugins from pycam.Utils.locations import get_all_program_locations CLIPBOARD_TARGETS = { "dxf": ("image/vnd.dxf", ), "ps": ("application/postscript", ), "stl": ("application/sla", ), "svg": ("image/x-inkscape-svg", "image/svg+xml"), } class Clipboard(pycam.Plugins.PluginBase): UI_FILE = "clipboard.ui" DEPENDS = ["Models"] CATEGORIES = ["System"] def setup(self): if self.gui: import gtk self._gtk = gtk self._gtk_handlers = [] self.clipboard = self._gtk.clipboard_get() self.core.set("clipboard-set", self._copy_text_to_clipboard) self._gtk_handlers.append((self.clipboard, "owner-change", self._update_clipboard_widget)) # menu item and shortcut self.copy_action = self.gui.get_object("CopyModelToClipboard") self._gtk_handlers.append((self.copy_action, "activate", self.copy_model_to_clipboard)) self.register_gtk_accelerator("clipboard", self.copy_action, "c", "CopyModelToClipboard") self.core.register_ui("edit_menu", "CopyModelToClipboard", self.copy_action, 20) self.paste_action = self.gui.get_object("PasteModelFromClipboard") self._gtk_handlers.append((self.paste_action, "activate", self.paste_model_from_clipboard)) self.register_gtk_accelerator("clipboard", self.paste_action, "v", "PasteModelFromClipboard") self.core.register_ui("edit_menu", "PasteModelFromClipboard", self.paste_action, 25) self._event_handlers = (("model-selection-changed", self._update_clipboard_widget), ) self.register_event_handlers(self._event_handlers) self.register_gtk_handlers(self._gtk_handlers) self._update_clipboard_widget() return True def teardown(self): if self.gui: self.unregister_gtk_accelerator("clipboard", self.copy_action) self.core.unregister_ui("edit_menu", self.copy_action) self.unregister_gtk_accelerator("clipboard", self.paste_action) self.core.unregister_ui("edit_menu", self.paste_action) self.unregister_event_handlers(self._event_handlers) self.unregister_gtk_handlers(self._gtk_handlers) self.core.set("clipboard-set", None) def _get_exportable_models(self): models = self.core.get("models").get_selected() exportable = [] for model in models: if model.model.is_export_supported(): exportable.append(model) return exportable def _update_clipboard_widget(self, widget=None, data=None): models = self._get_exportable_models() # copy button self.gui.get_object("CopyModelToClipboard").set_sensitive( len(models) > 0) data, importer = self._get_data_and_importer_from_clipboard() paste_button = self.gui.get_object("PasteModelFromClipboard") paste_button.set_sensitive(not data is None) def _copy_text_to_clipboard(self, text, targets=None): if targets is None: self.clipboard.set_text(text) else: if targets in CLIPBOARD_TARGETS: targets = CLIPBOARD_TARGETS[targets] clip_targets = [(key, self._gtk.TARGET_OTHER_WIDGET, index) for index, key in enumerate(targets)] def get_func(clipboard, selectiondata, info, (text, clip_type)): selectiondata.set(clip_type, 8, text) if "svg" in "".join(targets).lower(): # Inkscape for Windows strictly requires the BITMAP type clip_type = self._gtk.gdk.SELECTION_TYPE_BITMAP else: clip_type = self._gtk.gdk.SELECTION_TYPE_STRING result = self.clipboard.set_with_data(clip_targets, get_func, lambda *args: None, (text, clip_type)) self.clipboard.store() def copy_model_to_clipboard(self, widget=None): models = self._get_exportable_models() if not models: return text_buffer = StringIO.StringIO() # TODO: use a better way to discover the "merge" ability def same_type(m1, m2): return isinstance(m1, pycam.Geometry.Model.ContourModel) == \ isinstance(m2, pycam.Geometry.Model.ContourModel) merged_model = models.pop(0).model for model in models: # merge only 3D _or_ 2D models (don't mix them) if same_type(merged_model, model.model): merged_model += model.model # TODO: add "comment=get_meta_data()" here merged_model.export(unit=self.core.get("unit")).write(text_buffer) text_buffer.seek(0) is_contour = isinstance(merged_model, pycam.Geometry.Model.ContourModel) # TODO: this should not be decided here if is_contour: targets = CLIPBOARD_TARGETS["svg"] else: targets = CLIPBOARD_TARGETS["stl"] self._copy_text_to_clipboard(text_buffer.read(), targets) def _get_data_and_importer_from_clipboard(self): for targets, filename in ((CLIPBOARD_TARGETS["svg"], "foo.svg"), (CLIPBOARD_TARGETS["stl"], "foo.stl"), (CLIPBOARD_TARGETS["ps"], "foo.ps"), (CLIPBOARD_TARGETS["dxf"], "foo.dxf")): for target in targets: data = self.clipboard.wait_for_contents(target) if not data is None: importer = pycam.Importers.detect_file_type(filename)[1] return data, importer return None, None def paste_model_from_clipboard(self, widget=None): data, importer = self._get_data_and_importer_from_clipboard() progress = self.core.get("progress") if data: progress.update(text="Loading model from clipboard") text_buffer = StringIO.StringIO(data.data) model = importer(text_buffer, program_locations=get_all_program_locations(self.core), unit=self.core.get("unit"), fonts_cache=self.core.get("fonts"), callback=progress.update) if model: models = self.core.get("models") self.log.info("Loaded a model from clipboard") models.add_model(model, name_template="Pasted model #%d") else: self.log.warn("Failed to load a model from clipboard") else: self.log.warn("The clipboard does not contain suitable data") progress.finish()