diff --git a/freecad/silo_commands.py b/freecad/silo_commands.py index 7aac574..6fa500a 100644 --- a/freecad/silo_commands.py +++ b/freecad/silo_commands.py @@ -2785,7 +2785,7 @@ class SiloAuthDockWidget: def _refresh_activity_panel(self): """Refresh the Database Activity panel if it exists.""" - from PySide import QtWidgets + from PySide import QtCore, QtGui, QtWidgets mw = FreeCADGui.getMainWindow() if mw is None: @@ -2797,6 +2797,24 @@ class SiloAuthDockWidget: if activity_list is None: return activity_list.clear() + + # Connect interaction signals (once) + if not getattr(activity_list, "_silo_connected", False): + activity_list.itemDoubleClicked.connect(self._on_activity_double_click) + activity_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + activity_list.customContextMenuRequested.connect( + lambda pos: self._on_activity_context_menu(activity_list, pos) + ) + activity_list._silo_connected = True + + # Collect local part numbers for badge + local_pns = set() + try: + for lf in search_local_files(): + local_pns.add(lf.get("part_number", "")) + except Exception: + pass + try: items = _client.list_items() if isinstance(items, list): @@ -2806,12 +2824,96 @@ class SiloAuthDockWidget: updated = item.get("updated_at", "") if updated: updated = updated[:10] - activity_list.addItem(f"{pn} - {desc} - {updated}") + + # Fetch latest revision info + rev_num = "" + comment = "" + try: + revs = _client.get_revisions(pn) + if revs: + latest = revs[0] if isinstance(revs, list) else revs + rev_num = str(latest.get("revision_number", "")) + comment = latest.get("comment", "") or "" + except Exception: + pass + + # Truncate long descriptions + desc_display = desc + if len(desc_display) > 40: + desc_display = desc_display[:37] + "..." + + # Build display text + rev_part = f" \u2013 Rev {rev_num}" if rev_num else "" + date_part = f" \u2013 {updated}" if updated else "" + local_badge = " \u25cf local" if pn in local_pns else "" + line1 = ( + f"{pn} \u2013 {desc_display}{rev_part}{date_part}{local_badge}" + ) + + if comment: + line1 += f'\n "{comment}"' + else: + line1 += "\n (no comment)" + + list_item = QtWidgets.QListWidgetItem(line1) + list_item.setData(QtCore.Qt.UserRole, pn) + if desc and len(desc) > 40: + list_item.setToolTip(desc) + if pn in local_pns: + list_item.setForeground(QtGui.QColor("#4CAF50")) + activity_list.addItem(list_item) + if activity_list.count() == 0: activity_list.addItem("(No items in database)") except Exception: activity_list.addItem("(Unable to refresh activity)") + def _on_activity_double_click(self, item): + """Open/checkout item from activity pane.""" + pn = item.data(256) # Qt.UserRole + if not pn: + return + local_path = find_file_by_part_number(pn) + if local_path and local_path.exists(): + FreeCAD.openDocument(str(local_path)) + else: + _sync.open_item(pn) + + def _on_activity_context_menu(self, activity_list, pos): + """Show context menu for activity pane items.""" + from PySide import QtCore, QtGui + + item = activity_list.itemAt(pos) + if item is None: + return + pn = item.data(QtCore.Qt.UserRole) + if not pn: + return + + menu = QtGui.QMenu() + open_action = menu.addAction("Open in Create") + browser_action = menu.addAction("Open in Browser") + copy_action = menu.addAction("Copy Part Number") + revisions_action = menu.addAction("View Revisions") + + action = menu.exec_(activity_list.mapToGlobal(pos)) + if action == open_action: + local_path = find_file_by_part_number(pn) + if local_path and local_path.exists(): + FreeCAD.openDocument(str(local_path)) + else: + _sync.open_item(pn) + elif action == browser_action: + import webbrowser + + api_url = _get_api_url().rstrip("/") + base_url = api_url[:-4] if api_url.endswith("/api") else api_url + webbrowser.open(f"{base_url}/items/{pn}") + elif action == copy_action: + QtGui.QApplication.clipboard().setText(pn) + elif action == revisions_action: + FreeCADGui.runCommand("Silo_Info", 0) + # -- Actions ------------------------------------------------------------ def _on_login_clicked(self):