mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Edit Book: Live CSS: Allow copying of CSS rules by right clicking on the Live CSS panel. Fixes #1485237 [[Enhancement] Make info in Live CSS window copiable](https://bugs.launchpad.net/calibre/+bug/1485237)
This commit is contained in:
parent
f945643acc
commit
cecc7efb8a
@ -11,7 +11,7 @@ import json
|
||||
from PyQt5.Qt import (
|
||||
QWidget, QTimer, QStackedLayout, QLabel, QScrollArea, QVBoxLayout,
|
||||
QPainter, Qt, QPalette, QRect, QSize, QSizePolicy, pyqtSignal,
|
||||
QColor)
|
||||
QColor, QMenu, QApplication, QIcon)
|
||||
|
||||
from calibre.constants import iswindows
|
||||
from calibre.gui2.tweak_book import editors, actions, current_container, tprefs
|
||||
@ -22,6 +22,7 @@ from css_selectors import parse, SelectorError
|
||||
class Heading(QWidget): # {{{
|
||||
|
||||
toggled = pyqtSignal(object)
|
||||
context_menu_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, text, expanded=True, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
@ -32,6 +33,10 @@ class Heading(QWidget): # {{{
|
||||
self.hovering = False
|
||||
self.do_layout()
|
||||
|
||||
@property
|
||||
def lines_for_copy(self):
|
||||
return [self.text]
|
||||
|
||||
def do_layout(self):
|
||||
try:
|
||||
f = self.parent().font()
|
||||
@ -79,6 +84,9 @@ class Heading(QWidget): # {{{
|
||||
self.hovering = False
|
||||
self.update()
|
||||
return QWidget.leaveEvent(self, ev)
|
||||
|
||||
def contextMenuEvent(self, ev):
|
||||
self.context_menu_requested.emit(self, ev)
|
||||
# }}}
|
||||
|
||||
class Cell(object): # {{{
|
||||
@ -117,6 +125,7 @@ class Cell(object): # {{{
|
||||
class Declaration(QWidget):
|
||||
|
||||
hyperlink_activated = pyqtSignal(object)
|
||||
context_menu_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, html_name, data, is_first=False, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
@ -124,6 +133,7 @@ class Declaration(QWidget):
|
||||
self.data = data
|
||||
self.is_first = is_first
|
||||
self.html_name = html_name
|
||||
self.lines_for_copy = []
|
||||
self.do_layout()
|
||||
self.setMouseTracking(True)
|
||||
|
||||
@ -149,6 +159,7 @@ class Declaration(QWidget):
|
||||
Cell(sel, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), right_align=True)
|
||||
])
|
||||
ypos += max(br1.height(), br2.height()) + 2 * line_spacing
|
||||
self.lines_for_copy.append(name + ' ' + sel)
|
||||
|
||||
for prop in self.data['properties']:
|
||||
text = prop.name + ':\xa0'
|
||||
@ -159,6 +170,7 @@ class Declaration(QWidget):
|
||||
Cell(text, QRect(side_margin, ypos, br1.width(), br1.height()), color_role=QPalette.LinkVisited, is_overriden=prop.is_overriden),
|
||||
Cell(vtext, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), swatch=prop.color, is_overriden=prop.is_overriden)
|
||||
])
|
||||
self.lines_for_copy.append(text + vtext)
|
||||
ypos += max(br1.height(), br2.height()) + line_spacing
|
||||
|
||||
self.height_hint = ypos + line_spacing
|
||||
@ -204,7 +216,7 @@ class Declaration(QWidget):
|
||||
return QWidget.mouseMoveEvent(self, ev)
|
||||
|
||||
def mousePressEvent(self, ev):
|
||||
if hasattr(self, 'hyperlink_rect'):
|
||||
if hasattr(self, 'hyperlink_rect') and ev.button() == Qt.LeftButton:
|
||||
pos = ev.pos()
|
||||
if self.hyperlink_rect.contains(pos):
|
||||
self.emit_hyperlink_activated()
|
||||
@ -236,6 +248,9 @@ class Declaration(QWidget):
|
||||
cell.override_color = QColor(Qt.red) if hovering else None
|
||||
self.update()
|
||||
|
||||
def contextMenuEvent(self, ev):
|
||||
self.context_menu_requested.emit(self, ev)
|
||||
|
||||
class Box(QWidget):
|
||||
|
||||
hyperlink_activated = pyqtSignal(object)
|
||||
@ -250,7 +265,7 @@ class Box(QWidget):
|
||||
def show_data(self, data):
|
||||
for w in self.widgets:
|
||||
self.layout().removeWidget(w)
|
||||
for x in ('toggled', 'hyperlink_activated'):
|
||||
for x in ('toggled', 'hyperlink_activated', 'context_menu_requested'):
|
||||
if hasattr(w, x):
|
||||
try:
|
||||
getattr(w, x).disconnect()
|
||||
@ -279,6 +294,8 @@ class Box(QWidget):
|
||||
declaration = {'properties':[Property([k, ccss[k][0], '', ccss[k][1]]) for k in sorted(ccss)]}
|
||||
d = Declaration(None, declaration, is_first=True, parent=self)
|
||||
self.widgets.append(d), self.layout().addWidget(d)
|
||||
for w in self.widgets:
|
||||
w.context_menu_requested.connect(self.context_menu_requested)
|
||||
|
||||
def heading_toggled(self, heading):
|
||||
for i, w in enumerate(self.widgets):
|
||||
@ -294,6 +311,49 @@ class Box(QWidget):
|
||||
w.do_layout()
|
||||
w.updateGeometry()
|
||||
|
||||
@property
|
||||
def lines_for_copy(self):
|
||||
ans = []
|
||||
for w in self.widgets:
|
||||
ans += w.lines_for_copy
|
||||
return ans
|
||||
|
||||
def context_menu_requested(self, widget, ev):
|
||||
if isinstance(widget, Heading):
|
||||
start = widget
|
||||
else:
|
||||
found = False
|
||||
for w in reversed(self.widgets):
|
||||
if w is widget:
|
||||
found = True
|
||||
elif found and isinstance(w, Heading):
|
||||
start = w
|
||||
break
|
||||
else:
|
||||
return
|
||||
found = False
|
||||
lines = []
|
||||
for w in self.widgets:
|
||||
if found and isinstance(w, Heading):
|
||||
break
|
||||
if w is start:
|
||||
found = True
|
||||
if found:
|
||||
lines += w.lines_for_copy
|
||||
if not lines:
|
||||
return
|
||||
block = '\n'.join(lines).replace('\xa0', ' ')
|
||||
heading = lines[0]
|
||||
m = QMenu(self)
|
||||
m.addAction(QIcon(I('edit-copy.png')), _('Copy') + ' ' + heading.replace('\xa0', ' '), lambda : QApplication.instance().clipboard().setText(block))
|
||||
all_lines = []
|
||||
for w in self.widgets:
|
||||
all_lines += w.lines_for_copy
|
||||
all_text = '\n'.join(all_lines).replace('\xa0', ' ')
|
||||
m.addAction(QIcon(I('edit-copy.png')), _('Copy everything'), lambda : QApplication.instance().clipboard().setText(all_text))
|
||||
m.exec_(ev.globalPos())
|
||||
|
||||
|
||||
class Property(object):
|
||||
|
||||
__slots__ = 'name', 'value', 'important', 'color', 'specificity', 'is_overriden'
|
||||
|
Loading…
x
Reference in New Issue
Block a user