clapp-pm 1.0.44__py3-none-any.whl → 1.0.45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1370 +0,0 @@
1
- """
2
- PyCloud OS Rain Context Menu
3
- Masaüstü, dosya yöneticisi ve uygulama ikonları için dinamik sağ tık menü sistemi
4
- """
5
-
6
- import os
7
- import logging
8
- from pathlib import Path
9
- from typing import Dict, List, Optional, Any, Callable, Union
10
- from dataclasses import dataclass
11
- from enum import Enum
12
- import json
13
-
14
- try:
15
- from PyQt6.QtWidgets import (QMenu, QAction, QWidget, QApplication,
16
- QMessageBox, QInputDialog, QFileDialog)
17
- from PyQt6.QtCore import Qt, QPoint, pyqtSignal, QObject
18
- from PyQt6.QtGui import QIcon, QPixmap, QKeySequence
19
- PYQT_AVAILABLE = True
20
- except ImportError:
21
- # Dummy classes for when PyQt6 is not available
22
- class QMenu:
23
- def __init__(self, *args, **kwargs): pass
24
- def addAction(self, *args, **kwargs): return QAction()
25
- def addSeparator(self): pass
26
- def addMenu(self, *args, **kwargs): return QMenu()
27
- def exec(self, *args, **kwargs): return None
28
- def setStyleSheet(self, *args, **kwargs): pass
29
-
30
- class QAction:
31
- def __init__(self, *args, **kwargs): pass
32
- def triggered(self): return pyqtSignal()
33
- def setIcon(self, *args, **kwargs): pass
34
- def setEnabled(self, *args, **kwargs): pass
35
-
36
- class QWidget:
37
- def __init__(self, *args, **kwargs): pass
38
-
39
- class QObject:
40
- def __init__(self, *args, **kwargs): pass
41
-
42
- class pyqtSignal:
43
- def __init__(self, *args, **kwargs): pass
44
- def connect(self, *args, **kwargs): pass
45
- def emit(self, *args, **kwargs): pass
46
-
47
- class QIcon:
48
- def __init__(self, *args, **kwargs): pass
49
-
50
- class QPoint:
51
- def __init__(self, *args, **kwargs): pass
52
-
53
- PYQT_AVAILABLE = False
54
-
55
- class MenuType(Enum):
56
- """Menü türleri"""
57
- DESKTOP = "desktop"
58
- FILE = "file"
59
- FOLDER = "folder"
60
- APPLICATION = "application"
61
- WIDGET = "widget"
62
- SELECTION = "selection" # Çoklu seçim
63
-
64
- class MenuContext(Enum):
65
- """Menü bağlamları"""
66
- DESKTOP_EMPTY = "desktop_empty"
67
- DESKTOP_FILE = "desktop_file"
68
- DESKTOP_FOLDER = "desktop_folder"
69
- FILES_APP = "files_app"
70
- DOCK_APP = "dock_app"
71
- WIDGET_AREA = "widget_area"
72
- APP_PACKAGE = "app_package"
73
-
74
- @dataclass
75
- class MenuAction:
76
- """Menü eylemi"""
77
- id: str
78
- text: str
79
- icon: str = ""
80
- shortcut: str = ""
81
- enabled: bool = True
82
- visible: bool = True
83
- separator_after: bool = False
84
- submenu: List['MenuAction'] = None
85
- callback: Callable = None
86
-
87
- def __post_init__(self):
88
- if self.submenu is None:
89
- self.submenu = []
90
-
91
- @dataclass
92
- class MenuRequest:
93
- """Menü isteği"""
94
- menu_type: MenuType
95
- context: MenuContext
96
- target_path: str = ""
97
- target_paths: List[str] = None
98
- position: Optional[Any] = None
99
- widget: Optional[Any] = None
100
- extra_data: Dict = None
101
-
102
- def __post_init__(self):
103
- if self.target_paths is None:
104
- self.target_paths = []
105
- if self.extra_data is None:
106
- self.extra_data = {}
107
-
108
- class ContextMenuManager(QObject):
109
- """Bağlam menüsü yöneticisi"""
110
-
111
- # Sinyaller
112
- action_triggered = pyqtSignal(str, dict) # action_id, data
113
-
114
- def __init__(self, kernel=None):
115
- super().__init__()
116
- self.kernel = kernel
117
- self.logger = logging.getLogger("ContextMenuManager")
118
-
119
- # Menü şablonları
120
- self.menu_templates: Dict[MenuContext, List[MenuAction]] = {}
121
-
122
- # Eylem callback'leri
123
- self.action_callbacks: Dict[str, Callable] = {}
124
-
125
- # Clipboard
126
- self.clipboard_files: List[str] = []
127
- self.clipboard_operation = "" # "copy" veya "cut"
128
-
129
- # Başlangıç
130
- self.create_default_templates()
131
- self.register_default_actions()
132
-
133
- def create_default_templates(self):
134
- """Varsayılan menü şablonları oluştur"""
135
-
136
- # Masaüstü boş alan menüsü
137
- self.menu_templates[MenuContext.DESKTOP_EMPTY] = [
138
- MenuAction("new_file", "📄 Yeni", submenu=[
139
- MenuAction("new_text", "📝 Metin Belgesi"),
140
- MenuAction("new_python", "🐍 Python Dosyası"),
141
- MenuAction("new_folder", "📁 Klasör"),
142
- ]),
143
- MenuAction("paste", "📋 Yapıştır", shortcut="Ctrl+V", enabled=False),
144
- MenuAction("", "", separator_after=True),
145
- MenuAction("refresh", "🔄 Yenile", shortcut="F5"),
146
- MenuAction("properties", "🎨 Masaüstü Ayarları"),
147
- MenuAction("wallpaper", "🖼️ Duvar Kağıdı Değiştir"),
148
- ]
149
-
150
- # Dosya menüsü
151
- self.menu_templates[MenuContext.DESKTOP_FILE] = [
152
- MenuAction("open", "📂 Aç", shortcut="Enter"),
153
- MenuAction("open_with", "🔧 Aç ile...", submenu=[]),
154
- MenuAction("", "", separator_after=True),
155
- MenuAction("copy", "📋 Kopyala", shortcut="Ctrl+C"),
156
- MenuAction("cut", "✂️ Kes", shortcut="Ctrl+X"),
157
- MenuAction("", "", separator_after=True),
158
- MenuAction("rename", "✏️ Yeniden Adlandır", shortcut="F2"),
159
- MenuAction("delete", "🗑️ Sil", shortcut="Delete"),
160
- MenuAction("", "", separator_after=True),
161
- MenuAction("properties", "ℹ️ Özellikler", shortcut="Alt+Enter"),
162
- ]
163
-
164
- # Klasör menüsü
165
- self.menu_templates[MenuContext.DESKTOP_FOLDER] = [
166
- MenuAction("open", "📂 Aç", shortcut="Enter"),
167
- MenuAction("open_new_window", "🪟 Yeni Pencerede Aç"),
168
- MenuAction("", "", separator_after=True),
169
- MenuAction("copy", "📋 Kopyala", shortcut="Ctrl+C"),
170
- MenuAction("cut", "✂️ Kes", shortcut="Ctrl+X"),
171
- MenuAction("", "", separator_after=True),
172
- MenuAction("rename", "✏️ Yeniden Adlandır", shortcut="F2"),
173
- MenuAction("delete", "🗑️ Sil", shortcut="Delete"),
174
- MenuAction("", "", separator_after=True),
175
- MenuAction("properties", "ℹ️ Özellikler", shortcut="Alt+Enter"),
176
- ]
177
-
178
- # Uygulama menüsü (Dock)
179
- self.menu_templates[MenuContext.DOCK_APP] = [
180
- MenuAction("launch", "🚀 Başlat"),
181
- MenuAction("", "", separator_after=True),
182
- MenuAction("pin", "📌 Dock'a Sabitle"),
183
- MenuAction("unpin", "📌 Dock'tan Kaldır"),
184
- MenuAction("", "", separator_after=True),
185
- MenuAction("app_info", "ℹ️ Uygulama Bilgileri"),
186
- MenuAction("app_settings", "⚙️ Uygulama Ayarları"),
187
- ]
188
-
189
- # Dosya yöneticisi menüsü
190
- self.menu_templates[MenuContext.FILES_APP] = [
191
- MenuAction("open", "📂 Aç", shortcut="Enter"),
192
- MenuAction("open_with", "🔧 Aç ile...", submenu=[]),
193
- MenuAction("", "", separator_after=True),
194
- MenuAction("copy", "📋 Kopyala", shortcut="Ctrl+C"),
195
- MenuAction("cut", "✂️ Kes", shortcut="Ctrl+X"),
196
- MenuAction("paste", "📄 Yapıştır", shortcut="Ctrl+V"),
197
- MenuAction("", "", separator_after=True),
198
- MenuAction("rename", "✏️ Yeniden Adlandır", shortcut="F2"),
199
- MenuAction("delete", "🗑️ Sil", shortcut="Delete"),
200
- MenuAction("", "", separator_after=True),
201
- MenuAction("compress", "📦 Arşivle"),
202
- MenuAction("properties", "ℹ️ Özellikler", shortcut="Alt+Enter"),
203
- ]
204
-
205
- # Widget menüsü
206
- self.menu_templates[MenuContext.WIDGET_AREA] = [
207
- MenuAction("widget_settings", "⚙️ Widget Ayarları"),
208
- MenuAction("widget_resize", "📏 Yeniden Boyutlandır"),
209
- MenuAction("", "", separator_after=True),
210
- MenuAction("widget_close", "✕ Kapat"),
211
- ]
212
-
213
- # .app dosyası menüsü
214
- self.menu_templates[MenuContext.APP_PACKAGE] = [
215
- MenuAction("install_app", "📦 Uygulamayı Kur", shortcut="Enter"),
216
- MenuAction("", "", separator_after=True),
217
- MenuAction("copy", "📋 Kopyala", shortcut="Ctrl+C"),
218
- MenuAction("cut", "✂️ Kes", shortcut="Ctrl+X"),
219
- MenuAction("", "", separator_after=True),
220
- MenuAction("rename", "✏️ Yeniden Adlandır", shortcut="F2"),
221
- MenuAction("delete", "🗑️ Sil", shortcut="Delete"),
222
- MenuAction("", "", separator_after=True),
223
- MenuAction("app_package_info", "ℹ️ Paket Bilgileri"),
224
- MenuAction("properties", "📋 Özellikler", shortcut="Alt+Enter"),
225
- ]
226
-
227
- def register_default_actions(self):
228
- """Varsayılan eylem callback'lerini kaydet"""
229
- self.action_callbacks.update({
230
- # Dosya işlemleri
231
- "new_text": self.create_new_text_file,
232
- "new_python": self.create_new_python_file,
233
- "new_folder": self.create_new_folder,
234
- "open": self.open_item,
235
- "copy": self.copy_items,
236
- "cut": self.cut_items,
237
- "paste": self.paste_items,
238
- "rename": self.rename_item,
239
- "delete": self.delete_items,
240
- "properties": self.show_properties,
241
-
242
- # Masaüstü işlemleri
243
- "refresh": self.refresh_desktop,
244
- "wallpaper": self.change_wallpaper,
245
-
246
- # Uygulama işlemleri
247
- "launch": self.launch_app,
248
- "pin": self.pin_app,
249
- "unpin": self.unpin_app,
250
- "app_info": self.show_app_info,
251
- "app_settings": self.show_app_settings,
252
-
253
- # Widget işlemleri
254
- "widget_settings": self.show_widget_settings,
255
- "widget_resize": self.resize_widget,
256
- "widget_close": self.close_widget,
257
-
258
- # .app paketi işlemleri
259
- "install_app": self.install_app_from_context,
260
- "app_package_info": self.show_app_package_info,
261
- })
262
-
263
- def show_context_menu(self, request: MenuRequest) -> bool:
264
- """Bağlam menüsünü göster"""
265
- try:
266
- self.logger.info(f"show_context_menu called with context: {request.context}")
267
-
268
- # Runtime PyQt6 kontrolü
269
- pyqt_available = self._check_pyqt_availability()
270
- self.logger.info(f"Runtime PYQT_AVAILABLE: {pyqt_available}, position: {request.position}")
271
-
272
- if not pyqt_available or not request.position:
273
- self.logger.warning(f"Cannot show menu: PYQT_AVAILABLE={pyqt_available}, position={request.position}")
274
- return False
275
-
276
- # Menü şablonunu al
277
- template = self.menu_templates.get(request.context, [])
278
- if not template:
279
- self.logger.warning(f"No template found for context: {request.context}")
280
- return False
281
-
282
- self.logger.info(f"Found template with {len(template)} items")
283
-
284
- # Menüyü oluştur
285
- menu = self.create_menu(template, request)
286
- if not menu:
287
- self.logger.error("Failed to create menu")
288
- return False
289
-
290
- self.logger.info("Menu created successfully, showing...")
291
-
292
- # Menüyü göster - artık lambda'lar action'ları handle ediyor
293
- menu.exec(request.position)
294
-
295
- self.logger.info("Menu execution completed")
296
- return True
297
-
298
- except Exception as e:
299
- self.logger.error(f"Failed to show context menu: {e}")
300
- import traceback
301
- self.logger.error(traceback.format_exc())
302
- return False
303
-
304
- def _check_pyqt_availability(self) -> bool:
305
- """Runtime PyQt6 kontrolü"""
306
- try:
307
- from PyQt6.QtWidgets import QMenu
308
- return True
309
- except ImportError:
310
- return False
311
-
312
- def create_menu(self, template: List[MenuAction], request: MenuRequest) -> Optional[QMenu]:
313
- """Menü oluştur"""
314
- try:
315
- from PyQt6.QtWidgets import QMenu
316
- from PyQt6.QtGui import QAction, QKeySequence
317
-
318
- menu = QMenu()
319
- menu.setStyleSheet(self.get_menu_stylesheet())
320
-
321
- for action_def in template:
322
- if not action_def.visible:
323
- continue
324
-
325
- if action_def.id == "": # Ayırıcı
326
- menu.addSeparator()
327
- continue
328
-
329
- # Alt menü varsa
330
- if action_def.submenu:
331
- submenu = QMenu(action_def.text, menu)
332
- submenu.setStyleSheet(self.get_menu_stylesheet())
333
-
334
- for sub_action in action_def.submenu:
335
- if sub_action.visible:
336
- sub_qaction = QAction(sub_action.text, submenu)
337
- # Lambda ile action_id'yi bağla
338
- sub_qaction.triggered.connect(
339
- lambda checked, aid=sub_action.id: self.execute_action(aid, request)
340
- )
341
- sub_qaction.setEnabled(sub_action.enabled)
342
- submenu.addAction(sub_qaction)
343
-
344
- menu.addMenu(submenu)
345
- else:
346
- # Normal eylem
347
- qaction = QAction(action_def.text, menu)
348
- # Lambda ile action_id'yi bağla
349
- qaction.triggered.connect(
350
- lambda checked, aid=action_def.id: self.execute_action(aid, request)
351
- )
352
- qaction.setEnabled(self.is_action_enabled(action_def.id, request))
353
-
354
- # Shortcut ekle
355
- if action_def.shortcut:
356
- try:
357
- qaction.setShortcut(QKeySequence(action_def.shortcut))
358
- except Exception as e:
359
- self.logger.warning(f"Failed to set shortcut {action_def.shortcut}: {e}")
360
-
361
- menu.addAction(qaction)
362
-
363
- if action_def.separator_after:
364
- menu.addSeparator()
365
-
366
- # Dinamik menü öğeleri ekle (paste action güncelleme)
367
- self.update_paste_action_new(menu, request)
368
-
369
- return menu
370
-
371
- except Exception as e:
372
- self.logger.error(f"Failed to create menu: {e}")
373
- import traceback
374
- self.logger.error(traceback.format_exc())
375
- return None
376
-
377
- def add_dynamic_items(self, menu: QMenu, request: MenuRequest):
378
- """Dinamik menü öğeleri ekle"""
379
- try:
380
- # "Aç ile" alt menüsünü doldur
381
- if request.target_path and Path(request.target_path).is_file():
382
- self.populate_open_with_menu(menu, request.target_path)
383
-
384
- # Clipboard durumuna göre yapıştır'ı etkinleştir
385
- self.update_paste_action(menu)
386
-
387
- except Exception as e:
388
- self.logger.error(f"Failed to add dynamic items: {e}")
389
-
390
- def populate_open_with_menu(self, menu: QMenu, file_path: str):
391
- """Aç ile menüsünü doldur"""
392
- try:
393
- # Menüde "Aç ile" eylemini bul
394
- for action in menu.actions():
395
- if action.text().endswith("Aç ile..."):
396
- submenu = action.menu()
397
- if submenu:
398
- submenu.clear()
399
-
400
- # Dosya uzantısına göre uygulamaları öner
401
- file_ext = Path(file_path).suffix.lower()
402
- suggested_apps = self.get_suggested_apps(file_ext)
403
-
404
- for app_info in suggested_apps:
405
- app_action = QAction(f"{app_info['icon']} {app_info['name']}", submenu)
406
- app_action.setData(f"open_with:{app_info['id']}")
407
- submenu.addAction(app_action)
408
-
409
- if suggested_apps:
410
- submenu.addSeparator()
411
-
412
- # Diğer uygulama seç
413
- choose_action = QAction("🔍 Diğer Uygulama Seç...", submenu)
414
- choose_action.setData("open_with:choose")
415
- submenu.addAction(choose_action)
416
- break
417
-
418
- except Exception as e:
419
- self.logger.error(f"Failed to populate open with menu: {e}")
420
-
421
- def get_suggested_apps(self, file_ext: str) -> List[Dict]:
422
- """Dosya uzantısına göre önerilen uygulamalar"""
423
- app_suggestions = {
424
- ".txt": [{"id": "cloud.notepad", "name": "Notepad", "icon": "📝"}],
425
- ".py": [{"id": "cloud.pyide", "name": "Python IDE", "icon": "🐍"}],
426
- ".md": [{"id": "cloud.notepad", "name": "Notepad", "icon": "📝"}],
427
- ".json": [{"id": "cloud.notepad", "name": "Notepad", "icon": "📝"}],
428
- ".log": [{"id": "cloud.notepad", "name": "Notepad", "icon": "📝"}],
429
- ".html": [{"id": "cloud.browser", "name": "Browser", "icon": "🌐"}],
430
- ".pdf": [{"id": "cloud.browser", "name": "Browser", "icon": "🌐"}],
431
- ".app": [{"id": "install_app", "name": "Uygulamayı Kur", "icon": "📦"}],
432
- }
433
-
434
- return app_suggestions.get(file_ext, [])
435
-
436
- def update_paste_action(self, menu: QMenu):
437
- """Yapıştır eylemini güncelle"""
438
- try:
439
- for action in menu.actions():
440
- if action.data() == "paste":
441
- action.setEnabled(len(self.clipboard_files) > 0)
442
- break
443
- except Exception as e:
444
- self.logger.error(f"Failed to update paste action: {e}")
445
-
446
- def is_action_enabled(self, action_id: str, request: MenuRequest) -> bool:
447
- """Eylemin etkin olup olmadığını kontrol et"""
448
- try:
449
- # Genel kontroller
450
- if action_id == "paste":
451
- return len(self.clipboard_files) > 0
452
-
453
- if action_id in ["copy", "cut", "delete", "rename", "properties"]:
454
- return bool(request.target_path or request.target_paths)
455
-
456
- if action_id == "open":
457
- return bool(request.target_path)
458
-
459
- # Uygulama kontrolleri
460
- if action_id in ["launch", "pin", "unpin"]:
461
- return bool(request.extra_data.get("app_id"))
462
-
463
- return True
464
-
465
- except Exception as e:
466
- self.logger.error(f"Failed to check action enabled: {e}")
467
- return False
468
-
469
- def execute_action(self, action_id: str, request: MenuRequest):
470
- """Eylemi çalıştır"""
471
- try:
472
- self.logger.info(f"Executing action: {action_id}")
473
-
474
- # Özel eylemler (open_with gibi)
475
- if ":" in action_id:
476
- action_type, action_data = action_id.split(":", 1)
477
- if action_type == "open_with":
478
- self.open_with_app(request.target_path, action_data)
479
- return
480
-
481
- # Normal eylemler
482
- callback = self.action_callbacks.get(action_id)
483
- if callback:
484
- self.logger.info(f"Found callback for action: {action_id}")
485
- callback(request)
486
- else:
487
- self.logger.warning(f"No callback found for action: {action_id}")
488
-
489
- # Sinyal yayınla
490
- self.action_triggered.emit(action_id, {
491
- "target_path": request.target_path,
492
- "target_paths": request.target_paths,
493
- "context": request.context.value,
494
- "extra_data": request.extra_data
495
- })
496
-
497
- except Exception as e:
498
- self.logger.error(f"Failed to execute action {action_id}: {e}")
499
- import traceback
500
- self.logger.error(traceback.format_exc())
501
-
502
- def get_menu_stylesheet(self) -> str:
503
- """Menü stil sayfası"""
504
- return """
505
- QMenu {
506
- background-color: rgba(45, 45, 45, 0.95);
507
- border: 1px solid rgba(80, 80, 80, 0.8);
508
- border-radius: 8px;
509
- padding: 6px;
510
- color: #ffffff;
511
- }
512
-
513
- QMenu::item {
514
- background-color: transparent;
515
- padding: 10px 18px;
516
- border-radius: 6px;
517
- margin: 1px;
518
- color: #ffffff;
519
- }
520
-
521
- QMenu::item:selected {
522
- background-color: rgba(70, 70, 70, 0.8);
523
- color: #ffffff;
524
- }
525
-
526
- QMenu::item:disabled {
527
- color: #888888;
528
- background-color: transparent;
529
- }
530
-
531
- QMenu::separator {
532
- height: 1px;
533
- background-color: rgba(100, 100, 100, 0.6);
534
- margin: 6px 12px;
535
- }
536
-
537
- QMenu::indicator {
538
- width: 16px;
539
- height: 16px;
540
- }
541
- """
542
-
543
- # Eylem callback'leri
544
- def create_new_text_file(self, request: MenuRequest):
545
- """Yeni metin dosyası oluştur"""
546
- try:
547
- from PyQt6.QtWidgets import QInputDialog
548
-
549
- name, ok = QInputDialog.getText(None, "Yeni Dosya", "Dosya adı:", text="Yeni Metin Belgesi.txt")
550
- if ok and name:
551
- if not name.endswith('.txt'):
552
- name += '.txt'
553
-
554
- # Masaüstüne dosya oluştur
555
- desktop_path = Path("users/default/Desktop")
556
- desktop_path.mkdir(parents=True, exist_ok=True)
557
- file_path = desktop_path / name
558
-
559
- with open(file_path, 'w', encoding='utf-8') as f:
560
- f.write("")
561
-
562
- self.logger.info(f"Created text file: {file_path}")
563
-
564
- # Desktop'u yenile
565
- self.refresh_desktop(request)
566
-
567
- except Exception as e:
568
- self.logger.error(f"Failed to create text file: {e}")
569
- import traceback
570
- self.logger.error(traceback.format_exc())
571
-
572
- def create_new_python_file(self, request: MenuRequest):
573
- """Yeni Python dosyası oluştur"""
574
- try:
575
- from PyQt6.QtWidgets import QInputDialog
576
-
577
- name, ok = QInputDialog.getText(None, "Yeni Python Dosyası", "Dosya adı:", text="yeni_dosya.py")
578
- if ok and name:
579
- if not name.endswith('.py'):
580
- name += '.py'
581
-
582
- # Masaüstüne dosya oluştur
583
- desktop_path = Path("users/default/Desktop")
584
- desktop_path.mkdir(parents=True, exist_ok=True)
585
- file_path = desktop_path / name
586
-
587
- with open(file_path, 'w', encoding='utf-8') as f:
588
- f.write('#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n')
589
-
590
- self.logger.info(f"Created Python file: {file_path}")
591
-
592
- # Desktop'u yenile
593
- self.refresh_desktop(request)
594
-
595
- except Exception as e:
596
- self.logger.error(f"Failed to create Python file: {e}")
597
- import traceback
598
- self.logger.error(traceback.format_exc())
599
-
600
- def create_new_folder(self, request: MenuRequest):
601
- """Yeni klasör oluştur"""
602
- try:
603
- from PyQt6.QtWidgets import QInputDialog
604
-
605
- name, ok = QInputDialog.getText(None, "Yeni Klasör", "Klasör adı:", text="Yeni Klasör")
606
- if ok and name:
607
- # Masaüstüne klasör oluştur
608
- desktop_path = Path("users/default/Desktop")
609
- desktop_path.mkdir(parents=True, exist_ok=True)
610
- folder_path = desktop_path / name
611
- folder_path.mkdir(exist_ok=True)
612
-
613
- self.logger.info(f"Created folder: {folder_path}")
614
-
615
- # Desktop'u yenile
616
- self.refresh_desktop(request)
617
-
618
- except Exception as e:
619
- self.logger.error(f"Failed to create folder: {e}")
620
- import traceback
621
- self.logger.error(traceback.format_exc())
622
-
623
- def open_item(self, request: MenuRequest):
624
- """Öğeyi aç"""
625
- try:
626
- if not request.target_path:
627
- return
628
-
629
- path = Path(request.target_path)
630
-
631
- if path.is_dir():
632
- # Klasörü dosya yöneticisi ile aç
633
- self.open_folder_in_files(request.target_path)
634
- else:
635
- # Dosyayı varsayılan uygulama ile aç
636
- self.open_file_with_default(request.target_path)
637
-
638
- except Exception as e:
639
- self.logger.error(f"Failed to open item: {e}")
640
-
641
- def open_folder_in_files(self, folder_path: str):
642
- """Klasörü dosya yöneticisinde aç"""
643
- try:
644
- if self.kernel:
645
- launcher = self.kernel.get_module("launcher")
646
- if launcher:
647
- # PyCloud OS sanal dosya sistemi yolunu kullan
648
- pycloud_path = folder_path
649
- if not folder_path.startswith(("users/", "apps/", "system/", "temp/")):
650
- # Eğer gerçek dosya sistemi yolu ise, PyCloud OS yoluna çevir
651
- pycloud_path = str(Path("users") / "default" / "Desktop")
652
-
653
- launcher.launch_app("cloud_files", {"open_path": pycloud_path})
654
-
655
- except Exception as e:
656
- self.logger.error(f"Failed to open folder in files: {e}")
657
-
658
- def open_file_with_default(self, file_path: str):
659
- """Dosyayı varsayılan uygulama ile aç"""
660
- try:
661
- file_ext = Path(file_path).suffix.lower()
662
- file_name = Path(file_path).name.lower()
663
-
664
- # .app dosyası mı kontrol et
665
- if file_name.endswith('.app') or file_path.endswith('.app'):
666
- # .app dosyası - kurulum işlemi başlat
667
- self.install_app_package(file_path)
668
- return
669
-
670
- # Varsayılan uygulamalar - .py dosyaları için PyIDE eklendi
671
- default_apps = {
672
- ".txt": "cloud_notepad",
673
- ".py": "cloud_pyide", # Python dosyaları PyIDE ile açılsın
674
- ".js": "cloud_pyide", # JavaScript dosyaları da PyIDE ile
675
- ".html": "cloud_browser",
676
- ".css": "cloud_pyide", # CSS dosyaları PyIDE ile
677
- ".md": "cloud_notepad",
678
- ".json": "cloud_pyide", # JSON dosyaları PyIDE ile
679
- ".log": "cloud_notepad",
680
- ".pdf": "cloud_browser",
681
- }
682
-
683
- app_id = default_apps.get(file_ext, "cloud_notepad")
684
-
685
- if self.kernel:
686
- launcher = self.kernel.get_module("launcher")
687
- if launcher:
688
- # ÇÖZÜM: Absolute path kullan - boş açılma sorununu çözer
689
- absolute_path = str(Path(file_path).resolve().absolute())
690
-
691
- self.logger.info(f"🚀 Masaüstünden dosya açılıyor: {app_id} -> {absolute_path}")
692
-
693
- # Launcher API ile dosyayı aç
694
- launch_success = launcher.launch_app(app_id, open_file=absolute_path)
695
-
696
- if launch_success:
697
- self.logger.info(f"✅ Dosya başarıyla açıldı: {absolute_path}")
698
- else:
699
- self.logger.warning(f"⚠️ Dosya açma başarısız")
700
- # Fallback olarak sistem uygulamasını dene
701
- self._open_with_system_app(absolute_path)
702
- else:
703
- self.logger.warning("⚠️ Launcher modülü bulunamadı")
704
- self._open_with_system_app(str(Path(file_path).absolute()))
705
- else:
706
- self.logger.warning("⚠️ Kernel bulunamadı")
707
- self._open_with_system_app(str(Path(file_path).absolute()))
708
-
709
- except Exception as e:
710
- self.logger.error(f"Failed to open file with default app: {e}")
711
- import traceback
712
- self.logger.error(traceback.format_exc())
713
-
714
- def _open_with_system_app(self, absolute_path: str):
715
- """Sistem uygulamasıyla dosyayı aç"""
716
- try:
717
- import subprocess
718
- import platform
719
-
720
- system = platform.system()
721
- self.logger.info(f"🔄 Sistem uygulamasıyla açılıyor: {absolute_path}")
722
-
723
- if system == "Darwin": # macOS
724
- subprocess.run(["open", absolute_path], check=True)
725
- elif system == "Windows":
726
- subprocess.run(["start", absolute_path], shell=True, check=True)
727
- else: # Linux
728
- subprocess.run(["xdg-open", absolute_path], check=True)
729
-
730
- self.logger.info(f"✅ Sistem uygulamasıyla açıldı: {absolute_path}")
731
-
732
- except Exception as e:
733
- self.logger.error(f"❌ Sistem uygulamasıyla açma hatası: {e}")
734
-
735
- def install_app_package(self, app_path: str):
736
- """Uygulama paketini kur"""
737
- try:
738
- from PyQt6.QtWidgets import QMessageBox
739
-
740
- app_name = Path(app_path).stem
741
-
742
- # Onay dialogu
743
- reply = QMessageBox.question(None, "Uygulama Kurulumu",
744
- f"'{app_name}' uygulamasını kurmak istediğinizden emin misiniz?",
745
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
746
-
747
- if reply != QMessageBox.StandardButton.Yes:
748
- return
749
-
750
- # AppKit ile kurulum yap
751
- if self.kernel:
752
- appkit = self.kernel.get_module("appkit")
753
- if appkit:
754
- # İlerleme mesajı göster
755
- progress_msg = QMessageBox(None)
756
- progress_msg.setWindowTitle("Kurulum")
757
- progress_msg.setText(f"'{app_name}' kuruluyor...")
758
- progress_msg.setStandardButtons(QMessageBox.StandardButton.NoButton)
759
- progress_msg.show()
760
-
761
- # Kurulum işlemi
762
- result, message = appkit.install_app(app_path, force=False)
763
-
764
- progress_msg.close()
765
-
766
- # Sonuç mesajı
767
- if result.name == "SUCCESS":
768
- QMessageBox.information(None, "Başarılı", f"✅ {message}")
769
-
770
- # AppExplorer'ı yenile
771
- app_explorer = self.kernel.get_module("appexplorer")
772
- if app_explorer:
773
- app_explorer.force_discovery()
774
-
775
- # Desktop'u yenile
776
- self.refresh_desktop(None)
777
-
778
- elif result.name == "ALREADY_INSTALLED":
779
- reply = QMessageBox.question(None, "Zaten Kurulu",
780
- f"'{app_name}' zaten kurulu. Güncellemek istediğinizden emin misiniz?",
781
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
782
-
783
- if reply == QMessageBox.StandardButton.Yes:
784
- # Zorla kurulum (güncelleme)
785
- result, message = appkit.install_app(app_path, force=True)
786
- if result.name == "SUCCESS":
787
- QMessageBox.information(None, "Güncellendi", f"✅ {message}")
788
- else:
789
- QMessageBox.critical(None, "Hata", f"❌ {message}")
790
- else:
791
- QMessageBox.critical(None, "Kurulum Hatası", f"❌ {message}")
792
- else:
793
- QMessageBox.critical(None, "Hata", "AppKit modülü bulunamadı!")
794
- else:
795
- QMessageBox.critical(None, "Hata", "Sistem çekirdeği bulunamadı!")
796
-
797
- except Exception as e:
798
- self.logger.error(f"Failed to install app package: {e}")
799
- from PyQt6.QtWidgets import QMessageBox
800
- QMessageBox.critical(None, "Hata", f"Kurulum sırasında hata oluştu: {str(e)}")
801
-
802
- def open_with_app(self, file_path: str, app_id: str):
803
- """Dosyayı belirtilen uygulama ile aç"""
804
- try:
805
- if app_id == "choose":
806
- # Uygulama seçim dialogu
807
- self.show_app_chooser(file_path)
808
- return
809
-
810
- if self.kernel:
811
- launcher = self.kernel.get_module("launcher")
812
- if launcher:
813
- launcher.launch_app(app_id, {"open_file": file_path})
814
-
815
- except Exception as e:
816
- self.logger.error(f"Failed to open file with app {app_id}: {e}")
817
-
818
- def show_app_chooser(self, file_path: str):
819
- """Uygulama seçici dialogu göster"""
820
- # TODO: Uygulama seçici dialog implementasyonu
821
- self.logger.info(f"Show app chooser for: {file_path}")
822
-
823
- def copy_items(self, request: MenuRequest):
824
- """Öğeleri kopyala"""
825
- try:
826
- self.clipboard_files = request.target_paths if request.target_paths else [request.target_path]
827
- self.clipboard_operation = "copy"
828
-
829
- self.logger.info(f"Copied {len(self.clipboard_files)} items to clipboard")
830
-
831
- except Exception as e:
832
- self.logger.error(f"Failed to copy items: {e}")
833
-
834
- def cut_items(self, request: MenuRequest):
835
- """Öğeleri kes"""
836
- try:
837
- self.clipboard_files = request.target_paths if request.target_paths else [request.target_path]
838
- self.clipboard_operation = "cut"
839
-
840
- self.logger.info(f"Cut {len(self.clipboard_files)} items to clipboard")
841
-
842
- except Exception as e:
843
- self.logger.error(f"Failed to cut items: {e}")
844
-
845
- def paste_items(self, request: MenuRequest):
846
- """Öğeleri yapıştır"""
847
- try:
848
- if not self.clipboard_files:
849
- return
850
-
851
- target_dir = request.extra_data.get("current_dir", "users/demo/Desktop")
852
-
853
- for file_path in self.clipboard_files:
854
- source = Path(file_path)
855
- target = Path(target_dir) / source.name
856
-
857
- if self.clipboard_operation == "copy":
858
- if source.is_dir():
859
- import shutil
860
- shutil.copytree(source, target, dirs_exist_ok=True)
861
- else:
862
- import shutil
863
- shutil.copy2(source, target)
864
- elif self.clipboard_operation == "cut":
865
- source.rename(target)
866
-
867
- if self.clipboard_operation == "cut":
868
- self.clipboard_files.clear()
869
-
870
- self.logger.info(f"Pasted {len(self.clipboard_files)} items")
871
-
872
- except Exception as e:
873
- self.logger.error(f"Failed to paste items: {e}")
874
-
875
- def rename_item(self, request: MenuRequest):
876
- """Öğeyi yeniden adlandır"""
877
- try:
878
- from PyQt6.QtWidgets import QInputDialog
879
-
880
- if not request.target_path:
881
- self.logger.warning("No target path for rename")
882
- return
883
-
884
- path = Path(request.target_path)
885
- if not path.exists():
886
- self.logger.warning(f"Target path does not exist: {path}")
887
- return
888
-
889
- current_name = path.name
890
-
891
- new_name, ok = QInputDialog.getText(None, "Yeniden Adlandır",
892
- "Yeni ad:", text=current_name)
893
- if ok and new_name and new_name != current_name:
894
- new_path = path.parent / new_name
895
- path.rename(new_path)
896
-
897
- self.logger.info(f"Renamed {current_name} to {new_name}")
898
-
899
- # Desktop'u yenile
900
- self.refresh_desktop(request)
901
-
902
- except Exception as e:
903
- self.logger.error(f"Failed to rename item: {e}")
904
- import traceback
905
- self.logger.error(traceback.format_exc())
906
-
907
- def delete_items(self, request: MenuRequest):
908
- """Öğeleri sil"""
909
- try:
910
- from PyQt6.QtWidgets import QMessageBox
911
-
912
- items = request.target_paths if request.target_paths else [request.target_path]
913
- if not items or not items[0]:
914
- self.logger.warning("No items to delete")
915
- return
916
-
917
- # Onay dialogu
918
- reply = QMessageBox.question(None, "Silme Onayı",
919
- f"{len(items)} öğe silinecek. Emin misiniz?",
920
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
921
-
922
- if reply == QMessageBox.StandardButton.Yes:
923
- for item_path in items:
924
- if not item_path:
925
- continue
926
-
927
- path = Path(item_path)
928
- if not path.exists():
929
- self.logger.warning(f"Item does not exist: {path}")
930
- continue
931
-
932
- if path.is_dir():
933
- import shutil
934
- shutil.rmtree(path)
935
- else:
936
- path.unlink()
937
-
938
- self.logger.info(f"Deleted {len(items)} items")
939
-
940
- # Desktop'u yenile
941
- self.refresh_desktop(request)
942
-
943
- except Exception as e:
944
- self.logger.error(f"Failed to delete items: {e}")
945
- import traceback
946
- self.logger.error(traceback.format_exc())
947
-
948
- def show_properties(self, request: MenuRequest):
949
- """Özellikler dialogu göster"""
950
- # TODO: Özellikler dialog implementasyonu
951
- self.logger.info(f"Show properties for: {request.target_path}")
952
-
953
- def refresh_desktop(self, request: MenuRequest):
954
- """Masaüstünü yenile"""
955
- try:
956
- self.logger.info("Refreshing desktop...")
957
-
958
- if self.kernel:
959
- # rain_ui modülünü dene
960
- ui = self.kernel.get_module("rain_ui")
961
- if ui and hasattr(ui, 'desktop'):
962
- if hasattr(ui.desktop, 'refresh_desktop'):
963
- ui.desktop.refresh_desktop()
964
- self.logger.info("Desktop refreshed via rain_ui module")
965
- return
966
- elif hasattr(ui.desktop, 'load_desktop_items'):
967
- ui.desktop.load_desktop_items()
968
- self.logger.info("Desktop refreshed via load_desktop_items")
969
- return
970
- else:
971
- self.logger.warning("No refresh method found on desktop")
972
-
973
- # Alternatif: QApplication üzerinden tüm widget'ları yenile
974
- try:
975
- from PyQt6.QtWidgets import QApplication
976
- app = QApplication.instance()
977
- if app:
978
- # Tüm widget'ları yenile
979
- for widget in app.allWidgets():
980
- if hasattr(widget, 'load_desktop_items'):
981
- widget.load_desktop_items()
982
- self.logger.info("Desktop refreshed via QApplication widget search")
983
- return
984
- elif hasattr(widget, 'refresh_desktop'):
985
- widget.refresh_desktop()
986
- self.logger.info("Desktop refreshed via QApplication widget search")
987
- return
988
- except Exception as e:
989
- self.logger.warning(f"QApplication refresh failed: {e}")
990
-
991
- self.logger.warning("No UI module or desktop found for refresh")
992
- else:
993
- self.logger.warning("No kernel available for refresh")
994
-
995
- except Exception as e:
996
- self.logger.error(f"Failed to refresh desktop: {e}")
997
-
998
- def change_wallpaper(self, request: MenuRequest):
999
- """Duvar kağıdı değiştir - PyCloud OS dosya sistemi kullan"""
1000
- try:
1001
- # PyCloud OS wallpaper manager'ı kullan
1002
- if self.kernel:
1003
- wallpaper_manager = self.kernel.get_module("wallpaper")
1004
- if wallpaper_manager:
1005
- # Wallpaper dialog'unu göster
1006
- success = wallpaper_manager.show_wallpaper_dialog()
1007
- if success:
1008
- self.logger.info("Wallpaper changed via wallpaper manager")
1009
- return
1010
- else:
1011
- self.logger.info("Wallpaper dialog cancelled")
1012
- return
1013
-
1014
- # Fallback - PyCloud OS dosya sistemi ile manuel seçim
1015
- from PyQt6.QtWidgets import QFileDialog, QMessageBox
1016
-
1017
- # PyCloud OS wallpaper dizinleri
1018
- system_wallpapers = Path("system/wallpapers")
1019
- user_wallpapers = Path("users/default/wallpapers")
1020
-
1021
- # Dizinleri oluştur
1022
- system_wallpapers.mkdir(parents=True, exist_ok=True)
1023
- user_wallpapers.mkdir(parents=True, exist_ok=True)
1024
-
1025
- # Mevcut duvar kağıtlarını kontrol et
1026
- available_wallpapers = []
1027
-
1028
- # Sistem duvar kağıtları
1029
- for wallpaper in system_wallpapers.glob("*"):
1030
- if wallpaper.suffix.lower() in ['.png', '.jpg', '.jpeg', '.bmp', '.webp']:
1031
- available_wallpapers.append(str(wallpaper))
1032
-
1033
- # Kullanıcı duvar kağıtları
1034
- for wallpaper in user_wallpapers.glob("*"):
1035
- if wallpaper.suffix.lower() in ['.png', '.jpg', '.jpeg', '.bmp', '.webp']:
1036
- available_wallpapers.append(str(wallpaper))
1037
-
1038
- if available_wallpapers:
1039
- # Mevcut duvar kağıtlarından seç
1040
- from PyQt6.QtWidgets import QInputDialog
1041
-
1042
- wallpaper_names = [Path(wp).name for wp in available_wallpapers]
1043
-
1044
- selected_name, ok = QInputDialog.getItem(
1045
- None,
1046
- "Duvar Kağıdı Seç",
1047
- "Mevcut duvar kağıtlarından birini seçin:",
1048
- wallpaper_names,
1049
- 0,
1050
- False
1051
- )
1052
-
1053
- if ok and selected_name:
1054
- # Seçilen duvar kağıdının tam yolunu bul
1055
- selected_path = None
1056
- for wp in available_wallpapers:
1057
- if Path(wp).name == selected_name:
1058
- selected_path = wp
1059
- break
1060
-
1061
- if selected_path:
1062
- # Wallpaper manager ile ayarla
1063
- if wallpaper_manager:
1064
- wallpaper_manager.set_wallpaper(selected_path)
1065
- self.logger.info(f"Wallpaper set to: {selected_path}")
1066
- else:
1067
- # Rain UI'dan wallpaper değiştir
1068
- ui = self.kernel.get_module("ui")
1069
- if ui and hasattr(ui, 'set_wallpaper'):
1070
- ui.set_wallpaper(selected_path)
1071
- self.logger.info("Wallpaper changed via rain_ui")
1072
-
1073
- QMessageBox.information(
1074
- None,
1075
- "Duvar Kağıdı Değiştirildi",
1076
- f"Duvar kağıdı '{selected_name}' olarak ayarlandı."
1077
- )
1078
- return
1079
-
1080
- # Yeni duvar kağıdı ekleme seçeneği
1081
- reply = QMessageBox.question(
1082
- None,
1083
- "Duvar Kağıdı Bulunamadı",
1084
- "Sistem duvar kağıdı bulunamadı. Yeni bir duvar kağıdı eklemek ister misiniz?",
1085
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
1086
- )
1087
-
1088
- if reply == QMessageBox.StandardButton.Yes:
1089
- # PyCloud OS dosya sistemi içinden seç
1090
- file_path, _ = QFileDialog.getOpenFileName(
1091
- None,
1092
- "Duvar Kağıdı Seç - PyCloud OS",
1093
- str(user_wallpapers), # PyCloud OS kullanıcı wallpaper dizini
1094
- "Resim Dosyaları (*.png *.jpg *.jpeg *.bmp *.webp);;Tüm Dosyalar (*)"
1095
- )
1096
-
1097
- if file_path:
1098
- # Eğer PyCloud OS dışından seçildiyse, kopyala
1099
- file_path_obj = Path(file_path)
1100
-
1101
- if not str(file_path).startswith(str(Path.cwd())):
1102
- # Dosyayı PyCloud OS'a kopyala
1103
- dest_path = user_wallpapers / file_path_obj.name
1104
-
1105
- try:
1106
- import shutil
1107
- shutil.copy2(file_path_obj, dest_path)
1108
- file_path = str(dest_path)
1109
-
1110
- QMessageBox.information(
1111
- None,
1112
- "Duvar Kağıdı Kopyalandı",
1113
- f"Duvar kağıdı PyCloud OS'a kopyalandı:\n{dest_path}"
1114
- )
1115
- except Exception as e:
1116
- QMessageBox.critical(
1117
- None,
1118
- "Kopyalama Hatası",
1119
- f"Duvar kağıdı kopyalanamadı:\n{e}"
1120
- )
1121
- return
1122
-
1123
- # Wallpaper'ı ayarla
1124
- if wallpaper_manager:
1125
- wallpaper_manager.set_wallpaper(file_path)
1126
- self.logger.info(f"New wallpaper set: {file_path}")
1127
- else:
1128
- # Rain UI'dan wallpaper değiştir
1129
- ui = self.kernel.get_module("ui")
1130
- if ui and hasattr(ui, 'set_wallpaper'):
1131
- ui.set_wallpaper(file_path)
1132
- self.logger.info("New wallpaper changed via rain_ui")
1133
-
1134
- QMessageBox.information(
1135
- None,
1136
- "Duvar Kağıdı Ayarlandı",
1137
- f"Yeni duvar kağıdı başarıyla ayarlandı!"
1138
- )
1139
-
1140
- except Exception as e:
1141
- self.logger.error(f"Failed to change wallpaper: {e}")
1142
- import traceback
1143
- self.logger.error(traceback.format_exc())
1144
-
1145
- # Hata mesajı göster
1146
- try:
1147
- from PyQt6.QtWidgets import QMessageBox
1148
- QMessageBox.critical(
1149
- None,
1150
- "Duvar Kağıdı Hatası",
1151
- f"Duvar kağıdı değiştirilemedi:\n{e}"
1152
- )
1153
- except:
1154
- pass
1155
-
1156
- def launch_app(self, request: MenuRequest):
1157
- """Uygulamayı başlat"""
1158
- try:
1159
- app_id = request.extra_data.get("app_id")
1160
- if app_id and self.kernel:
1161
- launcher = self.kernel.get_module("launcher")
1162
- if launcher:
1163
- launcher.launch_app(app_id)
1164
-
1165
- except Exception as e:
1166
- self.logger.error(f"Failed to launch app: {e}")
1167
-
1168
- def pin_app(self, request: MenuRequest):
1169
- """Uygulamayı dock'a sabitle"""
1170
- try:
1171
- app_id = request.extra_data.get("app_id")
1172
- if app_id and self.kernel:
1173
- ui = self.kernel.get_module("ui")
1174
- if ui and hasattr(ui, 'dock'):
1175
- ui.dock.pin_app(app_id)
1176
-
1177
- except Exception as e:
1178
- self.logger.error(f"Failed to pin app: {e}")
1179
-
1180
- def unpin_app(self, request: MenuRequest):
1181
- """Uygulamayı dock'tan kaldır"""
1182
- try:
1183
- app_id = request.extra_data.get("app_id")
1184
- if app_id and self.kernel:
1185
- ui = self.kernel.get_module("ui")
1186
- if ui and hasattr(ui, 'dock'):
1187
- ui.dock.unpin_app(app_id)
1188
-
1189
- except Exception as e:
1190
- self.logger.error(f"Failed to unpin app: {e}")
1191
-
1192
- def show_app_info(self, request: MenuRequest):
1193
- """Uygulama bilgilerini göster"""
1194
- # TODO: Uygulama bilgi dialogu implementasyonu
1195
- app_id = request.extra_data.get("app_id")
1196
- self.logger.info(f"Show app info for: {app_id}")
1197
-
1198
- def show_app_settings(self, request: MenuRequest):
1199
- """Uygulama ayarlarını göster"""
1200
- # TODO: Uygulama ayar dialogu implementasyonu
1201
- app_id = request.extra_data.get("app_id")
1202
- self.logger.info(f"Show app settings for: {app_id}")
1203
-
1204
- def show_widget_settings(self, request: MenuRequest):
1205
- """Widget ayarlarını göster"""
1206
- try:
1207
- widget_id = request.extra_data.get("widget_id")
1208
- if widget_id and self.kernel:
1209
- widget_manager = self.kernel.get_module("widgets")
1210
- if widget_manager:
1211
- widget_manager.show_widget_settings(widget_id)
1212
-
1213
- except Exception as e:
1214
- self.logger.error(f"Failed to show widget settings: {e}")
1215
-
1216
- def resize_widget(self, request: MenuRequest):
1217
- """Widget'ı yeniden boyutlandır"""
1218
- # TODO: Widget boyutlandırma implementasyonu
1219
- widget_id = request.extra_data.get("widget_id")
1220
- self.logger.info(f"Resize widget: {widget_id}")
1221
-
1222
- def close_widget(self, request: MenuRequest):
1223
- """Widget'ı kapat"""
1224
- try:
1225
- widget_id = request.extra_data.get("widget_id")
1226
- if widget_id and self.kernel:
1227
- widget_manager = self.kernel.get_module("widgets")
1228
- if widget_manager:
1229
- widget_manager.remove_widget(widget_id)
1230
-
1231
- except Exception as e:
1232
- self.logger.error(f"Failed to close widget: {e}")
1233
-
1234
- def register_action(self, action_id: str, callback: Callable):
1235
- """Özel eylem kaydet"""
1236
- self.action_callbacks[action_id] = callback
1237
-
1238
- def add_menu_template(self, context: MenuContext, template: List[MenuAction]):
1239
- """Menü şablonu ekle"""
1240
- self.menu_templates[context] = template
1241
-
1242
- def shutdown(self):
1243
- """Context menu manager'ı kapat"""
1244
- try:
1245
- self.clipboard_files.clear()
1246
- self.action_callbacks.clear()
1247
- self.menu_templates.clear()
1248
-
1249
- self.logger.info("Context menu manager shutdown completed")
1250
-
1251
- except Exception as e:
1252
- self.logger.error(f"Context menu manager shutdown failed: {e}")
1253
-
1254
- def update_paste_action_new(self, menu, request):
1255
- """Yapıştır eylemini güncelle (PyQt6 uyumlu)"""
1256
- try:
1257
- # Menu'daki tüm action'ları kontrol et
1258
- for action in menu.actions():
1259
- if action.text() == "📋 Yapıştır":
1260
- action.setEnabled(len(self.clipboard_files) > 0)
1261
- break
1262
-
1263
- # Alt menüleri de kontrol et
1264
- for action in menu.actions():
1265
- if action.menu():
1266
- self.update_paste_action_new(action.menu(), request)
1267
-
1268
- except Exception as e:
1269
- self.logger.warning(f"Failed to update paste action: {e}")
1270
-
1271
- def install_app_from_context(self, request: MenuRequest):
1272
- """Context menu'den uygulama kur"""
1273
- if request.target_path:
1274
- self.install_app_package(request.target_path)
1275
-
1276
- def show_app_package_info(self, request: MenuRequest):
1277
- """Uygulama paketi bilgilerini göster"""
1278
- try:
1279
- from PyQt6.QtWidgets import QMessageBox
1280
-
1281
- if not request.target_path:
1282
- return
1283
-
1284
- app_path = Path(request.target_path)
1285
- app_json_path = app_path / "app.json"
1286
-
1287
- if not app_json_path.exists():
1288
- QMessageBox.warning(None, "Hata", "app.json dosyası bulunamadı!")
1289
- return
1290
-
1291
- try:
1292
- with open(app_json_path, 'r', encoding='utf-8') as f:
1293
- app_data = json.load(f)
1294
-
1295
- # Bilgi mesajı oluştur
1296
- info_text = f"""
1297
- 📦 Uygulama Paketi Bilgileri
1298
-
1299
- 🆔 ID: {app_data.get('id', 'Bilinmiyor')}
1300
- 📝 Ad: {app_data.get('name', 'Bilinmiyor')}
1301
- 🔢 Sürüm: {app_data.get('version', 'Bilinmiyor')}
1302
- 👤 Geliştirici: {app_data.get('developer', 'Bilinmiyor')}
1303
- 📋 Açıklama: {app_data.get('description', 'Açıklama yok')}
1304
- 🏷️ Kategori: {app_data.get('category', 'Bilinmiyor')}
1305
- 📄 Giriş Dosyası: {app_data.get('entry', 'main.py')}
1306
- 🏷️ Etiketler: {', '.join(app_data.get('tags', []))}
1307
- 📦 Boyut: {self._get_directory_size(app_path):.2f} MB
1308
- """.strip()
1309
-
1310
- QMessageBox.information(None, "Paket Bilgileri", info_text)
1311
-
1312
- except json.JSONDecodeError:
1313
- QMessageBox.critical(None, "Hata", "app.json dosyası geçersiz!")
1314
- except Exception as e:
1315
- QMessageBox.critical(None, "Hata", f"Bilgi okunamadı: {str(e)}")
1316
-
1317
- except Exception as e:
1318
- self.logger.error(f"Failed to show app package info: {e}")
1319
-
1320
- def _get_directory_size(self, directory: Path) -> float:
1321
- """Dizin boyutunu MB cinsinden hesapla"""
1322
- try:
1323
- total_size = 0
1324
- for file_path in directory.rglob("*"):
1325
- if file_path.is_file():
1326
- total_size += file_path.stat().st_size
1327
- return total_size / 1024 / 1024
1328
- except Exception:
1329
- return 0.0
1330
-
1331
- # Kolaylık fonksiyonları
1332
- _context_menu_manager = None
1333
-
1334
- def init_context_menu_manager(kernel=None) -> ContextMenuManager:
1335
- """Context menu manager'ı başlat"""
1336
- global _context_menu_manager
1337
-
1338
- # Debug: PyQt6 durumunu kontrol et
1339
- logger = logging.getLogger("ContextMenuInit")
1340
- logger.info(f"Initializing context menu manager, PYQT_AVAILABLE: {PYQT_AVAILABLE}")
1341
-
1342
- try:
1343
- # PyQt6 import'unu tekrar test et
1344
- from PyQt6.QtWidgets import QMenu
1345
- logger.info("PyQt6 import test successful")
1346
- except ImportError as e:
1347
- logger.error(f"PyQt6 import test failed: {e}")
1348
-
1349
- _context_menu_manager = ContextMenuManager(kernel)
1350
- logger.info("Context menu manager created successfully")
1351
- return _context_menu_manager
1352
-
1353
- def get_context_menu_manager() -> Optional[ContextMenuManager]:
1354
- """Context menu manager'ı al"""
1355
- return _context_menu_manager
1356
-
1357
- def show_context_menu(menu_type: MenuType, context: MenuContext,
1358
- position: QPoint, target_path: str = "",
1359
- **kwargs) -> bool:
1360
- """Bağlam menüsü göster (kısayol)"""
1361
- if _context_menu_manager:
1362
- request = MenuRequest(
1363
- menu_type=menu_type,
1364
- context=context,
1365
- target_path=target_path,
1366
- position=position,
1367
- **kwargs
1368
- )
1369
- return _context_menu_manager.show_context_menu(request)
1370
- return False