00001 #include <qfile.h>
00002 #include <qclipboard.h>
00003 #include <qapplication.h>
00004
00005 #include <kaction.h>
00006 #include <kstdaction.h>
00007 #include <kstddirs.h>
00008 #include <klocale.h>
00009 #include <kpopupmenu.h>
00010 #include <kiconloader.h>
00011 #include <kmainwindow.h>
00012 #include <khtmlview.h>
00013
00014 #include <kdevmainwindow.h>
00015
00016
00017
00018
00019 #include "kdevhtmlpart.h"
00020
00021 KDevHTMLPart::KDevHTMLPart()
00022 : KHTMLPart(0L, 0L, 0L, "KDevHTMLPart", BrowserViewGUI )
00023 {
00024 setXMLFile(locate("data", "kdevelop/kdevhtml_partui.rc"), true);
00025
00026 connect(browserExtension(), SIGNAL(openURLRequestDelayed(const KURL &,const KParts::URLArgs &)),
00027 this, SLOT(openURLRequest(const KURL &)) );
00028
00029 connect(this, SIGNAL(started(KIO::Job *)), this, SLOT(slotStarted(KIO::Job* )));
00030 connect(this, SIGNAL(completed()), this, SLOT(slotCompleted()));
00031 connect(this, SIGNAL(canceled(const QString &)), this, SLOT(slotCancelled(const QString &)));
00032
00033 KActionCollection * actions = actionCollection();
00034 reloadAction = new KAction( i18n( "Reload" ), "reload", 0,
00035 this, SLOT( slotReload() ), actions, "doc_reload" );
00036 reloadAction->setWhatsThis(i18n("<b>Reload</b><p>Reloads the current document."));
00037 stopAction = new KAction( i18n( "Stop" ), "stop", 0,
00038 this, SLOT( slotStop() ), actions, "doc_stop" );
00039 stopAction->setWhatsThis(i18n("<b>Stop</b><p>Stops the loading of current document."));
00040 duplicateAction = new KAction( i18n( "Duplicate Tab" ), "window_new", 0,
00041 this, SLOT( slotDuplicate() ), actions, "doc_dup" );
00042 duplicateAction->setWhatsThis(i18n("<b>Duplicate window</b><p>Opens current document in a new window."));
00043 printAction = KStdAction::print(this, SLOT(slotPrint()), actions, "print_doc");
00044 copyAction = KStdAction::copy(this, SLOT(slotCopy()), actions, "copy_doc_selection");
00045
00046 connect( this, SIGNAL(popupMenu(const QString &, const QPoint &)), this, SLOT(popup(const QString &, const QPoint &)));
00047 connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
00048
00049
00050
00051 m_backAction = new KToolBarPopupAction(i18n("Back"), "back", 0,
00052 this, SLOT(slotBack()),
00053 actions, "browser_back");
00054 m_backAction->setEnabled( false );
00055 m_backAction->setToolTip(i18n("Back"));
00056 m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the <b>documentation</b> browsing history."));
00057
00058 connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()),
00059 this, SLOT(slotBackAboutToShow()));
00060 connect(m_backAction->popupMenu(), SIGNAL(activated(int)),
00061 this, SLOT(slotPopupActivated(int)));
00062
00063 m_forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", 0,
00064 this, SLOT(slotForward()),
00065 actions, "browser_forward");
00066 m_forwardAction->setEnabled( false );
00067 m_forwardAction->setToolTip(i18n("Forward"));
00068 m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the <b>documentation</b> browsing history."));
00069
00070 connect(m_forwardAction->popupMenu(), SIGNAL(aboutToShow()),
00071 this, SLOT(slotForwardAboutToShow()));
00072 connect(m_forwardAction->popupMenu(), SIGNAL(activated(int)),
00073 this, SLOT(slotPopupActivated(int)));
00074
00075 m_restoring = false;
00076 m_Current = m_history.end();
00077
00078
00079 }
00080
00081 void KDevHTMLPart::popup( const QString & url, const QPoint & p )
00082 {
00083
00084 KPopupMenu popup(this->widget());
00085
00086 bool needSep = false;
00087 int idNewWindow = -2;
00088 if (!url.isEmpty() && (m_options & CanOpenInNewWindow))
00089 {
00090 idNewWindow = popup.insertItem(SmallIcon("window_new"),i18n("Open in New Tab"));
00091 popup.setWhatsThis(idNewWindow, i18n("<b>Open in new window</b><p>Opens current link in a new window."));
00092 needSep = true;
00093 }
00094 if (m_options & CanDuplicate)
00095 {
00096 duplicateAction->plug(&popup);
00097 needSep = true;
00098 }
00099 if (needSep)
00100 popup.insertSeparator();
00101
00102 m_backAction->plug( &popup );
00103 m_forwardAction->plug( &popup );
00104 reloadAction->plug(&popup);
00105
00106 popup.insertSeparator();
00107
00108 copyAction->plug( &popup );
00109 popup.insertSeparator();
00110
00111 printAction->plug(&popup);
00112 popup.insertSeparator();
00113
00114 KAction * incFontAction = this->action("incFontSizes");
00115 KAction * decFontAction = this->action("decFontSizes");
00116 if ( incFontAction && decFontAction )
00117 {
00118 incFontAction->plug( &popup );
00119 decFontAction->plug( &popup );
00120 popup.insertSeparator();
00121 }
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138 KAction *ac = action("setEncoding");
00139 if (ac)
00140 ac->plug(&popup);
00141
00142 int r = popup.exec(p);
00143
00144 if (r == idNewWindow)
00145 {
00146 KURL kurl;
00147 if (!KURL(url).path().startsWith("/"))
00148 {
00149 kdDebug() << "processing relative url: " << url << endl;
00150 if (url.startsWith("#"))
00151 {
00152 kurl = KURL(KDevHTMLPart::url());
00153 kurl.setRef(url.mid(1));
00154 }
00155 else
00156 kurl = KURL(KDevHTMLPart::url().upURL().url(true)+url);
00157 }
00158 else
00159 kurl = KURL(url);
00160
00161 if (kurl.isValid())
00162 slotOpenInNewWindow(kurl);
00163
00164 }
00165 }
00166
00167 void KDevHTMLPart::setContext(const QString &context)
00168 {
00169 m_context = context;
00170 }
00171
00172
00173 QString KDevHTMLPart::context() const
00174 {
00175 return m_context;
00176 }
00177
00178
00179
00180 static bool isUtf8(const char *buf) {
00181 int i, n;
00182 register unsigned char c;
00183 bool gotone = false;
00184
00185 #define F 0
00186 #define T 1
00187 #define I 2
00188 #define X 3
00189
00190 static const unsigned char text_chars[256] = {
00191
00192 F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F,
00193
00194 F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F,
00195 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
00196 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
00197 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
00198 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
00199 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
00200 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F,
00201
00202 X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X,
00203 X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
00204 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
00205 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
00206 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
00207 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
00208 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
00209 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I
00210 };
00211
00212
00213 for (i = 0; (c = buf[i]); i++) {
00214 if ((c & 0x80) == 0) {
00215
00216
00217
00218
00219
00220 if (text_chars[c] != T)
00221 return false;
00222
00223 } else if ((c & 0x40) == 0) {
00224 return false;
00225 } else {
00226 int following;
00227
00228 if ((c & 0x20) == 0) {
00229 following = 1;
00230 } else if ((c & 0x10) == 0) {
00231 following = 2;
00232 } else if ((c & 0x08) == 0) {
00233 following = 3;
00234 } else if ((c & 0x04) == 0) {
00235 following = 4;
00236 } else if ((c & 0x02) == 0) {
00237 following = 5;
00238 } else
00239 return false;
00240
00241 for (n = 0; n < following; n++) {
00242 i++;
00243 if (!(c = buf[i]))
00244 goto done;
00245
00246 if ((c & 0x80) == 0 || (c & 0x40))
00247 return false;
00248 }
00249 gotone = true;
00250 }
00251 }
00252 done:
00253 return gotone;
00254 }
00255 #undef F
00256 #undef T
00257 #undef I
00258 #undef X
00259
00260 QString KDevHTMLPart::resolveEnvVarsInURL(const QString& url)
00261 {
00262
00263 QString path = url;
00264 int nDollarPos = path.find( '$' );
00265
00266
00267 while( nDollarPos != -1 && nDollarPos+1 < static_cast<int>(path.length())) {
00268
00269 if( (path)[nDollarPos+1] == '(' ) {
00270 uint nEndPos = nDollarPos+1;
00271
00272 while ( (nEndPos <= path.length()) && (path[nEndPos]!=')') )
00273 nEndPos++;
00274 nEndPos++;
00275 QString cmd = path.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
00276
00277 QString result;
00278 FILE *fs = popen(QFile::encodeName(cmd).data(), "r");
00279 if (fs)
00280 {
00281 QTextStream ts(fs, IO_ReadOnly);
00282 result = ts.read().stripWhiteSpace();
00283 pclose(fs);
00284 }
00285 path.replace( nDollarPos, nEndPos-nDollarPos, result );
00286 } else if( (path)[nDollarPos+1] != '$' ) {
00287 uint nEndPos = nDollarPos+1;
00288
00289 QString aVarName;
00290 if (path[nEndPos]=='{')
00291 {
00292 while ( (nEndPos <= path.length()) && (path[nEndPos]!='}') )
00293 nEndPos++;
00294 nEndPos++;
00295 aVarName = path.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
00296 }
00297 else
00298 {
00299 while ( nEndPos <= path.length() && (path[nEndPos].isNumber()
00300 || path[nEndPos].isLetter() || path[nEndPos]=='_' ) )
00301 nEndPos++;
00302 aVarName = path.mid( nDollarPos+1, nEndPos-nDollarPos-1 );
00303 }
00304 const char* pEnv = 0;
00305 if (!aVarName.isEmpty())
00306 pEnv = getenv( aVarName.ascii() );
00307 if( pEnv ) {
00308
00309
00310
00311 if (isUtf8( pEnv ))
00312 path.replace( nDollarPos, nEndPos-nDollarPos, QString::fromUtf8(pEnv) );
00313 else
00314 path.replace( nDollarPos, nEndPos-nDollarPos, QString::fromLocal8Bit(pEnv) );
00315 } else
00316 path.remove( nDollarPos, nEndPos-nDollarPos );
00317 } else {
00318
00319 path.remove( nDollarPos, 1 );
00320 nDollarPos++;
00321 }
00322 nDollarPos = path.find( '$', nDollarPos );
00323 }
00324
00325 return path;
00326 }
00327
00328 bool KDevHTMLPart::openURL(const KURL &url)
00329 {
00330 QString path = resolveEnvVarsInURL(url.url());
00331 KURL newUrl(path);
00332
00333 bool retval = KHTMLPart::openURL(newUrl);
00334 if ( retval )
00335 {
00336 emit fileNameChanged(this);
00337 if ( !m_restoring )
00338 {
00339 addHistoryEntry();
00340 }
00341 }
00342
00343 m_backAction->setEnabled( m_Current != m_history.begin() );
00344 m_forwardAction->setEnabled( m_Current != m_history.fromLast() );
00345
00346 return retval;
00347 }
00348
00349 void KDevHTMLPart::openURLRequest(const KURL &url)
00350 {
00351 openURL( url );
00352 }
00353
00354 void KDevHTMLPart::slotReload( )
00355 {
00356 openURL( url() );
00357 }
00358
00359 void KDevHTMLPart::slotStop( )
00360 {
00361 closeURL();
00362 }
00363
00364 void KDevHTMLPart::slotStarted( KIO::Job * )
00365 {
00366 stopAction->setEnabled(true);
00367 }
00368
00369 void KDevHTMLPart::slotCompleted( )
00370 {
00371 stopAction->setEnabled(false);
00372 }
00373
00374 void KDevHTMLPart::slotCancelled( const QString & )
00375 {
00376 stopAction->setEnabled(false);
00377 }
00378
00379
00380
00381
00382
00383
00384 void KDevHTMLPart::slotPrint( )
00385 {
00386 view()->print();
00387 }
00388
00389 void KDevHTMLPart::slotBack()
00390 {
00391 if ( m_Current != m_history.begin() )
00392 {
00393 --m_Current;
00394 m_restoring = true;
00395 openURL( (*m_Current).url );
00396 m_restoring = false;
00397 }
00398 }
00399
00400 void KDevHTMLPart::slotForward()
00401 {
00402 if ( m_Current != m_history.fromLast() )
00403 {
00404 ++m_Current;
00405 m_restoring = true;
00406 openURL( (*m_Current).url );
00407 m_restoring = false;
00408 }
00409 }
00410
00411 void KDevHTMLPart::slotBackAboutToShow()
00412 {
00413 KPopupMenu *popup = m_backAction->popupMenu();
00414 popup->clear();
00415
00416 if ( m_Current == m_history.begin() ) return;
00417
00418 QValueList<DocumentationHistoryEntry>::Iterator it = m_Current;
00419 --it;
00420
00421 int i = 0;
00422 while( i < 10 )
00423 {
00424 if ( it == m_history.begin() )
00425 {
00426 popup->insertItem( (*it).url.url(), (*it).id );
00427 return;
00428 }
00429
00430 popup->insertItem( (*it).url.url(), (*it).id );
00431 ++i;
00432 --it;
00433 }
00434 }
00435
00436 void KDevHTMLPart::slotForwardAboutToShow()
00437 {
00438 KPopupMenu *popup = m_forwardAction->popupMenu();
00439 popup->clear();
00440
00441 if ( m_Current == m_history.fromLast() ) return;
00442
00443 QValueList<DocumentationHistoryEntry>::Iterator it = m_Current;
00444 ++it;
00445
00446 int i = 0;
00447 while( i < 10 )
00448 {
00449 if ( it == m_history.fromLast() )
00450 {
00451 popup->insertItem( (*it).url.url(), (*it).id );
00452 return;
00453 }
00454
00455 popup->insertItem( (*it).url.url(), (*it).id );
00456 ++i;
00457 ++it;
00458 }
00459 }
00460
00461 void KDevHTMLPart::slotPopupActivated( int id )
00462 {
00463 kdDebug(9000) << "id: " << id << endl;
00464
00465 QValueList<DocumentationHistoryEntry>::Iterator it = m_history.begin();
00466 while( it != m_history.end() )
00467 {
00468 kdDebug(9000) << "(*it).id: " << (*it).id << endl;
00469 if ( (*it).id == id )
00470 {
00471 m_Current = it;
00472 m_restoring = true;
00473 openURL( (*m_Current).url );
00474 m_restoring = false;
00475 return;
00476 }
00477 ++it;
00478 }
00479 }
00480
00481 void KDevHTMLPart::addHistoryEntry()
00482 {
00483 QValueList<DocumentationHistoryEntry>::Iterator it = m_Current;
00484
00485
00486 if ( it != m_history.end() && it != m_history.fromLast() )
00487 {
00488 m_history.erase( ++it, m_history.end() );
00489 }
00490
00491 DocumentationHistoryEntry newEntry( url() );
00492
00493
00494 if ( newEntry.url != (*m_Current).url )
00495 {
00496 m_history.append( newEntry );
00497 m_Current = m_history.fromLast();
00498 }
00499 }
00500
00501 void KDevHTMLPart::slotCopy( )
00502 {
00503 QString text = selectedText();
00504 text.replace( QChar( 0xa0 ), ' ' );
00505 QClipboard *cb = QApplication::clipboard();
00506 disconnect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
00507 cb->setText(text);
00508 connect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
00509 }
00510
00511 void KDevHTMLPart::slotSelectionChanged( )
00512 {
00513 if (selectedText().isEmpty())
00514 copyAction->setEnabled(false);
00515 else
00516 copyAction->setEnabled(true);
00517 }
00518
00519 #include "kdevhtmlpart.moc"