kgtk-qt3 – TDE dialogs in GTK 2.x applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

672 lines
18KB

  1. /************************************************************************
  2. *
  3. * All dialogs opened are created and used modal.
  4. *
  5. ************************************************************************
  6. * (C) Craig Drummond, 2006
  7. ************************************************************************
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2 of the License, or (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the
  21. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  22. * Boston, MA 02111-1307, USA.
  23. *
  24. ************************************************************************/
  25. #define KTQT_OVERLOAD_NON_STATIC_FILEDIALOGS
  26. #define _GNU_SOURCE
  27. #include <dlfcn.h>
  28. #include <fcntl.h>
  29. #include <string.h>
  30. #include <errno.h>
  31. #include <signal.h>
  32. #include <sys/wait.h>
  33. #include <stdio.h>
  34. #include <unistd.h>
  35. #include <stdlib.h>
  36. #include <pwd.h>
  37. #include <errno.h>
  38. #include <string.h>
  39. #include <sys/types.h>
  40. #include <tqstring.h>
  41. #include <tqstringlist.h>
  42. #include <tqwidget.h>
  43. #include <tqapplication.h>
  44. #ifdef KTQT_OVERLOAD_NON_STATIC_FILEDIALOGS
  45. #include <tqcombobox.h>
  46. #include <tqlineedit.h>
  47. #include <tqobjectlist.h>
  48. #define private public // HACK HACK HACK!!!
  49. #endif
  50. #include <tqfiledialog.h>
  51. #include <tqthread.h>
  52. #include <tqnamespace.h>
  53. #include <tqeventloop.h>
  54. #include "connect.h"
  55. #include "config.h"
  56. #include "mangled.h"
  57. static bool useKde=false;
  58. #define MAX_LINE_LEN 1024
  59. #define MAX_APP_NAME_LEN 32
  60. static char * getAppNameFromPid(int pid)
  61. {
  62. static char appName[MAX_APP_NAME_LEN+1]="\0";
  63. int procFile=-1;
  64. char cmdline[MAX_LINE_LEN+1];
  65. sprintf(cmdline, "/proc/%d/cmdline",pid);
  66. if(-1!=(procFile=open(cmdline, O_RDONLY)))
  67. {
  68. if(read(procFile, cmdline, MAX_LINE_LEN)>7)
  69. {
  70. int len=strlen(cmdline),
  71. pos=0;
  72. for(pos=len-1; pos>0 && cmdline[pos] && cmdline[pos]!='/'; --pos)
  73. ;
  74. if(pos>=0 && pos<len)
  75. {
  76. strncpy(appName, &cmdline[pos ? pos+1 : 0], MAX_APP_NAME_LEN);
  77. appName[MAX_APP_NAME_LEN]='\0';
  78. }
  79. }
  80. close(procFile);
  81. }
  82. return appName;
  83. }
  84. static const char * getAppName(bool useTQt=true)
  85. {
  86. static const char *appName=NULL;
  87. if(!appName)
  88. {
  89. const char *a=useTQt && tqApp ? tqApp->argv()[0] : getAppNameFromPid(getpid());
  90. const char *slash;
  91. // Was the cmdline app java? if so, try to use its parent name - just in case
  92. // its run from a shell script, etc. - e.g. as eclipse does
  93. if(a && 0==strcmp(a, "java"))
  94. a=getAppNameFromPid(getppid());
  95. if(a && a[0]=='\0')
  96. a=NULL;
  97. appName=a && (slash=strrchr(a, '/')) && '\0'!=slash[1]
  98. ? &(slash[1])
  99. : a ? a : "TQtApp";
  100. }
  101. return appName;
  102. }
  103. int TQApplication::exec()
  104. {
  105. static bool init=false;
  106. if(!init)
  107. {
  108. connectToKDialogD(getAppName(false));
  109. init=true;
  110. }
  111. static int (*realFunction)(void *);
  112. if(!realFunction)
  113. realFunction = (int (*)(void *)) dlsym(RTLD_NEXT, KQT_QAPPLICATION_EXEC);
  114. return (int)realFunction(this);
  115. };
  116. static TQString qt2KdeFilter(const TQString &f)
  117. {
  118. TQString filter;
  119. TQTextOStream str(&filter);
  120. TQStringList list(TQStringList::split(";;", f));
  121. TQStringList::Iterator it(list.begin()),
  122. end(list.end());
  123. bool first=true;
  124. for(; it!=end; ++it)
  125. {
  126. int ob=(*it).findRev('('),
  127. cb=(*it).findRev(')');
  128. if(-1!=cb && ob<cb)
  129. {
  130. if(first)
  131. first=false;
  132. else
  133. str << '\n';
  134. str << (*it).mid(ob+1, (cb-ob)-1) << '|' << (*it).mid(0, ob);
  135. }
  136. }
  137. return filter;
  138. }
  139. static void kde2TQtFilter(const TQString &orig, TQString *sel)
  140. {
  141. if(sel)
  142. {
  143. TQStringList list(TQStringList::split(";;", orig));
  144. TQStringList::Iterator it(list.begin()),
  145. end(list.end());
  146. int pos;
  147. for(; it!=end; ++it)
  148. if(-1!=(pos=(*it).find(*sel)) && pos>0 &&
  149. ('('==(*it)[pos-1] || ' '==(*it)[pos-1]) &&
  150. (*it).length()>=sel->length()+pos &&
  151. (')'==(*it)[pos+sel->length()] || ' '==(*it)[pos+sel->length()]))
  152. {
  153. *sel=*it;
  154. return;
  155. }
  156. }
  157. }
  158. #ifdef KTQT_OVERLOAD_NON_STATIC_FILEDIALOGS
  159. #ifdef KTQT_USE_TQFILEDIALOG_PRIVATE
  160. // HACK HACK HACK!!!
  161. // KGtk versions <=0.9.1 used this copied TQFileDialogPrivate to access the file filters
  162. // newer versions walk the file dialogs tqchildren...
  163. class TQFileDialogPrivate {
  164. public:
  165. ~TQFileDialogPrivate();
  166. TQStringList history;
  167. bool geometryDirty;
  168. TQComboBox * paths;
  169. TQComboBox * types;
  170. };
  171. #endif
  172. static const TQString getFilters(TQFileDialog *dlg, bool scribusSave=false)
  173. {
  174. TQString filter;
  175. #if KTQT_USE_TQFILEDIALOG_PRIVATE
  176. if(dlg && dlg->d && dlg->d->types)
  177. {
  178. TQTextOStream str(&filter);
  179. for(int i=0; i<dlg->d->types->count(); ++i)
  180. {
  181. if(i)
  182. str << ";;";
  183. if(scribusSave && -1!=dlg->d->types->text(i).find("(*.sla *.sla.gz *.scd *scd.gz)"))
  184. str << "Compressed Documents (*.sla.gz *scd.gz);;Documents (*.sla *.scd)";
  185. else
  186. str << dlg->d->types->text(i);
  187. }
  188. }
  189. #else
  190. if(dlg)
  191. {
  192. const TQObjectList tqchildren=((TQObject *)dlg)->childrenListObject();
  193. if(!tqchildren.isEmpty())
  194. {
  195. TQObjectList::ConstIterator it(tqchildren.begin()),
  196. end(tqchildren.end());
  197. for(; it!=end; ++it)
  198. if(::tqqt_cast<TQComboBox *>(*it) && 0==qstrcmp((*it)->name(), "file types"))
  199. {
  200. TQComboBox *types=(TQComboBox *)(*it);
  201. TQTextOStream str(&filter);
  202. for(int i=0; i<types->count(); ++i)
  203. {
  204. if(i)
  205. str << ";;";
  206. if(scribusSave && -1!=types->text(i).find("(*.sla *.sla.gz *.scd *scd.gz)"))
  207. str << "Compressed Documents (*.sla.gz *scd.gz);;Documents (*.sla *.scd)";
  208. else
  209. str << types->text(i);
  210. }
  211. break;
  212. }
  213. }
  214. }
  215. #endif
  216. return filter;
  217. }
  218. static TQString getCurrentFileName(TQFileDialog *dlg)
  219. {
  220. if(dlg)
  221. {
  222. const TQObjectList tqchildren=((TQObject *)dlg)->childrenListObject();
  223. if(!tqchildren.isEmpty())
  224. {
  225. TQObjectList::ConstIterator it(tqchildren.begin()),
  226. end(tqchildren.end());
  227. for(; it!=end; ++it)
  228. if(::tqqt_cast<TQLineEdit *>(*it)) // && 0==qstrcmp((*it)->name(), "name/filter editor"))
  229. return ((TQLineEdit *)(*it))->text();
  230. }
  231. }
  232. return TQString();
  233. }
  234. static TQString getDir(const TQString &f)
  235. {
  236. TQString d(f);
  237. int slashPos=d.findRev('/');
  238. if(slashPos!=-1)
  239. d.remove(slashPos+1, d.length());
  240. return d;
  241. }
  242. #endif
  243. static bool writeString(int fd, const TQString &str)
  244. {
  245. TQCString utf8(str.utf8());
  246. int size=utf8.length()+1;
  247. return writeBlock(fd, (char *)&size, 4) && writeBlock(fd, utf8.data(), size);
  248. }
  249. static bool writeBool(int fd, bool b)
  250. {
  251. char bv=b ? 1 : 0;
  252. return writeBlock(fd, (char *)&bv, 1);
  253. }
  254. class KTQtDialog : public TQDialog
  255. {
  256. public:
  257. KTQtDialog(TQWidget *parent) : TQDialog(parent, "kqt", true, WStyle_NoBorder|WX11BypassWM)
  258. {
  259. resize(1, 1);
  260. setWindowOpacity(0.0);
  261. setWindowState(WState_Minimized);
  262. move(32768, 32768);
  263. }
  264. /* void r() { TQDialog::reject(); }*/
  265. };
  266. class KTQtThread : public TQThread
  267. {
  268. public:
  269. KTQtThread(TQStringList &l, TQString &s, int f, KTQtDialog *dlg) : dialog(dlg), kdialogdError(false), res(l), selFilter(s), fd(f)
  270. { }
  271. bool readData(TQCString &buffer, int size)
  272. {
  273. buffer.resize(size);
  274. return ::readBlock(fd, buffer.data(), size);
  275. }
  276. bool readString(TQString &str, int size)
  277. {
  278. TQCString buffer;
  279. buffer.resize(size);
  280. if(!readBlock(fd, buffer.data(), size))
  281. return false;
  282. str=TQString::fromUtf8(buffer.data());
  283. return true;
  284. }
  285. void run()
  286. {
  287. TQString buffer;
  288. int num=0;
  289. if(readBlock(fd, (char *)&num, 4))
  290. {
  291. int n;
  292. for(n=0; n<num && !kdialogdError; ++n)
  293. {
  294. int size=0;
  295. if(readBlock(fd, (char *)&size, 4))
  296. {
  297. if(size>0)
  298. {
  299. if(readString(buffer, size))
  300. {
  301. //buffer[size-1]='\0';
  302. if('/'==buffer[0])
  303. res.append(buffer);
  304. else
  305. selFilter=buffer;
  306. }
  307. else
  308. kdialogdError=true;
  309. }
  310. }
  311. else
  312. kdialogdError=true;
  313. }
  314. }
  315. else
  316. kdialogdError=true;
  317. TQApplication::postEvent(dialog, new TQCloseEvent);
  318. }
  319. KTQtDialog *dialog;
  320. bool kdialogdError;
  321. TQStringList &res;
  322. TQString &selFilter;
  323. int fd;
  324. };
  325. static bool sendMessage(TQWidget *parent, Operation op, TQStringList &res, TQString &selFilter,
  326. const TQString &title, const TQString &p1, const TQString *p2, bool ow)
  327. {
  328. if(connectToKDialogD(getAppName()))
  329. {
  330. char o=(char)op;
  331. int xid=parent ? parent->tqtopLevelWidget()->winId() : tqApp->activeWindow()->winId();
  332. if(writeBlock(kdialogdSocket, &o, 1) &&
  333. writeBlock(kdialogdSocket, (char *)&xid, 4) &&
  334. writeString(kdialogdSocket, title) &&
  335. writeString(kdialogdSocket, p1) &&
  336. (p2? writeString(kdialogdSocket, *p2) : true) &&
  337. (OP_FILE_SAVE==op ? writeBool(kdialogdSocket, ow) : true))
  338. {
  339. KTQtDialog dlg(parent);
  340. KTQtThread thread(res, selFilter, kdialogdSocket, &dlg);
  341. thread.start();
  342. dlg.exec();
  343. thread.wait();
  344. if(thread.kdialogdError)
  345. {
  346. closeConnection();
  347. return false;
  348. }
  349. return true;
  350. }
  351. }
  352. return false;
  353. }
  354. static TQString getTitle(const TQString &title, Operation op)
  355. {
  356. if(!title.isEmpty())
  357. return title;
  358. return ".";
  359. }
  360. static bool openKdeDialog(TQWidget *widget, const TQString &title, const TQString &p1, const TQString *p2,
  361. Operation op, TQStringList &res, TQString *selFilter, bool ow=false)
  362. {
  363. TQString filter;
  364. bool rv=sendMessage(widget, op, res, filter, getTitle(title, op), p1, p2, ow);
  365. // If we failed to talk to, or start kdialogd, then dont keep trying - just fall back to TQt
  366. if(!rv)
  367. /*useKde=false*/;
  368. else if(selFilter)
  369. *selFilter=filter;
  370. return rv;
  371. }
  372. static void kqtExit()
  373. {
  374. if(useKde)
  375. closeConnection();
  376. }
  377. static bool kqtInit()
  378. {
  379. static bool initialised=false;
  380. if(!initialised)
  381. {
  382. initialised=true;
  383. useKde=NULL!=getenv("TDE_FULL_SESSION") && connectToKDialogD(getAppName());
  384. if(useKde)
  385. atexit(&kqtExit);
  386. }
  387. return useKde;
  388. }
  389. static TQString lastDir;
  390. static void storeLastDir(const TQString &f)
  391. {
  392. lastDir=f;
  393. int slashPos(lastDir.findRev('/'));
  394. if(slashPos!=-1)
  395. lastDir.remove(slashPos+1, lastDir.length());
  396. }
  397. static const TQString & startDir(const TQString &d)
  398. {
  399. return d.isEmpty() ? lastDir : d;
  400. }
  401. TQString TQFileDialog::getOpenFileName(const TQString &initially, const TQString &filter,
  402. TQWidget *parent, const char *name, const TQString &caption,
  403. TQString *selectedFilter, bool resolveSymlinks)
  404. {
  405. TQStringList res;
  406. TQString f(qt2KdeFilter(filter));
  407. kqtInit();
  408. if(openKdeDialog(parent, caption, startDir(initially), &f, OP_FILE_OPEN, res, selectedFilter))
  409. {
  410. kde2TQtFilter(filter, selectedFilter);
  411. TQString fn(res.first());
  412. storeLastDir(fn);
  413. return fn;
  414. }
  415. return TQString();
  416. }
  417. TQString TQFileDialog::getSaveFileName(const TQString &initially, const TQString &filter, TQWidget *parent,
  418. const char *name, const TQString &caption,
  419. TQString *selectedFilter, bool resolveSymlinks)
  420. {
  421. TQStringList res;
  422. TQString f(qt2KdeFilter(filter));
  423. kqtInit();
  424. if (openKdeDialog(parent, caption, startDir(initially), &f, OP_FILE_SAVE, res, selectedFilter))
  425. {
  426. kde2TQtFilter(filter, selectedFilter);
  427. TQString fn(res.first());
  428. storeLastDir(fn);
  429. return fn;
  430. }
  431. return TQString();
  432. }
  433. TQString TQFileDialog::getExistingDirectory(const TQString &dir, TQWidget *parent, const char *name,
  434. const TQString &caption, bool dirOnly, bool resolveSymlinks)
  435. {
  436. TQStringList res;
  437. TQString dummy;
  438. kqtInit();
  439. return openKdeDialog(parent, caption, dir, NULL, OP_FOLDER, res, &dummy)
  440. ? res.first()
  441. : TQString();
  442. }
  443. TQStringList TQFileDialog::getOpenFileNames(const TQString &filter, const TQString &dir, TQWidget *parent,
  444. const char *name, const TQString &caption,
  445. TQString *selectedFilter, bool resolveSymlinks)
  446. {
  447. TQStringList res;
  448. TQString f(qt2KdeFilter(filter));
  449. kqtInit();
  450. openKdeDialog(parent, caption, startDir(dir), &f, OP_FILE_OPEN_MULTIPLE, res, selectedFilter);
  451. if(res.count())
  452. {
  453. kde2TQtFilter(filter, selectedFilter);
  454. storeLastDir(res.first());
  455. }
  456. return res;
  457. }
  458. #ifdef KTQT_OVERLOAD_NON_STATIC_FILEDIALOGS
  459. static TQString getFile(const TQString &f)
  460. {
  461. TQString d(f);
  462. int slashPos=d.findRev('/');
  463. if(slashPos!=-1)
  464. d.remove(0, slashPos+1);
  465. return d;
  466. }
  467. int TQDialog::exec()
  468. {
  469. int res=TQDialog::Rejected;
  470. if(inherits("TQFileDialog"))
  471. {
  472. TQFileDialog *that=(TQFileDialog *)this;
  473. const TQDir *dirp=that->dir();
  474. TQString dir,
  475. selectedFilter,
  476. file,
  477. initialDir(dirp ? dirp->absPath() : TQDir::homeDirPath());
  478. TQStringList files;
  479. if(dirp)
  480. delete dirp;
  481. TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 1);
  482. switch(that->mode())
  483. {
  484. case TQFileDialog::Directory:
  485. case TQFileDialog::DirectoryOnly:
  486. dir=TQFileDialog::getExistingDirectory(initialDir, parentWidget(), NULL,
  487. caption(), true, true);
  488. if(!dir.isEmpty())
  489. res=TQDialog::Accepted;
  490. break;
  491. case TQFileDialog::AnyFile:
  492. {
  493. TQString app(getFile(tqApp->argv()[0])),
  494. initialFile(getCurrentFileName(that));
  495. if(!initialFile.isEmpty())
  496. initialDir=initialDir+TQChar('/')+initialFile;
  497. file=TQFileDialog::getSaveFileName(initialDir,
  498. getFilters(that, "scribus"==app ||
  499. "scribus-ng"==app),
  500. parentWidget(), NULL, caption(), &selectedFilter,
  501. true);
  502. if(!file.isEmpty())
  503. res=TQDialog::Accepted;
  504. break;
  505. }
  506. case TQFileDialog::ExistingFile:
  507. file=TQFileDialog::getOpenFileName(initialDir, getFilters(that), parentWidget(),
  508. NULL, caption(), &selectedFilter, true);
  509. if(!file.isEmpty())
  510. res=TQDialog::Accepted;
  511. break;
  512. case TQFileDialog::ExistingFiles:
  513. files=TQFileDialog::getOpenFileNames(getFilters(that), initialDir, parentWidget(),
  514. NULL, caption(), &selectedFilter, true);
  515. if(files.count())
  516. res=TQDialog::Accepted;
  517. break;
  518. }
  519. if(TQDialog::Accepted==res)
  520. {
  521. if(file.isEmpty() && files.count())
  522. file=files.first();
  523. if(dir.isEmpty() && !file.isEmpty())
  524. dir=getDir(file);
  525. if(!dir.isEmpty())
  526. that->setDir(dir);
  527. if(!selectedFilter.isEmpty())
  528. that->setSelectedFilter(selectedFilter);
  529. if(!file.isEmpty())
  530. that->setSelection(file);
  531. if(files.count() && that->nameEdit)
  532. {
  533. TQStringList::Iterator it(files.begin()),
  534. end(files.end());
  535. TQString filesStr;
  536. TQTextOStream str(&filesStr);
  537. for(; it!=end; ++it)
  538. str << "\"" << (*it) << "\" ";
  539. that->nameEdit->setText(filesStr);
  540. }
  541. TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 1);
  542. }
  543. }
  544. else
  545. {
  546. static int (*realFunction)(void *);
  547. if(!realFunction)
  548. realFunction = (int (*)(void *)) dlsym(RTLD_NEXT, KQT_QDIALOG_EXEC);
  549. return (int)realFunction(this);
  550. }
  551. return res;
  552. }
  553. #endif